@outputai/llm 0.11.0 → 0.11.1-next.2223fa5.0

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.
Files changed (69) hide show
  1. package/package.json +2 -2
  2. package/src/agent.js +105 -83
  3. package/src/agent.spec.js +312 -225
  4. package/src/ai_provider.js +2 -2
  5. package/src/ai_sdk_options.js +93 -39
  6. package/src/ai_sdk_options.spec.js +314 -154
  7. package/src/consts.js +6 -0
  8. package/src/generate.js +81 -0
  9. package/src/generate.spec.js +466 -0
  10. package/src/index.d.ts +222 -242
  11. package/src/index.js +3 -4
  12. package/src/prompt/content.js +70 -0
  13. package/src/prompt/content.spec.js +208 -0
  14. package/src/prompt/{escape.js → interpolations.js} +11 -18
  15. package/src/prompt/interpolations.spec.js +109 -0
  16. package/src/prompt/loader.full.spec.js +293 -0
  17. package/src/prompt/loader.js +45 -39
  18. package/src/prompt/loader.spec.js +186 -289
  19. package/src/prompt/markup/attributes.js +52 -0
  20. package/src/prompt/markup/attributes.spec.js +132 -0
  21. package/src/prompt/markup/nodes.js +71 -0
  22. package/src/prompt/markup/nodes.spec.js +333 -0
  23. package/src/prompt/markup/tokenizer.js +3 -0
  24. package/src/prompt/markup/tokenizer.spec.js +149 -0
  25. package/src/prompt/markup/tokens.js +26 -0
  26. package/src/prompt/markup/tokens.spec.js +250 -0
  27. package/src/prompt/validations.js +98 -67
  28. package/src/prompt/validations.spec.js +204 -47
  29. package/src/{prompt/load_content.js → utils/file.js} +12 -15
  30. package/src/utils/file.spec.js +89 -0
  31. package/src/utils/models.js +15 -0
  32. package/src/utils/models.spec.js +119 -0
  33. package/src/utils/skills.js +74 -0
  34. package/src/utils/skills.spec.js +168 -0
  35. package/src/utils/sources.js +48 -0
  36. package/src/utils/sources.spec.js +122 -0
  37. package/src/utils/stream.js +22 -0
  38. package/src/utils/stream.spec.js +55 -0
  39. package/src/utils/tools.js +47 -0
  40. package/src/utils/tools.spec.js +167 -0
  41. package/src/utils/wrap.js +148 -0
  42. package/src/utils/wrap.spec.js +359 -0
  43. package/src/validations.js +149 -27
  44. package/src/validations.spec.js +451 -53
  45. package/src/ai_model.js +0 -60
  46. package/src/ai_model.spec.js +0 -259
  47. package/src/ai_sdk.js +0 -92
  48. package/src/ai_sdk.spec.js +0 -564
  49. package/src/prompt/block_options.js +0 -58
  50. package/src/prompt/block_options.spec.js +0 -71
  51. package/src/prompt/blocks.js +0 -47
  52. package/src/prompt/blocks.spec.js +0 -63
  53. package/src/prompt/escape.spec.js +0 -159
  54. package/src/prompt/load_content.spec.js +0 -83
  55. package/src/prompt/loader_validation.spec.js +0 -128
  56. package/src/prompt/parser.js +0 -16
  57. package/src/prompt/parser.spec.js +0 -186
  58. package/src/prompt/prepare_text.js +0 -27
  59. package/src/prompt/prepare_text.spec.js +0 -141
  60. package/src/prompt/skill.js +0 -128
  61. package/src/prompt/skill.spec.js +0 -172
  62. package/src/utils/message.js +0 -3
  63. package/src/utils/message.spec.js +0 -29
  64. package/src/utils/response_wrappers.js +0 -100
  65. package/src/utils/response_wrappers.spec.js +0 -240
  66. package/src/utils/source_extraction.js +0 -53
  67. package/src/utils/source_extraction.spec.js +0 -194
  68. package/src/utils/trace.js +0 -19
  69. package/src/utils/trace.spec.js +0 -112
