feat(server): register Provider::Cohere on Hub for chat-compat (#332) - #341

Merged
moonming merged 3 commits into
mainfrom
fix/cohere-chat-hub-register
May 18, 2026
Merged

feat(server): register Provider::Cohere on Hub for chat-compat (#332)#341
moonming merged 3 commits into
mainfrom
fix/cohere-chat-hub-register

Conversation

@moonming

@moonmingmoonming commented May 18, 2026

Copy link
Copy Markdown
Member

Summary

Cohere exposes an OpenAI-compatible chat endpoint at https://api.cohere.com/compatibility/v1/chat/completions. Until this PR Provider::Cohere was intentionally NOT Hub-registered (per #213 Phases 1-2 rerank-only plan), so customers who selected Cohere in the dashboard and POSTed /v1/chat/completions got 503 ProviderUnavailable. The rerank surface at /v1/rerank continues to bypass the Bridge via aisix-proxy::rerank — this PR only adds the chat-compat dispatch.

Surfaced by AISIX-Cloud's source-blind E2E matrix audit on PR #349 (D3.1 OpenAI-adapter long-tail), which held back Cohere chat-compat scenarios pending this Hub registration.

Fix

OpenAiBridgeaisix-provider-openai/src/bridge.rs

  • COHERE_DEFAULT_BASE = "https://api.cohere.com/compatibility/v1" constant for the with_name("cohere") variant's fallback base.
  • default_base() arm: "cohere" => COHERE_DEFAULT_BASE.
  • normalize_canonical_cohere: operators who paste the bare canonical host https://api.cohere.com (the rerank path / dashboard placeholder) get /compatibility/v1 synthesized for chat. Non-canonical hosts pass through verbatim.

build_hubaisix-server/src/main.rs

  • hub.register(Provider::Cohere, Arc::new(OpenAiBridge::new().with_name("cohere"))).
  • Comment block updated: Jina alone stays rerank-only.

NOT touched

Provider::Cohere.default_base_url() in aisix-core stays as https://api.cohere.com (bare host) — the rerank URL builder appends /v1/rerank. The bridge handles chat-compat in its own resolve_base().

Tests

Three new bridge tests:

TestPins
cohere_default_base_targets_compatibility_v1empty api_base → bridge falls back to /compatibility/v1
cohere_api_base_tolerance_bare_host_synthesizes_compatibility_prefixbare host, trailing slash, full chat URL all normalize; custom host passes through
cohere_chat_compat_round_trips_openai_envelopeend-to-end chat through with_name("cohere") returns the OpenAI envelope verbatim

All 79 aisix-provider-openai tests pass; clippy clean; aisix-server builds clean.

References (per CLAUDE.md §7)

Test plan

  • CI green (cargo test + clippy + fmt)
  • AISIX-Cloud dashboard provider list update: defaultBase: 'https://api.cohere.com/compatibility/v1' (separate AISIX-Cloud PR; bridge tolerance covers bare-host PKs migration-free)
  • Once merged, AISIX-Cloud held-back D3.1 cohere matrix scenarios flip on

Closes#332

Summary by CodeRabbit

  • New Features

    • Added Cohere provider support for chat/completions and embeddings via an OpenAI-compatible bridge; hub now registers Cohere for chat-compat.
  • Documentation

    • Expanded provider docs and schema description to clarify Cohere routes and compatibility coverage.
  • Tests

    • Extended test coverage for Cohere routing, API base normalization variations, and an end-to-end chat round-trip.

Review Change Stack

Cohere exposes an OpenAI-compatible chat endpoint at
`https://api.cohere.com/compatibility/v1/chat/completions` per
<https://docs.cohere.com/reference/chat>. Before this commit `Provider::Cohere`
was intentionally NOT Hub-registered (per #213 Phases 1-2 rerank-only
plan), so a customer who selected Cohere in the dashboard and POSTed
/v1/chat/completions got 503 ProviderUnavailable. The rerank surface
at `/v1/rerank` continues to bypass the Bridge via aisix-proxy::rerank
— this commit only adds the chat-compat dispatch.
Fix
1. `OpenAiBridge` (`aisix-provider-openai/src/bridge.rs`)
- New `COHERE_DEFAULT_BASE = "https://api.cohere.com/compatibility/v1"`
constant for the `with_name("cohere")` variant's fallback base.
- `default_base()` arm: `"cohere" => COHERE_DEFAULT_BASE`.
- `normalize_api_base` arm + `normalize_canonical_cohere`:
operators who paste the bare canonical host
`https://api.cohere.com` (the rerank path / dashboard placeholder)
get `/compatibility/v1` synthesized for chat. Non-canonical
hosts pass through verbatim — operator's path on a custom host
wins.
2. `build_hub()` (`aisix-server/src/main.rs`)
- `hub.register(Provider::Cohere, Arc::new(OpenAiBridge::new().with_name("cohere")))`
- Comment block updated: Jina alone stays rerank-only; Cohere now
serves chat-compat via the bridge.
`Provider::Cohere.default_base_url()` in `aisix-core` stays as
`https://api.cohere.com` (bare host) because the rerank URL builder
appends `/v1/rerank` to it. The bridge handles chat-compat in its
own resolve_base().
Tests
Three new bridge tests under `aisix-provider-openai/src/bridge.rs::tests`:
- `cohere_default_base_targets_compatibility_v1` — empty `api_base`
falls back to `/compatibility/v1`.
- `cohere_api_base_tolerance_bare_host_synthesizes_compatibility_prefix`
— bare host, trailing slash, full chat URL all normalize; a
corporate-proxy host passes through unchanged.
- `cohere_chat_compat_round_trips_openai_envelope` — end-to-end
chat through `with_name("cohere")` returns the OpenAI envelope
verbatim. Pins the contract Hub.register relies on.
All 79 `aisix-provider-openai` tests pass; clippy clean; `aisix-server`
builds clean.
References (per CLAUDE.md §7)
- Cohere chat-compat docs: https://docs.cohere.com/reference/chat
- LiteLLM's Cohere chat-compat handler at
https://github.com/BerriAI/litellm/blob/main/litellm/llms/cohere/chat/transformation.py
uses the same `/compatibility/v1` namespace.
- Portkey's Cohere routing: similar — points at `/compatibility/v1`.
Closes#332
CopilotAI review requested due to automatic review settings May 18, 2026 15:17
@coderabbitai

coderabbitaiBot commented May 18, 2026

Copy link
Copy Markdown
ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Free

Run ID: 7b3bc6ea-948a-4c41-ba18-0a9dfe9c2793

📥 Commits

Reviewing files that changed from the base of the PR and between 848a1a0 and 09e8f81.

📒 Files selected for processing (3)
  • crates/aisix-core/src/models/model.rs
  • crates/aisix-server/src/main.rs
  • schemas/resources/model.schema.json
✅ Files skipped from review due to trivial changes (2)
  • schemas/resources/model.schema.json
  • crates/aisix-core/src/models/model.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/aisix-server/src/main.rs

📝 Walkthrough

Walkthrough

Adds Cohere support for OpenAI-compatible chat by normalizing Cohere canonical bases (synthesizing /compatibility/v1 when appropriate), registering Provider::Cohere with OpenAiBridge, and adding tests verifying base normalization and end-to-end chat compatibility.

Changes

Cohere OpenAI-Compatible Bridge Support

Layer / File(s)Summary
Cohere base and normalization in OpenAiBridge
crates/aisix-provider-openai/src/bridge.rs
Introduces COHERE_DEFAULT_BASE, extends default_base() and normalize_api_base() to dispatch "cohere", and implements COHERE_CANONICAL_HOSTS with normalize_canonical_cohere() to append /compatibility/v1 only for bare canonical hosts while preserving custom api_base values.
Hub registration and Cohere bridge testing
crates/aisix-provider-openai/src/bridge.rs, crates/aisix-server/src/main.rs, crates/aisix-core/src/models/model.rs, schemas/resources/model.schema.json
Registers Provider::Cohere in build_hub() via OpenAiBridge::with_name("cohere"), updates Provider::Cohere documentation and schema description, and adds tests covering default base routing, operator api_base tolerance, and an end-to-end chat compatibility round-trip.

🎯 3 (Moderate) | ⏱️ ~20 minutes


Note

🎁 Summarized by CodeRabbit Free

Your organization has reached its limit of developer seats under the Pro Plan. For new users, CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please add seats to your subscription by visiting https://app.coderabbit.ai/login.If you believe this is a mistake and have available seats, please assign one to the pull request author through the subscription management page using the link above.

Comment @coderabbitai help to get the list of available commands and usage tips.

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR enables Cohere chat-completions dispatch by registering Provider::Cohere with the OpenAI-compatible bridge and teaching that bridge to target Cohere’s /compatibility/v1 namespace.

Changes:

  • Adds Cohere default base URL and canonical host normalization in OpenAiBridge.
  • Registers Cohere in the server Hub using OpenAiBridge::with_name("cohere").
  • Adds bridge tests covering Cohere default base resolution, normalization, and chat round-trip behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

FileDescription
crates/aisix-server/src/main.rsRegisters Cohere on the Hub and updates provider-registration comments.
crates/aisix-provider-openai/src/bridge.rsAdds Cohere base URL handling, normalization, and unit coverage for chat compatibility.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +774 to +782
/// `Provider::Cohere` is registered against the OpenAI-compatible
/// chat endpoint at `https://api.cohere.com/compatibility/v1` (per
/// <https://docs.cohere.com/reference/chat>). Cohere's rerank surface
/// at `/v1/rerank` continues to bypass the Bridge via
/// `aisix-proxy::rerank` — the bridge here only serves `chat/completions`,
/// `embeddings`, and the other OpenAI-shape endpoints the bridge
/// supports. The chat-compat namespace gives an exact OpenAI envelope
/// shape so `OpenAiBridge::with_name("cohere")` can serve it directly
/// (closes #332).
…nt + build_hub registration tests
Independent audit (CLAUDE.md §7) of PR #341 surfaced:
- HIGH-1: `crates/aisix-core/src/models/model.rs:28-31` still
documented `Provider::Cohere` as "exposed for /v1/rerank only"
and "chat / generate APIs are not OpenAI-compatible". Both
statements contradict the new Hub registration. Updated to
describe the chat-compat path (https://api.cohere.com/compatibility/v1
per https://docs.cohere.com/reference/chat) and the model
coverage caveat (command-r / command-a family on chat-compat;
legacy command* not yet bridged).
- MEDIUM-1: nothing test-side ties `Provider::Cohere` to
`OpenAiBridge::with_name("cohere")` — the wiremock test in
bridge.rs uses an override api_base so a regression where
build_hub registered `OpenAiBridge::new()` (default name
"openai") would still pass. Added two tests on `build_hub()`:
- `build_hub_registers_cohere_chat_compat_variant` — asserts
`hub.get(Provider::Cohere).unwrap().name() == "cohere"`. A
`with_name("openai")` fallback would silently route Cohere
chat to OpenAI's host via the bridge's `default_base()`.
- `build_hub_does_not_register_jina_for_chat` — companion check
so a future Jina chat-compat enablement is a deliberate change,
not a silent regression.
MEDIUM-2 (cohere streaming e2e), MEDIUM-3 (tool-use forwarding),
MEDIUM-4 (model coverage gate) — scoped out of this PR per audit's
recommendation, will land as separate enhancements after the
chat-compat baseline merges.
LOW findings deferred (passthrough comment staleness; endpoint-
suffix corner case).
CopilotAI review requested due to automatic review settings May 18, 2026 15:31

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Comment on lines +795 to +798
hub.register(
Provider::Cohere,
Arc::new(OpenAiBridge::new().with_name("cohere")),
);
@moonming
moonming merged commit 9750da0 into mainMay 18, 2026
12 checks passed
janiussyafiq added a commit that referenced this pull request May 20, 2026
Integrate origin/main (commit 2c1d485 = post-PR-#326 / #348 plus
#330 / #341 / #343 / #345 / #346) into this branch via `git merge
--squash` to clear PR #344's lingering `mergeable: dirty` state.
Conflict on `docs/quickstart/self-hosted.md` was a 3-way-merge-base
artifact: base (3596c0a) read `- a reachable etcd instance`, main
changed `a` → `A` (via #326), this branch additionally inserted the
glossary link. Both changes are wanted; resolution per Umar's
approved plan was `git checkout --ours`, which preserves the branch's
self-hosted.md state (already integrates capital A + glossary link
+ first-time-build paragraph + keep-running framing). Other 4
overlapping doc files auto-merged cleanly (`bootstrap-config.md`,
`core-concepts.md`, `first-model-first-key-first-request.md`,
`openai-sdk.md`). Code files all auto-merged cleanly.
Additional Copilot review (post-`167196a` cycle) addressed:
- `docs/index.md:7` — change link display text from `[data-plane]`
to `[data plane]` to match the canonical glossary term. The URL
anchor `#data-plane` stays kebab-case (matches the glossary
heading's auto-anchor); only the display text changes. Comment
id 3271145422.
- `docs/quickstart/openai-sdk.md:43` — change `All three steps below`
to `All commands below`. The Install-the-SDK section has two
command blocks (mkdir+cd, npm install), not three; the prior
wording originated from a mental model (mkdir, cd, install)
that doesn't match the typographic count of code blocks under
the heading. Comment id 3271145458.
Copilot's third comment on `docs/overview/core-concepts.md`
Observability Exporter wording (id 3271145444) auto-resolves via
this merge — main's #326 rewrite supersedes the branch's pre-#326
wording at that location ("ships per-request span telemetry…
OTLP/HTTP-compatible backend…" replaces "Use this concept when
documenting…"). No separate edit needed; the merge IS the fix.
janiussyafiq added a commit that referenced this pull request May 20, 2026
…ickstart-polish
Resolve PR #344's lingering mergeable: dirty state by linking the
branch history to origin/main (2c1d485 = post-#326 / #348 / #330 /
#341 / #343 / #345 / #346).
The squash-merge commit landed earlier (e2af197) integrated main's
content into the branch tree but did not link the histories, so
GitHub's mergeable computation still saw the 3-way-merge-base
artifact conflict on docs/quickstart/self-hosted.md (a vs A + the
glossary link / "In another terminal" vs "Keep the gateway running"
framing). This explicit merge commit ties the branch to main's
history.
Self-hosted.md conflict resolved by taking OUR side — the branch's
edits already contain main's substantive changes (capital A,
first-time-build paragraph) plus this PR's additions (glossary
link, keep-running framing, YOUR_ADMIN_KEY note, config.yaml
location anchor).
The auto-merge of first-model-first-key-first-request.md duplicated
the :::warning callout that was already integrated via the squash
commit; removed the duplicate.
@jarvis9443
jarvis9443 deleted the fix/cohere-chat-hub-register branch June 25, 2026 06:26
Sign up for freeto 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.

Cohere chat-compat Hub registration missing — /v1/chat/completions returns 503 ProviderUnavailable

2 participants

@moonming
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

feat(server): register Provider::Cohere on Hub for chat-compat (#332) - #341

Merged
moonming merged 3 commits into
mainfrom
fix/cohere-chat-hub-register
May 18, 2026
Merged

feat(server): register Provider::Cohere on Hub for chat-compat (#332)#341
moonming merged 3 commits into
mainfrom
fix/cohere-chat-hub-register

Conversation

@moonming

@moonmingmoonming commented May 18, 2026

Copy link
Copy Markdown
Member

Summary

Cohere exposes an OpenAI-compatible chat endpoint at https://api.cohere.com/compatibility/v1/chat/completions. Until this PR Provider::Cohere was intentionally NOT Hub-registered (per #213 Phases 1-2 rerank-only plan), so customers who selected Cohere in the dashboard and POSTed /v1/chat/completions got 503 ProviderUnavailable. The rerank surface at /v1/rerank continues to bypass the Bridge via aisix-proxy::rerank — this PR only adds the chat-compat dispatch.

Surfaced by AISIX-Cloud's source-blind E2E matrix audit on PR #349 (D3.1 OpenAI-adapter long-tail), which held back Cohere chat-compat scenarios pending this Hub registration.

Fix

OpenAiBridgeaisix-provider-openai/src/bridge.rs

  • COHERE_DEFAULT_BASE = "https://api.cohere.com/compatibility/v1" constant for the with_name("cohere") variant's fallback base.
  • default_base() arm: "cohere" => COHERE_DEFAULT_BASE.
  • normalize_canonical_cohere: operators who paste the bare canonical host https://api.cohere.com (the rerank path / dashboard placeholder) get /compatibility/v1 synthesized for chat. Non-canonical hosts pass through verbatim.

build_hubaisix-server/src/main.rs

  • hub.register(Provider::Cohere, Arc::new(OpenAiBridge::new().with_name("cohere"))).
  • Comment block updated: Jina alone stays rerank-only.

NOT touched

Provider::Cohere.default_base_url() in aisix-core stays as https://api.cohere.com (bare host) — the rerank URL builder appends /v1/rerank. The bridge handles chat-compat in its own resolve_base().

Tests

Three new bridge tests:

TestPins
cohere_default_base_targets_compatibility_v1empty api_base → bridge falls back to /compatibility/v1
cohere_api_base_tolerance_bare_host_synthesizes_compatibility_prefixbare host, trailing slash, full chat URL all normalize; custom host passes through
cohere_chat_compat_round_trips_openai_envelopeend-to-end chat through with_name("cohere") returns the OpenAI envelope verbatim

All 79 aisix-provider-openai tests pass; clippy clean; aisix-server builds clean.

References (per CLAUDE.md §7)

Test plan

  • CI green (cargo test + clippy + fmt)
  • AISIX-Cloud dashboard provider list update: defaultBase: 'https://api.cohere.com/compatibility/v1' (separate AISIX-Cloud PR; bridge tolerance covers bare-host PKs migration-free)
  • Once merged, AISIX-Cloud held-back D3.1 cohere matrix scenarios flip on

Closes#332

Summary by CodeRabbit

  • New Features

    • Added Cohere provider support for chat/completions and embeddings via an OpenAI-compatible bridge; hub now registers Cohere for chat-compat.
  • Documentation

    • Expanded provider docs and schema description to clarify Cohere routes and compatibility coverage.
  • Tests

    • Extended test coverage for Cohere routing, API base normalization variations, and an end-to-end chat round-trip.

Review Change Stack

Cohere exposes an OpenAI-compatible chat endpoint at
`https://api.cohere.com/compatibility/v1/chat/completions` per
<https://docs.cohere.com/reference/chat>. Before this commit `Provider::Cohere`
was intentionally NOT Hub-registered (per #213 Phases 1-2 rerank-only
plan), so a customer who selected Cohere in the dashboard and POSTed
/v1/chat/completions got 503 ProviderUnavailable. The rerank surface
at `/v1/rerank` continues to bypass the Bridge via aisix-proxy::rerank
— this commit only adds the chat-compat dispatch.
Fix
1. `OpenAiBridge` (`aisix-provider-openai/src/bridge.rs`)
- New `COHERE_DEFAULT_BASE = "https://api.cohere.com/compatibility/v1"`
constant for the `with_name("cohere")` variant's fallback base.
- `default_base()` arm: `"cohere" => COHERE_DEFAULT_BASE`.
- `normalize_api_base` arm + `normalize_canonical_cohere`:
operators who paste the bare canonical host
`https://api.cohere.com` (the rerank path / dashboard placeholder)
get `/compatibility/v1` synthesized for chat. Non-canonical
hosts pass through verbatim — operator's path on a custom host
wins.
2. `build_hub()` (`aisix-server/src/main.rs`)
- `hub.register(Provider::Cohere, Arc::new(OpenAiBridge::new().with_name("cohere")))`
- Comment block updated: Jina alone stays rerank-only; Cohere now
serves chat-compat via the bridge.
`Provider::Cohere.default_base_url()` in `aisix-core` stays as
`https://api.cohere.com` (bare host) because the rerank URL builder
appends `/v1/rerank` to it. The bridge handles chat-compat in its
own resolve_base().
Tests
Three new bridge tests under `aisix-provider-openai/src/bridge.rs::tests`:
- `cohere_default_base_targets_compatibility_v1` — empty `api_base`
falls back to `/compatibility/v1`.
- `cohere_api_base_tolerance_bare_host_synthesizes_compatibility_prefix`
— bare host, trailing slash, full chat URL all normalize; a
corporate-proxy host passes through unchanged.
- `cohere_chat_compat_round_trips_openai_envelope` — end-to-end
chat through `with_name("cohere")` returns the OpenAI envelope
verbatim. Pins the contract Hub.register relies on.
All 79 `aisix-provider-openai` tests pass; clippy clean; `aisix-server`
builds clean.
References (per CLAUDE.md §7)
- Cohere chat-compat docs: https://docs.cohere.com/reference/chat
- LiteLLM's Cohere chat-compat handler at
https://github.com/BerriAI/litellm/blob/main/litellm/llms/cohere/chat/transformation.py
uses the same `/compatibility/v1` namespace.
- Portkey's Cohere routing: similar — points at `/compatibility/v1`.
Closes#332
CopilotAI review requested due to automatic review settings May 18, 2026 15:17
@coderabbitai

coderabbitaiBot commented May 18, 2026

Copy link
Copy Markdown
ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Free

Run ID: 7b3bc6ea-948a-4c41-ba18-0a9dfe9c2793

📥 Commits

Reviewing files that changed from the base of the PR and between 848a1a0 and 09e8f81.

📒 Files selected for processing (3)
  • crates/aisix-core/src/models/model.rs
  • crates/aisix-server/src/main.rs
  • schemas/resources/model.schema.json
✅ Files skipped from review due to trivial changes (2)
  • schemas/resources/model.schema.json
  • crates/aisix-core/src/models/model.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/aisix-server/src/main.rs

📝 Walkthrough

Walkthrough

Adds Cohere support for OpenAI-compatible chat by normalizing Cohere canonical bases (synthesizing /compatibility/v1 when appropriate), registering Provider::Cohere with OpenAiBridge, and adding tests verifying base normalization and end-to-end chat compatibility.

Changes

Cohere OpenAI-Compatible Bridge Support

Layer / File(s)Summary
Cohere base and normalization in OpenAiBridge
crates/aisix-provider-openai/src/bridge.rs
Introduces COHERE_DEFAULT_BASE, extends default_base() and normalize_api_base() to dispatch "cohere", and implements COHERE_CANONICAL_HOSTS with normalize_canonical_cohere() to append /compatibility/v1 only for bare canonical hosts while preserving custom api_base values.
Hub registration and Cohere bridge testing
crates/aisix-provider-openai/src/bridge.rs, crates/aisix-server/src/main.rs, crates/aisix-core/src/models/model.rs, schemas/resources/model.schema.json
Registers Provider::Cohere in build_hub() via OpenAiBridge::with_name("cohere"), updates Provider::Cohere documentation and schema description, and adds tests covering default base routing, operator api_base tolerance, and an end-to-end chat compatibility round-trip.

🎯 3 (Moderate) | ⏱️ ~20 minutes


Note

🎁 Summarized by CodeRabbit Free

Your organization has reached its limit of developer seats under the Pro Plan. For new users, CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please add seats to your subscription by visiting https://app.coderabbit.ai/login.If you believe this is a mistake and have available seats, please assign one to the pull request author through the subscription management page using the link above.

Comment @coderabbitai help to get the list of available commands and usage tips.

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR enables Cohere chat-completions dispatch by registering Provider::Cohere with the OpenAI-compatible bridge and teaching that bridge to target Cohere’s /compatibility/v1 namespace.

Changes:

  • Adds Cohere default base URL and canonical host normalization in OpenAiBridge.
  • Registers Cohere in the server Hub using OpenAiBridge::with_name("cohere").
  • Adds bridge tests covering Cohere default base resolution, normalization, and chat round-trip behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

FileDescription
crates/aisix-server/src/main.rsRegisters Cohere on the Hub and updates provider-registration comments.
crates/aisix-provider-openai/src/bridge.rsAdds Cohere base URL handling, normalization, and unit coverage for chat compatibility.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +774 to +782
/// `Provider::Cohere` is registered against the OpenAI-compatible
/// chat endpoint at `https://api.cohere.com/compatibility/v1` (per
/// <https://docs.cohere.com/reference/chat>). Cohere's rerank surface
/// at `/v1/rerank` continues to bypass the Bridge via
/// `aisix-proxy::rerank` — the bridge here only serves `chat/completions`,
/// `embeddings`, and the other OpenAI-shape endpoints the bridge
/// supports. The chat-compat namespace gives an exact OpenAI envelope
/// shape so `OpenAiBridge::with_name("cohere")` can serve it directly
/// (closes #332).
…nt + build_hub registration tests
Independent audit (CLAUDE.md §7) of PR #341 surfaced:
- HIGH-1: `crates/aisix-core/src/models/model.rs:28-31` still
documented `Provider::Cohere` as "exposed for /v1/rerank only"
and "chat / generate APIs are not OpenAI-compatible". Both
statements contradict the new Hub registration. Updated to
describe the chat-compat path (https://api.cohere.com/compatibility/v1
per https://docs.cohere.com/reference/chat) and the model
coverage caveat (command-r / command-a family on chat-compat;
legacy command* not yet bridged).
- MEDIUM-1: nothing test-side ties `Provider::Cohere` to
`OpenAiBridge::with_name("cohere")` — the wiremock test in
bridge.rs uses an override api_base so a regression where
build_hub registered `OpenAiBridge::new()` (default name
"openai") would still pass. Added two tests on `build_hub()`:
- `build_hub_registers_cohere_chat_compat_variant` — asserts
`hub.get(Provider::Cohere).unwrap().name() == "cohere"`. A
`with_name("openai")` fallback would silently route Cohere
chat to OpenAI's host via the bridge's `default_base()`.
- `build_hub_does_not_register_jina_for_chat` — companion check
so a future Jina chat-compat enablement is a deliberate change,
not a silent regression.
MEDIUM-2 (cohere streaming e2e), MEDIUM-3 (tool-use forwarding),
MEDIUM-4 (model coverage gate) — scoped out of this PR per audit's
recommendation, will land as separate enhancements after the
chat-compat baseline merges.
LOW findings deferred (passthrough comment staleness; endpoint-
suffix corner case).
CopilotAI review requested due to automatic review settings May 18, 2026 15:31

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Comment on lines +795 to +798
hub.register(
Provider::Cohere,
Arc::new(OpenAiBridge::new().with_name("cohere")),
);
@moonming
moonming merged commit 9750da0 into mainMay 18, 2026
12 checks passed
janiussyafiq added a commit that referenced this pull request May 20, 2026
Integrate origin/main (commit 2c1d485 = post-PR-#326 / #348 plus
#330 / #341 / #343 / #345 / #346) into this branch via `git merge
--squash` to clear PR #344's lingering `mergeable: dirty` state.
Conflict on `docs/quickstart/self-hosted.md` was a 3-way-merge-base
artifact: base (3596c0a) read `- a reachable etcd instance`, main
changed `a` → `A` (via #326), this branch additionally inserted the
glossary link. Both changes are wanted; resolution per Umar's
approved plan was `git checkout --ours`, which preserves the branch's
self-hosted.md state (already integrates capital A + glossary link
+ first-time-build paragraph + keep-running framing). Other 4
overlapping doc files auto-merged cleanly (`bootstrap-config.md`,
`core-concepts.md`, `first-model-first-key-first-request.md`,
`openai-sdk.md`). Code files all auto-merged cleanly.
Additional Copilot review (post-`167196a` cycle) addressed:
- `docs/index.md:7` — change link display text from `[data-plane]`
to `[data plane]` to match the canonical glossary term. The URL
anchor `#data-plane` stays kebab-case (matches the glossary
heading's auto-anchor); only the display text changes. Comment
id 3271145422.
- `docs/quickstart/openai-sdk.md:43` — change `All three steps below`
to `All commands below`. The Install-the-SDK section has two
command blocks (mkdir+cd, npm install), not three; the prior
wording originated from a mental model (mkdir, cd, install)
that doesn't match the typographic count of code blocks under
the heading. Comment id 3271145458.
Copilot's third comment on `docs/overview/core-concepts.md`
Observability Exporter wording (id 3271145444) auto-resolves via
this merge — main's #326 rewrite supersedes the branch's pre-#326
wording at that location ("ships per-request span telemetry…
OTLP/HTTP-compatible backend…" replaces "Use this concept when
documenting…"). No separate edit needed; the merge IS the fix.
janiussyafiq added a commit that referenced this pull request May 20, 2026
…ickstart-polish
Resolve PR #344's lingering mergeable: dirty state by linking the
branch history to origin/main (2c1d485 = post-#326 / #348 / #330 /
#341 / #343 / #345 / #346).
The squash-merge commit landed earlier (e2af197) integrated main's
content into the branch tree but did not link the histories, so
GitHub's mergeable computation still saw the 3-way-merge-base
artifact conflict on docs/quickstart/self-hosted.md (a vs A + the
glossary link / "In another terminal" vs "Keep the gateway running"
framing). This explicit merge commit ties the branch to main's
history.
Self-hosted.md conflict resolved by taking OUR side — the branch's
edits already contain main's substantive changes (capital A,
first-time-build paragraph) plus this PR's additions (glossary
link, keep-running framing, YOUR_ADMIN_KEY note, config.yaml
location anchor).
The auto-merge of first-model-first-key-first-request.md duplicated
the :::warning callout that was already integrated via the squash
commit; removed the duplicate.
@jarvis9443
jarvis9443 deleted the fix/cohere-chat-hub-register branch June 25, 2026 06:26
Sign up for freeto 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.

Cohere chat-compat Hub registration missing — /v1/chat/completions returns 503 ProviderUnavailable

2 participants

@moonming
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(server): register Provider::Cohere on Hub for chat-compat (#332) - #341

Merged
moonming merged 3 commits into
mainfrom
fix/cohere-chat-hub-register
May 18, 2026
Merged

feat(server): register Provider::Cohere on Hub for chat-compat (#332)#341
moonming merged 3 commits into
mainfrom
fix/cohere-chat-hub-register

Conversation

@moonming

@moonmingmoonming commented May 18, 2026

Copy link
Copy Markdown
Member

Summary

Cohere exposes an OpenAI-compatible chat endpoint at https://api.cohere.com/compatibility/v1/chat/completions. Until this PR Provider::Cohere was intentionally NOT Hub-registered (per #213 Phases 1-2 rerank-only plan), so customers who selected Cohere in the dashboard and POSTed /v1/chat/completions got 503 ProviderUnavailable. The rerank surface at /v1/rerank continues to bypass the Bridge via aisix-proxy::rerank — this PR only adds the chat-compat dispatch.

Surfaced by AISIX-Cloud's source-blind E2E matrix audit on PR #349 (D3.1 OpenAI-adapter long-tail), which held back Cohere chat-compat scenarios pending this Hub registration.

Fix

OpenAiBridgeaisix-provider-openai/src/bridge.rs

  • COHERE_DEFAULT_BASE = "https://api.cohere.com/compatibility/v1" constant for the with_name("cohere") variant's fallback base.
  • default_base() arm: "cohere" => COHERE_DEFAULT_BASE.
  • normalize_canonical_cohere: operators who paste the bare canonical host https://api.cohere.com (the rerank path / dashboard placeholder) get /compatibility/v1 synthesized for chat. Non-canonical hosts pass through verbatim.

build_hubaisix-server/src/main.rs

  • hub.register(Provider::Cohere, Arc::new(OpenAiBridge::new().with_name("cohere"))).
  • Comment block updated: Jina alone stays rerank-only.

NOT touched

Provider::Cohere.default_base_url() in aisix-core stays as https://api.cohere.com (bare host) — the rerank URL builder appends /v1/rerank. The bridge handles chat-compat in its own resolve_base().

Tests

Three new bridge tests:

TestPins
cohere_default_base_targets_compatibility_v1empty api_base → bridge falls back to /compatibility/v1
cohere_api_base_tolerance_bare_host_synthesizes_compatibility_prefixbare host, trailing slash, full chat URL all normalize; custom host passes through
cohere_chat_compat_round_trips_openai_envelopeend-to-end chat through with_name("cohere") returns the OpenAI envelope verbatim

All 79 aisix-provider-openai tests pass; clippy clean; aisix-server builds clean.

References (per CLAUDE.md §7)

Test plan

  • CI green (cargo test + clippy + fmt)
  • AISIX-Cloud dashboard provider list update: defaultBase: 'https://api.cohere.com/compatibility/v1' (separate AISIX-Cloud PR; bridge tolerance covers bare-host PKs migration-free)
  • Once merged, AISIX-Cloud held-back D3.1 cohere matrix scenarios flip on

Closes#332

Summary by CodeRabbit

  • New Features

    • Added Cohere provider support for chat/completions and embeddings via an OpenAI-compatible bridge; hub now registers Cohere for chat-compat.
  • Documentation

    • Expanded provider docs and schema description to clarify Cohere routes and compatibility coverage.
  • Tests

    • Extended test coverage for Cohere routing, API base normalization variations, and an end-to-end chat round-trip.

Review Change Stack

Cohere exposes an OpenAI-compatible chat endpoint at
`https://api.cohere.com/compatibility/v1/chat/completions` per
<https://docs.cohere.com/reference/chat>. Before this commit `Provider::Cohere`
was intentionally NOT Hub-registered (per #213 Phases 1-2 rerank-only
plan), so a customer who selected Cohere in the dashboard and POSTed
/v1/chat/completions got 503 ProviderUnavailable. The rerank surface
at `/v1/rerank` continues to bypass the Bridge via aisix-proxy::rerank
— this commit only adds the chat-compat dispatch.
Fix
1. `OpenAiBridge` (`aisix-provider-openai/src/bridge.rs`)
- New `COHERE_DEFAULT_BASE = "https://api.cohere.com/compatibility/v1"`
constant for the `with_name("cohere")` variant's fallback base.
- `default_base()` arm: `"cohere" => COHERE_DEFAULT_BASE`.
- `normalize_api_base` arm + `normalize_canonical_cohere`:
operators who paste the bare canonical host
`https://api.cohere.com` (the rerank path / dashboard placeholder)
get `/compatibility/v1` synthesized for chat. Non-canonical
hosts pass through verbatim — operator's path on a custom host
wins.
2. `build_hub()` (`aisix-server/src/main.rs`)
- `hub.register(Provider::Cohere, Arc::new(OpenAiBridge::new().with_name("cohere")))`
- Comment block updated: Jina alone stays rerank-only; Cohere now
serves chat-compat via the bridge.
`Provider::Cohere.default_base_url()` in `aisix-core` stays as
`https://api.cohere.com` (bare host) because the rerank URL builder
appends `/v1/rerank` to it. The bridge handles chat-compat in its
own resolve_base().
Tests
Three new bridge tests under `aisix-provider-openai/src/bridge.rs::tests`:
- `cohere_default_base_targets_compatibility_v1` — empty `api_base`
falls back to `/compatibility/v1`.
- `cohere_api_base_tolerance_bare_host_synthesizes_compatibility_prefix`
— bare host, trailing slash, full chat URL all normalize; a
corporate-proxy host passes through unchanged.
- `cohere_chat_compat_round_trips_openai_envelope` — end-to-end
chat through `with_name("cohere")` returns the OpenAI envelope
verbatim. Pins the contract Hub.register relies on.
All 79 `aisix-provider-openai` tests pass; clippy clean; `aisix-server`
builds clean.
References (per CLAUDE.md §7)
- Cohere chat-compat docs: https://docs.cohere.com/reference/chat
- LiteLLM's Cohere chat-compat handler at
https://github.com/BerriAI/litellm/blob/main/litellm/llms/cohere/chat/transformation.py
uses the same `/compatibility/v1` namespace.
- Portkey's Cohere routing: similar — points at `/compatibility/v1`.
Closes#332
CopilotAI review requested due to automatic review settings May 18, 2026 15:17
@coderabbitai

coderabbitaiBot commented May 18, 2026

Copy link
Copy Markdown
ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Free

Run ID: 7b3bc6ea-948a-4c41-ba18-0a9dfe9c2793

📥 Commits

Reviewing files that changed from the base of the PR and between 848a1a0 and 09e8f81.

📒 Files selected for processing (3)
  • crates/aisix-core/src/models/model.rs
  • crates/aisix-server/src/main.rs
  • schemas/resources/model.schema.json
✅ Files skipped from review due to trivial changes (2)
  • schemas/resources/model.schema.json
  • crates/aisix-core/src/models/model.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/aisix-server/src/main.rs

📝 Walkthrough

Walkthrough

Adds Cohere support for OpenAI-compatible chat by normalizing Cohere canonical bases (synthesizing /compatibility/v1 when appropriate), registering Provider::Cohere with OpenAiBridge, and adding tests verifying base normalization and end-to-end chat compatibility.

Changes

Cohere OpenAI-Compatible Bridge Support

Layer / File(s)Summary
Cohere base and normalization in OpenAiBridge
crates/aisix-provider-openai/src/bridge.rs
Introduces COHERE_DEFAULT_BASE, extends default_base() and normalize_api_base() to dispatch "cohere", and implements COHERE_CANONICAL_HOSTS with normalize_canonical_cohere() to append /compatibility/v1 only for bare canonical hosts while preserving custom api_base values.
Hub registration and Cohere bridge testing
crates/aisix-provider-openai/src/bridge.rs, crates/aisix-server/src/main.rs, crates/aisix-core/src/models/model.rs, schemas/resources/model.schema.json
Registers Provider::Cohere in build_hub() via OpenAiBridge::with_name("cohere"), updates Provider::Cohere documentation and schema description, and adds tests covering default base routing, operator api_base tolerance, and an end-to-end chat compatibility round-trip.

🎯 3 (Moderate) | ⏱️ ~20 minutes


Note

🎁 Summarized by CodeRabbit Free

Your organization has reached its limit of developer seats under the Pro Plan. For new users, CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please add seats to your subscription by visiting https://app.coderabbit.ai/login.If you believe this is a mistake and have available seats, please assign one to the pull request author through the subscription management page using the link above.

Comment @coderabbitai help to get the list of available commands and usage tips.

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR enables Cohere chat-completions dispatch by registering Provider::Cohere with the OpenAI-compatible bridge and teaching that bridge to target Cohere’s /compatibility/v1 namespace.

Changes:

  • Adds Cohere default base URL and canonical host normalization in OpenAiBridge.
  • Registers Cohere in the server Hub using OpenAiBridge::with_name("cohere").
  • Adds bridge tests covering Cohere default base resolution, normalization, and chat round-trip behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

FileDescription
crates/aisix-server/src/main.rsRegisters Cohere on the Hub and updates provider-registration comments.
crates/aisix-provider-openai/src/bridge.rsAdds Cohere base URL handling, normalization, and unit coverage for chat compatibility.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +774 to +782
/// `Provider::Cohere` is registered against the OpenAI-compatible
/// chat endpoint at `https://api.cohere.com/compatibility/v1` (per
/// <https://docs.cohere.com/reference/chat>). Cohere's rerank surface
/// at `/v1/rerank` continues to bypass the Bridge via
/// `aisix-proxy::rerank` — the bridge here only serves `chat/completions`,
/// `embeddings`, and the other OpenAI-shape endpoints the bridge
/// supports. The chat-compat namespace gives an exact OpenAI envelope
/// shape so `OpenAiBridge::with_name("cohere")` can serve it directly
/// (closes #332).
…nt + build_hub registration tests
Independent audit (CLAUDE.md §7) of PR #341 surfaced:
- HIGH-1: `crates/aisix-core/src/models/model.rs:28-31` still
documented `Provider::Cohere` as "exposed for /v1/rerank only"
and "chat / generate APIs are not OpenAI-compatible". Both
statements contradict the new Hub registration. Updated to
describe the chat-compat path (https://api.cohere.com/compatibility/v1
per https://docs.cohere.com/reference/chat) and the model
coverage caveat (command-r / command-a family on chat-compat;
legacy command* not yet bridged).
- MEDIUM-1: nothing test-side ties `Provider::Cohere` to
`OpenAiBridge::with_name("cohere")` — the wiremock test in
bridge.rs uses an override api_base so a regression where
build_hub registered `OpenAiBridge::new()` (default name
"openai") would still pass. Added two tests on `build_hub()`:
- `build_hub_registers_cohere_chat_compat_variant` — asserts
`hub.get(Provider::Cohere).unwrap().name() == "cohere"`. A
`with_name("openai")` fallback would silently route Cohere
chat to OpenAI's host via the bridge's `default_base()`.
- `build_hub_does_not_register_jina_for_chat` — companion check
so a future Jina chat-compat enablement is a deliberate change,
not a silent regression.
MEDIUM-2 (cohere streaming e2e), MEDIUM-3 (tool-use forwarding),
MEDIUM-4 (model coverage gate) — scoped out of this PR per audit's
recommendation, will land as separate enhancements after the
chat-compat baseline merges.
LOW findings deferred (passthrough comment staleness; endpoint-
suffix corner case).
CopilotAI review requested due to automatic review settings May 18, 2026 15:31

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Comment on lines +795 to +798
hub.register(
Provider::Cohere,
Arc::new(OpenAiBridge::new().with_name("cohere")),
);
@moonming
moonming merged commit 9750da0 into mainMay 18, 2026
12 checks passed
janiussyafiq added a commit that referenced this pull request May 20, 2026
Integrate origin/main (commit 2c1d485 = post-PR-#326 / #348 plus
#330 / #341 / #343 / #345 / #346) into this branch via `git merge
--squash` to clear PR #344's lingering `mergeable: dirty` state.
Conflict on `docs/quickstart/self-hosted.md` was a 3-way-merge-base
artifact: base (3596c0a) read `- a reachable etcd instance`, main
changed `a` → `A` (via #326), this branch additionally inserted the
glossary link. Both changes are wanted; resolution per Umar's
approved plan was `git checkout --ours`, which preserves the branch's
self-hosted.md state (already integrates capital A + glossary link
+ first-time-build paragraph + keep-running framing). Other 4
overlapping doc files auto-merged cleanly (`bootstrap-config.md`,
`core-concepts.md`, `first-model-first-key-first-request.md`,
`openai-sdk.md`). Code files all auto-merged cleanly.
Additional Copilot review (post-`167196a` cycle) addressed:
- `docs/index.md:7` — change link display text from `[data-plane]`
to `[data plane]` to match the canonical glossary term. The URL
anchor `#data-plane` stays kebab-case (matches the glossary
heading's auto-anchor); only the display text changes. Comment
id 3271145422.
- `docs/quickstart/openai-sdk.md:43` — change `All three steps below`
to `All commands below`. The Install-the-SDK section has two
command blocks (mkdir+cd, npm install), not three; the prior
wording originated from a mental model (mkdir, cd, install)
that doesn't match the typographic count of code blocks under
the heading. Comment id 3271145458.
Copilot's third comment on `docs/overview/core-concepts.md`
Observability Exporter wording (id 3271145444) auto-resolves via
this merge — main's #326 rewrite supersedes the branch's pre-#326
wording at that location ("ships per-request span telemetry…
OTLP/HTTP-compatible backend…" replaces "Use this concept when
documenting…"). No separate edit needed; the merge IS the fix.
janiussyafiq added a commit that referenced this pull request May 20, 2026
…ickstart-polish
Resolve PR #344's lingering mergeable: dirty state by linking the
branch history to origin/main (2c1d485 = post-#326 / #348 / #330 /
#341 / #343 / #345 / #346).
The squash-merge commit landed earlier (e2af197) integrated main's
content into the branch tree but did not link the histories, so
GitHub's mergeable computation still saw the 3-way-merge-base
artifact conflict on docs/quickstart/self-hosted.md (a vs A + the
glossary link / "In another terminal" vs "Keep the gateway running"
framing). This explicit merge commit ties the branch to main's
history.
Self-hosted.md conflict resolved by taking OUR side — the branch's
edits already contain main's substantive changes (capital A,
first-time-build paragraph) plus this PR's additions (glossary
link, keep-running framing, YOUR_ADMIN_KEY note, config.yaml
location anchor).
The auto-merge of first-model-first-key-first-request.md duplicated
the :::warning callout that was already integrated via the squash
commit; removed the duplicate.
@jarvis9443
jarvis9443 deleted the fix/cohere-chat-hub-register branch June 25, 2026 06:26
Sign up for freeto 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.

Cohere chat-compat Hub registration missing — /v1/chat/completions returns 503 ProviderUnavailable

2 participants

@moonming
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(server): register Provider::Cohere on Hub for chat-compat (#332) - #341

Merged
moonming merged 3 commits into
mainfrom
fix/cohere-chat-hub-register
May 18, 2026
Merged

feat(server): register Provider::Cohere on Hub for chat-compat (#332)#341
moonming merged 3 commits into
mainfrom
fix/cohere-chat-hub-register

Conversation

@moonming

@moonmingmoonming commented May 18, 2026

Copy link
Copy Markdown
Member

Summary

Cohere exposes an OpenAI-compatible chat endpoint at https://api.cohere.com/compatibility/v1/chat/completions. Until this PR Provider::Cohere was intentionally NOT Hub-registered (per #213 Phases 1-2 rerank-only plan), so customers who selected Cohere in the dashboard and POSTed /v1/chat/completions got 503 ProviderUnavailable. The rerank surface at /v1/rerank continues to bypass the Bridge via aisix-proxy::rerank — this PR only adds the chat-compat dispatch.

Surfaced by AISIX-Cloud's source-blind E2E matrix audit on PR #349 (D3.1 OpenAI-adapter long-tail), which held back Cohere chat-compat scenarios pending this Hub registration.

Fix

OpenAiBridgeaisix-provider-openai/src/bridge.rs

  • COHERE_DEFAULT_BASE = "https://api.cohere.com/compatibility/v1" constant for the with_name("cohere") variant's fallback base.
  • default_base() arm: "cohere" => COHERE_DEFAULT_BASE.
  • normalize_canonical_cohere: operators who paste the bare canonical host https://api.cohere.com (the rerank path / dashboard placeholder) get /compatibility/v1 synthesized for chat. Non-canonical hosts pass through verbatim.

build_hubaisix-server/src/main.rs

  • hub.register(Provider::Cohere, Arc::new(OpenAiBridge::new().with_name("cohere"))).
  • Comment block updated: Jina alone stays rerank-only.

NOT touched

Provider::Cohere.default_base_url() in aisix-core stays as https://api.cohere.com (bare host) — the rerank URL builder appends /v1/rerank. The bridge handles chat-compat in its own resolve_base().

Tests

Three new bridge tests:

TestPins
cohere_default_base_targets_compatibility_v1empty api_base → bridge falls back to /compatibility/v1
cohere_api_base_tolerance_bare_host_synthesizes_compatibility_prefixbare host, trailing slash, full chat URL all normalize; custom host passes through
cohere_chat_compat_round_trips_openai_envelopeend-to-end chat through with_name("cohere") returns the OpenAI envelope verbatim

All 79 aisix-provider-openai tests pass; clippy clean; aisix-server builds clean.

References (per CLAUDE.md §7)

Test plan

  • CI green (cargo test + clippy + fmt)
  • AISIX-Cloud dashboard provider list update: defaultBase: 'https://api.cohere.com/compatibility/v1' (separate AISIX-Cloud PR; bridge tolerance covers bare-host PKs migration-free)
  • Once merged, AISIX-Cloud held-back D3.1 cohere matrix scenarios flip on

Closes#332

Summary by CodeRabbit

  • New Features

    • Added Cohere provider support for chat/completions and embeddings via an OpenAI-compatible bridge; hub now registers Cohere for chat-compat.
  • Documentation

    • Expanded provider docs and schema description to clarify Cohere routes and compatibility coverage.
  • Tests

    • Extended test coverage for Cohere routing, API base normalization variations, and an end-to-end chat round-trip.

Review Change Stack

Cohere exposes an OpenAI-compatible chat endpoint at
`https://api.cohere.com/compatibility/v1/chat/completions` per
<https://docs.cohere.com/reference/chat>. Before this commit `Provider::Cohere`
was intentionally NOT Hub-registered (per #213 Phases 1-2 rerank-only
plan), so a customer who selected Cohere in the dashboard and POSTed
/v1/chat/completions got 503 ProviderUnavailable. The rerank surface
at `/v1/rerank` continues to bypass the Bridge via aisix-proxy::rerank
— this commit only adds the chat-compat dispatch.
Fix
1. `OpenAiBridge` (`aisix-provider-openai/src/bridge.rs`)
- New `COHERE_DEFAULT_BASE = "https://api.cohere.com/compatibility/v1"`
constant for the `with_name("cohere")` variant's fallback base.
- `default_base()` arm: `"cohere" => COHERE_DEFAULT_BASE`.
- `normalize_api_base` arm + `normalize_canonical_cohere`:
operators who paste the bare canonical host
`https://api.cohere.com` (the rerank path / dashboard placeholder)
get `/compatibility/v1` synthesized for chat. Non-canonical
hosts pass through verbatim — operator's path on a custom host
wins.
2. `build_hub()` (`aisix-server/src/main.rs`)
- `hub.register(Provider::Cohere, Arc::new(OpenAiBridge::new().with_name("cohere")))`
- Comment block updated: Jina alone stays rerank-only; Cohere now
serves chat-compat via the bridge.
`Provider::Cohere.default_base_url()` in `aisix-core` stays as
`https://api.cohere.com` (bare host) because the rerank URL builder
appends `/v1/rerank` to it. The bridge handles chat-compat in its
own resolve_base().
Tests
Three new bridge tests under `aisix-provider-openai/src/bridge.rs::tests`:
- `cohere_default_base_targets_compatibility_v1` — empty `api_base`
falls back to `/compatibility/v1`.
- `cohere_api_base_tolerance_bare_host_synthesizes_compatibility_prefix`
— bare host, trailing slash, full chat URL all normalize; a
corporate-proxy host passes through unchanged.
- `cohere_chat_compat_round_trips_openai_envelope` — end-to-end
chat through `with_name("cohere")` returns the OpenAI envelope
verbatim. Pins the contract Hub.register relies on.
All 79 `aisix-provider-openai` tests pass; clippy clean; `aisix-server`
builds clean.
References (per CLAUDE.md §7)
- Cohere chat-compat docs: https://docs.cohere.com/reference/chat
- LiteLLM's Cohere chat-compat handler at
https://github.com/BerriAI/litellm/blob/main/litellm/llms/cohere/chat/transformation.py
uses the same `/compatibility/v1` namespace.
- Portkey's Cohere routing: similar — points at `/compatibility/v1`.
Closes#332
CopilotAI review requested due to automatic review settings May 18, 2026 15:17
@coderabbitai

coderabbitaiBot commented May 18, 2026

Copy link
Copy Markdown
ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Free

Run ID: 7b3bc6ea-948a-4c41-ba18-0a9dfe9c2793

📥 Commits

Reviewing files that changed from the base of the PR and between 848a1a0 and 09e8f81.

📒 Files selected for processing (3)
  • crates/aisix-core/src/models/model.rs
  • crates/aisix-server/src/main.rs
  • schemas/resources/model.schema.json
✅ Files skipped from review due to trivial changes (2)
  • schemas/resources/model.schema.json
  • crates/aisix-core/src/models/model.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/aisix-server/src/main.rs

📝 Walkthrough

Walkthrough

Adds Cohere support for OpenAI-compatible chat by normalizing Cohere canonical bases (synthesizing /compatibility/v1 when appropriate), registering Provider::Cohere with OpenAiBridge, and adding tests verifying base normalization and end-to-end chat compatibility.

Changes

Cohere OpenAI-Compatible Bridge Support

Layer / File(s)Summary
Cohere base and normalization in OpenAiBridge
crates/aisix-provider-openai/src/bridge.rs
Introduces COHERE_DEFAULT_BASE, extends default_base() and normalize_api_base() to dispatch "cohere", and implements COHERE_CANONICAL_HOSTS with normalize_canonical_cohere() to append /compatibility/v1 only for bare canonical hosts while preserving custom api_base values.
Hub registration and Cohere bridge testing
crates/aisix-provider-openai/src/bridge.rs, crates/aisix-server/src/main.rs, crates/aisix-core/src/models/model.rs, schemas/resources/model.schema.json
Registers Provider::Cohere in build_hub() via OpenAiBridge::with_name("cohere"), updates Provider::Cohere documentation and schema description, and adds tests covering default base routing, operator api_base tolerance, and an end-to-end chat compatibility round-trip.

🎯 3 (Moderate) | ⏱️ ~20 minutes


Note

🎁 Summarized by CodeRabbit Free

Your organization has reached its limit of developer seats under the Pro Plan. For new users, CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please add seats to your subscription by visiting https://app.coderabbit.ai/login.If you believe this is a mistake and have available seats, please assign one to the pull request author through the subscription management page using the link above.

Comment @coderabbitai help to get the list of available commands and usage tips.

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR enables Cohere chat-completions dispatch by registering Provider::Cohere with the OpenAI-compatible bridge and teaching that bridge to target Cohere’s /compatibility/v1 namespace.

Changes:

  • Adds Cohere default base URL and canonical host normalization in OpenAiBridge.
  • Registers Cohere in the server Hub using OpenAiBridge::with_name("cohere").
  • Adds bridge tests covering Cohere default base resolution, normalization, and chat round-trip behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

FileDescription
crates/aisix-server/src/main.rsRegisters Cohere on the Hub and updates provider-registration comments.
crates/aisix-provider-openai/src/bridge.rsAdds Cohere base URL handling, normalization, and unit coverage for chat compatibility.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +774 to +782
/// `Provider::Cohere` is registered against the OpenAI-compatible
/// chat endpoint at `https://api.cohere.com/compatibility/v1` (per
/// <https://docs.cohere.com/reference/chat>). Cohere's rerank surface
/// at `/v1/rerank` continues to bypass the Bridge via
/// `aisix-proxy::rerank` — the bridge here only serves `chat/completions`,
/// `embeddings`, and the other OpenAI-shape endpoints the bridge
/// supports. The chat-compat namespace gives an exact OpenAI envelope
/// shape so `OpenAiBridge::with_name("cohere")` can serve it directly
/// (closes #332).
…nt + build_hub registration tests
Independent audit (CLAUDE.md §7) of PR #341 surfaced:
- HIGH-1: `crates/aisix-core/src/models/model.rs:28-31` still
documented `Provider::Cohere` as "exposed for /v1/rerank only"
and "chat / generate APIs are not OpenAI-compatible". Both
statements contradict the new Hub registration. Updated to
describe the chat-compat path (https://api.cohere.com/compatibility/v1
per https://docs.cohere.com/reference/chat) and the model
coverage caveat (command-r / command-a family on chat-compat;
legacy command* not yet bridged).
- MEDIUM-1: nothing test-side ties `Provider::Cohere` to
`OpenAiBridge::with_name("cohere")` — the wiremock test in
bridge.rs uses an override api_base so a regression where
build_hub registered `OpenAiBridge::new()` (default name
"openai") would still pass. Added two tests on `build_hub()`:
- `build_hub_registers_cohere_chat_compat_variant` — asserts
`hub.get(Provider::Cohere).unwrap().name() == "cohere"`. A
`with_name("openai")` fallback would silently route Cohere
chat to OpenAI's host via the bridge's `default_base()`.
- `build_hub_does_not_register_jina_for_chat` — companion check
so a future Jina chat-compat enablement is a deliberate change,
not a silent regression.
MEDIUM-2 (cohere streaming e2e), MEDIUM-3 (tool-use forwarding),
MEDIUM-4 (model coverage gate) — scoped out of this PR per audit's
recommendation, will land as separate enhancements after the
chat-compat baseline merges.
LOW findings deferred (passthrough comment staleness; endpoint-
suffix corner case).
CopilotAI review requested due to automatic review settings May 18, 2026 15:31

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Comment on lines +795 to +798
hub.register(
Provider::Cohere,
Arc::new(OpenAiBridge::new().with_name("cohere")),
);
@moonming
moonming merged commit 9750da0 into mainMay 18, 2026
12 checks passed
janiussyafiq added a commit that referenced this pull request May 20, 2026
Integrate origin/main (commit 2c1d485 = post-PR-#326 / #348 plus
#330 / #341 / #343 / #345 / #346) into this branch via `git merge
--squash` to clear PR #344's lingering `mergeable: dirty` state.
Conflict on `docs/quickstart/self-hosted.md` was a 3-way-merge-base
artifact: base (3596c0a) read `- a reachable etcd instance`, main
changed `a` → `A` (via #326), this branch additionally inserted the
glossary link. Both changes are wanted; resolution per Umar's
approved plan was `git checkout --ours`, which preserves the branch's
self-hosted.md state (already integrates capital A + glossary link
+ first-time-build paragraph + keep-running framing). Other 4
overlapping doc files auto-merged cleanly (`bootstrap-config.md`,
`core-concepts.md`, `first-model-first-key-first-request.md`,
`openai-sdk.md`). Code files all auto-merged cleanly.
Additional Copilot review (post-`167196a` cycle) addressed:
- `docs/index.md:7` — change link display text from `[data-plane]`
to `[data plane]` to match the canonical glossary term. The URL
anchor `#data-plane` stays kebab-case (matches the glossary
heading's auto-anchor); only the display text changes. Comment
id 3271145422.
- `docs/quickstart/openai-sdk.md:43` — change `All three steps below`
to `All commands below`. The Install-the-SDK section has two
command blocks (mkdir+cd, npm install), not three; the prior
wording originated from a mental model (mkdir, cd, install)
that doesn't match the typographic count of code blocks under
the heading. Comment id 3271145458.
Copilot's third comment on `docs/overview/core-concepts.md`
Observability Exporter wording (id 3271145444) auto-resolves via
this merge — main's #326 rewrite supersedes the branch's pre-#326
wording at that location ("ships per-request span telemetry…
OTLP/HTTP-compatible backend…" replaces "Use this concept when
documenting…"). No separate edit needed; the merge IS the fix.
janiussyafiq added a commit that referenced this pull request May 20, 2026
…ickstart-polish
Resolve PR #344's lingering mergeable: dirty state by linking the
branch history to origin/main (2c1d485 = post-#326 / #348 / #330 /
#341 / #343 / #345 / #346).
The squash-merge commit landed earlier (e2af197) integrated main's
content into the branch tree but did not link the histories, so
GitHub's mergeable computation still saw the 3-way-merge-base
artifact conflict on docs/quickstart/self-hosted.md (a vs A + the
glossary link / "In another terminal" vs "Keep the gateway running"
framing). This explicit merge commit ties the branch to main's
history.
Self-hosted.md conflict resolved by taking OUR side — the branch's
edits already contain main's substantive changes (capital A,
first-time-build paragraph) plus this PR's additions (glossary
link, keep-running framing, YOUR_ADMIN_KEY note, config.yaml
location anchor).
The auto-merge of first-model-first-key-first-request.md duplicated
the :::warning callout that was already integrated via the squash
commit; removed the duplicate.
@jarvis9443
jarvis9443 deleted the fix/cohere-chat-hub-register branch June 25, 2026 06:26
Sign up for freeto 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.

Cohere chat-compat Hub registration missing — /v1/chat/completions returns 503 ProviderUnavailable

2 participants

@moonming
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

feat(server): register Provider::Cohere on Hub for chat-compat (#332) - #341

Merged
moonming merged 3 commits into
mainfrom
fix/cohere-chat-hub-register
May 18, 2026
Merged

feat(server): register Provider::Cohere on Hub for chat-compat (#332)#341
moonming merged 3 commits into
mainfrom
fix/cohere-chat-hub-register

Conversation

@moonming

@moonmingmoonming commented May 18, 2026

Copy link
Copy Markdown
Member

Summary

Cohere exposes an OpenAI-compatible chat endpoint at https://api.cohere.com/compatibility/v1/chat/completions. Until this PR Provider::Cohere was intentionally NOT Hub-registered (per #213 Phases 1-2 rerank-only plan), so customers who selected Cohere in the dashboard and POSTed /v1/chat/completions got 503 ProviderUnavailable. The rerank surface at /v1/rerank continues to bypass the Bridge via aisix-proxy::rerank — this PR only adds the chat-compat dispatch.

Surfaced by AISIX-Cloud's source-blind E2E matrix audit on PR #349 (D3.1 OpenAI-adapter long-tail), which held back Cohere chat-compat scenarios pending this Hub registration.

Fix

OpenAiBridgeaisix-provider-openai/src/bridge.rs

  • COHERE_DEFAULT_BASE = "https://api.cohere.com/compatibility/v1" constant for the with_name("cohere") variant's fallback base.
  • default_base() arm: "cohere" => COHERE_DEFAULT_BASE.
  • normalize_canonical_cohere: operators who paste the bare canonical host https://api.cohere.com (the rerank path / dashboard placeholder) get /compatibility/v1 synthesized for chat. Non-canonical hosts pass through verbatim.

build_hubaisix-server/src/main.rs

  • hub.register(Provider::Cohere, Arc::new(OpenAiBridge::new().with_name("cohere"))).
  • Comment block updated: Jina alone stays rerank-only.

NOT touched

Provider::Cohere.default_base_url() in aisix-core stays as https://api.cohere.com (bare host) — the rerank URL builder appends /v1/rerank. The bridge handles chat-compat in its own resolve_base().

Tests

Three new bridge tests:

TestPins
cohere_default_base_targets_compatibility_v1empty api_base → bridge falls back to /compatibility/v1
cohere_api_base_tolerance_bare_host_synthesizes_compatibility_prefixbare host, trailing slash, full chat URL all normalize; custom host passes through
cohere_chat_compat_round_trips_openai_envelopeend-to-end chat through with_name("cohere") returns the OpenAI envelope verbatim

All 79 aisix-provider-openai tests pass; clippy clean; aisix-server builds clean.

References (per CLAUDE.md §7)

Test plan

  • CI green (cargo test + clippy + fmt)
  • AISIX-Cloud dashboard provider list update: defaultBase: 'https://api.cohere.com/compatibility/v1' (separate AISIX-Cloud PR; bridge tolerance covers bare-host PKs migration-free)
  • Once merged, AISIX-Cloud held-back D3.1 cohere matrix scenarios flip on

Closes#332

Summary by CodeRabbit

  • New Features

    • Added Cohere provider support for chat/completions and embeddings via an OpenAI-compatible bridge; hub now registers Cohere for chat-compat.
  • Documentation

    • Expanded provider docs and schema description to clarify Cohere routes and compatibility coverage.
  • Tests

    • Extended test coverage for Cohere routing, API base normalization variations, and an end-to-end chat round-trip.

Review Change Stack

Cohere exposes an OpenAI-compatible chat endpoint at
`https://api.cohere.com/compatibility/v1/chat/completions` per
<https://docs.cohere.com/reference/chat>. Before this commit `Provider::Cohere`
was intentionally NOT Hub-registered (per #213 Phases 1-2 rerank-only
plan), so a customer who selected Cohere in the dashboard and POSTed
/v1/chat/completions got 503 ProviderUnavailable. The rerank surface
at `/v1/rerank` continues to bypass the Bridge via aisix-proxy::rerank
— this commit only adds the chat-compat dispatch.
Fix
1. `OpenAiBridge` (`aisix-provider-openai/src/bridge.rs`)
- New `COHERE_DEFAULT_BASE = "https://api.cohere.com/compatibility/v1"`
constant for the `with_name("cohere")` variant's fallback base.
- `default_base()` arm: `"cohere" => COHERE_DEFAULT_BASE`.
- `normalize_api_base` arm + `normalize_canonical_cohere`:
operators who paste the bare canonical host
`https://api.cohere.com` (the rerank path / dashboard placeholder)
get `/compatibility/v1` synthesized for chat. Non-canonical
hosts pass through verbatim — operator's path on a custom host
wins.
2. `build_hub()` (`aisix-server/src/main.rs`)
- `hub.register(Provider::Cohere, Arc::new(OpenAiBridge::new().with_name("cohere")))`
- Comment block updated: Jina alone stays rerank-only; Cohere now
serves chat-compat via the bridge.
`Provider::Cohere.default_base_url()` in `aisix-core` stays as
`https://api.cohere.com` (bare host) because the rerank URL builder
appends `/v1/rerank` to it. The bridge handles chat-compat in its
own resolve_base().
Tests
Three new bridge tests under `aisix-provider-openai/src/bridge.rs::tests`:
- `cohere_default_base_targets_compatibility_v1` — empty `api_base`
falls back to `/compatibility/v1`.
- `cohere_api_base_tolerance_bare_host_synthesizes_compatibility_prefix`
— bare host, trailing slash, full chat URL all normalize; a
corporate-proxy host passes through unchanged.
- `cohere_chat_compat_round_trips_openai_envelope` — end-to-end
chat through `with_name("cohere")` returns the OpenAI envelope
verbatim. Pins the contract Hub.register relies on.
All 79 `aisix-provider-openai` tests pass; clippy clean; `aisix-server`
builds clean.
References (per CLAUDE.md §7)
- Cohere chat-compat docs: https://docs.cohere.com/reference/chat
- LiteLLM's Cohere chat-compat handler at
https://github.com/BerriAI/litellm/blob/main/litellm/llms/cohere/chat/transformation.py
uses the same `/compatibility/v1` namespace.
- Portkey's Cohere routing: similar — points at `/compatibility/v1`.
Closes#332
CopilotAI review requested due to automatic review settings May 18, 2026 15:17
@coderabbitai

coderabbitaiBot commented May 18, 2026

Copy link
Copy Markdown
ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Free

Run ID: 7b3bc6ea-948a-4c41-ba18-0a9dfe9c2793

📥 Commits

Reviewing files that changed from the base of the PR and between 848a1a0 and 09e8f81.

📒 Files selected for processing (3)
  • crates/aisix-core/src/models/model.rs
  • crates/aisix-server/src/main.rs
  • schemas/resources/model.schema.json
✅ Files skipped from review due to trivial changes (2)
  • schemas/resources/model.schema.json
  • crates/aisix-core/src/models/model.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/aisix-server/src/main.rs

📝 Walkthrough

Walkthrough

Adds Cohere support for OpenAI-compatible chat by normalizing Cohere canonical bases (synthesizing /compatibility/v1 when appropriate), registering Provider::Cohere with OpenAiBridge, and adding tests verifying base normalization and end-to-end chat compatibility.

Changes

Cohere OpenAI-Compatible Bridge Support

Layer / File(s)Summary
Cohere base and normalization in OpenAiBridge
crates/aisix-provider-openai/src/bridge.rs
Introduces COHERE_DEFAULT_BASE, extends default_base() and normalize_api_base() to dispatch "cohere", and implements COHERE_CANONICAL_HOSTS with normalize_canonical_cohere() to append /compatibility/v1 only for bare canonical hosts while preserving custom api_base values.
Hub registration and Cohere bridge testing
crates/aisix-provider-openai/src/bridge.rs, crates/aisix-server/src/main.rs, crates/aisix-core/src/models/model.rs, schemas/resources/model.schema.json
Registers Provider::Cohere in build_hub() via OpenAiBridge::with_name("cohere"), updates Provider::Cohere documentation and schema description, and adds tests covering default base routing, operator api_base tolerance, and an end-to-end chat compatibility round-trip.

🎯 3 (Moderate) | ⏱️ ~20 minutes


Note

🎁 Summarized by CodeRabbit Free

Your organization has reached its limit of developer seats under the Pro Plan. For new users, CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please add seats to your subscription by visiting https://app.coderabbit.ai/login.If you believe this is a mistake and have available seats, please assign one to the pull request author through the subscription management page using the link above.

Comment @coderabbitai help to get the list of available commands and usage tips.

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR enables Cohere chat-completions dispatch by registering Provider::Cohere with the OpenAI-compatible bridge and teaching that bridge to target Cohere’s /compatibility/v1 namespace.

Changes:

  • Adds Cohere default base URL and canonical host normalization in OpenAiBridge.
  • Registers Cohere in the server Hub using OpenAiBridge::with_name("cohere").
  • Adds bridge tests covering Cohere default base resolution, normalization, and chat round-trip behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

FileDescription
crates/aisix-server/src/main.rsRegisters Cohere on the Hub and updates provider-registration comments.
crates/aisix-provider-openai/src/bridge.rsAdds Cohere base URL handling, normalization, and unit coverage for chat compatibility.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +774 to +782
/// `Provider::Cohere` is registered against the OpenAI-compatible
/// chat endpoint at `https://api.cohere.com/compatibility/v1` (per
/// <https://docs.cohere.com/reference/chat>). Cohere's rerank surface
/// at `/v1/rerank` continues to bypass the Bridge via
/// `aisix-proxy::rerank` — the bridge here only serves `chat/completions`,
/// `embeddings`, and the other OpenAI-shape endpoints the bridge
/// supports. The chat-compat namespace gives an exact OpenAI envelope
/// shape so `OpenAiBridge::with_name("cohere")` can serve it directly
/// (closes #332).
…nt + build_hub registration tests
Independent audit (CLAUDE.md §7) of PR #341 surfaced:
- HIGH-1: `crates/aisix-core/src/models/model.rs:28-31` still
documented `Provider::Cohere` as "exposed for /v1/rerank only"
and "chat / generate APIs are not OpenAI-compatible". Both
statements contradict the new Hub registration. Updated to
describe the chat-compat path (https://api.cohere.com/compatibility/v1
per https://docs.cohere.com/reference/chat) and the model
coverage caveat (command-r / command-a family on chat-compat;
legacy command* not yet bridged).
- MEDIUM-1: nothing test-side ties `Provider::Cohere` to
`OpenAiBridge::with_name("cohere")` — the wiremock test in
bridge.rs uses an override api_base so a regression where
build_hub registered `OpenAiBridge::new()` (default name
"openai") would still pass. Added two tests on `build_hub()`:
- `build_hub_registers_cohere_chat_compat_variant` — asserts
`hub.get(Provider::Cohere).unwrap().name() == "cohere"`. A
`with_name("openai")` fallback would silently route Cohere
chat to OpenAI's host via the bridge's `default_base()`.
- `build_hub_does_not_register_jina_for_chat` — companion check
so a future Jina chat-compat enablement is a deliberate change,
not a silent regression.
MEDIUM-2 (cohere streaming e2e), MEDIUM-3 (tool-use forwarding),
MEDIUM-4 (model coverage gate) — scoped out of this PR per audit's
recommendation, will land as separate enhancements after the
chat-compat baseline merges.
LOW findings deferred (passthrough comment staleness; endpoint-
suffix corner case).
CopilotAI review requested due to automatic review settings May 18, 2026 15:31

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Comment on lines +795 to +798
hub.register(
Provider::Cohere,
Arc::new(OpenAiBridge::new().with_name("cohere")),
);
@moonming
moonming merged commit 9750da0 into mainMay 18, 2026
12 checks passed
janiussyafiq added a commit that referenced this pull request May 20, 2026
Integrate origin/main (commit 2c1d485 = post-PR-#326 / #348 plus
#330 / #341 / #343 / #345 / #346) into this branch via `git merge
--squash` to clear PR #344's lingering `mergeable: dirty` state.
Conflict on `docs/quickstart/self-hosted.md` was a 3-way-merge-base
artifact: base (3596c0a) read `- a reachable etcd instance`, main
changed `a` → `A` (via #326), this branch additionally inserted the
glossary link. Both changes are wanted; resolution per Umar's
approved plan was `git checkout --ours`, which preserves the branch's
self-hosted.md state (already integrates capital A + glossary link
+ first-time-build paragraph + keep-running framing). Other 4
overlapping doc files auto-merged cleanly (`bootstrap-config.md`,
`core-concepts.md`, `first-model-first-key-first-request.md`,
`openai-sdk.md`). Code files all auto-merged cleanly.
Additional Copilot review (post-`167196a` cycle) addressed:
- `docs/index.md:7` — change link display text from `[data-plane]`
to `[data plane]` to match the canonical glossary term. The URL
anchor `#data-plane` stays kebab-case (matches the glossary
heading's auto-anchor); only the display text changes. Comment
id 3271145422.
- `docs/quickstart/openai-sdk.md:43` — change `All three steps below`
to `All commands below`. The Install-the-SDK section has two
command blocks (mkdir+cd, npm install), not three; the prior
wording originated from a mental model (mkdir, cd, install)
that doesn't match the typographic count of code blocks under
the heading. Comment id 3271145458.
Copilot's third comment on `docs/overview/core-concepts.md`
Observability Exporter wording (id 3271145444) auto-resolves via
this merge — main's #326 rewrite supersedes the branch's pre-#326
wording at that location ("ships per-request span telemetry…
OTLP/HTTP-compatible backend…" replaces "Use this concept when
documenting…"). No separate edit needed; the merge IS the fix.
janiussyafiq added a commit that referenced this pull request May 20, 2026
…ickstart-polish
Resolve PR #344's lingering mergeable: dirty state by linking the
branch history to origin/main (2c1d485 = post-#326 / #348 / #330 /
#341 / #343 / #345 / #346).
The squash-merge commit landed earlier (e2af197) integrated main's
content into the branch tree but did not link the histories, so
GitHub's mergeable computation still saw the 3-way-merge-base
artifact conflict on docs/quickstart/self-hosted.md (a vs A + the
glossary link / "In another terminal" vs "Keep the gateway running"
framing). This explicit merge commit ties the branch to main's
history.
Self-hosted.md conflict resolved by taking OUR side — the branch's
edits already contain main's substantive changes (capital A,
first-time-build paragraph) plus this PR's additions (glossary
link, keep-running framing, YOUR_ADMIN_KEY note, config.yaml
location anchor).
The auto-merge of first-model-first-key-first-request.md duplicated
the :::warning callout that was already integrated via the squash
commit; removed the duplicate.
@jarvis9443
jarvis9443 deleted the fix/cohere-chat-hub-register branch June 25, 2026 06:26
Sign up for freeto 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.

Cohere chat-compat Hub registration missing — /v1/chat/completions returns 503 ProviderUnavailable

2 participants

@moonming
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(server): register Provider::Cohere on Hub for chat-compat (#332) - #341

Merged
moonming merged 3 commits into
mainfrom
fix/cohere-chat-hub-register
May 18, 2026
Merged

feat(server): register Provider::Cohere on Hub for chat-compat (#332)#341
moonming merged 3 commits into
mainfrom
fix/cohere-chat-hub-register

Conversation

@moonming

@moonmingmoonming commented May 18, 2026

Copy link
Copy Markdown
Member

Summary

Cohere exposes an OpenAI-compatible chat endpoint at https://api.cohere.com/compatibility/v1/chat/completions. Until this PR Provider::Cohere was intentionally NOT Hub-registered (per #213 Phases 1-2 rerank-only plan), so customers who selected Cohere in the dashboard and POSTed /v1/chat/completions got 503 ProviderUnavailable. The rerank surface at /v1/rerank continues to bypass the Bridge via aisix-proxy::rerank — this PR only adds the chat-compat dispatch.

Surfaced by AISIX-Cloud's source-blind E2E matrix audit on PR #349 (D3.1 OpenAI-adapter long-tail), which held back Cohere chat-compat scenarios pending this Hub registration.

Fix

OpenAiBridgeaisix-provider-openai/src/bridge.rs

  • COHERE_DEFAULT_BASE = "https://api.cohere.com/compatibility/v1" constant for the with_name("cohere") variant's fallback base.
  • default_base() arm: "cohere" => COHERE_DEFAULT_BASE.
  • normalize_canonical_cohere: operators who paste the bare canonical host https://api.cohere.com (the rerank path / dashboard placeholder) get /compatibility/v1 synthesized for chat. Non-canonical hosts pass through verbatim.

build_hubaisix-server/src/main.rs

  • hub.register(Provider::Cohere, Arc::new(OpenAiBridge::new().with_name("cohere"))).
  • Comment block updated: Jina alone stays rerank-only.

NOT touched

Provider::Cohere.default_base_url() in aisix-core stays as https://api.cohere.com (bare host) — the rerank URL builder appends /v1/rerank. The bridge handles chat-compat in its own resolve_base().

Tests

Three new bridge tests:

TestPins
cohere_default_base_targets_compatibility_v1empty api_base → bridge falls back to /compatibility/v1
cohere_api_base_tolerance_bare_host_synthesizes_compatibility_prefixbare host, trailing slash, full chat URL all normalize; custom host passes through
cohere_chat_compat_round_trips_openai_envelopeend-to-end chat through with_name("cohere") returns the OpenAI envelope verbatim

All 79 aisix-provider-openai tests pass; clippy clean; aisix-server builds clean.

References (per CLAUDE.md §7)

Test plan

  • CI green (cargo test + clippy + fmt)
  • AISIX-Cloud dashboard provider list update: defaultBase: 'https://api.cohere.com/compatibility/v1' (separate AISIX-Cloud PR; bridge tolerance covers bare-host PKs migration-free)
  • Once merged, AISIX-Cloud held-back D3.1 cohere matrix scenarios flip on

Closes#332

Summary by CodeRabbit

  • New Features

    • Added Cohere provider support for chat/completions and embeddings via an OpenAI-compatible bridge; hub now registers Cohere for chat-compat.
  • Documentation

    • Expanded provider docs and schema description to clarify Cohere routes and compatibility coverage.
  • Tests

    • Extended test coverage for Cohere routing, API base normalization variations, and an end-to-end chat round-trip.

Review Change Stack

Cohere exposes an OpenAI-compatible chat endpoint at
`https://api.cohere.com/compatibility/v1/chat/completions` per
<https://docs.cohere.com/reference/chat>. Before this commit `Provider::Cohere`
was intentionally NOT Hub-registered (per #213 Phases 1-2 rerank-only
plan), so a customer who selected Cohere in the dashboard and POSTed
/v1/chat/completions got 503 ProviderUnavailable. The rerank surface
at `/v1/rerank` continues to bypass the Bridge via aisix-proxy::rerank
— this commit only adds the chat-compat dispatch.
Fix
1. `OpenAiBridge` (`aisix-provider-openai/src/bridge.rs`)
- New `COHERE_DEFAULT_BASE = "https://api.cohere.com/compatibility/v1"`
constant for the `with_name("cohere")` variant's fallback base.
- `default_base()` arm: `"cohere" => COHERE_DEFAULT_BASE`.
- `normalize_api_base` arm + `normalize_canonical_cohere`:
operators who paste the bare canonical host
`https://api.cohere.com` (the rerank path / dashboard placeholder)
get `/compatibility/v1` synthesized for chat. Non-canonical
hosts pass through verbatim — operator's path on a custom host
wins.
2. `build_hub()` (`aisix-server/src/main.rs`)
- `hub.register(Provider::Cohere, Arc::new(OpenAiBridge::new().with_name("cohere")))`
- Comment block updated: Jina alone stays rerank-only; Cohere now
serves chat-compat via the bridge.
`Provider::Cohere.default_base_url()` in `aisix-core` stays as
`https://api.cohere.com` (bare host) because the rerank URL builder
appends `/v1/rerank` to it. The bridge handles chat-compat in its
own resolve_base().
Tests
Three new bridge tests under `aisix-provider-openai/src/bridge.rs::tests`:
- `cohere_default_base_targets_compatibility_v1` — empty `api_base`
falls back to `/compatibility/v1`.
- `cohere_api_base_tolerance_bare_host_synthesizes_compatibility_prefix`
— bare host, trailing slash, full chat URL all normalize; a
corporate-proxy host passes through unchanged.
- `cohere_chat_compat_round_trips_openai_envelope` — end-to-end
chat through `with_name("cohere")` returns the OpenAI envelope
verbatim. Pins the contract Hub.register relies on.
All 79 `aisix-provider-openai` tests pass; clippy clean; `aisix-server`
builds clean.
References (per CLAUDE.md §7)
- Cohere chat-compat docs: https://docs.cohere.com/reference/chat
- LiteLLM's Cohere chat-compat handler at
https://github.com/BerriAI/litellm/blob/main/litellm/llms/cohere/chat/transformation.py
uses the same `/compatibility/v1` namespace.
- Portkey's Cohere routing: similar — points at `/compatibility/v1`.
Closes#332
CopilotAI review requested due to automatic review settings May 18, 2026 15:17
@coderabbitai

coderabbitaiBot commented May 18, 2026

Copy link
Copy Markdown
ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Free

Run ID: 7b3bc6ea-948a-4c41-ba18-0a9dfe9c2793

📥 Commits

Reviewing files that changed from the base of the PR and between 848a1a0 and 09e8f81.

📒 Files selected for processing (3)
  • crates/aisix-core/src/models/model.rs
  • crates/aisix-server/src/main.rs
  • schemas/resources/model.schema.json
✅ Files skipped from review due to trivial changes (2)
  • schemas/resources/model.schema.json
  • crates/aisix-core/src/models/model.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/aisix-server/src/main.rs

📝 Walkthrough

Walkthrough

Adds Cohere support for OpenAI-compatible chat by normalizing Cohere canonical bases (synthesizing /compatibility/v1 when appropriate), registering Provider::Cohere with OpenAiBridge, and adding tests verifying base normalization and end-to-end chat compatibility.

Changes

Cohere OpenAI-Compatible Bridge Support

Layer / File(s)Summary
Cohere base and normalization in OpenAiBridge
crates/aisix-provider-openai/src/bridge.rs
Introduces COHERE_DEFAULT_BASE, extends default_base() and normalize_api_base() to dispatch "cohere", and implements COHERE_CANONICAL_HOSTS with normalize_canonical_cohere() to append /compatibility/v1 only for bare canonical hosts while preserving custom api_base values.
Hub registration and Cohere bridge testing
crates/aisix-provider-openai/src/bridge.rs, crates/aisix-server/src/main.rs, crates/aisix-core/src/models/model.rs, schemas/resources/model.schema.json
Registers Provider::Cohere in build_hub() via OpenAiBridge::with_name("cohere"), updates Provider::Cohere documentation and schema description, and adds tests covering default base routing, operator api_base tolerance, and an end-to-end chat compatibility round-trip.

🎯 3 (Moderate) | ⏱️ ~20 minutes


Note

🎁 Summarized by CodeRabbit Free

Your organization has reached its limit of developer seats under the Pro Plan. For new users, CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please add seats to your subscription by visiting https://app.coderabbit.ai/login.If you believe this is a mistake and have available seats, please assign one to the pull request author through the subscription management page using the link above.

Comment @coderabbitai help to get the list of available commands and usage tips.

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR enables Cohere chat-completions dispatch by registering Provider::Cohere with the OpenAI-compatible bridge and teaching that bridge to target Cohere’s /compatibility/v1 namespace.

Changes:

  • Adds Cohere default base URL and canonical host normalization in OpenAiBridge.
  • Registers Cohere in the server Hub using OpenAiBridge::with_name("cohere").
  • Adds bridge tests covering Cohere default base resolution, normalization, and chat round-trip behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

FileDescription
crates/aisix-server/src/main.rsRegisters Cohere on the Hub and updates provider-registration comments.
crates/aisix-provider-openai/src/bridge.rsAdds Cohere base URL handling, normalization, and unit coverage for chat compatibility.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +774 to +782
/// `Provider::Cohere` is registered against the OpenAI-compatible
/// chat endpoint at `https://api.cohere.com/compatibility/v1` (per
/// <https://docs.cohere.com/reference/chat>). Cohere's rerank surface
/// at `/v1/rerank` continues to bypass the Bridge via
/// `aisix-proxy::rerank` — the bridge here only serves `chat/completions`,
/// `embeddings`, and the other OpenAI-shape endpoints the bridge
/// supports. The chat-compat namespace gives an exact OpenAI envelope
/// shape so `OpenAiBridge::with_name("cohere")` can serve it directly
/// (closes #332).
…nt + build_hub registration tests
Independent audit (CLAUDE.md §7) of PR #341 surfaced:
- HIGH-1: `crates/aisix-core/src/models/model.rs:28-31` still
documented `Provider::Cohere` as "exposed for /v1/rerank only"
and "chat / generate APIs are not OpenAI-compatible". Both
statements contradict the new Hub registration. Updated to
describe the chat-compat path (https://api.cohere.com/compatibility/v1
per https://docs.cohere.com/reference/chat) and the model
coverage caveat (command-r / command-a family on chat-compat;
legacy command* not yet bridged).
- MEDIUM-1: nothing test-side ties `Provider::Cohere` to
`OpenAiBridge::with_name("cohere")` — the wiremock test in
bridge.rs uses an override api_base so a regression where
build_hub registered `OpenAiBridge::new()` (default name
"openai") would still pass. Added two tests on `build_hub()`:
- `build_hub_registers_cohere_chat_compat_variant` — asserts
`hub.get(Provider::Cohere).unwrap().name() == "cohere"`. A
`with_name("openai")` fallback would silently route Cohere
chat to OpenAI's host via the bridge's `default_base()`.
- `build_hub_does_not_register_jina_for_chat` — companion check
so a future Jina chat-compat enablement is a deliberate change,
not a silent regression.
MEDIUM-2 (cohere streaming e2e), MEDIUM-3 (tool-use forwarding),
MEDIUM-4 (model coverage gate) — scoped out of this PR per audit's
recommendation, will land as separate enhancements after the
chat-compat baseline merges.
LOW findings deferred (passthrough comment staleness; endpoint-
suffix corner case).
CopilotAI review requested due to automatic review settings May 18, 2026 15:31

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Comment on lines +795 to +798
hub.register(
Provider::Cohere,
Arc::new(OpenAiBridge::new().with_name("cohere")),
);
@moonming
moonming merged commit 9750da0 into mainMay 18, 2026
12 checks passed
janiussyafiq added a commit that referenced this pull request May 20, 2026
Integrate origin/main (commit 2c1d485 = post-PR-#326 / #348 plus
#330 / #341 / #343 / #345 / #346) into this branch via `git merge
--squash` to clear PR #344's lingering `mergeable: dirty` state.
Conflict on `docs/quickstart/self-hosted.md` was a 3-way-merge-base
artifact: base (3596c0a) read `- a reachable etcd instance`, main
changed `a` → `A` (via #326), this branch additionally inserted the
glossary link. Both changes are wanted; resolution per Umar's
approved plan was `git checkout --ours`, which preserves the branch's
self-hosted.md state (already integrates capital A + glossary link
+ first-time-build paragraph + keep-running framing). Other 4
overlapping doc files auto-merged cleanly (`bootstrap-config.md`,
`core-concepts.md`, `first-model-first-key-first-request.md`,
`openai-sdk.md`). Code files all auto-merged cleanly.
Additional Copilot review (post-`167196a` cycle) addressed:
- `docs/index.md:7` — change link display text from `[data-plane]`
to `[data plane]` to match the canonical glossary term. The URL
anchor `#data-plane` stays kebab-case (matches the glossary
heading's auto-anchor); only the display text changes. Comment
id 3271145422.
- `docs/quickstart/openai-sdk.md:43` — change `All three steps below`
to `All commands below`. The Install-the-SDK section has two
command blocks (mkdir+cd, npm install), not three; the prior
wording originated from a mental model (mkdir, cd, install)
that doesn't match the typographic count of code blocks under
the heading. Comment id 3271145458.
Copilot's third comment on `docs/overview/core-concepts.md`
Observability Exporter wording (id 3271145444) auto-resolves via
this merge — main's #326 rewrite supersedes the branch's pre-#326
wording at that location ("ships per-request span telemetry…
OTLP/HTTP-compatible backend…" replaces "Use this concept when
documenting…"). No separate edit needed; the merge IS the fix.
janiussyafiq added a commit that referenced this pull request May 20, 2026
…ickstart-polish
Resolve PR #344's lingering mergeable: dirty state by linking the
branch history to origin/main (2c1d485 = post-#326 / #348 / #330 /
#341 / #343 / #345 / #346).
The squash-merge commit landed earlier (e2af197) integrated main's
content into the branch tree but did not link the histories, so
GitHub's mergeable computation still saw the 3-way-merge-base
artifact conflict on docs/quickstart/self-hosted.md (a vs A + the
glossary link / "In another terminal" vs "Keep the gateway running"
framing). This explicit merge commit ties the branch to main's
history.
Self-hosted.md conflict resolved by taking OUR side — the branch's
edits already contain main's substantive changes (capital A,
first-time-build paragraph) plus this PR's additions (glossary
link, keep-running framing, YOUR_ADMIN_KEY note, config.yaml
location anchor).
The auto-merge of first-model-first-key-first-request.md duplicated
the :::warning callout that was already integrated via the squash
commit; removed the duplicate.
@jarvis9443
jarvis9443 deleted the fix/cohere-chat-hub-register branch June 25, 2026 06:26
Sign up for freeto 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.

Cohere chat-compat Hub registration missing — /v1/chat/completions returns 503 ProviderUnavailable

2 participants

@moonming
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(server): register Provider::Cohere on Hub for chat-compat (#332) - #341

Merged
moonming merged 3 commits into
mainfrom
fix/cohere-chat-hub-register
May 18, 2026
Merged

feat(server): register Provider::Cohere on Hub for chat-compat (#332)#341
moonming merged 3 commits into
mainfrom
fix/cohere-chat-hub-register

Conversation

@moonming

@moonmingmoonming commented May 18, 2026

Copy link
Copy Markdown
Member

Summary

Cohere exposes an OpenAI-compatible chat endpoint at https://api.cohere.com/compatibility/v1/chat/completions. Until this PR Provider::Cohere was intentionally NOT Hub-registered (per #213 Phases 1-2 rerank-only plan), so customers who selected Cohere in the dashboard and POSTed /v1/chat/completions got 503 ProviderUnavailable. The rerank surface at /v1/rerank continues to bypass the Bridge via aisix-proxy::rerank — this PR only adds the chat-compat dispatch.

Surfaced by AISIX-Cloud's source-blind E2E matrix audit on PR #349 (D3.1 OpenAI-adapter long-tail), which held back Cohere chat-compat scenarios pending this Hub registration.

Fix

OpenAiBridgeaisix-provider-openai/src/bridge.rs

  • COHERE_DEFAULT_BASE = "https://api.cohere.com/compatibility/v1" constant for the with_name("cohere") variant's fallback base.
  • default_base() arm: "cohere" => COHERE_DEFAULT_BASE.
  • normalize_canonical_cohere: operators who paste the bare canonical host https://api.cohere.com (the rerank path / dashboard placeholder) get /compatibility/v1 synthesized for chat. Non-canonical hosts pass through verbatim.

build_hubaisix-server/src/main.rs

  • hub.register(Provider::Cohere, Arc::new(OpenAiBridge::new().with_name("cohere"))).
  • Comment block updated: Jina alone stays rerank-only.

NOT touched

Provider::Cohere.default_base_url() in aisix-core stays as https://api.cohere.com (bare host) — the rerank URL builder appends /v1/rerank. The bridge handles chat-compat in its own resolve_base().

Tests

Three new bridge tests:

TestPins
cohere_default_base_targets_compatibility_v1empty api_base → bridge falls back to /compatibility/v1
cohere_api_base_tolerance_bare_host_synthesizes_compatibility_prefixbare host, trailing slash, full chat URL all normalize; custom host passes through
cohere_chat_compat_round_trips_openai_envelopeend-to-end chat through with_name("cohere") returns the OpenAI envelope verbatim

All 79 aisix-provider-openai tests pass; clippy clean; aisix-server builds clean.

References (per CLAUDE.md §7)

Test plan

  • CI green (cargo test + clippy + fmt)
  • AISIX-Cloud dashboard provider list update: defaultBase: 'https://api.cohere.com/compatibility/v1' (separate AISIX-Cloud PR; bridge tolerance covers bare-host PKs migration-free)
  • Once merged, AISIX-Cloud held-back D3.1 cohere matrix scenarios flip on

Closes#332

Summary by CodeRabbit

  • New Features

    • Added Cohere provider support for chat/completions and embeddings via an OpenAI-compatible bridge; hub now registers Cohere for chat-compat.
  • Documentation

    • Expanded provider docs and schema description to clarify Cohere routes and compatibility coverage.
  • Tests

    • Extended test coverage for Cohere routing, API base normalization variations, and an end-to-end chat round-trip.

Review Change Stack

Cohere exposes an OpenAI-compatible chat endpoint at
`https://api.cohere.com/compatibility/v1/chat/completions` per
<https://docs.cohere.com/reference/chat>. Before this commit `Provider::Cohere`
was intentionally NOT Hub-registered (per #213 Phases 1-2 rerank-only
plan), so a customer who selected Cohere in the dashboard and POSTed
/v1/chat/completions got 503 ProviderUnavailable. The rerank surface
at `/v1/rerank` continues to bypass the Bridge via aisix-proxy::rerank
— this commit only adds the chat-compat dispatch.
Fix
1. `OpenAiBridge` (`aisix-provider-openai/src/bridge.rs`)
- New `COHERE_DEFAULT_BASE = "https://api.cohere.com/compatibility/v1"`
constant for the `with_name("cohere")` variant's fallback base.
- `default_base()` arm: `"cohere" => COHERE_DEFAULT_BASE`.
- `normalize_api_base` arm + `normalize_canonical_cohere`:
operators who paste the bare canonical host
`https://api.cohere.com` (the rerank path / dashboard placeholder)
get `/compatibility/v1` synthesized for chat. Non-canonical
hosts pass through verbatim — operator's path on a custom host
wins.
2. `build_hub()` (`aisix-server/src/main.rs`)
- `hub.register(Provider::Cohere, Arc::new(OpenAiBridge::new().with_name("cohere")))`
- Comment block updated: Jina alone stays rerank-only; Cohere now
serves chat-compat via the bridge.
`Provider::Cohere.default_base_url()` in `aisix-core` stays as
`https://api.cohere.com` (bare host) because the rerank URL builder
appends `/v1/rerank` to it. The bridge handles chat-compat in its
own resolve_base().
Tests
Three new bridge tests under `aisix-provider-openai/src/bridge.rs::tests`:
- `cohere_default_base_targets_compatibility_v1` — empty `api_base`
falls back to `/compatibility/v1`.
- `cohere_api_base_tolerance_bare_host_synthesizes_compatibility_prefix`
— bare host, trailing slash, full chat URL all normalize; a
corporate-proxy host passes through unchanged.
- `cohere_chat_compat_round_trips_openai_envelope` — end-to-end
chat through `with_name("cohere")` returns the OpenAI envelope
verbatim. Pins the contract Hub.register relies on.
All 79 `aisix-provider-openai` tests pass; clippy clean; `aisix-server`
builds clean.
References (per CLAUDE.md §7)
- Cohere chat-compat docs: https://docs.cohere.com/reference/chat
- LiteLLM's Cohere chat-compat handler at
https://github.com/BerriAI/litellm/blob/main/litellm/llms/cohere/chat/transformation.py
uses the same `/compatibility/v1` namespace.
- Portkey's Cohere routing: similar — points at `/compatibility/v1`.
Closes#332
CopilotAI review requested due to automatic review settings May 18, 2026 15:17
@coderabbitai

coderabbitaiBot commented May 18, 2026

Copy link
Copy Markdown
ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Free

Run ID: 7b3bc6ea-948a-4c41-ba18-0a9dfe9c2793

📥 Commits

Reviewing files that changed from the base of the PR and between 848a1a0 and 09e8f81.

📒 Files selected for processing (3)
  • crates/aisix-core/src/models/model.rs
  • crates/aisix-server/src/main.rs
  • schemas/resources/model.schema.json
✅ Files skipped from review due to trivial changes (2)
  • schemas/resources/model.schema.json
  • crates/aisix-core/src/models/model.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/aisix-server/src/main.rs

📝 Walkthrough

Walkthrough

Adds Cohere support for OpenAI-compatible chat by normalizing Cohere canonical bases (synthesizing /compatibility/v1 when appropriate), registering Provider::Cohere with OpenAiBridge, and adding tests verifying base normalization and end-to-end chat compatibility.

Changes

Cohere OpenAI-Compatible Bridge Support

Layer / File(s)Summary
Cohere base and normalization in OpenAiBridge
crates/aisix-provider-openai/src/bridge.rs
Introduces COHERE_DEFAULT_BASE, extends default_base() and normalize_api_base() to dispatch "cohere", and implements COHERE_CANONICAL_HOSTS with normalize_canonical_cohere() to append /compatibility/v1 only for bare canonical hosts while preserving custom api_base values.
Hub registration and Cohere bridge testing
crates/aisix-provider-openai/src/bridge.rs, crates/aisix-server/src/main.rs, crates/aisix-core/src/models/model.rs, schemas/resources/model.schema.json
Registers Provider::Cohere in build_hub() via OpenAiBridge::with_name("cohere"), updates Provider::Cohere documentation and schema description, and adds tests covering default base routing, operator api_base tolerance, and an end-to-end chat compatibility round-trip.

🎯 3 (Moderate) | ⏱️ ~20 minutes


Note

🎁 Summarized by CodeRabbit Free

Your organization has reached its limit of developer seats under the Pro Plan. For new users, CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please add seats to your subscription by visiting https://app.coderabbit.ai/login.If you believe this is a mistake and have available seats, please assign one to the pull request author through the subscription management page using the link above.

Comment @coderabbitai help to get the list of available commands and usage tips.

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR enables Cohere chat-completions dispatch by registering Provider::Cohere with the OpenAI-compatible bridge and teaching that bridge to target Cohere’s /compatibility/v1 namespace.

Changes:

  • Adds Cohere default base URL and canonical host normalization in OpenAiBridge.
  • Registers Cohere in the server Hub using OpenAiBridge::with_name("cohere").
  • Adds bridge tests covering Cohere default base resolution, normalization, and chat round-trip behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

FileDescription
crates/aisix-server/src/main.rsRegisters Cohere on the Hub and updates provider-registration comments.
crates/aisix-provider-openai/src/bridge.rsAdds Cohere base URL handling, normalization, and unit coverage for chat compatibility.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +774 to +782
/// `Provider::Cohere` is registered against the OpenAI-compatible
/// chat endpoint at `https://api.cohere.com/compatibility/v1` (per
/// <https://docs.cohere.com/reference/chat>). Cohere's rerank surface
/// at `/v1/rerank` continues to bypass the Bridge via
/// `aisix-proxy::rerank` — the bridge here only serves `chat/completions`,
/// `embeddings`, and the other OpenAI-shape endpoints the bridge
/// supports. The chat-compat namespace gives an exact OpenAI envelope
/// shape so `OpenAiBridge::with_name("cohere")` can serve it directly
/// (closes #332).
…nt + build_hub registration tests
Independent audit (CLAUDE.md §7) of PR #341 surfaced:
- HIGH-1: `crates/aisix-core/src/models/model.rs:28-31` still
documented `Provider::Cohere` as "exposed for /v1/rerank only"
and "chat / generate APIs are not OpenAI-compatible". Both
statements contradict the new Hub registration. Updated to
describe the chat-compat path (https://api.cohere.com/compatibility/v1
per https://docs.cohere.com/reference/chat) and the model
coverage caveat (command-r / command-a family on chat-compat;
legacy command* not yet bridged).
- MEDIUM-1: nothing test-side ties `Provider::Cohere` to
`OpenAiBridge::with_name("cohere")` — the wiremock test in
bridge.rs uses an override api_base so a regression where
build_hub registered `OpenAiBridge::new()` (default name
"openai") would still pass. Added two tests on `build_hub()`:
- `build_hub_registers_cohere_chat_compat_variant` — asserts
`hub.get(Provider::Cohere).unwrap().name() == "cohere"`. A
`with_name("openai")` fallback would silently route Cohere
chat to OpenAI's host via the bridge's `default_base()`.
- `build_hub_does_not_register_jina_for_chat` — companion check
so a future Jina chat-compat enablement is a deliberate change,
not a silent regression.
MEDIUM-2 (cohere streaming e2e), MEDIUM-3 (tool-use forwarding),
MEDIUM-4 (model coverage gate) — scoped out of this PR per audit's
recommendation, will land as separate enhancements after the
chat-compat baseline merges.
LOW findings deferred (passthrough comment staleness; endpoint-
suffix corner case).
CopilotAI review requested due to automatic review settings May 18, 2026 15:31

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Comment on lines +795 to +798
hub.register(
Provider::Cohere,
Arc::new(OpenAiBridge::new().with_name("cohere")),
);
@moonming
moonming merged commit 9750da0 into mainMay 18, 2026
12 checks passed
janiussyafiq added a commit that referenced this pull request May 20, 2026
Integrate origin/main (commit 2c1d485 = post-PR-#326 / #348 plus
#330 / #341 / #343 / #345 / #346) into this branch via `git merge
--squash` to clear PR #344's lingering `mergeable: dirty` state.
Conflict on `docs/quickstart/self-hosted.md` was a 3-way-merge-base
artifact: base (3596c0a) read `- a reachable etcd instance`, main
changed `a` → `A` (via #326), this branch additionally inserted the
glossary link. Both changes are wanted; resolution per Umar's
approved plan was `git checkout --ours`, which preserves the branch's
self-hosted.md state (already integrates capital A + glossary link
+ first-time-build paragraph + keep-running framing). Other 4
overlapping doc files auto-merged cleanly (`bootstrap-config.md`,
`core-concepts.md`, `first-model-first-key-first-request.md`,
`openai-sdk.md`). Code files all auto-merged cleanly.
Additional Copilot review (post-`167196a` cycle) addressed:
- `docs/index.md:7` — change link display text from `[data-plane]`
to `[data plane]` to match the canonical glossary term. The URL
anchor `#data-plane` stays kebab-case (matches the glossary
heading's auto-anchor); only the display text changes. Comment
id 3271145422.
- `docs/quickstart/openai-sdk.md:43` — change `All three steps below`
to `All commands below`. The Install-the-SDK section has two
command blocks (mkdir+cd, npm install), not three; the prior
wording originated from a mental model (mkdir, cd, install)
that doesn't match the typographic count of code blocks under
the heading. Comment id 3271145458.
Copilot's third comment on `docs/overview/core-concepts.md`
Observability Exporter wording (id 3271145444) auto-resolves via
this merge — main's #326 rewrite supersedes the branch's pre-#326
wording at that location ("ships per-request span telemetry…
OTLP/HTTP-compatible backend…" replaces "Use this concept when
documenting…"). No separate edit needed; the merge IS the fix.
janiussyafiq added a commit that referenced this pull request May 20, 2026
…ickstart-polish
Resolve PR #344's lingering mergeable: dirty state by linking the
branch history to origin/main (2c1d485 = post-#326 / #348 / #330 /
#341 / #343 / #345 / #346).
The squash-merge commit landed earlier (e2af197) integrated main's
content into the branch tree but did not link the histories, so
GitHub's mergeable computation still saw the 3-way-merge-base
artifact conflict on docs/quickstart/self-hosted.md (a vs A + the
glossary link / "In another terminal" vs "Keep the gateway running"
framing). This explicit merge commit ties the branch to main's
history.
Self-hosted.md conflict resolved by taking OUR side — the branch's
edits already contain main's substantive changes (capital A,
first-time-build paragraph) plus this PR's additions (glossary
link, keep-running framing, YOUR_ADMIN_KEY note, config.yaml
location anchor).
The auto-merge of first-model-first-key-first-request.md duplicated
the :::warning callout that was already integrated via the squash
commit; removed the duplicate.
@jarvis9443
jarvis9443 deleted the fix/cohere-chat-hub-register branch June 25, 2026 06:26
Sign up for freeto 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.

Cohere chat-compat Hub registration missing — /v1/chat/completions returns 503 ProviderUnavailable

2 participants

@moonming
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

feat(server): register Provider::Cohere on Hub for chat-compat (#332) - #341

Merged
moonming merged 3 commits into
mainfrom
fix/cohere-chat-hub-register
May 18, 2026
Merged

feat(server): register Provider::Cohere on Hub for chat-compat (#332)#341
moonming merged 3 commits into
mainfrom
fix/cohere-chat-hub-register

Conversation

@moonming

@moonmingmoonming commented May 18, 2026

Copy link
Copy Markdown
Member

Summary

Cohere exposes an OpenAI-compatible chat endpoint at https://api.cohere.com/compatibility/v1/chat/completions. Until this PR Provider::Cohere was intentionally NOT Hub-registered (per #213 Phases 1-2 rerank-only plan), so customers who selected Cohere in the dashboard and POSTed /v1/chat/completions got 503 ProviderUnavailable. The rerank surface at /v1/rerank continues to bypass the Bridge via aisix-proxy::rerank — this PR only adds the chat-compat dispatch.

Surfaced by AISIX-Cloud's source-blind E2E matrix audit on PR #349 (D3.1 OpenAI-adapter long-tail), which held back Cohere chat-compat scenarios pending this Hub registration.

Fix

OpenAiBridgeaisix-provider-openai/src/bridge.rs

  • COHERE_DEFAULT_BASE = "https://api.cohere.com/compatibility/v1" constant for the with_name("cohere") variant's fallback base.
  • default_base() arm: "cohere" => COHERE_DEFAULT_BASE.
  • normalize_canonical_cohere: operators who paste the bare canonical host https://api.cohere.com (the rerank path / dashboard placeholder) get /compatibility/v1 synthesized for chat. Non-canonical hosts pass through verbatim.

build_hubaisix-server/src/main.rs

  • hub.register(Provider::Cohere, Arc::new(OpenAiBridge::new().with_name("cohere"))).
  • Comment block updated: Jina alone stays rerank-only.

NOT touched

Provider::Cohere.default_base_url() in aisix-core stays as https://api.cohere.com (bare host) — the rerank URL builder appends /v1/rerank. The bridge handles chat-compat in its own resolve_base().

Tests

Three new bridge tests:

TestPins
cohere_default_base_targets_compatibility_v1empty api_base → bridge falls back to /compatibility/v1
cohere_api_base_tolerance_bare_host_synthesizes_compatibility_prefixbare host, trailing slash, full chat URL all normalize; custom host passes through
cohere_chat_compat_round_trips_openai_envelopeend-to-end chat through with_name("cohere") returns the OpenAI envelope verbatim

All 79 aisix-provider-openai tests pass; clippy clean; aisix-server builds clean.

References (per CLAUDE.md §7)

Test plan

  • CI green (cargo test + clippy + fmt)
  • AISIX-Cloud dashboard provider list update: defaultBase: 'https://api.cohere.com/compatibility/v1' (separate AISIX-Cloud PR; bridge tolerance covers bare-host PKs migration-free)
  • Once merged, AISIX-Cloud held-back D3.1 cohere matrix scenarios flip on

Closes#332

Summary by CodeRabbit

  • New Features

    • Added Cohere provider support for chat/completions and embeddings via an OpenAI-compatible bridge; hub now registers Cohere for chat-compat.
  • Documentation

    • Expanded provider docs and schema description to clarify Cohere routes and compatibility coverage.
  • Tests

    • Extended test coverage for Cohere routing, API base normalization variations, and an end-to-end chat round-trip.

Review Change Stack

Cohere exposes an OpenAI-compatible chat endpoint at
`https://api.cohere.com/compatibility/v1/chat/completions` per
<https://docs.cohere.com/reference/chat>. Before this commit `Provider::Cohere`
was intentionally NOT Hub-registered (per #213 Phases 1-2 rerank-only
plan), so a customer who selected Cohere in the dashboard and POSTed
/v1/chat/completions got 503 ProviderUnavailable. The rerank surface
at `/v1/rerank` continues to bypass the Bridge via aisix-proxy::rerank
— this commit only adds the chat-compat dispatch.
Fix
1. `OpenAiBridge` (`aisix-provider-openai/src/bridge.rs`)
- New `COHERE_DEFAULT_BASE = "https://api.cohere.com/compatibility/v1"`
constant for the `with_name("cohere")` variant's fallback base.
- `default_base()` arm: `"cohere" => COHERE_DEFAULT_BASE`.
- `normalize_api_base` arm + `normalize_canonical_cohere`:
operators who paste the bare canonical host
`https://api.cohere.com` (the rerank path / dashboard placeholder)
get `/compatibility/v1` synthesized for chat. Non-canonical
hosts pass through verbatim — operator's path on a custom host
wins.
2. `build_hub()` (`aisix-server/src/main.rs`)
- `hub.register(Provider::Cohere, Arc::new(OpenAiBridge::new().with_name("cohere")))`
- Comment block updated: Jina alone stays rerank-only; Cohere now
serves chat-compat via the bridge.
`Provider::Cohere.default_base_url()` in `aisix-core` stays as
`https://api.cohere.com` (bare host) because the rerank URL builder
appends `/v1/rerank` to it. The bridge handles chat-compat in its
own resolve_base().
Tests
Three new bridge tests under `aisix-provider-openai/src/bridge.rs::tests`:
- `cohere_default_base_targets_compatibility_v1` — empty `api_base`
falls back to `/compatibility/v1`.
- `cohere_api_base_tolerance_bare_host_synthesizes_compatibility_prefix`
— bare host, trailing slash, full chat URL all normalize; a
corporate-proxy host passes through unchanged.
- `cohere_chat_compat_round_trips_openai_envelope` — end-to-end
chat through `with_name("cohere")` returns the OpenAI envelope
verbatim. Pins the contract Hub.register relies on.
All 79 `aisix-provider-openai` tests pass; clippy clean; `aisix-server`
builds clean.
References (per CLAUDE.md §7)
- Cohere chat-compat docs: https://docs.cohere.com/reference/chat
- LiteLLM's Cohere chat-compat handler at
https://github.com/BerriAI/litellm/blob/main/litellm/llms/cohere/chat/transformation.py
uses the same `/compatibility/v1` namespace.
- Portkey's Cohere routing: similar — points at `/compatibility/v1`.
Closes#332
CopilotAI review requested due to automatic review settings May 18, 2026 15:17
@coderabbitai

coderabbitaiBot commented May 18, 2026

Copy link
Copy Markdown
ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Free

Run ID: 7b3bc6ea-948a-4c41-ba18-0a9dfe9c2793

📥 Commits

Reviewing files that changed from the base of the PR and between 848a1a0 and 09e8f81.

📒 Files selected for processing (3)
  • crates/aisix-core/src/models/model.rs
  • crates/aisix-server/src/main.rs
  • schemas/resources/model.schema.json
✅ Files skipped from review due to trivial changes (2)
  • schemas/resources/model.schema.json
  • crates/aisix-core/src/models/model.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/aisix-server/src/main.rs

📝 Walkthrough

Walkthrough

Adds Cohere support for OpenAI-compatible chat by normalizing Cohere canonical bases (synthesizing /compatibility/v1 when appropriate), registering Provider::Cohere with OpenAiBridge, and adding tests verifying base normalization and end-to-end chat compatibility.

Changes

Cohere OpenAI-Compatible Bridge Support

Layer / File(s)Summary
Cohere base and normalization in OpenAiBridge
crates/aisix-provider-openai/src/bridge.rs
Introduces COHERE_DEFAULT_BASE, extends default_base() and normalize_api_base() to dispatch "cohere", and implements COHERE_CANONICAL_HOSTS with normalize_canonical_cohere() to append /compatibility/v1 only for bare canonical hosts while preserving custom api_base values.
Hub registration and Cohere bridge testing
crates/aisix-provider-openai/src/bridge.rs, crates/aisix-server/src/main.rs, crates/aisix-core/src/models/model.rs, schemas/resources/model.schema.json
Registers Provider::Cohere in build_hub() via OpenAiBridge::with_name("cohere"), updates Provider::Cohere documentation and schema description, and adds tests covering default base routing, operator api_base tolerance, and an end-to-end chat compatibility round-trip.

🎯 3 (Moderate) | ⏱️ ~20 minutes


Note

🎁 Summarized by CodeRabbit Free

Your organization has reached its limit of developer seats under the Pro Plan. For new users, CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please add seats to your subscription by visiting https://app.coderabbit.ai/login.If you believe this is a mistake and have available seats, please assign one to the pull request author through the subscription management page using the link above.

Comment @coderabbitai help to get the list of available commands and usage tips.

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR enables Cohere chat-completions dispatch by registering Provider::Cohere with the OpenAI-compatible bridge and teaching that bridge to target Cohere’s /compatibility/v1 namespace.

Changes:

  • Adds Cohere default base URL and canonical host normalization in OpenAiBridge.
  • Registers Cohere in the server Hub using OpenAiBridge::with_name("cohere").
  • Adds bridge tests covering Cohere default base resolution, normalization, and chat round-trip behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

FileDescription
crates/aisix-server/src/main.rsRegisters Cohere on the Hub and updates provider-registration comments.
crates/aisix-provider-openai/src/bridge.rsAdds Cohere base URL handling, normalization, and unit coverage for chat compatibility.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +774 to +782
/// `Provider::Cohere` is registered against the OpenAI-compatible
/// chat endpoint at `https://api.cohere.com/compatibility/v1` (per
/// <https://docs.cohere.com/reference/chat>). Cohere's rerank surface
/// at `/v1/rerank` continues to bypass the Bridge via
/// `aisix-proxy::rerank` — the bridge here only serves `chat/completions`,
/// `embeddings`, and the other OpenAI-shape endpoints the bridge
/// supports. The chat-compat namespace gives an exact OpenAI envelope
/// shape so `OpenAiBridge::with_name("cohere")` can serve it directly
/// (closes #332).
…nt + build_hub registration tests
Independent audit (CLAUDE.md §7) of PR #341 surfaced:
- HIGH-1: `crates/aisix-core/src/models/model.rs:28-31` still
documented `Provider::Cohere` as "exposed for /v1/rerank only"
and "chat / generate APIs are not OpenAI-compatible". Both
statements contradict the new Hub registration. Updated to
describe the chat-compat path (https://api.cohere.com/compatibility/v1
per https://docs.cohere.com/reference/chat) and the model
coverage caveat (command-r / command-a family on chat-compat;
legacy command* not yet bridged).
- MEDIUM-1: nothing test-side ties `Provider::Cohere` to
`OpenAiBridge::with_name("cohere")` — the wiremock test in
bridge.rs uses an override api_base so a regression where
build_hub registered `OpenAiBridge::new()` (default name
"openai") would still pass. Added two tests on `build_hub()`:
- `build_hub_registers_cohere_chat_compat_variant` — asserts
`hub.get(Provider::Cohere).unwrap().name() == "cohere"`. A
`with_name("openai")` fallback would silently route Cohere
chat to OpenAI's host via the bridge's `default_base()`.
- `build_hub_does_not_register_jina_for_chat` — companion check
so a future Jina chat-compat enablement is a deliberate change,
not a silent regression.
MEDIUM-2 (cohere streaming e2e), MEDIUM-3 (tool-use forwarding),
MEDIUM-4 (model coverage gate) — scoped out of this PR per audit's
recommendation, will land as separate enhancements after the
chat-compat baseline merges.
LOW findings deferred (passthrough comment staleness; endpoint-
suffix corner case).
CopilotAI review requested due to automatic review settings May 18, 2026 15:31

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Comment on lines +795 to +798
hub.register(
Provider::Cohere,
Arc::new(OpenAiBridge::new().with_name("cohere")),
);
@moonming
moonming merged commit 9750da0 into mainMay 18, 2026
12 checks passed
janiussyafiq added a commit that referenced this pull request May 20, 2026
Integrate origin/main (commit 2c1d485 = post-PR-#326 / #348 plus
#330 / #341 / #343 / #345 / #346) into this branch via `git merge
--squash` to clear PR #344's lingering `mergeable: dirty` state.
Conflict on `docs/quickstart/self-hosted.md` was a 3-way-merge-base
artifact: base (3596c0a) read `- a reachable etcd instance`, main
changed `a` → `A` (via #326), this branch additionally inserted the
glossary link. Both changes are wanted; resolution per Umar's
approved plan was `git checkout --ours`, which preserves the branch's
self-hosted.md state (already integrates capital A + glossary link
+ first-time-build paragraph + keep-running framing). Other 4
overlapping doc files auto-merged cleanly (`bootstrap-config.md`,
`core-concepts.md`, `first-model-first-key-first-request.md`,
`openai-sdk.md`). Code files all auto-merged cleanly.
Additional Copilot review (post-`167196a` cycle) addressed:
- `docs/index.md:7` — change link display text from `[data-plane]`
to `[data plane]` to match the canonical glossary term. The URL
anchor `#data-plane` stays kebab-case (matches the glossary
heading's auto-anchor); only the display text changes. Comment
id 3271145422.
- `docs/quickstart/openai-sdk.md:43` — change `All three steps below`
to `All commands below`. The Install-the-SDK section has two
command blocks (mkdir+cd, npm install), not three; the prior
wording originated from a mental model (mkdir, cd, install)
that doesn't match the typographic count of code blocks under
the heading. Comment id 3271145458.
Copilot's third comment on `docs/overview/core-concepts.md`
Observability Exporter wording (id 3271145444) auto-resolves via
this merge — main's #326 rewrite supersedes the branch's pre-#326
wording at that location ("ships per-request span telemetry…
OTLP/HTTP-compatible backend…" replaces "Use this concept when
documenting…"). No separate edit needed; the merge IS the fix.
janiussyafiq added a commit that referenced this pull request May 20, 2026
…ickstart-polish
Resolve PR #344's lingering mergeable: dirty state by linking the
branch history to origin/main (2c1d485 = post-#326 / #348 / #330 /
#341 / #343 / #345 / #346).
The squash-merge commit landed earlier (e2af197) integrated main's
content into the branch tree but did not link the histories, so
GitHub's mergeable computation still saw the 3-way-merge-base
artifact conflict on docs/quickstart/self-hosted.md (a vs A + the
glossary link / "In another terminal" vs "Keep the gateway running"
framing). This explicit merge commit ties the branch to main's
history.
Self-hosted.md conflict resolved by taking OUR side — the branch's
edits already contain main's substantive changes (capital A,
first-time-build paragraph) plus this PR's additions (glossary
link, keep-running framing, YOUR_ADMIN_KEY note, config.yaml
location anchor).
The auto-merge of first-model-first-key-first-request.md duplicated
the :::warning callout that was already integrated via the squash
commit; removed the duplicate.
@jarvis9443
jarvis9443 deleted the fix/cohere-chat-hub-register branch June 25, 2026 06:26
Sign up for freeto 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.

Cohere chat-compat Hub registration missing — /v1/chat/completions returns 503 ProviderUnavailable

2 participants

@moonming