@dogpile/sdk 0.1.0
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 +37 -0
- package/LICENSE +16 -0
- package/README.md +842 -0
- package/dist/browser/index.d.ts +8 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +4493 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/providers/openai-compatible.d.ts +44 -0
- package/dist/providers/openai-compatible.d.ts.map +1 -0
- package/dist/providers/openai-compatible.js +305 -0
- package/dist/providers/openai-compatible.js.map +1 -0
- package/dist/runtime/broadcast.d.ts +18 -0
- package/dist/runtime/broadcast.d.ts.map +1 -0
- package/dist/runtime/broadcast.js +335 -0
- package/dist/runtime/broadcast.js.map +1 -0
- package/dist/runtime/cancellation.d.ts +6 -0
- package/dist/runtime/cancellation.d.ts.map +1 -0
- package/dist/runtime/cancellation.js +35 -0
- package/dist/runtime/cancellation.js.map +1 -0
- package/dist/runtime/coordinator.d.ts +18 -0
- package/dist/runtime/coordinator.d.ts.map +1 -0
- package/dist/runtime/coordinator.js +434 -0
- package/dist/runtime/coordinator.js.map +1 -0
- package/dist/runtime/decisions.d.ts +5 -0
- package/dist/runtime/decisions.d.ts.map +1 -0
- package/dist/runtime/decisions.js +31 -0
- package/dist/runtime/decisions.js.map +1 -0
- package/dist/runtime/defaults.d.ts +63 -0
- package/dist/runtime/defaults.d.ts.map +1 -0
- package/dist/runtime/defaults.js +426 -0
- package/dist/runtime/defaults.js.map +1 -0
- package/dist/runtime/engine.d.ts +79 -0
- package/dist/runtime/engine.d.ts.map +1 -0
- package/dist/runtime/engine.js +723 -0
- package/dist/runtime/engine.js.map +1 -0
- package/dist/runtime/model.d.ts +14 -0
- package/dist/runtime/model.d.ts.map +1 -0
- package/dist/runtime/model.js +82 -0
- package/dist/runtime/model.js.map +1 -0
- package/dist/runtime/sequential.d.ts +18 -0
- package/dist/runtime/sequential.d.ts.map +1 -0
- package/dist/runtime/sequential.js +277 -0
- package/dist/runtime/sequential.js.map +1 -0
- package/dist/runtime/shared.d.ts +18 -0
- package/dist/runtime/shared.d.ts.map +1 -0
- package/dist/runtime/shared.js +288 -0
- package/dist/runtime/shared.js.map +1 -0
- package/dist/runtime/termination.d.ts +77 -0
- package/dist/runtime/termination.d.ts.map +1 -0
- package/dist/runtime/termination.js +355 -0
- package/dist/runtime/termination.js.map +1 -0
- package/dist/runtime/tools.d.ts +314 -0
- package/dist/runtime/tools.d.ts.map +1 -0
- package/dist/runtime/tools.js +969 -0
- package/dist/runtime/tools.js.map +1 -0
- package/dist/runtime/validation.d.ts +23 -0
- package/dist/runtime/validation.d.ts.map +1 -0
- package/dist/runtime/validation.js +656 -0
- package/dist/runtime/validation.js.map +1 -0
- package/dist/types.d.ts +2434 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +81 -0
- package/dist/types.js.map +1 -0
- package/package.json +157 -0
- package/src/browser/index.ts +7 -0
- package/src/index.ts +195 -0
- package/src/providers/openai-compatible.ts +406 -0
- package/src/runtime/broadcast.test.ts +355 -0
- package/src/runtime/broadcast.ts +428 -0
- package/src/runtime/cancellation.ts +40 -0
- package/src/runtime/coordinator.test.ts +468 -0
- package/src/runtime/coordinator.ts +581 -0
- package/src/runtime/decisions.ts +38 -0
- package/src/runtime/defaults.ts +547 -0
- package/src/runtime/engine.ts +880 -0
- package/src/runtime/model.ts +117 -0
- package/src/runtime/sequential.test.ts +262 -0
- package/src/runtime/sequential.ts +357 -0
- package/src/runtime/shared.test.ts +265 -0
- package/src/runtime/shared.ts +367 -0
- package/src/runtime/termination.ts +463 -0
- package/src/runtime/tools.ts +1518 -0
- package/src/runtime/validation.ts +771 -0
- package/src/types.ts +2729 -0
|
@@ -0,0 +1,4493 @@
|
|
|
1
|
+
const dogpileErrorCodes = [
|
|
2
|
+
"invalid-configuration",
|
|
3
|
+
"aborted",
|
|
4
|
+
"timeout",
|
|
5
|
+
"provider-authentication",
|
|
6
|
+
"provider-invalid-request",
|
|
7
|
+
"provider-invalid-response",
|
|
8
|
+
"provider-not-found",
|
|
9
|
+
"provider-rate-limited",
|
|
10
|
+
"provider-timeout",
|
|
11
|
+
"provider-unavailable",
|
|
12
|
+
"provider-unsupported",
|
|
13
|
+
"provider-error",
|
|
14
|
+
"unknown"
|
|
15
|
+
];
|
|
16
|
+
class DogpileErrorImpl extends Error {
|
|
17
|
+
name = "DogpileError";
|
|
18
|
+
/** Stable machine-readable error code. */
|
|
19
|
+
code;
|
|
20
|
+
/** Whether caller retry policy may safely retry the same operation. */
|
|
21
|
+
retryable;
|
|
22
|
+
/** Configured provider id associated with the failure, when available. */
|
|
23
|
+
providerId;
|
|
24
|
+
/** Optional serializable diagnostic detail. */
|
|
25
|
+
detail;
|
|
26
|
+
/** Original thrown value, if Dogpile wrapped a lower-level failure. */
|
|
27
|
+
cause;
|
|
28
|
+
constructor(options) {
|
|
29
|
+
super(options.message);
|
|
30
|
+
this.code = options.code;
|
|
31
|
+
if (options.retryable !== void 0) {
|
|
32
|
+
this.retryable = options.retryable;
|
|
33
|
+
}
|
|
34
|
+
if (options.providerId !== void 0) {
|
|
35
|
+
this.providerId = options.providerId;
|
|
36
|
+
}
|
|
37
|
+
if (options.detail !== void 0) {
|
|
38
|
+
this.detail = options.detail;
|
|
39
|
+
}
|
|
40
|
+
if (options.cause !== void 0) {
|
|
41
|
+
this.cause = options.cause;
|
|
42
|
+
}
|
|
43
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Cross-realm guard for Dogpile public errors.
|
|
47
|
+
*/
|
|
48
|
+
static isInstance(error) {
|
|
49
|
+
if (error instanceof DogpileErrorImpl) {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
if (!isRecord$2(error)) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
return error.name === "DogpileError" && isDogpileErrorCode(error.code) && typeof error.message === "string";
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* JSON-safe representation for logs, traces, and observability tools.
|
|
59
|
+
*/
|
|
60
|
+
toJSON() {
|
|
61
|
+
return {
|
|
62
|
+
name: this.name,
|
|
63
|
+
code: this.code,
|
|
64
|
+
message: this.message,
|
|
65
|
+
...this.retryable !== void 0 ? { retryable: this.retryable } : {},
|
|
66
|
+
...this.providerId !== void 0 ? { providerId: this.providerId } : {},
|
|
67
|
+
...this.detail !== void 0 ? { detail: this.detail } : {}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const DogpileError = DogpileErrorImpl;
|
|
72
|
+
function isDogpileErrorCode(value) {
|
|
73
|
+
return typeof value === "string" && dogpileErrorCodes.includes(value);
|
|
74
|
+
}
|
|
75
|
+
function isRecord$2(value) {
|
|
76
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
77
|
+
}
|
|
78
|
+
function normalizeProtocol(protocol) {
|
|
79
|
+
if (typeof protocol !== "string") {
|
|
80
|
+
return protocol;
|
|
81
|
+
}
|
|
82
|
+
switch (protocol) {
|
|
83
|
+
case "sequential":
|
|
84
|
+
return { kind: "sequential", maxTurns: 3 };
|
|
85
|
+
case "coordinator":
|
|
86
|
+
return { kind: "coordinator", maxTurns: 3 };
|
|
87
|
+
case "broadcast":
|
|
88
|
+
return { kind: "broadcast", maxRounds: 2 };
|
|
89
|
+
case "shared":
|
|
90
|
+
return { kind: "shared", maxTurns: 3 };
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function defaultAgents() {
|
|
94
|
+
return [
|
|
95
|
+
{ id: "agent-1", role: "planner", instructions: "Frame the mission and identify the important constraints." },
|
|
96
|
+
{ id: "agent-2", role: "critic", instructions: "Stress-test the previous contribution and improve weak spots." },
|
|
97
|
+
{ id: "agent-3", role: "synthesizer", instructions: "Produce the final useful answer from the accumulated work." }
|
|
98
|
+
];
|
|
99
|
+
}
|
|
100
|
+
function orderAgentsForTemperature(agents, temperature, seed) {
|
|
101
|
+
if (temperature !== 0) {
|
|
102
|
+
return agents;
|
|
103
|
+
}
|
|
104
|
+
if (seed !== void 0) {
|
|
105
|
+
return [...agents].sort((left, right) => compareAgentsBySeededSelection(left, right, seed));
|
|
106
|
+
}
|
|
107
|
+
return [...agents].sort(compareAgentsByStableIdentity);
|
|
108
|
+
}
|
|
109
|
+
function compareAgentsBySeededSelection(left, right, seed) {
|
|
110
|
+
const leftScore = deterministicSelectionScore(seed, left);
|
|
111
|
+
const rightScore = deterministicSelectionScore(seed, right);
|
|
112
|
+
if (leftScore !== rightScore) {
|
|
113
|
+
return leftScore - rightScore;
|
|
114
|
+
}
|
|
115
|
+
return compareAgentsByStableIdentity(left, right);
|
|
116
|
+
}
|
|
117
|
+
function deterministicSelectionScore(seed, agent) {
|
|
118
|
+
return stableHash(`${String(seed)}\0${agent.id}\0${agent.role}\0${agent.instructions ?? ""}`);
|
|
119
|
+
}
|
|
120
|
+
function stableHash(input) {
|
|
121
|
+
let hash = 2166136261;
|
|
122
|
+
for (let index = 0; index < input.length; index += 1) {
|
|
123
|
+
hash ^= input.charCodeAt(index);
|
|
124
|
+
hash = Math.imul(hash, 16777619);
|
|
125
|
+
}
|
|
126
|
+
return hash >>> 0;
|
|
127
|
+
}
|
|
128
|
+
function compareAgentsByStableIdentity(left, right) {
|
|
129
|
+
const idOrder = left.id.localeCompare(right.id);
|
|
130
|
+
if (idOrder !== 0) {
|
|
131
|
+
return idOrder;
|
|
132
|
+
}
|
|
133
|
+
const roleOrder = left.role.localeCompare(right.role);
|
|
134
|
+
if (roleOrder !== 0) {
|
|
135
|
+
return roleOrder;
|
|
136
|
+
}
|
|
137
|
+
return (left.instructions ?? "").localeCompare(right.instructions ?? "");
|
|
138
|
+
}
|
|
139
|
+
function tierTemperature(tier) {
|
|
140
|
+
switch (tier) {
|
|
141
|
+
case "fast":
|
|
142
|
+
return 0;
|
|
143
|
+
case "balanced":
|
|
144
|
+
return 0.2;
|
|
145
|
+
case "quality":
|
|
146
|
+
return 0.4;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
function emptyCost() {
|
|
150
|
+
return { usd: 0, inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
151
|
+
}
|
|
152
|
+
function addCost(left, right) {
|
|
153
|
+
return {
|
|
154
|
+
usd: left.usd + right.usd,
|
|
155
|
+
inputTokens: left.inputTokens + right.inputTokens,
|
|
156
|
+
outputTokens: left.outputTokens + right.outputTokens,
|
|
157
|
+
totalTokens: left.totalTokens + right.totalTokens
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function createTranscriptLink(transcript) {
|
|
161
|
+
return {
|
|
162
|
+
kind: "trace-transcript",
|
|
163
|
+
entryCount: transcript.length,
|
|
164
|
+
lastEntryIndex: transcript.length === 0 ? null : transcript.length - 1
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function createRunEventLog(runId, protocol, events) {
|
|
168
|
+
return {
|
|
169
|
+
kind: "run-event-log",
|
|
170
|
+
runId,
|
|
171
|
+
protocol,
|
|
172
|
+
eventTypes: events.map((event) => event.type),
|
|
173
|
+
eventCount: events.length,
|
|
174
|
+
events
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
function createRunUsage(cost) {
|
|
178
|
+
return {
|
|
179
|
+
usd: cost.usd,
|
|
180
|
+
inputTokens: cost.inputTokens,
|
|
181
|
+
outputTokens: cost.outputTokens,
|
|
182
|
+
totalTokens: cost.totalTokens
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
function createRunAccounting(options) {
|
|
186
|
+
const usage = createRunUsage(options.cost);
|
|
187
|
+
return {
|
|
188
|
+
kind: "run-accounting",
|
|
189
|
+
tier: options.tier,
|
|
190
|
+
...options.budget ? { budget: options.budget } : {},
|
|
191
|
+
...options.termination ? { termination: options.termination } : {},
|
|
192
|
+
usage,
|
|
193
|
+
cost: options.cost,
|
|
194
|
+
budgetStateChanges: createReplayTraceBudgetStateChanges(options.events),
|
|
195
|
+
...options.budget?.maxUsd !== void 0 ? { usdCapUtilization: options.budget.maxUsd === 0 ? 0 : options.cost.usd / options.budget.maxUsd } : {},
|
|
196
|
+
...options.budget?.maxTokens !== void 0 ? {
|
|
197
|
+
totalTokenCapUtilization: options.budget.maxTokens === 0 ? 0 : options.cost.totalTokens / options.budget.maxTokens
|
|
198
|
+
} : {}
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
function createRunMetadata(options) {
|
|
202
|
+
const firstEvent = options.events[0];
|
|
203
|
+
const lastEvent = options.events.at(-1);
|
|
204
|
+
return {
|
|
205
|
+
runId: options.runId,
|
|
206
|
+
protocol: options.protocol,
|
|
207
|
+
tier: options.tier,
|
|
208
|
+
modelProviderId: options.modelProviderId,
|
|
209
|
+
agentsUsed: options.agentsUsed,
|
|
210
|
+
startedAt: firstEvent?.at ?? "",
|
|
211
|
+
completedAt: lastEvent?.at ?? ""
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
function createReplayTraceRunInputs(options) {
|
|
215
|
+
return {
|
|
216
|
+
kind: "replay-trace-run-inputs",
|
|
217
|
+
intent: options.intent,
|
|
218
|
+
protocol: options.protocol,
|
|
219
|
+
tier: options.tier,
|
|
220
|
+
modelProviderId: options.modelProviderId,
|
|
221
|
+
agents: options.agents,
|
|
222
|
+
temperature: options.temperature
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
function createReplayTraceBudget(options) {
|
|
226
|
+
return {
|
|
227
|
+
kind: "replay-trace-budget",
|
|
228
|
+
tier: options.tier,
|
|
229
|
+
...options.caps ? { caps: options.caps } : {},
|
|
230
|
+
...options.termination ? { termination: options.termination } : {}
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
function createReplayTraceBudgetStateChanges(events) {
|
|
234
|
+
return events.flatMap((event, eventIndex) => {
|
|
235
|
+
switch (event.type) {
|
|
236
|
+
case "agent-turn":
|
|
237
|
+
case "broadcast":
|
|
238
|
+
case "final":
|
|
239
|
+
return [
|
|
240
|
+
{
|
|
241
|
+
kind: "replay-trace-budget-state-change",
|
|
242
|
+
eventIndex,
|
|
243
|
+
eventType: event.type,
|
|
244
|
+
at: event.at,
|
|
245
|
+
cost: event.cost
|
|
246
|
+
}
|
|
247
|
+
];
|
|
248
|
+
case "budget-stop":
|
|
249
|
+
return [
|
|
250
|
+
{
|
|
251
|
+
kind: "replay-trace-budget-state-change",
|
|
252
|
+
eventIndex,
|
|
253
|
+
eventType: event.type,
|
|
254
|
+
at: event.at,
|
|
255
|
+
cost: event.cost,
|
|
256
|
+
iteration: event.iteration,
|
|
257
|
+
elapsedMs: event.elapsedMs,
|
|
258
|
+
budgetReason: event.reason
|
|
259
|
+
}
|
|
260
|
+
];
|
|
261
|
+
case "role-assignment":
|
|
262
|
+
case "model-request":
|
|
263
|
+
case "model-response":
|
|
264
|
+
case "model-output-chunk":
|
|
265
|
+
case "tool-call":
|
|
266
|
+
case "tool-result":
|
|
267
|
+
return [];
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
function createReplayTraceSeed(seed) {
|
|
272
|
+
if (seed === void 0) {
|
|
273
|
+
return {
|
|
274
|
+
kind: "replay-trace-seed",
|
|
275
|
+
source: "none",
|
|
276
|
+
value: null
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
return {
|
|
280
|
+
kind: "replay-trace-seed",
|
|
281
|
+
source: "caller",
|
|
282
|
+
value: seed
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
function createReplayTraceProtocolDecision(protocol, event, eventIndex, options = {}) {
|
|
286
|
+
const base = {
|
|
287
|
+
kind: "replay-trace-protocol-decision",
|
|
288
|
+
eventIndex,
|
|
289
|
+
eventType: event.type,
|
|
290
|
+
protocol,
|
|
291
|
+
decision: options.decision ?? defaultProtocolDecision(event),
|
|
292
|
+
at: event.at,
|
|
293
|
+
...options.turn !== void 0 ? { turn: options.turn } : {},
|
|
294
|
+
...options.phase !== void 0 ? { phase: options.phase } : {},
|
|
295
|
+
...options.round !== void 0 ? { round: options.round } : {},
|
|
296
|
+
...options.transcriptEntryCount !== void 0 ? { transcriptEntryCount: options.transcriptEntryCount } : {},
|
|
297
|
+
...options.contributionCount !== void 0 ? { contributionCount: options.contributionCount } : {}
|
|
298
|
+
};
|
|
299
|
+
switch (event.type) {
|
|
300
|
+
case "role-assignment":
|
|
301
|
+
return {
|
|
302
|
+
...base,
|
|
303
|
+
agentId: event.agentId,
|
|
304
|
+
role: event.role
|
|
305
|
+
};
|
|
306
|
+
case "model-request":
|
|
307
|
+
return {
|
|
308
|
+
...base,
|
|
309
|
+
agentId: event.agentId,
|
|
310
|
+
role: event.role,
|
|
311
|
+
callId: event.callId,
|
|
312
|
+
providerId: event.providerId,
|
|
313
|
+
input: event.request.messages.map((message) => message.content).join("\n")
|
|
314
|
+
};
|
|
315
|
+
case "model-response":
|
|
316
|
+
return {
|
|
317
|
+
...base,
|
|
318
|
+
agentId: event.agentId,
|
|
319
|
+
role: event.role,
|
|
320
|
+
callId: event.callId,
|
|
321
|
+
providerId: event.providerId,
|
|
322
|
+
output: event.response.text
|
|
323
|
+
};
|
|
324
|
+
case "model-output-chunk":
|
|
325
|
+
return {
|
|
326
|
+
...base,
|
|
327
|
+
agentId: event.agentId,
|
|
328
|
+
role: event.role,
|
|
329
|
+
input: event.input,
|
|
330
|
+
output: event.output
|
|
331
|
+
};
|
|
332
|
+
case "tool-call":
|
|
333
|
+
return {
|
|
334
|
+
...base,
|
|
335
|
+
toolCallId: event.toolCallId,
|
|
336
|
+
tool: event.tool,
|
|
337
|
+
input: stableJsonStringify(event.input),
|
|
338
|
+
...eventAgentScope(event)
|
|
339
|
+
};
|
|
340
|
+
case "tool-result":
|
|
341
|
+
return {
|
|
342
|
+
...base,
|
|
343
|
+
toolCallId: event.toolCallId,
|
|
344
|
+
tool: event.tool,
|
|
345
|
+
output: stableJsonStringify(event.result),
|
|
346
|
+
...eventAgentScope(event)
|
|
347
|
+
};
|
|
348
|
+
case "agent-turn":
|
|
349
|
+
return {
|
|
350
|
+
...base,
|
|
351
|
+
agentId: event.agentId,
|
|
352
|
+
role: event.role,
|
|
353
|
+
input: event.input,
|
|
354
|
+
output: event.output,
|
|
355
|
+
cost: event.cost
|
|
356
|
+
};
|
|
357
|
+
case "broadcast":
|
|
358
|
+
return {
|
|
359
|
+
...base,
|
|
360
|
+
round: event.round,
|
|
361
|
+
contributionCount: options.contributionCount ?? event.contributions.length,
|
|
362
|
+
cost: event.cost
|
|
363
|
+
};
|
|
364
|
+
case "budget-stop":
|
|
365
|
+
return {
|
|
366
|
+
...base,
|
|
367
|
+
cost: event.cost,
|
|
368
|
+
budgetReason: event.reason
|
|
369
|
+
};
|
|
370
|
+
case "final":
|
|
371
|
+
return {
|
|
372
|
+
...base,
|
|
373
|
+
output: event.output,
|
|
374
|
+
cost: event.cost
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
function defaultProtocolDecision(event) {
|
|
379
|
+
switch (event.type) {
|
|
380
|
+
case "role-assignment":
|
|
381
|
+
return "assign-role";
|
|
382
|
+
case "model-request":
|
|
383
|
+
return "start-model-call";
|
|
384
|
+
case "model-response":
|
|
385
|
+
return "complete-model-call";
|
|
386
|
+
case "model-output-chunk":
|
|
387
|
+
return "observe-model-output";
|
|
388
|
+
case "tool-call":
|
|
389
|
+
return "start-tool-call";
|
|
390
|
+
case "tool-result":
|
|
391
|
+
return "complete-tool-call";
|
|
392
|
+
case "agent-turn":
|
|
393
|
+
return "select-agent-turn";
|
|
394
|
+
case "broadcast":
|
|
395
|
+
return "collect-broadcast-round";
|
|
396
|
+
case "budget-stop":
|
|
397
|
+
return "stop-for-budget";
|
|
398
|
+
case "final":
|
|
399
|
+
return "finalize-output";
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
function eventAgentScope(event) {
|
|
403
|
+
return {
|
|
404
|
+
...event.agentId !== void 0 ? { agentId: event.agentId } : {},
|
|
405
|
+
...event.role !== void 0 ? { role: event.role } : {}
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
function createReplayTraceFinalOutput(output, event) {
|
|
409
|
+
if (event.type === "final") {
|
|
410
|
+
return {
|
|
411
|
+
kind: "replay-trace-final-output",
|
|
412
|
+
output,
|
|
413
|
+
cost: event.cost,
|
|
414
|
+
completedAt: event.at,
|
|
415
|
+
transcript: event.transcript
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
return {
|
|
419
|
+
kind: "replay-trace-final-output",
|
|
420
|
+
output,
|
|
421
|
+
cost: emptyCost(),
|
|
422
|
+
completedAt: event.at,
|
|
423
|
+
transcript: {
|
|
424
|
+
kind: "trace-transcript",
|
|
425
|
+
entryCount: 0,
|
|
426
|
+
lastEntryIndex: null
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
function nextProviderCallId(runId, providerCalls) {
|
|
431
|
+
return `${runId}:provider-call:${providerCalls.length + 1}`;
|
|
432
|
+
}
|
|
433
|
+
function canonicalizeRunResult(result) {
|
|
434
|
+
const trace = canonicalizeSerializable(result.trace);
|
|
435
|
+
const eventLog = {
|
|
436
|
+
eventCount: trace.events.length,
|
|
437
|
+
eventTypes: trace.events.map((event) => event.type),
|
|
438
|
+
events: trace.events,
|
|
439
|
+
kind: "run-event-log",
|
|
440
|
+
protocol: trace.protocol,
|
|
441
|
+
runId: trace.runId
|
|
442
|
+
};
|
|
443
|
+
const canonicalResult = {
|
|
444
|
+
accounting: canonicalizeSerializable(result.accounting),
|
|
445
|
+
cost: canonicalizeSerializable(result.cost),
|
|
446
|
+
...result.evaluation !== void 0 ? { evaluation: canonicalizeSerializable(result.evaluation) } : {},
|
|
447
|
+
eventLog,
|
|
448
|
+
metadata: canonicalizeSerializable(result.metadata),
|
|
449
|
+
output: result.output,
|
|
450
|
+
...result.quality !== void 0 ? { quality: canonicalizeSerializable(result.quality) } : {},
|
|
451
|
+
trace,
|
|
452
|
+
transcript: trace.transcript,
|
|
453
|
+
usage: canonicalizeSerializable(result.usage)
|
|
454
|
+
};
|
|
455
|
+
return canonicalResult;
|
|
456
|
+
}
|
|
457
|
+
function stableJsonStringify(value) {
|
|
458
|
+
return JSON.stringify(canonicalizeSerializable(value));
|
|
459
|
+
}
|
|
460
|
+
function canonicalizeSerializable(value) {
|
|
461
|
+
if (Array.isArray(value)) {
|
|
462
|
+
return value.map((item) => canonicalizeSerializable(item));
|
|
463
|
+
}
|
|
464
|
+
if (typeof value === "number") {
|
|
465
|
+
if (Object.is(value, -0)) {
|
|
466
|
+
return 0;
|
|
467
|
+
}
|
|
468
|
+
if (!Number.isFinite(value)) {
|
|
469
|
+
return null;
|
|
470
|
+
}
|
|
471
|
+
return value;
|
|
472
|
+
}
|
|
473
|
+
if (value === null || typeof value !== "object") {
|
|
474
|
+
return value;
|
|
475
|
+
}
|
|
476
|
+
const input = value;
|
|
477
|
+
const output = {};
|
|
478
|
+
for (const key of Object.keys(input).sort()) {
|
|
479
|
+
const child = input[key];
|
|
480
|
+
if (child !== void 0) {
|
|
481
|
+
output[key] = canonicalizeSerializable(child);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
return output;
|
|
485
|
+
}
|
|
486
|
+
function throwIfAborted(signal, providerId) {
|
|
487
|
+
if (!signal?.aborted) {
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
throw createAbortErrorFromSignal(signal, providerId);
|
|
491
|
+
}
|
|
492
|
+
function createAbortError(providerId, detail, cause) {
|
|
493
|
+
return new DogpileError({
|
|
494
|
+
code: "aborted",
|
|
495
|
+
message: "The operation was aborted.",
|
|
496
|
+
retryable: false,
|
|
497
|
+
providerId,
|
|
498
|
+
...{},
|
|
499
|
+
...cause !== void 0 ? { cause } : {}
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
function createAbortErrorFromSignal(signal, providerId) {
|
|
503
|
+
if (DogpileError.isInstance(signal.reason)) {
|
|
504
|
+
return signal.reason;
|
|
505
|
+
}
|
|
506
|
+
return createAbortError(providerId, void 0, signal.reason);
|
|
507
|
+
}
|
|
508
|
+
function createTimeoutError(providerId, timeoutMs) {
|
|
509
|
+
return new DogpileError({
|
|
510
|
+
code: "timeout",
|
|
511
|
+
message: `The operation timed out after ${timeoutMs}ms.`,
|
|
512
|
+
retryable: true,
|
|
513
|
+
providerId,
|
|
514
|
+
detail: {
|
|
515
|
+
timeoutMs
|
|
516
|
+
}
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
function parseAgentDecision(output) {
|
|
520
|
+
const selectedRole = matchLine(output, /^role_selected:\s*(.+)$/imu);
|
|
521
|
+
const participation = matchLine(output, /^participation:\s*(contribute|abstain)$/imu);
|
|
522
|
+
const rationale = matchLine(output, /^rationale:\s*(.+)$/imu);
|
|
523
|
+
const contribution = matchContribution(output);
|
|
524
|
+
if (!selectedRole || !participation || !isAgentParticipation(participation) || !rationale || !contribution) {
|
|
525
|
+
return void 0;
|
|
526
|
+
}
|
|
527
|
+
return {
|
|
528
|
+
selectedRole,
|
|
529
|
+
participation,
|
|
530
|
+
rationale,
|
|
531
|
+
contribution
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
function isParticipatingDecision(decision) {
|
|
535
|
+
return decision?.participation !== "abstain";
|
|
536
|
+
}
|
|
537
|
+
function matchLine(output, pattern) {
|
|
538
|
+
const match = output.match(pattern);
|
|
539
|
+
return match?.[1]?.trim();
|
|
540
|
+
}
|
|
541
|
+
function matchContribution(output) {
|
|
542
|
+
const match = output.match(/^contribution:\s*\n([\s\S]*)$/imu);
|
|
543
|
+
const contribution = match?.[1]?.trim();
|
|
544
|
+
return contribution && contribution.length > 0 ? contribution : void 0;
|
|
545
|
+
}
|
|
546
|
+
function isAgentParticipation(value) {
|
|
547
|
+
return value === "contribute" || value === "abstain";
|
|
548
|
+
}
|
|
549
|
+
async function generateModelTurn(options) {
|
|
550
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
551
|
+
let response;
|
|
552
|
+
throwIfAborted(options.request.signal, options.model.id);
|
|
553
|
+
if (!options.model.stream) {
|
|
554
|
+
response = await options.model.generate(options.request);
|
|
555
|
+
throwIfAborted(options.request.signal, options.model.id);
|
|
556
|
+
recordProviderCall(response, startedAt, options);
|
|
557
|
+
return response;
|
|
558
|
+
}
|
|
559
|
+
let text = "";
|
|
560
|
+
let chunkIndex = 0;
|
|
561
|
+
let usage;
|
|
562
|
+
let costUsd;
|
|
563
|
+
let finishReason;
|
|
564
|
+
let toolRequests;
|
|
565
|
+
let metadata;
|
|
566
|
+
for await (const chunk of options.model.stream(options.request)) {
|
|
567
|
+
throwIfAborted(options.request.signal, options.model.id);
|
|
568
|
+
text += chunk.text;
|
|
569
|
+
options.emit({
|
|
570
|
+
type: "model-output-chunk",
|
|
571
|
+
runId: options.runId,
|
|
572
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
573
|
+
agentId: options.agent.id,
|
|
574
|
+
role: options.agent.role,
|
|
575
|
+
input: options.input,
|
|
576
|
+
chunkIndex,
|
|
577
|
+
text: chunk.text,
|
|
578
|
+
output: text
|
|
579
|
+
});
|
|
580
|
+
chunkIndex += 1;
|
|
581
|
+
if (chunk.usage) {
|
|
582
|
+
usage = chunk.usage;
|
|
583
|
+
}
|
|
584
|
+
if (chunk.costUsd !== void 0) {
|
|
585
|
+
costUsd = chunk.costUsd;
|
|
586
|
+
}
|
|
587
|
+
if (chunk.finishReason !== void 0) {
|
|
588
|
+
finishReason = chunk.finishReason;
|
|
589
|
+
}
|
|
590
|
+
if (chunk.toolRequests !== void 0) {
|
|
591
|
+
toolRequests = chunk.toolRequests;
|
|
592
|
+
}
|
|
593
|
+
if (chunk.metadata !== void 0) {
|
|
594
|
+
metadata = chunk.metadata;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
response = {
|
|
598
|
+
text,
|
|
599
|
+
...finishReason !== void 0 ? { finishReason } : {},
|
|
600
|
+
...toolRequests && toolRequests.length > 0 ? { toolRequests } : {},
|
|
601
|
+
...usage ? { usage } : {},
|
|
602
|
+
...costUsd !== void 0 ? { costUsd } : {},
|
|
603
|
+
...metadata !== void 0 ? { metadata } : {}
|
|
604
|
+
};
|
|
605
|
+
throwIfAborted(options.request.signal, options.model.id);
|
|
606
|
+
recordProviderCall(response, startedAt, options);
|
|
607
|
+
return response;
|
|
608
|
+
}
|
|
609
|
+
function recordProviderCall(response, startedAt, options) {
|
|
610
|
+
options.onProviderCall?.({
|
|
611
|
+
kind: "replay-trace-provider-call",
|
|
612
|
+
callId: options.callId,
|
|
613
|
+
providerId: options.model.id,
|
|
614
|
+
startedAt,
|
|
615
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
616
|
+
agentId: options.agent.id,
|
|
617
|
+
role: options.agent.role,
|
|
618
|
+
request: requestForTrace(options.request),
|
|
619
|
+
response
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
function requestForTrace(request) {
|
|
623
|
+
return {
|
|
624
|
+
messages: request.messages,
|
|
625
|
+
temperature: request.temperature,
|
|
626
|
+
metadata: request.metadata
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
function budget(options) {
|
|
630
|
+
return {
|
|
631
|
+
kind: "budget",
|
|
632
|
+
...options
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
function convergence(options) {
|
|
636
|
+
return {
|
|
637
|
+
kind: "convergence",
|
|
638
|
+
...options
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
function judge(options) {
|
|
642
|
+
return {
|
|
643
|
+
kind: "judge",
|
|
644
|
+
...options
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
function firstOf(...conditions) {
|
|
648
|
+
if (conditions.length === 0) {
|
|
649
|
+
throw new RangeError("firstOf requires at least one termination condition.");
|
|
650
|
+
}
|
|
651
|
+
return {
|
|
652
|
+
kind: "firstOf",
|
|
653
|
+
conditions
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
function evaluateTermination(condition, context) {
|
|
657
|
+
switch (condition.kind) {
|
|
658
|
+
case "budget":
|
|
659
|
+
return evaluateBudget(condition, context);
|
|
660
|
+
case "firstOf":
|
|
661
|
+
return evaluateFirstOf(condition, context).decision;
|
|
662
|
+
case "convergence":
|
|
663
|
+
return evaluateConvergence(condition, context);
|
|
664
|
+
case "judge":
|
|
665
|
+
return evaluateJudge(condition, context);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
function evaluateFirstOf(condition, context) {
|
|
669
|
+
const evaluated = [];
|
|
670
|
+
for (const [index, child] of condition.conditions.entries()) {
|
|
671
|
+
const decision = evaluateTermination(child, context);
|
|
672
|
+
evaluated.push(decision);
|
|
673
|
+
if (decision.type === "stop") {
|
|
674
|
+
return {
|
|
675
|
+
kind: "firstOf-output",
|
|
676
|
+
decision,
|
|
677
|
+
winningConditionIndex: index,
|
|
678
|
+
evaluated
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
return {
|
|
683
|
+
kind: "firstOf-output",
|
|
684
|
+
decision: { type: "continue", condition },
|
|
685
|
+
winningConditionIndex: null,
|
|
686
|
+
evaluated
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
function evaluateTerminationStop(condition, context) {
|
|
690
|
+
if (condition.kind === "firstOf") {
|
|
691
|
+
const output = evaluateFirstOf(condition, context);
|
|
692
|
+
if (output.decision.type !== "stop" || output.winningConditionIndex === null) {
|
|
693
|
+
return null;
|
|
694
|
+
}
|
|
695
|
+
const winningCondition = condition.conditions[output.winningConditionIndex];
|
|
696
|
+
if (!winningCondition) {
|
|
697
|
+
throw new RangeError("firstOf stop referenced a missing winning condition.");
|
|
698
|
+
}
|
|
699
|
+
return stopRecord(condition, output.decision, {
|
|
700
|
+
kind: "firstOf-stop",
|
|
701
|
+
winningConditionIndex: output.winningConditionIndex,
|
|
702
|
+
winningCondition,
|
|
703
|
+
firedCondition: output.decision.condition,
|
|
704
|
+
evaluated: output.evaluated
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
const decision = evaluateTermination(condition, context);
|
|
708
|
+
if (decision.type !== "stop") {
|
|
709
|
+
return null;
|
|
710
|
+
}
|
|
711
|
+
return stopRecord(condition, decision);
|
|
712
|
+
}
|
|
713
|
+
function combineTerminationDecisions(decisions) {
|
|
714
|
+
const stopDecisions = decisions.filter((decision) => decision.type === "stop");
|
|
715
|
+
if (stopDecisions.length === 0) {
|
|
716
|
+
const firstDecision = decisions[0];
|
|
717
|
+
if (!firstDecision) {
|
|
718
|
+
throw new RangeError("combineTerminationDecisions requires at least one decision.");
|
|
719
|
+
}
|
|
720
|
+
return firstDecision;
|
|
721
|
+
}
|
|
722
|
+
return stopDecisions.reduce(
|
|
723
|
+
(winner, candidate) => stopPrecedence(candidate.normalizedReason) < stopPrecedence(winner.normalizedReason) ? candidate : winner
|
|
724
|
+
);
|
|
725
|
+
}
|
|
726
|
+
function evaluateBudget(condition, context) {
|
|
727
|
+
const iteration = context.iteration ?? context.transcript.length;
|
|
728
|
+
const elapsedMs2 = context.elapsedMs ?? 0;
|
|
729
|
+
const costStop = stopIfReached(condition, "maxUsd", "cost", context.cost.usd);
|
|
730
|
+
if (costStop) {
|
|
731
|
+
return costStop;
|
|
732
|
+
}
|
|
733
|
+
const tokenStop = stopIfReached(condition, "maxTokens", "tokens", context.cost.totalTokens);
|
|
734
|
+
if (tokenStop) {
|
|
735
|
+
return tokenStop;
|
|
736
|
+
}
|
|
737
|
+
const iterationStop = stopIfReached(condition, "maxIterations", "iterations", iteration);
|
|
738
|
+
if (iterationStop) {
|
|
739
|
+
return iterationStop;
|
|
740
|
+
}
|
|
741
|
+
const timeoutStop = stopIfReached(condition, "timeoutMs", "timeout", elapsedMs2);
|
|
742
|
+
if (timeoutStop) {
|
|
743
|
+
return timeoutStop;
|
|
744
|
+
}
|
|
745
|
+
return { type: "continue", condition };
|
|
746
|
+
}
|
|
747
|
+
function evaluateConvergence(condition, context) {
|
|
748
|
+
const stableTurns = Math.max(1, Math.ceil(condition.stableTurns));
|
|
749
|
+
if (context.transcript.length < stableTurns) {
|
|
750
|
+
return { type: "continue", condition };
|
|
751
|
+
}
|
|
752
|
+
const recentEntries = context.transcript.slice(-stableTurns);
|
|
753
|
+
const recentOutputs = recentEntries.map((entry) => entry.output);
|
|
754
|
+
const similarities = consecutiveSimilarities(recentEntries);
|
|
755
|
+
const observedSimilarity = similarities.length === 0 ? 1 : Math.min(...similarities);
|
|
756
|
+
if (observedSimilarity < condition.minSimilarity) {
|
|
757
|
+
return { type: "continue", condition };
|
|
758
|
+
}
|
|
759
|
+
return {
|
|
760
|
+
type: "stop",
|
|
761
|
+
condition,
|
|
762
|
+
reason: "convergence",
|
|
763
|
+
normalizedReason: "convergence",
|
|
764
|
+
detail: {
|
|
765
|
+
protocol: context.protocol,
|
|
766
|
+
stableTurns,
|
|
767
|
+
minSimilarity: condition.minSimilarity,
|
|
768
|
+
observedSimilarity,
|
|
769
|
+
outputs: recentOutputs
|
|
770
|
+
}
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
function evaluateJudge(condition, context) {
|
|
774
|
+
const decision = context.judgeDecision ?? scoreDecisionFromQuality(context.quality);
|
|
775
|
+
if (!decision) {
|
|
776
|
+
return { type: "continue", condition };
|
|
777
|
+
}
|
|
778
|
+
switch (decision.type) {
|
|
779
|
+
case "accept":
|
|
780
|
+
return judgeStop(condition, "accepted", decision);
|
|
781
|
+
case "reject":
|
|
782
|
+
return judgeStop(condition, "rejected", decision);
|
|
783
|
+
case "score": {
|
|
784
|
+
const minScore = condition.minScore;
|
|
785
|
+
if (minScore !== void 0 && decision.score < minScore) {
|
|
786
|
+
return { type: "continue", condition };
|
|
787
|
+
}
|
|
788
|
+
return judgeStop(condition, "score-threshold", decision, minScore);
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
function stopIfReached(condition, cap, reason, observed) {
|
|
793
|
+
const limit = condition[cap];
|
|
794
|
+
if (limit === void 0 || observed < limit) {
|
|
795
|
+
return null;
|
|
796
|
+
}
|
|
797
|
+
return {
|
|
798
|
+
type: "stop",
|
|
799
|
+
condition,
|
|
800
|
+
reason: "budget",
|
|
801
|
+
normalizedReason: normalizeBudgetStopReason(reason),
|
|
802
|
+
budgetReason: reason,
|
|
803
|
+
detail: {
|
|
804
|
+
cap,
|
|
805
|
+
limit,
|
|
806
|
+
observed
|
|
807
|
+
}
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
function scoreDecisionFromQuality(quality) {
|
|
811
|
+
if (quality === void 0) {
|
|
812
|
+
return null;
|
|
813
|
+
}
|
|
814
|
+
return {
|
|
815
|
+
type: "score",
|
|
816
|
+
score: quality
|
|
817
|
+
};
|
|
818
|
+
}
|
|
819
|
+
function judgeStop(condition, judgeReason, decision, minScore) {
|
|
820
|
+
return {
|
|
821
|
+
type: "stop",
|
|
822
|
+
condition,
|
|
823
|
+
reason: "judge",
|
|
824
|
+
normalizedReason: normalizeJudgeStopReason(judgeReason),
|
|
825
|
+
judgeReason,
|
|
826
|
+
detail: judgeStopDetail(decision, minScore)
|
|
827
|
+
};
|
|
828
|
+
}
|
|
829
|
+
function normalizeBudgetStopReason(reason) {
|
|
830
|
+
switch (reason) {
|
|
831
|
+
case "cost":
|
|
832
|
+
return "budget:cost";
|
|
833
|
+
case "tokens":
|
|
834
|
+
return "budget:tokens";
|
|
835
|
+
case "iterations":
|
|
836
|
+
return "budget:iterations";
|
|
837
|
+
case "timeout":
|
|
838
|
+
return "budget:timeout";
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
function normalizeJudgeStopReason(reason) {
|
|
842
|
+
switch (reason) {
|
|
843
|
+
case "accepted":
|
|
844
|
+
return "judge:accepted";
|
|
845
|
+
case "rejected":
|
|
846
|
+
return "judge:rejected";
|
|
847
|
+
case "score-threshold":
|
|
848
|
+
return "judge:score-threshold";
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
function stopPrecedence(reason) {
|
|
852
|
+
if (reason.startsWith("budget:")) {
|
|
853
|
+
return 0;
|
|
854
|
+
}
|
|
855
|
+
if (reason.startsWith("judge:")) {
|
|
856
|
+
return 1;
|
|
857
|
+
}
|
|
858
|
+
return 2;
|
|
859
|
+
}
|
|
860
|
+
function judgeStopDetail(decision, minScore) {
|
|
861
|
+
return {
|
|
862
|
+
decision: decision.type,
|
|
863
|
+
...decision.score !== void 0 ? { score: decision.score } : {},
|
|
864
|
+
...minScore !== void 0 ? { minScore } : {},
|
|
865
|
+
...decision.rationale !== void 0 ? { rationale: decision.rationale } : {},
|
|
866
|
+
...decision.metadata !== void 0 ? { metadata: decision.metadata } : {}
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
function stopRecord(rootCondition, decision, firstOfRecord) {
|
|
870
|
+
return {
|
|
871
|
+
kind: "termination-stop",
|
|
872
|
+
rootCondition,
|
|
873
|
+
firedCondition: decision.condition,
|
|
874
|
+
reason: decision.reason,
|
|
875
|
+
normalizedReason: decision.normalizedReason,
|
|
876
|
+
...decision.budgetReason !== void 0 ? { budgetReason: decision.budgetReason } : {},
|
|
877
|
+
...decision.judgeReason !== void 0 ? { judgeReason: decision.judgeReason } : {},
|
|
878
|
+
...decision.detail !== void 0 ? { detail: decision.detail } : {},
|
|
879
|
+
...firstOfRecord !== void 0 ? { firstOf: firstOfRecord } : {}
|
|
880
|
+
};
|
|
881
|
+
}
|
|
882
|
+
function consecutiveSimilarities(entries) {
|
|
883
|
+
const similarities = [];
|
|
884
|
+
for (let index = 1; index < entries.length; index += 1) {
|
|
885
|
+
const previous = entries[index - 1];
|
|
886
|
+
const current = entries[index];
|
|
887
|
+
if (previous && current) {
|
|
888
|
+
similarities.push(outputSimilarity(previous.output, current.output));
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
return similarities;
|
|
892
|
+
}
|
|
893
|
+
function outputSimilarity(left, right) {
|
|
894
|
+
const normalizedLeft = normalizeOutput(left);
|
|
895
|
+
const normalizedRight = normalizeOutput(right);
|
|
896
|
+
if (normalizedLeft === normalizedRight) {
|
|
897
|
+
return 1;
|
|
898
|
+
}
|
|
899
|
+
const leftTokens = tokenize(normalizedLeft);
|
|
900
|
+
const rightTokens = tokenize(normalizedRight);
|
|
901
|
+
if (leftTokens.length === 0 || rightTokens.length === 0) {
|
|
902
|
+
return 0;
|
|
903
|
+
}
|
|
904
|
+
const leftSet = new Set(leftTokens);
|
|
905
|
+
const rightSet = new Set(rightTokens);
|
|
906
|
+
let intersection = 0;
|
|
907
|
+
for (const token of leftSet) {
|
|
908
|
+
if (rightSet.has(token)) {
|
|
909
|
+
intersection += 1;
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
const union = (/* @__PURE__ */ new Set([...leftSet, ...rightSet])).size;
|
|
913
|
+
return union === 0 ? 0 : intersection / union;
|
|
914
|
+
}
|
|
915
|
+
function normalizeOutput(output) {
|
|
916
|
+
return output.trim().toLowerCase();
|
|
917
|
+
}
|
|
918
|
+
function tokenize(output) {
|
|
919
|
+
return output.split(/[^a-z0-9]+/u).filter((token) => token.length > 0);
|
|
920
|
+
}
|
|
921
|
+
const protocolNames = ["coordinator", "sequential", "broadcast", "shared"];
|
|
922
|
+
const budgetTiers = ["fast", "balanced", "quality"];
|
|
923
|
+
function validateDogpileOptions(options) {
|
|
924
|
+
requireRecord(options, "options");
|
|
925
|
+
validateMissionIntent(options.intent);
|
|
926
|
+
if (options.protocol !== void 0) {
|
|
927
|
+
validateProtocolSelection(options.protocol, "protocol");
|
|
928
|
+
}
|
|
929
|
+
if (options.tier !== void 0) {
|
|
930
|
+
validateBudgetTier(options.tier, "tier");
|
|
931
|
+
}
|
|
932
|
+
validateModelProviderRegistration(options.model, "model");
|
|
933
|
+
validateOptionalAgents(options.agents, "agents");
|
|
934
|
+
validateOptionalRuntimeTools(options.tools, "tools");
|
|
935
|
+
validateOptionalTemperature(options.temperature, "temperature");
|
|
936
|
+
validateOptionalBudgetCaps(options.budget, "budget");
|
|
937
|
+
validateOptionalTerminationCondition(options.terminate, "terminate");
|
|
938
|
+
validateOptionalFunction(options.evaluate, "evaluate");
|
|
939
|
+
validateOptionalSeed(options.seed, "seed");
|
|
940
|
+
validateOptionalAbortSignal(options.signal, "signal");
|
|
941
|
+
}
|
|
942
|
+
function validateMissionIntent(intent, path = "intent") {
|
|
943
|
+
validateNonEmptyString(intent, path, "intent is required.");
|
|
944
|
+
}
|
|
945
|
+
function validateEngineOptions(options) {
|
|
946
|
+
requireRecord(options, "options");
|
|
947
|
+
validateProtocolSelection(options.protocol, "protocol");
|
|
948
|
+
validateBudgetTier(options.tier, "tier");
|
|
949
|
+
validateModelProviderRegistration(options.model, "model");
|
|
950
|
+
validateOptionalAgents(options.agents, "agents");
|
|
951
|
+
validateOptionalRuntimeTools(options.tools, "tools");
|
|
952
|
+
validateOptionalTemperature(options.temperature, "temperature");
|
|
953
|
+
validateOptionalBudgetCaps(options.budget, "budget");
|
|
954
|
+
validateOptionalTerminationCondition(options.terminate, "terminate");
|
|
955
|
+
validateOptionalFunction(options.evaluate, "evaluate");
|
|
956
|
+
validateOptionalSeed(options.seed, "seed");
|
|
957
|
+
validateOptionalAbortSignal(options.signal, "signal");
|
|
958
|
+
}
|
|
959
|
+
function validateProtocolSelection(value, path) {
|
|
960
|
+
if (typeof value === "string") {
|
|
961
|
+
if (!isProtocolName(value)) {
|
|
962
|
+
invalidConfiguration({
|
|
963
|
+
path,
|
|
964
|
+
rule: "enum",
|
|
965
|
+
message: "protocol must be one of coordinator, sequential, broadcast, or shared.",
|
|
966
|
+
expected: protocolNames.join(" | "),
|
|
967
|
+
actual: value
|
|
968
|
+
});
|
|
969
|
+
}
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
972
|
+
validateProtocolConfig(value, path);
|
|
973
|
+
}
|
|
974
|
+
function validateProtocolConfig(value, path) {
|
|
975
|
+
const record = requireRecord(value, path);
|
|
976
|
+
const kind = record.kind;
|
|
977
|
+
if (!isProtocolName(kind)) {
|
|
978
|
+
invalidConfiguration({
|
|
979
|
+
path: `${path}.kind`,
|
|
980
|
+
rule: "enum",
|
|
981
|
+
message: "protocol config kind must be one of coordinator, sequential, broadcast, or shared.",
|
|
982
|
+
expected: protocolNames.join(" | "),
|
|
983
|
+
actual: kind
|
|
984
|
+
});
|
|
985
|
+
}
|
|
986
|
+
switch (kind) {
|
|
987
|
+
case "coordinator":
|
|
988
|
+
case "sequential":
|
|
989
|
+
case "shared":
|
|
990
|
+
validateOptionalPositiveInteger(record.maxTurns, `${path}.maxTurns`);
|
|
991
|
+
if (kind === "shared") {
|
|
992
|
+
validateOptionalString(record.organizationalMemory, `${path}.organizationalMemory`);
|
|
993
|
+
}
|
|
994
|
+
return;
|
|
995
|
+
case "broadcast":
|
|
996
|
+
validateOptionalPositiveInteger(record.maxRounds, `${path}.maxRounds`);
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
function validateBudgetTier(value, path) {
|
|
1001
|
+
if (!isBudgetTier(value)) {
|
|
1002
|
+
invalidConfiguration({
|
|
1003
|
+
path,
|
|
1004
|
+
rule: "enum",
|
|
1005
|
+
message: "tier must be one of fast, balanced, or quality.",
|
|
1006
|
+
expected: budgetTiers.join(" | "),
|
|
1007
|
+
actual: value
|
|
1008
|
+
});
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
function validateModelProviderRegistration(value, path = "model") {
|
|
1012
|
+
const record = requireRecord(value, path);
|
|
1013
|
+
validateNonEmptyString(record.id, `${path}.id`, "model.id is required.");
|
|
1014
|
+
validateFunction(record.generate, `${path}.generate`);
|
|
1015
|
+
validateOptionalFunction(record.stream, `${path}.stream`);
|
|
1016
|
+
}
|
|
1017
|
+
function validateOptionalAgents(value, path) {
|
|
1018
|
+
if (value === void 0) {
|
|
1019
|
+
return;
|
|
1020
|
+
}
|
|
1021
|
+
if (!Array.isArray(value)) {
|
|
1022
|
+
invalidConfiguration({
|
|
1023
|
+
path,
|
|
1024
|
+
rule: "array",
|
|
1025
|
+
message: "agents must be an array when provided.",
|
|
1026
|
+
expected: "readonly AgentSpec[]",
|
|
1027
|
+
actual: value
|
|
1028
|
+
});
|
|
1029
|
+
}
|
|
1030
|
+
if (value.length === 0) {
|
|
1031
|
+
invalidConfiguration({
|
|
1032
|
+
path,
|
|
1033
|
+
rule: "array",
|
|
1034
|
+
message: "agents must contain at least one participant when provided.",
|
|
1035
|
+
expected: "non-empty readonly AgentSpec[]",
|
|
1036
|
+
actual: value
|
|
1037
|
+
});
|
|
1038
|
+
}
|
|
1039
|
+
value.forEach((agent, index) => {
|
|
1040
|
+
const agentPath = `${path}[${index}]`;
|
|
1041
|
+
const record = requireRecord(agent, agentPath);
|
|
1042
|
+
validateNonEmptyString(record.id, `${agentPath}.id`, "agent.id is required.");
|
|
1043
|
+
validateNonEmptyString(record.role, `${agentPath}.role`, "agent.role is required.");
|
|
1044
|
+
validateOptionalString(record.instructions, `${agentPath}.instructions`);
|
|
1045
|
+
});
|
|
1046
|
+
}
|
|
1047
|
+
function validateOptionalRuntimeTools(value, path) {
|
|
1048
|
+
if (value === void 0) {
|
|
1049
|
+
return;
|
|
1050
|
+
}
|
|
1051
|
+
validateRuntimeToolRegistrations(value, path);
|
|
1052
|
+
}
|
|
1053
|
+
function validateRuntimeToolRegistrations(value, path = "tools") {
|
|
1054
|
+
if (!Array.isArray(value)) {
|
|
1055
|
+
invalidConfiguration({
|
|
1056
|
+
path,
|
|
1057
|
+
rule: "array",
|
|
1058
|
+
message: "tools must be an array when provided.",
|
|
1059
|
+
expected: "readonly RuntimeTool[]",
|
|
1060
|
+
actual: value
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
1063
|
+
value.forEach((tool, index) => validateRuntimeTool(tool, `${path}[${index}]`));
|
|
1064
|
+
}
|
|
1065
|
+
function validateRuntimeTool(value, path) {
|
|
1066
|
+
const record = requireRecord(value, path);
|
|
1067
|
+
const identity = requireRecord(record.identity, `${path}.identity`);
|
|
1068
|
+
validateNonEmptyString(identity.id, `${path}.identity.id`, "tool identity id is required.");
|
|
1069
|
+
validateNonEmptyString(identity.name, `${path}.identity.name`, "tool identity name is required.");
|
|
1070
|
+
validateOptionalString(identity.namespace, `${path}.identity.namespace`);
|
|
1071
|
+
validateOptionalString(identity.version, `${path}.identity.version`);
|
|
1072
|
+
validateOptionalString(identity.description, `${path}.identity.description`);
|
|
1073
|
+
const inputSchema = requireRecord(record.inputSchema, `${path}.inputSchema`);
|
|
1074
|
+
if (inputSchema.kind !== "json-schema") {
|
|
1075
|
+
invalidConfiguration({
|
|
1076
|
+
path: `${path}.inputSchema.kind`,
|
|
1077
|
+
rule: "runtime-tool",
|
|
1078
|
+
message: "tool inputSchema.kind must be json-schema.",
|
|
1079
|
+
expected: "json-schema",
|
|
1080
|
+
actual: inputSchema.kind
|
|
1081
|
+
});
|
|
1082
|
+
}
|
|
1083
|
+
validateJsonObject(inputSchema.schema, `${path}.inputSchema.schema`);
|
|
1084
|
+
validateOptionalString(inputSchema.description, `${path}.inputSchema.description`);
|
|
1085
|
+
validateOptionalArray(record.permissions, `${path}.permissions`);
|
|
1086
|
+
validateOptionalFunction(record.validateInput, `${path}.validateInput`);
|
|
1087
|
+
validateFunction(record.execute, `${path}.execute`);
|
|
1088
|
+
}
|
|
1089
|
+
function validateOptionalBudgetCaps(value, path) {
|
|
1090
|
+
if (value === void 0) {
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
1093
|
+
validateBudgetCaps(value, path);
|
|
1094
|
+
}
|
|
1095
|
+
function validateBudgetCaps(value, path) {
|
|
1096
|
+
const record = requireRecord(value, path);
|
|
1097
|
+
validateOptionalNonNegativeNumber(record.maxUsd, `${path}.maxUsd`);
|
|
1098
|
+
validateOptionalNonNegativeInteger(record.maxTokens, `${path}.maxTokens`);
|
|
1099
|
+
validateOptionalNonNegativeInteger(record.maxIterations, `${path}.maxIterations`);
|
|
1100
|
+
validateOptionalNonNegativeInteger(record.timeoutMs, `${path}.timeoutMs`);
|
|
1101
|
+
validateOptionalNumberInRange(record.qualityWeight, `${path}.qualityWeight`, 0, 1);
|
|
1102
|
+
}
|
|
1103
|
+
function validateOptionalTerminationCondition(value, path) {
|
|
1104
|
+
if (value === void 0) {
|
|
1105
|
+
return;
|
|
1106
|
+
}
|
|
1107
|
+
validateTerminationCondition(value, path, /* @__PURE__ */ new Set());
|
|
1108
|
+
}
|
|
1109
|
+
function validateTerminationCondition(value, path, stack) {
|
|
1110
|
+
const record = requireRecord(value, path);
|
|
1111
|
+
if (stack.has(record)) {
|
|
1112
|
+
invalidConfiguration({
|
|
1113
|
+
path,
|
|
1114
|
+
rule: "termination-condition",
|
|
1115
|
+
message: "termination conditions must not contain cycles.",
|
|
1116
|
+
expected: "acyclic termination condition",
|
|
1117
|
+
actual: value
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
stack.add(record);
|
|
1121
|
+
try {
|
|
1122
|
+
switch (record.kind) {
|
|
1123
|
+
case "budget":
|
|
1124
|
+
validateBudgetCaps(record, path);
|
|
1125
|
+
return;
|
|
1126
|
+
case "convergence":
|
|
1127
|
+
validatePositiveInteger(record.stableTurns, `${path}.stableTurns`);
|
|
1128
|
+
validateNumberInRange(record.minSimilarity, `${path}.minSimilarity`, 0, 1);
|
|
1129
|
+
return;
|
|
1130
|
+
case "judge":
|
|
1131
|
+
validateJudgeRubric(record.rubric, `${path}.rubric`);
|
|
1132
|
+
validateOptionalNumberInRange(record.minScore, `${path}.minScore`, 0, 1);
|
|
1133
|
+
return;
|
|
1134
|
+
case "firstOf":
|
|
1135
|
+
validateFirstOfConditions(record.conditions, `${path}.conditions`, stack);
|
|
1136
|
+
return;
|
|
1137
|
+
default:
|
|
1138
|
+
invalidConfiguration({
|
|
1139
|
+
path: `${path}.kind`,
|
|
1140
|
+
rule: "termination-condition",
|
|
1141
|
+
message: "termination condition kind must be budget, convergence, judge, or firstOf.",
|
|
1142
|
+
expected: "budget | convergence | judge | firstOf",
|
|
1143
|
+
actual: record.kind
|
|
1144
|
+
});
|
|
1145
|
+
}
|
|
1146
|
+
} finally {
|
|
1147
|
+
stack.delete(record);
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
function validateFirstOfConditions(value, path, stack) {
|
|
1151
|
+
if (!Array.isArray(value)) {
|
|
1152
|
+
invalidConfiguration({
|
|
1153
|
+
path,
|
|
1154
|
+
rule: "array",
|
|
1155
|
+
message: "firstOf conditions must be a non-empty array.",
|
|
1156
|
+
expected: "non-empty termination condition array",
|
|
1157
|
+
actual: value
|
|
1158
|
+
});
|
|
1159
|
+
}
|
|
1160
|
+
if (value.length === 0) {
|
|
1161
|
+
invalidConfiguration({
|
|
1162
|
+
path,
|
|
1163
|
+
rule: "array",
|
|
1164
|
+
message: "firstOf conditions must contain at least one condition.",
|
|
1165
|
+
expected: "non-empty termination condition array",
|
|
1166
|
+
actual: value
|
|
1167
|
+
});
|
|
1168
|
+
}
|
|
1169
|
+
value.forEach((condition, index) => {
|
|
1170
|
+
validateTerminationCondition(condition, `${path}[${index}]`, stack);
|
|
1171
|
+
});
|
|
1172
|
+
}
|
|
1173
|
+
function validateJudgeRubric(value, path) {
|
|
1174
|
+
if (typeof value === "string") {
|
|
1175
|
+
validateNonEmptyString(value, path, "judge rubric must not be empty.");
|
|
1176
|
+
return;
|
|
1177
|
+
}
|
|
1178
|
+
validateJsonObject(value, path);
|
|
1179
|
+
}
|
|
1180
|
+
function validateOptionalTemperature(value, path) {
|
|
1181
|
+
validateOptionalNumberInRange(value, path, 0, 2);
|
|
1182
|
+
}
|
|
1183
|
+
function validateOptionalSeed(value, path) {
|
|
1184
|
+
if (value === void 0) {
|
|
1185
|
+
return;
|
|
1186
|
+
}
|
|
1187
|
+
if (typeof value === "string") {
|
|
1188
|
+
return;
|
|
1189
|
+
}
|
|
1190
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
1191
|
+
return;
|
|
1192
|
+
}
|
|
1193
|
+
invalidConfiguration({
|
|
1194
|
+
path,
|
|
1195
|
+
rule: "finite-number",
|
|
1196
|
+
message: "seed must be a string or finite number when provided.",
|
|
1197
|
+
expected: "string or finite number",
|
|
1198
|
+
actual: value
|
|
1199
|
+
});
|
|
1200
|
+
}
|
|
1201
|
+
function validateJsonObject(value, path) {
|
|
1202
|
+
if (!isJsonValue$1(value, /* @__PURE__ */ new Set()) || !isRecord$1(value)) {
|
|
1203
|
+
invalidConfiguration({
|
|
1204
|
+
path,
|
|
1205
|
+
rule: "json-object",
|
|
1206
|
+
message: "value must be a JSON-compatible object.",
|
|
1207
|
+
expected: "JSON-compatible object",
|
|
1208
|
+
actual: value
|
|
1209
|
+
});
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
function isJsonValue$1(value, stack) {
|
|
1213
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") {
|
|
1214
|
+
return true;
|
|
1215
|
+
}
|
|
1216
|
+
if (typeof value === "number") {
|
|
1217
|
+
return Number.isFinite(value);
|
|
1218
|
+
}
|
|
1219
|
+
if (Array.isArray(value)) {
|
|
1220
|
+
if (stack.has(value)) {
|
|
1221
|
+
return false;
|
|
1222
|
+
}
|
|
1223
|
+
stack.add(value);
|
|
1224
|
+
const valid = value.every((child) => isJsonValue$1(child, stack));
|
|
1225
|
+
stack.delete(value);
|
|
1226
|
+
return valid;
|
|
1227
|
+
}
|
|
1228
|
+
if (isRecord$1(value)) {
|
|
1229
|
+
if (stack.has(value)) {
|
|
1230
|
+
return false;
|
|
1231
|
+
}
|
|
1232
|
+
stack.add(value);
|
|
1233
|
+
const valid = Object.values(value).every((child) => isJsonValue$1(child, stack));
|
|
1234
|
+
stack.delete(value);
|
|
1235
|
+
return valid;
|
|
1236
|
+
}
|
|
1237
|
+
return false;
|
|
1238
|
+
}
|
|
1239
|
+
function validateOptionalString(value, path) {
|
|
1240
|
+
if (value === void 0) {
|
|
1241
|
+
return;
|
|
1242
|
+
}
|
|
1243
|
+
if (typeof value !== "string") {
|
|
1244
|
+
invalidConfiguration({
|
|
1245
|
+
path,
|
|
1246
|
+
rule: "non-empty-string",
|
|
1247
|
+
message: "value must be a string when provided.",
|
|
1248
|
+
expected: "string",
|
|
1249
|
+
actual: value
|
|
1250
|
+
});
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
function validateNonEmptyString(value, path, message) {
|
|
1254
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
1255
|
+
invalidConfiguration({
|
|
1256
|
+
path,
|
|
1257
|
+
rule: "non-empty-string",
|
|
1258
|
+
message,
|
|
1259
|
+
expected: "non-empty string",
|
|
1260
|
+
actual: value
|
|
1261
|
+
});
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
function validateOptionalFunction(value, path) {
|
|
1265
|
+
if (value === void 0) {
|
|
1266
|
+
return;
|
|
1267
|
+
}
|
|
1268
|
+
validateFunction(value, path);
|
|
1269
|
+
}
|
|
1270
|
+
function validateFunction(value, path) {
|
|
1271
|
+
if (typeof value !== "function") {
|
|
1272
|
+
invalidConfiguration({
|
|
1273
|
+
path,
|
|
1274
|
+
rule: "function",
|
|
1275
|
+
message: "value must be a function.",
|
|
1276
|
+
expected: "function",
|
|
1277
|
+
actual: value
|
|
1278
|
+
});
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
function validateOptionalArray(value, path) {
|
|
1282
|
+
if (value === void 0) {
|
|
1283
|
+
return;
|
|
1284
|
+
}
|
|
1285
|
+
if (!Array.isArray(value)) {
|
|
1286
|
+
invalidConfiguration({
|
|
1287
|
+
path,
|
|
1288
|
+
rule: "array",
|
|
1289
|
+
message: "value must be an array when provided.",
|
|
1290
|
+
expected: "array",
|
|
1291
|
+
actual: value
|
|
1292
|
+
});
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
function validateOptionalAbortSignal(value, path) {
|
|
1296
|
+
if (value === void 0) {
|
|
1297
|
+
return;
|
|
1298
|
+
}
|
|
1299
|
+
if (!isAbortSignalLike(value)) {
|
|
1300
|
+
invalidConfiguration({
|
|
1301
|
+
path,
|
|
1302
|
+
rule: "abort-signal",
|
|
1303
|
+
message: "value must be an AbortSignal when provided.",
|
|
1304
|
+
expected: "AbortSignal",
|
|
1305
|
+
actual: value
|
|
1306
|
+
});
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
function isAbortSignalLike(value) {
|
|
1310
|
+
if (!isRecord$1(value)) {
|
|
1311
|
+
return false;
|
|
1312
|
+
}
|
|
1313
|
+
return typeof value.aborted === "boolean" && typeof value.addEventListener === "function" && typeof value.removeEventListener === "function";
|
|
1314
|
+
}
|
|
1315
|
+
function validateOptionalPositiveInteger(value, path) {
|
|
1316
|
+
if (value === void 0) {
|
|
1317
|
+
return;
|
|
1318
|
+
}
|
|
1319
|
+
validatePositiveInteger(value, path);
|
|
1320
|
+
}
|
|
1321
|
+
function validatePositiveInteger(value, path) {
|
|
1322
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 1) {
|
|
1323
|
+
invalidConfiguration({
|
|
1324
|
+
path,
|
|
1325
|
+
rule: "positive-integer",
|
|
1326
|
+
message: "value must be a positive integer.",
|
|
1327
|
+
expected: "integer >= 1",
|
|
1328
|
+
actual: value
|
|
1329
|
+
});
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
function validateOptionalNonNegativeInteger(value, path) {
|
|
1333
|
+
if (value === void 0) {
|
|
1334
|
+
return;
|
|
1335
|
+
}
|
|
1336
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
|
|
1337
|
+
invalidConfiguration({
|
|
1338
|
+
path,
|
|
1339
|
+
rule: "non-negative-integer",
|
|
1340
|
+
message: "value must be a non-negative integer.",
|
|
1341
|
+
expected: "integer >= 0",
|
|
1342
|
+
actual: value
|
|
1343
|
+
});
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
function validateOptionalNonNegativeNumber(value, path) {
|
|
1347
|
+
if (value === void 0) {
|
|
1348
|
+
return;
|
|
1349
|
+
}
|
|
1350
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
1351
|
+
invalidConfiguration({
|
|
1352
|
+
path,
|
|
1353
|
+
rule: "non-negative-number",
|
|
1354
|
+
message: "value must be a non-negative finite number.",
|
|
1355
|
+
expected: "finite number >= 0",
|
|
1356
|
+
actual: value
|
|
1357
|
+
});
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
function validateOptionalNumberInRange(value, path, min, max) {
|
|
1361
|
+
if (value === void 0) {
|
|
1362
|
+
return;
|
|
1363
|
+
}
|
|
1364
|
+
validateNumberInRange(value, path, min, max);
|
|
1365
|
+
}
|
|
1366
|
+
function validateNumberInRange(value, path, min, max) {
|
|
1367
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < min || value > max) {
|
|
1368
|
+
invalidConfiguration({
|
|
1369
|
+
path,
|
|
1370
|
+
rule: "range",
|
|
1371
|
+
message: `value must be a finite number in the inclusive range ${min}..${max}.`,
|
|
1372
|
+
expected: `finite number in ${min}..${max}`,
|
|
1373
|
+
actual: value
|
|
1374
|
+
});
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
function requireRecord(value, path) {
|
|
1378
|
+
if (!isRecord$1(value)) {
|
|
1379
|
+
invalidConfiguration({
|
|
1380
|
+
path,
|
|
1381
|
+
rule: "object",
|
|
1382
|
+
message: "value must be an object.",
|
|
1383
|
+
expected: "object",
|
|
1384
|
+
actual: value
|
|
1385
|
+
});
|
|
1386
|
+
}
|
|
1387
|
+
return value;
|
|
1388
|
+
}
|
|
1389
|
+
function invalidConfiguration(options) {
|
|
1390
|
+
throw new DogpileError({
|
|
1391
|
+
code: "invalid-configuration",
|
|
1392
|
+
message: `Invalid Dogpile configuration at ${options.path}: ${options.message}`,
|
|
1393
|
+
retryable: false,
|
|
1394
|
+
detail: {
|
|
1395
|
+
kind: "configuration-validation",
|
|
1396
|
+
path: options.path,
|
|
1397
|
+
rule: options.rule,
|
|
1398
|
+
expected: options.expected,
|
|
1399
|
+
received: describeValue(options.actual)
|
|
1400
|
+
}
|
|
1401
|
+
});
|
|
1402
|
+
}
|
|
1403
|
+
function isProtocolName(value) {
|
|
1404
|
+
return typeof value === "string" && protocolNames.includes(value);
|
|
1405
|
+
}
|
|
1406
|
+
function isBudgetTier(value) {
|
|
1407
|
+
return typeof value === "string" && budgetTiers.includes(value);
|
|
1408
|
+
}
|
|
1409
|
+
function isRecord$1(value) {
|
|
1410
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1411
|
+
}
|
|
1412
|
+
function describeValue(value) {
|
|
1413
|
+
if (value === null) {
|
|
1414
|
+
return "null";
|
|
1415
|
+
}
|
|
1416
|
+
if (value === void 0) {
|
|
1417
|
+
return "undefined";
|
|
1418
|
+
}
|
|
1419
|
+
if (Array.isArray(value)) {
|
|
1420
|
+
return "array";
|
|
1421
|
+
}
|
|
1422
|
+
if (typeof value === "number" && !Number.isFinite(value)) {
|
|
1423
|
+
return String(value);
|
|
1424
|
+
}
|
|
1425
|
+
return typeof value;
|
|
1426
|
+
}
|
|
1427
|
+
const webSearchIdentity = {
|
|
1428
|
+
id: "dogpile.tools.webSearch",
|
|
1429
|
+
namespace: "dogpile",
|
|
1430
|
+
name: "webSearch",
|
|
1431
|
+
version: "1.0.0",
|
|
1432
|
+
description: "Search the web through a caller-provided fetch-compatible search adapter."
|
|
1433
|
+
};
|
|
1434
|
+
const codeExecIdentity = {
|
|
1435
|
+
id: "dogpile.tools.codeExec",
|
|
1436
|
+
namespace: "dogpile",
|
|
1437
|
+
name: "codeExec",
|
|
1438
|
+
version: "1.0.0",
|
|
1439
|
+
description: "Execute code through a caller-provided sandbox adapter."
|
|
1440
|
+
};
|
|
1441
|
+
const webSearchInputSchema = {
|
|
1442
|
+
kind: "json-schema",
|
|
1443
|
+
description: "Web search query and optional result cap.",
|
|
1444
|
+
schema: {
|
|
1445
|
+
type: "object",
|
|
1446
|
+
properties: {
|
|
1447
|
+
query: { type: "string" },
|
|
1448
|
+
maxResults: { type: "number", minimum: 1 }
|
|
1449
|
+
},
|
|
1450
|
+
required: ["query"],
|
|
1451
|
+
additionalProperties: false
|
|
1452
|
+
}
|
|
1453
|
+
};
|
|
1454
|
+
const codeExecInputSchema = {
|
|
1455
|
+
kind: "json-schema",
|
|
1456
|
+
description: "Code snippet plus language and optional timeout.",
|
|
1457
|
+
schema: {
|
|
1458
|
+
type: "object",
|
|
1459
|
+
properties: {
|
|
1460
|
+
language: {
|
|
1461
|
+
type: "string",
|
|
1462
|
+
enum: ["javascript", "typescript", "python", "bash", "shell"]
|
|
1463
|
+
},
|
|
1464
|
+
code: { type: "string" },
|
|
1465
|
+
timeoutMs: { type: "number", minimum: 1 }
|
|
1466
|
+
},
|
|
1467
|
+
required: ["language", "code"],
|
|
1468
|
+
additionalProperties: false
|
|
1469
|
+
}
|
|
1470
|
+
};
|
|
1471
|
+
const webSearchPermissions = [
|
|
1472
|
+
{
|
|
1473
|
+
kind: "network",
|
|
1474
|
+
allowPrivateNetwork: false
|
|
1475
|
+
}
|
|
1476
|
+
];
|
|
1477
|
+
const codeExecPermissions = [
|
|
1478
|
+
{
|
|
1479
|
+
kind: "code-execution",
|
|
1480
|
+
sandbox: "caller-provided",
|
|
1481
|
+
languages: ["javascript", "typescript", "python", "bash", "shell"],
|
|
1482
|
+
allowNetwork: false
|
|
1483
|
+
}
|
|
1484
|
+
];
|
|
1485
|
+
const codeExecLanguages = ["javascript", "typescript", "python", "bash", "shell"];
|
|
1486
|
+
function builtInDogpileToolIdentity(name) {
|
|
1487
|
+
return name === "webSearch" ? webSearchIdentity : codeExecIdentity;
|
|
1488
|
+
}
|
|
1489
|
+
function builtInDogpileToolInputSchema(name) {
|
|
1490
|
+
return name === "webSearch" ? webSearchInputSchema : codeExecInputSchema;
|
|
1491
|
+
}
|
|
1492
|
+
function builtInDogpileToolPermissions(name) {
|
|
1493
|
+
return name === "webSearch" ? webSearchPermissions : codeExecPermissions;
|
|
1494
|
+
}
|
|
1495
|
+
function validateBuiltInDogpileToolInput(name, input) {
|
|
1496
|
+
const issues = name === "webSearch" ? validateWebSearchInput(input) : validateCodeExecInput(input);
|
|
1497
|
+
return issues.length === 0 ? { type: "valid" } : { type: "invalid", issues };
|
|
1498
|
+
}
|
|
1499
|
+
function createRuntimeToolExecutor(options) {
|
|
1500
|
+
validateRuntimeToolRegistrations(options.tools);
|
|
1501
|
+
const tools = Array.from(options.tools);
|
|
1502
|
+
let callCount = 0;
|
|
1503
|
+
return {
|
|
1504
|
+
tools,
|
|
1505
|
+
async execute(request) {
|
|
1506
|
+
const tool = tools.find((candidate) => candidate.identity.id === request.toolId);
|
|
1507
|
+
const identity = tool?.identity ?? {
|
|
1508
|
+
id: request.toolId,
|
|
1509
|
+
name: request.toolId
|
|
1510
|
+
};
|
|
1511
|
+
const callIndex = callCount;
|
|
1512
|
+
callCount += 1;
|
|
1513
|
+
const toolCallId = request.toolCallId ?? options.makeToolCallId?.(identity, callIndex) ?? defaultToolCallId(options.runId, callIndex);
|
|
1514
|
+
const context = createExecutionContext(options, request, toolCallId);
|
|
1515
|
+
options.emit?.({
|
|
1516
|
+
type: "tool-call",
|
|
1517
|
+
runId: options.runId,
|
|
1518
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1519
|
+
toolCallId,
|
|
1520
|
+
tool: identity,
|
|
1521
|
+
input: request.input,
|
|
1522
|
+
...request.agentId ? { agentId: request.agentId } : {},
|
|
1523
|
+
...request.role ? { role: request.role } : {}
|
|
1524
|
+
});
|
|
1525
|
+
const result = await executeRuntimeTool(tool, identity, request.input, context);
|
|
1526
|
+
options.emit?.({
|
|
1527
|
+
type: "tool-result",
|
|
1528
|
+
runId: options.runId,
|
|
1529
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1530
|
+
toolCallId,
|
|
1531
|
+
tool: identity,
|
|
1532
|
+
result,
|
|
1533
|
+
...request.agentId ? { agentId: request.agentId } : {},
|
|
1534
|
+
...request.role ? { role: request.role } : {}
|
|
1535
|
+
});
|
|
1536
|
+
return result;
|
|
1537
|
+
}
|
|
1538
|
+
};
|
|
1539
|
+
}
|
|
1540
|
+
function runtimeToolManifest(tools) {
|
|
1541
|
+
return tools.map((tool) => {
|
|
1542
|
+
const inputSchema = {
|
|
1543
|
+
kind: tool.inputSchema.kind,
|
|
1544
|
+
schema: tool.inputSchema.schema,
|
|
1545
|
+
...tool.inputSchema.description ? { description: tool.inputSchema.description } : {}
|
|
1546
|
+
};
|
|
1547
|
+
return {
|
|
1548
|
+
identity: runtimeToolIdentityManifest(tool.identity),
|
|
1549
|
+
inputSchema,
|
|
1550
|
+
permissions: Array.from(tool.permissions ?? []).map(runtimeToolPermissionManifest)
|
|
1551
|
+
};
|
|
1552
|
+
});
|
|
1553
|
+
}
|
|
1554
|
+
function runtimeToolAvailability(tools) {
|
|
1555
|
+
const manifest = runtimeToolManifest(tools);
|
|
1556
|
+
return manifest.length > 0 ? { tools: manifest } : {};
|
|
1557
|
+
}
|
|
1558
|
+
async function executeModelResponseToolRequests(options) {
|
|
1559
|
+
const toolCalls = [];
|
|
1560
|
+
for (const request of options.response.toolRequests ?? []) {
|
|
1561
|
+
const result = await options.executor.execute({
|
|
1562
|
+
...request,
|
|
1563
|
+
agentId: request.agentId ?? options.agentId,
|
|
1564
|
+
role: request.role ?? options.role,
|
|
1565
|
+
turn: request.turn ?? options.turn,
|
|
1566
|
+
metadata: mergeToolMetadata(options.metadata, request.metadata)
|
|
1567
|
+
});
|
|
1568
|
+
toolCalls.push({
|
|
1569
|
+
toolCallId: result.toolCallId,
|
|
1570
|
+
tool: result.tool,
|
|
1571
|
+
input: request.input,
|
|
1572
|
+
result
|
|
1573
|
+
});
|
|
1574
|
+
}
|
|
1575
|
+
return toolCalls;
|
|
1576
|
+
}
|
|
1577
|
+
function runtimeToolIdentityManifest(identity) {
|
|
1578
|
+
return {
|
|
1579
|
+
id: identity.id,
|
|
1580
|
+
name: identity.name,
|
|
1581
|
+
...identity.namespace ? { namespace: identity.namespace } : {},
|
|
1582
|
+
...identity.version ? { version: identity.version } : {},
|
|
1583
|
+
...identity.description ? { description: identity.description } : {}
|
|
1584
|
+
};
|
|
1585
|
+
}
|
|
1586
|
+
function runtimeToolPermissionManifest(permission) {
|
|
1587
|
+
if (permission.kind === "network") {
|
|
1588
|
+
return {
|
|
1589
|
+
kind: permission.kind,
|
|
1590
|
+
...permission.allowHosts ? { allowHosts: Array.from(permission.allowHosts) } : {},
|
|
1591
|
+
...permission.allowPrivateNetwork === void 0 ? {} : { allowPrivateNetwork: permission.allowPrivateNetwork }
|
|
1592
|
+
};
|
|
1593
|
+
}
|
|
1594
|
+
if (permission.kind === "code-execution") {
|
|
1595
|
+
return {
|
|
1596
|
+
kind: permission.kind,
|
|
1597
|
+
sandbox: permission.sandbox,
|
|
1598
|
+
...permission.languages ? { languages: Array.from(permission.languages) } : {},
|
|
1599
|
+
...permission.allowNetwork === void 0 ? {} : { allowNetwork: permission.allowNetwork }
|
|
1600
|
+
};
|
|
1601
|
+
}
|
|
1602
|
+
return {
|
|
1603
|
+
kind: permission.kind,
|
|
1604
|
+
name: permission.name,
|
|
1605
|
+
...permission.description ? { description: permission.description } : {},
|
|
1606
|
+
...permission.metadata ? { metadata: permission.metadata } : {}
|
|
1607
|
+
};
|
|
1608
|
+
}
|
|
1609
|
+
function validateCodeExecAdapterInput(input, options) {
|
|
1610
|
+
const issues = [...validateCodeExecInput(input)];
|
|
1611
|
+
const languages = options.languages ?? codeExecLanguages;
|
|
1612
|
+
if (typeof input.language === "string" && isCodeExecLanguage(input.language) && !languages.includes(input.language)) {
|
|
1613
|
+
issues.push({
|
|
1614
|
+
code: "invalid-value",
|
|
1615
|
+
path: "language",
|
|
1616
|
+
message: "codeExec.language is not enabled for this adapter.",
|
|
1617
|
+
detail: {
|
|
1618
|
+
allowed: Array.from(languages)
|
|
1619
|
+
}
|
|
1620
|
+
});
|
|
1621
|
+
}
|
|
1622
|
+
const effectiveTimeoutMs = input.timeoutMs ?? options.defaultTimeoutMs;
|
|
1623
|
+
if (effectiveTimeoutMs !== void 0 && options.maxTimeoutMs !== void 0 && Number.isFinite(effectiveTimeoutMs) && Number.isFinite(options.maxTimeoutMs) && effectiveTimeoutMs > options.maxTimeoutMs) {
|
|
1624
|
+
issues.push({
|
|
1625
|
+
code: "out-of-range",
|
|
1626
|
+
path: input.timeoutMs === void 0 ? "defaultTimeoutMs" : "timeoutMs",
|
|
1627
|
+
message: `codeExec.timeoutMs must be less than or equal to ${options.maxTimeoutMs}.`,
|
|
1628
|
+
detail: {
|
|
1629
|
+
maximum: options.maxTimeoutMs
|
|
1630
|
+
}
|
|
1631
|
+
});
|
|
1632
|
+
}
|
|
1633
|
+
return issues.length === 0 ? { type: "valid" } : { type: "invalid", issues };
|
|
1634
|
+
}
|
|
1635
|
+
function createExecutionContext(options, request, toolCallId) {
|
|
1636
|
+
return {
|
|
1637
|
+
runId: options.runId,
|
|
1638
|
+
toolCallId,
|
|
1639
|
+
protocol: options.protocol,
|
|
1640
|
+
tier: options.tier,
|
|
1641
|
+
...request.agentId ? { agentId: request.agentId } : {},
|
|
1642
|
+
...request.role ? { role: request.role } : {},
|
|
1643
|
+
...request.turn !== void 0 ? { turn: request.turn } : {},
|
|
1644
|
+
...options.getTrace ? { trace: options.getTrace() } : {},
|
|
1645
|
+
...request.abortSignal ?? options.abortSignal ? { abortSignal: request.abortSignal ?? options.abortSignal } : {},
|
|
1646
|
+
...options.metadata || request.metadata ? { metadata: mergeToolMetadata(options.metadata, request.metadata) } : {}
|
|
1647
|
+
};
|
|
1648
|
+
}
|
|
1649
|
+
async function executeRuntimeTool(tool, identity, input, context) {
|
|
1650
|
+
if (!tool) {
|
|
1651
|
+
return {
|
|
1652
|
+
type: "error",
|
|
1653
|
+
toolCallId: context.toolCallId,
|
|
1654
|
+
tool: identity,
|
|
1655
|
+
error: {
|
|
1656
|
+
code: "unavailable",
|
|
1657
|
+
message: `Runtime tool "${identity.id}" is not registered.`,
|
|
1658
|
+
retryable: false
|
|
1659
|
+
}
|
|
1660
|
+
};
|
|
1661
|
+
}
|
|
1662
|
+
const validation = validateRuntimeToolInput(tool, input);
|
|
1663
|
+
if (validation.type === "invalid") {
|
|
1664
|
+
return {
|
|
1665
|
+
type: "error",
|
|
1666
|
+
toolCallId: context.toolCallId,
|
|
1667
|
+
tool: identity,
|
|
1668
|
+
error: {
|
|
1669
|
+
code: "invalid-input",
|
|
1670
|
+
message: "Runtime tool input failed validation.",
|
|
1671
|
+
retryable: false,
|
|
1672
|
+
detail: {
|
|
1673
|
+
issues: validation.issues.map((issue) => ({
|
|
1674
|
+
code: issue.code,
|
|
1675
|
+
path: issue.path,
|
|
1676
|
+
message: issue.message,
|
|
1677
|
+
...issue.detail ? { detail: issue.detail } : {}
|
|
1678
|
+
}))
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
};
|
|
1682
|
+
}
|
|
1683
|
+
try {
|
|
1684
|
+
return await tool.execute(input, context);
|
|
1685
|
+
} catch (error) {
|
|
1686
|
+
return {
|
|
1687
|
+
type: "error",
|
|
1688
|
+
toolCallId: context.toolCallId,
|
|
1689
|
+
tool: identity,
|
|
1690
|
+
error: normalizeRuntimeToolAdapterError(error)
|
|
1691
|
+
};
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
function validateRuntimeToolInput(tool, input) {
|
|
1695
|
+
if (typeof tool.validateInput !== "function") {
|
|
1696
|
+
return { type: "valid" };
|
|
1697
|
+
}
|
|
1698
|
+
return tool.validateInput(input);
|
|
1699
|
+
}
|
|
1700
|
+
function mergeToolMetadata(base, request) {
|
|
1701
|
+
return {
|
|
1702
|
+
...base ?? {},
|
|
1703
|
+
...request ?? {}
|
|
1704
|
+
};
|
|
1705
|
+
}
|
|
1706
|
+
function defaultToolCallId(runId, callIndex) {
|
|
1707
|
+
return `${runId}:tool-${callIndex + 1}`;
|
|
1708
|
+
}
|
|
1709
|
+
function normalizeRuntimeToolAdapterError(error) {
|
|
1710
|
+
if (isRuntimeToolAdapterError(error)) {
|
|
1711
|
+
return error;
|
|
1712
|
+
}
|
|
1713
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
1714
|
+
return {
|
|
1715
|
+
code: "aborted",
|
|
1716
|
+
message: error.message || "Tool execution was aborted.",
|
|
1717
|
+
retryable: true,
|
|
1718
|
+
detail: {
|
|
1719
|
+
name: error.name
|
|
1720
|
+
}
|
|
1721
|
+
};
|
|
1722
|
+
}
|
|
1723
|
+
if (error instanceof Error) {
|
|
1724
|
+
return {
|
|
1725
|
+
code: "backend-error",
|
|
1726
|
+
message: error.message,
|
|
1727
|
+
retryable: false,
|
|
1728
|
+
detail: {
|
|
1729
|
+
name: error.name
|
|
1730
|
+
}
|
|
1731
|
+
};
|
|
1732
|
+
}
|
|
1733
|
+
return {
|
|
1734
|
+
code: "unknown",
|
|
1735
|
+
message: "Tool execution failed with a non-Error value.",
|
|
1736
|
+
retryable: false,
|
|
1737
|
+
detail: {
|
|
1738
|
+
valueType: typeof error
|
|
1739
|
+
}
|
|
1740
|
+
};
|
|
1741
|
+
}
|
|
1742
|
+
function createWebSearchToolAdapter(options) {
|
|
1743
|
+
const identity = mergeIdentity(webSearchIdentity, options.identity);
|
|
1744
|
+
return normalizeBuiltInDogpileTool({
|
|
1745
|
+
name: "webSearch",
|
|
1746
|
+
...options.identity ? { identity: options.identity } : {},
|
|
1747
|
+
...options.permissions ? { permissions: options.permissions } : {},
|
|
1748
|
+
async execute(input, context) {
|
|
1749
|
+
const fetchImplementation = options.fetch ?? globalThis.fetch;
|
|
1750
|
+
if (!fetchImplementation) {
|
|
1751
|
+
return {
|
|
1752
|
+
type: "error",
|
|
1753
|
+
toolCallId: context.toolCallId,
|
|
1754
|
+
tool: identity,
|
|
1755
|
+
error: {
|
|
1756
|
+
code: "unavailable",
|
|
1757
|
+
message: "No fetch implementation is available for webSearch.",
|
|
1758
|
+
retryable: false
|
|
1759
|
+
}
|
|
1760
|
+
};
|
|
1761
|
+
}
|
|
1762
|
+
const request = options.buildRequest ? options.buildRequest(input, context) : defaultWebSearchRequest(options, input);
|
|
1763
|
+
const response = await fetchImplementation(request.url, {
|
|
1764
|
+
...request.init,
|
|
1765
|
+
...context.abortSignal ? { signal: context.abortSignal } : {}
|
|
1766
|
+
});
|
|
1767
|
+
if (!response.ok) {
|
|
1768
|
+
throw {
|
|
1769
|
+
code: response.status >= 500 ? "unavailable" : "backend-error",
|
|
1770
|
+
message: `Web search backend returned HTTP ${response.status}.`,
|
|
1771
|
+
retryable: response.status === 408 || response.status === 429 || response.status >= 500,
|
|
1772
|
+
detail: {
|
|
1773
|
+
status: response.status,
|
|
1774
|
+
statusText: response.statusText
|
|
1775
|
+
}
|
|
1776
|
+
};
|
|
1777
|
+
}
|
|
1778
|
+
const output = options.parseResponse ? await options.parseResponse(response, input, context) : await defaultWebSearchResponseParser(response);
|
|
1779
|
+
return {
|
|
1780
|
+
type: "success",
|
|
1781
|
+
toolCallId: context.toolCallId,
|
|
1782
|
+
tool: identity,
|
|
1783
|
+
output
|
|
1784
|
+
};
|
|
1785
|
+
}
|
|
1786
|
+
});
|
|
1787
|
+
}
|
|
1788
|
+
function createCodeExecToolAdapter(options) {
|
|
1789
|
+
const identity = mergeIdentity(codeExecIdentity, options.identity);
|
|
1790
|
+
const permissions = options.permissions ?? codeExecPermissionsFor(options.languages ?? codeExecLanguages, options.allowNetwork ?? false);
|
|
1791
|
+
const inputSchema = codeExecInputSchemaFor(options.languages ?? codeExecLanguages);
|
|
1792
|
+
return {
|
|
1793
|
+
identity,
|
|
1794
|
+
inputSchema,
|
|
1795
|
+
permissions,
|
|
1796
|
+
validateInput: (input) => validateCodeExecAdapterInput(input, options),
|
|
1797
|
+
async execute(input, context) {
|
|
1798
|
+
const validation = validateCodeExecAdapterInput(input, options);
|
|
1799
|
+
if (validation.type === "invalid") {
|
|
1800
|
+
return {
|
|
1801
|
+
type: "error",
|
|
1802
|
+
toolCallId: context.toolCallId,
|
|
1803
|
+
tool: identity,
|
|
1804
|
+
error: {
|
|
1805
|
+
code: "invalid-input",
|
|
1806
|
+
message: "Invalid codeExec tool input.",
|
|
1807
|
+
retryable: false,
|
|
1808
|
+
detail: {
|
|
1809
|
+
issues: validation.issues
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
};
|
|
1813
|
+
}
|
|
1814
|
+
const timeoutMs = input.timeoutMs ?? options.defaultTimeoutMs;
|
|
1815
|
+
const executionInput = timeoutMs === void 0 ? input : {
|
|
1816
|
+
...input,
|
|
1817
|
+
timeoutMs
|
|
1818
|
+
};
|
|
1819
|
+
try {
|
|
1820
|
+
const output = await executeSandboxWithPolicy(options.execute, executionInput, context, timeoutMs);
|
|
1821
|
+
return {
|
|
1822
|
+
type: "success",
|
|
1823
|
+
toolCallId: context.toolCallId,
|
|
1824
|
+
tool: identity,
|
|
1825
|
+
output
|
|
1826
|
+
};
|
|
1827
|
+
} catch (error) {
|
|
1828
|
+
return {
|
|
1829
|
+
type: "error",
|
|
1830
|
+
toolCallId: context.toolCallId,
|
|
1831
|
+
tool: identity,
|
|
1832
|
+
error: normalizeRuntimeToolAdapterError(error)
|
|
1833
|
+
};
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
};
|
|
1837
|
+
}
|
|
1838
|
+
function normalizeBuiltInDogpileTool(definition) {
|
|
1839
|
+
switch (definition.name) {
|
|
1840
|
+
case "webSearch": {
|
|
1841
|
+
const identity = mergeIdentity(webSearchIdentity, definition.identity);
|
|
1842
|
+
const permissions = definition.permissions ?? webSearchPermissions;
|
|
1843
|
+
const tool = {
|
|
1844
|
+
identity,
|
|
1845
|
+
inputSchema: definition.inputSchema ?? webSearchInputSchema,
|
|
1846
|
+
permissions,
|
|
1847
|
+
validateInput: (input) => validateBuiltInDogpileToolInput("webSearch", input),
|
|
1848
|
+
execute: (input, context) => executeBuiltInTool(identity, definition.execute, input, context, "webSearch")
|
|
1849
|
+
};
|
|
1850
|
+
return tool;
|
|
1851
|
+
}
|
|
1852
|
+
case "codeExec": {
|
|
1853
|
+
const identity = mergeIdentity(codeExecIdentity, definition.identity);
|
|
1854
|
+
const permissions = definition.permissions ?? codeExecPermissions;
|
|
1855
|
+
const tool = {
|
|
1856
|
+
identity,
|
|
1857
|
+
inputSchema: definition.inputSchema ?? codeExecInputSchema,
|
|
1858
|
+
permissions,
|
|
1859
|
+
validateInput: (input) => validateBuiltInDogpileToolInput("codeExec", input),
|
|
1860
|
+
execute: (input, context) => executeBuiltInTool(identity, definition.execute, input, context, "codeExec")
|
|
1861
|
+
};
|
|
1862
|
+
return tool;
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
function normalizeBuiltInDogpileTools(tools) {
|
|
1867
|
+
const normalized = [];
|
|
1868
|
+
if (tools.webSearch) {
|
|
1869
|
+
normalized.push(normalizeBuiltInDogpileTool(asWebSearchDefinition(tools.webSearch)));
|
|
1870
|
+
}
|
|
1871
|
+
if (tools.codeExec) {
|
|
1872
|
+
normalized.push(normalizeBuiltInDogpileTool(asCodeExecDefinition(tools.codeExec)));
|
|
1873
|
+
}
|
|
1874
|
+
return normalized;
|
|
1875
|
+
}
|
|
1876
|
+
async function executeBuiltInTool(identity, execute, input, context, name) {
|
|
1877
|
+
const validation = validateBuiltInDogpileToolInput(
|
|
1878
|
+
name,
|
|
1879
|
+
input
|
|
1880
|
+
);
|
|
1881
|
+
if (validation.type === "invalid") {
|
|
1882
|
+
return {
|
|
1883
|
+
type: "error",
|
|
1884
|
+
toolCallId: context.toolCallId,
|
|
1885
|
+
tool: identity,
|
|
1886
|
+
error: {
|
|
1887
|
+
code: "invalid-input",
|
|
1888
|
+
message: `Invalid ${name} tool input.`,
|
|
1889
|
+
retryable: false,
|
|
1890
|
+
detail: {
|
|
1891
|
+
issues: validation.issues
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
};
|
|
1895
|
+
}
|
|
1896
|
+
try {
|
|
1897
|
+
return await execute(input, context);
|
|
1898
|
+
} catch (error) {
|
|
1899
|
+
return {
|
|
1900
|
+
type: "error",
|
|
1901
|
+
toolCallId: context.toolCallId,
|
|
1902
|
+
tool: identity,
|
|
1903
|
+
error: normalizeRuntimeToolAdapterError(error)
|
|
1904
|
+
};
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
function asWebSearchDefinition(tool) {
|
|
1908
|
+
return typeof tool === "function" ? { name: "webSearch", execute: tool } : tool;
|
|
1909
|
+
}
|
|
1910
|
+
function asCodeExecDefinition(tool) {
|
|
1911
|
+
return typeof tool === "function" ? { name: "codeExec", execute: tool } : tool;
|
|
1912
|
+
}
|
|
1913
|
+
function mergeIdentity(defaultIdentity, options) {
|
|
1914
|
+
if (!options) {
|
|
1915
|
+
return defaultIdentity;
|
|
1916
|
+
}
|
|
1917
|
+
return {
|
|
1918
|
+
...defaultIdentity,
|
|
1919
|
+
...options.namespace !== void 0 ? { namespace: options.namespace } : {},
|
|
1920
|
+
...options.version !== void 0 ? { version: options.version } : {},
|
|
1921
|
+
...options.description !== void 0 ? { description: options.description } : {}
|
|
1922
|
+
};
|
|
1923
|
+
}
|
|
1924
|
+
function defaultWebSearchRequest(options, input, _context) {
|
|
1925
|
+
const url = new URL(String(options.endpoint));
|
|
1926
|
+
url.searchParams.set("q", input.query);
|
|
1927
|
+
url.searchParams.set("limit", String(input.maxResults ?? options.defaultMaxResults ?? 10));
|
|
1928
|
+
return {
|
|
1929
|
+
url,
|
|
1930
|
+
init: {
|
|
1931
|
+
method: "GET",
|
|
1932
|
+
...options.headers ? { headers: options.headers } : {}
|
|
1933
|
+
}
|
|
1934
|
+
};
|
|
1935
|
+
}
|
|
1936
|
+
async function defaultWebSearchResponseParser(response) {
|
|
1937
|
+
const payload = await response.json();
|
|
1938
|
+
const resultValues = Array.isArray(payload) ? payload : isJsonObject(payload) && Array.isArray(payload.results) ? payload.results : void 0;
|
|
1939
|
+
if (!resultValues) {
|
|
1940
|
+
throw {
|
|
1941
|
+
code: "backend-error",
|
|
1942
|
+
message: "Web search backend response must contain a results array.",
|
|
1943
|
+
retryable: false
|
|
1944
|
+
};
|
|
1945
|
+
}
|
|
1946
|
+
return {
|
|
1947
|
+
results: resultValues.map(normalizeWebSearchResult)
|
|
1948
|
+
};
|
|
1949
|
+
}
|
|
1950
|
+
function codeExecPermissionsFor(languages, allowNetwork) {
|
|
1951
|
+
return [
|
|
1952
|
+
{
|
|
1953
|
+
kind: "code-execution",
|
|
1954
|
+
sandbox: "caller-provided",
|
|
1955
|
+
languages,
|
|
1956
|
+
allowNetwork
|
|
1957
|
+
}
|
|
1958
|
+
];
|
|
1959
|
+
}
|
|
1960
|
+
function codeExecInputSchemaFor(languages) {
|
|
1961
|
+
return {
|
|
1962
|
+
kind: "json-schema",
|
|
1963
|
+
...codeExecInputSchema.description ? { description: codeExecInputSchema.description } : {},
|
|
1964
|
+
schema: {
|
|
1965
|
+
type: "object",
|
|
1966
|
+
properties: {
|
|
1967
|
+
language: {
|
|
1968
|
+
type: "string",
|
|
1969
|
+
enum: Array.from(languages)
|
|
1970
|
+
},
|
|
1971
|
+
code: { type: "string" },
|
|
1972
|
+
timeoutMs: { type: "number", minimum: 1 }
|
|
1973
|
+
},
|
|
1974
|
+
required: ["language", "code"],
|
|
1975
|
+
additionalProperties: false
|
|
1976
|
+
}
|
|
1977
|
+
};
|
|
1978
|
+
}
|
|
1979
|
+
async function executeSandboxWithPolicy(execute, input, context, timeoutMs) {
|
|
1980
|
+
if (context.abortSignal?.aborted) {
|
|
1981
|
+
throw {
|
|
1982
|
+
code: "aborted",
|
|
1983
|
+
message: "Code execution was aborted before the sandbox started.",
|
|
1984
|
+
retryable: true
|
|
1985
|
+
};
|
|
1986
|
+
}
|
|
1987
|
+
const execution = Promise.resolve().then(() => execute(input, context));
|
|
1988
|
+
if (timeoutMs === void 0 && context.abortSignal === void 0) {
|
|
1989
|
+
return await execution;
|
|
1990
|
+
}
|
|
1991
|
+
return await new Promise((resolve, reject) => {
|
|
1992
|
+
let timeoutId;
|
|
1993
|
+
const cleanup = () => {
|
|
1994
|
+
if (timeoutId !== void 0) {
|
|
1995
|
+
clearTimeout(timeoutId);
|
|
1996
|
+
}
|
|
1997
|
+
context.abortSignal?.removeEventListener("abort", abortHandler);
|
|
1998
|
+
};
|
|
1999
|
+
const abortHandler = () => {
|
|
2000
|
+
cleanup();
|
|
2001
|
+
reject({
|
|
2002
|
+
code: "aborted",
|
|
2003
|
+
message: "Code execution was aborted.",
|
|
2004
|
+
retryable: true
|
|
2005
|
+
});
|
|
2006
|
+
};
|
|
2007
|
+
if (context.abortSignal) {
|
|
2008
|
+
context.abortSignal.addEventListener("abort", abortHandler, { once: true });
|
|
2009
|
+
}
|
|
2010
|
+
if (timeoutMs !== void 0) {
|
|
2011
|
+
timeoutId = setTimeout(() => {
|
|
2012
|
+
cleanup();
|
|
2013
|
+
reject({
|
|
2014
|
+
code: "timeout",
|
|
2015
|
+
message: `Code execution exceeded timeout of ${timeoutMs}ms.`,
|
|
2016
|
+
retryable: true,
|
|
2017
|
+
detail: {
|
|
2018
|
+
timeoutMs
|
|
2019
|
+
}
|
|
2020
|
+
});
|
|
2021
|
+
}, timeoutMs);
|
|
2022
|
+
}
|
|
2023
|
+
execution.then(
|
|
2024
|
+
(output) => {
|
|
2025
|
+
cleanup();
|
|
2026
|
+
resolve(output);
|
|
2027
|
+
},
|
|
2028
|
+
(error) => {
|
|
2029
|
+
cleanup();
|
|
2030
|
+
reject(error);
|
|
2031
|
+
}
|
|
2032
|
+
);
|
|
2033
|
+
});
|
|
2034
|
+
}
|
|
2035
|
+
function normalizeWebSearchResult(value) {
|
|
2036
|
+
if (!isJsonObject(value)) {
|
|
2037
|
+
throw {
|
|
2038
|
+
code: "backend-error",
|
|
2039
|
+
message: "Web search result must be a JSON object.",
|
|
2040
|
+
retryable: false
|
|
2041
|
+
};
|
|
2042
|
+
}
|
|
2043
|
+
const title = jsonString(value.title, "title");
|
|
2044
|
+
const url = jsonString(value.url, "url");
|
|
2045
|
+
const snippet = optionalJsonString(value.snippet, "snippet");
|
|
2046
|
+
const metadata = optionalJsonObject(value.metadata, "metadata");
|
|
2047
|
+
return {
|
|
2048
|
+
title,
|
|
2049
|
+
url,
|
|
2050
|
+
...snippet !== void 0 ? { snippet } : {},
|
|
2051
|
+
...metadata !== void 0 ? { metadata } : {}
|
|
2052
|
+
};
|
|
2053
|
+
}
|
|
2054
|
+
function jsonString(value, fieldName) {
|
|
2055
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
2056
|
+
throw {
|
|
2057
|
+
code: "backend-error",
|
|
2058
|
+
message: `Web search result ${fieldName} must be a non-empty string.`,
|
|
2059
|
+
retryable: false
|
|
2060
|
+
};
|
|
2061
|
+
}
|
|
2062
|
+
return value;
|
|
2063
|
+
}
|
|
2064
|
+
function optionalJsonString(value, fieldName) {
|
|
2065
|
+
if (value === void 0) {
|
|
2066
|
+
return void 0;
|
|
2067
|
+
}
|
|
2068
|
+
if (typeof value !== "string") {
|
|
2069
|
+
throw {
|
|
2070
|
+
code: "backend-error",
|
|
2071
|
+
message: `Web search result ${fieldName} must be a string when present.`,
|
|
2072
|
+
retryable: false
|
|
2073
|
+
};
|
|
2074
|
+
}
|
|
2075
|
+
return value;
|
|
2076
|
+
}
|
|
2077
|
+
function optionalJsonObject(value, fieldName) {
|
|
2078
|
+
if (value === void 0) {
|
|
2079
|
+
return void 0;
|
|
2080
|
+
}
|
|
2081
|
+
if (!isJsonObject(value)) {
|
|
2082
|
+
throw {
|
|
2083
|
+
code: "backend-error",
|
|
2084
|
+
message: `Web search result ${fieldName} must be a JSON object when present.`,
|
|
2085
|
+
retryable: false
|
|
2086
|
+
};
|
|
2087
|
+
}
|
|
2088
|
+
return value;
|
|
2089
|
+
}
|
|
2090
|
+
function isJsonObject(value) {
|
|
2091
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2092
|
+
}
|
|
2093
|
+
function validateWebSearchInput(input) {
|
|
2094
|
+
const issues = [];
|
|
2095
|
+
if (typeof input.query !== "string") {
|
|
2096
|
+
issues.push({
|
|
2097
|
+
code: input.query === void 0 ? "missing-field" : "invalid-type",
|
|
2098
|
+
path: "query",
|
|
2099
|
+
message: "webSearch.query must be a string."
|
|
2100
|
+
});
|
|
2101
|
+
} else if (input.query.trim().length === 0) {
|
|
2102
|
+
issues.push({
|
|
2103
|
+
code: "invalid-value",
|
|
2104
|
+
path: "query",
|
|
2105
|
+
message: "webSearch.query must not be empty."
|
|
2106
|
+
});
|
|
2107
|
+
}
|
|
2108
|
+
if (input.maxResults !== void 0) {
|
|
2109
|
+
if (typeof input.maxResults !== "number" || !Number.isFinite(input.maxResults)) {
|
|
2110
|
+
issues.push({
|
|
2111
|
+
code: "invalid-type",
|
|
2112
|
+
path: "maxResults",
|
|
2113
|
+
message: "webSearch.maxResults must be a finite number."
|
|
2114
|
+
});
|
|
2115
|
+
} else if (input.maxResults < 1) {
|
|
2116
|
+
issues.push({
|
|
2117
|
+
code: "out-of-range",
|
|
2118
|
+
path: "maxResults",
|
|
2119
|
+
message: "webSearch.maxResults must be greater than or equal to 1.",
|
|
2120
|
+
detail: {
|
|
2121
|
+
minimum: 1
|
|
2122
|
+
}
|
|
2123
|
+
});
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
2126
|
+
return issues;
|
|
2127
|
+
}
|
|
2128
|
+
function validateCodeExecInput(input) {
|
|
2129
|
+
const issues = [];
|
|
2130
|
+
if (typeof input.language !== "string") {
|
|
2131
|
+
issues.push({
|
|
2132
|
+
code: input.language === void 0 ? "missing-field" : "invalid-type",
|
|
2133
|
+
path: "language",
|
|
2134
|
+
message: "codeExec.language must be a string."
|
|
2135
|
+
});
|
|
2136
|
+
} else if (!isCodeExecLanguage(input.language)) {
|
|
2137
|
+
issues.push({
|
|
2138
|
+
code: "invalid-value",
|
|
2139
|
+
path: "language",
|
|
2140
|
+
message: "codeExec.language must be one of javascript, typescript, python, bash, or shell.",
|
|
2141
|
+
detail: {
|
|
2142
|
+
allowed: ["javascript", "typescript", "python", "bash", "shell"]
|
|
2143
|
+
}
|
|
2144
|
+
});
|
|
2145
|
+
}
|
|
2146
|
+
if (typeof input.code !== "string") {
|
|
2147
|
+
issues.push({
|
|
2148
|
+
code: input.code === void 0 ? "missing-field" : "invalid-type",
|
|
2149
|
+
path: "code",
|
|
2150
|
+
message: "codeExec.code must be a string."
|
|
2151
|
+
});
|
|
2152
|
+
}
|
|
2153
|
+
if (input.timeoutMs !== void 0) {
|
|
2154
|
+
if (typeof input.timeoutMs !== "number" || !Number.isFinite(input.timeoutMs)) {
|
|
2155
|
+
issues.push({
|
|
2156
|
+
code: "invalid-type",
|
|
2157
|
+
path: "timeoutMs",
|
|
2158
|
+
message: "codeExec.timeoutMs must be a finite number."
|
|
2159
|
+
});
|
|
2160
|
+
} else if (input.timeoutMs < 1) {
|
|
2161
|
+
issues.push({
|
|
2162
|
+
code: "out-of-range",
|
|
2163
|
+
path: "timeoutMs",
|
|
2164
|
+
message: "codeExec.timeoutMs must be greater than or equal to 1.",
|
|
2165
|
+
detail: {
|
|
2166
|
+
minimum: 1
|
|
2167
|
+
}
|
|
2168
|
+
});
|
|
2169
|
+
}
|
|
2170
|
+
}
|
|
2171
|
+
return issues;
|
|
2172
|
+
}
|
|
2173
|
+
function isCodeExecLanguage(value) {
|
|
2174
|
+
return value === "javascript" || value === "typescript" || value === "python" || value === "bash" || value === "shell";
|
|
2175
|
+
}
|
|
2176
|
+
function isRuntimeToolAdapterError(error) {
|
|
2177
|
+
if (typeof error !== "object" || error === null || !("code" in error) || !("message" in error)) {
|
|
2178
|
+
return false;
|
|
2179
|
+
}
|
|
2180
|
+
const candidate = error;
|
|
2181
|
+
return isRuntimeToolAdapterErrorCode(candidate.code) && typeof candidate.message === "string";
|
|
2182
|
+
}
|
|
2183
|
+
function isRuntimeToolAdapterErrorCode(value) {
|
|
2184
|
+
return value === "invalid-input" || value === "permission-denied" || value === "timeout" || value === "aborted" || value === "unavailable" || value === "backend-error" || value === "unknown";
|
|
2185
|
+
}
|
|
2186
|
+
async function runBroadcast(options) {
|
|
2187
|
+
const runId = createRunId$3();
|
|
2188
|
+
const events = [];
|
|
2189
|
+
const transcript = [];
|
|
2190
|
+
const protocolDecisions = [];
|
|
2191
|
+
const providerCalls = [];
|
|
2192
|
+
let totalCost = emptyCost();
|
|
2193
|
+
const maxRounds = options.protocol.maxRounds ?? 2;
|
|
2194
|
+
let firstRoundContributions = [];
|
|
2195
|
+
let lastContributions = [];
|
|
2196
|
+
const startedAtMs = nowMs$3();
|
|
2197
|
+
let stopped = false;
|
|
2198
|
+
let termination;
|
|
2199
|
+
const emit = (event) => {
|
|
2200
|
+
events.push(event);
|
|
2201
|
+
options.emit?.(event);
|
|
2202
|
+
};
|
|
2203
|
+
const recordProtocolDecision = (event, decisionOptions) => {
|
|
2204
|
+
protocolDecisions.push(
|
|
2205
|
+
createReplayTraceProtocolDecision("broadcast", event, events.length - 1, decisionOptions)
|
|
2206
|
+
);
|
|
2207
|
+
};
|
|
2208
|
+
const toolExecutor = createRuntimeToolExecutor({
|
|
2209
|
+
runId,
|
|
2210
|
+
protocol: "broadcast",
|
|
2211
|
+
tier: options.tier,
|
|
2212
|
+
tools: options.tools,
|
|
2213
|
+
emit(event) {
|
|
2214
|
+
emit(event);
|
|
2215
|
+
recordProtocolDecision(event);
|
|
2216
|
+
},
|
|
2217
|
+
getTrace: () => ({ events, transcript }),
|
|
2218
|
+
...options.signal !== void 0 ? { abortSignal: options.signal } : {}
|
|
2219
|
+
});
|
|
2220
|
+
const toolAvailability = runtimeToolAvailability(toolExecutor.tools);
|
|
2221
|
+
throwIfAborted(options.signal, options.model.id);
|
|
2222
|
+
for (const agent of options.agents) {
|
|
2223
|
+
const event = {
|
|
2224
|
+
type: "role-assignment",
|
|
2225
|
+
runId,
|
|
2226
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2227
|
+
agentId: agent.id,
|
|
2228
|
+
role: agent.role
|
|
2229
|
+
};
|
|
2230
|
+
emit(event);
|
|
2231
|
+
recordProtocolDecision(event);
|
|
2232
|
+
}
|
|
2233
|
+
for (let round = 1; round <= maxRounds; round += 1) {
|
|
2234
|
+
if (stopIfNeeded()) {
|
|
2235
|
+
break;
|
|
2236
|
+
}
|
|
2237
|
+
const providerCallSlots = [];
|
|
2238
|
+
const turnResults = await Promise.all(
|
|
2239
|
+
options.agents.map(async (agent, agentIndex) => {
|
|
2240
|
+
const turn = transcript.length + agentIndex + 1;
|
|
2241
|
+
const input = buildBroadcastInput(options.intent, round, maxRounds, firstRoundContributions);
|
|
2242
|
+
const request = {
|
|
2243
|
+
temperature: options.temperature,
|
|
2244
|
+
...options.signal !== void 0 ? { signal: options.signal } : {},
|
|
2245
|
+
metadata: {
|
|
2246
|
+
runId,
|
|
2247
|
+
protocol: "broadcast",
|
|
2248
|
+
agentId: agent.id,
|
|
2249
|
+
role: agent.role,
|
|
2250
|
+
tier: options.tier,
|
|
2251
|
+
round,
|
|
2252
|
+
...toolAvailability
|
|
2253
|
+
},
|
|
2254
|
+
messages: [
|
|
2255
|
+
{
|
|
2256
|
+
role: "system",
|
|
2257
|
+
content: buildSystemPrompt$3(agent)
|
|
2258
|
+
},
|
|
2259
|
+
{
|
|
2260
|
+
role: "user",
|
|
2261
|
+
content: input
|
|
2262
|
+
}
|
|
2263
|
+
]
|
|
2264
|
+
};
|
|
2265
|
+
const response = await generateModelTurn({
|
|
2266
|
+
model: options.model,
|
|
2267
|
+
request,
|
|
2268
|
+
runId,
|
|
2269
|
+
agent,
|
|
2270
|
+
input,
|
|
2271
|
+
emit,
|
|
2272
|
+
callId: providerCallIdFor$2(runId, providerCalls.length + agentIndex + 1),
|
|
2273
|
+
onProviderCall(call) {
|
|
2274
|
+
providerCallSlots[agentIndex] = call;
|
|
2275
|
+
}
|
|
2276
|
+
});
|
|
2277
|
+
const decision = parseAgentDecision(response.text);
|
|
2278
|
+
const toolCalls = await executeModelResponseToolRequests({
|
|
2279
|
+
response,
|
|
2280
|
+
executor: toolExecutor,
|
|
2281
|
+
agentId: agent.id,
|
|
2282
|
+
role: agent.role,
|
|
2283
|
+
turn,
|
|
2284
|
+
metadata: {
|
|
2285
|
+
round
|
|
2286
|
+
}
|
|
2287
|
+
});
|
|
2288
|
+
throwIfAborted(options.signal, options.model.id);
|
|
2289
|
+
return {
|
|
2290
|
+
agent,
|
|
2291
|
+
agentIndex,
|
|
2292
|
+
turn,
|
|
2293
|
+
input,
|
|
2294
|
+
response,
|
|
2295
|
+
decision,
|
|
2296
|
+
toolCalls,
|
|
2297
|
+
turnCost: responseCost$3(response)
|
|
2298
|
+
};
|
|
2299
|
+
})
|
|
2300
|
+
);
|
|
2301
|
+
providerCalls.push(...providerCallSlots.filter((call) => call !== void 0));
|
|
2302
|
+
const contributions = [];
|
|
2303
|
+
for (const result of turnResults) {
|
|
2304
|
+
totalCost = addCost(totalCost, result.turnCost);
|
|
2305
|
+
transcript.push({
|
|
2306
|
+
agentId: result.agent.id,
|
|
2307
|
+
role: result.agent.role,
|
|
2308
|
+
input: result.input,
|
|
2309
|
+
output: result.response.text,
|
|
2310
|
+
...result.decision !== void 0 ? { decision: result.decision } : {},
|
|
2311
|
+
...result.toolCalls.length > 0 ? { toolCalls: result.toolCalls } : {}
|
|
2312
|
+
});
|
|
2313
|
+
contributions.push({
|
|
2314
|
+
agentId: result.agent.id,
|
|
2315
|
+
role: result.agent.role,
|
|
2316
|
+
output: result.response.text,
|
|
2317
|
+
...result.decision !== void 0 ? { decision: result.decision } : {}
|
|
2318
|
+
});
|
|
2319
|
+
const event = {
|
|
2320
|
+
type: "agent-turn",
|
|
2321
|
+
runId,
|
|
2322
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2323
|
+
agentId: result.agent.id,
|
|
2324
|
+
role: result.agent.role,
|
|
2325
|
+
input: result.input,
|
|
2326
|
+
output: result.response.text,
|
|
2327
|
+
...result.decision !== void 0 ? { decision: result.decision } : {},
|
|
2328
|
+
cost: totalCost
|
|
2329
|
+
};
|
|
2330
|
+
emit(event);
|
|
2331
|
+
recordProtocolDecision(event, {
|
|
2332
|
+
round,
|
|
2333
|
+
turn: result.turn,
|
|
2334
|
+
transcriptEntryCount: transcript.length,
|
|
2335
|
+
contributionCount: result.agentIndex + 1
|
|
2336
|
+
});
|
|
2337
|
+
}
|
|
2338
|
+
if (contributions.length === 0) {
|
|
2339
|
+
break;
|
|
2340
|
+
}
|
|
2341
|
+
if (round === 1) {
|
|
2342
|
+
firstRoundContributions = contributions;
|
|
2343
|
+
}
|
|
2344
|
+
lastContributions = contributions;
|
|
2345
|
+
const broadcast = {
|
|
2346
|
+
type: "broadcast",
|
|
2347
|
+
runId,
|
|
2348
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2349
|
+
round,
|
|
2350
|
+
contributions,
|
|
2351
|
+
cost: totalCost
|
|
2352
|
+
};
|
|
2353
|
+
emit(broadcast);
|
|
2354
|
+
recordProtocolDecision(broadcast, {
|
|
2355
|
+
round,
|
|
2356
|
+
transcriptEntryCount: transcript.length,
|
|
2357
|
+
contributionCount: contributions.length
|
|
2358
|
+
});
|
|
2359
|
+
if (stopIfNeeded()) {
|
|
2360
|
+
break;
|
|
2361
|
+
}
|
|
2362
|
+
}
|
|
2363
|
+
const output = synthesizeBroadcastOutput(lastContributions);
|
|
2364
|
+
throwIfAborted(options.signal, options.model.id);
|
|
2365
|
+
const final = {
|
|
2366
|
+
type: "final",
|
|
2367
|
+
runId,
|
|
2368
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2369
|
+
output,
|
|
2370
|
+
cost: totalCost,
|
|
2371
|
+
transcript: createTranscriptLink(transcript),
|
|
2372
|
+
...termination !== void 0 ? { termination } : {}
|
|
2373
|
+
};
|
|
2374
|
+
emit(final);
|
|
2375
|
+
recordProtocolDecision(final, {
|
|
2376
|
+
transcriptEntryCount: transcript.length
|
|
2377
|
+
});
|
|
2378
|
+
const finalEvent = events.at(-1);
|
|
2379
|
+
return {
|
|
2380
|
+
output,
|
|
2381
|
+
eventLog: createRunEventLog(runId, "broadcast", events),
|
|
2382
|
+
trace: {
|
|
2383
|
+
schemaVersion: "1.0",
|
|
2384
|
+
runId,
|
|
2385
|
+
protocol: "broadcast",
|
|
2386
|
+
tier: options.tier,
|
|
2387
|
+
modelProviderId: options.model.id,
|
|
2388
|
+
agentsUsed: options.agents,
|
|
2389
|
+
inputs: createReplayTraceRunInputs({
|
|
2390
|
+
intent: options.intent,
|
|
2391
|
+
protocol: options.protocol,
|
|
2392
|
+
tier: options.tier,
|
|
2393
|
+
modelProviderId: options.model.id,
|
|
2394
|
+
agents: options.agents,
|
|
2395
|
+
temperature: options.temperature
|
|
2396
|
+
}),
|
|
2397
|
+
budget: createReplayTraceBudget({
|
|
2398
|
+
tier: options.tier,
|
|
2399
|
+
...options.budget ? { caps: options.budget } : {},
|
|
2400
|
+
...options.terminate ? { termination: options.terminate } : {}
|
|
2401
|
+
}),
|
|
2402
|
+
budgetStateChanges: createReplayTraceBudgetStateChanges(events),
|
|
2403
|
+
seed: createReplayTraceSeed(options.seed),
|
|
2404
|
+
protocolDecisions,
|
|
2405
|
+
providerCalls,
|
|
2406
|
+
finalOutput: createReplayTraceFinalOutput(output, finalEvent ?? {
|
|
2407
|
+
type: "final",
|
|
2408
|
+
at: "",
|
|
2409
|
+
cost: totalCost,
|
|
2410
|
+
transcript: createTranscriptLink(transcript)
|
|
2411
|
+
}),
|
|
2412
|
+
events,
|
|
2413
|
+
transcript
|
|
2414
|
+
},
|
|
2415
|
+
transcript,
|
|
2416
|
+
usage: createRunUsage(totalCost),
|
|
2417
|
+
metadata: createRunMetadata({
|
|
2418
|
+
runId,
|
|
2419
|
+
protocol: "broadcast",
|
|
2420
|
+
tier: options.tier,
|
|
2421
|
+
modelProviderId: options.model.id,
|
|
2422
|
+
agentsUsed: options.agents,
|
|
2423
|
+
events
|
|
2424
|
+
}),
|
|
2425
|
+
accounting: createRunAccounting({
|
|
2426
|
+
tier: options.tier,
|
|
2427
|
+
...options.budget ? { budget: options.budget } : {},
|
|
2428
|
+
...options.terminate ? { termination: options.terminate } : {},
|
|
2429
|
+
cost: totalCost,
|
|
2430
|
+
events
|
|
2431
|
+
}),
|
|
2432
|
+
cost: totalCost
|
|
2433
|
+
};
|
|
2434
|
+
function stopIfNeeded() {
|
|
2435
|
+
throwIfAborted(options.signal, options.model.id);
|
|
2436
|
+
if (stopped || !options.terminate) {
|
|
2437
|
+
return stopped;
|
|
2438
|
+
}
|
|
2439
|
+
const stopRecord2 = evaluateTerminationStop(options.terminate, {
|
|
2440
|
+
protocol: "broadcast",
|
|
2441
|
+
tier: options.tier,
|
|
2442
|
+
cost: totalCost,
|
|
2443
|
+
transcript,
|
|
2444
|
+
iteration: transcript.length,
|
|
2445
|
+
elapsedMs: elapsedMs$3(startedAtMs)
|
|
2446
|
+
});
|
|
2447
|
+
if (!stopRecord2) {
|
|
2448
|
+
return false;
|
|
2449
|
+
}
|
|
2450
|
+
stopped = true;
|
|
2451
|
+
termination = stopRecord2;
|
|
2452
|
+
if (stopRecord2.reason === "budget") {
|
|
2453
|
+
emitBudgetStop(stopRecord2);
|
|
2454
|
+
}
|
|
2455
|
+
return true;
|
|
2456
|
+
}
|
|
2457
|
+
function emitBudgetStop(record) {
|
|
2458
|
+
const event = {
|
|
2459
|
+
type: "budget-stop",
|
|
2460
|
+
runId,
|
|
2461
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2462
|
+
reason: record.budgetReason ?? "cost",
|
|
2463
|
+
cost: totalCost,
|
|
2464
|
+
iteration: transcript.length,
|
|
2465
|
+
elapsedMs: elapsedMs$3(startedAtMs),
|
|
2466
|
+
detail: record.detail ?? {}
|
|
2467
|
+
};
|
|
2468
|
+
emit(event);
|
|
2469
|
+
recordProtocolDecision(event, {
|
|
2470
|
+
transcriptEntryCount: transcript.length
|
|
2471
|
+
});
|
|
2472
|
+
}
|
|
2473
|
+
}
|
|
2474
|
+
function buildSystemPrompt$3(agent) {
|
|
2475
|
+
const instruction = agent.instructions ? `
|
|
2476
|
+
Instructions: ${agent.instructions}` : "";
|
|
2477
|
+
return `You are ${agent.id}, acting as ${agent.role} in a Broadcast multi-agent protocol.${instruction}`;
|
|
2478
|
+
}
|
|
2479
|
+
function buildBroadcastInput(intent, round, maxRounds, firstRoundContributions) {
|
|
2480
|
+
if (maxRounds === 1) {
|
|
2481
|
+
return `Mission: ${intent}
|
|
2482
|
+
Broadcast round ${round}: contribute independently before synthesis.`;
|
|
2483
|
+
}
|
|
2484
|
+
if (round === 1) {
|
|
2485
|
+
return `Mission: ${intent}
|
|
2486
|
+
Broadcast round 1: broadcast your intended role and participation decision. Do not produce the final plan yet.`;
|
|
2487
|
+
}
|
|
2488
|
+
const intentions = firstRoundContributions.map((contribution) => `${contribution.role}:${contribution.agentId} => ${contribution.output}`).join("\n");
|
|
2489
|
+
return `Mission: ${intent}
|
|
2490
|
+
|
|
2491
|
+
Round 1 intentions:
|
|
2492
|
+
${intentions || "(none)"}
|
|
2493
|
+
|
|
2494
|
+
Broadcast round ${round}: make your final contribution or abstention decision informed by all round 1 intentions.`;
|
|
2495
|
+
}
|
|
2496
|
+
function synthesizeBroadcastOutput(contributions) {
|
|
2497
|
+
return contributions.map((entry) => `${entry.role}:${entry.agentId} => ${entry.output}`).join("\n");
|
|
2498
|
+
}
|
|
2499
|
+
function responseCost$3(response) {
|
|
2500
|
+
return {
|
|
2501
|
+
usd: response.costUsd ?? 0,
|
|
2502
|
+
inputTokens: response.usage?.inputTokens ?? 0,
|
|
2503
|
+
outputTokens: response.usage?.outputTokens ?? 0,
|
|
2504
|
+
totalTokens: response.usage?.totalTokens ?? 0
|
|
2505
|
+
};
|
|
2506
|
+
}
|
|
2507
|
+
function createRunId$3() {
|
|
2508
|
+
const random = globalThis.crypto?.randomUUID?.();
|
|
2509
|
+
return random ?? `run-${Date.now().toString(36)}`;
|
|
2510
|
+
}
|
|
2511
|
+
function nowMs$3() {
|
|
2512
|
+
return globalThis.performance?.now() ?? Date.now();
|
|
2513
|
+
}
|
|
2514
|
+
function elapsedMs$3(startedAtMs) {
|
|
2515
|
+
return Math.max(0, nowMs$3() - startedAtMs);
|
|
2516
|
+
}
|
|
2517
|
+
function providerCallIdFor$2(runId, oneBasedIndex) {
|
|
2518
|
+
return `${runId}:provider-call:${oneBasedIndex}`;
|
|
2519
|
+
}
|
|
2520
|
+
async function runCoordinator(options) {
|
|
2521
|
+
const runId = createRunId$2();
|
|
2522
|
+
const events = [];
|
|
2523
|
+
const transcript = [];
|
|
2524
|
+
const protocolDecisions = [];
|
|
2525
|
+
const providerCalls = [];
|
|
2526
|
+
let totalCost = emptyCost();
|
|
2527
|
+
const maxTurns = options.protocol.maxTurns ?? options.agents.length;
|
|
2528
|
+
const activeAgents = options.agents.slice(0, maxTurns);
|
|
2529
|
+
const coordinator = activeAgents[0];
|
|
2530
|
+
const startedAtMs = nowMs$2();
|
|
2531
|
+
let stopped = false;
|
|
2532
|
+
let termination;
|
|
2533
|
+
const emit = (event) => {
|
|
2534
|
+
events.push(event);
|
|
2535
|
+
options.emit?.(event);
|
|
2536
|
+
};
|
|
2537
|
+
const recordProtocolDecision = (event, decisionOptions) => {
|
|
2538
|
+
protocolDecisions.push(
|
|
2539
|
+
createReplayTraceProtocolDecision("coordinator", event, events.length - 1, decisionOptions)
|
|
2540
|
+
);
|
|
2541
|
+
};
|
|
2542
|
+
const toolExecutor = createRuntimeToolExecutor({
|
|
2543
|
+
runId,
|
|
2544
|
+
protocol: "coordinator",
|
|
2545
|
+
tier: options.tier,
|
|
2546
|
+
tools: options.tools,
|
|
2547
|
+
emit(event) {
|
|
2548
|
+
emit(event);
|
|
2549
|
+
recordProtocolDecision(event);
|
|
2550
|
+
},
|
|
2551
|
+
getTrace: () => ({ events, transcript }),
|
|
2552
|
+
...options.signal !== void 0 ? { abortSignal: options.signal } : {}
|
|
2553
|
+
});
|
|
2554
|
+
const toolAvailability = runtimeToolAvailability(toolExecutor.tools);
|
|
2555
|
+
throwIfAborted(options.signal, options.model.id);
|
|
2556
|
+
for (const agent of activeAgents) {
|
|
2557
|
+
const event = {
|
|
2558
|
+
type: "role-assignment",
|
|
2559
|
+
runId,
|
|
2560
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2561
|
+
agentId: agent.id,
|
|
2562
|
+
role: agent.role
|
|
2563
|
+
};
|
|
2564
|
+
emit(event);
|
|
2565
|
+
recordProtocolDecision(event);
|
|
2566
|
+
}
|
|
2567
|
+
if (coordinator) {
|
|
2568
|
+
if (!stopIfNeeded()) {
|
|
2569
|
+
totalCost = await runCoordinatorTurn({
|
|
2570
|
+
agent: coordinator,
|
|
2571
|
+
coordinator,
|
|
2572
|
+
input: buildCoordinatorPlanInput(options.intent, coordinator),
|
|
2573
|
+
phase: "plan",
|
|
2574
|
+
options,
|
|
2575
|
+
runId,
|
|
2576
|
+
transcript,
|
|
2577
|
+
totalCost,
|
|
2578
|
+
providerCalls,
|
|
2579
|
+
toolExecutor,
|
|
2580
|
+
toolAvailability,
|
|
2581
|
+
emit,
|
|
2582
|
+
recordProtocolDecision
|
|
2583
|
+
});
|
|
2584
|
+
stopIfNeeded();
|
|
2585
|
+
}
|
|
2586
|
+
if (!stopIfNeeded()) {
|
|
2587
|
+
const workers = activeAgents.slice(1);
|
|
2588
|
+
const providerCallSlots = [];
|
|
2589
|
+
const planTranscript = [...transcript];
|
|
2590
|
+
const workerResults = await Promise.all(
|
|
2591
|
+
workers.map(
|
|
2592
|
+
(agent, index) => runCoordinatorWorkerTurn({
|
|
2593
|
+
agent,
|
|
2594
|
+
coordinator,
|
|
2595
|
+
input: buildWorkerInput(options.intent, planTranscript, coordinator),
|
|
2596
|
+
options,
|
|
2597
|
+
runId,
|
|
2598
|
+
turn: transcript.length + index + 1,
|
|
2599
|
+
providerCallId: providerCallIdFor$1(runId, providerCalls.length + index + 1),
|
|
2600
|
+
providerCallIndex: index,
|
|
2601
|
+
providerCallSlots,
|
|
2602
|
+
toolExecutor,
|
|
2603
|
+
toolAvailability,
|
|
2604
|
+
emit
|
|
2605
|
+
})
|
|
2606
|
+
)
|
|
2607
|
+
);
|
|
2608
|
+
providerCalls.push(...providerCallSlots.filter((call) => call !== void 0));
|
|
2609
|
+
for (const result of workerResults) {
|
|
2610
|
+
totalCost = addCost(totalCost, result.turnCost);
|
|
2611
|
+
transcript.push({
|
|
2612
|
+
agentId: result.agent.id,
|
|
2613
|
+
role: result.agent.role,
|
|
2614
|
+
input: result.input,
|
|
2615
|
+
output: result.response.text,
|
|
2616
|
+
...result.decision !== void 0 ? { decision: result.decision } : {},
|
|
2617
|
+
...result.toolCalls.length > 0 ? { toolCalls: result.toolCalls } : {}
|
|
2618
|
+
});
|
|
2619
|
+
const event = {
|
|
2620
|
+
type: "agent-turn",
|
|
2621
|
+
runId,
|
|
2622
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2623
|
+
agentId: result.agent.id,
|
|
2624
|
+
role: result.agent.role,
|
|
2625
|
+
input: result.input,
|
|
2626
|
+
output: result.response.text,
|
|
2627
|
+
...result.decision !== void 0 ? { decision: result.decision } : {},
|
|
2628
|
+
cost: totalCost
|
|
2629
|
+
};
|
|
2630
|
+
emit(event);
|
|
2631
|
+
recordProtocolDecision(event, {
|
|
2632
|
+
turn: transcript.length,
|
|
2633
|
+
phase: "worker",
|
|
2634
|
+
transcriptEntryCount: transcript.length
|
|
2635
|
+
});
|
|
2636
|
+
}
|
|
2637
|
+
stopIfNeeded();
|
|
2638
|
+
}
|
|
2639
|
+
if (!stopIfNeeded()) {
|
|
2640
|
+
totalCost = await runCoordinatorTurn({
|
|
2641
|
+
agent: coordinator,
|
|
2642
|
+
coordinator,
|
|
2643
|
+
input: buildFinalSynthesisInput(options.intent, transcript, coordinator),
|
|
2644
|
+
phase: "final-synthesis",
|
|
2645
|
+
options,
|
|
2646
|
+
runId,
|
|
2647
|
+
transcript,
|
|
2648
|
+
totalCost,
|
|
2649
|
+
providerCalls,
|
|
2650
|
+
toolExecutor,
|
|
2651
|
+
toolAvailability,
|
|
2652
|
+
emit,
|
|
2653
|
+
recordProtocolDecision
|
|
2654
|
+
});
|
|
2655
|
+
stopIfNeeded();
|
|
2656
|
+
}
|
|
2657
|
+
}
|
|
2658
|
+
const output = transcript.at(-1)?.output ?? "";
|
|
2659
|
+
throwIfAborted(options.signal, options.model.id);
|
|
2660
|
+
const final = {
|
|
2661
|
+
type: "final",
|
|
2662
|
+
runId,
|
|
2663
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2664
|
+
output,
|
|
2665
|
+
cost: totalCost,
|
|
2666
|
+
transcript: createTranscriptLink(transcript),
|
|
2667
|
+
...termination !== void 0 ? { termination } : {}
|
|
2668
|
+
};
|
|
2669
|
+
emit(final);
|
|
2670
|
+
recordProtocolDecision(final, {
|
|
2671
|
+
transcriptEntryCount: transcript.length
|
|
2672
|
+
});
|
|
2673
|
+
const finalEvent = events.at(-1);
|
|
2674
|
+
return {
|
|
2675
|
+
output,
|
|
2676
|
+
eventLog: createRunEventLog(runId, "coordinator", events),
|
|
2677
|
+
trace: {
|
|
2678
|
+
schemaVersion: "1.0",
|
|
2679
|
+
runId,
|
|
2680
|
+
protocol: "coordinator",
|
|
2681
|
+
tier: options.tier,
|
|
2682
|
+
modelProviderId: options.model.id,
|
|
2683
|
+
agentsUsed: activeAgents,
|
|
2684
|
+
inputs: createReplayTraceRunInputs({
|
|
2685
|
+
intent: options.intent,
|
|
2686
|
+
protocol: options.protocol,
|
|
2687
|
+
tier: options.tier,
|
|
2688
|
+
modelProviderId: options.model.id,
|
|
2689
|
+
agents: activeAgents,
|
|
2690
|
+
temperature: options.temperature
|
|
2691
|
+
}),
|
|
2692
|
+
budget: createReplayTraceBudget({
|
|
2693
|
+
tier: options.tier,
|
|
2694
|
+
...options.budget ? { caps: options.budget } : {},
|
|
2695
|
+
...options.terminate ? { termination: options.terminate } : {}
|
|
2696
|
+
}),
|
|
2697
|
+
budgetStateChanges: createReplayTraceBudgetStateChanges(events),
|
|
2698
|
+
seed: createReplayTraceSeed(options.seed),
|
|
2699
|
+
protocolDecisions,
|
|
2700
|
+
providerCalls,
|
|
2701
|
+
finalOutput: createReplayTraceFinalOutput(output, finalEvent ?? {
|
|
2702
|
+
type: "final",
|
|
2703
|
+
at: "",
|
|
2704
|
+
cost: totalCost,
|
|
2705
|
+
transcript: createTranscriptLink(transcript)
|
|
2706
|
+
}),
|
|
2707
|
+
events,
|
|
2708
|
+
transcript
|
|
2709
|
+
},
|
|
2710
|
+
transcript,
|
|
2711
|
+
usage: createRunUsage(totalCost),
|
|
2712
|
+
metadata: createRunMetadata({
|
|
2713
|
+
runId,
|
|
2714
|
+
protocol: "coordinator",
|
|
2715
|
+
tier: options.tier,
|
|
2716
|
+
modelProviderId: options.model.id,
|
|
2717
|
+
agentsUsed: activeAgents,
|
|
2718
|
+
events
|
|
2719
|
+
}),
|
|
2720
|
+
accounting: createRunAccounting({
|
|
2721
|
+
tier: options.tier,
|
|
2722
|
+
...options.budget ? { budget: options.budget } : {},
|
|
2723
|
+
...options.terminate ? { termination: options.terminate } : {},
|
|
2724
|
+
cost: totalCost,
|
|
2725
|
+
events
|
|
2726
|
+
}),
|
|
2727
|
+
cost: totalCost
|
|
2728
|
+
};
|
|
2729
|
+
function stopIfNeeded() {
|
|
2730
|
+
throwIfAborted(options.signal, options.model.id);
|
|
2731
|
+
if (stopped || !options.terminate) {
|
|
2732
|
+
return stopped;
|
|
2733
|
+
}
|
|
2734
|
+
const stopRecord2 = evaluateTerminationStop(options.terminate, {
|
|
2735
|
+
protocol: "coordinator",
|
|
2736
|
+
tier: options.tier,
|
|
2737
|
+
cost: totalCost,
|
|
2738
|
+
transcript,
|
|
2739
|
+
iteration: transcript.length,
|
|
2740
|
+
elapsedMs: elapsedMs$2(startedAtMs)
|
|
2741
|
+
});
|
|
2742
|
+
if (!stopRecord2) {
|
|
2743
|
+
return false;
|
|
2744
|
+
}
|
|
2745
|
+
stopped = true;
|
|
2746
|
+
termination = stopRecord2;
|
|
2747
|
+
if (stopRecord2.reason === "budget") {
|
|
2748
|
+
emitBudgetStop(stopRecord2);
|
|
2749
|
+
}
|
|
2750
|
+
return true;
|
|
2751
|
+
}
|
|
2752
|
+
function emitBudgetStop(record) {
|
|
2753
|
+
const event = {
|
|
2754
|
+
type: "budget-stop",
|
|
2755
|
+
runId,
|
|
2756
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2757
|
+
reason: record.budgetReason ?? "cost",
|
|
2758
|
+
cost: totalCost,
|
|
2759
|
+
iteration: transcript.length,
|
|
2760
|
+
elapsedMs: elapsedMs$2(startedAtMs),
|
|
2761
|
+
detail: record.detail ?? {}
|
|
2762
|
+
};
|
|
2763
|
+
emit(event);
|
|
2764
|
+
recordProtocolDecision(event, {
|
|
2765
|
+
transcriptEntryCount: transcript.length
|
|
2766
|
+
});
|
|
2767
|
+
}
|
|
2768
|
+
}
|
|
2769
|
+
async function runCoordinatorTurn(turn) {
|
|
2770
|
+
throwIfAborted(turn.options.signal, turn.options.model.id);
|
|
2771
|
+
const request = {
|
|
2772
|
+
temperature: turn.options.temperature,
|
|
2773
|
+
...turn.options.signal !== void 0 ? { signal: turn.options.signal } : {},
|
|
2774
|
+
metadata: {
|
|
2775
|
+
runId: turn.runId,
|
|
2776
|
+
protocol: "coordinator",
|
|
2777
|
+
agentId: turn.agent.id,
|
|
2778
|
+
role: turn.agent.role,
|
|
2779
|
+
coordinatorAgentId: turn.coordinator.id,
|
|
2780
|
+
tier: turn.options.tier,
|
|
2781
|
+
phase: turn.phase,
|
|
2782
|
+
...turn.toolAvailability
|
|
2783
|
+
},
|
|
2784
|
+
messages: [
|
|
2785
|
+
{
|
|
2786
|
+
role: "system",
|
|
2787
|
+
content: buildSystemPrompt$2(turn.agent, turn.coordinator)
|
|
2788
|
+
},
|
|
2789
|
+
{
|
|
2790
|
+
role: "user",
|
|
2791
|
+
content: turn.input
|
|
2792
|
+
}
|
|
2793
|
+
]
|
|
2794
|
+
};
|
|
2795
|
+
const response = await generateModelTurn({
|
|
2796
|
+
model: turn.options.model,
|
|
2797
|
+
request,
|
|
2798
|
+
runId: turn.runId,
|
|
2799
|
+
agent: turn.agent,
|
|
2800
|
+
input: turn.input,
|
|
2801
|
+
emit: turn.emit,
|
|
2802
|
+
callId: nextProviderCallId(turn.runId, turn.providerCalls),
|
|
2803
|
+
onProviderCall(call) {
|
|
2804
|
+
turn.providerCalls.push(call);
|
|
2805
|
+
}
|
|
2806
|
+
});
|
|
2807
|
+
const decision = parseAgentDecision(response.text);
|
|
2808
|
+
const totalCost = addCost(turn.totalCost, responseCost$2(response));
|
|
2809
|
+
const toolCalls = await executeModelResponseToolRequests({
|
|
2810
|
+
response,
|
|
2811
|
+
executor: turn.toolExecutor,
|
|
2812
|
+
agentId: turn.agent.id,
|
|
2813
|
+
role: turn.agent.role,
|
|
2814
|
+
turn: turn.transcript.length + 1,
|
|
2815
|
+
metadata: {
|
|
2816
|
+
phase: turn.phase
|
|
2817
|
+
}
|
|
2818
|
+
});
|
|
2819
|
+
throwIfAborted(turn.options.signal, turn.options.model.id);
|
|
2820
|
+
turn.transcript.push({
|
|
2821
|
+
agentId: turn.agent.id,
|
|
2822
|
+
role: turn.agent.role,
|
|
2823
|
+
input: turn.input,
|
|
2824
|
+
output: response.text,
|
|
2825
|
+
...decision !== void 0 ? { decision } : {},
|
|
2826
|
+
...toolCalls.length > 0 ? { toolCalls } : {}
|
|
2827
|
+
});
|
|
2828
|
+
const event = {
|
|
2829
|
+
type: "agent-turn",
|
|
2830
|
+
runId: turn.runId,
|
|
2831
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2832
|
+
agentId: turn.agent.id,
|
|
2833
|
+
role: turn.agent.role,
|
|
2834
|
+
input: turn.input,
|
|
2835
|
+
output: response.text,
|
|
2836
|
+
...decision !== void 0 ? { decision } : {},
|
|
2837
|
+
cost: totalCost
|
|
2838
|
+
};
|
|
2839
|
+
turn.emit(event);
|
|
2840
|
+
turn.recordProtocolDecision(event, {
|
|
2841
|
+
turn: turn.transcript.length,
|
|
2842
|
+
phase: turn.phase,
|
|
2843
|
+
transcriptEntryCount: turn.transcript.length
|
|
2844
|
+
});
|
|
2845
|
+
return totalCost;
|
|
2846
|
+
}
|
|
2847
|
+
async function runCoordinatorWorkerTurn(turn) {
|
|
2848
|
+
throwIfAborted(turn.options.signal, turn.options.model.id);
|
|
2849
|
+
const request = {
|
|
2850
|
+
temperature: turn.options.temperature,
|
|
2851
|
+
...turn.options.signal !== void 0 ? { signal: turn.options.signal } : {},
|
|
2852
|
+
metadata: {
|
|
2853
|
+
runId: turn.runId,
|
|
2854
|
+
protocol: "coordinator",
|
|
2855
|
+
agentId: turn.agent.id,
|
|
2856
|
+
role: turn.agent.role,
|
|
2857
|
+
coordinatorAgentId: turn.coordinator.id,
|
|
2858
|
+
tier: turn.options.tier,
|
|
2859
|
+
phase: "worker",
|
|
2860
|
+
...turn.toolAvailability
|
|
2861
|
+
},
|
|
2862
|
+
messages: [
|
|
2863
|
+
{
|
|
2864
|
+
role: "system",
|
|
2865
|
+
content: buildSystemPrompt$2(turn.agent, turn.coordinator)
|
|
2866
|
+
},
|
|
2867
|
+
{
|
|
2868
|
+
role: "user",
|
|
2869
|
+
content: turn.input
|
|
2870
|
+
}
|
|
2871
|
+
]
|
|
2872
|
+
};
|
|
2873
|
+
const response = await generateModelTurn({
|
|
2874
|
+
model: turn.options.model,
|
|
2875
|
+
request,
|
|
2876
|
+
runId: turn.runId,
|
|
2877
|
+
agent: turn.agent,
|
|
2878
|
+
input: turn.input,
|
|
2879
|
+
emit: turn.emit,
|
|
2880
|
+
callId: turn.providerCallId,
|
|
2881
|
+
onProviderCall(call) {
|
|
2882
|
+
turn.providerCallSlots[turn.providerCallIndex] = call;
|
|
2883
|
+
}
|
|
2884
|
+
});
|
|
2885
|
+
const decision = parseAgentDecision(response.text);
|
|
2886
|
+
const toolCalls = await executeModelResponseToolRequests({
|
|
2887
|
+
response,
|
|
2888
|
+
executor: turn.toolExecutor,
|
|
2889
|
+
agentId: turn.agent.id,
|
|
2890
|
+
role: turn.agent.role,
|
|
2891
|
+
turn: turn.turn,
|
|
2892
|
+
metadata: {
|
|
2893
|
+
phase: "worker"
|
|
2894
|
+
}
|
|
2895
|
+
});
|
|
2896
|
+
throwIfAborted(turn.options.signal, turn.options.model.id);
|
|
2897
|
+
return {
|
|
2898
|
+
agent: turn.agent,
|
|
2899
|
+
input: turn.input,
|
|
2900
|
+
response,
|
|
2901
|
+
decision,
|
|
2902
|
+
toolCalls,
|
|
2903
|
+
turnCost: responseCost$2(response)
|
|
2904
|
+
};
|
|
2905
|
+
}
|
|
2906
|
+
function buildSystemPrompt$2(agent, coordinator) {
|
|
2907
|
+
const instruction = agent.instructions ? `
|
|
2908
|
+
Instructions: ${agent.instructions}` : "";
|
|
2909
|
+
const coordinatorText = coordinator && agent.id === coordinator.id ? "You are the coordinator: assign work, integrate worker contributions, and produce the final answer." : `You are a worker managed by coordinator ${coordinator?.id ?? "unknown"}.`;
|
|
2910
|
+
return `You are ${agent.id}, acting as ${agent.role} in a Coordinator multi-agent protocol. ${coordinatorText}${instruction}`;
|
|
2911
|
+
}
|
|
2912
|
+
function buildCoordinatorPlanInput(intent, coordinator) {
|
|
2913
|
+
return `Mission: ${intent}
|
|
2914
|
+
Coordinator ${coordinator.id}: assign the work, name the plan, and provide the first contribution.`;
|
|
2915
|
+
}
|
|
2916
|
+
function buildWorkerInput(intent, transcript, coordinator) {
|
|
2917
|
+
const prior = transcript.map((entry) => `${entry.role} (${entry.agentId}): ${entry.output}`).join("\n\n");
|
|
2918
|
+
return `Mission: ${intent}
|
|
2919
|
+
|
|
2920
|
+
Coordinator: ${coordinator.id}
|
|
2921
|
+
Prior contributions:
|
|
2922
|
+
${prior}
|
|
2923
|
+
|
|
2924
|
+
Follow the coordinator-managed plan and provide your assigned contribution.`;
|
|
2925
|
+
}
|
|
2926
|
+
function buildFinalSynthesisInput(intent, transcript, coordinator) {
|
|
2927
|
+
const prior = transcript.map((entry) => `${entry.role} (${entry.agentId}): ${entry.output}`).join("\n\n");
|
|
2928
|
+
return `Mission: ${intent}
|
|
2929
|
+
|
|
2930
|
+
Coordinator: ${coordinator.id}
|
|
2931
|
+
Prior contributions:
|
|
2932
|
+
${prior}
|
|
2933
|
+
|
|
2934
|
+
Synthesize the final answer as the coordinator.`;
|
|
2935
|
+
}
|
|
2936
|
+
function responseCost$2(response) {
|
|
2937
|
+
return {
|
|
2938
|
+
usd: response.costUsd ?? 0,
|
|
2939
|
+
inputTokens: response.usage?.inputTokens ?? 0,
|
|
2940
|
+
outputTokens: response.usage?.outputTokens ?? 0,
|
|
2941
|
+
totalTokens: response.usage?.totalTokens ?? 0
|
|
2942
|
+
};
|
|
2943
|
+
}
|
|
2944
|
+
function createRunId$2() {
|
|
2945
|
+
const random = globalThis.crypto?.randomUUID?.();
|
|
2946
|
+
return random ?? `run-${Date.now().toString(36)}`;
|
|
2947
|
+
}
|
|
2948
|
+
function nowMs$2() {
|
|
2949
|
+
return globalThis.performance?.now() ?? Date.now();
|
|
2950
|
+
}
|
|
2951
|
+
function elapsedMs$2(startedAtMs) {
|
|
2952
|
+
return Math.max(0, nowMs$2() - startedAtMs);
|
|
2953
|
+
}
|
|
2954
|
+
function providerCallIdFor$1(runId, oneBasedIndex) {
|
|
2955
|
+
return `${runId}:provider-call:${oneBasedIndex}`;
|
|
2956
|
+
}
|
|
2957
|
+
async function runSequential(options) {
|
|
2958
|
+
const runId = createRunId$1();
|
|
2959
|
+
const events = [];
|
|
2960
|
+
const transcript = [];
|
|
2961
|
+
const protocolDecisions = [];
|
|
2962
|
+
const providerCalls = [];
|
|
2963
|
+
let totalCost = emptyCost();
|
|
2964
|
+
const maxTurns = options.protocol.maxTurns ?? options.agents.length;
|
|
2965
|
+
const activeAgents = options.agents.slice(0, maxTurns);
|
|
2966
|
+
const startedAtMs = nowMs$1();
|
|
2967
|
+
let stopped = false;
|
|
2968
|
+
let termination;
|
|
2969
|
+
const emit = (event) => {
|
|
2970
|
+
events.push(event);
|
|
2971
|
+
options.emit?.(event);
|
|
2972
|
+
};
|
|
2973
|
+
const recordProtocolDecision = (event, decisionOptions) => {
|
|
2974
|
+
protocolDecisions.push(
|
|
2975
|
+
createReplayTraceProtocolDecision("sequential", event, events.length - 1, decisionOptions)
|
|
2976
|
+
);
|
|
2977
|
+
};
|
|
2978
|
+
const toolExecutor = createRuntimeToolExecutor({
|
|
2979
|
+
runId,
|
|
2980
|
+
protocol: "sequential",
|
|
2981
|
+
tier: options.tier,
|
|
2982
|
+
tools: options.tools,
|
|
2983
|
+
emit(event) {
|
|
2984
|
+
emit(event);
|
|
2985
|
+
recordProtocolDecision(event);
|
|
2986
|
+
},
|
|
2987
|
+
getTrace: () => ({ events, transcript }),
|
|
2988
|
+
...options.signal !== void 0 ? { abortSignal: options.signal } : {}
|
|
2989
|
+
});
|
|
2990
|
+
const toolAvailability = runtimeToolAvailability(toolExecutor.tools);
|
|
2991
|
+
throwIfAborted(options.signal, options.model.id);
|
|
2992
|
+
for (const agent of activeAgents) {
|
|
2993
|
+
const event = {
|
|
2994
|
+
type: "role-assignment",
|
|
2995
|
+
runId,
|
|
2996
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2997
|
+
agentId: agent.id,
|
|
2998
|
+
role: agent.role
|
|
2999
|
+
};
|
|
3000
|
+
emit(event);
|
|
3001
|
+
recordProtocolDecision(event);
|
|
3002
|
+
}
|
|
3003
|
+
for (const [index, agent] of activeAgents.entries()) {
|
|
3004
|
+
if (stopIfNeeded()) {
|
|
3005
|
+
break;
|
|
3006
|
+
}
|
|
3007
|
+
const turn = index + 1;
|
|
3008
|
+
const input = buildSequentialInput(options.intent, transcript);
|
|
3009
|
+
const request = {
|
|
3010
|
+
temperature: options.temperature,
|
|
3011
|
+
...options.signal !== void 0 ? { signal: options.signal } : {},
|
|
3012
|
+
metadata: {
|
|
3013
|
+
runId,
|
|
3014
|
+
protocol: "sequential",
|
|
3015
|
+
agentId: agent.id,
|
|
3016
|
+
role: agent.role,
|
|
3017
|
+
tier: options.tier,
|
|
3018
|
+
turn,
|
|
3019
|
+
...toolAvailability
|
|
3020
|
+
},
|
|
3021
|
+
messages: [
|
|
3022
|
+
{
|
|
3023
|
+
role: "system",
|
|
3024
|
+
content: buildSystemPrompt$1(agent)
|
|
3025
|
+
},
|
|
3026
|
+
{
|
|
3027
|
+
role: "user",
|
|
3028
|
+
content: input
|
|
3029
|
+
}
|
|
3030
|
+
]
|
|
3031
|
+
};
|
|
3032
|
+
const response = await generateModelTurn({
|
|
3033
|
+
model: options.model,
|
|
3034
|
+
request,
|
|
3035
|
+
runId,
|
|
3036
|
+
agent,
|
|
3037
|
+
input,
|
|
3038
|
+
emit,
|
|
3039
|
+
callId: nextProviderCallId(runId, providerCalls),
|
|
3040
|
+
onProviderCall(call) {
|
|
3041
|
+
providerCalls.push(call);
|
|
3042
|
+
}
|
|
3043
|
+
});
|
|
3044
|
+
const turnCost = responseCost$1(response);
|
|
3045
|
+
totalCost = addCost(totalCost, turnCost);
|
|
3046
|
+
const decision = parseAgentDecision(response.text);
|
|
3047
|
+
const toolCalls = await executeModelResponseToolRequests({
|
|
3048
|
+
response,
|
|
3049
|
+
executor: toolExecutor,
|
|
3050
|
+
agentId: agent.id,
|
|
3051
|
+
role: agent.role,
|
|
3052
|
+
turn
|
|
3053
|
+
});
|
|
3054
|
+
throwIfAborted(options.signal, options.model.id);
|
|
3055
|
+
transcript.push({
|
|
3056
|
+
agentId: agent.id,
|
|
3057
|
+
role: agent.role,
|
|
3058
|
+
input,
|
|
3059
|
+
output: response.text,
|
|
3060
|
+
...decision !== void 0 ? { decision } : {},
|
|
3061
|
+
...toolCalls.length > 0 ? { toolCalls } : {}
|
|
3062
|
+
});
|
|
3063
|
+
const event = {
|
|
3064
|
+
type: "agent-turn",
|
|
3065
|
+
runId,
|
|
3066
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3067
|
+
agentId: agent.id,
|
|
3068
|
+
role: agent.role,
|
|
3069
|
+
input,
|
|
3070
|
+
output: response.text,
|
|
3071
|
+
...decision !== void 0 ? { decision } : {},
|
|
3072
|
+
cost: totalCost
|
|
3073
|
+
};
|
|
3074
|
+
emit(event);
|
|
3075
|
+
recordProtocolDecision(event, {
|
|
3076
|
+
turn,
|
|
3077
|
+
transcriptEntryCount: transcript.length
|
|
3078
|
+
});
|
|
3079
|
+
if (stopIfNeeded()) {
|
|
3080
|
+
break;
|
|
3081
|
+
}
|
|
3082
|
+
}
|
|
3083
|
+
const output = [...transcript].reverse().find((entry) => isParticipatingDecision(entry.decision))?.output ?? "";
|
|
3084
|
+
throwIfAborted(options.signal, options.model.id);
|
|
3085
|
+
const final = {
|
|
3086
|
+
type: "final",
|
|
3087
|
+
runId,
|
|
3088
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3089
|
+
output,
|
|
3090
|
+
cost: totalCost,
|
|
3091
|
+
transcript: createTranscriptLink(transcript),
|
|
3092
|
+
...termination !== void 0 ? { termination } : {}
|
|
3093
|
+
};
|
|
3094
|
+
emit(final);
|
|
3095
|
+
recordProtocolDecision(final, {
|
|
3096
|
+
transcriptEntryCount: transcript.length
|
|
3097
|
+
});
|
|
3098
|
+
const finalEvent = events.at(-1);
|
|
3099
|
+
return {
|
|
3100
|
+
output,
|
|
3101
|
+
eventLog: createRunEventLog(runId, "sequential", events),
|
|
3102
|
+
trace: {
|
|
3103
|
+
schemaVersion: "1.0",
|
|
3104
|
+
runId,
|
|
3105
|
+
protocol: "sequential",
|
|
3106
|
+
tier: options.tier,
|
|
3107
|
+
modelProviderId: options.model.id,
|
|
3108
|
+
agentsUsed: activeAgents,
|
|
3109
|
+
inputs: createReplayTraceRunInputs({
|
|
3110
|
+
intent: options.intent,
|
|
3111
|
+
protocol: options.protocol,
|
|
3112
|
+
tier: options.tier,
|
|
3113
|
+
modelProviderId: options.model.id,
|
|
3114
|
+
agents: activeAgents,
|
|
3115
|
+
temperature: options.temperature
|
|
3116
|
+
}),
|
|
3117
|
+
budget: createReplayTraceBudget({
|
|
3118
|
+
tier: options.tier,
|
|
3119
|
+
...options.budget ? { caps: options.budget } : {},
|
|
3120
|
+
...options.terminate ? { termination: options.terminate } : {}
|
|
3121
|
+
}),
|
|
3122
|
+
budgetStateChanges: createReplayTraceBudgetStateChanges(events),
|
|
3123
|
+
seed: createReplayTraceSeed(options.seed),
|
|
3124
|
+
protocolDecisions,
|
|
3125
|
+
providerCalls,
|
|
3126
|
+
finalOutput: createReplayTraceFinalOutput(output, finalEvent ?? events[0] ?? {
|
|
3127
|
+
type: "final",
|
|
3128
|
+
at: "",
|
|
3129
|
+
cost: totalCost,
|
|
3130
|
+
transcript: createTranscriptLink(transcript)
|
|
3131
|
+
}),
|
|
3132
|
+
events,
|
|
3133
|
+
transcript
|
|
3134
|
+
},
|
|
3135
|
+
transcript,
|
|
3136
|
+
usage: createRunUsage(totalCost),
|
|
3137
|
+
metadata: createRunMetadata({
|
|
3138
|
+
runId,
|
|
3139
|
+
protocol: "sequential",
|
|
3140
|
+
tier: options.tier,
|
|
3141
|
+
modelProviderId: options.model.id,
|
|
3142
|
+
agentsUsed: activeAgents,
|
|
3143
|
+
events
|
|
3144
|
+
}),
|
|
3145
|
+
accounting: createRunAccounting({
|
|
3146
|
+
tier: options.tier,
|
|
3147
|
+
...options.budget ? { budget: options.budget } : {},
|
|
3148
|
+
...options.terminate ? { termination: options.terminate } : {},
|
|
3149
|
+
cost: totalCost,
|
|
3150
|
+
events
|
|
3151
|
+
}),
|
|
3152
|
+
cost: totalCost
|
|
3153
|
+
};
|
|
3154
|
+
function stopIfNeeded() {
|
|
3155
|
+
throwIfAborted(options.signal, options.model.id);
|
|
3156
|
+
if (stopped || !options.terminate) {
|
|
3157
|
+
return stopped;
|
|
3158
|
+
}
|
|
3159
|
+
const stopRecord2 = evaluateTerminationStop(options.terminate, {
|
|
3160
|
+
protocol: "sequential",
|
|
3161
|
+
tier: options.tier,
|
|
3162
|
+
cost: totalCost,
|
|
3163
|
+
transcript,
|
|
3164
|
+
iteration: transcript.length,
|
|
3165
|
+
elapsedMs: elapsedMs$1(startedAtMs)
|
|
3166
|
+
});
|
|
3167
|
+
if (!stopRecord2) {
|
|
3168
|
+
return false;
|
|
3169
|
+
}
|
|
3170
|
+
stopped = true;
|
|
3171
|
+
termination = stopRecord2;
|
|
3172
|
+
if (stopRecord2.reason === "budget") {
|
|
3173
|
+
emitBudgetStop(stopRecord2);
|
|
3174
|
+
}
|
|
3175
|
+
return true;
|
|
3176
|
+
}
|
|
3177
|
+
function emitBudgetStop(record) {
|
|
3178
|
+
const event = {
|
|
3179
|
+
type: "budget-stop",
|
|
3180
|
+
runId,
|
|
3181
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3182
|
+
reason: record.budgetReason ?? "cost",
|
|
3183
|
+
cost: totalCost,
|
|
3184
|
+
iteration: transcript.length,
|
|
3185
|
+
elapsedMs: elapsedMs$1(startedAtMs),
|
|
3186
|
+
detail: record.detail ?? {}
|
|
3187
|
+
};
|
|
3188
|
+
emit(event);
|
|
3189
|
+
recordProtocolDecision(event, {
|
|
3190
|
+
transcriptEntryCount: transcript.length
|
|
3191
|
+
});
|
|
3192
|
+
}
|
|
3193
|
+
}
|
|
3194
|
+
function buildSystemPrompt$1(agent) {
|
|
3195
|
+
const instruction = agent.instructions ? `
|
|
3196
|
+
Instructions: ${agent.instructions}` : "";
|
|
3197
|
+
return `You are ${agent.id}, acting as ${agent.role} in a Sequential multi-agent protocol.${instruction}`;
|
|
3198
|
+
}
|
|
3199
|
+
function buildSequentialInput(intent, transcript) {
|
|
3200
|
+
if (transcript.length === 0) {
|
|
3201
|
+
return `Mission: ${intent}
|
|
3202
|
+
Provide your contribution.`;
|
|
3203
|
+
}
|
|
3204
|
+
const prior = transcript.map((entry) => `${entry.role} (${entry.agentId}): ${entry.output}`).join("\n\n");
|
|
3205
|
+
return `Mission: ${intent}
|
|
3206
|
+
|
|
3207
|
+
Prior contributions:
|
|
3208
|
+
${prior}
|
|
3209
|
+
|
|
3210
|
+
Continue the sequence with a useful next contribution.`;
|
|
3211
|
+
}
|
|
3212
|
+
function responseCost$1(response) {
|
|
3213
|
+
return {
|
|
3214
|
+
usd: response.costUsd ?? 0,
|
|
3215
|
+
inputTokens: response.usage?.inputTokens ?? 0,
|
|
3216
|
+
outputTokens: response.usage?.outputTokens ?? 0,
|
|
3217
|
+
totalTokens: response.usage?.totalTokens ?? 0
|
|
3218
|
+
};
|
|
3219
|
+
}
|
|
3220
|
+
function createRunId$1() {
|
|
3221
|
+
const random = globalThis.crypto?.randomUUID?.();
|
|
3222
|
+
return random ?? `run-${Date.now().toString(36)}`;
|
|
3223
|
+
}
|
|
3224
|
+
function nowMs$1() {
|
|
3225
|
+
return globalThis.performance?.now() ?? Date.now();
|
|
3226
|
+
}
|
|
3227
|
+
function elapsedMs$1(startedAtMs) {
|
|
3228
|
+
return Math.max(0, nowMs$1() - startedAtMs);
|
|
3229
|
+
}
|
|
3230
|
+
async function runShared(options) {
|
|
3231
|
+
const runId = createRunId();
|
|
3232
|
+
const events = [];
|
|
3233
|
+
const transcript = [];
|
|
3234
|
+
const protocolDecisions = [];
|
|
3235
|
+
const providerCalls = [];
|
|
3236
|
+
let totalCost = emptyCost();
|
|
3237
|
+
const sharedState = options.protocol.organizationalMemory ?? "";
|
|
3238
|
+
const maxTurns = options.protocol.maxTurns ?? options.agents.length;
|
|
3239
|
+
const activeAgents = options.agents.slice(0, maxTurns);
|
|
3240
|
+
const startedAtMs = nowMs();
|
|
3241
|
+
let stopped = false;
|
|
3242
|
+
let termination;
|
|
3243
|
+
const emit = (event) => {
|
|
3244
|
+
events.push(event);
|
|
3245
|
+
options.emit?.(event);
|
|
3246
|
+
};
|
|
3247
|
+
const recordProtocolDecision = (event, decisionOptions) => {
|
|
3248
|
+
protocolDecisions.push(createReplayTraceProtocolDecision("shared", event, events.length - 1, decisionOptions));
|
|
3249
|
+
};
|
|
3250
|
+
const toolExecutor = createRuntimeToolExecutor({
|
|
3251
|
+
runId,
|
|
3252
|
+
protocol: "shared",
|
|
3253
|
+
tier: options.tier,
|
|
3254
|
+
tools: options.tools,
|
|
3255
|
+
emit(event) {
|
|
3256
|
+
emit(event);
|
|
3257
|
+
recordProtocolDecision(event);
|
|
3258
|
+
},
|
|
3259
|
+
getTrace: () => ({ events, transcript }),
|
|
3260
|
+
...options.signal !== void 0 ? { abortSignal: options.signal } : {}
|
|
3261
|
+
});
|
|
3262
|
+
const toolAvailability = runtimeToolAvailability(toolExecutor.tools);
|
|
3263
|
+
throwIfAborted(options.signal, options.model.id);
|
|
3264
|
+
for (const agent of activeAgents) {
|
|
3265
|
+
const event = {
|
|
3266
|
+
type: "role-assignment",
|
|
3267
|
+
runId,
|
|
3268
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3269
|
+
agentId: agent.id,
|
|
3270
|
+
role: agent.role
|
|
3271
|
+
};
|
|
3272
|
+
emit(event);
|
|
3273
|
+
recordProtocolDecision(event);
|
|
3274
|
+
}
|
|
3275
|
+
if (!stopIfNeeded()) {
|
|
3276
|
+
const providerCallSlots = [];
|
|
3277
|
+
const turnResults = await Promise.all(
|
|
3278
|
+
activeAgents.map(async (agent, index) => {
|
|
3279
|
+
const turn = index + 1;
|
|
3280
|
+
const input = buildSharedInput(options.intent, sharedState, turn);
|
|
3281
|
+
const request = {
|
|
3282
|
+
temperature: options.temperature,
|
|
3283
|
+
...options.signal !== void 0 ? { signal: options.signal } : {},
|
|
3284
|
+
metadata: {
|
|
3285
|
+
runId,
|
|
3286
|
+
protocol: "shared",
|
|
3287
|
+
agentId: agent.id,
|
|
3288
|
+
role: agent.role,
|
|
3289
|
+
tier: options.tier,
|
|
3290
|
+
turn,
|
|
3291
|
+
...toolAvailability
|
|
3292
|
+
},
|
|
3293
|
+
messages: [
|
|
3294
|
+
{
|
|
3295
|
+
role: "system",
|
|
3296
|
+
content: buildSystemPrompt(agent)
|
|
3297
|
+
},
|
|
3298
|
+
{
|
|
3299
|
+
role: "user",
|
|
3300
|
+
content: input
|
|
3301
|
+
}
|
|
3302
|
+
]
|
|
3303
|
+
};
|
|
3304
|
+
const response = await generateModelTurn({
|
|
3305
|
+
model: options.model,
|
|
3306
|
+
request,
|
|
3307
|
+
runId,
|
|
3308
|
+
agent,
|
|
3309
|
+
input,
|
|
3310
|
+
emit,
|
|
3311
|
+
callId: providerCallIdFor(runId, providerCalls.length + index + 1),
|
|
3312
|
+
onProviderCall(call) {
|
|
3313
|
+
providerCallSlots[index] = call;
|
|
3314
|
+
}
|
|
3315
|
+
});
|
|
3316
|
+
const decision = parseAgentDecision(response.text);
|
|
3317
|
+
const toolCalls = await executeModelResponseToolRequests({
|
|
3318
|
+
response,
|
|
3319
|
+
executor: toolExecutor,
|
|
3320
|
+
agentId: agent.id,
|
|
3321
|
+
role: agent.role,
|
|
3322
|
+
turn
|
|
3323
|
+
});
|
|
3324
|
+
throwIfAborted(options.signal, options.model.id);
|
|
3325
|
+
return {
|
|
3326
|
+
agent,
|
|
3327
|
+
turn,
|
|
3328
|
+
input,
|
|
3329
|
+
response,
|
|
3330
|
+
decision,
|
|
3331
|
+
toolCalls,
|
|
3332
|
+
turnCost: responseCost(response)
|
|
3333
|
+
};
|
|
3334
|
+
})
|
|
3335
|
+
);
|
|
3336
|
+
providerCalls.push(...providerCallSlots.filter((call) => call !== void 0));
|
|
3337
|
+
for (const result of turnResults) {
|
|
3338
|
+
totalCost = addCost(totalCost, result.turnCost);
|
|
3339
|
+
transcript.push({
|
|
3340
|
+
agentId: result.agent.id,
|
|
3341
|
+
role: result.agent.role,
|
|
3342
|
+
input: result.input,
|
|
3343
|
+
output: result.response.text,
|
|
3344
|
+
...result.decision !== void 0 ? { decision: result.decision } : {},
|
|
3345
|
+
...result.toolCalls.length > 0 ? { toolCalls: result.toolCalls } : {}
|
|
3346
|
+
});
|
|
3347
|
+
const event = {
|
|
3348
|
+
type: "agent-turn",
|
|
3349
|
+
runId,
|
|
3350
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3351
|
+
agentId: result.agent.id,
|
|
3352
|
+
role: result.agent.role,
|
|
3353
|
+
input: result.input,
|
|
3354
|
+
output: result.response.text,
|
|
3355
|
+
...result.decision !== void 0 ? { decision: result.decision } : {},
|
|
3356
|
+
cost: totalCost
|
|
3357
|
+
};
|
|
3358
|
+
emit(event);
|
|
3359
|
+
recordProtocolDecision(event, {
|
|
3360
|
+
turn: result.turn,
|
|
3361
|
+
transcriptEntryCount: transcript.length
|
|
3362
|
+
});
|
|
3363
|
+
}
|
|
3364
|
+
stopIfNeeded();
|
|
3365
|
+
}
|
|
3366
|
+
const output = synthesizeSharedOutput(transcript);
|
|
3367
|
+
throwIfAborted(options.signal, options.model.id);
|
|
3368
|
+
const final = {
|
|
3369
|
+
type: "final",
|
|
3370
|
+
runId,
|
|
3371
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3372
|
+
output,
|
|
3373
|
+
cost: totalCost,
|
|
3374
|
+
transcript: createTranscriptLink(transcript),
|
|
3375
|
+
...termination !== void 0 ? { termination } : {}
|
|
3376
|
+
};
|
|
3377
|
+
emit(final);
|
|
3378
|
+
recordProtocolDecision(final, {
|
|
3379
|
+
transcriptEntryCount: transcript.length
|
|
3380
|
+
});
|
|
3381
|
+
const finalEvent = events.at(-1);
|
|
3382
|
+
return {
|
|
3383
|
+
output,
|
|
3384
|
+
eventLog: createRunEventLog(runId, "shared", events),
|
|
3385
|
+
trace: {
|
|
3386
|
+
schemaVersion: "1.0",
|
|
3387
|
+
runId,
|
|
3388
|
+
protocol: "shared",
|
|
3389
|
+
tier: options.tier,
|
|
3390
|
+
modelProviderId: options.model.id,
|
|
3391
|
+
agentsUsed: activeAgents,
|
|
3392
|
+
inputs: createReplayTraceRunInputs({
|
|
3393
|
+
intent: options.intent,
|
|
3394
|
+
protocol: options.protocol,
|
|
3395
|
+
tier: options.tier,
|
|
3396
|
+
modelProviderId: options.model.id,
|
|
3397
|
+
agents: activeAgents,
|
|
3398
|
+
temperature: options.temperature
|
|
3399
|
+
}),
|
|
3400
|
+
budget: createReplayTraceBudget({
|
|
3401
|
+
tier: options.tier,
|
|
3402
|
+
...options.budget ? { caps: options.budget } : {},
|
|
3403
|
+
...options.terminate ? { termination: options.terminate } : {}
|
|
3404
|
+
}),
|
|
3405
|
+
budgetStateChanges: createReplayTraceBudgetStateChanges(events),
|
|
3406
|
+
seed: createReplayTraceSeed(options.seed),
|
|
3407
|
+
protocolDecisions,
|
|
3408
|
+
providerCalls,
|
|
3409
|
+
finalOutput: createReplayTraceFinalOutput(output, finalEvent ?? {
|
|
3410
|
+
type: "final",
|
|
3411
|
+
at: "",
|
|
3412
|
+
cost: totalCost,
|
|
3413
|
+
transcript: createTranscriptLink(transcript)
|
|
3414
|
+
}),
|
|
3415
|
+
events,
|
|
3416
|
+
transcript
|
|
3417
|
+
},
|
|
3418
|
+
transcript,
|
|
3419
|
+
usage: createRunUsage(totalCost),
|
|
3420
|
+
metadata: createRunMetadata({
|
|
3421
|
+
runId,
|
|
3422
|
+
protocol: "shared",
|
|
3423
|
+
tier: options.tier,
|
|
3424
|
+
modelProviderId: options.model.id,
|
|
3425
|
+
agentsUsed: activeAgents,
|
|
3426
|
+
events
|
|
3427
|
+
}),
|
|
3428
|
+
accounting: createRunAccounting({
|
|
3429
|
+
tier: options.tier,
|
|
3430
|
+
...options.budget ? { budget: options.budget } : {},
|
|
3431
|
+
...options.terminate ? { termination: options.terminate } : {},
|
|
3432
|
+
cost: totalCost,
|
|
3433
|
+
events
|
|
3434
|
+
}),
|
|
3435
|
+
cost: totalCost
|
|
3436
|
+
};
|
|
3437
|
+
function stopIfNeeded() {
|
|
3438
|
+
throwIfAborted(options.signal, options.model.id);
|
|
3439
|
+
if (stopped || !options.terminate) {
|
|
3440
|
+
return stopped;
|
|
3441
|
+
}
|
|
3442
|
+
const stopRecord2 = evaluateTerminationStop(options.terminate, {
|
|
3443
|
+
protocol: "shared",
|
|
3444
|
+
tier: options.tier,
|
|
3445
|
+
cost: totalCost,
|
|
3446
|
+
transcript,
|
|
3447
|
+
iteration: transcript.length,
|
|
3448
|
+
elapsedMs: elapsedMs(startedAtMs)
|
|
3449
|
+
});
|
|
3450
|
+
if (!stopRecord2) {
|
|
3451
|
+
return false;
|
|
3452
|
+
}
|
|
3453
|
+
stopped = true;
|
|
3454
|
+
termination = stopRecord2;
|
|
3455
|
+
if (stopRecord2.reason === "budget") {
|
|
3456
|
+
emitBudgetStop(stopRecord2);
|
|
3457
|
+
}
|
|
3458
|
+
return true;
|
|
3459
|
+
}
|
|
3460
|
+
function emitBudgetStop(record) {
|
|
3461
|
+
const event = {
|
|
3462
|
+
type: "budget-stop",
|
|
3463
|
+
runId,
|
|
3464
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3465
|
+
reason: record.budgetReason ?? "cost",
|
|
3466
|
+
cost: totalCost,
|
|
3467
|
+
iteration: transcript.length,
|
|
3468
|
+
elapsedMs: elapsedMs(startedAtMs),
|
|
3469
|
+
detail: record.detail ?? {}
|
|
3470
|
+
};
|
|
3471
|
+
emit(event);
|
|
3472
|
+
recordProtocolDecision(event, {
|
|
3473
|
+
transcriptEntryCount: transcript.length
|
|
3474
|
+
});
|
|
3475
|
+
}
|
|
3476
|
+
}
|
|
3477
|
+
function buildSystemPrompt(agent) {
|
|
3478
|
+
const instruction = agent.instructions ? `
|
|
3479
|
+
Instructions: ${agent.instructions}` : "";
|
|
3480
|
+
return `You are ${agent.id}, acting as ${agent.role} in a Shared multi-agent protocol. Read the shared state, update it with your best contribution, and preserve useful prior work.${instruction}`;
|
|
3481
|
+
}
|
|
3482
|
+
function buildSharedInput(intent, sharedState, turn) {
|
|
3483
|
+
const state = sharedState ? sharedState : "(empty)";
|
|
3484
|
+
return `Mission: ${intent}
|
|
3485
|
+
Shared turn ${turn}: read the shared state and return an improved shared-state update.
|
|
3486
|
+
|
|
3487
|
+
Shared state:
|
|
3488
|
+
${state}`;
|
|
3489
|
+
}
|
|
3490
|
+
function synthesizeSharedOutput(transcript) {
|
|
3491
|
+
return transcript.map((entry) => `${entry.role}:${entry.agentId} => ${entry.output}`).join("\n");
|
|
3492
|
+
}
|
|
3493
|
+
function responseCost(response) {
|
|
3494
|
+
return {
|
|
3495
|
+
usd: response.costUsd ?? 0,
|
|
3496
|
+
inputTokens: response.usage?.inputTokens ?? 0,
|
|
3497
|
+
outputTokens: response.usage?.outputTokens ?? 0,
|
|
3498
|
+
totalTokens: response.usage?.totalTokens ?? 0
|
|
3499
|
+
};
|
|
3500
|
+
}
|
|
3501
|
+
function createRunId() {
|
|
3502
|
+
const random = globalThis.crypto?.randomUUID?.();
|
|
3503
|
+
return random ?? `run-${Date.now().toString(36)}`;
|
|
3504
|
+
}
|
|
3505
|
+
function nowMs() {
|
|
3506
|
+
return globalThis.performance?.now() ?? Date.now();
|
|
3507
|
+
}
|
|
3508
|
+
function elapsedMs(startedAtMs) {
|
|
3509
|
+
return Math.max(0, nowMs() - startedAtMs);
|
|
3510
|
+
}
|
|
3511
|
+
function providerCallIdFor(runId, oneBasedIndex) {
|
|
3512
|
+
return `${runId}:provider-call:${oneBasedIndex}`;
|
|
3513
|
+
}
|
|
3514
|
+
const defaultHighLevelProtocol = "sequential";
|
|
3515
|
+
const defaultHighLevelTier = "balanced";
|
|
3516
|
+
function createEngine(options) {
|
|
3517
|
+
validateEngineOptions(options);
|
|
3518
|
+
const protocol = normalizeProtocol(options.protocol);
|
|
3519
|
+
const tools = options.tools ?? [];
|
|
3520
|
+
const temperature = options.temperature ?? tierTemperature(options.tier);
|
|
3521
|
+
const agents = orderAgentsForTemperature(options.agents ?? defaultAgents(), temperature, options.seed);
|
|
3522
|
+
const terminate = options.terminate ?? (options.budget ? conditionFromBudget(options.budget) : void 0);
|
|
3523
|
+
return {
|
|
3524
|
+
run(intent) {
|
|
3525
|
+
validateMissionIntent(intent);
|
|
3526
|
+
return runNonStreamingProtocol({
|
|
3527
|
+
intent,
|
|
3528
|
+
protocol,
|
|
3529
|
+
tier: options.tier,
|
|
3530
|
+
model: options.model,
|
|
3531
|
+
agents,
|
|
3532
|
+
tools,
|
|
3533
|
+
temperature,
|
|
3534
|
+
...options.budget ? { budget: options.budget } : {},
|
|
3535
|
+
...options.seed !== void 0 ? { seed: options.seed } : {},
|
|
3536
|
+
...options.signal !== void 0 ? { signal: options.signal } : {},
|
|
3537
|
+
...terminate ? { terminate } : {},
|
|
3538
|
+
...options.evaluate ? { evaluate: options.evaluate } : {}
|
|
3539
|
+
});
|
|
3540
|
+
},
|
|
3541
|
+
stream(intent) {
|
|
3542
|
+
validateMissionIntent(intent);
|
|
3543
|
+
const pendingEvents = [];
|
|
3544
|
+
const pendingResolvers = [];
|
|
3545
|
+
const emittedEvents = [];
|
|
3546
|
+
const subscribers = /* @__PURE__ */ new Set();
|
|
3547
|
+
const abortController = new AbortController();
|
|
3548
|
+
const timeoutLifecycle = createTimeoutAbortLifecycle({
|
|
3549
|
+
abortController,
|
|
3550
|
+
timeoutMs: runtimeTimeoutMs({ budget: options.budget, terminate }),
|
|
3551
|
+
providerId: options.model.id
|
|
3552
|
+
});
|
|
3553
|
+
const abortRace = createAbortRace(abortController.signal, options.model.id);
|
|
3554
|
+
let complete = false;
|
|
3555
|
+
let lastRunId = "";
|
|
3556
|
+
let pendingFinalEvent;
|
|
3557
|
+
let status = "running";
|
|
3558
|
+
let resolveResult;
|
|
3559
|
+
let rejectResult;
|
|
3560
|
+
let removeCallerAbortListener = () => {
|
|
3561
|
+
};
|
|
3562
|
+
const result = new Promise((resolve, reject) => {
|
|
3563
|
+
resolveResult = resolve;
|
|
3564
|
+
rejectResult = reject;
|
|
3565
|
+
});
|
|
3566
|
+
removeCallerAbortListener = wireCallerAbortSignal(options.signal, abortController, cancelRun);
|
|
3567
|
+
void execute();
|
|
3568
|
+
return {
|
|
3569
|
+
get status() {
|
|
3570
|
+
return status;
|
|
3571
|
+
},
|
|
3572
|
+
result,
|
|
3573
|
+
cancel() {
|
|
3574
|
+
cancelRun();
|
|
3575
|
+
},
|
|
3576
|
+
subscribe(subscriber) {
|
|
3577
|
+
subscribers.add(subscriber);
|
|
3578
|
+
for (const event of emittedEvents) {
|
|
3579
|
+
subscriber(event);
|
|
3580
|
+
}
|
|
3581
|
+
return {
|
|
3582
|
+
unsubscribe() {
|
|
3583
|
+
subscribers.delete(subscriber);
|
|
3584
|
+
}
|
|
3585
|
+
};
|
|
3586
|
+
},
|
|
3587
|
+
[Symbol.asyncIterator]() {
|
|
3588
|
+
return {
|
|
3589
|
+
next() {
|
|
3590
|
+
const event = pendingEvents.shift();
|
|
3591
|
+
if (event) {
|
|
3592
|
+
return Promise.resolve({ done: false, value: event });
|
|
3593
|
+
}
|
|
3594
|
+
if (complete) {
|
|
3595
|
+
return Promise.resolve({ done: true, value: void 0 });
|
|
3596
|
+
}
|
|
3597
|
+
return new Promise((resolve) => {
|
|
3598
|
+
pendingResolvers.push(resolve);
|
|
3599
|
+
});
|
|
3600
|
+
}
|
|
3601
|
+
};
|
|
3602
|
+
}
|
|
3603
|
+
};
|
|
3604
|
+
async function execute() {
|
|
3605
|
+
if (status !== "running") {
|
|
3606
|
+
return;
|
|
3607
|
+
}
|
|
3608
|
+
try {
|
|
3609
|
+
const baseResult = await abortRace.run(runProtocol({
|
|
3610
|
+
intent,
|
|
3611
|
+
protocol,
|
|
3612
|
+
tier: options.tier,
|
|
3613
|
+
model: options.model,
|
|
3614
|
+
agents,
|
|
3615
|
+
tools,
|
|
3616
|
+
temperature,
|
|
3617
|
+
...options.budget ? { budget: options.budget } : {},
|
|
3618
|
+
...options.seed !== void 0 ? { seed: options.seed } : {},
|
|
3619
|
+
signal: abortController.signal,
|
|
3620
|
+
...terminate ? { terminate } : {},
|
|
3621
|
+
emit(event) {
|
|
3622
|
+
if (status !== "running") {
|
|
3623
|
+
return;
|
|
3624
|
+
}
|
|
3625
|
+
lastRunId = event.runId;
|
|
3626
|
+
if (event.type === "final") {
|
|
3627
|
+
pendingFinalEvent = event;
|
|
3628
|
+
return;
|
|
3629
|
+
}
|
|
3630
|
+
publish(event);
|
|
3631
|
+
}
|
|
3632
|
+
}));
|
|
3633
|
+
if (status !== "running") {
|
|
3634
|
+
return;
|
|
3635
|
+
}
|
|
3636
|
+
const finalizedResult = await abortRace.run(applyRunEvaluation(baseResult, options.evaluate));
|
|
3637
|
+
if (status !== "running") {
|
|
3638
|
+
return;
|
|
3639
|
+
}
|
|
3640
|
+
const finalEvent = finalizedResult.trace.events.at(-1);
|
|
3641
|
+
if (finalEvent?.type === "final") {
|
|
3642
|
+
publish(finalEvent);
|
|
3643
|
+
} else if (pendingFinalEvent) {
|
|
3644
|
+
publish(pendingFinalEvent);
|
|
3645
|
+
}
|
|
3646
|
+
status = "completed";
|
|
3647
|
+
closeStream();
|
|
3648
|
+
resolveResult(finalizedResult);
|
|
3649
|
+
} catch (error) {
|
|
3650
|
+
if (isStreamHandleStatus(status, "cancelled")) {
|
|
3651
|
+
return;
|
|
3652
|
+
}
|
|
3653
|
+
const runtimeError = timeoutLifecycle.translateError(error);
|
|
3654
|
+
status = isCancellationError(runtimeError) ? "cancelled" : "failed";
|
|
3655
|
+
publish(createStreamErrorEvent(runtimeError, lastRunId));
|
|
3656
|
+
closeStream();
|
|
3657
|
+
rejectResult(runtimeError);
|
|
3658
|
+
}
|
|
3659
|
+
}
|
|
3660
|
+
function cancelRun(cause) {
|
|
3661
|
+
if (status !== "running") {
|
|
3662
|
+
return;
|
|
3663
|
+
}
|
|
3664
|
+
const error = createStreamCancellationError(options.model.id, cause);
|
|
3665
|
+
status = "cancelled";
|
|
3666
|
+
abortController.abort(error);
|
|
3667
|
+
publish(createStreamErrorEvent(error, lastRunId));
|
|
3668
|
+
closeStream();
|
|
3669
|
+
rejectResult(error);
|
|
3670
|
+
}
|
|
3671
|
+
function closeStream() {
|
|
3672
|
+
if (complete) {
|
|
3673
|
+
return;
|
|
3674
|
+
}
|
|
3675
|
+
complete = true;
|
|
3676
|
+
removeCallerAbortListener();
|
|
3677
|
+
timeoutLifecycle.cleanup();
|
|
3678
|
+
abortRace.cleanup();
|
|
3679
|
+
subscribers.clear();
|
|
3680
|
+
for (const resolver of pendingResolvers.splice(0)) {
|
|
3681
|
+
resolver({ done: true, value: void 0 });
|
|
3682
|
+
}
|
|
3683
|
+
}
|
|
3684
|
+
function publish(event) {
|
|
3685
|
+
if (complete) {
|
|
3686
|
+
return;
|
|
3687
|
+
}
|
|
3688
|
+
const canonicalEvent = canonicalizeSerializable(event);
|
|
3689
|
+
emittedEvents.push(canonicalEvent);
|
|
3690
|
+
for (const subscriber of subscribers) {
|
|
3691
|
+
try {
|
|
3692
|
+
subscriber(canonicalEvent);
|
|
3693
|
+
} catch {
|
|
3694
|
+
}
|
|
3695
|
+
}
|
|
3696
|
+
const resolver = pendingResolvers.shift();
|
|
3697
|
+
if (resolver) {
|
|
3698
|
+
resolver({ done: false, value: canonicalEvent });
|
|
3699
|
+
return;
|
|
3700
|
+
}
|
|
3701
|
+
pendingEvents.push(canonicalEvent);
|
|
3702
|
+
}
|
|
3703
|
+
}
|
|
3704
|
+
};
|
|
3705
|
+
}
|
|
3706
|
+
function isStreamHandleStatus(status, expected) {
|
|
3707
|
+
return status === expected;
|
|
3708
|
+
}
|
|
3709
|
+
function conditionFromBudget(budget$1) {
|
|
3710
|
+
return budget({
|
|
3711
|
+
...budget$1.maxUsd !== void 0 ? { maxUsd: budget$1.maxUsd } : {},
|
|
3712
|
+
...budget$1.maxTokens !== void 0 ? { maxTokens: budget$1.maxTokens } : {},
|
|
3713
|
+
...budget$1.maxIterations !== void 0 ? { maxIterations: budget$1.maxIterations } : {},
|
|
3714
|
+
...budget$1.timeoutMs !== void 0 ? { timeoutMs: budget$1.timeoutMs } : {}
|
|
3715
|
+
});
|
|
3716
|
+
}
|
|
3717
|
+
function createNonStreamingAbortLifecycle(options) {
|
|
3718
|
+
if (options.timeoutMs === void 0) {
|
|
3719
|
+
return {
|
|
3720
|
+
signal: options.callerSignal,
|
|
3721
|
+
async run(operation) {
|
|
3722
|
+
return await operation;
|
|
3723
|
+
},
|
|
3724
|
+
translateError(error) {
|
|
3725
|
+
return error;
|
|
3726
|
+
},
|
|
3727
|
+
cleanup() {
|
|
3728
|
+
}
|
|
3729
|
+
};
|
|
3730
|
+
}
|
|
3731
|
+
const abortController = new AbortController();
|
|
3732
|
+
const timeoutLifecycle = createTimeoutAbortLifecycle({
|
|
3733
|
+
abortController,
|
|
3734
|
+
timeoutMs: options.timeoutMs,
|
|
3735
|
+
providerId: options.providerId
|
|
3736
|
+
});
|
|
3737
|
+
const abortRace = createAbortRace(abortController.signal, options.providerId);
|
|
3738
|
+
const removeCallerAbortListener = wireCallerAbortSignal(options.callerSignal, abortController, () => {
|
|
3739
|
+
abortController.abort(readAbortSignalReason(options.callerSignal));
|
|
3740
|
+
});
|
|
3741
|
+
return {
|
|
3742
|
+
signal: abortController.signal,
|
|
3743
|
+
async run(operation) {
|
|
3744
|
+
return await abortRace.run(operation);
|
|
3745
|
+
},
|
|
3746
|
+
translateError(error) {
|
|
3747
|
+
return timeoutLifecycle.translateError(error);
|
|
3748
|
+
},
|
|
3749
|
+
cleanup() {
|
|
3750
|
+
timeoutLifecycle.cleanup();
|
|
3751
|
+
abortRace.cleanup();
|
|
3752
|
+
removeCallerAbortListener();
|
|
3753
|
+
}
|
|
3754
|
+
};
|
|
3755
|
+
}
|
|
3756
|
+
function createTimeoutAbortLifecycle(options) {
|
|
3757
|
+
if (options.timeoutMs === void 0) {
|
|
3758
|
+
return {
|
|
3759
|
+
translateError(error) {
|
|
3760
|
+
return error;
|
|
3761
|
+
},
|
|
3762
|
+
cleanup() {
|
|
3763
|
+
}
|
|
3764
|
+
};
|
|
3765
|
+
}
|
|
3766
|
+
const timeoutError = createTimeoutError(options.providerId, options.timeoutMs);
|
|
3767
|
+
const timeoutId = setTimeout(() => {
|
|
3768
|
+
options.abortController.abort(timeoutError);
|
|
3769
|
+
}, options.timeoutMs);
|
|
3770
|
+
return {
|
|
3771
|
+
translateError(error) {
|
|
3772
|
+
return options.abortController.signal.reason === timeoutError ? timeoutError : error;
|
|
3773
|
+
},
|
|
3774
|
+
cleanup() {
|
|
3775
|
+
clearTimeout(timeoutId);
|
|
3776
|
+
}
|
|
3777
|
+
};
|
|
3778
|
+
}
|
|
3779
|
+
function createAbortRace(signal, providerId) {
|
|
3780
|
+
let cleanupAbortListener = () => {
|
|
3781
|
+
};
|
|
3782
|
+
return {
|
|
3783
|
+
signal,
|
|
3784
|
+
async run(operation) {
|
|
3785
|
+
if (signal.aborted) {
|
|
3786
|
+
throw createAbortErrorFromSignal(signal, providerId);
|
|
3787
|
+
}
|
|
3788
|
+
const abortPromise = new Promise((_, reject) => {
|
|
3789
|
+
const abortHandler = () => {
|
|
3790
|
+
cleanupAbortListener();
|
|
3791
|
+
reject(createAbortErrorFromSignal(signal, providerId));
|
|
3792
|
+
};
|
|
3793
|
+
cleanupAbortListener = () => {
|
|
3794
|
+
signal.removeEventListener("abort", abortHandler);
|
|
3795
|
+
};
|
|
3796
|
+
signal.addEventListener("abort", abortHandler, { once: true });
|
|
3797
|
+
});
|
|
3798
|
+
try {
|
|
3799
|
+
return await Promise.race([operation, abortPromise]);
|
|
3800
|
+
} finally {
|
|
3801
|
+
cleanupAbortListener();
|
|
3802
|
+
cleanupAbortListener = () => {
|
|
3803
|
+
};
|
|
3804
|
+
}
|
|
3805
|
+
},
|
|
3806
|
+
translateError(error) {
|
|
3807
|
+
return error;
|
|
3808
|
+
},
|
|
3809
|
+
cleanup() {
|
|
3810
|
+
cleanupAbortListener();
|
|
3811
|
+
cleanupAbortListener = () => {
|
|
3812
|
+
};
|
|
3813
|
+
}
|
|
3814
|
+
};
|
|
3815
|
+
}
|
|
3816
|
+
function runtimeTimeoutMs(options) {
|
|
3817
|
+
const budgetTimeoutMs = options.budget?.timeoutMs;
|
|
3818
|
+
const terminationTimeoutMs = timeoutMsFromTermination(options.terminate);
|
|
3819
|
+
if (budgetTimeoutMs === void 0) {
|
|
3820
|
+
return terminationTimeoutMs;
|
|
3821
|
+
}
|
|
3822
|
+
if (terminationTimeoutMs === void 0) {
|
|
3823
|
+
return budgetTimeoutMs;
|
|
3824
|
+
}
|
|
3825
|
+
return Math.min(budgetTimeoutMs, terminationTimeoutMs);
|
|
3826
|
+
}
|
|
3827
|
+
function timeoutMsFromTermination(condition) {
|
|
3828
|
+
if (!condition) {
|
|
3829
|
+
return void 0;
|
|
3830
|
+
}
|
|
3831
|
+
switch (condition.kind) {
|
|
3832
|
+
case "budget":
|
|
3833
|
+
return condition.timeoutMs;
|
|
3834
|
+
case "firstOf":
|
|
3835
|
+
return condition.conditions.reduce((current, child) => {
|
|
3836
|
+
const childTimeoutMs = timeoutMsFromTermination(child);
|
|
3837
|
+
if (childTimeoutMs === void 0) {
|
|
3838
|
+
return current;
|
|
3839
|
+
}
|
|
3840
|
+
return current === void 0 ? childTimeoutMs : Math.min(current, childTimeoutMs);
|
|
3841
|
+
}, void 0);
|
|
3842
|
+
case "convergence":
|
|
3843
|
+
case "judge":
|
|
3844
|
+
return void 0;
|
|
3845
|
+
}
|
|
3846
|
+
}
|
|
3847
|
+
function readAbortSignalReason(signal) {
|
|
3848
|
+
return signal?.aborted ? signal.reason : void 0;
|
|
3849
|
+
}
|
|
3850
|
+
function createStreamErrorEvent(error, runId) {
|
|
3851
|
+
if (DogpileError.isInstance(error)) {
|
|
3852
|
+
return {
|
|
3853
|
+
type: "error",
|
|
3854
|
+
runId,
|
|
3855
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3856
|
+
name: error.name,
|
|
3857
|
+
message: error.message,
|
|
3858
|
+
detail: dogpileErrorStreamDetail(error)
|
|
3859
|
+
};
|
|
3860
|
+
}
|
|
3861
|
+
if (error instanceof Error) {
|
|
3862
|
+
return {
|
|
3863
|
+
type: "error",
|
|
3864
|
+
runId,
|
|
3865
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3866
|
+
name: error.name,
|
|
3867
|
+
message: error.message
|
|
3868
|
+
};
|
|
3869
|
+
}
|
|
3870
|
+
return {
|
|
3871
|
+
type: "error",
|
|
3872
|
+
runId,
|
|
3873
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3874
|
+
name: "Error",
|
|
3875
|
+
message: String(error)
|
|
3876
|
+
};
|
|
3877
|
+
}
|
|
3878
|
+
function dogpileErrorStreamDetail(error) {
|
|
3879
|
+
const detail = {
|
|
3880
|
+
code: error.code
|
|
3881
|
+
};
|
|
3882
|
+
if (error.providerId !== void 0) {
|
|
3883
|
+
detail.providerId = error.providerId;
|
|
3884
|
+
}
|
|
3885
|
+
if (error.retryable !== void 0) {
|
|
3886
|
+
detail.retryable = error.retryable;
|
|
3887
|
+
}
|
|
3888
|
+
if (error.detail !== void 0) {
|
|
3889
|
+
for (const [key, value] of Object.entries(error.detail)) {
|
|
3890
|
+
detail[key] = value;
|
|
3891
|
+
}
|
|
3892
|
+
}
|
|
3893
|
+
return detail;
|
|
3894
|
+
}
|
|
3895
|
+
async function runNonStreamingProtocol(options) {
|
|
3896
|
+
const abortLifecycle = createNonStreamingAbortLifecycle({
|
|
3897
|
+
callerSignal: options.signal,
|
|
3898
|
+
timeoutMs: runtimeTimeoutMs(options),
|
|
3899
|
+
providerId: options.model.id
|
|
3900
|
+
});
|
|
3901
|
+
try {
|
|
3902
|
+
const emittedEvents = [];
|
|
3903
|
+
const result = await abortLifecycle.run(runProtocol({
|
|
3904
|
+
...options,
|
|
3905
|
+
...abortLifecycle.signal !== void 0 ? { signal: abortLifecycle.signal } : {},
|
|
3906
|
+
emit(event) {
|
|
3907
|
+
emittedEvents.push(event);
|
|
3908
|
+
}
|
|
3909
|
+
}));
|
|
3910
|
+
const events = emittedEvents.length > 0 ? emittedEvents : result.trace.events;
|
|
3911
|
+
const trace = {
|
|
3912
|
+
...result.trace,
|
|
3913
|
+
events,
|
|
3914
|
+
budgetStateChanges: createReplayTraceBudgetStateChanges(events),
|
|
3915
|
+
finalOutput: createReplayTraceFinalOutput(result.output, events.at(-1) ?? result.trace.events.at(-1))
|
|
3916
|
+
};
|
|
3917
|
+
const runResult = {
|
|
3918
|
+
...result,
|
|
3919
|
+
accounting: createRunAccounting({
|
|
3920
|
+
tier: trace.tier,
|
|
3921
|
+
...trace.budget.caps ? { budget: trace.budget.caps } : {},
|
|
3922
|
+
...trace.budget.termination ? { termination: trace.budget.termination } : {},
|
|
3923
|
+
cost: result.cost,
|
|
3924
|
+
events
|
|
3925
|
+
}),
|
|
3926
|
+
eventLog: createRunEventLog(trace.runId, trace.protocol, events),
|
|
3927
|
+
trace
|
|
3928
|
+
};
|
|
3929
|
+
return canonicalizeRunResult(await abortLifecycle.run(applyRunEvaluation(runResult, options.evaluate)));
|
|
3930
|
+
} catch (error) {
|
|
3931
|
+
throw abortLifecycle.translateError(error);
|
|
3932
|
+
} finally {
|
|
3933
|
+
abortLifecycle.cleanup();
|
|
3934
|
+
}
|
|
3935
|
+
}
|
|
3936
|
+
async function applyRunEvaluation(result, evaluate) {
|
|
3937
|
+
if (!evaluate) {
|
|
3938
|
+
return canonicalizeRunResult(result);
|
|
3939
|
+
}
|
|
3940
|
+
const evaluation = await evaluate(result);
|
|
3941
|
+
const events = result.trace.events.map((event, index) => {
|
|
3942
|
+
if (index !== result.trace.events.length - 1 || event.type !== "final") {
|
|
3943
|
+
return event;
|
|
3944
|
+
}
|
|
3945
|
+
return finalEventWithEvaluation(event, evaluation);
|
|
3946
|
+
});
|
|
3947
|
+
const trace = {
|
|
3948
|
+
...result.trace,
|
|
3949
|
+
events
|
|
3950
|
+
};
|
|
3951
|
+
return canonicalizeRunResult({
|
|
3952
|
+
...result,
|
|
3953
|
+
quality: evaluation.quality,
|
|
3954
|
+
evaluation,
|
|
3955
|
+
trace,
|
|
3956
|
+
eventLog: createRunEventLog(trace.runId, trace.protocol, events)
|
|
3957
|
+
});
|
|
3958
|
+
}
|
|
3959
|
+
function finalEventWithEvaluation(event, evaluation) {
|
|
3960
|
+
return {
|
|
3961
|
+
...event,
|
|
3962
|
+
quality: evaluation.quality,
|
|
3963
|
+
evaluation
|
|
3964
|
+
};
|
|
3965
|
+
}
|
|
3966
|
+
function runProtocol(options) {
|
|
3967
|
+
switch (options.protocol.kind) {
|
|
3968
|
+
case "sequential":
|
|
3969
|
+
return runSequential({
|
|
3970
|
+
intent: options.intent,
|
|
3971
|
+
protocol: options.protocol,
|
|
3972
|
+
tier: options.tier,
|
|
3973
|
+
model: options.model,
|
|
3974
|
+
agents: options.agents,
|
|
3975
|
+
tools: options.tools,
|
|
3976
|
+
temperature: options.temperature,
|
|
3977
|
+
...options.budget ? { budget: options.budget } : {},
|
|
3978
|
+
...options.seed !== void 0 ? { seed: options.seed } : {},
|
|
3979
|
+
...options.signal !== void 0 ? { signal: options.signal } : {},
|
|
3980
|
+
...options.terminate ? { terminate: options.terminate } : {},
|
|
3981
|
+
...options.emit ? { emit: options.emit } : {}
|
|
3982
|
+
});
|
|
3983
|
+
case "broadcast":
|
|
3984
|
+
return runBroadcast({
|
|
3985
|
+
intent: options.intent,
|
|
3986
|
+
protocol: options.protocol,
|
|
3987
|
+
tier: options.tier,
|
|
3988
|
+
model: options.model,
|
|
3989
|
+
agents: options.agents,
|
|
3990
|
+
tools: options.tools,
|
|
3991
|
+
temperature: options.temperature,
|
|
3992
|
+
...options.budget ? { budget: options.budget } : {},
|
|
3993
|
+
...options.seed !== void 0 ? { seed: options.seed } : {},
|
|
3994
|
+
...options.signal !== void 0 ? { signal: options.signal } : {},
|
|
3995
|
+
...options.terminate ? { terminate: options.terminate } : {},
|
|
3996
|
+
...options.emit ? { emit: options.emit } : {}
|
|
3997
|
+
});
|
|
3998
|
+
case "coordinator":
|
|
3999
|
+
return runCoordinator({
|
|
4000
|
+
intent: options.intent,
|
|
4001
|
+
protocol: options.protocol,
|
|
4002
|
+
tier: options.tier,
|
|
4003
|
+
model: options.model,
|
|
4004
|
+
agents: options.agents,
|
|
4005
|
+
tools: options.tools,
|
|
4006
|
+
temperature: options.temperature,
|
|
4007
|
+
...options.budget ? { budget: options.budget } : {},
|
|
4008
|
+
...options.seed !== void 0 ? { seed: options.seed } : {},
|
|
4009
|
+
...options.signal !== void 0 ? { signal: options.signal } : {},
|
|
4010
|
+
...options.terminate ? { terminate: options.terminate } : {},
|
|
4011
|
+
...options.emit ? { emit: options.emit } : {}
|
|
4012
|
+
});
|
|
4013
|
+
case "shared":
|
|
4014
|
+
return runShared({
|
|
4015
|
+
intent: options.intent,
|
|
4016
|
+
protocol: options.protocol,
|
|
4017
|
+
tier: options.tier,
|
|
4018
|
+
model: options.model,
|
|
4019
|
+
agents: options.agents,
|
|
4020
|
+
tools: options.tools,
|
|
4021
|
+
temperature: options.temperature,
|
|
4022
|
+
...options.budget ? { budget: options.budget } : {},
|
|
4023
|
+
...options.seed !== void 0 ? { seed: options.seed } : {},
|
|
4024
|
+
...options.signal !== void 0 ? { signal: options.signal } : {},
|
|
4025
|
+
...options.terminate ? { terminate: options.terminate } : {},
|
|
4026
|
+
...options.emit ? { emit: options.emit } : {}
|
|
4027
|
+
});
|
|
4028
|
+
}
|
|
4029
|
+
}
|
|
4030
|
+
function run(options) {
|
|
4031
|
+
validateDogpileOptions(options);
|
|
4032
|
+
const { intent, ...engineOptions } = withHighLevelDefaults(options);
|
|
4033
|
+
return createEngine(engineOptions).run(intent);
|
|
4034
|
+
}
|
|
4035
|
+
function stream(options) {
|
|
4036
|
+
validateDogpileOptions(options);
|
|
4037
|
+
const { intent, ...engineOptions } = withHighLevelDefaults(options);
|
|
4038
|
+
return createEngine(engineOptions).stream(intent);
|
|
4039
|
+
}
|
|
4040
|
+
function replay(trace) {
|
|
4041
|
+
const cost = trace.finalOutput.cost;
|
|
4042
|
+
const lastEvent = trace.events.at(-1);
|
|
4043
|
+
const baseResult = {
|
|
4044
|
+
output: trace.finalOutput.output,
|
|
4045
|
+
eventLog: createRunEventLog(trace.runId, trace.protocol, trace.events),
|
|
4046
|
+
trace,
|
|
4047
|
+
transcript: trace.transcript,
|
|
4048
|
+
usage: createRunUsage(cost),
|
|
4049
|
+
metadata: createRunMetadata({
|
|
4050
|
+
runId: trace.runId,
|
|
4051
|
+
protocol: trace.protocol,
|
|
4052
|
+
tier: trace.tier,
|
|
4053
|
+
modelProviderId: trace.modelProviderId,
|
|
4054
|
+
agentsUsed: trace.agentsUsed,
|
|
4055
|
+
events: trace.events
|
|
4056
|
+
}),
|
|
4057
|
+
accounting: createRunAccounting({
|
|
4058
|
+
tier: trace.tier,
|
|
4059
|
+
...trace.budget.caps ? { budget: trace.budget.caps } : {},
|
|
4060
|
+
...trace.budget.termination ? { termination: trace.budget.termination } : {},
|
|
4061
|
+
cost,
|
|
4062
|
+
events: trace.events
|
|
4063
|
+
}),
|
|
4064
|
+
cost
|
|
4065
|
+
};
|
|
4066
|
+
if (lastEvent?.type !== "final") {
|
|
4067
|
+
return baseResult;
|
|
4068
|
+
}
|
|
4069
|
+
return {
|
|
4070
|
+
...baseResult,
|
|
4071
|
+
...lastEvent.quality !== void 0 ? { quality: lastEvent.quality } : {},
|
|
4072
|
+
...lastEvent.evaluation !== void 0 ? { evaluation: lastEvent.evaluation } : {}
|
|
4073
|
+
};
|
|
4074
|
+
}
|
|
4075
|
+
function replayStream(trace) {
|
|
4076
|
+
const result = Promise.resolve(replay(trace));
|
|
4077
|
+
return {
|
|
4078
|
+
get status() {
|
|
4079
|
+
return "completed";
|
|
4080
|
+
},
|
|
4081
|
+
result,
|
|
4082
|
+
cancel() {
|
|
4083
|
+
},
|
|
4084
|
+
subscribe(subscriber) {
|
|
4085
|
+
for (const event of trace.events) {
|
|
4086
|
+
subscriber(event);
|
|
4087
|
+
}
|
|
4088
|
+
return {
|
|
4089
|
+
unsubscribe() {
|
|
4090
|
+
}
|
|
4091
|
+
};
|
|
4092
|
+
},
|
|
4093
|
+
[Symbol.asyncIterator]() {
|
|
4094
|
+
let index = 0;
|
|
4095
|
+
return {
|
|
4096
|
+
next() {
|
|
4097
|
+
const event = trace.events[index];
|
|
4098
|
+
if (event) {
|
|
4099
|
+
index += 1;
|
|
4100
|
+
return Promise.resolve({ done: false, value: event });
|
|
4101
|
+
}
|
|
4102
|
+
return Promise.resolve({ done: true, value: void 0 });
|
|
4103
|
+
}
|
|
4104
|
+
};
|
|
4105
|
+
}
|
|
4106
|
+
};
|
|
4107
|
+
}
|
|
4108
|
+
function wireCallerAbortSignal(callerSignal, abortController, cancelRun) {
|
|
4109
|
+
if (!callerSignal) {
|
|
4110
|
+
return () => {
|
|
4111
|
+
};
|
|
4112
|
+
}
|
|
4113
|
+
const cancelFromCaller = () => {
|
|
4114
|
+
cancelRun(readAbortSignalReason(callerSignal));
|
|
4115
|
+
};
|
|
4116
|
+
if (callerSignal.aborted) {
|
|
4117
|
+
cancelFromCaller();
|
|
4118
|
+
return () => {
|
|
4119
|
+
};
|
|
4120
|
+
}
|
|
4121
|
+
callerSignal.addEventListener("abort", cancelFromCaller, { once: true });
|
|
4122
|
+
const remove = () => {
|
|
4123
|
+
callerSignal.removeEventListener("abort", cancelFromCaller);
|
|
4124
|
+
};
|
|
4125
|
+
abortController.signal.addEventListener("abort", remove, { once: true });
|
|
4126
|
+
return remove;
|
|
4127
|
+
}
|
|
4128
|
+
function createStreamCancellationError(providerId, cause) {
|
|
4129
|
+
return new DogpileError({
|
|
4130
|
+
code: "aborted",
|
|
4131
|
+
message: "The operation was aborted.",
|
|
4132
|
+
retryable: false,
|
|
4133
|
+
providerId,
|
|
4134
|
+
...cause !== void 0 ? { cause } : {},
|
|
4135
|
+
detail: {
|
|
4136
|
+
status: "cancelled"
|
|
4137
|
+
}
|
|
4138
|
+
});
|
|
4139
|
+
}
|
|
4140
|
+
function isCancellationError(error) {
|
|
4141
|
+
if (DogpileError.isInstance(error)) {
|
|
4142
|
+
return error.code === "aborted";
|
|
4143
|
+
}
|
|
4144
|
+
return error instanceof Error && error.name === "AbortError";
|
|
4145
|
+
}
|
|
4146
|
+
function withHighLevelDefaults(options) {
|
|
4147
|
+
return {
|
|
4148
|
+
...options,
|
|
4149
|
+
protocol: options.protocol ?? defaultHighLevelProtocol,
|
|
4150
|
+
tier: options.tier ?? defaultHighLevelTier
|
|
4151
|
+
};
|
|
4152
|
+
}
|
|
4153
|
+
function pile(options) {
|
|
4154
|
+
return run(options);
|
|
4155
|
+
}
|
|
4156
|
+
const Dogpile = {
|
|
4157
|
+
pile,
|
|
4158
|
+
replay,
|
|
4159
|
+
replayStream,
|
|
4160
|
+
stream,
|
|
4161
|
+
createEngine
|
|
4162
|
+
};
|
|
4163
|
+
const defaultBaseURL = "https://api.openai.com/v1";
|
|
4164
|
+
const defaultPath = "/chat/completions";
|
|
4165
|
+
function createOpenAICompatibleProvider(options) {
|
|
4166
|
+
validateOptions(options);
|
|
4167
|
+
const providerId = options.id ?? `openai-compatible:${options.model}`;
|
|
4168
|
+
const fetchImplementation = options.fetch ?? globalThis.fetch?.bind(globalThis);
|
|
4169
|
+
if (!fetchImplementation) {
|
|
4170
|
+
throw new DogpileError({
|
|
4171
|
+
code: "invalid-configuration",
|
|
4172
|
+
message: "createOpenAICompatibleProvider() requires a fetch implementation in this runtime.",
|
|
4173
|
+
retryable: false,
|
|
4174
|
+
providerId,
|
|
4175
|
+
detail: {
|
|
4176
|
+
kind: "configuration-validation",
|
|
4177
|
+
path: "fetch",
|
|
4178
|
+
expected: "a fetch-compatible function"
|
|
4179
|
+
}
|
|
4180
|
+
});
|
|
4181
|
+
}
|
|
4182
|
+
return {
|
|
4183
|
+
id: providerId,
|
|
4184
|
+
async generate(request) {
|
|
4185
|
+
const response = await fetchImplementation(createURL(options), {
|
|
4186
|
+
method: "POST",
|
|
4187
|
+
headers: createHeaders(options),
|
|
4188
|
+
body: JSON.stringify(createBody(options, request)),
|
|
4189
|
+
...request.signal !== void 0 ? { signal: request.signal } : {}
|
|
4190
|
+
});
|
|
4191
|
+
const payload = await readJson(response, providerId);
|
|
4192
|
+
if (!response.ok) {
|
|
4193
|
+
throw createProviderError(response, payload, providerId);
|
|
4194
|
+
}
|
|
4195
|
+
const completion = asChatCompletionResponse(payload, providerId);
|
|
4196
|
+
const text = readAssistantText(completion, providerId);
|
|
4197
|
+
const usage = normalizeUsage(completion.usage);
|
|
4198
|
+
const finishReason = normalizeFinishReason(completion.choices?.[0]?.finish_reason);
|
|
4199
|
+
const costUsd = options.costEstimator?.({
|
|
4200
|
+
providerId,
|
|
4201
|
+
request,
|
|
4202
|
+
response: completion,
|
|
4203
|
+
...usage ? { usage } : {}
|
|
4204
|
+
});
|
|
4205
|
+
return {
|
|
4206
|
+
text,
|
|
4207
|
+
...finishReason !== void 0 ? { finishReason } : {},
|
|
4208
|
+
...usage ? { usage } : {},
|
|
4209
|
+
...costUsd !== void 0 ? { costUsd } : {},
|
|
4210
|
+
metadata: {
|
|
4211
|
+
openAICompatible: responseMetadata(completion)
|
|
4212
|
+
}
|
|
4213
|
+
};
|
|
4214
|
+
}
|
|
4215
|
+
};
|
|
4216
|
+
}
|
|
4217
|
+
function validateOptions(options) {
|
|
4218
|
+
if (!isRecord(options)) {
|
|
4219
|
+
throwInvalid("options", "an options object");
|
|
4220
|
+
}
|
|
4221
|
+
if (!isNonEmptyString(options.model)) {
|
|
4222
|
+
throwInvalid("model", "a non-empty model id");
|
|
4223
|
+
}
|
|
4224
|
+
if (options.apiKey !== void 0 && !isNonEmptyString(options.apiKey)) {
|
|
4225
|
+
throwInvalid("apiKey", "a non-empty API key when provided");
|
|
4226
|
+
}
|
|
4227
|
+
if (options.id !== void 0 && !isNonEmptyString(options.id)) {
|
|
4228
|
+
throwInvalid("id", "a non-empty provider id when provided");
|
|
4229
|
+
}
|
|
4230
|
+
if (options.path !== void 0 && !isNonEmptyString(options.path)) {
|
|
4231
|
+
throwInvalid("path", "a non-empty request path when provided");
|
|
4232
|
+
}
|
|
4233
|
+
if (options.fetch !== void 0 && typeof options.fetch !== "function") {
|
|
4234
|
+
throwInvalid("fetch", "a fetch-compatible function when provided");
|
|
4235
|
+
}
|
|
4236
|
+
if (options.maxOutputTokens !== void 0 && (!Number.isInteger(options.maxOutputTokens) || options.maxOutputTokens <= 0)) {
|
|
4237
|
+
throwInvalid("maxOutputTokens", "a positive integer when provided");
|
|
4238
|
+
}
|
|
4239
|
+
if (options.costEstimator !== void 0 && typeof options.costEstimator !== "function") {
|
|
4240
|
+
throwInvalid("costEstimator", "a function when provided");
|
|
4241
|
+
}
|
|
4242
|
+
}
|
|
4243
|
+
function throwInvalid(path, expected) {
|
|
4244
|
+
throw new DogpileError({
|
|
4245
|
+
code: "invalid-configuration",
|
|
4246
|
+
message: `Invalid OpenAI-compatible provider option at ${path}.`,
|
|
4247
|
+
retryable: false,
|
|
4248
|
+
detail: {
|
|
4249
|
+
kind: "configuration-validation",
|
|
4250
|
+
path,
|
|
4251
|
+
expected
|
|
4252
|
+
}
|
|
4253
|
+
});
|
|
4254
|
+
}
|
|
4255
|
+
function createURL(options) {
|
|
4256
|
+
const baseURL = new URL(String(options.baseURL ?? defaultBaseURL));
|
|
4257
|
+
const path = options.path ?? defaultPath;
|
|
4258
|
+
return new URL(path.startsWith("/") ? path.slice(1) : path, ensureTrailingSlash(baseURL));
|
|
4259
|
+
}
|
|
4260
|
+
function ensureTrailingSlash(url) {
|
|
4261
|
+
const next = new URL(url);
|
|
4262
|
+
if (!next.pathname.endsWith("/")) {
|
|
4263
|
+
next.pathname = `${next.pathname}/`;
|
|
4264
|
+
}
|
|
4265
|
+
return next;
|
|
4266
|
+
}
|
|
4267
|
+
function createHeaders(options) {
|
|
4268
|
+
const headers = new Headers();
|
|
4269
|
+
for (const [key, value] of Object.entries(options.headers ?? {})) {
|
|
4270
|
+
if (value !== void 0) {
|
|
4271
|
+
headers.set(key, value);
|
|
4272
|
+
}
|
|
4273
|
+
}
|
|
4274
|
+
headers.set("content-type", "application/json");
|
|
4275
|
+
if (options.apiKey !== void 0 && !headers.has("authorization")) {
|
|
4276
|
+
headers.set("authorization", `Bearer ${options.apiKey}`);
|
|
4277
|
+
}
|
|
4278
|
+
return headers;
|
|
4279
|
+
}
|
|
4280
|
+
function createBody(options, request) {
|
|
4281
|
+
return {
|
|
4282
|
+
...options.extraBody ?? {},
|
|
4283
|
+
model: options.model,
|
|
4284
|
+
messages: request.messages.map(toChatMessage),
|
|
4285
|
+
temperature: request.temperature,
|
|
4286
|
+
...options.maxOutputTokens !== void 0 ? { max_tokens: options.maxOutputTokens } : {}
|
|
4287
|
+
};
|
|
4288
|
+
}
|
|
4289
|
+
function toChatMessage(message) {
|
|
4290
|
+
return {
|
|
4291
|
+
role: message.role,
|
|
4292
|
+
content: message.content
|
|
4293
|
+
};
|
|
4294
|
+
}
|
|
4295
|
+
async function readJson(response, providerId) {
|
|
4296
|
+
try {
|
|
4297
|
+
return await response.json();
|
|
4298
|
+
} catch (error) {
|
|
4299
|
+
throw new DogpileError({
|
|
4300
|
+
code: "provider-invalid-response",
|
|
4301
|
+
message: "OpenAI-compatible provider returned a non-JSON response.",
|
|
4302
|
+
cause: error,
|
|
4303
|
+
retryable: response.status >= 500,
|
|
4304
|
+
providerId,
|
|
4305
|
+
detail: {
|
|
4306
|
+
statusCode: response.status,
|
|
4307
|
+
statusText: response.statusText
|
|
4308
|
+
}
|
|
4309
|
+
});
|
|
4310
|
+
}
|
|
4311
|
+
}
|
|
4312
|
+
function asChatCompletionResponse(payload, providerId) {
|
|
4313
|
+
if (!isRecord(payload)) {
|
|
4314
|
+
throw new DogpileError({
|
|
4315
|
+
code: "provider-invalid-response",
|
|
4316
|
+
message: "OpenAI-compatible provider response must be a JSON object.",
|
|
4317
|
+
retryable: true,
|
|
4318
|
+
providerId
|
|
4319
|
+
});
|
|
4320
|
+
}
|
|
4321
|
+
return payload;
|
|
4322
|
+
}
|
|
4323
|
+
function readAssistantText(response, providerId) {
|
|
4324
|
+
const content = response.choices?.[0]?.message?.content;
|
|
4325
|
+
const text = normalizeContent(content);
|
|
4326
|
+
if (!text) {
|
|
4327
|
+
throw new DogpileError({
|
|
4328
|
+
code: "provider-invalid-response",
|
|
4329
|
+
message: "OpenAI-compatible provider response did not include assistant text.",
|
|
4330
|
+
retryable: true,
|
|
4331
|
+
providerId
|
|
4332
|
+
});
|
|
4333
|
+
}
|
|
4334
|
+
return text;
|
|
4335
|
+
}
|
|
4336
|
+
function normalizeContent(content) {
|
|
4337
|
+
if (typeof content === "string") {
|
|
4338
|
+
return content;
|
|
4339
|
+
}
|
|
4340
|
+
if (!Array.isArray(content)) {
|
|
4341
|
+
return "";
|
|
4342
|
+
}
|
|
4343
|
+
return content.map((part) => {
|
|
4344
|
+
if (!isRecord(part)) {
|
|
4345
|
+
return "";
|
|
4346
|
+
}
|
|
4347
|
+
const text = part.text;
|
|
4348
|
+
return typeof text === "string" ? text : "";
|
|
4349
|
+
}).filter(Boolean).join("");
|
|
4350
|
+
}
|
|
4351
|
+
function normalizeUsage(usage) {
|
|
4352
|
+
if (!usage) {
|
|
4353
|
+
return void 0;
|
|
4354
|
+
}
|
|
4355
|
+
const inputTokens = readTokenCount(usage.prompt_tokens ?? usage.input_tokens);
|
|
4356
|
+
const outputTokens = readTokenCount(usage.completion_tokens ?? usage.output_tokens);
|
|
4357
|
+
const totalTokens = readTokenCount(usage.total_tokens) ?? sumIfPresent(inputTokens, outputTokens);
|
|
4358
|
+
if (inputTokens === void 0 || outputTokens === void 0 || totalTokens === void 0) {
|
|
4359
|
+
return void 0;
|
|
4360
|
+
}
|
|
4361
|
+
return {
|
|
4362
|
+
inputTokens,
|
|
4363
|
+
outputTokens,
|
|
4364
|
+
totalTokens
|
|
4365
|
+
};
|
|
4366
|
+
}
|
|
4367
|
+
function readTokenCount(value) {
|
|
4368
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
4369
|
+
}
|
|
4370
|
+
function sumIfPresent(left, right) {
|
|
4371
|
+
return left === void 0 || right === void 0 ? void 0 : left + right;
|
|
4372
|
+
}
|
|
4373
|
+
function normalizeFinishReason(reason) {
|
|
4374
|
+
switch (reason) {
|
|
4375
|
+
case "stop":
|
|
4376
|
+
return "stop";
|
|
4377
|
+
case "length":
|
|
4378
|
+
return "length";
|
|
4379
|
+
case "content_filter":
|
|
4380
|
+
case "content-filter":
|
|
4381
|
+
return "content-filter";
|
|
4382
|
+
case "tool_calls":
|
|
4383
|
+
case "tool-calls":
|
|
4384
|
+
return "tool-calls";
|
|
4385
|
+
case void 0:
|
|
4386
|
+
case null:
|
|
4387
|
+
return void 0;
|
|
4388
|
+
default:
|
|
4389
|
+
return "other";
|
|
4390
|
+
}
|
|
4391
|
+
}
|
|
4392
|
+
function responseMetadata(response) {
|
|
4393
|
+
return removeUndefined({
|
|
4394
|
+
id: response.id,
|
|
4395
|
+
object: response.object,
|
|
4396
|
+
created: response.created,
|
|
4397
|
+
model: response.model,
|
|
4398
|
+
usage: isJsonValue(response.usage) ? response.usage : void 0
|
|
4399
|
+
});
|
|
4400
|
+
}
|
|
4401
|
+
function createProviderError(response, payload, providerId) {
|
|
4402
|
+
return new DogpileError({
|
|
4403
|
+
code: codeForStatus(response.status),
|
|
4404
|
+
message: errorMessage(response, payload),
|
|
4405
|
+
retryable: response.status === 408 || response.status === 429 || response.status >= 500,
|
|
4406
|
+
providerId,
|
|
4407
|
+
detail: removeUndefined({
|
|
4408
|
+
statusCode: response.status,
|
|
4409
|
+
statusText: response.statusText,
|
|
4410
|
+
response: isJsonValue(payload) ? payload : void 0
|
|
4411
|
+
})
|
|
4412
|
+
});
|
|
4413
|
+
}
|
|
4414
|
+
function codeForStatus(status) {
|
|
4415
|
+
if (status === 401 || status === 403) {
|
|
4416
|
+
return "provider-authentication";
|
|
4417
|
+
}
|
|
4418
|
+
if (status === 404) {
|
|
4419
|
+
return "provider-not-found";
|
|
4420
|
+
}
|
|
4421
|
+
if (status === 408 || status === 504) {
|
|
4422
|
+
return "provider-timeout";
|
|
4423
|
+
}
|
|
4424
|
+
if (status === 429) {
|
|
4425
|
+
return "provider-rate-limited";
|
|
4426
|
+
}
|
|
4427
|
+
if (status >= 500) {
|
|
4428
|
+
return "provider-unavailable";
|
|
4429
|
+
}
|
|
4430
|
+
if (status >= 400) {
|
|
4431
|
+
return "provider-invalid-request";
|
|
4432
|
+
}
|
|
4433
|
+
return "provider-error";
|
|
4434
|
+
}
|
|
4435
|
+
function errorMessage(response, payload) {
|
|
4436
|
+
const providerMessage = isRecord(payload) && isRecord(payload.error) && typeof payload.error.message === "string" ? payload.error.message : void 0;
|
|
4437
|
+
return providerMessage ?? `OpenAI-compatible provider request failed with HTTP ${response.status}.`;
|
|
4438
|
+
}
|
|
4439
|
+
function removeUndefined(values) {
|
|
4440
|
+
return Object.fromEntries(Object.entries(values).filter(([, value]) => value !== void 0));
|
|
4441
|
+
}
|
|
4442
|
+
function isRecord(value) {
|
|
4443
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4444
|
+
}
|
|
4445
|
+
function isNonEmptyString(value) {
|
|
4446
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
4447
|
+
}
|
|
4448
|
+
function isJsonValue(value) {
|
|
4449
|
+
if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
4450
|
+
return typeof value !== "number" || Number.isFinite(value);
|
|
4451
|
+
}
|
|
4452
|
+
if (Array.isArray(value)) {
|
|
4453
|
+
return value.every(isJsonValue);
|
|
4454
|
+
}
|
|
4455
|
+
if (isRecord(value)) {
|
|
4456
|
+
return Object.values(value).every(isJsonValue);
|
|
4457
|
+
}
|
|
4458
|
+
return false;
|
|
4459
|
+
}
|
|
4460
|
+
export {
|
|
4461
|
+
Dogpile,
|
|
4462
|
+
DogpileError,
|
|
4463
|
+
budget,
|
|
4464
|
+
builtInDogpileToolIdentity,
|
|
4465
|
+
builtInDogpileToolInputSchema,
|
|
4466
|
+
builtInDogpileToolPermissions,
|
|
4467
|
+
combineTerminationDecisions,
|
|
4468
|
+
convergence,
|
|
4469
|
+
createCodeExecToolAdapter,
|
|
4470
|
+
createEngine,
|
|
4471
|
+
createOpenAICompatibleProvider,
|
|
4472
|
+
createRuntimeToolExecutor,
|
|
4473
|
+
createWebSearchToolAdapter,
|
|
4474
|
+
evaluateBudget,
|
|
4475
|
+
evaluateConvergence,
|
|
4476
|
+
evaluateFirstOf,
|
|
4477
|
+
evaluateJudge,
|
|
4478
|
+
evaluateTermination,
|
|
4479
|
+
evaluateTerminationStop,
|
|
4480
|
+
firstOf,
|
|
4481
|
+
judge,
|
|
4482
|
+
normalizeBuiltInDogpileTool,
|
|
4483
|
+
normalizeBuiltInDogpileTools,
|
|
4484
|
+
normalizeRuntimeToolAdapterError,
|
|
4485
|
+
replay,
|
|
4486
|
+
replayStream,
|
|
4487
|
+
run,
|
|
4488
|
+
runtimeToolAvailability,
|
|
4489
|
+
runtimeToolManifest,
|
|
4490
|
+
stream,
|
|
4491
|
+
validateBuiltInDogpileToolInput
|
|
4492
|
+
};
|
|
4493
|
+
//# sourceMappingURL=index.js.map
|