diff --git a/src/renderer/src/config/__test__/models.test.ts b/src/renderer/src/config/__test__/models.test.ts new file mode 100644 index 0000000000..547ff42efd --- /dev/null +++ b/src/renderer/src/config/__test__/models.test.ts @@ -0,0 +1,90 @@ +import { + isImageEnhancementModel, + isPureGenerateImageModel, + isQwenReasoningModel, + isSupportedThinkingTokenQwenModel, + isVisionModel, + isWebSearchModel +} from '@renderer/config/models' +import { Model } from '@renderer/types' +import { beforeEach, describe, expect, test, vi } from 'vitest' + +// Suggested test cases +describe('Qwen Model Detection', () => { + beforeEach(() => { + vi.mock('@renderer/store/llm', () => ({ + initialState: {} + })) + vi.mock('@renderer/services/AssistantService', () => ({ + getProviderByModel: vi.fn().mockReturnValue({ id: 'cherryin' }) + })) + }) + test('isQwenReasoningModel', () => { + expect(isQwenReasoningModel({ id: 'qwen3-thinking' } as Model)).toBe(true) + expect(isQwenReasoningModel({ id: 'qwen3-instruct' } as Model)).toBe(false) + expect(isQwenReasoningModel({ id: 'qwen3-max' } as Model)).toBe(false) + expect(isQwenReasoningModel({ id: 'qwen3-8b' } as Model)).toBe(true) + expect(isQwenReasoningModel({ id: 'qwq-32b' } as Model)).toBe(true) + expect(isQwenReasoningModel({ id: 'qwen-plus' } as Model)).toBe(true) + expect(isQwenReasoningModel({ id: 'qwen3-coder' } as Model)).toBe(false) + }) + + test('isSupportedThinkingTokenQwenModel', () => { + expect(isSupportedThinkingTokenQwenModel({ id: 'qwen3-max' } as Model)).toBe(false) + expect(isSupportedThinkingTokenQwenModel({ id: 'qwen3-instruct' } as Model)).toBe(false) + expect(isSupportedThinkingTokenQwenModel({ id: 'qwen3-thinking' } as Model)).toBe(false) + expect(isSupportedThinkingTokenQwenModel({ id: 'qwen3-8b' } as Model)).toBe(true) + expect(isSupportedThinkingTokenQwenModel({ id: 'qwen3-235b-a22b-thinking-2507' } as Model)).toBe(false) + expect(isSupportedThinkingTokenQwenModel({ id: 'qwen-plus' } as Model)).toBe(true) + expect(isSupportedThinkingTokenQwenModel({ id: 'qwq-32b' } as Model)).toBe(false) + expect(isSupportedThinkingTokenQwenModel({ id: 'qwen3-coder' } as Model)).toBe(false) + }) + + test('isVisionModel', () => { + expect(isVisionModel({ id: 'qwen-vl-max' } as Model)).toBe(true) + expect(isVisionModel({ id: 'qwen-omni-turbo' } as Model)).toBe(true) + }) +}) + +describe('Vision Model Detection', () => { + beforeEach(() => { + vi.mock('@renderer/store/llm', () => ({ + initialState: {} + })) + vi.mock('@renderer/services/AssistantService', () => ({ + getProviderByModel: vi.fn().mockReturnValue({ id: 'cherryin' }) + })) + }) + test('isVisionModel', () => { + expect(isVisionModel({ id: 'qwen-vl-max' } as Model)).toBe(true) + expect(isVisionModel({ id: 'qwen-omni-turbo' } as Model)).toBe(true) + }) + test('isImageEnhancementModel', () => { + expect(isImageEnhancementModel({ id: 'gpt-image-1' } as Model)).toBe(true) + expect(isImageEnhancementModel({ id: 'gemini-2.5-flash-image-preview' } as Model)).toBe(true) + expect(isImageEnhancementModel({ id: 'gemini-2.0-flash-preview-image-generation' } as Model)).toBe(true) + expect(isImageEnhancementModel({ id: 'qwen-image-edit' } as Model)).toBe(true) + expect(isImageEnhancementModel({ id: 'grok-2-image-latest' } as Model)).toBe(true) + }) + test('isPureGenerateImageModel', () => { + expect(isPureGenerateImageModel({ id: 'gpt-image-1' } as Model)).toBe(true) + expect(isPureGenerateImageModel({ id: 'gemini-2.5-flash-image-preview' } as Model)).toBe(true) + expect(isPureGenerateImageModel({ id: 'gemini-2.0-flash-preview-image-generation' } as Model)).toBe(true) + expect(isPureGenerateImageModel({ id: 'grok-2-image-latest' } as Model)).toBe(true) + expect(isPureGenerateImageModel({ id: 'gpt-4o' } as Model)).toBe(false) + }) +}) + +describe('Web Search Model Detection', () => { + beforeEach(() => { + vi.mock('@renderer/store/llm', () => ({ + initialState: {} + })) + vi.mock('@renderer/services/AssistantService', () => ({ + getProviderByModel: vi.fn().mockReturnValue({ id: 'cherryin' }) + })) + }) + test('isWebSearchModel', () => { + expect(isWebSearchModel({ id: 'grok-2-image-latest' } as Model)).toBe(false) + }) +}) diff --git a/src/renderer/src/config/models.ts b/src/renderer/src/config/models.ts deleted file mode 100644 index 62ba886916..0000000000 --- a/src/renderer/src/config/models.ts +++ /dev/null @@ -1,3223 +0,0 @@ -import LongCatAppLogo from '@renderer/assets/images/apps/longcat.svg' -import Ai360ModelLogo from '@renderer/assets/images/models/360.png' -import Ai360ModelLogoDark from '@renderer/assets/images/models/360_dark.png' -import AdeptModelLogo from '@renderer/assets/images/models/adept.png' -import AdeptModelLogoDark from '@renderer/assets/images/models/adept_dark.png' -import Ai21ModelLogo from '@renderer/assets/images/models/ai21.png' -import Ai21ModelLogoDark from '@renderer/assets/images/models/ai21_dark.png' -import AimassModelLogo from '@renderer/assets/images/models/aimass.png' -import AimassModelLogoDark from '@renderer/assets/images/models/aimass_dark.png' -import AisingaporeModelLogo from '@renderer/assets/images/models/aisingapore.png' -import AisingaporeModelLogoDark from '@renderer/assets/images/models/aisingapore_dark.png' -import BaichuanModelLogo from '@renderer/assets/images/models/baichuan.png' -import BaichuanModelLogoDark from '@renderer/assets/images/models/baichuan_dark.png' -import BgeModelLogo from '@renderer/assets/images/models/bge.webp' -import BigcodeModelLogo from '@renderer/assets/images/models/bigcode.webp' -import BigcodeModelLogoDark from '@renderer/assets/images/models/bigcode_dark.webp' -import BytedanceModelLogo from '@renderer/assets/images/models/byte_dance.svg' -import ChatGLMModelLogo from '@renderer/assets/images/models/chatglm.png' -import ChatGLMModelLogoDark from '@renderer/assets/images/models/chatglm_dark.png' -import ChatGptModelLogo from '@renderer/assets/images/models/chatgpt.jpeg' -import ClaudeModelLogo from '@renderer/assets/images/models/claude.png' -import ClaudeModelLogoDark from '@renderer/assets/images/models/claude_dark.png' -import CodegeexModelLogo from '@renderer/assets/images/models/codegeex.png' -import CodegeexModelLogoDark from '@renderer/assets/images/models/codegeex_dark.png' -import CodestralModelLogo from '@renderer/assets/images/models/codestral.png' -import CohereModelLogo from '@renderer/assets/images/models/cohere.png' -import CohereModelLogoDark from '@renderer/assets/images/models/cohere_dark.png' -import CopilotModelLogo from '@renderer/assets/images/models/copilot.png' -import CopilotModelLogoDark from '@renderer/assets/images/models/copilot_dark.png' -import DalleModelLogo from '@renderer/assets/images/models/dalle.png' -import DalleModelLogoDark from '@renderer/assets/images/models/dalle_dark.png' -import DbrxModelLogo from '@renderer/assets/images/models/dbrx.png' -import DeepSeekModelLogo from '@renderer/assets/images/models/deepseek.png' -import DeepSeekModelLogoDark from '@renderer/assets/images/models/deepseek_dark.png' -import DianxinModelLogo from '@renderer/assets/images/models/dianxin.png' -import DianxinModelLogoDark from '@renderer/assets/images/models/dianxin_dark.png' -import DoubaoModelLogo from '@renderer/assets/images/models/doubao.png' -import DoubaoModelLogoDark from '@renderer/assets/images/models/doubao_dark.png' -import { - default as EmbeddingModelLogo, - default as EmbeddingModelLogoDark -} from '@renderer/assets/images/models/embedding.png' -import FlashaudioModelLogo from '@renderer/assets/images/models/flashaudio.png' -import FlashaudioModelLogoDark from '@renderer/assets/images/models/flashaudio_dark.png' -import FluxModelLogo from '@renderer/assets/images/models/flux.png' -import FluxModelLogoDark from '@renderer/assets/images/models/flux_dark.png' -import GeminiModelLogo from '@renderer/assets/images/models/gemini.png' -import GeminiModelLogoDark from '@renderer/assets/images/models/gemini_dark.png' -import GemmaModelLogo from '@renderer/assets/images/models/gemma.png' -import GemmaModelLogoDark from '@renderer/assets/images/models/gemma_dark.png' -import { default as GoogleModelLogo, default as GoogleModelLogoDark } from '@renderer/assets/images/models/google.png' -import ChatGPT35ModelLogo from '@renderer/assets/images/models/gpt_3.5.png' -import ChatGPT4ModelLogo from '@renderer/assets/images/models/gpt_4.png' -import { - default as ChatGPT4ModelLogoDark, - default as ChatGPT35ModelLogoDark, - default as ChatGptModelLogoDark, - default as ChatGPTo1ModelLogoDark -} from '@renderer/assets/images/models/gpt_dark.png' -import ChatGPTImageModelLogo from '@renderer/assets/images/models/gpt_image_1.png' -import ChatGPTo1ModelLogo from '@renderer/assets/images/models/gpt_o1.png' -import GPT5ModelLogo from '@renderer/assets/images/models/gpt-5.png' -import GPT5ChatModelLogo from '@renderer/assets/images/models/gpt-5-chat.png' -import GPT5MiniModelLogo from '@renderer/assets/images/models/gpt-5-mini.png' -import GPT5NanoModelLogo from '@renderer/assets/images/models/gpt-5-nano.png' -import GrokModelLogo from '@renderer/assets/images/models/grok.png' -import GrokModelLogoDark from '@renderer/assets/images/models/grok_dark.png' -import GrypheModelLogo from '@renderer/assets/images/models/gryphe.png' -import GrypheModelLogoDark from '@renderer/assets/images/models/gryphe_dark.png' -import HailuoModelLogo from '@renderer/assets/images/models/hailuo.png' -import HailuoModelLogoDark from '@renderer/assets/images/models/hailuo_dark.png' -import HuggingfaceModelLogo from '@renderer/assets/images/models/huggingface.png' -import HuggingfaceModelLogoDark from '@renderer/assets/images/models/huggingface_dark.png' -import HunyuanModelLogo from '@renderer/assets/images/models/hunyuan.png' -import HunyuanModelLogoDark from '@renderer/assets/images/models/hunyuan_dark.png' -import IbmModelLogo from '@renderer/assets/images/models/ibm.png' -import IbmModelLogoDark from '@renderer/assets/images/models/ibm_dark.png' -import IdeogramModelLogo from '@renderer/assets/images/models/ideogram.svg' -import InternlmModelLogo from '@renderer/assets/images/models/internlm.png' -import InternlmModelLogoDark from '@renderer/assets/images/models/internlm_dark.png' -import InternvlModelLogo from '@renderer/assets/images/models/internvl.png' -import JinaModelLogo from '@renderer/assets/images/models/jina.png' -import JinaModelLogoDark from '@renderer/assets/images/models/jina_dark.png' -import KeLingModelLogo from '@renderer/assets/images/models/keling.png' -import KeLingModelLogoDark from '@renderer/assets/images/models/keling_dark.png' -import LlamaModelLogo from '@renderer/assets/images/models/llama.png' -import LlamaModelLogoDark from '@renderer/assets/images/models/llama_dark.png' -import LLavaModelLogo from '@renderer/assets/images/models/llava.png' -import LLavaModelLogoDark from '@renderer/assets/images/models/llava_dark.png' -import LumaModelLogo from '@renderer/assets/images/models/luma.png' -import LumaModelLogoDark from '@renderer/assets/images/models/luma_dark.png' -import MagicModelLogo from '@renderer/assets/images/models/magic.png' -import MagicModelLogoDark from '@renderer/assets/images/models/magic_dark.png' -import MediatekModelLogo from '@renderer/assets/images/models/mediatek.png' -import MediatekModelLogoDark from '@renderer/assets/images/models/mediatek_dark.png' -import MicrosoftModelLogo from '@renderer/assets/images/models/microsoft.png' -import MicrosoftModelLogoDark from '@renderer/assets/images/models/microsoft_dark.png' -import MidjourneyModelLogo from '@renderer/assets/images/models/midjourney.png' -import MidjourneyModelLogoDark from '@renderer/assets/images/models/midjourney_dark.png' -import { - default as MinicpmModelLogo, - default as MinicpmModelLogoDark -} from '@renderer/assets/images/models/minicpm.webp' -import MinimaxModelLogo from '@renderer/assets/images/models/minimax.png' -import MinimaxModelLogoDark from '@renderer/assets/images/models/minimax_dark.png' -import MistralModelLogo from '@renderer/assets/images/models/mixtral.png' -import MistralModelLogoDark from '@renderer/assets/images/models/mixtral_dark.png' -import MoonshotModelLogo from '@renderer/assets/images/models/moonshot.png' -import MoonshotModelLogoDark from '@renderer/assets/images/models/moonshot_dark.png' -import { - default as NousResearchModelLogo, - default as NousResearchModelLogoDark -} from '@renderer/assets/images/models/nousresearch.png' -import NvidiaModelLogo from '@renderer/assets/images/models/nvidia.png' -import NvidiaModelLogoDark from '@renderer/assets/images/models/nvidia_dark.png' -import PalmModelLogo from '@renderer/assets/images/models/palm.png' -import PalmModelLogoDark from '@renderer/assets/images/models/palm_dark.png' -import PanguModelLogo from '@renderer/assets/images/models/pangu.svg' -import { - default as PerplexityModelLogo, - default as PerplexityModelLogoDark -} from '@renderer/assets/images/models/perplexity.png' -import PixtralModelLogo from '@renderer/assets/images/models/pixtral.png' -import PixtralModelLogoDark from '@renderer/assets/images/models/pixtral_dark.png' -import QwenModelLogo from '@renderer/assets/images/models/qwen.png' -import QwenModelLogoDark from '@renderer/assets/images/models/qwen_dark.png' -import RakutenaiModelLogo from '@renderer/assets/images/models/rakutenai.png' -import RakutenaiModelLogoDark from '@renderer/assets/images/models/rakutenai_dark.png' -import SparkDeskModelLogo from '@renderer/assets/images/models/sparkdesk.png' -import SparkDeskModelLogoDark from '@renderer/assets/images/models/sparkdesk_dark.png' -import StabilityModelLogo from '@renderer/assets/images/models/stability.png' -import StabilityModelLogoDark from '@renderer/assets/images/models/stability_dark.png' -import StepModelLogo from '@renderer/assets/images/models/step.png' -import StepModelLogoDark from '@renderer/assets/images/models/step_dark.png' -import SunoModelLogo from '@renderer/assets/images/models/suno.png' -import SunoModelLogoDark from '@renderer/assets/images/models/suno_dark.png' -import TeleModelLogo from '@renderer/assets/images/models/tele.png' -import TeleModelLogoDark from '@renderer/assets/images/models/tele_dark.png' -import TokenFluxModelLogo from '@renderer/assets/images/models/tokenflux.png' -import TokenFluxModelLogoDark from '@renderer/assets/images/models/tokenflux_dark.png' -import UpstageModelLogo from '@renderer/assets/images/models/upstage.png' -import UpstageModelLogoDark from '@renderer/assets/images/models/upstage_dark.png' -import ViduModelLogo from '@renderer/assets/images/models/vidu.png' -import ViduModelLogoDark from '@renderer/assets/images/models/vidu_dark.png' -import VoyageModelLogo from '@renderer/assets/images/models/voyageai.png' -import WenxinModelLogo from '@renderer/assets/images/models/wenxin.png' -import WenxinModelLogoDark from '@renderer/assets/images/models/wenxin_dark.png' -import XirangModelLogo from '@renderer/assets/images/models/xirang.png' -import XirangModelLogoDark from '@renderer/assets/images/models/xirang_dark.png' -import YiModelLogo from '@renderer/assets/images/models/yi.png' -import YiModelLogoDark from '@renderer/assets/images/models/yi_dark.png' -import ZhipuModelLogo from '@renderer/assets/images/models/zhipu.png' -import ZhipuModelLogoDark from '@renderer/assets/images/models/zhipu_dark.png' -import YoudaoLogo from '@renderer/assets/images/providers/netease-youdao.svg' -import NomicLogo from '@renderer/assets/images/providers/nomic.png' -import ZhipuProviderLogo from '@renderer/assets/images/providers/zhipu.png' -import { getProviderByModel } from '@renderer/services/AssistantService' -import { - isSystemProviderId, - Model, - ReasoningEffortConfig, - SystemProviderId, - ThinkingModelType, - ThinkingOptionConfig -} from '@renderer/types' -import { getLowerBaseModelName, isUserSelectedModelType } from '@renderer/utils' -import OpenAI from 'openai' - -import { WEB_SEARCH_PROMPT_FOR_OPENROUTER } from './prompts' -import { getWebSearchTools } from './tools' - -export function getModelLogo(modelId: string) { - const isLight = true - - if (!modelId) { - return undefined - } - - const logoMap = { - pixtral: isLight ? PixtralModelLogo : PixtralModelLogoDark, - jina: isLight ? JinaModelLogo : JinaModelLogoDark, - abab: isLight ? MinimaxModelLogo : MinimaxModelLogoDark, - minimax: isLight ? MinimaxModelLogo : MinimaxModelLogoDark, - veo: isLight ? GeminiModelLogo : GeminiModelLogoDark, - o1: isLight ? ChatGPTo1ModelLogo : ChatGPTo1ModelLogoDark, - o3: isLight ? ChatGPTo1ModelLogo : ChatGPTo1ModelLogoDark, - o4: isLight ? ChatGPTo1ModelLogo : ChatGPTo1ModelLogoDark, - 'gpt-image': ChatGPTImageModelLogo, - 'gpt-3': isLight ? ChatGPT35ModelLogo : ChatGPT35ModelLogoDark, - 'gpt-4': isLight ? ChatGPT4ModelLogo : ChatGPT4ModelLogoDark, - 'gpt-5-mini': GPT5MiniModelLogo, - 'gpt-5-nano': GPT5NanoModelLogo, - 'gpt-5-chat': GPT5ChatModelLogo, - 'gpt-5': GPT5ModelLogo, - gpts: isLight ? ChatGPT4ModelLogo : ChatGPT4ModelLogoDark, - 'gpt-oss(?:-[\\w-]+)': isLight ? ChatGptModelLogo : ChatGptModelLogoDark, - 'text-moderation': isLight ? ChatGptModelLogo : ChatGptModelLogoDark, - 'babbage-': isLight ? ChatGptModelLogo : ChatGptModelLogoDark, - '(sora-|sora_)': isLight ? ChatGptModelLogo : ChatGptModelLogoDark, - '(^|/)omni-': isLight ? ChatGptModelLogo : ChatGptModelLogoDark, - 'Embedding-V1': isLight ? WenxinModelLogo : WenxinModelLogoDark, - 'text-embedding-v': isLight ? QwenModelLogo : QwenModelLogoDark, - 'text-embedding': isLight ? ChatGptModelLogo : ChatGptModelLogoDark, - 'davinci-': isLight ? ChatGptModelLogo : ChatGptModelLogoDark, - glm: isLight ? ChatGLMModelLogo : ChatGLMModelLogoDark, - deepseek: isLight ? DeepSeekModelLogo : DeepSeekModelLogoDark, - '(qwen|qwq|qwq-|qvq-|wan-)': isLight ? QwenModelLogo : QwenModelLogoDark, - gemma: isLight ? GemmaModelLogo : GemmaModelLogoDark, - 'yi-': isLight ? YiModelLogo : YiModelLogoDark, - llama: isLight ? LlamaModelLogo : LlamaModelLogoDark, - mixtral: isLight ? MistralModelLogo : MistralModelLogo, - mistral: isLight ? MistralModelLogo : MistralModelLogoDark, - codestral: CodestralModelLogo, - ministral: isLight ? MistralModelLogo : MistralModelLogoDark, - magistral: isLight ? MistralModelLogo : MistralModelLogoDark, - moonshot: isLight ? MoonshotModelLogo : MoonshotModelLogoDark, - kimi: isLight ? MoonshotModelLogo : MoonshotModelLogoDark, - phi: isLight ? MicrosoftModelLogo : MicrosoftModelLogoDark, - baichuan: isLight ? BaichuanModelLogo : BaichuanModelLogoDark, - '(claude|anthropic-)': isLight ? ClaudeModelLogo : ClaudeModelLogoDark, - gemini: isLight ? GeminiModelLogo : GeminiModelLogoDark, - bison: isLight ? PalmModelLogo : PalmModelLogoDark, - palm: isLight ? PalmModelLogo : PalmModelLogoDark, - step: isLight ? StepModelLogo : StepModelLogoDark, - hailuo: isLight ? HailuoModelLogo : HailuoModelLogoDark, - doubao: isLight ? DoubaoModelLogo : DoubaoModelLogoDark, - seedream: isLight ? DoubaoModelLogo : DoubaoModelLogoDark, - 'ep-202': isLight ? DoubaoModelLogo : DoubaoModelLogoDark, - cohere: isLight ? CohereModelLogo : CohereModelLogoDark, - command: isLight ? CohereModelLogo : CohereModelLogoDark, - minicpm: isLight ? MinicpmModelLogo : MinicpmModelLogoDark, - '360': isLight ? Ai360ModelLogo : Ai360ModelLogoDark, - aimass: isLight ? AimassModelLogo : AimassModelLogoDark, - codegeex: isLight ? CodegeexModelLogo : CodegeexModelLogoDark, - copilot: isLight ? CopilotModelLogo : CopilotModelLogoDark, - creative: isLight ? CopilotModelLogo : CopilotModelLogoDark, - balanced: isLight ? CopilotModelLogo : CopilotModelLogoDark, - precise: isLight ? CopilotModelLogo : CopilotModelLogoDark, - dalle: isLight ? DalleModelLogo : DalleModelLogoDark, - 'dall-e': isLight ? DalleModelLogo : DalleModelLogoDark, - dbrx: isLight ? DbrxModelLogo : DbrxModelLogo, - flashaudio: isLight ? FlashaudioModelLogo : FlashaudioModelLogoDark, - flux: isLight ? FluxModelLogo : FluxModelLogoDark, - grok: isLight ? GrokModelLogo : GrokModelLogoDark, - hunyuan: isLight ? HunyuanModelLogo : HunyuanModelLogoDark, - internlm: isLight ? InternlmModelLogo : InternlmModelLogoDark, - internvl: InternvlModelLogo, - llava: isLight ? LLavaModelLogo : LLavaModelLogoDark, - magic: isLight ? MagicModelLogo : MagicModelLogoDark, - midjourney: isLight ? MidjourneyModelLogo : MidjourneyModelLogoDark, - 'mj-': isLight ? MidjourneyModelLogo : MidjourneyModelLogoDark, - 'tao-': isLight ? WenxinModelLogo : WenxinModelLogoDark, - 'ernie-': isLight ? WenxinModelLogo : WenxinModelLogoDark, - voice: isLight ? FlashaudioModelLogo : FlashaudioModelLogoDark, - 'tts-1': isLight ? ChatGptModelLogo : ChatGptModelLogoDark, - 'whisper-': isLight ? ChatGptModelLogo : ChatGptModelLogoDark, - 'stable-': isLight ? StabilityModelLogo : StabilityModelLogoDark, - sd2: isLight ? StabilityModelLogo : StabilityModelLogoDark, - sd3: isLight ? StabilityModelLogo : StabilityModelLogoDark, - sdxl: isLight ? StabilityModelLogo : StabilityModelLogoDark, - sparkdesk: isLight ? SparkDeskModelLogo : SparkDeskModelLogoDark, - generalv: isLight ? SparkDeskModelLogo : SparkDeskModelLogoDark, - wizardlm: isLight ? MicrosoftModelLogo : MicrosoftModelLogoDark, - microsoft: isLight ? MicrosoftModelLogo : MicrosoftModelLogoDark, - hermes: isLight ? NousResearchModelLogo : NousResearchModelLogoDark, - gryphe: isLight ? GrypheModelLogo : GrypheModelLogoDark, - suno: isLight ? SunoModelLogo : SunoModelLogoDark, - chirp: isLight ? SunoModelLogo : SunoModelLogoDark, - luma: isLight ? LumaModelLogo : LumaModelLogoDark, - keling: isLight ? KeLingModelLogo : KeLingModelLogoDark, - 'vidu-': isLight ? ViduModelLogo : ViduModelLogoDark, - ai21: isLight ? Ai21ModelLogo : Ai21ModelLogoDark, - 'jamba-': isLight ? Ai21ModelLogo : Ai21ModelLogoDark, - mythomax: isLight ? GrypheModelLogo : GrypheModelLogoDark, - nvidia: isLight ? NvidiaModelLogo : NvidiaModelLogoDark, - dianxin: isLight ? DianxinModelLogo : DianxinModelLogoDark, - tele: isLight ? TeleModelLogo : TeleModelLogoDark, - adept: isLight ? AdeptModelLogo : AdeptModelLogoDark, - aisingapore: isLight ? AisingaporeModelLogo : AisingaporeModelLogoDark, - bigcode: isLight ? BigcodeModelLogo : BigcodeModelLogoDark, - mediatek: isLight ? MediatekModelLogo : MediatekModelLogoDark, - upstage: isLight ? UpstageModelLogo : UpstageModelLogoDark, - rakutenai: isLight ? RakutenaiModelLogo : RakutenaiModelLogoDark, - ibm: isLight ? IbmModelLogo : IbmModelLogoDark, - 'google/': isLight ? GoogleModelLogo : GoogleModelLogoDark, - xirang: isLight ? XirangModelLogo : XirangModelLogoDark, - hugging: isLight ? HuggingfaceModelLogo : HuggingfaceModelLogoDark, - youdao: YoudaoLogo, - 'embedding-3': ZhipuProviderLogo, - embedding: isLight ? EmbeddingModelLogo : EmbeddingModelLogoDark, - perplexity: isLight ? PerplexityModelLogo : PerplexityModelLogoDark, - sonar: isLight ? PerplexityModelLogo : PerplexityModelLogoDark, - 'bge-': BgeModelLogo, - 'voyage-': VoyageModelLogo, - tokenflux: isLight ? TokenFluxModelLogo : TokenFluxModelLogoDark, - 'nomic-': NomicLogo, - 'pangu-': PanguModelLogo, - cogview: isLight ? ZhipuModelLogo : ZhipuModelLogoDark, - zhipu: isLight ? ZhipuModelLogo : ZhipuModelLogoDark, - longcat: LongCatAppLogo, - bytedance: BytedanceModelLogo, - '(V_1|V_1_TURBO|V_2|V_2A|V_2_TURBO|DESCRIBE|UPSCALE)': IdeogramModelLogo - } - - for (const key in logoMap) { - const regex = new RegExp(key, 'i') - if (regex.test(modelId)) { - return logoMap[key] - } - } - - return undefined -} - -export const glm45FlashModel: Model = { - id: 'glm-4.5-flash', - name: 'GLM-4.5-Flash', - provider: 'cherryin', - group: 'GLM-4.5' -} - -export const qwen38bModel: Model = { - id: 'Qwen/Qwen3-8B', - name: 'Qwen3-8B', - provider: 'cherryin', - group: 'Qwen' -} - -export const SYSTEM_MODELS: Record = { - defaultModel: [ - // Default assistant model - glm45FlashModel, - // Default topic naming model - qwen38bModel, - // Default translation model - glm45FlashModel, - // Default quick assistant model - glm45FlashModel - ], - cherryin: [ - { - id: 'glm-4.5-flash', - name: 'GLM-4.5-Flash', - provider: 'cherryin', - group: 'GLM-4.5' - }, - { - id: 'Qwen/Qwen3-8B', - name: 'Qwen3-8B', - provider: 'cherryin', - group: 'Qwen' - } - ], - vertexai: [], - '302ai': [ - { - id: 'deepseek-chat', - name: 'deepseek-chat', - provider: '302ai', - group: 'DeepSeek' - }, - { - id: 'deepseek-reasoner', - name: 'deepseek-reasoner', - provider: '302ai', - group: 'DeepSeek' - }, - { - id: 'chatgpt-4o-latest', - name: 'chatgpt-4o-latest', - provider: '302ai', - group: 'OpenAI' - }, - { - id: 'gpt-4.1', - name: 'gpt-4.1', - provider: '302ai', - group: 'OpenAI' - }, - { - id: 'o3', - name: 'o3', - provider: '302ai', - group: 'OpenAI' - }, - { - id: 'o4-mini', - name: 'o4-mini', - provider: '302ai', - group: 'OpenAI' - }, - { - id: 'qwen3-235b-a22b', - name: 'qwen3-235b-a22b', - provider: '302ai', - group: 'Qwen' - }, - { - id: 'gemini-2.5-flash-preview-05-20', - name: 'gemini-2.5-flash-preview-05-20', - provider: '302ai', - group: 'Gemini' - }, - { - id: 'gemini-2.5-pro-preview-06-05', - name: 'gemini-2.5-pro-preview-06-05', - provider: '302ai', - group: 'Gemini' - }, - { - id: 'claude-sonnet-4-20250514', - provider: '302ai', - name: 'claude-sonnet-4-20250514', - group: 'Anthropic' - }, - { - id: 'claude-opus-4-20250514', - provider: '302ai', - name: 'claude-opus-4-20250514', - group: 'Anthropic' - }, - { - id: 'jina-clip-v2', - name: 'jina-clip-v2', - provider: '302ai', - group: 'Jina AI' - }, - { - id: 'jina-reranker-m0', - name: 'jina-reranker-m0', - provider: '302ai', - group: 'Jina AI' - } - ], - ph8: [ - { - id: 'deepseek-v3-241226', - name: 'deepseek-v3-241226', - provider: 'ph8', - group: 'DeepSeek' - }, - { - id: 'deepseek-r1-250120', - name: 'deepseek-r1-250120', - provider: 'ph8', - group: 'DeepSeek' - } - ], - aihubmix: [ - { - id: 'gpt-5', - provider: 'aihubmix', - name: 'gpt-5', - group: 'OpenAI' - }, - { - id: 'gpt-5-mini', - provider: 'aihubmix', - name: 'gpt-5-mini', - group: 'OpenAI' - }, - { - id: 'gpt-5-nano', - provider: 'aihubmix', - name: 'gpt-5-nano', - group: 'OpenAI' - }, - { - id: 'gpt-5-chat-latest', - provider: 'aihubmix', - name: 'gpt-5-chat-latest', - group: 'OpenAI' - }, - { - id: 'o3', - provider: 'aihubmix', - name: 'o3', - group: 'OpenAI' - }, - { - id: 'o4-mini', - provider: 'aihubmix', - name: 'o4-mini', - group: 'OpenAI' - }, - { - id: 'gpt-4.1', - provider: 'aihubmix', - name: 'gpt-4.1', - group: 'OpenAI' - }, - { - id: 'gpt-4o', - provider: 'aihubmix', - name: 'gpt-4o', - group: 'OpenAI' - }, - { - id: 'gpt-image-1', - provider: 'aihubmix', - name: 'gpt-image-1', - group: 'OpenAI' - }, - { - id: 'DeepSeek-V3', - provider: 'aihubmix', - name: 'DeepSeek-V3', - group: 'DeepSeek' - }, - { - id: 'DeepSeek-R1', - provider: 'aihubmix', - name: 'DeepSeek-R1', - group: 'DeepSeek' - }, - { - id: 'claude-sonnet-4-20250514', - provider: 'aihubmix', - name: 'claude-sonnet-4-20250514', - group: 'Claude' - }, - { - id: 'gemini-2.5-pro', - provider: 'aihubmix', - name: 'gemini-2.5-pro', - group: 'Gemini' - }, - { - id: 'gemini-2.5-flash-nothink', - provider: 'aihubmix', - name: 'gemini-2.5-flash-nothink', - group: 'Gemini' - }, - { - id: 'gemini-2.5-flash', - provider: 'aihubmix', - name: 'gemini-2.5-flash', - group: 'Gemini' - }, - { - id: 'Qwen3-235B-A22B-Instruct-2507', - provider: 'aihubmix', - name: 'Qwen3-235B-A22B-Instruct-2507', - group: 'qwen' - }, - { - id: 'kimi-k2-0711-preview', - provider: 'aihubmix', - name: 'kimi-k2-0711-preview', - group: 'moonshot' - }, - { - id: 'Llama-4-Scout-17B-16E-Instruct', - provider: 'aihubmix', - name: 'Llama-4-Scout-17B-16E-Instruct', - group: 'llama' - }, - { - id: 'Llama-4-Maverick-17B-128E-Instruct-FP8', - provider: 'aihubmix', - name: 'Llama-4-Maverick-17B-128E-Instruct-FP8', - group: 'llama' - } - ], - - burncloud: [ - { id: 'claude-3-7-sonnet-20250219-thinking', provider: 'burncloud', name: 'Claude 3.7 thinking', group: 'Claude' }, - { id: 'claude-3-7-sonnet-20250219', provider: 'burncloud', name: 'Claude 3.7 Sonnet', group: 'Claude 3.7' }, - { id: 'claude-3-5-sonnet-20241022', provider: 'burncloud', name: 'Claude 3.5 Sonnet', group: 'Claude 3.5' }, - { id: 'claude-3-5-haiku-20241022', provider: 'burncloud', name: 'Claude 3.5 Haiku', group: 'Claude 3.5' }, - - { id: 'gpt-4.5-preview', provider: 'burncloud', name: 'gpt-4.5-preview', group: 'gpt-4.5' }, - { id: 'gpt-4o', provider: 'burncloud', name: 'GPT-4o', group: 'GPT 4o' }, - { id: 'gpt-4o-mini', provider: 'burncloud', name: 'GPT-4o-mini', group: 'GPT 4o' }, - { id: 'o3', provider: 'burncloud', name: 'GPT-o1-mini', group: 'o1' }, - { id: 'o3-mini', provider: 'burncloud', name: 'GPT-o1-preview', group: 'o1' }, - { id: 'o1-mini', provider: 'burncloud', name: 'GPT-o1-mini', group: 'o1' }, - - { id: 'gemini-2.5-pro-preview-03-25', provider: 'burncloud', name: 'Gemini 2.5 Preview', group: 'Geminit 2.5' }, - { id: 'gemini-2.5-pro-exp-03-25', provider: 'burncloud', name: 'Gemini 2.5 Pro Exp', group: 'Geminit 2.5' }, - { id: 'gemini-2.0-flash-lite', provider: 'burncloud', name: 'Gemini 2.0 Flash Lite', group: 'Geminit 2.0' }, - { id: 'gemini-2.0-flash-exp', provider: 'burncloud', name: 'Gemini 2.0 Flash Exp', group: 'Geminit 2.0' }, - { id: 'gemini-2.0-flash', provider: 'burncloud', name: 'Gemini 2.0 Flash', group: 'Geminit 2.0' }, - - { id: 'deepseek-r1', name: 'DeepSeek-R1', provider: 'burncloud', group: 'deepseek-ai' }, - { id: 'deepseek-v3', name: 'DeepSeek-V3', provider: 'burncloud', group: 'deepseek-ai' } - ], - ollama: [], - lmstudio: [], - silicon: [ - { - id: 'deepseek-ai/DeepSeek-R1', - name: 'deepseek-ai/DeepSeek-R1', - provider: 'silicon', - group: 'deepseek-ai' - }, - { - id: 'deepseek-ai/DeepSeek-V3', - name: 'deepseek-ai/DeepSeek-V3', - provider: 'silicon', - group: 'deepseek-ai' - }, - { - id: 'Qwen/Qwen2.5-7B-Instruct', - provider: 'silicon', - name: 'Qwen2.5-7B-Instruct', - group: 'Qwen' - }, - { - id: 'BAAI/bge-m3', - name: 'BAAI/bge-m3', - provider: 'silicon', - group: 'BAAI' - }, - { - id: 'Qwen/Qwen3-8B', - name: 'Qwen/Qwen3-8B', - provider: 'silicon', - group: 'Qwen' - } - ], - ppio: [ - { - id: 'deepseek/deepseek-r1-0528', - provider: 'ppio', - name: 'DeepSeek R1-0528', - group: 'deepseek' - }, - { - id: 'deepseek/deepseek-v3-0324', - provider: 'ppio', - name: 'DeepSeek V3-0324', - group: 'deepseek' - }, - { - id: 'deepseek/deepseek-r1-turbo', - provider: 'ppio', - name: 'DeepSeek R1 Turbo', - group: 'deepseek' - }, - { - id: 'deepseek/deepseek-v3-turbo', - provider: 'ppio', - name: 'DeepSeek V3 Turbo', - group: 'deepseek' - }, - { - id: 'deepseek/deepseek-r1/community', - name: 'DeepSeek: DeepSeek R1 (Community)', - provider: 'ppio', - group: 'deepseek' - }, - { - id: 'deepseek/deepseek-v3/community', - name: 'DeepSeek: DeepSeek V3 (Community)', - provider: 'ppio', - group: 'deepseek' - }, - { - id: 'minimaxai/minimax-m1-80k', - provider: 'ppio', - name: 'MiniMax M1-80K', - group: 'minimaxai' - }, - { - id: 'qwen/qwen3-235b-a22b-fp8', - provider: 'ppio', - name: 'Qwen3 235B', - group: 'qwen' - }, - { - id: 'qwen/qwen3-32b-fp8', - provider: 'ppio', - name: 'Qwen3 32B', - group: 'qwen' - }, - { - id: 'qwen/qwen3-30b-a3b-fp8', - provider: 'ppio', - name: 'Qwen3 30B', - group: 'qwen' - }, - { - id: 'qwen/qwen2.5-vl-72b-instruct', - provider: 'ppio', - name: 'Qwen2.5 VL 72B', - group: 'qwen' - }, - { - id: 'qwen/qwen3-embedding-8b', - provider: 'ppio', - name: 'Qwen3 Embedding 8B', - group: 'qwen' - }, - { - id: 'qwen/qwen3-reranker-8b', - provider: 'ppio', - name: 'Qwen3 Reranker 8B', - group: 'qwen' - } - ], - alayanew: [], - openai: [ - { id: 'gpt-4.5-preview', provider: 'openai', name: ' gpt-4.5-preview', group: 'gpt-4.5' }, - { id: 'gpt-4o', provider: 'openai', name: ' GPT-4o', group: 'GPT 4o' }, - { id: 'gpt-4o-mini', provider: 'openai', name: ' GPT-4o-mini', group: 'GPT 4o' }, - { id: 'o1-mini', provider: 'openai', name: ' o1-mini', group: 'o1' }, - { id: 'o1-preview', provider: 'openai', name: ' o1-preview', group: 'o1' } - ], - 'azure-openai': [ - { - id: 'gpt-4o', - provider: 'azure-openai', - name: ' GPT-4o', - group: 'GPT 4o' - }, - { - id: 'gpt-4o-mini', - provider: 'azure-openai', - name: ' GPT-4o-mini', - group: 'GPT 4o' - } - ], - gemini: [ - { - id: 'gemini-1.5-flash', - provider: 'gemini', - name: 'Gemini 1.5 Flash', - group: 'Gemini 1.5' - }, - { - id: 'gemini-1.5-flash-8b', - provider: 'gemini', - name: 'Gemini 1.5 Flash (8B)', - group: 'Gemini 1.5' - }, - { - id: 'gemini-1.5-pro', - name: 'Gemini 1.5 Pro', - provider: 'gemini', - group: 'Gemini 1.5' - }, - { - id: 'gemini-2.0-flash', - provider: 'gemini', - name: 'Gemini 2.0 Flash', - group: 'Gemini 2.0' - }, - { - id: 'gemini-2.5-flash-image-preview', - provider: 'gemini', - name: 'Gemini 2.5 Flash Image', - group: 'Gemini 2.5' - } - ], - anthropic: [ - { - id: 'claude-sonnet-4-20250514', - provider: 'anthropic', - name: 'Claude Sonnet 4', - group: 'Claude 4' - }, - { - id: 'claude-opus-4-20250514', - provider: 'anthropic', - name: 'Claude Opus 4', - group: 'Claude 4' - }, - { - id: 'claude-3-7-sonnet-20250219', - provider: 'anthropic', - name: 'Claude 3.7 Sonnet', - group: 'Claude 3.7' - }, - { - id: 'claude-3-5-sonnet-20241022', - provider: 'anthropic', - name: 'Claude 3.5 Sonnet', - group: 'Claude 3.5' - }, - { - id: 'claude-3-5-haiku-20241022', - provider: 'anthropic', - name: 'Claude 3.5 Haiku', - group: 'Claude 3.5' - }, - { - id: 'claude-3-5-sonnet-20240620', - provider: 'anthropic', - name: 'Claude 3.5 Sonnet (Legacy)', - group: 'Claude 3.5' - }, - { - id: 'claude-3-opus-20240229', - provider: 'anthropic', - name: 'Claude 3 Opus', - group: 'Claude 3' - }, - { - id: 'claude-3-haiku-20240307', - provider: 'anthropic', - name: 'Claude 3 Haiku', - group: 'Claude 3' - } - ], - deepseek: [ - { - id: 'deepseek-chat', - provider: 'deepseek', - name: 'DeepSeek Chat', - group: 'DeepSeek Chat' - }, - { - id: 'deepseek-reasoner', - provider: 'deepseek', - name: 'DeepSeek Reasoner', - group: 'DeepSeek Reasoner' - } - ], - together: [ - { - id: 'meta-llama/Llama-3.2-11B-Vision-Instruct-Turbo', - provider: 'together', - name: 'Llama-3.2-11B-Vision', - group: 'Llama-3.2' - }, - { - id: 'meta-llama/Llama-3.2-90B-Vision-Instruct-Turbo', - provider: 'together', - name: 'Llama-3.2-90B-Vision', - group: 'Llama-3.2' - }, - { - id: 'google/gemma-2-27b-it', - provider: 'together', - name: 'gemma-2-27b-it', - group: 'Gemma' - }, - { - id: 'google/gemma-2-9b-it', - provider: 'together', - name: 'gemma-2-9b-it', - group: 'Gemma' - } - ], - ocoolai: [ - { - id: 'deepseek-chat', - provider: 'ocoolai', - name: 'deepseek-chat', - group: 'DeepSeek' - }, - { - id: 'deepseek-reasoner', - provider: 'ocoolai', - name: 'deepseek-reasoner', - group: 'DeepSeek' - }, - { - id: 'deepseek-ai/DeepSeek-R1', - provider: 'ocoolai', - name: 'deepseek-ai/DeepSeek-R1', - group: 'DeepSeek' - }, - { - id: 'HiSpeed/DeepSeek-R1', - provider: 'ocoolai', - name: 'HiSpeed/DeepSeek-R1', - group: 'DeepSeek' - }, - { - id: 'ocoolAI/DeepSeek-R1', - provider: 'ocoolai', - name: 'ocoolAI/DeepSeek-R1', - group: 'DeepSeek' - }, - { - id: 'Azure/DeepSeek-R1', - provider: 'ocoolai', - name: 'Azure/DeepSeek-R1', - group: 'DeepSeek' - }, - { - id: 'gpt-4o', - provider: 'ocoolai', - name: 'gpt-4o', - group: 'OpenAI' - }, - { - id: 'gpt-4o-all', - provider: 'ocoolai', - name: 'gpt-4o-all', - group: 'OpenAI' - }, - { - id: 'gpt-4o-mini', - provider: 'ocoolai', - name: 'gpt-4o-mini', - group: 'OpenAI' - }, - { - id: 'gpt-4', - provider: 'ocoolai', - name: 'gpt-4', - group: 'OpenAI' - }, - { - id: 'o1-preview', - provider: 'ocoolai', - name: 'o1-preview', - group: 'OpenAI' - }, - { - id: 'o1-mini', - provider: 'ocoolai', - name: 'o1-mini', - group: 'OpenAI' - }, - { - id: 'claude-3-5-sonnet-20240620', - provider: 'ocoolai', - name: 'claude-3-5-sonnet-20240620', - group: 'Anthropic' - }, - { - id: 'claude-3-5-haiku-20241022', - provider: 'ocoolai', - name: 'claude-3-5-haiku-20241022', - group: 'Anthropic' - }, - { - id: 'gemini-pro', - provider: 'ocoolai', - name: 'gemini-pro', - group: 'Gemini' - }, - { - id: 'gemini-1.5-pro', - provider: 'ocoolai', - name: 'gemini-1.5-pro', - group: 'Gemini' - }, - { - id: 'meta-llama/Llama-3.2-90B-Vision-Instruct-Turbo', - provider: 'ocoolai', - name: 'Llama-3.2-90B-Vision-Instruct-Turbo', - group: 'Llama-3.2' - }, - { - id: 'meta-llama/Llama-3.2-11B-Vision-Instruct-Turbo', - provider: 'ocoolai', - name: 'Llama-3.2-11B-Vision-Instruct-Turbo', - group: 'Llama-3.2' - }, - { - id: 'meta-llama/Llama-3.2-3B-Vision-Instruct-Turbo', - provider: 'ocoolai', - name: 'Llama-3.2-3B-Vision-Instruct-Turbo', - group: 'Llama-3.2' - }, - { - id: 'google/gemma-2-27b-it', - provider: 'ocoolai', - name: 'gemma-2-27b-it', - group: 'Gemma' - }, - { - id: 'google/gemma-2-9b-it', - provider: 'ocoolai', - name: 'gemma-2-9b-it', - group: 'Gemma' - }, - { - id: 'Doubao-embedding', - provider: 'ocoolai', - name: 'Doubao-embedding', - group: 'Doubao' - }, - { - id: 'text-embedding-3-large', - provider: 'ocoolai', - name: 'text-embedding-3-large', - group: 'Embedding' - }, - { - id: 'text-embedding-3-small', - provider: 'ocoolai', - name: 'text-embedding-3-small', - group: 'Embedding' - }, - { - id: 'text-embedding-v2', - provider: 'ocoolai', - name: 'text-embedding-v2', - group: 'Embedding' - } - ], - github: [ - { - id: 'gpt-4o', - provider: 'github', - name: 'OpenAI GPT-4o', - group: 'OpenAI' - } - ], - copilot: [ - { - id: 'gpt-4o-mini', - provider: 'copilot', - name: 'OpenAI GPT-4o-mini', - group: 'OpenAI' - } - ], - yi: [ - { id: 'yi-lightning', name: 'Yi Lightning', provider: 'yi', group: 'yi-lightning', owned_by: '01.ai' }, - { id: 'yi-vision-v2', name: 'Yi Vision v2', provider: 'yi', group: 'yi-vision', owned_by: '01.ai' } - ], - zhipu: [ - { - id: 'glm-4.5-flash', - provider: 'zhipu', - name: 'GLM-4.5-Flash', - group: 'GLM-4.5' - }, - { - id: 'glm-4.5', - provider: 'zhipu', - name: 'GLM-4.5', - group: 'GLM-4.5' - }, - { - id: 'glm-4.5-air', - provider: 'zhipu', - name: 'GLM-4.5-Air', - group: 'GLM-4.5' - }, - { - id: 'glm-4.5-airx', - provider: 'zhipu', - name: 'GLM-4.5-AirX', - group: 'GLM-4.5' - }, - { - id: 'glm-4.5v', - provider: 'zhipu', - name: 'GLM-4.5V', - group: 'GLM-4.5V' - }, - { - id: 'embedding-3', - provider: 'zhipu', - name: 'Embedding-3', - group: 'Embedding' - } - ], - moonshot: [ - { - id: 'moonshot-v1-auto', - name: 'moonshot-v1-auto', - provider: 'moonshot', - group: 'moonshot-v1', - owned_by: 'moonshot', - capabilities: [{ type: 'text' }, { type: 'function_calling' }] - }, - { - id: 'kimi-k2-0711-preview', - name: 'kimi-k2-0711-preview', - provider: 'moonshot', - group: 'kimi-k2', - owned_by: 'moonshot', - capabilities: [{ type: 'text' }, { type: 'function_calling' }], - pricing: { - input_per_million_tokens: 0.6, - output_per_million_tokens: 2.5, - currencySymbol: 'USD' - } - } - ], - baichuan: [ - { - id: 'Baichuan4', - provider: 'baichuan', - name: 'Baichuan4', - group: 'Baichuan4' - }, - { - id: 'Baichuan3-Turbo', - provider: 'baichuan', - name: 'Baichuan3 Turbo', - group: 'Baichuan3' - }, - { - id: 'Baichuan3-Turbo-128k', - provider: 'baichuan', - name: 'Baichuan3 Turbo 128k', - group: 'Baichuan3' - } - ], - modelscope: [ - { - id: 'Qwen/Qwen2.5-72B-Instruct', - name: 'Qwen/Qwen2.5-72B-Instruct', - provider: 'modelscope', - group: 'Qwen' - }, - { - id: 'Qwen/Qwen2.5-VL-72B-Instruct', - name: 'Qwen/Qwen2.5-VL-72B-Instruct', - provider: 'modelscope', - group: 'Qwen' - }, - { - id: 'Qwen/Qwen2.5-Coder-32B-Instruct', - name: 'Qwen/Qwen2.5-Coder-32B-Instruct', - provider: 'modelscope', - group: 'Qwen' - }, - { - id: 'deepseek-ai/DeepSeek-R1', - name: 'deepseek-ai/DeepSeek-R1', - provider: 'modelscope', - group: 'deepseek-ai' - }, - { - id: 'deepseek-ai/DeepSeek-V3', - name: 'deepseek-ai/DeepSeek-V3', - provider: 'modelscope', - group: 'deepseek-ai' - } - ], - dashscope: [ - { id: 'qwen-vl-plus', name: 'qwen-vl-plus', provider: 'dashscope', group: 'qwen-vl', owned_by: 'system' }, - { id: 'qwen-coder-plus', name: 'qwen-coder-plus', provider: 'dashscope', group: 'qwen-coder', owned_by: 'system' }, - { id: 'qwen-flash', name: 'qwen-flash', provider: 'dashscope', group: 'qwen-flash', owned_by: 'system' }, - { id: 'qwen-plus', name: 'qwen-plus', provider: 'dashscope', group: 'qwen-plus', owned_by: 'system' }, - { id: 'qwen-max', name: 'qwen-max', provider: 'dashscope', group: 'qwen-max', owned_by: 'system' } - ], - stepfun: [ - { - id: 'step-1-8k', - provider: 'stepfun', - name: 'Step 1 8K', - group: 'Step 1' - }, - { - id: 'step-1-flash', - provider: 'stepfun', - name: 'Step 1 Flash', - group: 'Step 1' - } - ], - doubao: [ - { - id: 'doubao-1-5-vision-pro-32k-250115', - provider: 'doubao', - name: 'doubao-1.5-vision-pro', - group: 'Doubao-1.5-vision-pro' - }, - { - id: 'doubao-1-5-pro-32k-250115', - provider: 'doubao', - name: 'doubao-1.5-pro-32k', - group: 'Doubao-1.5-pro' - }, - { - id: 'doubao-1-5-pro-32k-character-250228', - provider: 'doubao', - name: 'doubao-1.5-pro-32k-character', - group: 'Doubao-1.5-pro' - }, - { - id: 'doubao-1-5-pro-256k-250115', - provider: 'doubao', - name: 'Doubao-1.5-pro-256k', - group: 'Doubao-1.5-pro' - }, - { - id: 'deepseek-r1-250120', - provider: 'doubao', - name: 'DeepSeek-R1', - group: 'DeepSeek' - }, - { - id: 'deepseek-r1-distill-qwen-32b-250120', - provider: 'doubao', - name: 'DeepSeek-R1-Distill-Qwen-32B', - group: 'DeepSeek' - }, - { - id: 'deepseek-r1-distill-qwen-7b-250120', - provider: 'doubao', - name: 'DeepSeek-R1-Distill-Qwen-7B', - group: 'DeepSeek' - }, - { - id: 'deepseek-v3-250324', - provider: 'doubao', - name: 'DeepSeek-V3', - group: 'DeepSeek' - }, - { - id: 'doubao-pro-32k-241215', - provider: 'doubao', - name: 'Doubao-pro-32k', - group: 'Doubao-pro' - }, - { - id: 'doubao-pro-32k-functioncall-241028', - provider: 'doubao', - name: 'Doubao-pro-32k-functioncall-241028', - group: 'Doubao-pro' - }, - { - id: 'doubao-pro-32k-character-241215', - provider: 'doubao', - name: 'Doubao-pro-32k-character-241215', - group: 'Doubao-pro' - }, - { - id: 'doubao-pro-256k-241115', - provider: 'doubao', - name: 'Doubao-pro-256k', - group: 'Doubao-pro' - }, - { - id: 'doubao-lite-4k-character-240828', - provider: 'doubao', - name: 'Doubao-lite-4k-character-240828', - group: 'Doubao-lite' - }, - { - id: 'doubao-lite-32k-240828', - provider: 'doubao', - name: 'Doubao-lite-32k', - group: 'Doubao-lite' - }, - { - id: 'doubao-lite-32k-character-241015', - provider: 'doubao', - name: 'Doubao-lite-32k-character-241015', - group: 'Doubao-lite' - }, - { - id: 'doubao-lite-128k-240828', - provider: 'doubao', - name: 'Doubao-lite-128k', - group: 'Doubao-lite' - }, - { - id: 'doubao-1-5-lite-32k-250115', - provider: 'doubao', - name: 'Doubao-1.5-lite-32k', - group: 'Doubao-lite' - }, - { - id: 'doubao-embedding-large-text-240915', - provider: 'doubao', - name: 'Doubao-embedding-large', - group: 'Doubao-embedding' - }, - { - id: 'doubao-embedding-text-240715', - provider: 'doubao', - name: 'Doubao-embedding', - group: 'Doubao-embedding' - }, - { - id: 'doubao-embedding-vision-241215', - provider: 'doubao', - name: 'Doubao-embedding-vision', - group: 'Doubao-embedding' - }, - { - id: 'doubao-vision-lite-32k-241015', - provider: 'doubao', - name: 'Doubao-vision-lite-32k', - group: 'Doubao-vision-lite-32k' - } - ], - minimax: [ - { - id: 'abab6.5s-chat', - provider: 'minimax', - name: 'abab6.5s', - group: 'abab6' - }, - { - id: 'abab6.5g-chat', - provider: 'minimax', - name: 'abab6.5g', - group: 'abab6' - }, - { - id: 'abab6.5t-chat', - provider: 'minimax', - name: 'abab6.5t', - group: 'abab6' - }, - { - id: 'abab5.5s-chat', - provider: 'minimax', - name: 'abab5.5s', - group: 'abab5' - }, - { - id: 'minimax-text-01', - provider: 'minimax', - name: 'minimax-01', - group: 'minimax-01' - } - ], - hyperbolic: [ - { - id: 'Qwen/Qwen2-VL-72B-Instruct', - provider: 'hyperbolic', - name: 'Qwen2-VL-72B-Instruct', - group: 'Qwen2-VL' - }, - { - id: 'Qwen/Qwen2-VL-7B-Instruct', - provider: 'hyperbolic', - name: 'Qwen2-VL-7B-Instruct', - group: 'Qwen2-VL' - }, - { - id: 'mistralai/Pixtral-12B-2409', - provider: 'hyperbolic', - name: 'Pixtral-12B-2409', - group: 'Pixtral' - }, - { - id: 'meta-llama/Meta-Llama-3.1-405B', - provider: 'hyperbolic', - name: 'Meta-Llama-3.1-405B', - group: 'Meta-Llama-3.1' - } - ], - grok: [ - { - id: 'grok-4', - provider: 'grok', - name: 'Grok 4', - group: 'Grok' - }, - { - id: 'grok-3', - provider: 'grok', - name: 'Grok 3', - group: 'Grok' - }, - { - id: 'grok-3-fast', - provider: 'grok', - name: 'Grok 3 Fast', - group: 'Grok' - }, - { - id: 'grok-3-mini', - provider: 'grok', - name: 'Grok 3 Mini', - group: 'Grok' - }, - { - id: 'grok-3-mini-fast', - provider: 'grok', - name: 'Grok 3 Mini Fast', - group: 'Grok' - }, - { - id: 'grok-2-vision-1212', - provider: 'grok', - name: 'Grok 2 Vision 1212', - group: 'Grok' - }, - { - id: 'grok-2-1212', - provider: 'grok', - name: 'Grok 2 1212', - group: 'Grok' - } - ], - mistral: [ - { - id: 'pixtral-12b-2409', - provider: 'mistral', - name: 'Pixtral 12B [Free]', - group: 'Pixtral' - }, - { - id: 'pixtral-large-latest', - provider: 'mistral', - name: 'Pixtral Large', - group: 'Pixtral' - }, - { - id: 'ministral-3b-latest', - provider: 'mistral', - name: 'Mistral 3B [Free]', - group: 'Mistral Mini' - }, - { - id: 'ministral-8b-latest', - provider: 'mistral', - name: 'Mistral 8B [Free]', - group: 'Mistral Mini' - }, - { - id: 'codestral-latest', - provider: 'mistral', - name: 'Mistral Codestral', - group: 'Mistral Code' - }, - { - id: 'mistral-large-latest', - provider: 'mistral', - name: 'Mistral Large', - group: 'Mistral Chat' - }, - { - id: 'mistral-small-latest', - provider: 'mistral', - name: 'Mistral Small', - group: 'Mistral Chat' - }, - { - id: 'open-mistral-nemo', - provider: 'mistral', - name: 'Mistral Nemo', - group: 'Mistral Chat' - }, - { - id: 'mistral-embed', - provider: 'mistral', - name: 'Mistral Embedding', - group: 'Mistral Embed' - } - ], - jina: [ - { - id: 'jina-clip-v1', - provider: 'jina', - name: 'jina-clip-v1', - group: 'Jina Clip' - }, - { - id: 'jina-clip-v2', - provider: 'jina', - name: 'jina-clip-v2', - group: 'Jina Clip' - }, - { - id: 'jina-embeddings-v2-base-en', - provider: 'jina', - name: 'jina-embeddings-v2-base-en', - group: 'Jina Embeddings V2' - }, - { - id: 'jina-embeddings-v2-base-es', - provider: 'jina', - name: 'jina-embeddings-v2-base-es', - group: 'Jina Embeddings V2' - }, - { - id: 'jina-embeddings-v2-base-de', - provider: 'jina', - name: 'jina-embeddings-v2-base-de', - group: 'Jina Embeddings V2' - }, - { - id: 'jina-embeddings-v2-base-zh', - provider: 'jina', - name: 'jina-embeddings-v2-base-zh', - group: 'Jina Embeddings V2' - }, - { - id: 'jina-embeddings-v2-base-code', - provider: 'jina', - name: 'jina-embeddings-v2-base-code', - group: 'Jina Embeddings V2' - }, - { - id: 'jina-embeddings-v3', - provider: 'jina', - name: 'jina-embeddings-v3', - group: 'Jina Embeddings V3' - } - ], - fireworks: [ - { - id: 'accounts/fireworks/models/mythomax-l2-13b', - provider: 'fireworks', - name: 'mythomax-l2-13b', - group: 'Gryphe' - }, - { - id: 'accounts/fireworks/models/llama-v3-70b-instruct', - provider: 'fireworks', - name: 'Llama-3-70B-Instruct', - group: 'Llama3' - } - ], - hunyuan: [ - { - id: 'hunyuan-pro', - provider: 'hunyuan', - name: 'hunyuan-pro', - group: 'Hunyuan' - }, - { - id: 'hunyuan-standard', - provider: 'hunyuan', - name: 'hunyuan-standard', - group: 'Hunyuan' - }, - { - id: 'hunyuan-lite', - provider: 'hunyuan', - name: 'hunyuan-lite', - group: 'Hunyuan' - }, - { - id: 'hunyuan-standard-256k', - provider: 'hunyuan', - name: 'hunyuan-standard-256k', - group: 'Hunyuan' - }, - { - id: 'hunyuan-vision', - provider: 'hunyuan', - name: 'hunyuan-vision', - group: 'Hunyuan' - }, - { - id: 'hunyuan-code', - provider: 'hunyuan', - name: 'hunyuan-code', - group: 'Hunyuan' - }, - { - id: 'hunyuan-role', - provider: 'hunyuan', - name: 'hunyuan-role', - group: 'Hunyuan' - }, - { - id: 'hunyuan-turbo', - provider: 'hunyuan', - name: 'hunyuan-turbo', - group: 'Hunyuan' - }, - { - id: 'hunyuan-turbos-latest', - provider: 'hunyuan', - name: 'hunyuan-turbos-latest', - group: 'Hunyuan' - }, - { - id: 'hunyuan-embedding', - provider: 'hunyuan', - name: 'hunyuan-embedding', - group: 'Embedding' - } - ], - nvidia: [ - { - id: '01-ai/yi-large', - provider: 'nvidia', - name: 'yi-large', - group: 'Yi' - }, - { - id: 'meta/llama-3.1-405b-instruct', - provider: 'nvidia', - name: 'llama-3.1-405b-instruct', - group: 'llama-3.1' - } - ], - openrouter: [ - { - id: 'google/gemini-2.5-flash-image-preview', - provider: 'openrouter', - name: 'Google: Gemini 2.5 Flash Image', - group: 'google' - }, - { - id: 'google/gemini-2.5-flash-preview', - provider: 'openrouter', - name: 'Google: Gemini 2.5 Flash Preview', - group: 'google' - }, - { - id: 'qwen/qwen-2.5-7b-instruct:free', - provider: 'openrouter', - name: 'Qwen: Qwen-2.5-7B Instruct', - group: 'qwen' - }, - { - id: 'deepseek/deepseek-chat', - provider: 'openrouter', - name: 'DeepSeek: V3', - group: 'deepseek' - }, - { - id: 'mistralai/mistral-7b-instruct:free', - provider: 'openrouter', - name: 'Mistral: Mistral 7B Instruct', - group: 'mistralai' - } - ], - groq: [ - { - id: 'llama3-8b-8192', - provider: 'groq', - name: 'LLaMA3 8B', - group: 'Llama3' - }, - { - id: 'llama3-70b-8192', - provider: 'groq', - name: 'LLaMA3 70B', - group: 'Llama3' - }, - { - id: 'mistral-saba-24b', - provider: 'groq', - name: 'Mistral Saba 24B', - group: 'Mistral' - }, - { - id: 'gemma-9b-it', - provider: 'groq', - name: 'Gemma 9B', - group: 'Gemma' - } - ], - 'baidu-cloud': [ - { - id: 'deepseek-r1', - provider: 'baidu-cloud', - name: 'DeepSeek R1', - group: 'DeepSeek' - }, - { - id: 'deepseek-v3', - provider: 'baidu-cloud', - name: 'DeepSeek V3', - group: 'DeepSeek' - }, - { - id: 'ernie-4.0-8k-latest', - provider: 'baidu-cloud', - name: 'ERNIE-4.0', - group: 'ERNIE' - }, - { - id: 'ernie-4.0-turbo-8k-latest', - provider: 'baidu-cloud', - name: 'ERNIE 4.0 Trubo', - group: 'ERNIE' - }, - { - id: 'ernie-speed-8k', - provider: 'baidu-cloud', - name: 'ERNIE Speed', - group: 'ERNIE' - }, - { - id: 'ernie-lite-8k', - provider: 'baidu-cloud', - name: 'ERNIE Lite', - group: 'ERNIE' - }, - { - id: 'bge-large-zh', - provider: 'baidu-cloud', - name: 'BGE Large ZH', - group: 'Embedding' - }, - { - id: 'bge-large-en', - provider: 'baidu-cloud', - name: 'BGE Large EN', - group: 'Embedding' - } - ], - dmxapi: [ - { - id: 'Qwen/Qwen2.5-7B-Instruct', - provider: 'dmxapi', - name: 'Qwen/Qwen2.5-7B-Instruct', - group: '免费模型' - }, - { - id: 'ERNIE-Speed-128K', - provider: 'dmxapi', - name: 'ERNIE-Speed-128K', - group: '免费模型' - }, - { - id: 'gpt-4o', - provider: 'dmxapi', - name: 'gpt-4o', - group: 'OpenAI' - }, - { - id: 'gpt-4o-mini', - provider: 'dmxapi', - name: 'gpt-4o-mini', - group: 'OpenAI' - }, - { - id: 'DMXAPI-DeepSeek-R1', - provider: 'dmxapi', - name: 'DMXAPI-DeepSeek-R1', - group: 'DeepSeek' - }, - { - id: 'DMXAPI-DeepSeek-V3', - provider: 'dmxapi', - name: 'DMXAPI-DeepSeek-V3', - group: 'DeepSeek' - }, - { - id: 'claude-3-5-sonnet-20241022', - provider: 'dmxapi', - name: 'claude-3-5-sonnet-20241022', - group: 'Claude' - }, - { - id: 'gemini-2.0-flash', - provider: 'dmxapi', - name: 'gemini-2.0-flash', - group: 'Gemini' - } - ], - perplexity: [ - { - id: 'sonar-reasoning-pro', - provider: 'perplexity', - name: 'sonar-reasoning-pro', - group: 'Sonar' - }, - { - id: 'sonar-reasoning', - provider: 'perplexity', - name: 'sonar-reasoning', - group: 'Sonar' - }, - { - id: 'sonar-pro', - provider: 'perplexity', - name: 'sonar-pro', - group: 'Sonar' - }, - { - id: 'sonar', - provider: 'perplexity', - name: 'sonar', - group: 'Sonar' - }, - { - id: 'sonar-deep-research', - provider: 'perplexity', - name: 'sonar-deep-research', - group: 'Sonar' - } - ], - infini: [ - { - id: 'deepseek-r1', - provider: 'infini', - name: 'deepseek-r1', - group: 'DeepSeek' - }, - { - id: 'deepseek-r1-distill-qwen-32b', - provider: 'infini', - name: 'deepseek-r1-distill-qwen-32b', - group: 'DeepSeek' - }, - { - id: 'deepseek-v3', - provider: 'infini', - name: 'deepseek-v3', - group: 'DeepSeek' - }, - { - id: 'qwen2.5-72b-instruct', - provider: 'infini', - name: 'qwen2.5-72b-instruct', - group: 'Qwen' - }, - { - id: 'qwen2.5-32b-instruct', - provider: 'infini', - name: 'qwen2.5-32b-instruct', - group: 'Qwen' - }, - { - id: 'qwen2.5-14b-instruct', - provider: 'infini', - name: 'qwen2.5-14b-instruct', - group: 'Qwen' - }, - { - id: 'qwen2.5-7b-instruct', - provider: 'infini', - name: 'qwen2.5-7b-instruct', - group: 'Qwen' - }, - { - id: 'qwen2-72b-instruct', - provider: 'infini', - name: 'qwen2-72b-instruct', - group: 'Qwen' - }, - { - id: 'qwq-32b-preview', - provider: 'infini', - name: 'qwq-32b-preview', - group: 'Qwen' - }, - { - id: 'qwen2.5-coder-32b-instruct', - provider: 'infini', - name: 'qwen2.5-coder-32b-instruct', - group: 'Qwen' - }, - { - id: 'llama-3.3-70b-instruct', - provider: 'infini', - name: 'llama-3.3-70b-instruct', - group: 'Llama' - }, - { - id: 'bge-m3', - provider: 'infini', - name: 'bge-m3', - group: 'BAAI' - }, - { - id: 'gemma-2-27b-it', - provider: 'infini', - name: 'gemma-2-27b-it', - group: 'Gemma' - }, - { - id: 'jina-embeddings-v2-base-zh', - provider: 'infini', - name: 'jina-embeddings-v2-base-zh', - group: 'Jina' - }, - { - id: 'jina-embeddings-v2-base-code', - provider: 'infini', - name: 'jina-embeddings-v2-base-code', - group: 'Jina' - } - ], - xirang: [], - 'tencent-cloud-ti': [ - { - id: 'deepseek-r1', - provider: 'tencent-cloud-ti', - name: 'DeepSeek R1', - group: 'DeepSeek' - }, - { - id: 'deepseek-v3', - provider: 'tencent-cloud-ti', - name: 'DeepSeek V3', - group: 'DeepSeek' - } - ], - gpustack: [], - voyageai: [ - { - id: 'voyage-3-large', - provider: 'voyageai', - name: 'voyage-3-large', - group: 'Voyage Embeddings V3' - }, - { - id: 'voyage-3', - provider: 'voyageai', - name: 'voyage-3', - group: 'Voyage Embeddings V3' - }, - { - id: 'voyage-3-lite', - provider: 'voyageai', - name: 'voyage-3-lite', - group: 'Voyage Embeddings V3' - }, - { - id: 'voyage-code-3', - provider: 'voyageai', - name: 'voyage-code-3', - group: 'Voyage Embeddings V3' - }, - { - id: 'voyage-finance-3', - provider: 'voyageai', - name: 'voyage-finance-3', - group: 'Voyage Embeddings V2' - }, - { - id: 'voyage-law-2', - provider: 'voyageai', - name: 'voyage-law-2', - group: 'Voyage Embeddings V2' - }, - { - id: 'voyage-code-2', - provider: 'voyageai', - name: 'voyage-code-2', - group: 'Voyage Embeddings V2' - }, - { - id: 'rerank-2', - provider: 'voyageai', - name: 'rerank-2', - group: 'Voyage Rerank V2' - }, - { - id: 'rerank-2-lite', - provider: 'voyageai', - name: 'rerank-2-lite', - group: 'Voyage Rerank V2' - } - ], - qiniu: [ - { - id: 'deepseek-r1', - provider: 'qiniu', - name: 'DeepSeek R1', - group: 'DeepSeek' - }, - { - id: 'deepseek-r1-search', - provider: 'qiniu', - name: 'DeepSeek R1 Search', - group: 'DeepSeek' - }, - { - id: 'deepseek-r1-32b', - provider: 'qiniu', - name: 'DeepSeek R1 32B', - group: 'DeepSeek' - }, - { - id: 'deepseek-v3', - provider: 'qiniu', - name: 'DeepSeek V3', - group: 'DeepSeek' - }, - { - id: 'deepseek-v3-search', - provider: 'qiniu', - name: 'DeepSeek V3 Search', - group: 'DeepSeek' - }, - { - id: 'deepseek-v3-tool', - provider: 'qiniu', - name: 'DeepSeek V3 Tool', - group: 'DeepSeek' - }, - { - id: 'qwq-32b', - provider: 'qiniu', - name: 'QWQ 32B', - group: 'Qwen' - }, - { - id: 'qwen2.5-72b-instruct', - provider: 'qiniu', - name: 'Qwen2.5 72B Instruct', - group: 'Qwen' - } - ], - tokenflux: [ - { - id: 'gpt-4.1', - provider: 'tokenflux', - name: 'GPT-4.1', - group: 'GPT-4.1' - }, - { - id: 'gpt-4.1-mini', - provider: 'tokenflux', - name: 'GPT-4.1 Mini', - group: 'GPT-4.1' - }, - { - id: 'claude-sonnet-4', - provider: 'tokenflux', - name: 'Claude Sonnet 4', - group: 'Claude' - }, - { - id: 'claude-3-7-sonnet', - provider: 'tokenflux', - name: 'Claude 3.7 Sonnet', - group: 'Claude' - }, - { - id: 'gemini-2.5-pro', - provider: 'tokenflux', - name: 'Gemini 2.5 Pro', - group: 'Gemini' - }, - { - id: 'gemini-2.5-flash', - provider: 'tokenflux', - name: 'Gemini 2.5 Flash', - group: 'Gemini' - }, - { - id: 'deepseek-r1', - provider: 'tokenflux', - name: 'DeepSeek R1', - group: 'DeepSeek' - }, - { - id: 'deepseek-v3', - provider: 'tokenflux', - name: 'DeepSeek V3', - group: 'DeepSeek' - }, - { - id: 'qwen-max', - provider: 'tokenflux', - name: 'Qwen Max', - group: 'Qwen' - }, - { - id: 'qwen-plus', - provider: 'tokenflux', - name: 'Qwen Plus', - group: 'Qwen' - } - ], - cephalon: [ - { - id: 'DeepSeek-R1', - provider: 'cephalon', - name: 'DeepSeek-R1满血版', - group: 'DeepSeek' - } - ], - lanyun: [ - { - id: '/maas/deepseek-ai/DeepSeek-R1-0528', - name: 'deepseek-ai/DeepSeek-R1', - provider: 'lanyun', - group: 'deepseek-ai' - }, - { - id: '/maas/deepseek-ai/DeepSeek-V3-0324', - name: 'deepseek-ai/DeepSeek-V3', - provider: 'lanyun', - group: 'deepseek-ai' - }, - { - id: '/maas/qwen/Qwen2.5-72B-Instruct', - provider: 'lanyun', - name: 'Qwen2.5-72B-Instruct', - group: 'Qwen' - }, - { - id: '/maas/qwen/Qwen3-235B-A22B', - name: 'Qwen/Qwen3-235B', - provider: 'lanyun', - group: 'Qwen' - }, - { - id: '/maas/minimax/MiniMax-M1-80k', - name: 'MiniMax-M1-80k', - provider: 'lanyun', - group: 'MiniMax' - }, - { - id: '/maas/google/Gemma3-27B', - name: 'Gemma3-27B', - provider: 'lanyun', - group: 'google' - } - ], - 'new-api': [], - 'aws-bedrock': [], - poe: [ - { - id: 'gpt-4o', - name: 'GPT-4o', - provider: 'poe', - group: 'poe' - } - ] -} - -// Vision models -const visionAllowedModels = [ - 'llava', - 'moondream', - 'minicpm', - 'gemini-1\\.5', - 'gemini-2\\.0', - 'gemini-2\\.5', - 'gemini-exp', - 'claude-3', - 'claude-sonnet-4', - 'claude-opus-4', - 'vision', - 'glm-4(?:\\.\\d+)?v(?:-[\\w-]+)?', - 'qwen-vl', - 'qwen2-vl', - 'qwen2.5-vl', - 'qwen2.5-omni', - 'qvq', - 'internvl2', - 'grok-vision-beta', - 'grok-4(?:-[\\w-]+)?', - 'pixtral', - 'gpt-4(?:-[\\w-]+)', - 'gpt-4.1(?:-[\\w-]+)?', - 'gpt-4o(?:-[\\w-]+)?', - 'gpt-4.5(?:-[\\w-]+)', - 'gpt-5(?:-[\\w-]+)?', - 'chatgpt-4o(?:-[\\w-]+)?', - 'o1(?:-[\\w-]+)?', - 'o3(?:-[\\w-]+)?', - 'o4(?:-[\\w-]+)?', - 'deepseek-vl(?:[\\w-]+)?', - 'kimi-latest', - 'gemma-3(?:-[\\w-]+)', - 'doubao-seed-1[.-]6(?:-[\\w-]+)?', - 'kimi-thinking-preview', - `gemma3(?:[-:\\w]+)?`, - 'kimi-vl-a3b-thinking(?:-[\\w-]+)?', - 'llama-guard-4(?:-[\\w-]+)?', - 'llama-4(?:-[\\w-]+)?', - 'step-1o(?:.*vision)?', - 'step-1v(?:-[\\w-]+)?' -] - -const visionExcludedModels = [ - 'gpt-4-\\d+-preview', - 'gpt-4-turbo-preview', - 'gpt-4-32k', - 'gpt-4-\\d+', - 'o1-mini', - 'o3-mini', - 'o1-preview', - 'AIDC-AI/Marco-o1' -] -export const VISION_REGEX = new RegExp( - `\\b(?!(?:${visionExcludedModels.join('|')})\\b)(${visionAllowedModels.join('|')})\\b`, - 'i' -) - -// Text to image models -export const TEXT_TO_IMAGE_REGEX = /flux|diffusion|stabilityai|sd-|dall|cogview|janus|midjourney|mj-|image|gpt-image/i - -// Reasoning models -export const REASONING_REGEX = - /^(o\d+(?:-[\w-]+)?|.*\b(?:reasoning|reasoner|thinking)\b.*|.*-[rR]\d+.*|.*\bqwq(?:-[\w-]+)?\b.*|.*\bhunyuan-t1(?:-[\w-]+)?\b.*|.*\bglm-zero-preview\b.*|.*\bgrok-(?:3-mini|4)(?:-[\w-]+)?\b.*)$/i - -// Embedding models -export const EMBEDDING_REGEX = - /(?:^text-|embed|bge-|e5-|LLM2Vec|retrieval|uae-|gte-|jina-clip|jina-embeddings|voyage-)/i - -// Rerank models -export const RERANKING_REGEX = /(?:rerank|re-rank|re-ranker|re-ranking|retrieval|retriever)/i - -export const NOT_SUPPORTED_REGEX = /(?:^tts|whisper|speech)/i - -// Tool calling models -export const FUNCTION_CALLING_MODELS = [ - 'gpt-4o', - 'gpt-4o-mini', - 'gpt-4', - 'gpt-4.5', - 'gpt-oss(?:-[\\w-]+)', - 'gpt-5(?:-[0-9-]+)?', - 'o(1|3|4)(?:-[\\w-]+)?', - 'claude', - 'qwen', - 'qwen3', - 'hunyuan', - 'deepseek', - 'glm-4(?:-[\\w-]+)?', - 'glm-4.5(?:-[\\w-]+)?', - 'learnlm(?:-[\\w-]+)?', - 'gemini(?:-[\\w-]+)?', // 提前排除了gemini的嵌入模型 - 'grok-3(?:-[\\w-]+)?', - 'doubao-seed-1[.-]6(?:-[\\w-]+)?', - 'kimi-k2(?:-[\\w-]+)?' -] - -const FUNCTION_CALLING_EXCLUDED_MODELS = [ - 'aqa(?:-[\\w-]+)?', - 'imagen(?:-[\\w-]+)?', - 'o1-mini', - 'o1-preview', - 'AIDC-AI/Marco-o1', - 'gemini-1(?:\\.[\\w-]+)?', - 'qwen-mt(?:-[\\w-]+)?', - 'gpt-5-chat(?:-[\\w-]+)?', - 'glm-4\\.5v' -] - -export const FUNCTION_CALLING_REGEX = new RegExp( - `\\b(?!(?:${FUNCTION_CALLING_EXCLUDED_MODELS.join('|')})\\b)(?:${FUNCTION_CALLING_MODELS.join('|')})\\b`, - 'i' -) - -export const CLAUDE_SUPPORTED_WEBSEARCH_REGEX = new RegExp( - `\\b(?:claude-3(-|\\.)(7|5)-sonnet(?:-[\\w-]+)|claude-3(-|\\.)5-haiku(?:-[\\w-]+)|claude-sonnet-4(?:-[\\w-]+)?|claude-opus-4(?:-[\\w-]+)?)\\b`, - 'i' -) - -// 模型类型到支持的reasoning_effort的映射表 -// TODO: refactor this. too many identical options -export const MODEL_SUPPORTED_REASONING_EFFORT: ReasoningEffortConfig = { - default: ['low', 'medium', 'high'] as const, - o: ['low', 'medium', 'high'] as const, - gpt5: ['minimal', 'low', 'medium', 'high'] as const, - grok: ['low', 'high'] as const, - gemini: ['low', 'medium', 'high', 'auto'] as const, - gemini_pro: ['low', 'medium', 'high', 'auto'] as const, - qwen: ['low', 'medium', 'high'] as const, - qwen_thinking: ['low', 'medium', 'high'] as const, - doubao: ['auto', 'high'] as const, - doubao_no_auto: ['high'] as const, - hunyuan: ['auto'] as const, - zhipu: ['auto'] as const, - perplexity: ['low', 'medium', 'high'] as const, - deepseek_hybrid: ['auto'] as const -} as const - -// 模型类型到支持选项的映射表 -export const MODEL_SUPPORTED_OPTIONS: ThinkingOptionConfig = { - default: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.default] as const, - o: MODEL_SUPPORTED_REASONING_EFFORT.o, - gpt5: [...MODEL_SUPPORTED_REASONING_EFFORT.gpt5] as const, - grok: MODEL_SUPPORTED_REASONING_EFFORT.grok, - gemini: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.gemini] as const, - gemini_pro: MODEL_SUPPORTED_REASONING_EFFORT.gemini_pro, - qwen: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.qwen] as const, - qwen_thinking: MODEL_SUPPORTED_REASONING_EFFORT.qwen_thinking, - doubao: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.doubao] as const, - doubao_no_auto: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.doubao_no_auto] as const, - hunyuan: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.hunyuan] as const, - zhipu: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.zhipu] as const, - perplexity: MODEL_SUPPORTED_REASONING_EFFORT.perplexity, - deepseek_hybrid: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.deepseek_hybrid] as const -} as const - -export const getThinkModelType = (model: Model): ThinkingModelType => { - let thinkingModelType: ThinkingModelType = 'default' - if (isGPT5SeriesModel(model)) { - thinkingModelType = 'gpt5' - } else if (isSupportedReasoningEffortOpenAIModel(model)) { - thinkingModelType = 'o' - } else if (isSupportedThinkingTokenGeminiModel(model)) { - if (GEMINI_FLASH_MODEL_REGEX.test(model.id)) { - thinkingModelType = 'gemini' - } else { - thinkingModelType = 'gemini_pro' - } - } else if (isSupportedReasoningEffortGrokModel(model)) thinkingModelType = 'grok' - else if (isSupportedThinkingTokenQwenModel(model)) { - if (isQwenAlwaysThinkModel(model)) { - thinkingModelType = 'qwen_thinking' - } - thinkingModelType = 'qwen' - } else if (isSupportedThinkingTokenDoubaoModel(model)) { - if (isDoubaoThinkingAutoModel(model)) { - thinkingModelType = 'doubao' - } else { - thinkingModelType = 'doubao_no_auto' - } - } else if (isSupportedThinkingTokenHunyuanModel(model)) thinkingModelType = 'hunyuan' - else if (isSupportedReasoningEffortPerplexityModel(model)) thinkingModelType = 'perplexity' - else if (isSupportedThinkingTokenZhipuModel(model)) thinkingModelType = 'zhipu' - else if (isDeepSeekHybridInferenceModel(model)) thinkingModelType = 'deepseek_hybrid' - return thinkingModelType -} - -export function isFunctionCallingModel(model?: Model): boolean { - if (!model || isEmbeddingModel(model) || isRerankModel(model) || isTextToImageModel(model)) { - return false - } - - const modelId = getLowerBaseModelName(model.id) - - if (isUserSelectedModelType(model, 'function_calling') !== undefined) { - return isUserSelectedModelType(model, 'function_calling')! - } - - if (model.provider === 'qiniu') { - return ['deepseek-v3-tool', 'deepseek-v3-0324', 'qwq-32b', 'qwen2.5-72b-instruct'].includes(modelId) - } - - if (model.provider === 'doubao' || modelId.includes('doubao')) { - return FUNCTION_CALLING_REGEX.test(modelId) || FUNCTION_CALLING_REGEX.test(model.name) - } - - if (['deepseek', 'anthropic', 'kimi', 'moonshot'].includes(model.provider)) { - return true - } - - // 2025/08/26 百炼与火山引擎均不支持 v3.1 函数调用 - // 先默认支持 - if (isDeepSeekHybridInferenceModel(model)) { - if (isSystemProviderId(model.provider)) { - switch (model.provider) { - case 'dashscope': - case 'doubao': - // case 'nvidia': // nvidia api 太烂了 测不了能不能用 先假设能用 - return false - } - } - return true - } - - return FUNCTION_CALLING_REGEX.test(modelId) -} - -// For middleware to identify models that must use the dedicated Image API -export const DEDICATED_IMAGE_MODELS = [ - 'grok-2-image', - 'grok-2-image-1212', - 'grok-2-image-latest', - 'dall-e-3', - 'dall-e-2', - 'gpt-image-1' -] - -// Models that should auto-enable image generation button when selected -export const AUTO_ENABLE_IMAGE_MODELS = ['gemini-2.5-flash-image-preview', ...DEDICATED_IMAGE_MODELS] - -export const OPENAI_IMAGE_GENERATION_MODELS = [ - 'o3', - 'gpt-4o', - 'gpt-4o-mini', - 'gpt-4.1', - 'gpt-4.1-mini', - 'gpt-4.1-nano', - 'gpt-5', - 'gpt-image-1' -] - -export const GENERATE_IMAGE_MODELS = [ - 'gemini-2.0-flash-exp', - 'gemini-2.0-flash-exp-image-generation', - 'gemini-2.0-flash-preview-image-generation', - 'gemini-2.5-flash-image-preview', - ...DEDICATED_IMAGE_MODELS -] - -export const isDedicatedImageGenerationModel = (model: Model): boolean => { - if (!model) return false - - const modelId = getLowerBaseModelName(model.id) - return DEDICATED_IMAGE_MODELS.some((m) => modelId.includes(m)) -} - -export const isAutoEnableImageGenerationModel = (model: Model): boolean => { - if (!model) return false - - const modelId = getLowerBaseModelName(model.id) - return AUTO_ENABLE_IMAGE_MODELS.some((m) => modelId.includes(m)) -} - -export function isGenerateImageModel(model: Model): boolean { - if (!model) { - return false - } - - const provider = getProviderByModel(model) - - if (!provider) { - return false - } - - if (isEmbeddingModel(model)) { - return false - } - - const modelId = getLowerBaseModelName(model.id, '/') - - if (provider.type === 'openai-response') { - return ( - OPENAI_IMAGE_GENERATION_MODELS.some((imageModel) => modelId.includes(imageModel)) || - GENERATE_IMAGE_MODELS.some((imageModel) => modelId.includes(imageModel)) - ) - } - - return GENERATE_IMAGE_MODELS.some((imageModel) => modelId.includes(imageModel)) -} - -export const GEMINI_SEARCH_REGEX = new RegExp('gemini-2\\..*', 'i') - -export const OPENAI_NO_SUPPORT_DEV_ROLE_MODELS = ['o1-preview', 'o1-mini'] - -export const PERPLEXITY_SEARCH_MODELS = [ - 'sonar-pro', - 'sonar', - 'sonar-reasoning', - 'sonar-reasoning-pro', - 'sonar-deep-research' -] - -export function isTextToImageModel(model: Model): boolean { - const modelId = getLowerBaseModelName(model.id) - return TEXT_TO_IMAGE_REGEX.test(modelId) -} - -export function isEmbeddingModel(model: Model): boolean { - if (!model || isRerankModel(model)) { - return false - } - - const modelId = getLowerBaseModelName(model.id) - - if (isUserSelectedModelType(model, 'embedding') !== undefined) { - return isUserSelectedModelType(model, 'embedding')! - } - - if (['anthropic'].includes(model?.provider)) { - return false - } - - if (model.provider === 'doubao' || modelId.includes('doubao')) { - return EMBEDDING_REGEX.test(model.name) - } - - return EMBEDDING_REGEX.test(modelId) || false -} - -export function isRerankModel(model: Model): boolean { - if (isUserSelectedModelType(model, 'rerank') !== undefined) { - return isUserSelectedModelType(model, 'rerank')! - } - const modelId = getLowerBaseModelName(model.id) - return model ? RERANKING_REGEX.test(modelId) || false : false -} - -export function isVisionModel(model: Model): boolean { - if (!model || isEmbeddingModel(model) || isRerankModel(model)) { - return false - } - // 新添字段 copilot-vision-request 后可使用 vision - // if (model.provider === 'copilot') { - // return false - // } - if (isUserSelectedModelType(model, 'vision') !== undefined) { - return isUserSelectedModelType(model, 'vision')! - } - - const modelId = getLowerBaseModelName(model.id) - if (model.provider === 'doubao' || modelId.includes('doubao')) { - return VISION_REGEX.test(model.name) || VISION_REGEX.test(modelId) || false - } - - return VISION_REGEX.test(modelId) || false -} - -export function isOpenAIReasoningModel(model: Model): boolean { - const modelId = getLowerBaseModelName(model.id, '/') - return isSupportedReasoningEffortOpenAIModel(model) || modelId.includes('o1') -} - -export function isOpenAILLMModel(model: Model): boolean { - if (!model) { - return false - } - const modelId = getLowerBaseModelName(model.id) - - if (modelId.includes('gpt-4o-image')) { - return false - } - if (isOpenAIReasoningModel(model)) { - return true - } - if (modelId.includes('gpt')) { - return true - } - return false -} - -export function isOpenAIModel(model: Model): boolean { - if (!model) { - return false - } - const modelId = getLowerBaseModelName(model.id) - - return modelId.includes('gpt') || isOpenAIReasoningModel(model) -} - -export function isSupportFlexServiceTierModel(model: Model): boolean { - if (!model) { - return false - } - const modelId = getLowerBaseModelName(model.id) - return ( - (modelId.includes('o3') && !modelId.includes('o3-mini')) || modelId.includes('o4-mini') || modelId.includes('gpt-5') - ) -} -export function isSupportedFlexServiceTier(model: Model): boolean { - return isSupportFlexServiceTierModel(model) -} - -export function isSupportVerbosityModel(model: Model): boolean { - const modelId = getLowerBaseModelName(model.id) - return isGPT5SeriesModel(model) && !modelId.includes('chat') -} - -export function isSupportedReasoningEffortOpenAIModel(model: Model): boolean { - const modelId = getLowerBaseModelName(model.id) - return ( - (modelId.includes('o1') && !(modelId.includes('o1-preview') || modelId.includes('o1-mini'))) || - modelId.includes('o3') || - modelId.includes('o4') || - modelId.includes('gpt-oss') || - (isGPT5SeriesModel(model) && !modelId.includes('chat')) - ) -} - -export function isOpenAIChatCompletionOnlyModel(model: Model): boolean { - if (!model) { - return false - } - - const modelId = getLowerBaseModelName(model.id) - return ( - modelId.includes('gpt-4o-search-preview') || - modelId.includes('gpt-4o-mini-search-preview') || - modelId.includes('o1-mini') || - modelId.includes('o1-preview') - ) -} - -export function isOpenAIWebSearchChatCompletionOnlyModel(model: Model): boolean { - const modelId = getLowerBaseModelName(model.id) - return modelId.includes('gpt-4o-search-preview') || modelId.includes('gpt-4o-mini-search-preview') -} - -export function isOpenAIWebSearchModel(model: Model): boolean { - const modelId = getLowerBaseModelName(model.id) - - return ( - modelId.includes('gpt-4o-search-preview') || - modelId.includes('gpt-4o-mini-search-preview') || - (modelId.includes('gpt-4.1') && !modelId.includes('gpt-4.1-nano')) || - (modelId.includes('gpt-4o') && !modelId.includes('gpt-4o-image')) || - modelId.includes('o3') || - modelId.includes('o4') || - (modelId.includes('gpt-5') && !modelId.includes('chat')) - ) -} - -/** 用于判断是否支持控制思考,但不一定以reasoning_effort的方式 */ -export function isSupportedThinkingTokenModel(model?: Model): boolean { - if (!model) { - return false - } - - // Specifically for DeepSeek V3.1. White list for now - if (isDeepSeekHybridInferenceModel(model)) { - return ( - ['openrouter', 'dashscope', 'modelscope', 'doubao', 'silicon', 'nvidia', 'ppio'] satisfies SystemProviderId[] - ).some((id) => id === model.provider) - } - - return ( - isSupportedThinkingTokenGeminiModel(model) || - isSupportedThinkingTokenQwenModel(model) || - isSupportedThinkingTokenClaudeModel(model) || - isSupportedThinkingTokenDoubaoModel(model) || - isSupportedThinkingTokenHunyuanModel(model) || - isSupportedThinkingTokenZhipuModel(model) - ) -} - -export function isSupportedReasoningEffortModel(model?: Model): boolean { - if (!model) { - return false - } - - return ( - isSupportedReasoningEffortOpenAIModel(model) || - isSupportedReasoningEffortGrokModel(model) || - isSupportedReasoningEffortPerplexityModel(model) - ) -} - -export function isGrokModel(model?: Model): boolean { - if (!model) { - return false - } - const modelId = getLowerBaseModelName(model.id) - return modelId.includes('grok') -} - -export function isSupportedReasoningEffortGrokModel(model?: Model): boolean { - if (!model) { - return false - } - - const modelId = getLowerBaseModelName(model.id) - if (modelId.includes('grok-3-mini')) { - return true - } - - return false -} - -export function isGrokReasoningModel(model?: Model): boolean { - if (!model) { - return false - } - const modelId = getLowerBaseModelName(model.id) - if (isSupportedReasoningEffortGrokModel(model) || modelId.includes('grok-4')) { - return true - } - - return false -} - -export function isGeminiReasoningModel(model?: Model): boolean { - if (!model) { - return false - } - - const modelId = getLowerBaseModelName(model.id) - if (modelId.startsWith('gemini') && modelId.includes('thinking')) { - return true - } - - if (isSupportedThinkingTokenGeminiModel(model)) { - return true - } - - return false -} - -export const isSupportedThinkingTokenGeminiModel = (model: Model): boolean => { - const modelId = getLowerBaseModelName(model.id, '/') - if (modelId.includes('gemini-2.5')) { - if (modelId.includes('image') || modelId.includes('tts')) { - return false - } - return true - } else { - return false - } -} - -/** 是否为Qwen推理模型 */ -export function isQwenReasoningModel(model?: Model): boolean { - if (!model) { - return false - } - - const modelId = getLowerBaseModelName(model.id, '/') - - if (modelId.startsWith('qwen3')) { - if (modelId.includes('thinking')) { - return true - } else if (modelId.includes('instruct')) { - return false - } - return true - } - - if (isSupportedThinkingTokenQwenModel(model)) { - return true - } - - if (modelId.includes('qwq') || modelId.includes('qvq')) { - return true - } - - return false -} - -/** 是否为支持思考控制的Qwen3推理模型 */ -export function isSupportedThinkingTokenQwenModel(model?: Model): boolean { - if (!model) { - return false - } - - const modelId = getLowerBaseModelName(model.id, '/') - - if (modelId.includes('coder')) { - return false - } - - if (modelId.startsWith('qwen3')) { - // instruct 是非思考模型 thinking 是思考模型,二者都不能控制思考 - if (modelId.includes('instruct') || modelId.includes('thinking')) { - return false - } - return true - } - - return [ - 'qwen-plus', - 'qwen-plus-latest', - 'qwen-plus-0428', - 'qwen-plus-2025-04-28', - 'qwen-plus-0714', - 'qwen-plus-2025-07-14', - 'qwen-turbo', - 'qwen-turbo-latest', - 'qwen-turbo-0428', - 'qwen-turbo-2025-04-28', - 'qwen-turbo-0715', - 'qwen-turbo-2025-07-15', - 'qwen-flash', - 'qwen-flash-2025-07-28' - ].includes(modelId) -} - -/** 是否为不支持思考控制的Qwen推理模型 */ -export function isQwenAlwaysThinkModel(model?: Model): boolean { - if (!model) { - return false - } - const modelId = getLowerBaseModelName(model.id, '/') - return modelId.startsWith('qwen3') && modelId.includes('thinking') -} - -export function isSupportedThinkingTokenDoubaoModel(model?: Model): boolean { - if (!model) { - return false - } - - const modelId = getLowerBaseModelName(model.id, '/') - - return DOUBAO_THINKING_MODEL_REGEX.test(modelId) || DOUBAO_THINKING_MODEL_REGEX.test(model.name) -} - -export function isClaudeReasoningModel(model?: Model): boolean { - if (!model) { - return false - } - const modelId = getLowerBaseModelName(model.id, '/') - return ( - modelId.includes('claude-3-7-sonnet') || - modelId.includes('claude-3.7-sonnet') || - modelId.includes('claude-sonnet-4') || - modelId.includes('claude-opus-4') - ) -} - -export const isSupportedThinkingTokenClaudeModel = isClaudeReasoningModel - -export const isSupportedThinkingTokenHunyuanModel = (model?: Model): boolean => { - if (!model) { - return false - } - const modelId = getLowerBaseModelName(model.id, '/') - return modelId.includes('hunyuan-a13b') -} - -export const isHunyuanReasoningModel = (model?: Model): boolean => { - if (!model) { - return false - } - const modelId = getLowerBaseModelName(model.id, '/') - - return isSupportedThinkingTokenHunyuanModel(model) || modelId.includes('hunyuan-t1') -} - -export const isPerplexityReasoningModel = (model?: Model): boolean => { - if (!model) { - return false - } - - const modelId = getLowerBaseModelName(model.id, '/') - return isSupportedReasoningEffortPerplexityModel(model) || modelId.includes('reasoning') -} - -export const isSupportedReasoningEffortPerplexityModel = (model: Model): boolean => { - const modelId = getLowerBaseModelName(model.id, '/') - return modelId.includes('sonar-deep-research') -} - -export const isSupportedThinkingTokenZhipuModel = (model: Model): boolean => { - const modelId = getLowerBaseModelName(model.id, '/') - return modelId.includes('glm-4.5') -} - -export const isDeepSeekHybridInferenceModel = (model: Model) => { - const modelId = getLowerBaseModelName(model.id) - // deepseek官方使用chat和reasoner做推理控制,其他provider需要单独判断,id可能会有所差别 - // openrouter: deepseek/deepseek-chat-v3.1 不知道会不会有其他provider仿照ds官方分出一个同id的作为非思考模式的模型,这里有风险 - return /deepseek-v3(?:\.1|-1-\d+)?/.test(modelId) || modelId.includes('deepseek-chat-v3.1') -} - -export const isSupportedThinkingTokenDeepSeekModel = isDeepSeekHybridInferenceModel - -export const isZhipuReasoningModel = (model?: Model): boolean => { - if (!model) { - return false - } - const modelId = getLowerBaseModelName(model.id, '/') - return isSupportedThinkingTokenZhipuModel(model) || modelId.includes('glm-z1') -} - -export const isStepReasoningModel = (model?: Model): boolean => { - if (!model) { - return false - } - const modelId = getLowerBaseModelName(model.id, '/') - return modelId.includes('step-3') || modelId.includes('step-r1-v-mini') -} - -export function isReasoningModel(model?: Model): boolean { - if (!model || isEmbeddingModel(model) || isRerankModel(model) || isTextToImageModel(model)) { - return false - } - - if (isUserSelectedModelType(model, 'reasoning') !== undefined) { - return isUserSelectedModelType(model, 'reasoning')! - } - - const modelId = getLowerBaseModelName(model.id) - - if (model.provider === 'doubao' || modelId.includes('doubao')) { - return ( - REASONING_REGEX.test(modelId) || - REASONING_REGEX.test(model.name) || - isSupportedThinkingTokenDoubaoModel(model) || - isDeepSeekHybridInferenceModel(model) || - isDeepSeekHybridInferenceModel({ ...model, id: model.name }) || - false - ) - } - - if ( - isClaudeReasoningModel(model) || - isOpenAIReasoningModel(model) || - isGeminiReasoningModel(model) || - isQwenReasoningModel(model) || - isGrokReasoningModel(model) || - isHunyuanReasoningModel(model) || - isPerplexityReasoningModel(model) || - isZhipuReasoningModel(model) || - isStepReasoningModel(model) || - isDeepSeekHybridInferenceModel(model) || - modelId.includes('magistral') || - modelId.includes('minimax-m1') || - modelId.includes('pangu-pro-moe') - ) { - return true - } - - return REASONING_REGEX.test(modelId) || false -} - -export function isSupportedModel(model: OpenAI.Models.Model): boolean { - if (!model) { - return false - } - - const modelId = getLowerBaseModelName(model.id) - - return !NOT_SUPPORTED_REGEX.test(modelId) -} - -export function isNotSupportTemperatureAndTopP(model: Model): boolean { - if (!model) { - return true - } - - if ( - (isOpenAIReasoningModel(model) && !isOpenAIOpenWeightModel(model)) || - isOpenAIChatCompletionOnlyModel(model) || - isQwenMTModel(model) - ) { - return true - } - - return false -} - -export function isWebSearchModel(model: Model): boolean { - if (!model || isEmbeddingModel(model) || isRerankModel(model) || isTextToImageModel(model)) { - return false - } - - if (isUserSelectedModelType(model, 'web_search') !== undefined) { - return isUserSelectedModelType(model, 'web_search')! - } - - const provider = getProviderByModel(model) - - if (!provider) { - return false - } - - const isEmbedding = isEmbeddingModel(model) - - if (isEmbedding) { - return false - } - - const modelId = getLowerBaseModelName(model.id, '/') - - // 不管哪个供应商都判断了 - if (isAnthropicModel(model)) { - return CLAUDE_SUPPORTED_WEBSEARCH_REGEX.test(modelId) - } - - if (provider.type === 'openai-response') { - if (isOpenAIWebSearchModel(model)) { - return true - } - - return false - } - - if (provider.id === 'perplexity') { - return PERPLEXITY_SEARCH_MODELS.includes(modelId) - } - - if (provider.id === 'aihubmix') { - // modelId 不以-search结尾 - if (!modelId.endsWith('-search') && GEMINI_SEARCH_REGEX.test(modelId)) { - return true - } - - if (isOpenAIWebSearchModel(model)) { - return true - } - - return false - } - - if (provider?.type === 'openai') { - if (GEMINI_SEARCH_REGEX.test(modelId) || isOpenAIWebSearchModel(model)) { - return true - } - } - - if (provider.id === 'gemini' || provider?.type === 'gemini' || provider.type === 'vertexai') { - return GEMINI_SEARCH_REGEX.test(modelId) - } - - if (provider.id === 'hunyuan') { - return modelId !== 'hunyuan-lite' - } - - if (provider.id === 'zhipu') { - return modelId?.startsWith('glm-4-') - } - - if (provider.id === 'dashscope') { - const models = ['qwen-turbo', 'qwen-max', 'qwen-plus', 'qwq', 'qwen-flash'] - // matches id like qwen-max-0919, qwen-max-latest - return models.some((i) => modelId.startsWith(i)) - } - - if (provider.id === 'openrouter') { - return true - } - - if (provider.id === 'grok') { - return true - } - - return false -} - -export function isMandatoryWebSearchModel(model: Model): boolean { - if (!model) { - return false - } - - const provider = getProviderByModel(model) - - if (!provider) { - return false - } - - const modelId = getLowerBaseModelName(model.id) - - if (provider.id === 'perplexity' || provider.id === 'openrouter') { - return PERPLEXITY_SEARCH_MODELS.includes(modelId) - } - - return false -} - -export function isOpenRouterBuiltInWebSearchModel(model: Model): boolean { - if (!model) { - return false - } - - const provider = getProviderByModel(model) - - if (provider.id !== 'openrouter') { - return false - } - - const modelId = getLowerBaseModelName(model.id) - - return isOpenAIWebSearchChatCompletionOnlyModel(model) || modelId.includes('sonar') -} - -export function isNotSupportedImageSizeModel(model?: Model): boolean { - if (!model) { - return false - } - - const baseName = getLowerBaseModelName(model.id, '/') - - return baseName.includes('grok-2-image') -} - -export function getOpenAIWebSearchParams(model: Model, isEnableWebSearch?: boolean): Record { - if (!isEnableWebSearch) { - return {} - } - - const webSearchTools = getWebSearchTools(model) - - if (model.provider === 'grok') { - return { - search_parameters: { - mode: 'auto', - return_citations: true, - sources: [{ type: 'web' }, { type: 'x' }, { type: 'news' }] - } - } - } - - if (model.provider === 'hunyuan') { - return { enable_enhancement: true, citation: true, search_info: true } - } - - if (model.provider === 'dashscope') { - return { - enable_search: true, - search_options: { - forced_search: true - } - } - } - - if (isOpenAIWebSearchChatCompletionOnlyModel(model)) { - return { - web_search_options: {} - } - } - - if (model.provider === 'openrouter') { - return { - plugins: [{ id: 'web', search_prompts: WEB_SEARCH_PROMPT_FOR_OPENROUTER }] - } - } - - return { - tools: webSearchTools - } -} - -export function isGemmaModel(model?: Model): boolean { - if (!model) { - return false - } - - const modelId = getLowerBaseModelName(model.id) - return modelId.includes('gemma-') || model.group === 'Gemma' -} - -export function isZhipuModel(model?: Model): boolean { - if (!model) { - return false - } - - return model.provider === 'zhipu' -} - -export function isHunyuanSearchModel(model?: Model): boolean { - if (!model) { - return false - } - - const modelId = getLowerBaseModelName(model.id) - - if (model.provider === 'hunyuan') { - return modelId !== 'hunyuan-lite' - } - - return false -} - -/** - * 按 Qwen 系列模型分组 - * @param models 模型列表 - * @returns 分组后的模型 - */ -export function groupQwenModels(models: Model[]): Record { - return models.reduce( - (groups, model) => { - const modelId = getLowerBaseModelName(model.id) - // 匹配 Qwen 系列模型的前缀 - const prefixMatch = modelId.match(/^(qwen(?:\d+\.\d+|2(?:\.\d+)?|-\d+b|-(?:max|coder|vl)))/i) - // 匹配 qwen2.5、qwen2、qwen-7b、qwen-max、qwen-coder 等 - const groupKey = prefixMatch ? prefixMatch[1] : model.group || '其他' - - if (!groups[groupKey]) { - groups[groupKey] = [] - } - groups[groupKey].push(model) - - return groups - }, - {} as Record - ) -} - -export const THINKING_TOKEN_MAP: Record = { - // Gemini models - 'gemini-2\\.5-flash-lite.*$': { min: 512, max: 24576 }, - 'gemini-.*-flash.*$': { min: 0, max: 24576 }, - 'gemini-.*-pro.*$': { min: 128, max: 32768 }, - - // Qwen models - 'qwen3-235b-a22b-thinking-2507$': { min: 0, max: 81_920 }, - 'qwen3-30b-a3b-thinking-2507$': { min: 0, max: 81_920 }, - 'qwen-plus-2025-07-28$': { min: 0, max: 81_920 }, - 'qwen-plus-latest$': { min: 0, max: 81_920 }, - 'qwen3-1\\.7b$': { min: 0, max: 30_720 }, - 'qwen3-0\\.6b$': { min: 0, max: 30_720 }, - 'qwen-plus.*$': { min: 0, max: 38_912 }, - 'qwen-turbo.*$': { min: 0, max: 38_912 }, - 'qwen-flash.*$': { min: 0, max: 81_920 }, - 'qwen3-.*$': { min: 1024, max: 38_912 }, - - // Claude models - 'claude-3[.-]7.*sonnet.*$': { min: 1024, max: 64000 }, - 'claude-(:?sonnet|opus)-4.*$': { min: 1024, max: 32000 } -} - -export const findTokenLimit = (modelId: string): { min: number; max: number } | undefined => { - for (const [pattern, limits] of Object.entries(THINKING_TOKEN_MAP)) { - if (new RegExp(pattern, 'i').test(modelId)) { - return limits - } - } - return undefined -} - -// Doubao 支持思考模式的模型正则 -export const DOUBAO_THINKING_MODEL_REGEX = - /doubao-(?:1[.-]5-thinking-vision-pro|1[.-]5-thinking-pro-m|seed-1[.-]6(?:-flash)?(?!-(?:thinking)(?:-|$)))(?:-[\w-]+)*/i - -// 支持 auto 的 Doubao 模型 doubao-seed-1.6-xxx doubao-seed-1-6-xxx doubao-1-5-thinking-pro-m-xxx -export const DOUBAO_THINKING_AUTO_MODEL_REGEX = - /doubao-(1-5-thinking-pro-m|seed-1[.-]6)(?!-(?:flash|thinking)(?:-|$))(?:-[\w-]+)*/i - -export function isDoubaoThinkingAutoModel(model: Model): boolean { - const modelId = getLowerBaseModelName(model.id) - return DOUBAO_THINKING_AUTO_MODEL_REGEX.test(modelId) || DOUBAO_THINKING_AUTO_MODEL_REGEX.test(model.name) -} - -export const GEMINI_FLASH_MODEL_REGEX = new RegExp('gemini-.*-flash.*$') - -// 模型集合功能测试 -export const isVisionModels = (models: Model[]) => { - return models.every((model) => isVisionModel(model)) -} - -export const isGenerateImageModels = (models: Model[]) => { - return models.every((model) => isGenerateImageModel(model)) -} - -export const isAnthropicModel = (model?: Model): boolean => { - if (!model) { - return false - } - - const modelId = getLowerBaseModelName(model.id) - return modelId.startsWith('claude') -} - -export const isQwenMTModel = (model: Model): boolean => { - const modelId = getLowerBaseModelName(model.id) - return modelId.includes('qwen-mt') -} - -export const isNotSupportedTextDelta = (model: Model): boolean => { - return isQwenMTModel(model) -} - -export const isNotSupportSystemMessageModel = (model: Model): boolean => { - return isQwenMTModel(model) || isGemmaModel(model) -} - -export const isGPT5SeriesModel = (model: Model) => { - const modelId = getLowerBaseModelName(model.id) - return modelId.includes('gpt-5') -} - -export const isGeminiModel = (model: Model) => { - const modelId = getLowerBaseModelName(model.id) - return modelId.includes('gemini') -} - -export const isOpenAIOpenWeightModel = (model: Model) => { - const modelId = getLowerBaseModelName(model.id) - return modelId.includes('gpt-oss') -} - -// zhipu 视觉推理模型用这组 special token 标记推理结果 -export const ZHIPU_RESULT_TOKENS = ['<|begin_of_box|>', '<|end_of_box|>'] as const diff --git a/src/renderer/src/config/models/default.ts b/src/renderer/src/config/models/default.ts new file mode 100644 index 0000000000..327af2ff47 --- /dev/null +++ b/src/renderer/src/config/models/default.ts @@ -0,0 +1,1788 @@ +import { Model, SystemProviderId } from '@renderer/types' + +export const glm45FlashModel: Model = { + id: 'glm-4.5-flash', + name: 'GLM-4.5-Flash', + provider: 'cherryin', + group: 'GLM-4.5' +} + +export const qwen38bModel: Model = { + id: 'Qwen/Qwen3-8B', + name: 'Qwen3-8B', + provider: 'cherryin', + group: 'Qwen' +} + +export const SYSTEM_MODELS: Record = { + defaultModel: [ + // Default assistant model + glm45FlashModel, + // Default topic naming model + qwen38bModel, + // Default translation model + glm45FlashModel, + // Default quick assistant model + glm45FlashModel + ], + cherryin: [ + { + id: 'glm-4.5-flash', + name: 'GLM-4.5-Flash', + provider: 'cherryin', + group: 'GLM-4.5' + }, + { + id: 'Qwen/Qwen3-8B', + name: 'Qwen3-8B', + provider: 'cherryin', + group: 'Qwen' + } + ], + vertexai: [], + '302ai': [ + { + id: 'deepseek-chat', + name: 'deepseek-chat', + provider: '302ai', + group: 'DeepSeek' + }, + { + id: 'deepseek-reasoner', + name: 'deepseek-reasoner', + provider: '302ai', + group: 'DeepSeek' + }, + { + id: 'chatgpt-4o-latest', + name: 'chatgpt-4o-latest', + provider: '302ai', + group: 'OpenAI' + }, + { + id: 'gpt-4.1', + name: 'gpt-4.1', + provider: '302ai', + group: 'OpenAI' + }, + { + id: 'o3', + name: 'o3', + provider: '302ai', + group: 'OpenAI' + }, + { + id: 'o4-mini', + name: 'o4-mini', + provider: '302ai', + group: 'OpenAI' + }, + { + id: 'qwen3-235b-a22b', + name: 'qwen3-235b-a22b', + provider: '302ai', + group: 'Qwen' + }, + { + id: 'gemini-2.5-flash-preview-05-20', + name: 'gemini-2.5-flash-preview-05-20', + provider: '302ai', + group: 'Gemini' + }, + { + id: 'gemini-2.5-pro-preview-06-05', + name: 'gemini-2.5-pro-preview-06-05', + provider: '302ai', + group: 'Gemini' + }, + { + id: 'claude-sonnet-4-20250514', + provider: '302ai', + name: 'claude-sonnet-4-20250514', + group: 'Anthropic' + }, + { + id: 'claude-opus-4-20250514', + provider: '302ai', + name: 'claude-opus-4-20250514', + group: 'Anthropic' + }, + { + id: 'jina-clip-v2', + name: 'jina-clip-v2', + provider: '302ai', + group: 'Jina AI' + }, + { + id: 'jina-reranker-m0', + name: 'jina-reranker-m0', + provider: '302ai', + group: 'Jina AI' + } + ], + ph8: [ + { + id: 'deepseek-v3-241226', + name: 'deepseek-v3-241226', + provider: 'ph8', + group: 'DeepSeek' + }, + { + id: 'deepseek-r1-250120', + name: 'deepseek-r1-250120', + provider: 'ph8', + group: 'DeepSeek' + } + ], + aihubmix: [ + { + id: 'gpt-5', + provider: 'aihubmix', + name: 'gpt-5', + group: 'OpenAI' + }, + { + id: 'gpt-5-mini', + provider: 'aihubmix', + name: 'gpt-5-mini', + group: 'OpenAI' + }, + { + id: 'gpt-5-nano', + provider: 'aihubmix', + name: 'gpt-5-nano', + group: 'OpenAI' + }, + { + id: 'gpt-5-chat-latest', + provider: 'aihubmix', + name: 'gpt-5-chat-latest', + group: 'OpenAI' + }, + { + id: 'o3', + provider: 'aihubmix', + name: 'o3', + group: 'OpenAI' + }, + { + id: 'o4-mini', + provider: 'aihubmix', + name: 'o4-mini', + group: 'OpenAI' + }, + { + id: 'gpt-4.1', + provider: 'aihubmix', + name: 'gpt-4.1', + group: 'OpenAI' + }, + { + id: 'gpt-4o', + provider: 'aihubmix', + name: 'gpt-4o', + group: 'OpenAI' + }, + { + id: 'gpt-image-1', + provider: 'aihubmix', + name: 'gpt-image-1', + group: 'OpenAI' + }, + { + id: 'DeepSeek-V3', + provider: 'aihubmix', + name: 'DeepSeek-V3', + group: 'DeepSeek' + }, + { + id: 'DeepSeek-R1', + provider: 'aihubmix', + name: 'DeepSeek-R1', + group: 'DeepSeek' + }, + { + id: 'claude-sonnet-4-20250514', + provider: 'aihubmix', + name: 'claude-sonnet-4-20250514', + group: 'Claude' + }, + { + id: 'gemini-2.5-pro', + provider: 'aihubmix', + name: 'gemini-2.5-pro', + group: 'Gemini' + }, + { + id: 'gemini-2.5-flash-nothink', + provider: 'aihubmix', + name: 'gemini-2.5-flash-nothink', + group: 'Gemini' + }, + { + id: 'gemini-2.5-flash', + provider: 'aihubmix', + name: 'gemini-2.5-flash', + group: 'Gemini' + }, + { + id: 'Qwen3-235B-A22B-Instruct-2507', + provider: 'aihubmix', + name: 'Qwen3-235B-A22B-Instruct-2507', + group: 'qwen' + }, + { + id: 'kimi-k2-0711-preview', + provider: 'aihubmix', + name: 'kimi-k2-0711-preview', + group: 'moonshot' + }, + { + id: 'Llama-4-Scout-17B-16E-Instruct', + provider: 'aihubmix', + name: 'Llama-4-Scout-17B-16E-Instruct', + group: 'llama' + }, + { + id: 'Llama-4-Maverick-17B-128E-Instruct-FP8', + provider: 'aihubmix', + name: 'Llama-4-Maverick-17B-128E-Instruct-FP8', + group: 'llama' + } + ], + + burncloud: [ + { id: 'claude-3-7-sonnet-20250219-thinking', provider: 'burncloud', name: 'Claude 3.7 thinking', group: 'Claude' }, + { id: 'claude-3-7-sonnet-20250219', provider: 'burncloud', name: 'Claude 3.7 Sonnet', group: 'Claude 3.7' }, + { id: 'claude-3-5-sonnet-20241022', provider: 'burncloud', name: 'Claude 3.5 Sonnet', group: 'Claude 3.5' }, + { id: 'claude-3-5-haiku-20241022', provider: 'burncloud', name: 'Claude 3.5 Haiku', group: 'Claude 3.5' }, + + { id: 'gpt-4.5-preview', provider: 'burncloud', name: 'gpt-4.5-preview', group: 'gpt-4.5' }, + { id: 'gpt-4o', provider: 'burncloud', name: 'GPT-4o', group: 'GPT 4o' }, + { id: 'gpt-4o-mini', provider: 'burncloud', name: 'GPT-4o-mini', group: 'GPT 4o' }, + { id: 'o3', provider: 'burncloud', name: 'GPT-o1-mini', group: 'o1' }, + { id: 'o3-mini', provider: 'burncloud', name: 'GPT-o1-preview', group: 'o1' }, + { id: 'o1-mini', provider: 'burncloud', name: 'GPT-o1-mini', group: 'o1' }, + + { id: 'gemini-2.5-pro-preview-03-25', provider: 'burncloud', name: 'Gemini 2.5 Preview', group: 'Geminit 2.5' }, + { id: 'gemini-2.5-pro-exp-03-25', provider: 'burncloud', name: 'Gemini 2.5 Pro Exp', group: 'Geminit 2.5' }, + { id: 'gemini-2.0-flash-lite', provider: 'burncloud', name: 'Gemini 2.0 Flash Lite', group: 'Geminit 2.0' }, + { id: 'gemini-2.0-flash-exp', provider: 'burncloud', name: 'Gemini 2.0 Flash Exp', group: 'Geminit 2.0' }, + { id: 'gemini-2.0-flash', provider: 'burncloud', name: 'Gemini 2.0 Flash', group: 'Geminit 2.0' }, + + { id: 'deepseek-r1', name: 'DeepSeek-R1', provider: 'burncloud', group: 'deepseek-ai' }, + { id: 'deepseek-v3', name: 'DeepSeek-V3', provider: 'burncloud', group: 'deepseek-ai' } + ], + ollama: [], + lmstudio: [], + silicon: [ + { + id: 'deepseek-ai/DeepSeek-R1', + name: 'deepseek-ai/DeepSeek-R1', + provider: 'silicon', + group: 'deepseek-ai' + }, + { + id: 'deepseek-ai/DeepSeek-V3', + name: 'deepseek-ai/DeepSeek-V3', + provider: 'silicon', + group: 'deepseek-ai' + }, + { + id: 'Qwen/Qwen2.5-7B-Instruct', + provider: 'silicon', + name: 'Qwen2.5-7B-Instruct', + group: 'Qwen' + }, + { + id: 'BAAI/bge-m3', + name: 'BAAI/bge-m3', + provider: 'silicon', + group: 'BAAI' + }, + { + id: 'Qwen/Qwen3-8B', + name: 'Qwen/Qwen3-8B', + provider: 'silicon', + group: 'Qwen' + } + ], + ppio: [ + { + id: 'deepseek/deepseek-r1-0528', + provider: 'ppio', + name: 'DeepSeek R1-0528', + group: 'deepseek' + }, + { + id: 'deepseek/deepseek-v3-0324', + provider: 'ppio', + name: 'DeepSeek V3-0324', + group: 'deepseek' + }, + { + id: 'deepseek/deepseek-r1-turbo', + provider: 'ppio', + name: 'DeepSeek R1 Turbo', + group: 'deepseek' + }, + { + id: 'deepseek/deepseek-v3-turbo', + provider: 'ppio', + name: 'DeepSeek V3 Turbo', + group: 'deepseek' + }, + { + id: 'deepseek/deepseek-r1/community', + name: 'DeepSeek: DeepSeek R1 (Community)', + provider: 'ppio', + group: 'deepseek' + }, + { + id: 'deepseek/deepseek-v3/community', + name: 'DeepSeek: DeepSeek V3 (Community)', + provider: 'ppio', + group: 'deepseek' + }, + { + id: 'minimaxai/minimax-m1-80k', + provider: 'ppio', + name: 'MiniMax M1-80K', + group: 'minimaxai' + }, + { + id: 'qwen/qwen3-235b-a22b-fp8', + provider: 'ppio', + name: 'Qwen3 235B', + group: 'qwen' + }, + { + id: 'qwen/qwen3-32b-fp8', + provider: 'ppio', + name: 'Qwen3 32B', + group: 'qwen' + }, + { + id: 'qwen/qwen3-30b-a3b-fp8', + provider: 'ppio', + name: 'Qwen3 30B', + group: 'qwen' + }, + { + id: 'qwen/qwen2.5-vl-72b-instruct', + provider: 'ppio', + name: 'Qwen2.5 VL 72B', + group: 'qwen' + }, + { + id: 'qwen/qwen3-embedding-8b', + provider: 'ppio', + name: 'Qwen3 Embedding 8B', + group: 'qwen' + }, + { + id: 'qwen/qwen3-reranker-8b', + provider: 'ppio', + name: 'Qwen3 Reranker 8B', + group: 'qwen' + } + ], + alayanew: [], + openai: [ + { id: 'gpt-4.5-preview', provider: 'openai', name: ' gpt-4.5-preview', group: 'gpt-4.5' }, + { id: 'gpt-4o', provider: 'openai', name: ' GPT-4o', group: 'GPT 4o' }, + { id: 'gpt-4o-mini', provider: 'openai', name: ' GPT-4o-mini', group: 'GPT 4o' }, + { id: 'o1-mini', provider: 'openai', name: ' o1-mini', group: 'o1' }, + { id: 'o1-preview', provider: 'openai', name: ' o1-preview', group: 'o1' } + ], + 'azure-openai': [ + { + id: 'gpt-4o', + provider: 'azure-openai', + name: ' GPT-4o', + group: 'GPT 4o' + }, + { + id: 'gpt-4o-mini', + provider: 'azure-openai', + name: ' GPT-4o-mini', + group: 'GPT 4o' + } + ], + gemini: [ + { + id: 'gemini-1.5-flash', + provider: 'gemini', + name: 'Gemini 1.5 Flash', + group: 'Gemini 1.5' + }, + { + id: 'gemini-1.5-flash-8b', + provider: 'gemini', + name: 'Gemini 1.5 Flash (8B)', + group: 'Gemini 1.5' + }, + { + id: 'gemini-1.5-pro', + name: 'Gemini 1.5 Pro', + provider: 'gemini', + group: 'Gemini 1.5' + }, + { + id: 'gemini-2.0-flash', + provider: 'gemini', + name: 'Gemini 2.0 Flash', + group: 'Gemini 2.0' + }, + { + id: 'gemini-2.5-flash-image-preview', + provider: 'gemini', + name: 'Gemini 2.5 Flash Image', + group: 'Gemini 2.5' + } + ], + anthropic: [ + { + id: 'claude-sonnet-4-20250514', + provider: 'anthropic', + name: 'Claude Sonnet 4', + group: 'Claude 4' + }, + { + id: 'claude-opus-4-20250514', + provider: 'anthropic', + name: 'Claude Opus 4', + group: 'Claude 4' + }, + { + id: 'claude-3-7-sonnet-20250219', + provider: 'anthropic', + name: 'Claude 3.7 Sonnet', + group: 'Claude 3.7' + }, + { + id: 'claude-3-5-sonnet-20241022', + provider: 'anthropic', + name: 'Claude 3.5 Sonnet', + group: 'Claude 3.5' + }, + { + id: 'claude-3-5-haiku-20241022', + provider: 'anthropic', + name: 'Claude 3.5 Haiku', + group: 'Claude 3.5' + }, + { + id: 'claude-3-5-sonnet-20240620', + provider: 'anthropic', + name: 'Claude 3.5 Sonnet (Legacy)', + group: 'Claude 3.5' + }, + { + id: 'claude-3-opus-20240229', + provider: 'anthropic', + name: 'Claude 3 Opus', + group: 'Claude 3' + }, + { + id: 'claude-3-haiku-20240307', + provider: 'anthropic', + name: 'Claude 3 Haiku', + group: 'Claude 3' + } + ], + deepseek: [ + { + id: 'deepseek-chat', + provider: 'deepseek', + name: 'DeepSeek Chat', + group: 'DeepSeek Chat' + }, + { + id: 'deepseek-reasoner', + provider: 'deepseek', + name: 'DeepSeek Reasoner', + group: 'DeepSeek Reasoner' + } + ], + together: [ + { + id: 'meta-llama/Llama-3.2-11B-Vision-Instruct-Turbo', + provider: 'together', + name: 'Llama-3.2-11B-Vision', + group: 'Llama-3.2' + }, + { + id: 'meta-llama/Llama-3.2-90B-Vision-Instruct-Turbo', + provider: 'together', + name: 'Llama-3.2-90B-Vision', + group: 'Llama-3.2' + }, + { + id: 'google/gemma-2-27b-it', + provider: 'together', + name: 'gemma-2-27b-it', + group: 'Gemma' + }, + { + id: 'google/gemma-2-9b-it', + provider: 'together', + name: 'gemma-2-9b-it', + group: 'Gemma' + } + ], + ocoolai: [ + { + id: 'deepseek-chat', + provider: 'ocoolai', + name: 'deepseek-chat', + group: 'DeepSeek' + }, + { + id: 'deepseek-reasoner', + provider: 'ocoolai', + name: 'deepseek-reasoner', + group: 'DeepSeek' + }, + { + id: 'deepseek-ai/DeepSeek-R1', + provider: 'ocoolai', + name: 'deepseek-ai/DeepSeek-R1', + group: 'DeepSeek' + }, + { + id: 'HiSpeed/DeepSeek-R1', + provider: 'ocoolai', + name: 'HiSpeed/DeepSeek-R1', + group: 'DeepSeek' + }, + { + id: 'ocoolAI/DeepSeek-R1', + provider: 'ocoolai', + name: 'ocoolAI/DeepSeek-R1', + group: 'DeepSeek' + }, + { + id: 'Azure/DeepSeek-R1', + provider: 'ocoolai', + name: 'Azure/DeepSeek-R1', + group: 'DeepSeek' + }, + { + id: 'gpt-4o', + provider: 'ocoolai', + name: 'gpt-4o', + group: 'OpenAI' + }, + { + id: 'gpt-4o-all', + provider: 'ocoolai', + name: 'gpt-4o-all', + group: 'OpenAI' + }, + { + id: 'gpt-4o-mini', + provider: 'ocoolai', + name: 'gpt-4o-mini', + group: 'OpenAI' + }, + { + id: 'gpt-4', + provider: 'ocoolai', + name: 'gpt-4', + group: 'OpenAI' + }, + { + id: 'o1-preview', + provider: 'ocoolai', + name: 'o1-preview', + group: 'OpenAI' + }, + { + id: 'o1-mini', + provider: 'ocoolai', + name: 'o1-mini', + group: 'OpenAI' + }, + { + id: 'claude-3-5-sonnet-20240620', + provider: 'ocoolai', + name: 'claude-3-5-sonnet-20240620', + group: 'Anthropic' + }, + { + id: 'claude-3-5-haiku-20241022', + provider: 'ocoolai', + name: 'claude-3-5-haiku-20241022', + group: 'Anthropic' + }, + { + id: 'gemini-pro', + provider: 'ocoolai', + name: 'gemini-pro', + group: 'Gemini' + }, + { + id: 'gemini-1.5-pro', + provider: 'ocoolai', + name: 'gemini-1.5-pro', + group: 'Gemini' + }, + { + id: 'meta-llama/Llama-3.2-90B-Vision-Instruct-Turbo', + provider: 'ocoolai', + name: 'Llama-3.2-90B-Vision-Instruct-Turbo', + group: 'Llama-3.2' + }, + { + id: 'meta-llama/Llama-3.2-11B-Vision-Instruct-Turbo', + provider: 'ocoolai', + name: 'Llama-3.2-11B-Vision-Instruct-Turbo', + group: 'Llama-3.2' + }, + { + id: 'meta-llama/Llama-3.2-3B-Vision-Instruct-Turbo', + provider: 'ocoolai', + name: 'Llama-3.2-3B-Vision-Instruct-Turbo', + group: 'Llama-3.2' + }, + { + id: 'google/gemma-2-27b-it', + provider: 'ocoolai', + name: 'gemma-2-27b-it', + group: 'Gemma' + }, + { + id: 'google/gemma-2-9b-it', + provider: 'ocoolai', + name: 'gemma-2-9b-it', + group: 'Gemma' + }, + { + id: 'Doubao-embedding', + provider: 'ocoolai', + name: 'Doubao-embedding', + group: 'Doubao' + }, + { + id: 'text-embedding-3-large', + provider: 'ocoolai', + name: 'text-embedding-3-large', + group: 'Embedding' + }, + { + id: 'text-embedding-3-small', + provider: 'ocoolai', + name: 'text-embedding-3-small', + group: 'Embedding' + }, + { + id: 'text-embedding-v2', + provider: 'ocoolai', + name: 'text-embedding-v2', + group: 'Embedding' + } + ], + github: [ + { + id: 'gpt-4o', + provider: 'github', + name: 'OpenAI GPT-4o', + group: 'OpenAI' + } + ], + copilot: [ + { + id: 'gpt-4o-mini', + provider: 'copilot', + name: 'OpenAI GPT-4o-mini', + group: 'OpenAI' + } + ], + yi: [ + { id: 'yi-lightning', name: 'Yi Lightning', provider: 'yi', group: 'yi-lightning', owned_by: '01.ai' }, + { id: 'yi-vision-v2', name: 'Yi Vision v2', provider: 'yi', group: 'yi-vision', owned_by: '01.ai' } + ], + zhipu: [ + { + id: 'glm-4.5-flash', + provider: 'zhipu', + name: 'GLM-4.5-Flash', + group: 'GLM-4.5' + }, + { + id: 'glm-4.5', + provider: 'zhipu', + name: 'GLM-4.5', + group: 'GLM-4.5' + }, + { + id: 'glm-4.5-air', + provider: 'zhipu', + name: 'GLM-4.5-Air', + group: 'GLM-4.5' + }, + { + id: 'glm-4.5-airx', + provider: 'zhipu', + name: 'GLM-4.5-AirX', + group: 'GLM-4.5' + }, + { + id: 'glm-4.5v', + provider: 'zhipu', + name: 'GLM-4.5V', + group: 'GLM-4.5V' + }, + { + id: 'embedding-3', + provider: 'zhipu', + name: 'Embedding-3', + group: 'Embedding' + } + ], + moonshot: [ + { + id: 'moonshot-v1-auto', + name: 'moonshot-v1-auto', + provider: 'moonshot', + group: 'moonshot-v1', + owned_by: 'moonshot', + capabilities: [{ type: 'text' }, { type: 'function_calling' }] + }, + { + id: 'kimi-k2-0711-preview', + name: 'kimi-k2-0711-preview', + provider: 'moonshot', + group: 'kimi-k2', + owned_by: 'moonshot', + capabilities: [{ type: 'text' }, { type: 'function_calling' }], + pricing: { + input_per_million_tokens: 0.6, + output_per_million_tokens: 2.5, + currencySymbol: 'USD' + } + } + ], + baichuan: [ + { + id: 'Baichuan4', + provider: 'baichuan', + name: 'Baichuan4', + group: 'Baichuan4' + }, + { + id: 'Baichuan3-Turbo', + provider: 'baichuan', + name: 'Baichuan3 Turbo', + group: 'Baichuan3' + }, + { + id: 'Baichuan3-Turbo-128k', + provider: 'baichuan', + name: 'Baichuan3 Turbo 128k', + group: 'Baichuan3' + } + ], + modelscope: [ + { + id: 'Qwen/Qwen2.5-72B-Instruct', + name: 'Qwen/Qwen2.5-72B-Instruct', + provider: 'modelscope', + group: 'Qwen' + }, + { + id: 'Qwen/Qwen2.5-VL-72B-Instruct', + name: 'Qwen/Qwen2.5-VL-72B-Instruct', + provider: 'modelscope', + group: 'Qwen' + }, + { + id: 'Qwen/Qwen2.5-Coder-32B-Instruct', + name: 'Qwen/Qwen2.5-Coder-32B-Instruct', + provider: 'modelscope', + group: 'Qwen' + }, + { + id: 'deepseek-ai/DeepSeek-R1', + name: 'deepseek-ai/DeepSeek-R1', + provider: 'modelscope', + group: 'deepseek-ai' + }, + { + id: 'deepseek-ai/DeepSeek-V3', + name: 'deepseek-ai/DeepSeek-V3', + provider: 'modelscope', + group: 'deepseek-ai' + } + ], + dashscope: [ + { id: 'qwen-vl-plus', name: 'qwen-vl-plus', provider: 'dashscope', group: 'qwen-vl', owned_by: 'system' }, + { id: 'qwen-coder-plus', name: 'qwen-coder-plus', provider: 'dashscope', group: 'qwen-coder', owned_by: 'system' }, + { id: 'qwen-flash', name: 'qwen-flash', provider: 'dashscope', group: 'qwen-flash', owned_by: 'system' }, + { id: 'qwen-plus', name: 'qwen-plus', provider: 'dashscope', group: 'qwen-plus', owned_by: 'system' }, + { id: 'qwen-max', name: 'qwen-max', provider: 'dashscope', group: 'qwen-max', owned_by: 'system' } + ], + stepfun: [ + { + id: 'step-1-8k', + provider: 'stepfun', + name: 'Step 1 8K', + group: 'Step 1' + }, + { + id: 'step-1-flash', + provider: 'stepfun', + name: 'Step 1 Flash', + group: 'Step 1' + } + ], + doubao: [ + { + id: 'doubao-1-5-vision-pro-32k-250115', + provider: 'doubao', + name: 'doubao-1.5-vision-pro', + group: 'Doubao-1.5-vision-pro' + }, + { + id: 'doubao-1-5-pro-32k-250115', + provider: 'doubao', + name: 'doubao-1.5-pro-32k', + group: 'Doubao-1.5-pro' + }, + { + id: 'doubao-1-5-pro-32k-character-250228', + provider: 'doubao', + name: 'doubao-1.5-pro-32k-character', + group: 'Doubao-1.5-pro' + }, + { + id: 'doubao-1-5-pro-256k-250115', + provider: 'doubao', + name: 'Doubao-1.5-pro-256k', + group: 'Doubao-1.5-pro' + }, + { + id: 'deepseek-r1-250120', + provider: 'doubao', + name: 'DeepSeek-R1', + group: 'DeepSeek' + }, + { + id: 'deepseek-r1-distill-qwen-32b-250120', + provider: 'doubao', + name: 'DeepSeek-R1-Distill-Qwen-32B', + group: 'DeepSeek' + }, + { + id: 'deepseek-r1-distill-qwen-7b-250120', + provider: 'doubao', + name: 'DeepSeek-R1-Distill-Qwen-7B', + group: 'DeepSeek' + }, + { + id: 'deepseek-v3-250324', + provider: 'doubao', + name: 'DeepSeek-V3', + group: 'DeepSeek' + }, + { + id: 'doubao-pro-32k-241215', + provider: 'doubao', + name: 'Doubao-pro-32k', + group: 'Doubao-pro' + }, + { + id: 'doubao-pro-32k-functioncall-241028', + provider: 'doubao', + name: 'Doubao-pro-32k-functioncall-241028', + group: 'Doubao-pro' + }, + { + id: 'doubao-pro-32k-character-241215', + provider: 'doubao', + name: 'Doubao-pro-32k-character-241215', + group: 'Doubao-pro' + }, + { + id: 'doubao-pro-256k-241115', + provider: 'doubao', + name: 'Doubao-pro-256k', + group: 'Doubao-pro' + }, + { + id: 'doubao-lite-4k-character-240828', + provider: 'doubao', + name: 'Doubao-lite-4k-character-240828', + group: 'Doubao-lite' + }, + { + id: 'doubao-lite-32k-240828', + provider: 'doubao', + name: 'Doubao-lite-32k', + group: 'Doubao-lite' + }, + { + id: 'doubao-lite-32k-character-241015', + provider: 'doubao', + name: 'Doubao-lite-32k-character-241015', + group: 'Doubao-lite' + }, + { + id: 'doubao-lite-128k-240828', + provider: 'doubao', + name: 'Doubao-lite-128k', + group: 'Doubao-lite' + }, + { + id: 'doubao-1-5-lite-32k-250115', + provider: 'doubao', + name: 'Doubao-1.5-lite-32k', + group: 'Doubao-lite' + }, + { + id: 'doubao-embedding-large-text-240915', + provider: 'doubao', + name: 'Doubao-embedding-large', + group: 'Doubao-embedding' + }, + { + id: 'doubao-embedding-text-240715', + provider: 'doubao', + name: 'Doubao-embedding', + group: 'Doubao-embedding' + }, + { + id: 'doubao-embedding-vision-241215', + provider: 'doubao', + name: 'Doubao-embedding-vision', + group: 'Doubao-embedding' + }, + { + id: 'doubao-vision-lite-32k-241015', + provider: 'doubao', + name: 'Doubao-vision-lite-32k', + group: 'Doubao-vision-lite-32k' + } + ], + minimax: [ + { + id: 'abab6.5s-chat', + provider: 'minimax', + name: 'abab6.5s', + group: 'abab6' + }, + { + id: 'abab6.5g-chat', + provider: 'minimax', + name: 'abab6.5g', + group: 'abab6' + }, + { + id: 'abab6.5t-chat', + provider: 'minimax', + name: 'abab6.5t', + group: 'abab6' + }, + { + id: 'abab5.5s-chat', + provider: 'minimax', + name: 'abab5.5s', + group: 'abab5' + }, + { + id: 'minimax-text-01', + provider: 'minimax', + name: 'minimax-01', + group: 'minimax-01' + } + ], + hyperbolic: [ + { + id: 'Qwen/Qwen2-VL-72B-Instruct', + provider: 'hyperbolic', + name: 'Qwen2-VL-72B-Instruct', + group: 'Qwen2-VL' + }, + { + id: 'Qwen/Qwen2-VL-7B-Instruct', + provider: 'hyperbolic', + name: 'Qwen2-VL-7B-Instruct', + group: 'Qwen2-VL' + }, + { + id: 'mistralai/Pixtral-12B-2409', + provider: 'hyperbolic', + name: 'Pixtral-12B-2409', + group: 'Pixtral' + }, + { + id: 'meta-llama/Meta-Llama-3.1-405B', + provider: 'hyperbolic', + name: 'Meta-Llama-3.1-405B', + group: 'Meta-Llama-3.1' + } + ], + grok: [ + { + id: 'grok-4', + provider: 'grok', + name: 'Grok 4', + group: 'Grok' + }, + { + id: 'grok-3', + provider: 'grok', + name: 'Grok 3', + group: 'Grok' + }, + { + id: 'grok-3-fast', + provider: 'grok', + name: 'Grok 3 Fast', + group: 'Grok' + }, + { + id: 'grok-3-mini', + provider: 'grok', + name: 'Grok 3 Mini', + group: 'Grok' + }, + { + id: 'grok-3-mini-fast', + provider: 'grok', + name: 'Grok 3 Mini Fast', + group: 'Grok' + }, + { + id: 'grok-2-vision-1212', + provider: 'grok', + name: 'Grok 2 Vision 1212', + group: 'Grok' + }, + { + id: 'grok-2-1212', + provider: 'grok', + name: 'Grok 2 1212', + group: 'Grok' + } + ], + mistral: [ + { + id: 'pixtral-12b-2409', + provider: 'mistral', + name: 'Pixtral 12B [Free]', + group: 'Pixtral' + }, + { + id: 'pixtral-large-latest', + provider: 'mistral', + name: 'Pixtral Large', + group: 'Pixtral' + }, + { + id: 'ministral-3b-latest', + provider: 'mistral', + name: 'Mistral 3B [Free]', + group: 'Mistral Mini' + }, + { + id: 'ministral-8b-latest', + provider: 'mistral', + name: 'Mistral 8B [Free]', + group: 'Mistral Mini' + }, + { + id: 'codestral-latest', + provider: 'mistral', + name: 'Mistral Codestral', + group: 'Mistral Code' + }, + { + id: 'mistral-large-latest', + provider: 'mistral', + name: 'Mistral Large', + group: 'Mistral Chat' + }, + { + id: 'mistral-small-latest', + provider: 'mistral', + name: 'Mistral Small', + group: 'Mistral Chat' + }, + { + id: 'open-mistral-nemo', + provider: 'mistral', + name: 'Mistral Nemo', + group: 'Mistral Chat' + }, + { + id: 'mistral-embed', + provider: 'mistral', + name: 'Mistral Embedding', + group: 'Mistral Embed' + } + ], + jina: [ + { + id: 'jina-clip-v1', + provider: 'jina', + name: 'jina-clip-v1', + group: 'Jina Clip' + }, + { + id: 'jina-clip-v2', + provider: 'jina', + name: 'jina-clip-v2', + group: 'Jina Clip' + }, + { + id: 'jina-embeddings-v2-base-en', + provider: 'jina', + name: 'jina-embeddings-v2-base-en', + group: 'Jina Embeddings V2' + }, + { + id: 'jina-embeddings-v2-base-es', + provider: 'jina', + name: 'jina-embeddings-v2-base-es', + group: 'Jina Embeddings V2' + }, + { + id: 'jina-embeddings-v2-base-de', + provider: 'jina', + name: 'jina-embeddings-v2-base-de', + group: 'Jina Embeddings V2' + }, + { + id: 'jina-embeddings-v2-base-zh', + provider: 'jina', + name: 'jina-embeddings-v2-base-zh', + group: 'Jina Embeddings V2' + }, + { + id: 'jina-embeddings-v2-base-code', + provider: 'jina', + name: 'jina-embeddings-v2-base-code', + group: 'Jina Embeddings V2' + }, + { + id: 'jina-embeddings-v3', + provider: 'jina', + name: 'jina-embeddings-v3', + group: 'Jina Embeddings V3' + } + ], + fireworks: [ + { + id: 'accounts/fireworks/models/mythomax-l2-13b', + provider: 'fireworks', + name: 'mythomax-l2-13b', + group: 'Gryphe' + }, + { + id: 'accounts/fireworks/models/llama-v3-70b-instruct', + provider: 'fireworks', + name: 'Llama-3-70B-Instruct', + group: 'Llama3' + } + ], + hunyuan: [ + { + id: 'hunyuan-pro', + provider: 'hunyuan', + name: 'hunyuan-pro', + group: 'Hunyuan' + }, + { + id: 'hunyuan-standard', + provider: 'hunyuan', + name: 'hunyuan-standard', + group: 'Hunyuan' + }, + { + id: 'hunyuan-lite', + provider: 'hunyuan', + name: 'hunyuan-lite', + group: 'Hunyuan' + }, + { + id: 'hunyuan-standard-256k', + provider: 'hunyuan', + name: 'hunyuan-standard-256k', + group: 'Hunyuan' + }, + { + id: 'hunyuan-vision', + provider: 'hunyuan', + name: 'hunyuan-vision', + group: 'Hunyuan' + }, + { + id: 'hunyuan-code', + provider: 'hunyuan', + name: 'hunyuan-code', + group: 'Hunyuan' + }, + { + id: 'hunyuan-role', + provider: 'hunyuan', + name: 'hunyuan-role', + group: 'Hunyuan' + }, + { + id: 'hunyuan-turbo', + provider: 'hunyuan', + name: 'hunyuan-turbo', + group: 'Hunyuan' + }, + { + id: 'hunyuan-turbos-latest', + provider: 'hunyuan', + name: 'hunyuan-turbos-latest', + group: 'Hunyuan' + }, + { + id: 'hunyuan-embedding', + provider: 'hunyuan', + name: 'hunyuan-embedding', + group: 'Embedding' + } + ], + nvidia: [ + { + id: '01-ai/yi-large', + provider: 'nvidia', + name: 'yi-large', + group: 'Yi' + }, + { + id: 'meta/llama-3.1-405b-instruct', + provider: 'nvidia', + name: 'llama-3.1-405b-instruct', + group: 'llama-3.1' + } + ], + openrouter: [ + { + id: 'google/gemini-2.5-flash-image-preview', + provider: 'openrouter', + name: 'Google: Gemini 2.5 Flash Image', + group: 'google' + }, + { + id: 'google/gemini-2.5-flash-preview', + provider: 'openrouter', + name: 'Google: Gemini 2.5 Flash Preview', + group: 'google' + }, + { + id: 'qwen/qwen-2.5-7b-instruct:free', + provider: 'openrouter', + name: 'Qwen: Qwen-2.5-7B Instruct', + group: 'qwen' + }, + { + id: 'deepseek/deepseek-chat', + provider: 'openrouter', + name: 'DeepSeek: V3', + group: 'deepseek' + }, + { + id: 'mistralai/mistral-7b-instruct:free', + provider: 'openrouter', + name: 'Mistral: Mistral 7B Instruct', + group: 'mistralai' + } + ], + groq: [ + { + id: 'llama3-8b-8192', + provider: 'groq', + name: 'LLaMA3 8B', + group: 'Llama3' + }, + { + id: 'llama3-70b-8192', + provider: 'groq', + name: 'LLaMA3 70B', + group: 'Llama3' + }, + { + id: 'mistral-saba-24b', + provider: 'groq', + name: 'Mistral Saba 24B', + group: 'Mistral' + }, + { + id: 'gemma-9b-it', + provider: 'groq', + name: 'Gemma 9B', + group: 'Gemma' + } + ], + 'baidu-cloud': [ + { + id: 'deepseek-r1', + provider: 'baidu-cloud', + name: 'DeepSeek R1', + group: 'DeepSeek' + }, + { + id: 'deepseek-v3', + provider: 'baidu-cloud', + name: 'DeepSeek V3', + group: 'DeepSeek' + }, + { + id: 'ernie-4.0-8k-latest', + provider: 'baidu-cloud', + name: 'ERNIE-4.0', + group: 'ERNIE' + }, + { + id: 'ernie-4.0-turbo-8k-latest', + provider: 'baidu-cloud', + name: 'ERNIE 4.0 Trubo', + group: 'ERNIE' + }, + { + id: 'ernie-speed-8k', + provider: 'baidu-cloud', + name: 'ERNIE Speed', + group: 'ERNIE' + }, + { + id: 'ernie-lite-8k', + provider: 'baidu-cloud', + name: 'ERNIE Lite', + group: 'ERNIE' + }, + { + id: 'bge-large-zh', + provider: 'baidu-cloud', + name: 'BGE Large ZH', + group: 'Embedding' + }, + { + id: 'bge-large-en', + provider: 'baidu-cloud', + name: 'BGE Large EN', + group: 'Embedding' + } + ], + dmxapi: [ + { + id: 'Qwen/Qwen2.5-7B-Instruct', + provider: 'dmxapi', + name: 'Qwen/Qwen2.5-7B-Instruct', + group: '免费模型' + }, + { + id: 'ERNIE-Speed-128K', + provider: 'dmxapi', + name: 'ERNIE-Speed-128K', + group: '免费模型' + }, + { + id: 'gpt-4o', + provider: 'dmxapi', + name: 'gpt-4o', + group: 'OpenAI' + }, + { + id: 'gpt-4o-mini', + provider: 'dmxapi', + name: 'gpt-4o-mini', + group: 'OpenAI' + }, + { + id: 'DMXAPI-DeepSeek-R1', + provider: 'dmxapi', + name: 'DMXAPI-DeepSeek-R1', + group: 'DeepSeek' + }, + { + id: 'DMXAPI-DeepSeek-V3', + provider: 'dmxapi', + name: 'DMXAPI-DeepSeek-V3', + group: 'DeepSeek' + }, + { + id: 'claude-3-5-sonnet-20241022', + provider: 'dmxapi', + name: 'claude-3-5-sonnet-20241022', + group: 'Claude' + }, + { + id: 'gemini-2.0-flash', + provider: 'dmxapi', + name: 'gemini-2.0-flash', + group: 'Gemini' + } + ], + perplexity: [ + { + id: 'sonar-reasoning-pro', + provider: 'perplexity', + name: 'sonar-reasoning-pro', + group: 'Sonar' + }, + { + id: 'sonar-reasoning', + provider: 'perplexity', + name: 'sonar-reasoning', + group: 'Sonar' + }, + { + id: 'sonar-pro', + provider: 'perplexity', + name: 'sonar-pro', + group: 'Sonar' + }, + { + id: 'sonar', + provider: 'perplexity', + name: 'sonar', + group: 'Sonar' + }, + { + id: 'sonar-deep-research', + provider: 'perplexity', + name: 'sonar-deep-research', + group: 'Sonar' + } + ], + infini: [ + { + id: 'deepseek-r1', + provider: 'infini', + name: 'deepseek-r1', + group: 'DeepSeek' + }, + { + id: 'deepseek-r1-distill-qwen-32b', + provider: 'infini', + name: 'deepseek-r1-distill-qwen-32b', + group: 'DeepSeek' + }, + { + id: 'deepseek-v3', + provider: 'infini', + name: 'deepseek-v3', + group: 'DeepSeek' + }, + { + id: 'qwen2.5-72b-instruct', + provider: 'infini', + name: 'qwen2.5-72b-instruct', + group: 'Qwen' + }, + { + id: 'qwen2.5-32b-instruct', + provider: 'infini', + name: 'qwen2.5-32b-instruct', + group: 'Qwen' + }, + { + id: 'qwen2.5-14b-instruct', + provider: 'infini', + name: 'qwen2.5-14b-instruct', + group: 'Qwen' + }, + { + id: 'qwen2.5-7b-instruct', + provider: 'infini', + name: 'qwen2.5-7b-instruct', + group: 'Qwen' + }, + { + id: 'qwen2-72b-instruct', + provider: 'infini', + name: 'qwen2-72b-instruct', + group: 'Qwen' + }, + { + id: 'qwq-32b-preview', + provider: 'infini', + name: 'qwq-32b-preview', + group: 'Qwen' + }, + { + id: 'qwen2.5-coder-32b-instruct', + provider: 'infini', + name: 'qwen2.5-coder-32b-instruct', + group: 'Qwen' + }, + { + id: 'llama-3.3-70b-instruct', + provider: 'infini', + name: 'llama-3.3-70b-instruct', + group: 'Llama' + }, + { + id: 'bge-m3', + provider: 'infini', + name: 'bge-m3', + group: 'BAAI' + }, + { + id: 'gemma-2-27b-it', + provider: 'infini', + name: 'gemma-2-27b-it', + group: 'Gemma' + }, + { + id: 'jina-embeddings-v2-base-zh', + provider: 'infini', + name: 'jina-embeddings-v2-base-zh', + group: 'Jina' + }, + { + id: 'jina-embeddings-v2-base-code', + provider: 'infini', + name: 'jina-embeddings-v2-base-code', + group: 'Jina' + } + ], + xirang: [], + 'tencent-cloud-ti': [ + { + id: 'deepseek-r1', + provider: 'tencent-cloud-ti', + name: 'DeepSeek R1', + group: 'DeepSeek' + }, + { + id: 'deepseek-v3', + provider: 'tencent-cloud-ti', + name: 'DeepSeek V3', + group: 'DeepSeek' + } + ], + gpustack: [], + voyageai: [ + { + id: 'voyage-3-large', + provider: 'voyageai', + name: 'voyage-3-large', + group: 'Voyage Embeddings V3' + }, + { + id: 'voyage-3', + provider: 'voyageai', + name: 'voyage-3', + group: 'Voyage Embeddings V3' + }, + { + id: 'voyage-3-lite', + provider: 'voyageai', + name: 'voyage-3-lite', + group: 'Voyage Embeddings V3' + }, + { + id: 'voyage-code-3', + provider: 'voyageai', + name: 'voyage-code-3', + group: 'Voyage Embeddings V3' + }, + { + id: 'voyage-finance-3', + provider: 'voyageai', + name: 'voyage-finance-3', + group: 'Voyage Embeddings V2' + }, + { + id: 'voyage-law-2', + provider: 'voyageai', + name: 'voyage-law-2', + group: 'Voyage Embeddings V2' + }, + { + id: 'voyage-code-2', + provider: 'voyageai', + name: 'voyage-code-2', + group: 'Voyage Embeddings V2' + }, + { + id: 'rerank-2', + provider: 'voyageai', + name: 'rerank-2', + group: 'Voyage Rerank V2' + }, + { + id: 'rerank-2-lite', + provider: 'voyageai', + name: 'rerank-2-lite', + group: 'Voyage Rerank V2' + } + ], + qiniu: [ + { + id: 'deepseek-r1', + provider: 'qiniu', + name: 'DeepSeek R1', + group: 'DeepSeek' + }, + { + id: 'deepseek-r1-search', + provider: 'qiniu', + name: 'DeepSeek R1 Search', + group: 'DeepSeek' + }, + { + id: 'deepseek-r1-32b', + provider: 'qiniu', + name: 'DeepSeek R1 32B', + group: 'DeepSeek' + }, + { + id: 'deepseek-v3', + provider: 'qiniu', + name: 'DeepSeek V3', + group: 'DeepSeek' + }, + { + id: 'deepseek-v3-search', + provider: 'qiniu', + name: 'DeepSeek V3 Search', + group: 'DeepSeek' + }, + { + id: 'deepseek-v3-tool', + provider: 'qiniu', + name: 'DeepSeek V3 Tool', + group: 'DeepSeek' + }, + { + id: 'qwq-32b', + provider: 'qiniu', + name: 'QWQ 32B', + group: 'Qwen' + }, + { + id: 'qwen2.5-72b-instruct', + provider: 'qiniu', + name: 'Qwen2.5 72B Instruct', + group: 'Qwen' + } + ], + tokenflux: [ + { + id: 'gpt-4.1', + provider: 'tokenflux', + name: 'GPT-4.1', + group: 'GPT-4.1' + }, + { + id: 'gpt-4.1-mini', + provider: 'tokenflux', + name: 'GPT-4.1 Mini', + group: 'GPT-4.1' + }, + { + id: 'claude-sonnet-4', + provider: 'tokenflux', + name: 'Claude Sonnet 4', + group: 'Claude' + }, + { + id: 'claude-3-7-sonnet', + provider: 'tokenflux', + name: 'Claude 3.7 Sonnet', + group: 'Claude' + }, + { + id: 'gemini-2.5-pro', + provider: 'tokenflux', + name: 'Gemini 2.5 Pro', + group: 'Gemini' + }, + { + id: 'gemini-2.5-flash', + provider: 'tokenflux', + name: 'Gemini 2.5 Flash', + group: 'Gemini' + }, + { + id: 'deepseek-r1', + provider: 'tokenflux', + name: 'DeepSeek R1', + group: 'DeepSeek' + }, + { + id: 'deepseek-v3', + provider: 'tokenflux', + name: 'DeepSeek V3', + group: 'DeepSeek' + }, + { + id: 'qwen-max', + provider: 'tokenflux', + name: 'Qwen Max', + group: 'Qwen' + }, + { + id: 'qwen-plus', + provider: 'tokenflux', + name: 'Qwen Plus', + group: 'Qwen' + } + ], + cephalon: [ + { + id: 'DeepSeek-R1', + provider: 'cephalon', + name: 'DeepSeek-R1满血版', + group: 'DeepSeek' + } + ], + lanyun: [ + { + id: '/maas/deepseek-ai/DeepSeek-R1-0528', + name: 'deepseek-ai/DeepSeek-R1', + provider: 'lanyun', + group: 'deepseek-ai' + }, + { + id: '/maas/deepseek-ai/DeepSeek-V3-0324', + name: 'deepseek-ai/DeepSeek-V3', + provider: 'lanyun', + group: 'deepseek-ai' + }, + { + id: '/maas/qwen/Qwen2.5-72B-Instruct', + provider: 'lanyun', + name: 'Qwen2.5-72B-Instruct', + group: 'Qwen' + }, + { + id: '/maas/qwen/Qwen3-235B-A22B', + name: 'Qwen/Qwen3-235B', + provider: 'lanyun', + group: 'Qwen' + }, + { + id: '/maas/minimax/MiniMax-M1-80k', + name: 'MiniMax-M1-80k', + provider: 'lanyun', + group: 'MiniMax' + }, + { + id: '/maas/google/Gemma3-27B', + name: 'Gemma3-27B', + provider: 'lanyun', + group: 'google' + } + ], + 'new-api': [], + 'aws-bedrock': [], + poe: [ + { + id: 'gpt-4o', + name: 'GPT-4o', + provider: 'poe', + group: 'poe' + } + ] +} diff --git a/src/renderer/src/config/models/embedding.ts b/src/renderer/src/config/models/embedding.ts new file mode 100644 index 0000000000..7aa48a6d07 --- /dev/null +++ b/src/renderer/src/config/models/embedding.ts @@ -0,0 +1,38 @@ +import { Model } from '@renderer/types' +import { getLowerBaseModelName, isUserSelectedModelType } from '@renderer/utils' + +// Embedding models +export const EMBEDDING_REGEX = + /(?:^text-|embed|bge-|e5-|LLM2Vec|retrieval|uae-|gte-|jina-clip|jina-embeddings|voyage-)/i + +// Rerank models +export const RERANKING_REGEX = /(?:rerank|re-rank|re-ranker|re-ranking|retrieval|retriever)/i +export function isEmbeddingModel(model: Model): boolean { + if (!model || isRerankModel(model)) { + return false + } + + const modelId = getLowerBaseModelName(model.id) + + if (isUserSelectedModelType(model, 'embedding') !== undefined) { + return isUserSelectedModelType(model, 'embedding')! + } + + if (['anthropic'].includes(model?.provider)) { + return false + } + + if (model.provider === 'doubao' || modelId.includes('doubao')) { + return EMBEDDING_REGEX.test(model.name) + } + + return EMBEDDING_REGEX.test(modelId) || false +} + +export function isRerankModel(model: Model): boolean { + if (isUserSelectedModelType(model, 'rerank') !== undefined) { + return isUserSelectedModelType(model, 'rerank')! + } + const modelId = getLowerBaseModelName(model.id) + return model ? RERANKING_REGEX.test(modelId) || false : false +} diff --git a/src/renderer/src/config/models/index.ts b/src/renderer/src/config/models/index.ts new file mode 100644 index 0000000000..53e2a60903 --- /dev/null +++ b/src/renderer/src/config/models/index.ts @@ -0,0 +1,8 @@ +export * from './default' +export * from './embedding' +export * from './logo' +export * from './reasoning' +export * from './tooluse' +export * from './utils' +export * from './vision' +export * from './websearch' diff --git a/src/renderer/src/config/models/logo.ts b/src/renderer/src/config/models/logo.ts new file mode 100644 index 0000000000..5f10f0543c --- /dev/null +++ b/src/renderer/src/config/models/logo.ts @@ -0,0 +1,299 @@ +import LongCatAppLogo from '@renderer/assets/images/apps/longcat.svg' +import Ai360ModelLogo from '@renderer/assets/images/models/360.png' +import Ai360ModelLogoDark from '@renderer/assets/images/models/360_dark.png' +import AdeptModelLogo from '@renderer/assets/images/models/adept.png' +import AdeptModelLogoDark from '@renderer/assets/images/models/adept_dark.png' +import Ai21ModelLogo from '@renderer/assets/images/models/ai21.png' +import Ai21ModelLogoDark from '@renderer/assets/images/models/ai21_dark.png' +import AimassModelLogo from '@renderer/assets/images/models/aimass.png' +import AimassModelLogoDark from '@renderer/assets/images/models/aimass_dark.png' +import AisingaporeModelLogo from '@renderer/assets/images/models/aisingapore.png' +import AisingaporeModelLogoDark from '@renderer/assets/images/models/aisingapore_dark.png' +import BaichuanModelLogo from '@renderer/assets/images/models/baichuan.png' +import BaichuanModelLogoDark from '@renderer/assets/images/models/baichuan_dark.png' +import BgeModelLogo from '@renderer/assets/images/models/bge.webp' +import BigcodeModelLogo from '@renderer/assets/images/models/bigcode.webp' +import BigcodeModelLogoDark from '@renderer/assets/images/models/bigcode_dark.webp' +import BytedanceModelLogo from '@renderer/assets/images/models/byte_dance.svg' +import ChatGLMModelLogo from '@renderer/assets/images/models/chatglm.png' +import ChatGLMModelLogoDark from '@renderer/assets/images/models/chatglm_dark.png' +import ChatGptModelLogo from '@renderer/assets/images/models/chatgpt.jpeg' +import ClaudeModelLogo from '@renderer/assets/images/models/claude.png' +import ClaudeModelLogoDark from '@renderer/assets/images/models/claude_dark.png' +import CodegeexModelLogo from '@renderer/assets/images/models/codegeex.png' +import CodegeexModelLogoDark from '@renderer/assets/images/models/codegeex_dark.png' +import CodestralModelLogo from '@renderer/assets/images/models/codestral.png' +import CohereModelLogo from '@renderer/assets/images/models/cohere.png' +import CohereModelLogoDark from '@renderer/assets/images/models/cohere_dark.png' +import CopilotModelLogo from '@renderer/assets/images/models/copilot.png' +import CopilotModelLogoDark from '@renderer/assets/images/models/copilot_dark.png' +import DalleModelLogo from '@renderer/assets/images/models/dalle.png' +import DalleModelLogoDark from '@renderer/assets/images/models/dalle_dark.png' +import DbrxModelLogo from '@renderer/assets/images/models/dbrx.png' +import DeepSeekModelLogo from '@renderer/assets/images/models/deepseek.png' +import DeepSeekModelLogoDark from '@renderer/assets/images/models/deepseek_dark.png' +import DianxinModelLogo from '@renderer/assets/images/models/dianxin.png' +import DianxinModelLogoDark from '@renderer/assets/images/models/dianxin_dark.png' +import DoubaoModelLogo from '@renderer/assets/images/models/doubao.png' +import DoubaoModelLogoDark from '@renderer/assets/images/models/doubao_dark.png' +import { + default as EmbeddingModelLogo, + default as EmbeddingModelLogoDark +} from '@renderer/assets/images/models/embedding.png' +import FlashaudioModelLogo from '@renderer/assets/images/models/flashaudio.png' +import FlashaudioModelLogoDark from '@renderer/assets/images/models/flashaudio_dark.png' +import FluxModelLogo from '@renderer/assets/images/models/flux.png' +import FluxModelLogoDark from '@renderer/assets/images/models/flux_dark.png' +import GeminiModelLogo from '@renderer/assets/images/models/gemini.png' +import GeminiModelLogoDark from '@renderer/assets/images/models/gemini_dark.png' +import GemmaModelLogo from '@renderer/assets/images/models/gemma.png' +import GemmaModelLogoDark from '@renderer/assets/images/models/gemma_dark.png' +import { default as GoogleModelLogo, default as GoogleModelLogoDark } from '@renderer/assets/images/models/google.png' +import ChatGPT35ModelLogo from '@renderer/assets/images/models/gpt_3.5.png' +import ChatGPT4ModelLogo from '@renderer/assets/images/models/gpt_4.png' +import { + default as ChatGPT4ModelLogoDark, + default as ChatGPT35ModelLogoDark, + default as ChatGptModelLogoDark, + default as ChatGPTo1ModelLogoDark +} from '@renderer/assets/images/models/gpt_dark.png' +import ChatGPTImageModelLogo from '@renderer/assets/images/models/gpt_image_1.png' +import ChatGPTo1ModelLogo from '@renderer/assets/images/models/gpt_o1.png' +import GPT5ModelLogo from '@renderer/assets/images/models/gpt-5.png' +import GPT5ChatModelLogo from '@renderer/assets/images/models/gpt-5-chat.png' +import GPT5MiniModelLogo from '@renderer/assets/images/models/gpt-5-mini.png' +import GPT5NanoModelLogo from '@renderer/assets/images/models/gpt-5-nano.png' +import GrokModelLogo from '@renderer/assets/images/models/grok.png' +import GrokModelLogoDark from '@renderer/assets/images/models/grok_dark.png' +import GrypheModelLogo from '@renderer/assets/images/models/gryphe.png' +import GrypheModelLogoDark from '@renderer/assets/images/models/gryphe_dark.png' +import HailuoModelLogo from '@renderer/assets/images/models/hailuo.png' +import HailuoModelLogoDark from '@renderer/assets/images/models/hailuo_dark.png' +import HuggingfaceModelLogo from '@renderer/assets/images/models/huggingface.png' +import HuggingfaceModelLogoDark from '@renderer/assets/images/models/huggingface_dark.png' +import HunyuanModelLogo from '@renderer/assets/images/models/hunyuan.png' +import HunyuanModelLogoDark from '@renderer/assets/images/models/hunyuan_dark.png' +import IbmModelLogo from '@renderer/assets/images/models/ibm.png' +import IbmModelLogoDark from '@renderer/assets/images/models/ibm_dark.png' +import IdeogramModelLogo from '@renderer/assets/images/models/ideogram.svg' +import InternlmModelLogo from '@renderer/assets/images/models/internlm.png' +import InternlmModelLogoDark from '@renderer/assets/images/models/internlm_dark.png' +import InternvlModelLogo from '@renderer/assets/images/models/internvl.png' +import JinaModelLogo from '@renderer/assets/images/models/jina.png' +import JinaModelLogoDark from '@renderer/assets/images/models/jina_dark.png' +import KeLingModelLogo from '@renderer/assets/images/models/keling.png' +import KeLingModelLogoDark from '@renderer/assets/images/models/keling_dark.png' +import LlamaModelLogo from '@renderer/assets/images/models/llama.png' +import LlamaModelLogoDark from '@renderer/assets/images/models/llama_dark.png' +import LLavaModelLogo from '@renderer/assets/images/models/llava.png' +import LLavaModelLogoDark from '@renderer/assets/images/models/llava_dark.png' +import LumaModelLogo from '@renderer/assets/images/models/luma.png' +import LumaModelLogoDark from '@renderer/assets/images/models/luma_dark.png' +import MagicModelLogo from '@renderer/assets/images/models/magic.png' +import MagicModelLogoDark from '@renderer/assets/images/models/magic_dark.png' +import MediatekModelLogo from '@renderer/assets/images/models/mediatek.png' +import MediatekModelLogoDark from '@renderer/assets/images/models/mediatek_dark.png' +import MicrosoftModelLogo from '@renderer/assets/images/models/microsoft.png' +import MicrosoftModelLogoDark from '@renderer/assets/images/models/microsoft_dark.png' +import MidjourneyModelLogo from '@renderer/assets/images/models/midjourney.png' +import MidjourneyModelLogoDark from '@renderer/assets/images/models/midjourney_dark.png' +import { + default as MinicpmModelLogo, + default as MinicpmModelLogoDark +} from '@renderer/assets/images/models/minicpm.webp' +import MinimaxModelLogo from '@renderer/assets/images/models/minimax.png' +import MinimaxModelLogoDark from '@renderer/assets/images/models/minimax_dark.png' +import MistralModelLogo from '@renderer/assets/images/models/mixtral.png' +import MistralModelLogoDark from '@renderer/assets/images/models/mixtral_dark.png' +import MoonshotModelLogo from '@renderer/assets/images/models/moonshot.png' +import MoonshotModelLogoDark from '@renderer/assets/images/models/moonshot_dark.png' +import { + default as NousResearchModelLogo, + default as NousResearchModelLogoDark +} from '@renderer/assets/images/models/nousresearch.png' +import NvidiaModelLogo from '@renderer/assets/images/models/nvidia.png' +import NvidiaModelLogoDark from '@renderer/assets/images/models/nvidia_dark.png' +import PalmModelLogo from '@renderer/assets/images/models/palm.png' +import PalmModelLogoDark from '@renderer/assets/images/models/palm_dark.png' +import PanguModelLogo from '@renderer/assets/images/models/pangu.svg' +import { + default as PerplexityModelLogo, + default as PerplexityModelLogoDark +} from '@renderer/assets/images/models/perplexity.png' +import PixtralModelLogo from '@renderer/assets/images/models/pixtral.png' +import PixtralModelLogoDark from '@renderer/assets/images/models/pixtral_dark.png' +import QwenModelLogo from '@renderer/assets/images/models/qwen.png' +import QwenModelLogoDark from '@renderer/assets/images/models/qwen_dark.png' +import RakutenaiModelLogo from '@renderer/assets/images/models/rakutenai.png' +import RakutenaiModelLogoDark from '@renderer/assets/images/models/rakutenai_dark.png' +import SparkDeskModelLogo from '@renderer/assets/images/models/sparkdesk.png' +import SparkDeskModelLogoDark from '@renderer/assets/images/models/sparkdesk_dark.png' +import StabilityModelLogo from '@renderer/assets/images/models/stability.png' +import StabilityModelLogoDark from '@renderer/assets/images/models/stability_dark.png' +import StepModelLogo from '@renderer/assets/images/models/step.png' +import StepModelLogoDark from '@renderer/assets/images/models/step_dark.png' +import SunoModelLogo from '@renderer/assets/images/models/suno.png' +import SunoModelLogoDark from '@renderer/assets/images/models/suno_dark.png' +import TeleModelLogo from '@renderer/assets/images/models/tele.png' +import TeleModelLogoDark from '@renderer/assets/images/models/tele_dark.png' +import TokenFluxModelLogo from '@renderer/assets/images/models/tokenflux.png' +import TokenFluxModelLogoDark from '@renderer/assets/images/models/tokenflux_dark.png' +import UpstageModelLogo from '@renderer/assets/images/models/upstage.png' +import UpstageModelLogoDark from '@renderer/assets/images/models/upstage_dark.png' +import ViduModelLogo from '@renderer/assets/images/models/vidu.png' +import ViduModelLogoDark from '@renderer/assets/images/models/vidu_dark.png' +import VoyageModelLogo from '@renderer/assets/images/models/voyageai.png' +import WenxinModelLogo from '@renderer/assets/images/models/wenxin.png' +import WenxinModelLogoDark from '@renderer/assets/images/models/wenxin_dark.png' +import XirangModelLogo from '@renderer/assets/images/models/xirang.png' +import XirangModelLogoDark from '@renderer/assets/images/models/xirang_dark.png' +import YiModelLogo from '@renderer/assets/images/models/yi.png' +import YiModelLogoDark from '@renderer/assets/images/models/yi_dark.png' +import ZhipuModelLogo from '@renderer/assets/images/models/zhipu.png' +import ZhipuModelLogoDark from '@renderer/assets/images/models/zhipu_dark.png' +import YoudaoLogo from '@renderer/assets/images/providers/netease-youdao.svg' +import NomicLogo from '@renderer/assets/images/providers/nomic.png' +import ZhipuProviderLogo from '@renderer/assets/images/providers/zhipu.png' + +export function getModelLogo(modelId: string) { + const isLight = true + + if (!modelId) { + return undefined + } + + const logoMap = { + pixtral: isLight ? PixtralModelLogo : PixtralModelLogoDark, + jina: isLight ? JinaModelLogo : JinaModelLogoDark, + abab: isLight ? MinimaxModelLogo : MinimaxModelLogoDark, + minimax: isLight ? MinimaxModelLogo : MinimaxModelLogoDark, + veo: isLight ? GeminiModelLogo : GeminiModelLogoDark, + o1: isLight ? ChatGPTo1ModelLogo : ChatGPTo1ModelLogoDark, + o3: isLight ? ChatGPTo1ModelLogo : ChatGPTo1ModelLogoDark, + o4: isLight ? ChatGPTo1ModelLogo : ChatGPTo1ModelLogoDark, + 'gpt-image': ChatGPTImageModelLogo, + 'gpt-3': isLight ? ChatGPT35ModelLogo : ChatGPT35ModelLogoDark, + 'gpt-4': isLight ? ChatGPT4ModelLogo : ChatGPT4ModelLogoDark, + 'gpt-5-mini': GPT5MiniModelLogo, + 'gpt-5-nano': GPT5NanoModelLogo, + 'gpt-5-chat': GPT5ChatModelLogo, + 'gpt-5': GPT5ModelLogo, + gpts: isLight ? ChatGPT4ModelLogo : ChatGPT4ModelLogoDark, + 'gpt-oss(?:-[\\w-]+)': isLight ? ChatGptModelLogo : ChatGptModelLogoDark, + 'text-moderation': isLight ? ChatGptModelLogo : ChatGptModelLogoDark, + 'babbage-': isLight ? ChatGptModelLogo : ChatGptModelLogoDark, + '(sora-|sora_)': isLight ? ChatGptModelLogo : ChatGptModelLogoDark, + '(^|/)omni-': isLight ? ChatGptModelLogo : ChatGptModelLogoDark, + 'Embedding-V1': isLight ? WenxinModelLogo : WenxinModelLogoDark, + 'text-embedding-v': isLight ? QwenModelLogo : QwenModelLogoDark, + 'text-embedding': isLight ? ChatGptModelLogo : ChatGptModelLogoDark, + 'davinci-': isLight ? ChatGptModelLogo : ChatGptModelLogoDark, + glm: isLight ? ChatGLMModelLogo : ChatGLMModelLogoDark, + deepseek: isLight ? DeepSeekModelLogo : DeepSeekModelLogoDark, + '(qwen|qwq|qwq-|qvq-|wan-)': isLight ? QwenModelLogo : QwenModelLogoDark, + gemma: isLight ? GemmaModelLogo : GemmaModelLogoDark, + 'yi-': isLight ? YiModelLogo : YiModelLogoDark, + llama: isLight ? LlamaModelLogo : LlamaModelLogoDark, + mixtral: isLight ? MistralModelLogo : MistralModelLogo, + mistral: isLight ? MistralModelLogo : MistralModelLogoDark, + codestral: CodestralModelLogo, + ministral: isLight ? MistralModelLogo : MistralModelLogoDark, + magistral: isLight ? MistralModelLogo : MistralModelLogoDark, + moonshot: isLight ? MoonshotModelLogo : MoonshotModelLogoDark, + kimi: isLight ? MoonshotModelLogo : MoonshotModelLogoDark, + phi: isLight ? MicrosoftModelLogo : MicrosoftModelLogoDark, + baichuan: isLight ? BaichuanModelLogo : BaichuanModelLogoDark, + '(claude|anthropic-)': isLight ? ClaudeModelLogo : ClaudeModelLogoDark, + gemini: isLight ? GeminiModelLogo : GeminiModelLogoDark, + bison: isLight ? PalmModelLogo : PalmModelLogoDark, + palm: isLight ? PalmModelLogo : PalmModelLogoDark, + step: isLight ? StepModelLogo : StepModelLogoDark, + hailuo: isLight ? HailuoModelLogo : HailuoModelLogoDark, + doubao: isLight ? DoubaoModelLogo : DoubaoModelLogoDark, + seedream: isLight ? DoubaoModelLogo : DoubaoModelLogoDark, + 'ep-202': isLight ? DoubaoModelLogo : DoubaoModelLogoDark, + cohere: isLight ? CohereModelLogo : CohereModelLogoDark, + command: isLight ? CohereModelLogo : CohereModelLogoDark, + minicpm: isLight ? MinicpmModelLogo : MinicpmModelLogoDark, + '360': isLight ? Ai360ModelLogo : Ai360ModelLogoDark, + aimass: isLight ? AimassModelLogo : AimassModelLogoDark, + codegeex: isLight ? CodegeexModelLogo : CodegeexModelLogoDark, + copilot: isLight ? CopilotModelLogo : CopilotModelLogoDark, + creative: isLight ? CopilotModelLogo : CopilotModelLogoDark, + balanced: isLight ? CopilotModelLogo : CopilotModelLogoDark, + precise: isLight ? CopilotModelLogo : CopilotModelLogoDark, + dalle: isLight ? DalleModelLogo : DalleModelLogoDark, + 'dall-e': isLight ? DalleModelLogo : DalleModelLogoDark, + dbrx: isLight ? DbrxModelLogo : DbrxModelLogo, + flashaudio: isLight ? FlashaudioModelLogo : FlashaudioModelLogoDark, + flux: isLight ? FluxModelLogo : FluxModelLogoDark, + grok: isLight ? GrokModelLogo : GrokModelLogoDark, + hunyuan: isLight ? HunyuanModelLogo : HunyuanModelLogoDark, + internlm: isLight ? InternlmModelLogo : InternlmModelLogoDark, + internvl: InternvlModelLogo, + llava: isLight ? LLavaModelLogo : LLavaModelLogoDark, + magic: isLight ? MagicModelLogo : MagicModelLogoDark, + midjourney: isLight ? MidjourneyModelLogo : MidjourneyModelLogoDark, + 'mj-': isLight ? MidjourneyModelLogo : MidjourneyModelLogoDark, + 'tao-': isLight ? WenxinModelLogo : WenxinModelLogoDark, + 'ernie-': isLight ? WenxinModelLogo : WenxinModelLogoDark, + voice: isLight ? FlashaudioModelLogo : FlashaudioModelLogoDark, + 'tts-1': isLight ? ChatGptModelLogo : ChatGptModelLogoDark, + 'whisper-': isLight ? ChatGptModelLogo : ChatGptModelLogoDark, + 'stable-': isLight ? StabilityModelLogo : StabilityModelLogoDark, + sd2: isLight ? StabilityModelLogo : StabilityModelLogoDark, + sd3: isLight ? StabilityModelLogo : StabilityModelLogoDark, + sdxl: isLight ? StabilityModelLogo : StabilityModelLogoDark, + sparkdesk: isLight ? SparkDeskModelLogo : SparkDeskModelLogoDark, + generalv: isLight ? SparkDeskModelLogo : SparkDeskModelLogoDark, + wizardlm: isLight ? MicrosoftModelLogo : MicrosoftModelLogoDark, + microsoft: isLight ? MicrosoftModelLogo : MicrosoftModelLogoDark, + hermes: isLight ? NousResearchModelLogo : NousResearchModelLogoDark, + gryphe: isLight ? GrypheModelLogo : GrypheModelLogoDark, + suno: isLight ? SunoModelLogo : SunoModelLogoDark, + chirp: isLight ? SunoModelLogo : SunoModelLogoDark, + luma: isLight ? LumaModelLogo : LumaModelLogoDark, + keling: isLight ? KeLingModelLogo : KeLingModelLogoDark, + 'vidu-': isLight ? ViduModelLogo : ViduModelLogoDark, + ai21: isLight ? Ai21ModelLogo : Ai21ModelLogoDark, + 'jamba-': isLight ? Ai21ModelLogo : Ai21ModelLogoDark, + mythomax: isLight ? GrypheModelLogo : GrypheModelLogoDark, + nvidia: isLight ? NvidiaModelLogo : NvidiaModelLogoDark, + dianxin: isLight ? DianxinModelLogo : DianxinModelLogoDark, + tele: isLight ? TeleModelLogo : TeleModelLogoDark, + adept: isLight ? AdeptModelLogo : AdeptModelLogoDark, + aisingapore: isLight ? AisingaporeModelLogo : AisingaporeModelLogoDark, + bigcode: isLight ? BigcodeModelLogo : BigcodeModelLogoDark, + mediatek: isLight ? MediatekModelLogo : MediatekModelLogoDark, + upstage: isLight ? UpstageModelLogo : UpstageModelLogoDark, + rakutenai: isLight ? RakutenaiModelLogo : RakutenaiModelLogoDark, + ibm: isLight ? IbmModelLogo : IbmModelLogoDark, + 'google/': isLight ? GoogleModelLogo : GoogleModelLogoDark, + xirang: isLight ? XirangModelLogo : XirangModelLogoDark, + hugging: isLight ? HuggingfaceModelLogo : HuggingfaceModelLogoDark, + youdao: YoudaoLogo, + 'embedding-3': ZhipuProviderLogo, + embedding: isLight ? EmbeddingModelLogo : EmbeddingModelLogoDark, + perplexity: isLight ? PerplexityModelLogo : PerplexityModelLogoDark, + sonar: isLight ? PerplexityModelLogo : PerplexityModelLogoDark, + 'bge-': BgeModelLogo, + 'voyage-': VoyageModelLogo, + tokenflux: isLight ? TokenFluxModelLogo : TokenFluxModelLogoDark, + 'nomic-': NomicLogo, + 'pangu-': PanguModelLogo, + cogview: isLight ? ZhipuModelLogo : ZhipuModelLogoDark, + zhipu: isLight ? ZhipuModelLogo : ZhipuModelLogoDark, + longcat: LongCatAppLogo, + bytedance: BytedanceModelLogo, + '(V_1|V_1_TURBO|V_2|V_2A|V_2_TURBO|DESCRIBE|UPSCALE)': IdeogramModelLogo + } + + for (const key in logoMap) { + const regex = new RegExp(key, 'i') + if (regex.test(modelId)) { + return logoMap[key] + } + } + + return undefined +} diff --git a/src/renderer/src/config/models/reasoning.ts b/src/renderer/src/config/models/reasoning.ts new file mode 100644 index 0000000000..ba36ae1938 --- /dev/null +++ b/src/renderer/src/config/models/reasoning.ts @@ -0,0 +1,436 @@ +import { + Model, + ReasoningEffortConfig, + SystemProviderId, + ThinkingModelType, + ThinkingOptionConfig +} from '@renderer/types' +import { getLowerBaseModelName, isUserSelectedModelType } from '@renderer/utils' + +import { isEmbeddingModel, isRerankModel } from './embedding' +import { isGPT5SeriesModel } from './utils' +import { isTextToImageModel } from './vision' +import { GEMINI_FLASH_MODEL_REGEX } from './websearch' + +// Reasoning models +export const REASONING_REGEX = + /^(o\d+(?:-[\w-]+)?|.*\b(?:reasoning|reasoner|thinking)\b.*|.*-[rR]\d+.*|.*\bqwq(?:-[\w-]+)?\b.*|.*\bhunyuan-t1(?:-[\w-]+)?\b.*|.*\bglm-zero-preview\b.*|.*\bgrok-(?:3-mini|4)(?:-[\w-]+)?\b.*)$/i + +// 模型类型到支持的reasoning_effort的映射表 +// TODO: refactor this. too many identical options +export const MODEL_SUPPORTED_REASONING_EFFORT: ReasoningEffortConfig = { + default: ['low', 'medium', 'high'] as const, + o: ['low', 'medium', 'high'] as const, + gpt5: ['minimal', 'low', 'medium', 'high'] as const, + grok: ['low', 'high'] as const, + gemini: ['low', 'medium', 'high', 'auto'] as const, + gemini_pro: ['low', 'medium', 'high', 'auto'] as const, + qwen: ['low', 'medium', 'high'] as const, + qwen_thinking: ['low', 'medium', 'high'] as const, + doubao: ['auto', 'high'] as const, + doubao_no_auto: ['high'] as const, + hunyuan: ['auto'] as const, + zhipu: ['auto'] as const, + perplexity: ['low', 'medium', 'high'] as const, + deepseek_hybrid: ['auto'] as const +} as const + +// 模型类型到支持选项的映射表 +export const MODEL_SUPPORTED_OPTIONS: ThinkingOptionConfig = { + default: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.default] as const, + o: MODEL_SUPPORTED_REASONING_EFFORT.o, + gpt5: [...MODEL_SUPPORTED_REASONING_EFFORT.gpt5] as const, + grok: MODEL_SUPPORTED_REASONING_EFFORT.grok, + gemini: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.gemini] as const, + gemini_pro: MODEL_SUPPORTED_REASONING_EFFORT.gemini_pro, + qwen: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.qwen] as const, + qwen_thinking: MODEL_SUPPORTED_REASONING_EFFORT.qwen_thinking, + doubao: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.doubao] as const, + doubao_no_auto: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.doubao_no_auto] as const, + hunyuan: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.hunyuan] as const, + zhipu: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.zhipu] as const, + perplexity: MODEL_SUPPORTED_REASONING_EFFORT.perplexity, + deepseek_hybrid: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.deepseek_hybrid] as const +} as const + +export const getThinkModelType = (model: Model): ThinkingModelType => { + let thinkingModelType: ThinkingModelType = 'default' + if (isGPT5SeriesModel(model)) { + thinkingModelType = 'gpt5' + } else if (isSupportedReasoningEffortOpenAIModel(model)) { + thinkingModelType = 'o' + } else if (isSupportedThinkingTokenGeminiModel(model)) { + if (GEMINI_FLASH_MODEL_REGEX.test(model.id)) { + thinkingModelType = 'gemini' + } else { + thinkingModelType = 'gemini_pro' + } + } else if (isSupportedReasoningEffortGrokModel(model)) thinkingModelType = 'grok' + else if (isSupportedThinkingTokenQwenModel(model)) { + if (isQwenAlwaysThinkModel(model)) { + thinkingModelType = 'qwen_thinking' + } + thinkingModelType = 'qwen' + } else if (isSupportedThinkingTokenDoubaoModel(model)) { + if (isDoubaoThinkingAutoModel(model)) { + thinkingModelType = 'doubao' + } else { + thinkingModelType = 'doubao_no_auto' + } + } else if (isSupportedThinkingTokenHunyuanModel(model)) thinkingModelType = 'hunyuan' + else if (isSupportedReasoningEffortPerplexityModel(model)) thinkingModelType = 'perplexity' + else if (isSupportedThinkingTokenZhipuModel(model)) thinkingModelType = 'zhipu' + else if (isDeepSeekHybridInferenceModel(model)) thinkingModelType = 'deepseek_hybrid' + return thinkingModelType +} + +/** 用于判断是否支持控制思考,但不一定以reasoning_effort的方式 */ +export function isSupportedThinkingTokenModel(model?: Model): boolean { + if (!model) { + return false + } + + // Specifically for DeepSeek V3.1. White list for now + if (isDeepSeekHybridInferenceModel(model)) { + return ( + ['openrouter', 'dashscope', 'modelscope', 'doubao', 'silicon', 'nvidia', 'ppio'] satisfies SystemProviderId[] + ).some((id) => id === model.provider) + } + + return ( + isSupportedThinkingTokenGeminiModel(model) || + isSupportedThinkingTokenQwenModel(model) || + isSupportedThinkingTokenClaudeModel(model) || + isSupportedThinkingTokenDoubaoModel(model) || + isSupportedThinkingTokenHunyuanModel(model) || + isSupportedThinkingTokenZhipuModel(model) + ) +} + +export function isSupportedReasoningEffortModel(model?: Model): boolean { + if (!model) { + return false + } + + return ( + isSupportedReasoningEffortOpenAIModel(model) || + isSupportedReasoningEffortGrokModel(model) || + isSupportedReasoningEffortPerplexityModel(model) + ) +} + +export function isSupportedReasoningEffortGrokModel(model?: Model): boolean { + if (!model) { + return false + } + + const modelId = getLowerBaseModelName(model.id) + if (modelId.includes('grok-3-mini')) { + return true + } + + return false +} + +export function isGrokReasoningModel(model?: Model): boolean { + if (!model) { + return false + } + const modelId = getLowerBaseModelName(model.id) + if (isSupportedReasoningEffortGrokModel(model) || modelId.includes('grok-4')) { + return true + } + + return false +} + +export function isGeminiReasoningModel(model?: Model): boolean { + if (!model) { + return false + } + + const modelId = getLowerBaseModelName(model.id) + if (modelId.startsWith('gemini') && modelId.includes('thinking')) { + return true + } + + if (isSupportedThinkingTokenGeminiModel(model)) { + return true + } + + return false +} + +export const isSupportedThinkingTokenGeminiModel = (model: Model): boolean => { + const modelId = getLowerBaseModelName(model.id, '/') + if (modelId.includes('gemini-2.5')) { + if (modelId.includes('image') || modelId.includes('tts')) { + return false + } + return true + } else { + return false + } +} + +/** 是否为Qwen推理模型 */ +export function isQwenReasoningModel(model?: Model): boolean { + if (!model) { + return false + } + + const modelId = getLowerBaseModelName(model.id, '/') + + if (modelId.startsWith('qwen3')) { + if (modelId.includes('thinking')) { + return true + } + } + + if (isSupportedThinkingTokenQwenModel(model)) { + return true + } + + if (modelId.includes('qwq') || modelId.includes('qvq')) { + return true + } + + return false +} + +/** 是否为支持思考控制的Qwen3推理模型 */ +export function isSupportedThinkingTokenQwenModel(model?: Model): boolean { + if (!model) { + return false + } + + const modelId = getLowerBaseModelName(model.id, '/') + + if (modelId.includes('coder')) { + return false + } + + if (modelId.startsWith('qwen3')) { + // instruct 是非思考模型 thinking 是思考模型,二者都不能控制思考 + if (modelId.includes('instruct') || modelId.includes('thinking') || modelId.includes('qwen3-max')) { + return false + } + return true + } + + return [ + 'qwen-plus', + 'qwen-plus-latest', + 'qwen-plus-0428', + 'qwen-plus-2025-04-28', + 'qwen-plus-0714', + 'qwen-plus-2025-07-14', + 'qwen-turbo', + 'qwen-turbo-latest', + 'qwen-turbo-0428', + 'qwen-turbo-2025-04-28', + 'qwen-turbo-0715', + 'qwen-turbo-2025-07-15', + 'qwen-flash', + 'qwen-flash-2025-07-28' + ].includes(modelId) +} + +/** 是否为不支持思考控制的Qwen推理模型 */ +export function isQwenAlwaysThinkModel(model?: Model): boolean { + if (!model) { + return false + } + const modelId = getLowerBaseModelName(model.id, '/') + return modelId.startsWith('qwen3') && modelId.includes('thinking') +} + +// Doubao 支持思考模式的模型正则 +export const DOUBAO_THINKING_MODEL_REGEX = + /doubao-(?:1[.-]5-thinking-vision-pro|1[.-]5-thinking-pro-m|seed-1[.-]6(?:-flash)?(?!-(?:thinking)(?:-|$)))(?:-[\w-]+)*/i + +// 支持 auto 的 Doubao 模型 doubao-seed-1.6-xxx doubao-seed-1-6-xxx doubao-1-5-thinking-pro-m-xxx +export const DOUBAO_THINKING_AUTO_MODEL_REGEX = + /doubao-(1-5-thinking-pro-m|seed-1[.-]6)(?!-(?:flash|thinking)(?:-|$))(?:-[\w-]+)*/i + +export function isDoubaoThinkingAutoModel(model: Model): boolean { + const modelId = getLowerBaseModelName(model.id) + return DOUBAO_THINKING_AUTO_MODEL_REGEX.test(modelId) || DOUBAO_THINKING_AUTO_MODEL_REGEX.test(model.name) +} + +export function isSupportedThinkingTokenDoubaoModel(model?: Model): boolean { + if (!model) { + return false + } + + const modelId = getLowerBaseModelName(model.id, '/') + + return DOUBAO_THINKING_MODEL_REGEX.test(modelId) || DOUBAO_THINKING_MODEL_REGEX.test(model.name) +} + +export function isClaudeReasoningModel(model?: Model): boolean { + if (!model) { + return false + } + const modelId = getLowerBaseModelName(model.id, '/') + return ( + modelId.includes('claude-3-7-sonnet') || + modelId.includes('claude-3.7-sonnet') || + modelId.includes('claude-sonnet-4') || + modelId.includes('claude-opus-4') + ) +} + +export const isSupportedThinkingTokenClaudeModel = isClaudeReasoningModel + +export const isSupportedThinkingTokenHunyuanModel = (model?: Model): boolean => { + if (!model) { + return false + } + const modelId = getLowerBaseModelName(model.id, '/') + return modelId.includes('hunyuan-a13b') +} + +export const isHunyuanReasoningModel = (model?: Model): boolean => { + if (!model) { + return false + } + const modelId = getLowerBaseModelName(model.id, '/') + + return isSupportedThinkingTokenHunyuanModel(model) || modelId.includes('hunyuan-t1') +} + +export const isPerplexityReasoningModel = (model?: Model): boolean => { + if (!model) { + return false + } + + const modelId = getLowerBaseModelName(model.id, '/') + return isSupportedReasoningEffortPerplexityModel(model) || modelId.includes('reasoning') +} + +export const isSupportedReasoningEffortPerplexityModel = (model: Model): boolean => { + const modelId = getLowerBaseModelName(model.id, '/') + return modelId.includes('sonar-deep-research') +} + +export const isSupportedThinkingTokenZhipuModel = (model: Model): boolean => { + const modelId = getLowerBaseModelName(model.id, '/') + return modelId.includes('glm-4.5') +} + +export const isDeepSeekHybridInferenceModel = (model: Model) => { + const modelId = getLowerBaseModelName(model.id) + // deepseek官方使用chat和reasoner做推理控制,其他provider需要单独判断,id可能会有所差别 + // openrouter: deepseek/deepseek-chat-v3.1 不知道会不会有其他provider仿照ds官方分出一个同id的作为非思考模式的模型,这里有风险 + return /deepseek-v3(?:\.1|-1-\d+)?/.test(modelId) || modelId.includes('deepseek-chat-v3.1') +} + +export const isSupportedThinkingTokenDeepSeekModel = isDeepSeekHybridInferenceModel + +export const isZhipuReasoningModel = (model?: Model): boolean => { + if (!model) { + return false + } + const modelId = getLowerBaseModelName(model.id, '/') + return isSupportedThinkingTokenZhipuModel(model) || modelId.includes('glm-z1') +} + +export const isStepReasoningModel = (model?: Model): boolean => { + if (!model) { + return false + } + const modelId = getLowerBaseModelName(model.id, '/') + return modelId.includes('step-3') || modelId.includes('step-r1-v-mini') +} + +export function isReasoningModel(model?: Model): boolean { + if (!model || isEmbeddingModel(model) || isRerankModel(model) || isTextToImageModel(model)) { + return false + } + + if (isUserSelectedModelType(model, 'reasoning') !== undefined) { + return isUserSelectedModelType(model, 'reasoning')! + } + + const modelId = getLowerBaseModelName(model.id) + + if (model.provider === 'doubao' || modelId.includes('doubao')) { + return ( + REASONING_REGEX.test(modelId) || + REASONING_REGEX.test(model.name) || + isSupportedThinkingTokenDoubaoModel(model) || + isDeepSeekHybridInferenceModel(model) || + isDeepSeekHybridInferenceModel({ ...model, id: model.name }) || + false + ) + } + + if ( + isClaudeReasoningModel(model) || + isOpenAIReasoningModel(model) || + isGeminiReasoningModel(model) || + isQwenReasoningModel(model) || + isGrokReasoningModel(model) || + isHunyuanReasoningModel(model) || + isPerplexityReasoningModel(model) || + isZhipuReasoningModel(model) || + isStepReasoningModel(model) || + isDeepSeekHybridInferenceModel(model) || + modelId.includes('magistral') || + modelId.includes('minimax-m1') || + modelId.includes('pangu-pro-moe') + ) { + return true + } + + return REASONING_REGEX.test(modelId) || false +} + +export function isOpenAIReasoningModel(model: Model): boolean { + const modelId = getLowerBaseModelName(model.id, '/') + return isSupportedReasoningEffortOpenAIModel(model) || modelId.includes('o1') +} + +export function isSupportedReasoningEffortOpenAIModel(model: Model): boolean { + const modelId = getLowerBaseModelName(model.id) + return ( + (modelId.includes('o1') && !(modelId.includes('o1-preview') || modelId.includes('o1-mini'))) || + modelId.includes('o3') || + modelId.includes('o4') || + modelId.includes('gpt-oss') || + (isGPT5SeriesModel(model) && !modelId.includes('chat')) + ) +} + +export const THINKING_TOKEN_MAP: Record = { + // Gemini models + 'gemini-2\\.5-flash-lite.*$': { min: 512, max: 24576 }, + 'gemini-.*-flash.*$': { min: 0, max: 24576 }, + 'gemini-.*-pro.*$': { min: 128, max: 32768 }, + + // Qwen models + 'qwen3-235b-a22b-thinking-2507$': { min: 0, max: 81_920 }, + 'qwen3-30b-a3b-thinking-2507$': { min: 0, max: 81_920 }, + 'qwen-plus-2025-07-28$': { min: 0, max: 81_920 }, + 'qwen-plus-latest$': { min: 0, max: 81_920 }, + 'qwen3-1\\.7b$': { min: 0, max: 30_720 }, + 'qwen3-0\\.6b$': { min: 0, max: 30_720 }, + 'qwen-plus.*$': { min: 0, max: 38_912 }, + 'qwen-turbo.*$': { min: 0, max: 38_912 }, + 'qwen-flash.*$': { min: 0, max: 81_920 }, + 'qwen3-.*$': { min: 1024, max: 38_912 }, + + // Claude models + 'claude-3[.-]7.*sonnet.*$': { min: 1024, max: 64000 }, + 'claude-(:?sonnet|opus)-4.*$': { min: 1024, max: 32000 } +} + +export const findTokenLimit = (modelId: string): { min: number; max: number } | undefined => { + for (const [pattern, limits] of Object.entries(THINKING_TOKEN_MAP)) { + if (new RegExp(pattern, 'i').test(modelId)) { + return limits + } + } + return undefined +} diff --git a/src/renderer/src/config/models/tooluse.ts b/src/renderer/src/config/models/tooluse.ts new file mode 100644 index 0000000000..f1f27e0fd7 --- /dev/null +++ b/src/renderer/src/config/models/tooluse.ts @@ -0,0 +1,92 @@ +import { isSystemProviderId, Model } from '@renderer/types' +import { getLowerBaseModelName, isUserSelectedModelType } from '@renderer/utils' + +import { isEmbeddingModel, isRerankModel } from './embedding' +import { isDeepSeekHybridInferenceModel } from './reasoning' +import { isPureGenerateImageModel, isTextToImageModel } from './vision' + +// Tool calling models +export const FUNCTION_CALLING_MODELS = [ + 'gpt-4o', + 'gpt-4o-mini', + 'gpt-4', + 'gpt-4.5', + 'gpt-oss(?:-[\\w-]+)', + 'gpt-5(?:-[0-9-]+)?', + 'o(1|3|4)(?:-[\\w-]+)?', + 'claude', + 'qwen', + 'qwen3', + 'hunyuan', + 'deepseek', + 'glm-4(?:-[\\w-]+)?', + 'glm-4.5(?:-[\\w-]+)?', + 'learnlm(?:-[\\w-]+)?', + 'gemini(?:-[\\w-]+)?', // 提前排除了gemini的嵌入模型 + 'grok-3(?:-[\\w-]+)?', + 'doubao-seed-1[.-]6(?:-[\\w-]+)?', + 'kimi-k2(?:-[\\w-]+)?' +] + +const FUNCTION_CALLING_EXCLUDED_MODELS = [ + 'aqa(?:-[\\w-]+)?', + 'imagen(?:-[\\w-]+)?', + 'o1-mini', + 'o1-preview', + 'AIDC-AI/Marco-o1', + 'gemini-1(?:\\.[\\w-]+)?', + 'qwen-mt(?:-[\\w-]+)?', + 'gpt-5-chat(?:-[\\w-]+)?', + 'glm-4\\.5v' +] + +export const FUNCTION_CALLING_REGEX = new RegExp( + `\\b(?!(?:${FUNCTION_CALLING_EXCLUDED_MODELS.join('|')})\\b)(?:${FUNCTION_CALLING_MODELS.join('|')})\\b`, + 'i' +) + +export function isFunctionCallingModel(model?: Model): boolean { + if ( + !model || + isEmbeddingModel(model) || + isRerankModel(model) || + isTextToImageModel(model) || + isPureGenerateImageModel(model) + ) { + return false + } + + const modelId = getLowerBaseModelName(model.id) + + if (isUserSelectedModelType(model, 'function_calling') !== undefined) { + return isUserSelectedModelType(model, 'function_calling')! + } + + if (model.provider === 'qiniu') { + return ['deepseek-v3-tool', 'deepseek-v3-0324', 'qwq-32b', 'qwen2.5-72b-instruct'].includes(modelId) + } + + if (model.provider === 'doubao' || modelId.includes('doubao')) { + return FUNCTION_CALLING_REGEX.test(modelId) || FUNCTION_CALLING_REGEX.test(model.name) + } + + if (['deepseek', 'anthropic', 'kimi', 'moonshot'].includes(model.provider)) { + return true + } + + // 2025/08/26 百炼与火山引擎均不支持 v3.1 函数调用 + // 先默认支持 + if (isDeepSeekHybridInferenceModel(model)) { + if (isSystemProviderId(model.provider)) { + switch (model.provider) { + case 'dashscope': + case 'doubao': + // case 'nvidia': // nvidia api 太烂了 测不了能不能用 先假设能用 + return false + } + } + return true + } + + return FUNCTION_CALLING_REGEX.test(modelId) +} diff --git a/src/renderer/src/config/models/utils.ts b/src/renderer/src/config/models/utils.ts new file mode 100644 index 0000000000..3fd27308f8 --- /dev/null +++ b/src/renderer/src/config/models/utils.ts @@ -0,0 +1,243 @@ +import { Model } from '@renderer/types' +import { getLowerBaseModelName } from '@renderer/utils' +import OpenAI from 'openai' + +import { WEB_SEARCH_PROMPT_FOR_OPENROUTER } from '../prompts' +import { getWebSearchTools } from '../tools' +import { isOpenAIReasoningModel } from './reasoning' +import { isGenerateImageModel, isVisionModel } from './vision' +import { isOpenAIWebSearchChatCompletionOnlyModel } from './websearch' +export const NOT_SUPPORTED_REGEX = /(?:^tts|whisper|speech)/i + +export const OPENAI_NO_SUPPORT_DEV_ROLE_MODELS = ['o1-preview', 'o1-mini'] + +export function isOpenAILLMModel(model: Model): boolean { + if (!model) { + return false + } + const modelId = getLowerBaseModelName(model.id) + + if (modelId.includes('gpt-4o-image')) { + return false + } + if (isOpenAIReasoningModel(model)) { + return true + } + if (modelId.includes('gpt')) { + return true + } + return false +} + +export function isOpenAIModel(model: Model): boolean { + if (!model) { + return false + } + const modelId = getLowerBaseModelName(model.id) + + return modelId.includes('gpt') || isOpenAIReasoningModel(model) +} + +export function isSupportFlexServiceTierModel(model: Model): boolean { + if (!model) { + return false + } + const modelId = getLowerBaseModelName(model.id) + return ( + (modelId.includes('o3') && !modelId.includes('o3-mini')) || modelId.includes('o4-mini') || modelId.includes('gpt-5') + ) +} +export function isSupportedFlexServiceTier(model: Model): boolean { + return isSupportFlexServiceTierModel(model) +} + +export function isSupportVerbosityModel(model: Model): boolean { + const modelId = getLowerBaseModelName(model.id) + return isGPT5SeriesModel(model) && !modelId.includes('chat') +} + +export function isOpenAIChatCompletionOnlyModel(model: Model): boolean { + if (!model) { + return false + } + + const modelId = getLowerBaseModelName(model.id) + return ( + modelId.includes('gpt-4o-search-preview') || + modelId.includes('gpt-4o-mini-search-preview') || + modelId.includes('o1-mini') || + modelId.includes('o1-preview') + ) +} + +export function isGrokModel(model?: Model): boolean { + if (!model) { + return false + } + const modelId = getLowerBaseModelName(model.id) + return modelId.includes('grok') +} + +export function isSupportedModel(model: OpenAI.Models.Model): boolean { + if (!model) { + return false + } + + const modelId = getLowerBaseModelName(model.id) + + return !NOT_SUPPORTED_REGEX.test(modelId) +} + +export function isNotSupportTemperatureAndTopP(model: Model): boolean { + if (!model) { + return true + } + + if ( + (isOpenAIReasoningModel(model) && !isOpenAIOpenWeightModel(model)) || + isOpenAIChatCompletionOnlyModel(model) || + isQwenMTModel(model) + ) { + return true + } + + return false +} + +export function getOpenAIWebSearchParams(model: Model, isEnableWebSearch?: boolean): Record { + if (!isEnableWebSearch) { + return {} + } + + const webSearchTools = getWebSearchTools(model) + + if (model.provider === 'grok') { + return { + search_parameters: { + mode: 'auto', + return_citations: true, + sources: [{ type: 'web' }, { type: 'x' }, { type: 'news' }] + } + } + } + + if (model.provider === 'hunyuan') { + return { enable_enhancement: true, citation: true, search_info: true } + } + + if (model.provider === 'dashscope') { + return { + enable_search: true, + search_options: { + forced_search: true + } + } + } + + if (isOpenAIWebSearchChatCompletionOnlyModel(model)) { + return { + web_search_options: {} + } + } + + if (model.provider === 'openrouter') { + return { + plugins: [{ id: 'web', search_prompts: WEB_SEARCH_PROMPT_FOR_OPENROUTER }] + } + } + + return { + tools: webSearchTools + } +} + +export function isGemmaModel(model?: Model): boolean { + if (!model) { + return false + } + + const modelId = getLowerBaseModelName(model.id) + return modelId.includes('gemma-') || model.group === 'Gemma' +} + +export function isZhipuModel(model?: Model): boolean { + if (!model) { + return false + } + + return model.provider === 'zhipu' +} + +/** + * 按 Qwen 系列模型分组 + * @param models 模型列表 + * @returns 分组后的模型 + */ +export function groupQwenModels(models: Model[]): Record { + return models.reduce( + (groups, model) => { + const modelId = getLowerBaseModelName(model.id) + // 匹配 Qwen 系列模型的前缀 + const prefixMatch = modelId.match(/^(qwen(?:\d+\.\d+|2(?:\.\d+)?|-\d+b|-(?:max|coder|vl)))/i) + // 匹配 qwen2.5、qwen2、qwen-7b、qwen-max、qwen-coder 等 + const groupKey = prefixMatch ? prefixMatch[1] : model.group || '其他' + + if (!groups[groupKey]) { + groups[groupKey] = [] + } + groups[groupKey].push(model) + + return groups + }, + {} as Record + ) +} + +// 模型集合功能测试 +export const isVisionModels = (models: Model[]) => { + return models.every((model) => isVisionModel(model)) +} + +export const isGenerateImageModels = (models: Model[]) => { + return models.every((model) => isGenerateImageModel(model)) +} + +export const isAnthropicModel = (model?: Model): boolean => { + if (!model) { + return false + } + + const modelId = getLowerBaseModelName(model.id) + return modelId.startsWith('claude') +} + +export const isQwenMTModel = (model: Model): boolean => { + const modelId = getLowerBaseModelName(model.id) + return modelId.includes('qwen-mt') +} + +export const isNotSupportedTextDelta = (model: Model): boolean => { + return isQwenMTModel(model) +} + +export const isNotSupportSystemMessageModel = (model: Model): boolean => { + return isQwenMTModel(model) || isGemmaModel(model) +} + +export const isGPT5SeriesModel = (model: Model) => { + const modelId = getLowerBaseModelName(model.id) + return modelId.includes('gpt-5') +} + +export const isGeminiModel = (model: Model) => { + const modelId = getLowerBaseModelName(model.id) + return modelId.includes('gemini') +} + +export const isOpenAIOpenWeightModel = (model: Model) => { + const modelId = getLowerBaseModelName(model.id) + return modelId.includes('gpt-oss') +} + +// zhipu 视觉推理模型用这组 special token 标记推理结果 +export const ZHIPU_RESULT_TOKENS = ['<|begin_of_box|>', '<|end_of_box|>'] as const diff --git a/src/renderer/src/config/models/vision.ts b/src/renderer/src/config/models/vision.ts new file mode 100644 index 0000000000..02ed3cd6fc --- /dev/null +++ b/src/renderer/src/config/models/vision.ts @@ -0,0 +1,212 @@ +import { getProviderByModel } from '@renderer/services/AssistantService' +import { Model } from '@renderer/types' +import { getLowerBaseModelName, isUserSelectedModelType } from '@renderer/utils' + +import { isEmbeddingModel, isRerankModel } from './embedding' + +// Vision models +const visionAllowedModels = [ + 'llava', + 'moondream', + 'minicpm', + 'gemini-1\\.5', + 'gemini-2\\.0', + 'gemini-2\\.5', + 'gemini-exp', + 'claude-3', + 'claude-sonnet-4', + 'claude-opus-4', + 'vision', + 'glm-4(?:\\.\\d+)?v(?:-[\\w-]+)?', + 'qwen-vl', + 'qwen2-vl', + 'qwen2.5-vl', + 'qwen2.5-omni', + 'qvq', + 'internvl2', + 'grok-vision-beta', + 'grok-4(?:-[\\w-]+)?', + 'pixtral', + 'gpt-4(?:-[\\w-]+)', + 'gpt-4.1(?:-[\\w-]+)?', + 'gpt-4o(?:-[\\w-]+)?', + 'gpt-4.5(?:-[\\w-]+)', + 'gpt-5(?:-[\\w-]+)?', + 'chatgpt-4o(?:-[\\w-]+)?', + 'o1(?:-[\\w-]+)?', + 'o3(?:-[\\w-]+)?', + 'o4(?:-[\\w-]+)?', + 'deepseek-vl(?:[\\w-]+)?', + 'kimi-latest', + 'gemma-3(?:-[\\w-]+)', + 'doubao-seed-1[.-]6(?:-[\\w-]+)?', + 'kimi-thinking-preview', + `gemma3(?:[-:\\w]+)?`, + 'kimi-vl-a3b-thinking(?:-[\\w-]+)?', + 'llama-guard-4(?:-[\\w-]+)?', + 'llama-4(?:-[\\w-]+)?', + 'step-1o(?:.*vision)?', + 'step-1v(?:-[\\w-]+)?', + 'qwen-omni(?:-[\\w-]+)?' +] + +const visionExcludedModels = [ + 'gpt-4-\\d+-preview', + 'gpt-4-turbo-preview', + 'gpt-4-32k', + 'gpt-4-\\d+', + 'o1-mini', + 'o3-mini', + 'o1-preview', + 'AIDC-AI/Marco-o1' +] +export const VISION_REGEX = new RegExp( + `\\b(?!(?:${visionExcludedModels.join('|')})\\b)(${visionAllowedModels.join('|')})\\b`, + 'i' +) + +// For middleware to identify models that must use the dedicated Image API +export const DEDICATED_IMAGE_MODELS = [ + 'grok-2-image', + 'grok-2-image-1212', + 'grok-2-image-latest', + 'dall-e-3', + 'dall-e-2', + 'gpt-image-1' +] + +export const IMAGE_ENHANCEMENT_MODELS = [ + 'grok-2-image(?:-[\\w-]+)?', + 'qwen-image-edit', + 'gpt-image-1', + 'gemini-2.5-flash-image-preview', + 'gemini-2.0-flash-preview-image-generation' +] + +const IMAGE_ENHANCEMENT_MODELS_REGEX = new RegExp(IMAGE_ENHANCEMENT_MODELS.join('|'), 'i') + +// Models that should auto-enable image generation button when selected +export const AUTO_ENABLE_IMAGE_MODELS = ['gemini-2.5-flash-image-preview', ...DEDICATED_IMAGE_MODELS] + +export const OPENAI_TOOL_USE_IMAGE_GENERATION_MODELS = [ + 'o3', + 'gpt-4o', + 'gpt-4o-mini', + 'gpt-4.1', + 'gpt-4.1-mini', + 'gpt-4.1-nano', + 'gpt-5' +] + +export const OPENAI_IMAGE_GENERATION_MODELS = [...OPENAI_TOOL_USE_IMAGE_GENERATION_MODELS, 'gpt-image-1'] + +export const GENERATE_IMAGE_MODELS = [ + 'gemini-2.0-flash-exp', + 'gemini-2.0-flash-exp-image-generation', + 'gemini-2.0-flash-preview-image-generation', + 'gemini-2.5-flash-image-preview', + ...DEDICATED_IMAGE_MODELS +] + +export const isDedicatedImageGenerationModel = (model: Model): boolean => { + if (!model) return false + + const modelId = getLowerBaseModelName(model.id) + return DEDICATED_IMAGE_MODELS.some((m) => modelId.includes(m)) +} + +export const isAutoEnableImageGenerationModel = (model: Model): boolean => { + if (!model) return false + + const modelId = getLowerBaseModelName(model.id) + return AUTO_ENABLE_IMAGE_MODELS.some((m) => modelId.includes(m)) +} + +/** + * 判断模型是否支持对话式的图片生成 + * @param model + * @returns + */ +export function isGenerateImageModel(model: Model): boolean { + if (!model || isEmbeddingModel(model) || isRerankModel(model)) { + return false + } + + const provider = getProviderByModel(model) + + if (!provider) { + return false + } + + const modelId = getLowerBaseModelName(model.id, '/') + + if (provider.type === 'openai-response') { + return ( + OPENAI_IMAGE_GENERATION_MODELS.some((imageModel) => modelId.includes(imageModel)) || + GENERATE_IMAGE_MODELS.some((imageModel) => modelId.includes(imageModel)) + ) + } + + return GENERATE_IMAGE_MODELS.some((imageModel) => modelId.includes(imageModel)) +} + +/** + * 判断模型是否支持纯图片生成(不支持通过工具调用) + * @param model + * @returns + */ +export function isPureGenerateImageModel(model: Model): boolean { + if (!isGenerateImageModel(model) || !isTextToImageModel(model)) { + return false + } + + const modelId = getLowerBaseModelName(model.id) + return !OPENAI_TOOL_USE_IMAGE_GENERATION_MODELS.some((imageModel) => modelId.includes(imageModel)) +} + +// Text to image models +export const TEXT_TO_IMAGE_REGEX = /flux|diffusion|stabilityai|sd-|dall|cogview|janus|midjourney|mj-|image|gpt-image/i + +export function isTextToImageModel(model: Model): boolean { + const modelId = getLowerBaseModelName(model.id) + return TEXT_TO_IMAGE_REGEX.test(modelId) +} + +export function isNotSupportedImageSizeModel(model?: Model): boolean { + if (!model) { + return false + } + + const baseName = getLowerBaseModelName(model.id, '/') + + return baseName.includes('grok-2-image') +} + +/** + * 判断模型是否支持图片增强(包括编辑、增强、修复等) + * @param model + */ +export function isImageEnhancementModel(model: Model): boolean { + const modelId = getLowerBaseModelName(model.id) + return IMAGE_ENHANCEMENT_MODELS_REGEX.test(modelId) +} + +export function isVisionModel(model: Model): boolean { + if (!model || isEmbeddingModel(model) || isRerankModel(model)) { + return false + } + // 新添字段 copilot-vision-request 后可使用 vision + // if (model.provider === 'copilot') { + // return false + // } + if (isUserSelectedModelType(model, 'vision') !== undefined) { + return isUserSelectedModelType(model, 'vision')! + } + + const modelId = getLowerBaseModelName(model.id) + if (model.provider === 'doubao' || modelId.includes('doubao')) { + return VISION_REGEX.test(model.name) || VISION_REGEX.test(modelId) || false + } + + return VISION_REGEX.test(modelId) || IMAGE_ENHANCEMENT_MODELS_REGEX.test(modelId) || false +} diff --git a/src/renderer/src/config/models/websearch.ts b/src/renderer/src/config/models/websearch.ts new file mode 100644 index 0000000000..aae6e83820 --- /dev/null +++ b/src/renderer/src/config/models/websearch.ts @@ -0,0 +1,181 @@ +import { getProviderByModel } from '@renderer/services/AssistantService' +import { Model } from '@renderer/types' +import { getLowerBaseModelName, isUserSelectedModelType } from '@renderer/utils' + +import { isEmbeddingModel, isRerankModel } from './embedding' +import { isAnthropicModel } from './utils' +import { isPureGenerateImageModel, isTextToImageModel } from './vision' + +export const CLAUDE_SUPPORTED_WEBSEARCH_REGEX = new RegExp( + `\\b(?:claude-3(-|\\.)(7|5)-sonnet(?:-[\\w-]+)|claude-3(-|\\.)5-haiku(?:-[\\w-]+)|claude-sonnet-4(?:-[\\w-]+)?|claude-opus-4(?:-[\\w-]+)?)\\b`, + 'i' +) + +export const GEMINI_FLASH_MODEL_REGEX = new RegExp('gemini-.*-flash.*$') + +export const GEMINI_SEARCH_REGEX = new RegExp('gemini-2\\..*', 'i') + +export const PERPLEXITY_SEARCH_MODELS = [ + 'sonar-pro', + 'sonar', + 'sonar-reasoning', + 'sonar-reasoning-pro', + 'sonar-deep-research' +] + +export function isWebSearchModel(model: Model): boolean { + if ( + !model || + isEmbeddingModel(model) || + isRerankModel(model) || + isTextToImageModel(model) || + isPureGenerateImageModel(model) + ) { + return false + } + + if (isUserSelectedModelType(model, 'web_search') !== undefined) { + return isUserSelectedModelType(model, 'web_search')! + } + + const provider = getProviderByModel(model) + + if (!provider) { + return false + } + + const modelId = getLowerBaseModelName(model.id, '/') + + // 不管哪个供应商都判断了 + if (isAnthropicModel(model)) { + return CLAUDE_SUPPORTED_WEBSEARCH_REGEX.test(modelId) + } + + if (provider.type === 'openai-response') { + if (isOpenAIWebSearchModel(model)) { + return true + } + + return false + } + + if (provider.id === 'perplexity') { + return PERPLEXITY_SEARCH_MODELS.includes(modelId) + } + + if (provider.id === 'aihubmix') { + // modelId 不以-search结尾 + if (!modelId.endsWith('-search') && GEMINI_SEARCH_REGEX.test(modelId)) { + return true + } + + if (isOpenAIWebSearchModel(model)) { + return true + } + + return false + } + + if (provider?.type === 'openai') { + if (GEMINI_SEARCH_REGEX.test(modelId) || isOpenAIWebSearchModel(model)) { + return true + } + } + + if (provider.id === 'gemini' || provider?.type === 'gemini' || provider.type === 'vertexai') { + return GEMINI_SEARCH_REGEX.test(modelId) + } + + if (provider.id === 'hunyuan') { + return modelId !== 'hunyuan-lite' + } + + if (provider.id === 'zhipu') { + return modelId?.startsWith('glm-4-') + } + + if (provider.id === 'dashscope') { + const models = ['qwen-turbo', 'qwen-max', 'qwen-plus', 'qwq', 'qwen-flash'] + // matches id like qwen-max-0919, qwen-max-latest + return models.some((i) => modelId.startsWith(i)) + } + + if (provider.id === 'openrouter') { + return true + } + + if (provider.id === 'grok') { + return true + } + + return false +} + +export function isMandatoryWebSearchModel(model: Model): boolean { + if (!model) { + return false + } + + const provider = getProviderByModel(model) + + if (!provider) { + return false + } + + const modelId = getLowerBaseModelName(model.id) + + if (provider.id === 'perplexity' || provider.id === 'openrouter') { + return PERPLEXITY_SEARCH_MODELS.includes(modelId) + } + + return false +} + +export function isOpenRouterBuiltInWebSearchModel(model: Model): boolean { + if (!model) { + return false + } + + const provider = getProviderByModel(model) + + if (provider.id !== 'openrouter') { + return false + } + + const modelId = getLowerBaseModelName(model.id) + + return isOpenAIWebSearchChatCompletionOnlyModel(model) || modelId.includes('sonar') +} + +export function isOpenAIWebSearchChatCompletionOnlyModel(model: Model): boolean { + const modelId = getLowerBaseModelName(model.id) + return modelId.includes('gpt-4o-search-preview') || modelId.includes('gpt-4o-mini-search-preview') +} + +export function isOpenAIWebSearchModel(model: Model): boolean { + const modelId = getLowerBaseModelName(model.id) + + return ( + modelId.includes('gpt-4o-search-preview') || + modelId.includes('gpt-4o-mini-search-preview') || + (modelId.includes('gpt-4.1') && !modelId.includes('gpt-4.1-nano')) || + (modelId.includes('gpt-4o') && !modelId.includes('gpt-4o-image')) || + modelId.includes('o3') || + modelId.includes('o4') || + (modelId.includes('gpt-5') && !modelId.includes('chat')) + ) +} + +export function isHunyuanSearchModel(model?: Model): boolean { + if (!model) { + return false + } + + const modelId = getLowerBaseModelName(model.id) + + if (model.provider === 'hunyuan') { + return modelId !== 'hunyuan-lite' + } + + return false +} diff --git a/src/renderer/src/store/migrate.ts b/src/renderer/src/store/migrate.ts index e7da5ba4e1..7111243cce 100644 --- a/src/renderer/src/store/migrate.ts +++ b/src/renderer/src/store/migrate.ts @@ -2356,11 +2356,14 @@ const migrateConfig = { if (state.settings && state.note) { const showWorkspaceValue = (state.settings as any)?.showWorkspace if (showWorkspaceValue !== undefined) { + // @ts-ignore eslint-disable-next-line state.note.settings.showWorkspace = showWorkspaceValue // Remove from settings delete (state.settings as any).showWorkspace + // @ts-ignore eslint-disable-next-line } else if (state.note.settings.showWorkspace === undefined) { // Set default value if not exists + // @ts-ignore eslint-disable-next-line state.note.settings.showWorkspace = true } }