@outputai/llm 0.11.0 → 0.11.1-next.2223fa5.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.
Files changed (69) hide show
  1. package/package.json +2 -2
  2. package/src/agent.js +105 -83
  3. package/src/agent.spec.js +312 -225
  4. package/src/ai_provider.js +2 -2
  5. package/src/ai_sdk_options.js +93 -39
  6. package/src/ai_sdk_options.spec.js +314 -154
  7. package/src/consts.js +6 -0
  8. package/src/generate.js +81 -0
  9. package/src/generate.spec.js +466 -0
  10. package/src/index.d.ts +222 -242
  11. package/src/index.js +3 -4
  12. package/src/prompt/content.js +70 -0
  13. package/src/prompt/content.spec.js +208 -0
  14. package/src/prompt/{escape.js → interpolations.js} +11 -18
  15. package/src/prompt/interpolations.spec.js +109 -0
  16. package/src/prompt/loader.full.spec.js +293 -0
  17. package/src/prompt/loader.js +45 -39
  18. package/src/prompt/loader.spec.js +186 -289
  19. package/src/prompt/markup/attributes.js +52 -0
  20. package/src/prompt/markup/attributes.spec.js +132 -0
  21. package/src/prompt/markup/nodes.js +71 -0
  22. package/src/prompt/markup/nodes.spec.js +333 -0
  23. package/src/prompt/markup/tokenizer.js +3 -0
  24. package/src/prompt/markup/tokenizer.spec.js +149 -0
  25. package/src/prompt/markup/tokens.js +26 -0
  26. package/src/prompt/markup/tokens.spec.js +250 -0
  27. package/src/prompt/validations.js +98 -67
  28. package/src/prompt/validations.spec.js +204 -47
  29. package/src/{prompt/load_content.js → utils/file.js} +12 -15
  30. package/src/utils/file.spec.js +89 -0
  31. package/src/utils/models.js +15 -0
  32. package/src/utils/models.spec.js +119 -0
  33. package/src/utils/skills.js +74 -0
  34. package/src/utils/skills.spec.js +168 -0
  35. package/src/utils/sources.js +48 -0
  36. package/src/utils/sources.spec.js +122 -0
  37. package/src/utils/stream.js +22 -0
  38. package/src/utils/stream.spec.js +55 -0
  39. package/src/utils/tools.js +47 -0
  40. package/src/utils/tools.spec.js +167 -0
  41. package/src/utils/wrap.js +148 -0
  42. package/src/utils/wrap.spec.js +359 -0
  43. package/src/validations.js +149 -27
  44. package/src/validations.spec.js +451 -53
  45. package/src/ai_model.js +0 -60
  46. package/src/ai_model.spec.js +0 -259
  47. package/src/ai_sdk.js +0 -92
  48. package/src/ai_sdk.spec.js +0 -564
  49. package/src/prompt/block_options.js +0 -58
  50. package/src/prompt/block_options.spec.js +0 -71
  51. package/src/prompt/blocks.js +0 -47
  52. package/src/prompt/blocks.spec.js +0 -63
  53. package/src/prompt/escape.spec.js +0 -159
  54. package/src/prompt/load_content.spec.js +0 -83
  55. package/src/prompt/loader_validation.spec.js +0 -128
  56. package/src/prompt/parser.js +0 -16
  57. package/src/prompt/parser.spec.js +0 -186
  58. package/src/prompt/prepare_text.js +0 -27
  59. package/src/prompt/prepare_text.spec.js +0 -141
  60. package/src/prompt/skill.js +0 -128
  61. package/src/prompt/skill.spec.js +0 -172
  62. package/src/utils/message.js +0 -3
  63. package/src/utils/message.spec.js +0 -29
  64. package/src/utils/response_wrappers.js +0 -100
  65. package/src/utils/response_wrappers.spec.js +0 -240
  66. package/src/utils/source_extraction.js +0 -53
  67. package/src/utils/source_extraction.spec.js +0 -194
  68. package/src/utils/trace.js +0 -19
  69. package/src/utils/trace.spec.js +0 -112
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@outputai/llm",
3
- "version": "0.11.0",
3
+ "version": "0.11.1-next.2223fa5.0",
4
4
  "description": "Framework abstraction to interact with LLM models",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -16,7 +16,7 @@
16
16
  "gray-matter": "4.0.3",
17
17
  "liquidjs": "10.27.2",
18
18
  "undici": "8.9.0",
19
- "@outputai/core": "0.11.0"
19
+ "@outputai/core": "0.11.1-next.2223fa5.0"
20
20
  },
