diff --git a/src/config/aiModels/spark.ts b/src/config/aiModels/spark.ts index 2624260f3370b..c31a9b8b583fc 100644 --- a/src/config/aiModels/spark.ts +++ b/src/config/aiModels/spark.ts @@ -32,6 +32,9 @@ const sparkChatModels: AIChatModelCard[] = [ type: 'chat', }, { + abilities: { + functionCall: true, + }, contextWindowTokens: 8192, description: 'Spark Max 为功能最为全面的版本,支持联网搜索及众多内置插件。其全面优化的核心能力以及系统角色设定和函数调用功能,使其在各种复杂应用场景中的表现极为优异和出色。', @@ -42,6 +45,9 @@ const sparkChatModels: AIChatModelCard[] = [ type: 'chat', }, { + abilities: { + functionCall: true, + }, contextWindowTokens: 32_768, description: 'Spark Max 32K 配置了大上下文处理能力,更强的上下文理解和逻辑推理能力,支持32K tokens的文本输入,适用于长文档阅读、私有知识问答等场景', @@ -52,6 +58,9 @@ const sparkChatModels: AIChatModelCard[] = [ type: 'chat', }, { + abilities: { + functionCall: true, + }, contextWindowTokens: 8192, description: 'Spark Ultra 是星火大模型系列中最为强大的版本,在升级联网搜索链路同时,提升对文本内容的理解和总结能力。它是用于提升办公生产力和准确响应需求的全方位解决方案,是引领行业的智能产品。', diff --git a/src/libs/agent-runtime/ai360/index.ts b/src/libs/agent-runtime/ai360/index.ts index 93887d7c7381a..11b345bfbb7e9 100644 --- a/src/libs/agent-runtime/ai360/index.ts +++ b/src/libs/agent-runtime/ai360/index.ts @@ -1,7 +1,7 @@ import { ModelProvider } from '../types'; import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory'; -import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels'; +import type { ChatModelCard } from '@/types/llm'; export interface Ai360ModelCard { id: string; @@ -22,28 +22,44 @@ export const LobeAi360AI = LobeOpenAICompatibleFactory({ debug: { chatCompletion: () => process.env.DEBUG_AI360_CHAT_COMPLETION === '1', }, - models: { - transformModel: (m) => { - const reasoningKeywords = [ - '360gpt2-o1', - '360zhinao2-o1', - ]; + models: async ({ client }) => { + const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels'); - const model = m as unknown as Ai360ModelCard; + const reasoningKeywords = [ + '360gpt2-o1', + '360zhinao2-o1', + ]; - return { - contextWindowTokens: model.total_tokens, - displayName: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.displayName ?? undefined, - enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.enabled || false, - functionCall: model.id === '360gpt-pro', - id: model.id, - maxTokens: - typeof model.max_tokens === 'number' - ? model.max_tokens - : undefined, - reasoning: reasoningKeywords.some(keyword => model.id.toLowerCase().includes(keyword)), - }; - }, + const modelsPage = await client.models.list() as any; + const modelList: Ai360ModelCard[] = modelsPage.data; + + return modelList + .map((model) => { + const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.id.toLowerCase() === m.id.toLowerCase()); + + return { + contextWindowTokens: model.total_tokens, + displayName: knownModel?.displayName ?? undefined, + enabled: knownModel?.enabled || false, + functionCall: + model.id === '360gpt-pro' + || knownModel?.abilities?.functionCall + || false, + id: model.id, + maxTokens: + typeof model.max_tokens === 'number' + ? model.max_tokens + : undefined, + reasoning: + reasoningKeywords.some(keyword => model.id.toLowerCase().includes(keyword)) + || knownModel?.abilities?.reasoning + || false, + vision: + knownModel?.abilities?.vision + || false, + }; + }) + .filter(Boolean) as ChatModelCard[]; }, provider: ModelProvider.Ai360, }); diff --git a/src/libs/agent-runtime/anthropic/index.ts b/src/libs/agent-runtime/anthropic/index.ts index d40c4a4f4bd3f..bdf64de089714 100644 --- a/src/libs/agent-runtime/anthropic/index.ts +++ b/src/libs/agent-runtime/anthropic/index.ts @@ -13,7 +13,6 @@ import { buildAnthropicMessages, buildAnthropicTools } from '../utils/anthropicH import { StreamingResponse } from '../utils/response'; import { AnthropicStream } from '../utils/streams'; -import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels'; import type { ChatModelCard } from '@/types/llm'; export interface AnthropicModelCard { @@ -114,6 +113,8 @@ export class LobeAnthropicAI implements LobeRuntimeAI { } async models() { + const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels'); + const url = `${this.baseURL}/v1/models`; const response = await fetch(url, { headers: { @@ -128,13 +129,24 @@ export class LobeAnthropicAI implements LobeRuntimeAI { return modelList .map((model) => { + const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.id.toLowerCase() === m.id.toLowerCase()); + return { - contextWindowTokens: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.contextWindowTokens ?? undefined, + contextWindowTokens: knownModel?.contextWindowTokens ?? undefined, displayName: model.display_name, - enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.enabled || false, - functionCall: model.id.toLowerCase().includes('claude-3'), + enabled: knownModel?.enabled || false, + functionCall: + model.id.toLowerCase().includes('claude-3') + || knownModel?.abilities?.functionCall + || false, id: model.id, - vision: model.id.toLowerCase().includes('claude-3') && !model.id.toLowerCase().includes('claude-3-5-haiku'), + reasoning: + knownModel?.abilities?.reasoning + || false, + vision: + model.id.toLowerCase().includes('claude-3') && !model.id.toLowerCase().includes('claude-3-5-haiku') + || knownModel?.abilities?.vision + || false, }; }) .filter(Boolean) as ChatModelCard[]; diff --git a/src/libs/agent-runtime/baichuan/index.ts b/src/libs/agent-runtime/baichuan/index.ts index 3a4b25ae467a1..cd36499769f4f 100644 --- a/src/libs/agent-runtime/baichuan/index.ts +++ b/src/libs/agent-runtime/baichuan/index.ts @@ -3,7 +3,6 @@ import OpenAI from 'openai'; import { ChatStreamPayload, ModelProvider } from '../types'; import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory'; -import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels'; import type { ChatModelCard } from '@/types/llm'; export interface BaichuanModelCard { @@ -32,18 +31,28 @@ export const LobeBaichuanAI = LobeOpenAICompatibleFactory({ chatCompletion: () => process.env.DEBUG_BAICHUAN_CHAT_COMPLETION === '1', }, models: async ({ client }) => { + const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels'); + const modelsPage = await client.models.list() as any; const modelList: BaichuanModelCard[] = modelsPage.data; return modelList .map((model) => { + const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.model.toLowerCase() === m.id.toLowerCase()); + return { contextWindowTokens: model.max_input_length, displayName: model.model_show_name, - enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.model === m.id)?.enabled || false, + enabled: knownModel?.enabled || false, functionCall: model.function_call, id: model.model, maxTokens: model.max_tokens, + reasoning: + knownModel?.abilities?.reasoning + || false, + vision: + knownModel?.abilities?.vision + || false, }; }) .filter(Boolean) as ChatModelCard[]; diff --git a/src/libs/agent-runtime/cloudflare/index.ts b/src/libs/agent-runtime/cloudflare/index.ts index ce30df82958e2..8ca3a0ff4c64a 100644 --- a/src/libs/agent-runtime/cloudflare/index.ts +++ b/src/libs/agent-runtime/cloudflare/index.ts @@ -12,7 +12,6 @@ import { debugStream } from '../utils/debugStream'; import { StreamingResponse } from '../utils/response'; import { createCallbacksTransformer } from '../utils/streams'; -import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels'; import { ChatModelCard } from '@/types/llm'; export interface CloudflareModelCard { @@ -113,6 +112,8 @@ export class LobeCloudflareAI implements LobeRuntimeAI { } async models(): Promise { + const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels'); + const url = `${DEFAULT_BASE_URL_PREFIX}/client/v4/accounts/${this.accountID}/ai/models/search`; const response = await fetch(url, { headers: { @@ -127,16 +128,30 @@ export class LobeCloudflareAI implements LobeRuntimeAI { return modelList .map((model) => { + const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.name.toLowerCase() === m.id.toLowerCase()); + return { contextWindowTokens: model.properties?.max_total_tokens ? Number(model.properties.max_total_tokens) - : LOBE_DEFAULT_MODEL_LIST.find((m) => model.name === m.id)?.contextWindowTokens ?? undefined, - displayName: LOBE_DEFAULT_MODEL_LIST.find((m) => model.name === m.id)?.displayName ?? (model.properties?.["beta"] === "true" ? `${model.name} (Beta)` : undefined), - enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.name === m.id)?.enabled || false, - functionCall: model.description.toLowerCase().includes('function call') || model.properties?.["function_calling"] === "true", + : knownModel?.contextWindowTokens ?? undefined, + displayName: knownModel?.displayName ?? (model.properties?.["beta"] === "true" ? `${model.name} (Beta)` : undefined), + enabled: knownModel?.enabled || false, + functionCall: + model.description.toLowerCase().includes('function call') + || model.properties?.["function_calling"] === "true" + || knownModel?.abilities?.functionCall + || false, id: model.name, - reasoning: model.name.toLowerCase().includes('deepseek-r1'), - vision: model.name.toLowerCase().includes('vision') || model.task?.name.toLowerCase().includes('image-to-text') || model.description.toLowerCase().includes('vision'), + reasoning: + model.name.toLowerCase().includes('deepseek-r1') + || knownModel?.abilities?.reasoning + || false, + vision: + model.name.toLowerCase().includes('vision') + || model.task?.name.toLowerCase().includes('image-to-text') + || model.description.toLowerCase().includes('vision') + || knownModel?.abilities?.vision + || false, }; }) .filter(Boolean) as ChatModelCard[]; diff --git a/src/libs/agent-runtime/deepseek/index.ts b/src/libs/agent-runtime/deepseek/index.ts index aa873d605b984..a96c35246b2e3 100644 --- a/src/libs/agent-runtime/deepseek/index.ts +++ b/src/libs/agent-runtime/deepseek/index.ts @@ -3,7 +3,7 @@ import OpenAI from 'openai'; import { ChatStreamPayload, ModelProvider } from '../types'; import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory'; -import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels'; +import type { ChatModelCard } from '@/types/llm'; export interface DeepSeekModelCard { id: string; @@ -59,19 +59,35 @@ export const LobeDeepSeekAI = LobeOpenAICompatibleFactory({ debug: { chatCompletion: () => process.env.DEBUG_DEEPSEEK_CHAT_COMPLETION === '1', }, - models: { - transformModel: (m) => { - const model = m as unknown as DeepSeekModelCard; + models: async ({ client }) => { + const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels'); - return { - contextWindowTokens: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.contextWindowTokens ?? undefined, - displayName: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.displayName ?? undefined, - enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.enabled || false, - functionCall: !model.id.toLowerCase().includes('deepseek-reasoner'), - id: model.id, - reasoning: model.id.toLowerCase().includes('deepseek-reasoner'), - }; - }, + const modelsPage = await client.models.list() as any; + const modelList: DeepSeekModelCard[] = modelsPage.data; + + return modelList + .map((model) => { + const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.id.toLowerCase() === m.id.toLowerCase()); + + return { + contextWindowTokens: knownModel?.contextWindowTokens ?? undefined, + displayName: knownModel?.displayName ?? undefined, + enabled: knownModel?.enabled || false, + functionCall: + !model.id.toLowerCase().includes('reasoner') + || knownModel?.abilities?.functionCall + || false, + id: model.id, + reasoning: + model.id.toLowerCase().includes('reasoner') + || knownModel?.abilities?.reasoning + || false, + vision: + knownModel?.abilities?.vision + || false, + }; + }) + .filter(Boolean) as ChatModelCard[]; }, provider: ModelProvider.DeepSeek, }); diff --git a/src/libs/agent-runtime/fireworksai/index.ts b/src/libs/agent-runtime/fireworksai/index.ts index dc52d8dfdf20a..5aa8c5e7f78cd 100644 --- a/src/libs/agent-runtime/fireworksai/index.ts +++ b/src/libs/agent-runtime/fireworksai/index.ts @@ -1,7 +1,7 @@ import { ModelProvider } from '../types'; import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory'; -import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels'; +import type { ChatModelCard } from '@/types/llm'; export interface FireworksAIModelCard { context_length: number; @@ -15,25 +15,37 @@ export const LobeFireworksAI = LobeOpenAICompatibleFactory({ debug: { chatCompletion: () => process.env.DEBUG_FIREWORKSAI_CHAT_COMPLETION === '1', }, - models: { - transformModel: (m) => { - const reasoningKeywords = [ - 'deepseek-r1', - 'qwq', - ]; + models: async ({ client }) => { + const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels'); - const model = m as unknown as FireworksAIModelCard; + const reasoningKeywords = [ + 'deepseek-r1', + 'qwq', + ]; - return { - contextWindowTokens: model.context_length, - displayName: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.displayName ?? undefined, - enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.enabled || false, - functionCall: model.supports_tools || model.id.toLowerCase().includes('function'), - id: model.id, - reasoning: reasoningKeywords.some(keyword => model.id.toLowerCase().includes(keyword)), - vision: model.supports_image_input, - }; - }, + const modelsPage = await client.models.list() as any; + const modelList: FireworksAIModelCard[] = modelsPage.data; + + return modelList + .map((model) => { + const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.id.toLowerCase() === m.id.toLowerCase()); + + return { + contextWindowTokens: model.context_length, + displayName: knownModel?.displayName ?? undefined, + enabled: knownModel?.enabled || false, + functionCall: + model.supports_tools + || model.id.toLowerCase().includes('function'), + id: model.id, + reasoning: + reasoningKeywords.some(keyword => model.id.toLowerCase().includes(keyword)) + || knownModel?.abilities?.reasoning + || false, + vision: model.supports_image_input, + }; + }) + .filter(Boolean) as ChatModelCard[]; }, provider: ModelProvider.FireworksAI, }); diff --git a/src/libs/agent-runtime/giteeai/index.ts b/src/libs/agent-runtime/giteeai/index.ts index 62ceacefdaa3d..7f08574a4cf1d 100644 --- a/src/libs/agent-runtime/giteeai/index.ts +++ b/src/libs/agent-runtime/giteeai/index.ts @@ -1,7 +1,7 @@ import { ModelProvider } from '../types'; import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory'; -import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels'; +import type { ChatModelCard } from '@/types/llm'; export interface GiteeAIModelCard { id: string; @@ -12,35 +12,51 @@ export const LobeGiteeAI = LobeOpenAICompatibleFactory({ debug: { chatCompletion: () => process.env.DEBUG_GITEE_AI_CHAT_COMPLETION === '1', }, - models: { - transformModel: (m) => { - const functionCallKeywords = [ - 'qwen2.5', - 'glm-4', - ]; - - const visionKeywords = [ - 'internvl', - 'qwen2-vl', - ]; - - const reasoningKeywords = [ - 'deepseek-r1', - 'qwq', - ]; - - const model = m as unknown as GiteeAIModelCard; - - return { - contextWindowTokens: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.contextWindowTokens ?? undefined, - displayName: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.displayName ?? undefined, - enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.enabled || false, - functionCall: functionCallKeywords.some(keyword => model.id.toLowerCase().includes(keyword)) && !model.id.toLowerCase().includes('qwen2.5-coder'), - id: model.id, - reasoning: reasoningKeywords.some(keyword => model.id.toLowerCase().includes(keyword)), - vision: visionKeywords.some(keyword => model.id.toLowerCase().includes(keyword)), - }; - }, + models: async ({ client }) => { + const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels'); + + const functionCallKeywords = [ + 'qwen2.5', + 'glm-4', + ]; + + const visionKeywords = [ + 'internvl', + 'qwen2-vl', + ]; + + const reasoningKeywords = [ + 'deepseek-r1', + 'qwq', + ]; + + const modelsPage = await client.models.list() as any; + const modelList: GiteeAIModelCard[] = modelsPage.data; + + return modelList + .map((model) => { + const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.id.toLowerCase() === m.id.toLowerCase()); + + return { + contextWindowTokens: knownModel?.contextWindowTokens ?? undefined, + displayName: knownModel?.displayName ?? undefined, + enabled: knownModel?.enabled || false, + functionCall: + functionCallKeywords.some(keyword => model.id.toLowerCase().includes(keyword)) && !model.id.toLowerCase().includes('qwen2.5-coder') + || knownModel?.abilities?.functionCall + || false, + id: model.id, + reasoning: + reasoningKeywords.some(keyword => model.id.toLowerCase().includes(keyword)) + || knownModel?.abilities?.reasoning + || false, + vision: + visionKeywords.some(keyword => model.id.toLowerCase().includes(keyword)) + || knownModel?.abilities?.vision + || false, + }; + }) + .filter(Boolean) as ChatModelCard[]; }, provider: ModelProvider.GiteeAI, }); diff --git a/src/libs/agent-runtime/github/index.test.ts b/src/libs/agent-runtime/github/index.test.ts index b7be7c5d0f8e3..0e1e501245e88 100644 --- a/src/libs/agent-runtime/github/index.test.ts +++ b/src/libs/agent-runtime/github/index.test.ts @@ -210,53 +210,4 @@ describe('LobeGithubAI', () => { }); }); }); - - describe('models', () => { - beforeEach(() => {}); - - it('should return a list of models', async () => { - // Arrange - const arr = [ - { - id: 'azureml://registries/azureml-cohere/models/Cohere-command-r/versions/3', - name: 'Cohere-command-r', - friendly_name: 'Cohere Command R', - model_version: 3, - publisher: 'cohere', - model_family: 'cohere', - model_registry: 'azureml-cohere', - license: 'custom', - task: 'chat-completion', - description: - "Command R is a highly performant generative large language model, optimized for a variety of use cases including reasoning, summarization, and question answering. \n\nThe model is optimized to perform well in the following languages: English, French, Spanish, Italian, German, Brazilian Portuguese, Japanese, Korean, Simplified Chinese, and Arabic.\n\nPre-training data additionally included the following 13 languages: Russian, Polish, Turkish, Vietnamese, Dutch, Czech, Indonesian, Ukrainian, Romanian, Greek, Hindi, Hebrew, Persian.\n\n## Resources\n\nFor full details of this model, [release blog post](https://aka.ms/cohere-blog).\n\n## Model Architecture\n\nThis is an auto-regressive language model that uses an optimized transformer architecture. After pretraining, this model uses supervised fine-tuning (SFT) and preference training to align model behavior to human preferences for helpfulness and safety.\n\n### Tool use capabilities\n\nCommand R has been specifically trained with conversational tool use capabilities. These have been trained into the model via a mixture of supervised fine-tuning and preference fine-tuning, using a specific prompt template. Deviating from this prompt template will likely reduce performance, but we encourage experimentation.\n\nCommand R's tool use functionality takes a conversation as input (with an optional user-system preamble), along with a list of available tools. The model will then generate a json-formatted list of actions to execute on a subset of those tools. Command R may use one of its supplied tools more than once.\n\nThe model has been trained to recognise a special directly_answer tool, which it uses to indicate that it doesn't want to use any of its other tools. The ability to abstain from calling a specific tool can be useful in a range of situations, such as greeting a user, or asking clarifying questions. We recommend including the directly_answer tool, but it can be removed or renamed if required.\n\n### Grounded Generation and RAG Capabilities\n\nCommand R has been specifically trained with grounded generation capabilities. This means that it can generate responses based on a list of supplied document snippets, and it will include grounding spans (citations) in its response indicating the source of the information. This can be used to enable behaviors such as grounded summarization and the final step of Retrieval Augmented Generation (RAG).This behavior has been trained into the model via a mixture of supervised fine-tuning and preference fine-tuning, using a specific prompt template. Deviating from this prompt template may reduce performance, but we encourage experimentation.\n\nCommand R's grounded generation behavior takes a conversation as input (with an optional user-supplied system preamble, indicating task, context and desired output style), along with a list of retrieved document snippets. The document snippets should be chunks, rather than long documents, typically around 100-400 words per chunk. Document snippets consist of key-value pairs. The keys should be short descriptive strings, the values can be text or semi-structured.\n\nBy default, Command R will generate grounded responses by first predicting which documents are relevant, then predicting which ones it will cite, then generating an answer. Finally, it will then insert grounding spans into the answer. See below for an example. This is referred to as accurate grounded generation.\n\nThe model is trained with a number of other answering modes, which can be selected by prompt changes . A fast citation mode is supported in the tokenizer, which will directly generate an answer with grounding spans in it, without first writing the answer out in full. This sacrifices some grounding accuracy in favor of generating fewer tokens.\n\n### Code Capabilities\n\nCommand R has been optimized to interact with your code, by requesting code snippets, code explanations, or code rewrites. It might not perform well out-of-the-box for pure code completion. For better performance, we also recommend using a low temperature (and even greedy decoding) for code-generation related instructions.\n", - summary: - 'Command R is a scalable generative model targeting RAG and Tool Use to enable production-scale AI for enterprise.', - tags: ['rag', 'multilingual'], - }, - ]; - vi.spyOn(instance['client'].models, 'list').mockResolvedValue({ - body: arr, - } as any); - - // Act & Assert - const models = await instance.models(); - - const modelsCount = models.length; - expect(modelsCount).toBe(arr.length); - - for (let i = 0; i < arr.length; i++) { - const model = models[i]; - expect(model).toEqual({ - contextWindowTokens: undefined, - description: arr[i].description, - displayName: arr[i].friendly_name, - enabled: false, - functionCall: true, - id: arr[i].name, - reasoning: false, - vision: false, - }); - } - }); - }); }); diff --git a/src/libs/agent-runtime/github/index.ts b/src/libs/agent-runtime/github/index.ts index 7552b98b5aa0b..ffe147256b35d 100644 --- a/src/libs/agent-runtime/github/index.ts +++ b/src/libs/agent-runtime/github/index.ts @@ -3,7 +3,6 @@ import { pruneReasoningPayload } from '../openai'; import { ModelProvider } from '../types'; import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory'; -import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels'; import type { ChatModelCard } from '@/types/llm'; export interface GithubModelCard { @@ -38,6 +37,8 @@ export const LobeGithubAI = LobeOpenAICompatibleFactory({ invalidAPIKey: AgentRuntimeErrorType.InvalidGithubToken, }, models: async ({ client }) => { + const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels'); + const functionCallKeywords = [ 'function', 'tool', @@ -58,15 +59,26 @@ export const LobeGithubAI = LobeOpenAICompatibleFactory({ return modelList .map((model) => { + const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.name.toLowerCase() === m.id.toLowerCase()); + return { - contextWindowTokens: LOBE_DEFAULT_MODEL_LIST.find((m) => model.name === m.id)?.contextWindowTokens ?? undefined, + contextWindowTokens: knownModel?.contextWindowTokens ?? undefined, description: model.description, displayName: model.friendly_name, - enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.name === m.id)?.enabled || false, - functionCall: functionCallKeywords.some(keyword => model.description.toLowerCase().includes(keyword)), + enabled: knownModel?.enabled || false, + functionCall: + functionCallKeywords.some(keyword => model.description.toLowerCase().includes(keyword)) + || knownModel?.abilities?.functionCall + || false, id: model.name, - reasoning: reasoningKeywords.some(keyword => model.name.toLowerCase().includes(keyword)), - vision: visionKeywords.some(keyword => model.description.toLowerCase().includes(keyword)), + reasoning: + reasoningKeywords.some(keyword => model.name.toLowerCase().includes(keyword)) + || knownModel?.abilities?.reasoning + || false, + vision: + visionKeywords.some(keyword => model.description.toLowerCase().includes(keyword)) + || knownModel?.abilities?.vision + || false, }; }) .filter(Boolean) as ChatModelCard[]; diff --git a/src/libs/agent-runtime/google/index.ts b/src/libs/agent-runtime/google/index.ts index 8f7de84ecb401..7dd9d77e898b5 100644 --- a/src/libs/agent-runtime/google/index.ts +++ b/src/libs/agent-runtime/google/index.ts @@ -8,7 +8,6 @@ import { SchemaType, } from '@google/generative-ai'; -import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels'; import type { ChatModelCard } from '@/types/llm'; import { imageUrlToBase64 } from '@/utils/imageToBase64'; import { safeParseJSON } from '@/utils/safeParseJSON'; @@ -137,6 +136,8 @@ export class LobeGoogleAI implements LobeRuntimeAI { } async models() { + const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels'); + const url = `${this.baseURL}/v1beta/models?key=${this.apiKey}`; const response = await fetch(url, { method: 'GET', @@ -149,17 +150,26 @@ export class LobeGoogleAI implements LobeRuntimeAI { .map((model) => { const modelName = model.name.replace(/^models\//, ''); + const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => modelName.toLowerCase() === m.id.toLowerCase()); + return { contextWindowTokens: model.inputTokenLimit + model.outputTokenLimit, displayName: model.displayName, - enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => modelName === m.id)?.enabled || false, - functionCall: modelName.toLowerCase().includes('gemini'), + enabled: knownModel?.enabled || false, + functionCall: + modelName.toLowerCase().includes('gemini') && !modelName.toLowerCase().includes('thinking') + || knownModel?.abilities?.functionCall + || false, id: modelName, - reasoning: modelName.toLowerCase().includes('thinking'), + reasoning: + modelName.toLowerCase().includes('thinking') + || knownModel?.abilities?.reasoning + || false, vision: - modelName.toLowerCase().includes('vision') || - (modelName.toLowerCase().includes('gemini') && - !modelName.toLowerCase().includes('gemini-1.0')), + modelName.toLowerCase().includes('vision') + || (modelName.toLowerCase().includes('gemini') && !modelName.toLowerCase().includes('gemini-1.0')) + || knownModel?.abilities?.vision + || false, }; }) .filter(Boolean) as ChatModelCard[]; diff --git a/src/libs/agent-runtime/groq/index.ts b/src/libs/agent-runtime/groq/index.ts index c8dd645b41999..9e5c918ad0eec 100644 --- a/src/libs/agent-runtime/groq/index.ts +++ b/src/libs/agent-runtime/groq/index.ts @@ -2,7 +2,7 @@ import { AgentRuntimeErrorType } from '../error'; import { ModelProvider } from '../types'; import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory'; -import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels'; +import type { ChatModelCard } from '@/types/llm'; export interface GroqModelCard { context_window: number; @@ -31,33 +31,49 @@ export const LobeGroq = LobeOpenAICompatibleFactory({ debug: { chatCompletion: () => process.env.DEBUG_GROQ_CHAT_COMPLETION === '1', }, - models: { - transformModel: (m) => { - const functionCallKeywords = [ - 'tool', - 'llama-3.3', - 'llama-3.1', - 'llama3-', - 'mixtral-8x7b-32768', - 'gemma2-9b-it', - ]; - - const reasoningKeywords = [ - 'deepseek-r1', - ]; - - const model = m as unknown as GroqModelCard; + models: async ({ client }) => { + const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels'); - return { - contextWindowTokens: model.context_window, - displayName: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.displayName ?? undefined, - enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.enabled || false, - functionCall: functionCallKeywords.some(keyword => model.id.toLowerCase().includes(keyword)), - id: model.id, - reasoning: reasoningKeywords.some(keyword => model.id.toLowerCase().includes(keyword)), - vision: model.id.toLowerCase().includes('vision'), - }; - }, + const functionCallKeywords = [ + 'tool', + 'llama-3.3', + 'llama-3.1', + 'llama3-', + 'mixtral-8x7b-32768', + 'gemma2-9b-it', + ]; + + const reasoningKeywords = [ + 'deepseek-r1', + ]; + + const modelsPage = await client.models.list() as any; + const modelList: GroqModelCard[] = modelsPage.data; + + return modelList + .map((model) => { + const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.id.toLowerCase() === m.id.toLowerCase()); + + return { + contextWindowTokens: model.context_window, + displayName: knownModel?.displayName ?? undefined, + enabled: knownModel?.enabled || false, + functionCall: + functionCallKeywords.some(keyword => model.id.toLowerCase().includes(keyword)) + || knownModel?.abilities?.functionCall + || false, + id: model.id, + reasoning: + reasoningKeywords.some(keyword => model.id.toLowerCase().includes(keyword)) + || knownModel?.abilities?.reasoning + || false, + vision: + model.id.toLowerCase().includes('vision') + || knownModel?.abilities?.vision + || false, + }; + }) + .filter(Boolean) as ChatModelCard[]; }, provider: ModelProvider.Groq, }); diff --git a/src/libs/agent-runtime/higress/index.ts b/src/libs/agent-runtime/higress/index.ts index 04554cd4d785a..afaa424de671f 100644 --- a/src/libs/agent-runtime/higress/index.ts +++ b/src/libs/agent-runtime/higress/index.ts @@ -1,11 +1,19 @@ import { uniqueId } from 'lodash-es'; -import { LOBE_DEFAULT_MODEL_LIST } from '@/config/modelProviders'; - import { ModelProvider } from '../types'; import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory'; -// import { OpenRouterModelCard } from './type'; +import type { ChatModelCard } from '@/types/llm'; + +export interface HigressModelCard { + context_length: number; + description: string; + id: string; + name: string; + top_provider: { + max_completion_tokens: number; + } +} export const LobeHigressAI = LobeOpenAICompatibleFactory({ constructorOptions: { @@ -18,29 +26,41 @@ export const LobeHigressAI = LobeOpenAICompatibleFactory({ debug: { chatCompletion: () => process.env.DEBUG_HIGRESS_CHAT_COMPLETION === '1', }, - models: { - transformModel: (m) => { - const model = m as any; + models: async ({ client }) => { + const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels'); - return { - contextWindowTokens: model.context_length, - description: model.description, - displayName: model.name, - enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.enabled || false, - functionCall: - model.description.includes('function calling') || model.description.includes('tools'), - id: model.id, - maxTokens: - typeof model.top_provider.max_completion_tokens === 'number' - ? model.top_provider.max_completion_tokens - : undefined, - reasoning: model.description.includes('reasoning'), - vision: - model.description.includes('vision') || - model.description.includes('multimodal') || - model.id.includes('vision'), - }; - }, + const modelsPage = await client.models.list() as any; + const modelList: HigressModelCard[] = modelsPage.data; + + return modelList + .map((model) => { + const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.id.toLowerCase() === m.id.toLowerCase()); + + return { + contextWindowTokens: model.context_length, + description: model.description, + displayName: model.name, + enabled: knownModel?.enabled || false, + functionCall: + model.description.includes('function calling') + || model.description.includes('tools') + || knownModel?.abilities?.functionCall + || false, + id: model.id, + maxTokens: model.top_provider.max_completion_tokens, + reasoning: + model.description.includes('reasoning') + || knownModel?.abilities?.reasoning + || false, + vision: + model.description.includes('vision') + || model.description.includes('multimodal') + || model.id.includes('vision') + || knownModel?.abilities?.vision + || false, + }; + }) + .filter(Boolean) as ChatModelCard[]; }, provider: ModelProvider.Higress, }); diff --git a/src/libs/agent-runtime/huggingface/index.ts b/src/libs/agent-runtime/huggingface/index.ts index d1847f41fa696..ffedc314b58ae 100644 --- a/src/libs/agent-runtime/huggingface/index.ts +++ b/src/libs/agent-runtime/huggingface/index.ts @@ -6,7 +6,6 @@ import { ModelProvider } from '../types'; import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory'; import { convertIterableToStream } from '../utils/streams'; -import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels'; import type { ChatModelCard } from '@/types/llm'; export interface HuggingFaceModelCard { @@ -56,6 +55,8 @@ export const LobeHuggingFaceAI = LobeOpenAICompatibleFactory({ chatCompletion: () => process.env.DEBUG_HUGGINGFACE_CHAT_COMPLETION === '1', }, models: async () => { + const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels'); + const visionKeywords = [ 'image-text-to-text', 'multimodal', @@ -79,16 +80,26 @@ export const LobeHuggingFaceAI = LobeOpenAICompatibleFactory({ return modelList .map((model) => { + const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.id.toLowerCase() === m.id.toLowerCase()); + return { - contextWindowTokens: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.contextWindowTokens ?? undefined, - displayName: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.displayName ?? undefined, - enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.enabled || false, - functionCall: model.tags.some(tag => tag.toLowerCase().includes('function-calling')), + contextWindowTokens: knownModel?.contextWindowTokens ?? undefined, + displayName: knownModel?.displayName ?? undefined, + enabled: knownModel?.enabled || false, + functionCall: + model.tags.some(tag => tag.toLowerCase().includes('function-calling')) + || knownModel?.abilities?.functionCall + || false, id: model.id, - reasoning: model.tags.some(tag => tag.toLowerCase().includes('reasoning')) || reasoningKeywords.some(keyword => model.id.toLowerCase().includes(keyword)), - vision: model.tags.some(tag => - visionKeywords.some(keyword => tag.toLowerCase().includes(keyword)) - ), + reasoning: + model.tags.some(tag => tag.toLowerCase().includes('reasoning')) + || reasoningKeywords.some(keyword => model.id.toLowerCase().includes(keyword)) + || knownModel?.abilities?.reasoning + || false, + vision: + model.tags.some(tag => visionKeywords.some(keyword => tag.toLowerCase().includes(keyword))) + || knownModel?.abilities?.vision + || false, }; }) .filter(Boolean) as ChatModelCard[]; diff --git a/src/libs/agent-runtime/hunyuan/index.ts b/src/libs/agent-runtime/hunyuan/index.ts index 5bcf827cc54ec..1abe27d4cc879 100644 --- a/src/libs/agent-runtime/hunyuan/index.ts +++ b/src/libs/agent-runtime/hunyuan/index.ts @@ -1,7 +1,7 @@ import { ModelProvider } from '../types'; import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory'; -import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels'; +import type { ChatModelCard } from '@/types/llm'; export interface HunyuanModelCard { id: string; @@ -12,25 +12,41 @@ export const LobeHunyuanAI = LobeOpenAICompatibleFactory({ debug: { chatCompletion: () => process.env.DEBUG_HUNYUAN_CHAT_COMPLETION === '1', }, - models: { - transformModel: (m) => { - const functionCallKeywords = [ - 'hunyuan-functioncall', - 'hunyuan-turbo', - 'hunyuan-pro', - ]; + models: async ({ client }) => { + const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels'); - const model = m as unknown as HunyuanModelCard; + const functionCallKeywords = [ + 'hunyuan-functioncall', + 'hunyuan-turbo', + 'hunyuan-pro', + ]; - return { - contextWindowTokens: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.contextWindowTokens ?? undefined, - displayName: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.displayName ?? undefined, - enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.enabled || false, - functionCall: functionCallKeywords.some(keyword => model.id.toLowerCase().includes(keyword)) && !model.id.toLowerCase().includes('vision'), - id: model.id, - vision: model.id.toLowerCase().includes('vision'), - }; - }, + const modelsPage = await client.models.list() as any; + const modelList: HunyuanModelCard[] = modelsPage.data; + + return modelList + .map((model) => { + const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.id.toLowerCase() === m.id.toLowerCase()); + + return { + contextWindowTokens: knownModel?.contextWindowTokens ?? undefined, + displayName: knownModel?.displayName ?? undefined, + enabled: knownModel?.enabled || false, + functionCall: + functionCallKeywords.some(keyword => model.id.toLowerCase().includes(keyword)) && !model.id.toLowerCase().includes('vision') + || knownModel?.abilities?.functionCall + || false, + id: model.id, + reasoning: + knownModel?.abilities?.reasoning + || false, + vision: + model.id.toLowerCase().includes('vision') + || knownModel?.abilities?.vision + || false, + }; + }) + .filter(Boolean) as ChatModelCard[]; }, provider: ModelProvider.Hunyuan, }); diff --git a/src/libs/agent-runtime/internlm/index.ts b/src/libs/agent-runtime/internlm/index.ts index 267371360eafa..d3e465364b3e0 100644 --- a/src/libs/agent-runtime/internlm/index.ts +++ b/src/libs/agent-runtime/internlm/index.ts @@ -1,7 +1,7 @@ import { ModelProvider } from '../types'; import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory'; -import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels'; +import type { ChatModelCard } from '@/types/llm'; export interface InternLMModelCard { id: string; @@ -20,18 +20,33 @@ export const LobeInternLMAI = LobeOpenAICompatibleFactory({ debug: { chatCompletion: () => process.env.DEBUG_INTERNLM_CHAT_COMPLETION === '1', }, - models: { - transformModel: (m) => { - const model = m as unknown as InternLMModelCard; + models: async ({ client }) => { + const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels'); - return { - contextWindowTokens: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.contextWindowTokens ?? undefined, - displayName: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.displayName ?? undefined, - enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.enabled || false, - functionCall: true, - id: model.id, - }; - }, + const modelsPage = await client.models.list() as any; + const modelList: InternLMModelCard[] = modelsPage.data; + + return modelList + .map((model) => { + const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.id.toLowerCase() === m.id.toLowerCase()); + + return { + contextWindowTokens: knownModel?.contextWindowTokens ?? undefined, + displayName: knownModel?.displayName ?? undefined, + enabled: knownModel?.enabled || false, + functionCall: + knownModel?.abilities?.functionCall + || false, + id: model.id, + reasoning: + knownModel?.abilities?.reasoning + || false, + vision: + knownModel?.abilities?.vision + || false, + }; + }) + .filter(Boolean) as ChatModelCard[]; }, provider: ModelProvider.InternLM, }); diff --git a/src/libs/agent-runtime/lmstudio/index.ts b/src/libs/agent-runtime/lmstudio/index.ts index 9ef31ab793071..5bfd99e9e61c0 100644 --- a/src/libs/agent-runtime/lmstudio/index.ts +++ b/src/libs/agent-runtime/lmstudio/index.ts @@ -1,11 +1,45 @@ import { ModelProvider } from '../types'; import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory'; +import type { ChatModelCard } from '@/types/llm'; + +export interface LMStudioModelCard { + id: string; +} + export const LobeLMStudioAI = LobeOpenAICompatibleFactory({ apiKey: 'placeholder-to-avoid-error', baseURL: 'http://127.0.0.1:1234/v1', debug: { chatCompletion: () => process.env.DEBUG_LMSTUDIO_CHAT_COMPLETION === '1', }, + models: async ({ client }) => { + const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels'); + + const modelsPage = await client.models.list() as any; + const modelList: LMStudioModelCard[] = modelsPage.data; + + return modelList + .map((model) => { + const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.id.toLowerCase() === m.id.toLowerCase()); + + return { + contextWindowTokens: knownModel?.contextWindowTokens ?? undefined, + displayName: knownModel?.displayName ?? undefined, + enabled: knownModel?.enabled || false, + functionCall: + knownModel?.abilities?.functionCall + || false, + id: model.id, + reasoning: + knownModel?.abilities?.reasoning + || false, + vision: + knownModel?.abilities?.vision + || false, + }; + }) + .filter(Boolean) as ChatModelCard[]; + }, provider: ModelProvider.LMStudio, }); diff --git a/src/libs/agent-runtime/mistral/index.ts b/src/libs/agent-runtime/mistral/index.ts index 27f695e36195a..83b1c9944f4aa 100644 --- a/src/libs/agent-runtime/mistral/index.ts +++ b/src/libs/agent-runtime/mistral/index.ts @@ -1,7 +1,7 @@ import { ModelProvider } from '../types'; import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory'; -import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels'; +import type { ChatModelCard } from '@/types/llm'; export interface MistralModelCard { capabilities: { @@ -30,20 +30,30 @@ export const LobeMistralAI = LobeOpenAICompatibleFactory({ debug: { chatCompletion: () => process.env.DEBUG_MISTRAL_CHAT_COMPLETION === '1', }, - models: { - transformModel: (m) => { - const model = m as unknown as MistralModelCard; + models: async ({ client }) => { + const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels'); - return { - contextWindowTokens: model.max_context_length, - description: model.description, - displayName: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.displayName ?? undefined, - enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.enabled || false, - functionCall: model.capabilities.function_calling, - id: model.id, - vision: model.capabilities.vision, - }; - }, + const modelsPage = await client.models.list() as any; + const modelList: MistralModelCard[] = modelsPage.data; + + return modelList + .map((model) => { + const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.id.toLowerCase() === m.id.toLowerCase()); + + return { + contextWindowTokens: model.max_context_length, + description: model.description, + displayName: knownModel?.displayName ?? undefined, + enabled: knownModel?.enabled || false, + functionCall: model.capabilities.function_calling, + id: model.id, + reasoning: + knownModel?.abilities?.reasoning + || false, + vision: model.capabilities.vision, + }; + }) + .filter(Boolean) as ChatModelCard[]; }, provider: ModelProvider.Mistral, }); diff --git a/src/libs/agent-runtime/moonshot/index.ts b/src/libs/agent-runtime/moonshot/index.ts index 9bb92fa2dc2a3..39541392fc851 100644 --- a/src/libs/agent-runtime/moonshot/index.ts +++ b/src/libs/agent-runtime/moonshot/index.ts @@ -3,7 +3,7 @@ import OpenAI from 'openai'; import { ChatStreamPayload, ModelProvider } from '../types'; import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory'; -import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels'; +import type { ChatModelCard } from '@/types/llm'; export interface MoonshotModelCard { id: string; @@ -24,19 +24,34 @@ export const LobeMoonshotAI = LobeOpenAICompatibleFactory({ debug: { chatCompletion: () => process.env.DEBUG_MOONSHOT_CHAT_COMPLETION === '1', }, - models: { - transformModel: (m) => { - const model = m as unknown as MoonshotModelCard; + models: async ({ client }) => { + const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels'); - return { - contextWindowTokens: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.contextWindowTokens ?? undefined, - displayName: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.displayName ?? undefined, - enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.enabled || false, - functionCall: true, - id: model.id, - vision: model.id.toLowerCase().includes('vision'), - }; - }, + const modelsPage = await client.models.list() as any; + const modelList: MoonshotModelCard[] = modelsPage.data; + + return modelList + .map((model) => { + const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.id.toLowerCase() === m.id.toLowerCase()); + + return { + contextWindowTokens: knownModel?.contextWindowTokens ?? undefined, + displayName: knownModel?.displayName ?? undefined, + enabled: knownModel?.enabled || false, + functionCall: + knownModel?.abilities?.functionCall + || false, + id: model.id, + reasoning: + knownModel?.abilities?.reasoning + || false, + vision: + model.id.toLowerCase().includes('vision') + || knownModel?.abilities?.vision + || false, + }; + }) + .filter(Boolean) as ChatModelCard[]; }, provider: ModelProvider.Moonshot, }); diff --git a/src/libs/agent-runtime/novita/index.ts b/src/libs/agent-runtime/novita/index.ts index a7c7d33f3253d..56dbadacdeaa6 100644 --- a/src/libs/agent-runtime/novita/index.ts +++ b/src/libs/agent-runtime/novita/index.ts @@ -2,7 +2,7 @@ import { ModelProvider } from '../types'; import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory'; import { NovitaModelCard } from './type'; -import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels'; +import type { ChatModelCard } from '@/types/llm'; export const LobeNovitaAI = LobeOpenAICompatibleFactory({ baseURL: 'https://api.novita.ai/v3/openai', @@ -14,25 +14,42 @@ export const LobeNovitaAI = LobeOpenAICompatibleFactory({ debug: { chatCompletion: () => process.env.DEBUG_NOVITA_CHAT_COMPLETION === '1', }, - models: { - transformModel: (m) => { - const reasoningKeywords = [ - 'deepseek-r1', - ]; + models: async ({ client }) => { + const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels'); - const model = m as unknown as NovitaModelCard; + const reasoningKeywords = [ + 'deepseek-r1', + ]; - return { - contextWindowTokens: model.context_size, - description: model.description, - displayName: model.title, - enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.enabled || false, - functionCall: model.description.toLowerCase().includes('function calling'), - id: model.id, - reasoning: model.description.toLowerCase().includes('reasoning task') || reasoningKeywords.some(keyword => model.id.toLowerCase().includes(keyword)), - vision: model.description.toLowerCase().includes('vision'), - }; - }, + const modelsPage = await client.models.list() as any; + const modelList: NovitaModelCard[] = modelsPage.data; + + return modelList + .map((model) => { + const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.id.toLowerCase() === m.id.toLowerCase()); + + return { + contextWindowTokens: model.context_size, + description: model.description, + displayName: model.title, + enabled: knownModel?.enabled || false, + functionCall: + model.description.toLowerCase().includes('function calling') + || knownModel?.abilities?.functionCall + || false, + id: model.id, + reasoning: + model.description.toLowerCase().includes('reasoning task') + || reasoningKeywords.some(keyword => model.id.toLowerCase().includes(keyword)) + || knownModel?.abilities?.reasoning + || false, + vision: + model.description.toLowerCase().includes('vision') + || knownModel?.abilities?.vision + || false, + }; + }) + .filter(Boolean) as ChatModelCard[]; }, provider: ModelProvider.Novita, }); diff --git a/src/libs/agent-runtime/ollama/index.test.ts b/src/libs/agent-runtime/ollama/index.test.ts index 79ab4360249a7..4a1b71639cc7d 100644 --- a/src/libs/agent-runtime/ollama/index.test.ts +++ b/src/libs/agent-runtime/ollama/index.test.ts @@ -145,7 +145,26 @@ describe('LobeOllamaAI', () => { const models = await ollamaAI.models(); expect(listMock).toHaveBeenCalled(); - expect(models).toEqual([{ id: 'model-1' }, { id: 'model-2' }]); + expect(models).toEqual([ + { + contextWindowTokens: undefined, + displayName: undefined, + enabled: false, + functionCall: false, + id: 'model-1', + reasoning: false, + vision: false + }, + { + contextWindowTokens: undefined, + displayName: undefined, + enabled: false, + functionCall: false, + id: 'model-2', + reasoning: false, + vision: false + } + ]); }); }); diff --git a/src/libs/agent-runtime/ollama/index.ts b/src/libs/agent-runtime/ollama/index.ts index 4bb9c4bcefded..7140b99491848 100644 --- a/src/libs/agent-runtime/ollama/index.ts +++ b/src/libs/agent-runtime/ollama/index.ts @@ -2,7 +2,6 @@ import { Ollama, Tool } from 'ollama/browser'; import { ClientOptions } from 'openai'; import { OpenAIChatMessage } from '@/libs/agent-runtime'; -import { ChatModelCard } from '@/types/llm'; import { LobeRuntimeAI } from '../BaseAI'; import { AgentRuntimeErrorType } from '../error'; @@ -20,6 +19,12 @@ import { OllamaStream, convertIterableToStream } from '../utils/streams'; import { parseDataUri } from '../utils/uriParser'; import { OllamaMessage } from './type'; +import { ChatModelCard } from '@/types/llm'; + +export interface OllamaModelCard { + name: string; +} + export class LobeOllamaAI implements LobeRuntimeAI { private client: Ollama; @@ -102,11 +107,34 @@ export class LobeOllamaAI implements LobeRuntimeAI { return await Promise.all(promises); } - async models(): Promise { + async models() { + const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels'); + const list = await this.client.list(); - return list.models.map((model) => ({ - id: model.name, - })); + + const modelList: OllamaModelCard[] = list.models; + + return modelList + .map((model) => { + const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.name.toLowerCase() === m.id.toLowerCase()); + + return { + contextWindowTokens: knownModel?.contextWindowTokens ?? undefined, + displayName: knownModel?.displayName ?? undefined, + enabled: knownModel?.enabled || false, + functionCall: + knownModel?.abilities?.functionCall + || false, + id: model.name, + reasoning: + knownModel?.abilities?.functionCall + || false, + vision: + knownModel?.abilities?.functionCall + || false, + }; + }) + .filter(Boolean) as ChatModelCard[]; } private invokeEmbeddingModel = async (payload: EmbeddingsPayload): Promise => { diff --git a/src/libs/agent-runtime/openai/__snapshots__/index.test.ts.snap b/src/libs/agent-runtime/openai/__snapshots__/index.test.ts.snap index a4541e4af7248..94207a061844e 100644 --- a/src/libs/agent-runtime/openai/__snapshots__/index.test.ts.snap +++ b/src/libs/agent-runtime/openai/__snapshots__/index.test.ts.snap @@ -2,6 +2,24 @@ exports[`LobeOpenAI > models > should get models 1`] = ` [ + { + "contextWindowTokens": undefined, + "displayName": "Whisper", + "enabled": false, + "functionCall": false, + "id": "whisper-1", + "reasoning": false, + "vision": false, + }, + { + "contextWindowTokens": undefined, + "displayName": undefined, + "enabled": false, + "functionCall": false, + "id": "davinci-002", + "reasoning": false, + "vision": false, + }, { "contextWindowTokens": 16385, "displayName": "GPT 3.5 Turbo", @@ -11,6 +29,15 @@ exports[`LobeOpenAI > models > should get models 1`] = ` "reasoning": false, "vision": false, }, + { + "contextWindowTokens": undefined, + "displayName": "DALL·E 2", + "enabled": false, + "functionCall": false, + "id": "dall-e-2", + "reasoning": false, + "vision": false, + }, { "contextWindowTokens": 16384, "displayName": "GPT 3.5 Turbo", @@ -20,6 +47,24 @@ exports[`LobeOpenAI > models > should get models 1`] = ` "reasoning": false, "vision": false, }, + { + "contextWindowTokens": undefined, + "displayName": undefined, + "enabled": false, + "functionCall": false, + "id": "tts-1-hd-1106", + "reasoning": false, + "vision": false, + }, + { + "contextWindowTokens": undefined, + "displayName": "TTS-1 HD", + "enabled": false, + "functionCall": false, + "id": "tts-1-hd", + "reasoning": false, + "vision": false, + }, { "contextWindowTokens": undefined, "displayName": undefined, @@ -29,6 +74,15 @@ exports[`LobeOpenAI > models > should get models 1`] = ` "reasoning": false, "vision": false, }, + { + "contextWindowTokens": 8192, + "displayName": "Text Embedding 3 Large", + "enabled": false, + "functionCall": false, + "id": "text-embedding-3-large", + "reasoning": false, + "vision": false, + }, { "contextWindowTokens": undefined, "displayName": undefined, @@ -92,6 +146,24 @@ exports[`LobeOpenAI > models > should get models 1`] = ` "reasoning": false, "vision": false, }, + { + "contextWindowTokens": undefined, + "displayName": "TTS-1", + "enabled": false, + "functionCall": false, + "id": "tts-1", + "reasoning": false, + "vision": false, + }, + { + "contextWindowTokens": undefined, + "displayName": "DALL·E 3", + "enabled": false, + "functionCall": false, + "id": "dall-e-3", + "reasoning": false, + "vision": false, + }, { "contextWindowTokens": 16385, "displayName": "GPT-3.5 Turbo 1106", @@ -110,6 +182,24 @@ exports[`LobeOpenAI > models > should get models 1`] = ` "reasoning": false, "vision": false, }, + { + "contextWindowTokens": undefined, + "displayName": undefined, + "enabled": false, + "functionCall": false, + "id": "babbage-002", + "reasoning": false, + "vision": false, + }, + { + "contextWindowTokens": undefined, + "displayName": undefined, + "enabled": false, + "functionCall": false, + "id": "tts-1-1106", + "reasoning": false, + "vision": false, + }, { "contextWindowTokens": 128000, "displayName": "GPT 4 Turbo with Vision Preview", @@ -119,6 +209,15 @@ exports[`LobeOpenAI > models > should get models 1`] = ` "reasoning": false, "vision": true, }, + { + "contextWindowTokens": 8192, + "displayName": "Text Embedding 3 Small", + "enabled": false, + "functionCall": false, + "id": "text-embedding-3-small", + "reasoning": false, + "vision": false, + }, { "contextWindowTokens": 128000, "displayName": "GPT 4 Turbo", @@ -126,6 +225,15 @@ exports[`LobeOpenAI > models > should get models 1`] = ` "functionCall": true, "id": "gpt-4", "reasoning": false, + "vision": true, + }, + { + "contextWindowTokens": undefined, + "displayName": undefined, + "enabled": false, + "functionCall": false, + "id": "text-embedding-ada-002", + "reasoning": false, "vision": false, }, { diff --git a/src/libs/agent-runtime/openai/index.ts b/src/libs/agent-runtime/openai/index.ts index e925b7b98dac0..dca49b623e2aa 100644 --- a/src/libs/agent-runtime/openai/index.ts +++ b/src/libs/agent-runtime/openai/index.ts @@ -1,7 +1,7 @@ import { ChatStreamPayload, ModelProvider, OpenAIChatMessage } from '../types'; import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory'; -import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels'; +import type { ChatModelCard } from '@/types/llm'; export interface OpenAIModelCard { id: string; @@ -55,36 +55,52 @@ export const LobeOpenAI = LobeOpenAICompatibleFactory({ debug: { chatCompletion: () => process.env.DEBUG_OPENAI_CHAT_COMPLETION === '1', }, - models: { - transformModel: (m) => { - const functionCallKeywords = [ - 'gpt-4', - 'gpt-3.5', - 'o3-mini', - ]; + models: async ({ client }) => { + const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels'); - const visionKeywords = [ - 'gpt-4o', - 'vision', - ]; + const functionCallKeywords = [ + 'gpt-4', + 'gpt-3.5', + 'o3-mini', + ]; - const reasoningKeywords = [ - 'o1', - 'o3', - ]; + const visionKeywords = [ + 'gpt-4o', + 'vision', + ]; - const model = m as unknown as OpenAIModelCard; + const reasoningKeywords = [ + 'o1', + 'o3', + ]; - return { - contextWindowTokens: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.contextWindowTokens ?? undefined, - displayName: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.displayName ?? undefined, - enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.enabled || false, - functionCall: functionCallKeywords.some(keyword => model.id.toLowerCase().includes(keyword)) && !model.id.toLowerCase().includes('audio'), - id: model.id, - reasoning: reasoningKeywords.some(keyword => model.id.toLowerCase().includes(keyword)), - vision: visionKeywords.some(keyword => model.id.toLowerCase().includes(keyword)) && !model.id.toLowerCase().includes('audio'), - }; - }, + const modelsPage = await client.models.list() as any; + const modelList: OpenAIModelCard[] = modelsPage.data; + + return modelList + .map((model) => { + const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.id.toLowerCase() === m.id.toLowerCase()); + + return { + contextWindowTokens: knownModel?.contextWindowTokens ?? undefined, + displayName: knownModel?.displayName ?? undefined, + enabled: knownModel?.enabled || false, + functionCall: + functionCallKeywords.some(keyword => model.id.toLowerCase().includes(keyword)) && !model.id.toLowerCase().includes('audio') + || knownModel?.abilities?.functionCall + || false, + id: model.id, + reasoning: + reasoningKeywords.some(keyword => model.id.toLowerCase().includes(keyword)) + || knownModel?.abilities?.reasoning + || false, + vision: + visionKeywords.some(keyword => model.id.toLowerCase().includes(keyword)) && !model.id.toLowerCase().includes('audio') + || knownModel?.abilities?.vision + || false, + }; + }) + .filter(Boolean) as ChatModelCard[]; }, provider: ModelProvider.OpenAI, }); diff --git a/src/libs/agent-runtime/openrouter/__snapshots__/index.test.ts.snap b/src/libs/agent-runtime/openrouter/__snapshots__/index.test.ts.snap index b66823705e6c5..d0322ee9356d5 100644 --- a/src/libs/agent-runtime/openrouter/__snapshots__/index.test.ts.snap +++ b/src/libs/agent-runtime/openrouter/__snapshots__/index.test.ts.snap @@ -2,6 +2,34 @@ exports[`LobeOpenRouterAI > models > should get models 1`] = ` [ + { + "contextWindowTokens": 131072, + "description": "Reflection Llama-3.1 70B is trained with a new technique called Reflection-Tuning that teaches a LLM to detect mistakes in its reasoning and correct course. + +The model was trained on synthetic data. + +_These are free, rate-limited endpoints for [Reflection 70B](/models/mattshumer/reflection-70b). Outputs may be cached. Read about rate limits [here](/docs/limits)._", + "displayName": "Reflection 70B (free)", + "enabled": false, + "functionCall": false, + "id": "mattshumer/reflection-70b:free", + "maxTokens": 4096, + "reasoning": false, + "vision": false, + }, + { + "contextWindowTokens": 131072, + "description": "Reflection Llama-3.1 70B is trained with a new technique called Reflection-Tuning that teaches a LLM to detect mistakes in its reasoning and correct course. + +The model was trained on synthetic data.", + "displayName": "Reflection 70B", + "enabled": false, + "functionCall": false, + "id": "mattshumer/reflection-70b", + "maxTokens": undefined, + "reasoning": false, + "vision": false, + }, { "contextWindowTokens": 128000, "description": "Command-R is a 35B parameter model that performs conversational language tasks at a higher quality, more reliably, and with a longer context than previous models. It can be used for complex workflows like code generation, retrieval augmented generation (RAG), tool use, and agents. @@ -510,11 +538,11 @@ GPT-4o mini achieves an 82% score on MMLU and presently ranks higher than GPT-4 Check out the [launch announcement](https://openai.com/index/gpt-4o-mini-advancing-cost-efficient-intelligence/) to learn more.", "displayName": "OpenAI: GPT-4o-mini", "enabled": true, - "functionCall": false, + "functionCall": true, "id": "openai/gpt-4o-mini", "maxTokens": 16384, "reasoning": false, - "vision": false, + "vision": true, }, { "contextWindowTokens": 32768, @@ -560,7 +588,7 @@ Gemma models are well-suited for a variety of text generation tasks, including q See the [launch announcement](https://blog.google/technology/developers/google-gemma-2/) for more details. Usage of Gemma is subject to Google's [Gemma Terms of Use](https://ai.google.dev/gemma/terms).", "displayName": "Google: Gemma 2 27B", - "enabled": true, + "enabled": false, "functionCall": false, "id": "google/gemma-2-27b-it", "maxTokens": undefined, @@ -940,7 +968,7 @@ Usage of Gemini is subject to Google's [Gemini Terms of Use](https://ai.google.d #multimodal", "displayName": "Google: Gemini Flash 1.5", "enabled": true, - "functionCall": false, + "functionCall": true, "id": "google/gemini-flash-1.5", "maxTokens": 32768, "reasoning": false, @@ -968,7 +996,7 @@ Compared with DeepSeek 67B, DeepSeek-V2 achieves stronger performance, and meanw DeepSeek-V2 achieves remarkable performance on both standard benchmarks and open-ended generation evaluations.", "displayName": "DeepSeek-V2 Chat", "enabled": true, - "functionCall": false, + "functionCall": true, "id": "deepseek/deepseek-chat", "maxTokens": 4096, "reasoning": false, @@ -1065,11 +1093,11 @@ For benchmarking against other models, it was briefly called ["im-also-a-good-gp For benchmarking against other models, it was briefly called ["im-also-a-good-gpt2-chatbot"](https://twitter.com/LiamFedus/status/1790064963966370209)", "displayName": "OpenAI: GPT-4o", "enabled": true, - "functionCall": false, + "functionCall": true, "id": "openai/gpt-4o", "maxTokens": 4096, "reasoning": false, - "vision": false, + "vision": true, }, { "contextWindowTokens": 128000, @@ -1336,7 +1364,7 @@ Usage of Gemini is subject to Google's [Gemini Terms of Use](https://ai.google.d #multimodal", "displayName": "Google: Gemini Pro 1.5", "enabled": true, - "functionCall": false, + "functionCall": true, "id": "google/gemini-pro-1.5", "maxTokens": 32768, "reasoning": false, @@ -1438,7 +1466,7 @@ See the launch announcement and benchmark results [here](https://www.anthropic.c #multimodal", "displayName": "Anthropic: Claude 3 Haiku", "enabled": true, - "functionCall": false, + "functionCall": true, "id": "anthropic/claude-3-haiku", "maxTokens": 4096, "reasoning": false, @@ -1503,7 +1531,7 @@ See the launch announcement and benchmark results [here](https://www.anthropic.c #multimodal", "displayName": "Anthropic: Claude 3 Opus", "enabled": true, - "functionCall": false, + "functionCall": true, "id": "anthropic/claude-3-opus", "maxTokens": 4096, "reasoning": false, @@ -2101,7 +2129,7 @@ Currently based on [jondurbin/airoboros-l2-70b](https://huggingface.co/jondurbin "description": "A 7.3B parameter model that outperforms Llama 2 13B on all benchmarks, with optimizations for speed and context length.", "displayName": "Mistral: Mistral 7B Instruct v0.1", "enabled": false, - "functionCall": false, + "functionCall": true, "id": "mistralai/mistral-7b-instruct-v0.1", "maxTokens": undefined, "reasoning": false, diff --git a/src/libs/agent-runtime/openrouter/index.ts b/src/libs/agent-runtime/openrouter/index.ts index 2767ca16c0306..eceed1744de00 100644 --- a/src/libs/agent-runtime/openrouter/index.ts +++ b/src/libs/agent-runtime/openrouter/index.ts @@ -1,9 +1,9 @@ -import { LOBE_DEFAULT_MODEL_LIST } from '@/config/modelProviders'; - import { ModelProvider } from '../types'; import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory'; import { OpenRouterModelCard } from './type'; +import type { ChatModelCard } from '@/types/llm'; + export const LobeOpenRouterAI = LobeOpenAICompatibleFactory({ baseURL: 'https://openrouter.ai/api/v1', chatCompletion: { @@ -24,40 +24,58 @@ export const LobeOpenRouterAI = LobeOpenAICompatibleFactory({ debug: { chatCompletion: () => process.env.DEBUG_OPENROUTER_CHAT_COMPLETION === '1', }, - models: { - transformModel: (m) => { - const visionKeywords = ['qwen/qvq', 'vision']; + models: async ({ client }) => { + const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels'); - const reasoningKeywords = [ - 'deepseek/deepseek-r1', - 'openai/o1', - 'openai/o3', - 'qwen/qvq', - 'qwen/qwq', - 'thinking', - ]; + const visionKeywords = [ + 'qwen/qvq', + 'vision' + ]; - const model = m as unknown as OpenRouterModelCard; + const reasoningKeywords = [ + 'deepseek/deepseek-r1', + 'openai/o1', + 'openai/o3', + 'qwen/qvq', + 'qwen/qwq', + 'thinking', + ]; - return { - contextWindowTokens: model.context_length, - description: model.description, - displayName: model.name, - enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.enabled || false, - functionCall: - model.description.includes('function calling') || model.description.includes('tools'), - id: model.id, - maxTokens: - typeof model.top_provider.max_completion_tokens === 'number' - ? model.top_provider.max_completion_tokens - : undefined, - reasoning: reasoningKeywords.some((keyword) => model.id.toLowerCase().includes(keyword)), - vision: - model.description.includes('vision') || - model.description.includes('multimodal') || - visionKeywords.some((keyword) => model.id.toLowerCase().includes(keyword)), - }; - }, + const modelsPage = await client.models.list() as any; + const modelList: OpenRouterModelCard[] = modelsPage.data; + + return modelList + .map((model) => { + const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.id.toLowerCase() === m.id.toLowerCase()); + + return { + contextWindowTokens: model.context_length, + description: model.description, + displayName: model.name, + enabled: knownModel?.enabled || false, + functionCall: + model.description.includes('function calling') + || model.description.includes('tools') + || knownModel?.abilities?.functionCall + || false, + id: model.id, + maxTokens: + typeof model.top_provider.max_completion_tokens === 'number' + ? model.top_provider.max_completion_tokens + : undefined, + reasoning: + reasoningKeywords.some(keyword => model.id.toLowerCase().includes(keyword)) + || knownModel?.abilities?.reasoning + || false, + vision: + model.description.includes('vision') + || model.description.includes('multimodal') + || visionKeywords.some(keyword => model.id.toLowerCase().includes(keyword)) + || knownModel?.abilities?.vision + || false, + }; + }) + .filter(Boolean) as ChatModelCard[]; }, provider: ModelProvider.OpenRouter, }); diff --git a/src/libs/agent-runtime/qwen/index.ts b/src/libs/agent-runtime/qwen/index.ts index 3802fdc83c519..7b3b950262369 100644 --- a/src/libs/agent-runtime/qwen/index.ts +++ b/src/libs/agent-runtime/qwen/index.ts @@ -3,7 +3,7 @@ import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory'; import { QwenAIStream } from '../utils/streams'; -import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels'; +import type { ChatModelCard } from '@/types/llm'; export interface QwenModelCard { id: string; @@ -70,38 +70,54 @@ export const LobeQwenAI = LobeOpenAICompatibleFactory({ debug: { chatCompletion: () => process.env.DEBUG_QWEN_CHAT_COMPLETION === '1', }, - models: { - transformModel: (m) => { - const functionCallKeywords = [ - 'qwen-max', - 'qwen-plus', - 'qwen-turbo', - 'qwen2.5', - ]; + models: async ({ client }) => { + const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels'); - const visionKeywords = [ - 'qvq', - 'vl', - ]; + const functionCallKeywords = [ + 'qwen-max', + 'qwen-plus', + 'qwen-turbo', + 'qwen2.5', + ]; - const reasoningKeywords = [ - 'qvq', - 'qwq', - 'deepseek-r1' - ]; + const visionKeywords = [ + 'qvq', + 'vl', + ]; - const model = m as unknown as QwenModelCard; + const reasoningKeywords = [ + 'qvq', + 'qwq', + 'deepseek-r1' + ]; - return { - contextWindowTokens: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.contextWindowTokens ?? undefined, - displayName: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.displayName ?? undefined, - enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.enabled || false, - functionCall: functionCallKeywords.some(keyword => model.id.toLowerCase().includes(keyword)), - id: model.id, - reasoning: reasoningKeywords.some(keyword => model.id.toLowerCase().includes(keyword)), - vision: visionKeywords.some(keyword => model.id.toLowerCase().includes(keyword)), - }; - }, + const modelsPage = await client.models.list() as any; + const modelList: QwenModelCard[] = modelsPage.data; + + return modelList + .map((model) => { + const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.id.toLowerCase() === m.id.toLowerCase()); + + return { + contextWindowTokens: knownModel?.contextWindowTokens ?? undefined, + displayName: knownModel?.displayName ?? undefined, + enabled: knownModel?.enabled || false, + functionCall: + functionCallKeywords.some(keyword => model.id.toLowerCase().includes(keyword)) + || knownModel?.abilities?.functionCall + || false, + id: model.id, + reasoning: + reasoningKeywords.some(keyword => model.id.toLowerCase().includes(keyword)) + || knownModel?.abilities?.reasoning + || false, + vision: + visionKeywords.some(keyword => model.id.toLowerCase().includes(keyword)) + || knownModel?.abilities?.vision + || false, + }; + }) + .filter(Boolean) as ChatModelCard[]; }, provider: ModelProvider.Qwen, }); diff --git a/src/libs/agent-runtime/sensenova/index.ts b/src/libs/agent-runtime/sensenova/index.ts index 123472019cdeb..e24ff58c49d47 100644 --- a/src/libs/agent-runtime/sensenova/index.ts +++ b/src/libs/agent-runtime/sensenova/index.ts @@ -1,7 +1,6 @@ import { ModelProvider } from '../types'; import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory'; -import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels'; import type { ChatModelCard } from '@/types/llm'; export interface SenseNovaModelCard { @@ -33,10 +32,17 @@ export const LobeSenseNovaAI = LobeOpenAICompatibleFactory({ chatCompletion: () => process.env.DEBUG_SENSENOVA_CHAT_COMPLETION === '1', }, models: async ({ client }) => { + const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels'); + const functionCallKeywords = [ + 'deepseek-v3', 'sensechat-5', ]; + const reasoningKeywords = [ + 'deepseek-r1' + ]; + client.baseURL = 'https://api.sensenova.cn/v1/llm'; const modelsPage = await client.models.list() as any; @@ -44,13 +50,25 @@ export const LobeSenseNovaAI = LobeOpenAICompatibleFactory({ return modelList .map((model) => { + const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.id.toLowerCase() === m.id.toLowerCase()); + return { - contextWindowTokens: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.contextWindowTokens ?? undefined, - displayName: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.displayName ?? undefined, - enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.enabled || false, - functionCall: functionCallKeywords.some(keyword => model.id.toLowerCase().includes(keyword)), + contextWindowTokens: knownModel?.contextWindowTokens ?? undefined, + displayName: knownModel?.displayName ?? undefined, + enabled: knownModel?.enabled || false, + functionCall: + functionCallKeywords.some(keyword => model.id.toLowerCase().includes(keyword)) + || knownModel?.abilities?.functionCall + || false, id: model.id, - vision: model.id.toLowerCase().includes('vision'), + reasoning: + reasoningKeywords.some(keyword => model.id.toLowerCase().includes(keyword)) + || knownModel?.abilities?.reasoning + || false, + vision: + model.id.toLowerCase().includes('vision') + || knownModel?.abilities?.vision + || false, }; }) .filter(Boolean) as ChatModelCard[]; diff --git a/src/libs/agent-runtime/siliconcloud/index.ts b/src/libs/agent-runtime/siliconcloud/index.ts index d2625dd9295a4..d72f83510f5d4 100644 --- a/src/libs/agent-runtime/siliconcloud/index.ts +++ b/src/libs/agent-runtime/siliconcloud/index.ts @@ -2,7 +2,7 @@ import { AgentRuntimeErrorType } from '../error'; import { ChatCompletionErrorPayload, ModelProvider } from '../types'; import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory'; -import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels'; +import type { ChatModelCard } from '@/types/llm'; export interface SiliconCloudModelCard { id: string; @@ -52,43 +52,59 @@ export const LobeSiliconCloudAI = LobeOpenAICompatibleFactory({ bizError: AgentRuntimeErrorType.ProviderBizError, invalidAPIKey: AgentRuntimeErrorType.InvalidProviderAPIKey, }, - models: { - transformModel: (m) => { - const functionCallKeywords = [ - 'qwen/qwen2.5', - 'thudm/glm-4', - 'deepseek-ai/deepseek', - 'internlm/internlm2_5', - 'meta-llama/meta-llama-3.1', - 'meta-llama/meta-llama-3.3', - ]; + models: async ({ client }) => { + const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels'); - const visionKeywords = [ - 'opengvlab/internvl', - 'qwen/qvq', - 'qwen/qwen2-vl', - 'teleai/telemm', - 'deepseek-ai/deepseek-vl', - ]; + const functionCallKeywords = [ + 'qwen/qwen2.5', + 'thudm/glm-4', + 'deepseek-ai/deepseek', + 'internlm/internlm2_5', + 'meta-llama/meta-llama-3.1', + 'meta-llama/meta-llama-3.3', + ]; - const reasoningKeywords = [ - 'deepseek-ai/deepseek-r1', - 'qwen/qvq', - 'qwen/qwq', - ]; + const visionKeywords = [ + 'opengvlab/internvl', + 'qwen/qvq', + 'qwen/qwen2-vl', + 'teleai/telemm', + 'deepseek-ai/deepseek-vl', + ]; - const model = m as unknown as SiliconCloudModelCard; + const reasoningKeywords = [ + 'deepseek-ai/deepseek-r1', + 'qwen/qvq', + 'qwen/qwq', + ]; - return { - contextWindowTokens: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.contextWindowTokens ?? undefined, - displayName: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.displayName ?? undefined, - enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.enabled || false, - functionCall: functionCallKeywords.some(keyword => model.id.toLowerCase().includes(keyword)) && !model.id.toLowerCase().includes('deepseek-r1'), - id: model.id, - reasoning: reasoningKeywords.some(keyword => model.id.toLowerCase().includes(keyword)), - vision: visionKeywords.some(keyword => model.id.toLowerCase().includes(keyword)), - }; - }, + const modelsPage = await client.models.list() as any; + const modelList: SiliconCloudModelCard[] = modelsPage.data; + + return modelList + .map((model) => { + const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.id.toLowerCase() === m.id.toLowerCase()); + + return { + contextWindowTokens: knownModel?.contextWindowTokens ?? undefined, + displayName: knownModel?.displayName ?? undefined, + enabled: knownModel?.enabled || false, + functionCall: + functionCallKeywords.some(keyword => model.id.toLowerCase().includes(keyword)) && !model.id.toLowerCase().includes('deepseek-r1') + || knownModel?.abilities?.functionCall + || false, + id: model.id, + reasoning: + reasoningKeywords.some(keyword => model.id.toLowerCase().includes(keyword)) + || knownModel?.abilities?.reasoning + || false, + vision: + visionKeywords.some(keyword => model.id.toLowerCase().includes(keyword)) + || knownModel?.abilities?.vision + || false, + }; + }) + .filter(Boolean) as ChatModelCard[]; }, provider: ModelProvider.SiliconCloud, }); diff --git a/src/libs/agent-runtime/stepfun/index.ts b/src/libs/agent-runtime/stepfun/index.ts index 598716e58a70c..03ed6c9bde8c7 100644 --- a/src/libs/agent-runtime/stepfun/index.ts +++ b/src/libs/agent-runtime/stepfun/index.ts @@ -1,7 +1,7 @@ import { ModelProvider } from '../types'; import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory'; -import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels'; +import type { ChatModelCard } from '@/types/llm'; export interface StepfunModelCard { id: string; @@ -20,32 +20,48 @@ export const LobeStepfunAI = LobeOpenAICompatibleFactory({ debug: { chatCompletion: () => process.env.DEBUG_STEPFUN_CHAT_COMPLETION === '1', }, - models: { - transformModel: (m) => { - // ref: https://platform.stepfun.com/docs/llm/modeloverview - const functionCallKeywords = [ - 'step-1-', - 'step-1o-', - 'step-1v-', - 'step-2-', - ]; - - const visionKeywords = [ - 'step-1o-', - 'step-1v-', - ]; - - const model = m as unknown as StepfunModelCard; + models: async ({ client }) => { + const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels'); - return { - contextWindowTokens: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.contextWindowTokens ?? undefined, - displayName: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.displayName ?? undefined, - enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.enabled || false, - functionCall: functionCallKeywords.some(keyword => model.id.toLowerCase().includes(keyword)), - id: model.id, - vision: visionKeywords.some(keyword => model.id.toLowerCase().includes(keyword)), - }; - }, + // ref: https://platform.stepfun.com/docs/llm/modeloverview + const functionCallKeywords = [ + 'step-1-', + 'step-1o-', + 'step-1v-', + 'step-2-', + ]; + + const visionKeywords = [ + 'step-1o-', + 'step-1v-', + ]; + + const modelsPage = await client.models.list() as any; + const modelList: StepfunModelCard[] = modelsPage.data; + + return modelList + .map((model) => { + const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.id.toLowerCase() === m.id.toLowerCase()); + + return { + contextWindowTokens: knownModel?.contextWindowTokens ?? undefined, + displayName: knownModel?.displayName ?? undefined, + enabled: knownModel?.enabled || false, + functionCall: + functionCallKeywords.some(keyword => model.id.toLowerCase().includes(keyword)) + || knownModel?.abilities?.functionCall + || false, + id: model.id, + reasoning: + knownModel?.abilities?.reasoning + || false, + vision: + visionKeywords.some(keyword => model.id.toLowerCase().includes(keyword)) + || knownModel?.abilities?.vision + || false, + }; + }) + .filter(Boolean) as ChatModelCard[]; }, provider: ModelProvider.Stepfun, }); diff --git a/src/libs/agent-runtime/tencentcloud/index.ts b/src/libs/agent-runtime/tencentcloud/index.ts index 9eb60a5284b67..5a21b8f39833b 100644 --- a/src/libs/agent-runtime/tencentcloud/index.ts +++ b/src/libs/agent-runtime/tencentcloud/index.ts @@ -1,10 +1,54 @@ import { ModelProvider } from '../types'; import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory'; +import type { ChatModelCard } from '@/types/llm'; + +export interface TencentCloudModelCard { + id: string; +} + export const LobeTencentCloudAI = LobeOpenAICompatibleFactory({ baseURL: 'https://api.lkeap.cloud.tencent.com/v1', debug: { chatCompletion: () => process.env.DEBUG_TENCENT_CLOUD_CHAT_COMPLETION === '1', }, + models: async ({ client }) => { + const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels'); + + const functionCallKeywords = [ + 'deepseek-v3', + ]; + + const reasoningKeywords = [ + 'deepseek-r1', + ]; + + const modelsPage = await client.models.list() as any; + const modelList: TencentCloudModelCard[] = modelsPage.data; + + return modelList + .map((model) => { + const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.id.toLowerCase() === m.id.toLowerCase()); + + return { + contextWindowTokens: knownModel?.contextWindowTokens ?? undefined, + displayName: knownModel?.displayName ?? undefined, + enabled: knownModel?.enabled || false, + functionCall: + functionCallKeywords.some(keyword => model.id.toLowerCase().includes(keyword)) + || knownModel?.abilities?.functionCall + || false, + id: model.id, + reasoning: + reasoningKeywords.some(keyword => model.id.toLowerCase().includes(keyword)) + || knownModel?.abilities?.reasoning + || false, + vision: + knownModel?.abilities?.vision + || false, + }; + }) + .filter(Boolean) as ChatModelCard[]; + }, provider: ModelProvider.TencentCloud, }); diff --git a/src/libs/agent-runtime/togetherai/index.ts b/src/libs/agent-runtime/togetherai/index.ts index 1515c751d8b5d..4a3110721da63 100644 --- a/src/libs/agent-runtime/togetherai/index.ts +++ b/src/libs/agent-runtime/togetherai/index.ts @@ -2,7 +2,6 @@ import { ModelProvider } from '../types'; import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory'; import { TogetherAIModel } from './type'; -import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels'; import type { ChatModelCard } from '@/types/llm'; export const LobeTogetherAI = LobeOpenAICompatibleFactory({ @@ -17,6 +16,8 @@ export const LobeTogetherAI = LobeOpenAICompatibleFactory({ chatCompletion: () => process.env.DEBUG_TOGETHERAI_CHAT_COMPLETION === '1', }, models: async ({ client }) => { + const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels'); + const visionKeywords = [ 'qvq', 'vision', @@ -34,17 +35,29 @@ export const LobeTogetherAI = LobeOpenAICompatibleFactory({ return modelList .map((model) => { + const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.name.toLowerCase() === m.id.toLowerCase()); + return { - contextWindowTokens: LOBE_DEFAULT_MODEL_LIST.find((m) => model.name === m.id)?.contextWindowTokens ?? undefined, + contextWindowTokens: knownModel?.contextWindowTokens ?? undefined, description: model.description, displayName: model.display_name, - enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.name === m.id)?.enabled || false, - functionCall: model.description?.toLowerCase().includes('function calling'), + enabled: knownModel?.enabled || false, + functionCall: + model.description?.toLowerCase().includes('function calling') + || knownModel?.abilities?.functionCall + || false, id: model.name, maxOutput: model.context_length, - reasoning: reasoningKeywords.some(keyword => model.name.toLowerCase().includes(keyword)), + reasoning: + reasoningKeywords.some(keyword => model.name.toLowerCase().includes(keyword)) + || knownModel?.abilities?.functionCall + || false, tokens: model.context_length, - vision: model.description?.toLowerCase().includes('vision') || visionKeywords.some(keyword => model.name?.toLowerCase().includes(keyword)), + vision: + model.description?.toLowerCase().includes('vision') + || visionKeywords.some(keyword => model.name?.toLowerCase().includes(keyword)) + || knownModel?.abilities?.functionCall + || false, }; }) .filter(Boolean) as ChatModelCard[]; diff --git a/src/libs/agent-runtime/xai/index.ts b/src/libs/agent-runtime/xai/index.ts index b59f1eefb7317..066092a678fa6 100644 --- a/src/libs/agent-runtime/xai/index.ts +++ b/src/libs/agent-runtime/xai/index.ts @@ -1,7 +1,7 @@ import { ModelProvider } from '../types'; import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory'; -import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels'; +import type { ChatModelCard } from '@/types/llm'; export interface XAIModelCard { id: string; @@ -12,19 +12,34 @@ export const LobeXAI = LobeOpenAICompatibleFactory({ debug: { chatCompletion: () => process.env.DEBUG_XAI_CHAT_COMPLETION === '1', }, - models: { - transformModel: (m) => { - const model = m as unknown as XAIModelCard; + models: async ({ client }) => { + const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels'); - return { - contextWindowTokens: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.contextWindowTokens ?? undefined, - displayName: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.displayName ?? undefined, - enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.enabled || false, - functionCall: true, - id: model.id, - vision: model.id.toLowerCase().includes('vision'), - }; - }, + const modelsPage = await client.models.list() as any; + const modelList: XAIModelCard[] = modelsPage.data; + + return modelList + .map((model) => { + const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.id.toLowerCase() === m.id.toLowerCase()); + + return { + contextWindowTokens: knownModel?.contextWindowTokens ?? undefined, + displayName: knownModel?.displayName ?? undefined, + enabled: knownModel?.enabled || false, + functionCall: + knownModel?.abilities?.functionCall + || false, + id: model.id, + reasoning: + knownModel?.abilities?.reasoning + || false, + vision: + model.id.toLowerCase().includes('vision') + || knownModel?.abilities?.functionCall + || false, + }; + }) + .filter(Boolean) as ChatModelCard[]; }, provider: ModelProvider.XAI, }); diff --git a/src/libs/agent-runtime/zeroone/index.ts b/src/libs/agent-runtime/zeroone/index.ts index 839c0a832f181..e58a6ba0dd293 100644 --- a/src/libs/agent-runtime/zeroone/index.ts +++ b/src/libs/agent-runtime/zeroone/index.ts @@ -1,7 +1,7 @@ import { ModelProvider } from '../types'; import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory'; -import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels'; +import type { ChatModelCard } from '@/types/llm'; export interface ZeroOneModelCard { id: string; @@ -12,19 +12,35 @@ export const LobeZeroOneAI = LobeOpenAICompatibleFactory({ debug: { chatCompletion: () => process.env.DEBUG_ZEROONE_CHAT_COMPLETION === '1', }, - models: { - transformModel: (m) => { - const model = m as unknown as ZeroOneModelCard; + models: async ({ client }) => { + const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels'); - return { - contextWindowTokens: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.contextWindowTokens ?? undefined, - displayName: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.displayName ?? undefined, - enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.enabled || false, - functionCall: model.id.toLowerCase().includes('fc'), - id: model.id, - vision: model.id.toLowerCase().includes('vision'), - }; - }, + const modelsPage = await client.models.list() as any; + const modelList: ZeroOneModelCard[] = modelsPage.data; + + return modelList + .map((model) => { + const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.id.toLowerCase() === m.id.toLowerCase()); + + return { + contextWindowTokens: knownModel?.contextWindowTokens ?? undefined, + displayName: knownModel?.displayName ?? undefined, + enabled: knownModel?.enabled || false, + functionCall: + model.id.toLowerCase().includes('fc') + || knownModel?.abilities?.functionCall + || false, + id: model.id, + reasoning: + knownModel?.abilities?.reasoning + || false, + vision: + model.id.toLowerCase().includes('vision') + || knownModel?.abilities?.vision + || false, + }; + }) + .filter(Boolean) as ChatModelCard[]; }, provider: ModelProvider.ZeroOne, }); diff --git a/src/libs/agent-runtime/zhipu/authToken.test.ts b/src/libs/agent-runtime/zhipu/authToken.test.ts deleted file mode 100644 index 406b99b5da6bd..0000000000000 --- a/src/libs/agent-runtime/zhipu/authToken.test.ts +++ /dev/null @@ -1,18 +0,0 @@ -// @vitest-environment node -import { generateApiToken } from './authToken'; - -describe('generateApiToken', () => { - it('should throw an error if no apiKey is provided', async () => { - await expect(generateApiToken()).rejects.toThrow('Invalid apiKey'); - }); - - it('should throw an error if apiKey is invalid', async () => { - await expect(generateApiToken('invalid')).rejects.toThrow('Invalid apiKey'); - }); - - it('should return a token if a valid apiKey is provided', async () => { - const apiKey = 'id.secret'; - const token = await generateApiToken(apiKey); - expect(token).toBeDefined(); - }); -}); diff --git a/src/libs/agent-runtime/zhipu/authToken.ts b/src/libs/agent-runtime/zhipu/authToken.ts deleted file mode 100644 index 6cb04afc95c2d..0000000000000 --- a/src/libs/agent-runtime/zhipu/authToken.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { SignJWT } from 'jose'; - -export const generateApiToken = async (apiKey?: string): Promise => { - if (!apiKey) { - throw new Error('Invalid apiKey'); - } - - const [id, secret] = apiKey.split('.'); - if (!id || !secret) { - throw new Error('Invalid apiKey'); - } - - const expSeconds = 60 * 60 * 24 * 30; - const nowSeconds = Math.floor(Date.now() / 1000); - const exp = nowSeconds + expSeconds; - const jwtConstructor = new SignJWT({ api_key: id }) - .setProtectedHeader({ alg: 'HS256', sign_type: 'SIGN', typ: 'JWT' }) - .setExpirationTime(exp) - .setIssuedAt(nowSeconds); - - return jwtConstructor.sign(new TextEncoder().encode(secret)); -}; diff --git a/src/libs/agent-runtime/zhipu/index.test.ts b/src/libs/agent-runtime/zhipu/index.test.ts index 54e6e648abf3a..a2cf3a6bc4ca2 100644 --- a/src/libs/agent-runtime/zhipu/index.test.ts +++ b/src/libs/agent-runtime/zhipu/index.test.ts @@ -5,21 +5,12 @@ import { Mock, afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { ChatStreamCallbacks, LobeOpenAI, LobeOpenAICompatibleRuntime } from '@/libs/agent-runtime'; import * as debugStreamModule from '@/libs/agent-runtime/utils/debugStream'; -import * as authTokenModule from './authToken'; import { LobeZhipuAI } from './index'; const bizErrorType = 'ProviderBizError'; const invalidErrorType = 'InvalidProviderAPIKey'; -// Mock相关依赖 -vi.mock('./authToken'); - describe('LobeZhipuAI', () => { - beforeEach(() => { - // Mock generateApiToken - vi.spyOn(authTokenModule, 'generateApiToken').mockResolvedValue('mocked_token'); - }); - afterEach(() => { vi.restoreAllMocks(); }); diff --git a/src/libs/agent-runtime/zhipu/index.ts b/src/libs/agent-runtime/zhipu/index.ts index 3e75d50687650..3e5f5e1c40606 100644 --- a/src/libs/agent-runtime/zhipu/index.ts +++ b/src/libs/agent-runtime/zhipu/index.ts @@ -3,7 +3,6 @@ import OpenAI from 'openai'; import { ChatStreamPayload, ModelProvider } from '../types'; import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory'; -import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels'; import type { ChatModelCard } from '@/types/llm'; export interface ZhipuModelCard { @@ -49,6 +48,8 @@ export const LobeZhipuAI = LobeOpenAICompatibleFactory({ chatCompletion: () => process.env.DEBUG_ZHIPU_CHAT_COMPLETION === '1', }, models: async ({ client }) => { + const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels'); + // ref: https://open.bigmodel.cn/console/modelcenter/square client.baseURL = 'https://open.bigmodel.cn/api/fine-tuning/model_center/list?pageSize=100&pageNum=1'; @@ -57,15 +58,26 @@ export const LobeZhipuAI = LobeOpenAICompatibleFactory({ return modelList .map((model) => { + const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.modelCode.toLowerCase() === m.id.toLowerCase()); + return { - contextWindowTokens: LOBE_DEFAULT_MODEL_LIST.find((m) => model.modelCode === m.id)?.contextWindowTokens ?? undefined, + contextWindowTokens: knownModel?.contextWindowTokens ?? undefined, description: model.description, displayName: model.modelName, - enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.modelCode === m.id)?.enabled || false, - functionCall: model.modelCode.toLowerCase().includes('glm-4') && !model.modelCode.toLowerCase().includes('glm-4v'), + enabled: knownModel?.enabled || false, + functionCall: + model.modelCode.toLowerCase().includes('glm-4') && !model.modelCode.toLowerCase().includes('glm-4v') + || knownModel?.abilities?.functionCall + || false, id: model.modelCode, - reasoning: model.modelCode.toLowerCase().includes('glm-zero-preview'), - vision: model.modelCode.toLowerCase().includes('glm-4v'), + reasoning: + model.modelCode.toLowerCase().includes('glm-zero-preview') + || knownModel?.abilities?.reasoning + || false, + vision: + model.modelCode.toLowerCase().includes('glm-4v') + || knownModel?.abilities?.vision + || false, }; }) .filter(Boolean) as ChatModelCard[];