@lobehub/chat 1.138.3 → 1.138.5

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 (24) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/changelog/v1.json +18 -0
  3. package/package.json +1 -1
  4. package/packages/database/src/repositories/aiInfra/index.test.ts +656 -0
  5. package/packages/model-runtime/src/core/contextBuilders/google.test.ts +585 -0
  6. package/packages/model-runtime/src/core/contextBuilders/google.ts +201 -0
  7. package/packages/model-runtime/src/core/openaiCompatibleFactory/index.test.ts +191 -179
  8. package/packages/model-runtime/src/core/openaiCompatibleFactory/index.ts +305 -47
  9. package/packages/model-runtime/src/providers/anthropic/generateObject.test.ts +93 -84
  10. package/packages/model-runtime/src/providers/anthropic/generateObject.ts +3 -3
  11. package/packages/model-runtime/src/providers/google/generateObject.test.ts +588 -83
  12. package/packages/model-runtime/src/providers/google/generateObject.ts +104 -6
  13. package/packages/model-runtime/src/providers/google/index.test.ts +0 -395
  14. package/packages/model-runtime/src/providers/google/index.ts +28 -194
  15. package/packages/model-runtime/src/providers/openai/index.test.ts +18 -17
  16. package/packages/model-runtime/src/types/structureOutput.ts +3 -4
  17. package/packages/types/src/aiChat.ts +0 -1
  18. package/src/app/(backend)/trpc/edge/[trpc]/route.ts +0 -2
  19. package/src/server/routers/edge/index.ts +2 -1
  20. package/src/server/routers/lambda/aiChat.ts +1 -2
  21. package/src/server/routers/lambda/index.ts +2 -0
  22. package/src/server/routers/lambda/upload.ts +16 -0
  23. package/src/services/__tests__/upload.test.ts +266 -18
  24. package/src/services/upload.ts +2 -2
