Skip to content

feat(delegate): dispatch the vision call on model.api (openai-responses, google-generative-ai, anthropic-messages) - #27

Open
quantmind-br wants to merge 5 commits into
getpipher:mainfrom
quantmind-br:fix/api-aware-vision-call
Open

quantmind-br wants to merge 5 commits into
getpipher:mainfrom
quantmind-br:fix/api-aware-vision-call

Conversation

@quantmind-br

@quantmind-br quantmind-br commented Oct 14, 2026 •

Copy link
Copy Markdown

Problem

callVisionModel always POSTs {baseUrl}/chat/completions, whatever the model's registered api. A vision model registered as google-generative-ai, anthropic-messages or openai-responses therefore hits a route that does not exist on its gateway and fails with an opaque 404 (vision_call_error). autoDetectDefaults() and the /vision model picker made it worse: they offer every image-capable model, so a model on an API the call cannot speak can be auto-picked and only fails on the first image.

Fix

The per-API request/response shapes live in a new lib/wire.ts, and callVisionModel dispatches on model.api:

api Request Auth Reasoning
openai-completions POST {baseUrl}/chat/completions, unchanged Authorization: Bearer reasoning_effort
openai-responses POST {baseUrl}/responses, input_image / input_text parts, system prompt as instructions Authorization: Bearer nested reasoning.effort; temperature never sent
google-generative-ai POST {baseUrl}/models/{id}:generateContent, inlineData x-goog-api-key thinkingConfig.thinkingLevel, honours thinkingLevelMap
anthropic-messages POST {baseUrl}/v1/messages, base64 image block, top-level system x-api-key + anthropic-version extended-thinking budget; temperature never sent
  • Responses text is the output_text parts of message items. reasoning items are skipped. An incomplete response reports incomplete_details.reason (e.g. max_output_tokens).
  • Unsupported APIs (google-vertex, bedrock-converse-stream, openai-codex-responses, …) are rejected before the network call and before the image is read, with an actionable unsupported_api error. This applies to both the primary and the fallback model.
  • Empty responses report the block/finish reason instead of a bare "no content".
  • autoDetectDefaults() and /vision model only consider models on a supported API. The settings panel and /vision model warn when the configured model uses an unsupported one.
  • README gains a "Supported provider APIs" section.

Relation to open PRs

Testing

  • pnpm typecheck: clean
  • pnpm test:run: 385/385 pass (+5 vs. the previous commit)
    • tests/wire.test.ts: Responses request URL, headers, input / instructions / reasoning shape, no temperature; Responses text extraction incl. skipped reasoning/refusal parts and incomplete reason
    • tests/delegate.test.ts: end-to-end openai-responses primary → /responses; unsupported-API tests now use bedrock-converse-stream
    • tests/defaults.test.ts: openai-responses models stay eligible for auto-detect
  • Live smoke against the opencode gateway, openai-responses model gpt-5.6-luna, reasoning: low, with a system prompt: HTTP 200, description returned through callVisionModel.

callVisionModel always POSTed {baseUrl}/chat/completions, whatever the
model's api. A model registered as google-generative-ai or
anthropic-messages therefore hit a route that does not exist on its
gateway and failed with an opaque 404 (vision_call_error).

Move the per-API request/response shapes into lib/wire.ts and dispatch
on model.api:
- openai-completions: unchanged request
- google-generative-ai: {baseUrl}/models/{id}:generateContent, x-goog-api-key,
  inlineData, thinkingConfig.thinkingLevel (honours thinkingLevelMap)
- anthropic-messages: {baseUrl}/v1/messages, x-api-key, base64 image block,
  extended-thinking budget; temperature is never sent

Any other api is rejected before the network, and before the image is read,
with an actionable unsupported_api error (primary and fallback). An empty
response now reports the block/finish reason.
autoDetectDefaults picked the first vision-capable model of any provider,
sorted by name, so a provider such as an anthropic-messages or
openai-responses one could win and fail on the first image. Only consider
models whose api the vision call can speak.
…ones

The /vision model picker listed every image-capable model. Restrict it to
models on a supported api, and warn when /vision model or the settings
panel sets a registered model whose api the vision call cannot speak.
A model registered as openai-responses was rejected with unsupported_api,
so a Responses-only gateway could not be the vision model. Add the
Responses shape to lib/wire.ts next to the other three APIs:

- POST {baseUrl}/responses with input_image / input_text parts
- system prompt goes in `instructions`
- reasoning is the nested `reasoning.effort` (only for reasoning models,
  and never when the level is off)
- `temperature` is never sent: reasoning models reject it
- response text is the output_text parts of message items; reasoning
  items are skipped, and an incomplete response reports its reason

Auto-detect and the /vision model picker now include openai-responses
models. README documents the new row.
@quantmind-br quantmind-br changed the title fix(delegate): dispatch the vision call on model.api (google-generative-ai, anthropic-messages) feat(delegate): dispatch the vision call on model.api (openai-responses, google-generative-ai, anthropic-messages) Oct 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant