@boostecom/provider 0.0.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/README.md +90 -0
- package/dist/index.cjs +2522 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +848 -0
- package/dist/index.d.ts +848 -0
- package/dist/index.js +2484 -0
- package/dist/index.js.map +1 -0
- package/docs/content/README.md +337 -0
- package/docs/content/agent-teams.mdx +324 -0
- package/docs/content/api.mdx +757 -0
- package/docs/content/best-practices.mdx +624 -0
- package/docs/content/examples.mdx +675 -0
- package/docs/content/guide.mdx +516 -0
- package/docs/content/index.mdx +99 -0
- package/docs/content/installation.mdx +246 -0
- package/docs/content/skills.mdx +548 -0
- package/docs/content/troubleshooting.mdx +588 -0
- package/docs/examples/README.md +499 -0
- package/docs/examples/abort-signal.ts +125 -0
- package/docs/examples/agent-teams.ts +122 -0
- package/docs/examples/basic-usage.ts +73 -0
- package/docs/examples/check-cli.ts +51 -0
- package/docs/examples/conversation-history.ts +69 -0
- package/docs/examples/custom-config.ts +90 -0
- package/docs/examples/generate-object-constraints.ts +209 -0
- package/docs/examples/generate-object.ts +211 -0
- package/docs/examples/hooks-callbacks.ts +63 -0
- package/docs/examples/images.ts +76 -0
- package/docs/examples/integration-test.ts +241 -0
- package/docs/examples/limitations.ts +150 -0
- package/docs/examples/logging-custom-logger.ts +99 -0
- package/docs/examples/logging-default.ts +55 -0
- package/docs/examples/logging-disabled.ts +74 -0
- package/docs/examples/logging-verbose.ts +64 -0
- package/docs/examples/long-running-tasks.ts +179 -0
- package/docs/examples/message-injection.ts +210 -0
- package/docs/examples/mid-stream-injection.ts +126 -0
- package/docs/examples/run-all-examples.sh +48 -0
- package/docs/examples/sdk-tools-callbacks.ts +49 -0
- package/docs/examples/skills-discovery.ts +144 -0
- package/docs/examples/skills-management.ts +140 -0
- package/docs/examples/stream-object.ts +80 -0
- package/docs/examples/streaming.ts +52 -0
- package/docs/examples/structured-output-repro.ts +227 -0
- package/docs/examples/tool-management.ts +215 -0
- package/docs/examples/tool-streaming.ts +132 -0
- package/docs/examples/zod4-compatibility-test.ts +290 -0
- package/docs/src/claude-code-language-model.test.ts +3883 -0
- package/docs/src/claude-code-language-model.ts +2586 -0
- package/docs/src/claude-code-provider.test.ts +97 -0
- package/docs/src/claude-code-provider.ts +179 -0
- package/docs/src/convert-to-claude-code-messages.images.test.ts +104 -0
- package/docs/src/convert-to-claude-code-messages.test.ts +193 -0
- package/docs/src/convert-to-claude-code-messages.ts +419 -0
- package/docs/src/errors.test.ts +213 -0
- package/docs/src/errors.ts +216 -0
- package/docs/src/index.test.ts +49 -0
- package/docs/src/index.ts +98 -0
- package/docs/src/logger.integration.test.ts +164 -0
- package/docs/src/logger.test.ts +184 -0
- package/docs/src/logger.ts +65 -0
- package/docs/src/map-claude-code-finish-reason.test.ts +120 -0
- package/docs/src/map-claude-code-finish-reason.ts +60 -0
- package/docs/src/mcp-helpers.test.ts +71 -0
- package/docs/src/mcp-helpers.ts +123 -0
- package/docs/src/message-injection.test.ts +460 -0
- package/docs/src/types.ts +447 -0
- package/docs/src/validation.test.ts +558 -0
- package/docs/src/validation.ts +360 -0
- package/package.json +124 -0
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { ClaudeCodeLanguageModel } from './claude-code-language-model.js';
|
|
3
|
+
import { claudeCodeSettingsSchema } from './validation.js';
|
|
4
|
+
import type { MessageInjector } from './types.js';
|
|
5
|
+
|
|
6
|
+
// Mock the SDK module
|
|
7
|
+
vi.mock('@anthropic-ai/claude-agent-sdk', () => {
|
|
8
|
+
return {
|
|
9
|
+
query: vi.fn(),
|
|
10
|
+
};
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
import { query as mockQuery } from '@anthropic-ai/claude-agent-sdk';
|
|
14
|
+
import type { SDKUserMessage } from '@anthropic-ai/claude-agent-sdk';
|
|
15
|
+
|
|
16
|
+
describe('MessageInjector', () => {
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
vi.clearAllMocks();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe('validation', () => {
|
|
22
|
+
it('should accept onStreamStart as a function', () => {
|
|
23
|
+
const settings = {
|
|
24
|
+
onStreamStart: () => {},
|
|
25
|
+
};
|
|
26
|
+
const result = claudeCodeSettingsSchema.safeParse(settings);
|
|
27
|
+
expect(result.success).toBe(true);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should reject onStreamStart if not a function', () => {
|
|
31
|
+
const settings = {
|
|
32
|
+
onStreamStart: 'not-a-function',
|
|
33
|
+
};
|
|
34
|
+
const result = claudeCodeSettingsSchema.safeParse(settings);
|
|
35
|
+
expect(result.success).toBe(false);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should accept settings without onStreamStart', () => {
|
|
39
|
+
const settings = {};
|
|
40
|
+
const result = claudeCodeSettingsSchema.safeParse(settings);
|
|
41
|
+
expect(result.success).toBe(true);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
describe('onStreamStart callback', () => {
|
|
46
|
+
it('should call onStreamStart when streaming input is enabled and prompt is iterated', async () => {
|
|
47
|
+
const onStreamStart = vi.fn((injector) => {
|
|
48
|
+
// Close immediately so the stream can complete
|
|
49
|
+
injector.close();
|
|
50
|
+
});
|
|
51
|
+
const model = new ClaudeCodeLanguageModel({
|
|
52
|
+
id: 'sonnet',
|
|
53
|
+
settings: {
|
|
54
|
+
streamingInput: 'always',
|
|
55
|
+
onStreamStart,
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Mock that iterates the prompt (which triggers onStreamStart)
|
|
60
|
+
vi.mocked(mockQuery).mockImplementation(({ prompt }: any) => {
|
|
61
|
+
// Consume the async iterable to trigger onStreamStart
|
|
62
|
+
void (async () => {
|
|
63
|
+
if (prompt && typeof prompt[Symbol.asyncIterator] === 'function') {
|
|
64
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
65
|
+
for await (const _ of prompt) {
|
|
66
|
+
// Just iterate to trigger the callback
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
})();
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
async *[Symbol.asyncIterator]() {
|
|
73
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
74
|
+
yield {
|
|
75
|
+
type: 'result',
|
|
76
|
+
subtype: 'success',
|
|
77
|
+
session_id: 'test-session',
|
|
78
|
+
usage: { input_tokens: 10, output_tokens: 20 },
|
|
79
|
+
};
|
|
80
|
+
},
|
|
81
|
+
} as any;
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
await model.doGenerate({
|
|
85
|
+
prompt: [{ role: 'user', content: [{ type: 'text', text: 'hello' }] }],
|
|
86
|
+
} as any);
|
|
87
|
+
|
|
88
|
+
expect(onStreamStart).toHaveBeenCalledTimes(1);
|
|
89
|
+
expect(onStreamStart).toHaveBeenCalledWith(
|
|
90
|
+
expect.objectContaining({
|
|
91
|
+
inject: expect.any(Function),
|
|
92
|
+
close: expect.any(Function),
|
|
93
|
+
})
|
|
94
|
+
);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('should not call onStreamStart when streamingInput is off', async () => {
|
|
98
|
+
const onStreamStart = vi.fn();
|
|
99
|
+
const model = new ClaudeCodeLanguageModel({
|
|
100
|
+
id: 'sonnet',
|
|
101
|
+
settings: {
|
|
102
|
+
streamingInput: 'off',
|
|
103
|
+
onStreamStart,
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
vi.mocked(mockQuery).mockImplementation(() => {
|
|
108
|
+
// With streamingInput off, prompt is a string, not AsyncIterable
|
|
109
|
+
// So onStreamStart should never be called
|
|
110
|
+
return {
|
|
111
|
+
async *[Symbol.asyncIterator]() {
|
|
112
|
+
yield {
|
|
113
|
+
type: 'result',
|
|
114
|
+
subtype: 'success',
|
|
115
|
+
session_id: 'test-session',
|
|
116
|
+
usage: { input_tokens: 10, output_tokens: 20 },
|
|
117
|
+
};
|
|
118
|
+
},
|
|
119
|
+
} as any;
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
await model.doGenerate({
|
|
123
|
+
prompt: [{ role: 'user', content: [{ type: 'text', text: 'hello' }] }],
|
|
124
|
+
} as any);
|
|
125
|
+
|
|
126
|
+
expect(onStreamStart).not.toHaveBeenCalled();
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('should provide a working MessageInjector when prompt is iterated', async () => {
|
|
130
|
+
let capturedInjector: MessageInjector | null = null;
|
|
131
|
+
const model = new ClaudeCodeLanguageModel({
|
|
132
|
+
id: 'sonnet',
|
|
133
|
+
settings: {
|
|
134
|
+
streamingInput: 'always',
|
|
135
|
+
onStreamStart: (injector) => {
|
|
136
|
+
capturedInjector = injector;
|
|
137
|
+
injector.close();
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
vi.mocked(mockQuery).mockImplementation(({ prompt }: any) => {
|
|
143
|
+
void (async () => {
|
|
144
|
+
if (prompt && typeof prompt[Symbol.asyncIterator] === 'function') {
|
|
145
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
146
|
+
for await (const _ of prompt) {
|
|
147
|
+
// Iterate to trigger callback
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
})();
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
async *[Symbol.asyncIterator]() {
|
|
154
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
155
|
+
yield {
|
|
156
|
+
type: 'result',
|
|
157
|
+
subtype: 'success',
|
|
158
|
+
session_id: 'test-session',
|
|
159
|
+
usage: { input_tokens: 10, output_tokens: 20 },
|
|
160
|
+
};
|
|
161
|
+
},
|
|
162
|
+
} as any;
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
await model.doGenerate({
|
|
166
|
+
prompt: [{ role: 'user', content: [{ type: 'text', text: 'hello' }] }],
|
|
167
|
+
} as any);
|
|
168
|
+
|
|
169
|
+
expect(capturedInjector).not.toBeNull();
|
|
170
|
+
expect(typeof capturedInjector!.inject).toBe('function');
|
|
171
|
+
expect(typeof capturedInjector!.close).toBe('function');
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
describe('message injection flow', () => {
|
|
176
|
+
it('should pass injected messages to the SDK prompt', async () => {
|
|
177
|
+
const collectedMessages: SDKUserMessage[] = [];
|
|
178
|
+
|
|
179
|
+
const model = new ClaudeCodeLanguageModel({
|
|
180
|
+
id: 'sonnet',
|
|
181
|
+
settings: {
|
|
182
|
+
streamingInput: 'always',
|
|
183
|
+
onStreamStart: (injector) => {
|
|
184
|
+
// Inject a message immediately
|
|
185
|
+
injector.inject('Injected message!');
|
|
186
|
+
// Then close to let the stream complete
|
|
187
|
+
injector.close();
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// Create a mock that collects all yielded messages from the prompt
|
|
193
|
+
vi.mocked(mockQuery).mockImplementation(({ prompt }: any) => {
|
|
194
|
+
// Consume the async iterable in the background
|
|
195
|
+
void (async () => {
|
|
196
|
+
if (prompt && typeof prompt[Symbol.asyncIterator] === 'function') {
|
|
197
|
+
for await (const msg of prompt) {
|
|
198
|
+
collectedMessages.push(msg);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
})();
|
|
202
|
+
|
|
203
|
+
// Return a mock response
|
|
204
|
+
return {
|
|
205
|
+
async *[Symbol.asyncIterator]() {
|
|
206
|
+
// Small delay to let injection happen
|
|
207
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
208
|
+
yield {
|
|
209
|
+
type: 'result',
|
|
210
|
+
subtype: 'success',
|
|
211
|
+
session_id: 'test-session',
|
|
212
|
+
usage: { input_tokens: 10, output_tokens: 20 },
|
|
213
|
+
};
|
|
214
|
+
},
|
|
215
|
+
} as any;
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
await model.doGenerate({
|
|
219
|
+
prompt: [{ role: 'user', content: [{ type: 'text', text: 'hello' }] }],
|
|
220
|
+
} as any);
|
|
221
|
+
|
|
222
|
+
// Should have initial message + injected message
|
|
223
|
+
expect(collectedMessages.length).toBeGreaterThanOrEqual(1);
|
|
224
|
+
|
|
225
|
+
// First message should be the initial user message
|
|
226
|
+
expect(collectedMessages[0].type).toBe('user');
|
|
227
|
+
expect(collectedMessages[0].message.role).toBe('user');
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it('should handle multiple injected messages', async () => {
|
|
231
|
+
const collectedMessages: SDKUserMessage[] = [];
|
|
232
|
+
|
|
233
|
+
const model = new ClaudeCodeLanguageModel({
|
|
234
|
+
id: 'sonnet',
|
|
235
|
+
settings: {
|
|
236
|
+
streamingInput: 'always',
|
|
237
|
+
onStreamStart: (injector) => {
|
|
238
|
+
// Inject multiple messages
|
|
239
|
+
injector.inject('First injection');
|
|
240
|
+
injector.inject('Second injection');
|
|
241
|
+
injector.inject('Third injection');
|
|
242
|
+
injector.close();
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
vi.mocked(mockQuery).mockImplementation(({ prompt }: any) => {
|
|
248
|
+
void (async () => {
|
|
249
|
+
if (prompt && typeof prompt[Symbol.asyncIterator] === 'function') {
|
|
250
|
+
for await (const msg of prompt) {
|
|
251
|
+
collectedMessages.push(msg);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
})();
|
|
255
|
+
|
|
256
|
+
return {
|
|
257
|
+
async *[Symbol.asyncIterator]() {
|
|
258
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
259
|
+
yield {
|
|
260
|
+
type: 'result',
|
|
261
|
+
subtype: 'success',
|
|
262
|
+
session_id: 'test-session',
|
|
263
|
+
usage: { input_tokens: 10, output_tokens: 20 },
|
|
264
|
+
};
|
|
265
|
+
},
|
|
266
|
+
} as any;
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
await model.doGenerate({
|
|
270
|
+
prompt: [{ role: 'user', content: [{ type: 'text', text: 'hello' }] }],
|
|
271
|
+
} as any);
|
|
272
|
+
|
|
273
|
+
// Should have initial + 3 injected messages = 4 total
|
|
274
|
+
// (though timing may affect this in tests)
|
|
275
|
+
expect(collectedMessages.length).toBeGreaterThanOrEqual(1);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it('should ignore inject calls after close', async () => {
|
|
279
|
+
const model = new ClaudeCodeLanguageModel({
|
|
280
|
+
id: 'sonnet',
|
|
281
|
+
settings: {
|
|
282
|
+
streamingInput: 'always',
|
|
283
|
+
onStreamStart: (injector) => {
|
|
284
|
+
injector.close();
|
|
285
|
+
// This should be ignored
|
|
286
|
+
injector.inject('Should be ignored');
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
const mockResponse = {
|
|
292
|
+
async *[Symbol.asyncIterator]() {
|
|
293
|
+
yield {
|
|
294
|
+
type: 'result',
|
|
295
|
+
subtype: 'success',
|
|
296
|
+
session_id: 'test-session',
|
|
297
|
+
usage: { input_tokens: 10, output_tokens: 20 },
|
|
298
|
+
};
|
|
299
|
+
},
|
|
300
|
+
};
|
|
301
|
+
vi.mocked(mockQuery).mockReturnValue(mockResponse as any);
|
|
302
|
+
|
|
303
|
+
// Should not throw
|
|
304
|
+
await expect(
|
|
305
|
+
model.doGenerate({
|
|
306
|
+
prompt: [{ role: 'user', content: [{ type: 'text', text: 'hello' }] }],
|
|
307
|
+
} as any)
|
|
308
|
+
).resolves.toBeDefined();
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
describe('delivery callback', () => {
|
|
313
|
+
it('should call onResult with true when message is delivered', async () => {
|
|
314
|
+
const deliveryResults: boolean[] = [];
|
|
315
|
+
|
|
316
|
+
const model = new ClaudeCodeLanguageModel({
|
|
317
|
+
id: 'sonnet',
|
|
318
|
+
settings: {
|
|
319
|
+
streamingInput: 'always',
|
|
320
|
+
onStreamStart: (injector) => {
|
|
321
|
+
injector.inject('Test message', (delivered) => {
|
|
322
|
+
deliveryResults.push(delivered);
|
|
323
|
+
});
|
|
324
|
+
injector.close();
|
|
325
|
+
},
|
|
326
|
+
},
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
vi.mocked(mockQuery).mockImplementation(({ prompt }: any) => {
|
|
330
|
+
void (async () => {
|
|
331
|
+
if (prompt && typeof prompt[Symbol.asyncIterator] === 'function') {
|
|
332
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
333
|
+
for await (const _ of prompt) {
|
|
334
|
+
// Iterate to trigger delivery
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
})();
|
|
338
|
+
|
|
339
|
+
return {
|
|
340
|
+
async *[Symbol.asyncIterator]() {
|
|
341
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
342
|
+
yield {
|
|
343
|
+
type: 'result',
|
|
344
|
+
subtype: 'success',
|
|
345
|
+
session_id: 'test-session',
|
|
346
|
+
usage: { input_tokens: 10, output_tokens: 20 },
|
|
347
|
+
};
|
|
348
|
+
},
|
|
349
|
+
} as any;
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
await model.doGenerate({
|
|
353
|
+
prompt: [{ role: 'user', content: [{ type: 'text', text: 'hello' }] }],
|
|
354
|
+
} as any);
|
|
355
|
+
|
|
356
|
+
expect(deliveryResults).toContain(true);
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
it('should call onResult with false when session ends before delivery', async () => {
|
|
360
|
+
const deliveryResults: boolean[] = [];
|
|
361
|
+
|
|
362
|
+
const model = new ClaudeCodeLanguageModel({
|
|
363
|
+
id: 'sonnet',
|
|
364
|
+
settings: {
|
|
365
|
+
streamingInput: 'always',
|
|
366
|
+
onStreamStart: (injector) => {
|
|
367
|
+
// Inject after a delay - session will end first
|
|
368
|
+
setTimeout(() => {
|
|
369
|
+
injector.inject('Too late message', (delivered) => {
|
|
370
|
+
deliveryResults.push(delivered);
|
|
371
|
+
});
|
|
372
|
+
}, 50);
|
|
373
|
+
},
|
|
374
|
+
},
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
vi.mocked(mockQuery).mockImplementation(({ prompt }: any) => {
|
|
378
|
+
// Start iterating prompt but end session quickly
|
|
379
|
+
void (async () => {
|
|
380
|
+
if (prompt && typeof prompt[Symbol.asyncIterator] === 'function') {
|
|
381
|
+
const iter = prompt[Symbol.asyncIterator]();
|
|
382
|
+
await iter.next(); // Get initial message
|
|
383
|
+
// Call next() again to resume generator and trigger onStreamStart
|
|
384
|
+
// Don't await - let it run in background while session ends
|
|
385
|
+
iter.next();
|
|
386
|
+
}
|
|
387
|
+
})();
|
|
388
|
+
|
|
389
|
+
return {
|
|
390
|
+
async *[Symbol.asyncIterator]() {
|
|
391
|
+
// Small delay then end - session ends before the 50ms inject
|
|
392
|
+
await new Promise((r) => setTimeout(r, 20));
|
|
393
|
+
yield {
|
|
394
|
+
type: 'result',
|
|
395
|
+
subtype: 'success',
|
|
396
|
+
session_id: 'test-session',
|
|
397
|
+
usage: { input_tokens: 10, output_tokens: 20 },
|
|
398
|
+
};
|
|
399
|
+
},
|
|
400
|
+
} as any;
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
await model.doGenerate({
|
|
404
|
+
prompt: [{ role: 'user', content: [{ type: 'text', text: 'hello' }] }],
|
|
405
|
+
} as any);
|
|
406
|
+
|
|
407
|
+
// Wait for the delayed inject to happen and be processed
|
|
408
|
+
await new Promise((r) => setTimeout(r, 150));
|
|
409
|
+
|
|
410
|
+
expect(deliveryResults).toContain(false);
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
it('should call onResult with false when inject is called after close', async () => {
|
|
414
|
+
const deliveryResults: boolean[] = [];
|
|
415
|
+
|
|
416
|
+
const model = new ClaudeCodeLanguageModel({
|
|
417
|
+
id: 'sonnet',
|
|
418
|
+
settings: {
|
|
419
|
+
streamingInput: 'always',
|
|
420
|
+
onStreamStart: (injector) => {
|
|
421
|
+
injector.close();
|
|
422
|
+
injector.inject('After close', (delivered) => {
|
|
423
|
+
deliveryResults.push(delivered);
|
|
424
|
+
});
|
|
425
|
+
},
|
|
426
|
+
},
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
vi.mocked(mockQuery).mockImplementation(({ prompt }: any) => {
|
|
430
|
+
// Iterate prompt to trigger onStreamStart
|
|
431
|
+
void (async () => {
|
|
432
|
+
if (prompt && typeof prompt[Symbol.asyncIterator] === 'function') {
|
|
433
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
434
|
+
for await (const _ of prompt) {
|
|
435
|
+
// Iterate to trigger callback
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
})();
|
|
439
|
+
|
|
440
|
+
return {
|
|
441
|
+
async *[Symbol.asyncIterator]() {
|
|
442
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
443
|
+
yield {
|
|
444
|
+
type: 'result',
|
|
445
|
+
subtype: 'success',
|
|
446
|
+
session_id: 'test-session',
|
|
447
|
+
usage: { input_tokens: 10, output_tokens: 20 },
|
|
448
|
+
};
|
|
449
|
+
},
|
|
450
|
+
} as any;
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
await model.doGenerate({
|
|
454
|
+
prompt: [{ role: 'user', content: [{ type: 'text', text: 'hello' }] }],
|
|
455
|
+
} as any);
|
|
456
|
+
|
|
457
|
+
expect(deliveryResults).toEqual([false]);
|
|
458
|
+
});
|
|
459
|
+
});
|
|
460
|
+
});
|