@ank1015/providers 0.0.1 → 0.0.2
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/LICENSE +21 -0
- package/README.md +93 -383
- package/dist/agent/conversation.d.ts +97 -0
- package/dist/agent/conversation.d.ts.map +1 -0
- package/dist/agent/conversation.js +328 -0
- package/dist/agent/conversation.js.map +1 -0
- package/dist/agent/runner.d.ts +37 -0
- package/dist/agent/runner.d.ts.map +1 -0
- package/dist/agent/runner.js +169 -0
- package/dist/agent/runner.js.map +1 -0
- package/dist/agent/tools/calculate.d.ts +15 -0
- package/dist/agent/tools/calculate.d.ts.map +1 -0
- package/dist/agent/tools/calculate.js +23 -0
- package/dist/agent/tools/calculate.js.map +1 -0
- package/dist/agent/tools/get-current-time.d.ts +15 -0
- package/dist/agent/tools/get-current-time.d.ts.map +1 -0
- package/dist/agent/tools/get-current-time.js +38 -0
- package/dist/agent/tools/get-current-time.js.map +1 -0
- package/dist/agent/tools/index.d.ts +3 -0
- package/dist/agent/tools/index.d.ts.map +1 -0
- package/dist/agent/tools/index.js +3 -0
- package/dist/agent/tools/index.js.map +1 -0
- package/dist/agent/types.d.ts +53 -31
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js +1 -2
- package/dist/agent/utils.d.ts +14 -0
- package/dist/agent/utils.d.ts.map +1 -0
- package/dist/agent/utils.js +59 -0
- package/dist/agent/utils.js.map +1 -0
- package/dist/index.d.ts +16 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -28
- package/dist/index.js.map +1 -1
- package/dist/llm.d.ts +15 -0
- package/dist/llm.d.ts.map +1 -0
- package/dist/llm.js +92 -0
- package/dist/llm.js.map +1 -0
- package/dist/models.d.ts +8 -1
- package/dist/models.d.ts.map +1 -1
- package/dist/models.generated.d.ts +25 -112
- package/dist/models.generated.d.ts.map +1 -1
- package/dist/models.generated.js +72 -227
- package/dist/models.generated.js.map +1 -1
- package/dist/models.js +30 -32
- package/dist/models.js.map +1 -1
- package/dist/providers/google/complete.d.ts +3 -0
- package/dist/providers/google/complete.d.ts.map +1 -0
- package/dist/providers/google/complete.js +53 -0
- package/dist/providers/google/complete.js.map +1 -0
- package/dist/providers/google/index.d.ts +6 -0
- package/dist/providers/google/index.d.ts.map +1 -0
- package/dist/providers/google/index.js +6 -0
- package/dist/providers/google/index.js.map +1 -0
- package/dist/providers/google/stream.d.ts +3 -0
- package/dist/providers/google/stream.d.ts.map +1 -0
- package/dist/providers/{google.js → google/stream.js} +67 -231
- package/dist/providers/google/stream.js.map +1 -0
- package/dist/providers/google/types.d.ts +8 -0
- package/dist/providers/google/types.d.ts.map +1 -0
- package/dist/providers/google/types.js +2 -0
- package/dist/providers/google/types.js.map +1 -0
- package/dist/providers/google/utils.d.ts +30 -0
- package/dist/providers/google/utils.d.ts.map +1 -0
- package/dist/providers/google/utils.js +354 -0
- package/dist/providers/google/utils.js.map +1 -0
- package/dist/providers/openai/complete.d.ts +3 -0
- package/dist/providers/openai/complete.d.ts.map +1 -0
- package/dist/providers/openai/complete.js +57 -0
- package/dist/providers/openai/complete.js.map +1 -0
- package/dist/providers/openai/index.d.ts +4 -0
- package/dist/providers/openai/index.d.ts.map +1 -0
- package/dist/providers/openai/index.js +4 -0
- package/dist/providers/openai/index.js.map +1 -0
- package/dist/providers/openai/stream.d.ts +3 -0
- package/dist/providers/openai/stream.d.ts.map +1 -0
- package/dist/providers/{openai.js → openai/stream.js} +74 -152
- package/dist/providers/openai/stream.js.map +1 -0
- package/dist/providers/openai/types.d.ts +8 -0
- package/dist/providers/openai/types.d.ts.map +1 -0
- package/dist/providers/openai/types.js +2 -0
- package/dist/providers/openai/types.js.map +1 -0
- package/dist/providers/openai/utils.d.ts +13 -0
- package/dist/providers/openai/utils.d.ts.map +1 -0
- package/dist/providers/openai/utils.js +285 -0
- package/dist/providers/openai/utils.js.map +1 -0
- package/dist/types.d.ts +95 -87
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -9
- package/dist/types.js.map +1 -1
- package/dist/utils/event-stream.d.ts +2 -2
- package/dist/utils/event-stream.d.ts.map +1 -1
- package/dist/utils/event-stream.js +2 -7
- package/dist/utils/event-stream.js.map +1 -1
- package/dist/utils/json-parse.js +3 -6
- package/dist/utils/json-parse.js.map +1 -1
- package/dist/utils/overflow.d.ts +51 -0
- package/dist/utils/overflow.d.ts.map +1 -0
- package/dist/utils/overflow.js +106 -0
- package/dist/utils/overflow.js.map +1 -0
- package/dist/utils/sanitize-unicode.js +1 -4
- package/dist/utils/sanitize-unicode.js.map +1 -1
- package/dist/utils/uuid.d.ts +6 -0
- package/dist/utils/uuid.d.ts.map +1 -0
- package/dist/utils/uuid.js +9 -0
- package/dist/utils/uuid.js.map +1 -0
- package/dist/utils/validation.d.ts +10 -3
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +20 -12
- package/dist/utils/validation.js.map +1 -1
- package/package.json +45 -8
- package/biome.json +0 -43
- package/dist/agent/agent-loop.d.ts +0 -5
- package/dist/agent/agent-loop.d.ts.map +0 -1
- package/dist/agent/agent-loop.js +0 -219
- package/dist/agent/agent-loop.js.map +0 -1
- package/dist/providers/convert.d.ts +0 -6
- package/dist/providers/convert.d.ts.map +0 -1
- package/dist/providers/convert.js +0 -207
- package/dist/providers/convert.js.map +0 -1
- package/dist/providers/google.d.ts +0 -26
- package/dist/providers/google.d.ts.map +0 -1
- package/dist/providers/google.js.map +0 -1
- package/dist/providers/openai.d.ts +0 -17
- package/dist/providers/openai.d.ts.map +0 -1
- package/dist/providers/openai.js.map +0 -1
- package/dist/stream.d.ts +0 -4
- package/dist/stream.d.ts.map +0 -1
- package/dist/stream.js +0 -40
- package/dist/stream.js.map +0 -1
- package/dist/test-google-agent-loop.d.ts +0 -2
- package/dist/test-google-agent-loop.d.ts.map +0 -1
- package/dist/test-google-agent-loop.js +0 -186
- package/dist/test-google-agent-loop.js.map +0 -1
- package/dist/test-google.d.ts +0 -2
- package/dist/test-google.d.ts.map +0 -1
- package/dist/test-google.js +0 -41
- package/dist/test-google.js.map +0 -1
- package/src/agent/agent-loop.ts +0 -275
- package/src/agent/types.ts +0 -80
- package/src/index.ts +0 -72
- package/src/models.generated.ts +0 -314
- package/src/models.ts +0 -45
- package/src/providers/convert.ts +0 -222
- package/src/providers/google.ts +0 -496
- package/src/providers/openai.ts +0 -437
- package/src/stream.ts +0 -60
- package/src/types.ts +0 -198
- package/src/utils/event-stream.ts +0 -60
- package/src/utils/json-parse.ts +0 -28
- package/src/utils/sanitize-unicode.ts +0 -25
- package/src/utils/validation.ts +0 -69
- package/test/core/agent-loop.test.ts +0 -958
- package/test/core/stream.test.ts +0 -409
- package/test/data/red-circle.png +0 -0
- package/test/data/superintelligentwill.pdf +0 -0
- package/test/edge-cases/general.test.ts +0 -565
- package/test/integration/e2e.test.ts +0 -530
- package/test/models/cost.test.ts +0 -499
- package/test/models/registry.test.ts +0 -298
- package/test/providers/convert.test.ts +0 -846
- package/test/providers/google-schema.test.ts +0 -666
- package/test/providers/google-stream.test.ts +0 -369
- package/test/providers/openai-stream.test.ts +0 -251
- package/test/utils/event-stream.test.ts +0 -289
- package/test/utils/json-parse.test.ts +0 -344
- package/test/utils/sanitize-unicode.test.ts +0 -329
- package/test/utils/validation.test.ts +0 -614
- package/tsconfig.json +0 -21
- package/vitest.config.ts +0 -9
|
@@ -1,369 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { Context } from '../../src/types';
|
|
3
|
-
import { MODELS } from '../../src/models.generated';
|
|
4
|
-
import { transformSchemaForGoogle } from '../../src/providers/google';
|
|
5
|
-
import { buildGoogleMessages } from '../../src/providers/convert';
|
|
6
|
-
|
|
7
|
-
describe('Google Provider Streaming Tests', () => {
|
|
8
|
-
const model = MODELS.google['gemini-2.5-flash'];
|
|
9
|
-
|
|
10
|
-
describe('Schema transformation', () => {
|
|
11
|
-
it('should transform const to enum', () => {
|
|
12
|
-
|
|
13
|
-
const schema = {
|
|
14
|
-
type: 'string',
|
|
15
|
-
const: 'active',
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
const result = transformSchemaForGoogle(schema);
|
|
19
|
-
|
|
20
|
-
expect(result).toHaveProperty('enum');
|
|
21
|
-
expect(result.enum).toEqual(['active']);
|
|
22
|
-
expect(result).not.toHaveProperty('const');
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('should transform anyOf with const values to enum', () => {
|
|
26
|
-
|
|
27
|
-
const schema = {
|
|
28
|
-
anyOf: [
|
|
29
|
-
{ const: 'option1' },
|
|
30
|
-
{ const: 'option2' },
|
|
31
|
-
{ const: 'option3' },
|
|
32
|
-
],
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const result = transformSchemaForGoogle(schema);
|
|
36
|
-
|
|
37
|
-
expect(result).toHaveProperty('enum');
|
|
38
|
-
expect(result.enum).toEqual(['option1', 'option2', 'option3']);
|
|
39
|
-
expect(result).not.toHaveProperty('anyOf');
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('should recursively transform nested schemas', () => {
|
|
43
|
-
|
|
44
|
-
const schema = {
|
|
45
|
-
type: 'object',
|
|
46
|
-
properties: {
|
|
47
|
-
status: { const: 'active' },
|
|
48
|
-
priority: {
|
|
49
|
-
anyOf: [
|
|
50
|
-
{ const: 'high' },
|
|
51
|
-
{ const: 'low' },
|
|
52
|
-
],
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
const result = transformSchemaForGoogle(schema);
|
|
58
|
-
|
|
59
|
-
expect(result.properties.status.enum).toEqual(['active']);
|
|
60
|
-
expect(result.properties.priority.enum).toEqual(['high', 'low']);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('should not transform anyOf without all const values', () => {
|
|
64
|
-
|
|
65
|
-
const schema = {
|
|
66
|
-
anyOf: [
|
|
67
|
-
{ const: 'value' },
|
|
68
|
-
{ type: 'number' },
|
|
69
|
-
],
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
const result = transformSchemaForGoogle(schema);
|
|
73
|
-
|
|
74
|
-
expect(result).toHaveProperty('anyOf');
|
|
75
|
-
expect(result).not.toHaveProperty('enum');
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('should preserve other schema properties', () => {
|
|
79
|
-
|
|
80
|
-
const schema = {
|
|
81
|
-
type: 'string',
|
|
82
|
-
description: 'A status field',
|
|
83
|
-
const: 'active',
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
const result = transformSchemaForGoogle(schema);
|
|
87
|
-
|
|
88
|
-
expect(result.description).toBe('A status field');
|
|
89
|
-
expect(result.type).toBe('string');
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
describe('Message building', () => {
|
|
94
|
-
it('should convert user text message correctly', () => {
|
|
95
|
-
|
|
96
|
-
const context: Context = {
|
|
97
|
-
messages: [
|
|
98
|
-
{
|
|
99
|
-
role: 'user',
|
|
100
|
-
content: [{ type: 'text', content: 'Hello' }],
|
|
101
|
-
timestamp: Date.now(),
|
|
102
|
-
},
|
|
103
|
-
],
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
const result = buildGoogleMessages(model, context);
|
|
107
|
-
|
|
108
|
-
expect(result).toHaveLength(1);
|
|
109
|
-
expect(result[0].role).toBe('user');
|
|
110
|
-
expect(result[0].parts).toBeDefined();
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it('should convert system prompt correctly', () => {
|
|
114
|
-
|
|
115
|
-
const context: Context = {
|
|
116
|
-
systemPrompt: 'You are a helpful assistant.',
|
|
117
|
-
messages: [],
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
const result = buildGoogleMessages(model, context);
|
|
121
|
-
|
|
122
|
-
// Google uses systemInstruction separately, not in messages
|
|
123
|
-
expect(result).toEqual([]);
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it('should handle user message with image', () => {
|
|
127
|
-
|
|
128
|
-
const context: Context = {
|
|
129
|
-
messages: [
|
|
130
|
-
{
|
|
131
|
-
role: 'user',
|
|
132
|
-
content: [
|
|
133
|
-
{ type: 'text', content: 'Describe:' },
|
|
134
|
-
{ type: 'image', data: 'base64data', mimeType: 'image/png' },
|
|
135
|
-
],
|
|
136
|
-
timestamp: Date.now(),
|
|
137
|
-
},
|
|
138
|
-
],
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
const result = buildGoogleMessages(model, context);
|
|
142
|
-
|
|
143
|
-
expect(result).toHaveLength(1);
|
|
144
|
-
expect(result[0].parts.length).toBeGreaterThan(1);
|
|
145
|
-
// Should have both text and inlineData parts
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
it('should handle user message with file', () => {
|
|
149
|
-
|
|
150
|
-
const context: Context = {
|
|
151
|
-
messages: [
|
|
152
|
-
{
|
|
153
|
-
role: 'user',
|
|
154
|
-
content: [
|
|
155
|
-
{ type: 'text', content: 'Analyze:' },
|
|
156
|
-
{ type: 'file', data: 'base64pdf', mimeType: 'application/pdf' },
|
|
157
|
-
],
|
|
158
|
-
timestamp: Date.now(),
|
|
159
|
-
},
|
|
160
|
-
],
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
const result = buildGoogleMessages(model, context);
|
|
164
|
-
|
|
165
|
-
expect(result).toHaveLength(1);
|
|
166
|
-
expect(result[0].parts.length).toBeGreaterThan(1);
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
it('should convert tool result messages', () => {
|
|
170
|
-
|
|
171
|
-
const context: Context = {
|
|
172
|
-
messages: [
|
|
173
|
-
{
|
|
174
|
-
role: 'toolResult',
|
|
175
|
-
toolName: 'calculator',
|
|
176
|
-
toolCallId: 'call_123',
|
|
177
|
-
content: [{ type: 'text', content: '42' }],
|
|
178
|
-
isError: false,
|
|
179
|
-
timestamp: Date.now(),
|
|
180
|
-
},
|
|
181
|
-
],
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
const result = buildGoogleMessages(model, context);
|
|
185
|
-
|
|
186
|
-
expect(result).toHaveLength(1);
|
|
187
|
-
expect(result[0].role).toBe('user');
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
it('should handle native Google assistant messages', () => {
|
|
191
|
-
|
|
192
|
-
const nativeMessage = {
|
|
193
|
-
role: 'assistant' as const,
|
|
194
|
-
_provider: 'google' as const,
|
|
195
|
-
message: {
|
|
196
|
-
candidates: [
|
|
197
|
-
{
|
|
198
|
-
content: {
|
|
199
|
-
parts: [{ text: 'Hello' }],
|
|
200
|
-
role: 'model',
|
|
201
|
-
},
|
|
202
|
-
finishReason: 'STOP',
|
|
203
|
-
},
|
|
204
|
-
],
|
|
205
|
-
usageMetadata: {
|
|
206
|
-
promptTokenCount: 10,
|
|
207
|
-
candidatesTokenCount: 5,
|
|
208
|
-
totalTokenCount: 15,
|
|
209
|
-
},
|
|
210
|
-
},
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
const context: Context = {
|
|
214
|
-
messages: [nativeMessage as any],
|
|
215
|
-
};
|
|
216
|
-
|
|
217
|
-
const result = buildGoogleMessages(model, context);
|
|
218
|
-
|
|
219
|
-
expect(result).toHaveLength(1);
|
|
220
|
-
expect(result[0].role).toBe('model');
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
it('should throw on cross-provider assistant messages', () => {
|
|
224
|
-
|
|
225
|
-
const openaiMessage = {
|
|
226
|
-
role: 'assistant' as const,
|
|
227
|
-
_provider: 'openai' as const,
|
|
228
|
-
_native: {},
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
const context: Context = {
|
|
232
|
-
messages: [openaiMessage as any],
|
|
233
|
-
};
|
|
234
|
-
|
|
235
|
-
expect(() => buildGoogleMessages(model, context)).toThrow();
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
it('should filter out non-supported content types', () => {
|
|
239
|
-
|
|
240
|
-
const textOnlyModel = {
|
|
241
|
-
...model,
|
|
242
|
-
input: ['text' as const],
|
|
243
|
-
};
|
|
244
|
-
|
|
245
|
-
const context: Context = {
|
|
246
|
-
messages: [
|
|
247
|
-
{
|
|
248
|
-
role: 'user',
|
|
249
|
-
content: [
|
|
250
|
-
{ type: 'text', content: 'Text' },
|
|
251
|
-
{ type: 'image', data: 'base64', mimeType: 'image/png' },
|
|
252
|
-
],
|
|
253
|
-
timestamp: Date.now(),
|
|
254
|
-
},
|
|
255
|
-
],
|
|
256
|
-
};
|
|
257
|
-
|
|
258
|
-
const result = buildGoogleMessages(textOnlyModel, context);
|
|
259
|
-
|
|
260
|
-
expect(result).toHaveLength(1);
|
|
261
|
-
// Should only have text parts
|
|
262
|
-
expect(result[0].parts.length).toBe(1);
|
|
263
|
-
expect(result[0].parts[0]).toHaveProperty('text');
|
|
264
|
-
});
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
describe('Event streaming', () => {
|
|
268
|
-
it('should emit start event', () => {
|
|
269
|
-
// This would require mocking the Google SDK
|
|
270
|
-
expect(true).toBe(true);
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
it('should emit text delta events', () => {
|
|
274
|
-
// This would require mocking the Google SDK
|
|
275
|
-
expect(true).toBe(true);
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
it('should detect thinking mode from part.thought flag', () => {
|
|
279
|
-
// This would require mocking the Google SDK
|
|
280
|
-
expect(true).toBe(true);
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
it('should emit done event with complete message', () => {
|
|
284
|
-
// This would require mocking the Google SDK
|
|
285
|
-
expect(true).toBe(true);
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
it('should handle function call streaming', () => {
|
|
289
|
-
// This would require mocking the Google SDK
|
|
290
|
-
expect(true).toBe(true);
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
it('should auto-generate function call IDs', () => {
|
|
294
|
-
// This would require mocking the Google SDK
|
|
295
|
-
expect(true).toBe(true);
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
it('should calculate token usage including thinking tokens', () => {
|
|
299
|
-
// This would require mocking the Google SDK
|
|
300
|
-
expect(true).toBe(true);
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
it('should calculate costs correctly', () => {
|
|
304
|
-
// This would require mocking the Google SDK
|
|
305
|
-
expect(true).toBe(true);
|
|
306
|
-
});
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
describe('Options handling', () => {
|
|
310
|
-
it('should pass through temperature parameter', () => {
|
|
311
|
-
// Would test that temperature is passed to Google SDK
|
|
312
|
-
expect(true).toBe(true);
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
it('should pass through maxOutputTokens parameter', () => {
|
|
316
|
-
// Would test that maxOutputTokens is passed to Google SDK
|
|
317
|
-
expect(true).toBe(true);
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
it('should handle extended thinking configuration', () => {
|
|
321
|
-
// Would test extended thinking parameters
|
|
322
|
-
expect(true).toBe(true);
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
it('should handle response MIME type setting', () => {
|
|
326
|
-
// Would test response MIME type configuration
|
|
327
|
-
expect(true).toBe(true);
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
it('should handle abort signal', () => {
|
|
331
|
-
// Would test abort signal is passed through
|
|
332
|
-
expect(true).toBe(true);
|
|
333
|
-
});
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
describe('Error handling', () => {
|
|
337
|
-
it('should handle invalid API key error', () => {
|
|
338
|
-
// Would test API key validation
|
|
339
|
-
expect(true).toBe(true);
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
it('should handle network errors', () => {
|
|
343
|
-
// Would test network error handling
|
|
344
|
-
expect(true).toBe(true);
|
|
345
|
-
});
|
|
346
|
-
|
|
347
|
-
it('should handle rate limit errors', () => {
|
|
348
|
-
// Would test rate limit handling
|
|
349
|
-
expect(true).toBe(true);
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
it('should handle finish reason mapping', () => {
|
|
353
|
-
// Would test FinishReason to StopReason mapping
|
|
354
|
-
expect(true).toBe(true);
|
|
355
|
-
});
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
describe('Parts accumulation', () => {
|
|
359
|
-
it('should accumulate consecutive text parts', () => {
|
|
360
|
-
// Would test that consecutive text parts are merged
|
|
361
|
-
expect(true).toBe(true);
|
|
362
|
-
});
|
|
363
|
-
|
|
364
|
-
it('should not merge parts with different types', () => {
|
|
365
|
-
// Would test that different part types are kept separate
|
|
366
|
-
expect(true).toBe(true);
|
|
367
|
-
});
|
|
368
|
-
});
|
|
369
|
-
});
|
|
@@ -1,251 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
import { streamOpenAI } from '../../src/providers/openai';
|
|
3
|
-
import { Context } from '../../src/types';
|
|
4
|
-
import { MODELS } from '../../src/models.generated';
|
|
5
|
-
import { buildOpenAIMessages } from '../../src/providers/convert';
|
|
6
|
-
import { ResponseInputMessageItem } from 'openai/resources/responses/responses.js';
|
|
7
|
-
|
|
8
|
-
describe('OpenAI Provider Streaming Tests', () => {
|
|
9
|
-
const model = MODELS.openai['gpt-5-mini'];
|
|
10
|
-
|
|
11
|
-
describe('Message building', () => {
|
|
12
|
-
it('should convert user text message correctly', () => {
|
|
13
|
-
|
|
14
|
-
const context: Context = {
|
|
15
|
-
messages: [
|
|
16
|
-
{
|
|
17
|
-
role: 'user',
|
|
18
|
-
content: [{ type: 'text', content: 'Hello' }],
|
|
19
|
-
timestamp: Date.now(),
|
|
20
|
-
},
|
|
21
|
-
],
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
const result = buildOpenAIMessages(model, context);
|
|
25
|
-
|
|
26
|
-
expect(result).toHaveLength(1);
|
|
27
|
-
expect((result[0] as ResponseInputMessageItem).role).toBe('user');
|
|
28
|
-
expect((result[0] as ResponseInputMessageItem).content ).toBeDefined();
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it('should convert system prompt to developer role', () => {
|
|
32
|
-
|
|
33
|
-
const context: Context = {
|
|
34
|
-
systemPrompt: 'You are a helpful assistant.',
|
|
35
|
-
messages: [],
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
const result = buildOpenAIMessages(model, context);
|
|
39
|
-
|
|
40
|
-
expect(result).toHaveLength(1);
|
|
41
|
-
expect((result[0] as ResponseInputMessageItem).role).toBe('developer');
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('should handle user message with mixed content', () => {
|
|
45
|
-
|
|
46
|
-
const context: Context = {
|
|
47
|
-
messages: [
|
|
48
|
-
{
|
|
49
|
-
role: 'user',
|
|
50
|
-
content: [
|
|
51
|
-
{ type: 'text', content: 'Describe this:' },
|
|
52
|
-
{ type: 'image', data: 'base64data', mimeType: 'image/png' },
|
|
53
|
-
],
|
|
54
|
-
timestamp: Date.now(),
|
|
55
|
-
},
|
|
56
|
-
],
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
const result = buildOpenAIMessages(model, context);
|
|
60
|
-
|
|
61
|
-
expect(result).toHaveLength(1);
|
|
62
|
-
expect(Array.isArray((result[0] as ResponseInputMessageItem).content)).toBe(true);
|
|
63
|
-
expect(((result[0] as ResponseInputMessageItem).content as any[]).length).toBeGreaterThan(1);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('should filter out non-supported content types', () => {
|
|
67
|
-
|
|
68
|
-
const textOnlyModel = {
|
|
69
|
-
...model,
|
|
70
|
-
input: ['text' as const],
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
const context: Context = {
|
|
74
|
-
messages: [
|
|
75
|
-
{
|
|
76
|
-
role: 'user',
|
|
77
|
-
content: [
|
|
78
|
-
{ type: 'text', content: 'Text' },
|
|
79
|
-
{ type: 'image', data: 'base64', mimeType: 'image/png' },
|
|
80
|
-
],
|
|
81
|
-
timestamp: Date.now(),
|
|
82
|
-
},
|
|
83
|
-
],
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
const result = buildOpenAIMessages(textOnlyModel, context);
|
|
87
|
-
|
|
88
|
-
expect(result).toHaveLength(1);
|
|
89
|
-
// Should only have text content
|
|
90
|
-
if (Array.isArray((result[0] as ResponseInputMessageItem).content)) {
|
|
91
|
-
expect((result[0] as ResponseInputMessageItem).content.length).toBe(1);
|
|
92
|
-
expect((result[0] as ResponseInputMessageItem).content[0].type).toBe('input_text');
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it('should convert tool result messages', () => {
|
|
97
|
-
|
|
98
|
-
const context: Context = {
|
|
99
|
-
messages: [
|
|
100
|
-
{
|
|
101
|
-
role: 'toolResult',
|
|
102
|
-
toolName: 'calculator',
|
|
103
|
-
toolCallId: 'call_123',
|
|
104
|
-
content: [{ type: 'text', content: '42' }],
|
|
105
|
-
isError: false,
|
|
106
|
-
timestamp: Date.now(),
|
|
107
|
-
},
|
|
108
|
-
],
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
const result = buildOpenAIMessages(model, context);
|
|
112
|
-
|
|
113
|
-
expect(result).toHaveLength(1);
|
|
114
|
-
expect((result[0] as ResponseInputMessageItem).role).toBe(undefined);
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
it('should handle native OpenAI assistant messages', () => {
|
|
118
|
-
|
|
119
|
-
const nativeMessage = {
|
|
120
|
-
role: 'assistant' as const,
|
|
121
|
-
_provider: 'openai' as const,
|
|
122
|
-
message: {
|
|
123
|
-
output: [
|
|
124
|
-
{
|
|
125
|
-
type: 'function_call',
|
|
126
|
-
call_id: 'call_123',
|
|
127
|
-
name: 'calculator',
|
|
128
|
-
arguments: '{"expression": "2 + 2"}',
|
|
129
|
-
},
|
|
130
|
-
],
|
|
131
|
-
id: 'resp_123',
|
|
132
|
-
object: "response",
|
|
133
|
-
created_at: 1740855869,
|
|
134
|
-
output_text: '',
|
|
135
|
-
status: "completed",
|
|
136
|
-
incomplete_details: null,
|
|
137
|
-
parallel_tool_calls: false,
|
|
138
|
-
error: null,
|
|
139
|
-
instructions: null,
|
|
140
|
-
max_output_tokens: null,
|
|
141
|
-
model: "gpt-4o-mini-2024-07-18",
|
|
142
|
-
user: undefined,
|
|
143
|
-
metadata: {},
|
|
144
|
-
previous_response_id: null,
|
|
145
|
-
temperature: 1,
|
|
146
|
-
text: {},
|
|
147
|
-
tool_choice: "auto",
|
|
148
|
-
tools: [],
|
|
149
|
-
top_p: 1,
|
|
150
|
-
truncation: "disabled",
|
|
151
|
-
usage: { input_tokens: 10, output_tokens: 5, total_tokens: 15, input_tokens_details: {cached_tokens: 0}, output_tokens_details: {reasoning_tokens: 0} },
|
|
152
|
-
},
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
const context: Context = {
|
|
156
|
-
messages: [nativeMessage as any],
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
const result = buildOpenAIMessages(model, context);
|
|
160
|
-
|
|
161
|
-
expect(result).toHaveLength(1);
|
|
162
|
-
expect((result[0] as ResponseInputMessageItem).role).toBe(undefined);
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
it('should throw on cross-provider assistant messages', () => {
|
|
166
|
-
|
|
167
|
-
const googleMessage = {
|
|
168
|
-
role: 'assistant' as const,
|
|
169
|
-
_provider: 'google' as const,
|
|
170
|
-
_native: {},
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
const context: Context = {
|
|
174
|
-
messages: [googleMessage as any],
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
expect(() => buildOpenAIMessages(model, context)).toThrow();
|
|
178
|
-
});
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
describe('Event streaming', () => {
|
|
182
|
-
it('should emit start event', () => {
|
|
183
|
-
// This would require mocking the OpenAI SDK
|
|
184
|
-
expect(true).toBe(true);
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
it('should emit text delta events', () => {
|
|
188
|
-
// This would require mocking the OpenAI SDK
|
|
189
|
-
expect(true).toBe(true);
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
it('should emit done event with complete message', () => {
|
|
193
|
-
// This would require mocking the OpenAI SDK
|
|
194
|
-
expect(true).toBe(true);
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
it('should handle tool call streaming', () => {
|
|
198
|
-
// This would require mocking the OpenAI SDK
|
|
199
|
-
expect(true).toBe(true);
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
it('should calculate token usage correctly', () => {
|
|
203
|
-
// This would require mocking the OpenAI SDK
|
|
204
|
-
expect(true).toBe(true);
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
it('should calculate costs correctly', () => {
|
|
208
|
-
// This would require mocking the OpenAI SDK
|
|
209
|
-
expect(true).toBe(true);
|
|
210
|
-
});
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
describe('Options handling', () => {
|
|
214
|
-
it('should pass through temperature parameter', () => {
|
|
215
|
-
// Would test that temperature is passed to OpenAI SDK
|
|
216
|
-
expect(true).toBe(true);
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
it('should pass through maxOutputTokens parameter', () => {
|
|
220
|
-
// Would test that max_tokens is passed to OpenAI SDK
|
|
221
|
-
expect(true).toBe(true);
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
it('should handle reasoning mode configuration', () => {
|
|
225
|
-
// Would test reasoning mode parameters
|
|
226
|
-
expect(true).toBe(true);
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
it('should handle abort signal', () => {
|
|
230
|
-
// Would test abort signal is passed through
|
|
231
|
-
expect(true).toBe(true);
|
|
232
|
-
});
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
describe('Error handling', () => {
|
|
236
|
-
it('should handle invalid API key error', () => {
|
|
237
|
-
// Would test API key validation
|
|
238
|
-
expect(true).toBe(true);
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
it('should handle network errors', () => {
|
|
242
|
-
// Would test network error handling
|
|
243
|
-
expect(true).toBe(true);
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
it('should handle rate limit errors', () => {
|
|
247
|
-
// Would test rate limit handling
|
|
248
|
-
expect(true).toBe(true);
|
|
249
|
-
});
|
|
250
|
-
});
|
|
251
|
-
});
|