@dogpile/sdk 0.1.1 → 0.2.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 +14 -0
- package/README.md +20 -8
- package/dist/browser/index.js +3773 -4061
- package/dist/browser/index.js.map +1 -1
- package/dist/providers/openai-compatible.d.ts.map +1 -1
- package/dist/providers/openai-compatible.js +51 -8
- package/dist/providers/openai-compatible.js.map +1 -1
- package/package.json +19 -7
- package/src/providers/openai-compatible.ts +62 -8
- package/src/runtime/broadcast.test.ts +0 -355
- package/src/runtime/coordinator.test.ts +0 -468
- package/src/runtime/sequential.test.ts +0 -262
- package/src/runtime/shared.test.ts +0 -265
|
@@ -1,468 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { createDeterministicCoordinatorTestMission } from "../internal.js";
|
|
3
|
-
import { Dogpile, run, runtimeToolManifest, stream } from "../index.js";
|
|
4
|
-
import type {
|
|
5
|
-
AgentSpec,
|
|
6
|
-
ConfiguredModelProvider,
|
|
7
|
-
JsonObject,
|
|
8
|
-
JsonValue,
|
|
9
|
-
ModelRequest,
|
|
10
|
-
ModelResponse,
|
|
11
|
-
RunEvent,
|
|
12
|
-
RuntimeTool
|
|
13
|
-
} from "../index.js";
|
|
14
|
-
|
|
15
|
-
describe("coordinator protocol", () => {
|
|
16
|
-
it("plans, dispatches workers, and synthesizes through the configured model provider", async () => {
|
|
17
|
-
const intent = "Decide whether the coordinator path is wired to the configured provider.";
|
|
18
|
-
const agents: readonly AgentSpec[] = [
|
|
19
|
-
{
|
|
20
|
-
id: "seat-coordinator",
|
|
21
|
-
role: "coordinator",
|
|
22
|
-
instructions: "Assign work and synthesize the final answer."
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
id: "seat-research",
|
|
26
|
-
role: "researcher",
|
|
27
|
-
instructions: "Inspect provider wiring evidence."
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
id: "seat-review",
|
|
31
|
-
role: "reviewer",
|
|
32
|
-
instructions: "Check the final path for hidden shortcuts."
|
|
33
|
-
}
|
|
34
|
-
];
|
|
35
|
-
const requests: ModelRequest[] = [];
|
|
36
|
-
const model: ConfiguredModelProvider = {
|
|
37
|
-
id: "capturing-coordinator-model",
|
|
38
|
-
async generate(request: ModelRequest): Promise<ModelResponse> {
|
|
39
|
-
requests.push(request);
|
|
40
|
-
const phase = String(request.metadata.phase);
|
|
41
|
-
const role = String(request.metadata.role);
|
|
42
|
-
const agentId = String(request.metadata.agentId);
|
|
43
|
-
|
|
44
|
-
return {
|
|
45
|
-
text: `${phase}:${role}:${agentId}`,
|
|
46
|
-
usage: {
|
|
47
|
-
inputTokens: 11,
|
|
48
|
-
outputTokens: 7,
|
|
49
|
-
totalTokens: 18
|
|
50
|
-
},
|
|
51
|
-
costUsd: 0.001
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
const result = await run({
|
|
57
|
-
intent,
|
|
58
|
-
protocol: { kind: "coordinator", maxTurns: 3 },
|
|
59
|
-
tier: "balanced",
|
|
60
|
-
model,
|
|
61
|
-
agents
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
expect(requests).toHaveLength(4);
|
|
65
|
-
expect(requests.map((request) => request.metadata)).toEqual([
|
|
66
|
-
expect.objectContaining({
|
|
67
|
-
protocol: "coordinator",
|
|
68
|
-
agentId: "seat-coordinator",
|
|
69
|
-
role: "coordinator",
|
|
70
|
-
coordinatorAgentId: "seat-coordinator",
|
|
71
|
-
tier: "balanced",
|
|
72
|
-
phase: "plan"
|
|
73
|
-
}),
|
|
74
|
-
expect.objectContaining({
|
|
75
|
-
protocol: "coordinator",
|
|
76
|
-
agentId: "seat-research",
|
|
77
|
-
role: "researcher",
|
|
78
|
-
coordinatorAgentId: "seat-coordinator",
|
|
79
|
-
tier: "balanced",
|
|
80
|
-
phase: "worker"
|
|
81
|
-
}),
|
|
82
|
-
expect.objectContaining({
|
|
83
|
-
protocol: "coordinator",
|
|
84
|
-
agentId: "seat-review",
|
|
85
|
-
role: "reviewer",
|
|
86
|
-
coordinatorAgentId: "seat-coordinator",
|
|
87
|
-
tier: "balanced",
|
|
88
|
-
phase: "worker"
|
|
89
|
-
}),
|
|
90
|
-
expect.objectContaining({
|
|
91
|
-
protocol: "coordinator",
|
|
92
|
-
agentId: "seat-coordinator",
|
|
93
|
-
role: "coordinator",
|
|
94
|
-
coordinatorAgentId: "seat-coordinator",
|
|
95
|
-
tier: "balanced",
|
|
96
|
-
phase: "final-synthesis"
|
|
97
|
-
})
|
|
98
|
-
]);
|
|
99
|
-
expect(requests[0]?.messages.find((message) => message.role === "user")?.content).toContain(intent);
|
|
100
|
-
expect(requests[1]?.messages.find((message) => message.role === "user")?.content).toContain(
|
|
101
|
-
"plan:coordinator:seat-coordinator"
|
|
102
|
-
);
|
|
103
|
-
expect(requests[3]?.messages.find((message) => message.role === "user")?.content).toContain(
|
|
104
|
-
"Synthesize the final answer as the coordinator."
|
|
105
|
-
);
|
|
106
|
-
expect(result.output).toBe("final-synthesis:coordinator:seat-coordinator");
|
|
107
|
-
expect(result.transcript).toHaveLength(4);
|
|
108
|
-
expect(result.trace.protocol).toBe("coordinator");
|
|
109
|
-
expect(result.trace.modelProviderId).toBe("capturing-coordinator-model");
|
|
110
|
-
expect(result.trace.events.map((event) => event.type)).toEqual([
|
|
111
|
-
"role-assignment",
|
|
112
|
-
"role-assignment",
|
|
113
|
-
"role-assignment",
|
|
114
|
-
"agent-turn",
|
|
115
|
-
"agent-turn",
|
|
116
|
-
"agent-turn",
|
|
117
|
-
"agent-turn",
|
|
118
|
-
"final"
|
|
119
|
-
]);
|
|
120
|
-
expect(JSON.parse(JSON.stringify(result.trace))).toEqual(result.trace);
|
|
121
|
-
expect(result.cost).toEqual({
|
|
122
|
-
usd: 0.004,
|
|
123
|
-
inputTokens: 44,
|
|
124
|
-
outputTokens: 28,
|
|
125
|
-
totalTokens: 72
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
it("starts coordinator workers in parallel from the same coordinator plan", async () => {
|
|
130
|
-
const requests: ModelRequest[] = [];
|
|
131
|
-
const workerRequests: ModelRequest[] = [];
|
|
132
|
-
let resolveWorkersStarted!: () => void;
|
|
133
|
-
let releaseWorkers!: () => void;
|
|
134
|
-
const workersStarted = new Promise<void>((resolve) => {
|
|
135
|
-
resolveWorkersStarted = resolve;
|
|
136
|
-
});
|
|
137
|
-
const workersReleased = new Promise<void>((resolve) => {
|
|
138
|
-
releaseWorkers = resolve;
|
|
139
|
-
});
|
|
140
|
-
const model: ConfiguredModelProvider = {
|
|
141
|
-
id: "parallel-coordinator-model",
|
|
142
|
-
async generate(request) {
|
|
143
|
-
requests.push(request);
|
|
144
|
-
const phase = String(request.metadata.phase);
|
|
145
|
-
const agentId = String(request.metadata.agentId);
|
|
146
|
-
if (phase === "plan") {
|
|
147
|
-
return { text: "coordinator plan output" };
|
|
148
|
-
}
|
|
149
|
-
if (phase === "worker") {
|
|
150
|
-
workerRequests.push(request);
|
|
151
|
-
if (workerRequests.length === 2) {
|
|
152
|
-
resolveWorkersStarted();
|
|
153
|
-
}
|
|
154
|
-
await workersReleased;
|
|
155
|
-
return { text: `worker output from ${agentId}` };
|
|
156
|
-
}
|
|
157
|
-
return { text: "final synthesis output" };
|
|
158
|
-
}
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
const resultPromise = run({
|
|
162
|
-
intent: "Verify coordinator worker parallelism.",
|
|
163
|
-
protocol: { kind: "coordinator", maxTurns: 3 },
|
|
164
|
-
tier: "fast",
|
|
165
|
-
model,
|
|
166
|
-
agents: [
|
|
167
|
-
{ id: "lead", role: "coordinator" },
|
|
168
|
-
{ id: "worker-a", role: "worker" },
|
|
169
|
-
{ id: "worker-b", role: "worker" }
|
|
170
|
-
]
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
await expect(Promise.race([workersStarted, rejectAfter(100, "workers did not start in parallel")])).resolves.toBeUndefined();
|
|
174
|
-
expect(workerRequests).toHaveLength(2);
|
|
175
|
-
expect(workerRequests[0]?.messages.find((message) => message.role === "user")?.content).toContain(
|
|
176
|
-
"coordinator plan output"
|
|
177
|
-
);
|
|
178
|
-
expect(workerRequests[1]?.messages.find((message) => message.role === "user")?.content).toContain(
|
|
179
|
-
"coordinator plan output"
|
|
180
|
-
);
|
|
181
|
-
expect(workerRequests[1]?.messages.find((message) => message.role === "user")?.content).not.toContain(
|
|
182
|
-
"worker output from worker-a"
|
|
183
|
-
);
|
|
184
|
-
|
|
185
|
-
releaseWorkers();
|
|
186
|
-
const result = await resultPromise;
|
|
187
|
-
const finalRequest = requests.at(-1);
|
|
188
|
-
expect(finalRequest?.metadata.phase).toBe("final-synthesis");
|
|
189
|
-
expect(finalRequest?.messages.find((message) => message.role === "user")?.content).toContain(
|
|
190
|
-
"worker output from worker-a"
|
|
191
|
-
);
|
|
192
|
-
expect(finalRequest?.messages.find((message) => message.role === "user")?.content).toContain(
|
|
193
|
-
"worker output from worker-b"
|
|
194
|
-
);
|
|
195
|
-
expect(result.output).toBe("final synthesis output");
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
it("threads shared runtime tool availability through every coordinator phase", async () => {
|
|
199
|
-
const requests: ModelRequest[] = [];
|
|
200
|
-
const lookupTool: RuntimeTool<JsonObject, JsonValue> = {
|
|
201
|
-
identity: {
|
|
202
|
-
id: "fixture.lookup",
|
|
203
|
-
namespace: "dogpile.test",
|
|
204
|
-
name: "lookup",
|
|
205
|
-
version: "1.0.0",
|
|
206
|
-
description: "Lookup release-readiness evidence."
|
|
207
|
-
},
|
|
208
|
-
inputSchema: {
|
|
209
|
-
kind: "json-schema",
|
|
210
|
-
description: "Release evidence lookup input.",
|
|
211
|
-
schema: {
|
|
212
|
-
type: "object",
|
|
213
|
-
properties: {
|
|
214
|
-
query: { type: "string" }
|
|
215
|
-
},
|
|
216
|
-
required: ["query"],
|
|
217
|
-
additionalProperties: false
|
|
218
|
-
}
|
|
219
|
-
},
|
|
220
|
-
permissions: [
|
|
221
|
-
{
|
|
222
|
-
kind: "custom",
|
|
223
|
-
name: "release-evidence",
|
|
224
|
-
description: "Reads caller-owned release evidence."
|
|
225
|
-
}
|
|
226
|
-
],
|
|
227
|
-
execute(input, context) {
|
|
228
|
-
return {
|
|
229
|
-
type: "success",
|
|
230
|
-
toolCallId: context.toolCallId,
|
|
231
|
-
tool: this.identity,
|
|
232
|
-
output: {
|
|
233
|
-
protocol: context.protocol
|
|
234
|
-
}
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
};
|
|
238
|
-
const model: ConfiguredModelProvider = {
|
|
239
|
-
id: "coordinator-tool-availability-model",
|
|
240
|
-
async generate(request) {
|
|
241
|
-
requests.push(request);
|
|
242
|
-
return { text: `${String(request.metadata.phase)}:${String(request.metadata.agentId)}` };
|
|
243
|
-
}
|
|
244
|
-
};
|
|
245
|
-
|
|
246
|
-
await run({
|
|
247
|
-
intent: "Use available tools while coordinating a release decision.",
|
|
248
|
-
protocol: { kind: "coordinator", maxTurns: 3 },
|
|
249
|
-
tier: "fast",
|
|
250
|
-
model,
|
|
251
|
-
agents: [
|
|
252
|
-
{ id: "lead", role: "coordinator" },
|
|
253
|
-
{ id: "risk", role: "risk-reviewer" },
|
|
254
|
-
{ id: "runtime", role: "runtime-reviewer" }
|
|
255
|
-
],
|
|
256
|
-
tools: [lookupTool]
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
expect(requests).toHaveLength(4);
|
|
260
|
-
expect(requests.map((request) => request.metadata.phase)).toEqual([
|
|
261
|
-
"plan",
|
|
262
|
-
"worker",
|
|
263
|
-
"worker",
|
|
264
|
-
"final-synthesis"
|
|
265
|
-
]);
|
|
266
|
-
expect(requests.map((request) => request.metadata.tools)).toEqual([
|
|
267
|
-
runtimeToolManifest([lookupTool]),
|
|
268
|
-
runtimeToolManifest([lookupTool]),
|
|
269
|
-
runtimeToolManifest([lookupTool]),
|
|
270
|
-
runtimeToolManifest([lookupTool])
|
|
271
|
-
]);
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
it("streams coordinator provider-backed turns before the final result", async () => {
|
|
275
|
-
const model = createPhaseEchoProvider("streaming-coordinator-model");
|
|
276
|
-
const handle = stream({
|
|
277
|
-
intent: "Stream a coordinator run.",
|
|
278
|
-
protocol: { kind: "coordinator", maxTurns: 2 },
|
|
279
|
-
tier: "fast",
|
|
280
|
-
model,
|
|
281
|
-
agents: [
|
|
282
|
-
{ id: "agent-1", role: "coordinator" },
|
|
283
|
-
{ id: "agent-2", role: "worker" }
|
|
284
|
-
]
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
const events: string[] = [];
|
|
288
|
-
for await (const event of handle) {
|
|
289
|
-
events.push(event.type);
|
|
290
|
-
}
|
|
291
|
-
const result = await handle.result;
|
|
292
|
-
|
|
293
|
-
expect(events).toEqual([
|
|
294
|
-
"role-assignment",
|
|
295
|
-
"role-assignment",
|
|
296
|
-
"agent-turn",
|
|
297
|
-
"agent-turn",
|
|
298
|
-
"agent-turn",
|
|
299
|
-
"final"
|
|
300
|
-
]);
|
|
301
|
-
expect(result.output).toBe("final-synthesis:coordinator:agent-1");
|
|
302
|
-
expect(result.trace.events.map((event) => event.type)).toEqual(events);
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
it("runs coordinator end to end with the configured provider and produces output, event log, and transcript", async () => {
|
|
306
|
-
const requests: ModelRequest[] = [];
|
|
307
|
-
const model = createPhaseEchoProvider("coordinator-e2e-provider", requests);
|
|
308
|
-
const handle = Dogpile.stream({
|
|
309
|
-
intent: "Produce an end-to-end coordinator release decision.",
|
|
310
|
-
protocol: { kind: "coordinator", maxTurns: 3 },
|
|
311
|
-
tier: "quality",
|
|
312
|
-
model,
|
|
313
|
-
agents: [
|
|
314
|
-
{ id: "lead", role: "release-coordinator" },
|
|
315
|
-
{ id: "risk", role: "risk-reviewer" },
|
|
316
|
-
{ id: "runtime", role: "runtime-reviewer" }
|
|
317
|
-
]
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
const eventLog: RunEvent[] = [];
|
|
321
|
-
for await (const event of handle) {
|
|
322
|
-
if (event.type !== "error") {
|
|
323
|
-
eventLog.push(event as RunEvent);
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
const result = await handle.result;
|
|
327
|
-
|
|
328
|
-
expect(requests).toHaveLength(4);
|
|
329
|
-
expect(result.output).toBe("final-synthesis:release-coordinator:lead");
|
|
330
|
-
expect(result.trace.protocol).toBe("coordinator");
|
|
331
|
-
expect(result.trace.modelProviderId).toBe("coordinator-e2e-provider");
|
|
332
|
-
expect(eventLog).toHaveLength(8);
|
|
333
|
-
expect(eventLog).toEqual(result.trace.events);
|
|
334
|
-
expect(eventLog.map((event) => event.type)).toEqual([
|
|
335
|
-
"role-assignment",
|
|
336
|
-
"role-assignment",
|
|
337
|
-
"role-assignment",
|
|
338
|
-
"agent-turn",
|
|
339
|
-
"agent-turn",
|
|
340
|
-
"agent-turn",
|
|
341
|
-
"agent-turn",
|
|
342
|
-
"final"
|
|
343
|
-
]);
|
|
344
|
-
expect(result.transcript).toHaveLength(4);
|
|
345
|
-
expect(result.trace.transcript).toEqual(result.transcript);
|
|
346
|
-
expect(result.transcript.every((entry) => entry.input.length > 0 && entry.output.length > 0)).toBe(true);
|
|
347
|
-
|
|
348
|
-
const finalEvent = eventLog.at(-1);
|
|
349
|
-
expect(finalEvent?.type).toBe("final");
|
|
350
|
-
if (finalEvent?.type !== "final") {
|
|
351
|
-
throw new Error("expected final event in coordinator e2e event log");
|
|
352
|
-
}
|
|
353
|
-
expect(finalEvent.output).toBe(result.output);
|
|
354
|
-
});
|
|
355
|
-
|
|
356
|
-
it("runs a deterministic coordinator mission end to end through the branded SDK call", async () => {
|
|
357
|
-
const result = await Dogpile.pile(createDeterministicCoordinatorTestMission());
|
|
358
|
-
const intent = "Decide whether the coordinator protocol can run a portable release triage end to end.";
|
|
359
|
-
const expectedTranscript = [
|
|
360
|
-
{
|
|
361
|
-
agentId: "agent-1",
|
|
362
|
-
role: "release-coordinator",
|
|
363
|
-
input: `Mission: ${intent}\nCoordinator agent-1: assign the work, name the plan, and provide the first contribution.`,
|
|
364
|
-
output: "release-coordinator:agent-1 planned the coordinator-managed mission."
|
|
365
|
-
},
|
|
366
|
-
{
|
|
367
|
-
agentId: "agent-2",
|
|
368
|
-
role: "evidence-reviewer",
|
|
369
|
-
input: [
|
|
370
|
-
`Mission: ${intent}`,
|
|
371
|
-
"",
|
|
372
|
-
"Coordinator: agent-1",
|
|
373
|
-
"Prior contributions:",
|
|
374
|
-
"release-coordinator (agent-1): release-coordinator:agent-1 planned the coordinator-managed mission.",
|
|
375
|
-
"",
|
|
376
|
-
"Follow the coordinator-managed plan and provide your assigned contribution."
|
|
377
|
-
].join("\n"),
|
|
378
|
-
output: "evidence-reviewer:agent-2 completed the coordinator-assigned work."
|
|
379
|
-
},
|
|
380
|
-
{
|
|
381
|
-
agentId: "agent-3",
|
|
382
|
-
role: "portability-reviewer",
|
|
383
|
-
input: [
|
|
384
|
-
`Mission: ${intent}`,
|
|
385
|
-
"",
|
|
386
|
-
"Coordinator: agent-1",
|
|
387
|
-
"Prior contributions:",
|
|
388
|
-
"release-coordinator (agent-1): release-coordinator:agent-1 planned the coordinator-managed mission.",
|
|
389
|
-
"",
|
|
390
|
-
"Follow the coordinator-managed plan and provide your assigned contribution."
|
|
391
|
-
].join("\n"),
|
|
392
|
-
output: "portability-reviewer:agent-3 completed the coordinator-assigned work."
|
|
393
|
-
},
|
|
394
|
-
{
|
|
395
|
-
agentId: "agent-1",
|
|
396
|
-
role: "release-coordinator",
|
|
397
|
-
input: [
|
|
398
|
-
`Mission: ${intent}`,
|
|
399
|
-
"",
|
|
400
|
-
"Coordinator: agent-1",
|
|
401
|
-
"Prior contributions:",
|
|
402
|
-
"release-coordinator (agent-1): release-coordinator:agent-1 planned the coordinator-managed mission.",
|
|
403
|
-
"",
|
|
404
|
-
"evidence-reviewer (agent-2): evidence-reviewer:agent-2 completed the coordinator-assigned work.",
|
|
405
|
-
"",
|
|
406
|
-
"portability-reviewer (agent-3): portability-reviewer:agent-3 completed the coordinator-assigned work.",
|
|
407
|
-
"",
|
|
408
|
-
"Synthesize the final answer as the coordinator."
|
|
409
|
-
].join("\n"),
|
|
410
|
-
output: "release-coordinator:agent-1 synthesized the coordinator-managed mission."
|
|
411
|
-
}
|
|
412
|
-
] as const;
|
|
413
|
-
|
|
414
|
-
expect(result.output).toBe("release-coordinator:agent-1 synthesized the coordinator-managed mission.");
|
|
415
|
-
expect(result.transcript).toEqual(expectedTranscript);
|
|
416
|
-
expect(result.trace.transcript).toEqual(expectedTranscript);
|
|
417
|
-
expect(result.trace.transcript).toEqual(result.transcript);
|
|
418
|
-
expect(result.trace.protocol).toBe("coordinator");
|
|
419
|
-
expect(result.trace.modelProviderId).toBe("deterministic-coordinator-model");
|
|
420
|
-
expect(result.trace.agentsUsed.map((agent) => agent.id)).toEqual(["agent-1", "agent-2", "agent-3"]);
|
|
421
|
-
expect(result.trace.events.map((event) => event.type)).toEqual([
|
|
422
|
-
"role-assignment",
|
|
423
|
-
"role-assignment",
|
|
424
|
-
"role-assignment",
|
|
425
|
-
"agent-turn",
|
|
426
|
-
"agent-turn",
|
|
427
|
-
"agent-turn",
|
|
428
|
-
"agent-turn",
|
|
429
|
-
"final"
|
|
430
|
-
]);
|
|
431
|
-
|
|
432
|
-
const finalEvent = result.trace.events.at(-1);
|
|
433
|
-
expect(finalEvent?.type).toBe("final");
|
|
434
|
-
if (finalEvent?.type !== "final") {
|
|
435
|
-
throw new Error("expected final event");
|
|
436
|
-
}
|
|
437
|
-
expect(finalEvent.output).toBe(result.output);
|
|
438
|
-
expect(finalEvent.cost).toEqual(result.cost);
|
|
439
|
-
expect(JSON.parse(JSON.stringify(result.trace))).toEqual(result.trace);
|
|
440
|
-
expect(result.cost.totalTokens).toBeGreaterThan(0);
|
|
441
|
-
});
|
|
442
|
-
});
|
|
443
|
-
|
|
444
|
-
function createPhaseEchoProvider(id: string, requests: ModelRequest[] = []): ConfiguredModelProvider {
|
|
445
|
-
return {
|
|
446
|
-
id,
|
|
447
|
-
async generate(request: ModelRequest): Promise<ModelResponse> {
|
|
448
|
-
requests.push(request);
|
|
449
|
-
return {
|
|
450
|
-
text: `${String(request.metadata.phase)}:${String(request.metadata.role)}:${String(
|
|
451
|
-
request.metadata.agentId
|
|
452
|
-
)}`,
|
|
453
|
-
usage: {
|
|
454
|
-
inputTokens: 1,
|
|
455
|
-
outputTokens: 1,
|
|
456
|
-
totalTokens: 2
|
|
457
|
-
},
|
|
458
|
-
costUsd: 0
|
|
459
|
-
};
|
|
460
|
-
}
|
|
461
|
-
};
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
function rejectAfter(ms: number, message: string): Promise<never> {
|
|
465
|
-
return new Promise((_, reject) => {
|
|
466
|
-
setTimeout(() => reject(new Error(message)), ms);
|
|
467
|
-
});
|
|
468
|
-
}
|