@output.ai/llm 0.2.3 → 0.2.4
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 +1 -1
- package/src/ai_sdk.js +8 -0
- package/src/index.d.ts +0 -3
- package/src/parser.spec.js +63 -0
- package/src/prompt_loader.spec.js +17 -0
- package/src/prompt_loader_validation.spec.js +174 -0
- package/src/prompt_validations.js +26 -6
- package/src/prompt_validations.spec.js +121 -0
package/package.json
CHANGED
package/src/ai_sdk.js
CHANGED
|
@@ -61,6 +61,8 @@ const extraAiSdkOptionsFromPrompt = prompt => {
|
|
|
61
61
|
* @param {object} args - Generation arguments
|
|
62
62
|
* @param {string} args.prompt - Prompt file name
|
|
63
63
|
* @param {Record<string, string | number>} [args.variables] - Variables to interpolate
|
|
64
|
+
* @throws {ValidationError} If the prompt config is invalid (e.g., snake_case fields)
|
|
65
|
+
* @throws {FatalError} If the prompt file is not found or template rendering fails
|
|
64
66
|
* @returns {Promise<string>} Generated text
|
|
65
67
|
*/
|
|
66
68
|
export async function generateText( { prompt, variables } ) {
|
|
@@ -83,6 +85,8 @@ export async function generateText( { prompt, variables } ) {
|
|
|
83
85
|
* @param {z.ZodObject} args.schema - Output schema
|
|
84
86
|
* @param {string} [args.schemaName] - Output schema name
|
|
85
87
|
* @param {string} [args.schemaDescription] - Output schema description
|
|
88
|
+
* @throws {ValidationError} If the prompt config is invalid (e.g., snake_case fields)
|
|
89
|
+
* @throws {FatalError} If the prompt file is not found or template rendering fails
|
|
86
90
|
* @returns {Promise<object>} Object matching the provided schema
|
|
87
91
|
*/
|
|
88
92
|
export async function generateObject( args ) {
|
|
@@ -112,6 +116,8 @@ export async function generateObject( args ) {
|
|
|
112
116
|
* @param {z.ZodType} args.schema - Output schema (array item)
|
|
113
117
|
* @param {string} [args.schemaName] - Output schema name
|
|
114
118
|
* @param {string} [args.schemaDescription] - Output schema description
|
|
119
|
+
* @throws {ValidationError} If the prompt config is invalid (e.g., snake_case fields)
|
|
120
|
+
* @throws {FatalError} If the prompt file is not found or template rendering fails
|
|
115
121
|
* @returns {Promise<object>} Array where each element matches the schema
|
|
116
122
|
*/
|
|
117
123
|
export async function generateArray( args ) {
|
|
@@ -139,6 +145,8 @@ export async function generateArray( args ) {
|
|
|
139
145
|
* @param {string} args.prompt - Prompt file name
|
|
140
146
|
* @param {Record<string, string | number>} [args.variables] - Variables to interpolate
|
|
141
147
|
* @param {string[]} args.enum - Allowed values for the generation
|
|
148
|
+
* @throws {ValidationError} If the prompt config is invalid (e.g., snake_case fields)
|
|
149
|
+
* @throws {FatalError} If the prompt file is not found or template rendering fails
|
|
142
150
|
* @returns {Promise<string>} One of the provided enum values
|
|
143
151
|
*/
|
|
144
152
|
export async function generateEnum( args ) {
|
package/src/index.d.ts
CHANGED
|
@@ -47,9 +47,6 @@ export type Prompt = {
|
|
|
47
47
|
/** Maximum tokens in the response */
|
|
48
48
|
maxTokens?: number;
|
|
49
49
|
|
|
50
|
-
/** Additional provider-specific options */
|
|
51
|
-
options?: Record<string, Record<string, JSONValue>>;
|
|
52
|
-
|
|
53
50
|
/** Provider-specific configurations */
|
|
54
51
|
providerOptions?: Record<string, unknown>;
|
|
55
52
|
};
|
package/src/parser.spec.js
CHANGED
|
@@ -56,4 +56,67 @@ This is just plain text without any message tags.`;
|
|
|
56
56
|
parsePrompt( raw );
|
|
57
57
|
} ).toThrow( /Expected format/ );
|
|
58
58
|
} );
|
|
59
|
+
|
|
60
|
+
it( 'should use providerOptions with budgetTokens in camelCase', () => {
|
|
61
|
+
// Frontmatter uses canonical format: providerOptions.thinking.budgetTokens
|
|
62
|
+
const raw = `---
|
|
63
|
+
provider: anthropic
|
|
64
|
+
model: claude-sonnet-4-20250514
|
|
65
|
+
temperature: 0.7
|
|
66
|
+
maxTokens: 64000
|
|
67
|
+
providerOptions:
|
|
68
|
+
thinking:
|
|
69
|
+
type: enabled
|
|
70
|
+
budgetTokens: 1500
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
<user>Test</user>`;
|
|
74
|
+
|
|
75
|
+
const result = parsePrompt( raw );
|
|
76
|
+
|
|
77
|
+
expect( result.config.provider ).toBe( 'anthropic' );
|
|
78
|
+
expect( result.config.model ).toBe( 'claude-sonnet-4-20250514' );
|
|
79
|
+
expect( result.config.temperature ).toBe( 0.7 );
|
|
80
|
+
expect( result.config.maxTokens ).toBe( 64000 );
|
|
81
|
+
|
|
82
|
+
// Now expects canonical schema format in front-matter
|
|
83
|
+
expect( result.config.providerOptions ).toBeDefined();
|
|
84
|
+
expect( result.config.providerOptions.thinking ).toBeDefined();
|
|
85
|
+
expect( result.config.providerOptions.thinking.type ).toBe( 'enabled' );
|
|
86
|
+
expect( result.config.providerOptions.thinking.budgetTokens ).toBe( 1500 );
|
|
87
|
+
} );
|
|
88
|
+
|
|
89
|
+
it( 'should parse snake_case fields as-is (validation catches them later)', () => {
|
|
90
|
+
// Parser extracts fields as-is from frontmatter; validation happens later
|
|
91
|
+
const raw = `---
|
|
92
|
+
provider: anthropic
|
|
93
|
+
model: claude-sonnet-4-20250514
|
|
94
|
+
temperature: 0.7
|
|
95
|
+
max_tokens: 64000
|
|
96
|
+
providerOptions:
|
|
97
|
+
thinking:
|
|
98
|
+
type: enabled
|
|
99
|
+
budget_tokens: 1500
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
<user>Test</user>`;
|
|
103
|
+
|
|
104
|
+
const result = parsePrompt( raw );
|
|
105
|
+
|
|
106
|
+
// Parser extracts snake_case fields as-is
|
|
107
|
+
expect( result.config.provider ).toBe( 'anthropic' );
|
|
108
|
+
expect( result.config.model ).toBe( 'claude-sonnet-4-20250514' );
|
|
109
|
+
expect( result.config.temperature ).toBe( 0.7 );
|
|
110
|
+
// snake_case preserved here because we're only calling parsePrompt, not validatePrompt
|
|
111
|
+
expect( result.config.max_tokens ).toBe( 64000 );
|
|
112
|
+
// camelCase not set because we only call parsePrompt
|
|
113
|
+
expect( result.config.maxTokens ).toBeUndefined();
|
|
114
|
+
|
|
115
|
+
// Snake_case fields in nested objects also preserved
|
|
116
|
+
expect( result.config.providerOptions ).toBeDefined();
|
|
117
|
+
expect( result.config.providerOptions.thinking ).toBeDefined();
|
|
118
|
+
expect( result.config.providerOptions.thinking.type ).toBe( 'enabled' );
|
|
119
|
+
expect( result.config.providerOptions.thinking.budget_tokens ).toBe( 1500 ); // snake_case preserved
|
|
120
|
+
expect( result.config.providerOptions.thinking.budgetTokens ).toBeUndefined(); // camelCase not set
|
|
121
|
+
} );
|
|
59
122
|
} );
|
|
@@ -129,4 +129,21 @@ temperature: 0.7
|
|
|
129
129
|
expect( result.config.model ).toBe( 'claude-sonnet-4' );
|
|
130
130
|
} );
|
|
131
131
|
|
|
132
|
+
it( 'should use camelCase config keys', () => {
|
|
133
|
+
const promptContent = `---
|
|
134
|
+
provider: anthropic
|
|
135
|
+
model: claude-3-5-sonnet-20241022
|
|
136
|
+
maxTokens: 1024
|
|
137
|
+
temperature: 0.7
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
<user>Hello</user>`;
|
|
141
|
+
|
|
142
|
+
loadContent.mockReturnValue( promptContent );
|
|
143
|
+
|
|
144
|
+
const result = loadPrompt( 'test', {} );
|
|
145
|
+
|
|
146
|
+
expect( result.config.maxTokens ).toBe( 1024 );
|
|
147
|
+
} );
|
|
148
|
+
|
|
132
149
|
} );
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { loadPrompt } from './prompt_loader.js';
|
|
3
|
+
|
|
4
|
+
vi.mock( './load_content.js', () => ( {
|
|
5
|
+
loadContent: vi.fn()
|
|
6
|
+
} ) );
|
|
7
|
+
|
|
8
|
+
import { loadContent } from './load_content.js';
|
|
9
|
+
import { ValidationError } from '@output.ai/core';
|
|
10
|
+
|
|
11
|
+
describe( 'loadPrompt - validation with real schema', () => {
|
|
12
|
+
beforeEach( () => {
|
|
13
|
+
vi.clearAllMocks();
|
|
14
|
+
} );
|
|
15
|
+
|
|
16
|
+
it( 'should accept valid camelCase format with providerOptions and budgetTokens', () => {
|
|
17
|
+
// Frontmatter uses canonical format: providerOptions.thinking.budgetTokens
|
|
18
|
+
const promptContent = `---
|
|
19
|
+
provider: anthropic
|
|
20
|
+
model: claude-sonnet-4-20250514
|
|
21
|
+
temperature: 0.7
|
|
22
|
+
maxTokens: 64000
|
|
23
|
+
providerOptions:
|
|
24
|
+
thinking:
|
|
25
|
+
type: enabled
|
|
26
|
+
budgetTokens: 1500
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
<user>Hello</user>`;
|
|
30
|
+
|
|
31
|
+
loadContent.mockReturnValue( promptContent );
|
|
32
|
+
|
|
33
|
+
const result = loadPrompt( 'test', {} );
|
|
34
|
+
|
|
35
|
+
// Validation passes with canonical schema format
|
|
36
|
+
expect( result.config.providerOptions ).toBeDefined();
|
|
37
|
+
expect( result.config.providerOptions.thinking.type ).toBe( 'enabled' );
|
|
38
|
+
expect( result.config.providerOptions.thinking.budgetTokens ).toBe( 1500 );
|
|
39
|
+
} );
|
|
40
|
+
|
|
41
|
+
it( 'should REJECT snake_case max_tokens (not canonical)', () => {
|
|
42
|
+
// Using snake_case max_tokens should fail validation
|
|
43
|
+
const promptContent = `---
|
|
44
|
+
provider: anthropic
|
|
45
|
+
model: claude-sonnet-4-20250514
|
|
46
|
+
max_tokens: 64000
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
<user>Hello</user>`;
|
|
50
|
+
|
|
51
|
+
loadContent.mockReturnValue( promptContent );
|
|
52
|
+
|
|
53
|
+
// loadPrompt should throw ValidationError because schema should reject max_tokens.
|
|
54
|
+
// Other tests check the actual message, but this one specifically checks that we throw
|
|
55
|
+
// the right kind of error, just to check that as well
|
|
56
|
+
expect( () => {
|
|
57
|
+
loadPrompt( 'test', {} );
|
|
58
|
+
} ).toThrow( ValidationError );
|
|
59
|
+
expect( () => {
|
|
60
|
+
loadPrompt( 'test', {} );
|
|
61
|
+
} ).toThrow( /max_tokens/ );
|
|
62
|
+
} );
|
|
63
|
+
|
|
64
|
+
it( 'should REJECT deprecated "options" field (use providerOptions)', () => {
|
|
65
|
+
// Using 'options' instead of 'providerOptions' should fail validation
|
|
66
|
+
const promptContent = `---
|
|
67
|
+
provider: anthropic
|
|
68
|
+
model: claude-sonnet-4-20250514
|
|
69
|
+
options:
|
|
70
|
+
thinking:
|
|
71
|
+
type: enabled
|
|
72
|
+
budgetTokens: 1500
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
<user>Hello</user>`;
|
|
76
|
+
|
|
77
|
+
loadContent.mockReturnValue( promptContent );
|
|
78
|
+
|
|
79
|
+
// loadPrompt should throw ValidationError because schema should reject 'options'
|
|
80
|
+
expect( () => {
|
|
81
|
+
loadPrompt( 'test', {} );
|
|
82
|
+
} ).toThrow( /Invalid prompt file/ );
|
|
83
|
+
} );
|
|
84
|
+
|
|
85
|
+
it( 'should REJECT snake_case budget_tokens in providerOptions', () => {
|
|
86
|
+
// Using snake_case budget_tokens should fail validation
|
|
87
|
+
const promptContent = `---
|
|
88
|
+
provider: anthropic
|
|
89
|
+
model: claude-sonnet-4-20250514
|
|
90
|
+
providerOptions:
|
|
91
|
+
thinking:
|
|
92
|
+
type: enabled
|
|
93
|
+
budget_tokens: 1500
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
<user>Hello</user>`;
|
|
97
|
+
|
|
98
|
+
loadContent.mockReturnValue( promptContent );
|
|
99
|
+
|
|
100
|
+
// loadPrompt should throw ValidationError because schema rejects budget_tokens
|
|
101
|
+
expect( () => {
|
|
102
|
+
loadPrompt( 'test', {} );
|
|
103
|
+
} ).toThrow( /Invalid prompt file/ );
|
|
104
|
+
} );
|
|
105
|
+
|
|
106
|
+
it( 'end-to-end: should accept canonical camelCase format', () => {
|
|
107
|
+
// End-to-end test: frontmatter uses canonical providerOptions.thinking.budgetTokens
|
|
108
|
+
const promptContent = `---
|
|
109
|
+
provider: anthropic
|
|
110
|
+
model: claude-sonnet-4-20250514
|
|
111
|
+
temperature: 0.7
|
|
112
|
+
maxTokens: 64000
|
|
113
|
+
providerOptions:
|
|
114
|
+
thinking:
|
|
115
|
+
type: enabled
|
|
116
|
+
budgetTokens: 1500
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
<user>Hello</user>`;
|
|
120
|
+
|
|
121
|
+
loadContent.mockReturnValue( promptContent );
|
|
122
|
+
|
|
123
|
+
const result = loadPrompt( 'test', {} );
|
|
124
|
+
|
|
125
|
+
// After loadPrompt, uses canonical format
|
|
126
|
+
expect( result.config.providerOptions ).toBeDefined();
|
|
127
|
+
expect( result.config.providerOptions.thinking.type ).toBe( 'enabled' );
|
|
128
|
+
expect( result.config.providerOptions.thinking.budgetTokens ).toBe( 1500 );
|
|
129
|
+
} );
|
|
130
|
+
|
|
131
|
+
it( 'end-to-end: should FAIL validation with snake_case budget_tokens', () => {
|
|
132
|
+
// End-to-end test: using snake_case should fail validation
|
|
133
|
+
const promptContent = `---
|
|
134
|
+
provider: anthropic
|
|
135
|
+
model: claude-sonnet-4-20250514
|
|
136
|
+
providerOptions:
|
|
137
|
+
thinking:
|
|
138
|
+
type: enabled
|
|
139
|
+
budget_tokens: 1500
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
<user>Hello</user>`;
|
|
143
|
+
|
|
144
|
+
loadContent.mockReturnValue( promptContent );
|
|
145
|
+
|
|
146
|
+
// loadPrompt should throw ValidationError because schema rejects budget_tokens
|
|
147
|
+
expect( () => {
|
|
148
|
+
loadPrompt( 'test', {} );
|
|
149
|
+
} ).toThrow( /Invalid prompt file/ );
|
|
150
|
+
} );
|
|
151
|
+
|
|
152
|
+
it( 'end-to-end: should FAIL validation with snake_case max_tokens', () => {
|
|
153
|
+
// End-to-end test: using snake_case should fail validation
|
|
154
|
+
const promptContent = `---
|
|
155
|
+
provider: anthropic
|
|
156
|
+
model: claude-sonnet-4-20250514
|
|
157
|
+
max_tokens: 4000
|
|
158
|
+
providerOptions:
|
|
159
|
+
thinking:
|
|
160
|
+
type: enabled
|
|
161
|
+
budget_tokens: 1500
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
<user>Hello</user>`;
|
|
165
|
+
|
|
166
|
+
loadContent.mockReturnValue( promptContent );
|
|
167
|
+
|
|
168
|
+
// loadPrompt should throw ValidationError because schema rejects max_tokens
|
|
169
|
+
expect( () => {
|
|
170
|
+
loadPrompt( 'test', {} );
|
|
171
|
+
} ).toThrow( /Invalid prompt file/ );
|
|
172
|
+
} );
|
|
173
|
+
|
|
174
|
+
} );
|
|
@@ -11,20 +11,40 @@ export const promptSchema = z.object( {
|
|
|
11
11
|
thinking: z.object( {
|
|
12
12
|
type: z.literal( 'enabled' ),
|
|
13
13
|
budgetTokens: z.number()
|
|
14
|
-
} ).optional()
|
|
15
|
-
} ).optional()
|
|
16
|
-
} ),
|
|
14
|
+
} ).strict().optional()
|
|
15
|
+
} ).strict().optional()
|
|
16
|
+
} ).strict(),
|
|
17
17
|
messages: z.array(
|
|
18
18
|
z.object( {
|
|
19
19
|
role: z.string(),
|
|
20
20
|
content: z.string()
|
|
21
|
-
} )
|
|
21
|
+
} ).strict()
|
|
22
22
|
)
|
|
23
|
-
} );
|
|
23
|
+
} ).strict();
|
|
24
|
+
|
|
25
|
+
const getHintForError = errorMessage => {
|
|
26
|
+
if ( errorMessage.includes( 'max_tokens' ) ) {
|
|
27
|
+
return '\nHint: Use "maxTokens" (camelCase) instead of "max_tokens".';
|
|
28
|
+
}
|
|
29
|
+
if ( errorMessage.includes( 'budget_tokens' ) ) {
|
|
30
|
+
return '\nHint: Use "budgetTokens" (camelCase) instead of "budget_tokens".';
|
|
31
|
+
}
|
|
32
|
+
if ( errorMessage.includes( '"options"' ) ) {
|
|
33
|
+
return '\nHint: Use "providerOptions" instead of "options".';
|
|
34
|
+
}
|
|
35
|
+
return '';
|
|
36
|
+
};
|
|
24
37
|
|
|
25
38
|
export function validatePrompt( prompt ) {
|
|
26
39
|
const result = promptSchema.safeParse( prompt );
|
|
27
40
|
if ( !result.success ) {
|
|
28
|
-
|
|
41
|
+
const promptIdentifier = prompt?.name ? `"${prompt.name}"` : '(unnamed)';
|
|
42
|
+
const errorMessage = z.prettifyError( result.error );
|
|
43
|
+
const hint = getHintForError( errorMessage );
|
|
44
|
+
|
|
45
|
+
throw new ValidationError(
|
|
46
|
+
`Invalid prompt file ${promptIdentifier}: ${errorMessage}${hint}`,
|
|
47
|
+
{ cause: result.error }
|
|
48
|
+
);
|
|
29
49
|
}
|
|
30
50
|
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { ValidationError } from '@output.ai/core';
|
|
3
|
+
import { validatePrompt } from './prompt_validations.js';
|
|
4
|
+
|
|
5
|
+
describe( 'validatePrompt', () => {
|
|
6
|
+
it( 'should validate a correct prompt with all required fields', () => {
|
|
7
|
+
const validPrompt = {
|
|
8
|
+
name: 'test-prompt',
|
|
9
|
+
config: {
|
|
10
|
+
provider: 'anthropic',
|
|
11
|
+
model: 'claude-3-opus-20240229',
|
|
12
|
+
temperature: 0.7,
|
|
13
|
+
maxTokens: 1000
|
|
14
|
+
},
|
|
15
|
+
messages: [
|
|
16
|
+
{
|
|
17
|
+
role: 'user',
|
|
18
|
+
content: 'Hello, world!'
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
expect( () => validatePrompt( validPrompt ) ).not.toThrow();
|
|
24
|
+
} );
|
|
25
|
+
|
|
26
|
+
it( 'should validate a minimal prompt with only required fields', () => {
|
|
27
|
+
const minimalPrompt = {
|
|
28
|
+
name: 'minimal-prompt',
|
|
29
|
+
config: {
|
|
30
|
+
provider: 'openai',
|
|
31
|
+
model: 'gpt-4'
|
|
32
|
+
},
|
|
33
|
+
messages: [
|
|
34
|
+
{
|
|
35
|
+
role: 'system',
|
|
36
|
+
content: 'You are a helpful assistant.'
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
expect( () => validatePrompt( minimalPrompt ) ).not.toThrow();
|
|
42
|
+
} );
|
|
43
|
+
|
|
44
|
+
it( 'should validate a prompt with thinking providerOptions', () => {
|
|
45
|
+
const promptWithThinking = {
|
|
46
|
+
name: 'thinking-prompt',
|
|
47
|
+
config: {
|
|
48
|
+
provider: 'anthropic',
|
|
49
|
+
model: 'claude-3-5-sonnet-20241022',
|
|
50
|
+
providerOptions: {
|
|
51
|
+
thinking: {
|
|
52
|
+
type: 'enabled',
|
|
53
|
+
budgetTokens: 5000
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
messages: [
|
|
58
|
+
{
|
|
59
|
+
role: 'user',
|
|
60
|
+
content: 'Solve this problem.'
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
expect( () => validatePrompt( promptWithThinking ) ).not.toThrow();
|
|
66
|
+
} );
|
|
67
|
+
|
|
68
|
+
it( 'should throw ValidationError when provider is invalid', () => {
|
|
69
|
+
const invalidPrompt = {
|
|
70
|
+
name: 'invalid-provider',
|
|
71
|
+
config: {
|
|
72
|
+
provider: 'invalid-provider',
|
|
73
|
+
model: 'some-model'
|
|
74
|
+
},
|
|
75
|
+
messages: [
|
|
76
|
+
{
|
|
77
|
+
role: 'user',
|
|
78
|
+
content: 'Test'
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
expect( () => validatePrompt( invalidPrompt ) ).toThrow( ValidationError );
|
|
84
|
+
} );
|
|
85
|
+
|
|
86
|
+
it( 'should throw ValidationError when required fields are missing', () => {
|
|
87
|
+
const missingNamePrompt = {
|
|
88
|
+
config: {
|
|
89
|
+
provider: 'anthropic',
|
|
90
|
+
model: 'claude-3-opus-20240229'
|
|
91
|
+
},
|
|
92
|
+
messages: [
|
|
93
|
+
{
|
|
94
|
+
role: 'user',
|
|
95
|
+
content: 'Test'
|
|
96
|
+
}
|
|
97
|
+
]
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
expect( () => validatePrompt( missingNamePrompt ) ).toThrow( ValidationError );
|
|
101
|
+
} );
|
|
102
|
+
|
|
103
|
+
it( 'should throw ValidationError when max tokens is snake_case', () => {
|
|
104
|
+
const maxTokensSnakeCase = {
|
|
105
|
+
name: 'test-prompt',
|
|
106
|
+
config: {
|
|
107
|
+
provider: 'anthropic',
|
|
108
|
+
model: 'claude-3-opus-20240229',
|
|
109
|
+
max_tokens: 4000
|
|
110
|
+
},
|
|
111
|
+
messages: [
|
|
112
|
+
{
|
|
113
|
+
role: 'user',
|
|
114
|
+
content: 'Test'
|
|
115
|
+
}
|
|
116
|
+
]
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
expect( () => validatePrompt( maxTokensSnakeCase ) ).toThrow( ValidationError );
|
|
120
|
+
} );
|
|
121
|
+
} );
|