@librechat/agents 1.9.93 → 1.9.95
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/common/enum.cjs +3 -0
- package/dist/cjs/common/enum.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +3 -3
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/stream.cjs +24 -0
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/esm/common/enum.mjs +3 -0
- package/dist/esm/common/enum.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +3 -3
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/stream.mjs +24 -0
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/types/common/enum.d.ts +3 -0
- package/dist/types/graphs/Graph.d.ts +1 -1
- package/dist/types/mockStream.d.ts +32 -0
- package/dist/types/splitStream.d.ts +35 -0
- package/dist/types/types/stream.d.ts +54 -4
- package/package.json +11 -11
- package/src/common/enum.ts +3 -0
- package/src/graphs/Graph.ts +5 -5
- package/src/mockStream.ts +99 -0
- package/src/splitStream.test.ts +539 -0
- package/src/splitStream.ts +193 -0
- package/src/stream.ts +26 -0
- package/src/types/stream.ts +47 -4
- package/src/utils/llmConfig.ts +3 -1
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { nanoid } from 'nanoid';
|
|
2
|
+
import type * as t from '@/types';
|
|
3
|
+
import { ContentTypes, GraphEvents, StepTypes } from '@/common';
|
|
4
|
+
|
|
5
|
+
export const SEPARATORS = ['.', '?', '!', '۔', '。', '‥', ';', '¡', '¿', '\n', '```'];
|
|
6
|
+
|
|
7
|
+
export class SplitStreamHandler {
|
|
8
|
+
private inCodeBlock = false;
|
|
9
|
+
private inThinkBlock = false;
|
|
10
|
+
private accumulate: boolean;
|
|
11
|
+
tokens: string[] = [];
|
|
12
|
+
lastToken = '';
|
|
13
|
+
reasoningTokens: string[] = [];
|
|
14
|
+
currentStepId?: string;
|
|
15
|
+
currentMessageId?: string;
|
|
16
|
+
currentType?: ContentTypes.TEXT | ContentTypes.THINK;
|
|
17
|
+
currentLength = 0;
|
|
18
|
+
reasoningKey: 'reasoning_content' | 'reasoning' = 'reasoning_content';
|
|
19
|
+
currentIndex = -1;
|
|
20
|
+
blockThreshold = 4500;
|
|
21
|
+
/** The run ID AKA the Message ID associated with the complete generation */
|
|
22
|
+
runId: string;
|
|
23
|
+
handlers?: t.SplitStreamHandlers;
|
|
24
|
+
constructor({
|
|
25
|
+
runId,
|
|
26
|
+
handlers,
|
|
27
|
+
accumulate,
|
|
28
|
+
reasoningKey,
|
|
29
|
+
blockThreshold,
|
|
30
|
+
}: {
|
|
31
|
+
runId: string,
|
|
32
|
+
accumulate?: boolean,
|
|
33
|
+
handlers: t.SplitStreamHandlers
|
|
34
|
+
blockThreshold?: number,
|
|
35
|
+
reasoningKey?: 'reasoning_content' | 'reasoning',
|
|
36
|
+
}) {
|
|
37
|
+
this.runId = runId;
|
|
38
|
+
this.handlers = handlers;
|
|
39
|
+
if (reasoningKey) {
|
|
40
|
+
this.reasoningKey = reasoningKey;
|
|
41
|
+
}
|
|
42
|
+
if (blockThreshold != null) {
|
|
43
|
+
this.blockThreshold = blockThreshold;
|
|
44
|
+
}
|
|
45
|
+
this.accumulate = accumulate ?? false;
|
|
46
|
+
}
|
|
47
|
+
getMessageId = (): string | undefined => {
|
|
48
|
+
const messageId = this.currentMessageId;
|
|
49
|
+
if (messageId != null && messageId) {
|
|
50
|
+
return messageId;
|
|
51
|
+
}
|
|
52
|
+
return undefined;
|
|
53
|
+
};
|
|
54
|
+
createMessageStep = (type?: ContentTypes.TEXT | ContentTypes.THINK): [string, string] => {
|
|
55
|
+
if (type != null && this.currentType !== type) {
|
|
56
|
+
this.currentType = type;
|
|
57
|
+
}
|
|
58
|
+
this.currentLength = 0;
|
|
59
|
+
this.currentIndex += 1;
|
|
60
|
+
this.currentStepId = `step_${nanoid()}`;
|
|
61
|
+
this.currentMessageId = `msg_${nanoid()}`;
|
|
62
|
+
return [this.currentStepId, this.currentMessageId];
|
|
63
|
+
};
|
|
64
|
+
dispatchRunStep = (stepId: string, stepDetails: t.StepDetails): void => {
|
|
65
|
+
const runStep: t.RunStep = {
|
|
66
|
+
id: stepId,
|
|
67
|
+
runId: this.runId,
|
|
68
|
+
type: stepDetails.type,
|
|
69
|
+
index: this.currentIndex,
|
|
70
|
+
stepDetails,
|
|
71
|
+
// usage: null,
|
|
72
|
+
};
|
|
73
|
+
this.handlers?.[GraphEvents.ON_RUN_STEP]?.({ event: GraphEvents.ON_RUN_STEP, data: runStep });
|
|
74
|
+
};
|
|
75
|
+
dispatchMessageDelta = (stepId: string, delta: t.MessageDelta): void => {
|
|
76
|
+
const messageDelta: t.MessageDeltaEvent = {
|
|
77
|
+
id: stepId,
|
|
78
|
+
delta,
|
|
79
|
+
};
|
|
80
|
+
this.handlers?.[GraphEvents.ON_MESSAGE_DELTA]?.({ event: GraphEvents.ON_MESSAGE_DELTA, data: messageDelta });
|
|
81
|
+
};
|
|
82
|
+
dispatchReasoningDelta = (stepId: string, delta: t.ReasoningDelta): void => {
|
|
83
|
+
const reasoningDelta: t.ReasoningDeltaEvent = {
|
|
84
|
+
id: stepId,
|
|
85
|
+
delta,
|
|
86
|
+
};
|
|
87
|
+
this.handlers?.[GraphEvents.ON_REASONING_DELTA]?.({ event: GraphEvents.ON_REASONING_DELTA, data: reasoningDelta });
|
|
88
|
+
};
|
|
89
|
+
handleContent = (content: string, stepId: string, _type: ContentTypes.TEXT | ContentTypes.THINK): void => {
|
|
90
|
+
let type = _type;
|
|
91
|
+
if (this.inThinkBlock && type === ContentTypes.TEXT) {
|
|
92
|
+
type = ContentTypes.THINK;
|
|
93
|
+
}
|
|
94
|
+
if (this.accumulate) {
|
|
95
|
+
if (type === ContentTypes.THINK) {
|
|
96
|
+
this.reasoningTokens.push(content);
|
|
97
|
+
} else {
|
|
98
|
+
this.tokens.push(content);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (this.currentType !== type) {
|
|
103
|
+
const [newStepId, newMessageId] = this.createMessageStep(type);
|
|
104
|
+
this.dispatchRunStep(newStepId, {
|
|
105
|
+
type: StepTypes.MESSAGE_CREATION,
|
|
106
|
+
message_creation: {
|
|
107
|
+
message_id: newMessageId,
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (content.includes('```')) {
|
|
113
|
+
this.inCodeBlock = !this.inCodeBlock;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
this.currentLength += content.length;
|
|
117
|
+
|
|
118
|
+
if (type === ContentTypes.THINK) {
|
|
119
|
+
this.dispatchReasoningDelta(stepId, {
|
|
120
|
+
content: [{
|
|
121
|
+
type: ContentTypes.THINK,
|
|
122
|
+
think: content,
|
|
123
|
+
}],
|
|
124
|
+
});
|
|
125
|
+
} else {
|
|
126
|
+
this.dispatchMessageDelta(stepId, {
|
|
127
|
+
content: [{
|
|
128
|
+
type: ContentTypes.TEXT,
|
|
129
|
+
text: content,
|
|
130
|
+
}],
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (this.inCodeBlock) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (this.currentLength > this.blockThreshold && SEPARATORS.some(sep => content.includes(sep))) {
|
|
139
|
+
const [newStepId, newMessageId] = this.createMessageStep(type);
|
|
140
|
+
this.dispatchRunStep(newStepId, {
|
|
141
|
+
type: StepTypes.MESSAGE_CREATION,
|
|
142
|
+
message_creation: {
|
|
143
|
+
message_id: newMessageId,
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
handle(chunk?: t.CustomChunk): void {
|
|
149
|
+
if (!chunk) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const content = chunk.choices?.[0]?.delta.content ?? '';
|
|
154
|
+
const reasoning_content = chunk.choices?.[0]?.delta[this.reasoningKey] ?? '';
|
|
155
|
+
|
|
156
|
+
if (!content.length && !reasoning_content.length) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (content === '<think>' && !this.inCodeBlock) {
|
|
161
|
+
this.inThinkBlock = true;
|
|
162
|
+
} else if (this.lastToken === '</think>' && !this.inCodeBlock) {
|
|
163
|
+
this.inThinkBlock = false;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
this.lastToken = content;
|
|
167
|
+
|
|
168
|
+
const message_id = this.getMessageId() ?? '';
|
|
169
|
+
|
|
170
|
+
if (!message_id) {
|
|
171
|
+
const initialContentType = this.inThinkBlock ? ContentTypes.THINK : ContentTypes.TEXT;
|
|
172
|
+
const initialType = reasoning_content ? ContentTypes.THINK : initialContentType;
|
|
173
|
+
const [stepId, message_id] = this.createMessageStep(initialType);
|
|
174
|
+
this.dispatchRunStep(stepId, {
|
|
175
|
+
type: StepTypes.MESSAGE_CREATION,
|
|
176
|
+
message_creation: {
|
|
177
|
+
message_id,
|
|
178
|
+
},
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const stepId = this.currentStepId ?? '';
|
|
183
|
+
if (!stepId) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (reasoning_content) {
|
|
188
|
+
this.handleContent(reasoning_content, stepId, ContentTypes.THINK);
|
|
189
|
+
} else {
|
|
190
|
+
this.handleContent(content, stepId, ContentTypes.TEXT);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
package/src/stream.ts
CHANGED
|
@@ -266,6 +266,17 @@ export function createContentAggregator(): ContentAggregatorResult {
|
|
|
266
266
|
update.tool_call_ids = contentPart.tool_call_ids;
|
|
267
267
|
}
|
|
268
268
|
contentParts[index] = update;
|
|
269
|
+
} else if (
|
|
270
|
+
partType.startsWith(ContentTypes.THINK) &&
|
|
271
|
+
ContentTypes.THINK in contentPart &&
|
|
272
|
+
typeof contentPart.think === 'string'
|
|
273
|
+
) {
|
|
274
|
+
const currentContent = contentParts[index] as t.ReasoningDeltaUpdate;
|
|
275
|
+
const update: t.ReasoningDeltaUpdate = {
|
|
276
|
+
type: ContentTypes.THINK,
|
|
277
|
+
think: (currentContent.think || '') + contentPart.think,
|
|
278
|
+
};
|
|
279
|
+
contentParts[index] = update;
|
|
269
280
|
} else if (partType === ContentTypes.IMAGE_URL && 'image_url' in contentPart) {
|
|
270
281
|
const currentContent = contentParts[index] as { type: 'image_url'; image_url: string };
|
|
271
282
|
contentParts[index] = {
|
|
@@ -332,6 +343,21 @@ export function createContentAggregator(): ContentAggregatorResult {
|
|
|
332
343
|
? messageDelta.delta.content[0]
|
|
333
344
|
: messageDelta.delta.content;
|
|
334
345
|
|
|
346
|
+
updateContent(runStep.index, contentPart);
|
|
347
|
+
}
|
|
348
|
+
} else if (event === GraphEvents.ON_REASONING_DELTA) {
|
|
349
|
+
const reasoningDelta = data as t.ReasoningDeltaEvent;
|
|
350
|
+
const runStep = stepMap.get(reasoningDelta.id);
|
|
351
|
+
if (!runStep) {
|
|
352
|
+
console.warn('No run step or runId found for message delta event');
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (reasoningDelta.delta.content) {
|
|
357
|
+
const contentPart = Array.isArray(reasoningDelta.delta.content)
|
|
358
|
+
? reasoningDelta.delta.content[0]
|
|
359
|
+
: reasoningDelta.delta.content;
|
|
360
|
+
|
|
335
361
|
updateContent(runStep.index, contentPart);
|
|
336
362
|
}
|
|
337
363
|
} else if (event === GraphEvents.ON_RUN_STEP_DELTA) {
|
package/src/types/stream.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
// src/types/stream.ts
|
|
2
|
+
import type OpenAITypes from 'openai';
|
|
2
3
|
import type { MessageContentImageUrl, MessageContentText, ToolMessage, BaseMessage } from '@langchain/core/messages';
|
|
3
4
|
import type { ToolCall, ToolCallChunk } from '@langchain/core/messages/tool';
|
|
4
5
|
import type { LLMResult, Generation } from '@langchain/core/outputs';
|
|
5
|
-
import { StepTypes, ContentTypes } from '@/common/enum';
|
|
6
|
+
import { StepTypes, ContentTypes, GraphEvents } from '@/common/enum';
|
|
6
7
|
|
|
7
8
|
export type HandleLLMEnd = (output: LLMResult, runId: string, parentRunId?: string, tags?: string[]) => void;
|
|
8
9
|
|
|
@@ -54,7 +55,7 @@ export type RunStep = {
|
|
|
54
55
|
index: number; // #new
|
|
55
56
|
stepIndex?: number; // #new
|
|
56
57
|
stepDetails: StepDetails;
|
|
57
|
-
usage
|
|
58
|
+
usage?: null | {
|
|
58
59
|
// Define usage structure if it's ever non-null
|
|
59
60
|
// prompt_tokens: number; // #new
|
|
60
61
|
// completion_tokens: number; // #new
|
|
@@ -178,13 +179,40 @@ export interface MessageDelta {
|
|
|
178
179
|
tool_call_ids?: string[];
|
|
179
180
|
}
|
|
180
181
|
|
|
182
|
+
/**
|
|
183
|
+
* Represents a reasoning delta i.e. any changed fields on a message during
|
|
184
|
+
* streaming.
|
|
185
|
+
*/
|
|
186
|
+
export interface ReasoningDeltaEvent {
|
|
187
|
+
/**
|
|
188
|
+
* The identifier of the message, which can be referenced in API endpoints.
|
|
189
|
+
*/
|
|
190
|
+
id: string;
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* The delta containing the fields that have changed.
|
|
194
|
+
*/
|
|
195
|
+
delta: ReasoningDelta;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* The reasoning delta containing the fields that have changed on the Message.
|
|
200
|
+
*/
|
|
201
|
+
export interface ReasoningDelta {
|
|
202
|
+
/**
|
|
203
|
+
* The content of the message in array of text and/or images.
|
|
204
|
+
*/
|
|
205
|
+
content?: MessageContentComplex[];
|
|
206
|
+
}
|
|
207
|
+
|
|
181
208
|
export type MessageDeltaUpdate = { type: ContentTypes.TEXT; text: string; tool_call_ids?: string[] };
|
|
209
|
+
export type ReasoningDeltaUpdate = { type: ContentTypes.THINK; think: string; };
|
|
182
210
|
|
|
183
|
-
export type ContentType = 'text' | 'image_url' | 'tool_call' | string;
|
|
211
|
+
export type ContentType = 'text' | 'image_url' | 'tool_call' | 'think' | string;
|
|
184
212
|
|
|
185
213
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
186
214
|
export type MessageContentComplex = (MessageContentText | MessageContentImageUrl | (Record<string, any> & {
|
|
187
|
-
type?: 'text' | 'image_url' | string;
|
|
215
|
+
type?: 'text' | 'image_url' | 'think' | string;
|
|
188
216
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
189
217
|
}) | (Record<string, any> & {
|
|
190
218
|
type?: never;
|
|
@@ -192,3 +220,18 @@ export type MessageContentComplex = (MessageContentText | MessageContentImageUrl
|
|
|
192
220
|
tool_call_ids?: string[];
|
|
193
221
|
};
|
|
194
222
|
// #new
|
|
223
|
+
|
|
224
|
+
export type CustomChunk = Partial<OpenAITypes.ChatCompletionChunk> & {
|
|
225
|
+
choices?: Partial<Array<Partial<OpenAITypes.Chat.Completions.ChatCompletionChunk.Choice> & {
|
|
226
|
+
delta?: Partial<OpenAITypes.Chat.Completions.ChatCompletionChunk.Choice.Delta> & {
|
|
227
|
+
reasoning?: string | null;
|
|
228
|
+
reasoning_content?: string | null;
|
|
229
|
+
};
|
|
230
|
+
}>>;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export type SplitStreamHandlers = Partial<{
|
|
234
|
+
[GraphEvents.ON_RUN_STEP]: ({ event, data}: { event: GraphEvents, data: RunStep }) => void;
|
|
235
|
+
[GraphEvents.ON_MESSAGE_DELTA]: ({ event, data}: { event: GraphEvents, data: MessageDeltaEvent }) => void;
|
|
236
|
+
[GraphEvents.ON_REASONING_DELTA]: ({ event, data}: { event: GraphEvents, data: ReasoningDeltaEvent }) => void;
|
|
237
|
+
}>
|
package/src/utils/llmConfig.ts
CHANGED
|
@@ -9,6 +9,7 @@ export const llmConfigs: Record<string, t.LLMConfig | undefined> = {
|
|
|
9
9
|
temperature: 0.7,
|
|
10
10
|
streaming: true,
|
|
11
11
|
streamUsage: true,
|
|
12
|
+
// disableStreaming: true,
|
|
12
13
|
},
|
|
13
14
|
[Providers.OLLAMA]: {
|
|
14
15
|
provider: Providers.OLLAMA,
|
|
@@ -44,7 +45,8 @@ export const llmConfigs: Record<string, t.LLMConfig | undefined> = {
|
|
|
44
45
|
},
|
|
45
46
|
[Providers.BEDROCK]: {
|
|
46
47
|
provider: Providers.BEDROCK,
|
|
47
|
-
model: 'anthropic.claude-3-sonnet-20240229-v1:0',
|
|
48
|
+
// model: 'anthropic.claude-3-sonnet-20240229-v1:0',
|
|
49
|
+
model: 'us.anthropic.claude-3-5-sonnet-20241022-v2:0',
|
|
48
50
|
region: process.env.BEDROCK_AWS_REGION,
|
|
49
51
|
credentials: {
|
|
50
52
|
accessKeyId: process.env.BEDROCK_AWS_ACCESS_KEY_ID!,
|