



















@@ -230,6 +230,20 @@ function resolveModelAsyncForTest(
230230});
231231}
232232233+type ResolveModelForTestResult =
234+| ReturnType<typeof resolveModelForTest>
235+| Awaited<ReturnType<typeof resolveModelAsyncForTest>>;
236+237+function expectResolvedModel(result: ResolveModelForTestResult) {
238+if (result.error !== undefined) {
239+throw new Error(`expected model resolution to succeed, got error: ${result.error}`);
240+}
241+if (!result.model) {
242+throw new Error("expected model resolution to return a model");
243+}
244+return result.model;
245+}
246+233247describe("resolveModel", () => {
234248it("skips PI auth and model discovery during dynamic model resolution", async () => {
235249const result = await resolveModelAsync(
@@ -243,8 +257,7 @@ describe("resolveModel", () => {
243257},
244258);
245259246-expect(result.error).toBeUndefined();
247-expect(result.model).toMatchObject({
260+expect(expectResolvedModel(result)).toMatchObject({
248261provider: "openrouter",
249262id: "openrouter/auto",
250263});
@@ -283,9 +296,7 @@ describe("resolveModel", () => {
283296},
284297} as unknown as OpenClawConfig);
285298286-expect(result.error).toBeUndefined();
287-expect(Array.isArray(result.model?.input)).toBe(true);
288-expect(result.model?.input).toEqual(["text"]);
299+expect(expectResolvedModel(result).input).toEqual(["text"]);
289300});
290301291302it("defaults missing model cost before handing models to PI", () => {
@@ -312,8 +323,7 @@ describe("resolveModel", () => {
312323313324const result = resolveModelForTest("openai", "gpt-5.5", "/tmp/agent", cfg);
314325315-expect(result.error).toBeUndefined();
316-expect(result.model).toMatchObject({
326+expect(expectResolvedModel(result)).toMatchObject({
317327provider: "openai",
318328id: "gpt-5.5",
319329cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
@@ -333,11 +343,12 @@ describe("resolveModel", () => {
333343} as unknown as OpenClawConfig;
334344335345const result = resolveModelForTest("custom", "missing-model", "/tmp/agent", cfg);
346+const model = expectResolvedModel(result);
336347337-expect(result.model?.baseUrl).toBe("http://localhost:9000");
338-expect(result.model?.provider).toBe("custom");
339-expect(result.model?.id).toBe("missing-model");
340-expect(result.model?.api).toBe("openai-completions");
348+expect(model.baseUrl).toBe("http://localhost:9000");
349+expect(model.provider).toBe("custom");
350+expect(model.id).toBe("missing-model");
351+expect(model.api).toBe("openai-completions");
341352});
342353343354it("defaults baseUrl-only local custom fallback models to chat completions", () => {
@@ -358,15 +369,15 @@ describe("resolveModel", () => {
358369} as unknown as OpenClawConfig;
359370360371const result = resolveModelForTest("local-agent-proxy", "gpt-5.2", "/tmp/agent", cfg);
372+const model = expectResolvedModel(result);
361373362-expect(result.error).toBeUndefined();
363-expect(result.model).toMatchObject({
374+expect(model).toMatchObject({
364375provider: "local-agent-proxy",
365376id: "gpt-5.2",
366377api: "openai-completions",
367378baseUrl: "http://127.0.0.1:3000/v1",
368379});
369-expect(getModelProviderRequestTransport(result.model ?? {})).toBeUndefined();
380+expect(getModelProviderRequestTransport(model)).toBeUndefined();
370381});
371382372383it("resolves explicitly configured qwen3.6-plus before Coding Plan built-in suppression", () => {
@@ -393,8 +404,7 @@ describe("resolveModel", () => {
393404394405const result = resolveModelForTest("qwen", "qwen3.6-plus", "/tmp/agent", cfg);
395406396-expect(result.error).toBeUndefined();
397-expect(result.model).toMatchObject({
407+expect(expectResolvedModel(result)).toMatchObject({
398408provider: "qwen",
399409id: "qwen3.6-plus",
400410api: "openai-completions",
@@ -448,8 +458,7 @@ describe("resolveModel", () => {
448458449459const result = resolveModelForTest("openai-codex", "gpt-5.4-mini", "/tmp/agent", cfg);
450460451-expect(result.error).toBeUndefined();
452-expect(result.model).toMatchObject({
461+expect(expectResolvedModel(result)).toMatchObject({
453462provider: "openai-codex",
454463id: "gpt-5.4-mini",
455464api: "openai-codex-responses",
@@ -473,7 +482,9 @@ describe("resolveModel", () => {
473482474483const result = resolveModelForTest("google-paid", "missing-model", "/tmp/agent", cfg);
475484476-expect(result.model?.baseUrl).toBe("https://generativelanguage.googleapis.com/v1beta");
485+expect(expectResolvedModel(result).baseUrl).toBe(
486+"https://generativelanguage.googleapis.com/v1beta",
487+);
477488});
478489479490it("normalizes configured Google override baseUrls when provider api is omitted", () => {
@@ -500,10 +511,10 @@ describe("resolveModel", () => {
500511} as unknown as OpenClawConfig;
501512502513const result = resolveModelForTest("google", "gemini-2.5-pro", "/tmp/agent", cfg);
514+const model = expectResolvedModel(result);
503515504-expect(result.error).toBeUndefined();
505-expect(result.model?.api).toBe("google-generative-ai");
506-expect(result.model?.baseUrl).toBe("https://generativelanguage.googleapis.com/v1beta");
516+expect(model.api).toBe("google-generative-ai");
517+expect(model.baseUrl).toBe("https://generativelanguage.googleapis.com/v1beta");
507518});
508519509520it("normalizes custom api.openai.com providers to responses transport", () => {
@@ -526,8 +537,7 @@ describe("resolveModel", () => {
526537527538const result = resolveModelForTest("custom-openai", "gpt-5.4", "/tmp/agent", cfg);
528539529-expect(result.error).toBeUndefined();
530-expect(result.model).toMatchObject({
540+expect(expectResolvedModel(result)).toMatchObject({
531541provider: "custom-openai",
532542id: "gpt-5.4",
533543api: "openai-responses",
@@ -555,8 +565,7 @@ describe("resolveModel", () => {
555565556566const result = resolveModelForTest("custom-xai", "grok-4.1-fast", "/tmp/agent", cfg);
557567558-expect(result.error).toBeUndefined();
559-expect(result.model).toMatchObject({
568+expect(expectResolvedModel(result)).toMatchObject({
560569provider: "custom-xai",
561570id: "grok-4.1-fast",
562571api: "openai-responses",
@@ -579,9 +588,9 @@ describe("resolveModel", () => {
579588580589// Requesting a non-listed model forces the providerCfg fallback branch.
581590const result = resolveModelForTest("custom", "missing-model", "/tmp/agent", cfg);
591+const model = expectResolvedModel(result) as unknown as { headers?: Record<string, string> };
582592583-expect(result.error).toBeUndefined();
584-expect((result.model as unknown as { headers?: Record<string, string> }).headers).toEqual({
593+expect(model.headers).toEqual({
585594"X-Custom-Auth": "token-123",
586595});
587596});
@@ -604,9 +613,9 @@ describe("resolveModel", () => {
604613} as unknown as OpenClawConfig;
605614606615const result = resolveModelForTest("custom", "missing-model", "/tmp/agent", cfg);
616+const model = expectResolvedModel(result) as unknown as { headers?: Record<string, string> };
607617608-expect(result.error).toBeUndefined();
609-expect((result.model as unknown as { headers?: Record<string, string> }).headers).toEqual({
618+expect(model.headers).toEqual({
610619"X-Custom-Auth": "token-123",
611620});
612621});
@@ -627,9 +636,9 @@ describe("resolveModel", () => {
627636});
628637629638const result = resolveModelForTest("custom", "listed-model", "/tmp/agent");
639+const model = expectResolvedModel(result) as unknown as { headers?: Record<string, string> };
630640631-expect(result.error).toBeUndefined();
632-expect((result.model as unknown as { headers?: Record<string, string> }).headers).toEqual({
641+expect(model.headers).toEqual({
633642"X-Static": "tenant-a",
634643});
635644});
@@ -658,9 +667,10 @@ describe("resolveModel", () => {
658667} as unknown as OpenClawConfig;
659668660669const result = resolveModelForTest("custom", "model-b", "/tmp/agent", cfg);
670+const model = expectResolvedModel(result);
661671662-expect(result.model?.contextWindow).toBe(262144);
663-expect(result.model?.maxTokens).toBe(32768);
672+expect(model.contextWindow).toBe(262144);
673+expect(model.maxTokens).toBe(32768);
664674});
665675666676it("merges configured model params with agent defaults for resolved models", () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。