Merge remote-tracking branch 'upstream/main'
This commit is contained in:
commit
f718ca030f
|
@ -88,7 +88,6 @@
|
||||||
|
|
||||||
## 最新动态
|
## 最新动态
|
||||||
|
|
||||||
- 🚀 v2.9.6 版本发布
|
|
||||||
- 🚀 v2.9.5 正式版本发布
|
- 🚀 v2.9.5 正式版本发布
|
||||||
- 🚀 v2.9.1-plugin-preview 预览版发布。
|
- 🚀 v2.9.1-plugin-preview 预览版发布。
|
||||||
|
|
||||||
|
|
20
README_CN.md
20
README_CN.md
|
@ -90,6 +90,20 @@ OpenAI 接口代理 URL,如果你手动配置了 openai 接口代理,请填
|
||||||
|
|
||||||
指定 OpenAI 中的组织 ID。
|
指定 OpenAI 中的组织 ID。
|
||||||
|
|
||||||
|
### `AZURE_URL` (可选)
|
||||||
|
|
||||||
|
> 形如:https://{azure-resource-url}/openai/deployments/{deploy-name}
|
||||||
|
|
||||||
|
Azure 部署地址。
|
||||||
|
|
||||||
|
### `AZURE_API_KEY` (可选)
|
||||||
|
|
||||||
|
Azure 密钥。
|
||||||
|
|
||||||
|
### `AZURE_API_VERSION` (可选)
|
||||||
|
|
||||||
|
Azure Api 版本,你可以在这里找到:[Azure 文档](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#chat-completions)。
|
||||||
|
|
||||||
### `HIDE_USER_API_KEY` (可选)
|
### `HIDE_USER_API_KEY` (可选)
|
||||||
|
|
||||||
如果你不想让用户自行填入 API Key,将此环境变量设置为 1 即可。
|
如果你不想让用户自行填入 API Key,将此环境变量设置为 1 即可。
|
||||||
|
@ -108,9 +122,9 @@ OpenAI 接口代理 URL,如果你手动配置了 openai 接口代理,请填
|
||||||
|
|
||||||
### `CUSTOM_MODELS` (可选)
|
### `CUSTOM_MODELS` (可选)
|
||||||
|
|
||||||
> 示例:`+qwen-7b-chat,+glm-6b,-gpt-3.5-turbo` 表示增加 `qwen-7b-chat` 和 `glm-6b` 到模型列表,而从列表中删除 `gpt-3.5-turbo`。
|
> 示例:`+qwen-7b-chat,+glm-6b,-gpt-3.5-turbo,gpt-4-1106-preview:gpt-4-turbo` 表示增加 `qwen-7b-chat` 和 `glm-6b` 到模型列表,而从列表中删除 `gpt-3.5-turbo`,并将 `gpt-4-1106-preview` 模型名字展示为 `gpt-4-turbo`。
|
||||||
|
|
||||||
用来控制模型列表,使用 `+` 增加一个模型,使用 `-` 来隐藏一个模型,用英文逗号隔开。
|
用来控制模型列表,使用 `+` 增加一个模型,使用 `-` 来隐藏一个模型,使用 `模型名:展示名` 来自定义模型的展示名,用英文逗号隔开。
|
||||||
|
|
||||||
## 开发
|
## 开发
|
||||||
|
|
||||||
|
@ -124,7 +138,7 @@ OpenAI 接口代理 URL,如果你手动配置了 openai 接口代理,请填
|
||||||
OPENAI_API_KEY=<your api key here>
|
OPENAI_API_KEY=<your api key here>
|
||||||
|
|
||||||
# 中国大陆用户,可以使用本项目自带的代理进行开发,你也可以自由选择其他代理地址
|
# 中国大陆用户,可以使用本项目自带的代理进行开发,你也可以自由选择其他代理地址
|
||||||
BASE_URL=https://nb.nextweb.fun/api/proxy
|
BASE_URL=https://a.nextweb.fun/api/proxy
|
||||||
```
|
```
|
||||||
|
|
||||||
### 本地开发
|
### 本地开发
|
||||||
|
|
|
@ -28,7 +28,7 @@ export function auth(req: NextRequest) {
|
||||||
const authToken = req.headers.get("Authorization") ?? "";
|
const authToken = req.headers.get("Authorization") ?? "";
|
||||||
|
|
||||||
// check if it is openai api key or user token
|
// check if it is openai api key or user token
|
||||||
const { accessCode, apiKey: token } = parseApiKey(authToken);
|
const { accessCode, apiKey } = parseApiKey(authToken);
|
||||||
|
|
||||||
const hashedCode = md5.hash(accessCode ?? "").trim();
|
const hashedCode = md5.hash(accessCode ?? "").trim();
|
||||||
|
|
||||||
|
@ -39,19 +39,32 @@ export function auth(req: NextRequest) {
|
||||||
console.log("[User IP] ", getIP(req));
|
console.log("[User IP] ", getIP(req));
|
||||||
console.log("[Time] ", new Date().toLocaleString());
|
console.log("[Time] ", new Date().toLocaleString());
|
||||||
|
|
||||||
if (serverConfig.needCode && !serverConfig.codes.has(hashedCode) && !token) {
|
if (serverConfig.needCode && !serverConfig.codes.has(hashedCode) && !apiKey) {
|
||||||
return {
|
return {
|
||||||
error: true,
|
error: true,
|
||||||
msg: !accessCode ? "empty access code" : "wrong access code",
|
msg: !accessCode ? "empty access code" : "wrong access code",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (serverConfig.hideUserApiKey && !!apiKey) {
|
||||||
|
return {
|
||||||
|
error: true,
|
||||||
|
msg: "you are not allowed to access openai with your own api key",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// if user does not provide an api key, inject system api key
|
// if user does not provide an api key, inject system api key
|
||||||
if (!token) {
|
if (!apiKey) {
|
||||||
const apiKey = serverConfig.apiKey;
|
const serverApiKey = serverConfig.isAzure
|
||||||
if (apiKey) {
|
? serverConfig.azureApiKey
|
||||||
|
: serverConfig.apiKey;
|
||||||
|
|
||||||
|
if (serverApiKey) {
|
||||||
console.log("[Auth] use system api key");
|
console.log("[Auth] use system api key");
|
||||||
req.headers.set("Authorization", `Bearer ${apiKey}`);
|
req.headers.set(
|
||||||
|
"Authorization",
|
||||||
|
`${serverConfig.isAzure ? "" : "Bearer "}${serverApiKey}`,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
console.log("[Auth] admin did not provide an api key");
|
console.log("[Auth] admin did not provide an api key");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,24 @@
|
||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
import { getServerSideConfig } from "../config/server";
|
import { getServerSideConfig } from "../config/server";
|
||||||
import { DEFAULT_MODELS, OPENAI_BASE_URL } from "../constant";
|
import { DEFAULT_MODELS, OPENAI_BASE_URL } from "../constant";
|
||||||
import { collectModelTable, collectModels } from "../utils/model";
|
import { collectModelTable } from "../utils/model";
|
||||||
|
import { makeAzurePath } from "../azure";
|
||||||
|
|
||||||
const serverConfig = getServerSideConfig();
|
const serverConfig = getServerSideConfig();
|
||||||
|
|
||||||
export async function requestOpenai(req: NextRequest) {
|
export async function requestOpenai(req: NextRequest) {
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
|
|
||||||
const authValue = req.headers.get("Authorization") ?? "";
|
const authValue = req.headers.get("Authorization") ?? "";
|
||||||
const openaiPath = `${req.nextUrl.pathname}${req.nextUrl.search}`.replaceAll(
|
const authHeaderName = serverConfig.isAzure ? "api-key" : "Authorization";
|
||||||
|
|
||||||
|
let path = `${req.nextUrl.pathname}${req.nextUrl.search}`.replaceAll(
|
||||||
"/api/openai/",
|
"/api/openai/",
|
||||||
"",
|
"",
|
||||||
);
|
);
|
||||||
|
|
||||||
let baseUrl = serverConfig.baseUrl ?? OPENAI_BASE_URL;
|
let baseUrl =
|
||||||
|
serverConfig.azureUrl || serverConfig.baseUrl || OPENAI_BASE_URL;
|
||||||
|
|
||||||
if (!baseUrl.startsWith("http")) {
|
if (!baseUrl.startsWith("http")) {
|
||||||
baseUrl = `https://${baseUrl}`;
|
baseUrl = `https://${baseUrl}`;
|
||||||
|
@ -23,7 +28,7 @@ export async function requestOpenai(req: NextRequest) {
|
||||||
baseUrl = baseUrl.slice(0, -1);
|
baseUrl = baseUrl.slice(0, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("[Proxy] ", openaiPath);
|
console.log("[Proxy] ", path);
|
||||||
console.log("[Base Url]", baseUrl);
|
console.log("[Base Url]", baseUrl);
|
||||||
console.log("[Org ID]", serverConfig.openaiOrgId);
|
console.log("[Org ID]", serverConfig.openaiOrgId);
|
||||||
|
|
||||||
|
@ -34,14 +39,24 @@ export async function requestOpenai(req: NextRequest) {
|
||||||
10 * 60 * 1000,
|
10 * 60 * 1000,
|
||||||
);
|
);
|
||||||
|
|
||||||
const fetchUrl = `${baseUrl}/${openaiPath}`;
|
if (serverConfig.isAzure) {
|
||||||
|
if (!serverConfig.azureApiVersion) {
|
||||||
|
return NextResponse.json({
|
||||||
|
error: true,
|
||||||
|
message: `missing AZURE_API_VERSION in server env vars`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
path = makeAzurePath(path, serverConfig.azureApiVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchUrl = `${baseUrl}/${path}`;
|
||||||
const fetchOptions: RequestInit = {
|
const fetchOptions: RequestInit = {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
"Cache-Control": "no-store",
|
"Cache-Control": "no-store",
|
||||||
Authorization: authValue,
|
[authHeaderName]: authValue,
|
||||||
...(process.env.OPENAI_ORG_ID && {
|
...(serverConfig.openaiOrgId && {
|
||||||
"OpenAI-Organization": process.env.OPENAI_ORG_ID,
|
"OpenAI-Organization": serverConfig.openaiOrgId,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
method: req.method,
|
method: req.method,
|
||||||
|
@ -66,7 +81,7 @@ export async function requestOpenai(req: NextRequest) {
|
||||||
const jsonBody = JSON.parse(clonedBody) as { model?: string };
|
const jsonBody = JSON.parse(clonedBody) as { model?: string };
|
||||||
|
|
||||||
// not undefined and is false
|
// not undefined and is false
|
||||||
if (modelTable[jsonBody?.model ?? ""] === false) {
|
if (modelTable[jsonBody?.model ?? ""].available === false) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{
|
{
|
||||||
error: true,
|
error: true,
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
export function makeAzurePath(path: string, apiVersion: string) {
|
||||||
|
// should omit /v1 prefix
|
||||||
|
path = path.replaceAll("v1/", "");
|
||||||
|
|
||||||
|
// should add api-key to query string
|
||||||
|
path += `${path.includes("?") ? "&" : "?"}api-version=${apiVersion}`;
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
import { getClientConfig } from "../config/client";
|
import { getClientConfig } from "../config/client";
|
||||||
import { ACCESS_CODE_PREFIX } from "../constant";
|
import { ACCESS_CODE_PREFIX, Azure, ServiceProvider } from "../constant";
|
||||||
import { ChatMessage, ModelType, useAccessStore } from "../store";
|
import { ChatMessage, ModelType, useAccessStore } from "../store";
|
||||||
import { ChatGPTApi } from "./platforms/openai";
|
import { ChatGPTApi } from "./platforms/openai";
|
||||||
|
|
||||||
|
@ -151,22 +151,26 @@ export const api = new ClientApi();
|
||||||
|
|
||||||
export function getHeaders() {
|
export function getHeaders() {
|
||||||
const accessStore = useAccessStore.getState();
|
const accessStore = useAccessStore.getState();
|
||||||
let headers: Record<string, string> = {
|
const headers: Record<string, string> = {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
"x-requested-with": "XMLHttpRequest",
|
"x-requested-with": "XMLHttpRequest",
|
||||||
};
|
};
|
||||||
|
|
||||||
const makeBearer = (token: string) => `Bearer ${token.trim()}`;
|
const isAzure = accessStore.provider === ServiceProvider.Azure;
|
||||||
|
const authHeader = isAzure ? "api-key" : "Authorization";
|
||||||
|
const apiKey = isAzure ? accessStore.azureApiKey : accessStore.openaiApiKey;
|
||||||
|
|
||||||
|
const makeBearer = (s: string) => `${isAzure ? "" : "Bearer "}${s.trim()}`;
|
||||||
const validString = (x: string) => x && x.length > 0;
|
const validString = (x: string) => x && x.length > 0;
|
||||||
|
|
||||||
// use user's api key first
|
// use user's api key first
|
||||||
if (validString(accessStore.token)) {
|
if (validString(apiKey)) {
|
||||||
headers.Authorization = makeBearer(accessStore.token);
|
headers[authHeader] = makeBearer(apiKey);
|
||||||
} else if (
|
} else if (
|
||||||
accessStore.enabledAccessControl() &&
|
accessStore.enabledAccessControl() &&
|
||||||
validString(accessStore.accessCode)
|
validString(accessStore.accessCode)
|
||||||
) {
|
) {
|
||||||
headers.Authorization = makeBearer(
|
headers[authHeader] = makeBearer(
|
||||||
ACCESS_CODE_PREFIX + accessStore.accessCode,
|
ACCESS_CODE_PREFIX + accessStore.accessCode,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import {
|
import {
|
||||||
|
ApiPath,
|
||||||
DEFAULT_API_HOST,
|
DEFAULT_API_HOST,
|
||||||
DEFAULT_MODELS,
|
DEFAULT_MODELS,
|
||||||
OpenaiPath,
|
OpenaiPath,
|
||||||
REQUEST_TIMEOUT_MS,
|
REQUEST_TIMEOUT_MS,
|
||||||
|
ServiceProvider,
|
||||||
} from "@/app/constant";
|
} from "@/app/constant";
|
||||||
import { useAccessStore, useAppConfig, useChatStore } from "@/app/store";
|
import { useAccessStore, useAppConfig, useChatStore } from "@/app/store";
|
||||||
|
|
||||||
|
@ -21,6 +23,7 @@ import {
|
||||||
} from "@fortaine/fetch-event-source";
|
} from "@fortaine/fetch-event-source";
|
||||||
import { prettyObject } from "@/app/utils/format";
|
import { prettyObject } from "@/app/utils/format";
|
||||||
import { getClientConfig } from "@/app/config/client";
|
import { getClientConfig } from "@/app/config/client";
|
||||||
|
import { makeAzurePath } from "@/app/azure";
|
||||||
|
|
||||||
export interface OpenAIListModelResponse {
|
export interface OpenAIListModelResponse {
|
||||||
object: string;
|
object: string;
|
||||||
|
@ -35,20 +38,35 @@ export class ChatGPTApi implements LLMApi {
|
||||||
private disableListModels = true;
|
private disableListModels = true;
|
||||||
|
|
||||||
path(path: string): string {
|
path(path: string): string {
|
||||||
let openaiUrl = useAccessStore.getState().openaiUrl;
|
const accessStore = useAccessStore.getState();
|
||||||
const apiPath = "/api/openai";
|
|
||||||
|
|
||||||
if (openaiUrl.length === 0) {
|
const isAzure = accessStore.provider === ServiceProvider.Azure;
|
||||||
|
|
||||||
|
if (isAzure && !accessStore.isValidAzure()) {
|
||||||
|
throw Error(
|
||||||
|
"incomplete azure config, please check it in your settings page",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let baseUrl = isAzure ? accessStore.azureUrl : accessStore.openaiUrl;
|
||||||
|
|
||||||
|
if (baseUrl.length === 0) {
|
||||||
const isApp = !!getClientConfig()?.isApp;
|
const isApp = !!getClientConfig()?.isApp;
|
||||||
openaiUrl = isApp ? DEFAULT_API_HOST : apiPath;
|
baseUrl = isApp ? DEFAULT_API_HOST : ApiPath.OpenAI;
|
||||||
}
|
}
|
||||||
if (openaiUrl.endsWith("/")) {
|
|
||||||
openaiUrl = openaiUrl.slice(0, openaiUrl.length - 1);
|
if (baseUrl.endsWith("/")) {
|
||||||
|
baseUrl = baseUrl.slice(0, baseUrl.length - 1);
|
||||||
}
|
}
|
||||||
if (!openaiUrl.startsWith("http") && !openaiUrl.startsWith(apiPath)) {
|
if (!baseUrl.startsWith("http") && !baseUrl.startsWith(ApiPath.OpenAI)) {
|
||||||
openaiUrl = "https://" + openaiUrl;
|
baseUrl = "https://" + baseUrl;
|
||||||
}
|
}
|
||||||
return [openaiUrl, path].join("/");
|
|
||||||
|
if (isAzure) {
|
||||||
|
path = makeAzurePath(path, accessStore.azureApiVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [baseUrl, path].join("/");
|
||||||
}
|
}
|
||||||
|
|
||||||
extractMessage(res: any) {
|
extractMessage(res: any) {
|
||||||
|
@ -163,14 +181,20 @@ export class ChatGPTApi implements LLMApi {
|
||||||
}
|
}
|
||||||
const text = msg.data;
|
const text = msg.data;
|
||||||
try {
|
try {
|
||||||
const json = JSON.parse(text);
|
const json = JSON.parse(text) as {
|
||||||
const delta = json.choices[0]?.delta.content;
|
choices: Array<{
|
||||||
|
delta: {
|
||||||
|
content: string;
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
const delta = json.choices[0]?.delta?.content;
|
||||||
if (delta) {
|
if (delta) {
|
||||||
responseText += delta;
|
responseText += delta;
|
||||||
options.onUpdate?.(responseText, delta);
|
options.onUpdate?.(responseText, delta);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("[Request] parse error", text, msg);
|
console.error("[Request] parse error", text);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onclose() {
|
onclose() {
|
||||||
|
|
|
@ -18,7 +18,7 @@ export function AuthPage() {
|
||||||
const goChat = () => navigate(Path.Chat);
|
const goChat = () => navigate(Path.Chat);
|
||||||
const resetAccessCode = () => {
|
const resetAccessCode = () => {
|
||||||
accessStore.update((access) => {
|
accessStore.update((access) => {
|
||||||
access.token = "";
|
access.openaiApiKey = "";
|
||||||
access.accessCode = "";
|
access.accessCode = "";
|
||||||
});
|
});
|
||||||
}; // Reset access code to empty string
|
}; // Reset access code to empty string
|
||||||
|
@ -56,11 +56,11 @@ export function AuthPage() {
|
||||||
<input
|
<input
|
||||||
className={styles["auth-input"]}
|
className={styles["auth-input"]}
|
||||||
type="password"
|
type="password"
|
||||||
placeholder={Locale.Settings.Token.Placeholder}
|
placeholder={Locale.Settings.Access.OpenAI.ApiKey.Placeholder}
|
||||||
value={accessStore.token}
|
value={accessStore.openaiApiKey}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
accessStore.update(
|
accessStore.update(
|
||||||
(access) => (access.token = e.currentTarget.value),
|
(access) => (access.openaiApiKey = e.currentTarget.value),
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -61,7 +61,10 @@ export function ChatItem(props: {
|
||||||
{props.narrow ? (
|
{props.narrow ? (
|
||||||
<div className={styles["chat-item-narrow"]}>
|
<div className={styles["chat-item-narrow"]}>
|
||||||
<div className={styles["chat-item-avatar"] + " no-dark"}>
|
<div className={styles["chat-item-avatar"] + " no-dark"}>
|
||||||
<MaskAvatar mask={props.mask} />
|
<MaskAvatar
|
||||||
|
avatar={props.mask.avatar}
|
||||||
|
model={props.mask.modelConfig.model}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles["chat-item-narrow-count"]}>
|
<div className={styles["chat-item-narrow-count"]}>
|
||||||
{props.count}
|
{props.count}
|
||||||
|
|
|
@ -442,11 +442,27 @@ export function ChatActions(props: {
|
||||||
|
|
||||||
// switch model
|
// switch model
|
||||||
const currentModel = chatStore.currentSession().mask.modelConfig.model;
|
const currentModel = chatStore.currentSession().mask.modelConfig.model;
|
||||||
const models = useAllModels()
|
const allModels = useAllModels();
|
||||||
.filter((m) => m.available)
|
const models = useMemo(
|
||||||
.map((m) => m.name);
|
() => allModels.filter((m) => m.available),
|
||||||
|
[allModels],
|
||||||
|
);
|
||||||
const [showModelSelector, setShowModelSelector] = useState(false);
|
const [showModelSelector, setShowModelSelector] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// if current model is not available
|
||||||
|
// switch to first available model
|
||||||
|
const isUnavaliableModel = !models.some((m) => m.name === currentModel);
|
||||||
|
if (isUnavaliableModel && models.length > 0) {
|
||||||
|
const nextModel = models[0].name as ModelType;
|
||||||
|
chatStore.updateCurrentSession(
|
||||||
|
(session) => (session.mask.modelConfig.model = nextModel),
|
||||||
|
);
|
||||||
|
showToast(nextModel);
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [currentModel, models]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles["chat-input-actions"]}>
|
<div className={styles["chat-input-actions"]}>
|
||||||
<div>
|
<div>
|
||||||
|
@ -525,8 +541,8 @@ export function ChatActions(props: {
|
||||||
<Selector
|
<Selector
|
||||||
defaultSelectedValue={currentModel}
|
defaultSelectedValue={currentModel}
|
||||||
items={models.map((m) => ({
|
items={models.map((m) => ({
|
||||||
title: m,
|
title: m.displayName,
|
||||||
value: m,
|
value: m.name,
|
||||||
}))}
|
}))}
|
||||||
onClose={() => setShowModelSelector(false)}
|
onClose={() => setShowModelSelector(false)}
|
||||||
onSelection={(s) => {
|
onSelection={(s) => {
|
||||||
|
@ -534,9 +550,6 @@ export function ChatActions(props: {
|
||||||
chatStore.updateCurrentSession((session) => {
|
chatStore.updateCurrentSession((session) => {
|
||||||
session.mask.modelConfig.model = s[0] as ModelType;
|
session.mask.modelConfig.model = s[0] as ModelType;
|
||||||
session.mask.syncGlobalConfig = false;
|
session.mask.syncGlobalConfig = false;
|
||||||
session.mask.usePlugins = /^gpt(?!.*03\d{2}$).*$/.test(
|
|
||||||
session.mask.modelConfig.model,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
showToast(s[0]);
|
showToast(s[0]);
|
||||||
}}
|
}}
|
||||||
|
@ -1028,7 +1041,9 @@ function _Chat() {
|
||||||
).then((res) => {
|
).then((res) => {
|
||||||
if (!res) return;
|
if (!res) return;
|
||||||
if (payload.key) {
|
if (payload.key) {
|
||||||
accessStore.update((access) => (access.token = payload.key!));
|
accessStore.update(
|
||||||
|
(access) => (access.openaiApiKey = payload.key!),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (payload.url) {
|
if (payload.url) {
|
||||||
accessStore.update((access) => (access.openaiUrl = payload.url!));
|
accessStore.update((access) => (access.openaiUrl = payload.url!));
|
||||||
|
@ -1188,7 +1203,12 @@ function _Chat() {
|
||||||
{["system"].includes(message.role) ? (
|
{["system"].includes(message.role) ? (
|
||||||
<Avatar avatar="2699-fe0f" />
|
<Avatar avatar="2699-fe0f" />
|
||||||
) : (
|
) : (
|
||||||
<MaskAvatar mask={session.mask} />
|
<MaskAvatar
|
||||||
|
avatar={session.mask.avatar}
|
||||||
|
model={
|
||||||
|
message.model || session.mask.modelConfig.model
|
||||||
|
}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -186,7 +186,8 @@
|
||||||
box-shadow: var(--card-shadow);
|
box-shadow: var(--card-shadow);
|
||||||
border: var(--border-in-light);
|
border: var(--border-in-light);
|
||||||
|
|
||||||
*:not(li) {
|
code,
|
||||||
|
pre {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* eslint-disable @next/next/no-img-element */
|
/* eslint-disable @next/next/no-img-element */
|
||||||
import { ChatMessage, useAppConfig, useChatStore } from "../store";
|
import { ChatMessage, ModelType, useAppConfig, useChatStore } from "../store";
|
||||||
import Locale from "../locales";
|
import Locale from "../locales";
|
||||||
import styles from "./exporter.module.scss";
|
import styles from "./exporter.module.scss";
|
||||||
import {
|
import {
|
||||||
|
@ -27,7 +27,7 @@ import { Avatar } from "./emoji";
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import NextImage from "next/image";
|
import NextImage from "next/image";
|
||||||
|
|
||||||
import { toBlob, toJpeg, toPng } from "html-to-image";
|
import { toBlob, toPng } from "html-to-image";
|
||||||
import { DEFAULT_MASK_AVATAR } from "../store/mask";
|
import { DEFAULT_MASK_AVATAR } from "../store/mask";
|
||||||
import { api } from "../client/api";
|
import { api } from "../client/api";
|
||||||
import { prettyObject } from "../utils/format";
|
import { prettyObject } from "../utils/format";
|
||||||
|
@ -41,7 +41,22 @@ const Markdown = dynamic(async () => (await import("./markdown")).Markdown, {
|
||||||
export function ExportMessageModal(props: { onClose: () => void }) {
|
export function ExportMessageModal(props: { onClose: () => void }) {
|
||||||
return (
|
return (
|
||||||
<div className="modal-mask">
|
<div className="modal-mask">
|
||||||
<Modal title={Locale.Export.Title} onClose={props.onClose}>
|
<Modal
|
||||||
|
title={Locale.Export.Title}
|
||||||
|
onClose={props.onClose}
|
||||||
|
footer={
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: "100%",
|
||||||
|
textAlign: "center",
|
||||||
|
fontSize: 14,
|
||||||
|
opacity: 0.5,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{Locale.Exporter.Description.Title}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
<div style={{ minHeight: "40vh" }}>
|
<div style={{ minHeight: "40vh" }}>
|
||||||
<MessageExporter />
|
<MessageExporter />
|
||||||
</div>
|
</div>
|
||||||
|
@ -149,7 +164,7 @@ export function MessageExporter() {
|
||||||
if (exportConfig.includeContext) {
|
if (exportConfig.includeContext) {
|
||||||
ret.push(...session.mask.context);
|
ret.push(...session.mask.context);
|
||||||
}
|
}
|
||||||
ret.push(...session.messages.filter((m, i) => selection.has(m.id)));
|
ret.push(...session.messages.filter((m) => selection.has(m.id)));
|
||||||
return ret;
|
return ret;
|
||||||
}, [
|
}, [
|
||||||
exportConfig.includeContext,
|
exportConfig.includeContext,
|
||||||
|
@ -260,7 +275,8 @@ export function RenderExport(props: {
|
||||||
});
|
});
|
||||||
|
|
||||||
props.onRender(renderMsgs);
|
props.onRender(renderMsgs);
|
||||||
});
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={domRef}>
|
<div ref={domRef}>
|
||||||
|
@ -437,13 +453,13 @@ export function ImagePreviewer(props: {
|
||||||
showToast(Locale.Export.Image.Toast);
|
showToast(Locale.Export.Image.Toast);
|
||||||
const dom = previewRef.current;
|
const dom = previewRef.current;
|
||||||
if (!dom) return;
|
if (!dom) return;
|
||||||
|
|
||||||
const isApp = getClientConfig()?.isApp;
|
const isApp = getClientConfig()?.isApp;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const blob = await toPng(dom);
|
const blob = await toPng(dom);
|
||||||
if (!blob) return;
|
if (!blob) return;
|
||||||
|
|
||||||
if (isMobile || (isApp && window.__TAURI__)) {
|
if (isMobile || (isApp && window.__TAURI__)) {
|
||||||
if (isApp && window.__TAURI__) {
|
if (isApp && window.__TAURI__) {
|
||||||
const result = await window.__TAURI__.dialog.save({
|
const result = await window.__TAURI__.dialog.save({
|
||||||
|
@ -459,7 +475,7 @@ export function ImagePreviewer(props: {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result !== null) {
|
if (result !== null) {
|
||||||
const response = await fetch(blob);
|
const response = await fetch(blob);
|
||||||
const buffer = await response.arrayBuffer();
|
const buffer = await response.arrayBuffer();
|
||||||
|
@ -604,8 +620,6 @@ export function MarkdownPreviewer(props: {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// modified by BackTrackZ now it's looks better
|
|
||||||
|
|
||||||
export function JsonPreviewer(props: {
|
export function JsonPreviewer(props: {
|
||||||
messages: ChatMessage[];
|
messages: ChatMessage[];
|
||||||
topic: string;
|
topic: string;
|
||||||
|
|
|
@ -5,13 +5,13 @@ import RemarkBreaks from "remark-breaks";
|
||||||
import RehypeKatex from "rehype-katex";
|
import RehypeKatex from "rehype-katex";
|
||||||
import RemarkGfm from "remark-gfm";
|
import RemarkGfm from "remark-gfm";
|
||||||
import RehypeHighlight from "rehype-highlight";
|
import RehypeHighlight from "rehype-highlight";
|
||||||
import { useRef, useState, RefObject, useEffect } from "react";
|
import { useRef, useState, RefObject, useEffect, useMemo } from "react";
|
||||||
import { copyToClipboard } from "../utils";
|
import { copyToClipboard } from "../utils";
|
||||||
import mermaid from "mermaid";
|
import mermaid from "mermaid";
|
||||||
|
|
||||||
import LoadingIcon from "../icons/three-dots.svg";
|
import LoadingIcon from "../icons/three-dots.svg";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useDebouncedCallback, useThrottledCallback } from "use-debounce";
|
import { useDebouncedCallback } from "use-debounce";
|
||||||
import { showImageModal } from "./ui-lib";
|
import { showImageModal } from "./ui-lib";
|
||||||
|
|
||||||
export function Mermaid(props: { code: string }) {
|
export function Mermaid(props: { code: string }) {
|
||||||
|
@ -99,7 +99,29 @@ export function PreCode(props: { children: any }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function escapeDollarNumber(text: string) {
|
||||||
|
let escapedText = "";
|
||||||
|
|
||||||
|
for (let i = 0; i < text.length; i += 1) {
|
||||||
|
let char = text[i];
|
||||||
|
const nextChar = text[i + 1] || " ";
|
||||||
|
|
||||||
|
if (char === "$" && nextChar >= "0" && nextChar <= "9") {
|
||||||
|
char = "\\$";
|
||||||
|
}
|
||||||
|
|
||||||
|
escapedText += char;
|
||||||
|
}
|
||||||
|
|
||||||
|
return escapedText;
|
||||||
|
}
|
||||||
|
|
||||||
function _MarkDownContent(props: { content: string }) {
|
function _MarkDownContent(props: { content: string }) {
|
||||||
|
const escapedContent = useMemo(
|
||||||
|
() => escapeDollarNumber(props.content),
|
||||||
|
[props.content],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactMarkdown
|
<ReactMarkdown
|
||||||
remarkPlugins={[RemarkMath, RemarkGfm, RemarkBreaks]}
|
remarkPlugins={[RemarkMath, RemarkGfm, RemarkBreaks]}
|
||||||
|
@ -124,7 +146,7 @@ function _MarkDownContent(props: { content: string }) {
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{props.content}
|
{escapedContent}
|
||||||
</ReactMarkdown>
|
</ReactMarkdown>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ import {
|
||||||
ChatMessage,
|
ChatMessage,
|
||||||
createMessage,
|
createMessage,
|
||||||
ModelConfig,
|
ModelConfig,
|
||||||
|
ModelType,
|
||||||
useAppConfig,
|
useAppConfig,
|
||||||
useChatStore,
|
useChatStore,
|
||||||
} from "../store";
|
} from "../store";
|
||||||
|
@ -58,11 +59,11 @@ function reorder<T>(list: T[], startIndex: number, endIndex: number): T[] {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function MaskAvatar(props: { mask: Mask }) {
|
export function MaskAvatar(props: { avatar: string; model?: ModelType }) {
|
||||||
return props.mask.avatar !== DEFAULT_MASK_AVATAR ? (
|
return props.avatar !== DEFAULT_MASK_AVATAR ? (
|
||||||
<Avatar avatar={props.mask.avatar} />
|
<Avatar avatar={props.avatar} />
|
||||||
) : (
|
) : (
|
||||||
<Avatar model={props.mask.modelConfig.model} />
|
<Avatar model={props.model} />
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,7 +124,10 @@ export function MaskConfig(props: {
|
||||||
onClick={() => setShowPicker(true)}
|
onClick={() => setShowPicker(true)}
|
||||||
style={{ cursor: "pointer" }}
|
style={{ cursor: "pointer" }}
|
||||||
>
|
>
|
||||||
<MaskAvatar mask={props.mask} />
|
<MaskAvatar
|
||||||
|
avatar={props.mask.avatar}
|
||||||
|
model={props.mask.modelConfig.model}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Popover>
|
</Popover>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
@ -398,7 +402,7 @@ export function MaskPage() {
|
||||||
setSearchText(text);
|
setSearchText(text);
|
||||||
if (text.length > 0) {
|
if (text.length > 0) {
|
||||||
const result = allMasks.filter((m) =>
|
const result = allMasks.filter((m) =>
|
||||||
m.name.toLowerCase().includes(text.toLowerCase())
|
m.name.toLowerCase().includes(text.toLowerCase()),
|
||||||
);
|
);
|
||||||
setSearchMasks(result);
|
setSearchMasks(result);
|
||||||
} else {
|
} else {
|
||||||
|
@ -523,7 +527,7 @@ export function MaskPage() {
|
||||||
<div className={styles["mask-item"]} key={m.id}>
|
<div className={styles["mask-item"]} key={m.id}>
|
||||||
<div className={styles["mask-header"]}>
|
<div className={styles["mask-header"]}>
|
||||||
<div className={styles["mask-icon"]}>
|
<div className={styles["mask-icon"]}>
|
||||||
<MaskAvatar mask={m} />
|
<MaskAvatar avatar={m.avatar} model={m.modelConfig.model} />
|
||||||
</div>
|
</div>
|
||||||
<div className={styles["mask-title"]}>
|
<div className={styles["mask-title"]}>
|
||||||
<div className={styles["mask-name"]}>{m.name}</div>
|
<div className={styles["mask-name"]}>{m.name}</div>
|
||||||
|
|
|
@ -58,8 +58,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.body {
|
.body {
|
||||||
flex-grow: 1;
|
flex: 1;
|
||||||
max-width: calc(100% - 40px);
|
max-width: calc(100% - 80px);
|
||||||
|
|
||||||
.date {
|
.date {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
@ -71,6 +71,12 @@
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.checkbox {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { ChatMessage, useAppConfig, useChatStore } from "../store";
|
import { ChatMessage, useAppConfig, useChatStore } from "../store";
|
||||||
import { Updater } from "../typing";
|
import { Updater } from "../typing";
|
||||||
import { IconButton } from "./button";
|
import { IconButton } from "./button";
|
||||||
|
@ -73,11 +73,23 @@ export function MessageSelector(props: {
|
||||||
const chatStore = useChatStore();
|
const chatStore = useChatStore();
|
||||||
const session = chatStore.currentSession();
|
const session = chatStore.currentSession();
|
||||||
const isValid = (m: ChatMessage) => m.content && !m.isError && !m.streaming;
|
const isValid = (m: ChatMessage) => m.content && !m.isError && !m.streaming;
|
||||||
const messages = session.messages.filter(
|
const allMessages = useMemo(() => {
|
||||||
(m, i) =>
|
let startIndex = Math.max(0, session.clearContextIndex ?? 0);
|
||||||
m.id && // message must have id
|
if (startIndex === session.messages.length - 1) {
|
||||||
isValid(m) &&
|
startIndex = 0;
|
||||||
(i >= session.messages.length - 1 || isValid(session.messages[i + 1])),
|
}
|
||||||
|
return session.messages.slice(startIndex);
|
||||||
|
}, [session.messages, session.clearContextIndex]);
|
||||||
|
|
||||||
|
const messages = useMemo(
|
||||||
|
() =>
|
||||||
|
allMessages.filter(
|
||||||
|
(m, i) =>
|
||||||
|
m.id && // message must have id
|
||||||
|
isValid(m) &&
|
||||||
|
(i >= allMessages.length - 1 || isValid(allMessages[i + 1])),
|
||||||
|
),
|
||||||
|
[allMessages],
|
||||||
);
|
);
|
||||||
const messageCount = messages.length;
|
const messageCount = messages.length;
|
||||||
const config = useAppConfig();
|
const config = useAppConfig();
|
||||||
|
@ -176,6 +188,8 @@ export function MessageSelector(props: {
|
||||||
<div className={styles["messages"]}>
|
<div className={styles["messages"]}>
|
||||||
{messages.map((m, i) => {
|
{messages.map((m, i) => {
|
||||||
if (!isInSearchResult(m.id!)) return null;
|
if (!isInSearchResult(m.id!)) return null;
|
||||||
|
const id = m.id ?? i;
|
||||||
|
const isSelected = props.selection.has(id);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
@ -185,7 +199,6 @@ export function MessageSelector(props: {
|
||||||
key={i}
|
key={i}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
props.updateSelection((selection) => {
|
props.updateSelection((selection) => {
|
||||||
const id = m.id ?? i;
|
|
||||||
selection.has(id) ? selection.delete(id) : selection.add(id);
|
selection.has(id) ? selection.delete(id) : selection.add(id);
|
||||||
});
|
});
|
||||||
onClickIndex(i);
|
onClickIndex(i);
|
||||||
|
@ -195,7 +208,10 @@ export function MessageSelector(props: {
|
||||||
{m.role === "user" ? (
|
{m.role === "user" ? (
|
||||||
<Avatar avatar={config.avatar}></Avatar>
|
<Avatar avatar={config.avatar}></Avatar>
|
||||||
) : (
|
) : (
|
||||||
<MaskAvatar mask={session.mask} />
|
<MaskAvatar
|
||||||
|
avatar={session.mask.avatar}
|
||||||
|
model={m.model || session.mask.modelConfig.model}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className={styles["body"]}>
|
<div className={styles["body"]}>
|
||||||
|
@ -206,6 +222,10 @@ export function MessageSelector(props: {
|
||||||
{m.content}
|
{m.content}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className={styles["checkbox"]}>
|
||||||
|
<input type="checkbox" checked={isSelected}></input>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
|
@ -25,11 +25,13 @@ export function ModelConfigList(props: {
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{allModels.map((v, i) => (
|
{allModels
|
||||||
<option value={v.name} key={i} disabled={!v.available}>
|
.filter((v) => v.available)
|
||||||
{v.name}
|
.map((v, i) => (
|
||||||
</option>
|
<option value={v.name} key={i}>
|
||||||
))}
|
{v.displayName}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<ListItem
|
<ListItem
|
||||||
|
|
|
@ -17,21 +17,13 @@ import { useCommand } from "../command";
|
||||||
import { showConfirm } from "./ui-lib";
|
import { showConfirm } from "./ui-lib";
|
||||||
import { BUILTIN_MASK_STORE } from "../masks";
|
import { BUILTIN_MASK_STORE } from "../masks";
|
||||||
|
|
||||||
function getIntersectionArea(aRect: DOMRect, bRect: DOMRect) {
|
|
||||||
const xmin = Math.max(aRect.x, bRect.x);
|
|
||||||
const xmax = Math.min(aRect.x + aRect.width, bRect.x + bRect.width);
|
|
||||||
const ymin = Math.max(aRect.y, bRect.y);
|
|
||||||
const ymax = Math.min(aRect.y + aRect.height, bRect.y + bRect.height);
|
|
||||||
const width = xmax - xmin;
|
|
||||||
const height = ymax - ymin;
|
|
||||||
const intersectionArea = width < 0 || height < 0 ? 0 : width * height;
|
|
||||||
return intersectionArea;
|
|
||||||
}
|
|
||||||
|
|
||||||
function MaskItem(props: { mask: Mask; onClick?: () => void }) {
|
function MaskItem(props: { mask: Mask; onClick?: () => void }) {
|
||||||
return (
|
return (
|
||||||
<div className={styles["mask"]} onClick={props.onClick}>
|
<div className={styles["mask"]} onClick={props.onClick}>
|
||||||
<MaskAvatar mask={props.mask} />
|
<MaskAvatar
|
||||||
|
avatar={props.mask.avatar}
|
||||||
|
model={props.mask.modelConfig.model}
|
||||||
|
/>
|
||||||
<div className={styles["mask-name"] + " one-line"}>{props.mask.name}</div>
|
<div className={styles["mask-name"] + " one-line"}>{props.mask.name}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -51,10 +51,13 @@ import Locale, {
|
||||||
import { copyToClipboard } from "../utils";
|
import { copyToClipboard } from "../utils";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import {
|
import {
|
||||||
|
Azure,
|
||||||
OPENAI_BASE_URL,
|
OPENAI_BASE_URL,
|
||||||
Path,
|
Path,
|
||||||
RELEASE_URL,
|
RELEASE_URL,
|
||||||
STORAGE_KEY,
|
STORAGE_KEY,
|
||||||
|
ServiceProvider,
|
||||||
|
SlotID,
|
||||||
UPDATE_URL,
|
UPDATE_URL,
|
||||||
} from "../constant";
|
} from "../constant";
|
||||||
import { Prompt, SearchService, usePromptStore } from "../store/prompt";
|
import { Prompt, SearchService, usePromptStore } from "../store/prompt";
|
||||||
|
@ -581,8 +584,16 @@ export function Settings() {
|
||||||
const accessStore = useAccessStore();
|
const accessStore = useAccessStore();
|
||||||
const shouldHideBalanceQuery = useMemo(() => {
|
const shouldHideBalanceQuery = useMemo(() => {
|
||||||
const isOpenAiUrl = accessStore.openaiUrl.includes(OPENAI_BASE_URL);
|
const isOpenAiUrl = accessStore.openaiUrl.includes(OPENAI_BASE_URL);
|
||||||
return accessStore.hideBalanceQuery || isOpenAiUrl;
|
return (
|
||||||
}, [accessStore.hideBalanceQuery, accessStore.openaiUrl]);
|
accessStore.hideBalanceQuery ||
|
||||||
|
isOpenAiUrl ||
|
||||||
|
accessStore.provider === ServiceProvider.Azure
|
||||||
|
);
|
||||||
|
}, [
|
||||||
|
accessStore.hideBalanceQuery,
|
||||||
|
accessStore.openaiUrl,
|
||||||
|
accessStore.provider,
|
||||||
|
]);
|
||||||
|
|
||||||
const usage = {
|
const usage = {
|
||||||
used: updateStore.used,
|
used: updateStore.used,
|
||||||
|
@ -878,16 +889,16 @@ export function Settings() {
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</List>
|
</List>
|
||||||
|
|
||||||
<List>
|
<List id={SlotID.CustomModel}>
|
||||||
{showAccessCode ? (
|
{showAccessCode && (
|
||||||
<ListItem
|
<ListItem
|
||||||
title={Locale.Settings.AccessCode.Title}
|
title={Locale.Settings.Access.AccessCode.Title}
|
||||||
subTitle={Locale.Settings.AccessCode.SubTitle}
|
subTitle={Locale.Settings.Access.AccessCode.SubTitle}
|
||||||
>
|
>
|
||||||
<PasswordInput
|
<PasswordInput
|
||||||
value={accessStore.accessCode}
|
value={accessStore.accessCode}
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={Locale.Settings.AccessCode.Placeholder}
|
placeholder={Locale.Settings.Access.AccessCode.Placeholder}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
accessStore.update(
|
accessStore.update(
|
||||||
(access) => (access.accessCode = e.currentTarget.value),
|
(access) => (access.accessCode = e.currentTarget.value),
|
||||||
|
@ -895,46 +906,154 @@ export function Settings() {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
) : (
|
|
||||||
<></>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!accessStore.hideUserApiKey ? (
|
{!accessStore.hideUserApiKey && (
|
||||||
<>
|
<>
|
||||||
<ListItem
|
<ListItem
|
||||||
title={Locale.Settings.Endpoint.Title}
|
title={Locale.Settings.Access.CustomEndpoint.Title}
|
||||||
subTitle={Locale.Settings.Endpoint.SubTitle}
|
subTitle={Locale.Settings.Access.CustomEndpoint.SubTitle}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="checkbox"
|
||||||
value={accessStore.openaiUrl}
|
checked={accessStore.useCustomConfig}
|
||||||
placeholder="https://api.openai.com/"
|
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
accessStore.update(
|
accessStore.update(
|
||||||
(access) => (access.openaiUrl = e.currentTarget.value),
|
(access) =>
|
||||||
|
(access.useCustomConfig = e.currentTarget.checked),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
></input>
|
></input>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<ListItem
|
{accessStore.useCustomConfig && (
|
||||||
title={Locale.Settings.Token.Title}
|
<>
|
||||||
subTitle={Locale.Settings.Token.SubTitle}
|
<ListItem
|
||||||
>
|
title={Locale.Settings.Access.Provider.Title}
|
||||||
<PasswordInput
|
subTitle={Locale.Settings.Access.Provider.SubTitle}
|
||||||
value={accessStore.token}
|
>
|
||||||
type="text"
|
<Select
|
||||||
placeholder={Locale.Settings.Token.Placeholder}
|
value={accessStore.provider}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
accessStore.update(
|
accessStore.update(
|
||||||
(access) => (access.token = e.currentTarget.value),
|
(access) =>
|
||||||
);
|
(access.provider = e.target
|
||||||
}}
|
.value as ServiceProvider),
|
||||||
/>
|
);
|
||||||
</ListItem>
|
}}
|
||||||
</>
|
>
|
||||||
) : null}
|
{Object.entries(ServiceProvider).map(([k, v]) => (
|
||||||
|
<option value={v} key={k}>
|
||||||
|
{k}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</ListItem>
|
||||||
|
|
||||||
{!shouldHideBalanceQuery ? (
|
{accessStore.provider === "OpenAI" ? (
|
||||||
|
<>
|
||||||
|
<ListItem
|
||||||
|
title={Locale.Settings.Access.OpenAI.Endpoint.Title}
|
||||||
|
subTitle={
|
||||||
|
Locale.Settings.Access.OpenAI.Endpoint.SubTitle
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={accessStore.openaiUrl}
|
||||||
|
placeholder={OPENAI_BASE_URL}
|
||||||
|
onChange={(e) =>
|
||||||
|
accessStore.update(
|
||||||
|
(access) =>
|
||||||
|
(access.openaiUrl = e.currentTarget.value),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
></input>
|
||||||
|
</ListItem>
|
||||||
|
<ListItem
|
||||||
|
title={Locale.Settings.Access.OpenAI.ApiKey.Title}
|
||||||
|
subTitle={Locale.Settings.Access.OpenAI.ApiKey.SubTitle}
|
||||||
|
>
|
||||||
|
<PasswordInput
|
||||||
|
value={accessStore.openaiApiKey}
|
||||||
|
type="text"
|
||||||
|
placeholder={
|
||||||
|
Locale.Settings.Access.OpenAI.ApiKey.Placeholder
|
||||||
|
}
|
||||||
|
onChange={(e) => {
|
||||||
|
accessStore.update(
|
||||||
|
(access) =>
|
||||||
|
(access.openaiApiKey = e.currentTarget.value),
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<ListItem
|
||||||
|
title={Locale.Settings.Access.Azure.Endpoint.Title}
|
||||||
|
subTitle={
|
||||||
|
Locale.Settings.Access.Azure.Endpoint.SubTitle +
|
||||||
|
Azure.ExampleEndpoint
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={accessStore.azureUrl}
|
||||||
|
placeholder={Azure.ExampleEndpoint}
|
||||||
|
onChange={(e) =>
|
||||||
|
accessStore.update(
|
||||||
|
(access) =>
|
||||||
|
(access.azureUrl = e.currentTarget.value),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
></input>
|
||||||
|
</ListItem>
|
||||||
|
<ListItem
|
||||||
|
title={Locale.Settings.Access.Azure.ApiKey.Title}
|
||||||
|
subTitle={Locale.Settings.Access.Azure.ApiKey.SubTitle}
|
||||||
|
>
|
||||||
|
<PasswordInput
|
||||||
|
value={accessStore.azureApiKey}
|
||||||
|
type="text"
|
||||||
|
placeholder={
|
||||||
|
Locale.Settings.Access.Azure.ApiKey.Placeholder
|
||||||
|
}
|
||||||
|
onChange={(e) => {
|
||||||
|
accessStore.update(
|
||||||
|
(access) =>
|
||||||
|
(access.azureApiKey = e.currentTarget.value),
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
<ListItem
|
||||||
|
title={Locale.Settings.Access.Azure.ApiVerion.Title}
|
||||||
|
subTitle={
|
||||||
|
Locale.Settings.Access.Azure.ApiVerion.SubTitle
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={accessStore.azureApiVersion}
|
||||||
|
placeholder="2023-08-01-preview"
|
||||||
|
onChange={(e) =>
|
||||||
|
accessStore.update(
|
||||||
|
(access) =>
|
||||||
|
(access.azureApiVersion =
|
||||||
|
e.currentTarget.value),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
></input>
|
||||||
|
</ListItem>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!shouldHideBalanceQuery && !clientConfig?.isApp ? (
|
||||||
<ListItem
|
<ListItem
|
||||||
title={Locale.Settings.Usage.Title}
|
title={Locale.Settings.Usage.Title}
|
||||||
subTitle={
|
subTitle={
|
||||||
|
@ -961,8 +1080,8 @@ export function Settings() {
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<ListItem
|
<ListItem
|
||||||
title={Locale.Settings.CustomModel.Title}
|
title={Locale.Settings.Access.CustomModel.Title}
|
||||||
subTitle={Locale.Settings.CustomModel.SubTitle}
|
subTitle={Locale.Settings.Access.CustomModel.SubTitle}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
|
|
@ -235,7 +235,7 @@
|
||||||
.select-with-icon-select {
|
.select-with-icon-select {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border: var(--border-in-light);
|
border: var(--border-in-light);
|
||||||
padding: 10px 25px 10px 10px;
|
padding: 10px 35px 10px 10px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
appearance: none;
|
appearance: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
|
@ -70,14 +70,12 @@ export function ListItem(props: {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function List(props: {
|
export function List(props: { children: React.ReactNode; id?: string }) {
|
||||||
children:
|
return (
|
||||||
| Array<JSX.Element | null | undefined>
|
<div className={styles.list} id={props.id}>
|
||||||
| JSX.Element
|
{props.children}
|
||||||
| null
|
</div>
|
||||||
| undefined;
|
);
|
||||||
}) {
|
|
||||||
return <div className={styles.list}>{props.children}</div>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Loading() {
|
export function Loading() {
|
||||||
|
@ -99,8 +97,9 @@ export function Loading() {
|
||||||
interface ModalProps {
|
interface ModalProps {
|
||||||
title: string;
|
title: string;
|
||||||
children?: any;
|
children?: any;
|
||||||
actions?: JSX.Element[];
|
actions?: React.ReactNode[];
|
||||||
defaultMax?: boolean;
|
defaultMax?: boolean;
|
||||||
|
footer?: React.ReactNode;
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
}
|
}
|
||||||
export function Modal(props: ModalProps) {
|
export function Modal(props: ModalProps) {
|
||||||
|
@ -149,6 +148,7 @@ export function Modal(props: ModalProps) {
|
||||||
<div className={styles["modal-content"]}>{props.children}</div>
|
<div className={styles["modal-content"]}>{props.children}</div>
|
||||||
|
|
||||||
<div className={styles["modal-footer"]}>
|
<div className={styles["modal-footer"]}>
|
||||||
|
{props.footer}
|
||||||
<div className={styles["modal-actions"]}>
|
<div className={styles["modal-actions"]}>
|
||||||
{props.actions?.map((action, i) => (
|
{props.actions?.map((action, i) => (
|
||||||
<div key={i} className={styles["modal-action"]}>
|
<div key={i} className={styles["modal-action"]}>
|
||||||
|
|
|
@ -4,19 +4,28 @@ import { DEFAULT_MODELS } from "../constant";
|
||||||
declare global {
|
declare global {
|
||||||
namespace NodeJS {
|
namespace NodeJS {
|
||||||
interface ProcessEnv {
|
interface ProcessEnv {
|
||||||
|
PROXY_URL?: string; // docker only
|
||||||
|
|
||||||
OPENAI_API_KEY?: string;
|
OPENAI_API_KEY?: string;
|
||||||
CODE?: string;
|
CODE?: string;
|
||||||
|
|
||||||
BASE_URL?: string;
|
BASE_URL?: string;
|
||||||
PROXY_URL?: string;
|
OPENAI_ORG_ID?: string; // openai only
|
||||||
OPENAI_ORG_ID?: string;
|
|
||||||
VERCEL?: string;
|
VERCEL?: string;
|
||||||
HIDE_USER_API_KEY?: string; // disable user's api key input
|
|
||||||
DISABLE_GPT4?: string; // allow user to use gpt-4 or not
|
|
||||||
BUILD_MODE?: "standalone" | "export";
|
BUILD_MODE?: "standalone" | "export";
|
||||||
BUILD_APP?: string; // is building desktop app
|
BUILD_APP?: string; // is building desktop app
|
||||||
|
|
||||||
|
HIDE_USER_API_KEY?: string; // disable user's api key input
|
||||||
|
DISABLE_GPT4?: string; // allow user to use gpt-4 or not
|
||||||
ENABLE_BALANCE_QUERY?: string; // allow user to query balance or not
|
ENABLE_BALANCE_QUERY?: string; // allow user to query balance or not
|
||||||
DISABLE_FAST_LINK?: string; // disallow parse settings from url or not
|
DISABLE_FAST_LINK?: string; // disallow parse settings from url or not
|
||||||
CUSTOM_MODELS?: string; // to control custom models
|
CUSTOM_MODELS?: string; // to control custom models
|
||||||
|
|
||||||
|
// azure only
|
||||||
|
AZURE_URL?: string; // https://{azure-url}/openai/deployments/{deploy-name}
|
||||||
|
AZURE_API_KEY?: string;
|
||||||
|
AZURE_API_VERSION?: string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,7 +50,7 @@ export const getServerSideConfig = () => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let disableGPT4 = !!process.env.DISABLE_GPT4;
|
const disableGPT4 = !!process.env.DISABLE_GPT4;
|
||||||
let customModels = process.env.CUSTOM_MODELS ?? "";
|
let customModels = process.env.CUSTOM_MODELS ?? "";
|
||||||
|
|
||||||
if (disableGPT4) {
|
if (disableGPT4) {
|
||||||
|
@ -51,15 +60,25 @@ export const getServerSideConfig = () => {
|
||||||
.join(",");
|
.join(",");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isAzure = !!process.env.AZURE_URL;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
baseUrl: process.env.BASE_URL,
|
||||||
apiKey: process.env.OPENAI_API_KEY,
|
apiKey: process.env.OPENAI_API_KEY,
|
||||||
|
openaiOrgId: process.env.OPENAI_ORG_ID,
|
||||||
|
|
||||||
|
isAzure,
|
||||||
|
azureUrl: process.env.AZURE_URL,
|
||||||
|
azureApiKey: process.env.AZURE_API_KEY,
|
||||||
|
azureApiVersion: process.env.AZURE_API_VERSION,
|
||||||
|
|
||||||
|
needCode: ACCESS_CODES.size > 0,
|
||||||
code: process.env.CODE,
|
code: process.env.CODE,
|
||||||
codes: ACCESS_CODES,
|
codes: ACCESS_CODES,
|
||||||
needCode: ACCESS_CODES.size > 0,
|
|
||||||
baseUrl: process.env.BASE_URL,
|
|
||||||
proxyUrl: process.env.PROXY_URL,
|
proxyUrl: process.env.PROXY_URL,
|
||||||
openaiOrgId: process.env.OPENAI_ORG_ID,
|
|
||||||
isVercel: !!process.env.VERCEL,
|
isVercel: !!process.env.VERCEL,
|
||||||
|
|
||||||
hideUserApiKey: !!process.env.HIDE_USER_API_KEY,
|
hideUserApiKey: !!process.env.HIDE_USER_API_KEY,
|
||||||
disableGPT4,
|
disableGPT4,
|
||||||
hideBalanceQuery: !process.env.ENABLE_BALANCE_QUERY,
|
hideBalanceQuery: !process.env.ENABLE_BALANCE_QUERY,
|
||||||
|
|
|
@ -8,7 +8,7 @@ export const FETCH_COMMIT_URL = `https://api.github.com/repos/${OWNER}/${REPO}/c
|
||||||
export const FETCH_TAG_URL = `https://api.github.com/repos/${OWNER}/${REPO}/tags?per_page=1`;
|
export const FETCH_TAG_URL = `https://api.github.com/repos/${OWNER}/${REPO}/tags?per_page=1`;
|
||||||
export const RUNTIME_CONFIG_DOM = "danger-runtime-config";
|
export const RUNTIME_CONFIG_DOM = "danger-runtime-config";
|
||||||
|
|
||||||
export const DEFAULT_CORS_HOST = "https://ab.nextweb.fun";
|
export const DEFAULT_CORS_HOST = "https://a.nextweb.fun";
|
||||||
export const DEFAULT_API_HOST = `${DEFAULT_CORS_HOST}/api/proxy`;
|
export const DEFAULT_API_HOST = `${DEFAULT_CORS_HOST}/api/proxy`;
|
||||||
export const OPENAI_BASE_URL = "https://api.openai.com";
|
export const OPENAI_BASE_URL = "https://api.openai.com";
|
||||||
|
|
||||||
|
@ -24,10 +24,12 @@ export enum Path {
|
||||||
|
|
||||||
export enum ApiPath {
|
export enum ApiPath {
|
||||||
Cors = "/api/cors",
|
Cors = "/api/cors",
|
||||||
|
OpenAI = "/api/openai",
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum SlotID {
|
export enum SlotID {
|
||||||
AppBody = "app-body",
|
AppBody = "app-body",
|
||||||
|
CustomModel = "custom-model",
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum FileName {
|
export enum FileName {
|
||||||
|
@ -63,6 +65,11 @@ export const REQUEST_TIMEOUT_MS = 60000;
|
||||||
|
|
||||||
export const EXPORT_MESSAGE_CLASS_NAME = "export-markdown";
|
export const EXPORT_MESSAGE_CLASS_NAME = "export-markdown";
|
||||||
|
|
||||||
|
export enum ServiceProvider {
|
||||||
|
OpenAI = "OpenAI",
|
||||||
|
Azure = "Azure",
|
||||||
|
}
|
||||||
|
|
||||||
export const OpenaiPath = {
|
export const OpenaiPath = {
|
||||||
ChatPath: "v1/chat/completions",
|
ChatPath: "v1/chat/completions",
|
||||||
UsagePath: "dashboard/billing/usage",
|
UsagePath: "dashboard/billing/usage",
|
||||||
|
@ -70,12 +77,18 @@ export const OpenaiPath = {
|
||||||
ListModelPath: "v1/models",
|
ListModelPath: "v1/models",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const Azure = {
|
||||||
|
ExampleEndpoint: "https://{resource-url}/openai/deployments/{deploy-id}",
|
||||||
|
};
|
||||||
|
|
||||||
export const DEFAULT_INPUT_TEMPLATE = `{{input}}`; // input / time / model / lang
|
export const DEFAULT_INPUT_TEMPLATE = `{{input}}`; // input / time / model / lang
|
||||||
export const DEFAULT_SYSTEM_TEMPLATE = `
|
export const DEFAULT_SYSTEM_TEMPLATE = `
|
||||||
You are ChatGPT, a large language model trained by OpenAI.
|
You are ChatGPT, a large language model trained by OpenAI.
|
||||||
Knowledge cutoff: {{cutoff}}
|
Knowledge cutoff: {{cutoff}}
|
||||||
Current model: {{model}}
|
Current model: {{model}}
|
||||||
Current time: {{time}}
|
Current time: {{time}}
|
||||||
|
Latex inline: $x^2$
|
||||||
|
Latex block: $$e=mc^2$$
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const SUMMARIZE_MODEL = "gpt-3.5-turbo";
|
export const SUMMARIZE_MODEL = "gpt-3.5-turbo";
|
||||||
|
|
|
@ -167,11 +167,7 @@ ${builtin} مدمجة، ${custom} تم تعريفها من قبل المستخد
|
||||||
Title: "حد الضغط للتاريخ",
|
Title: "حد الضغط للتاريخ",
|
||||||
SubTitle: "سيتم الضغط إذا تجاوزت طول الرسائل غير المضغوطة الحد المحدد",
|
SubTitle: "سيتم الضغط إذا تجاوزت طول الرسائل غير المضغوطة الحد المحدد",
|
||||||
},
|
},
|
||||||
Token: {
|
|
||||||
Title: "مفتاح API",
|
|
||||||
SubTitle: "استخدم مفتاحك لتجاوز حد رمز الوصول",
|
|
||||||
Placeholder: "مفتاح OpenAI API",
|
|
||||||
},
|
|
||||||
Usage: {
|
Usage: {
|
||||||
Title: "رصيد الحساب",
|
Title: "رصيد الحساب",
|
||||||
SubTitle(used: any, total: any) {
|
SubTitle(used: any, total: any) {
|
||||||
|
@ -181,15 +177,7 @@ ${builtin} مدمجة، ${custom} تم تعريفها من قبل المستخد
|
||||||
Check: "التحقق",
|
Check: "التحقق",
|
||||||
NoAccess: "أدخل مفتاح API للتحقق من الرصيد",
|
NoAccess: "أدخل مفتاح API للتحقق من الرصيد",
|
||||||
},
|
},
|
||||||
AccessCode: {
|
|
||||||
Title: "رمز الوصول",
|
|
||||||
SubTitle: "تم تمكين التحكم في الوصول",
|
|
||||||
Placeholder: "رمز الوصول المطلوب",
|
|
||||||
},
|
|
||||||
Endpoint: {
|
|
||||||
Title: "نقطة النهاية",
|
|
||||||
SubTitle: "يجب أن تبدأ نقطة النهاية المخصصة بـ http(s)://",
|
|
||||||
},
|
|
||||||
Model: "النموذج",
|
Model: "النموذج",
|
||||||
Temperature: {
|
Temperature: {
|
||||||
Title: "الحرارة",
|
Title: "الحرارة",
|
||||||
|
|
|
@ -199,11 +199,7 @@ const bn: PartialLocaleType = {
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"নকুল বার্তা দৈর্ঘ্য সীমা অতিক্রান্ত হলে ঐ বার্তাটি সঙ্কুচিত হবে",
|
"নকুল বার্তা দৈর্ঘ্য সীমা অতিক্রান্ত হলে ঐ বার্তাটি সঙ্কুচিত হবে",
|
||||||
},
|
},
|
||||||
Token: {
|
|
||||||
Title: "অ্যাপি কী",
|
|
||||||
SubTitle: "অ্যাক্সেস কোড সীমা উপেক্ষা করতে আপনার কীটি ব্যবহার করুন",
|
|
||||||
Placeholder: "OpenAI API কী",
|
|
||||||
},
|
|
||||||
Usage: {
|
Usage: {
|
||||||
Title: "একাউন্ট ব্যালেন্স",
|
Title: "একাউন্ট ব্যালেন্স",
|
||||||
SubTitle(used: any, total: any) {
|
SubTitle(used: any, total: any) {
|
||||||
|
@ -213,15 +209,7 @@ const bn: PartialLocaleType = {
|
||||||
Check: "চেক",
|
Check: "চেক",
|
||||||
NoAccess: "ব্যালেন্স চেক করতে অ্যাপি কী ইনপুট করুন",
|
NoAccess: "ব্যালেন্স চেক করতে অ্যাপি কী ইনপুট করুন",
|
||||||
},
|
},
|
||||||
AccessCode: {
|
|
||||||
Title: "অ্যাক্সেস কোড",
|
|
||||||
SubTitle: "অ্যাক্সেস নিয়ন্ত্রণ সক্রিয়",
|
|
||||||
Placeholder: "অ্যাক্সেস কোড প্রয়োজন",
|
|
||||||
},
|
|
||||||
Endpoint: {
|
|
||||||
Title: "ইনটারপয়েন্ট",
|
|
||||||
SubTitle: "কাস্টম এন্ডপয়েন্টটি হতে হবে http(s):// দিয়ে শুরু হতে হবে",
|
|
||||||
},
|
|
||||||
Model: "মডেল",
|
Model: "মডেল",
|
||||||
Temperature: {
|
Temperature: {
|
||||||
Title: "তাপমাত্রা",
|
Title: "তাপমাত্রা",
|
||||||
|
|
|
@ -87,8 +87,8 @@ const cn = {
|
||||||
Copy: "全部复制",
|
Copy: "全部复制",
|
||||||
Download: "下载文件",
|
Download: "下载文件",
|
||||||
Share: "分享到 ShareGPT",
|
Share: "分享到 ShareGPT",
|
||||||
MessageFromYou: "来自你的消息",
|
MessageFromYou: "用户",
|
||||||
MessageFromChatGPT: "来自 ChatGPT 的消息",
|
MessageFromChatGPT: "ChatGPT",
|
||||||
Format: {
|
Format: {
|
||||||
Title: "导出格式",
|
Title: "导出格式",
|
||||||
SubTitle: "可以导出 Markdown 文本或者 PNG 图片",
|
SubTitle: "可以导出 Markdown 文本或者 PNG 图片",
|
||||||
|
@ -260,11 +260,6 @@ const cn = {
|
||||||
Title: "历史消息长度压缩阈值",
|
Title: "历史消息长度压缩阈值",
|
||||||
SubTitle: "当未压缩的历史消息超过该值时,将进行压缩",
|
SubTitle: "当未压缩的历史消息超过该值时,将进行压缩",
|
||||||
},
|
},
|
||||||
Token: {
|
|
||||||
Title: "API Key",
|
|
||||||
SubTitle: "使用自己的 Key 可绕过密码访问限制",
|
|
||||||
Placeholder: "OpenAI API Key",
|
|
||||||
},
|
|
||||||
|
|
||||||
Usage: {
|
Usage: {
|
||||||
Title: "余额查询",
|
Title: "余额查询",
|
||||||
|
@ -275,19 +270,56 @@ const cn = {
|
||||||
Check: "重新检查",
|
Check: "重新检查",
|
||||||
NoAccess: "输入 API Key 或访问密码查看余额",
|
NoAccess: "输入 API Key 或访问密码查看余额",
|
||||||
},
|
},
|
||||||
AccessCode: {
|
|
||||||
Title: "访问密码",
|
Access: {
|
||||||
SubTitle: "管理员已开启加密访问",
|
AccessCode: {
|
||||||
Placeholder: "请输入访问密码",
|
Title: "访问密码",
|
||||||
},
|
SubTitle: "管理员已开启加密访问",
|
||||||
Endpoint: {
|
Placeholder: "请输入访问密码",
|
||||||
Title: "接口地址",
|
},
|
||||||
SubTitle: "除默认地址外,必须包含 http(s)://",
|
CustomEndpoint: {
|
||||||
},
|
Title: "自定义接口",
|
||||||
CustomModel: {
|
SubTitle: "是否使用自定义 Azure 或 OpenAI 服务",
|
||||||
Title: "自定义模型名",
|
},
|
||||||
SubTitle: "增加自定义模型可选项,使用英文逗号隔开",
|
Provider: {
|
||||||
|
Title: "模型服务商",
|
||||||
|
SubTitle: "切换不同的服务商",
|
||||||
|
},
|
||||||
|
OpenAI: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "API Key",
|
||||||
|
SubTitle: "使用自定义 OpenAI Key 绕过密码访问限制",
|
||||||
|
Placeholder: "OpenAI API Key",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "接口地址",
|
||||||
|
SubTitle: "除默认地址外,必须包含 http(s)://",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Azure: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "接口密钥",
|
||||||
|
SubTitle: "使用自定义 Azure Key 绕过密码访问限制",
|
||||||
|
Placeholder: "Azure API Key",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "接口地址",
|
||||||
|
SubTitle: "样例:",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVerion: {
|
||||||
|
Title: "接口版本 (azure api version)",
|
||||||
|
SubTitle: "选择指定的部分版本",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CustomModel: {
|
||||||
|
Title: "自定义模型名",
|
||||||
|
SubTitle: "增加自定义模型可选项,使用英文逗号隔开",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
Model: "模型 (model)",
|
Model: "模型 (model)",
|
||||||
Temperature: {
|
Temperature: {
|
||||||
Title: "随机性 (temperature)",
|
Title: "随机性 (temperature)",
|
||||||
|
@ -443,6 +475,9 @@ const cn = {
|
||||||
Config: "配置",
|
Config: "配置",
|
||||||
},
|
},
|
||||||
Exporter: {
|
Exporter: {
|
||||||
|
Description: {
|
||||||
|
Title: "只有清除上下文之后的消息会被展示",
|
||||||
|
},
|
||||||
Model: "模型",
|
Model: "模型",
|
||||||
Messages: "消息",
|
Messages: "消息",
|
||||||
Topic: "主题",
|
Topic: "主题",
|
||||||
|
|
|
@ -124,11 +124,7 @@ const cs: PartialLocaleType = {
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Komprese proběhne, pokud délka nekomprimovaných zpráv přesáhne tuto hodnotu",
|
"Komprese proběhne, pokud délka nekomprimovaných zpráv přesáhne tuto hodnotu",
|
||||||
},
|
},
|
||||||
Token: {
|
|
||||||
Title: "API klíč",
|
|
||||||
SubTitle: "Použitím klíče ignorujete omezení přístupového kódu",
|
|
||||||
Placeholder: "Klíč API OpenAI",
|
|
||||||
},
|
|
||||||
Usage: {
|
Usage: {
|
||||||
Title: "Stav účtu",
|
Title: "Stav účtu",
|
||||||
SubTitle(used: any, total: any) {
|
SubTitle(used: any, total: any) {
|
||||||
|
@ -138,11 +134,7 @@ const cs: PartialLocaleType = {
|
||||||
Check: "Zkontrolovat",
|
Check: "Zkontrolovat",
|
||||||
NoAccess: "Pro kontrolu zůstatku zadejte klíč API",
|
NoAccess: "Pro kontrolu zůstatku zadejte klíč API",
|
||||||
},
|
},
|
||||||
AccessCode: {
|
|
||||||
Title: "Přístupový kód",
|
|
||||||
SubTitle: "Kontrola přístupu povolena",
|
|
||||||
Placeholder: "Potřebujete přístupový kód",
|
|
||||||
},
|
|
||||||
Model: "Model",
|
Model: "Model",
|
||||||
Temperature: {
|
Temperature: {
|
||||||
Title: "Teplota",
|
Title: "Teplota",
|
||||||
|
|
|
@ -124,12 +124,7 @@ const de: PartialLocaleType = {
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Komprimierung, wenn die Länge der unkomprimierten Nachrichten den Wert überschreitet",
|
"Komprimierung, wenn die Länge der unkomprimierten Nachrichten den Wert überschreitet",
|
||||||
},
|
},
|
||||||
Token: {
|
|
||||||
Title: "API-Schlüssel",
|
|
||||||
SubTitle:
|
|
||||||
"Verwenden Sie Ihren Schlüssel, um das Zugangscode-Limit zu ignorieren",
|
|
||||||
Placeholder: "OpenAI API-Schlüssel",
|
|
||||||
},
|
|
||||||
Usage: {
|
Usage: {
|
||||||
Title: "Kontostand",
|
Title: "Kontostand",
|
||||||
SubTitle(used: any, total: any) {
|
SubTitle(used: any, total: any) {
|
||||||
|
@ -139,11 +134,6 @@ const de: PartialLocaleType = {
|
||||||
Check: "Erneut prüfen",
|
Check: "Erneut prüfen",
|
||||||
NoAccess: "API-Schlüssel eingeben, um den Kontostand zu überprüfen",
|
NoAccess: "API-Schlüssel eingeben, um den Kontostand zu überprüfen",
|
||||||
},
|
},
|
||||||
AccessCode: {
|
|
||||||
Title: "Zugangscode",
|
|
||||||
SubTitle: "Zugangskontrolle aktiviert",
|
|
||||||
Placeholder: "Zugangscode erforderlich",
|
|
||||||
},
|
|
||||||
Model: "Modell",
|
Model: "Modell",
|
||||||
Temperature: {
|
Temperature: {
|
||||||
Title: "Temperature", //Temperatur
|
Title: "Temperature", //Temperatur
|
||||||
|
|
|
@ -264,11 +264,7 @@ const en: LocaleType = {
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Will compress if uncompressed messages length exceeds the value",
|
"Will compress if uncompressed messages length exceeds the value",
|
||||||
},
|
},
|
||||||
Token: {
|
|
||||||
Title: "API Key",
|
|
||||||
SubTitle: "Use your key to ignore access code limit",
|
|
||||||
Placeholder: "OpenAI API Key",
|
|
||||||
},
|
|
||||||
Usage: {
|
Usage: {
|
||||||
Title: "Account Balance",
|
Title: "Account Balance",
|
||||||
SubTitle(used: any, total: any) {
|
SubTitle(used: any, total: any) {
|
||||||
|
@ -278,19 +274,55 @@ const en: LocaleType = {
|
||||||
Check: "Check",
|
Check: "Check",
|
||||||
NoAccess: "Enter API Key to check balance",
|
NoAccess: "Enter API Key to check balance",
|
||||||
},
|
},
|
||||||
AccessCode: {
|
Access: {
|
||||||
Title: "Access Code",
|
AccessCode: {
|
||||||
SubTitle: "Access control enabled",
|
Title: "Access Code",
|
||||||
Placeholder: "Need Access Code",
|
SubTitle: "Access control Enabled",
|
||||||
},
|
Placeholder: "Enter Code",
|
||||||
Endpoint: {
|
},
|
||||||
Title: "Endpoint",
|
CustomEndpoint: {
|
||||||
SubTitle: "Custom endpoint must start with http(s)://",
|
Title: "Custom Endpoint",
|
||||||
},
|
SubTitle: "Use custom Azure or OpenAI service",
|
||||||
CustomModel: {
|
},
|
||||||
Title: "Custom Models",
|
Provider: {
|
||||||
SubTitle: "Add extra model options, separate by comma",
|
Title: "Model Provider",
|
||||||
|
SubTitle: "Select Azure or OpenAI",
|
||||||
|
},
|
||||||
|
OpenAI: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "OpenAI API Key",
|
||||||
|
SubTitle: "User custom OpenAI Api Key",
|
||||||
|
Placeholder: "sk-xxx",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "OpenAI Endpoint",
|
||||||
|
SubTitle: "Must starts with http(s):// or use /api/openai as default",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Azure: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Azure Api Key",
|
||||||
|
SubTitle: "Check your api key from Azure console",
|
||||||
|
Placeholder: "Azure Api Key",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Azure Endpoint",
|
||||||
|
SubTitle: "Example: ",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVerion: {
|
||||||
|
Title: "Azure Api Version",
|
||||||
|
SubTitle: "Check your api version from azure console",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CustomModel: {
|
||||||
|
Title: "Custom Models",
|
||||||
|
SubTitle: "Custom model options, seperated by comma",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
Model: "Model",
|
Model: "Model",
|
||||||
Temperature: {
|
Temperature: {
|
||||||
Title: "Temperature",
|
Title: "Temperature",
|
||||||
|
@ -444,6 +476,9 @@ const en: LocaleType = {
|
||||||
Config: "Config",
|
Config: "Config",
|
||||||
},
|
},
|
||||||
Exporter: {
|
Exporter: {
|
||||||
|
Description: {
|
||||||
|
Title: "Only messages after clearing the context will be displayed",
|
||||||
|
},
|
||||||
Model: "Model",
|
Model: "Model",
|
||||||
Messages: "Messages",
|
Messages: "Messages",
|
||||||
Topic: "Topic",
|
Topic: "Topic",
|
||||||
|
|
|
@ -124,11 +124,7 @@ const es: PartialLocaleType = {
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Se comprimirán los mensajes si la longitud de los mensajes no comprimidos supera el valor",
|
"Se comprimirán los mensajes si la longitud de los mensajes no comprimidos supera el valor",
|
||||||
},
|
},
|
||||||
Token: {
|
|
||||||
Title: "Clave de API",
|
|
||||||
SubTitle: "Utiliza tu clave para ignorar el límite de código de acceso",
|
|
||||||
Placeholder: "Clave de la API de OpenAI",
|
|
||||||
},
|
|
||||||
Usage: {
|
Usage: {
|
||||||
Title: "Saldo de la cuenta",
|
Title: "Saldo de la cuenta",
|
||||||
SubTitle(used: any, total: any) {
|
SubTitle(used: any, total: any) {
|
||||||
|
@ -138,11 +134,7 @@ const es: PartialLocaleType = {
|
||||||
Check: "Comprobar de nuevo",
|
Check: "Comprobar de nuevo",
|
||||||
NoAccess: "Introduzca la clave API para comprobar el saldo",
|
NoAccess: "Introduzca la clave API para comprobar el saldo",
|
||||||
},
|
},
|
||||||
AccessCode: {
|
|
||||||
Title: "Código de acceso",
|
|
||||||
SubTitle: "Control de acceso habilitado",
|
|
||||||
Placeholder: "Necesita código de acceso",
|
|
||||||
},
|
|
||||||
Model: "Modelo",
|
Model: "Modelo",
|
||||||
Temperature: {
|
Temperature: {
|
||||||
Title: "Temperatura",
|
Title: "Temperatura",
|
||||||
|
|
|
@ -173,11 +173,7 @@ const fr: PartialLocaleType = {
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Comprimera si la longueur des messages non compressés dépasse cette valeur",
|
"Comprimera si la longueur des messages non compressés dépasse cette valeur",
|
||||||
},
|
},
|
||||||
Token: {
|
|
||||||
Title: "Clé API",
|
|
||||||
SubTitle: "Utilisez votre clé pour ignorer la limite du code d'accès",
|
|
||||||
Placeholder: "Clé OpenAI API",
|
|
||||||
},
|
|
||||||
Usage: {
|
Usage: {
|
||||||
Title: "Solde du compte",
|
Title: "Solde du compte",
|
||||||
SubTitle(used: any, total: any) {
|
SubTitle(used: any, total: any) {
|
||||||
|
@ -187,11 +183,7 @@ const fr: PartialLocaleType = {
|
||||||
Check: "Vérifier",
|
Check: "Vérifier",
|
||||||
NoAccess: "Entrez la clé API pour vérifier le solde",
|
NoAccess: "Entrez la clé API pour vérifier le solde",
|
||||||
},
|
},
|
||||||
AccessCode: {
|
|
||||||
Title: "Code d'accès",
|
|
||||||
SubTitle: "Contrôle d'accès activé",
|
|
||||||
Placeholder: "Code d'accès requis",
|
|
||||||
},
|
|
||||||
Model: "Modèle",
|
Model: "Modèle",
|
||||||
Temperature: {
|
Temperature: {
|
||||||
Title: "Température",
|
Title: "Température",
|
||||||
|
|
|
@ -4,8 +4,9 @@ import { PartialLocaleType } from "./index";
|
||||||
const id: PartialLocaleType = {
|
const id: PartialLocaleType = {
|
||||||
WIP: "Coming Soon...",
|
WIP: "Coming Soon...",
|
||||||
Error: {
|
Error: {
|
||||||
Unauthorized: "Akses tidak diizinkan, silakan masukkan kode akses atau masukkan kunci API OpenAI Anda. di halaman [autentikasi](/#/auth) atau di halaman [Pengaturan](/#/settings).",
|
Unauthorized:
|
||||||
},
|
"Akses tidak diizinkan, silakan masukkan kode akses atau masukkan kunci API OpenAI Anda. di halaman [autentikasi](/#/auth) atau di halaman [Pengaturan](/#/settings).",
|
||||||
|
},
|
||||||
Auth: {
|
Auth: {
|
||||||
Title: "Diperlukan Kode Akses",
|
Title: "Diperlukan Kode Akses",
|
||||||
Tips: "Masukkan kode akses di bawah",
|
Tips: "Masukkan kode akses di bawah",
|
||||||
|
@ -237,11 +238,7 @@ const id: PartialLocaleType = {
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Jika panjang pesan melebihi batas yang ditentukan, pesan tersebut akan dikompresi",
|
"Jika panjang pesan melebihi batas yang ditentukan, pesan tersebut akan dikompresi",
|
||||||
},
|
},
|
||||||
Token: {
|
|
||||||
Title: "Kunci API",
|
|
||||||
SubTitle: "Gunakan kunci Anda untuk melewati batas kode akses",
|
|
||||||
Placeholder: "Kunci API OpenAI",
|
|
||||||
},
|
|
||||||
Usage: {
|
Usage: {
|
||||||
Title: "Saldo Akun",
|
Title: "Saldo Akun",
|
||||||
SubTitle(used: any, total: any) {
|
SubTitle(used: any, total: any) {
|
||||||
|
@ -251,15 +248,7 @@ const id: PartialLocaleType = {
|
||||||
Check: "Periksa",
|
Check: "Periksa",
|
||||||
NoAccess: "Masukkan kunci API untuk memeriksa saldo",
|
NoAccess: "Masukkan kunci API untuk memeriksa saldo",
|
||||||
},
|
},
|
||||||
AccessCode: {
|
|
||||||
Title: "Kode Akses",
|
|
||||||
SubTitle: "Kontrol akses diaktifkan",
|
|
||||||
Placeholder: "Diperlukan kode akses",
|
|
||||||
},
|
|
||||||
Endpoint: {
|
|
||||||
Title: "Endpoint",
|
|
||||||
SubTitle: "Harus dimulai dengan http(s):// untuk endpoint kustom",
|
|
||||||
},
|
|
||||||
Model: "Model",
|
Model: "Model",
|
||||||
Temperature: {
|
Temperature: {
|
||||||
Title: "Suhu",
|
Title: "Suhu",
|
||||||
|
@ -379,6 +368,9 @@ const id: PartialLocaleType = {
|
||||||
Edit: "Edit",
|
Edit: "Edit",
|
||||||
},
|
},
|
||||||
Exporter: {
|
Exporter: {
|
||||||
|
Description: {
|
||||||
|
Title: "Hanya pesan setelah menghapus konteks yang akan ditampilkan",
|
||||||
|
},
|
||||||
Model: "Model",
|
Model: "Model",
|
||||||
Messages: "Pesan",
|
Messages: "Pesan",
|
||||||
Topic: "Topik",
|
Topic: "Topik",
|
||||||
|
|
|
@ -124,12 +124,7 @@ const it: PartialLocaleType = {
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Comprimerà se la lunghezza dei messaggi non compressi supera il valore",
|
"Comprimerà se la lunghezza dei messaggi non compressi supera il valore",
|
||||||
},
|
},
|
||||||
Token: {
|
|
||||||
Title: "API Key",
|
|
||||||
SubTitle:
|
|
||||||
"Utilizzare la chiave per ignorare il limite del codice di accesso",
|
|
||||||
Placeholder: "OpenAI API Key",
|
|
||||||
},
|
|
||||||
Usage: {
|
Usage: {
|
||||||
Title: "Bilancio Account",
|
Title: "Bilancio Account",
|
||||||
SubTitle(used: any, total: any) {
|
SubTitle(used: any, total: any) {
|
||||||
|
@ -139,11 +134,7 @@ const it: PartialLocaleType = {
|
||||||
Check: "Controlla ancora",
|
Check: "Controlla ancora",
|
||||||
NoAccess: "Inserire la chiave API per controllare il saldo",
|
NoAccess: "Inserire la chiave API per controllare il saldo",
|
||||||
},
|
},
|
||||||
AccessCode: {
|
|
||||||
Title: "Codice d'accesso",
|
|
||||||
SubTitle: "Controllo d'accesso abilitato",
|
|
||||||
Placeholder: "Inserisci il codice d'accesso",
|
|
||||||
},
|
|
||||||
Model: "Modello GPT",
|
Model: "Modello GPT",
|
||||||
Temperature: {
|
Temperature: {
|
||||||
Title: "Temperature",
|
Title: "Temperature",
|
||||||
|
|
|
@ -147,11 +147,7 @@ const jp: PartialLocaleType = {
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"圧縮されていない履歴メッセージがこの値を超えた場合、圧縮が行われます。",
|
"圧縮されていない履歴メッセージがこの値を超えた場合、圧縮が行われます。",
|
||||||
},
|
},
|
||||||
Token: {
|
|
||||||
Title: "APIキー",
|
|
||||||
SubTitle: "自分のキーを使用してパスワードアクセス制限を迂回する",
|
|
||||||
Placeholder: "OpenAI APIキー",
|
|
||||||
},
|
|
||||||
Usage: {
|
Usage: {
|
||||||
Title: "残高照会",
|
Title: "残高照会",
|
||||||
SubTitle(used: any, total: any) {
|
SubTitle(used: any, total: any) {
|
||||||
|
@ -161,11 +157,7 @@ const jp: PartialLocaleType = {
|
||||||
Check: "再確認",
|
Check: "再確認",
|
||||||
NoAccess: "APIキーまたはアクセスパスワードを入力して残高を表示",
|
NoAccess: "APIキーまたはアクセスパスワードを入力して残高を表示",
|
||||||
},
|
},
|
||||||
AccessCode: {
|
|
||||||
Title: "アクセスパスワード",
|
|
||||||
SubTitle: "暗号化アクセスが有効になっています",
|
|
||||||
Placeholder: "アクセスパスワードを入力してください",
|
|
||||||
},
|
|
||||||
Model: "モデル (model)",
|
Model: "モデル (model)",
|
||||||
Temperature: {
|
Temperature: {
|
||||||
Title: "ランダム性 (temperature)",
|
Title: "ランダム性 (temperature)",
|
||||||
|
|
|
@ -124,11 +124,7 @@ const ko: PartialLocaleType = {
|
||||||
Title: "기록 압축 임계값",
|
Title: "기록 압축 임계값",
|
||||||
SubTitle: "미압축 메시지 길이가 임계값을 초과하면 압축됨",
|
SubTitle: "미압축 메시지 길이가 임계값을 초과하면 압축됨",
|
||||||
},
|
},
|
||||||
Token: {
|
|
||||||
Title: "API 키",
|
|
||||||
SubTitle: "액세스 코드 제한을 무시하기 위해 키 사용",
|
|
||||||
Placeholder: "OpenAI API 키",
|
|
||||||
},
|
|
||||||
Usage: {
|
Usage: {
|
||||||
Title: "계정 잔액",
|
Title: "계정 잔액",
|
||||||
SubTitle(used: any, total: any) {
|
SubTitle(used: any, total: any) {
|
||||||
|
@ -138,11 +134,7 @@ const ko: PartialLocaleType = {
|
||||||
Check: "확인",
|
Check: "확인",
|
||||||
NoAccess: "잔액 확인을 위해 API 키를 입력하세요.",
|
NoAccess: "잔액 확인을 위해 API 키를 입력하세요.",
|
||||||
},
|
},
|
||||||
AccessCode: {
|
|
||||||
Title: "액세스 코드",
|
|
||||||
SubTitle: "액세스 제어가 활성화됨",
|
|
||||||
Placeholder: "액세스 코드 입력",
|
|
||||||
},
|
|
||||||
Model: "모델",
|
Model: "모델",
|
||||||
Temperature: {
|
Temperature: {
|
||||||
Title: "온도 (temperature)",
|
Title: "온도 (temperature)",
|
||||||
|
|
|
@ -106,12 +106,7 @@ const no: PartialLocaleType = {
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Komprimer dersom ikke-komprimert lengde på meldinger overskrider denne verdien",
|
"Komprimer dersom ikke-komprimert lengde på meldinger overskrider denne verdien",
|
||||||
},
|
},
|
||||||
Token: {
|
|
||||||
Title: "API Key",
|
|
||||||
SubTitle:
|
|
||||||
"Bruk din egen API-nøkkel for å ignorere tilgangskoden begrensning",
|
|
||||||
Placeholder: "OpenAI API-nøkkel",
|
|
||||||
},
|
|
||||||
Usage: {
|
Usage: {
|
||||||
Title: "Saldo for konto",
|
Title: "Saldo for konto",
|
||||||
SubTitle(used: any, total: any) {
|
SubTitle(used: any, total: any) {
|
||||||
|
@ -121,11 +116,7 @@ const no: PartialLocaleType = {
|
||||||
Check: "Sjekk",
|
Check: "Sjekk",
|
||||||
NoAccess: "Skriv inn API-nøkkelen for å sjekke saldo",
|
NoAccess: "Skriv inn API-nøkkelen for å sjekke saldo",
|
||||||
},
|
},
|
||||||
AccessCode: {
|
|
||||||
Title: "Tilgangskode",
|
|
||||||
SubTitle: "Tilgangskontroll på",
|
|
||||||
Placeholder: "Trenger tilgangskode",
|
|
||||||
},
|
|
||||||
Model: "Model",
|
Model: "Model",
|
||||||
Temperature: {
|
Temperature: {
|
||||||
Title: "Temperatur",
|
Title: "Temperatur",
|
||||||
|
|
|
@ -125,11 +125,7 @@ const ru: PartialLocaleType = {
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Будет сжимать, если длина несжатых сообщений превышает указанное значение",
|
"Будет сжимать, если длина несжатых сообщений превышает указанное значение",
|
||||||
},
|
},
|
||||||
Token: {
|
|
||||||
Title: "API ключ",
|
|
||||||
SubTitle: "Используйте свой ключ, чтобы игнорировать лимит доступа",
|
|
||||||
Placeholder: "API ключ OpenAI",
|
|
||||||
},
|
|
||||||
Usage: {
|
Usage: {
|
||||||
Title: "Баланс аккаунта",
|
Title: "Баланс аккаунта",
|
||||||
SubTitle(used: any, total: any) {
|
SubTitle(used: any, total: any) {
|
||||||
|
@ -139,11 +135,7 @@ const ru: PartialLocaleType = {
|
||||||
Check: "Проверить",
|
Check: "Проверить",
|
||||||
NoAccess: "Введите API ключ, чтобы проверить баланс",
|
NoAccess: "Введите API ключ, чтобы проверить баланс",
|
||||||
},
|
},
|
||||||
AccessCode: {
|
|
||||||
Title: "Код доступа",
|
|
||||||
SubTitle: "Контроль доступа включен",
|
|
||||||
Placeholder: "Требуется код доступа",
|
|
||||||
},
|
|
||||||
Model: "Модель",
|
Model: "Модель",
|
||||||
Temperature: {
|
Temperature: {
|
||||||
Title: "Температура",
|
Title: "Температура",
|
||||||
|
|
|
@ -124,11 +124,7 @@ const tr: PartialLocaleType = {
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Sıkıştırılmamış mesajların uzunluğu bu değeri aşarsa sıkıştırılır",
|
"Sıkıştırılmamış mesajların uzunluğu bu değeri aşarsa sıkıştırılır",
|
||||||
},
|
},
|
||||||
Token: {
|
|
||||||
Title: "API Anahtarı",
|
|
||||||
SubTitle: "Erişim kodu sınırını yoksaymak için anahtarınızı kullanın",
|
|
||||||
Placeholder: "OpenAI API Anahtarı",
|
|
||||||
},
|
|
||||||
Usage: {
|
Usage: {
|
||||||
Title: "Hesap Bakiyesi",
|
Title: "Hesap Bakiyesi",
|
||||||
SubTitle(used: any, total: any) {
|
SubTitle(used: any, total: any) {
|
||||||
|
@ -138,11 +134,7 @@ const tr: PartialLocaleType = {
|
||||||
Check: "Tekrar Kontrol Et",
|
Check: "Tekrar Kontrol Et",
|
||||||
NoAccess: "Bakiyeyi kontrol etmek için API anahtarını girin",
|
NoAccess: "Bakiyeyi kontrol etmek için API anahtarını girin",
|
||||||
},
|
},
|
||||||
AccessCode: {
|
|
||||||
Title: "Erişim Kodu",
|
|
||||||
SubTitle: "Erişim kontrolü etkinleştirme",
|
|
||||||
Placeholder: "Erişim Kodu Gerekiyor",
|
|
||||||
},
|
|
||||||
Model: "Model",
|
Model: "Model",
|
||||||
Temperature: {
|
Temperature: {
|
||||||
Title: "Gerçeklik",
|
Title: "Gerçeklik",
|
||||||
|
|
|
@ -120,11 +120,7 @@ const tw: PartialLocaleType = {
|
||||||
Title: "歷史訊息長度壓縮閾值",
|
Title: "歷史訊息長度壓縮閾值",
|
||||||
SubTitle: "當未壓縮的歷史訊息超過該值時,將進行壓縮",
|
SubTitle: "當未壓縮的歷史訊息超過該值時,將進行壓縮",
|
||||||
},
|
},
|
||||||
Token: {
|
|
||||||
Title: "API Key",
|
|
||||||
SubTitle: "使用自己的 Key 可規避授權存取限制",
|
|
||||||
Placeholder: "OpenAI API Key",
|
|
||||||
},
|
|
||||||
Usage: {
|
Usage: {
|
||||||
Title: "帳戶餘額",
|
Title: "帳戶餘額",
|
||||||
SubTitle(used: any, total: any) {
|
SubTitle(used: any, total: any) {
|
||||||
|
@ -134,11 +130,7 @@ const tw: PartialLocaleType = {
|
||||||
Check: "重新檢查",
|
Check: "重新檢查",
|
||||||
NoAccess: "輸入 API Key 檢視餘額",
|
NoAccess: "輸入 API Key 檢視餘額",
|
||||||
},
|
},
|
||||||
AccessCode: {
|
|
||||||
Title: "授權碼",
|
|
||||||
SubTitle: "目前是未授權存取狀態",
|
|
||||||
Placeholder: "請輸入授權碼",
|
|
||||||
},
|
|
||||||
Model: "模型 (model)",
|
Model: "模型 (model)",
|
||||||
Temperature: {
|
Temperature: {
|
||||||
Title: "隨機性 (temperature)",
|
Title: "隨機性 (temperature)",
|
||||||
|
|
|
@ -123,11 +123,7 @@ const vi: PartialLocaleType = {
|
||||||
Title: "Ngưỡng nén lịch sử tin nhắn",
|
Title: "Ngưỡng nén lịch sử tin nhắn",
|
||||||
SubTitle: "Thực hiện nén nếu số lượng tin nhắn chưa nén vượt quá ngưỡng",
|
SubTitle: "Thực hiện nén nếu số lượng tin nhắn chưa nén vượt quá ngưỡng",
|
||||||
},
|
},
|
||||||
Token: {
|
|
||||||
Title: "API Key",
|
|
||||||
SubTitle: "Sử dụng khóa của bạn để bỏ qua giới hạn mã truy cập",
|
|
||||||
Placeholder: "OpenAI API Key",
|
|
||||||
},
|
|
||||||
Usage: {
|
Usage: {
|
||||||
Title: "Hạn mức tài khoản",
|
Title: "Hạn mức tài khoản",
|
||||||
SubTitle(used: any, total: any) {
|
SubTitle(used: any, total: any) {
|
||||||
|
@ -137,11 +133,7 @@ const vi: PartialLocaleType = {
|
||||||
Check: "Kiểm tra",
|
Check: "Kiểm tra",
|
||||||
NoAccess: "Nhập API Key để kiểm tra hạn mức",
|
NoAccess: "Nhập API Key để kiểm tra hạn mức",
|
||||||
},
|
},
|
||||||
AccessCode: {
|
|
||||||
Title: "Mã truy cập",
|
|
||||||
SubTitle: "Đã bật kiểm soát truy cập",
|
|
||||||
Placeholder: "Nhập mã truy cập",
|
|
||||||
},
|
|
||||||
Model: "Mô hình",
|
Model: "Mô hình",
|
||||||
Temperature: {
|
Temperature: {
|
||||||
Title: "Tính ngẫu nhiên (temperature)",
|
Title: "Tính ngẫu nhiên (temperature)",
|
||||||
|
|
|
@ -1,25 +1,41 @@
|
||||||
import { DEFAULT_API_HOST, DEFAULT_MODELS, StoreKey } from "../constant";
|
import {
|
||||||
|
ApiPath,
|
||||||
|
DEFAULT_API_HOST,
|
||||||
|
ServiceProvider,
|
||||||
|
StoreKey,
|
||||||
|
} from "../constant";
|
||||||
import { getHeaders } from "../client/api";
|
import { getHeaders } from "../client/api";
|
||||||
import { getClientConfig } from "../config/client";
|
import { getClientConfig } from "../config/client";
|
||||||
import { createPersistStore } from "../utils/store";
|
import { createPersistStore } from "../utils/store";
|
||||||
|
import { ensure } from "../utils/clone";
|
||||||
|
|
||||||
let fetchState = 0; // 0 not fetch, 1 fetching, 2 done
|
let fetchState = 0; // 0 not fetch, 1 fetching, 2 done
|
||||||
|
|
||||||
const DEFAULT_OPENAI_URL =
|
const DEFAULT_OPENAI_URL =
|
||||||
getClientConfig()?.buildMode === "export" ? DEFAULT_API_HOST : "/api/openai/";
|
getClientConfig()?.buildMode === "export" ? DEFAULT_API_HOST : ApiPath.OpenAI;
|
||||||
console.log("[API] default openai url", DEFAULT_OPENAI_URL);
|
|
||||||
|
|
||||||
const DEFAULT_ACCESS_STATE = {
|
const DEFAULT_ACCESS_STATE = {
|
||||||
token: "",
|
|
||||||
accessCode: "",
|
accessCode: "",
|
||||||
|
useCustomConfig: false,
|
||||||
|
|
||||||
|
provider: ServiceProvider.OpenAI,
|
||||||
|
|
||||||
|
// openai
|
||||||
|
openaiUrl: DEFAULT_OPENAI_URL,
|
||||||
|
openaiApiKey: "",
|
||||||
|
|
||||||
|
// azure
|
||||||
|
azureUrl: "",
|
||||||
|
azureApiKey: "",
|
||||||
|
azureApiVersion: "2023-08-01-preview",
|
||||||
|
|
||||||
|
// server config
|
||||||
needCode: true,
|
needCode: true,
|
||||||
hideUserApiKey: false,
|
hideUserApiKey: false,
|
||||||
hideBalanceQuery: false,
|
hideBalanceQuery: false,
|
||||||
disableGPT4: false,
|
disableGPT4: false,
|
||||||
disableFastLink: false,
|
disableFastLink: false,
|
||||||
customModels: "",
|
customModels: "",
|
||||||
|
|
||||||
openaiUrl: DEFAULT_OPENAI_URL,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useAccessStore = createPersistStore(
|
export const useAccessStore = createPersistStore(
|
||||||
|
@ -31,12 +47,24 @@ export const useAccessStore = createPersistStore(
|
||||||
|
|
||||||
return get().needCode;
|
return get().needCode;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
isValidOpenAI() {
|
||||||
|
return ensure(get(), ["openaiApiKey"]);
|
||||||
|
},
|
||||||
|
|
||||||
|
isValidAzure() {
|
||||||
|
return ensure(get(), ["azureUrl", "azureApiKey", "azureApiVersion"]);
|
||||||
|
},
|
||||||
|
|
||||||
isAuthorized() {
|
isAuthorized() {
|
||||||
this.fetch();
|
this.fetch();
|
||||||
|
|
||||||
// has token or has code or disabled access control
|
// has token or has code or disabled access control
|
||||||
return (
|
return (
|
||||||
!!get().token || !!get().accessCode || !this.enabledAccessControl()
|
this.isValidOpenAI() ||
|
||||||
|
this.isValidAzure() ||
|
||||||
|
!this.enabledAccessControl() ||
|
||||||
|
(this.enabledAccessControl() && ensure(get(), ["accessCode"]))
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
fetch() {
|
fetch() {
|
||||||
|
@ -64,6 +92,19 @@ export const useAccessStore = createPersistStore(
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: StoreKey.Access,
|
name: StoreKey.Access,
|
||||||
version: 1,
|
version: 2,
|
||||||
|
migrate(persistedState, version) {
|
||||||
|
if (version < 2) {
|
||||||
|
const state = persistedState as {
|
||||||
|
token: string;
|
||||||
|
openaiApiKey: string;
|
||||||
|
azureApiVersion: string;
|
||||||
|
};
|
||||||
|
state.openaiApiKey = state.token;
|
||||||
|
state.azureApiVersion = "2023-08-01-preview";
|
||||||
|
}
|
||||||
|
|
||||||
|
return persistedState as any;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,3 +1,12 @@
|
||||||
export function deepClone<T>(obj: T) {
|
export function deepClone<T>(obj: T) {
|
||||||
return JSON.parse(JSON.stringify(obj));
|
return JSON.parse(JSON.stringify(obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function ensure<T extends object>(
|
||||||
|
obj: T,
|
||||||
|
keys: Array<[keyof T][number]>,
|
||||||
|
) {
|
||||||
|
return keys.every(
|
||||||
|
(k) => obj[k] !== undefined && obj[k] !== null && obj[k] !== "",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ export function useAllModels() {
|
||||||
const models = useMemo(() => {
|
const models = useMemo(() => {
|
||||||
return collectModels(
|
return collectModels(
|
||||||
configStore.models,
|
configStore.models,
|
||||||
[accessStore.customModels, configStore.customModels].join(","),
|
[configStore.customModels, accessStore.customModels].join(","),
|
||||||
);
|
);
|
||||||
}, [accessStore.customModels, configStore.customModels, configStore.models]);
|
}, [accessStore.customModels, configStore.customModels, configStore.models]);
|
||||||
|
|
||||||
|
|
|
@ -4,21 +4,34 @@ export function collectModelTable(
|
||||||
models: readonly LLMModel[],
|
models: readonly LLMModel[],
|
||||||
customModels: string,
|
customModels: string,
|
||||||
) {
|
) {
|
||||||
const modelTable: Record<string, boolean> = {};
|
const modelTable: Record<
|
||||||
|
string,
|
||||||
|
{ available: boolean; name: string; displayName: string }
|
||||||
|
> = {};
|
||||||
|
|
||||||
// default models
|
// default models
|
||||||
models.forEach((m) => (modelTable[m.name] = m.available));
|
models.forEach(
|
||||||
|
(m) =>
|
||||||
|
(modelTable[m.name] = {
|
||||||
|
...m,
|
||||||
|
displayName: m.name,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
// server custom models
|
// server custom models
|
||||||
customModels
|
customModels
|
||||||
.split(",")
|
.split(",")
|
||||||
.filter((v) => !!v && v.length > 0)
|
.filter((v) => !!v && v.length > 0)
|
||||||
.map((m) => {
|
.map((m) => {
|
||||||
if (m.startsWith("+")) {
|
const available = !m.startsWith("-");
|
||||||
modelTable[m.slice(1)] = true;
|
const nameConfig =
|
||||||
} else if (m.startsWith("-")) {
|
m.startsWith("+") || m.startsWith("-") ? m.slice(1) : m;
|
||||||
modelTable[m.slice(1)] = false;
|
const [name, displayName] = nameConfig.split(":");
|
||||||
} else modelTable[m] = true;
|
modelTable[name] = {
|
||||||
|
name,
|
||||||
|
displayName: displayName || name,
|
||||||
|
available,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
return modelTable;
|
return modelTable;
|
||||||
}
|
}
|
||||||
|
@ -31,10 +44,7 @@ export function collectModels(
|
||||||
customModels: string,
|
customModels: string,
|
||||||
) {
|
) {
|
||||||
const modelTable = collectModelTable(models, customModels);
|
const modelTable = collectModelTable(models, customModels);
|
||||||
const allModels = Object.keys(modelTable).map((m) => ({
|
const allModels = Object.values(modelTable);
|
||||||
name: m,
|
|
||||||
available: modelTable[m],
|
|
||||||
}));
|
|
||||||
|
|
||||||
return allModels;
|
return allModels;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { persist } from "zustand/middleware";
|
import { combine, persist } from "zustand/middleware";
|
||||||
import { Updater } from "../typing";
|
import { Updater } from "../typing";
|
||||||
import { deepClone } from "./clone";
|
import { deepClone } from "./clone";
|
||||||
|
|
||||||
|
@ -23,33 +23,42 @@ type SetStoreState<T> = (
|
||||||
replace?: boolean | undefined,
|
replace?: boolean | undefined,
|
||||||
) => void;
|
) => void;
|
||||||
|
|
||||||
export function createPersistStore<T, M>(
|
export function createPersistStore<T extends object, M>(
|
||||||
defaultState: T,
|
state: T,
|
||||||
methods: (
|
methods: (
|
||||||
set: SetStoreState<T & MakeUpdater<T>>,
|
set: SetStoreState<T & MakeUpdater<T>>,
|
||||||
get: () => T & MakeUpdater<T>,
|
get: () => T & MakeUpdater<T>,
|
||||||
) => M,
|
) => M,
|
||||||
persistOptions: SecondParam<typeof persist<T & M & MakeUpdater<T>>>,
|
persistOptions: SecondParam<typeof persist<T & M & MakeUpdater<T>>>,
|
||||||
) {
|
) {
|
||||||
return create<T & M & MakeUpdater<T>>()(
|
return create(
|
||||||
persist((set, get) => {
|
persist(
|
||||||
return {
|
combine(
|
||||||
...defaultState,
|
{
|
||||||
...methods(set as any, get),
|
...state,
|
||||||
|
lastUpdateTime: 0,
|
||||||
|
},
|
||||||
|
(set, get) => {
|
||||||
|
return {
|
||||||
|
...methods(set, get as any),
|
||||||
|
|
||||||
lastUpdateTime: 0,
|
markUpdate() {
|
||||||
markUpdate() {
|
set({ lastUpdateTime: Date.now() } as Partial<
|
||||||
set({ lastUpdateTime: Date.now() } as Partial<
|
T & M & MakeUpdater<T>
|
||||||
T & M & MakeUpdater<T>
|
>);
|
||||||
>);
|
},
|
||||||
|
update(updater) {
|
||||||
|
const state = deepClone(get());
|
||||||
|
updater(state);
|
||||||
|
set({
|
||||||
|
...state,
|
||||||
|
lastUpdateTime: Date.now(),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
} as M & MakeUpdater<T>;
|
||||||
},
|
},
|
||||||
update(updater) {
|
),
|
||||||
const state = deepClone(get());
|
persistOptions as any,
|
||||||
updater(state);
|
),
|
||||||
get().markUpdate();
|
|
||||||
set(state);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}, persistOptions),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
10
package.json
10
package.json
|
@ -25,16 +25,16 @@
|
||||||
"axios": "^1.4.0",
|
"axios": "^1.4.0",
|
||||||
"cheerio": "^1.0.0-rc.12",
|
"cheerio": "^1.0.0-rc.12",
|
||||||
"duck-duck-scrape": "^2.2.4",
|
"duck-duck-scrape": "^2.2.4",
|
||||||
"emoji-picker-react": "^4.5.1",
|
|
||||||
"encoding": "^0.1.13",
|
"encoding": "^0.1.13",
|
||||||
|
"emoji-picker-react": "^4.5.15",
|
||||||
"fuse.js": "^6.6.2",
|
"fuse.js": "^6.6.2",
|
||||||
"html-entities": "^2.4.0",
|
"html-entities": "^2.4.0",
|
||||||
"html-to-image": "^1.11.11",
|
"html-to-image": "^1.11.11",
|
||||||
"html-to-text": "^9.0.5",
|
"html-to-text": "^9.0.5",
|
||||||
"https-proxy-agent": "^7.0.2",
|
"https-proxy-agent": "^7.0.2",
|
||||||
"langchain": "^0.0.154",
|
"langchain": "^0.0.154",
|
||||||
"mermaid": "^10.3.1",
|
"mermaid": "^10.6.1",
|
||||||
"nanoid": "^4.0.2",
|
"nanoid": "^5.0.3",
|
||||||
"next": "^13.4.9",
|
"next": "^13.4.9",
|
||||||
"node-fetch": "^3.3.1",
|
"node-fetch": "^3.3.1",
|
||||||
"openai": "^4.6.0",
|
"openai": "^4.6.0",
|
||||||
|
@ -55,11 +55,11 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tauri-apps/cli": "^1.4.0",
|
"@tauri-apps/cli": "^1.4.0",
|
||||||
"@types/html-to-text": "^9.0.1",
|
"@types/html-to-text": "^9.0.1",
|
||||||
"@types/node": "^20.3.3",
|
"@types/node": "^20.9.0",
|
||||||
"@types/react": "^18.2.14",
|
"@types/react": "^18.2.14",
|
||||||
"@types/react-dom": "^18.2.7",
|
"@types/react-dom": "^18.2.7",
|
||||||
"@types/react-katex": "^3.0.0",
|
"@types/react-katex": "^3.0.0",
|
||||||
"@types/spark-md5": "^3.0.2",
|
"@types/spark-md5": "^3.0.4",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"eslint": "^8.49.0",
|
"eslint": "^8.49.0",
|
||||||
"eslint-config-next": "13.4.19",
|
"eslint-config-next": "13.4.19",
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
},
|
},
|
||||||
"package": {
|
"package": {
|
||||||
"productName": "ChatGPT Next Web",
|
"productName": "ChatGPT Next Web",
|
||||||
"version": "2.9.10"
|
"version": "2.9.11"
|
||||||
},
|
},
|
||||||
"tauri": {
|
"tauri": {
|
||||||
"allowlist": {
|
"allowlist": {
|
||||||
|
|
39
yarn.lock
39
yarn.lock
|
@ -2621,10 +2621,10 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.3.tgz#cef09e3ec9af1d63d2a6cc5b383a737e24e6dcf5"
|
resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.3.tgz#cef09e3ec9af1d63d2a6cc5b383a737e24e6dcf5"
|
||||||
integrity sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==
|
integrity sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==
|
||||||
|
|
||||||
"@types/spark-md5@^3.0.2":
|
"@types/spark-md5@^3.0.4":
|
||||||
version "3.0.2"
|
version "3.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/@types/spark-md5/-/spark-md5-3.0.2.tgz#da2e8a778a20335fc4f40b6471c4b0d86b70da55"
|
resolved "https://registry.yarnpkg.com/@types/spark-md5/-/spark-md5-3.0.4.tgz#c1221d63c069d95aba0c06a765b80661cacc12bf"
|
||||||
integrity sha512-82E/lVRaqelV9qmRzzJ1PKTpyrpnT7mwdneKNJB9hUtypZDMggloDfFUCIqRRx3lYRxteCwXSq9c+W71Vf0QnQ==
|
integrity sha512-qtOaDz+IXiNndPgYb6t1YoutnGvFRtWSNzpVjkAPCfB2UzTyybuD4Tjgs7VgRawum3JnJNRwNQd4N//SvrHg1Q==
|
||||||
|
|
||||||
"@types/unist@*", "@types/unist@^2.0.0":
|
"@types/unist@*", "@types/unist@^2.0.0":
|
||||||
version "2.0.6"
|
version "2.0.6"
|
||||||
|
@ -3292,11 +3292,6 @@ client-only@0.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1"
|
resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1"
|
||||||
integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==
|
integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==
|
||||||
|
|
||||||
clsx@^1.2.1:
|
|
||||||
version "1.2.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12"
|
|
||||||
integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==
|
|
||||||
|
|
||||||
color-convert@^1.9.0:
|
color-convert@^1.9.0:
|
||||||
version "1.9.3"
|
version "1.9.3"
|
||||||
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
|
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
|
||||||
|
@ -4007,12 +4002,10 @@ elkjs@^0.8.2:
|
||||||
resolved "https://registry.npmmirror.com/elkjs/-/elkjs-0.8.2.tgz#c37763c5a3e24e042e318455e0147c912a7c248e"
|
resolved "https://registry.npmmirror.com/elkjs/-/elkjs-0.8.2.tgz#c37763c5a3e24e042e318455e0147c912a7c248e"
|
||||||
integrity sha512-L6uRgvZTH+4OF5NE/MBbzQx/WYpru1xCBE9respNj6qznEewGUIfhzmm7horWWxbNO2M0WckQypGctR8lH79xQ==
|
integrity sha512-L6uRgvZTH+4OF5NE/MBbzQx/WYpru1xCBE9respNj6qznEewGUIfhzmm7horWWxbNO2M0WckQypGctR8lH79xQ==
|
||||||
|
|
||||||
emoji-picker-react@^4.5.1:
|
emoji-picker-react@^4.5.15:
|
||||||
version "4.5.1"
|
version "4.5.15"
|
||||||
resolved "https://registry.yarnpkg.com/emoji-picker-react/-/emoji-picker-react-4.5.1.tgz#341f27dc86ad09340a316e0632484fcb9aff7195"
|
resolved "https://registry.yarnpkg.com/emoji-picker-react/-/emoji-picker-react-4.5.15.tgz#e12797c50584cb8af8aee7eb6c7c8fd953e41f7e"
|
||||||
integrity sha512-zpm0ui0TWkXZDUIevyNM0rC9Jyqc08RvVXH0KgsbSkDr+VgMQmYLu6UeI4SIWMZKsKMjQwujPpncRCFlEeykjw==
|
integrity sha512-BTqo+pNUE8kqX8BKFTbD4fhlxcA69qfie5En4PerReLaaPfXVyRlDJ1uf85nKj2u5esUQ999iUf8YyqcPsM2Qw==
|
||||||
dependencies:
|
|
||||||
clsx "^1.2.1"
|
|
||||||
|
|
||||||
emoji-regex@^8.0.0:
|
emoji-regex@^8.0.0:
|
||||||
version "8.0.0"
|
version "8.0.0"
|
||||||
|
@ -5746,10 +5739,10 @@ merge2@^1.3.0, merge2@^1.4.1:
|
||||||
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
|
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
|
||||||
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
|
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
|
||||||
|
|
||||||
mermaid@^10.3.1:
|
mermaid@^10.6.1:
|
||||||
version "10.3.1"
|
version "10.6.1"
|
||||||
resolved "https://registry.yarnpkg.com/mermaid/-/mermaid-10.3.1.tgz#2f3c7e9f6bd7a8da2bef71cce2a542c8eba2a62e"
|
resolved "https://registry.yarnpkg.com/mermaid/-/mermaid-10.6.1.tgz#701f4160484137a417770ce757ce1887a98c00fc"
|
||||||
integrity sha512-hkenh7WkuRWPcob3oJtrN3W+yzrrIYuWF1OIfk/d0xGE8UWlvDhfexaHmDwwe8DKQgqMLI8DWEPwGprxkumjuw==
|
integrity sha512-Hky0/RpOw/1il9X8AvzOEChfJtVvmXm+y7JML5C//ePYMy0/9jCEmW1E1g86x9oDfW9+iVEdTV/i+M6KWRNs4A==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@braintree/sanitize-url" "^6.0.1"
|
"@braintree/sanitize-url" "^6.0.1"
|
||||||
"@types/d3-scale" "^4.0.3"
|
"@types/d3-scale" "^4.0.3"
|
||||||
|
@ -6158,10 +6151,10 @@ nanoid@^3.3.4:
|
||||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c"
|
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c"
|
||||||
integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==
|
integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==
|
||||||
|
|
||||||
nanoid@^4.0.2:
|
nanoid@^5.0.3:
|
||||||
version "4.0.2"
|
version "5.0.3"
|
||||||
resolved "https://registry.npmmirror.com/nanoid/-/nanoid-4.0.2.tgz#140b3c5003959adbebf521c170f282c5e7f9fb9e"
|
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-5.0.3.tgz#6c97f53d793a7a1de6a38ebb46f50f95bf9793c7"
|
||||||
integrity sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw==
|
integrity sha512-I7X2b22cxA4LIHXPSqbBCEQSL+1wv8TuoefejsX4HFWyC6jc5JG7CEaxOltiKjc1M+YCS2YkrZZcj4+dytw9GA==
|
||||||
|
|
||||||
natural-compare@^1.4.0:
|
natural-compare@^1.4.0:
|
||||||
version "1.4.0"
|
version "1.4.0"
|
||||||
|
|
Loading…
Reference in New Issue