mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:20:43 +00:00
test: share chat model select state fixtures
This commit is contained in:
@@ -10,101 +10,113 @@ import {
|
||||
DEFAULT_CHAT_MODEL_CATALOG,
|
||||
} from "./chat-model.test-helpers.ts";
|
||||
|
||||
type ChatModelStateInput = Parameters<typeof resolveChatModelSelectState>[0];
|
||||
type ResolvedChatModelState = ReturnType<typeof resolveChatModelSelectState>;
|
||||
|
||||
function createChatModelState(
|
||||
params: Partial<Omit<ChatModelStateInput, "sessionKey">> = {},
|
||||
): ChatModelStateInput {
|
||||
return {
|
||||
sessionKey: "main",
|
||||
chatModelOverrides: {},
|
||||
chatModelCatalog: [],
|
||||
sessionsResult: createSessionsListResult({ model: null, modelProvider: null }),
|
||||
...params,
|
||||
};
|
||||
}
|
||||
|
||||
function expectOptionValues(
|
||||
resolved: ResolvedChatModelState,
|
||||
params: { include?: string[]; exclude?: string[] },
|
||||
) {
|
||||
const values = resolved.options.map((option) => option.value);
|
||||
for (const value of params.include ?? []) {
|
||||
expect(values).toContain(value);
|
||||
}
|
||||
for (const value of params.exclude ?? []) {
|
||||
expect(values).not.toContain(value);
|
||||
}
|
||||
}
|
||||
|
||||
describe("chat-model-select-state", () => {
|
||||
it("uses the server-qualified value when the active session provider is present", () => {
|
||||
const state = {
|
||||
sessionKey: "main",
|
||||
chatModelOverrides: {},
|
||||
const state = createChatModelState({
|
||||
chatModelCatalog: createModelCatalog(DEEPSEEK_CHAT_MODEL),
|
||||
sessionsResult: createSessionsListResult({
|
||||
model: "deepseek-chat",
|
||||
modelProvider: "deepseek",
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
expect(resolveChatModelOverrideValue(state)).toBe("deepseek/deepseek-chat");
|
||||
});
|
||||
|
||||
it("falls back to the server-qualified value when catalog lookup fails", () => {
|
||||
const state = {
|
||||
sessionKey: "main",
|
||||
chatModelOverrides: {},
|
||||
chatModelCatalog: [],
|
||||
const state = createChatModelState({
|
||||
sessionsResult: createSessionsListResult({
|
||||
model: "gpt-5-mini",
|
||||
modelProvider: "openai",
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
expect(resolveChatModelOverrideValue(state)).toBe("openai/gpt-5-mini");
|
||||
});
|
||||
|
||||
it("normalizes cached bare overrides to the matching catalog option", () => {
|
||||
const state = {
|
||||
sessionKey: "main",
|
||||
const state = createChatModelState({
|
||||
chatModelOverrides: { main: { kind: "raw", value: "gpt-5-mini" } },
|
||||
chatModelCatalog: createModelCatalog(...DEFAULT_CHAT_MODEL_CATALOG),
|
||||
sessionsResult: createSessionsListResult({ model: null, modelProvider: null }),
|
||||
} as const;
|
||||
});
|
||||
|
||||
const resolved = resolveChatModelSelectState(state);
|
||||
expect(resolved.currentOverride).toBe("openai/gpt-5-mini");
|
||||
expect(resolved.options.map((option) => option.value)).toContain("openai/gpt-5-mini");
|
||||
expect(resolved.options.map((option) => option.value)).not.toContain("gpt-5-mini");
|
||||
expectOptionValues(resolved, { include: ["openai/gpt-5-mini"], exclude: ["gpt-5-mini"] });
|
||||
});
|
||||
|
||||
it("prefers catalog provider matches over stale session providers", () => {
|
||||
const state = {
|
||||
sessionKey: "main",
|
||||
chatModelOverrides: {},
|
||||
const state = createChatModelState({
|
||||
chatModelCatalog: createModelCatalog(DEEPSEEK_CHAT_MODEL),
|
||||
sessionsResult: createSessionsListResult({
|
||||
model: "deepseek-chat",
|
||||
modelProvider: "zai",
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
expect(resolveChatModelSelectState(state).currentOverride).toBe("deepseek/deepseek-chat");
|
||||
});
|
||||
|
||||
it("preserves already-qualified active-session models when the provider is stale and the catalog is empty", () => {
|
||||
const state = {
|
||||
sessionKey: "main",
|
||||
chatModelOverrides: {},
|
||||
chatModelCatalog: [],
|
||||
const state = createChatModelState({
|
||||
sessionsResult: createSessionsListResult({
|
||||
model: "openai/gpt-5-mini",
|
||||
modelProvider: "zai",
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
const resolved = resolveChatModelSelectState(state);
|
||||
expect(resolved.currentOverride).toBe("openai/gpt-5-mini");
|
||||
expect(resolved.options.map((option) => option.value)).toContain("openai/gpt-5-mini");
|
||||
expect(resolved.options.map((option) => option.value)).not.toContain("zai/openai/gpt-5-mini");
|
||||
expectOptionValues(resolved, {
|
||||
include: ["openai/gpt-5-mini"],
|
||||
exclude: ["zai/openai/gpt-5-mini"],
|
||||
});
|
||||
});
|
||||
|
||||
it("builds picker options without introducing a bare duplicate", () => {
|
||||
const state = {
|
||||
sessionKey: "main",
|
||||
chatModelOverrides: {},
|
||||
const state = createChatModelState({
|
||||
chatModelCatalog: createModelCatalog(...DEFAULT_CHAT_MODEL_CATALOG),
|
||||
sessionsResult: createSessionsListResult({
|
||||
model: "gpt-5-mini",
|
||||
modelProvider: "openai",
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
const resolved = resolveChatModelSelectState(state);
|
||||
expect(resolved.currentOverride).toBe("openai/gpt-5-mini");
|
||||
expect(resolved.options.map((option) => option.value)).toContain("openai/gpt-5-mini");
|
||||
expect(resolved.options.map((option) => option.value)).not.toContain("gpt-5-mini");
|
||||
expectOptionValues(resolved, { include: ["openai/gpt-5-mini"], exclude: ["gpt-5-mini"] });
|
||||
});
|
||||
|
||||
it("uses catalog names for the default label and matching picker options", () => {
|
||||
const state = {
|
||||
sessionKey: "main",
|
||||
chatModelOverrides: {},
|
||||
const state = createChatModelState({
|
||||
chatModelCatalog: createModelCatalog({
|
||||
id: "moonshotai/kimi-k2.5",
|
||||
alias: "Kimi K2.5 (NVIDIA)",
|
||||
@@ -117,7 +129,7 @@ describe("chat-model-select-state", () => {
|
||||
defaultsModel: "moonshotai/kimi-k2.5",
|
||||
defaultsProvider: "nvidia",
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
const resolved = resolveChatModelSelectState(state);
|
||||
expect(resolved.currentOverride).toBe("nvidia/moonshotai/kimi-k2.5");
|
||||
@@ -129,9 +141,7 @@ describe("chat-model-select-state", () => {
|
||||
});
|
||||
|
||||
it("disambiguates duplicate friendly names in picker options and default labels", () => {
|
||||
const state = {
|
||||
sessionKey: "main",
|
||||
chatModelOverrides: {},
|
||||
const state = createChatModelState({
|
||||
chatModelCatalog: createModelCatalog(
|
||||
{
|
||||
id: "claude-3-7-sonnet",
|
||||
@@ -150,7 +160,7 @@ describe("chat-model-select-state", () => {
|
||||
defaultsModel: "claude-3-7-sonnet",
|
||||
defaultsProvider: "openrouter",
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
const resolved = resolveChatModelSelectState(state);
|
||||
expect(resolved.currentOverride).toBe("anthropic/claude-3-7-sonnet");
|
||||
@@ -166,9 +176,7 @@ describe("chat-model-select-state", () => {
|
||||
});
|
||||
|
||||
it("falls back to id and provider when duplicate names share the same provider", () => {
|
||||
const state = {
|
||||
sessionKey: "main",
|
||||
chatModelOverrides: {},
|
||||
const state = createChatModelState({
|
||||
chatModelCatalog: createModelCatalog(
|
||||
{
|
||||
id: "claude-3-7-sonnet",
|
||||
@@ -187,7 +195,7 @@ describe("chat-model-select-state", () => {
|
||||
defaultsModel: "claude-3-7-sonnet-thinking",
|
||||
defaultsProvider: "anthropic",
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
const resolved = resolveChatModelSelectState(state);
|
||||
expect(resolved.currentOverride).toBe("anthropic/claude-3-7-sonnet");
|
||||
|
||||
Reference in New Issue
Block a user