@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.
- package/CHANGELOG.md +50 -0
- package/changelog/v1.json +18 -0
- package/package.json +1 -1
- package/packages/database/src/repositories/aiInfra/index.test.ts +656 -0
- package/packages/model-runtime/src/core/contextBuilders/google.test.ts +585 -0
- package/packages/model-runtime/src/core/contextBuilders/google.ts +201 -0
- package/packages/model-runtime/src/core/openaiCompatibleFactory/index.test.ts +191 -179
- package/packages/model-runtime/src/core/openaiCompatibleFactory/index.ts +305 -47
- package/packages/model-runtime/src/providers/anthropic/generateObject.test.ts +93 -84
- package/packages/model-runtime/src/providers/anthropic/generateObject.ts +3 -3
- package/packages/model-runtime/src/providers/google/generateObject.test.ts +588 -83
- package/packages/model-runtime/src/providers/google/generateObject.ts +104 -6
- package/packages/model-runtime/src/providers/google/index.test.ts +0 -395
- package/packages/model-runtime/src/providers/google/index.ts +28 -194
- package/packages/model-runtime/src/providers/openai/index.test.ts +18 -17
- package/packages/model-runtime/src/types/structureOutput.ts +3 -4
- package/packages/types/src/aiChat.ts +0 -1
- package/src/app/(backend)/trpc/edge/[trpc]/route.ts +0 -2
- package/src/server/routers/edge/index.ts +2 -1
- package/src/server/routers/lambda/aiChat.ts +1 -2
- package/src/server/routers/lambda/index.ts +2 -0
- package/src/server/routers/lambda/upload.ts +16 -0
- package/src/services/__tests__/upload.test.ts +266 -18
- package/src/services/upload.ts +2 -2
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
|
-
Content,
|
|
3
|
-
FunctionDeclaration,
|
|
4
2
|
GenerateContentConfig,
|
|
5
3
|
Tool as GoogleFunctionCallTool,
|
|
6
4
|
GoogleGenAI,
|
|
7
5
|
HttpOptions,
|
|
8
|
-
Part,
|
|
9
|
-
Type as SchemaType,
|
|
10
6
|
ThinkingConfig,
|
|
11
7
|
} from '@google/genai';
|
|
12
8
|
import debug from 'debug';
|
|
13
9
|
|
|
14
10
|
import { LobeRuntimeAI } from '../../core/BaseAI';
|
|
11
|
+
import { buildGoogleMessages, buildGoogleTools } from '../../core/contextBuilders/google';
|
|
15
12
|
import { GoogleGenerativeAIStream, VertexAIStream } from '../../core/streams';
|
|
16
13
|
import { LOBE_ERROR_KEY } from '../../core/streams/google';
|
|
17
14
|
import {
|
|
@@ -20,8 +17,6 @@ import {
|
|
|
20
17
|
ChatStreamPayload,
|
|
21
18
|
GenerateObjectOptions,
|
|
22
19
|
GenerateObjectPayload,
|
|
23
|
-
OpenAIChatMessage,
|
|
24
|
-
UserMessageContentPart,
|
|
25
20
|
} from '../../types';
|
|
26
21
|
import { AgentRuntimeErrorType } from '../../types/error';
|
|
27
22
|
import { CreateImagePayload, CreateImageResponse } from '../../types/image';
|
|
@@ -29,12 +24,9 @@ import { AgentRuntimeError } from '../../utils/createError';
|
|
|
29
24
|
import { debugStream } from '../../utils/debugStream';
|
|
30
25
|
import { getModelPricing } from '../../utils/getModelPricing';
|
|
31
26
|
import { parseGoogleErrorMessage } from '../../utils/googleErrorParser';
|
|
32
|
-
import { imageUrlToBase64 } from '../../utils/imageToBase64';
|
|
33
27
|
import { StreamingResponse } from '../../utils/response';
|
|
34
|
-
import { safeParseJSON } from '../../utils/safeParseJSON';
|
|
35
|
-
import { parseDataUri } from '../../utils/uriParser';
|
|
36
28
|
import { createGoogleImage } from './createImage';
|
|
37
|
-
import { createGoogleGenerateObject } from './generateObject';
|
|
29
|
+
import { createGoogleGenerateObject, createGoogleGenerateObjectWithTools } from './generateObject';
|
|
38
30
|
|
|
39
31
|
const log = debug('model-runtime:google');
|
|
40
32
|
|
|
@@ -217,7 +209,7 @@ export class LobeGoogleAI implements LobeRuntimeAI {
|
|
|
217
209
|
thinkingBudget: resolvedThinkingBudget,
|
|
218
210
|
};
|
|
219
211
|
|
|
220
|
-
const contents = await
|
|
212
|
+
const contents = await buildGoogleMessages(payload.messages);
|
|
221
213
|
|
|
222
214
|
const controller = new AbortController();
|
|
223
215
|
const originalSignal = options?.signal;
|
|
@@ -264,7 +256,7 @@ export class LobeGoogleAI implements LobeRuntimeAI {
|
|
|
264
256
|
modelsDisableInstuction.has(model) || model.toLowerCase().includes('learnlm')
|
|
265
257
|
? undefined
|
|
266
258
|
: thinkingConfig,
|
|
267
|
-
tools: this.
|
|
259
|
+
tools: this.buildGoogleToolsWithSearch(payload.tools, payload),
|
|
268
260
|
topP: payload.top_p,
|
|
269
261
|
};
|
|
270
262
|
|
|
@@ -330,16 +322,31 @@ export class LobeGoogleAI implements LobeRuntimeAI {
|
|
|
330
322
|
/**
|
|
331
323
|
* Generate structured output using Google Gemini API
|
|
332
324
|
* @see https://ai.google.dev/gemini-api/docs/structured-output
|
|
325
|
+
* @see https://ai.google.dev/gemini-api/docs/function-calling
|
|
333
326
|
*/
|
|
334
327
|
async generateObject(payload: GenerateObjectPayload, options?: GenerateObjectOptions) {
|
|
335
328
|
// Convert OpenAI messages to Google format
|
|
336
|
-
const contents = await
|
|
329
|
+
const contents = await buildGoogleMessages(payload.messages);
|
|
330
|
+
|
|
331
|
+
// Handle tools-based structured output
|
|
332
|
+
if (payload.tools && payload.tools.length > 0) {
|
|
333
|
+
return createGoogleGenerateObjectWithTools(
|
|
334
|
+
this.client,
|
|
335
|
+
{ contents, model: payload.model, tools: payload.tools },
|
|
336
|
+
options,
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Handle schema-based structured output
|
|
341
|
+
if (payload.schema) {
|
|
342
|
+
return createGoogleGenerateObject(
|
|
343
|
+
this.client,
|
|
344
|
+
{ contents, model: payload.model, schema: payload.schema },
|
|
345
|
+
options,
|
|
346
|
+
);
|
|
347
|
+
}
|
|
337
348
|
|
|
338
|
-
return
|
|
339
|
-
this.client,
|
|
340
|
-
{ contents, model: payload.model, schema: payload.schema },
|
|
341
|
-
options,
|
|
342
|
-
);
|
|
349
|
+
return undefined;
|
|
343
350
|
}
|
|
344
351
|
|
|
345
352
|
private createEnhancedStream(originalStream: any, signal: AbortSignal): ReadableStream {
|
|
@@ -489,147 +496,7 @@ export class LobeGoogleAI implements LobeRuntimeAI {
|
|
|
489
496
|
};
|
|
490
497
|
}
|
|
491
498
|
|
|
492
|
-
private
|
|
493
|
-
content: UserMessageContentPart,
|
|
494
|
-
): Promise<Part | undefined> => {
|
|
495
|
-
switch (content.type) {
|
|
496
|
-
default: {
|
|
497
|
-
return undefined;
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
case 'text': {
|
|
501
|
-
return { text: content.text };
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
case 'image_url': {
|
|
505
|
-
const { mimeType, base64, type } = parseDataUri(content.image_url.url);
|
|
506
|
-
|
|
507
|
-
if (type === 'base64') {
|
|
508
|
-
if (!base64) {
|
|
509
|
-
throw new TypeError("Image URL doesn't contain base64 data");
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
return {
|
|
513
|
-
inlineData: { data: base64, mimeType: mimeType || 'image/png' },
|
|
514
|
-
};
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
if (type === 'url') {
|
|
518
|
-
const { base64, mimeType } = await imageUrlToBase64(content.image_url.url);
|
|
519
|
-
|
|
520
|
-
return {
|
|
521
|
-
inlineData: { data: base64, mimeType },
|
|
522
|
-
};
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
throw new TypeError(`currently we don't support image url: ${content.image_url.url}`);
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
case 'video_url': {
|
|
529
|
-
const { mimeType, base64, type } = parseDataUri(content.video_url.url);
|
|
530
|
-
|
|
531
|
-
if (type === 'base64') {
|
|
532
|
-
if (!base64) {
|
|
533
|
-
throw new TypeError("Video URL doesn't contain base64 data");
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
return {
|
|
537
|
-
inlineData: { data: base64, mimeType: mimeType || 'video/mp4' },
|
|
538
|
-
};
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
if (type === 'url') {
|
|
542
|
-
// For video URLs, we need to fetch and convert to base64
|
|
543
|
-
// Note: This might need size/duration limits for practical use
|
|
544
|
-
const response = await fetch(content.video_url.url);
|
|
545
|
-
const arrayBuffer = await response.arrayBuffer();
|
|
546
|
-
const base64 = Buffer.from(arrayBuffer).toString('base64');
|
|
547
|
-
const mimeType = response.headers.get('content-type') || 'video/mp4';
|
|
548
|
-
|
|
549
|
-
return {
|
|
550
|
-
inlineData: { data: base64, mimeType },
|
|
551
|
-
};
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
throw new TypeError(`currently we don't support video url: ${content.video_url.url}`);
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
};
|
|
558
|
-
|
|
559
|
-
private convertOAIMessagesToGoogleMessage = async (
|
|
560
|
-
message: OpenAIChatMessage,
|
|
561
|
-
toolCallNameMap?: Map<string, string>,
|
|
562
|
-
): Promise<Content> => {
|
|
563
|
-
const content = message.content as string | UserMessageContentPart[];
|
|
564
|
-
if (!!message.tool_calls) {
|
|
565
|
-
return {
|
|
566
|
-
parts: message.tool_calls.map<Part>((tool) => ({
|
|
567
|
-
functionCall: {
|
|
568
|
-
args: safeParseJSON(tool.function.arguments)!,
|
|
569
|
-
name: tool.function.name,
|
|
570
|
-
},
|
|
571
|
-
})),
|
|
572
|
-
role: 'model',
|
|
573
|
-
};
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
// 将 tool_call result 转成 functionResponse part
|
|
577
|
-
if (message.role === 'tool' && toolCallNameMap && message.tool_call_id) {
|
|
578
|
-
const functionName = toolCallNameMap.get(message.tool_call_id);
|
|
579
|
-
if (functionName) {
|
|
580
|
-
return {
|
|
581
|
-
parts: [
|
|
582
|
-
{
|
|
583
|
-
functionResponse: {
|
|
584
|
-
name: functionName,
|
|
585
|
-
response: { result: message.content },
|
|
586
|
-
},
|
|
587
|
-
},
|
|
588
|
-
],
|
|
589
|
-
role: 'user',
|
|
590
|
-
};
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
const getParts = async () => {
|
|
595
|
-
if (typeof content === 'string') return [{ text: content }];
|
|
596
|
-
|
|
597
|
-
const parts = await Promise.all(
|
|
598
|
-
content.map(async (c) => await this.convertContentToGooglePart(c)),
|
|
599
|
-
);
|
|
600
|
-
return parts.filter(Boolean) as Part[];
|
|
601
|
-
};
|
|
602
|
-
|
|
603
|
-
return {
|
|
604
|
-
parts: await getParts(),
|
|
605
|
-
role: message.role === 'assistant' ? 'model' : 'user',
|
|
606
|
-
};
|
|
607
|
-
};
|
|
608
|
-
|
|
609
|
-
// convert messages from the OpenAI format to Google GenAI SDK
|
|
610
|
-
private buildGoogleMessages = async (messages: OpenAIChatMessage[]): Promise<Content[]> => {
|
|
611
|
-
const toolCallNameMap = new Map<string, string>();
|
|
612
|
-
messages.forEach((message) => {
|
|
613
|
-
if (message.role === 'assistant' && message.tool_calls) {
|
|
614
|
-
message.tool_calls.forEach((toolCall) => {
|
|
615
|
-
if (toolCall.type === 'function') {
|
|
616
|
-
toolCallNameMap.set(toolCall.id, toolCall.function.name);
|
|
617
|
-
}
|
|
618
|
-
});
|
|
619
|
-
}
|
|
620
|
-
});
|
|
621
|
-
|
|
622
|
-
const pools = messages
|
|
623
|
-
.filter((message) => message.role !== 'function')
|
|
624
|
-
.map(async (msg) => await this.convertOAIMessagesToGoogleMessage(msg, toolCallNameMap));
|
|
625
|
-
|
|
626
|
-
const contents = await Promise.all(pools);
|
|
627
|
-
|
|
628
|
-
// 筛除空消息: contents.parts must not be empty.
|
|
629
|
-
return contents.filter((content: Content) => content.parts && content.parts.length > 0);
|
|
630
|
-
};
|
|
631
|
-
|
|
632
|
-
private buildGoogleTools(
|
|
499
|
+
private buildGoogleToolsWithSearch(
|
|
633
500
|
tools: ChatCompletionTool[] | undefined,
|
|
634
501
|
payload?: ChatStreamPayload,
|
|
635
502
|
): GoogleFunctionCallTool[] | undefined {
|
|
@@ -640,7 +507,7 @@ export class LobeGoogleAI implements LobeRuntimeAI {
|
|
|
640
507
|
|
|
641
508
|
// 如果已经有 tool_calls,优先处理 function declarations
|
|
642
509
|
if (hasToolCalls && hasFunctionTools) {
|
|
643
|
-
return
|
|
510
|
+
return buildGoogleTools(tools);
|
|
644
511
|
}
|
|
645
512
|
|
|
646
513
|
// 构建并返回搜索相关工具(搜索工具不能与 FunctionCall 同时使用)
|
|
@@ -655,41 +522,8 @@ export class LobeGoogleAI implements LobeRuntimeAI {
|
|
|
655
522
|
}
|
|
656
523
|
|
|
657
524
|
// 最后考虑 function declarations
|
|
658
|
-
return
|
|
525
|
+
return buildGoogleTools(tools);
|
|
659
526
|
}
|
|
660
|
-
|
|
661
|
-
private buildFunctionDeclarations(
|
|
662
|
-
tools: ChatCompletionTool[] | undefined,
|
|
663
|
-
): GoogleFunctionCallTool[] | undefined {
|
|
664
|
-
if (!tools || tools.length === 0) return;
|
|
665
|
-
|
|
666
|
-
return [
|
|
667
|
-
{
|
|
668
|
-
functionDeclarations: tools.map((tool) => this.convertToolToGoogleTool(tool)),
|
|
669
|
-
},
|
|
670
|
-
];
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
private convertToolToGoogleTool = (tool: ChatCompletionTool): FunctionDeclaration => {
|
|
674
|
-
const functionDeclaration = tool.function;
|
|
675
|
-
const parameters = functionDeclaration.parameters;
|
|
676
|
-
// refs: https://github.com/lobehub/lobe-chat/pull/5002
|
|
677
|
-
const properties =
|
|
678
|
-
parameters?.properties && Object.keys(parameters.properties).length > 0
|
|
679
|
-
? parameters.properties
|
|
680
|
-
: { dummy: { type: 'string' } }; // dummy property to avoid empty object
|
|
681
|
-
|
|
682
|
-
return {
|
|
683
|
-
description: functionDeclaration.description,
|
|
684
|
-
name: functionDeclaration.name,
|
|
685
|
-
parameters: {
|
|
686
|
-
description: parameters?.description,
|
|
687
|
-
properties: properties,
|
|
688
|
-
required: parameters?.required,
|
|
689
|
-
type: SchemaType.OBJECT,
|
|
690
|
-
},
|
|
691
|
-
};
|
|
692
|
-
};
|
|
693
527
|
}
|
|
694
528
|
|
|
695
529
|
export default LobeGoogleAI;
|
|
@@ -59,10 +59,10 @@ describe('LobeOpenAI', () => {
|
|
|
59
59
|
const apiError = new OpenAI.APIError(
|
|
60
60
|
400,
|
|
61
61
|
{
|
|
62
|
-
status: 400,
|
|
63
62
|
error: {
|
|
64
63
|
message: 'Bad Request',
|
|
65
64
|
},
|
|
65
|
+
status: 400,
|
|
66
66
|
},
|
|
67
67
|
'Error message',
|
|
68
68
|
{},
|
|
@@ -178,13 +178,13 @@ describe('LobeOpenAI', () => {
|
|
|
178
178
|
} catch (e) {
|
|
179
179
|
expect(e).toEqual({
|
|
180
180
|
endpoint: 'https://api.openai.com/v1',
|
|
181
|
-
errorType: 'AgentRuntimeError',
|
|
182
|
-
provider: 'openai',
|
|
183
181
|
error: {
|
|
184
|
-
name: genericError.name,
|
|
185
182
|
cause: genericError.cause,
|
|
186
183
|
message: genericError.message,
|
|
184
|
+
name: genericError.name,
|
|
187
185
|
},
|
|
186
|
+
errorType: 'AgentRuntimeError',
|
|
187
|
+
provider: 'openai',
|
|
188
188
|
});
|
|
189
189
|
}
|
|
190
190
|
});
|
|
@@ -261,10 +261,10 @@ describe('LobeOpenAI', () => {
|
|
|
261
261
|
|
|
262
262
|
it('should use responses API when enabledSearch is true', async () => {
|
|
263
263
|
const payload = {
|
|
264
|
+
enabledSearch: true,
|
|
264
265
|
messages: [{ content: 'Hello', role: 'user' as const }],
|
|
265
266
|
model: 'gpt-4o',
|
|
266
267
|
temperature: 0.7,
|
|
267
|
-
enabledSearch: true,
|
|
268
268
|
};
|
|
269
269
|
|
|
270
270
|
await instance.chat(payload);
|
|
@@ -275,12 +275,12 @@ describe('LobeOpenAI', () => {
|
|
|
275
275
|
|
|
276
276
|
it('should handle -search- models with stripped parameters', async () => {
|
|
277
277
|
const payload = {
|
|
278
|
+
frequency_penalty: 0.5,
|
|
278
279
|
messages: [{ content: 'Hello', role: 'user' as const }],
|
|
279
280
|
model: 'gpt-4o-search-2024',
|
|
281
|
+
presence_penalty: 0.3,
|
|
280
282
|
temperature: 0.7,
|
|
281
283
|
top_p: 0.9,
|
|
282
|
-
frequency_penalty: 0.5,
|
|
283
|
-
presence_penalty: 0.3,
|
|
284
284
|
};
|
|
285
285
|
|
|
286
286
|
await instance.chat(payload);
|
|
@@ -296,12 +296,12 @@ describe('LobeOpenAI', () => {
|
|
|
296
296
|
|
|
297
297
|
it('should handle regular models with all parameters', async () => {
|
|
298
298
|
const payload = {
|
|
299
|
+
frequency_penalty: 0.5,
|
|
299
300
|
messages: [{ content: 'Hello', role: 'user' as const }],
|
|
300
301
|
model: 'gpt-4o',
|
|
302
|
+
presence_penalty: 0.3,
|
|
301
303
|
temperature: 0.7,
|
|
302
304
|
top_p: 0.9,
|
|
303
|
-
frequency_penalty: 0.5,
|
|
304
|
-
presence_penalty: 0.3,
|
|
305
305
|
};
|
|
306
306
|
|
|
307
307
|
await instance.chat(payload);
|
|
@@ -319,18 +319,19 @@ describe('LobeOpenAI', () => {
|
|
|
319
319
|
describe('responses.handlePayload', () => {
|
|
320
320
|
it('should add web_search tool when enabledSearch is true', async () => {
|
|
321
321
|
const payload = {
|
|
322
|
+
enabledSearch: true,
|
|
322
323
|
messages: [{ content: 'Hello', role: 'user' as const }],
|
|
323
|
-
model: 'gpt-4o',
|
|
324
|
+
model: 'gpt-4o',
|
|
325
|
+
// 使用常规模型,通过 enabledSearch 触发 responses API
|
|
324
326
|
temperature: 0.7,
|
|
325
|
-
|
|
326
|
-
tools: [{ type: 'function' as const, function: { name: 'test', description: 'test' } }],
|
|
327
|
+
tools: [{ function: { description: 'test', name: 'test' }, type: 'function' as const }],
|
|
327
328
|
};
|
|
328
329
|
|
|
329
330
|
await instance.chat(payload);
|
|
330
331
|
|
|
331
332
|
const createCall = (instance['client'].responses.create as Mock).mock.calls[0][0];
|
|
332
333
|
expect(createCall.tools).toEqual([
|
|
333
|
-
{
|
|
334
|
+
{ description: 'test', name: 'test', type: 'function' },
|
|
334
335
|
{ type: 'web_search' },
|
|
335
336
|
]);
|
|
336
337
|
});
|
|
@@ -339,10 +340,10 @@ describe('LobeOpenAI', () => {
|
|
|
339
340
|
// Note: oaiSearchContextSize is read at module load time, not runtime
|
|
340
341
|
// This test verifies the tool structure is correct when the env var would be set
|
|
341
342
|
const payload = {
|
|
343
|
+
enabledSearch: true,
|
|
342
344
|
messages: [{ content: 'Hello', role: 'user' as const }],
|
|
343
345
|
model: 'gpt-4o',
|
|
344
346
|
temperature: 0.7,
|
|
345
|
-
enabledSearch: true,
|
|
346
347
|
};
|
|
347
348
|
|
|
348
349
|
await instance.chat(payload);
|
|
@@ -358,8 +359,8 @@ describe('LobeOpenAI', () => {
|
|
|
358
359
|
const payload = {
|
|
359
360
|
messages: [{ content: 'Hello', role: 'user' as const }],
|
|
360
361
|
model: 'computer-use-preview',
|
|
361
|
-
temperature: 0.7,
|
|
362
362
|
reasoning: { effort: 'medium' },
|
|
363
|
+
temperature: 0.7,
|
|
363
364
|
};
|
|
364
365
|
|
|
365
366
|
await instance.chat(payload);
|
|
@@ -393,7 +394,7 @@ describe('LobeOpenAI', () => {
|
|
|
393
394
|
await instance.chat(payload);
|
|
394
395
|
|
|
395
396
|
const createCall = (instance['client'].responses.create as Mock).mock.calls[0][0];
|
|
396
|
-
expect(createCall.reasoning).toEqual({
|
|
397
|
+
expect(createCall.reasoning).toEqual({ effort: 'high', summary: 'auto' });
|
|
397
398
|
});
|
|
398
399
|
|
|
399
400
|
it('should set reasoning.effort to high for gpt-5-pro-2025-10-06 models', async () => {
|
|
@@ -406,7 +407,7 @@ describe('LobeOpenAI', () => {
|
|
|
406
407
|
await instance.chat(payload);
|
|
407
408
|
|
|
408
409
|
const createCall = (instance['client'].responses.create as Mock).mock.calls[0][0];
|
|
409
|
-
expect(createCall.reasoning).toEqual({
|
|
410
|
+
expect(createCall.reasoning).toEqual({ effort: 'high', summary: 'auto' });
|
|
410
411
|
});
|
|
411
412
|
});
|
|
412
413
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ChatCompletionTool } from './chat';
|
|
2
2
|
|
|
3
3
|
interface GenerateObjectMessage {
|
|
4
4
|
content: string;
|
|
@@ -6,7 +6,7 @@ interface GenerateObjectMessage {
|
|
|
6
6
|
role: 'user' | 'system' | 'assistant';
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
interface GenerateObjectSchema {
|
|
9
|
+
export interface GenerateObjectSchema {
|
|
10
10
|
description?: string;
|
|
11
11
|
name: string;
|
|
12
12
|
schema: {
|
|
@@ -23,8 +23,7 @@ export interface GenerateObjectPayload {
|
|
|
23
23
|
model: string;
|
|
24
24
|
responseApi?: boolean;
|
|
25
25
|
schema?: GenerateObjectSchema;
|
|
26
|
-
|
|
27
|
-
tools?: ChatCompletionFunctions[];
|
|
26
|
+
tools?: ChatCompletionTool[];
|
|
28
27
|
}
|
|
29
28
|
|
|
30
29
|
export interface GenerateObjectOptions {
|
|
@@ -75,7 +75,6 @@ export const StructureOutputSchema = z.object({
|
|
|
75
75
|
model: z.string(),
|
|
76
76
|
provider: z.string(),
|
|
77
77
|
schema: StructureSchema.optional(),
|
|
78
|
-
systemRole: z.string().optional(),
|
|
79
78
|
tools: z
|
|
80
79
|
.array(z.object({ function: LobeUniformToolSchema, type: z.literal('function') }))
|
|
81
80
|
.optional(),
|
|
@@ -60,8 +60,7 @@ export const aiChatRouter = router({
|
|
|
60
60
|
messages: input.messages,
|
|
61
61
|
model: input.model,
|
|
62
62
|
schema: input.schema,
|
|
63
|
-
|
|
64
|
-
tools: input.tools?.map((item) => item.function),
|
|
63
|
+
tools: input.tools,
|
|
65
64
|
});
|
|
66
65
|
|
|
67
66
|
log('generateObject completed, result: %O', result);
|
|
@@ -28,6 +28,7 @@ import { sessionRouter } from './session';
|
|
|
28
28
|
import { sessionGroupRouter } from './sessionGroup';
|
|
29
29
|
import { threadRouter } from './thread';
|
|
30
30
|
import { topicRouter } from './topic';
|
|
31
|
+
import { uploadRouter } from './upload';
|
|
31
32
|
import { userRouter } from './user';
|
|
32
33
|
|
|
33
34
|
export const lambdaRouter = router({
|
|
@@ -57,6 +58,7 @@ export const lambdaRouter = router({
|
|
|
57
58
|
sessionGroup: sessionGroupRouter,
|
|
58
59
|
thread: threadRouter,
|
|
59
60
|
topic: topicRouter,
|
|
61
|
+
upload: uploadRouter,
|
|
60
62
|
user: userRouter,
|
|
61
63
|
});
|
|
62
64
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
import { authedProcedure, router } from '@/libs/trpc/lambda';
|
|
4
|
+
import { S3 } from '@/server/modules/S3';
|
|
5
|
+
|
|
6
|
+
export const uploadRouter = router({
|
|
7
|
+
createS3PreSignedUrl: authedProcedure
|
|
8
|
+
.input(z.object({ pathname: z.string() }))
|
|
9
|
+
.mutation(async ({ input }) => {
|
|
10
|
+
const s3 = new S3();
|
|
11
|
+
|
|
12
|
+
return await s3.createPreSignedUrl(input.pathname);
|
|
13
|
+
}),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export type FileRouter = typeof uploadRouter;
|