mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 09:10:45 +00:00
fix(status): ignore malformed persisted model fields
This commit is contained in:
@@ -45,7 +45,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Plugins/tools: cold-load selected plugin tool registries when the active registry only has partial tool coverage, so wildcard-expanded allowlists no longer hide installed plugin tools from `tools.effective`. Fixes #76780. Thanks @lilesjtu.
|
||||
- Plugins/tools: compare cached and runtime plugin tool name conflicts with normalized core tool names, so case variants of core tools are blocked instead of leaking duplicate tool registrations. Thanks @vincentkoc.
|
||||
- Plugins/OpenRouter: advertise DeepSeek V4 thinking levels, including `xhigh` and `max`, through the runtime and lightweight provider policy surfaces so `/think` validation no longer rejects OpenRouter-routed DeepSeek V4 models. Fixes #74788. Thanks @vincentkoc.
|
||||
- Status/sessions: ignore malformed non-string persisted session provider/model metadata instead of throwing while rendering status summaries. Thanks @vincentkoc.
|
||||
- Status/sessions: ignore malformed non-string persisted session provider/model metadata instead of throwing while rendering status summaries. Fixes #76206. Thanks @vincentkoc.
|
||||
- CLI/config: remove only the targeted array element for `openclaw config unset array[index]` instead of replaying the unset during config write and deleting the shifted next element. Fixes #76290. Thanks @SymbolStar and @vincentkoc.
|
||||
- Plugins/voice-call: treat abnormal local Gateway close code 1006 as a standalone CLI fallback case, so `voicecall smoke` and related commands can still run the provider check path when the Gateway socket closes before returning a response.
|
||||
- Agents/tools: stop treating `tools.deny: ["write"]` as an implicit `apply_patch` deny; operators who want to block patch writes should deny `apply_patch` or `group:fs` explicitly. Fixes #76749. (#76795) Thanks @Nek-12 and @hclsys.
|
||||
|
||||
@@ -32,6 +32,18 @@ describe("model-selection-display", () => {
|
||||
}),
|
||||
).toBe("anthropic/claude-sonnet-4-6");
|
||||
});
|
||||
|
||||
it("ignores malformed persisted model values instead of throwing", () => {
|
||||
expect(
|
||||
resolveModelDisplayRef({
|
||||
runtimeProvider: { provider: "openai" },
|
||||
runtimeModel: false,
|
||||
overrideProvider: ["anthropic"],
|
||||
overrideModel: 123,
|
||||
fallbackModel: " openai/gpt-5.5 ",
|
||||
}),
|
||||
).toBe("openai/gpt-5.5");
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveModelDisplayName", () => {
|
||||
@@ -100,5 +112,21 @@ describe("model-selection-display", () => {
|
||||
model: "gpt-5.4",
|
||||
});
|
||||
});
|
||||
|
||||
it("ignores malformed persisted session model values", () => {
|
||||
expect(
|
||||
resolveSessionInfoModelSelection({
|
||||
currentProvider: { provider: "openai" },
|
||||
currentModel: false,
|
||||
defaultProvider: "anthropic",
|
||||
defaultModel: "claude-sonnet-4-6",
|
||||
entryProvider: ["openrouter"],
|
||||
entryModel: 123,
|
||||
}),
|
||||
).toEqual({
|
||||
modelProvider: "anthropic",
|
||||
model: "claude-sonnet-4-6",
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { normalizeOptionalString } from "../shared/string-coerce.js";
|
||||
|
||||
type ModelDisplaySelectionParams = {
|
||||
runtimeProvider?: string | null;
|
||||
runtimeModel?: string | null;
|
||||
overrideProvider?: string | null;
|
||||
overrideModel?: string | null;
|
||||
fallbackModel?: string | null;
|
||||
runtimeProvider?: unknown;
|
||||
runtimeModel?: unknown;
|
||||
overrideProvider?: unknown;
|
||||
overrideModel?: unknown;
|
||||
fallbackModel?: unknown;
|
||||
};
|
||||
|
||||
export function resolveModelDisplayRef(params: ModelDisplaySelectionParams): string | undefined {
|
||||
const runtimeModel = params.runtimeModel?.trim();
|
||||
const runtimeProvider = params.runtimeProvider?.trim();
|
||||
const runtimeModel = normalizeOptionalString(params.runtimeModel);
|
||||
const runtimeProvider = normalizeOptionalString(params.runtimeProvider);
|
||||
if (runtimeModel) {
|
||||
if (runtimeModel.includes("/")) {
|
||||
return runtimeModel;
|
||||
@@ -22,8 +24,8 @@ export function resolveModelDisplayRef(params: ModelDisplaySelectionParams): str
|
||||
return runtimeProvider;
|
||||
}
|
||||
|
||||
const overrideModel = params.overrideModel?.trim();
|
||||
const overrideProvider = params.overrideProvider?.trim();
|
||||
const overrideModel = normalizeOptionalString(params.overrideModel);
|
||||
const overrideProvider = normalizeOptionalString(params.overrideProvider);
|
||||
if (overrideModel) {
|
||||
if (overrideModel.includes("/")) {
|
||||
return overrideModel;
|
||||
@@ -37,7 +39,7 @@ export function resolveModelDisplayRef(params: ModelDisplaySelectionParams): str
|
||||
return overrideProvider;
|
||||
}
|
||||
|
||||
const fallbackModel = params.fallbackModel?.trim();
|
||||
const fallbackModel = normalizeOptionalString(params.fallbackModel);
|
||||
return fallbackModel || undefined;
|
||||
}
|
||||
|
||||
@@ -54,33 +56,39 @@ export function resolveModelDisplayName(params: ModelDisplaySelectionParams): st
|
||||
}
|
||||
|
||||
type SessionInfoModelSelectionParams = {
|
||||
currentProvider?: string | null;
|
||||
currentModel?: string | null;
|
||||
defaultProvider?: string | null;
|
||||
defaultModel?: string | null;
|
||||
entryProvider?: string | null;
|
||||
entryModel?: string | null;
|
||||
overrideProvider?: string | null;
|
||||
overrideModel?: string | null;
|
||||
currentProvider?: unknown;
|
||||
currentModel?: unknown;
|
||||
defaultProvider?: unknown;
|
||||
defaultModel?: unknown;
|
||||
entryProvider?: unknown;
|
||||
entryModel?: unknown;
|
||||
overrideProvider?: unknown;
|
||||
overrideModel?: unknown;
|
||||
};
|
||||
|
||||
export function resolveSessionInfoModelSelection(params: SessionInfoModelSelectionParams): {
|
||||
modelProvider?: string;
|
||||
model?: string;
|
||||
} {
|
||||
const fallbackProvider = params.currentProvider ?? params.defaultProvider ?? undefined;
|
||||
const fallbackModel = params.currentModel ?? params.defaultModel ?? undefined;
|
||||
const fallbackProvider =
|
||||
normalizeOptionalString(params.currentProvider) ??
|
||||
normalizeOptionalString(params.defaultProvider) ??
|
||||
undefined;
|
||||
const fallbackModel =
|
||||
normalizeOptionalString(params.currentModel) ??
|
||||
normalizeOptionalString(params.defaultModel) ??
|
||||
undefined;
|
||||
|
||||
if (params.entryProvider !== undefined || params.entryModel !== undefined) {
|
||||
return {
|
||||
modelProvider: params.entryProvider ?? fallbackProvider,
|
||||
model: params.entryModel ?? fallbackModel,
|
||||
modelProvider: normalizeOptionalString(params.entryProvider) ?? fallbackProvider,
|
||||
model: normalizeOptionalString(params.entryModel) ?? fallbackModel,
|
||||
};
|
||||
}
|
||||
|
||||
const overrideModel = params.overrideModel?.trim();
|
||||
const overrideModel = normalizeOptionalString(params.overrideModel);
|
||||
if (overrideModel) {
|
||||
const overrideProvider = params.overrideProvider?.trim();
|
||||
const overrideProvider = normalizeOptionalString(params.overrideProvider);
|
||||
return {
|
||||
modelProvider: overrideProvider || fallbackProvider,
|
||||
model: overrideModel,
|
||||
|
||||
@@ -389,6 +389,18 @@ describe("model-selection", () => {
|
||||
model: "kimi-code",
|
||||
});
|
||||
});
|
||||
|
||||
it("ignores malformed persisted model fields and tolerates a missing default provider", () => {
|
||||
expect(
|
||||
resolvePersistedModelRef({
|
||||
defaultProvider: undefined,
|
||||
runtimeProvider: { provider: "openai" },
|
||||
runtimeModel: false,
|
||||
overrideProvider: ["anthropic"],
|
||||
overrideModel: 123,
|
||||
}),
|
||||
).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolvePersistedOverrideModelRef", () => {
|
||||
@@ -416,6 +428,16 @@ describe("model-selection", () => {
|
||||
model: "kimi-code",
|
||||
});
|
||||
});
|
||||
|
||||
it("ignores malformed persisted override fields", () => {
|
||||
expect(
|
||||
resolvePersistedOverrideModelRef({
|
||||
defaultProvider: undefined,
|
||||
overrideProvider: ["anthropic"],
|
||||
overrideModel: 123,
|
||||
}),
|
||||
).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolvePersistedSelectedModelRef", () => {
|
||||
|
||||
@@ -81,13 +81,17 @@ export {
|
||||
};
|
||||
export { isCliProvider } from "./model-selection-cli.js";
|
||||
|
||||
function normalizePersistedDefaultProvider(value: unknown): string {
|
||||
return normalizeOptionalString(value) ?? DEFAULT_PROVIDER;
|
||||
}
|
||||
|
||||
export function resolvePersistedOverrideModelRef(params: {
|
||||
defaultProvider: string;
|
||||
defaultProvider?: unknown;
|
||||
overrideProvider?: unknown;
|
||||
overrideModel?: unknown;
|
||||
allowPluginNormalization?: boolean;
|
||||
}): ModelRef | null {
|
||||
const defaultProvider = params.defaultProvider.trim();
|
||||
const defaultProvider = normalizePersistedDefaultProvider(params.defaultProvider);
|
||||
const overrideProvider = normalizeOptionalString(params.overrideProvider);
|
||||
const overrideModel = normalizeOptionalString(params.overrideModel);
|
||||
if (!overrideModel) {
|
||||
@@ -109,14 +113,14 @@ export function resolvePersistedOverrideModelRef(params: {
|
||||
* Use this when callers intentionally want the last executed model identity.
|
||||
*/
|
||||
export function resolvePersistedModelRef(params: {
|
||||
defaultProvider: string;
|
||||
defaultProvider?: unknown;
|
||||
runtimeProvider?: unknown;
|
||||
runtimeModel?: unknown;
|
||||
overrideProvider?: unknown;
|
||||
overrideModel?: unknown;
|
||||
allowPluginNormalization?: boolean;
|
||||
}): ModelRef | null {
|
||||
const defaultProvider = params.defaultProvider.trim();
|
||||
const defaultProvider = normalizePersistedDefaultProvider(params.defaultProvider);
|
||||
const runtimeProvider = normalizeOptionalString(params.runtimeProvider);
|
||||
const runtimeModel = normalizeOptionalString(params.runtimeModel);
|
||||
if (runtimeModel) {
|
||||
@@ -146,7 +150,7 @@ export function resolvePersistedModelRef(params: {
|
||||
* overrides before falling back to runtime identity.
|
||||
*/
|
||||
export function resolvePersistedSelectedModelRef(params: {
|
||||
defaultProvider: string;
|
||||
defaultProvider?: unknown;
|
||||
runtimeProvider?: unknown;
|
||||
runtimeModel?: unknown;
|
||||
overrideProvider?: unknown;
|
||||
@@ -171,11 +175,11 @@ export function resolvePersistedSelectedModelRef(params: {
|
||||
}
|
||||
|
||||
export function normalizeStoredOverrideModel(params: {
|
||||
providerOverride?: string | null;
|
||||
modelOverride?: string | null;
|
||||
providerOverride?: unknown;
|
||||
modelOverride?: unknown;
|
||||
}): { providerOverride?: string; modelOverride?: string } {
|
||||
const providerOverride = params.providerOverride?.trim();
|
||||
const modelOverride = params.modelOverride?.trim();
|
||||
const providerOverride = normalizeOptionalString(params.providerOverride);
|
||||
const modelOverride = normalizeOptionalString(params.modelOverride);
|
||||
if (!providerOverride || !modelOverride) {
|
||||
return {
|
||||
providerOverride,
|
||||
|
||||
Reference in New Issue
Block a user