@outputai/llm 0.2.1-next.fd72d95.0 → 0.3.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 +5 -5
- package/src/agent.js +31 -27
- package/src/agent.spec.js +47 -13
- package/src/ai_sdk.js +12 -8
- package/src/ai_sdk.spec.js +83 -216
- package/src/cost/index.js +9 -40
- package/src/cost/index.spec.js +42 -20
- package/src/index.d.ts +57 -13
- package/src/index.js +1 -1
- package/src/utils/message.spec.js +29 -0
- package/src/utils/response_wrappers.js +70 -0
- package/src/utils/response_wrappers.spec.js +172 -0
- package/src/{source_extraction.js → utils/source_extraction.js} +14 -0
- package/src/{source_extraction.spec.js → utils/source_extraction.spec.js} +26 -1
- package/src/utils/trace.js +18 -0
- package/src/utils/trace.spec.js +95 -0
- package/src/response_utils.js +0 -21
- package/src/trace_utils.js +0 -30
- /package/src/{message_utils.js → utils/message.js} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@outputai/llm",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Framework abstraction to interact with LLM models",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -9,20 +9,20 @@
|
|
|
9
9
|
"./src"
|
|
10
10
|
],
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@ai-sdk/amazon-bedrock": "4.0.
|
|
12
|
+
"@ai-sdk/amazon-bedrock": "4.0.96",
|
|
13
13
|
"@ai-sdk/anthropic": "3.0.71",
|
|
14
14
|
"@ai-sdk/azure": "3.0.54",
|
|
15
15
|
"@ai-sdk/google-vertex": "4.0.112",
|
|
16
16
|
"@ai-sdk/openai": "3.0.53",
|
|
17
17
|
"@ai-sdk/perplexity": "3.0.29",
|
|
18
18
|
"@exalabs/ai-sdk": "2.0.1",
|
|
19
|
-
"@perplexity-ai/ai-sdk": "0.1.
|
|
19
|
+
"@perplexity-ai/ai-sdk": "0.1.3",
|
|
20
20
|
"@tavily/ai-sdk": "0.4.1",
|
|
21
21
|
"ai": "6.0.168",
|
|
22
22
|
"decimal.js": "10.6.0",
|
|
23
23
|
"gray-matter": "4.0.3",
|
|
24
|
-
"liquidjs": "10.25.
|
|
25
|
-
"@outputai/core": "0.
|
|
24
|
+
"liquidjs": "10.25.7",
|
|
25
|
+
"@outputai/core": "0.3.0"
|
|
26
26
|
},
|
|
27
27
|
"license": "Apache-2.0",
|
|
28
28
|
"publishConfig": {
|
package/src/agent.js
CHANGED
|
@@ -2,9 +2,9 @@ import { ValidationError } from '@outputai/core';
|
|
|
2
2
|
import { resolveInvocationDir } from '@outputai/core/sdk_utils';
|
|
3
3
|
import { ToolLoopAgent as AIToolLoopAgent, stepCountIs } from 'ai';
|
|
4
4
|
import { hydratePromptTemplate, loadAiSdkOptionsFromPrompt } from './ai_sdk.js';
|
|
5
|
-
import { startTrace, endTraceWithError
|
|
6
|
-
import {
|
|
7
|
-
import { ROLE, isRole, getContent } from './
|
|
5
|
+
import { startTrace, endTraceWithError } from './utils/trace.js';
|
|
6
|
+
import { wrapTextResponse, wrapStreamOnFinishResponse } from './utils/response_wrappers.js';
|
|
7
|
+
import { ROLE, isRole, getContent } from './utils/message.js';
|
|
8
8
|
|
|
9
9
|
export { skill } from './skill.js';
|
|
10
10
|
|
|
@@ -17,10 +17,10 @@ export const createMemoryConversationStore = () => {
|
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
export class Agent extends AIToolLoopAgent {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
#prompt;
|
|
21
|
+
#modelId;
|
|
22
|
+
#initialMessages;
|
|
23
|
+
#store;
|
|
24
24
|
|
|
25
25
|
constructor( {
|
|
26
26
|
prompt, promptDir, variables = {}, skills = [], tools = {},
|
|
@@ -52,48 +52,52 @@ export class Agent extends AIToolLoopAgent {
|
|
|
52
52
|
...rest
|
|
53
53
|
} );
|
|
54
54
|
|
|
55
|
-
this
|
|
56
|
-
this
|
|
57
|
-
this
|
|
58
|
-
this
|
|
55
|
+
this.#prompt = prompt;
|
|
56
|
+
this.#modelId = loadedPrompt.config.model;
|
|
57
|
+
this.#initialMessages = allMessages.filter( isRole( ROLE.USER ) );
|
|
58
|
+
this.#store = conversationStore ?? null;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
async
|
|
62
|
-
const priorMessages = this
|
|
63
|
-
return [ ...this
|
|
61
|
+
async #fetchMessages( userMessages ) {
|
|
62
|
+
const priorMessages = this.#store ? await this.#store.getMessages() : [];
|
|
63
|
+
return [ ...this.#initialMessages, ...priorMessages, ...userMessages ];
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
async
|
|
67
|
-
if ( this
|
|
68
|
-
await this.
|
|
66
|
+
async #storeMessages( userMessages, result ) {
|
|
67
|
+
if ( this.#store ) {
|
|
68
|
+
await this.#store.addMessages( [ ...userMessages, ...( result.response?.messages ?? [] ) ] );
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
async generate( { messages: userMessages = [], ...callOptions } = {} ) {
|
|
73
|
-
const traceId = startTrace( 'Agent.generate',
|
|
73
|
+
const traceId = startTrace( { name: 'Agent.generate', prompt: this.#prompt } );
|
|
74
74
|
try {
|
|
75
|
-
const messages = await this
|
|
76
|
-
const
|
|
77
|
-
const wrapped = await
|
|
78
|
-
await this
|
|
75
|
+
const messages = await this.#fetchMessages( userMessages );
|
|
76
|
+
const response = await super.generate( { messages, ...callOptions } );
|
|
77
|
+
const wrapped = await wrapTextResponse( { traceId, response, modelId: this.#modelId } );
|
|
78
|
+
await this.#storeMessages( userMessages, wrapped );
|
|
79
79
|
return wrapped;
|
|
80
80
|
} catch ( error ) {
|
|
81
|
-
endTraceWithError( traceId, error );
|
|
81
|
+
endTraceWithError( { traceId, error } );
|
|
82
82
|
throw error;
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
async stream( { messages: userMessages = [], onFinish, onError, ...callOptions } = {} ) {
|
|
87
|
-
const traceId = startTrace( 'Agent.stream',
|
|
87
|
+
const traceId = startTrace( { name: 'Agent.stream', prompt: this.#prompt } );
|
|
88
88
|
try {
|
|
89
|
-
const messages = await this
|
|
89
|
+
const messages = await this.#fetchMessages( userMessages );
|
|
90
90
|
return super.stream( {
|
|
91
91
|
messages,
|
|
92
92
|
...callOptions,
|
|
93
|
-
...
|
|
93
|
+
...wrapStreamOnFinishResponse( { traceId, modelId: this.#modelId, onFinish } ),
|
|
94
|
+
onError( event ) {
|
|
95
|
+
endTraceWithError( { traceId, error: event.error } );
|
|
96
|
+
onError?.( event );
|
|
97
|
+
}
|
|
94
98
|
} );
|
|
95
99
|
} catch ( error ) {
|
|
96
|
-
endTraceWithError( traceId, error );
|
|
100
|
+
endTraceWithError( { traceId, error } );
|
|
97
101
|
throw error;
|
|
98
102
|
}
|
|
99
103
|
}
|
package/src/agent.spec.js
CHANGED
|
@@ -45,16 +45,16 @@ vi.mock( './ai_sdk.js', () => ( {
|
|
|
45
45
|
|
|
46
46
|
const startTraceImpl = vi.fn( () => 'trace-id' );
|
|
47
47
|
const endTraceWithErrorImpl = vi.fn();
|
|
48
|
-
|
|
49
|
-
vi.mock( './trace_utils.js', () => ( {
|
|
48
|
+
vi.mock( './utils/trace.js', () => ( {
|
|
50
49
|
startTrace: ( ...args ) => startTraceImpl( ...args ),
|
|
51
|
-
endTraceWithError: ( ...args ) => endTraceWithErrorImpl( ...args )
|
|
52
|
-
traceStreamCallbacks: ( ...args ) => traceStreamCallbacksImpl( ...args )
|
|
50
|
+
endTraceWithError: ( ...args ) => endTraceWithErrorImpl( ...args )
|
|
53
51
|
} ) );
|
|
54
52
|
|
|
55
|
-
const
|
|
56
|
-
vi.
|
|
57
|
-
|
|
53
|
+
const wrapTextResponseImpl = vi.fn( async ( { response } ) => response );
|
|
54
|
+
const wrapStreamOnFinishResponseImpl = vi.fn( () => ( {} ) );
|
|
55
|
+
vi.mock( './utils/response_wrappers.js', () => ( {
|
|
56
|
+
wrapTextResponse: ( ...args ) => wrapTextResponseImpl( ...args ),
|
|
57
|
+
wrapStreamOnFinishResponse: ( ...args ) => wrapStreamOnFinishResponseImpl( ...args )
|
|
58
58
|
} ) );
|
|
59
59
|
|
|
60
60
|
vi.mock( './skill.js', () => ( {
|
|
@@ -88,6 +88,8 @@ beforeEach( () => {
|
|
|
88
88
|
} );
|
|
89
89
|
superGenerateImpl.mockResolvedValue( { text: 'response', response: { messages: [] } } );
|
|
90
90
|
superStreamImpl.mockReturnValue( { textStream: 'stream' } );
|
|
91
|
+
wrapTextResponseImpl.mockImplementation( async ( { response } ) => response );
|
|
92
|
+
wrapStreamOnFinishResponseImpl.mockReturnValue( {} );
|
|
91
93
|
} );
|
|
92
94
|
|
|
93
95
|
// ─── Tests ────────────────────────────────────────────────────────────────────
|
|
@@ -263,14 +265,44 @@ describe( 'createMemoryConversationStore()', () => {
|
|
|
263
265
|
} );
|
|
264
266
|
} );
|
|
265
267
|
|
|
268
|
+
describe( 'Agent — utils delegation', () => {
|
|
269
|
+
it( 'generate() calls trace and wrapTextResponse with model id and response', async () => {
|
|
270
|
+
const { Agent } = await importSut();
|
|
271
|
+
const agent = new Agent( { prompt: 'test@v1' } );
|
|
272
|
+
await agent.generate( { messages: [ { role: 'user', content: 'hi' } ] } );
|
|
273
|
+
|
|
274
|
+
expect( startTraceImpl ).toHaveBeenCalledWith( { name: 'Agent.generate', prompt: 'test@v1' } );
|
|
275
|
+
expect( wrapTextResponseImpl ).toHaveBeenCalledWith( {
|
|
276
|
+
traceId: 'trace-id',
|
|
277
|
+
modelId: 'claude-sonnet-4-6',
|
|
278
|
+
response: expect.objectContaining( { text: 'response' } )
|
|
279
|
+
} );
|
|
280
|
+
} );
|
|
281
|
+
|
|
282
|
+
it( 'stream() calls trace and wrapStreamOnFinishResponse', async () => {
|
|
283
|
+
const { Agent } = await importSut();
|
|
284
|
+
const agent = new Agent( { prompt: 'test@v1' } );
|
|
285
|
+
await agent.stream();
|
|
286
|
+
|
|
287
|
+
expect( startTraceImpl ).toHaveBeenCalledWith( { name: 'Agent.stream', prompt: 'test@v1' } );
|
|
288
|
+
expect( wrapStreamOnFinishResponseImpl ).toHaveBeenCalledWith( {
|
|
289
|
+
traceId: 'trace-id',
|
|
290
|
+
modelId: 'claude-sonnet-4-6',
|
|
291
|
+
onFinish: undefined
|
|
292
|
+
} );
|
|
293
|
+
} );
|
|
294
|
+
} );
|
|
295
|
+
|
|
266
296
|
describe( 'Agent.stream()', () => {
|
|
267
297
|
it( 'uses pre-rendered messages when no variables provided', async () => {
|
|
268
298
|
const { Agent } = await importSut();
|
|
269
299
|
const agent = new Agent( { prompt: 'test@v1' } );
|
|
270
300
|
await agent.stream();
|
|
271
|
-
expect( superStreamImpl ).toHaveBeenCalledWith(
|
|
272
|
-
|
|
273
|
-
|
|
301
|
+
expect( superStreamImpl ).toHaveBeenCalledWith(
|
|
302
|
+
expect.objectContaining( {
|
|
303
|
+
messages: defaultMessages
|
|
304
|
+
} )
|
|
305
|
+
);
|
|
274
306
|
} );
|
|
275
307
|
|
|
276
308
|
it( 'loads prior messages from store', async () => {
|
|
@@ -284,9 +316,11 @@ describe( 'Agent.stream()', () => {
|
|
|
284
316
|
const agent = new Agent( { prompt: 'test@v1', conversationStore: store } );
|
|
285
317
|
await agent.stream( { messages: [ { role: 'user', content: 'new' } ] } );
|
|
286
318
|
|
|
287
|
-
expect( superStreamImpl ).toHaveBeenCalledWith(
|
|
288
|
-
|
|
289
|
-
|
|
319
|
+
expect( superStreamImpl ).toHaveBeenCalledWith(
|
|
320
|
+
expect.objectContaining( {
|
|
321
|
+
messages: [ ...defaultMessages, ...priorMessages, { role: 'user', content: 'new' } ]
|
|
322
|
+
} )
|
|
323
|
+
);
|
|
290
324
|
} );
|
|
291
325
|
|
|
292
326
|
it( 'does not auto-append to store', async () => {
|
package/src/ai_sdk.js
CHANGED
|
@@ -4,8 +4,8 @@ import { stepCountIs } from 'ai';
|
|
|
4
4
|
import { validateGenerateTextArgs, validateStreamTextArgs } from './validations.js';
|
|
5
5
|
import { loadPrompt } from './prompt_loader.js';
|
|
6
6
|
import { buildSystemSkillsVar, buildLoadSkillTool, loadPromptSkills, loadColocatedSkills } from './skill.js';
|
|
7
|
-
import { startTrace, endTraceWithError
|
|
8
|
-
import {
|
|
7
|
+
import { startTrace, endTraceWithError } from './utils/trace.js';
|
|
8
|
+
import { wrapTextResponse, wrapStreamOnFinishResponse } from './utils/response_wrappers.js';
|
|
9
9
|
|
|
10
10
|
export const loadAiSdkOptionsFromPrompt = prompt => {
|
|
11
11
|
const options = {
|
|
@@ -72,7 +72,7 @@ export async function generateText( { prompt, variables, promptDir, skills = [],
|
|
|
72
72
|
|
|
73
73
|
validateGenerateTextArgs( { prompt, variables: allVariables } );
|
|
74
74
|
|
|
75
|
-
const traceId = startTrace( 'generateText',
|
|
75
|
+
const traceId = startTrace( { name: 'generateText', prompt, variables: allVariables, loadedPrompt } );
|
|
76
76
|
const { model: modelId } = loadedPrompt.config;
|
|
77
77
|
|
|
78
78
|
try {
|
|
@@ -82,9 +82,9 @@ export async function generateText( { prompt, variables, promptDir, skills = [],
|
|
|
82
82
|
...( hasTools ? { tools } : {} ),
|
|
83
83
|
...( hasTools && !extraAiSdkOptions.stopWhen ? { stopWhen: stepCountIs( maxSteps ) } : {} )
|
|
84
84
|
} );
|
|
85
|
-
return
|
|
85
|
+
return wrapTextResponse( { traceId, modelId, response } );
|
|
86
86
|
} catch ( error ) {
|
|
87
|
-
endTraceWithError( traceId, error );
|
|
87
|
+
endTraceWithError( { traceId, error } );
|
|
88
88
|
throw error;
|
|
89
89
|
}
|
|
90
90
|
}
|
|
@@ -92,17 +92,21 @@ export async function generateText( { prompt, variables, promptDir, skills = [],
|
|
|
92
92
|
export function streamText( { prompt, variables, onFinish, onError, ...restOptions } ) {
|
|
93
93
|
validateStreamTextArgs( { prompt, variables } );
|
|
94
94
|
const loadedPrompt = loadPrompt( prompt, variables );
|
|
95
|
-
const traceId = startTrace( 'streamText',
|
|
95
|
+
const traceId = startTrace( { name: 'streamText', prompt, variables, loadedPrompt } );
|
|
96
96
|
const { model: modelId } = loadedPrompt.config;
|
|
97
97
|
|
|
98
98
|
try {
|
|
99
99
|
return AI.streamText( {
|
|
100
100
|
...loadAiSdkOptionsFromPrompt( loadedPrompt ),
|
|
101
101
|
...restOptions,
|
|
102
|
-
...
|
|
102
|
+
...wrapStreamOnFinishResponse( { traceId, modelId, onFinish } ),
|
|
103
|
+
onError( event ) {
|
|
104
|
+
endTraceWithError( { traceId, error: event.error } );
|
|
105
|
+
onError?.( event );
|
|
106
|
+
}
|
|
103
107
|
} );
|
|
104
108
|
} catch ( error ) {
|
|
105
|
-
endTraceWithError( traceId, error );
|
|
109
|
+
endTraceWithError( { traceId, error } );
|
|
106
110
|
throw error;
|
|
107
111
|
}
|
|
108
112
|
}
|