


















@@ -421,6 +421,122 @@ describe("resolveModel", () => {
421421expect(result.model?.input).toEqual(["text", "image"]);
422422});
423423424+it("propagates image input when configured model ids include the provider prefix", () => {
425+const cfg = {
426+models: {
427+providers: {
428+custom: {
429+baseUrl: "http://localhost:9000",
430+api: "openai-completions",
431+models: [
432+{
433+ ...makeModel("custom/vision-model"),
434+input: ["text", "image"],
435+},
436+],
437+},
438+},
439+},
440+} as unknown as OpenClawConfig;
441+442+const result = resolveModelForTest("custom", "vision-model", "/tmp/agent", cfg);
443+444+expect(result.error).toBeUndefined();
445+expect(result.model).toMatchObject({
446+provider: "custom",
447+id: "custom/vision-model",
448+input: ["text", "image"],
449+});
450+});
451+452+it("matches provider-prefixed configured model ids through provider aliases", () => {
453+const cfg = {
454+models: {
455+providers: {
456+volcengine: {
457+baseUrl: "http://localhost:9000",
458+api: "openai-completions",
459+models: [
460+{
461+ ...makeModel("volcengine/vision-model"),
462+input: ["text", "image"],
463+},
464+],
465+},
466+},
467+},
468+} as unknown as OpenClawConfig;
469+470+const result = resolveModelForTest("bytedance", "vision-model", "/tmp/agent", cfg);
471+472+expect(result.error).toBeUndefined();
473+expect(result.model).toMatchObject({
474+id: "volcengine/vision-model",
475+input: ["text", "image"],
476+});
477+});
478+479+it("does not treat arbitrary namespaced model ids as provider prefixes", () => {
480+const cfg = {
481+models: {
482+providers: {
483+custom: {
484+baseUrl: "http://localhost:9000",
485+api: "openai-completions",
486+models: [
487+{
488+ ...makeModel("meta/vision-model"),
489+input: ["text", "image"],
490+},
491+],
492+},
493+},
494+},
495+} as unknown as OpenClawConfig;
496+497+const result = resolveModelForTest("custom", "vision-model", "/tmp/agent", cfg);
498+499+expect(result.model?.id).toBe("vision-model");
500+expect(result.model?.input).toEqual(["text"]);
501+});
502+503+it("prefers provider-prefixed configured metadata over discovered text-only models", () => {
504+mockDiscoveredModel(discoverModels, {
505+provider: "custom",
506+modelId: "vision-model",
507+templateModel: {
508+ ...makeModel("vision-model"),
509+provider: "custom",
510+input: ["text"],
511+},
512+});
513+const cfg = {
514+models: {
515+providers: {
516+custom: {
517+baseUrl: "http://localhost:9000",
518+api: "openai-completions",
519+models: [
520+{
521+ ...makeModel("custom/vision-model"),
522+input: ["text", "image"],
523+},
524+],
525+},
526+},
527+},
528+} as unknown as OpenClawConfig;
529+530+const result = resolveModelForTest("custom", "vision-model", "/tmp/agent", cfg);
531+532+expect(result.error).toBeUndefined();
533+expect(result.model).toMatchObject({
534+provider: "custom",
535+id: "custom/vision-model",
536+input: ["text", "image"],
537+});
538+});
539+424540it("keeps unknown fallback models text-only instead of borrowing image input from another configured model", () => {
425541const cfg = {
426542models: {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。