Skip to content

fix(custom): support wire = "responses" | "anthropic" for kind="openai-compatible" - #5714

Closed
whp233 wants to merge 3 commits into
Hmbown:mainfrom
whp233:fix/custom-wire-responses
Closed

fix(custom): support wire = "responses" | "anthropic" for kind="openai-compatible"#5714
whp233 wants to merge 3 commits into
Hmbown:mainfrom
whp233:fix/custom-wire-responses

Conversation

@whp233

@whp233 whp233 commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

fix(custom): support wire = "responses" | "anthropic" for kind="openai-compatible"

Summary / 摘要

Custom provider (kind = "openai-compatible") currently only supports Chat Completions wire. 配置 wire = "responses" / "anthropic" 被忽略,始终走 ChatCompletions,导致需要 Responses 或 Anthropic Messages 的模型(如 muse-spark-1.2 on opencode.ai/zen/v1)无法在 custom provider 下工作。

Custom provider (kind="openai-compatible") only speaks Chat Completions, ignoring wire setting. Models requiring Responses / Anthropic wire (e.g. muse-spark-1.2 via opencode.ai/zen/v1) cannot be used as custom.

Reproduction / 复现

Minimal config.toml:

[providers.myspark]
kind = "openai-compatible"
base_url = "https://opencode.ai/zen/v1"
api_key_env = "OPENCODE_API_KEY"  # or OPENCODE_ZEN_API_KEY
model = "muse-spark-1.2"
wire = "responses"   # 期望走 OpenAI Responses;改成 "anthropic" / "messages" 同理
# wire = "anthropic"

[providers.myspark2]
kind = "openai-compatible"
base_url = "https://opencode.ai/zen/v1"
api_key_env = "OPENCODE_API_KEY"
model = "muse-spark-1.2"
wire = "anthropic"

Steps:

  1. codewhale --provider myspark (or set as default)
  2. Send any message / codewhale doctor --verbose
  3. Observe request goes to /v1/chat/completions instead of /responses or /v1/messages

Expected / 期望

  • wire = "responses" (also response, openai-responses) → WireFormat::Responses, requests hit /responses (OpenAI Responses API, like openai_codex).
  • wire = "anthropic" / "messages" / "anthropic-messages"WireFormat::AnthropicMessages, requests hit /v1/messages.
  • wire omitted / "openai" / "chat" / "chat_completions"WireFormat::ChatCompletions (default, backward compatible).
  • Case-insensitive, trims whitespace.

Actual / 实际

Always WireFormat::ChatCompletions regardless of wire value. 对 opencode.ai/zen/v1 + muse-spark-1.2 这类 Responses-Anthropic hybrid 网关,请求 400/404 或 unsupported wire,因为服务端期望 Responses/Anthropic payload 而收到 Chat Completions schema.

Root cause / 根因

crates/config/src/provider.rs:1651-1653:

impl Provider for Custom {
    fn wire_policy(&self) -> WirePolicy {
        WirePolicy::Fixed(WireFormat::ChatCompletions) // ← fixed, ignores config
    }
}
  • Custom::wire_policy() is Fixed(ChatCompletions), so WirePolicy::resolve() never consults providers.<name>.wire.
  • crates/tui/src/config.rs & crates/tui/src/client.rs resolve wire_format via provider.wire_policy() + provider_wire_format_for_config(), but Custom never reaches the ModelAware / wire_config_prefers_anthropic path. providers.<name>.wire is parsed & stored in lib.rs (ProviderConfigToml::wire) yet never read for Custom.
  • Other built-ins (e.g. OpencodeZenModelAware, OpenaiCodexFixed(Responses), AnthropicFixed(AnthropicMessages)) demonstrate the wire abstraction already exists; Custom is the outlier.

Proposed fix / 修复建议

Branch: fix/custom-wire-responses (or main with following changes)

  1. crates/config/src/provider.rsCustom::wire_policy
    Make Custom respect wire field: either ModelAware or Fixed with override via wire string. Suggested mapping (reuse WirePolicy::resolve keys):

    • responsesResponses
    • anthropic / messages / anthropic_messages / anthropic-messagesAnthropicMessages
    • chat / chat_completions / openai / empty → ChatCompletions (default)
  2. crates/tui/src/config.rsprovider_wire_format_for_config / wire_config_prefers_anthropic
    Ensure ApiProvider::Custom path reads config.wire (like dual-wire vendors do). Normalize aliases (wire, wire_format, api_style already aliased in config_document.rs).

  3. crates/tui/src/client.rs — dispatch & validation
    No wire-specific hardcoding for Custom; rely on resolved wire_format for match self.wire_format { ChatCompletions | Responses | AnthropicMessages } → correct body builder / endpoint. Keep validate_route / limits intact.

