diff --git a/src/main/claude/pi-model-resolution.ts b/src/main/claude/pi-model-resolution.ts index ca6259996..f1b7ffcf3 100644 --- a/src/main/claude/pi-model-resolution.ts +++ b/src/main/claude/pi-model-resolution.ts @@ -139,7 +139,15 @@ export function buildSyntheticPiModel( provider, baseUrl: baseUrl || '', reasoning: autoReasoning, - input: ['text', 'image'], + // Default unknown/synthetic models to text-only input. We cannot know whether + // an arbitrary model supports image input, and falsely claiming vision support + // causes hard request failures rather than gracefully dropping images — e.g. + // Ollama returns HTTP 400 "this model does not support image input" for a + // text-only model like deepseek-v4-pro, which surfaces to the user as an + // opaque "invalid message format" error. Vision-capable models resolved from + // the pi-ai registry keep their real input modalities; only synthetic + // fallbacks are affected here. + input: ['text'], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: contextWindow || knownSpecs?.contextWindow || 128000, maxTokens: maxTokens || knownSpecs?.maxTokens || 16384, diff --git a/tests/synthetic-model-input.test.ts b/tests/synthetic-model-input.test.ts new file mode 100644 index 000000000..c9c066d62 --- /dev/null +++ b/tests/synthetic-model-input.test.ts @@ -0,0 +1,29 @@ +import { describe, it, expect } from 'vitest'; +import { buildSyntheticPiModel } from '../src/main/claude/pi-model-resolution'; + +/** + * Regression guard for the text-only-model image bug. + * + * Synthetic models (built for ids not in the pi-ai registry) must NOT claim + * image input. Falsely advertising vision support means image content — e.g. + * screenshots from the GUI/computer-use tools — is sent to text-only endpoints, + * which hard-fail instead of gracefully dropping the images. For example Ollama + * returns HTTP 400 "this model does not support image input" for a text-only + * model like deepseek-v4-pro, which the app surfaces as an opaque + * "invalid message format" error. + */ +describe('buildSyntheticPiModel input modalities', () => { + it('defaults an unknown synthetic model to text-only input', () => { + const m = buildSyntheticPiModel('deepseek-v4-pro', 'openai', 'openai', 'https://ollama.com/v1'); + expect(m.input).toEqual(['text']); + expect(m.input).not.toContain('image'); + }); + + it('still resolves other core model fields normally', () => { + const m = buildSyntheticPiModel('some-custom-model', 'openai', 'openai', 'https://example.com/v1'); + expect(m.id).toBe('some-custom-model'); + expect(m.api).toBeTruthy(); + expect(m.contextWindow).toBeGreaterThan(0); + expect(m.maxTokens).toBeGreaterThan(0); + }); +});