@pwshub/aisdk 0.0.3 → 0.0.5
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 +139 -42
- package/index.d.ts +18 -8
- package/package.json +8 -6
- package/src/coerce.test.js +142 -0
- package/src/config.js +30 -0
- package/src/config.test.js +142 -0
- package/src/errors.js +1 -1
- package/src/index.js +26 -14
- package/src/models.js +401 -0
- package/src/providers.js +109 -11
- package/src/registry.js +114 -37
- package/src/registry.test.js +314 -0
- package/src/validation.js +9 -3
- package/src/validation.test.js +410 -0
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Tests for validation module.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
describe, it,
|
|
7
|
+
} from 'node:test'
|
|
8
|
+
import assert from 'node:assert'
|
|
9
|
+
import { validateAskOptions } from '../src/validation.js'
|
|
10
|
+
|
|
11
|
+
describe('validateAskOptions', () => {
|
|
12
|
+
describe('required fields', () => {
|
|
13
|
+
it('should pass with valid model, apikey and prompt', () => {
|
|
14
|
+
assert.doesNotThrow(() => {
|
|
15
|
+
validateAskOptions({
|
|
16
|
+
model: 'gpt-4o', apikey: 'test-key', prompt: 'Hello',
|
|
17
|
+
})
|
|
18
|
+
})
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('should throw when model is missing', () => {
|
|
22
|
+
assert.throws(
|
|
23
|
+
() => validateAskOptions({
|
|
24
|
+
apikey: 'test-key', prompt: 'Hello',
|
|
25
|
+
}),
|
|
26
|
+
/"model" must be a non-empty string/
|
|
27
|
+
)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('should throw when model is empty string', () => {
|
|
31
|
+
assert.throws(
|
|
32
|
+
() => validateAskOptions({
|
|
33
|
+
model: '', apikey: 'test-key', prompt: 'Hello',
|
|
34
|
+
}),
|
|
35
|
+
/"model" must be a non-empty string/
|
|
36
|
+
)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('should throw when model is not a string', () => {
|
|
40
|
+
assert.throws(
|
|
41
|
+
() => validateAskOptions({
|
|
42
|
+
model: 123, apikey: 'test-key', prompt: 'Hello',
|
|
43
|
+
}),
|
|
44
|
+
/"model" must be a non-empty string/
|
|
45
|
+
)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('should throw when apikey is missing', () => {
|
|
49
|
+
assert.throws(
|
|
50
|
+
() => validateAskOptions({
|
|
51
|
+
model: 'gpt-4o', prompt: 'Hello',
|
|
52
|
+
}),
|
|
53
|
+
/"apikey" must be a non-empty string/
|
|
54
|
+
)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('should throw when apikey is empty string', () => {
|
|
58
|
+
assert.throws(
|
|
59
|
+
() => validateAskOptions({
|
|
60
|
+
model: 'gpt-4o', apikey: '', prompt: 'Hello',
|
|
61
|
+
}),
|
|
62
|
+
/"apikey" must be a non-empty string/
|
|
63
|
+
)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it('should throw when apikey is not a string', () => {
|
|
67
|
+
assert.throws(
|
|
68
|
+
() => validateAskOptions({
|
|
69
|
+
model: 'gpt-4o', apikey: 123, prompt: 'Hello',
|
|
70
|
+
}),
|
|
71
|
+
/"apikey" must be a non-empty string/
|
|
72
|
+
)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('should throw when prompt and messages are both missing', () => {
|
|
76
|
+
assert.throws(
|
|
77
|
+
() => validateAskOptions({
|
|
78
|
+
model: 'gpt-4o', apikey: 'test-key',
|
|
79
|
+
}),
|
|
80
|
+
/either "prompt" or "messages" must be provided/
|
|
81
|
+
)
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('should pass with empty string prompt', () => {
|
|
85
|
+
assert.doesNotThrow(
|
|
86
|
+
() => validateAskOptions({
|
|
87
|
+
model: 'gpt-4o', apikey: 'test-key', prompt: '',
|
|
88
|
+
})
|
|
89
|
+
)
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it('should throw when prompt is not a string', () => {
|
|
93
|
+
assert.throws(
|
|
94
|
+
() => validateAskOptions({
|
|
95
|
+
model: 'gpt-4o', apikey: 'test-key', prompt: 123,
|
|
96
|
+
}),
|
|
97
|
+
/"prompt" must be a string/
|
|
98
|
+
)
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
it('should pass with valid messages array', () => {
|
|
102
|
+
assert.doesNotThrow(
|
|
103
|
+
() => validateAskOptions({
|
|
104
|
+
model: 'gpt-4o',
|
|
105
|
+
apikey: 'test-key',
|
|
106
|
+
messages: [
|
|
107
|
+
{ role: 'user', content: 'Hello' },
|
|
108
|
+
{ role: 'assistant', content: 'Hi there!' },
|
|
109
|
+
],
|
|
110
|
+
})
|
|
111
|
+
)
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
it('should throw when messages is not an array', () => {
|
|
115
|
+
assert.throws(
|
|
116
|
+
() => validateAskOptions({
|
|
117
|
+
model: 'gpt-4o', apikey: 'test-key', messages: 'not-array',
|
|
118
|
+
}),
|
|
119
|
+
/"messages" must be an array/
|
|
120
|
+
)
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
it('should throw when messages has invalid role', () => {
|
|
124
|
+
assert.throws(
|
|
125
|
+
() => validateAskOptions({
|
|
126
|
+
model: 'gpt-4o',
|
|
127
|
+
apikey: 'test-key',
|
|
128
|
+
messages: [{ role: 'invalid', content: 'test' }],
|
|
129
|
+
}),
|
|
130
|
+
/messages\[0\]\.role must be 'user', 'assistant', or 'system'/
|
|
131
|
+
)
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
it('should throw when messages has missing content', () => {
|
|
135
|
+
assert.throws(
|
|
136
|
+
() => validateAskOptions({
|
|
137
|
+
model: 'gpt-4o',
|
|
138
|
+
apikey: 'test-key',
|
|
139
|
+
messages: [{ role: 'user' }],
|
|
140
|
+
}),
|
|
141
|
+
/messages\[0\]\.content must be a string/
|
|
142
|
+
)
|
|
143
|
+
})
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
describe('optional string fields', () => {
|
|
147
|
+
it('should pass with valid system prompt', () => {
|
|
148
|
+
assert.doesNotThrow(() => {
|
|
149
|
+
validateAskOptions({
|
|
150
|
+
model: 'gpt-4o', apikey: 'test-key',
|
|
151
|
+
prompt: 'Hello',
|
|
152
|
+
system: 'You are helpful',
|
|
153
|
+
})
|
|
154
|
+
})
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
it('should throw when system is not a string', () => {
|
|
158
|
+
assert.throws(
|
|
159
|
+
() => validateAskOptions({
|
|
160
|
+
model: 'gpt-4o', apikey: 'test-key',
|
|
161
|
+
prompt: 'Hello',
|
|
162
|
+
system: 123,
|
|
163
|
+
}),
|
|
164
|
+
/"system" must be a string/
|
|
165
|
+
)
|
|
166
|
+
})
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
describe('optional number fields', () => {
|
|
170
|
+
it('should pass with valid temperature', () => {
|
|
171
|
+
assert.doesNotThrow(() => {
|
|
172
|
+
validateAskOptions({
|
|
173
|
+
model: 'gpt-4o', apikey: 'test-key',
|
|
174
|
+
prompt: 'Hello',
|
|
175
|
+
temperature: 0.5,
|
|
176
|
+
})
|
|
177
|
+
})
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
it('should throw when temperature is not a number', () => {
|
|
181
|
+
assert.throws(
|
|
182
|
+
() => validateAskOptions({
|
|
183
|
+
model: 'gpt-4o', apikey: 'test-key',
|
|
184
|
+
prompt: 'Hello',
|
|
185
|
+
temperature: 'hot',
|
|
186
|
+
}),
|
|
187
|
+
/"temperature" must be a number/
|
|
188
|
+
)
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
it('should pass with valid maxTokens', () => {
|
|
192
|
+
assert.doesNotThrow(() => {
|
|
193
|
+
validateAskOptions({
|
|
194
|
+
model: 'gpt-4o', apikey: 'test-key',
|
|
195
|
+
prompt: 'Hello',
|
|
196
|
+
maxTokens: 100,
|
|
197
|
+
})
|
|
198
|
+
})
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
it('should throw when maxTokens is not a number', () => {
|
|
202
|
+
assert.throws(
|
|
203
|
+
() => validateAskOptions({
|
|
204
|
+
model: 'gpt-4o', apikey: 'test-key',
|
|
205
|
+
prompt: 'Hello',
|
|
206
|
+
maxTokens: '100',
|
|
207
|
+
}),
|
|
208
|
+
/"maxTokens" must be a number/
|
|
209
|
+
)
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
it('should pass with all optional number fields', () => {
|
|
213
|
+
assert.doesNotThrow(() => {
|
|
214
|
+
validateAskOptions({
|
|
215
|
+
model: 'gpt-4o', apikey: 'test-key',
|
|
216
|
+
prompt: 'Hello',
|
|
217
|
+
temperature: 0.5,
|
|
218
|
+
maxTokens: 100,
|
|
219
|
+
topP: 0.9,
|
|
220
|
+
topK: 50,
|
|
221
|
+
frequencyPenalty: 0.5,
|
|
222
|
+
presencePenalty: 0.5,
|
|
223
|
+
seed: 42,
|
|
224
|
+
})
|
|
225
|
+
})
|
|
226
|
+
})
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
describe('messages array', () => {
|
|
230
|
+
it('should pass with valid messages', () => {
|
|
231
|
+
assert.doesNotThrow(() => {
|
|
232
|
+
validateAskOptions({
|
|
233
|
+
model: 'gpt-4o', apikey: 'test-key',
|
|
234
|
+
prompt: 'Hello',
|
|
235
|
+
messages: [
|
|
236
|
+
{
|
|
237
|
+
role: 'user', content: 'Hi',
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
role: 'assistant', content: 'Hello!',
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
role: 'system', content: 'Be helpful',
|
|
244
|
+
},
|
|
245
|
+
],
|
|
246
|
+
})
|
|
247
|
+
})
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
it('should throw when messages is not an array', () => {
|
|
251
|
+
assert.throws(
|
|
252
|
+
() => validateAskOptions({
|
|
253
|
+
model: 'gpt-4o', apikey: 'test-key',
|
|
254
|
+
prompt: 'Hello',
|
|
255
|
+
messages: 'not an array',
|
|
256
|
+
}),
|
|
257
|
+
/"messages" must be an array/
|
|
258
|
+
)
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
it('should throw when message role is invalid', () => {
|
|
262
|
+
assert.throws(
|
|
263
|
+
() => validateAskOptions({
|
|
264
|
+
model: 'gpt-4o', apikey: 'test-key',
|
|
265
|
+
prompt: 'Hello',
|
|
266
|
+
messages: [{
|
|
267
|
+
role: 'bot', content: 'Hi',
|
|
268
|
+
}],
|
|
269
|
+
}),
|
|
270
|
+
/messages\[0\].role must be 'user', 'assistant', or 'system'/
|
|
271
|
+
)
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
it('should throw when message content is not a string', () => {
|
|
275
|
+
assert.throws(
|
|
276
|
+
() => validateAskOptions({
|
|
277
|
+
model: 'gpt-4o', apikey: 'test-key',
|
|
278
|
+
prompt: 'Hello',
|
|
279
|
+
messages: [{
|
|
280
|
+
role: 'user', content: 123,
|
|
281
|
+
}],
|
|
282
|
+
}),
|
|
283
|
+
/messages\[0\].content must be a string/
|
|
284
|
+
)
|
|
285
|
+
})
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
describe('fallbacks array', () => {
|
|
289
|
+
it('should pass with valid fallbacks', () => {
|
|
290
|
+
assert.doesNotThrow(() => {
|
|
291
|
+
validateAskOptions({
|
|
292
|
+
model: 'gpt-4o', apikey: 'test-key',
|
|
293
|
+
prompt: 'Hello',
|
|
294
|
+
fallbacks: ['gpt-4o-mini', 'claude-sonnet'],
|
|
295
|
+
})
|
|
296
|
+
})
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
it('should throw when fallbacks is not an array', () => {
|
|
300
|
+
assert.throws(
|
|
301
|
+
() => validateAskOptions({
|
|
302
|
+
model: 'gpt-4o', apikey: 'test-key',
|
|
303
|
+
prompt: 'Hello',
|
|
304
|
+
fallbacks: 'not an array',
|
|
305
|
+
}),
|
|
306
|
+
/"fallbacks" must be an array/
|
|
307
|
+
)
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
it('should throw when fallback item is not a string', () => {
|
|
311
|
+
assert.throws(
|
|
312
|
+
() => validateAskOptions({
|
|
313
|
+
model: 'gpt-4o', apikey: 'test-key',
|
|
314
|
+
prompt: 'Hello',
|
|
315
|
+
fallbacks: [123],
|
|
316
|
+
}),
|
|
317
|
+
/fallbacks\[0\] must be a string/
|
|
318
|
+
)
|
|
319
|
+
})
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
describe('stop field', () => {
|
|
323
|
+
it('should pass with string stop', () => {
|
|
324
|
+
assert.doesNotThrow(() => {
|
|
325
|
+
validateAskOptions({
|
|
326
|
+
model: 'gpt-4o', apikey: 'test-key',
|
|
327
|
+
prompt: 'Hello',
|
|
328
|
+
stop: 'END',
|
|
329
|
+
})
|
|
330
|
+
})
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
it('should pass with array of strings stop', () => {
|
|
334
|
+
assert.doesNotThrow(() => {
|
|
335
|
+
validateAskOptions({
|
|
336
|
+
model: 'gpt-4o', apikey: 'test-key',
|
|
337
|
+
prompt: 'Hello',
|
|
338
|
+
stop: ['END', 'STOP'],
|
|
339
|
+
})
|
|
340
|
+
})
|
|
341
|
+
})
|
|
342
|
+
|
|
343
|
+
it('should throw when stop is not string or array', () => {
|
|
344
|
+
assert.throws(
|
|
345
|
+
() => validateAskOptions({
|
|
346
|
+
model: 'gpt-4o', apikey: 'test-key',
|
|
347
|
+
prompt: 'Hello',
|
|
348
|
+
stop: 123,
|
|
349
|
+
}),
|
|
350
|
+
/"stop" must be a string or array of strings/
|
|
351
|
+
)
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
it('should throw when stop array contains non-string', () => {
|
|
355
|
+
assert.throws(
|
|
356
|
+
() => validateAskOptions({
|
|
357
|
+
model: 'gpt-4o', apikey: 'test-key',
|
|
358
|
+
prompt: 'Hello',
|
|
359
|
+
stop: ['END', 123],
|
|
360
|
+
}),
|
|
361
|
+
/stop\[1\] must be a string/
|
|
362
|
+
)
|
|
363
|
+
})
|
|
364
|
+
})
|
|
365
|
+
|
|
366
|
+
describe('providerOptions field', () => {
|
|
367
|
+
it('should pass with valid providerOptions object', () => {
|
|
368
|
+
assert.doesNotThrow(() => {
|
|
369
|
+
validateAskOptions({
|
|
370
|
+
model: 'gpt-4o', apikey: 'test-key',
|
|
371
|
+
prompt: 'Hello',
|
|
372
|
+
providerOptions: { safetySettings: [] },
|
|
373
|
+
})
|
|
374
|
+
})
|
|
375
|
+
})
|
|
376
|
+
|
|
377
|
+
it('should throw when providerOptions is not an object', () => {
|
|
378
|
+
assert.throws(
|
|
379
|
+
() => validateAskOptions({
|
|
380
|
+
model: 'gpt-4o', apikey: 'test-key',
|
|
381
|
+
prompt: 'Hello',
|
|
382
|
+
providerOptions: 'not an object',
|
|
383
|
+
}),
|
|
384
|
+
/"providerOptions" must be an object/
|
|
385
|
+
)
|
|
386
|
+
})
|
|
387
|
+
|
|
388
|
+
it('should throw when providerOptions is an array', () => {
|
|
389
|
+
assert.throws(
|
|
390
|
+
() => validateAskOptions({
|
|
391
|
+
model: 'gpt-4o', apikey: 'test-key',
|
|
392
|
+
prompt: 'Hello',
|
|
393
|
+
providerOptions: [],
|
|
394
|
+
}),
|
|
395
|
+
/"providerOptions" must be an object/
|
|
396
|
+
)
|
|
397
|
+
})
|
|
398
|
+
|
|
399
|
+
it('should throw when providerOptions is null', () => {
|
|
400
|
+
assert.throws(
|
|
401
|
+
() => validateAskOptions({
|
|
402
|
+
model: 'gpt-4o', apikey: 'test-key',
|
|
403
|
+
prompt: 'Hello',
|
|
404
|
+
providerOptions: null,
|
|
405
|
+
}),
|
|
406
|
+
/"providerOptions" must be an object/
|
|
407
|
+
)
|
|
408
|
+
})
|
|
409
|
+
})
|
|
410
|
+
})
|