@lobehub/chat 1.118.4 → 1.118.6

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 (165) hide show
  1. package/.github/workflows/test.yml +36 -1
  2. package/CHANGELOG.md +59 -0
  3. package/changelog/v1.json +18 -0
  4. package/locales/ar/setting.json +4 -0
  5. package/locales/bg-BG/setting.json +4 -0
  6. package/locales/de-DE/setting.json +4 -0
  7. package/locales/en-US/setting.json +4 -0
  8. package/locales/es-ES/setting.json +4 -0
  9. package/locales/fa-IR/setting.json +4 -0
  10. package/locales/fr-FR/setting.json +4 -0
  11. package/locales/it-IT/setting.json +4 -0
  12. package/locales/ja-JP/setting.json +4 -0
  13. package/locales/ko-KR/setting.json +4 -0
  14. package/locales/nl-NL/setting.json +4 -0
  15. package/locales/pl-PL/setting.json +4 -0
  16. package/locales/pt-BR/setting.json +4 -0
  17. package/locales/ru-RU/setting.json +4 -0
  18. package/locales/tr-TR/setting.json +4 -0
  19. package/locales/vi-VN/setting.json +4 -0
  20. package/locales/zh-CN/setting.json +4 -0
  21. package/locales/zh-TW/setting.json +4 -0
  22. package/package.json +2 -1
  23. package/packages/const/package.json +4 -1
  24. package/packages/const/src/image.ts +1 -1
  25. package/packages/const/src/settings/agent.ts +1 -0
  26. package/packages/database/src/repositories/aiInfra/index.ts +7 -2
  27. package/packages/model-bank/package.json +75 -0
  28. package/{src/config → packages/model-bank/src}/aiModels/bfl.ts +1 -1
  29. package/{src/config → packages/model-bank/src}/aiModels/fal.ts +1 -1
  30. package/{src/config → packages/model-bank/src}/aiModels/google.ts +1 -1
  31. package/{src/config → packages/model-bank/src}/aiModels/index.ts +2 -2
  32. package/{src/config → packages/model-bank/src}/aiModels/openai.ts +1 -1
  33. package/packages/model-bank/src/exports.test.ts +37 -0
  34. package/packages/model-bank/src/index.ts +2 -0
  35. package/{src/libs → packages/model-bank/src}/standard-parameters/index.ts +1 -1
  36. package/packages/model-bank/vitest.config.mts +11 -0
  37. package/packages/model-runtime/package.json +1 -0
  38. package/packages/model-runtime/src/ai360/index.ts +1 -1
  39. package/packages/model-runtime/src/aihubmix/index.ts +1 -1
  40. package/packages/model-runtime/src/anthropic/index.ts +6 -6
  41. package/packages/model-runtime/src/baichuan/index.ts +1 -1
  42. package/packages/model-runtime/src/bfl/createImage.ts +1 -2
  43. package/packages/model-runtime/src/cloudflare/index.ts +1 -1
  44. package/packages/model-runtime/src/cohere/index.ts +1 -1
  45. package/packages/model-runtime/src/deepseek/index.ts +1 -1
  46. package/packages/model-runtime/src/fal/index.ts +1 -2
  47. package/packages/model-runtime/src/fireworksai/index.ts +1 -1
  48. package/packages/model-runtime/src/groq/index.ts +1 -1
  49. package/packages/model-runtime/src/higress/index.ts +1 -1
  50. package/packages/model-runtime/src/huggingface/index.ts +1 -1
  51. package/packages/model-runtime/src/hunyuan/index.ts +1 -1
  52. package/packages/model-runtime/src/infiniai/index.ts +1 -1
  53. package/packages/model-runtime/src/internlm/index.ts +1 -1
  54. package/packages/model-runtime/src/jina/index.ts +1 -1
  55. package/packages/model-runtime/src/lmstudio/index.ts +1 -1
  56. package/packages/model-runtime/src/minimax/index.ts +1 -1
  57. package/packages/model-runtime/src/mistral/index.ts +1 -1
  58. package/packages/model-runtime/src/novita/__snapshots__/index.test.ts.snap +309 -21
  59. package/packages/model-runtime/src/novita/index.ts +31 -1
  60. package/packages/model-runtime/src/ollama/index.ts +1 -1
  61. package/packages/model-runtime/src/openai/__snapshots__/index.test.ts.snap +28 -0
  62. package/packages/model-runtime/src/openai/index.test.ts +0 -3
  63. package/packages/model-runtime/src/openrouter/__snapshots__/index.test.ts.snap +46 -0
  64. package/packages/model-runtime/src/openrouter/index.test.ts +21 -45
  65. package/packages/model-runtime/src/openrouter/index.ts +22 -25
  66. package/packages/model-runtime/src/openrouter/type.ts +12 -24
  67. package/packages/model-runtime/src/ppio/index.ts +1 -1
  68. package/packages/model-runtime/src/search1api/index.ts +1 -1
  69. package/packages/model-runtime/src/sensenova/index.ts +1 -1
  70. package/packages/model-runtime/src/stepfun/index.ts +1 -1
  71. package/packages/model-runtime/src/tencentcloud/index.ts +1 -1
  72. package/packages/model-runtime/src/togetherai/index.ts +1 -1
  73. package/packages/model-runtime/src/types/image.ts +1 -1
  74. package/packages/model-runtime/src/utils/modelParse.test.ts +5 -5
  75. package/packages/model-runtime/src/utils/modelParse.ts +47 -22
  76. package/packages/model-runtime/src/utils/openaiCompatibleFactory/createImage.ts +1 -2
  77. package/packages/model-runtime/src/utils/openaiCompatibleFactory/index.ts +1 -1
  78. package/packages/model-runtime/src/vllm/index.ts +1 -1
  79. package/packages/model-runtime/src/xinference/index.ts +1 -1
  80. package/packages/types/src/agent/chatConfig.ts +6 -0
  81. package/packages/types/src/aiModel.ts +1 -2
  82. package/packages/types/src/llm.ts +1 -1
  83. package/packages/utils/src/getFallbackModelProperty.test.ts +1 -1
  84. package/packages/utils/src/getFallbackModelProperty.ts +1 -1
  85. package/packages/utils/src/parseModels.test.ts +1 -2
  86. package/packages/utils/src/parseModels.ts +1 -1
  87. package/src/app/[variants]/(main)/image/features/GenerationFeed/BatchItem.tsx +1 -1
  88. package/src/app/[variants]/(main)/profile/features/ClerkProfile.tsx +1 -1
  89. package/src/app/[variants]/(main)/settings/_layout/Desktop/index.tsx +1 -5
  90. package/src/features/AgentSetting/AgentModal/index.tsx +9 -0
  91. package/src/locales/default/models.ts +1 -1
  92. package/src/locales/default/setting.ts +4 -0
  93. package/src/server/globalConfig/genServerAiProviderConfig.test.ts +3 -3
  94. package/src/server/globalConfig/genServerAiProviderConfig.ts +1 -1
  95. package/src/server/routers/async/image.ts +1 -1
  96. package/src/server/services/discover/index.test.ts +1 -1
  97. package/src/server/services/discover/index.ts +16 -8
  98. package/src/services/chat.ts +8 -1
  99. package/src/store/agent/slices/chat/selectors/__snapshots__/agent.test.ts.snap +1 -0
  100. package/src/store/aiInfra/slices/aiProvider/action.ts +1 -1
  101. package/src/store/image/slices/generationConfig/action.test.ts +2 -6
  102. package/src/store/image/slices/generationConfig/action.ts +3 -3
  103. package/src/store/image/slices/generationConfig/hooks.test.ts +2 -2
  104. package/src/store/image/slices/generationConfig/hooks.ts +1 -1
  105. package/src/store/image/slices/generationConfig/initialState.ts +2 -3
  106. package/src/store/image/slices/generationConfig/selectors.test.ts +1 -2
  107. package/src/store/image/slices/generationConfig/selectors.ts +1 -1
  108. package/src/store/user/slices/settings/selectors/__snapshots__/settings.test.ts.snap +2 -0
  109. /package/{src/config → packages/model-bank/src}/aiModels/ai21.ts +0 -0
  110. /package/{src/config → packages/model-bank/src}/aiModels/ai302.ts +0 -0
  111. /package/{src/config → packages/model-bank/src}/aiModels/ai360.ts +0 -0
  112. /package/{src/config → packages/model-bank/src}/aiModels/aihubmix.ts +0 -0
  113. /package/{src/config → packages/model-bank/src}/aiModels/akashchat.ts +0 -0
  114. /package/{src/config → packages/model-bank/src}/aiModels/anthropic.ts +0 -0
  115. /package/{src/config → packages/model-bank/src}/aiModels/azure.ts +0 -0
  116. /package/{src/config → packages/model-bank/src}/aiModels/azureai.ts +0 -0
  117. /package/{src/config → packages/model-bank/src}/aiModels/baichuan.ts +0 -0
  118. /package/{src/config → packages/model-bank/src}/aiModels/bedrock.ts +0 -0
  119. /package/{src/config → packages/model-bank/src}/aiModels/cloudflare.ts +0 -0
  120. /package/{src/config → packages/model-bank/src}/aiModels/cohere.ts +0 -0
  121. /package/{src/config → packages/model-bank/src}/aiModels/deepseek.ts +0 -0
  122. /package/{src/config → packages/model-bank/src}/aiModels/fireworksai.ts +0 -0
  123. /package/{src/config → packages/model-bank/src}/aiModels/giteeai.ts +0 -0
  124. /package/{src/config → packages/model-bank/src}/aiModels/github.ts +0 -0
  125. /package/{src/config → packages/model-bank/src}/aiModels/groq.ts +0 -0
  126. /package/{src/config → packages/model-bank/src}/aiModels/higress.ts +0 -0
  127. /package/{src/config → packages/model-bank/src}/aiModels/huggingface.ts +0 -0
  128. /package/{src/config → packages/model-bank/src}/aiModels/hunyuan.ts +0 -0
  129. /package/{src/config → packages/model-bank/src}/aiModels/infiniai.ts +0 -0
  130. /package/{src/config → packages/model-bank/src}/aiModels/internlm.ts +0 -0
  131. /package/{src/config → packages/model-bank/src}/aiModels/jina.ts +0 -0
  132. /package/{src/config → packages/model-bank/src}/aiModels/lmstudio.ts +0 -0
  133. /package/{src/config → packages/model-bank/src}/aiModels/lobehub.ts +0 -0
  134. /package/{src/config → packages/model-bank/src}/aiModels/minimax.ts +0 -0
  135. /package/{src/config → packages/model-bank/src}/aiModels/mistral.ts +0 -0
  136. /package/{src/config → packages/model-bank/src}/aiModels/modelscope.ts +0 -0
  137. /package/{src/config → packages/model-bank/src}/aiModels/moonshot.ts +0 -0
  138. /package/{src/config → packages/model-bank/src}/aiModels/novita.ts +0 -0
  139. /package/{src/config → packages/model-bank/src}/aiModels/nvidia.ts +0 -0
  140. /package/{src/config → packages/model-bank/src}/aiModels/ollama.ts +0 -0
  141. /package/{src/config → packages/model-bank/src}/aiModels/openrouter.ts +0 -0
  142. /package/{src/config → packages/model-bank/src}/aiModels/perplexity.ts +0 -0
  143. /package/{src/config → packages/model-bank/src}/aiModels/ppio.ts +0 -0
  144. /package/{src/config → packages/model-bank/src}/aiModels/qiniu.ts +0 -0
  145. /package/{src/config → packages/model-bank/src}/aiModels/qwen.ts +0 -0
  146. /package/{src/config → packages/model-bank/src}/aiModels/sambanova.ts +0 -0
  147. /package/{src/config → packages/model-bank/src}/aiModels/search1api.ts +0 -0
  148. /package/{src/config → packages/model-bank/src}/aiModels/sensenova.ts +0 -0
  149. /package/{src/config → packages/model-bank/src}/aiModels/siliconcloud.ts +0 -0
  150. /package/{src/config → packages/model-bank/src}/aiModels/spark.ts +0 -0
  151. /package/{src/config → packages/model-bank/src}/aiModels/stepfun.ts +0 -0
  152. /package/{src/config → packages/model-bank/src}/aiModels/taichu.ts +0 -0
  153. /package/{src/config → packages/model-bank/src}/aiModels/tencentcloud.ts +0 -0
  154. /package/{src/config → packages/model-bank/src}/aiModels/togetherai.ts +0 -0
  155. /package/{src/config → packages/model-bank/src}/aiModels/upstage.ts +0 -0
  156. /package/{src/config → packages/model-bank/src}/aiModels/v0.ts +0 -0
  157. /package/{src/config → packages/model-bank/src}/aiModels/vertexai.ts +0 -0
  158. /package/{src/config → packages/model-bank/src}/aiModels/vllm.ts +0 -0
  159. /package/{src/config → packages/model-bank/src}/aiModels/volcengine.ts +0 -0
  160. /package/{src/config → packages/model-bank/src}/aiModels/wenxin.ts +0 -0
  161. /package/{src/config → packages/model-bank/src}/aiModels/xai.ts +0 -0
  162. /package/{src/config → packages/model-bank/src}/aiModels/xinference.ts +0 -0
  163. /package/{src/config → packages/model-bank/src}/aiModels/zeroone.ts +0 -0
  164. /package/{src/config → packages/model-bank/src}/aiModels/zhipu.ts +0 -0
  165. /package/{src/libs → packages/model-bank/src}/standard-parameters/index.test.ts +0 -0
