mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
fix(ui-cron): include configured model suggestions for scheduled jobs (openclaw#29709) thanks @Sid-Qin
Verified: - pnpm install --frozen-lockfile - pnpm build - pnpm check - pnpm test:macmini Co-authored-by: Sid-Qin <201593046+Sid-Qin@users.noreply.github.com> Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
This commit is contained in:
@@ -65,6 +65,7 @@ import {
|
||||
import { buildExternalLinkRel, EXTERNAL_LINK_TARGET } from "./external-link.ts";
|
||||
import { icons } from "./icons.ts";
|
||||
import { normalizeBasePath, TAB_GROUPS, subtitleForTab, titleForTab } from "./navigation.ts";
|
||||
import { resolveConfiguredCronModelSuggestions } from "./views/agents-utils.ts";
|
||||
import { renderAgents } from "./views/agents.ts";
|
||||
import { renderChannels } from "./views/channels.ts";
|
||||
import { renderChat } from "./views/chat.ts";
|
||||
@@ -178,6 +179,7 @@ export function renderApp(state: AppViewState) {
|
||||
new Set(
|
||||
[
|
||||
...state.cronModelSuggestions,
|
||||
...resolveConfiguredCronModelSuggestions(configValue),
|
||||
...state.cronJobs
|
||||
.map((job) => {
|
||||
if (job.payload.kind !== "agentTurn" || typeof job.payload.model !== "string") {
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { resolveEffectiveModelFallbacks } from "./agents-utils.ts";
|
||||
import {
|
||||
resolveConfiguredCronModelSuggestions,
|
||||
resolveEffectiveModelFallbacks,
|
||||
} from "./agents-utils.ts";
|
||||
|
||||
describe("resolveEffectiveModelFallbacks", () => {
|
||||
it("inherits defaults when no entry fallbacks are configured", () => {
|
||||
@@ -40,3 +43,47 @@ describe("resolveEffectiveModelFallbacks", () => {
|
||||
expect(resolveEffectiveModelFallbacks(entryModel, defaultModel)).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveConfiguredCronModelSuggestions", () => {
|
||||
it("collects defaults primary/fallbacks, alias map keys, and per-agent model entries", () => {
|
||||
const result = resolveConfiguredCronModelSuggestions({
|
||||
agents: {
|
||||
defaults: {
|
||||
model: {
|
||||
primary: "openai/gpt-5.2",
|
||||
fallbacks: ["google/gemini-2.5-pro", "openai/gpt-5.2-mini"],
|
||||
},
|
||||
models: {
|
||||
"anthropic/claude-sonnet-4-5": { alias: "smart" },
|
||||
"openai/gpt-5.2": { alias: "main" },
|
||||
},
|
||||
},
|
||||
list: {
|
||||
writer: {
|
||||
model: { primary: "xai/grok-4", fallbacks: ["openai/gpt-5.2-mini"] },
|
||||
},
|
||||
planner: {
|
||||
model: "google/gemini-2.5-flash",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(result).toEqual([
|
||||
"anthropic/claude-sonnet-4-5",
|
||||
"google/gemini-2.5-flash",
|
||||
"google/gemini-2.5-pro",
|
||||
"openai/gpt-5.2",
|
||||
"openai/gpt-5.2-mini",
|
||||
"xai/grok-4",
|
||||
]);
|
||||
});
|
||||
|
||||
it("returns empty array for invalid or missing config shape", () => {
|
||||
expect(resolveConfiguredCronModelSuggestions(null)).toEqual([]);
|
||||
expect(resolveConfiguredCronModelSuggestions({})).toEqual([]);
|
||||
expect(resolveConfiguredCronModelSuggestions({ agents: { defaults: { model: "" } } })).toEqual(
|
||||
[],
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -251,6 +251,77 @@ export function resolveEffectiveModelFallbacks(
|
||||
return resolveModelFallbacks(entryModel) ?? resolveModelFallbacks(defaultModel);
|
||||
}
|
||||
|
||||
function addModelId(target: Set<string>, value: unknown) {
|
||||
if (typeof value !== "string") {
|
||||
return;
|
||||
}
|
||||
const trimmed = value.trim();
|
||||
if (!trimmed) {
|
||||
return;
|
||||
}
|
||||
target.add(trimmed);
|
||||
}
|
||||
|
||||
function addModelConfigIds(target: Set<string>, modelConfig: unknown) {
|
||||
if (!modelConfig) {
|
||||
return;
|
||||
}
|
||||
if (typeof modelConfig === "string") {
|
||||
addModelId(target, modelConfig);
|
||||
return;
|
||||
}
|
||||
if (typeof modelConfig !== "object") {
|
||||
return;
|
||||
}
|
||||
const record = modelConfig as Record<string, unknown>;
|
||||
addModelId(target, record.primary);
|
||||
addModelId(target, record.model);
|
||||
addModelId(target, record.id);
|
||||
addModelId(target, record.value);
|
||||
const fallbacks = Array.isArray(record.fallbacks)
|
||||
? record.fallbacks
|
||||
: Array.isArray(record.fallback)
|
||||
? record.fallback
|
||||
: [];
|
||||
for (const fallback of fallbacks) {
|
||||
addModelId(target, fallback);
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveConfiguredCronModelSuggestions(
|
||||
configForm: Record<string, unknown> | null,
|
||||
): string[] {
|
||||
if (!configForm || typeof configForm !== "object") {
|
||||
return [];
|
||||
}
|
||||
const agents = (configForm as { agents?: unknown }).agents;
|
||||
if (!agents || typeof agents !== "object") {
|
||||
return [];
|
||||
}
|
||||
const out = new Set<string>();
|
||||
const defaults = (agents as { defaults?: unknown }).defaults;
|
||||
if (defaults && typeof defaults === "object") {
|
||||
const defaultsRecord = defaults as Record<string, unknown>;
|
||||
addModelConfigIds(out, defaultsRecord.model);
|
||||
const defaultsModels = defaultsRecord.models;
|
||||
if (defaultsModels && typeof defaultsModels === "object") {
|
||||
for (const modelId of Object.keys(defaultsModels as Record<string, unknown>)) {
|
||||
addModelId(out, modelId);
|
||||
}
|
||||
}
|
||||
}
|
||||
const list = (agents as { list?: unknown }).list;
|
||||
if (list && typeof list === "object") {
|
||||
for (const entry of Object.values(list as Record<string, unknown>)) {
|
||||
if (!entry || typeof entry !== "object") {
|
||||
continue;
|
||||
}
|
||||
addModelConfigIds(out, (entry as Record<string, unknown>).model);
|
||||
}
|
||||
}
|
||||
return [...out].toSorted((a, b) => a.localeCompare(b));
|
||||
}
|
||||
|
||||
export function parseFallbackList(value: string): string[] {
|
||||
return value
|
||||
.split(",")
|
||||
|
||||
Reference in New Issue
Block a user