@librechat/agents 3.2.63 → 3.2.64
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/bedrock/utils/message_inputs.cjs +82 -34
- package/dist/cjs/llm/bedrock/utils/message_inputs.cjs.map +1 -1
- package/dist/esm/llm/bedrock/utils/message_inputs.mjs +82 -34
- package/dist/esm/llm/bedrock/utils/message_inputs.mjs.map +1 -1
- package/package.json +1 -1
- package/src/llm/bedrock/utils/message_inputs.test.ts +276 -1
- package/src/llm/bedrock/utils/message_inputs.ts +121 -73
|
@@ -88,6 +88,29 @@ function isSerializableBedrockReasoningBlock(
|
|
|
88
88
|
return content.redactedContent != null && content.redactedContent !== '';
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
function appendSerializableBedrockTextBlock(
|
|
92
|
+
contentBlocks: BedrockContentBlock[],
|
|
93
|
+
text: string
|
|
94
|
+
): boolean {
|
|
95
|
+
if (text === '') {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
const cleanedText = text.replace(/\n/g, '').trim();
|
|
99
|
+
if (cleanedText !== '') {
|
|
100
|
+
contentBlocks.push({ text });
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
const lastBlock = contentBlocks[contentBlocks.length - 1] as
|
|
104
|
+
| BedrockContentBlock
|
|
105
|
+
| undefined;
|
|
106
|
+
if (lastBlock == null || !('text' in lastBlock)) {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
const mergedTextContent = `${lastBlock.text}${text}`;
|
|
110
|
+
(lastBlock as { text: string }).text = mergedTextContent;
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
|
|
91
114
|
/**
|
|
92
115
|
* Concatenate consecutive reasoning blocks in content array.
|
|
93
116
|
*/
|
|
@@ -96,48 +119,57 @@ export function concatenateLangchainReasoningBlocks(
|
|
|
96
119
|
): Array<MessageContentComplex | MessageContentReasoningBlock> {
|
|
97
120
|
const result: Array<MessageContentComplex | MessageContentReasoningBlock> =
|
|
98
121
|
[];
|
|
122
|
+
let pendingReasoning: MessageContentReasoningBlock | undefined;
|
|
123
|
+
|
|
124
|
+
const flushPendingReasoning = (): void => {
|
|
125
|
+
if (pendingReasoning != null) {
|
|
126
|
+
result.push(pendingReasoning);
|
|
127
|
+
pendingReasoning = undefined;
|
|
128
|
+
}
|
|
129
|
+
};
|
|
99
130
|
|
|
100
131
|
for (const block of content) {
|
|
101
|
-
if (block.type
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
lastText !== '' &&
|
|
120
|
-
currentText != null &&
|
|
121
|
-
currentText !== ''
|
|
122
|
-
) {
|
|
123
|
-
lastReasoning.reasoningText!.text = lastText + currentText;
|
|
124
|
-
} else if (
|
|
125
|
-
currentReasoning.reasoningText.signature != null &&
|
|
126
|
-
currentReasoning.reasoningText.signature !== ''
|
|
127
|
-
) {
|
|
128
|
-
lastReasoning.reasoningText!.signature =
|
|
129
|
-
currentReasoning.reasoningText.signature;
|
|
130
|
-
}
|
|
131
|
-
continue;
|
|
132
|
-
}
|
|
132
|
+
if (block.type !== 'reasoning_content') {
|
|
133
|
+
flushPendingReasoning();
|
|
134
|
+
result.push(block);
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const currentReasoning = block as MessageContentReasoningBlock;
|
|
139
|
+
if (currentReasoning.reasoningText != null) {
|
|
140
|
+
const previousText = pendingReasoning?.reasoningText?.text;
|
|
141
|
+
const previousSignature = pendingReasoning?.reasoningText?.signature;
|
|
142
|
+
const { text, signature } = currentReasoning.reasoningText;
|
|
143
|
+
const mergedReasoningText: { text?: string; signature?: string } = {};
|
|
144
|
+
if (previousText !== undefined || text !== undefined) {
|
|
145
|
+
mergedReasoningText.text = (previousText ?? '') + (text ?? '');
|
|
146
|
+
}
|
|
147
|
+
if (previousSignature !== undefined || signature !== undefined) {
|
|
148
|
+
mergedReasoningText.signature =
|
|
149
|
+
(previousSignature ?? '') + (signature ?? '');
|
|
133
150
|
}
|
|
151
|
+
pendingReasoning = {
|
|
152
|
+
type: 'reasoning_content',
|
|
153
|
+
reasoningText: mergedReasoningText,
|
|
154
|
+
};
|
|
134
155
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
156
|
+
// A signature seals the accumulated reasoning text. Any following
|
|
157
|
+
// reasoning block starts a new independently signed Bedrock block.
|
|
158
|
+
if ('signature' in currentReasoning.reasoningText) {
|
|
159
|
+
flushPendingReasoning();
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (currentReasoning.redactedContent != null) {
|
|
164
|
+
flushPendingReasoning();
|
|
165
|
+
result.push({
|
|
166
|
+
type: 'reasoning_content',
|
|
167
|
+
redactedContent: currentReasoning.redactedContent,
|
|
168
|
+
});
|
|
138
169
|
}
|
|
139
170
|
}
|
|
140
171
|
|
|
172
|
+
flushPendingReasoning();
|
|
141
173
|
return result;
|
|
142
174
|
}
|
|
143
175
|
|
|
@@ -666,23 +698,7 @@ function convertAIMessageToConverseMessage(msg: BaseMessage): BedrockMessage {
|
|
|
666
698
|
concatenatedBlocks.forEach((block) => {
|
|
667
699
|
if (block.type === 'text') {
|
|
668
700
|
const text = (block as { text?: string }).text ?? '';
|
|
669
|
-
|
|
670
|
-
if (text === '') {
|
|
671
|
-
return;
|
|
672
|
-
}
|
|
673
|
-
// Merge whitespace/newlines with previous text blocks to avoid validation errors.
|
|
674
|
-
const cleanedText = text.replace(/\n/g, '').trim();
|
|
675
|
-
if (cleanedText === '') {
|
|
676
|
-
if (contentBlocks.length > 0) {
|
|
677
|
-
const lastBlock = contentBlocks[contentBlocks.length - 1];
|
|
678
|
-
if ('text' in lastBlock) {
|
|
679
|
-
const mergedTextContent = `${lastBlock.text}${text}`;
|
|
680
|
-
(lastBlock as { text: string }).text = mergedTextContent;
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
} else {
|
|
684
|
-
contentBlocks.push({ text });
|
|
685
|
-
}
|
|
701
|
+
appendSerializableBedrockTextBlock(contentBlocks, text);
|
|
686
702
|
} else if (block.type === 'reasoning_content') {
|
|
687
703
|
const reasoningBlock = block as MessageContentReasoningBlock;
|
|
688
704
|
// Bedrock Converse rejects reasoningContent whose reasoningText.text is
|
|
@@ -757,26 +773,35 @@ function convertAIMessageToConverseMessage(msg: BaseMessage): BedrockMessage {
|
|
|
757
773
|
function convertFromV1ToChatBedrockConverseMessage(
|
|
758
774
|
msg: BaseMessage
|
|
759
775
|
): BedrockMessage {
|
|
776
|
+
const contentBlocks: BedrockContentBlock[] = [];
|
|
760
777
|
const assistantMsg: BedrockMessage = {
|
|
761
778
|
role: 'assistant',
|
|
762
|
-
content:
|
|
779
|
+
content: contentBlocks,
|
|
763
780
|
};
|
|
781
|
+
let droppedUnserializableContent = false;
|
|
782
|
+
let unconvertedContentType: string | undefined;
|
|
764
783
|
|
|
765
784
|
if (Array.isArray(msg.content)) {
|
|
766
|
-
|
|
767
|
-
MessageContentComplex | MessageContentReasoningBlock
|
|
768
|
-
|
|
785
|
+
const concatenatedBlocks = concatenateLangchainReasoningBlocks(
|
|
786
|
+
msg.content as Array<MessageContentComplex | MessageContentReasoningBlock>
|
|
787
|
+
);
|
|
788
|
+
for (const block of concatenatedBlocks) {
|
|
769
789
|
if (typeof block === 'string') {
|
|
770
|
-
|
|
790
|
+
if (!appendSerializableBedrockTextBlock(contentBlocks, block)) {
|
|
791
|
+
droppedUnserializableContent = true;
|
|
792
|
+
}
|
|
771
793
|
} else if (block.type === 'text') {
|
|
772
|
-
|
|
794
|
+
const text = (block as { text?: string }).text ?? '';
|
|
795
|
+
if (!appendSerializableBedrockTextBlock(contentBlocks, text)) {
|
|
796
|
+
droppedUnserializableContent = true;
|
|
797
|
+
}
|
|
773
798
|
} else if (block.type === 'tool_call') {
|
|
774
799
|
const toolCall = block as {
|
|
775
800
|
id: string;
|
|
776
801
|
name: string;
|
|
777
802
|
args: Record<string, unknown>;
|
|
778
803
|
};
|
|
779
|
-
|
|
804
|
+
contentBlocks.push({
|
|
780
805
|
toolUse: {
|
|
781
806
|
toolUseId: toolCall.id,
|
|
782
807
|
name: toolCall.name,
|
|
@@ -784,38 +809,51 @@ function convertFromV1ToChatBedrockConverseMessage(
|
|
|
784
809
|
},
|
|
785
810
|
} as BedrockContentBlock);
|
|
786
811
|
} else if (block.type === 'reasoning') {
|
|
787
|
-
const reasoning = block as { reasoning
|
|
788
|
-
|
|
812
|
+
const reasoning = block as { reasoning?: string };
|
|
813
|
+
/** Bedrock Converse rejects `reasoningText` with a null/empty `text`
|
|
814
|
+
* (`Member must not be null`), e.g. when the producing model omitted
|
|
815
|
+
* reasoning text (`thinking.display: "omitted"`) — drop rather than send. */
|
|
816
|
+
if (reasoning.reasoning == null || reasoning.reasoning === '') {
|
|
817
|
+
droppedUnserializableContent = true;
|
|
818
|
+
continue;
|
|
819
|
+
}
|
|
820
|
+
contentBlocks.push({
|
|
789
821
|
reasoningContent: {
|
|
790
822
|
reasoningText: { text: reasoning.reasoning },
|
|
791
823
|
},
|
|
792
824
|
} as BedrockContentBlock);
|
|
793
825
|
} else if (block.type === 'reasoning_content') {
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
826
|
+
const reasoningBlock = block as MessageContentReasoningBlock;
|
|
827
|
+
if (!isSerializableBedrockReasoningBlock(reasoningBlock)) {
|
|
828
|
+
droppedUnserializableContent = true;
|
|
829
|
+
continue;
|
|
830
|
+
}
|
|
831
|
+
contentBlocks.push({
|
|
832
|
+
reasoningContent:
|
|
833
|
+
langchainReasoningBlockToBedrockReasoningBlock(reasoningBlock),
|
|
798
834
|
} as BedrockContentBlock);
|
|
835
|
+
} else {
|
|
836
|
+
unconvertedContentType ??= block.type;
|
|
799
837
|
}
|
|
800
838
|
}
|
|
801
|
-
} else if (typeof msg.content === 'string'
|
|
802
|
-
|
|
839
|
+
} else if (typeof msg.content === 'string') {
|
|
840
|
+
if (!appendSerializableBedrockTextBlock(contentBlocks, msg.content)) {
|
|
841
|
+
droppedUnserializableContent = true;
|
|
842
|
+
}
|
|
803
843
|
}
|
|
804
844
|
|
|
805
845
|
// Also handle tool_calls from the message
|
|
806
846
|
if (isAIMessage(msg) && msg.tool_calls != null && msg.tool_calls.length > 0) {
|
|
807
847
|
// Check if tool calls are already in content
|
|
808
848
|
const existingToolUseIds = new Set(
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
.map(
|
|
812
|
-
(c) => (c as { toolUse: { toolUseId: string } }).toolUse.toolUseId
|
|
813
|
-
) ?? []
|
|
849
|
+
contentBlocks
|
|
850
|
+
.filter((c) => 'toolUse' in c)
|
|
851
|
+
.map((c) => (c as { toolUse: { toolUseId: string } }).toolUse.toolUseId)
|
|
814
852
|
);
|
|
815
853
|
|
|
816
854
|
for (const tc of msg.tool_calls) {
|
|
817
855
|
if (!existingToolUseIds.has(tc.id ?? '')) {
|
|
818
|
-
|
|
856
|
+
contentBlocks.push({
|
|
819
857
|
toolUse: {
|
|
820
858
|
toolUseId: tc.id,
|
|
821
859
|
name: tc.name,
|
|
@@ -826,6 +864,16 @@ function convertFromV1ToChatBedrockConverseMessage(
|
|
|
826
864
|
}
|
|
827
865
|
}
|
|
828
866
|
|
|
867
|
+
const hasNoContent = contentBlocks.length === 0;
|
|
868
|
+
if (hasNoContent && unconvertedContentType != null) {
|
|
869
|
+
throw new Error(
|
|
870
|
+
`Unsupported v1 content block type: ${unconvertedContentType}`
|
|
871
|
+
);
|
|
872
|
+
}
|
|
873
|
+
if (hasNoContent && droppedUnserializableContent) {
|
|
874
|
+
contentBlocks.push({ text: BEDROCK_EMPTY_TEXT_PLACEHOLDER });
|
|
875
|
+
}
|
|
876
|
+
|
|
829
877
|
return assistantMsg;
|
|
830
878
|
}
|
|
831
879
|
|