@axlsdk/testing 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/README.md +142 -0
- package/dist/index.cjs +353 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +99 -0
- package/dist/index.d.ts +99 -0
- package/dist/index.js +314 -0
- package/dist/index.js.map +1 -0
- package/package.json +63 -0
package/README.md
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# axl-testing
|
|
2
|
+
|
|
3
|
+
Testing utilities for Axl agentic workflows. Provides deterministic mocks and assertions for unit testing workflows without hitting real LLM APIs.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install axl-testing --save-dev
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## API
|
|
12
|
+
|
|
13
|
+
### `MockProvider`
|
|
14
|
+
|
|
15
|
+
Mock LLM provider with multiple response modes:
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { MockProvider } from 'axl-testing';
|
|
19
|
+
|
|
20
|
+
// Sequence mode — return responses in order
|
|
21
|
+
const provider = MockProvider.sequence(['Hello!', 'World!']);
|
|
22
|
+
|
|
23
|
+
// Echo mode — return the user's prompt back
|
|
24
|
+
const provider = MockProvider.echo();
|
|
25
|
+
|
|
26
|
+
// JSON mode — return structured data
|
|
27
|
+
const provider = MockProvider.json({ answer: 42, confidence: 0.95 });
|
|
28
|
+
|
|
29
|
+
// Replay mode — replay from a recorded file
|
|
30
|
+
const provider = MockProvider.replay('./fixtures/conversation.json');
|
|
31
|
+
|
|
32
|
+
// Function mode — custom response logic
|
|
33
|
+
const provider = MockProvider.fn((messages, options) => {
|
|
34
|
+
const lastMessage = messages[messages.length - 1];
|
|
35
|
+
return `You said: ${lastMessage.content}`;
|
|
36
|
+
});
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
MockProvider also supports tool call simulation:
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
const provider = MockProvider.sequence([
|
|
43
|
+
{
|
|
44
|
+
content: '',
|
|
45
|
+
tool_calls: [{
|
|
46
|
+
id: 'call_1',
|
|
47
|
+
type: 'function',
|
|
48
|
+
function: { name: 'calculator', arguments: '{"expression":"2+2"}' },
|
|
49
|
+
}],
|
|
50
|
+
},
|
|
51
|
+
'The answer is 4.',
|
|
52
|
+
]);
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### `MockTool`
|
|
56
|
+
|
|
57
|
+
Wrap a tool to intercept and record calls:
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { MockTool } from 'axl-testing';
|
|
61
|
+
import { calculator } from './tools';
|
|
62
|
+
|
|
63
|
+
const mock = new MockTool(calculator);
|
|
64
|
+
|
|
65
|
+
// Use in agent
|
|
66
|
+
const myAgent = agent({ model: 'test:mock', system: 'Test', tools: [mock] });
|
|
67
|
+
|
|
68
|
+
// After execution, inspect calls
|
|
69
|
+
console.log(mock.calls); // [{ input: { expression: '2+2' }, output: { result: 4 } }]
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### `AxlTestRuntime`
|
|
73
|
+
|
|
74
|
+
Test runtime that mirrors `WorkflowContext` for deterministic testing:
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
import { AxlTestRuntime, MockProvider } from 'axl-testing';
|
|
78
|
+
|
|
79
|
+
const provider = MockProvider.sequence(['42']);
|
|
80
|
+
const runtime = new AxlTestRuntime({ provider });
|
|
81
|
+
|
|
82
|
+
// Ask an agent
|
|
83
|
+
const result = await runtime.ask(myAgent, 'What is the answer?');
|
|
84
|
+
expect(result).toBe('42');
|
|
85
|
+
|
|
86
|
+
// Inspect recorded calls
|
|
87
|
+
expect(runtime.agentCalls()).toHaveLength(1);
|
|
88
|
+
expect(runtime.toolCalls()).toHaveLength(0);
|
|
89
|
+
expect(runtime.totalCost()).toBe(0);
|
|
90
|
+
|
|
91
|
+
// Test spawn
|
|
92
|
+
const results = await runtime.spawn(3, async (i) => runtime.ask(myAgent, `Q${i}`));
|
|
93
|
+
expect(results.filter(r => r.ok)).toHaveLength(3);
|
|
94
|
+
|
|
95
|
+
// Test vote
|
|
96
|
+
const winner = runtime.vote(results, { strategy: 'majority', key: 'answer' });
|
|
97
|
+
|
|
98
|
+
// Test budget
|
|
99
|
+
const budgeted = await runtime.budget({ cost: '$1.00' }, async () => {
|
|
100
|
+
return runtime.ask(myAgent, 'Hello');
|
|
101
|
+
});
|
|
102
|
+
expect(budgeted.budgetExceeded).toBe(false);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Assertions
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
const runtime = new AxlTestRuntime({ provider });
|
|
109
|
+
|
|
110
|
+
// After running your workflow:
|
|
111
|
+
runtime.agentCalls(); // All recorded agent invocations
|
|
112
|
+
runtime.toolCalls(); // All recorded tool invocations
|
|
113
|
+
runtime.totalCost(); // Cumulative cost
|
|
114
|
+
runtime.steps(); // All recorded steps (agents + tools)
|
|
115
|
+
runtime.traces(); // All trace events
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Example: Testing a Workflow
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
import { describe, it, expect } from 'vitest';
|
|
122
|
+
import { AxlTestRuntime, MockProvider } from 'axl-testing';
|
|
123
|
+
import { myAgent } from '../agents';
|
|
124
|
+
|
|
125
|
+
describe('my workflow', () => {
|
|
126
|
+
it('returns structured output', async () => {
|
|
127
|
+
const provider = MockProvider.json({ answer: 42 });
|
|
128
|
+
const runtime = new AxlTestRuntime({ provider });
|
|
129
|
+
|
|
130
|
+
const result = await runtime.ask(myAgent, 'What is the answer?', {
|
|
131
|
+
schema: z.object({ answer: z.number() }),
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
expect(result).toEqual({ answer: 42 });
|
|
135
|
+
expect(runtime.agentCalls()).toHaveLength(1);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## License
|
|
141
|
+
|
|
142
|
+
[Apache 2.0](../../LICENSE)
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
AxlTestRuntime: () => AxlTestRuntime,
|
|
34
|
+
MockProvider: () => MockProvider,
|
|
35
|
+
MockTool: () => MockTool
|
|
36
|
+
});
|
|
37
|
+
module.exports = __toCommonJS(index_exports);
|
|
38
|
+
|
|
39
|
+
// src/mock-provider.ts
|
|
40
|
+
var import_node_fs = require("fs");
|
|
41
|
+
function randomAlphanumeric(length) {
|
|
42
|
+
const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
|
43
|
+
let result = "";
|
|
44
|
+
for (let i = 0; i < length; i++) {
|
|
45
|
+
result += chars[Math.floor(Math.random() * chars.length)];
|
|
46
|
+
}
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
function generateFromSchema(schema) {
|
|
50
|
+
const def = schema?._def;
|
|
51
|
+
if (!def) return {};
|
|
52
|
+
switch (def.typeName) {
|
|
53
|
+
case "ZodString": {
|
|
54
|
+
const length = 8 + Math.floor(Math.random() * 13);
|
|
55
|
+
return randomAlphanumeric(length);
|
|
56
|
+
}
|
|
57
|
+
case "ZodNumber": {
|
|
58
|
+
const checks = def.checks ?? [];
|
|
59
|
+
let min = 0;
|
|
60
|
+
let max = 100;
|
|
61
|
+
for (const check of checks) {
|
|
62
|
+
if (check.kind === "min") min = check.value;
|
|
63
|
+
if (check.kind === "max") max = check.value;
|
|
64
|
+
}
|
|
65
|
+
return min + Math.random() * (max - min);
|
|
66
|
+
}
|
|
67
|
+
case "ZodBoolean":
|
|
68
|
+
return Math.random() < 0.5;
|
|
69
|
+
case "ZodArray": {
|
|
70
|
+
const count = 1 + Math.floor(Math.random() * 3);
|
|
71
|
+
const items = [];
|
|
72
|
+
for (let i = 0; i < count; i++) {
|
|
73
|
+
items.push(generateFromSchema(def.type));
|
|
74
|
+
}
|
|
75
|
+
return items;
|
|
76
|
+
}
|
|
77
|
+
case "ZodObject": {
|
|
78
|
+
const shape = def.shape?.() ?? {};
|
|
79
|
+
const obj = {};
|
|
80
|
+
for (const [key, value] of Object.entries(shape)) {
|
|
81
|
+
obj[key] = generateFromSchema(value);
|
|
82
|
+
}
|
|
83
|
+
return obj;
|
|
84
|
+
}
|
|
85
|
+
case "ZodOptional": {
|
|
86
|
+
if (Math.random() < 0.5) return void 0;
|
|
87
|
+
return generateFromSchema(def.innerType);
|
|
88
|
+
}
|
|
89
|
+
case "ZodDefault":
|
|
90
|
+
return def.defaultValue();
|
|
91
|
+
case "ZodEnum": {
|
|
92
|
+
const values = def.values ?? [];
|
|
93
|
+
if (values.length === 0) return "";
|
|
94
|
+
return values[Math.floor(Math.random() * values.length)];
|
|
95
|
+
}
|
|
96
|
+
case "ZodNullable":
|
|
97
|
+
return null;
|
|
98
|
+
default:
|
|
99
|
+
return {};
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
var MockProvider = class _MockProvider {
|
|
103
|
+
constructor(responseFn) {
|
|
104
|
+
this.responseFn = responseFn;
|
|
105
|
+
}
|
|
106
|
+
name = "mock";
|
|
107
|
+
_calls = [];
|
|
108
|
+
get calls() {
|
|
109
|
+
return this._calls;
|
|
110
|
+
}
|
|
111
|
+
async chat(messages, options) {
|
|
112
|
+
this._calls.push({ messages, options });
|
|
113
|
+
return this.responseFn(messages, this._calls.length - 1);
|
|
114
|
+
}
|
|
115
|
+
async *stream(messages, options) {
|
|
116
|
+
const response = await this.chat(messages, options);
|
|
117
|
+
yield { type: "text_delta", content: response.content };
|
|
118
|
+
yield { type: "done", usage: response.usage };
|
|
119
|
+
}
|
|
120
|
+
static sequence(responses) {
|
|
121
|
+
return new _MockProvider((_messages, callIndex) => {
|
|
122
|
+
if (callIndex >= responses.length) {
|
|
123
|
+
throw new Error(
|
|
124
|
+
`MockProvider.sequence: no response for call index ${callIndex}. Only ${responses.length} responses defined.`
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
content: responses[callIndex].content,
|
|
129
|
+
tool_calls: responses[callIndex].tool_calls,
|
|
130
|
+
usage: { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },
|
|
131
|
+
cost: 0
|
|
132
|
+
};
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
static echo() {
|
|
136
|
+
return new _MockProvider((messages) => {
|
|
137
|
+
const lastUser = [...messages].reverse().find((m) => m.role === "user");
|
|
138
|
+
return {
|
|
139
|
+
content: lastUser?.content ?? "",
|
|
140
|
+
usage: { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },
|
|
141
|
+
cost: 0
|
|
142
|
+
};
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
static json(schema) {
|
|
146
|
+
return new _MockProvider(() => ({
|
|
147
|
+
content: JSON.stringify(generateFromSchema(schema)),
|
|
148
|
+
usage: { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },
|
|
149
|
+
cost: 0
|
|
150
|
+
}));
|
|
151
|
+
}
|
|
152
|
+
static replay(source) {
|
|
153
|
+
const data = typeof source === "string" ? JSON.parse((0, import_node_fs.readFileSync)(source, "utf-8")) : source;
|
|
154
|
+
return new _MockProvider((_messages, callIndex) => {
|
|
155
|
+
if (callIndex >= data.length) {
|
|
156
|
+
throw new Error(`MockProvider.replay: no recorded response for call index ${callIndex}`);
|
|
157
|
+
}
|
|
158
|
+
return data[callIndex];
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
static fn(handler) {
|
|
162
|
+
return new _MockProvider((messages, callIndex) => ({
|
|
163
|
+
...handler(messages, callIndex),
|
|
164
|
+
usage: { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },
|
|
165
|
+
cost: 0
|
|
166
|
+
}));
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
// src/mock-tool.ts
|
|
171
|
+
var MockTool = class _MockTool {
|
|
172
|
+
constructor(name, handler) {
|
|
173
|
+
this.name = name;
|
|
174
|
+
this.handler = handler;
|
|
175
|
+
}
|
|
176
|
+
_calls = [];
|
|
177
|
+
get calls() {
|
|
178
|
+
return this._calls;
|
|
179
|
+
}
|
|
180
|
+
async execute(input) {
|
|
181
|
+
this._calls.push({ input });
|
|
182
|
+
return this.handler(input);
|
|
183
|
+
}
|
|
184
|
+
static create(name, handler) {
|
|
185
|
+
return new _MockTool(name, handler);
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
// src/test-runtime.ts
|
|
190
|
+
var import_axl = require("@axlsdk/axl");
|
|
191
|
+
function generateExecutionId() {
|
|
192
|
+
return `test-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
193
|
+
}
|
|
194
|
+
var AxlTestRuntime = class {
|
|
195
|
+
workflows = /* @__PURE__ */ new Map();
|
|
196
|
+
mockProviders = /* @__PURE__ */ new Map();
|
|
197
|
+
mockToolMap = /* @__PURE__ */ new Map();
|
|
198
|
+
_toolCalls = [];
|
|
199
|
+
_agentCalls = [];
|
|
200
|
+
_steps = [];
|
|
201
|
+
_traceLog = [];
|
|
202
|
+
_totalCost = 0;
|
|
203
|
+
_stepCounter = 0;
|
|
204
|
+
recordPath;
|
|
205
|
+
recorded = [];
|
|
206
|
+
_executionId = generateExecutionId();
|
|
207
|
+
_humanDecisionHandler;
|
|
208
|
+
constructor(options) {
|
|
209
|
+
this.recordPath = options?.record;
|
|
210
|
+
this._humanDecisionHandler = options?.humanDecisions;
|
|
211
|
+
}
|
|
212
|
+
register(workflow) {
|
|
213
|
+
this.workflows.set(workflow.name, workflow);
|
|
214
|
+
}
|
|
215
|
+
mockProvider(name, provider) {
|
|
216
|
+
this.mockProviders.set(name, provider);
|
|
217
|
+
}
|
|
218
|
+
mockTool(name, handler) {
|
|
219
|
+
this.mockToolMap.set(name, MockTool.create(name, handler));
|
|
220
|
+
}
|
|
221
|
+
async execute(workflowName, input, options) {
|
|
222
|
+
const workflow = this.workflows.get(workflowName);
|
|
223
|
+
if (!workflow) throw new Error(`Workflow "${workflowName}" not registered`);
|
|
224
|
+
this._toolCalls = [];
|
|
225
|
+
this._agentCalls = [];
|
|
226
|
+
this._steps = [];
|
|
227
|
+
this._traceLog = [];
|
|
228
|
+
this._totalCost = 0;
|
|
229
|
+
this._stepCounter = 0;
|
|
230
|
+
this.recorded = [];
|
|
231
|
+
this._executionId = generateExecutionId();
|
|
232
|
+
const validated = workflow.inputSchema.parse(input);
|
|
233
|
+
const registry = new import_axl.ProviderRegistry();
|
|
234
|
+
registry.clearFactories();
|
|
235
|
+
for (const [name, provider] of this.mockProviders) {
|
|
236
|
+
registry.registerInstance(name, provider);
|
|
237
|
+
}
|
|
238
|
+
const defaultProvider = this.mockProviders.get("default");
|
|
239
|
+
if (defaultProvider) {
|
|
240
|
+
registry.setFallback(defaultProvider);
|
|
241
|
+
} else if (this.mockProviders.size === 1) {
|
|
242
|
+
const onlyProvider = this.mockProviders.values().next().value;
|
|
243
|
+
registry.setFallback(onlyProvider);
|
|
244
|
+
}
|
|
245
|
+
const toolOverrides = /* @__PURE__ */ new Map();
|
|
246
|
+
for (const [name, mockTool] of this.mockToolMap) {
|
|
247
|
+
toolOverrides.set(name, async (args) => {
|
|
248
|
+
return mockTool.execute(args);
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
this._recordStep("workflow_start", { workflow: workflowName, input: validated });
|
|
252
|
+
this._pushTrace({
|
|
253
|
+
type: "workflow_start",
|
|
254
|
+
workflow: workflowName,
|
|
255
|
+
data: { input: validated }
|
|
256
|
+
});
|
|
257
|
+
const init = {
|
|
258
|
+
input: validated,
|
|
259
|
+
executionId: this._executionId,
|
|
260
|
+
metadata: options?.metadata,
|
|
261
|
+
config: {},
|
|
262
|
+
providerRegistry: registry,
|
|
263
|
+
stateStore: new import_axl.MemoryStore(),
|
|
264
|
+
toolOverrides: toolOverrides.size > 0 ? toolOverrides : void 0,
|
|
265
|
+
awaitHumanHandler: this._humanDecisionHandler ? (opts) => this._humanDecisionHandler(opts) : void 0,
|
|
266
|
+
onTrace: (event) => {
|
|
267
|
+
this._traceLog.push(event);
|
|
268
|
+
this._stepCounter++;
|
|
269
|
+
this._steps.push({ step: this._stepCounter, type: event.type, data: event.data });
|
|
270
|
+
if (event.type === "tool_call" && event.data) {
|
|
271
|
+
const { args, result: result2 } = event.data;
|
|
272
|
+
this._toolCalls.push({ name: event.tool, args, result: result2 });
|
|
273
|
+
}
|
|
274
|
+
if (event.cost) {
|
|
275
|
+
this._totalCost += event.cost;
|
|
276
|
+
}
|
|
277
|
+
if (event.type === "agent_call" && event.data) {
|
|
278
|
+
const { response } = event.data;
|
|
279
|
+
if (response !== void 0) {
|
|
280
|
+
this.recorded.push({
|
|
281
|
+
content: response,
|
|
282
|
+
usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },
|
|
283
|
+
cost: event.cost ?? 0
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
onAgentCallComplete: (call) => {
|
|
289
|
+
this._agentCalls.push({
|
|
290
|
+
agent: call.agent,
|
|
291
|
+
prompt: call.prompt,
|
|
292
|
+
response: call.response,
|
|
293
|
+
model: call.model,
|
|
294
|
+
cost: call.cost,
|
|
295
|
+
duration: call.duration,
|
|
296
|
+
promptVersion: call.promptVersion
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
const ctx = new import_axl.WorkflowContext(init);
|
|
301
|
+
const result = await workflow.handler(ctx);
|
|
302
|
+
if (workflow.outputSchema) workflow.outputSchema.parse(result);
|
|
303
|
+
this._recordStep("workflow_end", { workflow: workflowName, result });
|
|
304
|
+
this._pushTrace({
|
|
305
|
+
type: "workflow_end",
|
|
306
|
+
workflow: workflowName,
|
|
307
|
+
data: { result }
|
|
308
|
+
});
|
|
309
|
+
if (this.recordPath) await this._writeRecording();
|
|
310
|
+
return result;
|
|
311
|
+
}
|
|
312
|
+
toolCalls(name) {
|
|
313
|
+
return name ? this._toolCalls.filter((c) => c.name === name) : this._toolCalls;
|
|
314
|
+
}
|
|
315
|
+
agentCalls(name) {
|
|
316
|
+
return name ? this._agentCalls.filter((c) => c.agent === name) : this._agentCalls;
|
|
317
|
+
}
|
|
318
|
+
totalCost() {
|
|
319
|
+
return this._totalCost;
|
|
320
|
+
}
|
|
321
|
+
steps() {
|
|
322
|
+
return this._steps;
|
|
323
|
+
}
|
|
324
|
+
traceLog() {
|
|
325
|
+
return this._traceLog;
|
|
326
|
+
}
|
|
327
|
+
_recordStep(type, data) {
|
|
328
|
+
this._stepCounter++;
|
|
329
|
+
this._steps.push({ step: this._stepCounter, type, data });
|
|
330
|
+
}
|
|
331
|
+
_pushTrace(partial) {
|
|
332
|
+
this._traceLog.push({
|
|
333
|
+
executionId: this._executionId,
|
|
334
|
+
step: this._stepCounter,
|
|
335
|
+
timestamp: Date.now(),
|
|
336
|
+
...partial
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
async _writeRecording() {
|
|
340
|
+
if (!this.recordPath) return;
|
|
341
|
+
const { writeFile, mkdir } = await import("fs/promises");
|
|
342
|
+
const { dirname } = await import("path");
|
|
343
|
+
await mkdir(dirname(this.recordPath), { recursive: true });
|
|
344
|
+
await writeFile(this.recordPath, JSON.stringify(this.recorded, null, 2), "utf8");
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
348
|
+
0 && (module.exports = {
|
|
349
|
+
AxlTestRuntime,
|
|
350
|
+
MockProvider,
|
|
351
|
+
MockTool
|
|
352
|
+
});
|
|
353
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/mock-provider.ts","../src/mock-tool.ts","../src/test-runtime.ts"],"sourcesContent":["export { AxlTestRuntime } from './test-runtime.js';\nexport type { RecordedToolCall, RecordedAgentCall, RecordedStep } from './test-runtime.js';\nexport { MockProvider } from './mock-provider.js';\nexport { MockTool } from './mock-tool.js';\n","import { readFileSync } from 'node:fs';\nimport type { ChatMessage, ToolCallMessage, ProviderResponse, StreamChunk, Provider } from '@axlsdk/axl';\n\nfunction randomAlphanumeric(length: number): string {\n const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';\n let result = '';\n for (let i = 0; i < length; i++) {\n result += chars[Math.floor(Math.random() * chars.length)];\n }\n return result;\n}\n\nfunction generateFromSchema(schema: any): unknown {\n const def = schema?._def;\n if (!def) return {};\n switch (def.typeName) {\n case 'ZodString': {\n const length = 8 + Math.floor(Math.random() * 13); // 8-20 chars\n return randomAlphanumeric(length);\n }\n case 'ZodNumber': {\n const checks: any[] = def.checks ?? [];\n let min = 0;\n let max = 100;\n for (const check of checks) {\n if (check.kind === 'min') min = check.value;\n if (check.kind === 'max') max = check.value;\n }\n return min + Math.random() * (max - min);\n }\n case 'ZodBoolean':\n return Math.random() < 0.5;\n case 'ZodArray': {\n const count = 1 + Math.floor(Math.random() * 3); // 1-3 items\n const items: unknown[] = [];\n for (let i = 0; i < count; i++) {\n items.push(generateFromSchema(def.type));\n }\n return items;\n }\n case 'ZodObject': {\n const shape = def.shape?.() ?? {};\n const obj: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(shape)) {\n obj[key] = generateFromSchema(value);\n }\n return obj;\n }\n case 'ZodOptional': {\n if (Math.random() < 0.5) return undefined;\n return generateFromSchema(def.innerType);\n }\n case 'ZodDefault':\n return def.defaultValue();\n case 'ZodEnum': {\n const values: unknown[] = def.values ?? [];\n if (values.length === 0) return '';\n return values[Math.floor(Math.random() * values.length)];\n }\n case 'ZodNullable':\n return null;\n default:\n return {};\n }\n}\n\nexport class MockProvider implements Provider {\n readonly name = 'mock';\n private _calls: { messages: ChatMessage[]; options: any }[] = [];\n\n private constructor(\n private responseFn: (messages: ChatMessage[], callIndex: number) => ProviderResponse,\n ) {}\n\n get calls() {\n return this._calls;\n }\n\n async chat(messages: ChatMessage[], options: any): Promise<ProviderResponse> {\n this._calls.push({ messages, options });\n return this.responseFn(messages, this._calls.length - 1);\n }\n\n async *stream(messages: ChatMessage[], options: any): AsyncGenerator<StreamChunk> {\n const response = await this.chat(messages, options);\n yield { type: 'text_delta', content: response.content };\n yield { type: 'done', usage: response.usage };\n }\n\n static sequence(\n responses: Array<{ content: string; tool_calls?: ToolCallMessage[] }>,\n ): MockProvider {\n return new MockProvider((_messages, callIndex) => {\n if (callIndex >= responses.length) {\n throw new Error(\n `MockProvider.sequence: no response for call index ${callIndex}. Only ${responses.length} responses defined.`,\n );\n }\n return {\n content: responses[callIndex].content,\n tool_calls: responses[callIndex].tool_calls,\n usage: { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },\n cost: 0,\n };\n });\n }\n\n static echo(): MockProvider {\n return new MockProvider((messages) => {\n const lastUser = [...messages].reverse().find((m) => m.role === 'user');\n return {\n content: lastUser?.content ?? '',\n usage: { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },\n cost: 0,\n };\n });\n }\n\n static json(schema: any): MockProvider {\n return new MockProvider(() => ({\n content: JSON.stringify(generateFromSchema(schema)),\n usage: { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },\n cost: 0,\n }));\n }\n\n static replay(source: string | ProviderResponse[]): MockProvider {\n const data =\n typeof source === 'string'\n ? (JSON.parse(readFileSync(source, 'utf-8')) as ProviderResponse[])\n : source;\n return new MockProvider((_messages, callIndex) => {\n if (callIndex >= data.length) {\n throw new Error(`MockProvider.replay: no recorded response for call index ${callIndex}`);\n }\n return data[callIndex];\n });\n }\n\n static fn(\n handler: (\n messages: ChatMessage[],\n callIndex: number,\n ) => { content: string; tool_calls?: ToolCallMessage[] },\n ): MockProvider {\n return new MockProvider((messages, callIndex) => ({\n ...handler(messages, callIndex),\n usage: { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },\n cost: 0,\n }));\n }\n}\n","export class MockTool {\n private _calls: { input: unknown }[] = [];\n\n constructor(\n readonly name: string,\n private handler: (input: any) => unknown | Promise<unknown>,\n ) {}\n\n get calls() {\n return this._calls;\n }\n\n async execute(input: unknown): Promise<unknown> {\n this._calls.push({ input });\n return this.handler(input);\n }\n\n static create(name: string, handler: (input: any) => unknown | Promise<unknown>): MockTool {\n return new MockTool(name, handler);\n }\n}\n","import { MockProvider } from './mock-provider.js';\nimport { MockTool } from './mock-tool.js';\nimport type { TraceEvent, AwaitHumanOptions, HumanDecision, ProviderResponse } from '@axlsdk/axl';\nimport { WorkflowContext, MemoryStore, ProviderRegistry } from '@axlsdk/axl';\nimport type { WorkflowContextInit } from '@axlsdk/axl';\n\ninterface WorkflowLike {\n readonly name: string;\n readonly inputSchema: any;\n readonly outputSchema: any | undefined;\n readonly handler: (ctx: any) => Promise<any>;\n}\n\nexport type RecordedToolCall = { name: string; args: unknown; result: unknown };\nexport type RecordedAgentCall = {\n agent: string;\n prompt: string;\n response: string;\n model?: string;\n cost?: number;\n duration?: number;\n promptVersion?: string;\n};\nexport type RecordedStep = { step: number; type: string; data: unknown };\n\nfunction generateExecutionId(): string {\n return `test-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\nexport type AxlTestRuntimeOptions = {\n record?: string;\n humanDecisions?: (options: AwaitHumanOptions) => HumanDecision;\n};\n\nexport class AxlTestRuntime {\n private workflows = new Map<string, WorkflowLike>();\n private mockProviders = new Map<string, MockProvider>();\n private mockToolMap = new Map<string, MockTool>();\n private _toolCalls: RecordedToolCall[] = [];\n private _agentCalls: RecordedAgentCall[] = [];\n private _steps: RecordedStep[] = [];\n private _traceLog: TraceEvent[] = [];\n private _totalCost = 0;\n private _stepCounter = 0;\n private recordPath?: string;\n private recorded: ProviderResponse[] = [];\n private _executionId: string = generateExecutionId();\n private _humanDecisionHandler?: (options: AwaitHumanOptions) => HumanDecision;\n\n constructor(options?: AxlTestRuntimeOptions) {\n this.recordPath = options?.record;\n this._humanDecisionHandler = options?.humanDecisions;\n }\n\n register(workflow: WorkflowLike): void {\n this.workflows.set(workflow.name, workflow);\n }\n\n mockProvider(name: string, provider: MockProvider): void {\n this.mockProviders.set(name, provider);\n }\n\n mockTool(name: string, handler: (input: any) => unknown | Promise<unknown>): void {\n this.mockToolMap.set(name, MockTool.create(name, handler));\n }\n\n async execute(\n workflowName: string,\n input: unknown,\n options?: { metadata?: Record<string, unknown> },\n ): Promise<unknown> {\n const workflow = this.workflows.get(workflowName);\n if (!workflow) throw new Error(`Workflow \"${workflowName}\" not registered`);\n\n // Reset state between executions\n this._toolCalls = [];\n this._agentCalls = [];\n this._steps = [];\n this._traceLog = [];\n this._totalCost = 0;\n this._stepCounter = 0;\n this.recorded = [];\n this._executionId = generateExecutionId();\n\n const validated = workflow.inputSchema.parse(input);\n\n // Build ProviderRegistry from registered mock providers\n const registry = new ProviderRegistry();\n registry.clearFactories(); // Remove built-in factories; only use explicit mocks\n for (const [name, provider] of this.mockProviders) {\n registry.registerInstance(name, provider as any);\n }\n // Fallback resolution: 'default' key or single-provider\n const defaultProvider = this.mockProviders.get('default');\n if (defaultProvider) {\n registry.setFallback(defaultProvider as any);\n } else if (this.mockProviders.size === 1) {\n const onlyProvider = this.mockProviders.values().next().value!;\n registry.setFallback(onlyProvider as any);\n }\n\n // Build toolOverrides from mockToolMap\n const toolOverrides = new Map<string, (args: unknown) => Promise<unknown>>();\n for (const [name, mockTool] of this.mockToolMap) {\n toolOverrides.set(name, async (args: unknown) => {\n return mockTool.execute(args);\n });\n }\n\n // Record steps and traces via callbacks\n this._recordStep('workflow_start', { workflow: workflowName, input: validated });\n this._pushTrace({\n type: 'workflow_start',\n workflow: workflowName,\n data: { input: validated },\n });\n\n const init: WorkflowContextInit = {\n input: validated,\n executionId: this._executionId,\n metadata: options?.metadata,\n config: {},\n providerRegistry: registry,\n stateStore: new MemoryStore(),\n toolOverrides: toolOverrides.size > 0 ? toolOverrides : undefined,\n awaitHumanHandler: this._humanDecisionHandler\n ? (opts: AwaitHumanOptions) => this._humanDecisionHandler!(opts)\n : undefined,\n onTrace: (event: TraceEvent) => {\n this._traceLog.push(event);\n this._stepCounter++;\n this._steps.push({ step: this._stepCounter, type: event.type, data: event.data });\n\n if (event.type === 'tool_call' && event.data) {\n const { args, result } = event.data as { args: unknown; result: unknown };\n this._toolCalls.push({ name: event.tool!, args, result });\n }\n\n if (event.cost) {\n this._totalCost += event.cost;\n }\n\n // Track provider responses for recording\n if (event.type === 'agent_call' && event.data) {\n const { response } = event.data as { prompt?: string; response?: string };\n if (response !== undefined) {\n this.recorded.push({\n content: response,\n usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },\n cost: event.cost ?? 0,\n });\n }\n }\n },\n onAgentCallComplete: (call) => {\n this._agentCalls.push({\n agent: call.agent,\n prompt: call.prompt,\n response: call.response,\n model: call.model,\n cost: call.cost,\n duration: call.duration,\n promptVersion: call.promptVersion,\n });\n },\n };\n\n const ctx = new WorkflowContext(init);\n const result = await workflow.handler(ctx);\n\n // Validate output schema if defined\n if (workflow.outputSchema) workflow.outputSchema.parse(result);\n\n this._recordStep('workflow_end', { workflow: workflowName, result });\n this._pushTrace({\n type: 'workflow_end',\n workflow: workflowName,\n data: { result },\n });\n\n if (this.recordPath) await this._writeRecording();\n return result;\n }\n\n toolCalls(name?: string): RecordedToolCall[] {\n return name ? this._toolCalls.filter((c) => c.name === name) : this._toolCalls;\n }\n\n agentCalls(name?: string): RecordedAgentCall[] {\n return name ? this._agentCalls.filter((c) => c.agent === name) : this._agentCalls;\n }\n\n totalCost(): number {\n return this._totalCost;\n }\n steps(): RecordedStep[] {\n return this._steps;\n }\n traceLog(): TraceEvent[] {\n return this._traceLog;\n }\n\n private _recordStep(type: string, data: unknown): void {\n this._stepCounter++;\n this._steps.push({ step: this._stepCounter, type, data });\n }\n\n private _pushTrace(partial: Omit<TraceEvent, 'executionId' | 'step' | 'timestamp'>): void {\n this._traceLog.push({\n executionId: this._executionId,\n step: this._stepCounter,\n timestamp: Date.now(),\n ...partial,\n });\n }\n\n private async _writeRecording(): Promise<void> {\n if (!this.recordPath) return;\n const { writeFile, mkdir } = await import('node:fs/promises');\n const { dirname } = await import('node:path');\n await mkdir(dirname(this.recordPath), { recursive: true });\n await writeFile(this.recordPath, JSON.stringify(this.recorded, null, 2), 'utf8');\n }\n}\n"],"mappings":"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","names":["result"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { Provider, ChatMessage, ProviderResponse, StreamChunk, ToolCallMessage, AwaitHumanOptions, HumanDecision, TraceEvent } from '@axlsdk/axl';
|
|
2
|
+
|
|
3
|
+
declare class MockProvider implements Provider {
|
|
4
|
+
private responseFn;
|
|
5
|
+
readonly name = "mock";
|
|
6
|
+
private _calls;
|
|
7
|
+
private constructor();
|
|
8
|
+
get calls(): {
|
|
9
|
+
messages: ChatMessage[];
|
|
10
|
+
options: any;
|
|
11
|
+
}[];
|
|
12
|
+
chat(messages: ChatMessage[], options: any): Promise<ProviderResponse>;
|
|
13
|
+
stream(messages: ChatMessage[], options: any): AsyncGenerator<StreamChunk>;
|
|
14
|
+
static sequence(responses: Array<{
|
|
15
|
+
content: string;
|
|
16
|
+
tool_calls?: ToolCallMessage[];
|
|
17
|
+
}>): MockProvider;
|
|
18
|
+
static echo(): MockProvider;
|
|
19
|
+
static json(schema: any): MockProvider;
|
|
20
|
+
static replay(source: string | ProviderResponse[]): MockProvider;
|
|
21
|
+
static fn(handler: (messages: ChatMessage[], callIndex: number) => {
|
|
22
|
+
content: string;
|
|
23
|
+
tool_calls?: ToolCallMessage[];
|
|
24
|
+
}): MockProvider;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface WorkflowLike {
|
|
28
|
+
readonly name: string;
|
|
29
|
+
readonly inputSchema: any;
|
|
30
|
+
readonly outputSchema: any | undefined;
|
|
31
|
+
readonly handler: (ctx: any) => Promise<any>;
|
|
32
|
+
}
|
|
33
|
+
type RecordedToolCall = {
|
|
34
|
+
name: string;
|
|
35
|
+
args: unknown;
|
|
36
|
+
result: unknown;
|
|
37
|
+
};
|
|
38
|
+
type RecordedAgentCall = {
|
|
39
|
+
agent: string;
|
|
40
|
+
prompt: string;
|
|
41
|
+
response: string;
|
|
42
|
+
model?: string;
|
|
43
|
+
cost?: number;
|
|
44
|
+
duration?: number;
|
|
45
|
+
promptVersion?: string;
|
|
46
|
+
};
|
|
47
|
+
type RecordedStep = {
|
|
48
|
+
step: number;
|
|
49
|
+
type: string;
|
|
50
|
+
data: unknown;
|
|
51
|
+
};
|
|
52
|
+
type AxlTestRuntimeOptions = {
|
|
53
|
+
record?: string;
|
|
54
|
+
humanDecisions?: (options: AwaitHumanOptions) => HumanDecision;
|
|
55
|
+
};
|
|
56
|
+
declare class AxlTestRuntime {
|
|
57
|
+
private workflows;
|
|
58
|
+
private mockProviders;
|
|
59
|
+
private mockToolMap;
|
|
60
|
+
private _toolCalls;
|
|
61
|
+
private _agentCalls;
|
|
62
|
+
private _steps;
|
|
63
|
+
private _traceLog;
|
|
64
|
+
private _totalCost;
|
|
65
|
+
private _stepCounter;
|
|
66
|
+
private recordPath?;
|
|
67
|
+
private recorded;
|
|
68
|
+
private _executionId;
|
|
69
|
+
private _humanDecisionHandler?;
|
|
70
|
+
constructor(options?: AxlTestRuntimeOptions);
|
|
71
|
+
register(workflow: WorkflowLike): void;
|
|
72
|
+
mockProvider(name: string, provider: MockProvider): void;
|
|
73
|
+
mockTool(name: string, handler: (input: any) => unknown | Promise<unknown>): void;
|
|
74
|
+
execute(workflowName: string, input: unknown, options?: {
|
|
75
|
+
metadata?: Record<string, unknown>;
|
|
76
|
+
}): Promise<unknown>;
|
|
77
|
+
toolCalls(name?: string): RecordedToolCall[];
|
|
78
|
+
agentCalls(name?: string): RecordedAgentCall[];
|
|
79
|
+
totalCost(): number;
|
|
80
|
+
steps(): RecordedStep[];
|
|
81
|
+
traceLog(): TraceEvent[];
|
|
82
|
+
private _recordStep;
|
|
83
|
+
private _pushTrace;
|
|
84
|
+
private _writeRecording;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
declare class MockTool {
|
|
88
|
+
readonly name: string;
|
|
89
|
+
private handler;
|
|
90
|
+
private _calls;
|
|
91
|
+
constructor(name: string, handler: (input: any) => unknown | Promise<unknown>);
|
|
92
|
+
get calls(): {
|
|
93
|
+
input: unknown;
|
|
94
|
+
}[];
|
|
95
|
+
execute(input: unknown): Promise<unknown>;
|
|
96
|
+
static create(name: string, handler: (input: any) => unknown | Promise<unknown>): MockTool;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export { AxlTestRuntime, MockProvider, MockTool, type RecordedAgentCall, type RecordedStep, type RecordedToolCall };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { Provider, ChatMessage, ProviderResponse, StreamChunk, ToolCallMessage, AwaitHumanOptions, HumanDecision, TraceEvent } from '@axlsdk/axl';
|
|
2
|
+
|
|
3
|
+
declare class MockProvider implements Provider {
|
|
4
|
+
private responseFn;
|
|
5
|
+
readonly name = "mock";
|
|
6
|
+
private _calls;
|
|
7
|
+
private constructor();
|
|
8
|
+
get calls(): {
|
|
9
|
+
messages: ChatMessage[];
|
|
10
|
+
options: any;
|
|
11
|
+
}[];
|
|
12
|
+
chat(messages: ChatMessage[], options: any): Promise<ProviderResponse>;
|
|
13
|
+
stream(messages: ChatMessage[], options: any): AsyncGenerator<StreamChunk>;
|
|
14
|
+
static sequence(responses: Array<{
|
|
15
|
+
content: string;
|
|
16
|
+
tool_calls?: ToolCallMessage[];
|
|
17
|
+
}>): MockProvider;
|
|
18
|
+
static echo(): MockProvider;
|
|
19
|
+
static json(schema: any): MockProvider;
|
|
20
|
+
static replay(source: string | ProviderResponse[]): MockProvider;
|
|
21
|
+
static fn(handler: (messages: ChatMessage[], callIndex: number) => {
|
|
22
|
+
content: string;
|
|
23
|
+
tool_calls?: ToolCallMessage[];
|
|
24
|
+
}): MockProvider;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface WorkflowLike {
|
|
28
|
+
readonly name: string;
|
|
29
|
+
readonly inputSchema: any;
|
|
30
|
+
readonly outputSchema: any | undefined;
|
|
31
|
+
readonly handler: (ctx: any) => Promise<any>;
|
|
32
|
+
}
|
|
33
|
+
type RecordedToolCall = {
|
|
34
|
+
name: string;
|
|
35
|
+
args: unknown;
|
|
36
|
+
result: unknown;
|
|
37
|
+
};
|
|
38
|
+
type RecordedAgentCall = {
|
|
39
|
+
agent: string;
|
|
40
|
+
prompt: string;
|
|
41
|
+
response: string;
|
|
42
|
+
model?: string;
|
|
43
|
+
cost?: number;
|
|
44
|
+
duration?: number;
|
|
45
|
+
promptVersion?: string;
|
|
46
|
+
};
|
|
47
|
+
type RecordedStep = {
|
|
48
|
+
step: number;
|
|
49
|
+
type: string;
|
|
50
|
+
data: unknown;
|
|
51
|
+
};
|
|
52
|
+
type AxlTestRuntimeOptions = {
|
|
53
|
+
record?: string;
|
|
54
|
+
humanDecisions?: (options: AwaitHumanOptions) => HumanDecision;
|
|
55
|
+
};
|
|
56
|
+
declare class AxlTestRuntime {
|
|
57
|
+
private workflows;
|
|
58
|
+
private mockProviders;
|
|
59
|
+
private mockToolMap;
|
|
60
|
+
private _toolCalls;
|
|
61
|
+
private _agentCalls;
|
|
62
|
+
private _steps;
|
|
63
|
+
private _traceLog;
|
|
64
|
+
private _totalCost;
|
|
65
|
+
private _stepCounter;
|
|
66
|
+
private recordPath?;
|
|
67
|
+
private recorded;
|
|
68
|
+
private _executionId;
|
|
69
|
+
private _humanDecisionHandler?;
|
|
70
|
+
constructor(options?: AxlTestRuntimeOptions);
|
|
71
|
+
register(workflow: WorkflowLike): void;
|
|
72
|
+
mockProvider(name: string, provider: MockProvider): void;
|
|
73
|
+
mockTool(name: string, handler: (input: any) => unknown | Promise<unknown>): void;
|
|
74
|
+
execute(workflowName: string, input: unknown, options?: {
|
|
75
|
+
metadata?: Record<string, unknown>;
|
|
76
|
+
}): Promise<unknown>;
|
|
77
|
+
toolCalls(name?: string): RecordedToolCall[];
|
|
78
|
+
agentCalls(name?: string): RecordedAgentCall[];
|
|
79
|
+
totalCost(): number;
|
|
80
|
+
steps(): RecordedStep[];
|
|
81
|
+
traceLog(): TraceEvent[];
|
|
82
|
+
private _recordStep;
|
|
83
|
+
private _pushTrace;
|
|
84
|
+
private _writeRecording;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
declare class MockTool {
|
|
88
|
+
readonly name: string;
|
|
89
|
+
private handler;
|
|
90
|
+
private _calls;
|
|
91
|
+
constructor(name: string, handler: (input: any) => unknown | Promise<unknown>);
|
|
92
|
+
get calls(): {
|
|
93
|
+
input: unknown;
|
|
94
|
+
}[];
|
|
95
|
+
execute(input: unknown): Promise<unknown>;
|
|
96
|
+
static create(name: string, handler: (input: any) => unknown | Promise<unknown>): MockTool;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export { AxlTestRuntime, MockProvider, MockTool, type RecordedAgentCall, type RecordedStep, type RecordedToolCall };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
// src/mock-provider.ts
|
|
2
|
+
import { readFileSync } from "fs";
|
|
3
|
+
function randomAlphanumeric(length) {
|
|
4
|
+
const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
|
5
|
+
let result = "";
|
|
6
|
+
for (let i = 0; i < length; i++) {
|
|
7
|
+
result += chars[Math.floor(Math.random() * chars.length)];
|
|
8
|
+
}
|
|
9
|
+
return result;
|
|
10
|
+
}
|
|
11
|
+
function generateFromSchema(schema) {
|
|
12
|
+
const def = schema?._def;
|
|
13
|
+
if (!def) return {};
|
|
14
|
+
switch (def.typeName) {
|
|
15
|
+
case "ZodString": {
|
|
16
|
+
const length = 8 + Math.floor(Math.random() * 13);
|
|
17
|
+
return randomAlphanumeric(length);
|
|
18
|
+
}
|
|
19
|
+
case "ZodNumber": {
|
|
20
|
+
const checks = def.checks ?? [];
|
|
21
|
+
let min = 0;
|
|
22
|
+
let max = 100;
|
|
23
|
+
for (const check of checks) {
|
|
24
|
+
if (check.kind === "min") min = check.value;
|
|
25
|
+
if (check.kind === "max") max = check.value;
|
|
26
|
+
}
|
|
27
|
+
return min + Math.random() * (max - min);
|
|
28
|
+
}
|
|
29
|
+
case "ZodBoolean":
|
|
30
|
+
return Math.random() < 0.5;
|
|
31
|
+
case "ZodArray": {
|
|
32
|
+
const count = 1 + Math.floor(Math.random() * 3);
|
|
33
|
+
const items = [];
|
|
34
|
+
for (let i = 0; i < count; i++) {
|
|
35
|
+
items.push(generateFromSchema(def.type));
|
|
36
|
+
}
|
|
37
|
+
return items;
|
|
38
|
+
}
|
|
39
|
+
case "ZodObject": {
|
|
40
|
+
const shape = def.shape?.() ?? {};
|
|
41
|
+
const obj = {};
|
|
42
|
+
for (const [key, value] of Object.entries(shape)) {
|
|
43
|
+
obj[key] = generateFromSchema(value);
|
|
44
|
+
}
|
|
45
|
+
return obj;
|
|
46
|
+
}
|
|
47
|
+
case "ZodOptional": {
|
|
48
|
+
if (Math.random() < 0.5) return void 0;
|
|
49
|
+
return generateFromSchema(def.innerType);
|
|
50
|
+
}
|
|
51
|
+
case "ZodDefault":
|
|
52
|
+
return def.defaultValue();
|
|
53
|
+
case "ZodEnum": {
|
|
54
|
+
const values = def.values ?? [];
|
|
55
|
+
if (values.length === 0) return "";
|
|
56
|
+
return values[Math.floor(Math.random() * values.length)];
|
|
57
|
+
}
|
|
58
|
+
case "ZodNullable":
|
|
59
|
+
return null;
|
|
60
|
+
default:
|
|
61
|
+
return {};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
var MockProvider = class _MockProvider {
|
|
65
|
+
constructor(responseFn) {
|
|
66
|
+
this.responseFn = responseFn;
|
|
67
|
+
}
|
|
68
|
+
name = "mock";
|
|
69
|
+
_calls = [];
|
|
70
|
+
get calls() {
|
|
71
|
+
return this._calls;
|
|
72
|
+
}
|
|
73
|
+
async chat(messages, options) {
|
|
74
|
+
this._calls.push({ messages, options });
|
|
75
|
+
return this.responseFn(messages, this._calls.length - 1);
|
|
76
|
+
}
|
|
77
|
+
async *stream(messages, options) {
|
|
78
|
+
const response = await this.chat(messages, options);
|
|
79
|
+
yield { type: "text_delta", content: response.content };
|
|
80
|
+
yield { type: "done", usage: response.usage };
|
|
81
|
+
}
|
|
82
|
+
static sequence(responses) {
|
|
83
|
+
return new _MockProvider((_messages, callIndex) => {
|
|
84
|
+
if (callIndex >= responses.length) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
`MockProvider.sequence: no response for call index ${callIndex}. Only ${responses.length} responses defined.`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
content: responses[callIndex].content,
|
|
91
|
+
tool_calls: responses[callIndex].tool_calls,
|
|
92
|
+
usage: { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },
|
|
93
|
+
cost: 0
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
static echo() {
|
|
98
|
+
return new _MockProvider((messages) => {
|
|
99
|
+
const lastUser = [...messages].reverse().find((m) => m.role === "user");
|
|
100
|
+
return {
|
|
101
|
+
content: lastUser?.content ?? "",
|
|
102
|
+
usage: { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },
|
|
103
|
+
cost: 0
|
|
104
|
+
};
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
static json(schema) {
|
|
108
|
+
return new _MockProvider(() => ({
|
|
109
|
+
content: JSON.stringify(generateFromSchema(schema)),
|
|
110
|
+
usage: { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },
|
|
111
|
+
cost: 0
|
|
112
|
+
}));
|
|
113
|
+
}
|
|
114
|
+
static replay(source) {
|
|
115
|
+
const data = typeof source === "string" ? JSON.parse(readFileSync(source, "utf-8")) : source;
|
|
116
|
+
return new _MockProvider((_messages, callIndex) => {
|
|
117
|
+
if (callIndex >= data.length) {
|
|
118
|
+
throw new Error(`MockProvider.replay: no recorded response for call index ${callIndex}`);
|
|
119
|
+
}
|
|
120
|
+
return data[callIndex];
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
static fn(handler) {
|
|
124
|
+
return new _MockProvider((messages, callIndex) => ({
|
|
125
|
+
...handler(messages, callIndex),
|
|
126
|
+
usage: { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },
|
|
127
|
+
cost: 0
|
|
128
|
+
}));
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// src/mock-tool.ts
|
|
133
|
+
var MockTool = class _MockTool {
|
|
134
|
+
constructor(name, handler) {
|
|
135
|
+
this.name = name;
|
|
136
|
+
this.handler = handler;
|
|
137
|
+
}
|
|
138
|
+
_calls = [];
|
|
139
|
+
get calls() {
|
|
140
|
+
return this._calls;
|
|
141
|
+
}
|
|
142
|
+
async execute(input) {
|
|
143
|
+
this._calls.push({ input });
|
|
144
|
+
return this.handler(input);
|
|
145
|
+
}
|
|
146
|
+
static create(name, handler) {
|
|
147
|
+
return new _MockTool(name, handler);
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
// src/test-runtime.ts
|
|
152
|
+
import { WorkflowContext, MemoryStore, ProviderRegistry } from "@axlsdk/axl";
|
|
153
|
+
function generateExecutionId() {
|
|
154
|
+
return `test-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
155
|
+
}
|
|
156
|
+
var AxlTestRuntime = class {
|
|
157
|
+
workflows = /* @__PURE__ */ new Map();
|
|
158
|
+
mockProviders = /* @__PURE__ */ new Map();
|
|
159
|
+
mockToolMap = /* @__PURE__ */ new Map();
|
|
160
|
+
_toolCalls = [];
|
|
161
|
+
_agentCalls = [];
|
|
162
|
+
_steps = [];
|
|
163
|
+
_traceLog = [];
|
|
164
|
+
_totalCost = 0;
|
|
165
|
+
_stepCounter = 0;
|
|
166
|
+
recordPath;
|
|
167
|
+
recorded = [];
|
|
168
|
+
_executionId = generateExecutionId();
|
|
169
|
+
_humanDecisionHandler;
|
|
170
|
+
constructor(options) {
|
|
171
|
+
this.recordPath = options?.record;
|
|
172
|
+
this._humanDecisionHandler = options?.humanDecisions;
|
|
173
|
+
}
|
|
174
|
+
register(workflow) {
|
|
175
|
+
this.workflows.set(workflow.name, workflow);
|
|
176
|
+
}
|
|
177
|
+
mockProvider(name, provider) {
|
|
178
|
+
this.mockProviders.set(name, provider);
|
|
179
|
+
}
|
|
180
|
+
mockTool(name, handler) {
|
|
181
|
+
this.mockToolMap.set(name, MockTool.create(name, handler));
|
|
182
|
+
}
|
|
183
|
+
async execute(workflowName, input, options) {
|
|
184
|
+
const workflow = this.workflows.get(workflowName);
|
|
185
|
+
if (!workflow) throw new Error(`Workflow "${workflowName}" not registered`);
|
|
186
|
+
this._toolCalls = [];
|
|
187
|
+
this._agentCalls = [];
|
|
188
|
+
this._steps = [];
|
|
189
|
+
this._traceLog = [];
|
|
190
|
+
this._totalCost = 0;
|
|
191
|
+
this._stepCounter = 0;
|
|
192
|
+
this.recorded = [];
|
|
193
|
+
this._executionId = generateExecutionId();
|
|
194
|
+
const validated = workflow.inputSchema.parse(input);
|
|
195
|
+
const registry = new ProviderRegistry();
|
|
196
|
+
registry.clearFactories();
|
|
197
|
+
for (const [name, provider] of this.mockProviders) {
|
|
198
|
+
registry.registerInstance(name, provider);
|
|
199
|
+
}
|
|
200
|
+
const defaultProvider = this.mockProviders.get("default");
|
|
201
|
+
if (defaultProvider) {
|
|
202
|
+
registry.setFallback(defaultProvider);
|
|
203
|
+
} else if (this.mockProviders.size === 1) {
|
|
204
|
+
const onlyProvider = this.mockProviders.values().next().value;
|
|
205
|
+
registry.setFallback(onlyProvider);
|
|
206
|
+
}
|
|
207
|
+
const toolOverrides = /* @__PURE__ */ new Map();
|
|
208
|
+
for (const [name, mockTool] of this.mockToolMap) {
|
|
209
|
+
toolOverrides.set(name, async (args) => {
|
|
210
|
+
return mockTool.execute(args);
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
this._recordStep("workflow_start", { workflow: workflowName, input: validated });
|
|
214
|
+
this._pushTrace({
|
|
215
|
+
type: "workflow_start",
|
|
216
|
+
workflow: workflowName,
|
|
217
|
+
data: { input: validated }
|
|
218
|
+
});
|
|
219
|
+
const init = {
|
|
220
|
+
input: validated,
|
|
221
|
+
executionId: this._executionId,
|
|
222
|
+
metadata: options?.metadata,
|
|
223
|
+
config: {},
|
|
224
|
+
providerRegistry: registry,
|
|
225
|
+
stateStore: new MemoryStore(),
|
|
226
|
+
toolOverrides: toolOverrides.size > 0 ? toolOverrides : void 0,
|
|
227
|
+
awaitHumanHandler: this._humanDecisionHandler ? (opts) => this._humanDecisionHandler(opts) : void 0,
|
|
228
|
+
onTrace: (event) => {
|
|
229
|
+
this._traceLog.push(event);
|
|
230
|
+
this._stepCounter++;
|
|
231
|
+
this._steps.push({ step: this._stepCounter, type: event.type, data: event.data });
|
|
232
|
+
if (event.type === "tool_call" && event.data) {
|
|
233
|
+
const { args, result: result2 } = event.data;
|
|
234
|
+
this._toolCalls.push({ name: event.tool, args, result: result2 });
|
|
235
|
+
}
|
|
236
|
+
if (event.cost) {
|
|
237
|
+
this._totalCost += event.cost;
|
|
238
|
+
}
|
|
239
|
+
if (event.type === "agent_call" && event.data) {
|
|
240
|
+
const { response } = event.data;
|
|
241
|
+
if (response !== void 0) {
|
|
242
|
+
this.recorded.push({
|
|
243
|
+
content: response,
|
|
244
|
+
usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },
|
|
245
|
+
cost: event.cost ?? 0
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
onAgentCallComplete: (call) => {
|
|
251
|
+
this._agentCalls.push({
|
|
252
|
+
agent: call.agent,
|
|
253
|
+
prompt: call.prompt,
|
|
254
|
+
response: call.response,
|
|
255
|
+
model: call.model,
|
|
256
|
+
cost: call.cost,
|
|
257
|
+
duration: call.duration,
|
|
258
|
+
promptVersion: call.promptVersion
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
const ctx = new WorkflowContext(init);
|
|
263
|
+
const result = await workflow.handler(ctx);
|
|
264
|
+
if (workflow.outputSchema) workflow.outputSchema.parse(result);
|
|
265
|
+
this._recordStep("workflow_end", { workflow: workflowName, result });
|
|
266
|
+
this._pushTrace({
|
|
267
|
+
type: "workflow_end",
|
|
268
|
+
workflow: workflowName,
|
|
269
|
+
data: { result }
|
|
270
|
+
});
|
|
271
|
+
if (this.recordPath) await this._writeRecording();
|
|
272
|
+
return result;
|
|
273
|
+
}
|
|
274
|
+
toolCalls(name) {
|
|
275
|
+
return name ? this._toolCalls.filter((c) => c.name === name) : this._toolCalls;
|
|
276
|
+
}
|
|
277
|
+
agentCalls(name) {
|
|
278
|
+
return name ? this._agentCalls.filter((c) => c.agent === name) : this._agentCalls;
|
|
279
|
+
}
|
|
280
|
+
totalCost() {
|
|
281
|
+
return this._totalCost;
|
|
282
|
+
}
|
|
283
|
+
steps() {
|
|
284
|
+
return this._steps;
|
|
285
|
+
}
|
|
286
|
+
traceLog() {
|
|
287
|
+
return this._traceLog;
|
|
288
|
+
}
|
|
289
|
+
_recordStep(type, data) {
|
|
290
|
+
this._stepCounter++;
|
|
291
|
+
this._steps.push({ step: this._stepCounter, type, data });
|
|
292
|
+
}
|
|
293
|
+
_pushTrace(partial) {
|
|
294
|
+
this._traceLog.push({
|
|
295
|
+
executionId: this._executionId,
|
|
296
|
+
step: this._stepCounter,
|
|
297
|
+
timestamp: Date.now(),
|
|
298
|
+
...partial
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
async _writeRecording() {
|
|
302
|
+
if (!this.recordPath) return;
|
|
303
|
+
const { writeFile, mkdir } = await import("fs/promises");
|
|
304
|
+
const { dirname } = await import("path");
|
|
305
|
+
await mkdir(dirname(this.recordPath), { recursive: true });
|
|
306
|
+
await writeFile(this.recordPath, JSON.stringify(this.recorded, null, 2), "utf8");
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
export {
|
|
310
|
+
AxlTestRuntime,
|
|
311
|
+
MockProvider,
|
|
312
|
+
MockTool
|
|
313
|
+
};
|
|
314
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/mock-provider.ts","../src/mock-tool.ts","../src/test-runtime.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport type { ChatMessage, ToolCallMessage, ProviderResponse, StreamChunk, Provider } from '@axlsdk/axl';\n\nfunction randomAlphanumeric(length: number): string {\n const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';\n let result = '';\n for (let i = 0; i < length; i++) {\n result += chars[Math.floor(Math.random() * chars.length)];\n }\n return result;\n}\n\nfunction generateFromSchema(schema: any): unknown {\n const def = schema?._def;\n if (!def) return {};\n switch (def.typeName) {\n case 'ZodString': {\n const length = 8 + Math.floor(Math.random() * 13); // 8-20 chars\n return randomAlphanumeric(length);\n }\n case 'ZodNumber': {\n const checks: any[] = def.checks ?? [];\n let min = 0;\n let max = 100;\n for (const check of checks) {\n if (check.kind === 'min') min = check.value;\n if (check.kind === 'max') max = check.value;\n }\n return min + Math.random() * (max - min);\n }\n case 'ZodBoolean':\n return Math.random() < 0.5;\n case 'ZodArray': {\n const count = 1 + Math.floor(Math.random() * 3); // 1-3 items\n const items: unknown[] = [];\n for (let i = 0; i < count; i++) {\n items.push(generateFromSchema(def.type));\n }\n return items;\n }\n case 'ZodObject': {\n const shape = def.shape?.() ?? {};\n const obj: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(shape)) {\n obj[key] = generateFromSchema(value);\n }\n return obj;\n }\n case 'ZodOptional': {\n if (Math.random() < 0.5) return undefined;\n return generateFromSchema(def.innerType);\n }\n case 'ZodDefault':\n return def.defaultValue();\n case 'ZodEnum': {\n const values: unknown[] = def.values ?? [];\n if (values.length === 0) return '';\n return values[Math.floor(Math.random() * values.length)];\n }\n case 'ZodNullable':\n return null;\n default:\n return {};\n }\n}\n\nexport class MockProvider implements Provider {\n readonly name = 'mock';\n private _calls: { messages: ChatMessage[]; options: any }[] = [];\n\n private constructor(\n private responseFn: (messages: ChatMessage[], callIndex: number) => ProviderResponse,\n ) {}\n\n get calls() {\n return this._calls;\n }\n\n async chat(messages: ChatMessage[], options: any): Promise<ProviderResponse> {\n this._calls.push({ messages, options });\n return this.responseFn(messages, this._calls.length - 1);\n }\n\n async *stream(messages: ChatMessage[], options: any): AsyncGenerator<StreamChunk> {\n const response = await this.chat(messages, options);\n yield { type: 'text_delta', content: response.content };\n yield { type: 'done', usage: response.usage };\n }\n\n static sequence(\n responses: Array<{ content: string; tool_calls?: ToolCallMessage[] }>,\n ): MockProvider {\n return new MockProvider((_messages, callIndex) => {\n if (callIndex >= responses.length) {\n throw new Error(\n `MockProvider.sequence: no response for call index ${callIndex}. Only ${responses.length} responses defined.`,\n );\n }\n return {\n content: responses[callIndex].content,\n tool_calls: responses[callIndex].tool_calls,\n usage: { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },\n cost: 0,\n };\n });\n }\n\n static echo(): MockProvider {\n return new MockProvider((messages) => {\n const lastUser = [...messages].reverse().find((m) => m.role === 'user');\n return {\n content: lastUser?.content ?? '',\n usage: { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },\n cost: 0,\n };\n });\n }\n\n static json(schema: any): MockProvider {\n return new MockProvider(() => ({\n content: JSON.stringify(generateFromSchema(schema)),\n usage: { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },\n cost: 0,\n }));\n }\n\n static replay(source: string | ProviderResponse[]): MockProvider {\n const data =\n typeof source === 'string'\n ? (JSON.parse(readFileSync(source, 'utf-8')) as ProviderResponse[])\n : source;\n return new MockProvider((_messages, callIndex) => {\n if (callIndex >= data.length) {\n throw new Error(`MockProvider.replay: no recorded response for call index ${callIndex}`);\n }\n return data[callIndex];\n });\n }\n\n static fn(\n handler: (\n messages: ChatMessage[],\n callIndex: number,\n ) => { content: string; tool_calls?: ToolCallMessage[] },\n ): MockProvider {\n return new MockProvider((messages, callIndex) => ({\n ...handler(messages, callIndex),\n usage: { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },\n cost: 0,\n }));\n }\n}\n","export class MockTool {\n private _calls: { input: unknown }[] = [];\n\n constructor(\n readonly name: string,\n private handler: (input: any) => unknown | Promise<unknown>,\n ) {}\n\n get calls() {\n return this._calls;\n }\n\n async execute(input: unknown): Promise<unknown> {\n this._calls.push({ input });\n return this.handler(input);\n }\n\n static create(name: string, handler: (input: any) => unknown | Promise<unknown>): MockTool {\n return new MockTool(name, handler);\n }\n}\n","import { MockProvider } from './mock-provider.js';\nimport { MockTool } from './mock-tool.js';\nimport type { TraceEvent, AwaitHumanOptions, HumanDecision, ProviderResponse } from '@axlsdk/axl';\nimport { WorkflowContext, MemoryStore, ProviderRegistry } from '@axlsdk/axl';\nimport type { WorkflowContextInit } from '@axlsdk/axl';\n\ninterface WorkflowLike {\n readonly name: string;\n readonly inputSchema: any;\n readonly outputSchema: any | undefined;\n readonly handler: (ctx: any) => Promise<any>;\n}\n\nexport type RecordedToolCall = { name: string; args: unknown; result: unknown };\nexport type RecordedAgentCall = {\n agent: string;\n prompt: string;\n response: string;\n model?: string;\n cost?: number;\n duration?: number;\n promptVersion?: string;\n};\nexport type RecordedStep = { step: number; type: string; data: unknown };\n\nfunction generateExecutionId(): string {\n return `test-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\nexport type AxlTestRuntimeOptions = {\n record?: string;\n humanDecisions?: (options: AwaitHumanOptions) => HumanDecision;\n};\n\nexport class AxlTestRuntime {\n private workflows = new Map<string, WorkflowLike>();\n private mockProviders = new Map<string, MockProvider>();\n private mockToolMap = new Map<string, MockTool>();\n private _toolCalls: RecordedToolCall[] = [];\n private _agentCalls: RecordedAgentCall[] = [];\n private _steps: RecordedStep[] = [];\n private _traceLog: TraceEvent[] = [];\n private _totalCost = 0;\n private _stepCounter = 0;\n private recordPath?: string;\n private recorded: ProviderResponse[] = [];\n private _executionId: string = generateExecutionId();\n private _humanDecisionHandler?: (options: AwaitHumanOptions) => HumanDecision;\n\n constructor(options?: AxlTestRuntimeOptions) {\n this.recordPath = options?.record;\n this._humanDecisionHandler = options?.humanDecisions;\n }\n\n register(workflow: WorkflowLike): void {\n this.workflows.set(workflow.name, workflow);\n }\n\n mockProvider(name: string, provider: MockProvider): void {\n this.mockProviders.set(name, provider);\n }\n\n mockTool(name: string, handler: (input: any) => unknown | Promise<unknown>): void {\n this.mockToolMap.set(name, MockTool.create(name, handler));\n }\n\n async execute(\n workflowName: string,\n input: unknown,\n options?: { metadata?: Record<string, unknown> },\n ): Promise<unknown> {\n const workflow = this.workflows.get(workflowName);\n if (!workflow) throw new Error(`Workflow \"${workflowName}\" not registered`);\n\n // Reset state between executions\n this._toolCalls = [];\n this._agentCalls = [];\n this._steps = [];\n this._traceLog = [];\n this._totalCost = 0;\n this._stepCounter = 0;\n this.recorded = [];\n this._executionId = generateExecutionId();\n\n const validated = workflow.inputSchema.parse(input);\n\n // Build ProviderRegistry from registered mock providers\n const registry = new ProviderRegistry();\n registry.clearFactories(); // Remove built-in factories; only use explicit mocks\n for (const [name, provider] of this.mockProviders) {\n registry.registerInstance(name, provider as any);\n }\n // Fallback resolution: 'default' key or single-provider\n const defaultProvider = this.mockProviders.get('default');\n if (defaultProvider) {\n registry.setFallback(defaultProvider as any);\n } else if (this.mockProviders.size === 1) {\n const onlyProvider = this.mockProviders.values().next().value!;\n registry.setFallback(onlyProvider as any);\n }\n\n // Build toolOverrides from mockToolMap\n const toolOverrides = new Map<string, (args: unknown) => Promise<unknown>>();\n for (const [name, mockTool] of this.mockToolMap) {\n toolOverrides.set(name, async (args: unknown) => {\n return mockTool.execute(args);\n });\n }\n\n // Record steps and traces via callbacks\n this._recordStep('workflow_start', { workflow: workflowName, input: validated });\n this._pushTrace({\n type: 'workflow_start',\n workflow: workflowName,\n data: { input: validated },\n });\n\n const init: WorkflowContextInit = {\n input: validated,\n executionId: this._executionId,\n metadata: options?.metadata,\n config: {},\n providerRegistry: registry,\n stateStore: new MemoryStore(),\n toolOverrides: toolOverrides.size > 0 ? toolOverrides : undefined,\n awaitHumanHandler: this._humanDecisionHandler\n ? (opts: AwaitHumanOptions) => this._humanDecisionHandler!(opts)\n : undefined,\n onTrace: (event: TraceEvent) => {\n this._traceLog.push(event);\n this._stepCounter++;\n this._steps.push({ step: this._stepCounter, type: event.type, data: event.data });\n\n if (event.type === 'tool_call' && event.data) {\n const { args, result } = event.data as { args: unknown; result: unknown };\n this._toolCalls.push({ name: event.tool!, args, result });\n }\n\n if (event.cost) {\n this._totalCost += event.cost;\n }\n\n // Track provider responses for recording\n if (event.type === 'agent_call' && event.data) {\n const { response } = event.data as { prompt?: string; response?: string };\n if (response !== undefined) {\n this.recorded.push({\n content: response,\n usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },\n cost: event.cost ?? 0,\n });\n }\n }\n },\n onAgentCallComplete: (call) => {\n this._agentCalls.push({\n agent: call.agent,\n prompt: call.prompt,\n response: call.response,\n model: call.model,\n cost: call.cost,\n duration: call.duration,\n promptVersion: call.promptVersion,\n });\n },\n };\n\n const ctx = new WorkflowContext(init);\n const result = await workflow.handler(ctx);\n\n // Validate output schema if defined\n if (workflow.outputSchema) workflow.outputSchema.parse(result);\n\n this._recordStep('workflow_end', { workflow: workflowName, result });\n this._pushTrace({\n type: 'workflow_end',\n workflow: workflowName,\n data: { result },\n });\n\n if (this.recordPath) await this._writeRecording();\n return result;\n }\n\n toolCalls(name?: string): RecordedToolCall[] {\n return name ? this._toolCalls.filter((c) => c.name === name) : this._toolCalls;\n }\n\n agentCalls(name?: string): RecordedAgentCall[] {\n return name ? this._agentCalls.filter((c) => c.agent === name) : this._agentCalls;\n }\n\n totalCost(): number {\n return this._totalCost;\n }\n steps(): RecordedStep[] {\n return this._steps;\n }\n traceLog(): TraceEvent[] {\n return this._traceLog;\n }\n\n private _recordStep(type: string, data: unknown): void {\n this._stepCounter++;\n this._steps.push({ step: this._stepCounter, type, data });\n }\n\n private _pushTrace(partial: Omit<TraceEvent, 'executionId' | 'step' | 'timestamp'>): void {\n this._traceLog.push({\n executionId: this._executionId,\n step: this._stepCounter,\n timestamp: Date.now(),\n ...partial,\n });\n }\n\n private async _writeRecording(): Promise<void> {\n if (!this.recordPath) return;\n const { writeFile, mkdir } = await import('node:fs/promises');\n const { dirname } = await import('node:path');\n await mkdir(dirname(this.recordPath), { recursive: true });\n await writeFile(this.recordPath, JSON.stringify(this.recorded, null, 2), 'utf8');\n }\n}\n"],"mappings":"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","names":["result"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@axlsdk/testing",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Testing utilities for Axl agentic workflows",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"require": {
|
|
16
|
+
"types": "./dist/index.d.cts",
|
|
17
|
+
"default": "./dist/index.cjs"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsup",
|
|
26
|
+
"typecheck": "tsc --noEmit",
|
|
27
|
+
"test": "vitest run",
|
|
28
|
+
"test:watch": "vitest watch",
|
|
29
|
+
"lint": "tsc --noEmit"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@axlsdk/axl": "workspace:*"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/node": "^25.2.3",
|
|
36
|
+
"tsup": "^8.3.0",
|
|
37
|
+
"typescript": "^5.7.0",
|
|
38
|
+
"vitest": "^3.0.0",
|
|
39
|
+
"zod": "^3.24.0"
|
|
40
|
+
},
|
|
41
|
+
"keywords": [
|
|
42
|
+
"ai",
|
|
43
|
+
"testing",
|
|
44
|
+
"mock",
|
|
45
|
+
"agents"
|
|
46
|
+
],
|
|
47
|
+
"license": "Apache-2.0",
|
|
48
|
+
"repository": {
|
|
49
|
+
"type": "git",
|
|
50
|
+
"url": "git+https://github.com/axl-sdk/axl.git",
|
|
51
|
+
"directory": "packages/axl-testing"
|
|
52
|
+
},
|
|
53
|
+
"homepage": "https://github.com/axl-sdk/axl#readme",
|
|
54
|
+
"bugs": {
|
|
55
|
+
"url": "https://github.com/axl-sdk/axl/issues"
|
|
56
|
+
},
|
|
57
|
+
"publishConfig": {
|
|
58
|
+
"access": "public"
|
|
59
|
+
},
|
|
60
|
+
"engines": {
|
|
61
|
+
"node": ">=20.0.0"
|
|
62
|
+
}
|
|
63
|
+
}
|