Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions packages/core/src/__tests__/model-catalog.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
67 changes: 67 additions & 0 deletions packages/core/src/__tests__/model-metadata.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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,
);
});
});
21 changes: 20 additions & 1 deletion packages/core/src/model-metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,19 @@ const STATIC_MODEL_METADATA: Partial<Record<ProviderType, Record<string, ModelMe
lastUpdated: '2026-08-24',
thinkingOptions: { efforts: ['low', 'high', 'max'], toggle: true },
},
'deepseek-v4-flash-vision-exp': {
capabilities: { vision: true, ...REASONING_FUNCTION_CALLING, webSearch: true },
thinkingOptions: { efforts: ['low', 'high', 'max'], toggle: true },
modalities: { input: ['text', 'image'], output: ['text'] },
displayName: 'DeepSeek-V4-Flash-Vision-Exp',
description:
'Experimental DeepSeek V4 Flash model for image understanding and multimodal agent tasks',
docsUrl: 'https://api-docs.deepseek.com/guides/vision/',
contextWindow: 1_000_000,
maxOutputTokens: 384_000,
structuredOutput: true,
lastUpdated: '2026-08-21',
},
'deepseek-v4-pro': {
capabilities: { ...REASONING_FUNCTION_CALLING, webSearch: true },
lastUpdated: '2026-08-13',
Expand Down Expand Up @@ -580,7 +593,13 @@ const CURATED_CATALOG_FALLBACK_MODELS: Partial<Record<ProviderType, readonly str
'claude-sonnet-4-5-20250929',
],
openai: ['gpt-5.5', 'gpt-5.5-pro', 'gpt-5.4', 'gpt-5.4-mini', 'gpt-5'],
deepseek: ['deepseek-v4-flash', 'deepseek-v4-pro', 'deepseek-reasoner', 'deepseek-chat'],
deepseek: [
'deepseek-v4-flash',
'deepseek-v4-flash-vision-exp',
'deepseek-v4-pro',
'deepseek-reasoner',
'deepseek-chat',
],
google: ['gemini-3.5-flash', 'gemini-3.1-pro-preview', 'gemini-2.5-pro', 'gemini-2.5-flash'],
'zai-coding-plan': ['glm-5.2', 'glm-5.1', 'glm-5-turbo', 'glm-4.7', 'glm-4.5-air'],
MiniMax: ['MiniMax-M3'],
Expand Down