loopengine 0.1.44 → 0.1.46
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/dist/core/agent-config.d.ts +6 -0
- package/dist/core/model-calls/openai-model-call.d.ts +10 -0
- package/dist/core/model-calls/openai-model-call.js +1 -0
- package/dist/web/agent-file-admin.d.ts +4 -0
- package/dist/web/agent-file-admin.js +24 -2
- package/dist/web/agents-config-page.js +15 -0
- package/package.json +1 -1
|
@@ -252,6 +252,12 @@ export type AgentModelConfig = {
|
|
|
252
252
|
model: string;
|
|
253
253
|
apiKey?: string;
|
|
254
254
|
maxTokens?: number;
|
|
255
|
+
/** Omit unless the model actually needs it — see
|
|
256
|
+
* OpenAIModelCallOptions.reasoningEffort's own doc comment
|
|
257
|
+
* (core/model-calls/openai-model-call.ts) for why a reasoning-
|
|
258
|
+
* capable model (gpt-5.x) can require this be `'none'` just to use
|
|
259
|
+
* function tools at all. */
|
|
260
|
+
reasoningEffort?: 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max';
|
|
255
261
|
} | {
|
|
256
262
|
provider: 'deepseek';
|
|
257
263
|
model: string;
|
|
@@ -9,6 +9,16 @@ export interface OpenAIModelCallOptions {
|
|
|
9
9
|
* one you actually want. */
|
|
10
10
|
model: string;
|
|
11
11
|
maxTokens?: number;
|
|
12
|
+
/** Omitted by default — a reasoning-capable model (the gpt-5.x family)
|
|
13
|
+
* otherwise gets whatever effort level OpenAI defaults it to, which
|
|
14
|
+
* some of these models refuse to combine with function tools on
|
|
15
|
+
* /v1/chat/completions at all ("Function tools with reasoning_effort
|
|
16
|
+
* are not supported ... set reasoning_effort to 'none'" — confirmed
|
|
17
|
+
* live against gpt-5.6-luna). Set this to `'none'` for a model that
|
|
18
|
+
* hits that error; every real loopengine call passes `tools` (even an
|
|
19
|
+
* empty array is still the `tools` field being present), so there's no
|
|
20
|
+
* tools-vs-no-tools branch here to get this wrong on. */
|
|
21
|
+
reasoningEffort?: OpenAI.Chat.Completions.ChatCompletionReasoningEffort;
|
|
12
22
|
/** Inject a pre-configured client instead of building one from apiKey — e.g. to pass a custom `fetch` in tests. */
|
|
13
23
|
client?: OpenAI;
|
|
14
24
|
}
|
|
@@ -120,6 +120,7 @@ export function createOpenAIModelCall(options) {
|
|
|
120
120
|
function: { name: t.name, description: t.description, parameters: t.input_schema },
|
|
121
121
|
}))
|
|
122
122
|
: undefined,
|
|
123
|
+
reasoning_effort: options.reasoningEffort,
|
|
123
124
|
});
|
|
124
125
|
return toModelResponse(response.choices[0]);
|
|
125
126
|
};
|
|
@@ -8,6 +8,8 @@ export interface AgentEditableFields {
|
|
|
8
8
|
model?: {
|
|
9
9
|
provider: Provider;
|
|
10
10
|
model?: string;
|
|
11
|
+
maxTokens?: number;
|
|
12
|
+
reasoningEffort?: string;
|
|
11
13
|
};
|
|
12
14
|
maxTurns?: number;
|
|
13
15
|
contextBudgetTokens?: number;
|
|
@@ -18,6 +20,8 @@ export interface AgentEditResult {
|
|
|
18
20
|
model?: {
|
|
19
21
|
provider: Provider;
|
|
20
22
|
model: string;
|
|
23
|
+
maxTokens?: number;
|
|
24
|
+
reasoningEffort?: string;
|
|
21
25
|
};
|
|
22
26
|
maxTurns?: number;
|
|
23
27
|
contextBudgetTokens?: number;
|
|
@@ -162,12 +162,34 @@ export function editAgentFile(agentName, fields) {
|
|
|
162
162
|
if (provider !== 'anthropic' && !modelName) {
|
|
163
163
|
throw new AgentModelError(`A model name is required for provider '${provider}' — only anthropic has a default (claude-sonnet-5).`);
|
|
164
164
|
}
|
|
165
|
+
const maxTokens = fields.model.maxTokens;
|
|
166
|
+
if (maxTokens !== undefined && (!Number.isInteger(maxTokens) || maxTokens < 1)) {
|
|
167
|
+
throw new AgentEditNotSupportedError('model.maxTokens must be a positive integer.');
|
|
168
|
+
}
|
|
169
|
+
const reasoningEffort = fields.model.reasoningEffort;
|
|
170
|
+
// Only AgentModelConfig's 'openai' variant carries this (see its own
|
|
171
|
+
// doc comment) — refusing rather than silently dropping it for
|
|
172
|
+
// another provider, same "don't guess" posture the model-name check
|
|
173
|
+
// right above already follows.
|
|
174
|
+
if (reasoningEffort !== undefined && provider !== 'openai') {
|
|
175
|
+
throw new AgentModelError(`model.reasoningEffort only applies to provider 'openai' — '${provider}' doesn't support it.`);
|
|
176
|
+
}
|
|
177
|
+
// Every field the form actually round-trips (not just provider/model)
|
|
178
|
+
// — omitting maxTokens/reasoningEffort here, the way this used to
|
|
179
|
+
// unconditionally build `{ provider, model }` alone, would silently
|
|
180
|
+
// wipe out a value that was already set the moment an operator
|
|
181
|
+
// changed anything else about the model through this same form.
|
|
182
|
+
const objectFields = [`provider: '${provider}'`, `model: ${tsStringLiteral(modelName)}`];
|
|
183
|
+
if (maxTokens !== undefined)
|
|
184
|
+
objectFields.push(`maxTokens: ${maxTokens}`);
|
|
185
|
+
if (reasoningEffort !== undefined)
|
|
186
|
+
objectFields.push(`reasoningEffort: ${tsStringLiteral(reasoningEffort)}`);
|
|
165
187
|
edits.push({
|
|
166
188
|
start: prop.initializer.getStart(sourceFile),
|
|
167
189
|
end: prop.initializer.getEnd(),
|
|
168
|
-
text: `{
|
|
190
|
+
text: `{ ${objectFields.join(', ')} }`,
|
|
169
191
|
});
|
|
170
|
-
result.model = { provider, model: modelName };
|
|
192
|
+
result.model = { provider, model: modelName, maxTokens, reasoningEffort };
|
|
171
193
|
// The property's own value is now correct, but a stale
|
|
172
194
|
// "// reads ANTHROPIC_API_KEY" sitting right after it (from before a
|
|
173
195
|
// provider change) would be actively misleading, not just cosmetic —
|
|
@@ -658,6 +658,7 @@ export const agentsConfigPageHtml = `<!doctype html>
|
|
|
658
658
|
'<dt>Provider</dt><dd>' + escapeHtml(cfg.model.provider) + '</dd>' +
|
|
659
659
|
'<dt>Model</dt><dd>' + escapeHtml(cfg.model.model || '(provider default)') + '</dd>' +
|
|
660
660
|
'<dt>Max tokens</dt><dd>' + escapeHtml(cfg.model.maxTokens != null ? cfg.model.maxTokens : '(default)') + '</dd>' +
|
|
661
|
+
'<dt>Reasoning effort</dt><dd>' + escapeHtml(cfg.model.reasoningEffort || (cfg.model.provider === 'openai' ? '(provider default)' : 'n/a')) + '</dd>' +
|
|
661
662
|
'</dl>' +
|
|
662
663
|
'<form class="add-source" id="modelForm" style="display:none">' +
|
|
663
664
|
'<label>Provider' +
|
|
@@ -670,6 +671,16 @@ export const agentsConfigPageHtml = `<!doctype html>
|
|
|
670
671
|
'<label>Model name <span class="hint">(required for openai/deepseek; defaults to claude-sonnet-5 for anthropic)</span>' +
|
|
671
672
|
'<input type="text" name="modelName" value="' + escapeHtml(cfg.model.model || '') + '">' +
|
|
672
673
|
'</label>' +
|
|
674
|
+
'<label>Max tokens <span class="hint">(blank for provider default)</span>' +
|
|
675
|
+
'<input type="number" name="maxTokens" min="1" step="1" value="' + (cfg.model.maxTokens != null ? escapeHtml(cfg.model.maxTokens) : '') + '">' +
|
|
676
|
+
'</label>' +
|
|
677
|
+
'<label>Reasoning effort <span class="hint">(openai only — some reasoning models reject function tools unless this is \\\'none\\\'; leave unset otherwise)</span>' +
|
|
678
|
+
'<select name="reasoningEffort">' +
|
|
679
|
+
['', 'none', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'].map(function (v) {
|
|
680
|
+
return '<option value="' + v + '"' + (v === (cfg.model.reasoningEffort || '') ? ' selected' : '') + '>' + (v || '(unset)') + '</option>';
|
|
681
|
+
}).join('') +
|
|
682
|
+
'</select>' +
|
|
683
|
+
'</label>' +
|
|
673
684
|
'<button type="submit">Save</button>' +
|
|
674
685
|
'<button type="button" id="cancelModelBtn">Cancel</button>' +
|
|
675
686
|
'<div class="error" id="modelError"></div>' +
|
|
@@ -793,6 +804,10 @@ export const agentsConfigPageHtml = `<!doctype html>
|
|
|
793
804
|
var body = { model: { provider: data.get('provider') } };
|
|
794
805
|
var modelName = data.get('modelName');
|
|
795
806
|
if (modelName && modelName.trim()) body.model.model = modelName;
|
|
807
|
+
var maxTokens = data.get('maxTokens');
|
|
808
|
+
if (maxTokens && String(maxTokens).trim()) body.model.maxTokens = parseInt(maxTokens, 10);
|
|
809
|
+
var reasoningEffort = data.get('reasoningEffort');
|
|
810
|
+
if (reasoningEffort) body.model.reasoningEffort = reasoningEffort;
|
|
796
811
|
fetch('/agents/' + encodeURIComponent(name), {
|
|
797
812
|
method: 'PUT',
|
|
798
813
|
headers: { 'content-type': 'application/json' },
|