@lobehub/chat 1.53.11 → 1.53.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/changelog/v1.json +9 -0
  3. package/package.json +1 -1
  4. package/src/config/aiModels/spark.ts +9 -0
  5. package/src/libs/agent-runtime/ai360/index.ts +37 -21
  6. package/src/libs/agent-runtime/anthropic/index.ts +17 -5
  7. package/src/libs/agent-runtime/baichuan/index.ts +11 -2
  8. package/src/libs/agent-runtime/cloudflare/index.ts +22 -7
  9. package/src/libs/agent-runtime/deepseek/index.ts +29 -13
  10. package/src/libs/agent-runtime/fireworksai/index.ts +30 -18
  11. package/src/libs/agent-runtime/giteeai/index.ts +46 -30
  12. package/src/libs/agent-runtime/github/index.test.ts +0 -49
  13. package/src/libs/agent-runtime/github/index.ts +18 -6
  14. package/src/libs/agent-runtime/google/index.ts +17 -7
  15. package/src/libs/agent-runtime/groq/index.ts +43 -27
  16. package/src/libs/agent-runtime/higress/index.ts +45 -25
  17. package/src/libs/agent-runtime/huggingface/index.ts +20 -9
  18. package/src/libs/agent-runtime/hunyuan/index.ts +34 -18
  19. package/src/libs/agent-runtime/internlm/index.ts +27 -12
  20. package/src/libs/agent-runtime/lmstudio/index.ts +34 -0
  21. package/src/libs/agent-runtime/mistral/index.ts +24 -14
  22. package/src/libs/agent-runtime/moonshot/index.ts +28 -13
  23. package/src/libs/agent-runtime/novita/index.ts +35 -18
  24. package/src/libs/agent-runtime/ollama/index.test.ts +20 -1
  25. package/src/libs/agent-runtime/ollama/index.ts +33 -5
  26. package/src/libs/agent-runtime/openai/__snapshots__/index.test.ts.snap +108 -0
  27. package/src/libs/agent-runtime/openai/index.ts +43 -27
  28. package/src/libs/agent-runtime/openrouter/__snapshots__/index.test.ts.snap +39 -11
  29. package/src/libs/agent-runtime/openrouter/index.ts +51 -33
  30. package/src/libs/agent-runtime/qwen/index.ts +45 -29
  31. package/src/libs/agent-runtime/sensenova/index.ts +24 -6
  32. package/src/libs/agent-runtime/siliconcloud/index.ts +50 -34
  33. package/src/libs/agent-runtime/stepfun/index.ts +42 -26
  34. package/src/libs/agent-runtime/tencentcloud/index.ts +44 -0
  35. package/src/libs/agent-runtime/togetherai/index.ts +19 -6
  36. package/src/libs/agent-runtime/xai/index.ts +28 -13
  37. package/src/libs/agent-runtime/zeroone/index.ts +29 -13
  38. package/src/libs/agent-runtime/zhipu/index.test.ts +0 -9
  39. package/src/libs/agent-runtime/zhipu/index.ts +18 -6
  40. package/src/libs/agent-runtime/zhipu/authToken.test.ts +0 -18
  41. package/src/libs/agent-runtime/zhipu/authToken.ts +0 -22
