@axlsdk/testing 0.3.0 → 0.4.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 +61 -44
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -18,21 +18,24 @@ Mock LLM provider with multiple response modes:
|
|
|
18
18
|
import { MockProvider } from '@axlsdk/testing';
|
|
19
19
|
|
|
20
20
|
// Sequence mode — return responses in order
|
|
21
|
-
const provider = MockProvider.sequence([
|
|
21
|
+
const provider = MockProvider.sequence([
|
|
22
|
+
{ content: 'Hello!' },
|
|
23
|
+
{ content: 'World!' },
|
|
24
|
+
]);
|
|
22
25
|
|
|
23
26
|
// Echo mode — return the user's prompt back
|
|
24
27
|
const provider = MockProvider.echo();
|
|
25
28
|
|
|
26
|
-
// JSON mode — return
|
|
27
|
-
const provider = MockProvider.json({ answer:
|
|
29
|
+
// JSON mode — return data matching a Zod schema
|
|
30
|
+
const provider = MockProvider.json(z.object({ answer: z.number() }));
|
|
28
31
|
|
|
29
32
|
// Replay mode — replay from a recorded file
|
|
30
33
|
const provider = MockProvider.replay('./fixtures/conversation.json');
|
|
31
34
|
|
|
32
35
|
// Function mode — custom response logic
|
|
33
|
-
const provider = MockProvider.fn((messages,
|
|
36
|
+
const provider = MockProvider.fn((messages, callIndex) => {
|
|
34
37
|
const lastMessage = messages[messages.length - 1];
|
|
35
|
-
return `You said: ${lastMessage.content}
|
|
38
|
+
return { content: `You said: ${lastMessage.content}` };
|
|
36
39
|
});
|
|
37
40
|
```
|
|
38
41
|
|
|
@@ -48,71 +51,73 @@ const provider = MockProvider.sequence([
|
|
|
48
51
|
function: { name: 'calculator', arguments: '{"expression":"2+2"}' },
|
|
49
52
|
}],
|
|
50
53
|
},
|
|
51
|
-
'The answer is 4.',
|
|
54
|
+
{ content: 'The answer is 4.' },
|
|
52
55
|
]);
|
|
53
56
|
```
|
|
54
57
|
|
|
55
58
|
### `MockTool`
|
|
56
59
|
|
|
57
|
-
|
|
60
|
+
Create a mock tool to intercept and record calls:
|
|
58
61
|
|
|
59
62
|
```typescript
|
|
60
63
|
import { MockTool } from '@axlsdk/testing';
|
|
61
|
-
import { calculator } from './tools';
|
|
62
|
-
|
|
63
|
-
const mock = new MockTool(calculator);
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
const mock = MockTool.create('calculator', async ({ expression }) => ({
|
|
66
|
+
result: eval(expression),
|
|
67
|
+
}));
|
|
67
68
|
|
|
68
|
-
//
|
|
69
|
-
console.log(mock.calls); // [{ input: { expression: '2+2' }
|
|
69
|
+
// Inspect calls after execution
|
|
70
|
+
console.log(mock.calls); // [{ input: { expression: '2+2' } }]
|
|
70
71
|
```
|
|
71
72
|
|
|
72
73
|
### `AxlTestRuntime`
|
|
73
74
|
|
|
74
|
-
Test runtime that
|
|
75
|
+
Test runtime that wraps `WorkflowContext` for deterministic testing:
|
|
75
76
|
|
|
76
77
|
```typescript
|
|
77
78
|
import { AxlTestRuntime, MockProvider } from '@axlsdk/testing';
|
|
78
79
|
|
|
79
|
-
const
|
|
80
|
-
|
|
80
|
+
const runtime = new AxlTestRuntime();
|
|
81
|
+
runtime.register(myWorkflow);
|
|
82
|
+
|
|
83
|
+
// Mock the LLM
|
|
84
|
+
runtime.mockProvider('openai', MockProvider.sequence([
|
|
85
|
+
{ content: '42' },
|
|
86
|
+
]));
|
|
87
|
+
|
|
88
|
+
// Mock tools
|
|
89
|
+
runtime.mockTool('calculator', async ({ expression }) => ({ result: 4 }));
|
|
81
90
|
|
|
82
|
-
//
|
|
83
|
-
const result = await runtime.
|
|
84
|
-
expect(result).toBe('42');
|
|
91
|
+
// Execute
|
|
92
|
+
const result = await runtime.execute('my-workflow', { question: 'What is 2+2?' });
|
|
85
93
|
|
|
86
94
|
// Inspect recorded calls
|
|
87
95
|
expect(runtime.agentCalls()).toHaveLength(1);
|
|
88
|
-
expect(runtime.toolCalls()).toHaveLength(
|
|
96
|
+
expect(runtime.toolCalls()).toHaveLength(1);
|
|
89
97
|
expect(runtime.totalCost()).toBe(0);
|
|
98
|
+
```
|
|
90
99
|
|
|
91
|
-
|
|
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' });
|
|
100
|
+
For testing human-in-the-loop flows:
|
|
97
101
|
|
|
98
|
-
|
|
99
|
-
const
|
|
100
|
-
|
|
102
|
+
```typescript
|
|
103
|
+
const runtime = new AxlTestRuntime({
|
|
104
|
+
humanDecisions: (opts) => ({ approved: true }),
|
|
101
105
|
});
|
|
102
|
-
expect(budgeted.budgetExceeded).toBe(false);
|
|
103
106
|
```
|
|
104
107
|
|
|
105
108
|
### Assertions
|
|
106
109
|
|
|
107
110
|
```typescript
|
|
108
|
-
const runtime = new AxlTestRuntime(
|
|
111
|
+
const runtime = new AxlTestRuntime();
|
|
112
|
+
runtime.register(myWorkflow);
|
|
113
|
+
runtime.mockProvider('openai', provider);
|
|
109
114
|
|
|
110
115
|
// After running your workflow:
|
|
111
116
|
runtime.agentCalls(); // All recorded agent invocations
|
|
112
117
|
runtime.toolCalls(); // All recorded tool invocations
|
|
113
118
|
runtime.totalCost(); // Cumulative cost
|
|
114
119
|
runtime.steps(); // All recorded steps (agents + tools)
|
|
115
|
-
runtime.
|
|
120
|
+
runtime.traceLog(); // All trace events
|
|
116
121
|
```
|
|
117
122
|
|
|
118
123
|
## Example: Testing a Workflow
|
|
@@ -120,19 +125,31 @@ runtime.traces(); // All trace events
|
|
|
120
125
|
```typescript
|
|
121
126
|
import { describe, it, expect } from 'vitest';
|
|
122
127
|
import { AxlTestRuntime, MockProvider } from '@axlsdk/testing';
|
|
123
|
-
import {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const runtime = new AxlTestRuntime(
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
128
|
+
import { z } from 'zod';
|
|
129
|
+
import { HandleSupport } from '../workflows/support';
|
|
130
|
+
|
|
131
|
+
describe('HandleSupport workflow', () => {
|
|
132
|
+
it('processes a refund', async () => {
|
|
133
|
+
const runtime = new AxlTestRuntime();
|
|
134
|
+
runtime.register(HandleSupport);
|
|
135
|
+
|
|
136
|
+
runtime.mockProvider('openai', MockProvider.sequence([
|
|
137
|
+
{ content: 'Your refund has been processed.' },
|
|
138
|
+
]));
|
|
139
|
+
|
|
140
|
+
runtime.mockTool('get_order', async ({ orderId }) => ({
|
|
141
|
+
id: orderId, status: 'delivered', amount: 49.99,
|
|
142
|
+
}));
|
|
143
|
+
runtime.mockTool('refund_order', async ({ orderId }) => ({
|
|
144
|
+
success: true,
|
|
145
|
+
}));
|
|
146
|
+
|
|
147
|
+
const result = await runtime.execute('HandleSupport', {
|
|
148
|
+
msg: 'I want a refund for order 123',
|
|
132
149
|
});
|
|
133
150
|
|
|
134
|
-
expect(result).
|
|
135
|
-
expect(runtime.
|
|
151
|
+
expect(result).toContain('refund');
|
|
152
|
+
expect(runtime.toolCalls('refund_order')).toHaveLength(1);
|
|
136
153
|
});
|
|
137
154
|
});
|
|
138
155
|
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axlsdk/testing",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Testing utilities for Axl agentic workflows",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"dist"
|
|
23
23
|
],
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@axlsdk/axl": "0.
|
|
25
|
+
"@axlsdk/axl": "0.4.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/node": "^25.2.3",
|