@copilotkit/aimock 1.13.0 → 1.14.1
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +4 -4
- package/README.md +12 -7
- package/dist/cli.cjs +13 -2
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +13 -2
- package/dist/cli.js.map +1 -1
- package/dist/config-loader.d.cts.map +1 -1
- package/dist/fixture-loader.cjs +131 -29
- package/dist/fixture-loader.cjs.map +1 -1
- package/dist/fixture-loader.d.cts +9 -2
- package/dist/fixture-loader.d.cts.map +1 -1
- package/dist/fixture-loader.d.ts +9 -2
- package/dist/fixture-loader.d.ts.map +1 -1
- package/dist/fixture-loader.js +132 -31
- package/dist/fixture-loader.js.map +1 -1
- package/dist/gemini.cjs +76 -55
- package/dist/gemini.cjs.map +1 -1
- package/dist/gemini.d.cts.map +1 -1
- package/dist/gemini.d.ts.map +1 -1
- package/dist/gemini.js +77 -56
- package/dist/gemini.js.map +1 -1
- package/dist/helpers.cjs +142 -76
- package/dist/helpers.cjs.map +1 -1
- package/dist/helpers.d.cts +14 -4
- package/dist/helpers.d.cts.map +1 -1
- package/dist/helpers.d.ts +14 -4
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +142 -77
- package/dist/helpers.js.map +1 -1
- package/dist/index.cjs +10 -0
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +3 -3
- package/dist/journal.cjs +6 -0
- package/dist/journal.cjs.map +1 -1
- package/dist/journal.d.cts +15 -1
- package/dist/journal.d.cts.map +1 -1
- package/dist/journal.d.ts +15 -1
- package/dist/journal.d.ts.map +1 -1
- package/dist/journal.js +6 -0
- package/dist/journal.js.map +1 -1
- package/dist/llmock.cjs +1 -1
- package/dist/llmock.cjs.map +1 -1
- package/dist/llmock.d.cts +6 -6
- package/dist/llmock.d.cts.map +1 -1
- package/dist/llmock.d.ts +6 -6
- package/dist/llmock.d.ts.map +1 -1
- package/dist/llmock.js +2 -2
- package/dist/llmock.js.map +1 -1
- package/dist/messages.cjs +69 -63
- package/dist/messages.cjs.map +1 -1
- package/dist/messages.d.cts.map +1 -1
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +70 -64
- package/dist/messages.js.map +1 -1
- package/dist/recorder.cjs +1 -1
- package/dist/recorder.cjs.map +1 -1
- package/dist/recorder.js +1 -1
- package/dist/recorder.js.map +1 -1
- package/dist/responses.cjs +66 -57
- package/dist/responses.cjs.map +1 -1
- package/dist/responses.d.cts +3 -3
- package/dist/responses.d.cts.map +1 -1
- package/dist/responses.d.ts +3 -3
- package/dist/responses.d.ts.map +1 -1
- package/dist/responses.js +67 -58
- package/dist/responses.js.map +1 -1
- package/dist/server.cjs +58 -31
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +59 -32
- package/dist/server.js.map +1 -1
- package/dist/stream-collapse.cjs.map +1 -1
- package/dist/stream-collapse.d.cts.map +1 -1
- package/dist/stream-collapse.d.ts.map +1 -1
- package/dist/stream-collapse.js.map +1 -1
- package/dist/types.d.cts +74 -11
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +74 -11
- package/dist/types.d.ts.map +1 -1
- package/dist/vector-types.d.ts.map +1 -1
- package/fixtures/example-multi-turn.json +1 -1
- package/fixtures/example-tool-call.json +1 -1
- package/fixtures/examples/adk/gemini-agent.json +47 -0
- package/fixtures/examples/crewai/multi-agent-crew.json +16 -0
- package/fixtures/examples/langchain/agent-loop.json +27 -0
- package/fixtures/examples/llamaindex/aimock-config.json +62 -0
- package/fixtures/examples/llamaindex/rag-pipeline.json +34 -0
- package/fixtures/examples/mastra/agent-workflow.json +32 -0
- package/fixtures/examples/pydanticai/structured-output.json +15 -0
- package/package.json +2 -1
- package/skills/write-fixtures/SKILL.md +148 -22
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"fixtures": [
|
|
3
|
+
{
|
|
4
|
+
"match": { "userMessage": "plan a trip", "sequenceIndex": 0 },
|
|
5
|
+
"response": {
|
|
6
|
+
"content": "I'll help plan your trip. Let me look up some options."
|
|
7
|
+
}
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"match": { "userMessage": "plan a trip", "sequenceIndex": 1 },
|
|
11
|
+
"response": {
|
|
12
|
+
"toolCalls": [
|
|
13
|
+
{
|
|
14
|
+
"name": "search_flights",
|
|
15
|
+
"arguments": { "origin": "SFO", "dest": "NRT" }
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"match": { "userMessage": "plan a trip", "sequenceIndex": 2 },
|
|
22
|
+
"response": {
|
|
23
|
+
"content": "I found 3 flights from SFO to Tokyo Narita. The best option is..."
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"llm": {
|
|
3
|
+
"fixtures": "./rag-pipeline.json"
|
|
4
|
+
},
|
|
5
|
+
"vector": {
|
|
6
|
+
"collections": [
|
|
7
|
+
{
|
|
8
|
+
"name": "knowledge-base",
|
|
9
|
+
"dimension": 3,
|
|
10
|
+
"vectors": [
|
|
11
|
+
{
|
|
12
|
+
"id": "doc-gravity",
|
|
13
|
+
"values": [0.9, 0.1, 0.05],
|
|
14
|
+
"metadata": {
|
|
15
|
+
"source": "physics.pdf",
|
|
16
|
+
"page": 12,
|
|
17
|
+
"text": "Gravity is a fundamental force of nature that attracts objects with mass toward one another."
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": "doc-orbits",
|
|
22
|
+
"values": [0.75, 0.3, 0.15],
|
|
23
|
+
"metadata": {
|
|
24
|
+
"source": "physics.pdf",
|
|
25
|
+
"page": 45,
|
|
26
|
+
"text": "Orbital mechanics describes the motion of planets and satellites under gravitational influence."
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"id": "doc-tides",
|
|
31
|
+
"values": [0.6, 0.5, 0.2],
|
|
32
|
+
"metadata": {
|
|
33
|
+
"source": "physics.pdf",
|
|
34
|
+
"page": 78,
|
|
35
|
+
"text": "Tidal forces result from the differential gravitational pull of the Moon and Sun on Earth's oceans."
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
],
|
|
39
|
+
"queryResults": [
|
|
40
|
+
{
|
|
41
|
+
"id": "doc-gravity",
|
|
42
|
+
"score": 0.97,
|
|
43
|
+
"metadata": {
|
|
44
|
+
"source": "physics.pdf",
|
|
45
|
+
"page": 12,
|
|
46
|
+
"text": "Gravity is a fundamental force of nature that attracts objects with mass toward one another."
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"id": "doc-orbits",
|
|
51
|
+
"score": 0.82,
|
|
52
|
+
"metadata": {
|
|
53
|
+
"source": "physics.pdf",
|
|
54
|
+
"page": 45,
|
|
55
|
+
"text": "Orbital mechanics describes the motion of planets and satellites under gravitational influence."
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"fixtures": [
|
|
3
|
+
{
|
|
4
|
+
"match": { "userMessage": "What is gravity?" },
|
|
5
|
+
"response": {
|
|
6
|
+
"content": "Based on the retrieved documents, gravity is a fundamental force of nature that attracts objects with mass toward one another. It is described by Newton's law of universal gravitation and Einstein's general theory of relativity."
|
|
7
|
+
}
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"match": { "userMessage": "Summarize the document" },
|
|
11
|
+
"response": {
|
|
12
|
+
"content": "The document covers three main topics: gravitational force, orbital mechanics, and tidal effects. It explains how gravity governs planetary motion and influences ocean tides on Earth."
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"match": { "inputText": "What is gravity?", "endpoint": "embedding" },
|
|
17
|
+
"response": {
|
|
18
|
+
"embedding": [0.9, 0.1, 0.05]
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"match": { "inputText": "Gravity is a fundamental force", "endpoint": "embedding" },
|
|
23
|
+
"response": {
|
|
24
|
+
"embedding": [0.88, 0.12, 0.07]
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"match": { "inputText": "orbital mechanics and planetary motion", "endpoint": "embedding" },
|
|
29
|
+
"response": {
|
|
30
|
+
"embedding": [0.75, 0.3, 0.15]
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"fixtures": [
|
|
3
|
+
{
|
|
4
|
+
"match": { "userMessage": "plan a trip", "sequenceIndex": 0 },
|
|
5
|
+
"response": {
|
|
6
|
+
"toolCalls": [
|
|
7
|
+
{
|
|
8
|
+
"name": "search_flights",
|
|
9
|
+
"arguments": { "origin": "SFO", "destination": "NRT", "date": "2025-03-15" }
|
|
10
|
+
}
|
|
11
|
+
]
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"match": { "userMessage": "plan a trip", "sequenceIndex": 1 },
|
|
16
|
+
"response": {
|
|
17
|
+
"toolCalls": [
|
|
18
|
+
{
|
|
19
|
+
"name": "search_hotels",
|
|
20
|
+
"arguments": { "city": "Tokyo", "checkIn": "2025-03-15", "checkOut": "2025-03-22" }
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"match": { "userMessage": "plan a trip", "sequenceIndex": 2 },
|
|
27
|
+
"response": {
|
|
28
|
+
"content": "I found a great itinerary for your Tokyo trip!\n\n**Flight:** SFO → NRT on March 15, departing 11:30 AM (United UA837) — $890 round trip\n\n**Hotel:** Hotel Gracery Shinjuku, March 15–22 — $185/night\n\nWould you like me to book these, or would you prefer different options?"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@copilotkit/aimock",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.14.1",
|
|
4
4
|
"description": "Mock infrastructure for AI application testing — LLM APIs, image generation, text-to-speech, transcription, video generation, MCP tools, A2A agents, AG-UI event streams, vector databases, search, rerank, and moderation. One package, one port, zero dependencies.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"embeddings",
|
|
36
36
|
"copilotkit"
|
|
37
37
|
],
|
|
38
|
+
"homepage": "https://aimock.copilotkit.dev",
|
|
38
39
|
"repository": {
|
|
39
40
|
"type": "git",
|
|
40
41
|
"url": "https://github.com/CopilotKit/aimock"
|
|
@@ -7,7 +7,7 @@ description: Use when writing test fixtures for @copilotkit/aimock — mock LLM
|
|
|
7
7
|
|
|
8
8
|
## What aimock Is
|
|
9
9
|
|
|
10
|
-
aimock is a zero-dependency mock
|
|
10
|
+
aimock is a zero-dependency mock infrastructure for AI apps. Fixture-driven. Multi-provider (OpenAI, Anthropic, Gemini, AWS Bedrock, Azure OpenAI, Vertex AI, Ollama, Cohere). Multimedia endpoints (image generation, text-to-speech, audio transcription, video generation). MCP, A2A, AG-UI, and vector DB mocking. Runs a real HTTP server on a real port — works across processes, unlike MSW-style interceptors. WebSocket support for OpenAI Responses/Realtime and Gemini Live APIs. Record-and-replay for all endpoints including multimedia. Chaos testing and Prometheus metrics.
|
|
11
11
|
|
|
12
12
|
## Core Mental Model
|
|
13
13
|
|
|
@@ -19,19 +19,20 @@ aimock is a zero-dependency mock LLM server. Fixture-driven. Multi-provider (Ope
|
|
|
19
19
|
|
|
20
20
|
## Match Field Reference
|
|
21
21
|
|
|
22
|
-
| Field | Type | Matches Against
|
|
23
|
-
| ---------------- | ----------------------------------------- |
|
|
24
|
-
| `userMessage` | `string` | Substring of last `role: "user"` message text
|
|
25
|
-
| `userMessage` | `RegExp` | Pattern test on last `role: "user"` message text
|
|
26
|
-
| `inputText` | `string` | Substring of embedding input text (concatenated if multiple inputs)
|
|
27
|
-
| `inputText` | `RegExp` | Pattern test on embedding input text
|
|
28
|
-
| `toolName` | `string` | Exact match on any tool in request's `tools[]` array (by `function.name`)
|
|
29
|
-
| `toolCallId` | `string` | Exact match on `tool_call_id` of last `role: "tool"` message
|
|
30
|
-
| `model` | `string` | Exact match on `req.model`
|
|
31
|
-
| `model` | `RegExp` | Pattern test on `req.model`
|
|
32
|
-
| `responseFormat` | `string` | Exact match on `req.response_format.type` (`"json_object"`, `"json_schema"`)
|
|
33
|
-
| `sequenceIndex` | `number` | Matches only when this fixture's match count equals the given index (0-based)
|
|
34
|
-
| `
|
|
22
|
+
| Field | Type | Matches Against |
|
|
23
|
+
| ---------------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------------- |
|
|
24
|
+
| `userMessage` | `string` | Substring of last `role: "user"` message text |
|
|
25
|
+
| `userMessage` | `RegExp` | Pattern test on last `role: "user"` message text |
|
|
26
|
+
| `inputText` | `string` | Substring of embedding input text (concatenated if multiple inputs) |
|
|
27
|
+
| `inputText` | `RegExp` | Pattern test on embedding input text |
|
|
28
|
+
| `toolName` | `string` | Exact match on any tool in request's `tools[]` array (by `function.name`) |
|
|
29
|
+
| `toolCallId` | `string` | Exact match on `tool_call_id` of last `role: "tool"` message |
|
|
30
|
+
| `model` | `string` | Exact match on `req.model` |
|
|
31
|
+
| `model` | `RegExp` | Pattern test on `req.model` |
|
|
32
|
+
| `responseFormat` | `string` | Exact match on `req.response_format.type` (`"json_object"`, `"json_schema"`) |
|
|
33
|
+
| `sequenceIndex` | `number` | Matches only when this fixture's match count equals the given index (0-based) |
|
|
34
|
+
| `endpoint` | `string` | Restrict to endpoint type: `"chat"`, `"image"`, `"speech"`, `"transcription"`, `"video"`, `"embedding"` |
|
|
35
|
+
| `predicate` | `(req: ChatCompletionRequest) => boolean` | Custom function — full access to request |
|
|
35
36
|
|
|
36
37
|
**AND logic**: all specified fields must match. Empty match `{}` = catch-all.
|
|
37
38
|
|
|
@@ -50,12 +51,18 @@ Multi-part content (e.g., `[{type: "text", text: "hello"}]`) is automatically ex
|
|
|
50
51
|
### Tool Calls
|
|
51
52
|
|
|
52
53
|
```typescript
|
|
54
|
+
// Preferred: object form (auto-stringified by the fixture loader)
|
|
55
|
+
{
|
|
56
|
+
toolCalls: [{ name: "get_weather", arguments: { city: "SF" } }];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Also accepted: JSON string form (backward compatible)
|
|
53
60
|
{
|
|
54
61
|
toolCalls: [{ name: "get_weather", arguments: '{"city":"SF"}' }];
|
|
55
62
|
}
|
|
56
63
|
```
|
|
57
64
|
|
|
58
|
-
|
|
65
|
+
**Both object and string forms are accepted** for `arguments`. The fixture loader auto-stringifies objects via `JSON.stringify()`. Object form is preferred for readability.
|
|
59
66
|
|
|
60
67
|
### Embedding
|
|
61
68
|
|
|
@@ -67,6 +74,49 @@ Multi-part content (e.g., `[{type: "text", text: "hello"}]`) is automatically ex
|
|
|
67
74
|
|
|
68
75
|
The embedding vector is returned for each input in the request. If no embedding fixture matches, deterministic embeddings are auto-generated from the input text hash — you only need fixtures when you want specific vectors.
|
|
69
76
|
|
|
77
|
+
### Image
|
|
78
|
+
|
|
79
|
+
<!-- prettier-ignore -->
|
|
80
|
+
```typescript
|
|
81
|
+
// Single image
|
|
82
|
+
{
|
|
83
|
+
image: {
|
|
84
|
+
url: "https://example.com/generated.png"
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Multiple images
|
|
88
|
+
{
|
|
89
|
+
images: [{ url: "https://example.com/1.png" }, { b64Json: "iVBOR..." }]
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Use `match: { endpoint: "image" }` to prevent cross-matching with chat fixtures.
|
|
94
|
+
|
|
95
|
+
### Speech (TTS)
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
{ audio: "base64-encoded-audio-data" }
|
|
99
|
+
// With explicit format (default: mp3)
|
|
100
|
+
{ audio: "base64-data", format: "opus" }
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Transcription
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
// Simple
|
|
107
|
+
{ transcription: { text: "Hello world" } }
|
|
108
|
+
// Verbose with timestamps
|
|
109
|
+
{ transcription: { text: "Hello world", language: "en", duration: 2.5, words: [...], segments: [...] } }
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Video
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
{ video: { id: "vid-1", status: "completed", url: "https://example.com/video.mp4" } }
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Video uses async polling — `POST /v1/videos` creates, `GET /v1/videos/{id}` checks status.
|
|
119
|
+
|
|
70
120
|
### Error
|
|
71
121
|
|
|
72
122
|
```typescript
|
|
@@ -104,7 +154,7 @@ The most common pattern. Fixture 1 triggers the tool call, fixture 2 handles the
|
|
|
104
154
|
```typescript
|
|
105
155
|
// Step 1: User asks about weather → LLM calls tool
|
|
106
156
|
mock.onMessage("weather", {
|
|
107
|
-
toolCalls: [{ name: "get_weather", arguments:
|
|
157
|
+
toolCalls: [{ name: "get_weather", arguments: { city: "SF" } }],
|
|
108
158
|
});
|
|
109
159
|
|
|
110
160
|
// Step 2: Tool result comes back → LLM responds with text
|
|
@@ -154,7 +204,7 @@ mock.addFixture({
|
|
|
154
204
|
// First call returns tool call, second returns text
|
|
155
205
|
mock.on(
|
|
156
206
|
{ userMessage: "status", sequenceIndex: 0 },
|
|
157
|
-
{ toolCalls: [{ name: "check_status", arguments:
|
|
207
|
+
{ toolCalls: [{ name: "check_status", arguments: {} }] },
|
|
158
208
|
);
|
|
159
209
|
mock.on({ userMessage: "status", sequenceIndex: 1 }, { content: "All systems operational." });
|
|
160
210
|
```
|
|
@@ -189,7 +239,7 @@ mock.addFixture({
|
|
|
189
239
|
return typeof sys === "string" && sys.includes("Flights found: false");
|
|
190
240
|
},
|
|
191
241
|
},
|
|
192
|
-
response: { toolCalls: [{ name: "search_flights", arguments:
|
|
242
|
+
response: { toolCalls: [{ name: "search_flights", arguments: {} }] },
|
|
193
243
|
});
|
|
194
244
|
```
|
|
195
245
|
|
|
@@ -263,6 +313,17 @@ mock.nextRequestError(429, { message: "Rate limited", type: "rate_limit_error" }
|
|
|
263
313
|
"match": { "userMessage": "hello" },
|
|
264
314
|
"response": { "content": "Hi!" }
|
|
265
315
|
},
|
|
316
|
+
{
|
|
317
|
+
"match": { "userMessage": "weather" },
|
|
318
|
+
"response": {
|
|
319
|
+
"toolCalls": [
|
|
320
|
+
{
|
|
321
|
+
"name": "get_weather",
|
|
322
|
+
"arguments": { "city": "SF", "units": "fahrenheit" }
|
|
323
|
+
}
|
|
324
|
+
]
|
|
325
|
+
}
|
|
326
|
+
},
|
|
266
327
|
{
|
|
267
328
|
"match": { "inputText": "search query" },
|
|
268
329
|
"response": { "embedding": [0.1, 0.2, 0.3] }
|
|
@@ -275,6 +336,8 @@ mock.nextRequestError(429, { message: "Rate limited", type: "rate_limit_error" }
|
|
|
275
336
|
}
|
|
276
337
|
```
|
|
277
338
|
|
|
339
|
+
**JSON auto-stringify**: In JSON fixture files, `arguments` and `content` can be objects — the loader auto-stringifies them with `JSON.stringify()`. The escaped-string form (`"{\"city\":\"SF\"}"`) still works but objects are preferred for readability.
|
|
340
|
+
|
|
278
341
|
JSON files cannot use `RegExp` or `predicate` — those are code-only features. `streamingProfile` is supported in JSON fixture files.
|
|
279
342
|
|
|
280
343
|
Load with `mock.loadFixtureFile("./fixtures/greetings.json")` or `mock.loadFixtureDir("./fixtures/")`.
|
|
@@ -309,12 +372,71 @@ All providers share the same fixture pool — write fixtures once, they work for
|
|
|
309
372
|
| `WS /v1/responses` | OpenAI | WebSocket |
|
|
310
373
|
| `WS /v1/realtime` | OpenAI | WebSocket |
|
|
311
374
|
| `WS /ws/google.ai...BidiGenerateContent` | Gemini Live | WebSocket |
|
|
375
|
+
| `POST /v1/images/generations` | OpenAI | HTTP |
|
|
376
|
+
| `POST /v1beta/models/{model}:predict` | Gemini Imagen | HTTP |
|
|
377
|
+
| `POST /v1/audio/speech` | OpenAI | HTTP |
|
|
378
|
+
| `POST /v1/audio/transcriptions` | OpenAI | HTTP |
|
|
379
|
+
| `POST /v1/videos` | OpenAI | HTTP |
|
|
380
|
+
| `GET /v1/videos/{id}` | OpenAI | HTTP |
|
|
381
|
+
|
|
382
|
+
## Response Template Overrides
|
|
383
|
+
|
|
384
|
+
Fixture responses can include optional override fields to control auto-generated envelope values. These are merged into the provider-specific response format (OpenAI, Claude, Gemini, Responses API).
|
|
385
|
+
|
|
386
|
+
| Field | Type | Default | Description |
|
|
387
|
+
| ------------------- | ------ | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
388
|
+
| `id` | string | auto-generated | Override response ID (e.g., `chatcmpl-custom`) |
|
|
389
|
+
| `created` | number | `Date.now()/1000` | Override Unix timestamp |
|
|
390
|
+
| `model` | string | echoes request | Override model name in response |
|
|
391
|
+
| `usage` | object | zeroed | Override token counts: `{ prompt_tokens, completion_tokens, total_tokens }`. OpenAI Chat includes usage in response body; Responses API uses `response.usage`. When omitted, auto-computed from content length |
|
|
392
|
+
| `finishReason` | string | `"stop"` / `"tool_calls"` | Override finish reason. Mappings: `stop` -> `end_turn` (Claude), `STOP` (Gemini); `tool_calls` -> `tool_use` (Claude), `FUNCTION_CALL` (Gemini); `length` -> `max_tokens` (Claude), `MAX_TOKENS` (Gemini); `content_filter` -> `SAFETY` (Gemini), `failed` (Responses API) |
|
|
393
|
+
| `role` | string | `"assistant"` | Override message role |
|
|
394
|
+
| `systemFingerprint` | string | (omitted) | Add `system_fingerprint` to response |
|
|
395
|
+
|
|
396
|
+
### Example
|
|
397
|
+
|
|
398
|
+
```typescript
|
|
399
|
+
mock.onMessage("hello", {
|
|
400
|
+
content: "Hi!",
|
|
401
|
+
model: "gpt-4-turbo-2024-04-09",
|
|
402
|
+
usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 },
|
|
403
|
+
systemFingerprint: "fp_abc123",
|
|
404
|
+
});
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
### In JSON fixtures
|
|
408
|
+
|
|
409
|
+
```json
|
|
410
|
+
{
|
|
411
|
+
"match": { "userMessage": "hello" },
|
|
412
|
+
"response": {
|
|
413
|
+
"content": "Hi!",
|
|
414
|
+
"model": "gpt-4-turbo-2024-04-09",
|
|
415
|
+
"usage": { "prompt_tokens": 10, "completion_tokens": 5, "total_tokens": 15 },
|
|
416
|
+
"systemFingerprint": "fp_abc123"
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
These fields map correctly across all provider formats — for example, `finishReason: "stop"` becomes `finish_reason: "stop"` in OpenAI, `stop_reason: "end_turn"` in Claude, and `finishReason: "STOP"` in Gemini.
|
|
422
|
+
|
|
423
|
+
## Provider Support Matrix
|
|
424
|
+
|
|
425
|
+
| Feature | OpenAI Chat | OpenAI Responses | Claude | Gemini | Bedrock | Azure | Ollama | Cohere |
|
|
426
|
+
| -------------------- | ----------- | ---------------- | ------ | ------ | ------- | ----- | ------ | ------ |
|
|
427
|
+
| Text | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
|
|
428
|
+
| Tool Calls | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
|
|
429
|
+
| Content + Tool Calls | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
|
|
430
|
+
| Streaming | SSE | SSE | SSE | SSE | Binary | SSE | NDJSON | SSE |
|
|
431
|
+
| Reasoning | Yes | Yes | Yes | Yes | Yes | Yes | -- | -- |
|
|
432
|
+
| Web Searches | -- | Yes | -- | -- | -- | -- | -- | -- |
|
|
433
|
+
| Response Overrides | Yes | Yes | Yes | Yes | -- | Yes | -- | -- |
|
|
312
434
|
|
|
313
435
|
## Critical Gotchas
|
|
314
436
|
|
|
315
437
|
1. **Order matters** — first match wins. Specific fixtures before general ones. Use `prependFixture()` to force priority.
|
|
316
438
|
|
|
317
|
-
2. **`arguments`
|
|
439
|
+
2. **`arguments` accepts both objects and strings** — `"arguments": {"key":"value"}` (preferred, auto-stringified) or `"arguments": "{\"key\":\"value\"}"` (legacy). The same applies to `content` fields that contain JSON. The fixture loader detects `typeof === "object"` and calls `JSON.stringify()` automatically.
|
|
318
440
|
|
|
319
441
|
3. **Latency is per-chunk, not total** — `latency: 100` means 100ms between each SSE chunk, not 100ms total response time. Similarly, `truncateAfterChunks` and `disconnectAfterMs` are for simulating stream interruptions (added in v1.3.0).
|
|
320
442
|
|
|
@@ -559,6 +681,10 @@ const mock = await LLMock.create({ port: 0 }); // creates + starts in one call
|
|
|
559
681
|
| `onSearch(pattern, results)` | Match search requests by query |
|
|
560
682
|
| `onRerank(pattern, results)` | Match rerank requests by query |
|
|
561
683
|
| `onModerate(pattern, result)` | Match moderation requests by input |
|
|
684
|
+
| `onImage(pattern, response)` | Match image generation by prompt |
|
|
685
|
+
| `onSpeech(pattern, response)` | Match TTS by input text |
|
|
686
|
+
| `onTranscription(response)` | Match audio transcription |
|
|
687
|
+
| `onVideo(pattern, response)` | Match video generation by prompt |
|
|
562
688
|
| `mount(path, handler)` | Mount a Mountable (VectorMock, etc.) |
|
|
563
689
|
| `url` / `baseUrl` | Server URL (throws if not started) |
|
|
564
690
|
| `port` | Server port number |
|
|
@@ -567,19 +693,19 @@ Sequential responses use `on()` with `sequenceIndex` in the match — there is n
|
|
|
567
693
|
|
|
568
694
|
## Record-and-Replay (VCR Mode)
|
|
569
695
|
|
|
570
|
-
|
|
696
|
+
aimock supports a VCR-style record-and-replay workflow for ALL endpoints including multimedia (image, TTS, transcription, video): unmatched requests are proxied to real provider APIs, and the responses are saved as standard aimock fixture files for deterministic replay. Binary TTS responses are base64-encoded with format derived from Content-Type. Multimedia fixtures automatically include `endpoint` in their match criteria for correct routing on replay.
|
|
571
697
|
|
|
572
698
|
### CLI usage
|
|
573
699
|
|
|
574
700
|
```bash
|
|
575
701
|
# Record mode: proxy unmatched requests to real OpenAI and Anthropic APIs
|
|
576
|
-
|
|
702
|
+
aimock --record \
|
|
577
703
|
--provider-openai https://api.openai.com \
|
|
578
704
|
--provider-anthropic https://api.anthropic.com \
|
|
579
705
|
-f ./fixtures
|
|
580
706
|
|
|
581
707
|
# Strict mode: fail on unmatched requests (no proxying, no catch-all 404)
|
|
582
|
-
|
|
708
|
+
aimock --strict -f ./fixtures
|
|
583
709
|
```
|
|
584
710
|
|
|
585
711
|
- `--record` enables proxy-on-miss. Requires at least one `--provider-*` flag.
|