Skip to content

Let named custom providers select a typed Responses dialect #5094

Description

@Hmbown

Problem

Named [providers.<name>] entries with kind = "openai-compatible" are currently fixed to Chat Completions by Custom::wire_policy. A Responses-compatible endpoint therefore cannot be selected explicitly. Inferring the protocol from a model slug or base_url would reintroduce the ambiguity that #185 removed.

For v0.9.4, custom endpoints need an explicit, typed Responses opt-in while existing configurations remain byte-for-byte and behaviorally compatible.

Current evidence

  • crates/config/src/provider.rs::Custom::wire_policy returns WireFormat::ChatCompletions.
  • crates/config/src/lib.rs::named_custom_provider_table validates the custom provider kind, but ProviderConfigToml has no wire-format or Responses-profile field.
  • crates/config/src/route/resolver.rs already carries protocol information on resolved route candidates.
  • crates/tui/src/client.rs::prepare_outbound_request already has a Responses preparation path and a custom route shape, but a custom provider cannot currently resolve to that path.
  • docs/CONFIGURATION.md documents custom OpenAI-compatible gateways only as Chat Completions routes.

Scope

  1. Extend named custom provider configuration with typed fields:
    • wire_format = "chat_completions" | "responses"
    • responses_profile = "standard" | "stateless_plain_reasoning", valid only when wire_format = "responses"
  2. Default an omitted wire_format to chat_completions; do not infer it from model names or URLs.
  3. Parse and validate the new fields without changing kind = "openai-compatible" or weakening round-trip preservation in the editable config document.
  4. Carry the selected wire format and Responses profile through route resolution into the prepared request.
  5. Build a custom Responses URL from the configured base URL and the normal /responses path. Provider-only endpoints, authentication headers, and account-scoped behavior must not become selectable custom behavior.
  6. Keep path_suffix explicit and cover its interaction with the Responses path in tests.
  7. Show the selected wire format/profile in request preview and provider diagnostics without exposing credentials.
  8. Document the configuration, compatibility default, supported profiles, and validation failures.

Do not add arbitrary JSON body patches or user-defined event transforms. New behavior must enter through an allowlisted typed profile.

Key files

  • crates/config/src/provider.rs
  • crates/config/src/provider_kind.rs
  • crates/config/src/lib.rs
  • crates/config/src/config_document.rs
  • crates/config/src/route/resolver.rs
  • crates/config/src/route/tests.rs
  • crates/tui/src/client.rs
  • crates/tui/src/client/prepared.rs
  • docs/CONFIGURATION.md

Acceptance criteria

  • Existing custom provider configuration still resolves to Chat Completions with no configuration changes.
  • An explicitly configured Responses custom provider sends the exact configured model and authentication to the configured origin's /responses route.
  • The standard and stateless/plain-reasoning profiles produce distinct, documented, typed behavior.
  • Unknown profiles and invalid wire/profile combinations fail before any network request.
  • Custom provider IDs continue to flow through agent profiles and Fleets as exact provider/model selections.
  • Provider-specific OAuth headers, account identifiers, and privileged endpoints are never inherited by custom routes.
  • Request preview and production execution are derived from the same prepared request.
  • Secrets remain redacted in previews, diagnostics, logs, and errors.
  • Configuration and route tests require no live provider access.

Verification

  • cargo fmt --all -- --check
  • cargo test -p codewhale-config
  • cargo test -p codewhale-tui client::prepared
  • cargo test -p codewhale-tui client::responses
  • python3 scripts/check_model_registry.py
  • Confirm the final diff contains no generated credentials, tokens, or unrelated formatting churn.

Out of scope

  • Adding a new first-class provider or model
  • Arbitrary request/event transformation scripts
  • New OAuth flows
  • Enabling server-side tools for custom endpoints by default
  • Pricing claims or live billable requests

Related

Triage note

Milestone: v0.9.4. This issue is ready for an implementation lane once the typed profile names and validation contract above are accepted.

Metadata

Metadata

Assignees

Labels

agent-readySelf-contained for a fresh-clone cloud agent; implement in a tested PR with no live credentialsdocumentationImprovements or additions to documentationenhancementNew feature or requestmodel-labModel Lab open-model discovery, evaluation, routing, and export workflowsresponses-apiResponses API integration, streaming, or provider protocol issuestuiTerminal UI behavior, rendering, or interaction

Projects

Relationships

None yet

Development

No branches or pull requests

Issue actions