package/CHANGELOG.md CHANGED
@@ -2,6 +2,31 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 1.53.12](https://github.com/lobehub/lobe-chat/compare/v1.53.11...v1.53.12)
6
+
7
+ <sup>Released on **2025-02-14**</sup>
8
+
9
+ #### ♻ Code Refactoring
10
+
11
+ - **misc**: Improve model fetch behavior.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### Code refactoring
19
+
20
+ - **misc**: Improve model fetch behavior, closes [#6055](https://github.com/lobehub/lobe-chat/issues/6055) ([4c2aaf6](https://github.com/lobehub/lobe-chat/commit/4c2aaf6))
21
+
22
+ </details>
23
+
24
+ <div align="right">
25
+
26
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
27
+
28
+ </div>
29
+
5
30
  ### [Version 1.53.11](https://github.com/lobehub/lobe-chat/compare/v1.53.10...v1.53.11)
6
31
 
7
32
  <sup>Released on **2025-02-13**</sup>
package/changelog/v1.json CHANGED
@@ -1,4 +1,13 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "improvements": [
5
+ "Improve model fetch behavior."
6
+ ]
7
+ },
8
+ "date": "2025-02-14",
9
+ "version": "1.53.12"
10
+ },
2
11
  {
3
12
  "children": {
4
13
  "fixes": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "1.53.11",
3
+ "version": "1.53.12",
4
4
  "description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
5
5
  "keywords": [
6
6
  "framework",
@@ -32,6 +32,9 @@ const sparkChatModels: AIChatModelCard[] = [
32
32
  type: 'chat',
33
33
  },
34
34
  {
35
+ abilities: {
36
+ functionCall: true,
37
+ },
35
38
  contextWindowTokens: 8192,
36
39
  description:
37
40
  'Spark Max 为功能最为全面的版本,支持联网搜索及众多内置插件。其全面优化的核心能力以及系统角色设定和函数调用功能,使其在各种复杂应用场景中的表现极为优异和出色。',
@@ -42,6 +45,9 @@ const sparkChatModels: AIChatModelCard[] = [
42
45
  type: 'chat',
43
46
  },
44
47
  {
48
+ abilities: {
49
+ functionCall: true,
50
+ },
45
51
  contextWindowTokens: 32_768,
46
52
  description:
47
53
  'Spark Max 32K 配置了大上下文处理能力,更强的上下文理解和逻辑推理能力,支持32K tokens的文本输入,适用于长文档阅读、私有知识问答等场景',
@@ -52,6 +58,9 @@ const sparkChatModels: AIChatModelCard[] = [
52
58
  type: 'chat',
53
59
  },
54
60
  {
61
+ abilities: {
62
+ functionCall: true,
63
+ },
55
64
  contextWindowTokens: 8192,
56
65
  description:
57
66
  'Spark Ultra 是星火大模型系列中最为强大的版本,在升级联网搜索链路同时,提升对文本内容的理解和总结能力。它是用于提升办公生产力和准确响应需求的全方位解决方案,是引领行业的智能产品。',
@@ -1,7 +1,7 @@
1
1
  import { ModelProvider } from '../types';
2
2
  import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory';
3
3
 
4
- import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels';
4
+ import type { ChatModelCard } from '@/types/llm';
5
5
 
6
6
  export interface Ai360ModelCard {
7
7
  id: string;
@@ -22,28 +22,44 @@ export const LobeAi360AI = LobeOpenAICompatibleFactory({
22
22
  debug: {
23
23
  chatCompletion: () => process.env.DEBUG_AI360_CHAT_COMPLETION === '1',
24
24
  },
25
- models: {
26
- transformModel: (m) => {
27
- const reasoningKeywords = [
28
- '360gpt2-o1',
29
- '360zhinao2-o1',
30
- ];
25
+ models: async ({ client }) => {
26
+ const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels');
31
27
 
32
- const model = m as unknown as Ai360ModelCard;
28
+ const reasoningKeywords = [
29
+ '360gpt2-o1',
30
+ '360zhinao2-o1',
31
+ ];
33
32
 
34
- return {
35
- contextWindowTokens: model.total_tokens,
36
- displayName: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.displayName ?? undefined,
37
- enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.enabled || false,
38
- functionCall: model.id === '360gpt-pro',
39
- id: model.id,
40
- maxTokens:
41
- typeof model.max_tokens === 'number'
42
- ? model.max_tokens
43
- : undefined,
44
- reasoning: reasoningKeywords.some(keyword => model.id.toLowerCase().includes(keyword)),
45
- };
46
- },
33
+ const modelsPage = await client.models.list() as any;
34
+ const modelList: Ai360ModelCard[] = modelsPage.data;
35
+
36
+ return modelList
37
+ .map((model) => {
38
+ const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.id.toLowerCase() === m.id.toLowerCase());
39
+
40
+ return {
41
+ contextWindowTokens: model.total_tokens,
42
+ displayName: knownModel?.displayName ?? undefined,
43
+ enabled: knownModel?.enabled || false,
44
+ functionCall:
45
+ model.id === '360gpt-pro'
46
+ || knownModel?.abilities?.functionCall
47
+ || false,
48
+ id: model.id,
49
+ maxTokens:
50
+ typeof model.max_tokens === 'number'
51
+ ? model.max_tokens
52
+ : undefined,
53
+ reasoning:
54
+ reasoningKeywords.some(keyword => model.id.toLowerCase().includes(keyword))
55
+ || knownModel?.abilities?.reasoning
56
+ || false,
57
+ vision:
58
+ knownModel?.abilities?.vision
59
+ || false,
60
+ };
61
+ })
62
+ .filter(Boolean) as ChatModelCard[];
47
63
  },
48
64
  provider: ModelProvider.Ai360,
49
65
  });
@@ -13,7 +13,6 @@ import { buildAnthropicMessages, buildAnthropicTools } from '../utils/anthropicH
13
13
  import { StreamingResponse } from '../utils/response';
14
14
  import { AnthropicStream } from '../utils/streams';
15
15
 
16
- import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels';
17
16
  import type { ChatModelCard } from '@/types/llm';
18
17
 
19
18
  export interface AnthropicModelCard {
@@ -114,6 +113,8 @@ export class LobeAnthropicAI implements LobeRuntimeAI {
114
113
  }
115
114
 
116
115
  async models() {
116
+ const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels');
117
+
117
118
  const url = `${this.baseURL}/v1/models`;
118
119
  const response = await fetch(url, {
119
120
  headers: {
@@ -128,13 +129,24 @@ export class LobeAnthropicAI implements LobeRuntimeAI {
128
129
 
129
130
  return modelList
130
131
  .map((model) => {
132
+ const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.id.toLowerCase() === m.id.toLowerCase());
133
+
131
134
  return {
132
- contextWindowTokens: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.contextWindowTokens ?? undefined,
135
+ contextWindowTokens: knownModel?.contextWindowTokens ?? undefined,
133
136
  displayName: model.display_name,
134
- enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.enabled || false,
135
- functionCall: model.id.toLowerCase().includes('claude-3'),
137
+ enabled: knownModel?.enabled || false,
138
+ functionCall:
139
+ model.id.toLowerCase().includes('claude-3')
140
+ || knownModel?.abilities?.functionCall
141
+ || false,
136
142
  id: model.id,
137
- vision: model.id.toLowerCase().includes('claude-3') && !model.id.toLowerCase().includes('claude-3-5-haiku'),
143
+ reasoning:
144
+ knownModel?.abilities?.reasoning
145
+ || false,
146
+ vision:
147
+ model.id.toLowerCase().includes('claude-3') && !model.id.toLowerCase().includes('claude-3-5-haiku')
148
+ || knownModel?.abilities?.vision
149
+ || false,
138
150
  };
139
151
  })
140
152
  .filter(Boolean) as ChatModelCard[];
@@ -3,7 +3,6 @@ import OpenAI from 'openai';
3
3
  import { ChatStreamPayload, ModelProvider } from '../types';
4
4
  import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory';
5
5
 
6
- import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels';
7
6
  import type { ChatModelCard } from '@/types/llm';
8
7
 
9
8
  export interface BaichuanModelCard {
@@ -32,18 +31,28 @@ export const LobeBaichuanAI = LobeOpenAICompatibleFactory({
32
31
  chatCompletion: () => process.env.DEBUG_BAICHUAN_CHAT_COMPLETION === '1',
33
32
  },
34
33
  models: async ({ client }) => {
34
+ const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels');
35
+
35
36
  const modelsPage = await client.models.list() as any;
36
37
  const modelList: BaichuanModelCard[] = modelsPage.data;
37
38
 
38
39
  return modelList
39
40
  .map((model) => {
41
+ const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.model.toLowerCase() === m.id.toLowerCase());
42
+
40
43
  return {
41
44
  contextWindowTokens: model.max_input_length,
42
45
  displayName: model.model_show_name,
43
- enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.model === m.id)?.enabled || false,
46
+ enabled: knownModel?.enabled || false,
44
47
  functionCall: model.function_call,
45
48
  id: model.model,
46
49
  maxTokens: model.max_tokens,
50
+ reasoning:
51
+ knownModel?.abilities?.reasoning
52
+ || false,
53
+ vision:
54
+ knownModel?.abilities?.vision
55
+ || false,
47
56
  };
48
57
  })
49
58
  .filter(Boolean) as ChatModelCard[];
@@ -12,7 +12,6 @@ import { debugStream } from '../utils/debugStream';
12
12
  import { StreamingResponse } from '../utils/response';
13
13
  import { createCallbacksTransformer } from '../utils/streams';
14
14
 
15
- import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels';
16
15
  import { ChatModelCard } from '@/types/llm';
17
16
 
18
17
  export interface CloudflareModelCard {
@@ -113,6 +112,8 @@ export class LobeCloudflareAI implements LobeRuntimeAI {
113
112
  }
114
113
 
115
114
  async models(): Promise<ChatModelCard[]> {
115
+ const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels');
116
+
116
117
  const url = `${DEFAULT_BASE_URL_PREFIX}/client/v4/accounts/${this.accountID}/ai/models/search`;
117
118
  const response = await fetch(url, {
118
119
  headers: {
@@ -127,16 +128,30 @@ export class LobeCloudflareAI implements LobeRuntimeAI {
127
128
 
128
129
  return modelList
129
130
  .map((model) => {
131
+ const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.name.toLowerCase() === m.id.toLowerCase());
132
+
130
133
  return {
131
134
  contextWindowTokens: model.properties?.max_total_tokens
132
135
  ? Number(model.properties.max_total_tokens)
133
- : LOBE_DEFAULT_MODEL_LIST.find((m) => model.name === m.id)?.contextWindowTokens ?? undefined,
134
- displayName: LOBE_DEFAULT_MODEL_LIST.find((m) => model.name === m.id)?.displayName ?? (model.properties?.["beta"] === "true" ? `${model.name} (Beta)` : undefined),
135
- enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.name === m.id)?.enabled || false,
136
- functionCall: model.description.toLowerCase().includes('function call') || model.properties?.["function_calling"] === "true",
136
+ : knownModel?.contextWindowTokens ?? undefined,
137
+ displayName: knownModel?.displayName ?? (model.properties?.["beta"] === "true" ? `${model.name} (Beta)` : undefined),
138
+ enabled: knownModel?.enabled || false,
139
+ functionCall:
140
+ model.description.toLowerCase().includes('function call')
141
+ || model.properties?.["function_calling"] === "true"
142
+ || knownModel?.abilities?.functionCall
143
+ || false,
137
144
  id: model.name,
138
- reasoning: model.name.toLowerCase().includes('deepseek-r1'),
139
- vision: model.name.toLowerCase().includes('vision') || model.task?.name.toLowerCase().includes('image-to-text') || model.description.toLowerCase().includes('vision'),
145
+ reasoning:
146
+ model.name.toLowerCase().includes('deepseek-r1')
147
+ || knownModel?.abilities?.reasoning
148
+ || false,
149
+ vision:
150
+ model.name.toLowerCase().includes('vision')
151
+ || model.task?.name.toLowerCase().includes('image-to-text')
152
+ || model.description.toLowerCase().includes('vision')
153
+ || knownModel?.abilities?.vision
154
+ || false,
140
155
  };
141
156
  })
142
157
  .filter(Boolean) as ChatModelCard[];
@@ -3,7 +3,7 @@ import OpenAI from 'openai';
3
3
  import { ChatStreamPayload, ModelProvider } from '../types';
4
4
  import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory';
5
5
 
6
- import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels';
6
+ import type { ChatModelCard } from '@/types/llm';
7
7
 
8
8
  export interface DeepSeekModelCard {
9
9
  id: string;
@@ -59,19 +59,35 @@ export const LobeDeepSeekAI = LobeOpenAICompatibleFactory({
59
59
  debug: {
60
60
  chatCompletion: () => process.env.DEBUG_DEEPSEEK_CHAT_COMPLETION === '1',
61
61
  },
62
- models: {
63
- transformModel: (m) => {
64
- const model = m as unknown as DeepSeekModelCard;
62
+ models: async ({ client }) => {
63
+ const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels');
65
64
 
66
- return {
67
- contextWindowTokens: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.contextWindowTokens ?? undefined,
68
- displayName: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.displayName ?? undefined,
69
- enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.enabled || false,
70
- functionCall: !model.id.toLowerCase().includes('deepseek-reasoner'),
71
- id: model.id,
72
- reasoning: model.id.toLowerCase().includes('deepseek-reasoner'),
73
- };
74
- },
65
+ const modelsPage = await client.models.list() as any;
66
+ const modelList: DeepSeekModelCard[] = modelsPage.data;
67
+
68
+ return modelList
69
+ .map((model) => {
70
+ const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.id.toLowerCase() === m.id.toLowerCase());
71
+
72
+ return {
73
+ contextWindowTokens: knownModel?.contextWindowTokens ?? undefined,
74
+ displayName: knownModel?.displayName ?? undefined,
75
+ enabled: knownModel?.enabled || false,
76
+ functionCall:
77
+ !model.id.toLowerCase().includes('reasoner')
78
+ || knownModel?.abilities?.functionCall
79
+ || false,
80
+ id: model.id,
81
+ reasoning:
82
+ model.id.toLowerCase().includes('reasoner')
83
+ || knownModel?.abilities?.reasoning
84
+ || false,
85
+ vision:
86
+ knownModel?.abilities?.vision
87
+ || false,
88
+ };
89
+ })
90
+ .filter(Boolean) as ChatModelCard[];
75
91
  },
76
92
  provider: ModelProvider.DeepSeek,
77
93
  });
@@ -1,7 +1,7 @@
1
1
  import { ModelProvider } from '../types';
2
2
  import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory';
3
3
 
4
- import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels';
4
+ import type { ChatModelCard } from '@/types/llm';
5
5
 
6
6
  export interface FireworksAIModelCard {
7
7
  context_length: number;
@@ -15,25 +15,37 @@ export const LobeFireworksAI = LobeOpenAICompatibleFactory({
15
15
  debug: {
16
16
  chatCompletion: () => process.env.DEBUG_FIREWORKSAI_CHAT_COMPLETION === '1',
17
17
  },
18
- models: {
19
- transformModel: (m) => {
20
- const reasoningKeywords = [
21
- 'deepseek-r1',
22
- 'qwq',
23
- ];
18
+ models: async ({ client }) => {
19
+ const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels');
24
20
 
25
- const model = m as unknown as FireworksAIModelCard;
21
+ const reasoningKeywords = [
22
+ 'deepseek-r1',
23
+ 'qwq',
24
+ ];
26
25
 
27
- return {
28
- contextWindowTokens: model.context_length,
29
- displayName: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.displayName ?? undefined,
30
- enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.enabled || false,
31
- functionCall: model.supports_tools || model.id.toLowerCase().includes('function'),
32
- id: model.id,
33
- reasoning: reasoningKeywords.some(keyword => model.id.toLowerCase().includes(keyword)),
34
- vision: model.supports_image_input,
35
- };
36
- },
26
+ const modelsPage = await client.models.list() as any;
27
+ const modelList: FireworksAIModelCard[] = modelsPage.data;
28
+
29
+ return modelList
30
+ .map((model) => {
31
+ const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.id.toLowerCase() === m.id.toLowerCase());
32
+
33
+ return {
34
+ contextWindowTokens: model.context_length,
35
+ displayName: knownModel?.displayName ?? undefined,
36
+ enabled: knownModel?.enabled || false,
37
+ functionCall:
38
+ model.supports_tools
39
+ || model.id.toLowerCase().includes('function'),
40
+ id: model.id,
41
+ reasoning:
42
+ reasoningKeywords.some(keyword => model.id.toLowerCase().includes(keyword))
43
+ || knownModel?.abilities?.reasoning
44
+ || false,
45
+ vision: model.supports_image_input,
46
+ };
47
+ })
48
+ .filter(Boolean) as ChatModelCard[];
37
49
  },
38
50
  provider: ModelProvider.FireworksAI,
39
51
  });
@@ -1,7 +1,7 @@
1
1
  import { ModelProvider } from '../types';
2
2
  import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory';
3
3
 
4
- import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels';
4
+ import type { ChatModelCard } from '@/types/llm';
5
5
 
6
6
  export interface GiteeAIModelCard {
7
7
  id: string;
@@ -12,35 +12,51 @@ export const LobeGiteeAI = LobeOpenAICompatibleFactory({
12
12
  debug: {
13
13
  chatCompletion: () => process.env.DEBUG_GITEE_AI_CHAT_COMPLETION === '1',
14
14
  },
15
- models: {
16
- transformModel: (m) => {
17
- const functionCallKeywords = [
18
- 'qwen2.5',
19
- 'glm-4',
20
- ];
21
-
22
- const visionKeywords = [
23
- 'internvl',
24
- 'qwen2-vl',
25
- ];
26
-
27
- const reasoningKeywords = [
28
- 'deepseek-r1',
29
- 'qwq',
30
- ];
31
-
32
- const model = m as unknown as GiteeAIModelCard;
33
-
34
- return {
35
- contextWindowTokens: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.contextWindowTokens ?? undefined,
36
- displayName: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.displayName ?? undefined,
37
- enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id === m.id)?.enabled || false,
38
- functionCall: functionCallKeywords.some(keyword => model.id.toLowerCase().includes(keyword)) && !model.id.toLowerCase().includes('qwen2.5-coder'),
39
- id: model.id,
40
- reasoning: reasoningKeywords.some(keyword => model.id.toLowerCase().includes(keyword)),
41
- vision: visionKeywords.some(keyword => model.id.toLowerCase().includes(keyword)),
42
- };
43
- },
15
+ models: async ({ client }) => {
16
+ const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels');
17
+
18
+ const functionCallKeywords = [
19
+ 'qwen2.5',
20
+ 'glm-4',
21
+ ];
22
+
23
+ const visionKeywords = [
24
+ 'internvl',
25
+ 'qwen2-vl',
26
+ ];
27
+
28
+ const reasoningKeywords = [
29
+ 'deepseek-r1',
30
+ 'qwq',
31
+ ];
32
+
33
+ const modelsPage = await client.models.list() as any;
34
+ const modelList: GiteeAIModelCard[] = modelsPage.data;
35
+
36
+ return modelList
37
+ .map((model) => {
38
+ const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.id.toLowerCase() === m.id.toLowerCase());
39
+
40
+ return {
41
+ contextWindowTokens: knownModel?.contextWindowTokens ?? undefined,
42
+ displayName: knownModel?.displayName ?? undefined,
43
+ enabled: knownModel?.enabled || false,
44
+ functionCall:
45
+ functionCallKeywords.some(keyword => model.id.toLowerCase().includes(keyword)) && !model.id.toLowerCase().includes('qwen2.5-coder')
46
+ || knownModel?.abilities?.functionCall
47
+ || false,
48
+ id: model.id,
49
+ reasoning:
50
+ reasoningKeywords.some(keyword => model.id.toLowerCase().includes(keyword))
51
+ || knownModel?.abilities?.reasoning
52
+ || false,
53
+ vision:
54
+ visionKeywords.some(keyword => model.id.toLowerCase().includes(keyword))
55
+ || knownModel?.abilities?.vision
56
+ || false,
57
+ };
58
+ })
59
+ .filter(Boolean) as ChatModelCard[];
44
60
  },
45
61
  provider: ModelProvider.GiteeAI,
46
62
  });
@@ -210,53 +210,4 @@ describe('LobeGithubAI', () => {
210
210
  });
211
211
  });
212
212
  });
213
-
214
- describe('models', () => {
215
- beforeEach(() => {});
216
-
217
- it('should return a list of models', async () => {
218
- // Arrange
219
- const arr = [
220
- {
221
- id: 'azureml://registries/azureml-cohere/models/Cohere-command-r/versions/3',
222
- name: 'Cohere-command-r',
223
- friendly_name: 'Cohere Command R',
224
- model_version: 3,
225
- publisher: 'cohere',
226
- model_family: 'cohere',
227
- model_registry: 'azureml-cohere',
228
- license: 'custom',
229
- task: 'chat-completion',
230
- description:
231
- "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",
232
- summary:
233
- 'Command R is a scalable generative model targeting RAG and Tool Use to enable production-scale AI for enterprise.',
234
- tags: ['rag', 'multilingual'],
235
- },
236
- ];
237
- vi.spyOn(instance['client'].models, 'list').mockResolvedValue({
238
- body: arr,
239
- } as any);
240
-
241
- // Act & Assert
242
- const models = await instance.models();
243
-
244
- const modelsCount = models.length;
245
- expect(modelsCount).toBe(arr.length);
246
-
247
- for (let i = 0; i < arr.length; i++) {
248
- const model = models[i];
249
- expect(model).toEqual({
250
- contextWindowTokens: undefined,
251
- description: arr[i].description,
252
- displayName: arr[i].friendly_name,
253
- enabled: false,
254
- functionCall: true,
255
- id: arr[i].name,
256
- reasoning: false,
257
- vision: false,
258
- });
259
- }
260
- });
261
- });
262
213
  });
@@ -3,7 +3,6 @@ import { pruneReasoningPayload } from '../openai';
3
3
  import { ModelProvider } from '../types';
4
4
  import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory';
5
5
 
6
- import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels';
7
6
  import type { ChatModelCard } from '@/types/llm';
8
7
 
9
8
  export interface GithubModelCard {
@@ -38,6 +37,8 @@ export const LobeGithubAI = LobeOpenAICompatibleFactory({
38
37
  invalidAPIKey: AgentRuntimeErrorType.InvalidGithubToken,
39
38
  },
40
39
  models: async ({ client }) => {
40
+ const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels');
41
+
41
42
  const functionCallKeywords = [
42
43
  'function',
43
44
  'tool',
@@ -58,15 +59,26 @@ export const LobeGithubAI = LobeOpenAICompatibleFactory({
58
59
 
59
60
  return modelList
60
61
  .map((model) => {
62
+ const knownModel = LOBE_DEFAULT_MODEL_LIST.find((m) => model.name.toLowerCase() === m.id.toLowerCase());
63
+
61
64
  return {
62
- contextWindowTokens: LOBE_DEFAULT_MODEL_LIST.find((m) => model.name === m.id)?.contextWindowTokens ?? undefined,
65
+ contextWindowTokens: knownModel?.contextWindowTokens ?? undefined,
63
66
  description: model.description,
64
67
  displayName: model.friendly_name,
65
- enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.name === m.id)?.enabled || false,
66
- functionCall: functionCallKeywords.some(keyword => model.description.toLowerCase().includes(keyword)),
68
+ enabled: knownModel?.enabled || false,
69
+ functionCall:
70
+ functionCallKeywords.some(keyword => model.description.toLowerCase().includes(keyword))
71
+ || knownModel?.abilities?.functionCall
72
+ || false,
67
73
  id: model.name,
68
- reasoning: reasoningKeywords.some(keyword => model.name.toLowerCase().includes(keyword)),
69
- vision: visionKeywords.some(keyword => model.description.toLowerCase().includes(keyword)),
74
+ reasoning:
75
+ reasoningKeywords.some(keyword => model.name.toLowerCase().includes(keyword))
76
+ || knownModel?.abilities?.reasoning
77
+ || false,
78
+ vision:
79
+ visionKeywords.some(keyword => model.description.toLowerCase().includes(keyword))
80
+ || knownModel?.abilities?.vision
81
+ || false,
70
82
  };
71
83
  })
72
84
  .filter(Boolean) as ChatModelCard[];