Backward compat: 无 wire 时保持 ChatCompletions,不影响现有 custom 用户。

Workaround / 临时绕过

Reuse the existing Responses-fixed built-in provider openai_codex + env override (since Custom is blocked):

[providers.openai_codex]
# base_url/model 留空,由 env 覆盖,避免写死官方 URL
export OPENAI_CODEX_BASE_URL="https://opencode.ai/zen/v1"
export OPENAI_CODEX_API_KEY="$OPENCODE_API_KEY"   # or OPENCODE_ZEN_API_KEY
# 可选:指定默认模型
export OPENAI_CODEX_MODEL="muse-spark-1.2"
codewhale --provider openai_codex --model muse-spark-1.2

或直接改 env 启动:

$env:OPENAI_CODEX_BASE_URL="https://opencode.ai/zen/v1"
$env:OPENAI_CODEX_API_KEY=$env:OPENCODE_API_KEY
codewhale --provider openai_codex

原理:OpenaiCodex::wire_policy = Fixed(Responses) 天然走 /responses,绕过 Custom 的 Fixed-Chat 限制。For Anthropic wire, use providers.anthropic similarly if gateway supports it.

缺点:占用 openai_codex 槽位,不能同时配多个 custom Responses 网关;env 覆盖对多路由不友好。

Related files / 相关文件

  • crates/config/src/provider.rsCustom struct & wire_policy() (root cause)
  • crates/tui/src/config.rsprovider_wire_format_for_config(), wire_config_prefers_anthropic(), ProviderConfigToml::wire handling
  • crates/tui/src/client.rsprovider_default_wire_format(), DeepSeekClient::wire_format dispatch (ChatCompletions / Responses / AnthropicMessages)
  • crates/config/src/lib.rsProviderKind::Custom, ProviderConfigToml, wire field definition

Environment

  • CodeWhale: main @ a0341b5a4 (pre-fix) / branch fix/custom-wire-responses
  • Provider: kind = "openai-compatible" (Custom) → opencode.ai/zen/v1 / muse-spark-1.2
  • OS: Windows 10 (also repro on Linux/macOS)

Fix branch: whp233/CodeWhale#fix/custom-wire-responses (commit 934a69a)
Closes #5713

whp233 and others added 3 commits August 29, 2026 15:09
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
…nd="openai-compatible"

Custom provider was fixed to ChatCompletions, ignoring providers.<name>.wire.
Now honors per-config wire in both client::provider_wire_format_for_config
and config::provider_capability, keeping Custom::wire_policy default as Chat
for compat. Aliases: responses/openai-responses/responses-api -> Responses;
anthropic/messages/claude -> AnthropicMessages; default -> Chat.

Fixes custom muse-spark-1.2 on opencode.ai/zen/v1 needing Responses.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Thanks @whp233 for taking the time to contribute.

This repository is observing a maintainer-managed PR intake gate in dry-run mode, so this pull request is staying open. This note helps maintainers prepare the allowlist before any enforcement is considered.

Please read CONTRIBUTING.md for the expected contribution shape. A maintainer can grant recurring PR access by commenting /lgtm on a pull request.

whp233 added a commit to whp233/CodeWhale that referenced this pull request Aug 29, 2026
Hmbown pushed a commit that referenced this pull request Aug 29, 2026
The contribution gate leaves an unlisted contributor's workflow runs at
`action_required`, so their CI never starts and the PR sits looking stalled
through no fault of theirs. whp233's #5714 and #5716 had SEVEN workflows each
parked that way; I approved those runs by hand, but the allowlist is the actual
fix.

Added (all five have landed or open work):
  whp233    open PRs #5714, #5716 — the runs that were parked
  musichen  merged #5689 (DeepSeek configured-view picker)
  M-Maciej  merged #5591 (goal continuation cadence)
  serephus  merged #5669 (nixpkgs update)
  Pinvou    fork owner behind #5686 (Moonshot/Kimi native search)

