@outputai/llm 0.11.1-next.da26845.0 → 0.12.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/package.json +7 -3
- package/src/agent.js +7 -2
- package/src/generate.js +1 -0
- package/src/index.d.ts +14 -6
- package/src/utils/wrap.js +35 -2
- package/src/agent.spec.js +0 -595
- package/src/ai_provider.spec.js +0 -217
- package/src/ai_sdk_options.spec.js +0 -390
- package/src/fixtures/image_response_v7_google_vertex.js +0 -72
- package/src/fixtures/image_response_v7_openai.js +0 -77
- package/src/fixtures/models_api_light.json +0 -675
- package/src/fixtures/models_api_v7_subset.json +0 -259
- package/src/fixtures/stream_response_v7_anthropic.js +0 -389
- package/src/fixtures/stream_response_v7_google_vertex.js +0 -462
- package/src/fixtures/stream_response_v7_openai.js +0 -353
- package/src/fixtures/stream_response_v7_perplexity.js +0 -671
- package/src/fixtures/text_response_v7_anthropic.js +0 -359
- package/src/fixtures/text_response_v7_anthropic_cache.js +0 -719
- package/src/fixtures/text_response_v7_google_vertex.js +0 -435
- package/src/fixtures/text_response_v7_openai.js +0 -347
- package/src/fixtures/text_response_v7_perplexity.js +0 -764
- package/src/generate.spec.js +0 -525
- package/src/prompt/content.spec.js +0 -208
- package/src/prompt/interpolations.spec.js +0 -109
- package/src/prompt/loader.full.spec.js +0 -309
- package/src/prompt/loader.spec.js +0 -258
- package/src/prompt/markup/attributes.spec.js +0 -132
- package/src/prompt/markup/nodes.spec.js +0 -333
- package/src/prompt/markup/tokenizer.spec.js +0 -149
- package/src/prompt/markup/tokens.spec.js +0 -250
- package/src/prompt/validations.spec.js +0 -812
- package/src/utils/cost.full.spec.js +0 -272
- package/src/utils/cost.spec.js +0 -290
- package/src/utils/error_handler.spec.js +0 -230
- package/src/utils/file.spec.js +0 -89
- package/src/utils/image.spec.js +0 -20
- package/src/utils/legacy_cost_attribute.spec.js +0 -278
- package/src/utils/models.spec.js +0 -119
- package/src/utils/models_pricing.spec.js +0 -162
- package/src/utils/skills.spec.js +0 -168
- package/src/utils/sources.spec.js +0 -122
- package/src/utils/stream.spec.js +0 -55
- package/src/utils/tools.spec.js +0 -167
- package/src/utils/usage.full.spec.js +0 -215
- package/src/utils/usage.spec.js +0 -246
- package/src/utils/wrap.spec.js +0 -467
- package/src/validations.spec.js +0 -631
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@outputai/llm",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Framework abstraction to interact with LLM models",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -9,7 +9,11 @@
|
|
|
9
9
|
"node": ">=24.15.0"
|
|
10
10
|
},
|
|
11
11
|
"files": [
|
|
12
|
-
"./src"
|
|
12
|
+
"./src/!(*.spec).js",
|
|
13
|
+
"./src/index.d.ts",
|
|
14
|
+
"./src/prompt/!(*.spec).js",
|
|
15
|
+
"./src/prompt/markup/!(*.spec).js",
|
|
16
|
+
"./src/utils/!(*.spec).js"
|
|
13
17
|
],
|
|
14
18
|
"dependencies": {
|
|
15
19
|
"decimal.js": "10.6.0",
|
|
@@ -17,7 +21,7 @@
|
|
|
17
21
|
"gray-matter": "4.0.3",
|
|
18
22
|
"liquidjs": "10.27.2",
|
|
19
23
|
"undici": "8.9.0",
|
|
20
|
-
"@outputai/core": "0.
|
|
24
|
+
"@outputai/core": "0.12.0"
|
|
21
25
|
},
|
|
22
26
|
"devDependencies": {
|
|
23
27
|
"@ai-sdk/amazon-bedrock": "5.0.57",
|
package/src/agent.js
CHANGED
|
@@ -58,7 +58,9 @@ export class Agent {
|
|
|
58
58
|
...( abortSignal && { abortSignal } ),
|
|
59
59
|
...( toolChoice && { toolChoice } )
|
|
60
60
|
} );
|
|
61
|
-
|
|
61
|
+
if ( response.finishReason !== 'error' ) {
|
|
62
|
+
await this.#storeMessages( messages.concat( response.responseMessages ?? [] ) );
|
|
63
|
+
}
|
|
62
64
|
return response;
|
|
63
65
|
}
|
|
64
66
|
} );
|
|
@@ -95,7 +97,9 @@ export class Agent {
|
|
|
95
97
|
}
|
|
96
98
|
|
|
97
99
|
state.response.output = await stream.output;
|
|
98
|
-
|
|
100
|
+
if ( state.response.finishReason !== 'error' ) {
|
|
101
|
+
await this.#storeMessages( messages.concat( state.response.responseMessages ?? [] ) );
|
|
102
|
+
}
|
|
99
103
|
|
|
100
104
|
return state.response;
|
|
101
105
|
}
|
|
@@ -109,6 +113,7 @@ export class Agent {
|
|
|
109
113
|
return wrapStream( {
|
|
110
114
|
name: 'Agent.stream',
|
|
111
115
|
prompt: this.#prompt,
|
|
116
|
+
abortSignal,
|
|
112
117
|
fn: ( { onEndHook, onErrorHook } ) => this.#agent.stream( {
|
|
113
118
|
messages: combinedMessages,
|
|
114
119
|
allowSystemInMessages: true,
|
package/src/generate.js
CHANGED
|
@@ -26,6 +26,7 @@ export const streamText = args => {
|
|
|
26
26
|
return wrapStream( {
|
|
27
27
|
name: 'streamText',
|
|
28
28
|
prompt,
|
|
29
|
+
abortSignal: aiOptions.abortSignal,
|
|
29
30
|
fn: ( { onEndHook, onErrorHook } ) => AI.streamText( {
|
|
30
31
|
...loadAiSdkTextOptions( { prompt, skills, ...aiOptions } ),
|
|
31
32
|
...( onChunk && { onChunk } ),
|
package/src/index.d.ts
CHANGED
|
@@ -281,12 +281,20 @@ export type GenerateImageInput =
|
|
|
281
281
|
mediaType?: string;
|
|
282
282
|
};
|
|
283
283
|
|
|
284
|
+
type GenerateImageInputs =
|
|
285
|
+
| {
|
|
286
|
+
/** Runtime image inputs for image-to-image generation */
|
|
287
|
+
images: GenerateImageInput[];
|
|
288
|
+
/** Optional mask for image editing */
|
|
289
|
+
mask?: GenerateImageInput;
|
|
290
|
+
} |
|
|
291
|
+
{
|
|
292
|
+
images?: undefined;
|
|
293
|
+
mask?: never;
|
|
294
|
+
};
|
|
295
|
+
|
|
284
296
|
/** Parameters accepted by {@link generateImage}. */
|
|
285
|
-
export type GenerateImageParameters = PromptCallOptions & {
|
|
286
|
-
/** Runtime image inputs for image-to-image generation */
|
|
287
|
-
images?: GenerateImageInput[];
|
|
288
|
-
/** Optional mask for image editing; requires `images` */
|
|
289
|
-
mask?: GenerateImageInput;
|
|
297
|
+
export type GenerateImageParameters = PromptCallOptions & GenerateImageInputs & {
|
|
290
298
|
/** Abort signal for the request */
|
|
291
299
|
abortSignal?: AbortSignal;
|
|
292
300
|
};
|
|
@@ -493,7 +501,7 @@ export function getProviderNames(): string[];
|
|
|
493
501
|
* Use an LLM model to generate text.
|
|
494
502
|
*
|
|
495
503
|
* This function is a wrapper over the AI SDK's `generateText`.
|
|
496
|
-
* The prompt sets `model`, `messages`,
|
|
504
|
+
* The prompt sets `model`, `messages`, generation settings, `maxSteps`, `skills`, and
|
|
497
505
|
* `providerOptions`. Pass either a prompt filename with optional `promptDir` / `variables`, or a
|
|
498
506
|
* loaded or custom {@link Prompt} object. Other call arguments are `tools`, `output`, `toolChoice`,
|
|
499
507
|
* `stopWhen`, and `abortSignal`.
|
package/src/utils/wrap.js
CHANGED
|
@@ -7,6 +7,7 @@ import { mapAiError } from './error_handler.js';
|
|
|
7
7
|
import { isPromise } from 'node:util/types';
|
|
8
8
|
import { Logger } from '@outputai/core';
|
|
9
9
|
import { convertCostToLegacy } from './legacy_cost_attribute.js';
|
|
10
|
+
import { randomBytes } from 'node:crypto';
|
|
10
11
|
|
|
11
12
|
/** Creates a proxy of the AI SDK response and attach virtual getters to it based on an object map */
|
|
12
13
|
const createResponseProxy = ( { response, properties } ) => new Proxy( response, {
|
|
@@ -17,7 +18,7 @@ const createResponseProxy = ( { response, properties } ) => new Proxy( response,
|
|
|
17
18
|
|
|
18
19
|
/** Generate a trace id, starts the tracing and return the id */
|
|
19
20
|
const startTrace = ( { name, prompt } ) => {
|
|
20
|
-
const traceId = `${name}-${Date.now()}`;
|
|
21
|
+
const traceId = `${name}-${Date.now()}-${randomBytes( 4 ).toString( 'hex' )}`;
|
|
21
22
|
Tracing.addEventStart( { kind: 'llm', id: traceId, name, details: { prompt } } );
|
|
22
23
|
return traceId;
|
|
23
24
|
};
|
|
@@ -105,20 +106,48 @@ export const wrapGeneration = async ( { name, prompt, fn } ) => {
|
|
|
105
106
|
*
|
|
106
107
|
* A throw or rejected Promise from `fn` (stream creation / Agent setup) is mapped and recorded
|
|
107
108
|
* on the LLM trace. A non-Promise return (the `streamText` stream) is returned immediately.
|
|
109
|
+
* When `abortSignal` aborts, its reason is recorded as an LLM trace error. The listener is removed
|
|
110
|
+
* when the stream ends, reports an error, or fails during setup.
|
|
108
111
|
*
|
|
109
112
|
* @param {object} args
|
|
110
113
|
* @param {string} args.name - Trace event name
|
|
111
114
|
* @param {object} args.prompt - Loaded prompt (`config.provider` / `config.model` used for cost)
|
|
115
|
+
* @param {AbortSignal} [args.abortSignal] - Optional signal used to trace stream cancellation
|
|
112
116
|
* @param {(hooks: { onEndHook: Function, onErrorHook: Function }) => object | Promise<object>} args.fn -
|
|
113
117
|
* Return the SDK stream, or a Promise of that stream (`Agent.stream`); wire the hooks into SDK
|
|
114
118
|
* `onEnd` / `onError`
|
|
115
119
|
* @returns {object | Promise<object>} Value returned by `fn`; a rejected Promise is remapped
|
|
116
120
|
*/
|
|
117
|
-
export const wrapStream = ( { name, prompt, fn } ) => {
|
|
121
|
+
export const wrapStream = ( { name, prompt, abortSignal, fn } ) => {
|
|
118
122
|
const traceId = startTrace( { name, prompt } );
|
|
119
123
|
|
|
124
|
+
const onAbortHook = reason =>
|
|
125
|
+
handleError( {
|
|
126
|
+
traceId,
|
|
127
|
+
error: reason instanceof Error ? reason : new Error( 'Streaming aborted.', { cause: reason } )
|
|
128
|
+
} );
|
|
129
|
+
|
|
130
|
+
const handleAbort = () => {
|
|
131
|
+
if ( !abortSignal ) {
|
|
132
|
+
return () => {};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if ( abortSignal.aborted ) {
|
|
136
|
+
onAbortHook( abortSignal.reason );
|
|
137
|
+
return () => {};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const listener = () => onAbortHook( abortSignal.reason );
|
|
141
|
+
abortSignal.addEventListener( 'abort', listener, { once: true } );
|
|
142
|
+
|
|
143
|
+
return () => abortSignal.removeEventListener( 'abort', listener );
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const removeAbortListener = handleAbort();
|
|
147
|
+
|
|
120
148
|
const onEndHook = async ( response, callback ) => {
|
|
121
149
|
const state = { proxyResponse: null };
|
|
150
|
+
removeAbortListener();
|
|
122
151
|
try {
|
|
123
152
|
const { text: result, finalStep, usage } = response;
|
|
124
153
|
const { providerMetadata } = finalStep;
|
|
@@ -127,6 +156,7 @@ export const wrapStream = ( { name, prompt, fn } ) => {
|
|
|
127
156
|
Tracing.addEventEnd( { id: traceId, details: { result, usage, providerMetadata, sources } } );
|
|
128
157
|
state.proxyResponse = createResponseProxy( { response, properties: { cost, sources, result } } );
|
|
129
158
|
} catch ( error ) {
|
|
159
|
+
Logger.error( 'Stream onEnd() handler failed', { namespace: 'LLM', error: error?.message ?? String( error ) } );
|
|
130
160
|
throw handleError( { traceId, error } );
|
|
131
161
|
}
|
|
132
162
|
|
|
@@ -143,6 +173,7 @@ export const wrapStream = ( { name, prompt, fn } ) => {
|
|
|
143
173
|
|
|
144
174
|
const onErrorHook = async ( event, callback ) => {
|
|
145
175
|
const error = handleError( { traceId, error: event.error } );
|
|
176
|
+
removeAbortListener();
|
|
146
177
|
try {
|
|
147
178
|
await callback?.( error );
|
|
148
179
|
} catch ( callbackError ) {
|
|
@@ -157,9 +188,11 @@ export const wrapStream = ( { name, prompt, fn } ) => {
|
|
|
157
188
|
try {
|
|
158
189
|
const stream = fn( { onEndHook, onErrorHook } );
|
|
159
190
|
return isPromise( stream ) ? stream.catch( error => {
|
|
191
|
+
removeAbortListener();
|
|
160
192
|
throw handleError( { traceId, error } );
|
|
161
193
|
} ) : stream;
|
|
162
194
|
} catch ( error ) {
|
|
195
|
+
removeAbortListener();
|
|
163
196
|
throw handleError( { traceId, error } );
|
|
164
197
|
}
|
|
165
198
|
};
|