@lobehub/chat 1.136.12 → 1.136.13

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 (83) hide show
  1. package/.github/workflows/claude-translator.yml +13 -1
  2. package/CHANGELOG.md +34 -0
  3. package/changelog/v1.json +12 -0
  4. package/locales/ar/modelProvider.json +12 -0
  5. package/locales/ar/models.json +39 -24
  6. package/locales/bg-BG/modelProvider.json +12 -0
  7. package/locales/bg-BG/models.json +39 -24
  8. package/locales/de-DE/modelProvider.json +12 -0
  9. package/locales/de-DE/models.json +39 -24
  10. package/locales/en-US/modelProvider.json +12 -0
  11. package/locales/en-US/models.json +39 -24
  12. package/locales/es-ES/modelProvider.json +12 -0
  13. package/locales/es-ES/models.json +39 -24
  14. package/locales/fa-IR/modelProvider.json +12 -0
  15. package/locales/fa-IR/models.json +39 -24
  16. package/locales/fr-FR/modelProvider.json +12 -0
  17. package/locales/fr-FR/models.json +39 -24
  18. package/locales/it-IT/modelProvider.json +12 -0
  19. package/locales/it-IT/models.json +39 -24
  20. package/locales/ja-JP/modelProvider.json +12 -0
  21. package/locales/ja-JP/models.json +39 -24
  22. package/locales/ko-KR/modelProvider.json +12 -0
  23. package/locales/ko-KR/models.json +39 -24
  24. package/locales/nl-NL/modelProvider.json +12 -0
  25. package/locales/nl-NL/models.json +39 -24
  26. package/locales/pl-PL/modelProvider.json +12 -0
  27. package/locales/pl-PL/models.json +39 -24
  28. package/locales/pt-BR/modelProvider.json +12 -0
  29. package/locales/pt-BR/models.json +39 -24
  30. package/locales/ru-RU/modelProvider.json +12 -0
  31. package/locales/ru-RU/models.json +39 -24
  32. package/locales/tr-TR/modelProvider.json +12 -0
  33. package/locales/tr-TR/models.json +39 -24
  34. package/locales/vi-VN/modelProvider.json +12 -0
  35. package/locales/vi-VN/models.json +39 -24
  36. package/locales/zh-CN/modelProvider.json +12 -0
  37. package/locales/zh-CN/models.json +39 -24
  38. package/locales/zh-TW/modelProvider.json +12 -0
  39. package/locales/zh-TW/models.json +39 -24
  40. package/package.json +3 -3
  41. package/packages/const/src/settings/index.ts +1 -0
  42. package/packages/database/package.json +7 -5
  43. package/packages/electron-client-ipc/src/events/index.ts +2 -2
  44. package/packages/electron-client-ipc/src/events/{localFile.ts → localSystem.ts} +25 -6
  45. package/packages/electron-client-ipc/src/types/index.ts +1 -1
  46. package/packages/electron-client-ipc/src/types/{localFile.ts → localSystem.ts} +89 -4
  47. package/packages/file-loaders/package.json +1 -2
  48. package/packages/file-loaders/src/loadFile.ts +4 -1
  49. package/packages/file-loaders/src/loaders/doc/__snapshots__/index.test.ts.snap +46 -0
  50. package/packages/file-loaders/src/loaders/doc/index.test.ts +38 -0
  51. package/packages/file-loaders/src/loaders/doc/index.ts +57 -0
  52. package/packages/file-loaders/src/loaders/docx/index.ts +36 -45
  53. package/packages/file-loaders/src/loaders/index.ts +2 -0
  54. package/packages/file-loaders/src/types/word-extractor.d.ts +9 -0
  55. package/packages/file-loaders/src/types.ts +1 -1
  56. package/packages/model-runtime/src/core/openaiCompatibleFactory/index.test.ts +267 -38
  57. package/packages/model-runtime/src/core/openaiCompatibleFactory/index.ts +45 -0
  58. package/packages/model-runtime/src/providerTestUtils.ts +0 -5
  59. package/packages/model-runtime/src/providers/anthropic/generateObject.test.ts +57 -44
  60. package/packages/model-runtime/src/providers/anthropic/generateObject.ts +28 -20
  61. package/packages/model-runtime/src/providers/deepseek/index.ts +5 -0
  62. package/packages/model-runtime/src/providers/openai/index.test.ts +0 -5
  63. package/packages/model-runtime/src/providers/openrouter/index.test.ts +3 -3
  64. package/packages/model-runtime/src/providers/openrouter/index.ts +32 -20
  65. package/packages/model-runtime/src/providers/openrouter/type.ts +25 -24
  66. package/packages/model-runtime/src/providers/zhipu/index.test.ts +0 -1
  67. package/packages/model-runtime/src/types/structureOutput.ts +13 -1
  68. package/packages/model-runtime/src/utils/handleOpenAIError.test.ts +0 -5
  69. package/packages/model-runtime/src/utils/handleOpenAIError.ts +2 -2
  70. package/packages/types/src/aiChat.ts +13 -1
  71. package/packages/types/src/index.ts +1 -0
  72. package/src/features/ChatInput/InputEditor/index.tsx +39 -26
  73. package/src/features/Conversation/Messages/Assistant/Tool/Render/LoadingPlaceholder/index.tsx +1 -1
  74. package/src/server/routers/lambda/agent.ts +2 -3
  75. package/src/server/routers/lambda/aiChat.ts +33 -1
  76. package/src/server/routers/lambda/chunk.ts +2 -2
  77. package/src/services/electron/file.ts +1 -2
  78. package/src/services/electron/localFileService.ts +40 -0
  79. package/src/tools/local-system/Placeholder/ListFiles.tsx +23 -0
  80. package/src/tools/local-system/Placeholder/ReadLocalFile.tsx +9 -0
  81. package/src/tools/local-system/Placeholder/SearchFiles.tsx +55 -0
  82. package/src/tools/local-system/Placeholder/index.tsx +25 -0
  83. package/src/tools/placeholders.ts +3 -0