@@ -28,9 +28,9 @@ describe('Anthropic generateObject', () => {
28
28
  create: vi.fn().mockResolvedValue({
29
29
  content: [
30
30
  {
31
- type: 'tool_use',
31
+ input: { age: 30, name: 'John' },
32
32
  name: 'person_extractor',
33
- input: { name: 'John', age: 30 },
33
+ type: 'tool_use',
34
34
  },
35
35
  ],
36
36
  }),
@@ -39,48 +39,48 @@ describe('Anthropic generateObject', () => {
39
39
 
40
40
  const payload = {
41
41
  messages: [{ content: 'Generate a person object', role: 'user' as const }],
42
+ model: 'claude-3-5-sonnet-20241022',
42
43
  schema: {
43
- name: 'person_extractor',
44
44
  description: 'Extract person information',
45
+ name: 'person_extractor',
45
46
  schema: {
46
- type: 'object' as const,
47
- properties: { name: { type: 'string' }, age: { type: 'number' } },
47
+ properties: { age: { type: 'number' }, name: { type: 'string' } },
48
48
  required: ['name', 'age'],
49
+ type: 'object' as const,
49
50
  },
50
51
  },
51
- model: 'claude-3-5-sonnet-20241022',
52
52
  };
53
53
 
54
54
  const result = await createAnthropicGenerateObject(mockClient as any, payload);
55
55
 
56
56
  expect(mockClient.messages.create).toHaveBeenCalledWith(
57
57
  expect.objectContaining({
58
- model: 'claude-3-5-sonnet-20241022',
59
58
  max_tokens: 8192,
60
59
  messages: [{ content: 'Generate a person object', role: 'user' }],
60
+ model: 'claude-3-5-sonnet-20241022',
61
+ tool_choice: {
62
+ name: 'person_extractor',
63
+ type: 'tool',
64
+ },
61
65
  tools: [
62
66
  {
63
- name: 'person_extractor',
64
67
  description: 'Extract person information',
65
68
  input_schema: {
66
- type: 'object',
67
69
  properties: {
68
- name: { type: 'string' },
69
70
  age: { type: 'number' },
71
+ name: { type: 'string' },
70
72
  },
71
73
  required: ['name', 'age'],
74
+ type: 'object',
72
75
  },
76
+ name: 'person_extractor',
73
77
  },
74
78
  ],
75
- tool_choice: {
76
- type: 'tool',
77
- name: 'person_extractor',
78
- },
79
79
  }),
80
80
  expect.objectContaining({}),
81
81
  );
82
82
 
83
- expect(result).toEqual({ name: 'John', age: 30 });
83
+ expect(result).toEqual({ age: 30, name: 'John' });
84
84
  });
85
85
 
86
86
  it('should handle system messages correctly', async () => {
@@ -89,9 +89,9 @@ describe('Anthropic generateObject', () => {
89
89
  create: vi.fn().mockResolvedValue({
90
90
  content: [
91
91
  {
92
- type: 'tool_use',
93
- name: 'status_extractor',
94
92
  input: { status: 'success' },
93
+ name: 'status_extractor',
94
+ type: 'tool_use',
95
95
  },
96
96
  ],
97
97
  }),
@@ -103,19 +103,19 @@ describe('Anthropic generateObject', () => {
103
103
  { content: 'You are a helpful assistant', role: 'system' as const },
104
104
  { content: 'Generate status', role: 'user' as const },
105
105
  ],
106
+ model: 'claude-3-5-sonnet-20241022',
106
107
  schema: {
107
108
  name: 'status_extractor',
108
- schema: { type: 'object' as const, properties: { status: { type: 'string' } } },
109
+ schema: { properties: { status: { type: 'string' } }, type: 'object' as const },
109
110
  },
110
- model: 'claude-3-5-sonnet-20241022',
111
111
  };
112
112
 
113
113
  const result = await createAnthropicGenerateObject(mockClient as any, payload);
114
114
 
115
115
  expect(mockClient.messages.create).toHaveBeenCalledWith(
116
116
  expect.objectContaining({
117
- system: [{ text: 'You are a helpful assistant', type: 'text' }],
118
117
  messages: expect.any(Array),
118
+ system: [{ text: 'You are a helpful assistant', type: 'text' }],
119
119
  }),
120
120
  expect.any(Object),
121
121
  );
@@ -129,9 +129,9 @@ describe('Anthropic generateObject', () => {
129
129
  create: vi.fn().mockResolvedValue({
130
130
  content: [
131
131
  {
132
- type: 'tool_use',
133
- name: 'data_extractor',
134
132
  input: { data: 'test' },
133
+ name: 'data_extractor',
134
+ type: 'tool_use',
135
135
  },
136
136
  ],
137
137
  }),
@@ -140,11 +140,11 @@ describe('Anthropic generateObject', () => {
140
140
 
141
141
  const payload = {
142
142
  messages: [{ content: 'Generate data', role: 'user' as const }],
143
+ model: 'claude-3-5-sonnet-20241022',
143
144
  schema: {
144
145
  name: 'data_extractor',
145
- schema: { type: 'object' as const, properties: { data: { type: 'string' } } },
146
+ schema: { properties: { data: { type: 'string' } }, type: 'object' as const },
146
147
  },
147
- model: 'claude-3-5-sonnet-20241022',
148
148
  };
149
149
 
150
150
  const options = {
@@ -169,8 +169,8 @@ describe('Anthropic generateObject', () => {
169
169
  create: vi.fn().mockResolvedValue({
170
170
  content: [
171
171
  {
172
- type: 'text',
173
172
  text: 'Some text response without tool use',
173
+ type: 'text',
174
174
  },
175
175
  ],
176
176
  }),
@@ -179,11 +179,11 @@ describe('Anthropic generateObject', () => {
179
179
 
180
180
  const payload = {
181
181
  messages: [{ content: 'Generate data', role: 'user' as const }],
182
+ model: 'claude-3-5-sonnet-20241022',
182
183
  schema: {
183
184
  name: 'test_tool',
184
185
  schema: { type: 'object' },
185
186
  },
186
- model: 'claude-3-5-sonnet-20241022',
187
187
  };
188
188
 
189
189
  const result = await createAnthropicGenerateObject(mockClient as any, payload as any);
@@ -197,9 +197,10 @@ describe('Anthropic generateObject', () => {
197
197
  create: vi.fn().mockResolvedValue({
198
198
  content: [
199
199
  {
200
- type: 'tool_use',
201
- name: 'user_extractor',
202
200
  input: {
201
+ metadata: {
202
+ created: '2024-01-01',
203
+ },
203
204
  user: {
204
205
  name: 'Alice',
205
206
  profile: {
@@ -207,10 +208,9 @@ describe('Anthropic generateObject', () => {
207
208
  preferences: ['music', 'sports'],
208
209
  },
209
210
  },
210
- metadata: {
211
- created: '2024-01-01',
212
- },
213
211
  },
212
+ name: 'user_extractor',
213
+ type: 'tool_use',
214
214
  },
215
215
  ],
216
216
  }),
@@ -219,35 +219,38 @@ describe('Anthropic generateObject', () => {
219
219
 
220
220
  const payload = {
221
221
  messages: [{ content: 'Generate complex user data', role: 'user' as const }],
222
+ model: 'claude-3-5-sonnet-20241022',
222
223
  schema: {
223
- name: 'user_extractor',
224
224
  description: 'Extract complex user information',
225
+ name: 'user_extractor',
225
226
  schema: {
226
- type: 'object' as const,
227
227
  properties: {
228
+ metadata: { type: 'object' },
228
229
  user: {
229
- type: 'object',
230
230
  properties: {
231
231
  name: { type: 'string' },
232
232
  profile: {
233
- type: 'object',
234
233
  properties: {
235
234
  age: { type: 'number' },
236
- preferences: { type: 'array', items: { type: 'string' } },
235
+ preferences: { items: { type: 'string' }, type: 'array' },
237
236
  },
237
+ type: 'object',
238
238
  },
239
239
  },
240
+ type: 'object',
240
241
  },
241
- metadata: { type: 'object' },
242
242
  },
243
+ type: 'object' as const,
243
244
  },
244
245
  },
245
- model: 'claude-3-5-sonnet-20241022',
246
246
  };
247
247
 
248
248
  const result = await createAnthropicGenerateObject(mockClient as any, payload);
249
249
 
250
250
  expect(result).toEqual({
251
+ metadata: {
252
+ created: '2024-01-01',
253
+ },
251
254
  user: {
252
255
  name: 'Alice',
253
256
  profile: {
@@ -255,9 +258,6 @@ describe('Anthropic generateObject', () => {
255
258
  preferences: ['music', 'sports'],
256
259
  },
257
260
  },
258
- metadata: {
259
- created: '2024-01-01',
260
- },
261
261
  });
262
262
  });
263
263
  });
@@ -269,14 +269,14 @@ describe('Anthropic generateObject', () => {
269
269
  create: vi.fn().mockResolvedValue({
270
270
  content: [
271
271
  {
272
- type: 'tool_use',
273
- name: 'get_weather',
274
272
  input: { city: 'New York', unit: 'celsius' },
273
+ name: 'get_weather',
274
+ type: 'tool_use',
275
275
  },
276
276
  {
277
- type: 'tool_use',
278
- name: 'get_time',
279
277
  input: { timezone: 'America/New_York' },
278
+ name: 'get_time',
279
+ type: 'tool_use',
280
280
  },
281
281
  ],
282
282
  }),
@@ -285,69 +285,75 @@ describe('Anthropic generateObject', () => {
285
285
 
286
286
  const payload = {
287
287
  messages: [{ content: 'What is the weather and time in New York?', role: 'user' as const }],
288
+ model: 'claude-3-5-sonnet-20241022',
288
289
  tools: [
289
290
  {
290
- name: 'get_weather',
291
- description: 'Get weather information',
292
- parameters: {
293
- type: 'object' as const,
294
- properties: {
295
- city: { type: 'string' },
296
- unit: { type: 'string' },
291
+ function: {
292
+ description: 'Get weather information',
293
+ name: 'get_weather',
294
+ parameters: {
295
+ properties: {
296
+ city: { type: 'string' },
297
+ unit: { type: 'string' },
298
+ },
299
+ required: ['city'],
300
+ type: 'object' as const,
297
301
  },
298
- required: ['city'],
299
302
  },
303
+ type: 'function' as const,
300
304
  },
301
305
  {
302
- name: 'get_time',
303
- description: 'Get current time',
304
- parameters: {
305
- type: 'object' as const,
306
- properties: {
307
- timezone: { type: 'string' },
306
+ function: {
307
+ description: 'Get current time',
308
+ name: 'get_time',
309
+ parameters: {
310
+ properties: {
311
+ timezone: { type: 'string' },
312
+ },
313
+ required: ['timezone'],
314
+ type: 'object' as const,
308
315
  },
309
- required: ['timezone'],
310
316
  },
317
+ type: 'function' as const,
311
318
  },
312
319
  ],
313
- model: 'claude-3-5-sonnet-20241022',
314
320
  };
315
321
 
316
322
  const result = await createAnthropicGenerateObject(mockClient as any, payload as any);
317
323
 
318
324
  expect(mockClient.messages.create).toHaveBeenCalledWith(
319
325
  expect.objectContaining({
320
- model: 'claude-3-5-sonnet-20241022',
321
326
  max_tokens: 8192,
322
327
  messages: [{ content: 'What is the weather and time in New York?', role: 'user' }],
328
+ model: 'claude-3-5-sonnet-20241022',
329
+ tool_choice: {
330
+ type: 'any',
331
+ },
323
332
  tools: [
324
333
  {
325
- name: 'get_weather',
326
334
  description: 'Get weather information',
327
335
  input_schema: {
328
- type: 'object',
329
336
  properties: {
330
337
  city: { type: 'string' },
331
338
  unit: { type: 'string' },
332
339
  },
333
340
  required: ['city'],
341
+ type: 'object',
334
342
  },
343
+ name: 'get_weather',
335
344
  },
336
345
  {
337
- name: 'get_time',
338
346
  description: 'Get current time',
339
347
  input_schema: {
340
- type: 'object',
341
348
  properties: {
342
349
  timezone: { type: 'string' },
343
350
  },
344
351
  required: ['timezone'],
352
+ type: 'object',
345
353
  },
354
+ name: 'get_time',
346
355
  },
347
356
  ],
348
- tool_choice: {
349
- type: 'any',
350
- },
351
357
  }),
352
358
  expect.objectContaining({}),
353
359
  );
@@ -364,9 +370,9 @@ describe('Anthropic generateObject', () => {
364
370
  create: vi.fn().mockResolvedValue({
365
371
  content: [
366
372
  {
367
- type: 'tool_use',
373
+ input: { a: 5, b: 3, operation: 'add' },
368
374
  name: 'calculate',
369
- input: { operation: 'add', a: 5, b: 3 },
375
+ type: 'tool_use',
370
376
  },
371
377
  ],
372
378
  }),
@@ -375,27 +381,30 @@ describe('Anthropic generateObject', () => {
375
381
 
376
382
  const payload = {
377
383
  messages: [{ content: 'Add 5 and 3', role: 'user' as const }],
384
+ model: 'claude-3-5-sonnet-20241022',
378
385
  tools: [
379
386
  {
380
- name: 'calculate',
381
- description: 'Perform mathematical calculation',
382
- parameters: {
383
- type: 'object' as const,
384
- properties: {
385
- operation: { type: 'string' },
386
- a: { type: 'number' },
387
- b: { type: 'number' },
387
+ function: {
388
+ description: 'Perform mathematical calculation',
389
+ name: 'calculate',
390
+ parameters: {
391
+ properties: {
392
+ a: { type: 'number' },
393
+ b: { type: 'number' },
394
+ operation: { type: 'string' },
395
+ },
396
+ required: ['operation', 'a', 'b'],
397
+ type: 'object' as const,
388
398
  },
389
- required: ['operation', 'a', 'b'],
390
399
  },
400
+ type: 'function' as const,
391
401
  },
392
402
  ],
393
- model: 'claude-3-5-sonnet-20241022',
394
403
  };
395
404
 
396
405
  const result = await createAnthropicGenerateObject(mockClient as any, payload as any);
397
406
 
398
- expect(result).toEqual([{ arguments: { operation: 'add', a: 5, b: 3 }, name: 'calculate' }]);
407
+ expect(result).toEqual([{ arguments: { a: 5, b: 3, operation: 'add' }, name: 'calculate' }]);
399
408
  });
400
409
  });
401
410
 
@@ -410,11 +419,11 @@ describe('Anthropic generateObject', () => {
410
419
 
411
420
  const payload = {
412
421
  messages: [{ content: 'Generate data', role: 'user' as const }],
422
+ model: 'claude-3-5-sonnet-20241022',
413
423
  schema: {
414
424
  name: 'test_tool',
415
425
  schema: { type: 'object' },
416
426
  },
417
- model: 'claude-3-5-sonnet-20241022',
418
427
  };
419
428
 
420
429
  await expect(createAnthropicGenerateObject(mockClient as any, payload as any)).rejects.toThrow(
@@ -434,11 +443,11 @@ describe('Anthropic generateObject', () => {
434
443
 
435
444
  const payload = {
436
445
  messages: [{ content: 'Generate data', role: 'user' as const }],
446
+ model: 'claude-3-5-sonnet-20241022',
437
447
  schema: {
438
448
  name: 'test_tool',
439
449
  schema: { type: 'object' },
440
450
  },
441
- model: 'claude-3-5-sonnet-20241022',
442
451
  };
443
452
 
444
453
  const options = {
@@ -14,14 +14,14 @@ export const createAnthropicGenerateObject = async (
14
14
  payload: GenerateObjectPayload,
15
15
  options?: GenerateObjectOptions,
16
16
  ) => {
17
- const { schema, messages, systemRole, model, tools } = payload;
17
+ const { schema, messages, model, tools } = payload;
18
18
 
19
19
  log('generateObject called with model: %s', model);
20
20
  log('schema: %O', schema);
21
21
  log('messages count: %d', messages.length);
22
22
 
23
23
  // Convert messages to Anthropic format
24
- const system_message = systemRole || messages.find((m) => m.role === 'system')?.content;
24
+ const system_message = messages.find((m) => m.role === 'system')?.content;
25
25
  const user_messages = messages.filter((m) => m.role !== 'system');
26
26
  const anthropicMessages = await buildAnthropicMessages(user_messages);
27
27
 
@@ -39,7 +39,7 @@ export const createAnthropicGenerateObject = async (
39
39
  let finalTools;
40
40
  let tool_choice: Anthropic.ToolChoiceAny | Anthropic.ToolChoiceTool;
41
41
  if (tools) {
42
- finalTools = buildAnthropicTools(tools.map((item) => ({ function: item, type: 'function' })));
42
+ finalTools = buildAnthropicTools(tools);
43
43
  tool_choice = { type: 'any' };
44
44
  } else if (schema) {
45
45
  // Convert OpenAI-style schema to Anthropic tool format