@outputai/llm 0.1.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.
- package/LICENSE +201 -0
- package/README.md +11 -0
- package/package.json +31 -0
- package/src/ai_model.js +130 -0
- package/src/ai_model.spec.js +779 -0
- package/src/ai_sdk.js +145 -0
- package/src/ai_sdk.spec.js +633 -0
- package/src/cost/fetch_models_pricing.js +38 -0
- package/src/cost/fetch_models_pricing.spec.js +121 -0
- package/src/cost/fixtures/models_api_light.json +661 -0
- package/src/cost/index.js +73 -0
- package/src/cost/index.spec.js +162 -0
- package/src/index.d.ts +309 -0
- package/src/index.js +8 -0
- package/src/load_content.js +43 -0
- package/src/load_content.spec.js +81 -0
- package/src/parser.js +28 -0
- package/src/parser.spec.js +122 -0
- package/src/prompt_loader.js +45 -0
- package/src/prompt_loader.spec.js +179 -0
- package/src/prompt_loader_validation.spec.js +95 -0
- package/src/prompt_validations.js +60 -0
- package/src/prompt_validations.spec.js +346 -0
- package/src/source_extraction.js +49 -0
- package/src/source_extraction.spec.js +169 -0
- package/src/validations.js +21 -0
package/src/ai_sdk.js
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { Tracing, emitEvent } from '@outputai/core/sdk_activity_integration';
|
|
2
|
+
import { loadModel, loadTools } from './ai_model.js';
|
|
3
|
+
import * as AI from 'ai';
|
|
4
|
+
import { validateGenerateTextArgs, validateStreamTextArgs } from './validations.js';
|
|
5
|
+
import { loadPrompt } from './prompt_loader.js';
|
|
6
|
+
import { extractSourcesFromSteps } from './source_extraction.js';
|
|
7
|
+
import { calculateLLMCallCost } from './cost/index.js';
|
|
8
|
+
|
|
9
|
+
// Starts the LLM trace, with the start event
|
|
10
|
+
const startTrace = ( name, details ) => {
|
|
11
|
+
const traceId = `${name}-${Date.now()}`;
|
|
12
|
+
Tracing.addEventStart( { kind: 'llm', name, id: traceId, details } );
|
|
13
|
+
return traceId;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const loadAiSdkOptionsFromPrompt = prompt => {
|
|
17
|
+
const options = {
|
|
18
|
+
model: loadModel( prompt ),
|
|
19
|
+
messages: prompt.messages,
|
|
20
|
+
providerOptions: prompt.config.providerOptions
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
if ( Number.isFinite( prompt.config.temperature ) ) {
|
|
24
|
+
options.temperature = prompt.config.temperature;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if ( prompt.config.maxTokens ) {
|
|
28
|
+
options.maxOutputTokens = prompt.config.maxTokens;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const tools = loadTools( prompt );
|
|
32
|
+
if ( tools ) {
|
|
33
|
+
options.tools = tools;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return options;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Use an LLM model to generate text.
|
|
41
|
+
*
|
|
42
|
+
* Accepts additional AI SDK options (tools, maxRetries, seed, etc.) that are passed through
|
|
43
|
+
* to the underlying provider. Options from the prompt file can be overridden at call time.
|
|
44
|
+
*
|
|
45
|
+
* @param {object} args - Generation arguments
|
|
46
|
+
* @param {string} args.prompt - Prompt file name
|
|
47
|
+
* @param {Record<string, string | number>} [args.variables] - Variables to interpolate
|
|
48
|
+
* @param {object} [args.tools] - AI SDK tools the model can call
|
|
49
|
+
* @param {'auto'|'none'|'required'|object} [args.toolChoice] - Tool selection strategy
|
|
50
|
+
* @param {number} [args.maxRetries] - Max retry attempts (default: 2)
|
|
51
|
+
* @param {number} [args.seed] - Seed for deterministic output
|
|
52
|
+
* @param {AbortSignal} [args.abortSignal] - Signal to abort the request
|
|
53
|
+
* @throws {ValidationError} If the prompt config is invalid (e.g., snake_case fields)
|
|
54
|
+
* @throws {FatalError} If the prompt file is not found or template rendering fails
|
|
55
|
+
* @returns {Promise<GenerateTextResult>} AI SDK response with text, toolCalls, and metadata
|
|
56
|
+
*/
|
|
57
|
+
export async function generateText( { prompt, variables, promptDir, ...extraAiSdkOptions } ) {
|
|
58
|
+
validateGenerateTextArgs( { prompt, variables } );
|
|
59
|
+
const loadedPrompt = promptDir ? loadPrompt( prompt, variables, promptDir ) : loadPrompt( prompt, variables );
|
|
60
|
+
const traceId = startTrace( 'generateText', { prompt, variables, loadedPrompt } );
|
|
61
|
+
const { model: modelId } = loadedPrompt.config;
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
const response = await AI.generateText( {
|
|
65
|
+
...loadAiSdkOptionsFromPrompt( loadedPrompt ),
|
|
66
|
+
...extraAiSdkOptions
|
|
67
|
+
} );
|
|
68
|
+
const { text: result, totalUsage: usage, providerMetadata } = response;
|
|
69
|
+
const sourcesFromTools = extractSourcesFromSteps( response.steps );
|
|
70
|
+
const cost = await calculateLLMCallCost( { usage, modelId } );
|
|
71
|
+
|
|
72
|
+
emitEvent( 'llm:call_cost', { modelId, cost, usage } );
|
|
73
|
+
Tracing.addEventEnd( { id: traceId, details: { result, usage, cost, providerMetadata, sourcesFromTools } } );
|
|
74
|
+
|
|
75
|
+
// Creates a proxy over the response from AI SDK to add
|
|
76
|
+
// - result: a shortcut for the actual SDK response (eg. .text);
|
|
77
|
+
// - sources: a way to retrieve the computed sources;
|
|
78
|
+
// It uses proxies instead of spreading the response object because AI SDK uses getters
|
|
79
|
+
// (and they don't deconstruct properly in JS).
|
|
80
|
+
return new Proxy( response, {
|
|
81
|
+
get( target, prop, receiver ) {
|
|
82
|
+
if ( prop === 'result' ) {
|
|
83
|
+
return target.text;
|
|
84
|
+
}
|
|
85
|
+
if ( prop === 'sources' && sourcesFromTools.length > 0 ) {
|
|
86
|
+
const responseSources = Array.isArray( target[prop] ) ? target[prop] : [];
|
|
87
|
+
const byUrl = new Map( [ ...sourcesFromTools, ...responseSources ].map( s => [ s.url, s ] ) );
|
|
88
|
+
return [ ...byUrl.values() ];
|
|
89
|
+
}
|
|
90
|
+
return Reflect.get( target, prop, receiver );
|
|
91
|
+
}
|
|
92
|
+
} );
|
|
93
|
+
} catch ( error ) {
|
|
94
|
+
Tracing.addEventError( { id: traceId, details: error } );
|
|
95
|
+
throw error;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Use an LLM model to stream text generation.
|
|
101
|
+
*
|
|
102
|
+
* Accepts additional AI SDK options (tools, onChunk, onFinish, onError, etc.) that are passed
|
|
103
|
+
* through to the underlying provider. Options from the prompt file can be overridden at call time.
|
|
104
|
+
*
|
|
105
|
+
* @param {object} args - Generation arguments
|
|
106
|
+
* @param {string} args.prompt - Prompt file name
|
|
107
|
+
* @param {Record<string, string | number>} [args.variables] - Variables to interpolate
|
|
108
|
+
* @param {object} [args.tools] - AI SDK tools the model can call
|
|
109
|
+
* @param {'auto'|'none'|'required'|object} [args.toolChoice] - Tool selection strategy
|
|
110
|
+
* @param {number} [args.maxRetries] - Max retry attempts (default: 2)
|
|
111
|
+
* @param {AbortSignal} [args.abortSignal] - Signal to abort the request
|
|
112
|
+
* @param {Function} [args.onChunk] - Callback for each stream chunk
|
|
113
|
+
* @param {Function} [args.onFinish] - Callback when stream finishes (called after internal tracing)
|
|
114
|
+
* @param {Function} [args.onError] - Callback when stream errors (called after internal tracing)
|
|
115
|
+
* @throws {ValidationError} If required arguments are missing or prompt file has invalid config
|
|
116
|
+
* @throws {FatalError} If the prompt file is not found or template rendering fails
|
|
117
|
+
* @returns {AIStreamTextResult} AI SDK stream result with textStream, fullStream, and metadata promises (synchronous)
|
|
118
|
+
*/
|
|
119
|
+
export function streamText( { prompt, variables, onFinish: userOnFinish, onError: userOnError, ...restOptions } ) {
|
|
120
|
+
validateStreamTextArgs( { prompt, variables } );
|
|
121
|
+
const loadedPrompt = loadPrompt( prompt, variables );
|
|
122
|
+
const traceId = startTrace( 'streamText', { prompt, variables, loadedPrompt } );
|
|
123
|
+
const { model: modelId } = loadedPrompt.config;
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
return AI.streamText( {
|
|
127
|
+
...loadAiSdkOptionsFromPrompt( loadedPrompt ),
|
|
128
|
+
...restOptions,
|
|
129
|
+
async onFinish( response ) {
|
|
130
|
+
const { text: result, totalUsage: usage, providerMetadata } = response;
|
|
131
|
+
const cost = await calculateLLMCallCost( { usage, modelId } );
|
|
132
|
+
emitEvent( 'llm:call_cost', { modelId, cost, usage } );
|
|
133
|
+
Tracing.addEventEnd( { id: traceId, details: { result, usage, cost, providerMetadata } } );
|
|
134
|
+
userOnFinish?.( response );
|
|
135
|
+
},
|
|
136
|
+
onError( event ) {
|
|
137
|
+
Tracing.addEventError( { id: traceId, details: event.error } );
|
|
138
|
+
userOnError?.( event );
|
|
139
|
+
}
|
|
140
|
+
} );
|
|
141
|
+
} catch ( error ) {
|
|
142
|
+
Tracing.addEventError( { id: traceId, details: error } );
|
|
143
|
+
throw error;
|
|
144
|
+
}
|
|
145
|
+
}
|