@ai-sdk/harness 1.0.101 → 1.0.103
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/CHANGELOG.md +32 -0
- package/dist/agent/index.d.ts +65 -4
- package/dist/agent/index.js +493 -441
- package/dist/agent/index.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/utils/index.d.ts +17 -3
- package/dist/utils/index.js +321 -18
- package/dist/utils/index.js.map +1 -1
- package/package.json +5 -5
- package/src/agent/harness-agent-session.ts +5 -0
- package/src/agent/harness-agent-settings.ts +82 -3
- package/src/agent/harness-agent.ts +67 -0
- package/src/agent/internal/harness-stream-text-result.ts +33 -13
- package/src/agent/internal/run-prompt.ts +209 -87
- package/src/agent/internal/turn-telemetry.ts +293 -444
- package/src/utils/index.ts +5 -0
- package/src/utils/write-instructions.ts +373 -0
- package/src/utils/write-skills.ts +59 -2
- package/src/v1/harness-v1-session.ts +5 -0
|
@@ -1,515 +1,364 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import type {
|
|
2
|
+
Context,
|
|
3
|
+
InferToolSetContext,
|
|
4
|
+
ModelMessage,
|
|
5
|
+
ToolSet,
|
|
5
6
|
} from '@ai-sdk/provider-utils';
|
|
6
7
|
import { createTelemetryDispatcher } from 'ai/internal';
|
|
7
|
-
import type {
|
|
8
|
+
import type {
|
|
9
|
+
ContentPart,
|
|
10
|
+
GenerateTextOnEndCallback,
|
|
11
|
+
GenerateTextOnStartCallback,
|
|
12
|
+
GenerateTextOnStepEndCallback,
|
|
13
|
+
GenerateTextOnStepStartCallback,
|
|
14
|
+
LanguageModelUsage,
|
|
15
|
+
OnLanguageModelCallEndCallback,
|
|
16
|
+
OnLanguageModelCallStartCallback,
|
|
17
|
+
OnToolExecutionEndCallback,
|
|
18
|
+
OnToolExecutionStartCallback,
|
|
19
|
+
OutputInterface as Output,
|
|
20
|
+
StepResult,
|
|
21
|
+
TelemetryOptions,
|
|
22
|
+
ToolExecutionEndEvent,
|
|
23
|
+
ToolExecutionStartEvent,
|
|
24
|
+
TypedToolCall,
|
|
25
|
+
TypedToolError,
|
|
26
|
+
TypedToolResult,
|
|
27
|
+
} from 'ai';
|
|
8
28
|
import type { HarnessV1ToolSpec } from '../../v1';
|
|
9
29
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
*
|
|
25
|
-
* Telemetry is opt-in: the framework only drives it when `settings.telemetry`
|
|
26
|
-
* is set (the dispatcher then also honours globally-registered integrations).
|
|
27
|
-
*/
|
|
30
|
+
export type HarnessAgentLifecycleCallbacks<
|
|
31
|
+
TOOLS extends ToolSet,
|
|
32
|
+
RUNTIME_CONTEXT extends Context,
|
|
33
|
+
OUTPUT extends Output,
|
|
34
|
+
> = {
|
|
35
|
+
onStart?: GenerateTextOnStartCallback<TOOLS, RUNTIME_CONTEXT, OUTPUT>;
|
|
36
|
+
onStepStart?: GenerateTextOnStepStartCallback<TOOLS, RUNTIME_CONTEXT, OUTPUT>;
|
|
37
|
+
onLanguageModelCallStart?: OnLanguageModelCallStartCallback;
|
|
38
|
+
onLanguageModelCallEnd?: OnLanguageModelCallEndCallback<TOOLS>;
|
|
39
|
+
onToolExecutionStart?: OnToolExecutionStartCallback<TOOLS>;
|
|
40
|
+
onToolExecutionEnd?: OnToolExecutionEndCallback<TOOLS>;
|
|
41
|
+
onStepEnd?: GenerateTextOnStepEndCallback<TOOLS, RUNTIME_CONTEXT>;
|
|
42
|
+
onEnd?: GenerateTextOnEndCallback<TOOLS, RUNTIME_CONTEXT>;
|
|
43
|
+
};
|
|
28
44
|
|
|
29
45
|
type Dispatcher = ReturnType<typeof createTelemetryDispatcher>;
|
|
30
|
-
type EventArg<K extends keyof Dispatcher> = Dispatcher[K] extends
|
|
31
|
-
| ((event: infer E) => unknown)
|
|
32
|
-
| undefined
|
|
33
|
-
? E
|
|
34
|
-
: never;
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* An output content part accumulated over a step — the model's assistant turn.
|
|
38
|
-
* Shaped for the gen_ai output-message conventions `@ai-sdk/otel` reads.
|
|
39
|
-
*/
|
|
40
|
-
export type TurnContentPart =
|
|
41
|
-
| { type: 'text'; text: string }
|
|
42
|
-
| { type: 'reasoning'; text: string }
|
|
43
|
-
| { type: 'tool-call'; toolCallId: string; toolName: string; input: unknown };
|
|
44
46
|
|
|
45
|
-
export interface
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
* Idempotent — the first call wins.
|
|
50
|
-
*/
|
|
47
|
+
export interface TurnLifecycle<
|
|
48
|
+
TOOLS extends ToolSet,
|
|
49
|
+
RUNTIME_CONTEXT extends Context,
|
|
50
|
+
> {
|
|
51
51
|
start(modelId?: string): Promise<void>;
|
|
52
|
-
/** Open a step span lazily, before the first content of a step. */
|
|
53
52
|
ensureStepOpen(): Promise<void>;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
providerMetadata
|
|
59
|
-
/** The model's output content for this step (text/reasoning/tool-calls). */
|
|
60
|
-
content?: TurnContentPart[];
|
|
53
|
+
languageModelCallEnd(input: {
|
|
54
|
+
finishReason: StepResult<TOOLS, RUNTIME_CONTEXT>['finishReason'];
|
|
55
|
+
usage: LanguageModelUsage;
|
|
56
|
+
content: ContentPart<TOOLS>[];
|
|
57
|
+
providerMetadata: StepResult<TOOLS, RUNTIME_CONTEXT>['providerMetadata'];
|
|
61
58
|
}): Promise<void>;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
59
|
+
toolExecutionStart(input: { toolCall: TypedToolCall<TOOLS> }): Promise<void>;
|
|
60
|
+
toolExecutionEnd(input: {
|
|
61
|
+
toolCall: TypedToolCall<TOOLS>;
|
|
62
|
+
toolOutput: TypedToolResult<TOOLS> | TypedToolError<TOOLS>;
|
|
63
|
+
toolExecutionMs: number;
|
|
64
|
+
}): Promise<void>;
|
|
65
|
+
stepEnd(step: StepResult<TOOLS, RUNTIME_CONTEXT>): Promise<void>;
|
|
66
|
+
end(input: {
|
|
67
|
+
steps: StepResult<TOOLS, RUNTIME_CONTEXT>[];
|
|
68
|
+
usage: LanguageModelUsage;
|
|
67
69
|
}): Promise<void>;
|
|
68
|
-
/** Execute a host tool through each telemetry integration's context wrapper. */
|
|
69
70
|
executeTool<T>(input: {
|
|
70
71
|
toolCallId: string;
|
|
71
72
|
execute: () => PromiseLike<T>;
|
|
72
73
|
}): Promise<T>;
|
|
73
|
-
|
|
74
|
-
* A tool execution completed (on its `tool-result` or after host execution).
|
|
75
|
-
* Idempotent per `toolCallId` — the first caller wins, so provider-executed
|
|
76
|
-
* and host-executed paths can both call it without double-counting.
|
|
77
|
-
*/
|
|
78
|
-
toolEnd(
|
|
79
|
-
toolCallId: string,
|
|
80
|
-
output: { ok: true; output: unknown } | { ok: false; error: unknown },
|
|
81
|
-
): Promise<void>;
|
|
82
|
-
/** The turn ended (on a harness `finish`). */
|
|
83
|
-
end(info: { finishReason: unknown; usage: unknown }): Promise<void>;
|
|
84
|
-
/** The turn failed. */
|
|
85
|
-
error(err: unknown): Promise<void>;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const NOOP: TurnTelemetry = {
|
|
89
|
-
async start() {},
|
|
90
|
-
async ensureStepOpen() {},
|
|
91
|
-
async stepFinish() {},
|
|
92
|
-
async toolStart() {},
|
|
93
|
-
async executeTool({ execute }) {
|
|
94
|
-
return await execute();
|
|
95
|
-
},
|
|
96
|
-
async toolEnd() {},
|
|
97
|
-
async end() {},
|
|
98
|
-
async error() {},
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
function normalizeFinishReason(finishReason: unknown): unknown {
|
|
102
|
-
if (
|
|
103
|
-
finishReason != null &&
|
|
104
|
-
typeof finishReason === 'object' &&
|
|
105
|
-
'unified' in finishReason
|
|
106
|
-
) {
|
|
107
|
-
return (finishReason as { unified: unknown }).unified;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return finishReason;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function addTokenCounts(
|
|
114
|
-
tokenCount1: number | undefined,
|
|
115
|
-
tokenCount2: number | undefined,
|
|
116
|
-
): number | undefined {
|
|
117
|
-
return tokenCount1 == null && tokenCount2 == null
|
|
118
|
-
? undefined
|
|
119
|
-
: (tokenCount1 ?? 0) + (tokenCount2 ?? 0);
|
|
74
|
+
error(error: unknown): Promise<void>;
|
|
120
75
|
}
|
|
121
76
|
|
|
122
|
-
function
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
const inputTokens = (usage as { inputTokens: unknown }).inputTokens;
|
|
133
|
-
const outputTokens = (usage as { outputTokens: unknown }).outputTokens;
|
|
134
|
-
|
|
135
|
-
if (
|
|
136
|
-
inputTokens == null ||
|
|
137
|
-
typeof inputTokens !== 'object' ||
|
|
138
|
-
outputTokens == null ||
|
|
139
|
-
typeof outputTokens !== 'object'
|
|
140
|
-
) {
|
|
141
|
-
return usage;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const input = inputTokens as Record<string, number | undefined>;
|
|
145
|
-
const output = outputTokens as Record<string, number | undefined>;
|
|
146
|
-
|
|
147
|
-
return {
|
|
148
|
-
inputTokens: input.total,
|
|
149
|
-
inputTokenDetails: {
|
|
150
|
-
noCacheTokens: input.noCache,
|
|
151
|
-
cacheReadTokens: input.cacheRead,
|
|
152
|
-
cacheWriteTokens: input.cacheWrite,
|
|
153
|
-
},
|
|
154
|
-
outputTokens: output.total,
|
|
155
|
-
outputTokenDetails: {
|
|
156
|
-
textTokens: output.text,
|
|
157
|
-
reasoningTokens: output.reasoning,
|
|
158
|
-
},
|
|
159
|
-
totalTokens: addTokenCounts(input.total, output.total),
|
|
160
|
-
raw: (usage as { raw?: LanguageModelUsage['raw'] }).raw,
|
|
161
|
-
};
|
|
77
|
+
async function notify<EVENT>(
|
|
78
|
+
event: EVENT,
|
|
79
|
+
...callbacks: Array<((event: EVENT) => unknown) | undefined>
|
|
80
|
+
): Promise<void> {
|
|
81
|
+
await Promise.allSettled(
|
|
82
|
+
callbacks.map(async callback => {
|
|
83
|
+
await callback?.(event);
|
|
84
|
+
}),
|
|
85
|
+
);
|
|
162
86
|
}
|
|
163
87
|
|
|
164
|
-
export function
|
|
88
|
+
export function createTurnLifecycle<
|
|
89
|
+
TOOLS extends ToolSet,
|
|
90
|
+
RUNTIME_CONTEXT extends Context,
|
|
91
|
+
OUTPUT extends Output,
|
|
92
|
+
>(options: {
|
|
93
|
+
callId: string;
|
|
165
94
|
telemetry: TelemetryOptions | undefined;
|
|
95
|
+
callbacks: HarnessAgentLifecycleCallbacks<TOOLS, RUNTIME_CONTEXT, OUTPUT>;
|
|
166
96
|
harnessId: string;
|
|
167
97
|
modelId: string | undefined;
|
|
168
98
|
instructions: string | undefined;
|
|
169
|
-
tools:
|
|
99
|
+
tools: TOOLS;
|
|
100
|
+
toolsContext: InferToolSetContext<TOOLS>;
|
|
170
101
|
activeToolNames: string[];
|
|
171
102
|
toolSpecs: HarnessV1ToolSpec[];
|
|
172
|
-
|
|
173
|
-
runtimeContext:
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
let
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
];
|
|
103
|
+
messages: ModelMessage[];
|
|
104
|
+
runtimeContext: RUNTIME_CONTEXT;
|
|
105
|
+
output: OUTPUT | undefined;
|
|
106
|
+
}): TurnLifecycle<TOOLS, RUNTIME_CONTEXT> {
|
|
107
|
+
const telemetry =
|
|
108
|
+
options.telemetry == null
|
|
109
|
+
? ({} as Dispatcher)
|
|
110
|
+
: createTelemetryDispatcher({ telemetry: options.telemetry });
|
|
111
|
+
const provider = `harness:${options.harnessId}`;
|
|
112
|
+
let modelId = options.modelId ?? '';
|
|
113
|
+
let started = false;
|
|
114
|
+
let stepOpen = false;
|
|
115
|
+
let stepNumber = 0;
|
|
116
|
+
let ended = false;
|
|
117
|
+
let modelCallStartedAt = 0;
|
|
118
|
+
const completedSteps: StepResult<TOOLS, RUNTIME_CONTEXT>[] = [];
|
|
189
119
|
const languageModelTools =
|
|
190
|
-
|
|
120
|
+
options.toolSpecs.length === 0
|
|
191
121
|
? undefined
|
|
192
|
-
:
|
|
122
|
+
: options.toolSpecs.map(tool => ({
|
|
193
123
|
type: 'function' as const,
|
|
194
124
|
name: tool.name,
|
|
195
|
-
...(tool.description
|
|
196
|
-
? {
|
|
197
|
-
: {}),
|
|
198
|
-
...(tool.inputSchema
|
|
199
|
-
? {
|
|
200
|
-
: {}),
|
|
125
|
+
...(tool.description == null
|
|
126
|
+
? {}
|
|
127
|
+
: { description: tool.description }),
|
|
128
|
+
...(tool.inputSchema == null
|
|
129
|
+
? {}
|
|
130
|
+
: { inputSchema: tool.inputSchema }),
|
|
201
131
|
}));
|
|
202
132
|
|
|
203
|
-
|
|
204
|
-
let stepOpen = false;
|
|
205
|
-
let stepNumber = 0;
|
|
206
|
-
let ended = false;
|
|
207
|
-
let finalStepText = '';
|
|
208
|
-
let finalStepReasoning: Array<{ text: string }> = [];
|
|
209
|
-
let finalStepProviderMetadata: unknown;
|
|
210
|
-
let outputToolCalls: Array<{
|
|
211
|
-
type: 'tool-call';
|
|
212
|
-
toolCallId: string;
|
|
213
|
-
toolName: string;
|
|
214
|
-
input: unknown;
|
|
215
|
-
}> = [];
|
|
216
|
-
/** Tool calls started in the current turn and not yet ended. */
|
|
217
|
-
const openTools = new Map<
|
|
218
|
-
string,
|
|
219
|
-
{ toolCallId: string; toolName: string; input: unknown }
|
|
220
|
-
>();
|
|
221
|
-
|
|
222
|
-
const cast = <K extends keyof Dispatcher>(event: unknown): EventArg<K> =>
|
|
223
|
-
event as EventArg<K>;
|
|
224
|
-
|
|
225
|
-
// onStart — open the operation (root) span. Deferred until `start()` so the
|
|
226
|
-
// runtime-resolved model can be attached to the operation span + trace label.
|
|
227
|
-
const fireStart = async (): Promise<void> => {
|
|
133
|
+
const start = async (overrideModelId?: string): Promise<void> => {
|
|
228
134
|
if (started) return;
|
|
135
|
+
if (overrideModelId != null) modelId = overrideModelId;
|
|
229
136
|
started = true;
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
137
|
+
const event = {
|
|
138
|
+
callId: options.callId,
|
|
139
|
+
operationId: 'ai.harness',
|
|
140
|
+
provider,
|
|
141
|
+
modelId,
|
|
142
|
+
tools: options.tools,
|
|
143
|
+
toolChoice: undefined,
|
|
144
|
+
activeTools: options.activeToolNames,
|
|
145
|
+
toolOrder: [],
|
|
146
|
+
maxRetries: 0,
|
|
147
|
+
timeout: undefined,
|
|
148
|
+
headers: undefined,
|
|
149
|
+
providerOptions: undefined,
|
|
150
|
+
output: options.output,
|
|
151
|
+
toolsContext: options.toolsContext,
|
|
152
|
+
runtimeContext: options.runtimeContext,
|
|
153
|
+
instructions: options.instructions,
|
|
154
|
+
messages: options.messages,
|
|
155
|
+
};
|
|
156
|
+
await notify(
|
|
157
|
+
event,
|
|
158
|
+
options.callbacks.onStart,
|
|
159
|
+
telemetry.onStart as typeof options.callbacks.onStart,
|
|
249
160
|
);
|
|
250
161
|
};
|
|
251
162
|
|
|
252
|
-
const start = async (overrideModelId?: string): Promise<void> => {
|
|
253
|
-
if (started) return;
|
|
254
|
-
if (overrideModelId) modelId = overrideModelId;
|
|
255
|
-
await fireStart();
|
|
256
|
-
};
|
|
257
|
-
|
|
258
163
|
const ensureStepOpen = async (): Promise<void> => {
|
|
259
|
-
if (!started) await
|
|
164
|
+
if (!started) await start();
|
|
260
165
|
if (stepOpen || ended) return;
|
|
261
166
|
stepOpen = true;
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
167
|
+
const stepStartEvent = {
|
|
168
|
+
callId: options.callId,
|
|
169
|
+
provider,
|
|
170
|
+
modelId,
|
|
171
|
+
stepNumber,
|
|
172
|
+
tools: options.tools,
|
|
173
|
+
toolChoice: undefined,
|
|
174
|
+
activeTools: options.activeToolNames,
|
|
175
|
+
toolOrder: [],
|
|
176
|
+
steps: [...completedSteps],
|
|
177
|
+
providerOptions: undefined,
|
|
178
|
+
output: options.output,
|
|
179
|
+
runtimeContext: options.runtimeContext,
|
|
180
|
+
toolsContext: options.toolsContext,
|
|
181
|
+
instructions: options.instructions,
|
|
182
|
+
messages: options.messages,
|
|
183
|
+
};
|
|
184
|
+
await notify(
|
|
185
|
+
stepStartEvent,
|
|
186
|
+
options.callbacks.onStepStart,
|
|
187
|
+
telemetry.onStepStart as typeof options.callbacks.onStepStart,
|
|
278
188
|
);
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
189
|
+
|
|
190
|
+
const modelStartEvent = {
|
|
191
|
+
callId: options.callId,
|
|
192
|
+
provider,
|
|
193
|
+
modelId,
|
|
194
|
+
instructions: options.instructions,
|
|
195
|
+
messages: options.messages,
|
|
196
|
+
tools: languageModelTools,
|
|
197
|
+
};
|
|
198
|
+
modelCallStartedAt = Date.now();
|
|
199
|
+
await notify(
|
|
200
|
+
modelStartEvent,
|
|
201
|
+
options.callbacks.onLanguageModelCallStart,
|
|
202
|
+
telemetry.onLanguageModelCallStart as typeof options.callbacks.onLanguageModelCallStart,
|
|
290
203
|
);
|
|
291
204
|
};
|
|
292
205
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
usage: unknown;
|
|
297
|
-
content: TurnContentPart[];
|
|
298
|
-
providerMetadata?: unknown;
|
|
299
|
-
}): Promise<void> => {
|
|
300
|
-
const finishReason = normalizeFinishReason(info.finishReason);
|
|
301
|
-
const usage = normalizeUsage(info.usage);
|
|
206
|
+
return {
|
|
207
|
+
start,
|
|
208
|
+
ensureStepOpen,
|
|
302
209
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
210
|
+
async languageModelCallEnd(input) {
|
|
211
|
+
await ensureStepOpen();
|
|
212
|
+
const event = {
|
|
213
|
+
callId: options.callId,
|
|
306
214
|
provider,
|
|
307
215
|
modelId,
|
|
308
|
-
finishReason,
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
? { providerMetadata: info.providerMetadata }
|
|
314
|
-
: {}),
|
|
216
|
+
finishReason: input.finishReason,
|
|
217
|
+
usage: input.usage,
|
|
218
|
+
content: input.content,
|
|
219
|
+
responseId: `${options.callId}-${stepNumber}`,
|
|
220
|
+
providerMetadata: input.providerMetadata,
|
|
315
221
|
performance: {
|
|
316
|
-
responseTimeMs:
|
|
222
|
+
responseTimeMs: Math.max(0, Date.now() - modelCallStartedAt),
|
|
223
|
+
effectiveOutputTokensPerSecond: 0,
|
|
224
|
+
outputTokensPerSecond: undefined,
|
|
225
|
+
inputTokensPerSecond: undefined,
|
|
226
|
+
effectiveTotalTokensPerSecond: 0,
|
|
317
227
|
timeToFirstOutputMs: undefined,
|
|
318
228
|
timeBetweenOutputChunksMs: undefined,
|
|
319
229
|
},
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
for (const part of content) {
|
|
329
|
-
if (part.type === 'text') {
|
|
330
|
-
finalStepText += part.text;
|
|
331
|
-
} else if (part.type === 'reasoning') {
|
|
332
|
-
finalStepReasoning.push({ text: part.text });
|
|
333
|
-
} else if (part.type === 'tool-call') {
|
|
334
|
-
outputToolCalls.push(part);
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
};
|
|
230
|
+
};
|
|
231
|
+
await notify(
|
|
232
|
+
event,
|
|
233
|
+
options.callbacks.onLanguageModelCallEnd,
|
|
234
|
+
telemetry.onLanguageModelCallEnd as typeof options.callbacks.onLanguageModelCallEnd,
|
|
235
|
+
);
|
|
236
|
+
},
|
|
338
237
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
input: call.input,
|
|
351
|
-
dynamic: true,
|
|
352
|
-
},
|
|
353
|
-
toolContext: undefined,
|
|
354
|
-
toolOutput: { type: 'error', error: new Error('tool span unclosed') },
|
|
355
|
-
}),
|
|
238
|
+
async toolExecutionStart({ toolCall }) {
|
|
239
|
+
const event = {
|
|
240
|
+
callId: options.callId,
|
|
241
|
+
messages: options.messages,
|
|
242
|
+
toolCall,
|
|
243
|
+
toolContext: undefined,
|
|
244
|
+
} as ToolExecutionStartEvent<TOOLS>;
|
|
245
|
+
await notify(
|
|
246
|
+
event,
|
|
247
|
+
options.callbacks.onToolExecutionStart,
|
|
248
|
+
telemetry.onToolExecutionStart as typeof options.callbacks.onToolExecutionStart,
|
|
356
249
|
);
|
|
357
|
-
}
|
|
358
|
-
openTools.clear();
|
|
359
|
-
};
|
|
250
|
+
},
|
|
360
251
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
252
|
+
async toolExecutionEnd({ toolCall, toolOutput, toolExecutionMs }) {
|
|
253
|
+
const event = {
|
|
254
|
+
callId: options.callId,
|
|
255
|
+
messages: options.messages,
|
|
256
|
+
toolCall,
|
|
257
|
+
toolContext: undefined,
|
|
258
|
+
toolOutput,
|
|
259
|
+
toolExecutionMs,
|
|
260
|
+
} as ToolExecutionEndEvent<TOOLS>;
|
|
261
|
+
await notify(
|
|
262
|
+
event,
|
|
263
|
+
options.callbacks.onToolExecutionEnd,
|
|
264
|
+
telemetry.onToolExecutionEnd as typeof options.callbacks.onToolExecutionEnd,
|
|
265
|
+
);
|
|
266
|
+
},
|
|
364
267
|
|
|
365
|
-
async
|
|
268
|
+
async stepEnd(step) {
|
|
366
269
|
if (!stepOpen) return;
|
|
367
|
-
|
|
368
|
-
const
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
270
|
+
completedSteps.push(step);
|
|
271
|
+
const telemetryStepEndEvent = Object.assign(Object.create(null), {
|
|
272
|
+
callId: step.callId,
|
|
273
|
+
stepNumber: step.stepNumber,
|
|
274
|
+
model: step.model,
|
|
275
|
+
toolsContext: step.toolsContext,
|
|
276
|
+
runtimeContext: step.runtimeContext,
|
|
277
|
+
content: step.content,
|
|
278
|
+
finishReason: step.finishReason,
|
|
279
|
+
rawFinishReason: step.rawFinishReason,
|
|
280
|
+
usage: step.usage,
|
|
281
|
+
performance: step.performance,
|
|
282
|
+
warnings: step.warnings,
|
|
283
|
+
request: step.request,
|
|
284
|
+
response: step.response,
|
|
285
|
+
providerMetadata: step.providerMetadata,
|
|
286
|
+
text: step.text,
|
|
287
|
+
reasoning: step.reasoning,
|
|
288
|
+
reasoningText: step.reasoningText,
|
|
289
|
+
files: step.files,
|
|
290
|
+
sources: step.sources,
|
|
291
|
+
toolCalls: step.toolCalls,
|
|
292
|
+
staticToolCalls: step.staticToolCalls,
|
|
293
|
+
dynamicToolCalls: step.dynamicToolCalls,
|
|
294
|
+
toolResults: step.toolResults,
|
|
295
|
+
staticToolResults: step.staticToolResults,
|
|
296
|
+
dynamicToolResults: step.dynamicToolResults,
|
|
378
297
|
});
|
|
379
|
-
await
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
finishReason,
|
|
384
|
-
usage,
|
|
385
|
-
providerMetadata: info.providerMetadata,
|
|
386
|
-
content,
|
|
387
|
-
response: {
|
|
388
|
-
id: callId,
|
|
389
|
-
modelId,
|
|
390
|
-
timestamp: new Date(0),
|
|
391
|
-
messages: [],
|
|
392
|
-
},
|
|
393
|
-
}),
|
|
394
|
-
);
|
|
298
|
+
await Promise.allSettled([
|
|
299
|
+
options.callbacks.onStepEnd?.(step),
|
|
300
|
+
telemetry.onStepEnd?.(telemetryStepEndEvent),
|
|
301
|
+
]);
|
|
395
302
|
stepOpen = false;
|
|
396
303
|
stepNumber += 1;
|
|
397
304
|
},
|
|
398
305
|
|
|
399
|
-
async
|
|
400
|
-
await
|
|
401
|
-
if (
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
306
|
+
async end({ steps, usage }) {
|
|
307
|
+
if (!started) await start();
|
|
308
|
+
if (ended || steps.length === 0) return;
|
|
309
|
+
ended = true;
|
|
310
|
+
const finalStep = steps[steps.length - 1]!;
|
|
311
|
+
const event = {
|
|
312
|
+
callId: options.callId,
|
|
313
|
+
stepNumber: finalStep.stepNumber,
|
|
314
|
+
model: finalStep.model,
|
|
315
|
+
toolsContext: finalStep.toolsContext,
|
|
316
|
+
runtimeContext: finalStep.runtimeContext,
|
|
317
|
+
content: steps.flatMap(step => step.content),
|
|
318
|
+
text: finalStep.text,
|
|
319
|
+
reasoning: finalStep.reasoning,
|
|
320
|
+
reasoningText: finalStep.reasoningText,
|
|
321
|
+
files: steps.flatMap(step => step.files),
|
|
322
|
+
sources: steps.flatMap(step => step.sources),
|
|
323
|
+
toolCalls: steps.flatMap(step => step.toolCalls),
|
|
324
|
+
staticToolCalls: steps.flatMap(step => step.staticToolCalls),
|
|
325
|
+
dynamicToolCalls: steps.flatMap(step => step.dynamicToolCalls),
|
|
326
|
+
toolResults: steps.flatMap(step => step.toolResults),
|
|
327
|
+
staticToolResults: steps.flatMap(step => step.staticToolResults),
|
|
328
|
+
dynamicToolResults: steps.flatMap(step => step.dynamicToolResults),
|
|
329
|
+
finishReason: finalStep.finishReason,
|
|
330
|
+
rawFinishReason: finalStep.rawFinishReason,
|
|
331
|
+
usage,
|
|
332
|
+
totalUsage: usage,
|
|
333
|
+
warnings: steps.flatMap(step => step.warnings ?? []),
|
|
334
|
+
request: finalStep.request,
|
|
335
|
+
response: finalStep.response,
|
|
336
|
+
providerMetadata: finalStep.providerMetadata,
|
|
337
|
+
responseMessages: steps.flatMap(step => step.response.messages),
|
|
338
|
+
steps,
|
|
339
|
+
finalStep,
|
|
340
|
+
};
|
|
341
|
+
await notify(
|
|
342
|
+
event,
|
|
343
|
+
options.callbacks.onEnd,
|
|
344
|
+
telemetry.onEnd as typeof options.callbacks.onEnd,
|
|
416
345
|
);
|
|
417
346
|
},
|
|
418
347
|
|
|
419
348
|
async executeTool({ toolCallId, execute }) {
|
|
420
|
-
if (
|
|
421
|
-
return await
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
const normalizedOutput = output.ok
|
|
427
|
-
? { type: 'tool-result' as const, output: output.output }
|
|
428
|
-
: { type: 'error' as const, error: output.error };
|
|
429
|
-
if (call == null) return;
|
|
430
|
-
openTools.delete(toolCallId);
|
|
431
|
-
await dispatcher.onToolExecutionEnd?.(
|
|
432
|
-
cast<'onToolExecutionEnd'>({
|
|
433
|
-
callId,
|
|
434
|
-
toolExecutionMs: 0,
|
|
435
|
-
messages: [],
|
|
436
|
-
toolCall: {
|
|
437
|
-
type: 'tool-call',
|
|
438
|
-
toolCallId: call.toolCallId,
|
|
439
|
-
toolName: call.toolName,
|
|
440
|
-
input: call.input,
|
|
441
|
-
dynamic: true,
|
|
442
|
-
},
|
|
443
|
-
toolContext: undefined,
|
|
444
|
-
toolOutput: normalizedOutput,
|
|
445
|
-
}),
|
|
446
|
-
);
|
|
447
|
-
},
|
|
448
|
-
|
|
449
|
-
async end(info) {
|
|
450
|
-
if (ended) return;
|
|
451
|
-
const finishReason = normalizeFinishReason(info.finishReason);
|
|
452
|
-
const usage = normalizeUsage(info.usage);
|
|
453
|
-
if (!started) await fireStart();
|
|
454
|
-
if (stepOpen) {
|
|
455
|
-
await closeOpenTools();
|
|
456
|
-
await inferenceEnd({
|
|
457
|
-
finishReason,
|
|
458
|
-
usage,
|
|
459
|
-
content: [],
|
|
460
|
-
});
|
|
461
|
-
await dispatcher.onStepEnd?.(
|
|
462
|
-
cast<'onStepEnd'>({
|
|
463
|
-
callId,
|
|
464
|
-
stepNumber,
|
|
465
|
-
finishReason,
|
|
466
|
-
usage,
|
|
467
|
-
providerMetadata: undefined,
|
|
468
|
-
content: [],
|
|
469
|
-
response: {
|
|
470
|
-
id: callId,
|
|
471
|
-
modelId,
|
|
472
|
-
timestamp: new Date(0),
|
|
473
|
-
messages: [],
|
|
474
|
-
},
|
|
475
|
-
}),
|
|
476
|
-
);
|
|
477
|
-
stepOpen = false;
|
|
478
|
-
}
|
|
479
|
-
ended = true;
|
|
480
|
-
await dispatcher.onEnd?.(
|
|
481
|
-
cast<'onEnd'>({
|
|
482
|
-
callId,
|
|
483
|
-
operationId: 'ai.harness',
|
|
484
|
-
finishReason,
|
|
485
|
-
usage,
|
|
486
|
-
totalUsage: usage,
|
|
487
|
-
content: [],
|
|
488
|
-
text: finalStepText,
|
|
489
|
-
finalStep: {
|
|
490
|
-
reasoning: finalStepReasoning,
|
|
491
|
-
providerMetadata: finalStepProviderMetadata,
|
|
492
|
-
},
|
|
493
|
-
toolCalls: outputToolCalls,
|
|
494
|
-
files: [],
|
|
495
|
-
steps: new Array(stepNumber),
|
|
496
|
-
response: {
|
|
497
|
-
id: callId,
|
|
498
|
-
modelId,
|
|
499
|
-
timestamp: new Date(0),
|
|
500
|
-
messages: [],
|
|
501
|
-
},
|
|
502
|
-
runtimeContext,
|
|
503
|
-
}),
|
|
504
|
-
);
|
|
349
|
+
if (telemetry.executeTool == null) return await execute();
|
|
350
|
+
return await telemetry.executeTool({
|
|
351
|
+
callId: options.callId,
|
|
352
|
+
toolCallId,
|
|
353
|
+
execute,
|
|
354
|
+
});
|
|
505
355
|
},
|
|
506
356
|
|
|
507
|
-
async error(
|
|
357
|
+
async error(error) {
|
|
508
358
|
if (ended) return;
|
|
509
|
-
if (!started) await
|
|
510
|
-
await closeOpenTools();
|
|
359
|
+
if (!started) await start();
|
|
511
360
|
ended = true;
|
|
512
|
-
await
|
|
361
|
+
await telemetry.onError?.(error);
|
|
513
362
|
},
|
|
514
363
|
};
|
|
515
364
|
}
|