@output.ai/llm 0.2.13 → 0.3.0-dev.pr341-daa6878
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/package.json +7 -7
- package/src/ai_model.js +24 -6
- package/src/ai_model.spec.js +65 -1
- package/src/ai_sdk.js +66 -43
- package/src/ai_sdk.spec.js +168 -114
- package/src/index.d.ts +109 -30
- package/src/index.js +2 -1
- package/src/prompt_loader_validation.spec.js +9 -88
- package/src/prompt_validations.js +25 -42
- package/src/prompt_validations.spec.js +124 -8
- package/src/validations.js +3 -13
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
2
|
import { ValidationError } from '@output.ai/core';
|
|
3
3
|
import { validatePrompt } from './prompt_validations.js';
|
|
4
4
|
|
|
@@ -65,6 +65,52 @@ describe( 'validatePrompt', () => {
|
|
|
65
65
|
expect( () => validatePrompt( promptWithThinking ) ).not.toThrow();
|
|
66
66
|
} );
|
|
67
67
|
|
|
68
|
+
it( 'should validate a prompt with thinking type disabled', () => {
|
|
69
|
+
const promptWithThinkingDisabled = {
|
|
70
|
+
name: 'thinking-disabled-prompt',
|
|
71
|
+
config: {
|
|
72
|
+
provider: 'anthropic',
|
|
73
|
+
model: 'claude-3-5-sonnet-20241022',
|
|
74
|
+
providerOptions: {
|
|
75
|
+
thinking: {
|
|
76
|
+
type: 'disabled'
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
messages: [
|
|
81
|
+
{
|
|
82
|
+
role: 'user',
|
|
83
|
+
content: 'Simple task.'
|
|
84
|
+
}
|
|
85
|
+
]
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
expect( () => validatePrompt( promptWithThinkingDisabled ) ).not.toThrow();
|
|
89
|
+
} );
|
|
90
|
+
|
|
91
|
+
it( 'should validate a prompt with thinking without budgetTokens', () => {
|
|
92
|
+
const promptWithThinkingNoBudget = {
|
|
93
|
+
name: 'thinking-no-budget',
|
|
94
|
+
config: {
|
|
95
|
+
provider: 'anthropic',
|
|
96
|
+
model: 'claude-3-5-sonnet-20241022',
|
|
97
|
+
providerOptions: {
|
|
98
|
+
thinking: {
|
|
99
|
+
type: 'enabled'
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
messages: [
|
|
104
|
+
{
|
|
105
|
+
role: 'user',
|
|
106
|
+
content: 'Think about this.'
|
|
107
|
+
}
|
|
108
|
+
]
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
expect( () => validatePrompt( promptWithThinkingNoBudget ) ).not.toThrow();
|
|
112
|
+
} );
|
|
113
|
+
|
|
68
114
|
it( 'should validate a prompt with anthropic-specific providerOptions', () => {
|
|
69
115
|
const promptWithAnthropicOptions = {
|
|
70
116
|
name: 'anthropic-options-prompt',
|
|
@@ -174,11 +220,50 @@ describe( 'validatePrompt', () => {
|
|
|
174
220
|
expect( () => validatePrompt( promptWithMixedOptions ) ).not.toThrow();
|
|
175
221
|
} );
|
|
176
222
|
|
|
177
|
-
it( 'should
|
|
178
|
-
const
|
|
179
|
-
name: '
|
|
223
|
+
it( 'should accept custom provider names for dynamic providers', () => {
|
|
224
|
+
const customProviderPrompt = {
|
|
225
|
+
name: 'custom-provider-prompt',
|
|
226
|
+
config: {
|
|
227
|
+
provider: 'my-custom-provider',
|
|
228
|
+
model: 'custom-model-v1'
|
|
229
|
+
},
|
|
230
|
+
messages: [
|
|
231
|
+
{
|
|
232
|
+
role: 'user',
|
|
233
|
+
content: 'Test'
|
|
234
|
+
}
|
|
235
|
+
]
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
expect( () => validatePrompt( customProviderPrompt ) ).not.toThrow();
|
|
239
|
+
} );
|
|
240
|
+
|
|
241
|
+
it( 'should accept extra config fields via passthrough', () => {
|
|
242
|
+
const extraFieldsPrompt = {
|
|
243
|
+
name: 'extra-fields-prompt',
|
|
244
|
+
config: {
|
|
245
|
+
provider: 'openai',
|
|
246
|
+
model: 'gpt-4',
|
|
247
|
+
topP: 0.9,
|
|
248
|
+
seed: 42,
|
|
249
|
+
stopSequences: [ 'END' ]
|
|
250
|
+
},
|
|
251
|
+
messages: [
|
|
252
|
+
{
|
|
253
|
+
role: 'user',
|
|
254
|
+
content: 'Test'
|
|
255
|
+
}
|
|
256
|
+
]
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
expect( () => validatePrompt( extraFieldsPrompt ) ).not.toThrow();
|
|
260
|
+
} );
|
|
261
|
+
|
|
262
|
+
it( 'should throw ValidationError when provider is empty string', () => {
|
|
263
|
+
const emptyProviderPrompt = {
|
|
264
|
+
name: 'empty-provider',
|
|
180
265
|
config: {
|
|
181
|
-
provider: '
|
|
266
|
+
provider: '',
|
|
182
267
|
model: 'some-model'
|
|
183
268
|
},
|
|
184
269
|
messages: [
|
|
@@ -189,7 +274,7 @@ describe( 'validatePrompt', () => {
|
|
|
189
274
|
]
|
|
190
275
|
};
|
|
191
276
|
|
|
192
|
-
expect( () => validatePrompt(
|
|
277
|
+
expect( () => validatePrompt( emptyProviderPrompt ) ).toThrow( ValidationError );
|
|
193
278
|
} );
|
|
194
279
|
|
|
195
280
|
it( 'should throw ValidationError when required fields are missing', () => {
|
|
@@ -209,7 +294,38 @@ describe( 'validatePrompt', () => {
|
|
|
209
294
|
expect( () => validatePrompt( missingNamePrompt ) ).toThrow( ValidationError );
|
|
210
295
|
} );
|
|
211
296
|
|
|
212
|
-
it( 'should
|
|
297
|
+
it( 'should pass through budget_tokens in thinking and warn about snake_case', () => {
|
|
298
|
+
const warnSpy = vi.spyOn( console, 'warn' ).mockImplementation( () => {} );
|
|
299
|
+
|
|
300
|
+
const promptWithBudgetTokensSnake = {
|
|
301
|
+
name: 'thinking-budget-snake',
|
|
302
|
+
config: {
|
|
303
|
+
provider: 'anthropic',
|
|
304
|
+
model: 'claude-sonnet-4-20250514',
|
|
305
|
+
providerOptions: {
|
|
306
|
+
thinking: {
|
|
307
|
+
type: 'enabled',
|
|
308
|
+
budget_tokens: 10000
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
},
|
|
312
|
+
messages: [
|
|
313
|
+
{
|
|
314
|
+
role: 'user',
|
|
315
|
+
content: 'Think hard.'
|
|
316
|
+
}
|
|
317
|
+
]
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
expect( () => validatePrompt( promptWithBudgetTokensSnake ) ).not.toThrow();
|
|
321
|
+
expect( warnSpy ).toHaveBeenCalledWith(
|
|
322
|
+
'[output-llm] "budget_tokens" found in providerOptions.thinking. Did you mean "budgetTokens"?'
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
warnSpy.mockRestore();
|
|
326
|
+
} );
|
|
327
|
+
|
|
328
|
+
it( 'should allow snake_case fields in config via passthrough (no longer strict)', () => {
|
|
213
329
|
const maxTokensSnakeCase = {
|
|
214
330
|
name: 'test-prompt',
|
|
215
331
|
config: {
|
|
@@ -225,6 +341,6 @@ describe( 'validatePrompt', () => {
|
|
|
225
341
|
]
|
|
226
342
|
};
|
|
227
343
|
|
|
228
|
-
expect( () => validatePrompt( maxTokensSnakeCase ) ).toThrow(
|
|
344
|
+
expect( () => validatePrompt( maxTokensSnakeCase ) ).not.toThrow();
|
|
229
345
|
} );
|
|
230
346
|
} );
|
package/src/validations.js
CHANGED
|
@@ -5,17 +5,7 @@ const generateTextArgsSchema = z.object( {
|
|
|
5
5
|
variables: z.any().optional()
|
|
6
6
|
} );
|
|
7
7
|
|
|
8
|
-
const
|
|
9
|
-
prompt: z.string(),
|
|
10
|
-
variables: z.any().optional(),
|
|
11
|
-
schema: z.custom( v => v instanceof z.ZodObject, {
|
|
12
|
-
message: 'schema must be a ZodObject'
|
|
13
|
-
} ),
|
|
14
|
-
schemaName: z.string().optional(),
|
|
15
|
-
schemaDescription: z.string().optional()
|
|
16
|
-
} );
|
|
17
|
-
|
|
18
|
-
const generateArrayArgsSchema = z.object( {
|
|
8
|
+
const generateSchemaArgsSchema = z.object( {
|
|
19
9
|
prompt: z.string(),
|
|
20
10
|
variables: z.any().optional(),
|
|
21
11
|
schema: z.custom( v => v instanceof z.ZodType, {
|
|
@@ -43,11 +33,11 @@ export function validateGenerateTextArgs( args ) {
|
|
|
43
33
|
}
|
|
44
34
|
|
|
45
35
|
export function validateGenerateObjectArgs( args ) {
|
|
46
|
-
validateSchema(
|
|
36
|
+
validateSchema( generateSchemaArgsSchema, args, 'Invalid generateObject() arguments' );
|
|
47
37
|
}
|
|
48
38
|
|
|
49
39
|
export function validateGenerateArrayArgs( args ) {
|
|
50
|
-
validateSchema(
|
|
40
|
+
validateSchema( generateSchemaArgsSchema, args, 'Invalid generateArray() arguments' );
|
|
51
41
|
}
|
|
52
42
|
|
|
53
43
|
export function validateGenerateEnumArgs( args ) {
|