@@ -10,6 +10,7 @@ exports[`LobeOpenAI > models > should get models 1`] = `
10
10
  "functionCall": false,
11
11
  "id": "whisper-1",
12
12
  "maxOutput": undefined,
13
+ "pricing": undefined,
13
14
  "reasoning": false,
14
15
  "releasedAt": "2023-02-27",
15
16
  "type": "stt",
@@ -23,6 +24,7 @@ exports[`LobeOpenAI > models > should get models 1`] = `
23
24
  "functionCall": false,
24
25
  "id": "davinci-002",
25
26
  "maxOutput": undefined,
27
+ "pricing": undefined,
26
28
  "reasoning": false,
27
29
  "releasedAt": "2023-08-21",
28
30
  "type": "chat",
@@ -36,6 +38,7 @@ exports[`LobeOpenAI > models > should get models 1`] = `
36
38
  "functionCall": true,
37
39
  "id": "gpt-3.5-turbo",
38
40
  "maxOutput": undefined,
41
+ "pricing": undefined,
39
42
  "reasoning": false,
40
43
  "releasedAt": "2023-02-28",
41
44
  "type": "chat",
@@ -65,6 +68,7 @@ exports[`LobeOpenAI > models > should get models 1`] = `
65
68
  ],
66
69
  },
