diff --git a/packages/core/src/__tests__/model-catalog.test.ts b/packages/core/src/__tests__/model-catalog.test.ts index 85ac3287b3..81b36bbfff 100644 --- a/packages/core/src/__tests__/model-catalog.test.ts +++ b/packages/core/src/__tests__/model-catalog.test.ts @@ -351,3 +351,34 @@ test('Alibaba (China) catalogs Qwen3.8 Max as the default model on the China end assert.deepEqual(model?.modalities, { input: ['text', 'image', 'pdf'], output: ['text'] }); assert.equal(model?.canUseAsChatDefault, true); }); + +test('DeepSeek catalogs the V4 vision model display metadata from a bare provider id', () => { + const modelId = 'deepseek-v4-flash-vision-exp'; + const [model] = buildConnectionModelCatalogEntries({ + connection: { + slug: 'deepseek', + providerType: 'deepseek', + defaultModel: modelId, + models: [{ id: modelId }], + modelSource: 'fetched', + }, + }); + + assert.equal(model?.displayName, 'DeepSeek-V4-Flash-Vision-Exp'); + assert.equal( + model?.description, + 'Experimental DeepSeek V4 Flash model for image understanding and multimodal agent tasks', + ); + assert.equal(model?.docsUrl, 'https://api-docs.deepseek.com/guides/vision/'); + assert.equal(model?.contextWindow, 1_000_000); + assert.equal(model?.maxOutputTokens, 384_000); + assert.equal(model?.structuredOutput, true); + assert.equal(model?.lastUpdated, '2026-08-21'); + assert.deepEqual(model?.capabilities, { + reasoning: true, + functionCalling: true, + vision: true, + }); + assert.deepEqual(model?.modalities, { input: ['text', 'image'], output: ['text'] }); + assert.equal(model?.canUseAsChatDefault, true); +}); diff --git a/packages/core/src/__tests__/model-metadata.test.ts b/packages/core/src/__tests__/model-metadata.test.ts index 9eb6090bf8..32f543b784 100644 --- a/packages/core/src/__tests__/model-metadata.test.ts +++ b/packages/core/src/__tests__/model-metadata.test.ts @@ -20,8 +20,10 @@ import { strict as assert } from 'node:assert'; import { describe, it } from 'node:test'; import { + curatedCatalogFallbackModelsForProvider, lookupModelMetadata, openAiAdapterApiProtocol, + resolveModelInputModalities, resolveModelVisionSupport, } from '../model-metadata.js'; import type { ModelInfo, ProviderType } from '../llm-connections.js'; @@ -105,3 +107,68 @@ describe('openAiAdapterApiProtocol', () => { assert.equal(openAiAdapterApiProtocol('minimax-m3', 'opencode-go'), 'openai-chat'); }); }); + +describe('deepseek v4 flash vision exp metadata regression', () => { + it('resolves the bare model id with vision support', () => { + assert.equal( + resolveModelVisionSupport('deepseek', undefined, 'deepseek-v4-flash-vision-exp'), + true, + ); + }); + + it('accepts both text and image input modalities', () => { + const input = resolveModelInputModalities( + 'deepseek', + undefined, + 'deepseek-v4-flash-vision-exp', + ); + assert.ok(input.includes('text')); + assert.ok(input.includes('image')); + }); + + it('keeps the model present in the deepseek fallback catalog', () => { + assert.ok( + curatedCatalogFallbackModelsForProvider('deepseek')?.includes('deepseek-v4-flash-vision-exp'), + ); + }); + + it('returns expected metadata from lookupModelMetadata', () => { + const modelId = 'deepseek-v4-flash-vision-exp'; + const metadata = lookupModelMetadata('deepseek', modelId); + + assert.equal(metadata.displayName, 'DeepSeek-V4-Flash-Vision-Exp'); + assert.equal( + metadata.description, + 'Experimental DeepSeek V4 Flash model for image understanding and multimodal agent tasks', + ); + assert.equal(metadata.docsUrl, 'https://api-docs.deepseek.com/guides/vision/'); + assert.equal(metadata.contextWindow, 1_000_000); + assert.equal(metadata.maxOutputTokens, 384_000); + assert.equal(metadata.structuredOutput, true); + assert.equal(metadata.lastUpdated, '2026-08-21'); + assert.deepEqual(metadata.thinkingOptions, { + efforts: ['low', 'high', 'max'], + toggle: true, + }); + assert.equal(metadata.capabilities?.vision, true); + assert.deepEqual(metadata.modalities, { input: ['text', 'image'], output: ['text'] }); + }); + + it('is recognized from a bare discovered id', () => { + const modelId = 'deepseek-v4-flash-vision-exp'; + const discovered: ModelInfo[] = [{ id: modelId }]; + const metadata = lookupModelMetadata('deepseek', modelId); + + assert.equal(metadata.displayName, 'DeepSeek-V4-Flash-Vision-Exp'); + assert.equal(metadata.capabilities?.vision, true); + assert.deepEqual(resolveModelInputModalities('deepseek', discovered, modelId), [ + 'text', + 'image', + ]); + assert.equal(resolveModelVisionSupport('deepseek', discovered, modelId), true); + assert.equal( + resolveModelVisionSupport('deepseek', [{ id: 'deepseek-v4-flash' }], 'deepseek-v4-flash'), + false, + ); + }); +}); diff --git a/packages/core/src/model-metadata.ts b/packages/core/src/model-metadata.ts index 5949b79fad..79afdd0da8 100644 --- a/packages/core/src/model-metadata.ts +++ b/packages/core/src/model-metadata.ts @@ -435,6 +435,19 @@ const STATIC_MODEL_METADATA: Partial