21
21
  "devDependencies": {
22
22
  "@ai-sdk/amazon-bedrock": "4.0.138",
package/src/agent.js CHANGED
@@ -1,112 +1,134 @@
1
- import { ValidationError } from '@outputai/core';
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 { prepareTextPrompt } from './prompt/prepare_text.js';
6
- import { startTrace, endTraceWithError } from './utils/trace.js';
7
- import { wrapTextResponse, wrapStreamOnFinishResponse } from './utils/response_wrappers.js';
8
- import { ROLE, isRole } from './utils/message.js';
9
- export { skill } from './prompt/skill.js';
10
-
11
- export const createMemoryConversationStore = () => {
12
- const messages = [];
13
- return {
14
- getMessages: () => messages,
15
- addMessages: newMessages => messages.push( ...newMessages )
16
- };
17
- };
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';
18
10
 
19
11
  export class Agent extends AIToolLoopAgent {
20
12
  #prompt;
21
- #modelId;
22
- #providerId;
23
13
  #initialMessages;
24
14
  #store;
25
15
 
26
- constructor( {
27
- prompt,
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();
16
+ constructor( args ) {
17
+ const { promptFile, promptDir, variables, tools, output, stopWhen, messageStore } = Validator.parseAgentArgs( args );
44
18
 
45
- const { loadedPrompt, tools } = prepareTextPrompt( { prompt, variables, promptDir: resolvedPromptDir, skills, tools: toolsArg } );
19
+ const prompt = loadPrompt( promptFile, variables, promptDir );
20
+ const skills = loadSkills( prompt );
46
21
 
47
- const { system, messages, ...constructorOptions } = loadAiSdkTextOptions( loadedPrompt );
22
+ const { system, messages, ...aiOptions } = loadAiSdkTextOptions( { prompt, skills, tools, output, stopWhen } );
48
23
 
49
24
  // loadAiSdkTextOptions routes system blocks to the `system` slot (preserving
50
25
  // per-message providerOptions); pass them as the agent's `instructions`.
51
26
  super( {
52
- ...constructorOptions,
53
- ...( system.length > 0 ? { instructions: system } : {} ),
54
- ...( tools ? { tools } : {} ),
55
- stopWhen: stopWhen ?? stepCountIs( maxSteps ),
56
- ...rest
27
+ ...aiOptions,
28
+ ...( system.length > 0 ? { instructions: system } : {} )
57
29
  } );
58
30
 
59
31
  this.#prompt = prompt;
60
- this.#modelId = loadedPrompt.config.model;
61
- this.#providerId = loadedPrompt.config.provider;
62
- // `messages` is system-free but may still hold authored <assistant>/<tool>
63
- // blocks; seed only <user> turns into each generate()/stream() call.
64
- this.#initialMessages = messages.filter( isRole( ROLE.USER ) );
65
- this.#store = conversationStore ?? null;
32
+ // `messages` is system-free but may still hold authored <assistant> blocks;
33
+ // seed only <user> turns into each generate()/stream() call.
34
+ this.#initialMessages = messages.filter( m => m.role === Role.USER );
35
+ this.#store = messageStore ?? null;
66
36
  }
67
37
 
68
- async #fetchMessages( userMessages ) {
69
- const priorMessages = this.#store ? await this.#store.getMessages() : [];
70
- return [ ...this.#initialMessages, ...priorMessages, ...userMessages ];
38
+ async #combineWithPreviousMessages( messages ) {
39
+ return [ ...this.#initialMessages, ...( await this.#store?.getMessages() ?? [] ), ...messages ];
71
40
  }
72
41
 
73
- async #storeMessages( userMessages, result ) {
42
+ async #storeMessages( messages ) {
74
43
  if ( this.#store ) {
75
- await this.#store.addMessages( [ ...userMessages, ...( result.response?.messages ?? [] ) ] );
44
+ await this.#store.addMessages( messages );
76
45
  }
77
46
  }
78
47
 
79
- async generate( { messages: userMessages = [], ...callOptions } = {} ) {
80
- const traceId = startTrace( { name: 'Agent.generate', prompt: this.#prompt } );
81
- try {
82
- const messages = await this.#fetchMessages( userMessages );
83
- const response = await super.generate( { messages, allowSystemInMessages: true, ...callOptions } );
84
- const wrapped = await wrapTextResponse( { traceId, response, providerId: this.#providerId, modelId: this.#modelId } );
85
- await this.#storeMessages( userMessages, wrapped );
86
- return wrapped;
87
- } catch ( error ) {
88
- endTraceWithError( { traceId, error } );
89
- throw error;
90
- }
48
+ async generate( args ) {
49
+ const { messages, abortSignal, toolChoice } = Validator.parseAgentGenerateArgs( args );
50
+ const combinedMessages = await this.#combineWithPreviousMessages( messages );
51
+
52
+ return wrapGeneration( {
53
+ name: 'Agent.generate',
54
+ prompt: this.#prompt,
55
+ fn: async () => {
56
+ const response = await super.generate( {
57
+ messages: combinedMessages,
58
+ allowSystemInMessages: true,
59
+ ...( abortSignal && { abortSignal } ),
60
+ ...( toolChoice && { toolChoice } )
61
+ } );
62
+ await this.#storeMessages( messages.concat( response.response?.messages ?? [] ) );
63
+ return response;
64
+ }
65
+ } );
91
66
  }
92
67
 
93
- async stream( { messages: userMessages = [], onFinish, onError, ...callOptions } = {} ) {
94
- const traceId = startTrace( { name: 'Agent.stream', prompt: this.#prompt } );
95
- try {
96
- const messages = await this.#fetchMessages( userMessages );
97
- return super.stream( {
98
- messages,
99
- allowSystemInMessages: true,
100
- ...callOptions,
101
- ...wrapStreamOnFinishResponse( { traceId, modelId: this.#modelId, providerId: this.#providerId, onFinish } ),
102
- onError( event ) {
103
- endTraceWithError( { traceId, error: event.error } );
104
- onError?.( event );
68
+ /**
69
+ * Generates a completed agent response over streaming transport, invoking `onChunk` as parts arrive.
70
+ */
71
+ async generateWithStreaming( args ) {
72
+ const { messages, abortSignal, toolChoice, onChunk } = Validator.parseAgentGenerateWithStreamingArgs( args );
73
+ const combinedMessages = await this.#combineWithPreviousMessages( messages );
74
+
75
+ return wrapGeneration( {
76
+ name: 'Agent.generateWithStreaming',
77
+ prompt: this.#prompt,
78
+ fn: async () => {
79
+ const state = { response: null };
80
+ const stream = await super.stream( {
81
+ messages: combinedMessages,
82
+ allowSystemInMessages: true,
83
+ ...( onChunk && { onChunk } ),
84
+ ...( abortSignal && { abortSignal } ),
85
+ ...( toolChoice && { toolChoice } ),
86
+ onFinish: res => {
87
+ state.response = res;
88
+ },
89
+ onError: _ => {} // Suppress AI-SDK console printing
90
+ } );
91
+
92
+ await drainStream( stream, abortSignal );
93
+
94
+ if ( !state.response ) {
95
+ throw new Error( 'Agent streaming generation completed without a response.' );
105
96
  }
106
- } );
107
- } catch ( error ) {
108
- endTraceWithError( { traceId, error } );
109
- throw error;
110
- }
97
+
98
+ state.response.output = await stream.output;
99
+ await this.#storeMessages( messages.concat( state.response.response?.messages ?? [] ) );
100
+
101
+ return state.response;
102
+ }
103
+ } );
104
+ }
105
+
106
+ async stream( args ) {
107
+ const { messages, abortSignal, toolChoice, onChunk, onFinish, onError } = Validator.parseAgentStreamArgs( args );
108
+ const combinedMessages = await this.#combineWithPreviousMessages( messages );
109
+
110
+ return wrapStream( {
111
+ name: 'Agent.stream',
112
+ prompt: this.#prompt,
113
+ fn: ( { onFinishHook, onErrorHook } ) => super.stream( {
114
+ messages: combinedMessages,
115
+ allowSystemInMessages: true,
116
+ ...( onChunk && { onChunk } ),
117
+ ...( abortSignal && { abortSignal } ),
118
+ ...( toolChoice && { toolChoice } ),
119
+ onFinish: response =>
120
+ onFinishHook( response, async parsedResponse => {
121
+ if ( response.finishReason !== 'error' ) {
122
+ await this.#storeMessages( messages.concat( response.response?.messages ?? [] ) )
123
+ .catch( error => Logger.error( 'Agent.stream message store persistence failed', {
124
+ namespace: 'LLM',
125
+ error: error instanceof Error ? error.message : String( error )
126
+ } ) );
127
+ }
128
+ await onFinish?.( parsedResponse );
129
+ } ),
130
+ onError: event => onErrorHook( event, error => onError?.( { ...event, error } ) )
131
+ } )
132
+ } );
111
133
  }
112
134
  }