Skip to content

fix: support OPENAI_BASE_URL and api-key header for Azure APIM-style gateways#35

Closed
Rom Iluz (romiluz13) wants to merge 4 commits into
langchain-ai:mainfrom
romiluz13:fix/azure-apim-api-key-header
Closed

fix: support OPENAI_BASE_URL and api-key header for Azure APIM-style gateways#35
Rom Iluz (romiluz13) wants to merge 4 commits into
langchain-ai:mainfrom
romiluz13:fix/azure-apim-api-key-header

Conversation

@romiluz13

@romiluz13 Rom Iluz (romiluz13) commented Jul 2, 2026

Copy link
Copy Markdown

Problem

Two bugs when using OpenWiki with OpenAI-compatible gateways (e.g. Azure APIM, Grove):

1. Wrong auth header (401)
These gateways use an api-key request header instead of Authorization: Bearer. Every request failed with 401 because the OpenAI SDK sends the wrong header.

Additionally, OPENAI_BASE_URL was in the deprecatedEnvKeys list in src/env.ts, so even if a user set it in ~/.openwiki/.env it was silently dropped on load.

2. File content blocks rejected (400)
DeepAgents reads repo files and sends them as {type:'file', mimeType, data} content blocks in the message history. Third-party gateways (e.g. GLM-5.2 via Azure APIM) only accept plain string message content and reject these with:

Input should be a valid string, field: messages[N].content.str, value: [{'type': 'file', ...}]

Fix

src/constants.ts — export OPENAI_BASE_URL_ENV_KEY constant.

src/env.ts

  • Remove OPENAI_BASE_URL from deprecatedEnvKeys so it's no longer stripped on load/save.
  • Add it to managedEnvKeys so it's persisted and included in diagnostics.

src/agent/index.ts — two changes in createModel():

  • Read OPENAI_BASE_URL from the environment; pass api-key as a defaultHeaders entry for Azure APIM-style auth.
  • When OPENAI_BASE_URL is set, use TextOnlyChatOpenAI — a thin ChatOpenAI subclass that intercepts _generate and _streamResponseChunks to decode base64 file blocks into {type:'text'} blocks before they reach the API. Standard providers are unaffected.

Usage

export OPENWIKI_PROVIDER=openai
export OPENAI_API_KEY="<your-gateway-key>"
export OPENAI_BASE_URL="https://your-gateway.azure-api.net/openai/v1"
export OPENWIKI_MODEL_ID="your-model-id"
openwiki --init

Or persist once in ~/.openwiki/.env:

OPENWIKI_PROVIDER="openai"
OPENAI_API_KEY="<your-gateway-key>"
OPENAI_BASE_URL="https://your-gateway.azure-api.net/openai/v1"
OPENWIKI_MODEL_ID="your-model-id"

Test

Verified end-to-end against a live Azure APIM gateway (Grove) with FW-GLM-5.2 on a real Python repo — previously 401 on hello-world, then 400 on file blocks during --init; both now work correctly.

🤖 Generated with Claude Code

…gateways

OpenAI-compatible gateways (e.g. Azure APIM, Grove) often require an
`api-key` header instead of `Authorization: Bearer`. The `openai` provider
branch of `createModel` now reads `OPENAI_BASE_URL` from the environment
and, when set, passes `api-key` as a `defaultHeaders` entry alongside the
base URL so requests authenticate correctly against these gateways.

`OPENAI_BASE_URL` is also removed from the deprecated-keys list in env.ts
and added to `managedEnvKeys` so it can be persisted to and loaded from
`~/.openwiki/.env` like other credentials.
DeepAgents reads repo files and sends them as {type:'file', mimeType, data}
content blocks in the message history. Third-party OpenAI-compatible gateways
(e.g. GLM-5.2 via Azure APIM/Grove) reject these with a 400 validation error
because they only accept plain string message content.

Add TextOnlyChatOpenAI, a thin ChatOpenAI subclass that intercepts _generate
and _streamResponseChunks to decode base64 file blocks into {type:'text'}
blocks before the messages reach the API. It is used automatically whenever
OPENAI_BASE_URL is set (i.e. any custom gateway), leaving the standard
providers untouched.
@romiluz13

Copy link
Copy Markdown
Author

Closing for now — the approach works but needs more testing before it's ready for upstream.

GLM-5.2 via Azure APIM rejects tool/function messages whose content is
an array even when every element is a text block. After stripping file
blocks, join all-text arrays into a single string so every message has
string content before it reaches the gateway.
…locks

The TextOnlyChatOpenAI subclass approach missed DeepAgents code paths that
bypass _generate/_streamResponseChunks. Replace with a global fetch
interceptor (installFileBlockScrubber) that rewrites the JSON body of every
chat/completions request destined for the custom base URL, converting
{type:'file', data:base64} content blocks to plain text strings before they
leave the process. This is guaranteed to catch all paths regardless of which
internal LangChain method is used.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

OPENAI_BASE_URL is added as a supported configuration, but it's still listed in deprecatedEnvKeys. That means users who intentionally configure a custom gateway via OPENAI_BASE_URL will continue to receive deprecation warnings even though this PR officially introduces it as the supported mechanism. Please remove OPENAI_BASE_URL from deprecatedEnvKeys (or update the deprecation logic) so the diagnostics match the new behavior.

@colifran

Copy link
Copy Markdown
Collaborator

Thanks for the PR! The second issue was fixed upstream in deepagentsjs. The first issue was superseded by #328.

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.

3 participants