diff --git a/app/client/platforms/alibaba.ts b/app/client/platforms/alibaba.ts index 44dbd847a..88511768c 100644 --- a/app/client/platforms/alibaba.ts +++ b/app/client/platforms/alibaba.ts @@ -1,10 +1,5 @@ "use client"; -import { - ApiPath, - Alibaba, - ALIBABA_BASE_URL, - REQUEST_TIMEOUT_MS, -} from "@/app/constant"; +import { ApiPath, Alibaba, ALIBABA_BASE_URL } from "@/app/constant"; import { useAccessStore, useAppConfig, @@ -25,6 +20,7 @@ import { getClientConfig } from "@/app/config/client"; import { getMessageTextContent, getMessageTextContentWithoutThinking, + getTimeoutMSByModel, } from "@/app/utils"; import { fetch } from "@/app/utils/stream"; @@ -144,7 +140,7 @@ export class QwenApi implements LLMApi { // make a fetch request const requestTimeoutId = setTimeout( () => controller.abort(), - REQUEST_TIMEOUT_MS, + getTimeoutMSByModel(options.config.model), ); if (shouldStream) { @@ -199,8 +195,8 @@ export class QwenApi implements LLMApi { // Skip if both content and reasoning_content are empty or null if ( - (!reasoning || reasoning.trim().length === 0) && - (!content || content.trim().length === 0) + (!reasoning || reasoning.length === 0) && + (!content || content.length === 0) ) { return { isThinking: false, @@ -208,12 +204,12 @@ export class QwenApi implements LLMApi { }; } - if (reasoning && reasoning.trim().length > 0) { + if (reasoning && reasoning.length > 0) { return { isThinking: true, content: reasoning, }; - } else if (content && content.trim().length > 0) { + } else if (content && content.length > 0) { return { isThinking: false, content: content, diff --git a/app/client/platforms/baidu.ts b/app/client/platforms/baidu.ts index 9e8c2f139..dc990db41 100644 --- a/app/client/platforms/baidu.ts +++ b/app/client/platforms/baidu.ts @@ -1,10 +1,5 @@ "use client"; -import { - ApiPath, - Baidu, - BAIDU_BASE_URL, - REQUEST_TIMEOUT_MS, -} from "@/app/constant"; +import { ApiPath, Baidu, BAIDU_BASE_URL } from "@/app/constant"; import { useAccessStore, useAppConfig, useChatStore } from "@/app/store"; import { getAccessToken } from "@/app/utils/baidu"; @@ -23,7 +18,7 @@ import { } from "@fortaine/fetch-event-source"; import { prettyObject } from "@/app/utils/format"; import { getClientConfig } from "@/app/config/client"; -import { getMessageTextContent } from "@/app/utils"; +import { getMessageTextContent, getTimeoutMSByModel } from "@/app/utils"; import { fetch } from "@/app/utils/stream"; export interface OpenAIListModelResponse { @@ -155,7 +150,7 @@ export class ErnieApi implements LLMApi { // make a fetch request const requestTimeoutId = setTimeout( () => controller.abort(), - REQUEST_TIMEOUT_MS, + getTimeoutMSByModel(options.config.model), ); if (shouldStream) { diff --git a/app/client/platforms/bytedance.ts b/app/client/platforms/bytedance.ts index 5e2e63f58..f9524cba2 100644 --- a/app/client/platforms/bytedance.ts +++ b/app/client/platforms/bytedance.ts @@ -1,10 +1,5 @@ "use client"; -import { - ApiPath, - ByteDance, - BYTEDANCE_BASE_URL, - REQUEST_TIMEOUT_MS, -} from "@/app/constant"; +import { ApiPath, ByteDance, BYTEDANCE_BASE_URL } from "@/app/constant"; import { useAccessStore, useAppConfig, @@ -25,7 +20,10 @@ import { import { streamWithThink } from "@/app/utils/chat"; import { getClientConfig } from "@/app/config/client"; import { preProcessImageContent } from "@/app/utils/chat"; -import { getMessageTextContentWithoutThinking } from "@/app/utils"; +import { + getMessageTextContentWithoutThinking, + getTimeoutMSByModel, +} from "@/app/utils"; import { fetch } from "@/app/utils/stream"; export interface OpenAIListModelResponse { @@ -130,7 +128,7 @@ export class DoubaoApi implements LLMApi { // make a fetch request const requestTimeoutId = setTimeout( () => controller.abort(), - REQUEST_TIMEOUT_MS, + getTimeoutMSByModel(options.config.model), ); if (shouldStream) { @@ -184,8 +182,8 @@ export class DoubaoApi implements LLMApi { // Skip if both content and reasoning_content are empty or null if ( - (!reasoning || reasoning.trim().length === 0) && - (!content || content.trim().length === 0) + (!reasoning || reasoning.length === 0) && + (!content || content.length === 0) ) { return { isThinking: false, @@ -193,12 +191,12 @@ export class DoubaoApi implements LLMApi { }; } - if (reasoning && reasoning.trim().length > 0) { + if (reasoning && reasoning.length > 0) { return { isThinking: true, content: reasoning, }; - } else if (content && content.trim().length > 0) { + } else if (content && content.length > 0) { return { isThinking: false, content: content, diff --git a/app/client/platforms/deepseek.ts b/app/client/platforms/deepseek.ts index c436ae61d..b21d24cef 100644 --- a/app/client/platforms/deepseek.ts +++ b/app/client/platforms/deepseek.ts @@ -1,12 +1,6 @@ "use client"; // azure and openai, using same models. so using same LLMApi. -import { - ApiPath, - DEEPSEEK_BASE_URL, - DeepSeek, - REQUEST_TIMEOUT_MS, - REQUEST_TIMEOUT_MS_FOR_THINKING, -} from "@/app/constant"; +import { ApiPath, DEEPSEEK_BASE_URL, DeepSeek } from "@/app/constant"; import { useAccessStore, useAppConfig, @@ -26,6 +20,7 @@ import { getClientConfig } from "@/app/config/client"; import { getMessageTextContent, getMessageTextContentWithoutThinking, + getTimeoutMSByModel, } from "@/app/utils"; import { RequestPayload } from "./openai"; import { fetch } from "@/app/utils/stream"; @@ -116,16 +111,10 @@ export class DeepSeekApi implements LLMApi { headers: getHeaders(), }; - // console.log(chatPayload); - - const isR1 = - options.config.model.endsWith("-reasoner") || - options.config.model.endsWith("-r1"); - // make a fetch request const requestTimeoutId = setTimeout( () => controller.abort(), - isR1 ? REQUEST_TIMEOUT_MS_FOR_THINKING : REQUEST_TIMEOUT_MS, + getTimeoutMSByModel(options.config.model), ); if (shouldStream) { @@ -176,8 +165,8 @@ export class DeepSeekApi implements LLMApi { // Skip if both content and reasoning_content are empty or null if ( - (!reasoning || reasoning.trim().length === 0) && - (!content || content.trim().length === 0) + (!reasoning || reasoning.length === 0) && + (!content || content.length === 0) ) { return { isThinking: false, @@ -185,12 +174,12 @@ export class DeepSeekApi implements LLMApi { }; } - if (reasoning && reasoning.trim().length > 0) { + if (reasoning && reasoning.length > 0) { return { isThinking: true, content: reasoning, }; - } else if (content && content.trim().length > 0) { + } else if (content && content.length > 0) { return { isThinking: false, content: content, diff --git a/app/client/platforms/glm.ts b/app/client/platforms/glm.ts index a8d1869e3..98b10277d 100644 --- a/app/client/platforms/glm.ts +++ b/app/client/platforms/glm.ts @@ -1,10 +1,5 @@ "use client"; -import { - ApiPath, - CHATGLM_BASE_URL, - ChatGLM, - REQUEST_TIMEOUT_MS, -} from "@/app/constant"; +import { ApiPath, CHATGLM_BASE_URL, ChatGLM } from "@/app/constant"; import { useAccessStore, useAppConfig, @@ -21,7 +16,11 @@ import { SpeechOptions, } from "../api"; import { getClientConfig } from "@/app/config/client"; -import { getMessageTextContent, isVisionModel } from "@/app/utils"; +import { + getMessageTextContent, + isVisionModel, + getTimeoutMSByModel, +} from "@/app/utils"; import { RequestPayload } from "./openai"; import { fetch } from "@/app/utils/stream"; import { preProcessImageContent } from "@/app/utils/chat"; @@ -191,7 +190,7 @@ export class ChatGLMApi implements LLMApi { const requestTimeoutId = setTimeout( () => controller.abort(), - REQUEST_TIMEOUT_MS, + getTimeoutMSByModel(options.config.model), ); if (modelType === "image" || modelType === "video") { diff --git a/app/client/platforms/google.ts b/app/client/platforms/google.ts index 1e593dd42..654f0e3e4 100644 --- a/app/client/platforms/google.ts +++ b/app/client/platforms/google.ts @@ -1,9 +1,4 @@ -import { - ApiPath, - Google, - REQUEST_TIMEOUT_MS, - REQUEST_TIMEOUT_MS_FOR_THINKING, -} from "@/app/constant"; +import { ApiPath, Google } from "@/app/constant"; import { ChatOptions, getHeaders, @@ -27,6 +22,7 @@ import { getMessageTextContent, getMessageImages, isVisionModel, + getTimeoutMSByModel, } from "@/app/utils"; import { preProcessImageContent } from "@/app/utils/chat"; import { nanoid } from "nanoid"; @@ -206,7 +202,7 @@ export class GeminiProApi implements LLMApi { // make a fetch request const requestTimeoutId = setTimeout( () => controller.abort(), - isThinking ? REQUEST_TIMEOUT_MS_FOR_THINKING : REQUEST_TIMEOUT_MS, + getTimeoutMSByModel(options.config.model), ); if (shouldStream) { diff --git a/app/client/platforms/openai.ts b/app/client/platforms/openai.ts index 9d43c8161..c6f3fc425 100644 --- a/app/client/platforms/openai.ts +++ b/app/client/platforms/openai.ts @@ -8,7 +8,6 @@ import { Azure, REQUEST_TIMEOUT_MS, ServiceProvider, - REQUEST_TIMEOUT_MS_FOR_THINKING, } from "@/app/constant"; import { ChatMessageTool, @@ -42,6 +41,7 @@ import { getMessageTextContent, isVisionModel, isDalle3 as _isDalle3, + getTimeoutMSByModel, } from "@/app/utils"; import { fetch } from "@/app/utils/stream"; @@ -340,8 +340,8 @@ export class ChatGPTApi implements LLMApi { // Skip if both content and reasoning_content are empty or null if ( - (!reasoning || reasoning.trim().length === 0) && - (!content || content.trim().length === 0) + (!reasoning || reasoning.length === 0) && + (!content || content.length === 0) ) { return { isThinking: false, @@ -349,12 +349,12 @@ export class ChatGPTApi implements LLMApi { }; } - if (reasoning && reasoning.trim().length > 0) { + if (reasoning && reasoning.length > 0) { return { isThinking: true, content: reasoning, }; - } else if (content && content.trim().length > 0) { + } else if (content && content.length > 0) { return { isThinking: false, content: content, @@ -396,9 +396,7 @@ export class ChatGPTApi implements LLMApi { // make a fetch request const requestTimeoutId = setTimeout( () => controller.abort(), - isDalle3 || isO1OrO3 - ? REQUEST_TIMEOUT_MS_FOR_THINKING - : REQUEST_TIMEOUT_MS, // dalle3 using b64_json is slow. + getTimeoutMSByModel(options.config.model), ); const res = await fetch(chatPath, chatPayload); diff --git a/app/client/platforms/siliconflow.ts b/app/client/platforms/siliconflow.ts index 1ad316a61..92c0261c4 100644 --- a/app/client/platforms/siliconflow.ts +++ b/app/client/platforms/siliconflow.ts @@ -1,11 +1,6 @@ "use client"; // azure and openai, using same models. so using same LLMApi. -import { - ApiPath, - SILICONFLOW_BASE_URL, - SiliconFlow, - REQUEST_TIMEOUT_MS_FOR_THINKING, -} from "@/app/constant"; +import { ApiPath, SILICONFLOW_BASE_URL, SiliconFlow } from "@/app/constant"; import { useAccessStore, useAppConfig, @@ -25,6 +20,7 @@ import { getClientConfig } from "@/app/config/client"; import { getMessageTextContent, getMessageTextContentWithoutThinking, + getTimeoutMSByModel, } from "@/app/utils"; import { RequestPayload } from "./openai"; import { fetch } from "@/app/utils/stream"; @@ -123,7 +119,7 @@ export class SiliconflowApi implements LLMApi { // Use extended timeout for thinking models as they typically require more processing time const requestTimeoutId = setTimeout( () => controller.abort(), - REQUEST_TIMEOUT_MS_FOR_THINKING, + getTimeoutMSByModel(options.config.model), ); if (shouldStream) { diff --git a/app/client/platforms/tencent.ts b/app/client/platforms/tencent.ts index 580844a5b..8adeb1b3e 100644 --- a/app/client/platforms/tencent.ts +++ b/app/client/platforms/tencent.ts @@ -1,5 +1,5 @@ "use client"; -import { ApiPath, TENCENT_BASE_URL, REQUEST_TIMEOUT_MS } from "@/app/constant"; +import { ApiPath, TENCENT_BASE_URL } from "@/app/constant"; import { useAccessStore, useAppConfig, useChatStore } from "@/app/store"; import { @@ -17,7 +17,11 @@ import { } from "@fortaine/fetch-event-source"; import { prettyObject } from "@/app/utils/format"; import { getClientConfig } from "@/app/config/client"; -import { getMessageTextContent, isVisionModel } from "@/app/utils"; +import { + getMessageTextContent, + isVisionModel, + getTimeoutMSByModel, +} from "@/app/utils"; import mapKeys from "lodash-es/mapKeys"; import mapValues from "lodash-es/mapValues"; import isArray from "lodash-es/isArray"; @@ -135,7 +139,7 @@ export class HunyuanApi implements LLMApi { // make a fetch request const requestTimeoutId = setTimeout( () => controller.abort(), - REQUEST_TIMEOUT_MS, + getTimeoutMSByModel(options.config.model), ); if (shouldStream) { diff --git a/app/client/platforms/xai.ts b/app/client/platforms/xai.ts index 8c41c2d98..830ad4778 100644 --- a/app/client/platforms/xai.ts +++ b/app/client/platforms/xai.ts @@ -1,6 +1,6 @@ "use client"; // azure and openai, using same models. so using same LLMApi. -import { ApiPath, XAI_BASE_URL, XAI, REQUEST_TIMEOUT_MS } from "@/app/constant"; +import { ApiPath, XAI_BASE_URL, XAI } from "@/app/constant"; import { useAccessStore, useAppConfig, @@ -17,6 +17,7 @@ import { SpeechOptions, } from "../api"; import { getClientConfig } from "@/app/config/client"; +import { getTimeoutMSByModel } from "@/app/utils"; import { preProcessImageContent } from "@/app/utils/chat"; import { RequestPayload } from "./openai"; import { fetch } from "@/app/utils/stream"; @@ -103,7 +104,7 @@ export class XAIApi implements LLMApi { // make a fetch request const requestTimeoutId = setTimeout( () => controller.abort(), - REQUEST_TIMEOUT_MS, + getTimeoutMSByModel(options.config.model), ); if (shouldStream) { 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"];