@librechat/agents 3.1.85 → 3.1.87
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/README.md +69 -0
- package/dist/cjs/agents/AgentContext.cjs +7 -2
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/events.cjs +23 -0
- package/dist/cjs/events.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +133 -18
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/index.cjs +251 -53
- package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
- package/dist/cjs/llm/init.cjs +1 -5
- package/dist/cjs/llm/init.cjs.map +1 -1
- package/dist/cjs/llm/openai/index.cjs +113 -24
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
- package/dist/cjs/llm/openrouter/index.cjs +3 -1
- package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
- package/dist/cjs/main.cjs +18 -5
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/openai/index.cjs +253 -0
- package/dist/cjs/openai/index.cjs.map +1 -0
- package/dist/cjs/responses/index.cjs +448 -0
- package/dist/cjs/responses/index.cjs.map +1 -0
- package/dist/cjs/run.cjs +108 -7
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/session/AgentSession.cjs +1057 -0
- package/dist/cjs/session/AgentSession.cjs.map +1 -0
- package/dist/cjs/session/JsonlSessionStore.cjs +425 -0
- package/dist/cjs/session/JsonlSessionStore.cjs.map +1 -0
- package/dist/cjs/session/handlers.cjs +221 -0
- package/dist/cjs/session/handlers.cjs.map +1 -0
- package/dist/cjs/session/ids.cjs +22 -0
- package/dist/cjs/session/ids.cjs.map +1 -0
- package/dist/cjs/session/messageSerialization.cjs +179 -0
- package/dist/cjs/session/messageSerialization.cjs.map +1 -0
- package/dist/cjs/stream.cjs +472 -11
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/summarization/node.cjs +1 -1
- package/dist/cjs/summarization/node.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +177 -59
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/eagerEventExecution.cjs +113 -0
- package/dist/cjs/tools/eagerEventExecution.cjs.map +1 -0
- package/dist/cjs/tools/handlers.cjs +1 -1
- package/dist/cjs/tools/handlers.cjs.map +1 -1
- package/dist/cjs/tools/streamedToolCallSeals.cjs +42 -0
- package/dist/cjs/tools/streamedToolCallSeals.cjs.map +1 -0
- package/dist/esm/agents/AgentContext.mjs +7 -2
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/events.mjs +23 -1
- package/dist/esm/events.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +133 -18
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/llm/anthropic/index.mjs +251 -53
- package/dist/esm/llm/anthropic/index.mjs.map +1 -1
- package/dist/esm/llm/init.mjs +1 -5
- package/dist/esm/llm/init.mjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs +113 -25
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
- package/dist/esm/llm/openrouter/index.mjs +4 -2
- package/dist/esm/llm/openrouter/index.mjs.map +1 -1
- package/dist/esm/main.mjs +5 -1
- package/dist/esm/main.mjs.map +1 -1
- package/dist/esm/openai/index.mjs +246 -0
- package/dist/esm/openai/index.mjs.map +1 -0
- package/dist/esm/responses/index.mjs +440 -0
- package/dist/esm/responses/index.mjs.map +1 -0
- package/dist/esm/run.mjs +108 -7
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/session/AgentSession.mjs +1054 -0
- package/dist/esm/session/AgentSession.mjs.map +1 -0
- package/dist/esm/session/JsonlSessionStore.mjs +422 -0
- package/dist/esm/session/JsonlSessionStore.mjs.map +1 -0
- package/dist/esm/session/handlers.mjs +219 -0
- package/dist/esm/session/handlers.mjs.map +1 -0
- package/dist/esm/session/ids.mjs +17 -0
- package/dist/esm/session/ids.mjs.map +1 -0
- package/dist/esm/session/messageSerialization.mjs +173 -0
- package/dist/esm/session/messageSerialization.mjs.map +1 -0
- package/dist/esm/stream.mjs +473 -12
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/summarization/node.mjs +1 -1
- package/dist/esm/summarization/node.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +177 -59
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/eagerEventExecution.mjs +107 -0
- package/dist/esm/tools/eagerEventExecution.mjs.map +1 -0
- package/dist/esm/tools/handlers.mjs +1 -1
- package/dist/esm/tools/handlers.mjs.map +1 -1
- package/dist/esm/tools/streamedToolCallSeals.mjs +36 -0
- package/dist/esm/tools/streamedToolCallSeals.mjs.map +1 -0
- package/dist/types/events.d.ts +1 -0
- package/dist/types/graphs/Graph.d.ts +24 -9
- package/dist/types/index.d.ts +1 -0
- package/dist/types/llm/openai/index.d.ts +1 -0
- package/dist/types/openai/index.d.ts +75 -0
- package/dist/types/responses/index.d.ts +97 -0
- package/dist/types/run.d.ts +2 -0
- package/dist/types/session/AgentSession.d.ts +32 -0
- package/dist/types/session/JsonlSessionStore.d.ts +67 -0
- package/dist/types/session/handlers.d.ts +8 -0
- package/dist/types/session/ids.d.ts +4 -0
- package/dist/types/session/index.d.ts +5 -0
- package/dist/types/session/messageSerialization.d.ts +7 -0
- package/dist/types/session/types.d.ts +191 -0
- package/dist/types/tools/ToolNode.d.ts +12 -1
- package/dist/types/tools/eagerEventExecution.d.ts +23 -0
- package/dist/types/tools/streamedToolCallSeals.d.ts +13 -0
- package/dist/types/types/hitl.d.ts +4 -0
- package/dist/types/types/run.d.ts +11 -1
- package/dist/types/types/tools.d.ts +36 -0
- package/package.json +19 -2
- package/src/__tests__/stream.eagerEventExecution.test.ts +2458 -0
- package/src/agents/AgentContext.ts +7 -2
- package/src/agents/__tests__/AgentContext.test.ts +254 -5
- package/src/events.ts +29 -0
- package/src/graphs/Graph.ts +224 -50
- package/src/graphs/MultiAgentGraph.ts +1 -1
- package/src/graphs/__tests__/composition.smoke.test.ts +30 -0
- package/src/index.ts +3 -0
- package/src/llm/anthropic/index.ts +356 -84
- package/src/llm/anthropic/llm.spec.ts +64 -0
- package/src/llm/custom-chat-models.smoke.test.ts +175 -4
- package/src/llm/openai/contentBlocks.test.ts +35 -0
- package/src/llm/openai/deepseek.test.ts +201 -2
- package/src/llm/openai/index.ts +171 -26
- package/src/llm/openai/utils/index.ts +22 -0
- package/src/llm/openrouter/index.ts +4 -2
- package/src/openai/__tests__/openai.test.ts +337 -0
- package/src/openai/index.ts +404 -0
- package/src/responses/__tests__/responses.test.ts +652 -0
- package/src/responses/index.ts +677 -0
- package/src/run.ts +158 -8
- package/src/scripts/compare_pi_vs_ours.ts +592 -173
- package/src/scripts/session_live.ts +548 -0
- package/src/session/AgentSession.ts +1432 -0
- package/src/session/JsonlSessionStore.ts +572 -0
- package/src/session/__tests__/JsonlSessionStore.test.ts +1410 -0
- package/src/session/__tests__/handlers.test.ts +161 -0
- package/src/session/handlers.ts +272 -0
- package/src/session/ids.ts +17 -0
- package/src/session/index.ts +44 -0
- package/src/session/messageSerialization.ts +207 -0
- package/src/session/types.ts +275 -0
- package/src/specs/custom-event-await.test.ts +89 -0
- package/src/specs/summarization.test.ts +1 -1
- package/src/stream.ts +755 -48
- package/src/summarization/node.ts +1 -1
- package/src/tools/ToolNode.ts +299 -126
- package/src/tools/__tests__/ToolNode.eagerEventExecution.test.ts +373 -0
- package/src/tools/__tests__/handlers.test.ts +2 -1
- package/src/tools/__tests__/hitl.test.ts +206 -110
- package/src/tools/eagerEventExecution.ts +153 -0
- package/src/tools/handlers.ts +8 -4
- package/src/tools/streamedToolCallSeals.ts +57 -0
- package/src/types/hitl.ts +4 -0
- package/src/types/run.ts +11 -0
- package/src/types/tools.ts +36 -0
- package/dist/cjs/llm/text.cjs +0 -69
- package/dist/cjs/llm/text.cjs.map +0 -1
- package/dist/esm/llm/text.mjs +0 -67
- package/dist/esm/llm/text.mjs.map +0 -1
|
@@ -0,0 +1,677 @@
|
|
|
1
|
+
import { GraphEvents } from '@/common';
|
|
2
|
+
import type { UsageMetadata } from '@langchain/core/messages';
|
|
3
|
+
import type * as t from '@/types';
|
|
4
|
+
|
|
5
|
+
export interface ResponsesCompatibleWriter {
|
|
6
|
+
write(data: string): void | Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type ResponseStatus =
|
|
10
|
+
| 'in_progress'
|
|
11
|
+
| 'completed'
|
|
12
|
+
| 'failed'
|
|
13
|
+
| 'incomplete';
|
|
14
|
+
export type ItemStatus = 'in_progress' | 'incomplete' | 'completed';
|
|
15
|
+
|
|
16
|
+
export interface ResponseContext {
|
|
17
|
+
responseId: string;
|
|
18
|
+
model: string;
|
|
19
|
+
createdAt: number;
|
|
20
|
+
previousResponseId?: string;
|
|
21
|
+
instructions?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface ResponseOutputTextContent {
|
|
25
|
+
type: 'output_text';
|
|
26
|
+
text: string;
|
|
27
|
+
annotations: [];
|
|
28
|
+
logprobs: [];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface ResponseMessageItem {
|
|
32
|
+
type: 'message';
|
|
33
|
+
id: string;
|
|
34
|
+
role: 'assistant';
|
|
35
|
+
status: ItemStatus;
|
|
36
|
+
content: ResponseOutputTextContent[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface ResponseFunctionCallItem {
|
|
40
|
+
type: 'function_call';
|
|
41
|
+
id: string;
|
|
42
|
+
call_id: string;
|
|
43
|
+
name: string;
|
|
44
|
+
arguments: string;
|
|
45
|
+
status: ItemStatus;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface ResponseReasoningItem {
|
|
49
|
+
type: 'reasoning';
|
|
50
|
+
id: string;
|
|
51
|
+
status: ItemStatus;
|
|
52
|
+
content: Array<{ type: 'reasoning_text'; text: string }>;
|
|
53
|
+
summary: [];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export type ResponseOutputItem =
|
|
57
|
+
| ResponseMessageItem
|
|
58
|
+
| ResponseFunctionCallItem
|
|
59
|
+
| ResponseReasoningItem;
|
|
60
|
+
|
|
61
|
+
export interface ResponseObject {
|
|
62
|
+
id: string;
|
|
63
|
+
object: 'response';
|
|
64
|
+
created_at: number;
|
|
65
|
+
completed_at: number | null;
|
|
66
|
+
status: ResponseStatus;
|
|
67
|
+
model: string;
|
|
68
|
+
previous_response_id: string | null;
|
|
69
|
+
instructions: string | null;
|
|
70
|
+
output: ResponseOutputItem[];
|
|
71
|
+
error: { type: string; message: string; code?: string } | null;
|
|
72
|
+
usage: {
|
|
73
|
+
input_tokens: number;
|
|
74
|
+
output_tokens: number;
|
|
75
|
+
total_tokens: number;
|
|
76
|
+
} | null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface ResponseEvent {
|
|
80
|
+
type: string;
|
|
81
|
+
sequence_number: number;
|
|
82
|
+
[key: string]: unknown;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface ResponseTracker {
|
|
86
|
+
sequenceNumber: number;
|
|
87
|
+
items: ResponseOutputItem[];
|
|
88
|
+
message: ResponseMessageItem | undefined;
|
|
89
|
+
reasoning: ResponseReasoningItem | undefined;
|
|
90
|
+
functionCalls: Map<string, ResponseFunctionCallItem>;
|
|
91
|
+
functionCallsByStep: Map<string, Array<ResponseFunctionCallItem | undefined>>;
|
|
92
|
+
responseCreated: boolean;
|
|
93
|
+
usage: {
|
|
94
|
+
inputTokens: number;
|
|
95
|
+
outputTokens: number;
|
|
96
|
+
};
|
|
97
|
+
nextSequence(): number;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface ResponsesHandlerConfig {
|
|
101
|
+
writer: ResponsesCompatibleWriter;
|
|
102
|
+
context: ResponseContext;
|
|
103
|
+
tracker: ResponseTracker;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
let responseItemId = 0;
|
|
107
|
+
|
|
108
|
+
function createItemId(prefix: string): string {
|
|
109
|
+
return `${prefix}_${Date.now().toString(36)}${(responseItemId++).toString(36)}`;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function createResponseTracker(): ResponseTracker {
|
|
113
|
+
const tracker: ResponseTracker = {
|
|
114
|
+
sequenceNumber: 0,
|
|
115
|
+
items: [],
|
|
116
|
+
message: undefined,
|
|
117
|
+
reasoning: undefined,
|
|
118
|
+
functionCalls: new Map(),
|
|
119
|
+
functionCallsByStep: new Map(),
|
|
120
|
+
responseCreated: false,
|
|
121
|
+
usage: {
|
|
122
|
+
inputTokens: 0,
|
|
123
|
+
outputTokens: 0,
|
|
124
|
+
},
|
|
125
|
+
nextSequence: () => tracker.sequenceNumber++,
|
|
126
|
+
};
|
|
127
|
+
return tracker;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function getTokenCount(value: number | null | undefined): number {
|
|
131
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : 0;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
interface ResponseToolCallFragment {
|
|
135
|
+
index?: number;
|
|
136
|
+
id?: string;
|
|
137
|
+
name?: string;
|
|
138
|
+
args?: string | object;
|
|
139
|
+
function?: {
|
|
140
|
+
name?: string;
|
|
141
|
+
arguments?: string | object;
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function getToolCallIndex(
|
|
146
|
+
toolCall: ResponseToolCallFragment,
|
|
147
|
+
fallbackIndex: number
|
|
148
|
+
): number {
|
|
149
|
+
return typeof toolCall.index === 'number' ? toolCall.index : fallbackIndex;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function getToolCallPositionKey(
|
|
153
|
+
stepId: string,
|
|
154
|
+
toolCall: ResponseToolCallFragment,
|
|
155
|
+
fallbackIndex: number
|
|
156
|
+
): string {
|
|
157
|
+
const index = getToolCallIndex(toolCall, fallbackIndex);
|
|
158
|
+
if (stepId !== '') {
|
|
159
|
+
return `${stepId}:${index}`;
|
|
160
|
+
}
|
|
161
|
+
return `tool:${index}`;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function getToolCallIdKey(
|
|
165
|
+
toolCall: ResponseToolCallFragment
|
|
166
|
+
): string | undefined {
|
|
167
|
+
return toolCall.id != null && toolCall.id !== '' ? toolCall.id : undefined;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function getExistingFunctionCall(
|
|
171
|
+
config: ResponsesHandlerConfig,
|
|
172
|
+
idKey: string | undefined,
|
|
173
|
+
positionKey: string | undefined,
|
|
174
|
+
stepId: string,
|
|
175
|
+
toolCall: ResponseToolCallFragment
|
|
176
|
+
): ResponseFunctionCallItem | undefined {
|
|
177
|
+
const keyed =
|
|
178
|
+
idKey == null
|
|
179
|
+
? undefined
|
|
180
|
+
: (config.tracker.functionCalls.get(idKey) ??
|
|
181
|
+
(positionKey == null
|
|
182
|
+
? undefined
|
|
183
|
+
: config.tracker.functionCalls.get(positionKey)));
|
|
184
|
+
if (keyed != null) {
|
|
185
|
+
return keyed;
|
|
186
|
+
}
|
|
187
|
+
const positioned =
|
|
188
|
+
positionKey == null
|
|
189
|
+
? undefined
|
|
190
|
+
: config.tracker.functionCalls.get(positionKey);
|
|
191
|
+
if (positioned != null) {
|
|
192
|
+
return positioned;
|
|
193
|
+
}
|
|
194
|
+
if (stepId === '') {
|
|
195
|
+
return undefined;
|
|
196
|
+
}
|
|
197
|
+
return findFunctionCallByStep(config, stepId, toolCall);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function findFunctionCallByStep(
|
|
201
|
+
config: ResponsesHandlerConfig,
|
|
202
|
+
stepId: string,
|
|
203
|
+
toolCall: ResponseToolCallFragment
|
|
204
|
+
): ResponseFunctionCallItem | undefined {
|
|
205
|
+
const items = config.tracker.functionCallsByStep.get(stepId);
|
|
206
|
+
if (items == null) {
|
|
207
|
+
return undefined;
|
|
208
|
+
}
|
|
209
|
+
const index = typeof toolCall.index === 'number' ? toolCall.index : undefined;
|
|
210
|
+
if (index != null) {
|
|
211
|
+
return items[index];
|
|
212
|
+
}
|
|
213
|
+
const name = getToolCallName(toolCall);
|
|
214
|
+
let fallback: ResponseFunctionCallItem | undefined;
|
|
215
|
+
for (const item of items) {
|
|
216
|
+
if (item == null || item.status === 'completed') {
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
if (name !== '' && item.name !== name) {
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
if (fallback != null) {
|
|
223
|
+
return undefined;
|
|
224
|
+
}
|
|
225
|
+
fallback = item;
|
|
226
|
+
}
|
|
227
|
+
return fallback;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function trackFunctionCallKeys(
|
|
231
|
+
config: ResponsesHandlerConfig,
|
|
232
|
+
item: ResponseFunctionCallItem,
|
|
233
|
+
idKey: string | undefined,
|
|
234
|
+
positionKey: string
|
|
235
|
+
): void {
|
|
236
|
+
config.tracker.functionCalls.set(positionKey, item);
|
|
237
|
+
if (idKey != null) {
|
|
238
|
+
config.tracker.functionCalls.set(idKey, item);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function trackFunctionCallStep(
|
|
243
|
+
config: ResponsesHandlerConfig,
|
|
244
|
+
stepId: string,
|
|
245
|
+
item: ResponseFunctionCallItem,
|
|
246
|
+
fallbackIndex: number
|
|
247
|
+
): void {
|
|
248
|
+
if (stepId === '') {
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
const items = config.tracker.functionCallsByStep.get(stepId) ?? [];
|
|
252
|
+
items[fallbackIndex] = item;
|
|
253
|
+
config.tracker.functionCallsByStep.set(stepId, items);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function getToolCallName(toolCall: ResponseToolCallFragment): string {
|
|
257
|
+
return toolCall.name ?? toolCall.function?.name ?? '';
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function getToolCallArguments(toolCall: ResponseToolCallFragment): string {
|
|
261
|
+
const args = toolCall.args ?? toolCall.function?.arguments;
|
|
262
|
+
if (args == null) {
|
|
263
|
+
return '';
|
|
264
|
+
}
|
|
265
|
+
if (typeof args === 'string') {
|
|
266
|
+
return args;
|
|
267
|
+
}
|
|
268
|
+
return JSON.stringify(args);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
async function ensureFunctionCall(
|
|
272
|
+
config: ResponsesHandlerConfig,
|
|
273
|
+
stepId: string,
|
|
274
|
+
toolCall: ResponseToolCallFragment,
|
|
275
|
+
fallbackIndex: number
|
|
276
|
+
): Promise<ResponseFunctionCallItem> {
|
|
277
|
+
await ensureResponseCreated(config);
|
|
278
|
+
const positionKey = getToolCallPositionKey(stepId, toolCall, fallbackIndex);
|
|
279
|
+
const idKey = getToolCallIdKey(toolCall);
|
|
280
|
+
const existing = getExistingFunctionCall(
|
|
281
|
+
config,
|
|
282
|
+
idKey,
|
|
283
|
+
positionKey,
|
|
284
|
+
stepId,
|
|
285
|
+
toolCall
|
|
286
|
+
);
|
|
287
|
+
const toolCallIndex = getToolCallIndex(toolCall, fallbackIndex);
|
|
288
|
+
const name = getToolCallName(toolCall);
|
|
289
|
+
if (existing) {
|
|
290
|
+
trackFunctionCallKeys(config, existing, idKey, positionKey);
|
|
291
|
+
trackFunctionCallStep(config, stepId, existing, toolCallIndex);
|
|
292
|
+
if (idKey != null) {
|
|
293
|
+
existing.call_id = idKey;
|
|
294
|
+
}
|
|
295
|
+
if (name !== '') {
|
|
296
|
+
existing.name = name;
|
|
297
|
+
}
|
|
298
|
+
return existing;
|
|
299
|
+
}
|
|
300
|
+
const item: ResponseFunctionCallItem = {
|
|
301
|
+
type: 'function_call',
|
|
302
|
+
id: createItemId('fc'),
|
|
303
|
+
call_id: idKey ?? positionKey,
|
|
304
|
+
name,
|
|
305
|
+
arguments: '',
|
|
306
|
+
status: 'in_progress',
|
|
307
|
+
};
|
|
308
|
+
trackFunctionCallKeys(config, item, idKey, positionKey);
|
|
309
|
+
trackFunctionCallStep(config, stepId, item, toolCallIndex);
|
|
310
|
+
config.tracker.items.push(item);
|
|
311
|
+
await writeResponseEvent(config.writer, {
|
|
312
|
+
type: 'response.output_item.added',
|
|
313
|
+
sequence_number: config.tracker.nextSequence(),
|
|
314
|
+
output_index: config.tracker.items.length - 1,
|
|
315
|
+
item,
|
|
316
|
+
});
|
|
317
|
+
return item;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
async function emitFunctionCallArgumentsDelta(params: {
|
|
321
|
+
config: ResponsesHandlerConfig;
|
|
322
|
+
item: ResponseFunctionCallItem;
|
|
323
|
+
delta: string;
|
|
324
|
+
}): Promise<void> {
|
|
325
|
+
if (params.delta === '') {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
params.item.arguments += params.delta;
|
|
329
|
+
await writeResponseEvent(params.config.writer, {
|
|
330
|
+
type: 'response.function_call_arguments.delta',
|
|
331
|
+
sequence_number: params.config.tracker.nextSequence(),
|
|
332
|
+
item_id: params.item.id,
|
|
333
|
+
output_index: params.config.tracker.items.indexOf(params.item),
|
|
334
|
+
call_id: params.item.call_id,
|
|
335
|
+
delta: params.delta,
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
async function emitFunctionCallArgumentsDone(
|
|
340
|
+
config: ResponsesHandlerConfig,
|
|
341
|
+
item: ResponseFunctionCallItem
|
|
342
|
+
): Promise<void> {
|
|
343
|
+
await writeResponseEvent(config.writer, {
|
|
344
|
+
type: 'response.function_call_arguments.done',
|
|
345
|
+
sequence_number: config.tracker.nextSequence(),
|
|
346
|
+
item_id: item.id,
|
|
347
|
+
output_index: config.tracker.items.indexOf(item),
|
|
348
|
+
call_id: item.call_id,
|
|
349
|
+
name: item.name,
|
|
350
|
+
arguments: item.arguments,
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
async function completeFunctionCall(
|
|
355
|
+
config: ResponsesHandlerConfig,
|
|
356
|
+
stepId: string,
|
|
357
|
+
toolCall: ResponseToolCallFragment
|
|
358
|
+
): Promise<void> {
|
|
359
|
+
const fallbackIndex =
|
|
360
|
+
typeof toolCall.index === 'number' ? toolCall.index : undefined;
|
|
361
|
+
const positionKey =
|
|
362
|
+
fallbackIndex == null
|
|
363
|
+
? undefined
|
|
364
|
+
: getToolCallPositionKey(stepId, toolCall, fallbackIndex);
|
|
365
|
+
const item =
|
|
366
|
+
getExistingFunctionCall(
|
|
367
|
+
config,
|
|
368
|
+
getToolCallIdKey(toolCall),
|
|
369
|
+
positionKey,
|
|
370
|
+
stepId,
|
|
371
|
+
toolCall
|
|
372
|
+
) ??
|
|
373
|
+
(await ensureFunctionCall(config, stepId, toolCall, fallbackIndex ?? 0));
|
|
374
|
+
if (item.status === 'completed') {
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
const finalArguments = getToolCallArguments(toolCall);
|
|
378
|
+
if (
|
|
379
|
+
finalArguments !== '' &&
|
|
380
|
+
finalArguments !== item.arguments &&
|
|
381
|
+
finalArguments.startsWith(item.arguments)
|
|
382
|
+
) {
|
|
383
|
+
await emitFunctionCallArgumentsDelta({
|
|
384
|
+
config,
|
|
385
|
+
item,
|
|
386
|
+
delta: finalArguments.slice(item.arguments.length),
|
|
387
|
+
});
|
|
388
|
+
} else if (finalArguments !== '') {
|
|
389
|
+
item.arguments = finalArguments;
|
|
390
|
+
}
|
|
391
|
+
await emitFunctionCallArgumentsDone(config, item);
|
|
392
|
+
item.status = 'completed';
|
|
393
|
+
await writeResponseEvent(config.writer, {
|
|
394
|
+
type: 'response.output_item.done',
|
|
395
|
+
sequence_number: config.tracker.nextSequence(),
|
|
396
|
+
output_index: config.tracker.items.indexOf(item),
|
|
397
|
+
item,
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
export function buildResponse(
|
|
402
|
+
context: ResponseContext,
|
|
403
|
+
tracker: ResponseTracker,
|
|
404
|
+
status: ResponseStatus = 'in_progress'
|
|
405
|
+
): ResponseObject {
|
|
406
|
+
const completed = status === 'completed';
|
|
407
|
+
return {
|
|
408
|
+
id: context.responseId,
|
|
409
|
+
object: 'response',
|
|
410
|
+
created_at: context.createdAt,
|
|
411
|
+
completed_at: completed ? Math.floor(Date.now() / 1000) : null,
|
|
412
|
+
status,
|
|
413
|
+
model: context.model,
|
|
414
|
+
previous_response_id: context.previousResponseId ?? null,
|
|
415
|
+
instructions: context.instructions ?? null,
|
|
416
|
+
output: tracker.items,
|
|
417
|
+
error: null,
|
|
418
|
+
usage: completed
|
|
419
|
+
? {
|
|
420
|
+
input_tokens: tracker.usage.inputTokens,
|
|
421
|
+
output_tokens: tracker.usage.outputTokens,
|
|
422
|
+
total_tokens: tracker.usage.inputTokens + tracker.usage.outputTokens,
|
|
423
|
+
}
|
|
424
|
+
: null,
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
export async function writeResponseEvent(
|
|
429
|
+
writer: ResponsesCompatibleWriter,
|
|
430
|
+
event: ResponseEvent
|
|
431
|
+
): Promise<void> {
|
|
432
|
+
await writer.write(`event: ${event.type}\n`);
|
|
433
|
+
await writer.write(`data: ${JSON.stringify(event)}\n\n`);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
export async function writeResponsesDone(
|
|
437
|
+
writer: ResponsesCompatibleWriter
|
|
438
|
+
): Promise<void> {
|
|
439
|
+
await writer.write('data: [DONE]\n\n');
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
export async function ensureResponseCreated(
|
|
443
|
+
config: ResponsesHandlerConfig
|
|
444
|
+
): Promise<void> {
|
|
445
|
+
if (config.tracker.responseCreated) {
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
config.tracker.responseCreated = true;
|
|
449
|
+
await writeResponseEvent(config.writer, {
|
|
450
|
+
type: 'response.created',
|
|
451
|
+
sequence_number: config.tracker.nextSequence(),
|
|
452
|
+
response: buildResponse(config.context, config.tracker),
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
async function emitOutputContentDone(
|
|
457
|
+
config: ResponsesHandlerConfig,
|
|
458
|
+
item: ResponseMessageItem | ResponseReasoningItem
|
|
459
|
+
): Promise<void> {
|
|
460
|
+
const outputIndex = config.tracker.items.indexOf(item);
|
|
461
|
+
if (item.type === 'message') {
|
|
462
|
+
await writeResponseEvent(config.writer, {
|
|
463
|
+
type: 'response.output_text.done',
|
|
464
|
+
sequence_number: config.tracker.nextSequence(),
|
|
465
|
+
item_id: item.id,
|
|
466
|
+
output_index: outputIndex,
|
|
467
|
+
content_index: 0,
|
|
468
|
+
text: item.content[0].text,
|
|
469
|
+
});
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
await writeResponseEvent(config.writer, {
|
|
473
|
+
type: 'response.reasoning_text.done',
|
|
474
|
+
sequence_number: config.tracker.nextSequence(),
|
|
475
|
+
item_id: item.id,
|
|
476
|
+
output_index: outputIndex,
|
|
477
|
+
content_index: 0,
|
|
478
|
+
text: item.content[0].text,
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
async function ensureMessage(
|
|
483
|
+
config: ResponsesHandlerConfig
|
|
484
|
+
): Promise<ResponseMessageItem> {
|
|
485
|
+
await ensureResponseCreated(config);
|
|
486
|
+
if (config.tracker.message) {
|
|
487
|
+
return config.tracker.message;
|
|
488
|
+
}
|
|
489
|
+
const item: ResponseMessageItem = {
|
|
490
|
+
type: 'message',
|
|
491
|
+
id: createItemId('msg'),
|
|
492
|
+
role: 'assistant',
|
|
493
|
+
status: 'in_progress',
|
|
494
|
+
content: [{ type: 'output_text', text: '', annotations: [], logprobs: [] }],
|
|
495
|
+
};
|
|
496
|
+
config.tracker.message = item;
|
|
497
|
+
config.tracker.items.push(item);
|
|
498
|
+
await writeResponseEvent(config.writer, {
|
|
499
|
+
type: 'response.output_item.added',
|
|
500
|
+
sequence_number: config.tracker.nextSequence(),
|
|
501
|
+
output_index: config.tracker.items.length - 1,
|
|
502
|
+
item,
|
|
503
|
+
});
|
|
504
|
+
return item;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
async function ensureReasoning(
|
|
508
|
+
config: ResponsesHandlerConfig
|
|
509
|
+
): Promise<ResponseReasoningItem> {
|
|
510
|
+
await ensureResponseCreated(config);
|
|
511
|
+
if (config.tracker.reasoning) {
|
|
512
|
+
return config.tracker.reasoning;
|
|
513
|
+
}
|
|
514
|
+
const item: ResponseReasoningItem = {
|
|
515
|
+
type: 'reasoning',
|
|
516
|
+
id: createItemId('reason'),
|
|
517
|
+
status: 'in_progress',
|
|
518
|
+
content: [{ type: 'reasoning_text', text: '' }],
|
|
519
|
+
summary: [],
|
|
520
|
+
};
|
|
521
|
+
config.tracker.reasoning = item;
|
|
522
|
+
config.tracker.items.push(item);
|
|
523
|
+
await writeResponseEvent(config.writer, {
|
|
524
|
+
type: 'response.output_item.added',
|
|
525
|
+
sequence_number: config.tracker.nextSequence(),
|
|
526
|
+
output_index: config.tracker.items.length - 1,
|
|
527
|
+
item,
|
|
528
|
+
});
|
|
529
|
+
return item;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
export function createResponsesEventHandlers(
|
|
533
|
+
config: ResponsesHandlerConfig
|
|
534
|
+
): Record<string, t.EventHandler> {
|
|
535
|
+
return {
|
|
536
|
+
[GraphEvents.ON_MESSAGE_DELTA]: {
|
|
537
|
+
handle: async (_event, data): Promise<void> => {
|
|
538
|
+
const item = await ensureMessage(config);
|
|
539
|
+
for (const part of (data as t.MessageDeltaEvent).delta.content ?? []) {
|
|
540
|
+
if (!('text' in part) || typeof part.text !== 'string') {
|
|
541
|
+
continue;
|
|
542
|
+
}
|
|
543
|
+
item.content[0].text += part.text;
|
|
544
|
+
await writeResponseEvent(config.writer, {
|
|
545
|
+
type: 'response.output_text.delta',
|
|
546
|
+
sequence_number: config.tracker.nextSequence(),
|
|
547
|
+
item_id: item.id,
|
|
548
|
+
output_index: config.tracker.items.indexOf(item),
|
|
549
|
+
content_index: 0,
|
|
550
|
+
delta: part.text,
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
},
|
|
554
|
+
},
|
|
555
|
+
[GraphEvents.ON_REASONING_DELTA]: {
|
|
556
|
+
handle: async (_event, data): Promise<void> => {
|
|
557
|
+
const item = await ensureReasoning(config);
|
|
558
|
+
for (const part of (data as t.ReasoningDeltaEvent).delta.content ??
|
|
559
|
+
[]) {
|
|
560
|
+
let text: string | undefined;
|
|
561
|
+
if ('think' in part && typeof part.think === 'string') {
|
|
562
|
+
text = part.think;
|
|
563
|
+
} else if ('text' in part && typeof part.text === 'string') {
|
|
564
|
+
text = part.text;
|
|
565
|
+
}
|
|
566
|
+
if (typeof text !== 'string') {
|
|
567
|
+
continue;
|
|
568
|
+
}
|
|
569
|
+
item.content[0].text += text;
|
|
570
|
+
await writeResponseEvent(config.writer, {
|
|
571
|
+
type: 'response.reasoning_text.delta',
|
|
572
|
+
sequence_number: config.tracker.nextSequence(),
|
|
573
|
+
item_id: item.id,
|
|
574
|
+
output_index: config.tracker.items.indexOf(item),
|
|
575
|
+
content_index: 0,
|
|
576
|
+
delta: text,
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
},
|
|
580
|
+
},
|
|
581
|
+
[GraphEvents.ON_RUN_STEP]: {
|
|
582
|
+
handle: async (_event, data): Promise<void> => {
|
|
583
|
+
const runStep = data as t.RunStep;
|
|
584
|
+
if (runStep.stepDetails.type !== 'tool_calls') {
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
const toolCalls = runStep.stepDetails.tool_calls ?? [];
|
|
588
|
+
for (let index = 0; index < toolCalls.length; index++) {
|
|
589
|
+
await ensureFunctionCall(config, runStep.id, toolCalls[index], index);
|
|
590
|
+
}
|
|
591
|
+
},
|
|
592
|
+
},
|
|
593
|
+
[GraphEvents.ON_RUN_STEP_DELTA]: {
|
|
594
|
+
handle: async (_event, data): Promise<void> => {
|
|
595
|
+
const runStepDelta = data as t.RunStepDeltaEvent;
|
|
596
|
+
if (runStepDelta.delta.type !== 'tool_calls') {
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
const toolCalls = runStepDelta.delta.tool_calls ?? [];
|
|
600
|
+
for (let index = 0; index < toolCalls.length; index++) {
|
|
601
|
+
const item = await ensureFunctionCall(
|
|
602
|
+
config,
|
|
603
|
+
runStepDelta.id,
|
|
604
|
+
toolCalls[index],
|
|
605
|
+
index
|
|
606
|
+
);
|
|
607
|
+
await emitFunctionCallArgumentsDelta({
|
|
608
|
+
config,
|
|
609
|
+
item,
|
|
610
|
+
delta: getToolCallArguments(toolCalls[index]),
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
},
|
|
614
|
+
},
|
|
615
|
+
[GraphEvents.ON_RUN_STEP_COMPLETED]: {
|
|
616
|
+
handle: async (_event, data): Promise<void> => {
|
|
617
|
+
const completed = data as {
|
|
618
|
+
result?: {
|
|
619
|
+
id?: string;
|
|
620
|
+
index?: number;
|
|
621
|
+
type?: string;
|
|
622
|
+
tool_call?: ResponseToolCallFragment;
|
|
623
|
+
};
|
|
624
|
+
};
|
|
625
|
+
if (!completed.result?.tool_call) {
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
await completeFunctionCall(
|
|
629
|
+
config,
|
|
630
|
+
completed.result.id ?? '',
|
|
631
|
+
completed.result.tool_call
|
|
632
|
+
);
|
|
633
|
+
},
|
|
634
|
+
},
|
|
635
|
+
[GraphEvents.CHAT_MODEL_END]: {
|
|
636
|
+
handle: (_event, data): void => {
|
|
637
|
+
const usage = (data as t.ModelEndData)?.output?.usage_metadata as
|
|
638
|
+
| Partial<UsageMetadata>
|
|
639
|
+
| undefined;
|
|
640
|
+
if (!usage) {
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
config.tracker.usage.inputTokens += getTokenCount(usage.input_tokens);
|
|
644
|
+
config.tracker.usage.outputTokens += getTokenCount(usage.output_tokens);
|
|
645
|
+
},
|
|
646
|
+
},
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
export async function emitResponseCompleted(
|
|
651
|
+
config: ResponsesHandlerConfig
|
|
652
|
+
): Promise<void> {
|
|
653
|
+
await ensureResponseCreated(config);
|
|
654
|
+
for (const item of config.tracker.items) {
|
|
655
|
+
if (item.status === 'completed') {
|
|
656
|
+
continue;
|
|
657
|
+
}
|
|
658
|
+
if (item.type === 'message' || item.type === 'reasoning') {
|
|
659
|
+
await emitOutputContentDone(config, item);
|
|
660
|
+
} else {
|
|
661
|
+
await emitFunctionCallArgumentsDone(config, item);
|
|
662
|
+
}
|
|
663
|
+
item.status = 'completed';
|
|
664
|
+
await writeResponseEvent(config.writer, {
|
|
665
|
+
type: 'response.output_item.done',
|
|
666
|
+
sequence_number: config.tracker.nextSequence(),
|
|
667
|
+
output_index: config.tracker.items.indexOf(item),
|
|
668
|
+
item,
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
await writeResponseEvent(config.writer, {
|
|
672
|
+
type: 'response.completed',
|
|
673
|
+
sequence_number: config.tracker.nextSequence(),
|
|
674
|
+
response: buildResponse(config.context, config.tracker, 'completed'),
|
|
675
|
+
});
|
|
676
|
+
await writeResponsesDone(config.writer);
|
|
677
|
+
}
|