@@ -1,259 +0,0 @@
1
- import { afterEach, describe, expect, it, vi } from 'vitest';
2
-
3
- const getProvider = vi.hoisted( () => vi.fn() );
4
-
5
- vi.mock( './ai_provider.js', () => ( {
6
- getProvider
7
- } ) );
8
-
9
- import { loadImageModel, loadTextModel, loadTools } from './ai_model.js';
10
-
11
- afterEach( () => {
12
- vi.clearAllMocks();
13
- } );
14
-
15
- describe( 'loadTextModel', () => {
16
- it( 'loads a text model using the prompt provider and model', () => {
17
- const provider = vi.fn( model => ( { type: 'text-model', model } ) );
18
- getProvider.mockReturnValue( provider );
19
-
20
- const result = loadTextModel( {
21
- config: {
22
- provider: 'openai',
23
- model: 'gpt-4o-mini'
24
- }
25
- } );
26
-
27
- expect( getProvider ).toHaveBeenCalledWith( 'openai' );
28
- expect( provider ).toHaveBeenCalledWith( 'gpt-4o-mini' );
29
- expect( result ).toEqual( {
30
- type: 'text-model',
31
- model: 'gpt-4o-mini'
32
- } );
33
- } );
34
-
35
- it( 'propagates provider lookup errors', () => {
36
- getProvider.mockImplementation( () => {
37
- throw new Error( 'Unsupported provider "missing"' );
38
- } );
39
-
40
- expect( () => loadTextModel( {
41
- config: {
42
- provider: 'missing',
43
- model: 'model'
44
- }
45
- } ) ).toThrow( 'Unsupported provider "missing"' );
46
- } );
47
- } );
48
-
49
- describe( 'loadImageModel', () => {
50
- it( 'loads an image model using provider.image', () => {
51
- const textProvider = vi.fn();
52
- textProvider.image = vi.fn( model => ( { type: 'image-model', model } ) );
53
- getProvider.mockReturnValue( textProvider );
54
-
55
- const result = loadImageModel( {
56
- config: {
57
- provider: 'openai',
58
- model: 'gpt-image-1'
59
- }
60
- } );
61
-
62
- expect( getProvider ).toHaveBeenCalledWith( 'openai' );
63
- expect( textProvider.image ).toHaveBeenCalledWith( 'gpt-image-1' );
64
- expect( textProvider ).not.toHaveBeenCalled();
65
- expect( result ).toEqual( {
66
- type: 'image-model',
67
- model: 'gpt-image-1'
68
- } );
69
- } );
70
-
71
- it( 'falls back to provider.imageModel', () => {
72
- const provider = vi.fn();
73
- provider.imageModel = vi.fn( model => ( { type: 'legacy-image-model', model } ) );
74
- getProvider.mockReturnValue( provider );
75
-
76
- const result = loadImageModel( {
77
- config: {
78
- provider: 'custom',
79
- model: 'image-v1'
80
- }
81
- } );
82
-
83
- expect( provider.imageModel ).toHaveBeenCalledWith( 'image-v1' );
84
- expect( result ).toEqual( {
85
- type: 'legacy-image-model',
86
- model: 'image-v1'
87
- } );
88
- } );
89
-
90
- it( 'prefers provider.image when both image factories exist', () => {
91
- const provider = vi.fn();
92
- provider.image = vi.fn( model => ( { type: 'image', model } ) );
93
- provider.imageModel = vi.fn( model => ( { type: 'imageModel', model } ) );
94
- getProvider.mockReturnValue( provider );
95
-
96
- const result = loadImageModel( {
97
- config: {
98
- provider: 'custom',
99
- model: 'image-v1'
100
- }
101
- } );
102
-
103
- expect( provider.image ).toHaveBeenCalledWith( 'image-v1' );
104
- expect( provider.imageModel ).not.toHaveBeenCalled();
105
- expect( result.type ).toBe( 'image' );
106
- } );
107
-
108
- it( 'throws a clear error when the provider does not support image models', () => {
109
- getProvider.mockReturnValue( vi.fn() );
110
-
111
- expect( () => loadImageModel( {
112
- config: {
113
- provider: 'azure',
114
- model: 'gpt-image-1'
115
- }
116
- } ) ).toThrow( 'Provider "azure" does not support image models.' );
117
- } );
118
- } );
119
-
120
- describe( 'loadTools', () => {
121
- it( 'returns null and does not load the provider when no tools are configured', () => {
122
- const result = loadTools( {
123
- config: {
124
- provider: 'vertex',
125
- model: 'gemini-2.0-flash'
126
- }
127
- } );
128
-
129
- expect( result ).toBeNull();
130
- expect( getProvider ).not.toHaveBeenCalled();
131
- } );
132
-
133
- it( 'returns null and does not load the provider when tools config is empty', () => {
134
- const result = loadTools( {
135
- config: {
136
- provider: 'vertex',
137
- tools: {}
138
- }
139
- } );
140
-
141
- expect( result ).toBeNull();
142
- expect( getProvider ).not.toHaveBeenCalled();
143
- } );
144
-
145
- it( 'throws when the provider has no tools object', () => {
146
- getProvider.mockReturnValue( vi.fn() );
147
-
148
- expect( () => loadTools( {
149
- config: {
150
- provider: 'azure',
151
- tools: {
152
- webSearch: {}
153
- }
154
- }
155
- } ) ).toThrow( 'Provider "azure" does not support provider-specific tools.' );
156
- } );
157
-
158
- it( 'loads a single provider tool with config', () => {
159
- const googleSearch = vi.fn( config => ( { type: 'googleSearch', config } ) );
160
- const provider = {
161
- tools: {
162
- googleSearch
163
- }
164
- };
165
- getProvider.mockReturnValue( provider );
166
-
167
- const result = loadTools( {
168
- config: {
169
- provider: 'vertex',
170
- tools: {
171
- googleSearch: {
172
- mode: 'MODE_DYNAMIC',
173
- dynamicThreshold: 0.8
174
- }
175
- }
176
- }
177
- } );
178
-
179
- expect( getProvider ).toHaveBeenCalledWith( 'vertex' );
180
- expect( googleSearch ).toHaveBeenCalledWith( {
181
- mode: 'MODE_DYNAMIC',
182
- dynamicThreshold: 0.8
183
- } );
184
- expect( result ).toEqual( {
185
- googleSearch: {
186
- type: 'googleSearch',
187
- config: {
188
- mode: 'MODE_DYNAMIC',
189
- dynamicThreshold: 0.8
190
- }
191
- }
192
- } );
193
- } );
194
-
195
- it( 'loads multiple provider tools', () => {
196
- const googleSearch = vi.fn( config => ( { type: 'googleSearch', config } ) );
197
- const urlContext = vi.fn( config => ( { type: 'urlContext', config } ) );
198
- getProvider.mockReturnValue( {
199
- tools: {
200
- googleSearch,
201
- urlContext
202
- }
203
- } );
204
-
205
- const result = loadTools( {
206
- config: {
207
- provider: 'vertex',
208
- tools: {
209
- googleSearch: {
210
- mode: 'MODE_DYNAMIC'
211
- },
212
- urlContext: {}
213
- }
214
- }
215
- } );
216
-
217
- expect( Object.keys( result ) ).toEqual( [ 'googleSearch', 'urlContext' ] );
218
- expect( googleSearch ).toHaveBeenCalledWith( { mode: 'MODE_DYNAMIC' } );
219
- expect( urlContext ).toHaveBeenCalledWith( {} );
220
- expect( result.googleSearch.type ).toBe( 'googleSearch' );
221
- expect( result.urlContext.type ).toBe( 'urlContext' );
222
- } );
223
-
224
- it( 'throws when a configured tool is not supported by the provider', () => {
225
- getProvider.mockReturnValue( {
226
- tools: {
227
- googleSearch: vi.fn(),
228
- urlContext: vi.fn()
229
- }
230
- } );
231
-
232
- expect( () => loadTools( {
233
- config: {
234
- provider: 'vertex',
235
- tools: {
236
- unknownTool: {}
237
- }
238
- }
239
- } ) ).toThrow( 'Invalid tool(s) unknownTool for provider "vertex". Available: googleSearch, urlContext.' );
240
- } );
241
-
242
- it( 'reports all unsupported configured tools', () => {
243
- getProvider.mockReturnValue( {
244
- tools: {
245
- googleSearch: vi.fn()
246
- }
247
- } );
248
-
249
- expect( () => loadTools( {
250
- config: {
251
- provider: 'vertex',
252
- tools: {
253
- unknownTool: {},
254
- anotherUnknownTool: {}
255
- }
256
- }
257
- } ) ).toThrow( 'Invalid tool(s) unknownTool, anotherUnknownTool for provider "vertex". Available: googleSearch.' );
258
- } );
259
- } );
package/src/ai_sdk.js DELETED
@@ -1,92 +0,0 @@
1
- import { types as utilTypes } from 'node:util';
2
- import * as AI from 'ai';
3
- import { stepCountIs } from 'ai';
4
- import { ValidationError } from '@outputai/core';
5
- import { validateGenerateTextArgs, validateStreamTextArgs, validateGenerateImageArgs } from './validations.js';
6
- import { loadPrompt } from './prompt/loader.js';
7
- import { startTrace, endTraceWithError } from './utils/trace.js';
8
- import { wrapTextResponse, wrapStreamOnFinishResponse, wrapImageResponse } from './utils/response_wrappers.js';
9
- import { loadAiSdkTextOptions, loadAiSdkImageOptions } from './ai_sdk_options.js';
10
- import { prepareTextPrompt } from './prompt/prepare_text.js';
11
- import { mapAiError } from './utils/error_handler.js';
12
-
13
- export async function generateText( { prompt, variables, promptDir, skills = [], maxSteps = 10, ...aiSdkArgs } ) {
14
- validateGenerateTextArgs( { prompt, variables, promptDir, skills, maxSteps } );
15
-
16
- const parsedSkills = typeof skills === 'function' ? await skills( variables ) : skills;
17
- const { loadedPrompt, tools } = prepareTextPrompt( { prompt, variables, promptDir, skills: parsedSkills, tools: aiSdkArgs.tools } );
18
-
19
- const traceId = startTrace( { name: 'generateText', prompt, variables, loadedPrompt } );
20
- const { model: modelId, provider: providerId } = loadedPrompt.config;
21
-
22
- try {
23
- const response = await AI.generateText( {
24
- ...loadAiSdkTextOptions( loadedPrompt ),
25
- allowSystemInMessages: true,
26
- maxRetries: 0,
27
- ...aiSdkArgs,
28
- ...( tools && { tools } ),
29
- ...( tools && !aiSdkArgs.stopWhen ? { stopWhen: stepCountIs( maxSteps ) } : {} )
30
- } );
31
- return wrapTextResponse( { traceId, providerId, modelId, response } );
32
- } catch ( originalError ) {
33
- const error = mapAiError( originalError );
34
- endTraceWithError( { traceId, error } );
35
- throw error;
36
- }
37
- }
38
-
39
- export function streamText( { prompt, variables, promptDir, skills = [], maxSteps = 10, onFinish, onError: _onError, ...aiSdkArgs } ) {
40
- validateStreamTextArgs( { prompt, variables, promptDir, skills, maxSteps } );
41
-
42
- const parsedSkills = typeof skills === 'function' ? skills( variables ) : skills;
43
- if ( utilTypes.isPromise( parsedSkills ) ) {
44
- throw new ValidationError( 'streamText() skills must be synchronous because streamText() returns a stream immediately.' );
45
- }
46
- const { loadedPrompt, tools } = prepareTextPrompt( { prompt, variables, promptDir, skills: parsedSkills, tools: aiSdkArgs.tools } );
47
-
48
- const traceId = startTrace( { name: 'streamText', prompt, variables, loadedPrompt } );
49
- const { model: modelId, provider: providerId } = loadedPrompt.config;
50
-
51
- try {
52
- return AI.streamText( {
53
- ...loadAiSdkTextOptions( loadedPrompt ),
54
- allowSystemInMessages: true,
55
- maxRetries: 0,
56
- ...aiSdkArgs,
57
- ...( tools && { tools } ),
58
- ...( tools && !aiSdkArgs.stopWhen ? { stopWhen: stepCountIs( maxSteps ) } : {} ),
59
- ...wrapStreamOnFinishResponse( { traceId, modelId, providerId, onFinish } ),
60
- onError( event ) {
61
- const error = mapAiError( event.error );
62
- endTraceWithError( { traceId, error } );
63
- _onError?.( { ...event, error } );
64
- }
65
- } );
66
- } catch ( originalError ) {
67
- const error = mapAiError( originalError );
68
- endTraceWithError( { traceId, error } );
69
- throw error;
70
- }
71
- }
72
-
73
- export async function generateImage( { prompt, variables, promptDir, images, mask, ...aiSdkArgs } ) {
74
- validateGenerateImageArgs( { prompt, variables, promptDir, images, mask } );
75
-
76
- const loadedPrompt = loadPrompt( prompt, variables, promptDir );
77
- const traceId = startTrace( { name: 'generateImage', prompt, variables, loadedPrompt } );
78
- const { model: modelId, provider: providerId } = loadedPrompt.config;
79
-
80
- try {
81
- const response = await AI.generateImage( {
82
- ...loadAiSdkImageOptions( { prompt: loadedPrompt, images, mask } ),
83
- maxRetries: 0,
84
- ...aiSdkArgs
85
- } );
86
- return wrapImageResponse( { traceId, providerId, modelId, response } );
87
- } catch ( originalError ) {
88
- const error = mapAiError( originalError );
89
- endTraceWithError( { traceId, error } );
90
- throw error;
91
- }
92
- }