@@ -12,7 +12,7 @@ describe('Anthropic generateObject', () => {
12
12
  content: [
13
13
  {
14
14
  type: 'tool_use',
15
- name: 'structured_output',
15
+ name: 'person_extractor',
16
16
  input: { name: 'John', age: 30 },
17
17
  },
18
18
  ],
@@ -23,8 +23,13 @@ describe('Anthropic generateObject', () => {
23
23
  const payload = {
24
24
  messages: [{ content: 'Generate a person object', role: 'user' as const }],
25
25
  schema: {
26
- type: 'object',
27
- properties: { name: { type: 'string' }, age: { type: 'number' } },
26
+ name: 'person_extractor',
27
+ description: 'Extract person information',
28
+ schema: {
29
+ type: 'object' as const,
30
+ properties: { name: { type: 'string' }, age: { type: 'number' } },
31
+ required: ['name', 'age'],
32
+ },
28
33
  },
29
34
  model: 'claude-3-5-sonnet-20241022',
30
35
  };
@@ -35,30 +40,24 @@ describe('Anthropic generateObject', () => {
35
40
  expect.objectContaining({
36
41
  model: 'claude-3-5-sonnet-20241022',
37
42
  max_tokens: 8192,
38
- messages: [
39
- { content: 'Generate a person object', role: 'user' },
40
- {
41
- content:
42
- 'Please use the structured_output tool to provide your response in the required format.',
43
- role: 'user',
44
- },
45
- ],
43
+ messages: [{ content: 'Generate a person object', role: 'user' }],
46
44
  tools: [
47
45
  {
48
- name: 'structured_output',
49
- description: 'Generate structured output according to the provided schema',
46
+ name: 'person_extractor',
47
+ description: 'Extract person information',
50
48
  input_schema: {
51
49
  type: 'object',
52
50
  properties: {
53
51
  name: { type: 'string' },
54
52
  age: { type: 'number' },
55
53
  },
54
+ required: ['name', 'age'],
56
55
  },
57
56
  },
58
57
  ],
59
58
  tool_choice: {
60
59
  type: 'tool',
61
- name: 'structured_output',
60
+ name: 'person_extractor',
62
61
  },
63
62
  }),
64
63
  expect.objectContaining({}),
@@ -74,7 +73,7 @@ describe('Anthropic generateObject', () => {
74
73
  content: [
75
74
  {
76
75
  type: 'tool_use',
77
- name: 'structured_output',
76
+ name: 'status_extractor',
78
77
  input: { status: 'success' },
79
78
  },
80
79
  ],
@@ -87,7 +86,10 @@ describe('Anthropic generateObject', () => {
87
86
  { content: 'You are a helpful assistant', role: 'system' as const },
88
87
  { content: 'Generate status', role: 'user' as const },
89
88
  ],
90
- schema: { type: 'object', properties: { status: { type: 'string' } } },
89
+ schema: {
90
+ name: 'status_extractor',
91
+ schema: { type: 'object' as const, properties: { status: { type: 'string' } } },
92
+ },
91
93
  model: 'claude-3-5-sonnet-20241022',
92
94
  };
93
95
 
@@ -111,7 +113,7 @@ describe('Anthropic generateObject', () => {
111
113
  content: [
112
114
  {
113
115
  type: 'tool_use',
114
- name: 'structured_output',
116
+ name: 'data_extractor',
115
117
  input: { data: 'test' },
116
118
  },
117
119
  ],
@@ -121,7 +123,10 @@ describe('Anthropic generateObject', () => {
121
123
 
122
124
  const payload = {
123
125
  messages: [{ content: 'Generate data', role: 'user' as const }],
124
- schema: { type: 'object', properties: { data: { type: 'string' } } },
126
+ schema: {
127
+ name: 'data_extractor',
128
+ schema: { type: 'object' as const, properties: { data: { type: 'string' } } },
129
+ },
125
130
  model: 'claude-3-5-sonnet-20241022',
126
131
  };
127
132
 
@@ -155,20 +160,18 @@ describe('Anthropic generateObject', () => {
155
160
  },
156
161
  };
157
162
 
158
- const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
159
-
160
163
  const payload = {
161
164
  messages: [{ content: 'Generate data', role: 'user' as const }],
162
- schema: { type: 'object' },
165
+ schema: {
166
+ name: 'test_tool',
167
+ schema: { type: 'object' },
168
+ },
163
169
  model: 'claude-3-5-sonnet-20241022',
164
170
  };
165
171
 
166
- const result = await createAnthropicGenerateObject(mockClient as any, payload);
172
+ const result = await createAnthropicGenerateObject(mockClient as any, payload as any);
167
173
 
168
- expect(consoleSpy).toHaveBeenCalledWith('No structured output tool use found in response');
169
174
  expect(result).toBeUndefined();
170
-
171
- consoleSpy.mockRestore();
172
175
  });
173
176
 
174
177
  it('should handle complex nested schemas', async () => {
@@ -178,7 +181,7 @@ describe('Anthropic generateObject', () => {
178
181
  content: [
179
182
  {
180
183
  type: 'tool_use',
181
- name: 'structured_output',
184
+ name: 'user_extractor',
182
185
  input: {
183
186
  user: {
184
187
  name: 'Alice',
@@ -200,22 +203,26 @@ describe('Anthropic generateObject', () => {
200
203
  const payload = {
201
204
  messages: [{ content: 'Generate complex user data', role: 'user' as const }],
202
205
  schema: {
203
- type: 'object',
204
- properties: {
205
- user: {
206
- type: 'object',
207
- properties: {
208
- name: { type: 'string' },
209
- profile: {
210
- type: 'object',
211
- properties: {
212
- age: { type: 'number' },
213
- preferences: { type: 'array', items: { type: 'string' } },
206
+ name: 'user_extractor',
207
+ description: 'Extract complex user information',
208
+ schema: {
209
+ type: 'object' as const,
210
+ properties: {
211
+ user: {
212
+ type: 'object',
213
+ properties: {
214
+ name: { type: 'string' },
215
+ profile: {
216
+ type: 'object',
217
+ properties: {
218
+ age: { type: 'number' },
219
+ preferences: { type: 'array', items: { type: 'string' } },
220
+ },
214
221
  },
215
222
  },
216
223
  },
224
+ metadata: { type: 'object' },
217
225
  },
218
- metadata: { type: 'object' },
219
226
  },
220
227
  },
221
228
  model: 'claude-3-5-sonnet-20241022',
@@ -248,13 +255,16 @@ describe('Anthropic generateObject', () => {
248
255
 
249
256
  const payload = {
250
257
  messages: [{ content: 'Generate data', role: 'user' as const }],
251
- schema: { type: 'object' },
258
+ schema: {
259
+ name: 'test_tool',
260
+ schema: { type: 'object' },
261
+ },
252
262
  model: 'claude-3-5-sonnet-20241022',
253
263
  };
254
264
 
255
- await expect(createAnthropicGenerateObject(mockClient as any, payload)).rejects.toThrow(
256
- 'API Error: Model not found',
257
- );
265
+ await expect(
266
+ createAnthropicGenerateObject(mockClient as any, payload as any),
267
+ ).rejects.toThrow('API Error: Model not found');
258
268
  });
259
269
 
260
270
  it('should handle abort signals correctly', async () => {
@@ -269,7 +279,10 @@ describe('Anthropic generateObject', () => {
269
279
 
270
280
  const payload = {
271
281
  messages: [{ content: 'Generate data', role: 'user' as const }],
272
- schema: { type: 'object' },
282
+ schema: {
283
+ name: 'test_tool',
284
+ schema: { type: 'object' },
285
+ },
273
286
  model: 'claude-3-5-sonnet-20241022',
274
287
  };
275
288
 
@@ -278,7 +291,7 @@ describe('Anthropic generateObject', () => {
278
291
  };
279
292
 
280
293
  await expect(
281
- createAnthropicGenerateObject(mockClient as any, payload, options),
294
+ createAnthropicGenerateObject(mockClient as any, payload as any, options),
282
295
  ).rejects.toThrow();
283
296
  });
284
297
  });
@@ -1,8 +1,11 @@
1
- import Anthropic from '@anthropic-ai/sdk';
1
+ import type Anthropic from '@anthropic-ai/sdk';
2
+ import debug from 'debug';
2
3
 
3
4
  import { buildAnthropicMessages } from '../../core/contextBuilders/anthropic';
4
5
  import { GenerateObjectOptions, GenerateObjectPayload } from '../../types';
5
6
 
7
+ const log = debug('lobe-model-runtime:anthropic:generate-object');
8
+
6
9
  /**
7
10
  * Generate structured output using Anthropic Claude API with Function Calling
8
11
  */
@@ -13,26 +16,25 @@ export const createAnthropicGenerateObject = async (
13
16
  ) => {
14
17
  const { schema, messages, model } = payload;
15
18
 
16
- // Convert OpenAI schema to Anthropic tool format
17
- const tool = {
18
- description: 'Generate structured output according to the provided schema',
19
- input_schema: schema,
20
- name: 'structured_output',
19
+ log('generateObject called with model: %s', model);
20
+ log('schema: %O', schema);
21
+ log('messages count: %d', messages.length);
22
+
23
+ // Convert OpenAI-style schema to Anthropic tool format
24
+ const tool: Anthropic.ToolUnion = {
25
+ description:
26
+ schema.description || 'Generate structured output according to the provided schema',
27
+ input_schema: schema.schema as any,
28
+ name: schema.name || 'structured_output',
21
29
  };
30
+
31
+ log('converted tool: %O', tool);
22
32
  // Convert messages to Anthropic format
23
33
  const system_message = messages.find((m) => m.role === 'system');
24
34
  const user_messages = messages.filter((m) => m.role !== 'system');
25
35
  const anthropicMessages = await buildAnthropicMessages(user_messages);
26
36
 
27
- // Add instruction to use the structured output tool
28
- const enhancedMessages = [
29
- ...anthropicMessages,
30
- {
31
- content:
32
- 'Please use the structured_output tool to provide your response in the required format.',
33
- role: 'user' as const,
34
- },
35
- ];
37
+ log('converted %d messages to Anthropic format', anthropicMessages.length);
36
38
 
37
39
  const systemPrompts = system_message?.content
38
40
  ? [
@@ -44,13 +46,15 @@ export const createAnthropicGenerateObject = async (
44
46
  : undefined;
45
47
 
46
48
  try {
49
+ log('calling Anthropic API with max_tokens: %d', 8192);
50
+
47
51
  const response = await client.messages.create(
48
52
  {
49
53
  max_tokens: 8192,
50
- messages: enhancedMessages,
54
+ messages: anthropicMessages,
51
55
  model,
52
56
  system: systemPrompts,
53
- tool_choice: { name: 'structured_output', type: 'tool' },
57
+ tool_choice: { name: tool.name, type: 'tool' },
54
58
  tools: [tool],
55
59
  },
56
60
  {
@@ -58,19 +62,23 @@ export const createAnthropicGenerateObject = async (
58
62
  },
59
63
  );
60
64
 
65
+ log('received response with %d content blocks', response.content.length);
66
+ log('response: %O', response);
67
+
61
68
  // Extract the tool use result
62
69
  const toolUseBlock = response.content.find(
63
- (block) => block.type === 'tool_use' && block.name === 'structured_output',
70
+ (block) => block.type === 'tool_use' && block.name === tool.name,
64
71
  );
65
72
 
66
73
  if (!toolUseBlock || toolUseBlock.type !== 'tool_use') {
67
- console.error('No structured output tool use found in response');
74
+ log('no tool use found in response (expected tool: %s)', tool.name);
68
75
  return undefined;
69
76
  }
70
77
 
78
+ log('extracted tool input: %O', toolUseBlock.input);
71
79
  return toolUseBlock.input;
72
80
  } catch (error) {
73
- console.error('Anthropic generateObject error:', error);
81
+ log('generateObject error: %O', error);
74
82
  throw error;
75
83
  }
76
84
  };
@@ -12,6 +12,11 @@ export const LobeDeepSeekAI = createOpenAICompatibleRuntime({
12
12
  debug: {
13
13
  chatCompletion: () => process.env.DEBUG_DEEPSEEK_CHAT_COMPLETION === '1',
14
14
  },
15
+ // Deepseek don't support json format well
16
+ // use Tools calling to simulate
17
+ generateObject: {
18
+ useToolsCalling: true,
19
+ },
15
20
  models: async ({ client }) => {
16
21
  const modelsPage = (await client.models.list()) as any;
17
22
  const modelList: DeepSeekModelCard[] = modelsPage.data;
@@ -101,7 +101,6 @@ describe('LobeOpenAI', () => {
101
101
  it('should return ProviderBizError with the cause when OpenAI.APIError is thrown with cause', async () => {
102
102
  // Arrange
103
103
  const errorInfo = {
104
- stack: 'abc',
105
104
  cause: {
106
105
  message: 'api is undefined',
107
106
  },
@@ -122,7 +121,6 @@ describe('LobeOpenAI', () => {
122
121
  endpoint: 'https://api.openai.com/v1',
123
122
  error: {
124
123
  cause: { message: 'api is undefined' },
125
- stack: 'abc',
126
124
  },
127
125
  errorType: 'ProviderBizError',
128
126
  provider: 'openai',
@@ -133,7 +131,6 @@ describe('LobeOpenAI', () => {
133
131
  it('should return ProviderBizError with an cause response with desensitize Url', async () => {
134
132
  // Arrange
135
133
  const errorInfo = {
136
- stack: 'abc',
137
134
  cause: { message: 'api is undefined' },
138
135
  };
139
136
  const apiError = new OpenAI.APIError(400, errorInfo, 'module error', {});
@@ -158,7 +155,6 @@ describe('LobeOpenAI', () => {
158
155
  endpoint: 'https://api.***.com/v1',
159
156
  error: {
160
157
  cause: { message: 'api is undefined' },
161
- stack: 'abc',
162
158
  },
163
159
  errorType: 'ProviderBizError',
164
160
  provider: 'openai',
@@ -188,7 +184,6 @@ describe('LobeOpenAI', () => {
188
184
  name: genericError.name,
189
185
  cause: genericError.cause,
190
186
  message: genericError.message,
191
- stack: genericError.stack,
192
187
  },
193
188
  });
194
189
  }
@@ -21,7 +21,7 @@ testProvider({
21
21
  });
22
22
 
23
23
  // Mock the console.error to avoid polluting test output
24
- vi.spyOn(console, 'error').mockImplementation(() => {});
24
+ vi.spyOn(console, 'error').mockImplementation(() => { });
25
25
 
26
26
  let instance: LobeOpenAICompatibleRuntime;
27
27
 
@@ -154,8 +154,8 @@ describe('LobeOpenRouterAI', () => {
154
154
 
155
155
  const list = await instance.models();
156
156
 
157
- // 验证在当前实现中,当 frontend fetch 返回非 ok 时,会返回空列表
158
- expect(fetch).toHaveBeenCalledWith('https://openrouter.ai/api/frontend/models');
157
+ // 验证在当前实现中,当 model fetch 返回非 ok 时,会返回空列表
158
+ expect(fetch).toHaveBeenCalledWith('https://openrouter.ai/api/v1/models');
159
159
  expect(list.length).toBe(0);
160
160
  expect(list).toEqual([]);
161
161
  });
@@ -58,7 +58,7 @@ export const LobeOpenRouterAI = createOpenAICompatibleRuntime({
58
58
  let modelList: OpenRouterModelCard[] = [];
59
59
 
60
60
  try {
61
- const response = await fetch('https://openrouter.ai/api/frontend/models');
61
+ const response = await fetch('https://openrouter.ai/api/v1/models');
62
62
  if (response.ok) {
63
63
  const data = await response.json();
64
64
  modelList = data['data'];
@@ -70,19 +70,31 @@ export const LobeOpenRouterAI = createOpenAICompatibleRuntime({
70
70
 
71
71
  // 处理前端获取的模型信息,转换为标准格式
72
72
  const formattedModels = modelList.map((model) => {
73
- const { endpoint } = model;
74
- const endpointModel = endpoint?.model;
73
+ const { top_provider, architecture, pricing, supported_parameters } = model;
75
74
 
76
- const inputModalities = endpointModel?.input_modalities || model.input_modalities;
75
+ const inputModalities = architecture.input_modalities || [];
77
76
 
78
- let displayName = model.slug?.toLowerCase().includes('deepseek') && !model.short_name?.toLowerCase().includes('deepseek')
79
- ? (model.name ?? model.slug)
80
- : (model.short_name ?? model.name ?? model.slug);
77
+ // 处理 name,默认去除冒号及其前面的内容
78
+ let displayName = model.name;
79
+ const colonIndex = displayName.indexOf(':');
80
+ if (colonIndex !== -1) {
81
+ const prefix = displayName.substring(0, colonIndex).trim();
82
+ const suffix = displayName.substring(colonIndex + 1).trim();
81
83
 
82
- const inputPrice = formatPrice(endpoint?.pricing?.prompt);
83
- const outputPrice = formatPrice(endpoint?.pricing?.completion);
84
- const cachedInputPrice = formatPrice(endpoint?.pricing?.input_cache_read);
85
- const writeCacheInputPrice = formatPrice(endpoint?.pricing?.input_cache_write);
84
+ const isDeepSeekPrefix = prefix.toLowerCase() === 'deepseek';
85
+ const suffixHasDeepSeek = suffix.toLowerCase().includes('deepseek');
86
+
87
+ if (isDeepSeekPrefix && !suffixHasDeepSeek) {
88
+ displayName = model.name;
89
+ } else {
90
+ displayName = suffix;
91
+ }
92
+ }
93
+
94
+ const inputPrice = formatPrice(pricing.prompt);
95
+ const outputPrice = formatPrice(pricing.completion);
96
+ const cachedInputPrice = formatPrice(pricing.input_cache_read);
97
+ const writeCacheInputPrice = formatPrice(pricing.input_cache_write);
86
98
 
87
99
  const isFree = (inputPrice === 0 || outputPrice === 0) && !displayName.endsWith('(free)');
88
100
  if (isFree) {
@@ -90,14 +102,14 @@ export const LobeOpenRouterAI = createOpenAICompatibleRuntime({
90
102
  }
91
103
 
92
104
  return {
93
- contextWindowTokens: endpoint?.context_length || model.context_length,
94
- description: endpointModel?.description || model.description,
105
+ contextWindowTokens: top_provider.context_length || model.context_length,
106
+ description: model.description,
95
107
  displayName,
96
- functionCall: endpoint?.supports_tool_parameters || false,
97
- id: endpoint?.model_variant_slug || model.slug,
108
+ functionCall: supported_parameters.includes('tools'),
109
+ id: model.id,
98
110
  maxOutput:
99
- typeof endpoint?.max_completion_tokens === 'number'
100
- ? endpoint.max_completion_tokens
111
+ typeof top_provider.max_completion_tokens === 'number'
112
+ ? top_provider.max_completion_tokens
101
113
  : undefined,
102
114
  pricing: {
103
115
  input: inputPrice,
@@ -105,9 +117,9 @@ export const LobeOpenRouterAI = createOpenAICompatibleRuntime({
105
117
  writeCacheInput: writeCacheInputPrice,
106
118
  output: outputPrice,
107
119
  },
108
- reasoning: endpoint?.supports_reasoning || false,
109
- releasedAt: new Date(model.created_at).toISOString().split('T')[0],
110
- vision: Array.isArray(inputModalities) && inputModalities.includes('image'),
120
+ reasoning: supported_parameters.includes('reasoning'),
121
+ releasedAt: new Date(model.created * 1000).toISOString().split('T')[0],
122
+ vision: inputModalities.includes('image'),
111
123
  };
112
124
  });
113
125
 
@@ -5,37 +5,38 @@ interface ModelPricing {
5
5
  input_cache_write?: string;
6
6
  prompt: string;
7
7
  request?: string;
8
+ web_search?: string;
9
+ internal_reasoning?: string;
8
10
  }
9
11
 
10
- export interface OpenRouterModelCard {
12
+ interface TopProvider {
11
13
  context_length: number;
12
- created_at: number;
13
- description?: string;
14
- endpoint: OpenRouterModelEndpoint;
15
- input_modalities?: string[];
16
- name?: string;
17
- output_modalities?: string[];
18
- per_request_limits?: any | null;
19
- short_name?: string;
20
- slug: string;
14
+ max_completion_tokens: number | null;
15
+ is_moderated: boolean;
21
16
  }
22
17
 
23
- interface OpenRouterModelEndpoint {
24
- context_length?: number;
25
- max_completion_tokens: number | null;
26
- model?: {
27
- description?: string;
28
- input_modalities?: string[];
29
- name?: string;
30
- short_name?: string;
31
- slug: string;
32
- };
33
- model_variant_slug?: string;
18
+ interface Architecture {
19
+ modality: string;
20
+ input_modalities: string[];
21
+ output_modalities: string[];
22
+ tokenizer: string;
23
+ instruct_type: string | null;
24
+ }
25
+
26
+ export interface OpenRouterModelCard {
27
+ id: string;
28
+ canonical_slug: string;
29
+ hugging_face_id?: string;
30
+ name: string;
31
+ created: number;
32
+ description?: string;
33
+ context_length: number;
34
+ architecture: Architecture;
34
35
  pricing: ModelPricing;
36
+ top_provider: TopProvider;
37
+ per_request_limits?: any | null;
35
38
  supported_parameters: string[];
36
- supports_reasoning?: boolean;
37
- supports_tool_parameters?: boolean;
38
- variant?: 'free' | 'standard' | 'unknown';
39
+ default_parameters?: any | null;
39
40
  }
40
41
 
41
42
  interface OpenRouterOpenAIReasoning {
@@ -263,7 +263,6 @@ describe('LobeZhipuAI', () => {
263
263
  name: genericError.name,
264
264
  cause: genericError.cause,
265
265
  message: genericError.message,
266
- stack: genericError.stack,
267
266
  },
268
267
  });
269
268
  }
@@ -4,11 +4,23 @@ interface GenerateObjectMessage {
4
4
  role: 'user' | 'system' | 'assistant';
5
5
  }
6
6
 
7
+ interface GenerateObjectSchema {
8
+ description?: string;
9
+ name: string;
10
+ schema: {
11
+ additionalProperties?: boolean;
12
+ properties: Record<string, any>;
13
+ required?: string[];
14
+ type: 'object';
15
+ };
16
+ strict?: boolean;
17
+ }
18
+
7
19
  export interface GenerateObjectPayload {
8
20
  messages: GenerateObjectMessage[];
9
21
  model: string;
10
22
  responseApi?: boolean;
11
- schema: any;
23
+ schema: GenerateObjectSchema;
12
24
  }
13
25
 
14
26
  export interface GenerateObjectOptions {
@@ -48,7 +48,6 @@ describe('handleOpenAIError', () => {
48
48
 
49
49
  expect(result.errorResult).toEqual({
50
50
  headers: { headers, status: 401 },
51
- stack: apiError.stack,
52
51
  status: 472,
53
52
  });
54
53
  expect(result.RuntimeError).toBeUndefined();
@@ -84,7 +83,6 @@ describe('handleOpenAIError', () => {
84
83
  cause: { details: 'Error details' },
85
84
  message: 'Generic error',
86
85
  name: 'Error',
87
- stack: error.stack,
88
86
  },
89
87
  });
90
88
  });
@@ -100,7 +98,6 @@ describe('handleOpenAIError', () => {
100
98
  cause: undefined,
101
99
  message: 'Simple error',
102
100
  name: 'Error',
103
- stack: error.stack,
104
101
  },
105
102
  });
106
103
  });
@@ -122,7 +119,6 @@ describe('handleOpenAIError', () => {
122
119
  cause: undefined,
123
120
  message: 'Custom error message',
124
121
  name: 'CustomError',
125
- stack: error.stack,
126
122
  },
127
123
  });
128
124
  });
@@ -141,7 +137,6 @@ describe('handleOpenAIError', () => {
141
137
  cause: undefined,
142
138
  message: 'Object error',
143
139
  name: undefined,
144
- stack: undefined,
145
140
  },
146
141
  });
147
142
  });
@@ -20,7 +20,7 @@ export const handleOpenAIError = (
20
20
  }
21
21
  // if there is no other request error, the error object is a Response like object
22
22
  else {
23
- errorResult = { headers: error.headers, stack: error.stack, status: error.status };
23
+ errorResult = { headers: error.headers, status: error.status };
24
24
  }
25
25
 
26
26
  return {
@@ -29,7 +29,7 @@ export const handleOpenAIError = (
29
29
  } else {
30
30
  const err = error as Error;
31
31
 
32
- errorResult = { cause: err.cause, message: err.message, name: err.name, stack: err.stack };
32
+ errorResult = { cause: err.cause, message: err.message, name: err.name };
33
33
 
34
34
  return {
35
35
  RuntimeError: AgentRuntimeErrorType.AgentRuntimeError,
@@ -54,12 +54,24 @@ export interface SendMessageServerResponse {
54
54
  userMessageId: string;
55
55
  }
56
56
 
57
+ export const StructureSchema = z.object({
58
+ description: z.string().optional(),
59
+ name: z.string(),
60
+ schema: z.object({
61
+ additionalProperties: z.boolean().optional(),
62
+ properties: z.record(z.string(), z.any()),
63
+ required: z.array(z.string()).optional(),
64
+ type: z.literal('object'),
65
+ }),
66
+ strict: z.boolean().optional(),
67
+ });
68
+
57
69
  export const StructureOutputSchema = z.object({
58
70
  keyVaultsPayload: z.string(),
59
71
  messages: z.array(z.any()),
60
72
  model: z.string(),
61
73
  provider: z.string(),
62
- schema: z.any(),
74
+ schema: StructureSchema,
63
75
  });
64
76
 
65
77
  export interface StructureOutputParams {
@@ -10,6 +10,7 @@ export * from './clientDB';
10
10
  export * from './discover';
11
11
  export * from './eval';
12
12
  export * from './fetch';
13
+ export * from './files';
13
14
  export * from './hotkey';
14
15
  export * from './knowledgeBase';
15
16
  export * from './llm';