ai.matey.testing 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cjs/fixture-capture.js +169 -0
  3. package/dist/cjs/fixture-capture.js.map +1 -0
  4. package/dist/cjs/fixture-helpers.js +240 -0
  5. package/dist/cjs/fixture-helpers.js.map +1 -0
  6. package/dist/cjs/fixture-loader.js +196 -0
  7. package/dist/cjs/fixture-loader.js.map +1 -0
  8. package/dist/cjs/fixture-types.js +20 -0
  9. package/dist/cjs/fixture-types.js.map +1 -0
  10. package/dist/cjs/index.js +66 -0
  11. package/dist/cjs/index.js.map +1 -0
  12. package/dist/cjs/property-testing.js +288 -0
  13. package/dist/cjs/property-testing.js.map +1 -0
  14. package/dist/cjs/test-helpers.js +289 -0
  15. package/dist/cjs/test-helpers.js.map +1 -0
  16. package/dist/esm/fixture-capture.js +163 -0
  17. package/dist/esm/fixture-capture.js.map +1 -0
  18. package/dist/esm/fixture-helpers.js +228 -0
  19. package/dist/esm/fixture-helpers.js.map +1 -0
  20. package/dist/esm/fixture-loader.js +154 -0
  21. package/dist/esm/fixture-loader.js.map +1 -0
  22. package/dist/esm/fixture-types.js +16 -0
  23. package/dist/esm/fixture-types.js.map +1 -0
  24. package/dist/esm/index.js +15 -0
  25. package/dist/esm/index.js.map +1 -0
  26. package/dist/esm/property-testing.js +274 -0
  27. package/dist/esm/property-testing.js.map +1 -0
  28. package/dist/esm/test-helpers.js +272 -0
  29. package/dist/esm/test-helpers.js.map +1 -0
  30. package/dist/types/fixture-capture.d.ts +55 -0
  31. package/dist/types/fixture-capture.d.ts.map +1 -0
  32. package/dist/types/fixture-helpers.d.ts +69 -0
  33. package/dist/types/fixture-helpers.d.ts.map +1 -0
  34. package/dist/types/fixture-loader.d.ts +40 -0
  35. package/dist/types/fixture-loader.d.ts.map +1 -0
  36. package/dist/types/fixture-types.d.ts +76 -0
  37. package/dist/types/fixture-types.d.ts.map +1 -0
  38. package/dist/types/index.d.ts +12 -0
  39. package/dist/types/index.d.ts.map +1 -0
  40. package/dist/types/property-testing.d.ts +79 -0
  41. package/dist/types/property-testing.d.ts.map +1 -0
  42. package/dist/types/test-helpers.d.ts +81 -0
  43. package/dist/types/test-helpers.d.ts.map +1 -0
  44. package/package.json +68 -0
  45. package/readme.md +31 -0
