Skip to content

feat(ai): add image and decision entrypoints for catalog providers - #59380

Merged
rekram1-node merged 6 commits into
devfrom
media-entrypoints
Oct 26, 2026
Merged

rekram1-node merged 6 commits into
devfrom
media-entrypoints

Conversation

@rekram1-node

@rekram1-node rekram1-node commented Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

Image and decision models in the models.dev catalog point their opencodeai package at protocol packages (protocols/openai-images, protocols/system-one, ...) or at a bare provider root. Protocols don't know the host or auth, and the provider roots only expose image/decision factories on an unconfigured default, so there's no model(modelID, settings) core can call for them.

Change

I added provider-plus-API entrypoints, the same shape as the chat ones:

@opencode/ai/providers/<P>/images
@opencode/ai/providers/<P>/decisions

Each one is a two-line file that re-exports a configured imageModel / decisionModel from the provider facade. Those take the same Settings as the provider's chat model() and reuse its config, so auth, base URL, headers and body match chat. Leftover settings become the model's default provider options, like chat, so a variant setting such as { thinkingLevel: "HIGH" } ends up in the request:

 export const chatModel = (modelID, settings) => configure(config(settings)).chat(modelID)
+export const imageModel = (modelID: string, settings: Settings) => {
+  const input = config(settings)
+  return configure(input).image(modelID, input.providerOptions)
+}

To make that work, media and decision models now carry optional default providerOptions, merged under each request's own:

MediaRoute submit
  providerOptions = merge(model.providerOptions, request.providerOptions)
  body.from(request with merged providerOptions)

New code was only needed where a facade had no factory yet: images on openai-compatible, openrouter, digitalocean, azure and google-vertex, and a settings-based decisionModel on typesafe-ai and opencode.

<P> is the package the same models.dev provider uses for its chat models (openai-compatible when it has none). The exception: if the model already pointed at a provider package that knows its wire format, I kept it. Decision APIs come in three formats (OpenAI decisions, System One, Cloudflare run), so the System One hosts (amd, greenpt, melious, nano-gpt, ollama-cloud, vivgrid) go to typesafe-ai/decisions and opencode Zen to opencode/decisions.

entrypoint catalog models
openrouter/images, openrouter/decisions 61, 30
vercel-ai-gateway/images, vercel-ai-gateway/decisions 37, 14
openai-compatible/images 33
alibaba/images 16
typesafe-ai/decisions 14
google/images 7 (14 API entries)
cloudflare-workers-ai/images, cloudflare-workers-ai/decisions 9, 3
openai/images 7
azure/images 5
google-vertex/images 5
digitalocean/images, digitalocean/decisions 4, 1
xai/images 3
zai/images 2
baseten/decisions 2
cloudflare-ai-gateway/decisions 2
opencode/decisions 2
models.dev provider → old → new
models.dev provider type API key old opencodeai new entrypoint models
aihubmix image images protocols/openai-images providers/openai-compatible/images 8
alibaba image images protocols/alibaba-images providers/alibaba/images 4
alibaba-cn image images protocols/alibaba-images providers/alibaba/images 4
alibaba-token-plan image images protocols/alibaba-images providers/alibaba/images 4
alibaba-token-plan-cn image images protocols/alibaba-images providers/alibaba/images 4
amd decision systemone providers/typesafe-ai providers/typesafe-ai/decisions 3
azure image images protocols/openai-images providers/azure/images 5
baseten decision systemone providers/baseten providers/baseten/decisions 2
cloudflare-ai-gateway decision workers-ai-run protocols/cloudflare-run-decisions providers/cloudflare-ai-gateway/decisions 2
cloudflare-workers-ai decision workers-ai-run protocols/cloudflare-run-decisions providers/cloudflare-workers-ai/decisions 3
cloudflare-workers-ai image workers-ai-run protocols/cloudflare-workers-ai-images providers/cloudflare-workers-ai/images 9
digitalocean decision systemone protocols/system-one providers/digitalocean/decisions 1
digitalocean image images protocols/openai-images providers/digitalocean/images 4
edenai image images protocols/openai-images providers/openai-compatible/images 12
fastrouter image images protocols/openai-images providers/openai-compatible/images 3
google image images protocols/google-images providers/google/images 7
google image generate-content providers/google providers/google/images 7
google-vertex image images protocols/google-images providers/google-vertex/images 5
greenpt decision systemone providers/typesafe-ai providers/typesafe-ai/decisions 1
kenari image images protocols/openai-images providers/openai-compatible/images 6
melious decision systemone providers/typesafe-ai providers/typesafe-ai/decisions 2
nan image images protocols/openai-images providers/openai-compatible/images 1
nano-gpt decision systemone providers/typesafe-ai providers/typesafe-ai/decisions 6
nearai image images protocols/openai-images providers/openai-compatible/images 1
ollama-cloud decision systemone providers/typesafe-ai providers/typesafe-ai/decisions 1
openai image images protocols/openai-images providers/openai/images 7
opencode decision systemone providers/opencode providers/opencode/decisions 2
openrouter decision systemone providers/openrouter providers/openrouter/decisions 30
openrouter image images protocols/openai-images providers/openrouter/images 61
regolo-ai image images protocols/openai-images providers/openai-compatible/images 1
vercel decision decisions protocols/openai-decisions providers/vercel-ai-gateway/decisions 14
vercel image images protocols/openai-images providers/vercel-ai-gateway/images 37
vivgrid decision systemone providers/typesafe-ai providers/typesafe-ai/decisions 1
xai image images protocols/xai-images providers/xai/images 3
zai image images protocols/zai-images providers/zai/images 1
zenmux image images protocols/openai-images providers/openai-compatible/images 1
zhipuai image images protocols/zai-images providers/zai/images 1

