mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-17 13:00:48 +00:00
refactor: unify model-input normalization usage
This commit is contained in:
@@ -718,9 +718,14 @@ Time format in system prompt. Default: `auto` (OS preference).
|
||||
}
|
||||
```
|
||||
|
||||
- `model`: accepts either a string (`"provider/model"`) or an object (`{ primary, fallbacks }`).
|
||||
- String form sets only the primary model.
|
||||
- Object form sets primary plus ordered failover models.
|
||||
- `imageModel`: accepts either a string (`"provider/model"`) or an object (`{ primary, fallbacks }`).
|
||||
- Only used if the selected/default model cannot accept image input.
|
||||
- `model.primary`: format `provider/model` (e.g. `anthropic/claude-opus-4-6`). If you omit the provider, OpenClaw assumes `anthropic` (deprecated).
|
||||
- `models`: the configured model catalog and allowlist for `/model`. Each entry can include `alias` (shortcut) and `params` (provider-specific: `temperature`, `maxTokens`).
|
||||
- `imageModel`: only used if the primary model lacks image input.
|
||||
- Config writers that mutate these fields (for example `/models set`, `/models set-image`, and fallback add/remove commands) save canonical object form and preserve existing fallback lists when possible.
|
||||
- `maxConcurrent`: max parallel agent runs across sessions (each session still serialized). Default: 1.
|
||||
|
||||
**Built-in alias shorthands** (only apply when the model is in `agents.defaults.models`):
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import path from "node:path";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { resolveAgentModelFallbackValues } from "../config/model-input.js";
|
||||
import { resolveStateDir } from "../config/paths.js";
|
||||
import { createSubsystemLogger } from "../logging/subsystem.js";
|
||||
import {
|
||||
@@ -205,10 +206,7 @@ export function resolveEffectiveModelFallbacks(params: {
|
||||
if (!params.hasSessionModelOverride) {
|
||||
return agentFallbacksOverride;
|
||||
}
|
||||
const defaultFallbacks =
|
||||
typeof params.cfg.agents?.defaults?.model === "object"
|
||||
? (params.cfg.agents.defaults.model.fallbacks ?? [])
|
||||
: [];
|
||||
const defaultFallbacks = resolveAgentModelFallbackValues(params.cfg.agents?.defaults?.model);
|
||||
return agentFallbacksOverride ?? defaultFallbacks;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import {
|
||||
resolveAgentModelFallbackValues,
|
||||
resolveAgentModelPrimaryValue,
|
||||
} from "../config/model-input.js";
|
||||
import {
|
||||
ensureAuthProfileStore,
|
||||
getSoonestCooldownExpiry,
|
||||
@@ -151,26 +155,13 @@ function resolveImageFallbackCandidates(params: {
|
||||
if (params.modelOverride?.trim()) {
|
||||
addRaw(params.modelOverride, false);
|
||||
} else {
|
||||
const imageModel = params.cfg?.agents?.defaults?.imageModel as
|
||||
| { primary?: string }
|
||||
| string
|
||||
| undefined;
|
||||
const primary = typeof imageModel === "string" ? imageModel.trim() : imageModel?.primary;
|
||||
const primary = resolveAgentModelPrimaryValue(params.cfg?.agents?.defaults?.imageModel);
|
||||
if (primary?.trim()) {
|
||||
addRaw(primary, false);
|
||||
}
|
||||
}
|
||||
|
||||
const imageFallbacks = (() => {
|
||||
const imageModel = params.cfg?.agents?.defaults?.imageModel as
|
||||
| { fallbacks?: string[] }
|
||||
| string
|
||||
| undefined;
|
||||
if (imageModel && typeof imageModel === "object") {
|
||||
return imageModel.fallbacks ?? [];
|
||||
}
|
||||
return [];
|
||||
})();
|
||||
const imageFallbacks = resolveAgentModelFallbackValues(params.cfg?.agents?.defaults?.imageModel);
|
||||
|
||||
for (const raw of imageFallbacks) {
|
||||
addRaw(raw, true);
|
||||
@@ -220,14 +211,7 @@ function resolveFallbackCandidates(params: {
|
||||
if (!sameModelCandidate(normalizedPrimary, configuredPrimary)) {
|
||||
return []; // Override model failed → go straight to configured default
|
||||
}
|
||||
const model = params.cfg?.agents?.defaults?.model as
|
||||
| { fallbacks?: string[] }
|
||||
| string
|
||||
| undefined;
|
||||
if (model && typeof model === "object") {
|
||||
return model.fallbacks ?? [];
|
||||
}
|
||||
return [];
|
||||
return resolveAgentModelFallbackValues(params.cfg?.agents?.defaults?.model);
|
||||
})();
|
||||
|
||||
for (const raw of modelFallbacks) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { resolveAgentModelPrimaryValue } from "../config/model-input.js";
|
||||
import { resolveAgentModelPrimaryValue, toAgentModelListLike } from "../config/model-input.js";
|
||||
import { createSubsystemLogger } from "../logging/subsystem.js";
|
||||
import { resolveAgentConfig, resolveAgentEffectiveModelPrimary } from "./agent-scope.js";
|
||||
import { DEFAULT_MODEL, DEFAULT_PROVIDER } from "./defaults.js";
|
||||
@@ -309,9 +309,7 @@ export function resolveDefaultModelForAgent(params: {
|
||||
defaults: {
|
||||
...params.cfg.agents?.defaults,
|
||||
model: {
|
||||
...(typeof params.cfg.agents?.defaults?.model === "object"
|
||||
? params.cfg.agents.defaults.model
|
||||
: undefined),
|
||||
...toAgentModelListLike(params.cfg.agents?.defaults?.model),
|
||||
primary: agentModelOverride,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import type { AssistantMessage } from "@mariozechner/pi-ai";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import {
|
||||
resolveAgentModelFallbackValues,
|
||||
resolveAgentModelPrimaryValue,
|
||||
} from "../../config/model-input.js";
|
||||
import { extractAssistantText } from "../pi-embedded-utils.js";
|
||||
|
||||
export type ImageModelConfig = { primary?: string; fallbacks?: string[] };
|
||||
@@ -51,12 +55,8 @@ export function coerceImageAssistantText(params: {
|
||||
}
|
||||
|
||||
export function coerceImageModelConfig(cfg?: OpenClawConfig): ImageModelConfig {
|
||||
const imageModel = cfg?.agents?.defaults?.imageModel as
|
||||
| { primary?: string; fallbacks?: string[] }
|
||||
| string
|
||||
| undefined;
|
||||
const primary = typeof imageModel === "string" ? imageModel.trim() : imageModel?.primary;
|
||||
const fallbacks = typeof imageModel === "object" ? (imageModel?.fallbacks ?? []) : [];
|
||||
const primary = resolveAgentModelPrimaryValue(cfg?.agents?.defaults?.imageModel);
|
||||
const fallbacks = resolveAgentModelFallbackValues(cfg?.agents?.defaults?.imageModel);
|
||||
return {
|
||||
...(primary?.trim() ? { primary: primary.trim() } : {}),
|
||||
...(fallbacks.length > 0 ? { fallbacks } : {}),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { toAgentModelListLike } from "../config/model-input.js";
|
||||
import { githubCopilotLoginCommand } from "../providers/github-copilot-auth.js";
|
||||
import type { ApplyAuthChoiceParams, ApplyAuthChoiceResult } from "./auth-choice.apply.js";
|
||||
import { applyAuthProfileConfig } from "./onboard-auth.js";
|
||||
@@ -49,9 +50,7 @@ export async function applyAuthChoiceGitHubCopilot(
|
||||
defaults: {
|
||||
...nextConfig.agents?.defaults,
|
||||
model: {
|
||||
...(typeof nextConfig.agents?.defaults?.model === "object"
|
||||
? nextConfig.agents.defaults.model
|
||||
: undefined),
|
||||
...toAgentModelListLike(nextConfig.agents?.defaults?.model),
|
||||
primary: model,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
resolveConfiguredModelRef,
|
||||
} from "../agents/model-selection.js";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { resolveAgentModelPrimaryValue } from "../config/model-input.js";
|
||||
import type { WizardPrompter, WizardSelectOption } from "../wizard/prompts.js";
|
||||
import { formatTokenK } from "./models/shared.js";
|
||||
import { OPENAI_CODEX_DEFAULT_MODEL } from "./openai-codex-model-default.js";
|
||||
@@ -77,11 +78,7 @@ function createProviderAuthChecker(params: {
|
||||
}
|
||||
|
||||
function resolveConfiguredModelRaw(cfg: OpenClawConfig): string {
|
||||
const raw = cfg.agents?.defaults?.model as { primary?: string } | string | undefined;
|
||||
if (typeof raw === "string") {
|
||||
return raw.trim();
|
||||
}
|
||||
return raw?.primary?.trim() ?? "";
|
||||
return resolveAgentModelPrimaryValue(cfg.agents?.defaults?.model) ?? "";
|
||||
}
|
||||
|
||||
function resolveConfiguredModelKeys(cfg: OpenClawConfig): string[] {
|
||||
|
||||
@@ -5,6 +5,10 @@ import {
|
||||
resolveModelRefFromString,
|
||||
} from "../../agents/model-selection.js";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import {
|
||||
resolveAgentModelFallbackValues,
|
||||
resolveAgentModelPrimaryValue,
|
||||
} from "../../config/model-input.js";
|
||||
import type { ConfiguredEntry } from "./list.types.js";
|
||||
import { DEFAULT_MODEL, DEFAULT_PROVIDER, modelKey } from "./shared.js";
|
||||
|
||||
@@ -37,16 +41,9 @@ export function resolveConfiguredEntries(cfg: OpenClawConfig) {
|
||||
|
||||
addEntry(resolvedDefault, "default");
|
||||
|
||||
const modelConfig = cfg.agents?.defaults?.model as
|
||||
| { primary?: string; fallbacks?: string[] }
|
||||
| undefined;
|
||||
const imageModelConfig = cfg.agents?.defaults?.imageModel as
|
||||
| { primary?: string; fallbacks?: string[] }
|
||||
| undefined;
|
||||
const modelFallbacks = typeof modelConfig === "object" ? (modelConfig?.fallbacks ?? []) : [];
|
||||
const imageFallbacks =
|
||||
typeof imageModelConfig === "object" ? (imageModelConfig?.fallbacks ?? []) : [];
|
||||
const imagePrimary = imageModelConfig?.primary?.trim() ?? "";
|
||||
const modelFallbacks = resolveAgentModelFallbackValues(cfg.agents?.defaults?.model);
|
||||
const imageFallbacks = resolveAgentModelFallbackValues(cfg.agents?.defaults?.imageModel);
|
||||
const imagePrimary = resolveAgentModelPrimaryValue(cfg.agents?.defaults?.imageModel) ?? "";
|
||||
|
||||
modelFallbacks.forEach((raw, idx) => {
|
||||
const resolved = resolveModelRefFromString({
|
||||
|
||||
@@ -26,6 +26,10 @@ import {
|
||||
import { formatCliCommand } from "../../cli/command-format.js";
|
||||
import { withProgressTotals } from "../../cli/progress.js";
|
||||
import { CONFIG_PATH, loadConfig } from "../../config/config.js";
|
||||
import {
|
||||
resolveAgentModelFallbackValues,
|
||||
resolveAgentModelPrimaryValue,
|
||||
} from "../../config/model-input.js";
|
||||
import {
|
||||
formatUsageWindowSummary,
|
||||
loadProviderUsageSummary,
|
||||
@@ -87,24 +91,14 @@ export async function modelsStatusCommand(
|
||||
defaultModel: DEFAULT_MODEL,
|
||||
});
|
||||
|
||||
const modelConfig = cfg.agents?.defaults?.model as
|
||||
| { primary?: string; fallbacks?: string[] }
|
||||
| string
|
||||
| undefined;
|
||||
const imageConfig = cfg.agents?.defaults?.imageModel as
|
||||
| { primary?: string; fallbacks?: string[] }
|
||||
| string
|
||||
| undefined;
|
||||
const rawDefaultsModel =
|
||||
typeof modelConfig === "string" ? modelConfig.trim() : (modelConfig?.primary?.trim() ?? "");
|
||||
const rawDefaultsModel = resolveAgentModelPrimaryValue(cfg.agents?.defaults?.model) ?? "";
|
||||
const rawModel = agentModelPrimary ?? rawDefaultsModel;
|
||||
const resolvedLabel = `${resolved.provider}/${resolved.model}`;
|
||||
const defaultLabel = rawModel || resolvedLabel;
|
||||
const defaultsFallbacks = typeof modelConfig === "object" ? (modelConfig?.fallbacks ?? []) : [];
|
||||
const defaultsFallbacks = resolveAgentModelFallbackValues(cfg.agents?.defaults?.model);
|
||||
const fallbacks = agentFallbacksOverride ?? defaultsFallbacks;
|
||||
const imageModel =
|
||||
typeof imageConfig === "string" ? imageConfig.trim() : (imageConfig?.primary?.trim() ?? "");
|
||||
const imageFallbacks = typeof imageConfig === "object" ? (imageConfig?.fallbacks ?? []) : [];
|
||||
const imageModel = resolveAgentModelPrimaryValue(cfg.agents?.defaults?.imageModel) ?? "";
|
||||
const imageFallbacks = resolveAgentModelFallbackValues(cfg.agents?.defaults?.imageModel);
|
||||
const aliases = Object.entries(cfg.agents?.defaults?.models ?? {}).reduce<Record<string, string>>(
|
||||
(acc, [key, entry]) => {
|
||||
const alias = typeof entry?.alias === "string" ? entry.alias.trim() : undefined;
|
||||
|
||||
@@ -4,6 +4,7 @@ import { type ModelScanResult, scanOpenRouterModels } from "../../agents/model-s
|
||||
import { withProgressTotals } from "../../cli/progress.js";
|
||||
import { loadConfig } from "../../config/config.js";
|
||||
import { logConfigUpdated } from "../../config/logging.js";
|
||||
import { toAgentModelListLike } from "../../config/model-input.js";
|
||||
import type { RuntimeEnv } from "../../runtime.js";
|
||||
import {
|
||||
stylePromptHint,
|
||||
@@ -297,9 +298,7 @@ export async function modelsScanCommand(
|
||||
nextModels[entry] = {};
|
||||
}
|
||||
}
|
||||
const existingImageModel = cfg.agents?.defaults?.imageModel as
|
||||
| { primary?: string; fallbacks?: string[] }
|
||||
| undefined;
|
||||
const existingImageModel = toAgentModelListLike(cfg.agents?.defaults?.imageModel);
|
||||
const nextImageModel =
|
||||
selectedImages.length > 0
|
||||
? {
|
||||
@@ -308,9 +307,7 @@ export async function modelsScanCommand(
|
||||
...(opts.setImage ? { primary: selectedImages[0] } : {}),
|
||||
}
|
||||
: cfg.agents?.defaults?.imageModel;
|
||||
const existingModel = cfg.agents?.defaults?.model as
|
||||
| { primary?: string; fallbacks?: string[] }
|
||||
| undefined;
|
||||
const existingModel = toAgentModelListLike(cfg.agents?.defaults?.model);
|
||||
const defaults = {
|
||||
...cfg.agents?.defaults,
|
||||
model: {
|
||||
|
||||
@@ -6,6 +6,7 @@ import { cancel, isCancel } from "@clack/prompts";
|
||||
import { DEFAULT_AGENT_WORKSPACE_DIR, ensureAgentWorkspace } from "../agents/workspace.js";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { CONFIG_PATH } from "../config/config.js";
|
||||
import { resolveAgentModelPrimaryValue } from "../config/model-input.js";
|
||||
import { resolveSessionTranscriptsDirForAgent } from "../config/sessions.js";
|
||||
import { callGateway } from "../gateway/call.js";
|
||||
import { normalizeControlUiBasePath } from "../gateway/control-ui-shared.js";
|
||||
@@ -43,7 +44,7 @@ export function summarizeExistingConfig(config: OpenClawConfig): string {
|
||||
rows.push(shortenHomeInString(`workspace: ${defaults.workspace}`));
|
||||
}
|
||||
if (defaults?.model) {
|
||||
const model = typeof defaults.model === "string" ? defaults.model : defaults.model.primary;
|
||||
const model = resolveAgentModelPrimaryValue(defaults.model);
|
||||
if (model) {
|
||||
rows.push(shortenHomeInString(`model: ${model}`));
|
||||
}
|
||||
|
||||
@@ -10,6 +10,10 @@ import {
|
||||
} from "../agents/model-catalog.js";
|
||||
import type { MsgContext } from "../auto-reply/templating.js";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import {
|
||||
resolveAgentModelFallbackValues,
|
||||
resolveAgentModelPrimaryValue,
|
||||
} from "../config/model-input.js";
|
||||
import type {
|
||||
MediaUnderstandingConfig,
|
||||
MediaUnderstandingModelConfig,
|
||||
@@ -418,28 +422,19 @@ async function resolveKeyEntry(params: {
|
||||
}
|
||||
|
||||
function resolveImageModelFromAgentDefaults(cfg: OpenClawConfig): MediaUnderstandingModelConfig[] {
|
||||
const imageModel = cfg.agents?.defaults?.imageModel as
|
||||
| { primary?: string; fallbacks?: string[] }
|
||||
| string
|
||||
| undefined;
|
||||
if (!imageModel) {
|
||||
return [];
|
||||
}
|
||||
const refs: string[] = [];
|
||||
if (typeof imageModel === "string") {
|
||||
if (imageModel.trim()) {
|
||||
refs.push(imageModel.trim());
|
||||
}
|
||||
} else {
|
||||
if (imageModel.primary?.trim()) {
|
||||
refs.push(imageModel.primary.trim());
|
||||
}
|
||||
for (const fb of imageModel.fallbacks ?? []) {
|
||||
if (fb?.trim()) {
|
||||
refs.push(fb.trim());
|
||||
}
|
||||
const primary = resolveAgentModelPrimaryValue(cfg.agents?.defaults?.imageModel);
|
||||
if (primary?.trim()) {
|
||||
refs.push(primary.trim());
|
||||
}
|
||||
for (const fb of resolveAgentModelFallbackValues(cfg.agents?.defaults?.imageModel)) {
|
||||
if (fb?.trim()) {
|
||||
refs.push(fb.trim());
|
||||
}
|
||||
}
|
||||
if (refs.length === 0) {
|
||||
return [];
|
||||
}
|
||||
const entries: MediaUnderstandingModelConfig[] = [];
|
||||
for (const ref of refs) {
|
||||
const slashIdx = ref.indexOf("/");
|
||||
|
||||
Reference in New Issue
Block a user