67
70
  },
71
+ "pricing": undefined,
68
72
  "reasoning": false,
69
73
  "releasedAt": "2023-11-01",
70
74
  "type": "image",
@@ -78,6 +82,7 @@ exports[`LobeOpenAI > models > should get models 1`] = `
78
82
  "functionCall": false,
79
83
  "id": "gpt-3.5-turbo-16k",
80
84
  "maxOutput": undefined,
85
+ "pricing": undefined,
81
86
  "reasoning": false,
82
87
  "releasedAt": "2023-05-10",
83
88
  "type": "chat",
@@ -91,6 +96,7 @@ exports[`LobeOpenAI > models > should get models 1`] = `
91
96
  "functionCall": false,
92
97
  "id": "tts-1-hd-1106",
93
98
  "maxOutput": undefined,
99
+ "pricing": undefined,
94
100
  "reasoning": false,
95
101
  "releasedAt": "2023-11-03",
96
102
  "type": "chat",
@@ -104,6 +110,7 @@ exports[`LobeOpenAI > models > should get models 1`] = `
104
110
  "functionCall": false,
105
111
  "id": "tts-1-hd",
106
112
  "maxOutput": undefined,
113
+ "pricing": undefined,
107
114
  "reasoning": false,
108
115
  "releasedAt": "2023-11-03",
109
116
  "type": "tts",
@@ -117,6 +124,7 @@ exports[`LobeOpenAI > models > should get models 1`] = `
117
124
  "functionCall": false,