Core changes, kept small:

  • the new entrypoints are in core's bundled package list, since provider.test.ts requires every @opencode/ai entrypoint that exports model() to be bundled
  • google/images and google-vertex/images get Gemini's reasoning variants: effort becomes thinkingConfig.thinkingLevel, which the Gemini image API reads from generationConfig. The other image and decision APIs have no reasoning setting, so they get no variants, and the "every bundled package has a spelling" test skips /images and /decisions entrypoints

Out of scope

  • Pointing the catalog at these entrypoints and running image and decision generation through core is a separate change.
  • The Cloudflare and Azure entrypoints build their own URL like their chat entrypoints, so they belong in core's BUILDS_OWN_URL list in that change.
  • Images made through a chat API (chat-completions / responses API keys) are unchanged.

Verification

  • bun test test/provider/media-entrypoints.test.ts in packages/ai: one case per entrypoint (20) checking the sent URL, auth header, model id, custom header and body
  • bun test in packages/ai: 1928 pass, 0 fail. bun typecheck: clean
  • bun run script/test.ts test/variant.test.ts test/provider.test.ts test/aisdk-native.test.ts test/model-resolver.test.ts test/models-dev.test.ts in packages/core: 92 pass
  • bun run check at the root: clean

Added by the opencode bot (AI-generated).

Public API: adds providers/<P>/images and providers/<P>/decisions entrypoints, imageModel/decisionModel facade exports and optional model-level providerOptions on media and decision models in @opencode/ai (backwards compatible)

@opencode-agent opencode-agent 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.

I found no problems: the new images/decisions entrypoints build working models from the same settings as each provider's chat model(), and the settings refactors leave the existing chat paths unchanged.

Core doesn't load these entrypoints yet, so I checked them at the package level. In packages/ai, media-entrypoints.test.ts passes (19/19), the full suite passes (1927 pass, 0 fail), and tsc --noEmit is clean with both the default config and tsconfig.types.json.

Public API: additions only to @opencode/ai, all backwards compatible:

  • 19 new subpath exports, providers/<P>/images and providers/<P>/decisions, for the providers in the description's table
  • new imageModel / decisionModel exports on those facades
  • new image factories on openrouter, digitalocean, azure, google-vertex and openai-compatible, and decision on openai-compatible

Nothing was removed or changed.

@opencode-agent opencode-agent 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.

The new commit adds the 19 new entrypoints to core's bundled package list and changes nothing else. I found no new concerns, and my earlier review raised none.

Every new @opencode/ai entrypoint is in the list. In packages/core, bun test test/provider.test.ts passes (4/4) and tsc -b tsconfig.json tsconfig.tests.json is clean. Registering the entrypoints alone doesn't change core's behaviour, since nothing in the catalog points at them yet.

