























1+import { describe, expect, it, vi } from "vitest";
2+import type { OpenClawConfig } from "../config/types.openclaw.js";
3+import type { Model } from "../llm/types.js";
4+import { appendPrioritizedDynamicLiveModels } from "./live-model-dynamic-candidates.js";
5+6+const REGISTRY = { find: () => undefined } as never;
7+type DynamicModelResolver = NonNullable<
8+Parameters<typeof appendPrioritizedDynamicLiveModels>[0]["resolveDynamicModel"]
9+>;
10+type DynamicModelPreparer = NonNullable<
11+Parameters<typeof appendPrioritizedDynamicLiveModels>[0]["prepareDynamicModel"]
12+>;
13+14+function model(provider: string, id: string): Model {
15+return {
16+ id,
17+name: id,
18+ provider,
19+api: "openai-completions",
20+baseUrl: "https://example.test/v1",
21+reasoning: false,
22+input: ["text"],
23+cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
24+contextWindow: 128_000,
25+maxTokens: 4_096,
26+};
27+}
28+29+describe("appendPrioritizedDynamicLiveModels", () => {
30+it("materializes prioritized refs from provider dynamic model hooks", async () => {
31+const resolveDynamicModel: DynamicModelResolver = vi.fn((params) =>
32+params.context.provider === "opencode-go" && params.context.modelId === "glm-5"
33+ ? model("opencode-go", "glm-5")
34+ : undefined,
35+);
36+const prepareDynamicModel: DynamicModelPreparer = vi.fn(async () => undefined);
37+const config = {
38+models: {
39+providers: {
40+"opencode-go": {
41+api: "openai-completions",
42+baseUrl: "https://configured.example/v1",
43+models: [],
44+},
45+},
46+},
47+} as OpenClawConfig;
48+49+const result = await appendPrioritizedDynamicLiveModels({
50+models: [model("anthropic", "claude-sonnet-4-6")],
51+ config,
52+agentDir: "/tmp/openclaw-agent",
53+modelRegistry: REGISTRY,
54+ resolveDynamicModel,
55+ prepareDynamicModel,
56+refs: [
57+{ provider: "anthropic", id: "claude-sonnet-4-6" },
58+{ provider: "opencode-go", id: "glm-5" },
59+],
60+});
61+62+expect(result.added.map((entry) => `${entry.provider}/${entry.id}`)).toEqual([
63+"opencode-go/glm-5",
64+]);
65+expect(result.models.map((entry) => `${entry.provider}/${entry.id}`)).toEqual([
66+"anthropic/claude-sonnet-4-6",
67+"opencode-go/glm-5",
68+]);
69+expect(prepareDynamicModel).toHaveBeenCalledTimes(1);
70+expect(prepareDynamicModel).toHaveBeenCalledWith(
71+expect.objectContaining({
72+provider: "opencode-go",
73+context: expect.objectContaining({
74+agentDir: "/tmp/openclaw-agent",
75+modelId: "glm-5",
76+modelRegistry: REGISTRY,
77+provider: "opencode-go",
78+providerConfig: config.models?.providers?.["opencode-go"],
79+}),
80+}),
81+);
82+expect(resolveDynamicModel).toHaveBeenCalledTimes(1);
83+expect(resolveDynamicModel).toHaveBeenCalledWith(
84+expect.objectContaining({
85+provider: "opencode-go",
86+context: expect.objectContaining({
87+agentDir: "/tmp/openclaw-agent",
88+modelId: "glm-5",
89+modelRegistry: REGISTRY,
90+provider: "opencode-go",
91+providerConfig: config.models?.providers?.["opencode-go"],
92+}),
93+}),
94+);
95+});
96+97+it("does not duplicate refs already present in the generated registry", async () => {
98+const resolveDynamicModel: DynamicModelResolver = vi.fn(() => model("opencode-go", "glm-5"));
99+const prepareDynamicModel: DynamicModelPreparer = vi.fn(async () => undefined);
100+101+const result = await appendPrioritizedDynamicLiveModels({
102+models: [model("opencode-go", "glm-5")],
103+agentDir: "/tmp/openclaw-agent",
104+modelRegistry: REGISTRY,
105+ resolveDynamicModel,
106+ prepareDynamicModel,
107+refs: [{ provider: "opencode-go", id: "glm-5" }],
108+});
109+110+expect(result.added).toEqual([]);
111+expect(result.models).toHaveLength(1);
112+expect(prepareDynamicModel).not.toHaveBeenCalled();
113+expect(resolveDynamicModel).not.toHaveBeenCalled();
114+});
115+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。