118
125
  "id": "gpt-3.5-turbo-16k-0613",
119
126
  "maxOutput": undefined,
127
+ "pricing": undefined,
120
128
  "reasoning": false,
121
129
  "releasedAt": "2023-05-30",
122
130
  "type": "chat",
@@ -130,6 +138,7 @@ exports[`LobeOpenAI > models > should get models 1`] = `
130
138
  "functionCall": false,
131
139
  "id": "text-embedding-3-large",
132
140
  "maxOutput": undefined,
141
+ "pricing": undefined,
133
142
  "reasoning": false,
134
143
  "releasedAt": "2024-01-22",
135
144
  "type": "embedding",
@@ -143,6 +152,7 @@ exports[`LobeOpenAI > models > should get models 1`] = `
143
152
  "functionCall": false,
144
153
  "id": "gpt-4-1106-vision-preview",
145
154
  "maxOutput": undefined,
155
+ "pricing": undefined,
146
156
  "reasoning": false,
147
157
  "releasedAt": "2024-03-26",
148
158
  "type": "chat",
@@ -156,6 +166,7 @@ exports[`LobeOpenAI > models > should get models 1`] = `
156
166
  "functionCall": false,
157
167
  "id": "gpt-3.5-turbo-instruct-0914",
158
168
  "maxOutput": undefined,
169
+ "pricing": undefined,
159
170
  "reasoning": false,
160
171
  "releasedAt": "2023-09-07",
161
172
  "type": "chat",
@@ -169,6 +180,7 @@ exports[`LobeOpenAI > models > should get models 1`] = `
169
180
  "functionCall": true,
170
181
  "id": "gpt-4-0125-preview",
171
182
  "maxOutput": undefined,
183
+ "pricing": undefined,
172
184
  "reasoning": false,
173
185
  "releasedAt": "2024-01-23",
174
186
  "type": "chat",
@@ -182,6 +194,7 @@ exports[`LobeOpenAI > models > should get models 1`] = `
182
194
  "functionCall": true,
183
195
  "id": "gpt-4-turbo-preview",
184
196
  "maxOutput": undefined,
197
+ "pricing": undefined,
185
198
  "reasoning": false,
186
199
  "releasedAt": "2024-01-23",
187
200
  "type": "chat",
@@ -195,6 +208,7 @@ exports[`LobeOpenAI > models > should get models 1`] = `
195
208
  "functionCall": false,
196
209
  "id": "gpt-3.5-turbo-instruct",
197
210
  "maxOutput": undefined,
211
+ "pricing": undefined,
198
212
  "reasoning": false,
199
213
  "releasedAt": "2023-08-24",
200
214
  "type": "chat",
@@ -208,6 +222,7 @@ exports[`LobeOpenAI > models > should get models 1`] = `
208
222
  "functionCall": false,
209
223
  "id": "gpt-3.5-turbo-0301",
210
224
  "maxOutput": undefined,
225
+ "pricing": undefined,
211
226
  "reasoning": false,
212
227
  "releasedAt": "2023-03-01",
213
228
  "type": "chat",
@@ -221,6 +236,7 @@ exports[`LobeOpenAI > models > should get models 1`] = `
221
236
  "functionCall": false,
222
237
  "id": "gpt-3.5-turbo-0613",
223
238
  "maxOutput": undefined,
239
+ "pricing": undefined,
224
240
  "reasoning": false,
225
241
  "releasedAt": "2023-06-12",
226
242
  "type": "chat",
@@ -234,6 +250,7 @@ exports[`LobeOpenAI > models > should get models 1`] = `
234
250
  "functionCall": false,
235
251
  "id": "tts-1",
236
252
  "maxOutput": undefined,
253
+ "pricing": undefined,
237
254
  "reasoning": false,
238
255
  "releasedAt": "2023-04-19",
239
256
  "type": "tts",
@@ -260,6 +277,7 @@ exports[`LobeOpenAI > models > should get models 1`] = `
260
277
  ],
261
278
  },
262
279
  },
280
+ "pricing": undefined,
263
281
  "reasoning": false,
264
282
  "releasedAt": "2023-10-31",
265
283
  "type": "image",
@@ -273,6 +291,7 @@ exports[`LobeOpenAI > models > should get models 1`] = `
273
291
  "functionCall": true,
274
292
  "id": "gpt-3.5-turbo-1106",
275
293
  "maxOutput": undefined,
294
+ "pricing": undefined,
276
295
  "reasoning": false,
277
296
  "releasedAt": "2023-11-02",
278
297
  "type": "chat",
@@ -286,6 +305,7 @@ exports[`LobeOpenAI > models > should get models 1`] = `
286
305
  "functionCall": true,
287
306
  "id": "gpt-4-1106-preview",
288
307
  "maxOutput": undefined,
308
+ "pricing": undefined,
289
309
  "reasoning": false,
290
310
  "releasedAt": "2023-11-02",
291
311
  "type": "chat",
@@ -299,6 +319,7 @@ exports[`LobeOpenAI > models > should get models 1`] = `
299
319
  "functionCall": false,
300
320
  "id": "babbage-002",
301
321
  "maxOutput": undefined,
322
+ "pricing": undefined,
302
323
  "reasoning": false,
303
324
  "releasedAt": "2023-08-21",
304
325
  "type": "chat",
@@ -312,6 +333,7 @@ exports[`LobeOpenAI > models > should get models 1`] = `
312
333
  "functionCall": false,
