Skip to content

feat(vision): add chat and Google sidecars - #1161

Draft
waw4303 wants to merge 1 commit into
lidge-jun:devfrom
waw4303:pr/vision-chat-sidecar
Draft

feat(vision): add chat and Google sidecars#1161
waw4303 wants to merge 1 commit into
lidge-jun:devfrom
waw4303:pr/vision-chat-sidecar

Conversation

@waw4303

@waw4303 waw4303 commented Aug 6, 2026

Copy link
Copy Markdown

Problem

Text-only routed models such as DeepSeek need image descriptions before they can reason about an attached image. The existing vision sidecar only supported OpenAI and Anthropic backends, so users could not use an OpenAI-compatible provider such as Mimo or the existing Google Antigravity provider.

Changes

  • Add a chat vision sidecar backend for OpenAI-compatible providers.
  • Send images as standard image_url content parts, matching the Hermes auxiliary vision flow.
  • Reuse the existing Google adapter for Google Antigravity OAuth and Cloud Code Assist envelopes.
  • Resolve namespaced sidecar models to their configured provider.
  • Add Mimo and Google Gemini choices to the vision sidecar GUI.
  • Keep web search limited to its existing OpenAI and Anthropic server-side tool backends.
  • Add focused executor tests for OpenAI-compatible and Google wire formats.

Compatibility

  • Existing OpenAI and Anthropic vision sidecars are unchanged.
  • The new backend is opt-in through the vision sidecar setting.
  • Web search behavior is unchanged.
  • Existing GUI model and backend selections continue to work.

Verification

  • bun x tsc --noEmit
  • bun test ./tests/vision-chat.test.ts ./tests/vision-sidecar-e2e.test.ts ./tests/vision-anthropic.test.ts ./tests/vision-fail-closed.test.ts ./tests/vision-cache.test.ts — 31 passed
  • npm run build in gui — passed
  • bun test ./gui/tests/dashboard-tabs.test.ts ./gui/tests/dashboard-contracts.test.ts ./gui/tests/dashboard-sync-feedback.test.tsx — 28 passed

The live local verification also exercised DeepSeek image requests through Mimo and Gemini Antigravity.

Summary by CodeRabbit

  • New Features
    • Added Chat API as a vision backend for image descriptions.
    • Added support for compatible chat and Google providers, including streaming responses.
    • Added dashboard controls to select the vision backend and model.
    • Added chat support to Claude Code configuration and command-line options.
  • Localization
    • Added Chat API labels across English, German, Japanese, Korean, Russian, and Chinese interfaces.
  • Bug Fixes
    • Improved model selection when switching vision backends, preserving compatible choices where possible.

Review readiness checklist

This PR stays in draft until every box below is ticked. Tick all four boxes once the requirements are met:

  • All CI tests are green on my local testing.

  • I pushed my PR to the latest dev commit.

  • I resolved all correct Codex and CodeRabbit findings.

  • My PR is ready for review.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds chat as a vision sidecar backend. It supports Google and OpenAI-compatible providers, streamed image descriptions, configuration persistence, dashboard selection, localized labels, and integration tests.

Changes

Chat vision support

