@librechat/agents 3.0.25 → 3.0.27
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/dist/cjs/llm/google/index.cjs +78 -9
- package/dist/cjs/llm/google/index.cjs.map +1 -1
- package/dist/cjs/llm/google/utils/common.cjs +185 -28
- package/dist/cjs/llm/google/utils/common.cjs.map +1 -1
- package/dist/esm/llm/google/index.mjs +79 -10
- package/dist/esm/llm/google/index.mjs.map +1 -1
- package/dist/esm/llm/google/utils/common.mjs +184 -30
- package/dist/esm/llm/google/utils/common.mjs.map +1 -1
- package/dist/types/llm/google/index.d.ts +10 -0
- package/dist/types/llm/google/types.d.ts +11 -1
- package/dist/types/llm/google/utils/common.d.ts +17 -2
- package/package.json +1 -1
- package/src/llm/google/data/gettysburg10.wav +0 -0
- package/src/llm/google/data/hotdog.jpg +0 -0
- package/src/llm/google/index.ts +129 -14
- package/src/llm/google/llm.spec.ts +932 -0
- package/src/llm/google/types.ts +14 -1
- package/src/llm/google/utils/common.ts +262 -49
package/src/llm/google/types.ts
CHANGED
|
@@ -5,11 +5,17 @@ import {
|
|
|
5
5
|
} from '@google/generative-ai';
|
|
6
6
|
import { BindToolsInput } from '@langchain/core/language_models/chat_models';
|
|
7
7
|
|
|
8
|
+
/** New GoogleSearch tool for Gemini 2.0+ models */
|
|
9
|
+
export interface GoogleSearchTool {
|
|
10
|
+
googleSearch: Record<string, never>;
|
|
11
|
+
}
|
|
12
|
+
|
|
8
13
|
export type GoogleGenerativeAIToolType =
|
|
9
14
|
| BindToolsInput
|
|
10
15
|
| GoogleGenerativeAIFunctionDeclarationsTool
|
|
11
16
|
| CodeExecutionTool
|
|
12
|
-
| GoogleSearchRetrievalTool
|
|
17
|
+
| GoogleSearchRetrievalTool
|
|
18
|
+
| GoogleSearchTool;
|
|
13
19
|
|
|
14
20
|
/** Enum for content modality types */
|
|
15
21
|
enum Modality {
|
|
@@ -27,6 +33,13 @@ interface ModalityTokenCount {
|
|
|
27
33
|
tokenCount: number;
|
|
28
34
|
}
|
|
29
35
|
|
|
36
|
+
/** Interface for input token details with cache and tier tracking */
|
|
37
|
+
export interface InputTokenDetails {
|
|
38
|
+
cache_read?: number;
|
|
39
|
+
over_200k?: number;
|
|
40
|
+
cache_read_over_200k?: number;
|
|
41
|
+
}
|
|
42
|
+
|
|
30
43
|
/** Main interface for Gemini API usage metadata */
|
|
31
44
|
export interface GeminiApiUsageMetadata {
|
|
32
45
|
promptTokenCount?: number;
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
type FunctionDeclarationsTool as GoogleGenerativeAIFunctionDeclarationsTool,
|
|
13
13
|
} from '@google/generative-ai';
|
|
14
14
|
import {
|
|
15
|
+
AIMessage,
|
|
15
16
|
AIMessageChunk,
|
|
16
17
|
BaseMessage,
|
|
17
18
|
ChatMessage,
|
|
@@ -29,7 +30,7 @@ import {
|
|
|
29
30
|
isDataContentBlock,
|
|
30
31
|
} from '@langchain/core/messages';
|
|
31
32
|
import { ChatGenerationChunk } from '@langchain/core/outputs';
|
|
32
|
-
import type { ChatGeneration } from '@langchain/core/outputs';
|
|
33
|
+
import type { ChatGeneration, ChatResult } from '@langchain/core/outputs';
|
|
33
34
|
import { isLangChainTool } from '@langchain/core/utils/function_calling';
|
|
34
35
|
import { isOpenAITool } from '@langchain/core/language_models/base';
|
|
35
36
|
import { ToolCallChunk } from '@langchain/core/messages/tool';
|
|
@@ -40,6 +41,20 @@ import {
|
|
|
40
41
|
} from './zod_to_genai_parameters';
|
|
41
42
|
import { GoogleGenerativeAIToolType } from '../types';
|
|
42
43
|
|
|
44
|
+
export const _FUNCTION_CALL_THOUGHT_SIGNATURES_MAP_KEY =
|
|
45
|
+
'__gemini_function_call_thought_signatures__';
|
|
46
|
+
|
|
47
|
+
const DUMMY_SIGNATURE =
|
|
48
|
+
'ErYCCrMCAdHtim9kOoOkrPiCNVsmlpMIKd7ZMxgiFbVQOkgp7nlLcDMzVsZwIzvuT7nQROivoXA72ccC2lSDvR0Gh7dkWaGuj7ctv6t7ZceHnecx0QYa+ix8tYpRfjhyWozQ49lWiws6+YGjCt10KRTyWsZ2h6O7iHTYJwKIRwGUHRKy/qK/6kFxJm5ML00gLq4D8s5Z6DBpp2ZlR+uF4G8jJgeWQgyHWVdx2wGYElaceVAc66tZdPQRdOHpWtgYSI1YdaXgVI8KHY3/EfNc2YqqMIulvkDBAnuMhkAjV9xmBa54Tq+ih3Im4+r3DzqhGqYdsSkhS0kZMwte4Hjs65dZzCw9lANxIqYi1DJ639WNPYihp/DCJCos7o+/EeSPJaio5sgWDyUnMGkY1atsJZ+m7pj7DD5tvQ==';
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Executes a function immediately and returns its result.
|
|
52
|
+
* Functional utility similar to an Immediately Invoked Function Expression (IIFE).
|
|
53
|
+
* @param fn The function to execute.
|
|
54
|
+
* @returns The result of invoking fn.
|
|
55
|
+
*/
|
|
56
|
+
export const iife = <T>(fn: () => T): T => fn();
|
|
57
|
+
|
|
43
58
|
export function getMessageAuthor(message: BaseMessage): string {
|
|
44
59
|
const type = message._getType();
|
|
45
60
|
if (ChatMessage.isInstance(message)) {
|
|
@@ -301,20 +316,6 @@ function _convertLangChainContentToPart(
|
|
|
301
316
|
mimeType,
|
|
302
317
|
},
|
|
303
318
|
};
|
|
304
|
-
} else if (
|
|
305
|
-
content.type === 'document' ||
|
|
306
|
-
content.type === 'audio' ||
|
|
307
|
-
content.type === 'video'
|
|
308
|
-
) {
|
|
309
|
-
if (!isMultimodalModel) {
|
|
310
|
-
throw new Error(`This model does not support ${content.type}s`);
|
|
311
|
-
}
|
|
312
|
-
return {
|
|
313
|
-
inlineData: {
|
|
314
|
-
data: content.data,
|
|
315
|
-
mimeType: content.mimeType,
|
|
316
|
-
},
|
|
317
|
-
};
|
|
318
319
|
} else if (content.type === 'media') {
|
|
319
320
|
return messageContentMedia(content);
|
|
320
321
|
} else if (content.type === 'tool_use') {
|
|
@@ -352,7 +353,8 @@ function _convertLangChainContentToPart(
|
|
|
352
353
|
export function convertMessageContentToParts(
|
|
353
354
|
message: BaseMessage,
|
|
354
355
|
isMultimodalModel: boolean,
|
|
355
|
-
previousMessages: BaseMessage[]
|
|
356
|
+
previousMessages: BaseMessage[],
|
|
357
|
+
model?: string
|
|
356
358
|
): Part[] {
|
|
357
359
|
if (isToolMessage(message)) {
|
|
358
360
|
const messageName =
|
|
@@ -409,13 +411,33 @@ export function convertMessageContentToParts(
|
|
|
409
411
|
);
|
|
410
412
|
}
|
|
411
413
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
+
const functionThoughtSignatures = (
|
|
415
|
+
message.additional_kwargs as BaseMessage['additional_kwargs'] | undefined
|
|
416
|
+
)?.[_FUNCTION_CALL_THOUGHT_SIGNATURES_MAP_KEY] as
|
|
417
|
+
| Record<string, string>
|
|
418
|
+
| undefined;
|
|
419
|
+
|
|
420
|
+
if (isAIMessage(message) && (message.tool_calls?.length ?? 0) > 0) {
|
|
421
|
+
functionCalls = (message.tool_calls ?? []).map((tc) => {
|
|
422
|
+
const thoughtSignature = iife(() => {
|
|
423
|
+
if (tc.id != null && tc.id !== '') {
|
|
424
|
+
const signature = functionThoughtSignatures?.[tc.id];
|
|
425
|
+
if (signature != null && signature !== '') {
|
|
426
|
+
return signature;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
if (model?.includes('gemini-3') === true) {
|
|
430
|
+
return DUMMY_SIGNATURE;
|
|
431
|
+
}
|
|
432
|
+
return '';
|
|
433
|
+
});
|
|
434
|
+
|
|
414
435
|
return {
|
|
415
436
|
functionCall: {
|
|
416
437
|
name: tc.name,
|
|
417
438
|
args: tc.args,
|
|
418
439
|
},
|
|
440
|
+
...(thoughtSignature ? { thoughtSignature } : {}),
|
|
419
441
|
};
|
|
420
442
|
});
|
|
421
443
|
}
|
|
@@ -426,7 +448,9 @@ export function convertMessageContentToParts(
|
|
|
426
448
|
export function convertBaseMessagesToContent(
|
|
427
449
|
messages: BaseMessage[],
|
|
428
450
|
isMultimodalModel: boolean,
|
|
429
|
-
convertSystemMessageToHumanContent: boolean = false
|
|
451
|
+
convertSystemMessageToHumanContent: boolean = false,
|
|
452
|
+
|
|
453
|
+
model?: string
|
|
430
454
|
): Content[] | undefined {
|
|
431
455
|
return messages.reduce<{
|
|
432
456
|
content: Content[] | undefined;
|
|
@@ -456,7 +480,8 @@ export function convertBaseMessagesToContent(
|
|
|
456
480
|
const parts = convertMessageContentToParts(
|
|
457
481
|
message,
|
|
458
482
|
isMultimodalModel,
|
|
459
|
-
messages.slice(0, index)
|
|
483
|
+
messages.slice(0, index),
|
|
484
|
+
model
|
|
460
485
|
);
|
|
461
486
|
|
|
462
487
|
if (acc.mergeWithPreviousContent) {
|
|
@@ -505,11 +530,32 @@ export function convertResponseContentToChatGenerationChunk(
|
|
|
505
530
|
if (!response.candidates || response.candidates.length === 0) {
|
|
506
531
|
return null;
|
|
507
532
|
}
|
|
508
|
-
const functionCalls = response.functionCalls();
|
|
509
533
|
const [candidate] = response.candidates as [
|
|
510
534
|
Partial<GenerateContentCandidate> | undefined,
|
|
511
535
|
];
|
|
512
536
|
const { content: candidateContent, ...generationInfo } = candidate ?? {};
|
|
537
|
+
|
|
538
|
+
// Extract function calls directly from parts to preserve thoughtSignature
|
|
539
|
+
const functionCalls =
|
|
540
|
+
(candidateContent?.parts as Part[] | undefined)?.reduce(
|
|
541
|
+
(acc, p) => {
|
|
542
|
+
if ('functionCall' in p && p.functionCall) {
|
|
543
|
+
acc.push({
|
|
544
|
+
...p,
|
|
545
|
+
id:
|
|
546
|
+
'id' in p.functionCall && typeof p.functionCall.id === 'string'
|
|
547
|
+
? p.functionCall.id
|
|
548
|
+
: uuidv4(),
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
return acc;
|
|
552
|
+
},
|
|
553
|
+
[] as (
|
|
554
|
+
| undefined
|
|
555
|
+
| (FunctionCallPart & { id: string; thoughtSignature?: string })
|
|
556
|
+
)[]
|
|
557
|
+
) ?? [];
|
|
558
|
+
|
|
513
559
|
let content: MessageContent | undefined;
|
|
514
560
|
// Checks if some parts do not have text. If false, it means that the content is a string.
|
|
515
561
|
const reasoningParts: string[] = [];
|
|
@@ -529,27 +575,30 @@ export function convertResponseContentToChatGenerationChunk(
|
|
|
529
575
|
}
|
|
530
576
|
content = textParts.join('');
|
|
531
577
|
} else if (candidateContent && Array.isArray(candidateContent.parts)) {
|
|
532
|
-
content = candidateContent.parts
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
578
|
+
content = candidateContent.parts
|
|
579
|
+
.map((p) => {
|
|
580
|
+
if ('text' in p && 'thought' in p && p.thought === true) {
|
|
581
|
+
reasoningParts.push(p.text ?? '');
|
|
582
|
+
return undefined;
|
|
583
|
+
} else if ('text' in p) {
|
|
584
|
+
return {
|
|
585
|
+
type: 'text',
|
|
586
|
+
text: p.text,
|
|
587
|
+
};
|
|
588
|
+
} else if ('executableCode' in p) {
|
|
589
|
+
return {
|
|
590
|
+
type: 'executableCode',
|
|
591
|
+
executableCode: p.executableCode,
|
|
592
|
+
};
|
|
593
|
+
} else if ('codeExecutionResult' in p) {
|
|
594
|
+
return {
|
|
595
|
+
type: 'codeExecutionResult',
|
|
596
|
+
codeExecutionResult: p.codeExecutionResult,
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
return p;
|
|
600
|
+
})
|
|
601
|
+
.filter((p) => p !== undefined);
|
|
553
602
|
} else {
|
|
554
603
|
// no content returned - likely due to abnormal stop reason, e.g. malformed function call
|
|
555
604
|
content = [];
|
|
@@ -566,20 +615,36 @@ export function convertResponseContentToChatGenerationChunk(
|
|
|
566
615
|
}
|
|
567
616
|
|
|
568
617
|
const toolCallChunks: ToolCallChunk[] = [];
|
|
569
|
-
if (functionCalls) {
|
|
618
|
+
if (functionCalls.length > 0) {
|
|
570
619
|
toolCallChunks.push(
|
|
571
620
|
...functionCalls.map((fc) => ({
|
|
572
|
-
...fc,
|
|
573
|
-
args: JSON.stringify(fc.args),
|
|
574
|
-
// Un-commenting this causes LangChain to incorrectly merge tool calls together
|
|
575
|
-
// index: extra.index,
|
|
576
621
|
type: 'tool_call_chunk' as const,
|
|
577
|
-
id:
|
|
622
|
+
id: fc?.id,
|
|
623
|
+
name: fc?.functionCall.name,
|
|
624
|
+
args: JSON.stringify(fc?.functionCall.args),
|
|
578
625
|
}))
|
|
579
626
|
);
|
|
580
627
|
}
|
|
581
628
|
|
|
582
|
-
|
|
629
|
+
// Extract thought signatures from function calls for Gemini 3+
|
|
630
|
+
const functionThoughtSignatures = functionCalls.reduce(
|
|
631
|
+
(acc, fc) => {
|
|
632
|
+
if (
|
|
633
|
+
fc &&
|
|
634
|
+
'thoughtSignature' in fc &&
|
|
635
|
+
typeof fc.thoughtSignature === 'string'
|
|
636
|
+
) {
|
|
637
|
+
acc[fc.id] = fc.thoughtSignature;
|
|
638
|
+
}
|
|
639
|
+
return acc;
|
|
640
|
+
},
|
|
641
|
+
{} as Record<string, string>
|
|
642
|
+
);
|
|
643
|
+
|
|
644
|
+
const additional_kwargs: ChatGeneration['message']['additional_kwargs'] = {
|
|
645
|
+
[_FUNCTION_CALL_THOUGHT_SIGNATURES_MAP_KEY]: functionThoughtSignatures,
|
|
646
|
+
};
|
|
647
|
+
|
|
583
648
|
if (reasoningParts.length > 0) {
|
|
584
649
|
additional_kwargs.reasoning = reasoningParts.join('');
|
|
585
650
|
}
|
|
@@ -608,6 +673,154 @@ export function convertResponseContentToChatGenerationChunk(
|
|
|
608
673
|
});
|
|
609
674
|
}
|
|
610
675
|
|
|
676
|
+
/**
|
|
677
|
+
* Maps a Google GenerateContentResult to a LangChain ChatResult
|
|
678
|
+
*/
|
|
679
|
+
export function mapGenerateContentResultToChatResult(
|
|
680
|
+
response: EnhancedGenerateContentResponse,
|
|
681
|
+
extra?: {
|
|
682
|
+
usageMetadata: UsageMetadata | undefined;
|
|
683
|
+
}
|
|
684
|
+
): ChatResult {
|
|
685
|
+
if (
|
|
686
|
+
!response.candidates ||
|
|
687
|
+
response.candidates.length === 0 ||
|
|
688
|
+
!response.candidates[0]
|
|
689
|
+
) {
|
|
690
|
+
return {
|
|
691
|
+
generations: [],
|
|
692
|
+
llmOutput: {
|
|
693
|
+
filters: response.promptFeedback,
|
|
694
|
+
},
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
const [candidate] = response.candidates as [
|
|
698
|
+
Partial<GenerateContentCandidate> | undefined,
|
|
699
|
+
];
|
|
700
|
+
const { content: candidateContent, ...generationInfo } = candidate ?? {};
|
|
701
|
+
|
|
702
|
+
// Extract function calls directly from parts to preserve thoughtSignature
|
|
703
|
+
const functionCalls =
|
|
704
|
+
candidateContent?.parts.reduce(
|
|
705
|
+
(acc, p) => {
|
|
706
|
+
if ('functionCall' in p && p.functionCall) {
|
|
707
|
+
acc.push({
|
|
708
|
+
...p,
|
|
709
|
+
id:
|
|
710
|
+
'id' in p.functionCall && typeof p.functionCall.id === 'string'
|
|
711
|
+
? p.functionCall.id
|
|
712
|
+
: uuidv4(),
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
return acc;
|
|
716
|
+
},
|
|
717
|
+
[] as (FunctionCallPart & { id: string; thoughtSignature?: string })[]
|
|
718
|
+
) ?? [];
|
|
719
|
+
|
|
720
|
+
let content: MessageContent | undefined;
|
|
721
|
+
const reasoningParts: string[] = [];
|
|
722
|
+
if (
|
|
723
|
+
Array.isArray(candidateContent?.parts) &&
|
|
724
|
+
candidateContent.parts.length === 1 &&
|
|
725
|
+
candidateContent.parts[0].text &&
|
|
726
|
+
!(
|
|
727
|
+
'thought' in candidateContent.parts[0] &&
|
|
728
|
+
candidateContent.parts[0].thought === true
|
|
729
|
+
)
|
|
730
|
+
) {
|
|
731
|
+
content = candidateContent.parts[0].text;
|
|
732
|
+
} else if (
|
|
733
|
+
Array.isArray(candidateContent?.parts) &&
|
|
734
|
+
candidateContent.parts.length > 0
|
|
735
|
+
) {
|
|
736
|
+
content = candidateContent.parts
|
|
737
|
+
.map((p) => {
|
|
738
|
+
if ('text' in p && 'thought' in p && p.thought === true) {
|
|
739
|
+
reasoningParts.push(p.text ?? '');
|
|
740
|
+
return undefined;
|
|
741
|
+
} else if ('text' in p) {
|
|
742
|
+
return {
|
|
743
|
+
type: 'text',
|
|
744
|
+
text: p.text,
|
|
745
|
+
};
|
|
746
|
+
} else if ('executableCode' in p) {
|
|
747
|
+
return {
|
|
748
|
+
type: 'executableCode',
|
|
749
|
+
executableCode: p.executableCode,
|
|
750
|
+
};
|
|
751
|
+
} else if ('codeExecutionResult' in p) {
|
|
752
|
+
return {
|
|
753
|
+
type: 'codeExecutionResult',
|
|
754
|
+
codeExecutionResult: p.codeExecutionResult,
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
return p;
|
|
758
|
+
})
|
|
759
|
+
.filter((p) => p !== undefined);
|
|
760
|
+
} else {
|
|
761
|
+
content = [];
|
|
762
|
+
}
|
|
763
|
+
let text = '';
|
|
764
|
+
if (typeof content === 'string') {
|
|
765
|
+
text = content;
|
|
766
|
+
} else if (Array.isArray(content) && content.length > 0) {
|
|
767
|
+
const block = content.find((b) => 'text' in b) as
|
|
768
|
+
| { text: string }
|
|
769
|
+
| undefined;
|
|
770
|
+
text = block?.text ?? text;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
const additional_kwargs: ChatGeneration['message']['additional_kwargs'] = {
|
|
774
|
+
...generationInfo,
|
|
775
|
+
};
|
|
776
|
+
if (reasoningParts.length > 0) {
|
|
777
|
+
additional_kwargs.reasoning = reasoningParts.join('');
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
// Extract thought signatures from function calls for Gemini 3+
|
|
781
|
+
const functionThoughtSignatures = functionCalls.reduce(
|
|
782
|
+
(acc, fc) => {
|
|
783
|
+
if ('thoughtSignature' in fc && typeof fc.thoughtSignature === 'string') {
|
|
784
|
+
acc[fc.id] = fc.thoughtSignature;
|
|
785
|
+
}
|
|
786
|
+
return acc;
|
|
787
|
+
},
|
|
788
|
+
{} as Record<string, string>
|
|
789
|
+
);
|
|
790
|
+
|
|
791
|
+
const tool_calls = functionCalls.map((fc) => ({
|
|
792
|
+
type: 'tool_call' as const,
|
|
793
|
+
id: fc.id,
|
|
794
|
+
name: fc.functionCall.name,
|
|
795
|
+
args: fc.functionCall.args,
|
|
796
|
+
}));
|
|
797
|
+
|
|
798
|
+
// Store thought signatures map for later retrieval
|
|
799
|
+
additional_kwargs[_FUNCTION_CALL_THOUGHT_SIGNATURES_MAP_KEY] =
|
|
800
|
+
functionThoughtSignatures;
|
|
801
|
+
|
|
802
|
+
const generation: ChatGeneration = {
|
|
803
|
+
text,
|
|
804
|
+
message: new AIMessage({
|
|
805
|
+
content: content ?? '',
|
|
806
|
+
tool_calls,
|
|
807
|
+
additional_kwargs,
|
|
808
|
+
usage_metadata: extra?.usageMetadata,
|
|
809
|
+
}),
|
|
810
|
+
generationInfo,
|
|
811
|
+
};
|
|
812
|
+
return {
|
|
813
|
+
generations: [generation],
|
|
814
|
+
llmOutput: {
|
|
815
|
+
tokenUsage: {
|
|
816
|
+
promptTokens: extra?.usageMetadata?.input_tokens,
|
|
817
|
+
completionTokens: extra?.usageMetadata?.output_tokens,
|
|
818
|
+
totalTokens: extra?.usageMetadata?.total_tokens,
|
|
819
|
+
},
|
|
820
|
+
},
|
|
821
|
+
};
|
|
822
|
+
}
|
|
823
|
+
|
|
611
824
|
export function convertToGenerativeAITools(
|
|
612
825
|
tools: GoogleGenerativeAIToolType[]
|
|
613
826
|
): GoogleGenerativeAIFunctionDeclarationsTool[] {
|