313
334
  "id": "tts-1-1106",
314
335
  "maxOutput": undefined,
336
+ "pricing": undefined,
315
337
  "reasoning": false,
316
338
  "releasedAt": "2023-11-03",
317
339
  "type": "chat",
@@ -325,6 +347,7 @@ exports[`LobeOpenAI > models > should get models 1`] = `
325
347
  "functionCall": false,
326
348
  "id": "gpt-4-vision-preview",
327
349
  "maxOutput": undefined,
350
+ "pricing": undefined,
328
351
  "reasoning": false,
329
352
  "releasedAt": "2023-11-02",
330
353
  "type": "chat",
@@ -338,6 +361,7 @@ exports[`LobeOpenAI > models > should get models 1`] = `
338
361
  "functionCall": false,
339
362
  "id": "text-embedding-3-small",
340
363
  "maxOutput": undefined,
364
+ "pricing": undefined,
341
365
  "reasoning": false,
342
366
  "releasedAt": "2024-01-22",
343
367
  "type": "embedding",
@@ -351,6 +375,7 @@ exports[`LobeOpenAI > models > should get models 1`] = `
351
375
  "functionCall": true,
352
376
  "id": "gpt-4",
353
377
  "maxOutput": undefined,
378
+ "pricing": undefined,
354
379
  "reasoning": false,
355
380
  "releasedAt": "2023-06-27",
356
381
  "type": "chat",
@@ -364,6 +389,7 @@ exports[`LobeOpenAI > models > should get models 1`] = `
364
389
  "functionCall": false,
365
390
  "id": "text-embedding-ada-002",
366
391
  "maxOutput": undefined,
392
+ "pricing": undefined,
367
393
  "reasoning": false,
368
394
  "releasedAt": "2022-12-16",
369
395
  "type": "embedding",
@@ -377,6 +403,7 @@ exports[`LobeOpenAI > models > should get models 1`] = `
377
403
  "functionCall": true,
378
404
  "id": "gpt-3.5-turbo-0125",
379
405
  "maxOutput": undefined,
406
+ "pricing": undefined,
380
407
  "reasoning": false,
381
408
  "releasedAt": "2024-01-23",
382
409
  "type": "chat",
@@ -390,6 +417,7 @@ exports[`LobeOpenAI > models > should get models 1`] = `
390
417
  "functionCall": true,
391
418
  "id": "gpt-4-0613",
392
419
  "maxOutput": undefined,
420
+ "pricing": undefined,
393
421
  "reasoning": false,
394
422
  "releasedAt": "2023-06-12",
395
423
  "type": "chat",
@@ -2,9 +2,6 @@
2
2
  import OpenAI from 'openai';
