返回 DeepSeek-Reasonix
provider-vision-capability.test.ts
根目录 / desktop / frontend / src / __tests__ / provider-vision-capability.test.ts
1 // Run: tsx src/__tests__/provider-vision-capability.test.ts
2
3 import type { ProviderView } from "../lib/types";
4 import { providerModelVisionCapability, providerVisionModelsForView } from "../lib/providerVisionCapability";
5 import { createMockModelScopePreset } from "../lib/mockModelScopePreset";
6
7 let passed = 0;
8 let failed = 0;
9
10 function ok(value: boolean, label: string) {
11 if (value) {
12 process.stdout.write(` PASS ${label}\n`);
13 passed += 1;
14 } else {
15 process.stdout.write(` FAIL ${label}\n`);
16 failed += 1;
17 }
18 }
19
20 function provider(overrides: Partial<ProviderView> = {}): ProviderView {
21 return {
22 name: "custom",
23 builtIn: false,
24 added: true,
25 kind: "openai",
26 baseUrl: "https://example.test/v1",
27 modelsUrl: "",
28 models: ["vision-model", "text-model", "opaque-model"],
29 visionModels: ["vision-model"],
30 visionModelsConfigured: true,
31 default: "vision-model",
32 apiKeyEnv: "CUSTOM_API_KEY",
33 keySet: true,
34 balanceUrl: "",
35 contextWindow: 0,
36 reasoningProtocol: "",
37 thinking: "",
38 supportedEfforts: [],
39 defaultEffort: "",
40 ...overrides,
41 };
42 }
43
44 const legacy = provider({
45 modelOverrides: [{
46 model: "vision-model",
47 reasoningProtocol: "",
48 supportedEfforts: [],
49 defaultEffort: "",
50 vision: false,
51 }],
52 });
53 ok(
54 providerVisionModelsForView(legacy).length === 0,
55 "model override can disable a legacy vision list entry",
56 );
57
58 const metadata = provider({
59 visionModels: [],
60 visionModelsConfigured: false,
61 modelCapabilities: [
62 { model: "vision-model", inputModalities: ["text", "image"], state: "supported", source: "adapter" },
63 { model: "text-model", inputModalities: ["text"], state: "unsupported", source: "adapter" },
64 ],
65 modelOverrides: [],
66 });
67 const adapterMetadataVision = providerVisionModelsForView(metadata);
68 ok(adapterMetadataVision.length === 1 && adapterMetadataVision[0] === "vision-model", "adapter metadata enables native vision without VisionModels");
69 ok(providerModelVisionCapability(metadata, "text-model", adapterMetadataVision) === "unsupported", "adapter text-only metadata stays read-only");
70 ok(
71 providerModelVisionCapability(
72 provider({
73 visionModels: [],
74 visionModelsConfigured: false,
75 visionCapability: "unsupported",
76 modelCapabilities: [],
77 }),
78 "opaque-model",
79 [],
80 ) === "unsupported",
81 "legacy provider-level unsupported capability remains a read-only fallback",
82 );
83
84 const overrideMetadata = provider({
85 visionModels: [],
86 visionModelsConfigured: false,
87 modelOverrides: [{
88 model: "vision-model",
89 reasoningProtocol: "",
90 supportedEfforts: [],
91 defaultEffort: "",
92 vision: true,
93 }],
94 });
95 const metadataVision = providerVisionModelsForView(overrideMetadata);
96 ok(metadataVision.length === 1 && metadataVision[0] === "vision-model", "model override enables native vision without VisionModels");
97 ok(providerModelVisionCapability(overrideMetadata, "vision-model", metadataVision) === "supported", "supported model is marked read-only");
98 ok(providerModelVisionCapability(overrideMetadata, "opaque-model", metadataVision) === "unknown", "unknown model stays conservative");
99
100 const modelScopePreset = createMockModelScopePreset(
101 (input) => ({ ...provider(), ...input, visionModels: [], visionModelsConfigured: false }),
102 (id, label, description, keyEnv, provider) => ({ id, label, description, keyEnv, provider, providers: [provider] }),
103 );
104 const modelScopeVision = providerVisionModelsForView(modelScopePreset.provider);
105 ok(modelScopeVision.length === 3 && modelScopeVision.every((model) => model.startsWith("Qwen/")), "mock ModelScope preserves model-level vision metadata");
106
107 process.stdout.write(`provider vision capability: ${passed} passed, ${failed} failed\n`);
108 if (failed > 0) process.exitCode = 1;
109
109 lines TYPESCRIPT