@output.ai/llm 0.2.8 → 0.2.10

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 CHANGED
@@ -3,80 +3,9 @@
3
3
  Unified LLM generation API with built-in prompt templating for Output Framework workflows.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/@output.ai/llm)](https://www.npmjs.com/package/@output.ai/llm)
6
- [![Documentation](https://img.shields.io/badge/docs-docs.output.ai-blue)](https://docs.output.ai/packages/llm)
7
-
8
- ## Installation
9
-
10
- ```bash
11
- npm install @output.ai/llm
12
- ```
13
-
14
- ## Quick Start
15
-
16
- ```typescript
17
- import { generateText } from '@output.ai/llm';
18
-
19
- const result = await generateText({
20
- prompt: 'summarize@v1',
21
- variables: { text: 'Your content here' }
22
- });
23
- ```
24
-
25
- ## Generation Functions
26
-
27
- | Function | Description |
28
- |----------|-------------|
29
- | `generateText` | Generate unstructured text |
30
- | `generateObject` | Generate a structured object matching a Zod schema |
31
- | `generateArray` | Generate an array of structured items |
32
- | `generateEnum` | Generate a value from allowed options |
33
-
34
- ### Example: Structured Output
35
-
36
- ```typescript
37
- import { generateObject } from '@output.ai/llm';
38
- import { z } from '@output.ai/core';
39
-
40
- const recipe = await generateObject({
41
- prompt: 'recipe@v1',
42
- variables: { dish: 'lasagna' },
43
- schema: z.object({
44
- title: z.string(),
45
- ingredients: z.array(z.string()),
46
- steps: z.array(z.string())
47
- })
48
- });
49
- ```
50
-
51
- ## Prompt Files
52
-
53
- Prompt files use YAML frontmatter for configuration and LiquidJS for templating:
54
-
55
- ```yaml
56
- ---
57
- provider: anthropic
58
- model: claude-sonnet-4-20250514
59
- temperature: 0.7
60
- ---
61
-
62
- <system>
63
- You are a helpful assistant.
64
- </system>
65
-
66
- <user>
67
- {{ user_message }}
68
- </user>
69
- ```
70
-
71
- ### Supported Providers
72
-
73
- - **Anthropic** - Requires `ANTHROPIC_API_KEY`
74
- - **OpenAI** - Requires `OPENAI_API_KEY`
75
- - **Azure OpenAI** - Requires Azure-specific environment variables
76
6
 
77
7
  ## Documentation
8
+ - [README](https://docs.output.ai/packages/llm)
9
+ - [API Reference](https://output-ai-reference-code-docs.onrender.com/modules/llm_src.html)
78
10
 
79
- For comprehensive documentation, visit:
80
-
81
- - [Package Reference](https://docs.output.ai/packages/llm)
82
- - [Getting Started](https://docs.output.ai/quickstart)
11
+ <!-- Internal Dev Note: The documentation for this package is found at docs/guides/packages/llm.mdx -->
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@output.ai/llm",
3
- "version": "0.2.8",
3
+ "version": "0.2.10",
4
4
  "description": "Framework abstraction to interact with LLM models",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/src/ai_sdk.js CHANGED
@@ -38,14 +38,14 @@ const startTrace = ( name, details ) => {
38
38
  return traceId;
39
39
  };
40
40
 
41
- const extraAiSdkOptionsFromPrompt = prompt => {
41
+ const aiSdkOptionsFromPrompt = prompt => {
42
42
  const options = {
43
43
  model: loadModel( prompt ),
44
44
  messages: prompt.messages,
45
45
  providerOptions: prompt.config.providerOptions
46
46
  };
47
47
 
48
- if ( prompt.config.temperature ) {
48
+ if ( prompt.config.temperature !== undefined ) {
49
49
  options.temperature = prompt.config.temperature;
50
50
  }
51
51
 
@@ -59,42 +59,59 @@ const extraAiSdkOptionsFromPrompt = prompt => {
59
59
  /**
60
60
  * Use an LLM model to generate text.
61
61
  *
62
+ * Accepts additional AI SDK options (tools, maxRetries, seed, etc.) that are passed through
63
+ * to the underlying provider. Options from the prompt file can be overridden at call time.
64
+ *
62
65
  * @param {object} args - Generation arguments
63
66
  * @param {string} args.prompt - Prompt file name
64
67
  * @param {Record<string, string | number>} [args.variables] - Variables to interpolate
68
+ * @param {object} [args.tools] - AI SDK tools the model can call
69
+ * @param {'auto'|'none'|'required'|object} [args.toolChoice] - Tool selection strategy
70
+ * @param {number} [args.maxRetries] - Max retry attempts (default: 2)
71
+ * @param {number} [args.seed] - Seed for deterministic output
72
+ * @param {AbortSignal} [args.abortSignal] - Signal to abort the request
65
73
  * @throws {ValidationError} If the prompt config is invalid (e.g., snake_case fields)
66
74
  * @throws {FatalError} If the prompt file is not found or template rendering fails
67
- * @returns {Promise<GenerateTextResult>} AI SDK response with text and metadata
75
+ * @returns {Promise<GenerateTextResult>} AI SDK response with text, toolCalls, and metadata
68
76
  */
69
- export async function generateText( { prompt, variables } ) {
77
+ export async function generateText( { prompt, variables, ...extraAiSdkOptions } ) {
70
78
  validateGenerateTextArgs( { prompt, variables } );
71
79
  const loadedPrompt = loadPrompt( prompt, variables );
72
80
  const traceId = startTrace( 'generateText', { prompt, variables, loadedPrompt } );
73
81
 
74
82
  return traceWrapper( {
75
83
  traceId, resultProperty: 'text', fn: async () =>
76
- AI.generateText( extraAiSdkOptionsFromPrompt( loadedPrompt ) )
84
+ AI.generateText( {
85
+ ...aiSdkOptionsFromPrompt( loadedPrompt ),
86
+ ...extraAiSdkOptions
87
+ } )
77
88
  } );
78
89
  }
79
90
 
80
91
  /**
81
92
  * Use an LLM model to generate an object with a fixed schema.
82
93
  *
94
+ * Accepts additional AI SDK options (maxRetries, seed, etc.) that are passed through
95
+ * to the underlying provider. Options from the prompt file can be overridden at call time.
96
+ *
83
97
  * @param {object} args - Generation arguments
84
98
  * @param {string} args.prompt - Prompt file name
85
99
  * @param {Record<string, string | number>} [args.variables] - Variables to interpolate
86
100
  * @param {z.ZodObject} args.schema - Output schema
87
101
  * @param {string} [args.schemaName] - Output schema name
88
102
  * @param {string} [args.schemaDescription] - Output schema description
103
+ * @param {number} [args.maxRetries] - Max retry attempts (default: 2)
104
+ * @param {number} [args.seed] - Seed for deterministic output
105
+ * @param {AbortSignal} [args.abortSignal] - Signal to abort the request
89
106
  * @throws {ValidationError} If the prompt config is invalid (e.g., snake_case fields)
90
107
  * @throws {FatalError} If the prompt file is not found or template rendering fails
91
108
  * @returns {Promise<GenerateObjectResult>} AI SDK response with object and metadata
92
109
  */
93
110
  export async function generateObject( args ) {
94
111
  validateGenerateObjectArgs( args );
95
- const { prompt, variables, schema, schemaName, schemaDescription } = args;
112
+ const { prompt, variables, schema, schemaName, schemaDescription, ...extraAiSdkOptions } = args;
96
113
  const loadedPrompt = loadPrompt( prompt, variables );
97
- const traceId = startTrace( 'generateObject', { ...args, schema: z.toJSONSchema( schema ), loadedPrompt } );
114
+ const traceId = startTrace( 'generateObject', { prompt, variables, schema: z.toJSONSchema( schema ), loadedPrompt } );
98
115
 
99
116
  return traceWrapper( {
100
117
  traceId, resultProperty: 'object', fn: async () =>
@@ -103,7 +120,8 @@ export async function generateObject( args ) {
103
120
  schema,
104
121
  schemaName,
105
122
  schemaDescription,
106
- ...extraAiSdkOptionsFromPrompt( loadedPrompt )
123
+ ...aiSdkOptionsFromPrompt( loadedPrompt ),
124
+ ...extraAiSdkOptions
107
125
  } )
108
126
  } );
109
127
  }
@@ -111,21 +129,27 @@ export async function generateObject( args ) {
111
129
  /**
112
130
  * Use an LLM model to generate an array of values with a fixed schema.
113
131
  *
132
+ * Accepts additional AI SDK options (maxRetries, seed, etc.) that are passed through
133
+ * to the underlying provider. Options from the prompt file can be overridden at call time.
134
+ *
114
135
  * @param {object} args - Generation arguments
115
136
  * @param {string} args.prompt - Prompt file name
116
137
  * @param {Record<string, string | number>} [args.variables] - Variables to interpolate
117
138
  * @param {z.ZodType} args.schema - Output schema (array item)
118
139
  * @param {string} [args.schemaName] - Output schema name
119
140
  * @param {string} [args.schemaDescription] - Output schema description
141
+ * @param {number} [args.maxRetries] - Max retry attempts (default: 2)
142
+ * @param {number} [args.seed] - Seed for deterministic output
143
+ * @param {AbortSignal} [args.abortSignal] - Signal to abort the request
120
144
  * @throws {ValidationError} If the prompt config is invalid (e.g., snake_case fields)
121
145
  * @throws {FatalError} If the prompt file is not found or template rendering fails
122
146
  * @returns {Promise<GenerateObjectResult>} AI SDK response with array and metadata
123
147
  */
124
148
  export async function generateArray( args ) {
125
149
  validateGenerateArrayArgs( args );
126
- const { prompt, variables, schema, schemaName, schemaDescription } = args;
150
+ const { prompt, variables, schema, schemaName, schemaDescription, ...extraAiSdkOptions } = args;
127
151
  const loadedPrompt = loadPrompt( prompt, variables );
128
- const traceId = startTrace( 'generateArray', { ...args, schema: z.toJSONSchema( schema ), loadedPrompt } );
152
+ const traceId = startTrace( 'generateArray', { prompt, variables, schema: z.toJSONSchema( schema ), loadedPrompt } );
129
153
 
130
154
  return traceWrapper( {
131
155
  traceId, resultProperty: 'object', fn: async () =>
@@ -134,7 +158,8 @@ export async function generateArray( args ) {
134
158
  schema,
135
159
  schemaName,
136
160
  schemaDescription,
137
- ...extraAiSdkOptionsFromPrompt( loadedPrompt )
161
+ ...aiSdkOptionsFromPrompt( loadedPrompt ),
162
+ ...extraAiSdkOptions
138
163
  } )
139
164
  } );
140
165
  }
@@ -142,26 +167,33 @@ export async function generateArray( args ) {
142
167
  /**
143
168
  * Use an LLM model to generate a result from an enum (array of string values).
144
169
  *
170
+ * Accepts additional AI SDK options (maxRetries, seed, etc.) that are passed through
171
+ * to the underlying provider. Options from the prompt file can be overridden at call time.
172
+ *
145
173
  * @param {object} args - Generation arguments
146
174
  * @param {string} args.prompt - Prompt file name
147
175
  * @param {Record<string, string | number>} [args.variables] - Variables to interpolate
148
176
  * @param {string[]} args.enum - Allowed values for the generation
177
+ * @param {number} [args.maxRetries] - Max retry attempts (default: 2)
178
+ * @param {number} [args.seed] - Seed for deterministic output
179
+ * @param {AbortSignal} [args.abortSignal] - Signal to abort the request
149
180
  * @throws {ValidationError} If the prompt config is invalid (e.g., snake_case fields)
150
181
  * @throws {FatalError} If the prompt file is not found or template rendering fails
151
182
  * @returns {Promise<GenerateObjectResult>} AI SDK response with enum value and metadata
152
183
  */
153
184
  export async function generateEnum( args ) {
154
185
  validateGenerateEnumArgs( args );
155
- const { prompt, variables, enum: _enum } = args;
186
+ const { prompt, variables, enum: _enum, ...extraAiSdkOptions } = args;
156
187
  const loadedPrompt = loadPrompt( prompt, variables );
157
- const traceId = startTrace( 'generateEnum', { ...args, loadedPrompt } );
188
+ const traceId = startTrace( 'generateEnum', { prompt, variables, loadedPrompt } );
158
189
 
159
190
  return traceWrapper( {
160
191
  traceId, resultProperty: 'object', fn: async () =>
161
192
  AI.generateObject( {
162
193
  output: 'enum',
163
194
  enum: _enum,
164
- ...extraAiSdkOptionsFromPrompt( loadedPrompt )
195
+ ...aiSdkOptionsFromPrompt( loadedPrompt ),
196
+ ...extraAiSdkOptions
165
197
  } )
166
198
  } );
167
199
  }
@@ -494,4 +494,138 @@ describe( 'ai_sdk', () => {
494
494
  expect( response.object ).toEqual( { value: 42 } );
495
495
  expect( response.result ).toEqual( { value: 42 } );
496
496
  } );
497
+
498
+ it( 'generateText: passes through AI SDK options like tools and maxRetries', async () => {
499
+ const { generateText } = await importSut();
500
+ const mockTools = { calculator: { description: 'A calculator tool' } };
501
+
502
+ await generateText( {
503
+ prompt: 'test_prompt@v1',
504
+ tools: mockTools,
505
+ toolChoice: 'required',
506
+ maxRetries: 5,
507
+ seed: 42
508
+ } );
509
+
510
+ expect( aiFns.generateText ).toHaveBeenCalledWith(
511
+ expect.objectContaining( {
512
+ tools: mockTools,
513
+ toolChoice: 'required',
514
+ maxRetries: 5,
515
+ seed: 42
516
+ } )
517
+ );
518
+ } );
519
+
520
+ it( 'generateText: user-provided temperature overrides prompt temperature', async () => {
521
+ loadPromptImpl.mockReturnValueOnce( {
522
+ config: {
523
+ provider: 'openai',
524
+ model: 'gpt-4o',
525
+ temperature: 0.7
526
+ },
527
+ messages: [ { role: 'user', content: 'Hi' } ]
528
+ } );
529
+
530
+ const { generateText } = await importSut();
531
+ await generateText( { prompt: 'test_prompt@v1', temperature: 0.2 } );
532
+
533
+ expect( aiFns.generateText ).toHaveBeenCalledWith(
534
+ expect.objectContaining( { temperature: 0.2 } )
535
+ );
536
+ } );
537
+
538
+ it( 'generateText: passes through temperature: 0 from prompt', async () => {
539
+ loadPromptImpl.mockReturnValueOnce( {
540
+ config: {
541
+ provider: 'openai',
542
+ model: 'gpt-4o',
543
+ temperature: 0
544
+ },
545
+ messages: [ { role: 'user', content: 'Hi' } ]
546
+ } );
547
+
548
+ const { generateText } = await importSut();
549
+ await generateText( { prompt: 'test_prompt@v1' } );
550
+
551
+ expect( aiFns.generateText ).toHaveBeenCalledWith(
552
+ expect.objectContaining( { temperature: 0 } )
553
+ );
554
+ } );
555
+
556
+ it( 'generateObject: passes through AI SDK options like maxRetries and seed', async () => {
557
+ const { generateObject } = await importSut();
558
+ const schema = z.object( { a: z.number() } );
559
+
560
+ await generateObject( {
561
+ prompt: 'test_prompt@v1',
562
+ schema,
563
+ maxRetries: 3,
564
+ seed: 123,
565
+ topP: 0.9
566
+ } );
567
+
568
+ expect( aiFns.generateObject ).toHaveBeenCalledWith(
569
+ expect.objectContaining( {
570
+ maxRetries: 3,
571
+ seed: 123,
572
+ topP: 0.9
573
+ } )
574
+ );
575
+ } );
576
+
577
+ it( 'generateArray: passes through AI SDK options', async () => {
578
+ const { generateArray } = await importSut();
579
+ const schema = z.string();
580
+ const controller = new AbortController();
581
+
582
+ await generateArray( {
583
+ prompt: 'test_prompt@v1',
584
+ schema,
585
+ abortSignal: controller.signal,
586
+ headers: { 'X-Custom': 'value' }
587
+ } );
588
+
589
+ expect( aiFns.generateObject ).toHaveBeenCalledWith(
590
+ expect.objectContaining( {
591
+ abortSignal: controller.signal,
592
+ headers: { 'X-Custom': 'value' }
593
+ } )
594
+ );
595
+ } );
596
+
597
+ it( 'generateEnum: passes through AI SDK options', async () => {
598
+ const { generateEnum } = await importSut();
599
+
600
+ await generateEnum( {
601
+ prompt: 'test_prompt@v1',
602
+ enum: [ 'A', 'B' ],
603
+ stopSequences: [ 'END' ],
604
+ presencePenalty: 0.5
605
+ } );
606
+
607
+ expect( aiFns.generateObject ).toHaveBeenCalledWith(
608
+ expect.objectContaining( {
609
+ stopSequences: [ 'END' ],
610
+ presencePenalty: 0.5
611
+ } )
612
+ );
613
+ } );
614
+
615
+ it( 'generateText: passes through unknown future options for forward compatibility', async () => {
616
+ const { generateText } = await importSut();
617
+
618
+ await generateText( {
619
+ prompt: 'test_prompt@v1',
620
+ experimental_futureOption: { key: 'value' },
621
+ unknownOption: true
622
+ } );
623
+
624
+ expect( aiFns.generateText ).toHaveBeenCalledWith(
625
+ expect.objectContaining( {
626
+ experimental_futureOption: { key: 'value' },
627
+ unknownOption: true
628
+ } )
629
+ );
630
+ } );
497
631
  } );
package/src/index.d.ts CHANGED
@@ -1,7 +1,10 @@
1
1
  import type { z } from '@output.ai/core';
2
2
  import type {
3
3
  GenerateTextResult as AIGenerateTextResult,
4
- GenerateObjectResult as AIGenerateObjectResult
4
+ GenerateObjectResult as AIGenerateObjectResult,
5
+ CallSettings,
6
+ ToolSet,
7
+ ToolChoice
5
8
  } from 'ai';
6
9
 
7
10
  /**
@@ -71,9 +74,35 @@ export type {
71
74
  FinishReason,
72
75
  LanguageModelResponseMetadata,
73
76
  ProviderMetadata,
74
- CallWarning
77
+ CallWarning,
78
+ CallSettings,
79
+ ToolSet,
80
+ ToolChoice,
81
+ Tool
75
82
  } from 'ai';
76
83
 
84
+ // Re-export the tool helper function for creating tools
85
+ export { tool } from 'ai';
86
+
87
+ /**
88
+ * Common AI SDK options that can be passed through to all generate functions.
89
+ * These options are passed directly to the underlying AI SDK call.
90
+ */
91
+ type AiSdkOptions = Partial<Omit<CallSettings, 'maxOutputTokens'>>;
92
+
93
+ /**
94
+ * AI SDK options specific to generateText, including tool calling support.
95
+ * @typeParam TOOLS - The tools available for the model to call
96
+ */
97
+ type GenerateTextAiSdkOptions<TOOLS extends ToolSet = ToolSet> = AiSdkOptions & {
98
+ /** Tools the model can call */
99
+ tools?: TOOLS;
100
+ /** Tool choice strategy: 'auto', 'none', 'required', or specific tool */
101
+ toolChoice?: ToolChoice<TOOLS>;
102
+ /** Limit which tools are active without changing types */
103
+ activeTools?: Array<keyof TOOLS>;
104
+ };
105
+
77
106
  /**
78
107
  * Result from generateText including full AI SDK response metadata.
79
108
  * Extends AI SDK's GenerateTextResult with a unified `result` field.
@@ -112,17 +141,20 @@ export function loadPrompt(
112
141
  *
113
142
  * This function is a wrapper over the AI SDK's `generateText`.
114
143
  * The prompt file sets `model`, `messages`, `temperature`, `maxTokens`, and `providerOptions`.
144
+ * Additional AI SDK options (tools, maxRetries, etc.) can be passed through.
115
145
  *
116
146
  * @param args - Generation arguments.
117
147
  * @param args.prompt - Prompt file name.
118
148
  * @param args.variables - Variables to interpolate.
149
+ * @param args.tools - Tools the model can call (optional).
150
+ * @param args.toolChoice - Tool selection strategy (optional).
119
151
  * @returns AI SDK response with text and metadata.
120
152
  */
121
- export function generateText(
153
+ export function generateText<TOOLS extends ToolSet = ToolSet>(
122
154
  args: {
123
155
  prompt: string,
124
156
  variables?: Record<string, string | number | boolean>
125
- }
157
+ } & GenerateTextAiSdkOptions<TOOLS>
126
158
  ): Promise<GenerateTextResult>;
127
159
 
128
160
  /**
@@ -130,6 +162,7 @@ export function generateText(
130
162
  *
131
163
  * This function is a wrapper over the AI SDK's `generateObject`.
132
164
  * The prompt file sets `model`, `messages`, `temperature`, `maxTokens`, and `providerOptions`.
165
+ * Additional AI SDK options (maxRetries, seed, etc.) can be passed through.
133
166
  *
134
167
  * @param args - Generation arguments.
135
168
  * @param args.prompt - Prompt file name.
@@ -146,7 +179,7 @@ export function generateObject<TSchema extends z.ZodObject>(
146
179
  schema: TSchema,
147
180
  schemaName?: string,
148
181
  schemaDescription?: string
149
- }
182
+ } & AiSdkOptions
150
183
  ): Promise<GenerateObjectResult<z.infer<TSchema>>>;
151
184
 
152
185
  /**
@@ -154,6 +187,7 @@ export function generateObject<TSchema extends z.ZodObject>(
154
187
  *
155
188
  * This function is a wrapper over the AI SDK's `generateObject` with `output: 'array'`.
156
189
  * The prompt file sets `model`, `messages`, `temperature`, `maxTokens`, and `providerOptions`.
190
+ * Additional AI SDK options (maxRetries, seed, etc.) can be passed through.
157
191
  *
158
192
  * @param args - Generation arguments.
159
193
  * @param args.prompt - Prompt file name.
@@ -170,7 +204,7 @@ export function generateArray<TSchema extends z.ZodType>(
170
204
  schema: TSchema,
171
205
  schemaName?: string,
172
206
  schemaDescription?: string
173
- }
207
+ } & AiSdkOptions
174
208
  ): Promise<GenerateObjectResult<Array<z.infer<TSchema>>>>;
175
209
 
176
210
  /**
@@ -178,6 +212,7 @@ export function generateArray<TSchema extends z.ZodType>(
178
212
  *
179
213
  * This function is a wrapper over the AI SDK's `generateObject` with `output: 'enum'`.
180
214
  * The prompt file sets `model`, `messages`, `temperature`, `maxTokens`, and `providerOptions`.
215
+ * Additional AI SDK options (maxRetries, seed, etc.) can be passed through.
181
216
  *
182
217
  * @param args - Generation arguments.
183
218
  * @param args.prompt - Prompt file name.
@@ -190,5 +225,5 @@ export function generateEnum<const TEnum extends readonly [string, ...string[]]>
190
225
  prompt: string,
191
226
  variables?: Record<string, string | number | boolean>,
192
227
  enum: TEnum
193
- }
228
+ } & AiSdkOptions
194
229
  ): Promise<GenerateObjectResult<TEnum[number]>>;
package/src/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export { generateText, generateArray, generateObject, generateEnum } from './ai_sdk.js';
2
2
  export { loadPrompt } from './prompt_loader.js';
3
+ export { tool } from 'ai';
3
4
  export * as ai from 'ai';