Skip to content

feat(action): allow an explicit Azure endpoint via AZURE_BASE_URL - #71

Open
theoephraim wants to merge 1 commit into
pullfrog:mainfrom
dmno-dev:feat/azure-base-url-override
Open

feat(action): allow an explicit Azure endpoint via AZURE_BASE_URL#71
theoephraim wants to merge 1 commit into
pullfrog:mainfrom
dmno-dev:feat/azure-base-url-override

Conversation

@theoephraim

@theoephraim theoephraim commented Jul 28, 2026

Copy link
Copy Markdown

Problem

Azure models work today through the models.dev passthrough, but only for one endpoint shape. @ai-sdk/azure derives its URL from a resource name:

https://<AZURE_RESOURCE_NAME>.openai.azure.com/openai

That can't express an Azure AI Foundry / AI Services host, an API Management front door, or an AI gateway. Those deployments are simply unreachable — there's no config surface to point the provider anywhere else.

Change

AZURE_BASE_URL (plus AZURE_COGNITIVE_SERVICES_BASE_URL for the sibling provider), surfaced as provider.<id>.options.baseURL in the generated OpenCode config.

It lives in opencodeShared.ts and is spread into both the v1 and v2 buildSecurityConfig provider blocks, per that module's stated purpose of keeping the two runners from drifting. It returns {} when unset, so the emitted config is byte-identical for anyone not using it.

Verification

I traced this through OpenCode rather than assuming the schema key was wired:

  • getSDK spreads provider.options, calls loadBaseURL (options["baseURL"] ?? model.api.url), and passes the result to BUNDLED_PROVIDERS["@ai-sdk/azure"] === createAzure
  • config provider.<id>.options is merged over OpenCode's own via mergeDeep
  • per the ai-sdk source, resourceName is ignored and no longer required once baseURL is set

Then confirmed end to end against a local probe server, with AZURE_API_KEY set and provider.azure.options.baseURL pointed at it:

HIT POST /openai/v1/responses?api-version=v1

So the override reaches the SDK, and the Responses API is used (matching the azure custom loader's sdk.responses()). The base URL should carry the /openai suffix — the SDK appends /v1{path}.

One nice consequence, also verified empirically: OpenCode enables a provider when any of its models.dev env vars is present (provider.env.map(...).find(Boolean)). AZURE_API_KEY alone lists 66 azure/* models, zero with no azure var set. So a custom endpoint needs no dummy AZURE_RESOURCE_NAME to pass the authorization gate.

Tests

Six cases in agents/opencode.test.ts covering both providers, trimming, whitespace-only, and the empty-by-default contract. pnpm typecheck clean; utils/ + agents/ 347/347. Full suite not run locally (no GitHub App credentials).

Notes

Branched off main, independent of #69 and #70. If #69 lands, AZURE_BASE_URL and AZURE_RESOURCE_NAME are both worth adding to its non-secret allowlist so they stay readable in run logs — happy to do that in whichever merges second.


Note

Low Risk
Config-only change gated on optional env vars; default emitted config is unchanged when vars are absent.

Overview
Adds optional Azure endpoint overrides for OpenCode by mapping AZURE_BASE_URL and AZURE_COGNITIVE_SERVICES_BASE_URL into provider.<id>.options.baseURL in the generated security config, so deployments behind AI Foundry, API Management, or custom gateways are reachable without relying on resource-name URL derivation.

The helper azureBaseUrlOverrides() lives in opencodeShared.ts (trimmed values, empty when unset) and is spread into the provider block in both v1 and v2 OpenCode harnesses so behavior stays aligned. When a var is set, an info log records which provider was overridden.

Tests cover both env vars, independent dual mapping, trimming, whitespace-only treated as unset, and the default {} no-op contract.

Reviewed by Cursor Bugbot for commit ff33573. Bugbot is set up for automated code reviews on this repo. Configure here.

@ai-sdk/azure derives its endpoint from a resource name as
https://<AZURE_RESOURCE_NAME>.openai.azure.com/openai, which can't express
an AI Foundry / AI Services host, an API Management front door, or an AI
gateway. Those deployments are unreachable today.

Add AZURE_BASE_URL (and the cognitive-services equivalent), surfaced as
provider.<id>.options.baseURL in the generated OpenCode config. OpenCode
merges config provider options over its own and forwards options.baseURL
into the createAzure factory, and the SDK drops the resourceName
requirement once a base URL is set — so a custom endpoint needs no dummy
resource name.

Lives in opencodeShared.ts and is spread into both the v1 and v2 config
builders, so the two runners can't drift. Returns an empty object when
unset, so the config is byte-identical for everyone else.

@pullfrog pullfrog 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.

✅ No new issues found.

Reviewed changes — adds an optional explicit-endpoint override for the Azure OpenCode providers so deployments behind AI Foundry, API Management, or a gateway are reachable without relying on resource-name URL derivation.

  • Add azureBaseUrlOverrides() helper — new function in agents/opencodeShared.ts maps AZURE_BASE_URLprovider.azure.options.baseURL and AZURE_COGNITIVE_SERVICES_BASE_URLprovider.azure-cognitive-services.options.baseURL, trimming values and returning {} when unset.
  • Wire into both harnesses — spread into the provider block of buildSecurityConfig in both agents/opencode.ts (v1) and agents/opencode_v2.ts (v2), keeping the two runners aligned.
  • Tests — six cases in agents/opencode.test.ts covering each provider, independent dual mapping, whitespace trimming, whitespace-only-as-unset, and the empty-by-default contract.

Notes from review, all clean:

  • The ...azureBaseUrlOverrides() spread sits last in the provider literal but only ever emits azure / azure-cognitive-services keys, so it cannot clobber the existing google / openrouter entries.
  • Logging the resolved base URL at info level is appropriate — AZURE_BASE_URL / AZURE_COGNITIVE_SERVICES_BASE_URL end in _URL, outside the _KEY/_SECRET/_TOKEN/_PASSWORD/_CREDENTIAL sensitive-suffix set, so they are intentionally treated as non-secret config rather than masked.
  • The author's forward note about adding these vars to #69's non-secret allowlist (if it lands first) is the right follow-up and needs no action here.

Pullfrog  | View workflow run | Using Claude Opus (free via Pullfrog for OSS) | 𝕏

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