@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
@@ -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 function registerProvider( name, providerFn ) {
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.
@@ -1,72 +1,126 @@
1
- import { loadImageModel, loadTextModel, loadTools } from './ai_model.js';
2
- import { resolveMessageProviderOptions } from './prompt/block_options.js';
3
- import { ROLE, isRole } from './utils/message.js';
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 { stepCountIs } 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
- * Convert a loaded prompt into AI SDK text generation options.
36
+ * Build options for AI SDK text generation.
8
37
  *
9
- * System blocks are routed to the `system` option (as `SystemModelMessage[]`, so
10
- * per-message providerOptions like `cacheControl` are preserved) rather than left
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} prompt - Loaded prompt object
15
- * @returns {object} Options for AI SDK text calls
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 options = {
25
- model: loadTextModel( prompt ),
26
- system: resolvedMessages.filter( isSystem ),
27
- messages: resolvedMessages.filter( message => !isSystem( message ) ),
28
- providerOptions: prompt.config.providerOptions
29
- };
56
+ const systemMessages = prompt.messages.filter( m => m.role === Role.SYSTEM );
57
+ const allMessages = prompt.messages.filter( m => m.role !== Role.SYSTEM );
30
58
 
31
- if ( Number.isFinite( prompt.config.temperature ) ) {
32
- options.temperature = prompt.config.temperature;
59
+ if ( skills.length > 0 ) {
60
+ const skillsMessageContent = buildSkillsMessageContent( skills );
61
+ if ( systemMessages.length > 0 ) {
62
+ systemMessages[0] = { ...systemMessages[0], content: `${systemMessages[0].content}\n\n${skillsMessageContent}` };
63
+ } else {
64
+ systemMessages.push( { role: Role.SYSTEM, content: skillsMessageContent } );
65
+ }
33
66
  }
34
67
 
35
- if ( prompt.config.maxTokens ) {
36
- options.maxOutputTokens = prompt.config.maxTokens;
37
- }
68
+ const options = {
69
+ allowSystemInMessages: true,
70
+ maxRetries: 0,
71
+ model: loadTextModel( prompt ),
72
+ system: systemMessages,
73
+ messages: allMessages,
74
+ ...( output && { output } ),
75
+ ...( abortSignal && { abortSignal } ),
76
+ ...( stopWhen && { stopWhen } ),
77
+ ...select( textPromptConfigKeys, prompt.config )
78
+ };
38
79
 
39
- const tools = loadTools( prompt );
40
- if ( tools ) {
41
- options.tools = tools;
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 = stepCountIs( prompt.config.maxSteps );
91
+ }
42
92
  }
43
93
 
44
94
  return options;
45
95
  };
46
96
 
47
97
  /**
48
- * Convert a loaded prompt into AI SDK image generation options.
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} prompt - Loaded prompt object
51
- * @returns {object} Options for AI SDK image calls
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
- const options = {
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
- providerOptions: prompt.config.providerOptions
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
  };