Public API: this commit doesn't change it. The pull request as a whole only adds things to @opencode/ai: the providers/<P>/images and providers/<P>/decisions subpath exports and the imageModel/decisionModel facade exports. All of it is backwards compatible.

@opencode-agent opencode-agent 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.

The new commit is fine and I found no new concerns; my earlier reviews raised none.

It changes one test in packages/core/test/variant.test.ts: the check that every bundled package has reasoning variants now skips packages ending in /images or /decisions. That check fails as of the previous commit, which added those entrypoints to core's bundled list, and media models have no reasoning settings. variant.test.ts and provider.test.ts in packages/core now pass (21/21).

Public API: this commit doesn't change it. The pull request as a whole only adds things to @opencode/ai: the providers/<P>/images and providers/<P>/decisions subpath exports and the imageModel/decisionModel facade exports. All of it is backwards compatible.

@opencode-agent opencode-agent 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.

The new commit looks right and I found no new concerns; my earlier reviews raised none.

I checked how the new default providerOptions are merged when a request is sent. Decisions go through the same media send path, so they get the merge too. The new tests show a thinkingLevel setting reaches the Google image body and a request's own providerOptions override it. Moving System One decisions to typesafe-ai/decisions and opencode/decisions keeps each provider's own metadata key and defaults.

Results: full bun test in packages/ai passes (1930 pass, 0 fail). tsc is clean in packages/ai and packages/core. The five core test files you listed pass (93).

Public API, all in @opencode/ai:

  • removes providers/openai-compatible/decisions and openai-compatible's decision/decisionModel, which were only added earlier in this pull request and never released
  • adds providers/typesafe-ai/decisions and providers/opencode/decisions, with decisionModel and Settings exports on both facades
  • adds an optional providerOptions field on image, video, speech, transcription and decision models; the provider image(id) and decision(id) factories take it as an optional second argument

All of it is backwards compatible.

@opencode-agent opencode-agent 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.

This commit can send chat-only reasoning fields to image APIs that don't accept them. It maps each new image and decision entrypoint to its provider's chat reasoning spelling, and leftover settings are copied straight into the image request.

I confirmed it for openai-compatible/images: with the chat spelling { reasoningEffort: "high" }, the sent OpenAI Images body includes a raw "reasoningEffort":"high" field. The bundled catalog lists reasoning effort for 8 edenai Gemini image models on that API. The same risk applies to openrouter/images (reasoning: { effort }) and to openai/images, azure/images and xai/images (the Responses fields reasoningEffort, reasoningSummary and include). google/images is fine: the Gemini spelling produces a valid generationConfig.thinkingConfig.

The commit also removes the tests that checked settings reaching the image and decision request bodies. That merge still exists in the code, so it would be good to keep a small test for it.

Results: the full packages/ai suite passes (1928 pass, 0 fail), the five core test files you listed pass (92), and tsc is clean in packages/core. This commit doesn't change the public API.

Comment thread packages/core/src/variant.ts Outdated
"@opencode/ai/providers/xai/images": openResponses,

"@opencode/ai/providers/openai-compatible": openaiCompatible,
"@opencode/ai/providers/openai-compatible/images": openaiCompatible,

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.

This entrypoint now uses the chat spelling { reasoningEffort }, but OpenAIImages sends leftover options to the Images API unchanged. With reasoningEffort: "high", the body is {"model":"google/gemini-3.1-flash-image","prompt":"x","reasoningEffort":"high"}, and the catalog lists effort for 8 edenai Gemini image models on this API. openrouter/images, openai/images, azure/images and xai/images have the same problem with their chat or Responses fields. Could image entrypoints without a matching image-API spelling stay unmapped, as in the previous commit, or get a spelling written for their image API?

@opencode-agent opencode-agent 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.

This fixes the earlier variant concern. Only google/images and google-vertex/images now get reasoning variants, using Gemini's spelling. The other image and decision entrypoints are unmapped, and Variant.resolve gives no variants for unmapped packages, so chat reasoning fields no longer reach the Images bodies. The core test files you listed pass (92) and tsc is clean in packages/core. This commit doesn't change the public API.

One point from last round is still open but not blocking: the tests that checked settings reaching the image and decision request bodies are still gone, so nothing covers that merge.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant