neoctl 0.2.53 → 0.2.54
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/README.md +0 -1
- package/dist/agents/agent-tool.js +4 -4
- package/dist/agents/agent-tool.js.map +1 -1
- package/dist/core/query-engine.d.ts +10 -0
- package/dist/core/query-engine.js +72 -16
- package/dist/core/query-engine.js.map +1 -1
- package/dist/core/query.d.ts +1 -0
- package/dist/core/query.js +5 -0
- package/dist/core/query.js.map +1 -1
- package/dist/core/run-agent.js +45 -36
- package/dist/core/run-agent.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/model/agent-settings-boundary.d.ts +11 -0
- package/dist/model/agent-settings-boundary.js +52 -0
- package/dist/model/agent-settings-boundary.js.map +1 -0
- package/dist/model/anthropic-adapter.d.ts +24 -0
- package/dist/model/anthropic-adapter.js +106 -0
- package/dist/model/anthropic-adapter.js.map +1 -0
- package/dist/model/anthropic-mapper.d.ts +14 -0
- package/dist/model/anthropic-mapper.js +340 -0
- package/dist/model/anthropic-mapper.js.map +1 -0
- package/dist/model/composition-runtime.d.ts +31 -0
- package/dist/model/composition-runtime.js +31 -0
- package/dist/model/composition-runtime.js.map +1 -0
- package/dist/model/config.d.ts +10 -3
- package/dist/model/config.js +26 -10
- package/dist/model/config.js.map +1 -1
- package/dist/model/context-window.d.ts +16 -1
- package/dist/model/context-window.js +171 -5
- package/dist/model/context-window.js.map +1 -1
- package/dist/model/env.js +0 -1
- package/dist/model/env.js.map +1 -1
- package/dist/model/model-gateway.d.ts +3 -1
- package/dist/model/model-gateway.js +4 -0
- package/dist/model/model-gateway.js.map +1 -1
- package/dist/model/model-metadata.json +9 -3
- package/dist/model/openai-adapter.js +2 -1
- package/dist/model/openai-adapter.js.map +1 -1
- package/dist/model/provider-factory.js +28 -1
- package/dist/model/provider-factory.js.map +1 -1
- package/dist/model/settings-files.d.ts +3 -0
- package/dist/model/settings-files.js +43 -0
- package/dist/model/settings-files.js.map +1 -0
- package/dist/model/settings-store.d.ts +84 -0
- package/dist/model/settings-store.js +191 -0
- package/dist/model/settings-store.js.map +1 -0
- package/dist/model/settings-sync.d.ts +23 -0
- package/dist/model/settings-sync.js +68 -0
- package/dist/model/settings-sync.js.map +1 -0
- package/dist/repl/commands.d.ts +1 -1
- package/dist/repl/commands.js +3 -2
- package/dist/repl/commands.js.map +1 -1
- package/dist/repl/index.js +50 -22
- package/dist/repl/index.js.map +1 -1
- package/dist/session/session-store.d.ts +2 -0
- package/dist/session/session-store.js +7 -1
- package/dist/session/session-store.js.map +1 -1
- package/dist/tasks/task-persistence.js +2 -1
- package/dist/tasks/task-persistence.js.map +1 -1
- package/dist/tools/builtins/image-generation-tool.js +2 -0
- package/dist/tools/builtins/image-generation-tool.js.map +1 -1
- package/dist/tools/builtins/search-providers.js +2 -0
- package/dist/tools/builtins/search-providers.js.map +1 -1
- package/dist/tools/builtins/search-tool.d.ts +2 -0
- package/dist/tools/builtins/search-tool.js +2 -2
- package/dist/tools/builtins/search-tool.js.map +1 -1
- package/dist/web/index.d.ts +9 -3
- package/dist/web/index.js +121 -29
- package/dist/web/index.js.map +1 -1
- package/dist/web/model-settings-api.d.ts +9 -0
- package/dist/web/model-settings-api.js +221 -0
- package/dist/web/model-settings-api.js.map +1 -0
- package/package.json +2 -2
- package/scripts/initialize-model-metadata.mjs +6 -0
package/dist/web/index.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ import { QueryEngine } from "../core/query-engine.js";
|
|
|
11
11
|
import { type AppPromptStore } from "../app/app-prompt.js";
|
|
12
12
|
import { type ModelProviderName } from "../model/config.js";
|
|
13
13
|
import { CommunicationLogger, LoggingModelGateway } from "../model/communication-logger.js";
|
|
14
|
-
import type { ModelUsage, ReasoningConfig
|
|
14
|
+
import type { ModelUsage, ReasoningConfig } from "../model/model-gateway.js";
|
|
15
15
|
import { ToolRegistry } from "../tools/registry.js";
|
|
16
16
|
import type { Tool, ToolUseContext } from "../tools/tool.js";
|
|
17
17
|
import { type ToolPresentation } from "../tools/tool-catalog.js";
|
|
@@ -451,6 +451,7 @@ export declare class WebRepl {
|
|
|
451
451
|
isDemo(engine?: QueryEngine): boolean;
|
|
452
452
|
startDemo(expectedSessionId: string): Promise<WebActionResult>;
|
|
453
453
|
private recordDemoInput;
|
|
454
|
+
private readonly settingsListener;
|
|
454
455
|
constructor(runtime: WebRuntime, demoOptions?: {
|
|
455
456
|
wait?: DemoWait;
|
|
456
457
|
});
|
|
@@ -726,6 +727,7 @@ export declare class WebRepl {
|
|
|
726
727
|
modelSettings: {
|
|
727
728
|
model: string | undefined;
|
|
728
729
|
reasoning: ReasoningConfig | null | undefined;
|
|
730
|
+
imageInput: boolean | undefined;
|
|
729
731
|
};
|
|
730
732
|
pendingSessionSettings: import("../core/query.js").SessionSettingsPatch & {
|
|
731
733
|
sessionPrompt?: import("../core/session-settings-prompt.js").SessionPromptState | null;
|
|
@@ -735,12 +737,16 @@ export declare class WebRepl {
|
|
|
735
737
|
catalog: {
|
|
736
738
|
commands: import("../repl/commands.js").ReplCommandDefinition[];
|
|
737
739
|
modelIds: string[];
|
|
740
|
+
modelProtocol: "openai" | "anthropic" | undefined;
|
|
738
741
|
modelReasoning: {
|
|
739
|
-
[k: string]:
|
|
742
|
+
[k: string]: string[];
|
|
743
|
+
};
|
|
744
|
+
modelReasoningEnabled: {
|
|
745
|
+
[k: string]: boolean;
|
|
740
746
|
};
|
|
741
747
|
reasoning: string[];
|
|
742
748
|
envPath: string;
|
|
743
|
-
}
|
|
749
|
+
};
|
|
744
750
|
interactive: {
|
|
745
751
|
sessions: boolean;
|
|
746
752
|
login: LoginFormPayload;
|
package/dist/web/index.js
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { trackSettingsGateway } from "../model/settings-sync.js";
|
|
3
|
+
import { observeModelSettings } from "../model/settings-sync.js";
|
|
4
|
+
import { modelProtocols } from "../model/context-window.js";
|
|
5
|
+
import { isReasoningEffort } from "../model/model-gateway.js";
|
|
6
|
+
import { ModelSettingsStore, compositionModelConfig } from "../model/settings-store.js";
|
|
7
|
+
import { registerCompositionTools } from "../model/composition-runtime.js";
|
|
8
|
+
import { handleModelSettingsRequest } from "./model-settings-api.js";
|
|
2
9
|
import { paginateSessions, sessionPageOptions } from "../session/session-pagination.js";
|
|
3
10
|
import { applyToolInputChanges } from "../model/tool-input-stream.js";
|
|
4
11
|
import { toolActivity } from "./tool-activity.js";
|
|
@@ -29,7 +36,7 @@ import { loadDefaultDotEnvFiles } from "../model/env.js";
|
|
|
29
36
|
import { readModelProviderConfig } from "../model/config.js";
|
|
30
37
|
import { findModelMetadata, loadModelCatalog, reasoningEffortsForModel, resolveContextWindowTokens } from "../model/context-window.js";
|
|
31
38
|
import { CommunicationLogger, LoggingModelGateway } from "../model/communication-logger.js";
|
|
32
|
-
import { createModelGatewayFromConfig
|
|
39
|
+
import { createModelGatewayFromConfig } from "../model/provider-factory.js";
|
|
33
40
|
import { ToolRegistry, isToolEnabledByDefault } from "../tools/registry.js";
|
|
34
41
|
import { builtinToolPresentation, toolPresentationForSource } from "../tools/tool-catalog.js";
|
|
35
42
|
import { editTool, writeTool } from "../tools/builtins/edit-tool.js";
|
|
@@ -147,9 +154,13 @@ function parseWebArgs(argv) {
|
|
|
147
154
|
}
|
|
148
155
|
export async function createWebRuntime(options = {}) {
|
|
149
156
|
const envLoad = loadDefaultDotEnvFiles({ override: true });
|
|
150
|
-
|
|
157
|
+
loadModelCatalog();
|
|
158
|
+
const settingsStore = new ModelSettingsStore();
|
|
159
|
+
const appliedSettings = settingsStore.read()?.applied;
|
|
160
|
+
const settingsRevisions = new WeakMap();
|
|
161
|
+
const modelConfig = appliedSettings ? compositionModelConfig(appliedSettings.composition, appliedSettings.connections) : readModelProviderConfig(process.env);
|
|
151
162
|
const communicationLogger = new CommunicationLogger();
|
|
152
|
-
const modelGateway = new LoggingModelGateway(
|
|
163
|
+
const modelGateway = trackSettingsGateway(new LoggingModelGateway(createModelGatewayFromConfig(modelConfig), communicationLogger), appliedSettings?.revision);
|
|
153
164
|
const taskStore = new TaskStore();
|
|
154
165
|
const pluginCatalog = normalizeWebRuntimePlugins(options.externalPlugins);
|
|
155
166
|
const pluginOverrides = normalizePluginOverrides(options.sessionPluginOverrides, pluginCatalog);
|
|
@@ -179,6 +190,13 @@ export async function createWebRuntime(options = {}) {
|
|
|
179
190
|
registerTool(createLoadImageTool(), { source: "builtin" });
|
|
180
191
|
if (modelConfig?.provider === "openai")
|
|
181
192
|
registerTool(createOpenAIImageGenerationTool(), { source: "builtin" });
|
|
193
|
+
if (appliedSettings)
|
|
194
|
+
registerCompositionTools(tools, appliedSettings.composition, appliedSettings.connections);
|
|
195
|
+
for (const name of ["web_search", "image_create"]) {
|
|
196
|
+
const tool = tools.get(name);
|
|
197
|
+
if (tool)
|
|
198
|
+
catalogTool(tool, { source: "builtin" });
|
|
199
|
+
}
|
|
182
200
|
registerTool(planTool, { source: "builtin" });
|
|
183
201
|
const frameworkFeedback = new FrameworkFeedbackStore(options.frameworkFeedbackDir);
|
|
184
202
|
registerTool(createFrameworkFeedbackTool(frameworkFeedback), { source: "builtin" });
|
|
@@ -217,6 +235,41 @@ export async function createWebRuntime(options = {}) {
|
|
|
217
235
|
tools,
|
|
218
236
|
contextManagerFactory: (cwd) => new DefaultContextManager({ cwd }),
|
|
219
237
|
acquireTurnResources: (engine) => acquireWebPluginTurn(runtime, true, engine),
|
|
238
|
+
beforeQuery: (engine) => {
|
|
239
|
+
const applied = settingsStore.read()?.applied;
|
|
240
|
+
const captureTools = () => {
|
|
241
|
+
const snapshot = tools.clone();
|
|
242
|
+
compositionToolSnapshots.set(engine, snapshot);
|
|
243
|
+
const session = engine.snapshot().session?.sessionId;
|
|
244
|
+
if (session)
|
|
245
|
+
sessionCompositionSnapshots.set(session, new WeakRef(engine));
|
|
246
|
+
};
|
|
247
|
+
if (!applied) {
|
|
248
|
+
if (!compositionToolSnapshots.has(engine))
|
|
249
|
+
captureTools();
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
const revision = applied.revision;
|
|
253
|
+
if (settingsRevisions.get(engine) === revision)
|
|
254
|
+
return;
|
|
255
|
+
const config = compositionModelConfig(applied.composition, applied.connections);
|
|
256
|
+
if (!config)
|
|
257
|
+
return;
|
|
258
|
+
const gateway = trackSettingsGateway(new LoggingModelGateway(createModelGatewayFromConfig(config), communicationLogger), revision);
|
|
259
|
+
engine.setModelProvider({ modelGateway: gateway, model: config.model, reasoning: config.defaultReasoning });
|
|
260
|
+
effectiveModelProviders.set(engine, config.provider);
|
|
261
|
+
registerCompositionTools(tools, applied.composition, applied.connections);
|
|
262
|
+
for (const name of ["web_search", "image_create"]) {
|
|
263
|
+
const tool = tools.get(name);
|
|
264
|
+
if (tool)
|
|
265
|
+
catalogTool(tool, { source: "builtin" });
|
|
266
|
+
}
|
|
267
|
+
runtime.modelGateway = gateway;
|
|
268
|
+
runtime.agentRuntime.modelGateway = gateway;
|
|
269
|
+
runtime.defaultReasoning = config.defaultReasoning;
|
|
270
|
+
settingsRevisions.set(engine, revision);
|
|
271
|
+
captureTools();
|
|
272
|
+
},
|
|
220
273
|
refreshTools: (engine) => {
|
|
221
274
|
if (runtime)
|
|
222
275
|
refreshRuntimePlugins(runtime, engine);
|
|
@@ -353,6 +406,8 @@ function resolveRuntimePluginOverrides(runtime, engine, sessionId = engine.snaps
|
|
|
353
406
|
return support.overrides;
|
|
354
407
|
}
|
|
355
408
|
/** Children acquire independent tools/prompts; they never refresh the parent's in-flight context. */
|
|
409
|
+
const compositionToolSnapshots = new WeakMap();
|
|
410
|
+
const sessionCompositionSnapshots = new Map();
|
|
356
411
|
function acquireWebPluginTurn(runtime, main, engine = runtime.engine, sessionId) {
|
|
357
412
|
if (main) {
|
|
358
413
|
refreshRuntimePlugins(runtime, engine);
|
|
@@ -364,6 +419,22 @@ function acquireWebPluginTurn(runtime, main, engine = runtime.engine, sessionId)
|
|
|
364
419
|
try {
|
|
365
420
|
const catalog = normalizeWebRuntimePlugins(snapshot?.plugins ?? support?.resolveCatalog?.() ?? support?.catalog);
|
|
366
421
|
const tools = buildPluginRegistry(runtime, catalog, overrides);
|
|
422
|
+
const owner = sessionId ? sessionCompositionSnapshots.get(sessionId)?.deref() : engine;
|
|
423
|
+
const frozen = owner ? compositionToolSnapshots.get(owner)?.clone() : undefined;
|
|
424
|
+
if (sessionId && !owner)
|
|
425
|
+
sessionCompositionSnapshots.delete(sessionId);
|
|
426
|
+
if (frozen)
|
|
427
|
+
for (const name of ["web_search", "image_create"]) {
|
|
428
|
+
const enabled = tools.isEnabled(name) ?? frozen.isEnabled(name);
|
|
429
|
+
frozen.setEnabled(name, true);
|
|
430
|
+
const tool = frozen.get(name);
|
|
431
|
+
tools.unregister(name);
|
|
432
|
+
if (tool) {
|
|
433
|
+
tools.register(tool);
|
|
434
|
+
if (enabled === false)
|
|
435
|
+
tools.setEnabled(name, false);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
367
438
|
return { tools, release: snapshot?.release ?? (() => { }),
|
|
368
439
|
...(!main ? { promptSections: [...(support?.basePromptSections ?? []), ...activeWebRuntimePlugins(catalog, overrides).flatMap(plugin => plugin.promptSections ?? [])] } : {}) };
|
|
369
440
|
}
|
|
@@ -747,9 +818,17 @@ export class WebRepl {
|
|
|
747
818
|
this.demoMessageQueues.delete(engine);
|
|
748
819
|
}
|
|
749
820
|
}
|
|
821
|
+
settingsListener = () => {
|
|
822
|
+
if (this.deletingSession)
|
|
823
|
+
return;
|
|
824
|
+
this.runtime.engine.refreshModelConfigurationIfIdle();
|
|
825
|
+
for (const subscriber of this.subscribers)
|
|
826
|
+
this.send(subscriber, "sync", this.snapshot());
|
|
827
|
+
};
|
|
750
828
|
constructor(runtime, demoOptions = {}) {
|
|
751
829
|
this.runtime = runtime;
|
|
752
830
|
this.demoOptions = demoOptions;
|
|
831
|
+
observeModelSettings(this.settingsListener);
|
|
753
832
|
const lineId = { current: 0 };
|
|
754
833
|
this.lines = initialLines(runtime, lineId);
|
|
755
834
|
this.lineId = lineId.current;
|
|
@@ -830,6 +909,7 @@ export class WebRepl {
|
|
|
830
909
|
}
|
|
831
910
|
snapshot(includeCatalog = false) {
|
|
832
911
|
const backgroundTasks = this.backgroundTasks();
|
|
912
|
+
this.runtime.engine.refreshModelConfigurationIfIdle?.();
|
|
833
913
|
const engineSnapshot = this.runtime.engine.snapshot();
|
|
834
914
|
const pendingSessionSettings = this.runtime.engine.getPendingSessionSettings?.() ?? {};
|
|
835
915
|
// Include retained reasoning alongside a queued model. Explicit undefined is
|
|
@@ -855,11 +935,11 @@ export class WebRepl {
|
|
|
855
935
|
backgroundSessionRunCount: this.backgroundSessionRuns.size,
|
|
856
936
|
runningSessionIds: this.runningSessionIds(),
|
|
857
937
|
session: engineSnapshot.session,
|
|
858
|
-
modelSettings: { model: engineSnapshot.model, reasoning: engineSnapshot.reasoning },
|
|
938
|
+
modelSettings: { model: engineSnapshot.model, reasoning: engineSnapshot.reasoning, imageInput: findModelMetadata(engineSnapshot.model || "")?.imageInput },
|
|
859
939
|
pendingSessionSettings,
|
|
860
940
|
fastMode: this.runtime.engine.isFastMode(),
|
|
861
941
|
appPrompt: this.runtime.engine.getAppPrompt(),
|
|
862
|
-
catalog:
|
|
942
|
+
catalog: webCatalog(this.runtime),
|
|
863
943
|
interactive: includeCatalog ? webInteractiveCatalog(this.runtime) : undefined,
|
|
864
944
|
};
|
|
865
945
|
}
|
|
@@ -1457,11 +1537,15 @@ export class WebRepl {
|
|
|
1457
1537
|
if (typeof model !== "string" || !model.trim() || model.length > 256 || /[\s\x00-\x1f]/u.test(model.trim())) {
|
|
1458
1538
|
return actionFailure("MODEL_INVALID", "invalid model");
|
|
1459
1539
|
}
|
|
1460
|
-
if (reasoning !== undefined && (typeof reasoning !== "string" || !
|
|
1540
|
+
if (reasoning !== undefined && (typeof reasoning !== "string" || !(reasoning === "default" || reasoning === "off" || isReasoningEffort(reasoning)))) {
|
|
1461
1541
|
return actionFailure("MODEL_INVALID", "invalid reasoning effort");
|
|
1462
1542
|
}
|
|
1463
1543
|
const engine = this.runtime.engine;
|
|
1464
1544
|
const nextModel = model.trim();
|
|
1545
|
+
const provider = effectiveModelProviders.get(engine) ?? readModelProviderConfig()?.provider;
|
|
1546
|
+
const metadata = (engine.getActiveModelCatalog?.() ?? loadModelCatalog()).models.find(m => m.modelIds.includes(nextModel));
|
|
1547
|
+
if (metadata && provider && !modelProtocols(metadata).includes(provider))
|
|
1548
|
+
return actionFailure("MODEL_INVALID", "模型不支持当前主连接接口");
|
|
1465
1549
|
const current = { ...engine.getModelSettings(), ...engine.getPendingSessionSettings() };
|
|
1466
1550
|
const argument = reasoning;
|
|
1467
1551
|
const validationError = validateModelReasoningArgument(nextModel, argument);
|
|
@@ -1567,9 +1651,10 @@ export class WebRepl {
|
|
|
1567
1651
|
async saveLogin(providerValue, values) {
|
|
1568
1652
|
const provider = parseLoginProvider(providerValue);
|
|
1569
1653
|
if (!provider)
|
|
1570
|
-
return actionFailure("LOGIN_INVALID", "provider must be openai");
|
|
1654
|
+
return actionFailure("LOGIN_INVALID", "provider must be openai or anthropic");
|
|
1571
1655
|
if (dockerEnabled() && !values.apiKey?.trim()) {
|
|
1572
|
-
|
|
1656
|
+
const key = provider === "anthropic" ? "ANTHROPIC_API_KEY" : "OPENAI_API_KEY";
|
|
1657
|
+
values = { ...values, apiKey: parseEnvFileSafe(this.runtime.envPath)[key] || process.env[key] || "" };
|
|
1573
1658
|
}
|
|
1574
1659
|
const payload = { ...createLoginFormPayload(this.runtime.envPath, provider), provider, values };
|
|
1575
1660
|
const validationError = validateLoginFormPayload(payload);
|
|
@@ -2963,6 +3048,8 @@ export async function handleWebRequest(req, res, router) {
|
|
|
2963
3048
|
}
|
|
2964
3049
|
if (req.method === "POST" && url.pathname === "/api/sessions/new")
|
|
2965
3050
|
return sendJson(res, await repl.newSession());
|
|
3051
|
+
if (await handleModelSettingsRequest(req, res, url))
|
|
3052
|
+
return;
|
|
2966
3053
|
if (req.method === "GET" && url.pathname === "/api/login")
|
|
2967
3054
|
return sendJson(res, repl.loginForm(url.searchParams.get("provider") ?? undefined));
|
|
2968
3055
|
if (req.method === "POST" && url.pathname === "/api/login") {
|
|
@@ -3083,12 +3170,18 @@ function sanitizeWebAttachments(value) {
|
|
|
3083
3170
|
return [];
|
|
3084
3171
|
});
|
|
3085
3172
|
}
|
|
3173
|
+
const effectiveModelProviders = new WeakMap();
|
|
3086
3174
|
function webCatalog(runtime) {
|
|
3087
|
-
const
|
|
3175
|
+
const catalog = runtime.engine.getActiveModelCatalog?.() ?? loadModelCatalog();
|
|
3176
|
+
const provider = effectiveModelProviders.get(runtime.engine) ?? readModelProviderConfig()?.provider;
|
|
3177
|
+
const models = catalog.models.filter(model => !provider || modelProtocols(model).includes(provider));
|
|
3178
|
+
const modelIds = [...new Set(models.flatMap(model => model.modelIds.length ? model.modelIds : [model.id]))].sort((left, right) => left.localeCompare(right, "en", { numeric: true }));
|
|
3088
3179
|
return {
|
|
3089
3180
|
commands: replCommandDefinitions,
|
|
3090
3181
|
modelIds,
|
|
3091
|
-
|
|
3182
|
+
modelProtocol: provider,
|
|
3183
|
+
modelReasoning: Object.fromEntries(modelIds.map(model => [model, reasoningEffortsForModel(model, catalog) ?? []])),
|
|
3184
|
+
modelReasoningEnabled: Object.fromEntries(models.flatMap(m => m.modelIds.map(id => [id, m.reasoning !== false]))),
|
|
3092
3185
|
reasoning: ["none", "minimal", "low", "medium", "high", "xhigh", "max", "default", "off"],
|
|
3093
3186
|
envPath: runtime.envPath,
|
|
3094
3187
|
};
|
|
@@ -3448,28 +3541,32 @@ async function persistModelCommandSettings(runtime, command, reasoningUpdate) {
|
|
|
3448
3541
|
return { providerChanged: targetProvider !== currentProvider };
|
|
3449
3542
|
}
|
|
3450
3543
|
function currentModelProvider() {
|
|
3451
|
-
return "openai";
|
|
3544
|
+
return parseLoginProvider(process.env.MODEL_PROVIDER) ?? "openai";
|
|
3452
3545
|
}
|
|
3453
3546
|
function parseLoginProvider(value) {
|
|
3454
|
-
if (value === "openai")
|
|
3547
|
+
if (value === "openai" || value === "anthropic")
|
|
3455
3548
|
return value;
|
|
3456
3549
|
return undefined;
|
|
3457
3550
|
}
|
|
3458
|
-
const LOGIN_PROVIDERS = ["openai"];
|
|
3551
|
+
const LOGIN_PROVIDERS = ["openai", "anthropic"];
|
|
3459
3552
|
const SHARED_LOGIN_FIELDS = [
|
|
3460
3553
|
{ key: "reasoningEffort", label: "Reasoning effort", envKey: "MODEL_REASONING_EFFORT", scope: "shared", options: ["", "off", "none", "minimal", "low", "medium", "high", "xhigh", "max"] },
|
|
3461
|
-
{ key: "
|
|
3462
|
-
{ key: "maxOutputTokens", label: "Max output tokens", envKey: "MODEL_MAX_OUTPUT_TOKENS", scope: "shared" },
|
|
3554
|
+
{ key: "maxOutputTokens", label: "Max output tokens", envKey: "MODEL_MAX_OUTPUT_TOKENS", scope: "shared", placeholder: "Leave empty for server default" },
|
|
3463
3555
|
{ key: "timeoutMs", label: "Timeout ms", envKey: "MODEL_TIMEOUT_MS", scope: "shared", placeholder: "120000" },
|
|
3464
3556
|
{ key: "streamIdleTimeoutMs", label: "Stream idle timeout ms", envKey: "MODEL_STREAM_IDLE_TIMEOUT_MS", scope: "shared", placeholder: "120000" },
|
|
3465
3557
|
{ key: "maxRetries", label: "Max retries", envKey: "MODEL_MAX_RETRIES", scope: "shared", placeholder: "2" },
|
|
3466
3558
|
];
|
|
3467
3559
|
const LOGIN_FIELD_DEFINITIONS = {
|
|
3560
|
+
anthropic: [
|
|
3561
|
+
{ key: "apiKey", label: "API / CPA key", envKey: "ANTHROPIC_API_KEY", scope: "provider", required: true, secret: true },
|
|
3562
|
+
{ key: "baseUrl", label: "Base URL", envKey: "ANTHROPIC_BASE_URL", scope: "provider", placeholder: "https://api.anthropic.com" },
|
|
3563
|
+
{ key: "model", label: "Model", envKey: "ANTHROPIC_MODEL", scope: "provider", required: true, placeholder: "claude-sonnet-4-6" },
|
|
3564
|
+
...SHARED_LOGIN_FIELDS.map(field => field.key === "reasoningEffort" ? { ...field, options: ["", "off", "none", "low", "medium", "high", "xhigh", "max"] } : field.key === "maxOutputTokens" ? { ...field, placeholder: "8192" } : field),
|
|
3565
|
+
],
|
|
3468
3566
|
openai: [
|
|
3469
3567
|
{ key: "apiKey", label: "API key", envKey: "OPENAI_API_KEY", scope: "provider", required: true, secret: true, placeholder: "sk-..." },
|
|
3470
3568
|
{ key: "baseUrl", label: "Base URL", envKey: "OPENAI_BASE_URL", scope: "provider", placeholder: "https://api.openai.com" },
|
|
3471
3569
|
{ key: "model", label: "Model", envKey: "OPENAI_MODEL", scope: "provider", required: true, placeholder: "gpt-5.6" },
|
|
3472
|
-
{ key: "endpoint", label: "Endpoint", envKey: "OPENAI_ENDPOINT", scope: "provider", placeholder: "auto", options: ["auto", "responses", "chat"] },
|
|
3473
3570
|
...SHARED_LOGIN_FIELDS,
|
|
3474
3571
|
],
|
|
3475
3572
|
};
|
|
@@ -3496,18 +3593,16 @@ function loginValuesForProvider(provider, env) {
|
|
|
3496
3593
|
values.baseUrl = defaultBaseUrlForLoginProvider(provider);
|
|
3497
3594
|
if (!values.model)
|
|
3498
3595
|
values.model = defaultModelForLoginProvider(provider);
|
|
3499
|
-
if (provider === "openai" && !values.endpoint)
|
|
3500
|
-
values.endpoint = "auto";
|
|
3501
3596
|
return values;
|
|
3502
3597
|
}
|
|
3503
|
-
function guessLoginProvider(
|
|
3504
|
-
return currentModelProvider();
|
|
3598
|
+
function guessLoginProvider(env) {
|
|
3599
|
+
return env.ANTHROPIC_API_KEY && !env.OPENAI_API_KEY ? "anthropic" : currentModelProvider();
|
|
3505
3600
|
}
|
|
3506
|
-
function defaultBaseUrlForLoginProvider(
|
|
3507
|
-
return "https://api.openai.com";
|
|
3601
|
+
function defaultBaseUrlForLoginProvider(provider) {
|
|
3602
|
+
return provider === "anthropic" ? "https://api.anthropic.com" : "https://api.openai.com";
|
|
3508
3603
|
}
|
|
3509
|
-
function defaultModelForLoginProvider(
|
|
3510
|
-
return "gpt-5.6";
|
|
3604
|
+
function defaultModelForLoginProvider(provider) {
|
|
3605
|
+
return provider === "anthropic" ? "claude-sonnet-4-6" : "gpt-5.6";
|
|
3511
3606
|
}
|
|
3512
3607
|
function validateLoginFormPayload(payload) {
|
|
3513
3608
|
for (const field of LOGIN_FIELD_DEFINITIONS[payload.provider]) {
|
|
@@ -3522,9 +3617,6 @@ function validateLoginFormPayload(payload) {
|
|
|
3522
3617
|
if (value && !Number.isFinite(Number(value)))
|
|
3523
3618
|
return `${fieldKey} must be a number.`;
|
|
3524
3619
|
}
|
|
3525
|
-
if (payload.provider === "openai" && payload.values.endpoint === "chat" && payload.values.reasoningSummary?.trim()) {
|
|
3526
|
-
return "Reasoning summary requires the Responses API endpoint.";
|
|
3527
|
-
}
|
|
3528
3620
|
return undefined;
|
|
3529
3621
|
}
|
|
3530
3622
|
async function saveLoginPayloadToEnv(payload) {
|
|
@@ -3559,8 +3651,8 @@ function stripEnvQuotes(value) {
|
|
|
3559
3651
|
return value.slice(1, -1);
|
|
3560
3652
|
return value;
|
|
3561
3653
|
}
|
|
3562
|
-
function modelEnvKeyForProvider(
|
|
3563
|
-
return "OPENAI_MODEL";
|
|
3654
|
+
function modelEnvKeyForProvider(provider) {
|
|
3655
|
+
return provider === "anthropic" ? "ANTHROPIC_MODEL" : "OPENAI_MODEL";
|
|
3564
3656
|
}
|
|
3565
3657
|
function envValueForReasoning(reasoning) {
|
|
3566
3658
|
if (reasoning === null)
|