onbuzz 4.8.0 → 4.8.1
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 +1 -1
- package/src/core/__tests__/agentScheduler.nativePromptPick.test.js +319 -0
- package/src/core/agentPool.js +12 -0
- package/src/core/agentScheduler.js +174 -2
- package/src/services/__tests__/modelRouterNaming.test.js +41 -23
- package/src/tools/__tests__/baseTool.test.js +142 -0
- package/src/tools/baseTool.js +83 -1
- package/src/tools/openaiFunctionSchemas.js +14 -0
- package/src/tools/skillsTool.js +282 -277
- package/src/utilities/constants.js +19 -1
package/src/tools/skillsTool.js
CHANGED
|
@@ -1,277 +1,282 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Skills Tool - Global skills library for agents
|
|
3
|
-
*
|
|
4
|
-
* Purpose:
|
|
5
|
-
* - Allow agents to discover, browse, and read reusable skill instructions
|
|
6
|
-
* - Support progressive disclosure: list → describe → read-section → read
|
|
7
|
-
* - Support CRUD operations and importing skills from disk
|
|
8
|
-
* - Skills are global (shared across agents) and persist across package updates
|
|
9
|
-
*
|
|
10
|
-
* Actions:
|
|
11
|
-
* - list: List all skills with descriptions, section headings, and sizes
|
|
12
|
-
* - describe: Get full metadata for a skill without loading content
|
|
13
|
-
* - read: Read a skill's full content
|
|
14
|
-
* - read-section: Read only a specific section of a skill
|
|
15
|
-
* - read-file: Read a supporting file from a skill directory
|
|
16
|
-
* - create: Create a new skill
|
|
17
|
-
* - update: Update an existing skill
|
|
18
|
-
* - delete: Remove a skill
|
|
19
|
-
* - import: Import a skill from an external file or directory
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
import { BaseTool } from './baseTool.js';
|
|
23
|
-
import { getSkillsService } from '../services/skillsService.js';
|
|
24
|
-
import { SKILLS_ACTIONS } from '../utilities/toolConstants.js';
|
|
25
|
-
|
|
26
|
-
class SkillsTool extends BaseTool {
|
|
27
|
-
constructor(config = {}, logger = null) {
|
|
28
|
-
super(config, logger);
|
|
29
|
-
|
|
30
|
-
this.skillsService = null;
|
|
31
|
-
this.requiresProject = false;
|
|
32
|
-
this.isAsync = false;
|
|
33
|
-
this.timeout = 30000;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
async _ensureSkillsService() {
|
|
37
|
-
if (!this.skillsService) {
|
|
38
|
-
this.skillsService = getSkillsService(this.logger);
|
|
39
|
-
await this.skillsService.initialize();
|
|
40
|
-
}
|
|
41
|
-
return this.skillsService;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
getDescription() {
|
|
45
|
-
return `Skills Tool: Browse and manage a global library of reusable skill instructions.
|
|
46
|
-
|
|
47
|
-
Skills are structured knowledge packages containing instructions, checklists, templates, and reference files.
|
|
48
|
-
Each skill is a directory with a skill.md file and optional supporting files.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
\`\`\`json
|
|
108
|
-
{ "toolId": "skills", "action": "
|
|
109
|
-
\`\`\`
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
type: 'string',
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Skills Tool - Global skills library for agents
|
|
3
|
+
*
|
|
4
|
+
* Purpose:
|
|
5
|
+
* - Allow agents to discover, browse, and read reusable skill instructions
|
|
6
|
+
* - Support progressive disclosure: list → describe → read-section → read
|
|
7
|
+
* - Support CRUD operations and importing skills from disk
|
|
8
|
+
* - Skills are global (shared across agents) and persist across package updates
|
|
9
|
+
*
|
|
10
|
+
* Actions:
|
|
11
|
+
* - list: List all skills with descriptions, section headings, and sizes
|
|
12
|
+
* - describe: Get full metadata for a skill without loading content
|
|
13
|
+
* - read: Read a skill's full content
|
|
14
|
+
* - read-section: Read only a specific section of a skill
|
|
15
|
+
* - read-file: Read a supporting file from a skill directory
|
|
16
|
+
* - create: Create a new skill
|
|
17
|
+
* - update: Update an existing skill
|
|
18
|
+
* - delete: Remove a skill
|
|
19
|
+
* - import: Import a skill from an external file or directory
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { BaseTool } from './baseTool.js';
|
|
23
|
+
import { getSkillsService } from '../services/skillsService.js';
|
|
24
|
+
import { SKILLS_ACTIONS } from '../utilities/toolConstants.js';
|
|
25
|
+
|
|
26
|
+
class SkillsTool extends BaseTool {
|
|
27
|
+
constructor(config = {}, logger = null) {
|
|
28
|
+
super(config, logger);
|
|
29
|
+
|
|
30
|
+
this.skillsService = null;
|
|
31
|
+
this.requiresProject = false;
|
|
32
|
+
this.isAsync = false;
|
|
33
|
+
this.timeout = 30000;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async _ensureSkillsService() {
|
|
37
|
+
if (!this.skillsService) {
|
|
38
|
+
this.skillsService = getSkillsService(this.logger);
|
|
39
|
+
await this.skillsService.initialize();
|
|
40
|
+
}
|
|
41
|
+
return this.skillsService;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
getDescription() {
|
|
45
|
+
return `Skills Tool: Browse and manage a global library of reusable skill instructions.
|
|
46
|
+
|
|
47
|
+
Skills are structured knowledge packages containing instructions, checklists, templates, and reference files.
|
|
48
|
+
Each skill is a directory with a skill.md file and optional supporting files.
|
|
49
|
+
|
|
50
|
+
★ PROACTIVE USE — When a new task arrives or your focus shifts, your FIRST move should be:
|
|
51
|
+
\`{ "toolId": "skills", "action": "list" }\`
|
|
52
|
+
Skim the names + descriptions. If anything looks relevant, "describe" it and follow its
|
|
53
|
+
checklist instead of improvising. The team curates skills specifically so you don't have
|
|
54
|
+
to re-derive recurring playbooks from scratch.
|
|
55
|
+
|
|
56
|
+
PROGRESSIVE DISCLOSURE — Use this flow to minimize context usage:
|
|
57
|
+
1. "list" → See all skills with section headings and sizes
|
|
58
|
+
2. "describe" → Inspect a specific skill's structure in detail
|
|
59
|
+
3. "read-section" → Load only the section you need
|
|
60
|
+
4. "read" → Load full content only when necessary
|
|
61
|
+
|
|
62
|
+
ACTIONS:
|
|
63
|
+
|
|
64
|
+
1. LIST all skills:
|
|
65
|
+
\`\`\`json
|
|
66
|
+
{ "toolId": "skills", "action": "list" }
|
|
67
|
+
\`\`\`
|
|
68
|
+
Returns: name, description, section headings, line count, file count for each skill.
|
|
69
|
+
|
|
70
|
+
2. DESCRIBE a skill (metadata only, no content):
|
|
71
|
+
\`\`\`json
|
|
72
|
+
{ "toolId": "skills", "action": "describe", "name": "code-review" }
|
|
73
|
+
\`\`\`
|
|
74
|
+
Returns: description, sections with line ranges, file list, size.
|
|
75
|
+
|
|
76
|
+
3. READ full skill content:
|
|
77
|
+
\`\`\`json
|
|
78
|
+
{ "toolId": "skills", "action": "read", "name": "code-review" }
|
|
79
|
+
\`\`\`
|
|
80
|
+
Returns: full skill.md content + list of files in the skill directory.
|
|
81
|
+
|
|
82
|
+
4. READ a specific SECTION:
|
|
83
|
+
\`\`\`json
|
|
84
|
+
{ "toolId": "skills", "action": "read-section", "name": "code-review", "section": "Checklist" }
|
|
85
|
+
\`\`\`
|
|
86
|
+
Returns: only the content under the specified ## heading.
|
|
87
|
+
|
|
88
|
+
5. READ a supporting FILE:
|
|
89
|
+
\`\`\`json
|
|
90
|
+
{ "toolId": "skills", "action": "read-file", "name": "email-templates", "file": "templates/welcome.html" }
|
|
91
|
+
\`\`\`
|
|
92
|
+
Returns: content of a specific file within the skill directory.
|
|
93
|
+
|
|
94
|
+
6. CREATE a new skill:
|
|
95
|
+
\`\`\`json
|
|
96
|
+
{ "toolId": "skills", "action": "create", "name": "my-skill", "content": "# My Skill\\n\\nInstructions here...\\n\\n## Section One\\n..." }
|
|
97
|
+
\`\`\`
|
|
98
|
+
Optional: "files" array of { "path": "relative/path.ext", "content": "..." } for supporting files.
|
|
99
|
+
|
|
100
|
+
7. UPDATE an existing skill:
|
|
101
|
+
\`\`\`json
|
|
102
|
+
{ "toolId": "skills", "action": "update", "name": "my-skill", "content": "# Updated content..." }
|
|
103
|
+
\`\`\`
|
|
104
|
+
Optional: "files" array to add/update supporting files.
|
|
105
|
+
|
|
106
|
+
8. DELETE a skill:
|
|
107
|
+
\`\`\`json
|
|
108
|
+
{ "toolId": "skills", "action": "delete", "name": "my-skill" }
|
|
109
|
+
\`\`\`
|
|
110
|
+
|
|
111
|
+
9. IMPORT a skill from disk:
|
|
112
|
+
\`\`\`json
|
|
113
|
+
{ "toolId": "skills", "action": "import", "source": "/path/to/skill-dir-or-file" }
|
|
114
|
+
\`\`\`
|
|
115
|
+
Optional: "name" to override the derived skill name. If source is a directory, it must contain a skill.md file.
|
|
116
|
+
|
|
117
|
+
SKILL NAMING: Names must be kebab-case (lowercase, hyphens). Example: "code-review", "email-templates".`;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
parseParameters(content) {
|
|
121
|
+
return content;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
getRequiredParameters() {
|
|
125
|
+
return ['action'];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
getSupportedActions() {
|
|
129
|
+
return Object.values(SKILLS_ACTIONS);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
validateParameterTypes(params) {
|
|
133
|
+
const errors = [];
|
|
134
|
+
if (params.action && typeof params.action !== 'string') {
|
|
135
|
+
errors.push('action must be a string');
|
|
136
|
+
}
|
|
137
|
+
if (params.name !== undefined && typeof params.name !== 'string') {
|
|
138
|
+
errors.push('name must be a string');
|
|
139
|
+
}
|
|
140
|
+
if (params.content !== undefined && typeof params.content !== 'string') {
|
|
141
|
+
errors.push('content must be a string');
|
|
142
|
+
}
|
|
143
|
+
if (params.section !== undefined && typeof params.section !== 'string') {
|
|
144
|
+
errors.push('section must be a string');
|
|
145
|
+
}
|
|
146
|
+
if (params.file !== undefined && typeof params.file !== 'string') {
|
|
147
|
+
errors.push('file must be a string');
|
|
148
|
+
}
|
|
149
|
+
if (params.source !== undefined && typeof params.source !== 'string') {
|
|
150
|
+
errors.push('source must be a string');
|
|
151
|
+
}
|
|
152
|
+
return errors;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
customValidateParameters(params) {
|
|
156
|
+
const errors = [];
|
|
157
|
+
const { action, name, content, section, file, source } = params;
|
|
158
|
+
|
|
159
|
+
const validActions = this.getSupportedActions();
|
|
160
|
+
if (!validActions.includes(action)) {
|
|
161
|
+
errors.push(`Invalid action: "${action}". Valid actions: ${validActions.join(', ')}`);
|
|
162
|
+
return errors;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Action-specific required params
|
|
166
|
+
const needsName = [SKILLS_ACTIONS.DESCRIBE, SKILLS_ACTIONS.READ, SKILLS_ACTIONS.READ_SECTION, SKILLS_ACTIONS.READ_FILE, SKILLS_ACTIONS.CREATE, SKILLS_ACTIONS.UPDATE, SKILLS_ACTIONS.DELETE];
|
|
167
|
+
if (needsName.includes(action) && !name) {
|
|
168
|
+
errors.push(`"name" is required for action "${action}"`);
|
|
169
|
+
}
|
|
170
|
+
if (action === SKILLS_ACTIONS.CREATE && !content) {
|
|
171
|
+
errors.push('"content" is required for action "create"');
|
|
172
|
+
}
|
|
173
|
+
if (action === SKILLS_ACTIONS.READ_SECTION && !section) {
|
|
174
|
+
errors.push('"section" is required for action "read-section"');
|
|
175
|
+
}
|
|
176
|
+
if (action === SKILLS_ACTIONS.READ_FILE && !file) {
|
|
177
|
+
errors.push('"file" is required for action "read-file"');
|
|
178
|
+
}
|
|
179
|
+
if (action === SKILLS_ACTIONS.IMPORT && !source) {
|
|
180
|
+
errors.push('"source" is required for action "import"');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return errors;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async execute(params, context = {}) {
|
|
187
|
+
const service = await this._ensureSkillsService();
|
|
188
|
+
const { action } = params;
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
switch (action) {
|
|
192
|
+
case SKILLS_ACTIONS.LIST:
|
|
193
|
+
return this._formatResult(await service.listSkills(), 'Skills listed');
|
|
194
|
+
|
|
195
|
+
case SKILLS_ACTIONS.DESCRIBE:
|
|
196
|
+
return this._formatResult(await service.describeSkill(params.name), `Skill described: ${params.name}`);
|
|
197
|
+
|
|
198
|
+
case SKILLS_ACTIONS.READ:
|
|
199
|
+
return this._formatResult(await service.readSkill(params.name), `Skill read: ${params.name}`);
|
|
200
|
+
|
|
201
|
+
case SKILLS_ACTIONS.READ_SECTION:
|
|
202
|
+
return this._formatResult(await service.readSkillSection(params.name, params.section), `Section read: ${params.section}`);
|
|
203
|
+
|
|
204
|
+
case SKILLS_ACTIONS.READ_FILE:
|
|
205
|
+
return this._formatResult(await service.readSkillFile(params.name, params.file), `File read: ${params.file}`);
|
|
206
|
+
|
|
207
|
+
case SKILLS_ACTIONS.CREATE:
|
|
208
|
+
return this._formatResult(await service.createSkill(params.name, params.content, params.files || [], params.description || null), `Skill created: ${params.name}`);
|
|
209
|
+
|
|
210
|
+
case SKILLS_ACTIONS.UPDATE:
|
|
211
|
+
return this._formatResult(await service.updateSkill(params.name, params.content || null, params.files || [], params.description || null), `Skill updated: ${params.name}`);
|
|
212
|
+
|
|
213
|
+
case SKILLS_ACTIONS.DELETE:
|
|
214
|
+
await service.deleteSkill(params.name);
|
|
215
|
+
return this._formatResult({ deleted: params.name }, `Skill deleted: ${params.name}`);
|
|
216
|
+
|
|
217
|
+
case SKILLS_ACTIONS.IMPORT:
|
|
218
|
+
return this._formatResult(await service.importSkill(params.source, params.name || null, params.description || null), `Skill imported: ${params.name || params.source}`);
|
|
219
|
+
|
|
220
|
+
default:
|
|
221
|
+
return { success: false, error: `Unknown action: ${action}` };
|
|
222
|
+
}
|
|
223
|
+
} catch (error) {
|
|
224
|
+
return { success: false, error: error.message };
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
_formatResult(data, message) {
|
|
229
|
+
return {
|
|
230
|
+
success: true,
|
|
231
|
+
result: data,
|
|
232
|
+
message
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
getParameterSchema() {
|
|
237
|
+
return {
|
|
238
|
+
type: 'object',
|
|
239
|
+
required: ['action'],
|
|
240
|
+
properties: {
|
|
241
|
+
action: {
|
|
242
|
+
type: 'string',
|
|
243
|
+
enum: Object.values(SKILLS_ACTIONS),
|
|
244
|
+
description: 'The skill action to perform'
|
|
245
|
+
},
|
|
246
|
+
name: {
|
|
247
|
+
type: 'string',
|
|
248
|
+
description: 'Skill name (kebab-case)'
|
|
249
|
+
},
|
|
250
|
+
content: {
|
|
251
|
+
type: 'string',
|
|
252
|
+
description: 'Skill content (markdown)'
|
|
253
|
+
},
|
|
254
|
+
section: {
|
|
255
|
+
type: 'string',
|
|
256
|
+
description: 'Section heading to read (for read-section action)'
|
|
257
|
+
},
|
|
258
|
+
file: {
|
|
259
|
+
type: 'string',
|
|
260
|
+
description: 'Relative file path within skill directory (for read-file action)'
|
|
261
|
+
},
|
|
262
|
+
source: {
|
|
263
|
+
type: 'string',
|
|
264
|
+
description: 'Source file or directory path (for import action)'
|
|
265
|
+
},
|
|
266
|
+
files: {
|
|
267
|
+
type: 'array',
|
|
268
|
+
items: {
|
|
269
|
+
type: 'object',
|
|
270
|
+
properties: {
|
|
271
|
+
path: { type: 'string' },
|
|
272
|
+
content: { type: 'string' }
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
description: 'Additional supporting files (for create/update actions)'
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export default SkillsTool;
|
|
@@ -26,8 +26,26 @@ const SYSTEM_DEFAULTS = {
|
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
// Model Router Configuration
|
|
29
|
+
//
|
|
30
|
+
// ROUTER_MODEL is the model the Dynamic Model Routing feature calls
|
|
31
|
+
// (via a cheap chat-completion request) to decide which "real" model
|
|
32
|
+
// should handle each turn. Resolution order:
|
|
33
|
+
// 1. env LOXIA_ROUTER_MODEL — operator override, no rebuild needed
|
|
34
|
+
// 2. 'gpt-4.1-nano' — current live default. The platform's
|
|
35
|
+
// autopilot-model-router deployment uses gpt-4.1-nano as its
|
|
36
|
+
// underlying model, and the model-catalog keys entries by the
|
|
37
|
+
// underlying model name (NOT the Azure deployment name), so the
|
|
38
|
+
// CLI must ask for 'gpt-4.1-nano' to be matched. Cheaper than the
|
|
39
|
+
// retired OpenAI 'model-router' product, same job.
|
|
40
|
+
//
|
|
41
|
+
// Historical note: this used to be the literal string 'model-router',
|
|
42
|
+
// matching an OpenAI product name. That product is no longer in our
|
|
43
|
+
// Azure catalog (no deployment keyed under that name), which caused
|
|
44
|
+
// every routing call to fail with HTTP 400 "Unsupported model:
|
|
45
|
+
// model-router" until the circuit breaker tripped. The fix migrates
|
|
46
|
+
// the default to the underlying model name that IS in the catalog.
|
|
29
47
|
const MODEL_ROUTER_CONFIG = {
|
|
30
|
-
ROUTER_MODEL: '
|
|
48
|
+
ROUTER_MODEL: process.env.LOXIA_ROUTER_MODEL || 'gpt-4.1-nano',
|
|
31
49
|
CONTEXT_MESSAGES_COUNT: 5, // Number of recent messages to include
|
|
32
50
|
BENCHMARK_REFRESH_INTERVAL: 3600000, // 1 hour in milliseconds
|
|
33
51
|
FALLBACK_ON_ERROR: true, // Continue with previous model on router error
|