@@ -0,0 +1,274 @@
1
+ /**
2
+ * Property-based testing utilities - generate random valid inputs
3
+ */
4
+ /**
5
+ * Random seed generator for deterministic randomness
6
+ */
7
+ export class SeededRandom {
8
+ seed;
9
+ constructor(seed = Date.now()) {
10
+ this.seed = seed;
11
+ }
12
+ /**
13
+ * Generate next random number between 0 and 1
14
+ */
15
+ next() {
16
+ this.seed = (this.seed * 9301 + 49297) % 233280;
17
+ return this.seed / 233280;
18
+ }
19
+ /**
20
+ * Generate random integer between min and max (inclusive)
21
+ */
22
+ nextInt(min, max) {
23
+ return Math.floor(this.next() * (max - min + 1)) + min;
24
+ }
25
+ /**
26
+ * Pick random element from array
27
+ */
28
+ pick(array) {
29
+ if (array.length === 0) {
30
+ throw new Error('Cannot pick from empty array');
31
+ }
32
+ return array[this.nextInt(0, array.length - 1)];
33
+ }
34
+ /**
35
+ * Generate random boolean
36
+ */
37
+ nextBool(probability = 0.5) {
38
+ return this.next() < probability;
39
+ }
40
+ }
41
+ /**
42
+ * Generate random text content
43
+ */
44
+ export function generateTextContent(random) {
45
+ const texts = [
46
+ 'Hello, how are you?',
47
+ 'What is the weather today?',
48
+ 'Explain quantum computing',
49
+ 'Write a haiku about programming',
50
+ 'What is 2 + 2?',
51
+ 'Tell me a joke',
52
+ 'Summarize the main points',
53
+ 'Translate this to French',
54
+ 'Help me debug this code',
55
+ 'What are the benefits?',
56
+ ];
57
+ return {
58
+ type: 'text',
59
+ text: random.pick(texts),
60
+ };
61
+ }
62
+ /**
63
+ * Generate random user message
64
+ */
65
+ export function generateUserMessage(random) {
66
+ const contentCount = random.nextInt(1, 3);
67
+ const content = [];
68
+ for (let i = 0; i < contentCount; i++) {
69
+ content.push(generateTextContent(random));
70
+ }
71
+ return {
72
+ role: 'user',
73
+ content,
74
+ };
75
+ }
76
+ /**
77
+ * Generate random assistant message
78
+ */
79
+ export function generateAssistantMessage(random) {
80
+ return {
81
+ role: 'assistant',
82
+ content: [generateTextContent(random)],
83
+ };
84
+ }
85
+ /**
86
+ * Generate random system message
87
+ */
88
+ export function generateSystemMessage(random) {
89
+ const systemPrompts = [
90
+ 'You are a helpful assistant',
91
+ 'You are an expert programmer',
92
+ 'You speak like a pirate',
93
+ 'You are a teacher',
94
+ 'You provide concise answers',
95
+ ];
96
+ return {
97
+ role: 'system',
98
+ content: [
99
+ {
100
+ type: 'text',
101
+ text: random.pick(systemPrompts),
102
+ },
103
+ ],
104
+ };
105
+ }
106
+ /**
107
+ * Generate random parameters
108
+ */
109
+ export function generateParameters(random) {
110
+ const models = [
111
+ 'gpt-4',
112
+ 'gpt-3.5-turbo',
113
+ 'claude-3-5-sonnet-20241022',
114
+ 'claude-3-haiku-20240307',
115
+ 'gemini-1.5-pro',
116
+ 'llama3.2:1b',
117
+ ];
118
+ return {
119
+ model: random.pick(models),
120
+ temperature: Math.round(random.next() * 20) / 20, // 0.0 to 1.0 in 0.05 increments
121
+ maxTokens: random.nextInt(50, 500),
122
+ topP: random.nextBool() ? Math.round(random.next() * 20) / 20 : undefined,
123
+ };
124
+ }
125
+ /**
126
+ * Generate random chat request
127
+ */
128
+ export function generateChatRequest(random, options) {
129
+ const messages = [];
130
+ // Add system message if requested
131
+ if (options?.includeSystem && random.nextBool(0.5)) {
132
+ messages.push(generateSystemMessage(random));
133
+ }
134
+ // Add conversation turns
135
+ const minMessages = options?.minMessages ?? 1;
136
+ const maxMessages = options?.maxMessages ?? 5;
137
+ const messageCount = random.nextInt(minMessages, maxMessages);
138
+ if (options?.multiTurn) {
139
+ // Multi-turn conversation
140
+ const turns = Math.floor(messageCount / 2);
141
+ for (let i = 0; i < turns; i++) {
142
+ messages.push(generateUserMessage(random));
143
+ messages.push(generateAssistantMessage(random));
144
+ }
145
+ }
146
+ // Always end with user message
147
+ messages.push(generateUserMessage(random));
148
+ return {
149
+ messages,
150
+ parameters: generateParameters(random),
151
+ metadata: {
152
+ requestId: `gen-${random.nextInt(1000, 9999)}`,
153
+ timestamp: Date.now(),
154
+ },
155
+ };
156
+ }
157
+ /**
158
+ * Property test: run test with multiple generated inputs
159
+ */
160
+ export async function forAll(generator, test, options) {
161
+ const runs = options?.runs ?? 100;
162
+ const seed = options?.seed ?? Date.now();
163
+ const random = new SeededRandom(seed);
164
+ const failures = [];
165
+ for (let i = 0; i < runs; i++) {
166
+ const value = generator(random);
167
+ try {
168
+ await test(value);
169
+ }
170
+ catch (error) {
171
+ failures.push({
172
+ run: i,
173
+ value,
174
+ error: error,
175
+ });
176
+ }
177
+ }
178
+ if (failures.length > 0) {
179
+ const firstFailure = failures[0];
180
+ throw new Error(`Property test failed on run ${firstFailure.run + 1}/${runs}\n` +
181
+ `Seed: ${seed}\n` +
182
+ `Value: ${JSON.stringify(firstFailure.value, null, 2)}\n` +
183
+ `Error: ${firstFailure.error.message}\n` +
184
+ `Total failures: ${failures.length}/${runs}`);
185
+ }
186
+ }
187
+ /**
188
+ * Shrink a failing value to find minimal failing case
189
+ */
190
+ export function shrinkChatRequest(request) {
191
+ const shrunk = [];
192
+ // Try removing messages
193
+ if (request.messages.length > 1) {
194
+ shrunk.push({
195
+ ...request,
196
+ messages: request.messages.slice(0, -1),
197
+ });
198
+ }
199
+ // Try simplifying parameters
200
+ shrunk.push({
201
+ ...request,
202
+ parameters: {
203
+ ...request.parameters,
204
+ temperature: 0.7,
205
+ maxTokens: 100,
206
+ topP: undefined,
207
+ },
208
+ });
209
+ // Try removing content from messages
210
+ for (let i = 0; i < request.messages.length; i++) {
211
+ const message = request.messages[i];
212
+ if (Array.isArray(message.content) && message.content.length > 1) {
213
+ const newMessages = [...request.messages];
214
+ newMessages[i] = {
215
+ ...message,
216
+ content: [message.content[0]],
217
+ };
218
+ shrunk.push({
219
+ ...request,
220
+ messages: newMessages,
221
+ });
222
+ }
223
+ }
224
+ return shrunk;
225
+ }
226
+ /**
227
+ * Example property tests
228
+ */
229
+ /**
230
+ * Property: All generated requests should be valid
231
+ */
232
+ export async function propertyValidRequest() {
233
+ await forAll((random) => generateChatRequest(random), (request) => {
234
+ // Request should have messages
235
+ if (request.messages.length === 0) {
236
+ throw new Error('Request must have at least one message');
237
+ }
238
+ // Last message should be from user
239
+ const lastMessage = request.messages[request.messages.length - 1];
240
+ if (lastMessage?.role !== 'user') {
241
+ throw new Error('Last message must be from user');
242
+ }
243
+ // Parameters should be in valid ranges
244
+ if (request.parameters?.temperature !== undefined) {
245
+ if (request.parameters.temperature < 0 || request.parameters.temperature > 2) {
246
+ throw new Error('Temperature must be between 0 and 2');
247
+ }
248
+ }
249
+ if (request.parameters?.maxTokens !== undefined) {
250
+ if (request.parameters.maxTokens < 1) {
251
+ throw new Error('maxTokens must be positive');
252
+ }
253
+ }
254
+ }, { runs: 100 });
255
+ }
256
+ /**
257
+ * Property: Multi-turn conversations should alternate user/assistant
258
+ */
259
+ export async function propertyMultiTurnAlternates() {
260
+ await forAll((random) => generateChatRequest(random, { multiTurn: true }), (request) => {
261
+ const nonSystemMessages = request.messages.filter((m) => m.role !== 'system');
262
+ for (let i = 0; i < nonSystemMessages.length - 1; i++) {
263
+ const current = nonSystemMessages[i];
264
+ const next = nonSystemMessages[i + 1];
265
+ if (current.role === 'user' && next.role !== 'assistant') {
266
+ throw new Error('User message must be followed by assistant message');
267
+ }
268
+ if (current.role === 'assistant' && next.role !== 'user') {
269
+ throw new Error('Assistant message must be followed by user message');
270
+ }
271
+ }
272
+ }, { runs: 100 });
273
+ }
274
+ //# sourceMappingURL=property-testing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"property-testing.js","sourceRoot":"","sources":["../../src/property-testing.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,272 @@
1
+ /**
2
+ * Test helper utilities - assertions and test builders
3
+ */
4
+ import { expect } from 'vitest';
5
+ /**
6
+ * Assert that a response has the correct structure
7
+ */
8
+ export function assertValidChatResponse(response) {
9
+ expect(response).toBeDefined();
10
+ expect(response.message).toBeDefined();
11
+ expect(response.message.role).toBe('assistant');
12
+ expect(response.message.content).toBeDefined();
13
+ expect(Array.isArray(response.message.content)).toBe(true);
14
+ expect(response.message.content.length).toBeGreaterThan(0);
15
+ expect(response.usage).toBeDefined();
16
+ if (response.usage) {
17
+ expect(response.usage.promptTokens).toBeGreaterThanOrEqual(0);
18
+ expect(response.usage.completionTokens).toBeGreaterThanOrEqual(0);
19
+ expect(response.usage.totalTokens).toBeGreaterThanOrEqual(0);
20
+ }
21
+ expect(response.finishReason).toBeDefined();
22
+ expect(['stop', 'length', 'tool_use', 'content_filter']).toContain(response.finishReason);
23
+ expect(response.metadata).toBeDefined();
24
+ expect(response.metadata.requestId).toBeDefined();
25
+ expect(response.metadata.timestamp).toBeDefined();
26
+ }
27
+ /**
28
+ * Assert that a stream chunk has the correct structure
29
+ */
30
+ export function assertValidStreamChunk(chunk) {
31
+ expect(chunk).toBeDefined();
32
+ expect(chunk.type).toBeDefined();
33
+ expect(['start', 'content', 'tool_use', 'error', 'metadata', 'done']).toContain(chunk.type);
34
+ }
35
+ /**
36
+ * Assert that a request has the correct structure
37
+ */
38
+ export function assertValidChatRequest(request) {
39
+ expect(request).toBeDefined();
40
+ expect(request.messages).toBeDefined();
41
+ expect(Array.isArray(request.messages)).toBe(true);
42
+ expect(request.messages.length).toBeGreaterThan(0);
43
+ // Validate each message
44
+ for (const message of request.messages) {
45
+ assertValidMessage(message);
46
+ }
47
+ expect(request.parameters).toBeDefined();
48
+ expect(request.metadata).toBeDefined();
49
+ expect(request.metadata.requestId).toBeDefined();
50
+ expect(request.metadata.timestamp).toBeDefined();
51
+ }
52
+ /**
53
+ * Assert that a message has the correct structure
54
+ */
55
+ export function assertValidMessage(message) {
56
+ expect(message).toBeDefined();
57
+ expect(message.role).toBeDefined();
58
+ expect(['user', 'assistant', 'system', 'tool']).toContain(message.role);
59
+ expect(message.content).toBeDefined();
60
+ if (Array.isArray(message.content)) {
61
+ expect(message.content.length).toBeGreaterThan(0);
62
+ for (const content of message.content) {
63
+ assertValidMessageContent(content);
64
+ }
65
+ }
66
+ }
67
+ /**
68
+ * Assert that message content has the correct structure
69
+ */
70
+ export function assertValidMessageContent(content) {
71
+ expect(content).toBeDefined();
72
+ expect(content.type).toBeDefined();
73
+ switch (content.type) {
74
+ case 'text':
75
+ expect(content.text).toBeDefined();
76
+ expect(typeof content.text).toBe('string');
77
+ break;
78
+ case 'image':
79
+ expect(content.source).toBeDefined();
80
+ expect(content.source.type).toBeDefined();
81
+ break;
82
+ case 'tool_use':
83
+ expect(content.id).toBeDefined();
84
+ expect(content.name).toBeDefined();
85
+ expect(content.input).toBeDefined();
86
+ break;
87
+ case 'tool_result':
88
+ expect(content.toolUseId).toBeDefined();
89
+ expect(content.content).toBeDefined();
90
+ break;
91
+ }
92
+ }
93
+ /**
94
+ * Assert that response contains text
95
+ */
96
+ export function assertResponseHasText(response) {
97
+ if (!Array.isArray(response.message.content)) {
98
+ throw new Error('Response content must be an array');
99
+ }
100
+ const textContent = response.message.content.find((c) => c.type === 'text');
101
+ expect(textContent).toBeDefined();
102
+ if (textContent?.type === 'text') {
103
+ expect(textContent.text).toBeDefined();
104
+ expect(textContent.text.length).toBeGreaterThan(0);
105
+ }
106
+ }
107
+ /**
108
+ * Assert that response contains tool use
109
+ */
110
+ export function assertResponseHasToolUse(response) {
111
+ if (!Array.isArray(response.message.content)) {
112
+ throw new Error('Response content must be an array');
113
+ }
114
+ const toolUse = response.message.content.find((c) => c.type === 'tool_use');
115
+ expect(toolUse).toBeDefined();
116
+ if (toolUse?.type === 'tool_use') {
117
+ expect(toolUse.id).toBeDefined();
118
+ expect(toolUse.name).toBeDefined();
119
+ expect(toolUse.input).toBeDefined();
120
+ }
121
+ expect(response.finishReason).toBe('tool_use');
122
+ }
123
+ /**
124
+ * Assert that streaming produces expected sequence
125
+ */
126
+ export async function assertValidStreamSequence(stream) {
127
+ const chunks = [];
128
+ for await (const chunk of stream) {
129
+ assertValidStreamChunk(chunk);
130
+ chunks.push(chunk);
131
+ }
132
+ expect(chunks.length).toBeGreaterThan(0);
133
+ // First chunk should be 'start'
134
+ const firstChunk = chunks[0];
135
+ if (firstChunk) {
136
+ expect(firstChunk.type).toBe('start');
137
+ }
138
+ // Last chunk should be 'done'
139
+ const lastChunk = chunks[chunks.length - 1];
140
+ if (lastChunk) {
141
+ expect(lastChunk.type).toBe('done');
142
+ }
143
+ return chunks;
144
+ }
145
+ /**
146
+ * Build a simple chat request for testing
147
+ */
148
+ export function buildChatRequest(userMessage, options) {
149
+ const messages = [];
150
+ if (options?.systemMessage) {
151
+ messages.push({
152
+ role: 'system',
153
+ content: [{ type: 'text', text: options.systemMessage }],
154
+ });
155
+ }
156
+ messages.push({
157
+ role: 'user',
158
+ content: [{ type: 'text', text: userMessage }],
159
+ });
160
+ return {
161
+ messages,
162
+ parameters: {
163
+ model: options?.model || 'test-model',
164
+ temperature: options?.temperature ?? 0.7,
165
+ maxTokens: options?.maxTokens ?? 100,
166
+ },
167
+ metadata: {
168
+ requestId: `test-${Date.now()}`,
169
+ timestamp: Date.now(),
170
+ },
171
+ };
172
+ }
173
+ /**
174
+ * Build a multi-turn chat request for testing
175
+ */
176
+ export function buildMultiTurnRequest(exchanges, finalUserMessage, options) {
177
+ const messages = [];
178
+ for (const exchange of exchanges) {
179
+ messages.push({
180
+ role: 'user',
181
+ content: [{ type: 'text', text: exchange.user }],
182
+ });
183
+ messages.push({
184
+ role: 'assistant',
185
+ content: [{ type: 'text', text: exchange.assistant }],
186
+ });
187
+ }
188
+ messages.push({
189
+ role: 'user',
190
+ content: [{ type: 'text', text: finalUserMessage }],
191
+ });
192
+ return {
193
+ messages,
194
+ parameters: {
195
+ model: options?.model || 'test-model',
196
+ temperature: options?.temperature ?? 0.7,
197
+ maxTokens: options?.maxTokens ?? 100,
198
+ },
199
+ metadata: {
200
+ requestId: `test-${Date.now()}`,
201
+ timestamp: Date.now(),
202
+ },
203
+ };
204
+ }
205
+ /**
206
+ * Extract text from response
207
+ */
208
+ export function extractTextFromResponse(response) {
209
+ if (!Array.isArray(response.message.content)) {
210
+ return '';
211
+ }
212
+ const textContent = response.message.content.filter((c) => c.type === 'text');
213
+ return textContent.map((c) => (c.type === 'text' ? c.text : '')).join('');
214
+ }
215
+ /**
216
+ * Extract tool uses from response
217
+ */
218
+ export function extractToolUsesFromResponse(response) {
219
+ if (!Array.isArray(response.message.content)) {
220
+ return [];
221
+ }
222
+ return response.message.content
223
+ .filter((c) => c.type === 'tool_use')
224
+ .map((c) => {
225
+ if (c.type === 'tool_use') {
226
+ return {
227
+ id: c.id,
228
+ name: c.name,
229
+ input: c.input,
230
+ };
231
+ }
232
+ return null;
233
+ })
234
+ .filter(Boolean);
235
+ }
236
+ /**
237
+ * Accumulate text from stream chunks
238
+ */
239
+ export async function accumulateStreamText(stream) {
240
+ let text = '';
241
+ for await (const chunk of stream) {
242
+ if (chunk.type === 'content' && chunk.delta) {
243
+ text += chunk.delta;
244
+ }
245
+ }
246
+ return text;
247
+ }
248
+ /**
249
+ * Count tokens in text (rough estimate)
250
+ */
251
+ export function estimateTokens(text) {
252
+ // Rough estimate: ~4 characters per token
253
+ return Math.ceil(text.length / 4);
254
+ }
255
+ /**
256
+ * Assert that usage statistics are reasonable
257
+ */
258
+ export function assertReasonableUsage(response) {
259
+ const { usage } = response;
260
+ if (!usage) {
261
+ throw new Error('Response must have usage statistics');
262
+ }
263
+ // Tokens should be positive
264
+ expect(usage.promptTokens).toBeGreaterThan(0);
265
+ expect(usage.completionTokens).toBeGreaterThan(0);
266
+ expect(usage.totalTokens).toBeGreaterThan(0);
267
+ // Total should equal sum
268
+ expect(usage.totalTokens).toBe(usage.promptTokens + usage.completionTokens);
269
+ // Token counts should be reasonable (not absurdly high)
270
+ expect(usage.totalTokens).toBeLessThan(1000000);
271
+ }
272
+ //# sourceMappingURL=test-helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-helpers.js","sourceRoot":"","sources":["../../src/test-helpers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Fixture capture tool - captures real API calls and saves them as fixtures
3
+ */
4
+ import type { IRChatRequest, IRChatResponse, IRStreamChunk } from 'ai.matey.types';
5
+ import type { ChatFixture, StreamingFixture } from './fixture-types.js';
6
+ /**
7
+ * Capture configuration
8
+ */
9
+ export interface CaptureConfig {
10
+ /** Provider name */
11
+ readonly provider: string;
12
+ /** Scenario name */
13
+ readonly scenario: string;
14
+ /** Description of this fixture */
15
+ readonly description?: string;
16
+ /** Tags for categorization */
17
+ readonly tags?: readonly string[];
18
+ /** API version */
19
+ readonly apiVersion?: string;
20
+ /** Whether to sanitize sensitive data */
21
+ readonly sanitize?: boolean;
22
+ /** Custom output directory */
23
+ readonly outputDir?: string;
24
+ }
25
+ /**
26
+ * Capture a non-streaming chat interaction
27
+ */
28
+ export declare function captureChat(config: CaptureConfig, request: IRChatRequest, response: IRChatResponse, providerData?: {
29
+ providerRequest?: unknown;
30
+ providerResponse?: unknown;
31
+ }): Promise<ChatFixture>;
32
+ /**
33
+ * Capture a streaming chat interaction
34
+ */
35
+ export declare function captureStream(config: CaptureConfig, request: IRChatRequest, chunks: readonly IRStreamChunk[], finalResponse?: IRChatResponse, providerData?: {
36
+ providerRequest?: unknown;
37
+ providerStreamEvents?: readonly unknown[];
38
+ }): Promise<StreamingFixture>;
39
+ /**
40
+ * Capture middleware - wraps a backend adapter to automatically capture fixtures
41
+ */
42
+ export declare function createCaptureMiddleware(config: Omit<CaptureConfig, 'scenario'>): {
43
+ name: string;
44
+ processRequest(request: IRChatRequest): IRChatRequest;
45
+ processResponse(response: IRChatResponse, request: IRChatRequest): Promise<IRChatResponse>;
46
+ };
47
+ /**
48
+ * Bulk capture utility - capture multiple fixtures at once
49
+ */
50
+ export declare function bulkCapture(config: Omit<CaptureConfig, 'scenario'>, captures: Array<{
51
+ scenario: string;
52
+ request: IRChatRequest;
53
+ response: IRChatResponse;
54
+ }>): Promise<void>;
55
+ //# sourceMappingURL=fixture-capture.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixture-capture.d.ts","sourceRoot":"","sources":["../../src/fixture-capture.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACnF,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAmB,MAAM,oBAAoB,CAAC;AAGzF;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,oBAAoB;IACpB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B,oBAAoB;IACpB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B,kCAAkC;IAClC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAE9B,8BAA8B;IAC9B,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAElC,kBAAkB;IAClB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAE7B,yCAAyC;IACzC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAE5B,8BAA8B;IAC9B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AA8BD;;GAEG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,cAAc,EACxB,YAAY,CAAC,EAAE;IACb,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,GACA,OAAO,CAAC,WAAW,CAAC,CA2BtB;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,SAAS,aAAa,EAAE,EAChC,aAAa,CAAC,EAAE,cAAc,EAC9B,YAAY,CAAC,EAAE;IACb,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,oBAAoB,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;CAC3C,GACA,OAAO,CAAC,gBAAgB,CAAC,CA4B3B;AAuBD;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,IAAI,CAAC,aAAa,EAAE,UAAU,CAAC;;4BAInD,aAAa,GAAG,aAAa;8BAOzC,cAAc,WACf,aAAa,GACrB,OAAO,CAAC,cAAc,CAAC;EAiB7B;AAiCD;;GAEG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,IAAI,CAAC,aAAa,EAAE,UAAU,CAAC,EACvC,QAAQ,EAAE,KAAK,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,aAAa,CAAC;IACvB,QAAQ,EAAE,cAAc,CAAC;CAC1B,CAAC,GACD,OAAO,CAAC,IAAI,CAAC,CAaf"}