@axiastudio/aioc 0.1.0-alpha.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/LICENSE +21 -0
- package/README.md +77 -0
- package/dist/agent.d.ts +29 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +36 -0
- package/dist/config.d.ts +5 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +18 -0
- package/dist/errors.d.ts +37 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +46 -0
- package/dist/examples/basic/hello-world.d.ts +2 -0
- package/dist/examples/basic/hello-world.d.ts.map +1 -0
- package/dist/examples/basic/hello-world.js +20 -0
- package/dist/examples/basic/run-record-sink.d.ts +2 -0
- package/dist/examples/basic/run-record-sink.d.ts.map +1 -0
- package/dist/examples/basic/run-record-sink.js +103 -0
- package/dist/examples/basic/tools.d.ts +2 -0
- package/dist/examples/basic/tools.d.ts.map +1 -0
- package/dist/examples/basic/tools.js +84 -0
- package/dist/examples/guardrail-smoke.d.ts +2 -0
- package/dist/examples/guardrail-smoke.d.ts.map +1 -0
- package/dist/examples/guardrail-smoke.js +110 -0
- package/dist/examples/hello-run.d.ts +2 -0
- package/dist/examples/hello-run.d.ts.map +1 -0
- package/dist/examples/hello-run.js +20 -0
- package/dist/examples/learn-01-hello-run.d.ts +2 -0
- package/dist/examples/learn-01-hello-run.d.ts.map +1 -0
- package/dist/examples/learn-01-hello-run.js +29 -0
- package/dist/examples/learn-02-tool-policy.d.ts +2 -0
- package/dist/examples/learn-02-tool-policy.d.ts.map +1 -0
- package/dist/examples/learn-02-tool-policy.js +87 -0
- package/dist/examples/learn-03-controlled-handoff.d.ts +2 -0
- package/dist/examples/learn-03-controlled-handoff.d.ts.map +1 -0
- package/dist/examples/learn-03-controlled-handoff.js +84 -0
- package/dist/examples/learn-04-output-guardrail.d.ts +2 -0
- package/dist/examples/learn-04-output-guardrail.d.ts.map +1 -0
- package/dist/examples/learn-04-output-guardrail.js +61 -0
- package/dist/examples/learn-05-run-record-audit.d.ts +2 -0
- package/dist/examples/learn-05-run-record-audit.d.ts.map +1 -0
- package/dist/examples/learn-05-run-record-audit.js +135 -0
- package/dist/examples/mistral-smoke.d.ts +2 -0
- package/dist/examples/mistral-smoke.d.ts.map +1 -0
- package/dist/examples/mistral-smoke.js +63 -0
- package/dist/examples/onboarding-governance-smoke.d.ts +2 -0
- package/dist/examples/onboarding-governance-smoke.d.ts.map +1 -0
- package/dist/examples/onboarding-governance-smoke.js +477 -0
- package/dist/examples/onboarding-governance-tutorial.d.ts +2 -0
- package/dist/examples/onboarding-governance-tutorial.d.ts.map +1 -0
- package/dist/examples/onboarding-governance-tutorial.js +342 -0
- package/dist/examples/policy-smoke.d.ts +2 -0
- package/dist/examples/policy-smoke.d.ts.map +1 -0
- package/dist/examples/policy-smoke.js +137 -0
- package/dist/examples/support/scripted-provider.d.ts +8 -0
- package/dist/examples/support/scripted-provider.d.ts.map +1 -0
- package/dist/examples/support/scripted-provider.js +21 -0
- package/dist/examples/tool-policy-finance.d.ts +2 -0
- package/dist/examples/tool-policy-finance.d.ts.map +1 -0
- package/dist/examples/tool-policy-finance.js +77 -0
- package/dist/examples/tool-run.d.ts +2 -0
- package/dist/examples/tool-run.d.ts.map +1 -0
- package/dist/examples/tool-run.js +70 -0
- package/dist/guardrails.d.ts +20 -0
- package/dist/guardrails.d.ts.map +1 -0
- package/dist/guardrails.js +6 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +33 -0
- package/dist/logger.d.ts +76 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +53 -0
- package/dist/messages.d.ts +5 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +21 -0
- package/dist/policy.d.ts +41 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +20 -0
- package/dist/provider-setup.d.ts +14 -0
- package/dist/provider-setup.d.ts.map +1 -0
- package/dist/provider-setup.js +43 -0
- package/dist/providers/base.d.ts +26 -0
- package/dist/providers/base.d.ts.map +1 -0
- package/dist/providers/base.js +2 -0
- package/dist/providers/chat-completions.d.ts +13 -0
- package/dist/providers/chat-completions.d.ts.map +1 -0
- package/dist/providers/chat-completions.js +314 -0
- package/dist/providers/mistral.d.ts +8 -0
- package/dist/providers/mistral.d.ts.map +1 -0
- package/dist/providers/mistral.js +14 -0
- package/dist/providers/openai.d.ts +11 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/openai.js +23 -0
- package/dist/run-context.d.ts +5 -0
- package/dist/run-context.d.ts.map +1 -0
- package/dist/run-context.js +10 -0
- package/dist/run-log-emitter.d.ts +21 -0
- package/dist/run-log-emitter.d.ts.map +1 -0
- package/dist/run-log-emitter.js +184 -0
- package/dist/run-record.d.ts +61 -0
- package/dist/run-record.d.ts.map +1 -0
- package/dist/run-record.js +2 -0
- package/dist/run-recorder-runtime.d.ts +38 -0
- package/dist/run-recorder-runtime.d.ts.map +1 -0
- package/dist/run-recorder-runtime.js +148 -0
- package/dist/run.d.ts +19 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +497 -0
- package/dist/tests/integration/chat-completions.integration.d.ts +2 -0
- package/dist/tests/integration/chat-completions.integration.d.ts.map +1 -0
- package/dist/tests/integration/chat-completions.integration.js +110 -0
- package/dist/tests/integration/index.d.ts +2 -0
- package/dist/tests/integration/index.d.ts.map +1 -0
- package/dist/tests/integration/index.js +12 -0
- package/dist/tests/regression/handoff-policy-trace.regression.d.ts +2 -0
- package/dist/tests/regression/handoff-policy-trace.regression.d.ts.map +1 -0
- package/dist/tests/regression/handoff-policy-trace.regression.js +92 -0
- package/dist/tests/regression/handoff-transition.regression.d.ts +2 -0
- package/dist/tests/regression/handoff-transition.regression.d.ts.map +1 -0
- package/dist/tests/regression/handoff-transition.regression.js +62 -0
- package/dist/tests/regression/index.d.ts +2 -0
- package/dist/tests/regression/index.d.ts.map +1 -0
- package/dist/tests/regression/index.js +14 -0
- package/dist/tests/regression/policy-default-deny.regression.d.ts +2 -0
- package/dist/tests/regression/policy-default-deny.regression.d.ts.map +1 -0
- package/dist/tests/regression/policy-default-deny.regression.js +106 -0
- package/dist/tests/support/handoff-name.d.ts +2 -0
- package/dist/tests/support/handoff-name.d.ts.map +1 -0
- package/dist/tests/support/handoff-name.js +14 -0
- package/dist/tests/support/scripted-provider.d.ts +8 -0
- package/dist/tests/support/scripted-provider.d.ts.map +1 -0
- package/dist/tests/support/scripted-provider.js +19 -0
- package/dist/tests/unit/guardrail.unit.d.ts +2 -0
- package/dist/tests/unit/guardrail.unit.d.ts.map +1 -0
- package/dist/tests/unit/guardrail.unit.js +48 -0
- package/dist/tests/unit/handoff.unit.d.ts +2 -0
- package/dist/tests/unit/handoff.unit.d.ts.map +1 -0
- package/dist/tests/unit/handoff.unit.js +178 -0
- package/dist/tests/unit/index.d.ts +2 -0
- package/dist/tests/unit/index.d.ts.map +1 -0
- package/dist/tests/unit/index.js +24 -0
- package/dist/tests/unit/logger.unit.d.ts +2 -0
- package/dist/tests/unit/logger.unit.d.ts.map +1 -0
- package/dist/tests/unit/logger.unit.js +65 -0
- package/dist/tests/unit/policy.unit.d.ts +2 -0
- package/dist/tests/unit/policy.unit.d.ts.map +1 -0
- package/dist/tests/unit/policy.unit.js +167 -0
- package/dist/tests/unit/provider-setup.unit.d.ts +2 -0
- package/dist/tests/unit/provider-setup.unit.d.ts.map +1 -0
- package/dist/tests/unit/provider-setup.unit.js +75 -0
- package/dist/tests/unit/run-record.unit.d.ts +2 -0
- package/dist/tests/unit/run-record.unit.d.ts.map +1 -0
- package/dist/tests/unit/run-record.unit.js +118 -0
- package/dist/tests/unit/run.unit.d.ts +2 -0
- package/dist/tests/unit/run.unit.d.ts.map +1 -0
- package/dist/tests/unit/run.unit.js +30 -0
- package/dist/tool.d.ts +16 -0
- package/dist/tool.d.ts.map +1 -0
- package/dist/tool.js +6 -0
- package/dist/types.d.ts +67 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/package.json +64 -0
package/dist/run.js
ADDED
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.StreamedRunResult = void 0;
|
|
4
|
+
exports.run = run;
|
|
5
|
+
const config_1 = require("./config");
|
|
6
|
+
const errors_1 = require("./errors");
|
|
7
|
+
const messages_1 = require("./messages");
|
|
8
|
+
const run_log_emitter_1 = require("./run-log-emitter");
|
|
9
|
+
const run_recorder_runtime_1 = require("./run-recorder-runtime");
|
|
10
|
+
const run_context_1 = require("./run-context");
|
|
11
|
+
const zod_1 = require("zod");
|
|
12
|
+
function normalizeInput(input) {
|
|
13
|
+
if (typeof input === "string") {
|
|
14
|
+
return [(0, messages_1.user)(input)];
|
|
15
|
+
}
|
|
16
|
+
return [...input];
|
|
17
|
+
}
|
|
18
|
+
function parseArguments(rawArguments) {
|
|
19
|
+
const trimmed = rawArguments.trim();
|
|
20
|
+
if (!trimmed) {
|
|
21
|
+
return {};
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
return JSON.parse(trimmed);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return {
|
|
28
|
+
__raw: rawArguments,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function resolveAgentModel(agent) {
|
|
33
|
+
const model = agent.model?.trim();
|
|
34
|
+
if (!model) {
|
|
35
|
+
throw new Error(`Agent "${agent.name}" has no model configured. Set "model" explicitly.`);
|
|
36
|
+
}
|
|
37
|
+
return model;
|
|
38
|
+
}
|
|
39
|
+
function sanitizeToolSegment(input) {
|
|
40
|
+
const sanitized = input
|
|
41
|
+
.trim()
|
|
42
|
+
.toLowerCase()
|
|
43
|
+
.replace(/[^a-z0-9]+/g, "_")
|
|
44
|
+
.replace(/^_+|_+$/g, "");
|
|
45
|
+
return sanitized || "agent";
|
|
46
|
+
}
|
|
47
|
+
function buildTurnTools(agent) {
|
|
48
|
+
const handoffRegistry = new Map();
|
|
49
|
+
const handoffTools = [];
|
|
50
|
+
const reservedNames = new Set(agent.tools.map((tool) => tool.name));
|
|
51
|
+
for (const handoffAgent of agent.handoffs) {
|
|
52
|
+
const baseName = `handoff_to_${sanitizeToolSegment(handoffAgent.name)}`;
|
|
53
|
+
let toolName = baseName;
|
|
54
|
+
let suffix = 2;
|
|
55
|
+
while (reservedNames.has(toolName) || handoffRegistry.has(toolName)) {
|
|
56
|
+
toolName = `${baseName}_${suffix}`;
|
|
57
|
+
suffix += 1;
|
|
58
|
+
}
|
|
59
|
+
handoffRegistry.set(toolName, handoffAgent);
|
|
60
|
+
reservedNames.add(toolName);
|
|
61
|
+
handoffTools.push({
|
|
62
|
+
name: toolName,
|
|
63
|
+
description: handoffAgent.handoffDescription
|
|
64
|
+
? `Handoff to agent "${handoffAgent.name}". ${handoffAgent.handoffDescription}`
|
|
65
|
+
: `Handoff to agent "${handoffAgent.name}".`,
|
|
66
|
+
parameters: zod_1.z.object({}).passthrough(),
|
|
67
|
+
execute: () => ({ handoffTo: handoffAgent.name }),
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
providerTools: [...agent.tools, ...handoffTools],
|
|
72
|
+
handoffRegistry,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function toErrorMetadata(error) {
|
|
76
|
+
if (error instanceof Error) {
|
|
77
|
+
return {
|
|
78
|
+
errorName: error.name,
|
|
79
|
+
errorMessage: error.message,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
errorName: "Error",
|
|
84
|
+
errorMessage: String(error),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function createDeniedPolicyResult(reason, metadata) {
|
|
88
|
+
return {
|
|
89
|
+
decision: "deny",
|
|
90
|
+
reason,
|
|
91
|
+
metadata,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function shouldReturnDeniedAsToolResult(policyResult) {
|
|
95
|
+
return (policyResult.decision === "deny" && policyResult.denyMode === "tool_result");
|
|
96
|
+
}
|
|
97
|
+
function toAllowedToolResultEnvelope(data) {
|
|
98
|
+
return {
|
|
99
|
+
status: "ok",
|
|
100
|
+
code: null,
|
|
101
|
+
publicReason: null,
|
|
102
|
+
data,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function toDeniedToolResultEnvelope(policyResult) {
|
|
106
|
+
return {
|
|
107
|
+
status: "denied",
|
|
108
|
+
code: policyResult.reason,
|
|
109
|
+
publicReason: policyResult.publicReason?.trim() || "Action not allowed.",
|
|
110
|
+
data: null,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function isPolicyResult(value) {
|
|
114
|
+
if (typeof value !== "object" || value === null) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
const candidate = value;
|
|
118
|
+
const validDecision = candidate.decision === "allow" || candidate.decision === "deny";
|
|
119
|
+
const validPublicReason = typeof candidate.publicReason === "undefined" ||
|
|
120
|
+
typeof candidate.publicReason === "string";
|
|
121
|
+
const validDenyMode = typeof candidate.denyMode === "undefined" ||
|
|
122
|
+
candidate.denyMode === "throw" ||
|
|
123
|
+
candidate.denyMode === "tool_result";
|
|
124
|
+
return (validDecision &&
|
|
125
|
+
typeof candidate.reason === "string" &&
|
|
126
|
+
candidate.reason.trim().length > 0 &&
|
|
127
|
+
validPublicReason &&
|
|
128
|
+
validDenyMode);
|
|
129
|
+
}
|
|
130
|
+
async function evaluateToolPolicy(agent, call, parsedArguments, runContext, turn, policies) {
|
|
131
|
+
const policy = policies?.toolPolicy;
|
|
132
|
+
if (!policy) {
|
|
133
|
+
return createDeniedPolicyResult("policy_not_configured");
|
|
134
|
+
}
|
|
135
|
+
let rawResult;
|
|
136
|
+
try {
|
|
137
|
+
rawResult = await policy({
|
|
138
|
+
agentName: agent.name,
|
|
139
|
+
toolName: call.name,
|
|
140
|
+
rawArguments: call.arguments,
|
|
141
|
+
parsedArguments,
|
|
142
|
+
runContext,
|
|
143
|
+
turn,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
return createDeniedPolicyResult("policy_error", toErrorMetadata(error));
|
|
148
|
+
}
|
|
149
|
+
if (!isPolicyResult(rawResult)) {
|
|
150
|
+
return createDeniedPolicyResult("invalid_policy_result");
|
|
151
|
+
}
|
|
152
|
+
return rawResult;
|
|
153
|
+
}
|
|
154
|
+
async function evaluateHandoffPolicy(fromAgent, toAgent, call, handoffPayload, runContext, turn, policies) {
|
|
155
|
+
const policy = policies?.handoffPolicy;
|
|
156
|
+
if (!policy) {
|
|
157
|
+
return createDeniedPolicyResult("policy_not_configured");
|
|
158
|
+
}
|
|
159
|
+
let rawResult;
|
|
160
|
+
try {
|
|
161
|
+
rawResult = await policy({
|
|
162
|
+
fromAgentName: fromAgent.name,
|
|
163
|
+
toAgentName: toAgent.name,
|
|
164
|
+
handoffPayload,
|
|
165
|
+
runContext,
|
|
166
|
+
turn,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
return createDeniedPolicyResult("policy_error", toErrorMetadata(error));
|
|
171
|
+
}
|
|
172
|
+
if (!isPolicyResult(rawResult)) {
|
|
173
|
+
return createDeniedPolicyResult("invalid_policy_result");
|
|
174
|
+
}
|
|
175
|
+
return rawResult;
|
|
176
|
+
}
|
|
177
|
+
async function executeToolCall(agent, call, parsedArguments, runContext, turn, logEmitter, policies, onPolicyDecision) {
|
|
178
|
+
const definition = agent.tools.find((tool) => tool.name === call.name);
|
|
179
|
+
if (!definition) {
|
|
180
|
+
throw new errors_1.ToolCallError(`Tool "${call.name}" is not registered.`);
|
|
181
|
+
}
|
|
182
|
+
const policyResult = await evaluateToolPolicy(agent, call, parsedArguments, runContext, turn, policies);
|
|
183
|
+
await logEmitter.toolPolicyEvaluated(agent.name, turn, call.name, call.callId, policyResult.decision, policyResult.reason, policyResult.policyVersion, policyResult.metadata);
|
|
184
|
+
onPolicyDecision?.({
|
|
185
|
+
turn,
|
|
186
|
+
callId: call.callId,
|
|
187
|
+
decision: policyResult.decision,
|
|
188
|
+
reason: policyResult.reason,
|
|
189
|
+
policyVersion: policyResult.policyVersion,
|
|
190
|
+
resource: {
|
|
191
|
+
kind: "tool",
|
|
192
|
+
name: call.name,
|
|
193
|
+
},
|
|
194
|
+
metadata: policyResult.metadata,
|
|
195
|
+
});
|
|
196
|
+
if (policyResult.decision !== "allow") {
|
|
197
|
+
if (shouldReturnDeniedAsToolResult(policyResult)) {
|
|
198
|
+
return toDeniedToolResultEnvelope(policyResult);
|
|
199
|
+
}
|
|
200
|
+
throw new errors_1.ToolCallPolicyDeniedError({
|
|
201
|
+
toolName: call.name,
|
|
202
|
+
policyResult,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
const validated = definition.parameters.parse(parsedArguments);
|
|
206
|
+
const toolOutput = await definition.execute(validated, runContext);
|
|
207
|
+
return toAllowedToolResultEnvelope(toolOutput);
|
|
208
|
+
}
|
|
209
|
+
async function evaluateOutputGuardrails(agent, runContext, history, outputText, logEmitter, turn, onGuardrailDecision) {
|
|
210
|
+
for (const guardrail of agent.outputGuardrails) {
|
|
211
|
+
await logEmitter.outputGuardrailStarted(agent.name, turn, guardrail.name);
|
|
212
|
+
const output = await guardrail.execute({
|
|
213
|
+
agent,
|
|
214
|
+
runContext,
|
|
215
|
+
outputText,
|
|
216
|
+
history: [...history],
|
|
217
|
+
});
|
|
218
|
+
if (output.tripwireTriggered) {
|
|
219
|
+
onGuardrailDecision?.({
|
|
220
|
+
turn,
|
|
221
|
+
guardrailName: guardrail.name,
|
|
222
|
+
decision: "triggered",
|
|
223
|
+
reason: output.reason,
|
|
224
|
+
metadata: output.metadata,
|
|
225
|
+
});
|
|
226
|
+
await logEmitter.outputGuardrailTriggered(agent.name, turn, guardrail.name, output.reason);
|
|
227
|
+
throw new errors_1.OutputGuardrailTripwireTriggered({
|
|
228
|
+
guardrail: guardrail.name,
|
|
229
|
+
output,
|
|
230
|
+
outputText,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
onGuardrailDecision?.({
|
|
234
|
+
turn,
|
|
235
|
+
guardrailName: guardrail.name,
|
|
236
|
+
decision: "pass",
|
|
237
|
+
});
|
|
238
|
+
await logEmitter.outputGuardrailPassed(agent.name, turn, guardrail.name);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
async function* runLoop(state, provider, runContext, maxTurns, logger, policies, onPolicyDecision, onGuardrailDecision) {
|
|
242
|
+
const logEmitter = new run_log_emitter_1.RunLogEmitter(logger);
|
|
243
|
+
await logEmitter.runStarted(state.lastAgent.name, maxTurns, state.history.length);
|
|
244
|
+
yield {
|
|
245
|
+
type: "agent_updated_stream_event",
|
|
246
|
+
agent: state.lastAgent,
|
|
247
|
+
};
|
|
248
|
+
await logEmitter.agentActivated(state.lastAgent.name, 1);
|
|
249
|
+
let activeTurn = 0;
|
|
250
|
+
try {
|
|
251
|
+
for (let turn = 0; turn < maxTurns; turn += 1) {
|
|
252
|
+
activeTurn = turn + 1;
|
|
253
|
+
const currentAgent = state.lastAgent;
|
|
254
|
+
await logEmitter.turnStarted(currentAgent.name, activeTurn);
|
|
255
|
+
const { providerTools, handoffRegistry } = buildTurnTools(currentAgent);
|
|
256
|
+
const providerStream = provider.stream({
|
|
257
|
+
model: resolveAgentModel(currentAgent),
|
|
258
|
+
systemPrompt: await currentAgent.resolveInstructions(runContext),
|
|
259
|
+
messages: state.history,
|
|
260
|
+
tools: providerTools,
|
|
261
|
+
modelSettings: currentAgent.modelSettings,
|
|
262
|
+
});
|
|
263
|
+
let outputText = "";
|
|
264
|
+
const pendingToolCalls = [];
|
|
265
|
+
let sawDelta = false;
|
|
266
|
+
const hasOutputGuardrails = currentAgent.outputGuardrails.length > 0;
|
|
267
|
+
const bufferedDeltas = [];
|
|
268
|
+
for await (const modelEvent of providerStream) {
|
|
269
|
+
if (modelEvent.type === "delta") {
|
|
270
|
+
sawDelta = true;
|
|
271
|
+
outputText += modelEvent.delta;
|
|
272
|
+
if (hasOutputGuardrails) {
|
|
273
|
+
bufferedDeltas.push(modelEvent.delta);
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
yield {
|
|
277
|
+
type: "raw_model_stream_event",
|
|
278
|
+
data: { delta: modelEvent.delta },
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
if (modelEvent.type === "tool_call") {
|
|
284
|
+
pendingToolCalls.push({
|
|
285
|
+
callId: modelEvent.callId,
|
|
286
|
+
name: modelEvent.name,
|
|
287
|
+
arguments: modelEvent.arguments,
|
|
288
|
+
});
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
if (!sawDelta && modelEvent.message) {
|
|
292
|
+
outputText = modelEvent.message;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
if (hasOutputGuardrails) {
|
|
296
|
+
await evaluateOutputGuardrails(currentAgent, runContext, state.history, outputText, logEmitter, activeTurn, onGuardrailDecision);
|
|
297
|
+
for (const delta of bufferedDeltas) {
|
|
298
|
+
yield {
|
|
299
|
+
type: "raw_model_stream_event",
|
|
300
|
+
data: { delta },
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
if (pendingToolCalls.length > 0) {
|
|
305
|
+
for (const call of pendingToolCalls) {
|
|
306
|
+
const callItemArguments = parseArguments(call.arguments);
|
|
307
|
+
const callItem = {
|
|
308
|
+
type: "tool_call_item",
|
|
309
|
+
callId: call.callId,
|
|
310
|
+
name: call.name,
|
|
311
|
+
arguments: callItemArguments,
|
|
312
|
+
rawItem: {
|
|
313
|
+
type: "function_call",
|
|
314
|
+
id: call.callId,
|
|
315
|
+
name: call.name,
|
|
316
|
+
arguments: call.arguments,
|
|
317
|
+
},
|
|
318
|
+
};
|
|
319
|
+
state.history.push(callItem);
|
|
320
|
+
yield {
|
|
321
|
+
type: "run_item_stream_event",
|
|
322
|
+
item: callItem,
|
|
323
|
+
};
|
|
324
|
+
await logEmitter.toolCallStarted(currentAgent.name, activeTurn, call.name, call.callId);
|
|
325
|
+
const handoffTarget = handoffRegistry.get(call.name);
|
|
326
|
+
let toolOutput;
|
|
327
|
+
let handoffTransitioned = false;
|
|
328
|
+
try {
|
|
329
|
+
if (handoffTarget) {
|
|
330
|
+
const handoffPolicyResult = await evaluateHandoffPolicy(currentAgent, handoffTarget, call, callItemArguments, runContext, activeTurn, policies);
|
|
331
|
+
await logEmitter.handoffPolicyEvaluated(currentAgent.name, activeTurn, call.name, call.callId, handoffTarget.name, handoffPolicyResult.decision, handoffPolicyResult.reason, handoffPolicyResult.policyVersion, handoffPolicyResult.metadata);
|
|
332
|
+
onPolicyDecision?.({
|
|
333
|
+
turn: activeTurn,
|
|
334
|
+
callId: call.callId,
|
|
335
|
+
decision: handoffPolicyResult.decision,
|
|
336
|
+
reason: handoffPolicyResult.reason,
|
|
337
|
+
policyVersion: handoffPolicyResult.policyVersion,
|
|
338
|
+
resource: {
|
|
339
|
+
kind: "handoff",
|
|
340
|
+
name: handoffTarget.name,
|
|
341
|
+
},
|
|
342
|
+
metadata: handoffPolicyResult.metadata,
|
|
343
|
+
});
|
|
344
|
+
if (handoffPolicyResult.decision !== "allow") {
|
|
345
|
+
if (shouldReturnDeniedAsToolResult(handoffPolicyResult)) {
|
|
346
|
+
toolOutput = toDeniedToolResultEnvelope(handoffPolicyResult);
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
throw new errors_1.HandoffPolicyDeniedError({
|
|
350
|
+
fromAgent: currentAgent.name,
|
|
351
|
+
toAgent: handoffTarget.name,
|
|
352
|
+
policyResult: handoffPolicyResult,
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
else {
|
|
357
|
+
toolOutput = toAllowedToolResultEnvelope({
|
|
358
|
+
handoffTo: handoffTarget.name,
|
|
359
|
+
accepted: true,
|
|
360
|
+
payload: callItemArguments,
|
|
361
|
+
});
|
|
362
|
+
state.lastAgent = handoffTarget;
|
|
363
|
+
handoffTransitioned = true;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
else {
|
|
367
|
+
toolOutput = await executeToolCall(currentAgent, call, callItemArguments, runContext, activeTurn, logEmitter, policies, onPolicyDecision);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
catch (error) {
|
|
371
|
+
await logEmitter.toolCallFailed(currentAgent.name, activeTurn, call.name, call.callId, error);
|
|
372
|
+
throw error;
|
|
373
|
+
}
|
|
374
|
+
await logEmitter.toolCallCompleted(currentAgent.name, activeTurn, call.name, call.callId);
|
|
375
|
+
const outputItem = {
|
|
376
|
+
type: "tool_call_output_item",
|
|
377
|
+
callId: call.callId,
|
|
378
|
+
output: toolOutput,
|
|
379
|
+
rawItem: {
|
|
380
|
+
type: "function_call_result",
|
|
381
|
+
call_id: call.callId,
|
|
382
|
+
output: toolOutput,
|
|
383
|
+
},
|
|
384
|
+
};
|
|
385
|
+
state.history.push(outputItem);
|
|
386
|
+
yield {
|
|
387
|
+
type: "run_item_stream_event",
|
|
388
|
+
item: outputItem,
|
|
389
|
+
};
|
|
390
|
+
if (handoffTransitioned) {
|
|
391
|
+
yield {
|
|
392
|
+
type: "agent_updated_stream_event",
|
|
393
|
+
agent: state.lastAgent,
|
|
394
|
+
};
|
|
395
|
+
await logEmitter.agentActivated(state.lastAgent.name, activeTurn);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
continue;
|
|
399
|
+
}
|
|
400
|
+
const outputItem = {
|
|
401
|
+
type: "message_output_item",
|
|
402
|
+
content: outputText,
|
|
403
|
+
};
|
|
404
|
+
yield {
|
|
405
|
+
type: "run_item_stream_event",
|
|
406
|
+
item: outputItem,
|
|
407
|
+
};
|
|
408
|
+
state.history.push({
|
|
409
|
+
type: "message",
|
|
410
|
+
role: "assistant",
|
|
411
|
+
content: outputText,
|
|
412
|
+
});
|
|
413
|
+
await logEmitter.runCompleted(currentAgent.name, activeTurn, outputText.length);
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
throw new errors_1.MaxTurnsExceededError(maxTurns);
|
|
417
|
+
}
|
|
418
|
+
catch (error) {
|
|
419
|
+
await logEmitter.runFailed(state.lastAgent.name, activeTurn || undefined, error);
|
|
420
|
+
throw error;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
class StreamedRunResult {
|
|
424
|
+
consumed = false;
|
|
425
|
+
stream;
|
|
426
|
+
state;
|
|
427
|
+
constructor(stream, state) {
|
|
428
|
+
this.stream = stream;
|
|
429
|
+
this.state = state;
|
|
430
|
+
}
|
|
431
|
+
toStream() {
|
|
432
|
+
if (this.consumed) {
|
|
433
|
+
throw new Error("This stream can only be consumed once.");
|
|
434
|
+
}
|
|
435
|
+
this.consumed = true;
|
|
436
|
+
return this.stream;
|
|
437
|
+
}
|
|
438
|
+
get history() {
|
|
439
|
+
return this.state.history;
|
|
440
|
+
}
|
|
441
|
+
get lastAgent() {
|
|
442
|
+
return this.state.lastAgent;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
exports.StreamedRunResult = StreamedRunResult;
|
|
446
|
+
async function run(startingAgent, input, options = {}) {
|
|
447
|
+
const runContext = new run_context_1.RunContext((options.context ?? {}));
|
|
448
|
+
const state = {
|
|
449
|
+
history: normalizeInput(input),
|
|
450
|
+
lastAgent: startingAgent,
|
|
451
|
+
};
|
|
452
|
+
const provider = (0, config_1.getDefaultProvider)();
|
|
453
|
+
const maxTurns = options.maxTurns ?? 10;
|
|
454
|
+
const runRecorder = await run_recorder_runtime_1.RunRecorder.create({
|
|
455
|
+
input,
|
|
456
|
+
context: runContext.context,
|
|
457
|
+
providerName: provider.constructor.name,
|
|
458
|
+
recordOptions: options.record,
|
|
459
|
+
});
|
|
460
|
+
const rawStream = runLoop(state, provider, runContext, maxTurns, options.logger, options.policies, runRecorder.onPolicyDecision, runRecorder.onGuardrailDecision);
|
|
461
|
+
const runRecordSnapshot = () => ({
|
|
462
|
+
agentName: state.lastAgent.name,
|
|
463
|
+
model: state.lastAgent.model,
|
|
464
|
+
items: state.history,
|
|
465
|
+
});
|
|
466
|
+
const stream = (async function* () {
|
|
467
|
+
try {
|
|
468
|
+
for await (const event of rawStream) {
|
|
469
|
+
if (event.type === "run_item_stream_event" &&
|
|
470
|
+
event.item.type === "message_output_item") {
|
|
471
|
+
runRecorder.onMessageOutput(event.item.content);
|
|
472
|
+
}
|
|
473
|
+
yield event;
|
|
474
|
+
}
|
|
475
|
+
await runRecorder.emitCompleted(runRecordSnapshot());
|
|
476
|
+
}
|
|
477
|
+
catch (error) {
|
|
478
|
+
await runRecorder.emitFailed(runRecordSnapshot(), error);
|
|
479
|
+
throw error;
|
|
480
|
+
}
|
|
481
|
+
})();
|
|
482
|
+
if (options.stream === true) {
|
|
483
|
+
return new StreamedRunResult(stream, state);
|
|
484
|
+
}
|
|
485
|
+
let finalOutput = "";
|
|
486
|
+
for await (const event of stream) {
|
|
487
|
+
if (event.type === "run_item_stream_event" &&
|
|
488
|
+
event.item.type === "message_output_item") {
|
|
489
|
+
finalOutput = event.item.content;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
return {
|
|
493
|
+
finalOutput,
|
|
494
|
+
history: state.history,
|
|
495
|
+
lastAgent: state.lastAgent,
|
|
496
|
+
};
|
|
497
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat-completions.integration.d.ts","sourceRoot":"","sources":["../../../src/tests/integration/chat-completions.integration.ts"],"names":[],"mappings":"AAmBA,wBAAsB,kCAAkC,IAAI,OAAO,CAAC,IAAI,CAAC,CA8GxE"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.runChatCompletionsIntegrationTests = runChatCompletionsIntegrationTests;
|
|
7
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
8
|
+
const zod_1 = require("zod");
|
|
9
|
+
const messages_1 = require("../../messages");
|
|
10
|
+
const tool_1 = require("../../tool");
|
|
11
|
+
const chat_completions_1 = require("../../providers/chat-completions");
|
|
12
|
+
function createSseBody(chunks) {
|
|
13
|
+
const encoder = new TextEncoder();
|
|
14
|
+
return new ReadableStream({
|
|
15
|
+
start(controller) {
|
|
16
|
+
for (const chunk of chunks) {
|
|
17
|
+
controller.enqueue(encoder.encode(chunk));
|
|
18
|
+
}
|
|
19
|
+
controller.close();
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
async function runChatCompletionsIntegrationTests() {
|
|
24
|
+
const originalFetch = globalThis.fetch;
|
|
25
|
+
let capturedBody = "";
|
|
26
|
+
const streamBody = createSseBody([
|
|
27
|
+
`data: ${JSON.stringify({ choices: [{ delta: { content: "Ciao " } }] })}\n\n`,
|
|
28
|
+
`data: ${JSON.stringify({
|
|
29
|
+
choices: [
|
|
30
|
+
{
|
|
31
|
+
delta: {
|
|
32
|
+
tool_calls: [
|
|
33
|
+
{
|
|
34
|
+
index: 0,
|
|
35
|
+
id: "call_1",
|
|
36
|
+
function: {
|
|
37
|
+
name: "lookup",
|
|
38
|
+
arguments: JSON.stringify({ q: "aioc" }),
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
})}\n\n`,
|
|
46
|
+
`data: ${JSON.stringify({ choices: [{ finish_reason: "tool_calls" }] })}\n\n`,
|
|
47
|
+
"data: [DONE]\n\n",
|
|
48
|
+
]);
|
|
49
|
+
globalThis.fetch = (async (_input, init) => {
|
|
50
|
+
capturedBody = String(init?.body ?? "");
|
|
51
|
+
return new Response(streamBody, {
|
|
52
|
+
status: 200,
|
|
53
|
+
headers: {
|
|
54
|
+
"Content-Type": "text/event-stream",
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
try {
|
|
59
|
+
const lookupTool = (0, tool_1.tool)({
|
|
60
|
+
name: "lookup",
|
|
61
|
+
description: "Lookup docs",
|
|
62
|
+
parameters: zod_1.z.object({
|
|
63
|
+
q: zod_1.z.string(),
|
|
64
|
+
}),
|
|
65
|
+
execute: () => ({ ok: true }),
|
|
66
|
+
});
|
|
67
|
+
const provider = new chat_completions_1.ChatCompletionsProvider({
|
|
68
|
+
apiKey: "test-key",
|
|
69
|
+
baseURL: "https://example.test/v1/",
|
|
70
|
+
});
|
|
71
|
+
const events = [];
|
|
72
|
+
for await (const event of provider.stream({
|
|
73
|
+
model: "test-model",
|
|
74
|
+
systemPrompt: "System instructions",
|
|
75
|
+
messages: [(0, messages_1.user)("Hi")],
|
|
76
|
+
tools: [lookupTool],
|
|
77
|
+
modelSettings: {
|
|
78
|
+
temperature: 0.1,
|
|
79
|
+
top_p: 0.9,
|
|
80
|
+
ignored_setting: true,
|
|
81
|
+
},
|
|
82
|
+
})) {
|
|
83
|
+
events.push(event);
|
|
84
|
+
}
|
|
85
|
+
const payload = JSON.parse(capturedBody);
|
|
86
|
+
strict_1.default.equal(payload.model, "test-model");
|
|
87
|
+
strict_1.default.equal(payload.stream, true);
|
|
88
|
+
strict_1.default.equal(payload.temperature, 0.1);
|
|
89
|
+
strict_1.default.equal(payload.top_p, 0.9);
|
|
90
|
+
strict_1.default.equal(payload.ignored_setting, undefined);
|
|
91
|
+
strict_1.default.equal(payload.tool_choice, "auto");
|
|
92
|
+
const messages = payload.messages;
|
|
93
|
+
strict_1.default.equal(messages[0]?.role, "system");
|
|
94
|
+
strict_1.default.equal(messages[0]?.content, "System instructions");
|
|
95
|
+
strict_1.default.equal(messages[1]?.role, "user");
|
|
96
|
+
strict_1.default.equal(messages[1]?.content, "Hi");
|
|
97
|
+
const tools = payload.tools;
|
|
98
|
+
strict_1.default.equal(tools.length, 1);
|
|
99
|
+
strict_1.default.equal(tools[0]?.function?.name, "lookup");
|
|
100
|
+
strict_1.default.equal(events[0]?.type, "delta");
|
|
101
|
+
strict_1.default.equal(events[0].delta, "Ciao ");
|
|
102
|
+
strict_1.default.equal(events[1]?.type, "tool_call");
|
|
103
|
+
strict_1.default.equal(events[1].name, "lookup");
|
|
104
|
+
strict_1.default.equal(events[2]?.type, "completed");
|
|
105
|
+
strict_1.default.equal(events[2].finishReason, "tool_calls");
|
|
106
|
+
}
|
|
107
|
+
finally {
|
|
108
|
+
globalThis.fetch = originalFetch;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tests/integration/index.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const chat_completions_integration_1 = require("./chat-completions.integration");
|
|
4
|
+
async function main() {
|
|
5
|
+
await (0, chat_completions_integration_1.runChatCompletionsIntegrationTests)();
|
|
6
|
+
process.stdout.write("Integration tests passed.\n");
|
|
7
|
+
}
|
|
8
|
+
main().catch((error) => {
|
|
9
|
+
const message = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
|
|
10
|
+
process.stderr.write(`${message}\n`);
|
|
11
|
+
process.exit(1);
|
|
12
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handoff-policy-trace.regression.d.ts","sourceRoot":"","sources":["../../../src/tests/regression/handoff-policy-trace.regression.ts"],"names":[],"mappings":"AAaA,wBAAsB,oCAAoC,IAAI,OAAO,CAAC,IAAI,CAAC,CA4G1E"}
|