@@ -576,9 +562,14 @@ export function ImagePreviewer(props: {
key={i}
>
diff --git a/app/components/settings.tsx b/app/components/settings.tsx
index 3b990ed2c..68ebcf084 100644
--- a/app/components/settings.tsx
+++ b/app/components/settings.tsx
@@ -74,6 +74,7 @@ import {
SAAS_CHAT_URL,
ChatGLM,
DeepSeek,
+ SiliconFlow,
} from "../constant";
import { Prompt, SearchService, usePromptStore } from "../store/prompt";
import { ErrorBoundary } from "./error";
@@ -1318,6 +1319,46 @@ export function Settings() {
>
);
+ const siliconflowConfigComponent = accessStore.provider ===
+ ServiceProvider.SiliconFlow && (
+ <>
+
+
+ accessStore.update(
+ (access) => (access.siliconflowUrl = e.currentTarget.value),
+ )
+ }
+ >
+
+
+ {
+ accessStore.update(
+ (access) => (access.siliconflowApiKey = e.currentTarget.value),
+ );
+ }}
+ />
+
+ >
+ );
const stabilityConfigComponent = accessStore.provider ===
ServiceProvider.Stability && (
@@ -1780,6 +1821,7 @@ export function Settings() {
{lflytekConfigComponent}
{XAIConfigComponent}
{chatglmConfigComponent}
+ {siliconflowConfigComponent}
>
)}
>
diff --git a/app/components/ui-lib.tsx b/app/components/ui-lib.tsx
index a64265235..7b9f5ace0 100644
--- a/app/components/ui-lib.tsx
+++ b/app/components/ui-lib.tsx
@@ -23,6 +23,7 @@ import React, {
useRef,
} from "react";
import { IconButton } from "./button";
+import { Avatar } from "./emoji";
import clsx from "clsx";
export function Popover(props: {
@@ -522,6 +523,7 @@ export function Selector
(props: {
key={i}
title={item.title}
subTitle={item.subTitle}
+ icon={}
onClick={(e) => {
if (item.disable) {
e.stopPropagation();
diff --git a/app/config/server.ts b/app/config/server.ts
index b57c8ba0d..17fccde02 100644
--- a/app/config/server.ts
+++ b/app/config/server.ts
@@ -85,6 +85,10 @@ declare global {
CHATGLM_URL?: string;
CHATGLM_API_KEY?: string;
+ // siliconflow only
+ SILICONFLOW_URL?: string;
+ SILICONFLOW_API_KEY?: string;
+
// custom template for preprocessing user input
DEFAULT_INPUT_TEMPLATE?: string;
@@ -163,6 +167,7 @@ export const getServerSideConfig = () => {
const isDeepSeek = !!process.env.DEEPSEEK_API_KEY;
const isXAI = !!process.env.XAI_API_KEY;
const isChatGLM = !!process.env.CHATGLM_API_KEY;
+ const isSiliconFlow = !!process.env.SILICONFLOW_API_KEY;
// const apiKeyEnvVar = process.env.OPENAI_API_KEY ?? "";
// const apiKeys = apiKeyEnvVar.split(",").map((v) => v.trim());
// const randomIndex = Math.floor(Math.random() * apiKeys.length);
@@ -242,6 +247,10 @@ export const getServerSideConfig = () => {
cloudflareKVApiKey: getApiKey(process.env.CLOUDFLARE_KV_API_KEY),
cloudflareKVTTL: process.env.CLOUDFLARE_KV_TTL,
+ isSiliconFlow,
+ siliconFlowUrl: process.env.SILICONFLOW_URL,
+ siliconFlowApiKey: getApiKey(process.env.SILICONFLOW_API_KEY),
+
gtmId: process.env.GTM_ID,
gaId: process.env.GA_ID || DEFAULT_GA_ID,
diff --git a/app/constant.ts b/app/constant.ts
index 60200af41..c1b135485 100644
--- a/app/constant.ts
+++ b/app/constant.ts
@@ -34,6 +34,8 @@ export const XAI_BASE_URL = "https://api.x.ai";
export const CHATGLM_BASE_URL = "https://open.bigmodel.cn";
+export const SILICONFLOW_BASE_URL = "https://api.siliconflow.cn";
+
export const CACHE_URL_PREFIX = "/api/cache";
export const UPLOAD_URL = `${CACHE_URL_PREFIX}/upload`;
@@ -69,6 +71,7 @@ export enum ApiPath {
XAI = "/api/xai",
ChatGLM = "/api/chatglm",
DeepSeek = "/api/deepseek",
+ SiliconFlow = "/api/siliconflow",
}
export enum SlotID {
@@ -107,6 +110,7 @@ export const UNFINISHED_INPUT = (id: string) => "unfinished-input-" + id;
export const STORAGE_KEY = "chatgpt-next-web";
export const REQUEST_TIMEOUT_MS = 60000;
+export const REQUEST_TIMEOUT_MS_FOR_THINKING = REQUEST_TIMEOUT_MS * 5;
export const EXPORT_MESSAGE_CLASS_NAME = "export-markdown";
@@ -125,6 +129,7 @@ export enum ServiceProvider {
XAI = "XAI",
ChatGLM = "ChatGLM",
DeepSeek = "DeepSeek",
+ SiliconFlow = "SiliconFlow",
}
// Google API safety settings, see https://ai.google.dev/gemini-api/docs/safety-settings
@@ -150,6 +155,7 @@ export enum ModelProvider {
XAI = "XAI",
ChatGLM = "ChatGLM",
DeepSeek = "DeepSeek",
+ SiliconFlow = "SiliconFlow",
}
export const Stability = {
@@ -215,7 +221,12 @@ export const ByteDance = {
export const Alibaba = {
ExampleEndpoint: ALIBABA_BASE_URL,
- ChatPath: "v1/services/aigc/text-generation/generation",
+ ChatPath: (modelName: string) => {
+ if (modelName.includes("vl") || modelName.includes("omni")) {
+ return "v1/services/aigc/multimodal-generation/generation";
+ }
+ return `v1/services/aigc/text-generation/generation`;
+ },
};
export const Tencent = {
@@ -249,6 +260,12 @@ export const ChatGLM = {
VideoPath: "api/paas/v4/videos/generations",
};
+export const SiliconFlow = {
+ ExampleEndpoint: SILICONFLOW_BASE_URL,
+ ChatPath: "v1/chat/completions",
+ ListModelPath: "v1/models?&sub_type=chat",
+};
+
export const DEFAULT_INPUT_TEMPLATE = `{{input}}`; // input / time / model / lang
// export const DEFAULT_SYSTEM_TEMPLATE = `
// You are ChatGPT, a large language model trained by {{ServiceProvider}}.
@@ -413,7 +430,7 @@ export const KnowledgeCutOffDate: Record = {
"o1-preview-2024-09-12": "2023-10",
"o1-preview": "2023-10",
"o1-2024-12-17": "2023-10",
- "o1": "2023-10",
+ o1: "2023-10",
"o3-mini-2025-01-31": "2023-10",
"o3-mini": "2023-10",
// After improvements,
@@ -451,6 +468,7 @@ export const VISION_MODEL_REGEXES = [
/gpt-4-turbo(?!.*preview)/, // Matches "gpt-4-turbo" but not "gpt-4-turbo-preview"
/^dall-e-3$/, // Matches exactly "dall-e-3"
/glm-4v/,
+ /vl/i,
];
export const EXCLUDE_VISION_MODEL_REGEXES = [/claude-3-5-haiku-20241022/];
@@ -499,10 +517,14 @@ const googleModels = [
"gemini-exp-1114",
"gemini-exp-1121",
"gemini-exp-1206",
+ "gemini-2.0-flash",
"gemini-2.0-flash-exp",
+ "gemini-2.0-flash-lite-preview-02-05",
"gemini-2.0-flash-thinking-exp",
"gemini-2.0-flash-thinking-exp-1219",
"gemini-2.0-flash-thinking-exp-01-21",
+ "gemini-2.0-pro-exp",
+ "gemini-2.0-pro-exp-02-05",
];
const anthropicModels = [
@@ -518,6 +540,8 @@ const anthropicModels = [
"claude-3-5-sonnet-20240620",
"claude-3-5-sonnet-20241022",
"claude-3-5-sonnet-latest",
+ "claude-3-7-sonnet-20250219",
+ "claude-3-7-sonnet-latest",
];
const baiduModels = [
@@ -551,6 +575,9 @@ const alibabaModes = [
"qwen-max-0403",
"qwen-max-0107",
"qwen-max-longcontext",
+ "qwen-omni-turbo",
+ "qwen-vl-plus",
+ "qwen-vl-max",
];
const tencentModels = [
@@ -575,7 +602,16 @@ const iflytekModels = [
const deepseekModels = ["deepseek-chat", "deepseek-coder", "deepseek-reasoner"];
-const xAIModes = ["grok-beta"];
+const xAIModes = [
+ "grok-beta",
+ "grok-2",
+ "grok-2-1212",
+ "grok-2-latest",
+ "grok-vision-beta",
+ "grok-2-vision-1212",
+ "grok-2-vision",
+ "grok-2-vision-latest",
+];
const chatglmModels = [
"glm-4-plus",
@@ -597,6 +633,23 @@ const chatglmModels = [
// "cogvideox-flash", // free
];
+const siliconflowModels = [
+ "Qwen/Qwen2.5-7B-Instruct",
+ "Qwen/Qwen2.5-72B-Instruct",
+ "deepseek-ai/DeepSeek-R1",
+ "deepseek-ai/DeepSeek-R1-Distill-Llama-70B",
+ "deepseek-ai/DeepSeek-R1-Distill-Llama-8B",
+ "deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B",
+ "deepseek-ai/DeepSeek-R1-Distill-Qwen-14B",
+ "deepseek-ai/DeepSeek-R1-Distill-Qwen-32B",
+ "deepseek-ai/DeepSeek-R1-Distill-Qwen-7B",
+ "deepseek-ai/DeepSeek-V3",
+ "meta-llama/Llama-3.3-70B-Instruct",
+ "THUDM/glm-4-9b-chat",
+ "Pro/deepseek-ai/DeepSeek-R1",
+ "Pro/deepseek-ai/DeepSeek-V3",
+];
+
let seq = 1000; // 内置的模型序号生成器从1000开始
export const DEFAULT_MODELS = [
...openaiModels.map((name) => ({
@@ -742,6 +795,17 @@ export const DEFAULT_MODELS = [
sorted: 13,
},
})),
+ ...siliconflowModels.map((name) => ({
+ name,
+ available: true,
+ sorted: seq++,
+ provider: {
+ id: "siliconflow",
+ providerName: "SiliconFlow",
+ providerType: "siliconflow",
+ sorted: 14,
+ },
+ })),
] as const;
export const CHAT_PAGE_SIZE = 15;
@@ -762,5 +826,5 @@ export const internalAllowedWebDavEndpoints = [
export const DEFAULT_GA_ID = "G-89WN60ZK2E";
-export const SAAS_CHAT_URL = "https://nextchat.dev/chat";
-export const SAAS_CHAT_UTM_URL = "https://nextchat.dev/chat?utm=github";
+export const SAAS_CHAT_URL = "https://nextchat.club";
+export const SAAS_CHAT_UTM_URL = "https://nextchat.club?utm=github";
diff --git a/app/icons/llm-icons/chatglm.svg b/app/icons/llm-icons/chatglm.svg
new file mode 100644
index 000000000..642750f3e
--- /dev/null
+++ b/app/icons/llm-icons/chatglm.svg
@@ -0,0 +1,14 @@
+
\ No newline at end of file
diff --git a/app/icons/llm-icons/claude.svg b/app/icons/llm-icons/claude.svg
new file mode 100644
index 000000000..ca8e447bb
--- /dev/null
+++ b/app/icons/llm-icons/claude.svg
@@ -0,0 +1,8 @@
+
\ No newline at end of file
diff --git a/app/icons/llm-icons/deepseek.svg b/app/icons/llm-icons/deepseek.svg
new file mode 100644
index 000000000..30440e316
--- /dev/null
+++ b/app/icons/llm-icons/deepseek.svg
@@ -0,0 +1,8 @@
+
\ No newline at end of file
diff --git a/app/icons/llm-icons/default.svg b/app/icons/llm-icons/default.svg
new file mode 100644
index 000000000..2ebff6b3f
--- /dev/null
+++ b/app/icons/llm-icons/default.svg
@@ -0,0 +1,27 @@
+
\ No newline at end of file
diff --git a/app/icons/llm-icons/doubao.svg b/app/icons/llm-icons/doubao.svg
new file mode 100644
index 000000000..79b1b822a
--- /dev/null
+++ b/app/icons/llm-icons/doubao.svg
@@ -0,0 +1,14 @@
+
\ No newline at end of file
diff --git a/app/icons/llm-icons/gemini.svg b/app/icons/llm-icons/gemini.svg
new file mode 100644
index 000000000..587669135
--- /dev/null
+++ b/app/icons/llm-icons/gemini.svg
@@ -0,0 +1,15 @@
+
\ No newline at end of file
diff --git a/app/icons/llm-icons/gemma.svg b/app/icons/llm-icons/gemma.svg
new file mode 100644
index 000000000..daf1a035c
--- /dev/null
+++ b/app/icons/llm-icons/gemma.svg
@@ -0,0 +1,15 @@
+
\ No newline at end of file
diff --git a/app/icons/llm-icons/grok.svg b/app/icons/llm-icons/grok.svg
new file mode 100644
index 000000000..8125cd610
--- /dev/null
+++ b/app/icons/llm-icons/grok.svg
@@ -0,0 +1,8 @@
+
\ No newline at end of file
diff --git a/app/icons/llm-icons/hunyuan.svg b/app/icons/llm-icons/hunyuan.svg
new file mode 100644
index 000000000..f67930c98
--- /dev/null
+++ b/app/icons/llm-icons/hunyuan.svg
@@ -0,0 +1,17 @@
+
\ No newline at end of file
diff --git a/app/icons/llm-icons/meta.svg b/app/icons/llm-icons/meta.svg
new file mode 100644
index 000000000..75dc40df7
--- /dev/null
+++ b/app/icons/llm-icons/meta.svg
@@ -0,0 +1,93 @@
+
\ No newline at end of file
diff --git a/app/icons/llm-icons/mistral.svg b/app/icons/llm-icons/mistral.svg
new file mode 100644
index 000000000..e577faca5
--- /dev/null
+++ b/app/icons/llm-icons/mistral.svg
@@ -0,0 +1,15 @@
+
\ No newline at end of file
diff --git a/app/icons/llm-icons/moonshot.svg b/app/icons/llm-icons/moonshot.svg
new file mode 100644
index 000000000..5206e0f12
--- /dev/null
+++ b/app/icons/llm-icons/moonshot.svg
@@ -0,0 +1,8 @@
+
\ No newline at end of file
diff --git a/app/icons/llm-icons/openai.svg b/app/icons/llm-icons/openai.svg
new file mode 100644
index 000000000..564cd5e87
--- /dev/null
+++ b/app/icons/llm-icons/openai.svg
@@ -0,0 +1,8 @@
+
\ No newline at end of file
diff --git a/app/icons/llm-icons/qwen.svg b/app/icons/llm-icons/qwen.svg
new file mode 100644
index 000000000..857ce2186
--- /dev/null
+++ b/app/icons/llm-icons/qwen.svg
@@ -0,0 +1,14 @@
+
\ No newline at end of file
diff --git a/app/icons/llm-icons/wenxin.svg b/app/icons/llm-icons/wenxin.svg
new file mode 100644
index 000000000..0030b0e01
--- /dev/null
+++ b/app/icons/llm-icons/wenxin.svg
@@ -0,0 +1,18 @@
+
\ No newline at end of file
diff --git a/app/locales/cn.ts b/app/locales/cn.ts
index 39498f662..81b609cde 100644
--- a/app/locales/cn.ts
+++ b/app/locales/cn.ts
@@ -496,6 +496,17 @@ const cn = {
SubTitle: "样例:",
},
},
+ SiliconFlow: {
+ ApiKey: {
+ Title: "接口密钥",
+ SubTitle: "使用自定义硅基流动 API Key",
+ Placeholder: "硅基流动 API Key",
+ },
+ Endpoint: {
+ Title: "接口地址",
+ SubTitle: "样例:",
+ },
+ },
Stability: {
ApiKey: {
Title: "接口密钥",
diff --git a/app/locales/da.ts b/app/locales/da.ts
new file mode 100644
index 000000000..7090b062b
--- /dev/null
+++ b/app/locales/da.ts
@@ -0,0 +1,832 @@
+import { getClientConfig } from "../config/client";
+import { SubmitKey } from "../store/config";
+import { SAAS_CHAT_UTM_URL } from "@/app/constant";
+import { PartialLocaleType } from "./index";
+
+const isApp = !!getClientConfig()?.isApp;
+const da: PartialLocaleType = {
+ WIP: "Der kommer snart mere...",
+ Error: {
+ Unauthorized: isApp
+ ? `Hov, der skete en fejl. Sådan kan du komme videre:
+ \\ 1️⃣ Er du ny her? [Tryk for at starte nu 🚀](${SAAS_CHAT_UTM_URL})
+ \\ 2️⃣ Vil du bruge dine egne OpenAI-nøgler? [Tryk her](/#/settings) for at ændre indstillinger ⚙️`
+ : `Hov, der skete en fejl. Lad os løse det:
+ \\ 1️⃣ Er du ny her? [Tryk for at starte nu 🚀](${SAAS_CHAT_UTM_URL})
+ \\ 2️⃣ Bruger du en privat opsætning? [Tryk her](/#/auth) for at taste din nøgle 🔑
+ \\ 3️⃣ Vil du bruge dine egne OpenAI-nøgler? [Tryk her](/#/settings) for at ændre indstillinger ⚙️
+ `,
+ },
+ Auth: {
+ Return: "Tilbage",
+ Title: "Adgangskode",
+ Tips: "Skriv venligst koden herunder",
+ SubTips: "Eller brug din egen OpenAI- eller Google-nøgle",
+ Input: "Adgangskode",
+ Confirm: "OK",
+ Later: "Senere",
+ SaasTips: "Hvis det er for svært, kan du starte nu",
+ },
+ ChatItem: {
+ ChatItemCount: (count: number) => `${count} beskeder`,
+ },
+ Chat: {
+ SubTitle: (count: number) => `${count} beskeder`,
+ EditMessage: {
+ Title: "Rediger beskeder",
+ Topic: {
+ Title: "Emne",
+ SubTitle: "Skift emne for denne chat",
+ },
+ },
+ Actions: {
+ ChatList: "Gå til chatliste",
+ CompressedHistory: "Komprimeret historie",
+ Export: "Eksporter alle beskeder som Markdown",
+ Copy: "Kopiér",
+ Stop: "Stop",
+ Retry: "Prøv igen",
+ Pin: "Fastgør",
+ PinToastContent: "1 besked er nu fastgjort",
+ PinToastAction: "Se",
+ Delete: "Slet",
+ Edit: "Rediger",
+ FullScreen: "Fuld skærm",
+ RefreshTitle: "Opdatér titel",
+ RefreshToast: "Anmodning om ny titel sendt",
+ Speech: "Afspil",
+ StopSpeech: "Stop",
+ },
+ Commands: {
+ new: "Ny chat",
+ newm: "Ny chat med persona",
+ next: "Næste chat",
+ prev: "Forrige chat",
+ clear: "Ryd alt før",
+ fork: "Kopiér chat",
+ del: "Slet chat",
+ },
+ InputActions: {
+ Stop: "Stop",
+ ToBottom: "Ned til nyeste",
+ Theme: {
+ auto: "Automatisk",
+ light: "Lyst tema",
+ dark: "Mørkt tema",
+ },
+ Prompt: "Prompts",
+ Masks: "Personaer",
+ Clear: "Ryd kontekst",
+ Settings: "Indstillinger",
+ UploadImage: "Upload billeder",
+ },
+ Rename: "Omdøb chat",
+ Typing: "Skriver…",
+ Input: (submitKey: string) => {
+ let inputHints = `${submitKey} for at sende`;
+ if (submitKey === String(SubmitKey.Enter)) {
+ inputHints += ", Shift + Enter for ny linje";
+ }
+ return (
+ inputHints + ", / for at søge i prompts, : for at bruge kommandoer"
+ );
+ },
+ Send: "Send",
+ StartSpeak: "Start oplæsning",
+ StopSpeak: "Stop oplæsning",
+ Config: {
+ Reset: "Nulstil til standard",
+ SaveAs: "Gem som persona",
+ },
+ IsContext: "Ekstra prompt til baggrund",
+ ShortcutKey: {
+ Title: "Hurtigtaster",
+ newChat: "Åbn ny chat",
+ focusInput: "Fokus på tekstfeltet",
+ copyLastMessage: "Kopiér sidste svar",
+ copyLastCode: "Kopiér sidste kodeblok",
+ showShortcutKey: "Vis hurtigtaster",
+ clearContext: "Ryd kontekst",
+ },
+ },
+ Export: {
+ Title: "Eksportér beskeder",
+ Copy: "Kopiér alt",
+ Download: "Download",
+ MessageFromYou: "Fra dig",
+ MessageFromChatGPT: "Fra ChatGPT",
+ Share: "Del til ShareGPT",
+ Format: {
+ Title: "Filformat",
+ SubTitle: "Vælg enten Markdown eller PNG-billede",
+ },
+ IncludeContext: {
+ Title: "Tag baggrund med",
+ SubTitle: "Skal ekstra baggrund (persona) med i eksporten?",
+ },
+ Steps: {
+ Select: "Vælg",
+ Preview: "Forhåndsvis",
+ },
+ Image: {
+ Toast: "Laver billede...",
+ Modal: "Tryk længe eller højreklik for at gemme",
+ },
+ Artifacts: {
+ Title: "Del side",
+ Error: "Fejl ved deling",
+ },
+ },
+ Select: {
+ Search: "Søg",
+ All: "Vælg alle",
+ Latest: "Vælg nyeste",
+ Clear: "Ryd alt",
+ },
+ Memory: {
+ Title: "Huskesætning",
+ EmptyContent: "Ingenting lige nu.",
+ Send: "Send huskesætning",
+ Copy: "Kopiér huskesætning",
+ Reset: "Nulstil chat",
+ ResetConfirm:
+ "Dette sletter nuværende samtale og hukommelse. Er du sikker?",
+ },
+ Home: {
+ NewChat: "Ny Chat",
+ DeleteChat: "Vil du slette den valgte chat?",
+ DeleteToast: "Chat slettet",
+ Revert: "Fortryd",
+ },
+ Settings: {
+ Title: "Indstillinger",
+ SubTitle: "Alle indstillinger",
+ ShowPassword: "Vis kodeord",
+ Danger: {
+ Reset: {
+ Title: "Nulstil alle indstillinger",
+ SubTitle: "Gendan alt til standard",
+ Action: "Nulstil",
+ Confirm: "Vil du virkelig nulstille alt?",
+ },
+ Clear: {
+ Title: "Slet alle data",
+ SubTitle: "Sletter alt om beskeder og indstillinger",
+ Action: "Slet",
+ Confirm: "Er du sikker på, at du vil slette alt?",
+ },
+ },
+ Lang: {
+ Name: "Language",
+ All: "Alle sprog",
+ },
+ Avatar: "Avatar",
+ FontSize: {
+ Title: "Skriftstørrelse",
+ SubTitle: "Vælg, hvor stor teksten skal være",
+ },
+ FontFamily: {
+ Title: "Skrifttype",
+ SubTitle: "Hvis tom, bruger den standard skrifttype",
+ Placeholder: "Skrifttype-navn",
+ },
+ InjectSystemPrompts: {
+ Title: "Tilføj system-prompt",
+ SubTitle: "Læg altid en ekstra prompt først i anmodninger",
+ },
+ InputTemplate: {
+ Title: "Tekstskabelon",
+ SubTitle: "Den seneste besked placeres i denne skabelon",
+ },
+ Update: {
+ Version: (x: string) => `Version: ${x}`,
+ IsLatest: "Du har nyeste version",
+ CheckUpdate: "Tjek efter opdatering",
+ IsChecking: "Tjekker...",
+ FoundUpdate: (x: string) => `Ny version fundet: ${x}`,
+ GoToUpdate: "Opdatér",
+ Success: "Opdatering lykkedes.",
+ Failed: "Opdatering mislykkedes.",
+ },
+ SendKey: "Tast for send",
+ Theme: "Tema",
+ TightBorder: "Stram kant",
+ SendPreviewBubble: {
+ Title: "Forhåndsvisnings-boble",
+ SubTitle: "Vis tekst, før den sendes",
+ },
+ AutoGenerateTitle: {
+ Title: "Lav titel automatisk",
+ SubTitle: "Foreslå en titel ud fra chatten",
+ },
+ Sync: {
+ CloudState: "Seneste opdatering",
+ NotSyncYet: "Endnu ikke synkroniseret",
+ Success: "Synkronisering lykkedes",
+ Fail: "Synkronisering mislykkedes",
+ Config: {
+ Modal: {
+ Title: "Indstil synk",
+ Check: "Tjek forbindelse",
+ },
+ SyncType: {
+ Title: "Synk-type",
+ SubTitle: "Vælg en synk-tjeneste",
+ },
+ Proxy: {
+ Title: "Aktivér proxy",
+ SubTitle: "Brug proxy for at undgå netværksproblemer",
+ },
+ ProxyUrl: {
+ Title: "Proxy-adresse",
+ SubTitle: "Bruges kun til projektets egen proxy",
+ },
+ WebDav: {
+ Endpoint: "WebDAV-adresse",
+ UserName: "Brugernavn",
+ Password: "Kodeord",
+ },
+ UpStash: {
+ Endpoint: "UpStash Redis REST URL",
+ UserName: "Backup-navn",
+ Password: "UpStash Redis REST Token",
+ },
+ },
+ LocalState: "Lokale data",
+ Overview: (overview: any) =>
+ `${overview.chat} chats, ${overview.message} beskeder, ${overview.prompt} prompts, ${overview.mask} personaer`,
+ ImportFailed: "Import mislykkedes",
+ },
+ Mask: {
+ Splash: {
+ Title: "Persona-forside",
+ SubTitle: "Vis denne side, når du opretter ny chat",
+ },
+ Builtin: {
+ Title: "Skjul indbyggede personaer",
+ SubTitle: "Vis ikke de indbyggede personaer i listen",
+ },
+ },
+ Prompt: {
+ Disable: {
+ Title: "Slå auto-forslag fra",
+ SubTitle: "Tast / for at få forslag",
+ },
+ List: "Prompt-liste",
+ ListCount: (builtin: number, custom: number) =>
+ `${builtin} indbygget, ${custom} brugerdefineret`,
+ Edit: "Rediger",
+ Modal: {
+ Title: "Prompt-liste",
+ Add: "Tilføj",
+ Search: "Søg prompts",
+ },
+ EditModal: {
+ Title: "Rediger prompt",
+ },
+ },
+ HistoryCount: {
+ Title: "Antal beskeder, der følger med",
+ SubTitle: "Hvor mange af de tidligere beskeder, der sendes hver gang",
+ },
+ CompressThreshold: {
+ Title: "Komprimeringsgrænse",
+ SubTitle:
+ "Hvis chatten bliver for lang, vil den komprimeres efter dette antal tegn",
+ },
+ Usage: {
+ Title: "Brug og saldo",
+ SubTitle(used: any, total: any) {
+ return `Du har brugt $${used} i denne måned, og din grænse er $${total}.`;
+ },
+ IsChecking: "Tjekker...",
+ Check: "Tjek igen",
+ NoAccess: "Indtast API-nøgle for at se forbrug",
+ },
+ Access: {
+ AccessCode: {
+ Title: "Adgangskode",
+ SubTitle: "Adgangskontrol er slået til",
+ Placeholder: "Skriv kode her",
+ },
+ CustomEndpoint: {
+ Title: "Brugerdefineret adresse",
+ SubTitle: "Brug Azure eller OpenAI fra egen server",
+ },
+ Provider: {
+ Title: "Model-udbyder",
+ SubTitle: "Vælg Azure eller OpenAI",
+ },
+ OpenAI: {
+ ApiKey: {
+ Title: "OpenAI API-nøgle",
+ SubTitle: "Brug din egen nøgle",
+ Placeholder: "sk-xxx",
+ },
+ Endpoint: {
+ Title: "OpenAI Endpoint",
+ SubTitle: "Skal starte med http(s):// eller /api/openai som standard",
+ },
+ },
+ Azure: {
+ ApiKey: {
+ Title: "Azure Api Key",
+ SubTitle: "Hent din nøgle fra Azure-portalen",
+ Placeholder: "Azure Api Key",
+ },
+ Endpoint: {
+ Title: "Azure Endpoint",
+ SubTitle: "F.eks.: ",
+ },
+ ApiVerion: {
+ Title: "Azure Api Version",
+ SubTitle: "Hentet fra Azure-portalen",
+ },
+ },
+ Anthropic: {
+ ApiKey: {
+ Title: "Anthropic API-nøgle",
+ SubTitle: "Brug din egen Anthropic-nøgle",
+ Placeholder: "Anthropic API Key",
+ },
+ Endpoint: {
+ Title: "Endpoint-adresse",
+ SubTitle: "F.eks.: ",
+ },
+ ApiVerion: {
+ Title: "API-version (Claude)",
+ SubTitle: "Vælg den ønskede version",
+ },
+ },
+ Baidu: {
+ ApiKey: {
+ Title: "Baidu-nøgle",
+ SubTitle: "Din egen Baidu-nøgle",
+ Placeholder: "Baidu API Key",
+ },
+ SecretKey: {
+ Title: "Baidu hemmelig nøgle",
+ SubTitle: "Din egen hemmelige nøgle fra Baidu",
+ Placeholder: "Baidu Secret Key",
+ },
+ Endpoint: {
+ Title: "Adresse",
+ SubTitle: "Kan ikke ændres, se .env",
+ },
+ },
+ Tencent: {
+ ApiKey: {
+ Title: "Tencent-nøgle",
+ SubTitle: "Din egen nøgle fra Tencent",
+ Placeholder: "Tencent API Key",
+ },
+ SecretKey: {
+ Title: "Tencent hemmelig nøgle",
+ SubTitle: "Din egen hemmelige nøgle fra Tencent",
+ Placeholder: "Tencent Secret Key",
+ },
+ Endpoint: {
+ Title: "Adresse",
+ SubTitle: "Kan ikke ændres, se .env",
+ },
+ },
+ ByteDance: {
+ ApiKey: {
+ Title: "ByteDance-nøgle",
+ SubTitle: "Din egen nøgle til ByteDance",
+ Placeholder: "ByteDance API Key",
+ },
+ Endpoint: {
+ Title: "Adresse",
+ SubTitle: "F.eks.: ",
+ },
+ },
+ Alibaba: {
+ ApiKey: {
+ Title: "Alibaba-nøgle",
+ SubTitle: "Din egen Alibaba Cloud-nøgle",
+ Placeholder: "Alibaba Cloud API Key",
+ },
+ Endpoint: {
+ Title: "Adresse",
+ SubTitle: "F.eks.: ",
+ },
+ },
+ Moonshot: {
+ ApiKey: {
+ Title: "Moonshot-nøgle",
+ SubTitle: "Din egen Moonshot-nøgle",
+ Placeholder: "Moonshot API Key",
+ },
+ Endpoint: {
+ Title: "Adresse",
+ SubTitle: "F.eks.: ",
+ },
+ },
+ DeepSeek: {
+ ApiKey: {
+ Title: "DeepSeek-nøgle",
+ SubTitle: "Din egen DeepSeek-nøgle",
+ Placeholder: "DeepSeek API Key",
+ },
+ Endpoint: {
+ Title: "Adresse",
+ SubTitle: "F.eks.: ",
+ },
+ },
+ XAI: {
+ ApiKey: {
+ Title: "XAI-nøgle",
+ SubTitle: "Din egen XAI-nøgle",
+ Placeholder: "XAI API Key",
+ },
+ Endpoint: {
+ Title: "Adresse",
+ SubTitle: "F.eks.: ",
+ },
+ },
+ ChatGLM: {
+ ApiKey: {
+ Title: "ChatGLM-nøgle",
+ SubTitle: "Din egen ChatGLM-nøgle",
+ Placeholder: "ChatGLM API Key",
+ },
+ Endpoint: {
+ Title: "Adresse",
+ SubTitle: "F.eks.: ",
+ },
+ },
+ SiliconFlow: {
+ ApiKey: {
+ Title: "SiliconFlow-nøgle",
+ SubTitle: "Din egen SiliconFlow-nøgle",
+ Placeholder: "SiliconFlow API Key",
+ },
+ Endpoint: {
+ Title: "Adresse",
+ SubTitle: "F.eks.: ",
+ },
+ },
+ Stability: {
+ ApiKey: {
+ Title: "Stability-nøgle",
+ SubTitle: "Din egen Stability-nøgle",
+ Placeholder: "Stability API Key",
+ },
+ Endpoint: {
+ Title: "Adresse",
+ SubTitle: "F.eks.: ",
+ },
+ },
+ Iflytek: {
+ ApiKey: {
+ Title: "Iflytek API Key",
+ SubTitle: "Nøgle fra Iflytek",
+ Placeholder: "Iflytek API Key",
+ },
+ ApiSecret: {
+ Title: "Iflytek hemmelig nøgle",
+ SubTitle: "Hentet fra Iflytek",
+ Placeholder: "Iflytek API Secret",
+ },
+ Endpoint: {
+ Title: "Adresse",
+ SubTitle: "F.eks.: ",
+ },
+ },
+ CustomModel: {
+ Title: "Egne modelnavne",
+ SubTitle: "Skriv komma-adskilte navne",
+ },
+ Google: {
+ ApiKey: {
+ Title: "Google-nøgle",
+ SubTitle: "Få din nøgle hos Google AI",
+ Placeholder: "Google AI API Key",
+ },
+ Endpoint: {
+ Title: "Adresse",
+ SubTitle: "F.eks.: ",
+ },
+ ApiVersion: {
+ Title: "API-version (til gemini-pro)",
+ SubTitle: "Vælg en bestemt version",
+ },
+ GoogleSafetySettings: {
+ Title: "Google sikkerhedsindstillinger",
+ SubTitle: "Vælg et niveau for indholdskontrol",
+ },
+ },
+ },
+ Model: "Model",
+ CompressModel: {
+ Title: "Opsummeringsmodel",
+ SubTitle: "Bruges til at korte historik ned og lave titel",
+ },
+ Temperature: {
+ Title: "Temperatur",
+ SubTitle: "Jo højere tal, jo mere kreativt svar",
+ },
+ TopP: {
+ Title: "Top P",
+ SubTitle: "Skal ikke ændres sammen med temperatur",
+ },
+ MaxTokens: {
+ Title: "Maks. længde",
+ SubTitle: "Hvor mange tokens (ord/stykker tekst) der kan bruges",
+ },
+ PresencePenalty: {
+ Title: "Nye emner",
+ SubTitle: "Jo højere tal, jo mere nyt indhold",
+ },
+ FrequencyPenalty: {
+ Title: "Gentagelsesstraf",
+ SubTitle: "Jo højere tal, jo mindre gentagelse",
+ },
+ TTS: {
+ Enable: {
+ Title: "Tænd for oplæsning (TTS)",
+ SubTitle: "Slå tekst-til-tale til",
+ },
+ Autoplay: {
+ Title: "Automatisk oplæsning",
+ SubTitle: "Laver lyd automatisk, hvis TTS er slået til",
+ },
+ Model: "Model",
+ Voice: {
+ Title: "Stemme",
+ SubTitle: "Hvilken stemme der bruges til lyd",
+ },
+ Speed: {
+ Title: "Hastighed",
+ SubTitle: "Hvor hurtigt der oplæses",
+ },
+ Engine: "TTS-motor",
+ },
+ Realtime: {
+ Enable: {
+ Title: "Live-chat",
+ SubTitle: "Slå live-svar til",
+ },
+ Provider: {
+ Title: "Modeludbyder",
+ SubTitle: "Vælg forskellig udbyder",
+ },
+ Model: {
+ Title: "Model",
+ SubTitle: "Vælg en model",
+ },
+ ApiKey: {
+ Title: "API-nøgle",
+ SubTitle: "Din nøgle",
+ Placeholder: "API-nøgle",
+ },
+ Azure: {
+ Endpoint: {
+ Title: "Adresse",
+ SubTitle: "Endpoint til Azure",
+ },
+ Deployment: {
+ Title: "Udrulningsnavn",
+ SubTitle: "Navn for dit Azure-setup",
+ },
+ },
+ Temperature: {
+ Title: "Temperatur",
+ SubTitle: "Højere tal = mere varierede svar",
+ },
+ },
+ },
+ Store: {
+ DefaultTopic: "Ny samtale",
+ BotHello: "Hej! Hvordan kan jeg hjælpe dig i dag?",
+ Error: "Noget gik galt. Prøv igen senere.",
+ Prompt: {
+ History: (content: string) =>
+ "Her er et kort resume af, hvad vi har snakket om: " + content,
+ Topic:
+ "Find en kort overskrift med 4-5 ord om emnet. Ingen tegnsætning eller anførselstegn.",
+ Summarize:
+ "Skriv et kort resumé (under 200 ord) af vores samtale til senere brug.",
+ },
+ },
+ Copy: {
+ Success: "Kopieret",
+ Failed: "Kunne ikke kopiere. Giv adgang til udklipsholder.",
+ },
+ Download: {
+ Success: "Filen er downloadet.",
+ Failed: "Download fejlede.",
+ },
+ Context: {
+ Toast: (x: any) => `Inkluderer ${x} ekstra prompts`,
+ Edit: "Chatindstillinger",
+ Add: "Tilføj prompt",
+ Clear: "Kontekst ryddet",
+ Revert: "Fortryd",
+ },
+ Discovery: {
+ Name: "Søgning og plugins",
+ },
+ Mcp: {
+ Name: "MCP",
+ },
+ FineTuned: {
+ Sysmessage: "Du er en hjælper, der skal...",
+ },
+ SearchChat: {
+ Name: "Søg",
+ Page: {
+ Title: "Søg i tidligere chats",
+ Search: "Skriv her for at søge",
+ NoResult: "Ingen resultater",
+ NoData: "Ingen data",
+ Loading: "Henter...",
+ SubTitle: (count: number) => `Fandt ${count} resultater`,
+ },
+ Item: {
+ View: "Vis",
+ },
+ },
+ Plugin: {
+ Name: "Plugin",
+ Page: {
+ Title: "Plugins",
+ SubTitle: (count: number) => `${count} plugins`,
+ Search: "Søg plugin",
+ Create: "Opret nyt",
+ Find: "Du kan finde flere plugins på GitHub: ",
+ },
+ Item: {
+ Info: (count: number) => `${count} metode`,
+ View: "Vis",
+ Edit: "Rediger",
+ Delete: "Slet",
+ DeleteConfirm: "Vil du slette?",
+ },
+ Auth: {
+ None: "Ingen",
+ Basic: "Basic",
+ Bearer: "Bearer",
+ Custom: "Tilpasset",
+ CustomHeader: "Parameternavn",
+ Token: "Token",
+ Proxy: "Brug Proxy",
+ ProxyDescription: "Løs CORS-problemer med Proxy",
+ Location: "Sted",
+ LocationHeader: "Header",
+ LocationQuery: "Query",
+ LocationBody: "Body",
+ },
+ EditModal: {
+ Title: (readonly: boolean) =>
+ `Rediger Plugin ${readonly ? "(skrivebeskyttet)" : ""}`,
+ Download: "Download",
+ Auth: "Godkendelsestype",
+ Content: "OpenAPI Schema",
+ Load: "Hent fra URL",
+ Method: "Metode",
+ Error: "Fejl i OpenAPI Schema",
+ },
+ },
+ Mask: {
+ Name: "Persona",
+ Page: {
+ Title: "Prompts som personaer",
+ SubTitle: (count: number) => `${count} skabeloner`,
+ Search: "Søg skabeloner",
+ Create: "Opret ny",
+ },
+ Item: {
+ Info: (count: number) => `${count} prompts`,
+ Chat: "Chat",
+ View: "Vis",
+ Edit: "Rediger",
+ Delete: "Slet",
+ DeleteConfirm: "Vil du slette?",
+ },
+ EditModal: {
+ Title: (readonly: boolean) =>
+ `Rediger skabelon ${readonly ? "(skrivebeskyttet)" : ""}`,
+ Download: "Download",
+ Clone: "Klon",
+ },
+ Config: {
+ Avatar: "Chat-avatar",
+ Name: "Chat-navn",
+ Sync: {
+ Title: "Brug globale indstillinger",
+ SubTitle: "Gældende for denne chat",
+ Confirm: "Erstat nuværende indstillinger med globale?",
+ },
+ HideContext: {
+ Title: "Skjul ekstra prompts",
+ SubTitle: "Vis dem ikke på chat-skærmen",
+ },
+ Artifacts: {
+ Title: "Brug Artefakter",
+ SubTitle: "Gør det muligt at vise HTML-sider",
+ },
+ CodeFold: {
+ Title: "Fold kode sammen",
+ SubTitle: "Luk/åbn lange kodestykker automatisk",
+ },
+ Share: {
+ Title: "Del denne persona",
+ SubTitle: "Få et link til denne skabelon",
+ Action: "Kopiér link",
+ },
+ },
+ },
+ NewChat: {
+ Return: "Tilbage",
+ Skip: "Start straks",
+ Title: "Vælg en persona",
+ SubTitle: "Chat med den persona, du vælger",
+ More: "Se flere",
+ NotShow: "Vis ikke igen",
+ ConfirmNoShow:
+ "Er du sikker på, at du ikke vil se det igen? Du kan altid slå det til under indstillinger.",
+ },
+ UI: {
+ Confirm: "OK",
+ Cancel: "Fortryd",
+ Close: "Luk",
+ Create: "Opret",
+ Edit: "Rediger",
+ Export: "Eksporter",
+ Import: "Importér",
+ Sync: "Synk",
+ Config: "Konfigurer",
+ },
+ Exporter: {
+ Description: {
+ Title: "Kun beskeder efter sidste rydning vises",
+ },
+ Model: "Model",
+ Messages: "Beskeder",
+ Topic: "Emne",
+ Time: "Tid",
+ },
+ URLCommand: {
+ Code: "Så ud til, at der var en kode i linket. Vil du bruge den?",
+ Settings: "Så ud til, at der var indstillinger i linket. Vil du bruge dem?",
+ },
+ SdPanel: {
+ Prompt: "Prompt",
+ NegativePrompt: "Negativ prompt",
+ PleaseInput: (name: string) => `Indtast: ${name}`,
+ AspectRatio: "Billedformat",
+ ImageStyle: "Stil",
+ OutFormat: "Uddataformat",
+ AIModel: "AI-model",
+ ModelVersion: "Version",
+ Submit: "Send",
+ ParamIsRequired: (name: string) => `${name} er krævet`,
+ Styles: {
+ D3Model: "3d-model",
+ AnalogFilm: "analog-film",
+ Anime: "anime",
+ Cinematic: "cinematisk",
+ ComicBook: "tegneserie",
+ DigitalArt: "digital-art",
+ Enhance: "enhance",
+ FantasyArt: "fantasy-art",
+ Isometric: "isometric",
+ LineArt: "line-art",
+ LowPoly: "low-poly",
+ ModelingCompound: "modeling-compound",
+ NeonPunk: "neon-punk",
+ Origami: "origami",
+ Photographic: "fotografisk",
+ PixelArt: "pixel-art",
+ TileTexture: "tile-texture",
+ },
+ },
+ Sd: {
+ SubTitle: (count: number) => `${count} billeder`,
+ Actions: {
+ Params: "Se indstillinger",
+ Copy: "Kopiér prompt",
+ Delete: "Slet",
+ Retry: "Prøv igen",
+ ReturnHome: "Til forsiden",
+ History: "Historik",
+ },
+ EmptyRecord: "Ingen billeder endnu",
+ Status: {
+ Name: "Status",
+ Success: "Ok",
+ Error: "Fejl",
+ Wait: "Venter",
+ Running: "I gang",
+ },
+ Danger: {
+ Delete: "Vil du slette?",
+ },
+ GenerateParams: "Genereringsvalg",
+ Detail: "Detaljer",
+ },
+};
+
+export default da;
diff --git a/app/locales/en.ts b/app/locales/en.ts
index 8c2c19f18..8fecf8bf7 100644
--- a/app/locales/en.ts
+++ b/app/locales/en.ts
@@ -480,6 +480,17 @@ const en: LocaleType = {
SubTitle: "Example: ",
},
},
+ SiliconFlow: {
+ ApiKey: {
+ Title: "SiliconFlow API Key",
+ SubTitle: "Use a custom SiliconFlow API Key",
+ Placeholder: "SiliconFlow API Key",
+ },
+ Endpoint: {
+ Title: "Endpoint Address",
+ SubTitle: "Example: ",
+ },
+ },
Stability: {
ApiKey: {
Title: "Stability API Key",
diff --git a/app/locales/index.ts b/app/locales/index.ts
index c8eb64df6..43b17ae81 100644
--- a/app/locales/index.ts
+++ b/app/locales/index.ts
@@ -2,6 +2,7 @@ import cn from "./cn";
import en from "./en";
import pt from "./pt";
import tw from "./tw";
+import da from "./da";
import id from "./id";
import fr from "./fr";
import es from "./es";
@@ -30,6 +31,7 @@ const ALL_LANGS = {
en,
tw,
pt,
+ da,
jp,
ko,
id,
@@ -56,6 +58,7 @@ export const ALL_LANG_OPTIONS: Record = {
en: "English",
pt: "Português",
tw: "繁體中文",
+ da: "Dansk",
jp: "日本語",
ko: "한국어",
id: "Indonesia",
@@ -141,6 +144,7 @@ export const STT_LANG_MAP: Record = {
en: "en-US",
pt: "pt-BR",
tw: "zh-TW",
+ da: "da-DK",
jp: "ja-JP",
ko: "ko-KR",
id: "id-ID",
diff --git a/app/store/access.ts b/app/store/access.ts
index e2b0a84b8..d5387e747 100644
--- a/app/store/access.ts
+++ b/app/store/access.ts
@@ -16,6 +16,7 @@ import {
DEEPSEEK_BASE_URL,
XAI_BASE_URL,
CHATGLM_BASE_URL,
+ SILICONFLOW_BASE_URL,
} from "../constant";
import { getHeaders } from "../client/api";
import { getClientConfig } from "../config/client";
@@ -54,6 +55,10 @@ const DEFAULT_XAI_URL = isApp ? XAI_BASE_URL : ApiPath.XAI;
const DEFAULT_CHATGLM_URL = isApp ? CHATGLM_BASE_URL : ApiPath.ChatGLM;
+const DEFAULT_SILICONFLOW_URL = isApp
+ ? SILICONFLOW_BASE_URL
+ : ApiPath.SiliconFlow;
+
const DEFAULT_ACCESS_STATE = {
accessCode: "",
useCustomConfig: false,
@@ -123,6 +128,10 @@ const DEFAULT_ACCESS_STATE = {
chatglmUrl: DEFAULT_CHATGLM_URL,
chatglmApiKey: "",
+ // siliconflow
+ siliconflowUrl: DEFAULT_SILICONFLOW_URL,
+ siliconflowApiKey: "",
+
// server config
needCode: true,
hideUserApiKey: false,
@@ -207,6 +216,10 @@ export const useAccessStore = createPersistStore(
return ensure(get(), ["chatglmApiKey"]);
},
+ isValidSiliconFlow() {
+ return ensure(get(), ["siliconflowApiKey"]);
+ },
+
isAuthorized() {
this.fetch();
@@ -225,6 +238,7 @@ export const useAccessStore = createPersistStore(
this.isValidDeepSeek() ||
this.isValidXAI() ||
this.isValidChatGLM() ||
+ this.isValidSiliconFlow() ||
!this.enabledAccessControl() ||
(this.enabledAccessControl() && ensure(get(), ["accessCode"]))
);
diff --git a/app/utils.ts b/app/utils.ts
index f23378019..6183e03b0 100644
--- a/app/utils.ts
+++ b/app/utils.ts
@@ -2,7 +2,11 @@ import { useEffect, useState } from "react";
import { showToast } from "./components/ui-lib";
import Locale from "./locales";
import { RequestMessage } from "./client/api";
-import { ServiceProvider } from "./constant";
+import {
+ REQUEST_TIMEOUT_MS,
+ REQUEST_TIMEOUT_MS_FOR_THINKING,
+ ServiceProvider,
+} from "./constant";
// import { fetch as tauriFetch, ResponseType } from "@tauri-apps/api/http";
import { fetch as tauriStreamFetch } from "./utils/stream";
import { VISION_MODEL_REGEXES, EXCLUDE_VISION_MODEL_REGEXES } from "./constant";
@@ -292,6 +296,20 @@ export function isDalle3(model: string) {
return "dall-e-3" === model;
}
+export function getTimeoutMSByModel(model: string) {
+ model = model.toLowerCase();
+ if (
+ model.startsWith("dall-e") ||
+ model.startsWith("dalle") ||
+ model.startsWith("o1") ||
+ model.startsWith("o3") ||
+ model.includes("deepseek-r") ||
+ model.includes("-thinking")
+ )
+ return REQUEST_TIMEOUT_MS_FOR_THINKING;
+ return REQUEST_TIMEOUT_MS;
+}
+
export function getModelSizes(model: string): ModelSize[] {
if (isDalle3(model)) {
return ["1024x1024", "1792x1024", "1024x1792"];
diff --git a/app/utils/chat.ts b/app/utils/chat.ts
index c04d33cbf..cae775512 100644
--- a/app/utils/chat.ts
+++ b/app/utils/chat.ts
@@ -3,7 +3,7 @@ import {
UPLOAD_URL,
REQUEST_TIMEOUT_MS,
} from "@/app/constant";
-import { RequestMessage } from "@/app/client/api";
+import { MultimodalContent, RequestMessage } from "@/app/client/api";
import Locale from "@/app/locales";
import {
EventStreamContentType,
@@ -70,8 +70,9 @@ export function compressImage(file: Blob, maxSize: number): Promise {
});
}
-export async function preProcessImageContent(
+export async function preProcessImageContentBase(
content: RequestMessage["content"],
+ transformImageUrl: (url: string) => Promise<{ [key: string]: any }>,
) {
if (typeof content === "string") {
return content;
@@ -81,7 +82,7 @@ export async function preProcessImageContent(
if (part?.type == "image_url" && part?.image_url?.url) {
try {
const url = await cacheImageToBase64Image(part?.image_url?.url);
- result.push({ type: part.type, image_url: { url } });
+ result.push(await transformImageUrl(url));
} catch (error) {
console.error("Error processing image URL:", error);
}
@@ -92,6 +93,23 @@ export async function preProcessImageContent(
return result;
}
+export async function preProcessImageContent(
+ content: RequestMessage["content"],
+) {
+ return preProcessImageContentBase(content, async (url) => ({
+ type: "image_url",
+ image_url: { url },
+ })) as Promise;
+}
+
+export async function preProcessImageContentForAlibabaDashScope(
+ content: RequestMessage["content"],
+) {
+ return preProcessImageContentBase(content, async (url) => ({
+ image: url,
+ }));
+}
+
const imageCaches: Record = {};
export function cacheImageToBase64Image(imageUrl: string) {
if (imageUrl.includes(CACHE_URL_PREFIX)) {
@@ -400,6 +418,7 @@ export function streamWithThink(
let responseRes: Response;
let isInThinkingMode = false;
let lastIsThinking = false;
+ let lastIsThinkingTagged = false; //between and tags
// animate response to make it looks smooth
function animateResponseText() {
@@ -576,9 +595,26 @@ export function streamWithThink(
try {
const chunk = parseSSE(text, runTools);
// Skip if content is empty
- if (!chunk?.content || chunk.content.trim().length === 0) {
+ if (!chunk?.content || chunk.content.length === 0) {
return;
}
+
+ // deal with and tags start
+ if (!chunk.isThinking) {
+ if (chunk.content.startsWith("")) {
+ chunk.isThinking = true;
+ chunk.content = chunk.content.slice(7).trim();
+ lastIsThinkingTagged = true;
+ } else if (chunk.content.endsWith("")) {
+ chunk.isThinking = false;
+ chunk.content = chunk.content.slice(0, -8).trim();
+ lastIsThinkingTagged = false;
+ } else if (lastIsThinkingTagged) {
+ chunk.isThinking = true;
+ }
+ }
+ // deal with and tags start
+
// Check if thinking mode changed
const isThinkingChanged = lastIsThinking !== chunk.isThinking;
lastIsThinking = chunk.isThinking;