@librechat/agents 3.1.77-dev.1 → 3.1.77
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/openai/index.cjs +317 -1
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs +318 -2
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/types/llm/openai/index.d.ts +17 -0
- package/package.json +1 -1
- package/src/llm/openai/deepseek.test.ts +479 -0
- package/src/llm/openai/index.ts +484 -1
|
@@ -53,6 +53,47 @@ function normalizeHeaders(headers) {
|
|
|
53
53
|
});
|
|
54
54
|
return Object.fromEntries(output.entries());
|
|
55
55
|
}
|
|
56
|
+
function createUsageMetadata(usage) {
|
|
57
|
+
const usageMetadata = {
|
|
58
|
+
input_tokens: usage?.prompt_tokens ?? 0,
|
|
59
|
+
output_tokens: usage?.completion_tokens ?? 0,
|
|
60
|
+
total_tokens: usage?.total_tokens ?? 0,
|
|
61
|
+
};
|
|
62
|
+
if (usage == null) {
|
|
63
|
+
return usageMetadata;
|
|
64
|
+
}
|
|
65
|
+
const inputTokenDetails = {};
|
|
66
|
+
const outputTokenDetails = {};
|
|
67
|
+
let hasInputTokenDetails = false;
|
|
68
|
+
let hasOutputTokenDetails = false;
|
|
69
|
+
const audioInputTokens = usage.prompt_tokens_details?.audio_tokens;
|
|
70
|
+
const cachedInputTokens = usage.prompt_tokens_details?.cached_tokens;
|
|
71
|
+
const audioOutputTokens = usage.completion_tokens_details?.audio_tokens;
|
|
72
|
+
const reasoningOutputTokens = usage.completion_tokens_details?.reasoning_tokens;
|
|
73
|
+
if (audioInputTokens != null) {
|
|
74
|
+
inputTokenDetails.audio = audioInputTokens;
|
|
75
|
+
hasInputTokenDetails = true;
|
|
76
|
+
}
|
|
77
|
+
if (cachedInputTokens != null) {
|
|
78
|
+
inputTokenDetails.cache_read = cachedInputTokens;
|
|
79
|
+
hasInputTokenDetails = true;
|
|
80
|
+
}
|
|
81
|
+
if (audioOutputTokens != null) {
|
|
82
|
+
outputTokenDetails.audio = audioOutputTokens;
|
|
83
|
+
hasOutputTokenDetails = true;
|
|
84
|
+
}
|
|
85
|
+
if (reasoningOutputTokens != null) {
|
|
86
|
+
outputTokenDetails.reasoning = reasoningOutputTokens;
|
|
87
|
+
hasOutputTokenDetails = true;
|
|
88
|
+
}
|
|
89
|
+
if (hasInputTokenDetails) {
|
|
90
|
+
usageMetadata.input_token_details = inputTokenDetails;
|
|
91
|
+
}
|
|
92
|
+
if (hasOutputTokenDetails) {
|
|
93
|
+
usageMetadata.output_token_details = outputTokenDetails;
|
|
94
|
+
}
|
|
95
|
+
return usageMetadata;
|
|
96
|
+
}
|
|
56
97
|
function getExposedOpenAIClient(completions, responses, preferResponses) {
|
|
57
98
|
const responsesClient = responses.client;
|
|
58
99
|
if (responsesClient?.abortHandler != null) {
|
|
@@ -793,6 +834,54 @@ class ChatDeepSeek extends deepseek.ChatDeepSeek {
|
|
|
793
834
|
static lc_name() {
|
|
794
835
|
return 'LibreChatDeepSeek';
|
|
795
836
|
}
|
|
837
|
+
_convertDeepSeekMessages(messages) {
|
|
838
|
+
return index._convertMessagesToOpenAIParams(messages, this.model, {
|
|
839
|
+
includeReasoningContent: true,
|
|
840
|
+
});
|
|
841
|
+
}
|
|
842
|
+
async _generate(messages$1, options, runManager) {
|
|
843
|
+
options.signal?.throwIfAborted();
|
|
844
|
+
const params = this.invocationParams(options);
|
|
845
|
+
if (params.stream === true) {
|
|
846
|
+
return super._generate(messages$1, options, runManager);
|
|
847
|
+
}
|
|
848
|
+
const messagesMapped = this._convertDeepSeekMessages(messages$1);
|
|
849
|
+
const response = await this.completionWithRetry({
|
|
850
|
+
...params,
|
|
851
|
+
stream: false,
|
|
852
|
+
messages: messagesMapped,
|
|
853
|
+
}, {
|
|
854
|
+
signal: options.signal,
|
|
855
|
+
...options.options,
|
|
856
|
+
});
|
|
857
|
+
const usageMetadata = createUsageMetadata(response.usage);
|
|
858
|
+
const generations = response.choices.map((part) => {
|
|
859
|
+
const text = part.message.content ?? '';
|
|
860
|
+
const generation = {
|
|
861
|
+
text,
|
|
862
|
+
message: this._convertCompletionsMessageToBaseMessage(part.message, response),
|
|
863
|
+
};
|
|
864
|
+
generation.generationInfo = {
|
|
865
|
+
finish_reason: part.finish_reason,
|
|
866
|
+
...(part.logprobs != null ? { logprobs: part.logprobs } : {}),
|
|
867
|
+
};
|
|
868
|
+
if (messages.isAIMessage(generation.message)) {
|
|
869
|
+
generation.message.usage_metadata = usageMetadata;
|
|
870
|
+
}
|
|
871
|
+
generation.message = new messages.AIMessage(Object.fromEntries(Object.entries(generation.message).filter(([key]) => !key.startsWith('lc_'))));
|
|
872
|
+
return generation;
|
|
873
|
+
});
|
|
874
|
+
return {
|
|
875
|
+
generations,
|
|
876
|
+
llmOutput: {
|
|
877
|
+
tokenUsage: {
|
|
878
|
+
promptTokens: usageMetadata.input_tokens,
|
|
879
|
+
completionTokens: usageMetadata.output_tokens,
|
|
880
|
+
totalTokens: usageMetadata.total_tokens,
|
|
881
|
+
},
|
|
882
|
+
},
|
|
883
|
+
};
|
|
884
|
+
}
|
|
796
885
|
_getClientOptions(options) {
|
|
797
886
|
if (!this.client) {
|
|
798
887
|
const openAIEndpointConfig = {
|
|
@@ -817,7 +906,234 @@ class ChatDeepSeek extends deepseek.ChatDeepSeek {
|
|
|
817
906
|
return requestOptions;
|
|
818
907
|
}
|
|
819
908
|
async *_streamResponseChunks(messages, options, runManager) {
|
|
820
|
-
yield* delayStreamChunks(
|
|
909
|
+
yield* delayStreamChunks(this._streamResponseChunksWithReasoning(messages, options, runManager), this._lc_stream_delay);
|
|
910
|
+
}
|
|
911
|
+
/** Parses raw `<think>` fallback tags across chunks and emits sanitized DeepSeek stream chunks. */
|
|
912
|
+
async *_streamResponseChunksWithReasoning(messages$1, options, runManager) {
|
|
913
|
+
const stream = this._streamResponseChunksFromReasoningMessages(messages$1, options);
|
|
914
|
+
const thinkStartTag = '<think>';
|
|
915
|
+
const thinkEndTag = '</think>';
|
|
916
|
+
let tokensBuffer = '';
|
|
917
|
+
let isThinking = false;
|
|
918
|
+
for await (const chunk of stream) {
|
|
919
|
+
if (options.signal?.aborted === true) {
|
|
920
|
+
throw new Error('AbortError');
|
|
921
|
+
}
|
|
922
|
+
const reasoningContent = chunk.message.additional_kwargs.reasoning_content;
|
|
923
|
+
if (reasoningContent != null && reasoningContent !== '') {
|
|
924
|
+
yield* this._yieldDeepSeekStreamChunk(chunk, runManager);
|
|
925
|
+
continue;
|
|
926
|
+
}
|
|
927
|
+
const text = chunk.text;
|
|
928
|
+
if (text === '') {
|
|
929
|
+
yield* this._yieldDeepSeekStreamChunk(chunk, runManager);
|
|
930
|
+
continue;
|
|
931
|
+
}
|
|
932
|
+
tokensBuffer += text;
|
|
933
|
+
while (tokensBuffer !== '') {
|
|
934
|
+
if (isThinking) {
|
|
935
|
+
const thinkEndIndex = tokensBuffer.indexOf(thinkEndTag);
|
|
936
|
+
if (thinkEndIndex !== -1) {
|
|
937
|
+
const thoughtContent = tokensBuffer.substring(0, thinkEndIndex);
|
|
938
|
+
if (thoughtContent !== '') {
|
|
939
|
+
yield* this._yieldDeepSeekReasoningText(chunk, thoughtContent, runManager);
|
|
940
|
+
}
|
|
941
|
+
tokensBuffer = tokensBuffer.substring(thinkEndIndex + thinkEndTag.length);
|
|
942
|
+
isThinking = false;
|
|
943
|
+
continue;
|
|
944
|
+
}
|
|
945
|
+
const splitIndex = this._getDeepSeekPartialTagSplitIndex(tokensBuffer, thinkEndTag);
|
|
946
|
+
if (splitIndex !== -1) {
|
|
947
|
+
const safeToYield = tokensBuffer.substring(0, splitIndex);
|
|
948
|
+
if (safeToYield !== '') {
|
|
949
|
+
yield* this._yieldDeepSeekReasoningText(chunk, safeToYield, runManager);
|
|
950
|
+
}
|
|
951
|
+
tokensBuffer = tokensBuffer.substring(splitIndex);
|
|
952
|
+
break;
|
|
953
|
+
}
|
|
954
|
+
yield* this._yieldDeepSeekReasoningText(chunk, tokensBuffer, runManager);
|
|
955
|
+
tokensBuffer = '';
|
|
956
|
+
break;
|
|
957
|
+
}
|
|
958
|
+
const thinkStartIndex = tokensBuffer.indexOf(thinkStartTag);
|
|
959
|
+
if (thinkStartIndex !== -1) {
|
|
960
|
+
const beforeThink = tokensBuffer.substring(0, thinkStartIndex);
|
|
961
|
+
if (beforeThink !== '') {
|
|
962
|
+
yield* this._yieldDeepSeekStreamChunk(this._createDeepSeekStreamChunk(chunk, beforeThink), runManager);
|
|
963
|
+
}
|
|
964
|
+
tokensBuffer = tokensBuffer.substring(thinkStartIndex + thinkStartTag.length);
|
|
965
|
+
isThinking = true;
|
|
966
|
+
continue;
|
|
967
|
+
}
|
|
968
|
+
const splitIndex = this._getDeepSeekPartialTagSplitIndex(tokensBuffer, thinkStartTag);
|
|
969
|
+
if (splitIndex !== -1) {
|
|
970
|
+
const safeToYield = tokensBuffer.substring(0, splitIndex);
|
|
971
|
+
if (safeToYield !== '') {
|
|
972
|
+
yield* this._yieldDeepSeekStreamChunk(this._createDeepSeekStreamChunk(chunk, safeToYield), runManager);
|
|
973
|
+
}
|
|
974
|
+
tokensBuffer = tokensBuffer.substring(splitIndex);
|
|
975
|
+
break;
|
|
976
|
+
}
|
|
977
|
+
yield* this._yieldDeepSeekStreamChunk(this._createDeepSeekStreamChunk(chunk, tokensBuffer), runManager);
|
|
978
|
+
tokensBuffer = '';
|
|
979
|
+
break;
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
if (tokensBuffer === '') {
|
|
983
|
+
return;
|
|
984
|
+
}
|
|
985
|
+
if (isThinking) {
|
|
986
|
+
yield* this._yieldDeepSeekStreamChunk(new outputs.ChatGenerationChunk({
|
|
987
|
+
message: new messages.AIMessageChunk({
|
|
988
|
+
content: '',
|
|
989
|
+
additional_kwargs: {
|
|
990
|
+
reasoning_content: tokensBuffer,
|
|
991
|
+
},
|
|
992
|
+
}),
|
|
993
|
+
text: '',
|
|
994
|
+
}), runManager);
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
997
|
+
yield* this._yieldDeepSeekStreamChunk(new outputs.ChatGenerationChunk({
|
|
998
|
+
message: new messages.AIMessageChunk({
|
|
999
|
+
content: tokensBuffer,
|
|
1000
|
+
}),
|
|
1001
|
+
text: tokensBuffer,
|
|
1002
|
+
}), runManager);
|
|
1003
|
+
}
|
|
1004
|
+
async *_streamResponseChunksFromReasoningMessages(messages$1, options) {
|
|
1005
|
+
const params = {
|
|
1006
|
+
...this.invocationParams(options, { streaming: true }),
|
|
1007
|
+
stream: true,
|
|
1008
|
+
};
|
|
1009
|
+
const messagesMapped = this._convertDeepSeekMessages(messages$1);
|
|
1010
|
+
const streamIterable = await this.completionWithRetry({
|
|
1011
|
+
...params,
|
|
1012
|
+
messages: messagesMapped,
|
|
1013
|
+
}, {
|
|
1014
|
+
signal: options.signal,
|
|
1015
|
+
...options.options,
|
|
1016
|
+
});
|
|
1017
|
+
let defaultRole;
|
|
1018
|
+
let usage;
|
|
1019
|
+
for await (const data of streamIterable) {
|
|
1020
|
+
if (options.signal?.aborted === true) {
|
|
1021
|
+
throw new Error('AbortError');
|
|
1022
|
+
}
|
|
1023
|
+
if (data.usage != null) {
|
|
1024
|
+
usage = data.usage;
|
|
1025
|
+
}
|
|
1026
|
+
if (data.choices.length === 0) {
|
|
1027
|
+
continue;
|
|
1028
|
+
}
|
|
1029
|
+
const choice = data.choices[0];
|
|
1030
|
+
const { delta } = choice;
|
|
1031
|
+
const messageChunk = this._convertCompletionsDeltaToBaseMessageChunk(delta, data, defaultRole);
|
|
1032
|
+
defaultRole = delta.role ?? defaultRole;
|
|
1033
|
+
if (typeof messageChunk.content !== 'string') {
|
|
1034
|
+
continue;
|
|
1035
|
+
}
|
|
1036
|
+
const messageText = messageChunk.content;
|
|
1037
|
+
const newTokenIndices = {
|
|
1038
|
+
prompt: options.promptIndex ?? 0,
|
|
1039
|
+
completion: choice.index,
|
|
1040
|
+
};
|
|
1041
|
+
const generationInfo = { ...newTokenIndices };
|
|
1042
|
+
if (choice.finish_reason != null) {
|
|
1043
|
+
Object.assign(generationInfo, {
|
|
1044
|
+
finish_reason: choice.finish_reason,
|
|
1045
|
+
system_fingerprint: data.system_fingerprint,
|
|
1046
|
+
model_name: data.model,
|
|
1047
|
+
service_tier: data.service_tier,
|
|
1048
|
+
});
|
|
1049
|
+
}
|
|
1050
|
+
if (this.logprobs === true) {
|
|
1051
|
+
Object.assign(generationInfo, { logprobs: choice.logprobs });
|
|
1052
|
+
}
|
|
1053
|
+
const generationChunk = new outputs.ChatGenerationChunk({
|
|
1054
|
+
message: messageChunk,
|
|
1055
|
+
text: messageText,
|
|
1056
|
+
generationInfo,
|
|
1057
|
+
});
|
|
1058
|
+
yield generationChunk;
|
|
1059
|
+
}
|
|
1060
|
+
if (usage != null) {
|
|
1061
|
+
const usageMetadata = createUsageMetadata(usage);
|
|
1062
|
+
const generationChunk = new outputs.ChatGenerationChunk({
|
|
1063
|
+
message: new messages.AIMessageChunk({
|
|
1064
|
+
content: '',
|
|
1065
|
+
response_metadata: {
|
|
1066
|
+
usage: { ...usage },
|
|
1067
|
+
},
|
|
1068
|
+
usage_metadata: usageMetadata,
|
|
1069
|
+
}),
|
|
1070
|
+
text: '',
|
|
1071
|
+
generationInfo: {
|
|
1072
|
+
prompt: 0,
|
|
1073
|
+
completion: 0,
|
|
1074
|
+
},
|
|
1075
|
+
});
|
|
1076
|
+
yield generationChunk;
|
|
1077
|
+
}
|
|
1078
|
+
if (options.signal?.aborted === true) {
|
|
1079
|
+
throw new Error('AbortError');
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
_createDeepSeekStreamChunk(chunk, content, additionalKwargs, text = content) {
|
|
1083
|
+
if (!(chunk.message instanceof messages.AIMessageChunk)) {
|
|
1084
|
+
return new outputs.ChatGenerationChunk({
|
|
1085
|
+
message: new messages.AIMessageChunk({
|
|
1086
|
+
content,
|
|
1087
|
+
additional_kwargs: additionalKwargs ?? chunk.message.additional_kwargs,
|
|
1088
|
+
response_metadata: chunk.message.response_metadata,
|
|
1089
|
+
id: chunk.message.id,
|
|
1090
|
+
}),
|
|
1091
|
+
text,
|
|
1092
|
+
generationInfo: chunk.generationInfo,
|
|
1093
|
+
});
|
|
1094
|
+
}
|
|
1095
|
+
const message = chunk.message;
|
|
1096
|
+
return new outputs.ChatGenerationChunk({
|
|
1097
|
+
message: new messages.AIMessageChunk({
|
|
1098
|
+
content,
|
|
1099
|
+
additional_kwargs: additionalKwargs ?? message.additional_kwargs,
|
|
1100
|
+
response_metadata: message.response_metadata,
|
|
1101
|
+
tool_calls: message.tool_calls,
|
|
1102
|
+
tool_call_chunks: message.tool_call_chunks,
|
|
1103
|
+
id: message.id,
|
|
1104
|
+
}),
|
|
1105
|
+
text,
|
|
1106
|
+
generationInfo: chunk.generationInfo,
|
|
1107
|
+
});
|
|
1108
|
+
}
|
|
1109
|
+
_createDeepSeekReasoningStreamChunk(chunk, reasoningContent) {
|
|
1110
|
+
return this._createDeepSeekStreamChunk(chunk, '', {
|
|
1111
|
+
...chunk.message.additional_kwargs,
|
|
1112
|
+
reasoning_content: reasoningContent,
|
|
1113
|
+
}, '');
|
|
1114
|
+
}
|
|
1115
|
+
async *_yieldDeepSeekReasoningText(chunk, reasoningContent, runManager) {
|
|
1116
|
+
yield* this._yieldDeepSeekStreamChunk(this._createDeepSeekReasoningStreamChunk(chunk, reasoningContent), runManager);
|
|
1117
|
+
}
|
|
1118
|
+
async *_yieldDeepSeekStreamChunk(chunk, runManager) {
|
|
1119
|
+
yield chunk;
|
|
1120
|
+
await runManager?.handleLLMNewToken(chunk.text, this._getDeepSeekTokenIndices(chunk), undefined, undefined, undefined, { chunk });
|
|
1121
|
+
}
|
|
1122
|
+
_getDeepSeekTokenIndices(chunk) {
|
|
1123
|
+
const prompt = chunk.generationInfo?.prompt;
|
|
1124
|
+
const completion = chunk.generationInfo?.completion;
|
|
1125
|
+
if (typeof prompt === 'number' && typeof completion === 'number') {
|
|
1126
|
+
return { prompt, completion };
|
|
1127
|
+
}
|
|
1128
|
+
return undefined;
|
|
1129
|
+
}
|
|
1130
|
+
_getDeepSeekPartialTagSplitIndex(text, tag) {
|
|
1131
|
+
for (let i = tag.length - 1; i >= 1; i--) {
|
|
1132
|
+
if (text.endsWith(tag.substring(0, i))) {
|
|
1133
|
+
return text.length - i;
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
return -1;
|
|
821
1137
|
}
|
|
822
1138
|
}
|
|
823
1139
|
class ChatMoonshot extends ChatOpenAI {
|