@outputai/llm 0.11.1-next.3a40eca.0 → 0.11.1-next.a07f3e4.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 +22 -17
- package/src/agent.js +114 -88
- package/src/ai_provider.js +2 -2
- package/src/ai_sdk_options.js +93 -39
- package/src/consts.js +6 -0
- package/src/generate.js +82 -0
- package/src/index.d.ts +335 -278
- package/src/index.js +3 -4
- package/src/prompt/content.js +70 -0
- package/src/prompt/{escape.js → interpolations.js} +11 -18
- package/src/prompt/loader.js +45 -39
- package/src/prompt/markup/attributes.js +52 -0
- package/src/prompt/markup/nodes.js +71 -0
- package/src/prompt/markup/tokenizer.js +3 -0
- package/src/prompt/markup/tokens.js +26 -0
- package/src/prompt/validations.js +100 -68
- package/src/utils/cost.js +137 -0
- package/src/{prompt/load_content.js → utils/file.js} +12 -15
- package/src/utils/legacy_cost_attribute.js +75 -0
- package/src/utils/models.js +15 -0
- package/src/utils/models_pricing.js +68 -0
- package/src/utils/skills.js +74 -0
- package/src/utils/sources.js +48 -0
- package/src/utils/stream.js +22 -0
- package/src/utils/tools.js +47 -0
- package/src/utils/usage.js +121 -0
- package/src/utils/wrap.js +198 -0
- package/src/validations.js +188 -28
- package/src/agent.spec.js +0 -491
- package/src/ai_model.js +0 -60
- package/src/ai_model.spec.js +0 -259
- package/src/ai_provider.spec.js +0 -217
- package/src/ai_sdk.js +0 -92
- package/src/ai_sdk.spec.js +0 -564
- package/src/ai_sdk_options.spec.js +0 -230
- package/src/cost/fetch_models_pricing.js +0 -60
- package/src/cost/fetch_models_pricing.spec.js +0 -168
- package/src/cost/fixtures/models_api_light.json +0 -675
- package/src/cost/index.js +0 -58
- package/src/cost/index.spec.js +0 -350
- package/src/prompt/block_options.js +0 -58
- package/src/prompt/block_options.spec.js +0 -71
- package/src/prompt/blocks.js +0 -47
- package/src/prompt/blocks.spec.js +0 -63
- package/src/prompt/escape.spec.js +0 -159
- package/src/prompt/load_content.spec.js +0 -83
- package/src/prompt/loader.spec.js +0 -361
- package/src/prompt/loader_validation.spec.js +0 -128
- package/src/prompt/parser.js +0 -16
- package/src/prompt/parser.spec.js +0 -186
- package/src/prompt/prepare_text.js +0 -27
- package/src/prompt/prepare_text.spec.js +0 -141
- package/src/prompt/skill.js +0 -128
- package/src/prompt/skill.spec.js +0 -172
- package/src/prompt/validations.spec.js +0 -641
- package/src/utils/__fixtures__/image_response.json +0 -38
- package/src/utils/__fixtures__/stream_response.json +0 -294
- package/src/utils/__fixtures__/text_response.json +0 -201
- package/src/utils/error_handler.spec.js +0 -230
- package/src/utils/image.spec.js +0 -20
- package/src/utils/message.js +0 -3
- package/src/utils/message.spec.js +0 -29
- package/src/utils/response_wrappers.js +0 -100
- package/src/utils/response_wrappers.spec.js +0 -240
- package/src/utils/source_extraction.js +0 -53
- package/src/utils/source_extraction.spec.js +0 -194
- package/src/utils/trace.js +0 -19
- package/src/utils/trace.spec.js +0 -112
- package/src/validations.spec.js +0 -166
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@outputai/llm",
|
|
3
|
-
"version": "0.11.1-next.
|
|
3
|
+
"version": "0.11.1-next.a07f3e4.0",
|
|
4
4
|
"description": "Framework abstraction to interact with LLM models",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -9,32 +9,37 @@
|
|
|
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": {
|
|
19
|
+
"decimal.js": "10.6.0",
|
|
15
20
|
"entities": "8.0.0",
|
|
16
21
|
"gray-matter": "4.0.3",
|
|
17
22
|
"liquidjs": "10.27.2",
|
|
18
23
|
"undici": "8.9.0",
|
|
19
|
-
"@outputai/core": "0.11.1-next.
|
|
24
|
+
"@outputai/core": "0.11.1-next.a07f3e4.0"
|
|
20
25
|
},
|
|
21
26
|
"devDependencies": {
|
|
22
|
-
"@ai-sdk/amazon-bedrock": "
|
|
23
|
-
"@ai-sdk/anthropic": "
|
|
24
|
-
"@ai-sdk/azure": "
|
|
25
|
-
"@ai-sdk/google-vertex": "
|
|
26
|
-
"@ai-sdk/openai": "
|
|
27
|
-
"@ai-sdk/perplexity": "
|
|
28
|
-
"ai": "
|
|
27
|
+
"@ai-sdk/amazon-bedrock": "5.0.57",
|
|
28
|
+
"@ai-sdk/anthropic": "4.0.39",
|
|
29
|
+
"@ai-sdk/azure": "4.0.43",
|
|
30
|
+
"@ai-sdk/google-vertex": "5.0.54",
|
|
31
|
+
"@ai-sdk/openai": "4.0.42",
|
|
32
|
+
"@ai-sdk/perplexity": "4.0.29",
|
|
33
|
+
"ai": "7.0.66"
|
|
29
34
|
},
|
|
30
35
|
"peerDependencies": {
|
|
31
|
-
"@ai-sdk/amazon-bedrock": ">=
|
|
32
|
-
"@ai-sdk/anthropic": ">=
|
|
33
|
-
"@ai-sdk/azure": ">=
|
|
34
|
-
"@ai-sdk/google-vertex": ">=
|
|
35
|
-
"@ai-sdk/openai": ">=
|
|
36
|
-
"@ai-sdk/perplexity": ">=
|
|
37
|
-
"ai": ">=
|
|
36
|
+
"@ai-sdk/amazon-bedrock": ">=5 <6",
|
|
37
|
+
"@ai-sdk/anthropic": ">=4 <5",
|
|
38
|
+
"@ai-sdk/azure": ">=4 <5",
|
|
39
|
+
"@ai-sdk/google-vertex": ">=5 <6",
|
|
40
|
+
"@ai-sdk/openai": ">=4 <5",
|
|
41
|
+
"@ai-sdk/perplexity": ">=4 <5",
|
|
42
|
+
"ai": ">=7 <8"
|
|
38
43
|
},
|
|
39
44
|
"license": "Apache-2.0",
|
|
40
45
|
"publishConfig": {
|
package/src/agent.js
CHANGED
|
@@ -1,112 +1,138 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Path } from '@outputai/core/sdk/helpers';
|
|
3
|
-
import { ToolLoopAgent as AIToolLoopAgent, stepCountIs } from 'ai';
|
|
1
|
+
import { ToolLoopAgent as AIToolLoopAgent } from 'ai';
|
|
4
2
|
import { loadAiSdkTextOptions } from './ai_sdk_options.js';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
addMessages: newMessages => messages.push( ...newMessages )
|
|
16
|
-
};
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
export class Agent extends AIToolLoopAgent {
|
|
3
|
+
import { wrapGeneration, wrapStream } from './utils/wrap.js';
|
|
4
|
+
import { Role } from './consts.js';
|
|
5
|
+
import { drainStream } from './utils/stream.js';
|
|
6
|
+
import { loadPrompt } from './prompt/loader.js';
|
|
7
|
+
import { loadSkills } from './utils/skills.js';
|
|
8
|
+
import * as Validator from './validations.js';
|
|
9
|
+
import { Logger } from '@outputai/core';
|
|
10
|
+
|
|
11
|
+
export class Agent {
|
|
12
|
+
#agent;
|
|
20
13
|
#prompt;
|
|
21
|
-
#modelId;
|
|
22
|
-
#providerId;
|
|
23
14
|
#initialMessages;
|
|
24
15
|
#store;
|
|
25
16
|
|
|
26
|
-
constructor( {
|
|
27
|
-
|
|
28
|
-
promptDir,
|
|
29
|
-
variables = {},
|
|
30
|
-
skills = [],
|
|
31
|
-
tools: toolsArg,
|
|
32
|
-
stopWhen,
|
|
33
|
-
maxSteps = 10,
|
|
34
|
-
conversationStore,
|
|
35
|
-
...rest
|
|
36
|
-
} ) {
|
|
37
|
-
if ( !prompt ) {
|
|
38
|
-
throw new ValidationError( 'Agent requires a prompt' );
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Must be captured synchronously — Temporal async activity execution
|
|
42
|
-
// breaks the call stack, so Path.resolveInvocationDir() fails if called lazily.
|
|
43
|
-
const resolvedPromptDir = promptDir ?? Path.resolveInvocationDir();
|
|
17
|
+
constructor( args ) {
|
|
18
|
+
const { promptFile, promptObject, promptDir, variables, tools, output, stopWhen, messageStore } = Validator.parseAgentArgs( args );
|
|
44
19
|
|
|
45
|
-
const
|
|
20
|
+
const prompt = promptObject ?? loadPrompt( promptFile, variables, promptDir );
|
|
21
|
+
const skills = loadSkills( prompt );
|
|
46
22
|
|
|
47
|
-
const {
|
|
23
|
+
const { instructions, messages, ...aiOptions } = loadAiSdkTextOptions( { prompt, skills, tools, output, stopWhen } );
|
|
48
24
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
...constructorOptions,
|
|
53
|
-
...( system.length > 0 ? { instructions: system } : {} ),
|
|
54
|
-
...( tools ? { tools } : {} ),
|
|
55
|
-
stopWhen: stopWhen ?? stepCountIs( maxSteps ),
|
|
56
|
-
...rest
|
|
25
|
+
this.#agent = new AIToolLoopAgent( {
|
|
26
|
+
...aiOptions,
|
|
27
|
+
...( instructions.length > 0 ? { instructions } : {} )
|
|
57
28
|
} );
|
|
58
29
|
|
|
59
30
|
this.#prompt = prompt;
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
this.#initialMessages = messages.filter( isRole( ROLE.USER ) );
|
|
65
|
-
this.#store = conversationStore ?? null;
|
|
31
|
+
// `messages` is system-free but may still hold authored <assistant> blocks;
|
|
32
|
+
// seed only <user> turns into each generate()/stream() call.
|
|
33
|
+
this.#initialMessages = messages.filter( m => m.role === Role.USER );
|
|
34
|
+
this.#store = messageStore ?? null;
|
|
66
35
|
}
|
|
67
36
|
|
|
68
|
-
async #
|
|
69
|
-
|
|
70
|
-
return [ ...this.#initialMessages, ...priorMessages, ...userMessages ];
|
|
37
|
+
async #combineWithPreviousMessages( messages ) {
|
|
38
|
+
return [ ...this.#initialMessages, ...( await this.#store?.getMessages() ?? [] ), ...messages ];
|
|
71
39
|
}
|
|
72
40
|
|
|
73
|
-
async #storeMessages(
|
|
41
|
+
async #storeMessages( messages ) {
|
|
74
42
|
if ( this.#store ) {
|
|
75
|
-
await this.#store.addMessages(
|
|
43
|
+
await this.#store.addMessages( messages );
|
|
76
44
|
}
|
|
77
45
|
}
|
|
78
46
|
|
|
79
|
-
async generate(
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
47
|
+
async generate( args ) {
|
|
48
|
+
const { messages, abortSignal, toolChoice } = Validator.parseAgentGenerateArgs( args );
|
|
49
|
+
const combinedMessages = await this.#combineWithPreviousMessages( messages );
|
|
50
|
+
|
|
51
|
+
return wrapGeneration( {
|
|
52
|
+
name: 'Agent.generate',
|
|
53
|
+
prompt: this.#prompt,
|
|
54
|
+
fn: async () => {
|
|
55
|
+
const response = await this.#agent.generate( {
|
|
56
|
+
messages: combinedMessages,
|
|
57
|
+
allowSystemInMessages: true,
|
|
58
|
+
...( abortSignal && { abortSignal } ),
|
|
59
|
+
...( toolChoice && { toolChoice } )
|
|
60
|
+
} );
|
|
61
|
+
if ( response.finishReason !== 'error' ) {
|
|
62
|
+
await this.#storeMessages( messages.concat( response.responseMessages ?? [] ) );
|
|
63
|
+
}
|
|
64
|
+
return response;
|
|
65
|
+
}
|
|
66
|
+
} );
|
|
91
67
|
}
|
|
92
68
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
69
|
+
/**
|
|
70
|
+
* Generates a completed agent response over streaming transport, invoking `onChunk` as parts arrive.
|
|
71
|
+
*/
|
|
72
|
+
async generateWithStreaming( args ) {
|
|
73
|
+
const { messages, abortSignal, toolChoice, onChunk } = Validator.parseAgentGenerateWithStreamingArgs( args );
|
|
74
|
+
const combinedMessages = await this.#combineWithPreviousMessages( messages );
|
|
75
|
+
|
|
76
|
+
return wrapGeneration( {
|
|
77
|
+
name: 'Agent.generateWithStreaming',
|
|
78
|
+
prompt: this.#prompt,
|
|
79
|
+
fn: async () => {
|
|
80
|
+
const state = { response: null };
|
|
81
|
+
const stream = await this.#agent.stream( {
|
|
82
|
+
messages: combinedMessages,
|
|
83
|
+
allowSystemInMessages: true,
|
|
84
|
+
...( onChunk && { onChunk } ),
|
|
85
|
+
...( abortSignal && { abortSignal } ),
|
|
86
|
+
...( toolChoice && { toolChoice } ),
|
|
87
|
+
onEnd: res => {
|
|
88
|
+
state.response = res;
|
|
89
|
+
},
|
|
90
|
+
onError: _ => {} // Suppress AI-SDK console printing
|
|
91
|
+
} );
|
|
92
|
+
|
|
93
|
+
await drainStream( stream, abortSignal );
|
|
94
|
+
|
|
95
|
+
if ( !state.response ) {
|
|
96
|
+
throw new Error( 'Agent streaming generation completed without a response.' );
|
|
105
97
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
98
|
+
|
|
99
|
+
state.response.output = await stream.output;
|
|
100
|
+
if ( state.response.finishReason !== 'error' ) {
|
|
101
|
+
await this.#storeMessages( messages.concat( state.response.responseMessages ?? [] ) );
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return state.response;
|
|
105
|
+
}
|
|
106
|
+
} );
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async stream( args ) {
|
|
110
|
+
const { messages, abortSignal, toolChoice, onChunk, onEnd, onError } = Validator.parseAgentStreamArgs( args );
|
|
111
|
+
const combinedMessages = await this.#combineWithPreviousMessages( messages );
|
|
112
|
+
|
|
113
|
+
return wrapStream( {
|
|
114
|
+
name: 'Agent.stream',
|
|
115
|
+
prompt: this.#prompt,
|
|
116
|
+
abortSignal,
|
|
117
|
+
fn: ( { onEndHook, onErrorHook } ) => this.#agent.stream( {
|
|
118
|
+
messages: combinedMessages,
|
|
119
|
+
allowSystemInMessages: true,
|
|
120
|
+
...( onChunk && { onChunk } ),
|
|
121
|
+
...( abortSignal && { abortSignal } ),
|
|
122
|
+
...( toolChoice && { toolChoice } ),
|
|
123
|
+
onEnd: response =>
|
|
124
|
+
onEndHook( response, async parsedResponse => {
|
|
125
|
+
if ( response.finishReason !== 'error' ) {
|
|
126
|
+
await this.#storeMessages( messages.concat( response.responseMessages ?? [] ) )
|
|
127
|
+
.catch( error => Logger.error( 'Agent.stream message store persistence failed', {
|
|
128
|
+
namespace: 'LLM',
|
|
129
|
+
error: error instanceof Error ? error.message : String( error )
|
|
130
|
+
} ) );
|
|
131
|
+
}
|
|
132
|
+
await onEnd?.( parsedResponse );
|
|
133
|
+
} ),
|
|
134
|
+
onError: event => onErrorHook( event, error => onError?.( { ...event, error } ) )
|
|
135
|
+
} )
|
|
136
|
+
} );
|
|
111
137
|
}
|
|
112
138
|
}
|
package/src/ai_provider.js
CHANGED
|
@@ -53,7 +53,7 @@ const registerProviderSchema = z.object( {
|
|
|
53
53
|
* @param {Function} providerFn - Factory function that receives a model id
|
|
54
54
|
* @returns {void}
|
|
55
55
|
*/
|
|
56
|
-
export
|
|
56
|
+
export const registerProvider = ( name, providerFn ) => {
|
|
57
57
|
const result = registerProviderSchema.safeParse( { name, providerFn } );
|
|
58
58
|
if ( !result.success ) {
|
|
59
59
|
throw new ValidationError( `Invalid provider registration: ${z.prettifyError( result.error )}` );
|
|
@@ -65,7 +65,7 @@ export function registerProvider( name, providerFn ) {
|
|
|
65
65
|
);
|
|
66
66
|
}
|
|
67
67
|
registeredProviders[name] = providerFn;
|
|
68
|
-
}
|
|
68
|
+
};
|
|
69
69
|
|
|
70
70
|
/**
|
|
71
71
|
* Return a provider by its name.
|
package/src/ai_sdk_options.js
CHANGED
|
@@ -1,72 +1,126 @@
|
|
|
1
|
-
import { loadImageModel, loadTextModel
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { loadImageModel, loadTextModel } from './utils/models.js';
|
|
2
|
+
import { buildLoadSkillTool, loadPromptTools } from './utils/tools.js';
|
|
3
|
+
import { Role } from './consts.js';
|
|
4
4
|
import { FatalError } from '@outputai/core';
|
|
5
|
+
import { isStepCount } from 'ai';
|
|
6
|
+
|
|
7
|
+
const buildSkillsMessageContent = skills =>
|
|
8
|
+
'Available skills (use load_skill to get full instructions):\n' +
|
|
9
|
+
skills.map( s => `- ${s.name}: ${s.description}` ).join( '\n' );
|
|
10
|
+
|
|
11
|
+
const textPromptConfigKeys = [
|
|
12
|
+
'frequencyPenalty',
|
|
13
|
+
'maxOutputTokens',
|
|
14
|
+
'presencePenalty',
|
|
15
|
+
'providerOptions',
|
|
16
|
+
'seed',
|
|
17
|
+
'stopSequences',
|
|
18
|
+
'temperature',
|
|
19
|
+
'topK',
|
|
20
|
+
'topP'
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
const imagePromptConfigKeys = [
|
|
24
|
+
'aspectRatio',
|
|
25
|
+
'maxImagesPerCall',
|
|
26
|
+
'n',
|
|
27
|
+
'providerOptions',
|
|
28
|
+
'seed',
|
|
29
|
+
'size'
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
const select = ( keys, target ) =>
|
|
33
|
+
Object.fromEntries( Object.entries( target ).filter( ( [ k, v ] ) => keys.includes( k ) && v !== undefined ) );
|
|
5
34
|
|
|
6
35
|
/**
|
|
7
|
-
*
|
|
36
|
+
* Build options for AI SDK text generation.
|
|
8
37
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* in `messages` — the AI SDK flags system roles inside `messages` as a prompt
|
|
12
|
-
* injection risk, and `system` is the provider-recommended slot.
|
|
38
|
+
* Splits system vs chat messages, applies prompt generation config, and merges tools
|
|
39
|
+
* with a step limit when any tools are present. Always sets `maxRetries` to 0.
|
|
13
40
|
*
|
|
14
|
-
* @param {object}
|
|
15
|
-
* @
|
|
41
|
+
* @param {object} args
|
|
42
|
+
* @param {object} args.prompt - Loaded prompt object
|
|
43
|
+
* @param {object} [args.tools] - Caller tools
|
|
44
|
+
* @param {Skill[]} args.skills - Resolved skills
|
|
45
|
+
* @param {unknown} [args.stopWhen] - Caller stop condition; otherwise prompt `maxSteps` when tools exist
|
|
46
|
+
* @param {unknown} [args.output] - Structured output spec
|
|
47
|
+
* @param {unknown} [args.toolChoice] - Tool choice, set only when tools exist
|
|
48
|
+
* @param {AbortSignal} [args.abortSignal] - Abort signal for the request
|
|
49
|
+
* @returns {object} AI SDK text options
|
|
16
50
|
*/
|
|
17
|
-
export const loadAiSdkTextOptions = prompt => {
|
|
51
|
+
export const loadAiSdkTextOptions = ( { prompt, tools, skills, stopWhen, output, toolChoice, abortSignal } ) => {
|
|
18
52
|
if ( prompt.messages.length === 0 ) {
|
|
19
53
|
throw new FatalError( `Prompt "${prompt.name}" has no chat-style messages. Add role-tagged blocks like <system> or <user>.` );
|
|
20
54
|
}
|
|
21
|
-
const isSystem = isRole( ROLE.SYSTEM );
|
|
22
|
-
const resolvedMessages = resolveMessageProviderOptions( prompt );
|
|
23
55
|
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
system: resolvedMessages.filter( isSystem ),
|
|
27
|
-
messages: resolvedMessages.filter( message => !isSystem( message ) ),
|
|
28
|
-
providerOptions: prompt.config.providerOptions
|
|
29
|
-
};
|
|
56
|
+
const instructions = prompt.messages.filter( m => m.role === Role.SYSTEM );
|
|
57
|
+
const messages = prompt.messages.filter( m => m.role !== Role.SYSTEM );
|
|
30
58
|
|
|
31
|
-
if (
|
|
32
|
-
|
|
59
|
+
if ( skills.length > 0 ) {
|
|
60
|
+
const skillsMessageContent = buildSkillsMessageContent( skills );
|
|
61
|
+
if ( instructions.length > 0 ) {
|
|
62
|
+
instructions[0] = { ...instructions[0], content: `${instructions[0].content}\n\n${skillsMessageContent}` };
|
|
63
|
+
} else {
|
|
64
|
+
instructions.push( { role: Role.SYSTEM, content: skillsMessageContent } );
|
|
65
|
+
}
|
|
33
66
|
}
|
|
34
67
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
68
|
+
const options = {
|
|
69
|
+
allowSystemInMessages: true,
|
|
70
|
+
maxRetries: 0,
|
|
71
|
+
model: loadTextModel( prompt ),
|
|
72
|
+
instructions,
|
|
73
|
+
messages,
|
|
74
|
+
...( output && { output } ),
|
|
75
|
+
...( abortSignal && { abortSignal } ),
|
|
76
|
+
...( stopWhen && { stopWhen } ),
|
|
77
|
+
...select( textPromptConfigKeys, prompt.config )
|
|
78
|
+
};
|
|
38
79
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
80
|
+
// Tools parsing
|
|
81
|
+
const promptTools = loadPromptTools( prompt );
|
|
82
|
+
const skillsTools = skills.length > 0 ? { load_skill: buildLoadSkillTool( skills ) } : {};
|
|
83
|
+
const mergedTools = { ...promptTools, ...tools, ...skillsTools };
|
|
84
|
+
if ( Object.keys( mergedTools ).length > 0 ) {
|
|
85
|
+
options.tools = mergedTools;
|
|
86
|
+
if ( toolChoice ) {
|
|
87
|
+
options.toolChoice = toolChoice;
|
|
88
|
+
}
|
|
89
|
+
if ( !options.stopWhen ) {
|
|
90
|
+
options.stopWhen = isStepCount( prompt.config.maxSteps );
|
|
91
|
+
}
|
|
42
92
|
}
|
|
43
93
|
|
|
44
94
|
return options;
|
|
45
95
|
};
|
|
46
96
|
|
|
47
97
|
/**
|
|
48
|
-
*
|
|
98
|
+
* Build options for AI SDK image generation.
|
|
99
|
+
*
|
|
100
|
+
* Uses prompt instructions (with optional source images and mask) and prompt-owned
|
|
101
|
+
* image config (`n`, `size`, `aspectRatio`, `seed`, `maxImagesPerCall`, `providerOptions`).
|
|
102
|
+
* Always sets `maxRetries` to 0.
|
|
49
103
|
*
|
|
50
|
-
* @param {object}
|
|
51
|
-
* @
|
|
104
|
+
* @param {object} args
|
|
105
|
+
* @param {object} args.prompt - Loaded prompt object
|
|
106
|
+
* @param {unknown} [args.images] - Source images for image-to-image
|
|
107
|
+
* @param {unknown} [args.mask] - Inpainting mask
|
|
108
|
+
* @param {AbortSignal} [args.abortSignal] - Abort signal for the request
|
|
109
|
+
* @returns {object} AI SDK image options
|
|
52
110
|
*/
|
|
53
|
-
export const loadAiSdkImageOptions = ( { prompt, images, mask } ) => {
|
|
111
|
+
export const loadAiSdkImageOptions = ( { prompt, images, mask, abortSignal } ) => {
|
|
54
112
|
if ( !prompt.instructions ) {
|
|
55
113
|
throw new FatalError( `Prompt "${prompt.name}" has no instructions. Image prompts must use plain instructions.` );
|
|
56
114
|
}
|
|
57
|
-
|
|
115
|
+
return {
|
|
116
|
+
maxRetries: 0,
|
|
58
117
|
model: loadImageModel( prompt ),
|
|
59
118
|
prompt: ( images || mask ) ? {
|
|
60
119
|
text: prompt.instructions,
|
|
61
120
|
...( images && { images } ),
|
|
62
121
|
...( mask && { mask } )
|
|
63
122
|
} : prompt.instructions,
|
|
64
|
-
|
|
123
|
+
...( abortSignal && { abortSignal } ),
|
|
124
|
+
...select( imagePromptConfigKeys, prompt.config )
|
|
65
125
|
};
|
|
66
|
-
for ( const key of [ 'n', 'maxImagesPerCall', 'size', 'aspectRatio', 'seed' ] ) {
|
|
67
|
-
if ( prompt.config[key] !== undefined ) {
|
|
68
|
-
options[key] = prompt.config[key];
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
return options;
|
|
72
126
|
};
|
package/src/consts.js
ADDED
package/src/generate.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import * as AI from 'ai';
|
|
2
|
+
import { loadPrompt } from './prompt/loader.js';
|
|
3
|
+
import { wrapGeneration, wrapStream } from './utils/wrap.js';
|
|
4
|
+
import { loadAiSdkTextOptions, loadAiSdkImageOptions } from './ai_sdk_options.js';
|
|
5
|
+
import { drainStream } from './utils/stream.js';
|
|
6
|
+
import { loadSkills } from './utils/skills.js';
|
|
7
|
+
import * as Validator from './validations.js';
|
|
8
|
+
|
|
9
|
+
export const generateText = async args => {
|
|
10
|
+
const { promptFile, promptObject, variables, promptDir, ...aiOptions } = Validator.parseGenerateTextArgs( args );
|
|
11
|
+
const prompt = promptObject ?? loadPrompt( promptFile, variables, promptDir );
|
|
12
|
+
const skills = loadSkills( prompt );
|
|
13
|
+
|
|
14
|
+
return wrapGeneration( {
|
|
15
|
+
name: 'generateText',
|
|
16
|
+
prompt,
|
|
17
|
+
fn: () => AI.generateText( loadAiSdkTextOptions( { prompt, skills, ...aiOptions } ) )
|
|
18
|
+
} );
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const streamText = args => {
|
|
22
|
+
const { promptFile, promptObject, variables, promptDir, onEnd, onError, onChunk, ...aiOptions } = Validator.parseStreamTextArgs( args );
|
|
23
|
+
const prompt = promptObject ?? loadPrompt( promptFile, variables, promptDir );
|
|
24
|
+
const skills = loadSkills( prompt );
|
|
25
|
+
|
|
26
|
+
return wrapStream( {
|
|
27
|
+
name: 'streamText',
|
|
28
|
+
prompt,
|
|
29
|
+
abortSignal: aiOptions.abortSignal,
|
|
30
|
+
fn: ( { onEndHook, onErrorHook } ) => AI.streamText( {
|
|
31
|
+
...loadAiSdkTextOptions( { prompt, skills, ...aiOptions } ),
|
|
32
|
+
...( onChunk && { onChunk } ),
|
|
33
|
+
onEnd: response => onEndHook( response, onEnd ),
|
|
34
|
+
onError: event => onErrorHook( event, error => onError?.( { ...event, error } ) )
|
|
35
|
+
} )
|
|
36
|
+
} );
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Generates a completed text response over streaming transport, invoking `onChunk` as parts arrive.
|
|
41
|
+
*/
|
|
42
|
+
export const generateTextWithStreaming = async args => {
|
|
43
|
+
const { promptFile, promptObject, variables, promptDir, onChunk, ...aiOptions } = Validator.parseGenerateTextWithStreamingArgs( args );
|
|
44
|
+
const prompt = promptObject ?? loadPrompt( promptFile, variables, promptDir );
|
|
45
|
+
const skills = loadSkills( prompt );
|
|
46
|
+
|
|
47
|
+
return wrapGeneration( {
|
|
48
|
+
name: 'generateTextWithStreaming',
|
|
49
|
+
prompt,
|
|
50
|
+
fn: async () => {
|
|
51
|
+
const state = { response: null };
|
|
52
|
+
const stream = AI.streamText( {
|
|
53
|
+
...loadAiSdkTextOptions( { prompt, skills, ...aiOptions } ),
|
|
54
|
+
...( onChunk && { onChunk } ),
|
|
55
|
+
onEnd: res => {
|
|
56
|
+
state.response = res;
|
|
57
|
+
},
|
|
58
|
+
onError: _ => {} // Suppress AI-SDK console printing
|
|
59
|
+
} );
|
|
60
|
+
|
|
61
|
+
await drainStream( stream, aiOptions.abortSignal );
|
|
62
|
+
|
|
63
|
+
if ( !state.response ) {
|
|
64
|
+
throw new Error( 'Streaming generation completed without a response.' );
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
state.response.output = await stream.output;
|
|
68
|
+
return state.response;
|
|
69
|
+
}
|
|
70
|
+
} );
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export const generateImage = async args => {
|
|
74
|
+
const { promptFile, promptObject, promptDir, variables, ...aiOptions } = Validator.parseGenerateImageArgs( args );
|
|
75
|
+
const prompt = promptObject ?? loadPrompt( promptFile, variables, promptDir );
|
|
76
|
+
|
|
77
|
+
return wrapGeneration( {
|
|
78
|
+
name: 'generateImage',
|
|
79
|
+
prompt,
|
|
80
|
+
fn: () => AI.generateImage( loadAiSdkImageOptions( { prompt, ...aiOptions } ) )
|
|
81
|
+
} );
|
|
82
|
+
};
|