Entries use `all:` to match the existing convention for active contributors.

No-Issue: contribution-gate hygiene; no issue tracks it
Signed-off-by: CodeWhale Bot <bot@codewhale.net>
@whp233
whp233 marked this pull request as ready for review August 29, 2026 13:23
@whp233
whp233 requested a review from Hmbown as a code owner August 29, 2026 13:23
Hmbown pushed a commit that referenced this pull request Aug 29, 2026
Every rule here is something that actually went wrong on the 2026-08-29 night
shift, written down so the next agent does not rediscover it.

Landing other people's work:
- A contributor's branch goes stale because WE land things. #5686 was
  CONFLICTING purely from Route Contract Phase 1 plus that same contributor's
  own earlier PRs merging into the same files. A maintainer resolves that.
- Conflicts that split mid-function do not resolve by keeping both sides — the
  markers landed inside two function bodies and the naive resolution failed to
  compile with 'unclosed delimiter'. Take one side whole, re-insert the other's
  additions at their anchor.
- maintainerCanModify did not grant push access to the fork (403), so the
  resolved merge went to integration/moonshot-kimi-5686-20260829 instead. That
  integration-branch path is now the documented default for conflicted work.
- whp233's #5714 and #5716 each had SEVEN workflows parked at action_required
  because the author was not on .github/APPROVED_CONTRIBUTORS. The PRs looked
  stalled; nobody had looked. Five contributors have since been added.
- Credit is mechanical: AUTHOR_MAP and .mailmap are project conventions and
  GitHub reads neither for the contribution graph.

Merging under a gate:
- #5698 merged while ACCEPTANCE_MATRIX.md still said FAIL and 37 minutes after
  a review confirming five findings were unchanged. Five real bugs reached
  main. A gate is its artifact, and check rollups are not the review thread.

Claiming a test passed:
- cargo test with a non-matching filter exits 0 having run ZERO tests; that was
  briefly mistaken for a pass here.
- A harness scored 72 PASS of which 12 were never evaluated: 'ok = ok and X or
  True' parses as '(ok and X) or True'.

CLAUDE.md already imports this file, so no second entrypoint was created.

No-Issue: process documentation from the night shift
Signed-off-by: CodeWhale Bot <bot@codewhale.net>
Hmbown pushed a commit that referenced this pull request Aug 29, 2026
…rule

scripts/check-coauthor-trailers.py rejects bot/tool co-author trailers because
those trailers feed the GitHub contribution graph and are for humans. That rule
was documented nowhere a contributor would look — CONTRIBUTING.md mentioned
Co-authored-by only under harvesting, i.e. how WE credit THEM.

The cost is real and current: whp233's #5714 and #5716 both fail Lint solely
because their assistant appended 'Co-authored-by: Claude <noreply@anthropic.com>'
to 3 and 4 commits respectively. Their code is fine. They had no way to know,
and this will catch every contributor who uses an AI assistant — which is most
of them now.

Adds the rule to the Commit Messages section with the exact rebase command, and
restates that co-authoring a person requires their GitHub-linked noreply address
or the credit silently does not register.

No-Issue: contributor documentation gap found while triaging #5714/#5716
Signed-off-by: CodeWhale Bot <bot@codewhale.net>
@Hmbown

Hmbown commented Aug 29, 2026

Copy link
Copy Markdown
Owner

Closing as superseded by #5716 — verified, not assumed: of #5716's 8 files, the 4 this PR touches (crates/tui/src/client.rs, crates/tui/src/config.rs, .github/workflows/build-windows.yml byte-identical diffs; crates/config/src/provider.rs the same hunk after rebase) are fully contained there, and #5716 adds the opencode-zen muse-spark routing, route-resolver integration, config example, and the dev proxy on top.

Full credit to @whp233 for the wire-dialect design — the wire = "responses" | "anthropic" | "chat" selector for kind = "openai-compatible" custom providers originates here and is the core of what #5716 carries forward. It aligns with the open design asks #5094/#5713.

Both PRs are red on the same checks (Lint, Tests ×3); the fix-forward is running on #5716's lane against current main (it is the superset). Nothing is lost by closing this one — the commits stay, and #5716's merge will carry the work.

@Hmbown Hmbown closed this Aug 29, 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.

fix(custom): support wire = "responses" | "anthropic" for kind="openai-compatible"

2 participants