3
3
  import { Mock, afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
4
4
 
5
- // 引入模块以便于对函数进行spy
6
- import { ChatStreamCallbacks } from '@/libs/model-runtime';
7
-
8
5
  import * as debugStreamModule from '../utils/debugStream';
9
6
  import officalOpenAIModels from './fixtures/openai-models.json';
10
7
  import { LobeOpenAI } from './index';
@@ -1,5 +1,51 @@
1
1
  // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
2
 
3
+ exports[`LobeOpenRouterAI > chat > should handle fetch error gracefully 1`] = `
4
+ [
5
+ {
6
+ "contextWindowTokens": 131072,
7
+ "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.
8
+
9
+ The model was trained on synthetic data.
10
+
11
+ _These are free, rate-limited endpoints for [Reflection 70B](/models/mattshumer/reflection-70b). Outputs may be cached. Read about rate limits [here](/docs/limits)._",
12
+ "displayName": "Reflection 70B",
13
+ "enabled": false,
14
+ "functionCall": false,
15
+ "id": "mattshumer/reflection-70b:free",
16
+ "maxOutput": undefined,
17
+ "pricing": undefined,
18
+ "reasoning": false,
19
+ "releasedAt": "1970-01-20",
20
+ "type": "chat",
21
+ "vision": false,
22
+ },
23
+ ]
24
+ `;
25
+
26
+ exports[`LobeOpenRouterAI > chat > should handle fetch failure gracefully 1`] = `
27
+ [
28
+ {
29
+ "contextWindowTokens": 131072,
30
+ "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.
31
+
32
+ The model was trained on synthetic data.
33
+
34
+ _These are free, rate-limited endpoints for [Reflection 70B](/models/mattshumer/reflection-70b). Outputs may be cached. Read about rate limits [here](/docs/limits)._",
35
+ "displayName": "Reflection 70B",
36
+ "enabled": false,
37
+ "functionCall": false,
38
+ "id": "mattshumer/reflection-70b:free",
39
+ "maxOutput": undefined,
40
+ "pricing": undefined,
41
+ "reasoning": false,
42
+ "releasedAt": "1970-01-20",
43
+ "type": "chat",
44
+ "vision": false,
45
+ },
46
+ ]
47
+ `;
48
+
3
49
  exports[`LobeOpenRouterAI > models > should get models with frontend models data 1`] = `
4
50
  [
5
51
  {
@@ -4,7 +4,6 @@ import { Mock, afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
4
4
  import { LobeOpenAICompatibleRuntime } from '@/libs/model-runtime';
5
5
  import { testProvider } from '@/libs/model-runtime/providerTestUtils';
6
6
 
7
- import frontendModels from './fixtures/frontendModels.json';
8
7
  import models from './fixtures/models.json';
9
8
  import { LobeOpenRouterAI } from './index';
10
9
 
@@ -30,11 +29,17 @@ let instance: LobeOpenAICompatibleRuntime;
30
29
  beforeEach(() => {
31
30
  instance = new LobeOpenRouterAI({ apiKey: 'test' });
32
31
 
33
- // 使用 vi.spyOn 来模拟 chat.completions.create 方法
34
- vi.spyOn(instance['client'].chat.completions, 'create').mockResolvedValue(
35
- new ReadableStream() as any,
36
- );
37
- vi.spyOn(instance['client'].models, 'list').mockResolvedValue({ data: [] } as any);
32
+ // 用一个完整的假 client 覆盖实例的 client,避免访问深层属性时出现初始化顺序问题
33
+ instance['client'] = {
34
+ chat: {
35
+ completions: {
36
+ create: vi.fn().mockResolvedValue(Promise.resolve(new ReadableStream())),
37
+ },
38
+ },
39
+ models: {
40
+ list: vi.fn().mockResolvedValue({ data: [] }),
41
+ },
42
+ } as any;
38
43
  });
39
44
 
40
45
  afterEach(() => {
@@ -44,12 +49,12 @@ afterEach(() => {
44
49
  describe('LobeOpenRouterAI', () => {
45
50
  describe('init', () => {
46
51
  it('should correctly initialize with a custom base URL', async () => {
47
- const instance = new LobeOpenRouterAI({
52
+ const inst = new LobeOpenRouterAI({
48
53
  apiKey: 'test_api_key',
49
54
  baseURL: 'https://api.abc.com/v1',
50
55
  });
51
- expect(instance).toBeInstanceOf(LobeOpenRouterAI);
52
- expect(instance.baseURL).toEqual('https://api.abc.com/v1');
56
+ expect(inst).toBeInstanceOf(LobeOpenRouterAI);
57
+ expect(inst.baseURL).toEqual('https://api.abc.com/v1');
53
58
  });
54
59
  });
55
60
 
@@ -135,35 +140,6 @@ describe('LobeOpenRouterAI', () => {
135
140
  );
136
141
  expect(result).toBeInstanceOf(Response);
137
142
  });
138
- });
139
-
140
- describe('models', () => {
141
- it('should get models with frontend models data', async () => {
142
- // mock the models.list method
143
- (instance['client'].models.list as Mock).mockResolvedValue({ data: models });
144
-
145
- // 模拟成功的 fetch 响应
146
- vi.stubGlobal(
147
- 'fetch',
148
- vi.fn().mockResolvedValue({
149
- ok: true,
150
- json: vi.fn().mockResolvedValue(frontendModels),
151
- }),
152
- );
153
-
154
- const list = await instance.models();
155
-
156
- // 验证 fetch 被正确调用
157
- expect(fetch).toHaveBeenCalledWith('https://openrouter.ai/api/frontend/models');
158
-
159
- // 验证模型列表中包含了从前端 API 获取的额外信息
160
- const reflectionModel = list.find((model) => model.id === 'mattshumer/reflection-70b:free');
161
- expect(reflectionModel).toBeDefined();
162
- expect(reflectionModel?.reasoning).toBe(true);
163
- expect(reflectionModel?.functionCall).toBe(true);
164
-
165
- expect(list).toMatchSnapshot();
166
- });
167
143
 
168
144
  it('should handle fetch failure gracefully', async () => {
169
145
  // mock the models.list method
@@ -179,10 +155,10 @@ describe('LobeOpenRouterAI', () => {
179
155
 
180
156
  const list = await instance.models();
181
157
 
182
- // 验证即使 fetch 失败,方法仍然能返回有效的模型列表
158
+ // 验证在当前实现中,当 frontend fetch 返回非 ok 时,会返回空列表
183
159
  expect(fetch).toHaveBeenCalledWith('https://openrouter.ai/api/frontend/models');
184
- expect(list.length).toBeGreaterThan(0); // 确保返回了模型列表
185
- expect(list).toMatchSnapshot();
160
+ expect(list.length).toBe(0);
161
+ expect(list).toEqual([]);
186
162
  });
187
163
 
188
164
  it('should handle fetch error gracefully', async () => {
@@ -190,15 +166,15 @@ describe('LobeOpenRouterAI', () => {
190
166
  (instance['client'].models.list as Mock).mockResolvedValue({ data: models });
191
167
 
192
168
  // 在测试环境中,需要先修改 fetch 的实现,确保错误被捕获
193
- vi.spyOn(global, 'fetch').mockImplementation(() => {
169
+ vi.spyOn(globalThis, 'fetch').mockImplementation(() => {
194
170
  throw new Error('Network error');
195
171
  });
196
172
 
197
173
  const list = await instance.models();
198
174
 
199
- // 验证即使 fetch 出错,方法仍然能返回有效的模型列表
200
- expect(list.length).toBeGreaterThan(0); // 确保返回了模型列表
201
- expect(list).toMatchSnapshot();
175
+ // 验证在当前实现中,当 frontend fetch 抛错时,会返回空列表
176
+ expect(list.length).toBe(0);
177
+ expect(list).toEqual([]);
202
178
  });
203
179
  });
204
180
  });
@@ -1,9 +1,9 @@
1
- import OpenRouterModels from '@/config/aiModels/openrouter';
1
+ import { openrouter as OpenRouterModels } from 'model-bank';
2
2
 
3
3
  import { ModelProvider } from '../types';
4
4
  import { processMultiProviderModelList } from '../utils/modelParse';
5
5
  import { createOpenAICompatibleRuntime } from '../utils/openaiCompatibleFactory';
6
- import { OpenRouterModelCard, OpenRouterModelExtraInfo, OpenRouterReasoning } from './type';
6
+ import { OpenRouterModelCard, OpenRouterReasoning } from './type';
7
7
 
8
8
  const formatPrice = (price: string) => {
9
9
  if (price === '-1') return undefined;
@@ -55,48 +55,45 @@ export const LobeOpenRouterAI = createOpenAICompatibleRuntime({
55
55
  debug: {
56
56
  chatCompletion: () => process.env.DEBUG_OPENROUTER_CHAT_COMPLETION === '1',
57
57
  },
58
- models: async ({ client }) => {
59
- const modelsPage = (await client.models.list()) as any;
60
- const modelList: OpenRouterModelCard[] = modelsPage.data;
58
+ models: async () => {
59
+ let modelList: OpenRouterModelCard[] = [];
61
60
 
62
- const modelsExtraInfo: OpenRouterModelExtraInfo[] = [];
63
61
  try {
64
62
  const response = await fetch('https://openrouter.ai/api/frontend/models');
65
63
  if (response.ok) {
66
64
  const data = await response.json();
67
- modelsExtraInfo.push(...data['data']);
65
+ modelList = data['data'];
68
66
  }
69
67
  } catch (error) {
70
68
  console.error('Failed to fetch OpenRouter frontend models:', error);
69
+ return [];
71
70
  }
72
71
 
73
- // 先处理抓取的模型信息,转换为标准格式
72
+ // 处理前端获取的模型信息,转换为标准格式
74
73
  const formattedModels = modelList.map((model) => {
75
- const extraInfo = modelsExtraInfo.find(
76
- (m) => m.slug.toLowerCase() === model.id.toLowerCase(),
77
- );
74
+ const displayName = model.slug?.toLowerCase().includes('deepseek')
75
+ ? (model.name ?? model.slug)
76
+ : (model.short_name ?? model.name ?? model.slug);
78
77
 
79
78
  return {
80
79
  contextWindowTokens: model.context_length,
81
80
  description: model.description,
82
- displayName: model.name,
83
- functionCall:
84
- model.description.includes('function calling') ||
85
- model.description.includes('tools') ||
86
- extraInfo?.endpoint?.supports_tool_parameters ||
87
- false,
88
- id: model.id,
81
+ displayName,
82
+ functionCall: model.endpoint?.supports_tool_parameters || false,
83
+ id: model.slug,
89
84
  maxOutput:
90
- typeof model.top_provider.max_completion_tokens === 'number'
91
- ? model.top_provider.max_completion_tokens
85
+ typeof model.endpoint?.max_completion_tokens === 'number'
86
+ ? model.endpoint.max_completion_tokens
92
87
  : undefined,
93
88
  pricing: {
94
- input: formatPrice(model.pricing.prompt),
95
- output: formatPrice(model.pricing.completion),
89
+ input: formatPrice(model.endpoint?.pricing?.prompt),
90
+ output: formatPrice(model.endpoint?.pricing?.completion),
96
91
  },
97
- reasoning: extraInfo?.endpoint?.supports_reasoning || false,
98
- releasedAt: new Date(model.created * 1000).toISOString().split('T')[0],
99
- vision: model.architecture.modality.includes('image') || false,
92
+ reasoning: model.endpoint?.supports_reasoning || false,
93
+ releasedAt: new Date(model.created_at).toISOString().split('T')[0],
94
+ vision:
95
+ (Array.isArray(model.input_modalities) && model.input_modalities.includes('image')) ||
96
+ false,
100
97
  };
101
98
  });
102
99
 
@@ -5,39 +5,27 @@ interface ModelPricing {
5
5
  request: string;
6
6
  }
7
7
 
8
- interface ModelArchitecture {
9
- instruct_type: string | null;
10
- modality: string;
11
- tokenizer: string;
12
- }
13
-
14
- interface ModelTopProvider {
15
- is_moderated: boolean;
16
- max_completion_tokens: number | null;
17
- }
18
-
19
8
  export interface OpenRouterModelCard {
20
- architecture: ModelArchitecture;
21
9
  context_length: number;
22
- created: number;
23
- description: string;
24
- id: string;
25
- name: string;
26
- per_request_limits: any | null;
27
- pricing: ModelPricing;
28
- top_provider: ModelTopProvider;
10
+ created_at: number;
11
+ description?: string;
12
+ endpoint: OpenRouterModelEndpoint;
13
+ input_modalities?: string[];
14
+ name?: string;
15
+ output_modalities?: string[];
16
+ per_request_limits?: any | null;
17
+ short_name?: string;
18
+ slug: string;
29
19
  }
30
20
 
31
21
  interface OpenRouterModelEndpoint {
22
+ max_completion_tokens: number | null;
23
+ pricing: ModelPricing;
24
+ supported_parameters: string[];
32
25
  supports_reasoning?: boolean;
33
26
  supports_tool_parameters?: boolean;
34
27
  }
35
28
 
36
- export interface OpenRouterModelExtraInfo {
37
- endpoint?: OpenRouterModelEndpoint;
38
- slug: string;
39
- }
40
-
41
29
  interface OpenRouterOpenAIReasoning {
42
30
  effort: 'high' | 'medium' | 'low';
43
31
  exclude?: boolean;
@@ -15,7 +15,7 @@ export const LobePPIOAI = createOpenAICompatibleRuntime({
15
15
  chatCompletion: () => process.env.DEBUG_PPIO_CHAT_COMPLETION === '1',
16
16
  },
17
17
  models: async ({ client }) => {
18
- const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels');
18
+ const { LOBE_DEFAULT_MODEL_LIST } = await import('model-bank');
19
19
 
20
20
  const reasoningKeywords = ['deepseek-r1'];
21
21
 
@@ -37,7 +37,7 @@ export const LobeSearch1API = createOpenAICompatibleRuntime({
37
37
  chatCompletion: () => process.env.DEBUG_SEARCH1API_CHAT_COMPLETION === '1',
38
38
  },
39
39
  models: async ({ client }) => {
40
- const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels');
40
+ const { LOBE_DEFAULT_MODEL_LIST } = await import('model-bank');
41
41
 
42
42
  const modelsPage = (await client.models.list()) as any;
43
43
  const modelList: Search1APIModelCard[] = modelsPage.data;
@@ -54,7 +54,7 @@ export const LobeSenseNovaAI = createOpenAICompatibleRuntime({
54
54
  chatCompletion: () => process.env.DEBUG_SENSENOVA_CHAT_COMPLETION === '1',
55
55
  },
56
56
  models: async ({ client }) => {
57
- const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels');
57
+ const { LOBE_DEFAULT_MODEL_LIST } = await import('model-bank');
58
58
 
59
59
  const functionCallKeywords = ['1202'];
60
60
 
@@ -36,7 +36,7 @@ export const LobeStepfunAI = createOpenAICompatibleRuntime({
36
36
  chatCompletion: () => process.env.DEBUG_STEPFUN_CHAT_COMPLETION === '1',
37
37
  },
38
38
  models: async ({ client }) => {
39
- const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels');
39
+ const { LOBE_DEFAULT_MODEL_LIST } = await import('model-bank');
40
40
 
41
41
  // ref: https://platform.stepfun.com/docs/llm/modeloverview
42
42
  const functionCallKeywords = ['step-1-', 'step-1o-', 'step-1v-', 'step-2-'];
@@ -13,7 +13,7 @@ export const LobeTencentCloudAI = createOpenAICompatibleRuntime({
13
13
  chatCompletion: () => process.env.DEBUG_TENCENT_CLOUD_CHAT_COMPLETION === '1',
14
14
  },
15
15
  models: async ({ client }) => {
16
- const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels');
16
+ const { LOBE_DEFAULT_MODEL_LIST } = await import('model-bank');
17
17
 
18
18
  const reasoningKeywords = ['deepseek-r1'];
19
19
 
@@ -16,7 +16,7 @@ export const LobeTogetherAI = createOpenAICompatibleRuntime({
16
16
  chatCompletion: () => process.env.DEBUG_TOGETHERAI_CHAT_COMPLETION === '1',
17
17
  },
18
18
  models: async ({ client }) => {
19
- const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels');
19
+ const { LOBE_DEFAULT_MODEL_LIST } = await import('model-bank');
20
20
 
21
21
  const visionKeywords = ['qvq', 'vision'];
22
22
 
@@ -1,4 +1,4 @@
1
- import { RuntimeImageGenParams } from '@/libs/standard-parameters/index';
1
+ import { RuntimeImageGenParams } from 'model-bank';
2
2
 
3
3
  export type CreateImagePayload = {
4
4
  model: string;
@@ -98,7 +98,7 @@ const mockDefaultModelList: (Partial<ChatModelCard> & { id: string })[] = [
98
98
  ];
99
99
 
100
100
  // Mock the import
101
- vi.mock('@/config/aiModels', () => ({
101
+ vi.mock('model-bank', () => ({
102
102
  LOBE_DEFAULT_MODEL_LIST: mockDefaultModelList,
103
103
  }));
104
104
 
@@ -394,7 +394,7 @@ describe('modelParse', () => {
394
394
  reasoning: true,
395
395
  },
396
396
  };
397
- const mockModule = await import('@/config/aiModels');
397
+ const mockModule = await import('model-bank');
398
398
  mockModule.LOBE_DEFAULT_MODEL_LIST.push(tempMockEntry as any);
399
399
 
400
400
  const modelList = [{ id: modelId }];
@@ -481,7 +481,7 @@ describe('modelParse', () => {
481
481
  contextWindowTokens: 5000,
482
482
  };
483
483
 
484
- const mockModule = await import('@/config/aiModels');
484
+ const mockModule = await import('model-bank');
485
485
  mockModule.LOBE_DEFAULT_MODEL_LIST.push(tempModelEntry as any);
486
486
 
487
487
  const config = MODEL_LIST_CONFIGS.openai;
@@ -706,7 +706,7 @@ describe('modelParse', () => {
706
706
  reasoning: true,
707
707
  },
708
708
  };
709
- const mockModule = await import('@/config/aiModels');
709
+ const mockModule = await import('model-bank');
710
710
  mockModule.LOBE_DEFAULT_MODEL_LIST.push(tempMockEntry as any);
711
711
 
712
712
  const modelList = [{ id: modelId }];
@@ -735,7 +735,7 @@ describe('modelParse', () => {
735
735
  reasoning: false,
736
736
  },
737
737
  };
738
- const mockModule = await import('@/config/aiModels');
738
+ const mockModule = await import('model-bank');
739
739
  mockModule.LOBE_DEFAULT_MODEL_LIST.push(tempMockEntry as any);
740
740
 
741
741
  const modelList = [{ id: modelId }];