Layer / File(s) Summary
Backend contracts and configuration
src/types.ts, src/cli/*, src/server/management/*
Backend types, CLI usage, validation, and persistence now accept chat for vision sidecars.
Chat vision description execution
src/vision/describe-chat.ts
Adds Google and OpenAI-compatible streaming requests with authentication, timeouts, abort handling, response parsing, error redaction, and outcome recording.
Provider selection and execution
src/vision/index.ts, tests/vision-chat.test.ts
Vision planning discovers authenticated chat providers and models. Chat plans invoke the new description flow. Tests cover Google and OpenAI-compatible requests.
Dashboard selection and localization
gui/src/pages/*, gui/src/i18n/*
The dashboard adds chat backend selection, dedicated vision model options, compatible model retention, and localized Chat API labels.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant VisionPlanner
  participant describeImageChat
  participant ProviderAPI
  VisionPlanner->>describeImageChat: Send image, context, provider, and model
  describeImageChat->>ProviderAPI: Request streamed image description
  ProviderAPI-->>describeImageChat: Return streamed response events
  describeImageChat-->>VisionPlanner: Return description outcome
Loading

Possibly related PRs

Suggested labels: enhancement

Suggested reviewers: lidge-jun, wibias, ingwannu

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 23.81% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding chat and Google vision sidecar backends.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@waw4303
waw4303 marked this pull request as ready for review August 6, 2026 23:20

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@gui/src/pages/dashboard-shared.ts`:
- Around line 185-189: The unresolved model fallback in sidecarBackendForModel
must return a distinct unresolved result rather than "chat"; update
gui/src/pages/dashboard-shared.ts lines 185-189 accordingly. In
gui/src/pages/dashboard-overview-sections.tsx lines 330-337, retain the current
model only when it resolves to an enabled model whose backend matches the
selected backend; otherwise select a discovered namespaced model consistent with
the management API response.

In `@src/cli/agent.ts`:
- Line 23: Update the sidecar usage text in the agent CLI help to document
backend choices by status: show openai|anthropic for web and
openai|anthropic|chat for vision, while preserving the existing model and status
syntax.

In `@src/vision/describe-chat.ts`:
- Around line 34-40: Require HTTPS through one shared guard before either OAuth
flow obtains or sends a bearer token: update the Google OAuth path in
src/vision/describe-chat.ts:34-40 and the generic chat OAuth path in
src/vision/describe-chat.ts:127-130 to return a non-sensitive error for
non-HTTPS URLs before token retrieval or Authorization construction. Add
coverage in tests/vision-chat.test.ts:31-81 for Google and OpenAI-compatible
OAuth, asserting HTTP URLs do not call fetch.

In `@src/vision/index.ts`:
- Around line 180-220: Add focused regression tests near the existing
vision/chat tests for planVisionSidecar: verify a provider-qualified model
selects the named chat/google provider with the expected model, and verify
disabled or unauthenticated providers produce no chat plan. Reuse existing test
fixtures and assertions without changing describeImageChat request-formatting
coverage.

In `@tests/vision-chat.test.ts`:
- Around line 31-81: Add regression coverage in the existing chat vision tests
for OAuth-configured Google and OpenAI-compatible providers using an http:
baseUrl. For each case, assert describeImageChat returns an error and verify the
fetch mock was not called, while preserving the existing successful HTTPS
adapter tests.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 40d779d5-757f-4c69-ab7a-b731bf456a7f

📥 Commits

Reviewing files that changed from the base of the PR and between b39eecf and 97d0c7d.

📒 Files selected for processing (19)
  • gui/src/i18n/de.ts
  • gui/src/i18n/en.ts
  • gui/src/i18n/ja.ts
  • gui/src/i18n/ko.ts
  • gui/src/i18n/ru.ts
  • gui/src/i18n/zh.ts
  • gui/src/pages/claude-code-sections.tsx
  • gui/src/pages/claude-manual-env.ts
  • gui/src/pages/dashboard-overview-sections.tsx
  • gui/src/pages/dashboard-shared.ts
  • gui/src/pages/use-dashboard-data.ts
  • src/cli/agent.ts
  • src/cli/integrations.ts
  • src/server/management/agent-settings-routes.ts
  • src/server/management/config-routes.ts
  • src/types.ts
  • src/vision/describe-chat.ts
  • src/vision/index.ts
  • tests/vision-chat.test.ts

Comment on lines 185 to +189
export function sidecarBackendForModel(models: ModelInfo[], modelId: string): SidecarBackend {
return models.find(model => model.id === modelId)?.provider === "anthropic" ? "anthropic" : "openai";
const model = models.find(item => item.id === modelId || item.namespaced === modelId);
if (model?.provider === "anthropic") return "anthropic";
if (model?.provider === "openai") return "openai";
return "chat";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Do not classify an unresolved model ID as the chat backend.

A missing configured vision model is retained in gui/src/pages/use-dashboard-data.ts. The current helper maps that unresolved ID to chat. Selecting Chat API then preserves the stale ID instead of selecting a discovered, namespaced Chat API model. The backend cannot resolve that stale model to a configured provider.

  • gui/src/pages/dashboard-shared.ts#L185-L189: return a distinct unresolved result instead of using chat as the fallback.
  • gui/src/pages/dashboard-overview-sections.tsx#L330-L337: retain the current model only when it resolves to an enabled model with the selected backend.

As per path instructions, GUI state changes must stay consistent with management API responses.

📍 Affects 2 files
  • gui/src/pages/dashboard-shared.ts#L185-L189 (this comment)
  • gui/src/pages/dashboard-overview-sections.tsx#L330-L337
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@gui/src/pages/dashboard-shared.ts` around lines 185 - 189, The unresolved
model fallback in sidecarBackendForModel must return a distinct unresolved
result rather than "chat"; update gui/src/pages/dashboard-shared.ts lines
185-189 accordingly. In gui/src/pages/dashboard-overview-sections.tsx lines
330-337, retain the current model only when it resolves to an enabled model
whose backend matches the selected backend; otherwise select a discovered
namespaced model consistent with the management API response.

Source: Path instructions

Comment thread src/cli/agent.ts
ocx agent subagents <status|set|clear> [model,model...] [--json]
ocx agent fallback <status|set|clear> [model,model...] [--poll-ms <5000-600000>] [--json]
ocx agent sidecar <status|web|vision> [--model <id|->] [--backend <openai|anthropic|->]
ocx agent sidecar <status|web|vision> [--model <id|->] [--backend <openai|anthropic|chat|->]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Separate the web and vision backend choices in the usage text.

Line 23 advertises chat for ocx agent sidecar web. The management API rejects webSearch.backend: "chat". Document openai|anthropic for web and openai|anthropic|chat for vision.

Proposed fix
-  ocx agent sidecar <status|web|vision> [--model <id|->] [--backend <openai|anthropic|chat|->]
+  ocx agent sidecar web [--model <id|->] [--backend <openai|anthropic|->]
+  ocx agent sidecar vision [--model <id|->] [--backend <openai|anthropic|chat|->]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
ocx agent sidecar <status|web|vision> [--model <id|->] [--backend <openai|anthropic|chat|->]
ocx agent sidecar web [--model <id|->] [--backend <openai|anthropic|->]
ocx agent sidecar vision [--model <id|->] [--backend <openai|anthropic|chat|->]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cli/agent.ts` at line 23, Update the sidecar usage text in the agent CLI
help to document backend choices by status: show openai|anthropic for web and
openai|anthropic|chat for vision, while preserving the existing model and status
syntax.

Comment on lines +34 to +40
if (provider.authMode === "oauth") {
try {
const token = await getValidAccessToken(providerName);
const project = provider.googleMode === "cloud-code-assist"
? getOAuthCredentialProjectId(providerName)
: provider.project;
requestProvider = { ...provider, apiKey: token, ...(project ? { project } : {}) };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Require HTTPS before obtaining or sending an OAuth bearer token.

The provider URL contract permits http: for operator-controlled providers. Both OAuth paths can send bearer credentials to that URL. Add one shared HTTPS guard before the Google dispatch at Line 114. Return a non-sensitive error when the URL is not HTTPS.

  • src/vision/describe-chat.ts#L34-L40: route Google OAuth through the shared HTTPS guard before token retrieval.
  • src/vision/describe-chat.ts#L127-L130: route generic chat OAuth through the shared HTTPS guard before building Authorization.
  • tests/vision-chat.test.ts#L31-L81: add Google and OpenAI-compatible OAuth tests that assert an HTTP URL does not call fetch.

Based on learnings: OAuth adapters that attach Bearer credentials must enforce HTTPS when cleartext transmission is unacceptable.

📍 Affects 2 files
  • src/vision/describe-chat.ts#L34-L40 (this comment)
  • src/vision/describe-chat.ts#L127-L130
  • tests/vision-chat.test.ts#L31-L81
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/vision/describe-chat.ts` around lines 34 - 40, Require HTTPS through one
shared guard before either OAuth flow obtains or sends a bearer token: update
the Google OAuth path in src/vision/describe-chat.ts:34-40 and the generic chat
OAuth path in src/vision/describe-chat.ts:127-130 to return a non-sensitive
error for non-HTTPS URLs before token retrieval or Authorization construction.
Add coverage in tests/vision-chat.test.ts:31-81 for Google and OpenAI-compatible
OAuth, asserting HTTP URLs do not call fetch.

Sources: Path instructions, Learnings

Comment thread src/vision/index.ts
Comment on lines +180 to +220
/** Find the chat-adapter or google-adapter provider for `model` with usable auth. */
function findChatVisionProvider(config: OcxConfig, model: string): { provider: OcxProviderConfig; providerName: string; model: string } | undefined {
let providerName = "";
let bareModel = model;
if (model.includes("/")) {
const sep = model.indexOf("/");
providerName = model.slice(0, sep);
bareModel = model.slice(sep + 1);
}
const isChatLike = (p: OcxProviderConfig) => p.adapter === "openai-chat" || p.adapter === "google";
const hasAuth = (p: OcxProviderConfig) => {
if (p.apiKey ?? p.apiKeyPool?.[0]?.key) return true;
if (p.authMode === "oauth") return true;
return false;
};
if (providerName) {
const provider = config.providers[providerName];
if (provider && provider.disabled !== true && isChatLike(provider) && hasAuth(provider)) {
return { provider, providerName, model: bareModel };
}
return undefined;
}
for (const [name, provider] of Object.entries(config.providers)) {
if (provider.disabled === true) continue;
if (!isChatLike(provider)) continue;
if (!hasAuth(provider)) continue;
const models = provider.models ?? [];
const defaultModel = provider.defaultModel;
if (bareModel === defaultModel || models.includes(bareModel) || models.some(m => m.endsWith("/" + bareModel) || m === bareModel)) {
return { provider, providerName: name, model: bareModel };
}
}
for (const [name, provider] of Object.entries(config.providers)) {
if (provider.disabled === true) continue;
if (!isChatLike(provider)) continue;
if (!hasAuth(provider)) continue;
if (provider.liveModels === true) {
return { provider, providerName: name, model: bareModel };
}
}
return undefined;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add plan-selection regression tests for the chat backend.

The added tests cover describeImageChat request formatting only. Add tests for planVisionSidecar that verify provider-qualified models resolve to the selected provider and that disabled or unauthenticated providers produce no chat plan.

As per path instructions, “A behavior change in src/ should come with a focused regression test near the existing tests for that subsystem.”

Also applies to: 288-297

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/vision/index.ts` around lines 180 - 220, Add focused regression tests
near the existing vision/chat tests for planVisionSidecar: verify a
provider-qualified model selects the named chat/google provider with the
expected model, and verify disabled or unauthenticated providers produce no chat
plan. Reuse existing test fixtures and assertions without changing
describeImageChat request-formatting coverage.

Source: Path instructions

Comment thread tests/vision-chat.test.ts
Comment on lines +31 to +81
describe("chat vision sidecar", () => {
test("sends an image_url through an OpenAI-compatible provider", async () => {
let capturedUrl = "";
let capturedBody: Record<string, any> | undefined;
globalThis.fetch = (async (url, init) => {
capturedUrl = String(url);
capturedBody = JSON.parse(String(init?.body));
return chatSse("Mimo description");
}) as typeof fetch;
const provider: OcxProviderConfig = {
adapter: "openai-chat",
baseUrl: "https://vision.example/v1",
authMode: "key",
apiKey: "test-key",
};

const result = await describeImageChat(image, "high", "describe this", provider, "mimo", settings);

expect(result).toEqual({ text: "Mimo description" });
expect(capturedUrl).toBe("https://vision.example/v1/chat/completions");
expect(capturedBody?.model).toBe("vision-test");
expect(capturedBody?.messages[0].content).toEqual([
{ type: "text", text: "describe this" },
{ type: "image_url", image_url: { url: image, detail: "high" } },
]);
});

test("uses the native Google adapter wire format", async () => {
let capturedUrl = "";
let capturedBody: Record<string, any> | undefined;
globalThis.fetch = (async (url, init) => {
capturedUrl = String(url);
capturedBody = JSON.parse(String(init?.body));
return geminiSse("Gemini description");
}) as typeof fetch;
const provider: OcxProviderConfig = {
adapter: "google",
baseUrl: "https://generativelanguage.googleapis.com",
authMode: "key",
apiKey: "test-key",
};

const result = await describeImageChat(image, "high", "describe this", provider, "gemini", { model: "gemini-test", timeoutMs: 5000 });

expect(result).toEqual({ text: "Gemini description" });
expect(capturedUrl).toBe("https://generativelanguage.googleapis.com/v1beta/models/gemini-test:streamGenerateContent?alt=sse");
expect(capturedBody?.contents?.[0]?.parts).toEqual([
{ text: "describe this" },
{ inline_data: { mime_type: "image/png", data: "aGVsbG8=" } },
]);
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Add a regression test for OAuth transport enforcement.

After adding the HTTPS guard, test both Google and OpenAI-compatible OAuth providers with an http: base URL. Assert that describeImageChat returns an error and does not call fetch.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/vision-chat.test.ts` around lines 31 - 81, Add regression coverage in
the existing chat vision tests for OAuth-configured Google and OpenAI-compatible
providers using an http: baseUrl. For each case, assert describeImageChat
returns an error and verify the fetch mock was not called, while preserving the
existing successful HTTPS adapter tests.

Sources: Path instructions, Learnings

@github-actions github-actions Bot added the enhancement New feature or request label Aug 6, 2026
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

⏳ DRAFT

  • UI screenshot required.

What to do

  • Add a screenshot of the UI change to the PR description.
  • Tick all four boxes in the PR description once you're done (currently 0/4).

Review readiness checklist

  • ⬜ All CI tests are green on my local testing.
  • ⬜ I pushed my PR to the latest dev commit.
  • ⬜ I resolved all correct Codex and CodeRabbit findings.
  • ⬜ My PR is ready for review.

0/4 boxes ticked.

This pull request was already a draft. Its draft status will be preserved after every issue above is resolved.
@waw4303 Tick the boxes once your local CI is green, your branch is on the latest dev commit, and every correct Codex and CodeRabbit finding is resolved.

@github-actions
github-actions Bot marked this pull request as draft August 6, 2026 23:25
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Deterministic PR hygiene checks passed.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 97d0c7dbf9

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

const project = provider.googleMode === "cloud-code-assist"
? getOAuthCredentialProjectId(providerName)
: provider.project;
requestProvider = { ...provider, apiKey: token, ...(project ? { project } : {}) };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Pin OAuth sidecar transport before adding token

When the new Google vision sidecar is used with an OAuth provider such as google-antigravity, this line attaches the access token to the raw configured provider. Unlike the existing main routing/image fallback paths, no registry transport pinning happens first, so a stale or tampered baseUrl in config causes createGoogleAdapter to send the bearer token to that host. Resolve or pin the provider transport before adding apiKey.

AGENTS.md reference: AGENTS.md:L218-L223

Useful? React with 👍 / 👎.

method: request.method,
headers: request.headers,
body: request.body,
signal: abortSignal,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Apply the vision timeout to Google streams

For Google AI Studio, adapter.fetchResponse is absent, so this fallback fetch uses only the caller's abort signal and never applies settings.timeoutMs; the subsequent parseStream also runs without a timeout. If the sidecar opens a stream and then stalls, the main text-only request waits indefinitely instead of degrading after the configured vision timeout. Use the same linked timeout signal/body-cancel pattern as the other vision sidecars.

AGENTS.md reference: src/AGENTS.md:L17-L17

Useful? React with 👍 / 👎.

Comment on lines +333 to +334
const compatible = target === null
? visionSidecarModels.find(option => sidecarBackendForModel(models, option.value) === "openai")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Do not save an OpenAI model for Auto vision

When users switch the vision backend to Auto on a setup with an Anthropic OAuth sidecar, this branch clears backend but persists the first OpenAI model. Runtime auto resolution still prefers Anthropic when available, so the next image request asks the Anthropic sidecar to run an OpenAI model id and the description fails. Auto should preserve a backend-compatible model or clear model so the runtime can use its backend default.

AGENTS.md reference: gui/AGENTS.md:L9-L10

Useful? React with 👍 / 👎.

return { text: "", error: `oauth token failed: ${e instanceof Error ? e.message : String(e)}` };
}
} else {
const apiKey = provider.apiKey ?? provider.apiKeyPool?.[0]?.key ?? provider.headers?.Authorization?.replace(/^Bearer\s+/i, "");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Resolve env-backed keys before chat sidecar calls

When a chat vision sidecar uses a key provider configured with an env reference such as apiKey = "$MIMO_API_KEY", this reads the raw config value and builds Authorization: Bearer $MIMO_API_KEY, while the normal routing path resolves env-backed keys first. That makes env-based provider configs fail only for the new sidecar; resolve the value before composing the header.

AGENTS.md reference: src/AGENTS.md:L10-L10

Useful? React with 👍 / 👎.

Comment thread src/vision/index.ts
Comment on lines +190 to +193
const hasAuth = (p: OcxProviderConfig) => {
if (p.apiKey ?? p.apiKeyPool?.[0]?.key) return true;
if (p.authMode === "oauth") return true;
return false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Allow keyless local chat vision providers

With the new vision model picker, users can select OpenAI-compatible local vision models such as ollama/llava, but this auth check rejects every keyless authMode: "local" or keyOptional provider before a plan is built. Those requests strip the image as unavailable instead of calling the local endpoint; treat keyless providers as usable and omit Authorization in the executor.

Useful? React with 👍 / 👎.

Comment thread src/vision/index.ts
if (providerName) {
const provider = config.providers[providerName];
if (provider && provider.disabled !== true && isChatLike(provider) && hasAuth(provider)) {
return { provider, providerName, model: bareModel };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Decode routed slugs for chat sidecar models

When users choose a chat sidecar model whose native id contains / (for example OpenRouter models), the dashboard persists the Codex-facing slug provider/<encoded-id>. This branch strips the provider and returns the encoded slug without decoding it against provider.models, so the sidecar sends anthropic-claude... upstream instead of anthropic/claude... and the request fails. Decode the routed slug before storing it in the plan.

Useful? React with 👍 / 👎.

if (model.provider === "openai" || model.provider === "anthropic") {
out.push({ value: model.id, label: `${model.provider}/${model.id}` });
} else {
out.push({ value: model.namespaced, label: model.namespaced });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Filter the vision picker to usable chat providers

This branch adds every non-OpenAI/Anthropic row to the vision picker and sidecarBackendForModel classifies it as chat, but the runtime chat sidecar only accepts providers whose adapter is openai-chat or google. Selecting rows such as anthropic-apikey/..., combos, or other adapters saves a model the backend can never plan, so text-only requests strip the image as unavailable. Source this list from runtime-eligible providers or include adapter capability data.

AGENTS.md reference: gui/AGENTS.md:L9-L10

Useful? React with 👍 / 👎.


const content: unknown[] = [
{ type: "text", text: contextText || "Describe this image." },
toChatImagePart(imageUrl, detail),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate image URLs before chat sidecar forwarding

The new chat backend forwards imageUrl directly as an image_url part without the validation used by the existing vision sidecars. If a request supplies an unsupported scheme or an oversized data URL, the sidecar now passes it to the configured OpenAI-compatible provider instead of producing the bounded local error marker, which can break local or strict providers and bypass the existing image-size guard. Validate before building this part.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant