mirror of
https://github.com/Alishahryar1/free-claude-code.git
synced 2026-07-09 16:00:45 +00:00
## Problem
FCC does not expose Cohere's OpenAI-compatible chat models, so users
with Cohere keys cannot route Claude, Codex, or messaging prompts
through Cohere.
## Changes
| Before | After |
| --- | --- |
| Provider catalog did not include Cohere. | Provider catalog includes
Cohere with `COHERE_API_KEY`, `COHERE_PROXY`, admin status, and smoke
model wiring. |
| Requests could not target Cohere's compatibility API. |
`providers/cohere` routes OpenAI-chat requests to Cohere's compatibility
API with Cohere-specific request policy. |
| Docs and templates omitted Cohere setup. | README, `.env.example`, and
architecture docs document Cohere setup and ownership. |
| Cohere behavior had no regression coverage. | Provider, runtime,
admin, config, smoke, and catalog tests cover Cohere integration. |
<!-- greptile_comment -->
<details open><summary><h3>Greptile Summary</h3></summary>
This PR adds Cohere as a new OpenAI-compatible chat provider. The main
changes are:
- Cohere provider metadata in the catalog, settings, Admin UI manifest,
and runtime factory.
- A new `CohereProvider` using the shared OpenAI chat transport with
Cohere-specific request shaping.
- Cohere API key, proxy, smoke model, README, architecture, and
environment template updates.
- Tests for Admin config, settings, provider catalog order, smoke
config, runtime creation, and Cohere request/stream behavior.
- Version and lockfile updates for the new provider feature.
</details>
<h3>Confidence Score: 5/5</h3>
Safe to merge with minimal risk.
No functional, security, or contract issues were identified. Cohere is
consistently wired through settings, catalog metadata, factory creation,
Admin config, smoke defaults, docs, versioning, and targeted tests. The
implemented Cohere `reasoning_effort` values match the Compatibility API
behavior checked during review.
No files require special attention.
<details><summary><h3><a href="https://www.greptile.com/trex"><img
alt="T-Rex"
src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg"
height="20" align="absmiddle"></a> T-Rex Logs</h3></summary>
**What T-Rex did**
- Validated the provider runtime handling of Cohere requests, including
the request body policy, streaming parsing, and default base URL and API
key behavior.
- Verified that the runtime descriptor wiring and provider config
proxy/key behavior pass in the general contract validation.
- Confirmed the admin/config smoke contract artifact shows the Cohere
environment settings, admin config masking, feature/provider catalog
contracts, and smoke configuration passing.
- Compared the initial -01-before.log and the clean -02-after.log
captures to confirm the same scoped commands are present and both exit
with code 0.
<a
href="https://app.greptile.com/trex/runs/13309850/artifacts"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifactsDark.svg?v=4"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"><img
alt="View all artifacts"
src="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"></picture></a>
<sub><a href="https://www.greptile.com/trex"><img alt="T-Rex"
src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg"
height="14" align="absmiddle"></a> Ran code and verified through
T-Rex</sub>
</details>
<details open><summary><h3>Important Files Changed</h3></summary>
| Filename | Overview |
|----------|----------|
| providers/cohere/client.py | Implements Cohere request shaping over
shared OpenAI chat transport, including allowed extra body and reasoning
mapping; no issues found. |
| config/provider_catalog.py | Registers Cohere with credential, proxy,
base URL, transport, and capability metadata; no issues found. |
| providers/runtime/factory.py | Wires Cohere into runtime provider
factory dispatch; no issues found. |
| config/settings.py | Adds Cohere API key and proxy settings aliases;
no issues found. |
| api/admin_config/provider_manifest.py | Adds Cohere API key
labeling/description through catalog-derived Admin fields; no issues
found. |
| smoke/lib/config.py | Adds Cohere default smoke model and credential
detection; no issues found. |
| tests/providers/test_cohere.py | Adds request-policy and streaming
adapter tests for the Cohere provider; no issues found. |
| tests/providers/test_provider_runtime.py | Adds Cohere descriptor,
config build, and factory instantiation coverage; no issues found. |
| README.md | Adds Cohere setup instructions and updates provider
counts/order; no issues found. |
| pyproject.toml | Bumps package version for the new provider feature;
no issues found. |
| uv.lock | Updates the lockfile package version to match
`pyproject.toml`; no issues found. |
</details>
<details open><summary><h3>Sequence Diagram</h3></summary>
<a href="#gh-light-mode-only">
```mermaid
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant User as User/Admin config
participant Catalog as Provider catalog/settings
participant Factory as Runtime factory
participant Cohere as CohereProvider
participant Transport as OpenAI chat transport
participant API as Cohere Compatibility API
User->>Catalog: "Configure MODEL=cohere/... and COHERE_API_KEY"
Catalog->>Factory: Build ProviderConfig for provider_id cohere
Factory->>Cohere: Instantiate CohereProvider(config)
Cohere->>Transport: Build chat body with Cohere policy
Transport->>API: "POST /chat/completions stream=true"
API-->>Transport: Streaming OpenAI-compatible chunks
Transport-->>User: Anthropic SSE events
```
</a>
<a href="#gh-dark-mode-only">
```mermaid
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant User as User/Admin config
participant Catalog as Provider catalog/settings
participant Factory as Runtime factory
participant Cohere as CohereProvider
participant Transport as OpenAI chat transport
participant API as Cohere Compatibility API
User->>Catalog: "Configure MODEL=cohere/... and COHERE_API_KEY"
Catalog->>Factory: Build ProviderConfig for provider_id cohere
Factory->>Cohere: Instantiate CohereProvider(config)
Cohere->>Transport: Build chat body with Cohere policy
Transport->>API: "POST /chat/completions stream=true"
API-->>Transport: Streaming OpenAI-compatible chunks
Transport-->>User: Anthropic SSE events
```
</a>
</details>
<sub>Reviews (1): Last reviewed commit: ["Add Cohere
provider"](7956802968)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=41887611)</sub>
<!-- /greptile_comment -->
199 lines
6 KiB
Python
199 lines
6 KiB
Python
"""Provider factory wiring and lazy adapter construction."""
|
|
|
|
from collections.abc import Callable
|
|
|
|
from config.provider_catalog import (
|
|
PROVIDER_CATALOG,
|
|
SUPPORTED_PROVIDER_IDS,
|
|
)
|
|
from config.settings import Settings
|
|
from providers.base import BaseProvider, ProviderConfig
|
|
from providers.exceptions import UnknownProviderTypeError
|
|
|
|
from .config import build_provider_config
|
|
|
|
ProviderFactory = Callable[[ProviderConfig, Settings], BaseProvider]
|
|
|
|
|
|
def _create_nvidia_nim(config: ProviderConfig, settings: Settings) -> BaseProvider:
|
|
from providers.nvidia_nim import NvidiaNimProvider
|
|
|
|
return NvidiaNimProvider(config, nim_settings=settings.nim)
|
|
|
|
|
|
def _create_open_router(config: ProviderConfig, _settings: Settings) -> BaseProvider:
|
|
from providers.open_router import OpenRouterProvider
|
|
|
|
return OpenRouterProvider(config)
|
|
|
|
|
|
def _create_mistral(config: ProviderConfig, _settings: Settings) -> BaseProvider:
|
|
from providers.mistral import MistralProvider
|
|
|
|
return MistralProvider(config)
|
|
|
|
|
|
def _create_mistral_codestral(
|
|
config: ProviderConfig, _settings: Settings
|
|
) -> BaseProvider:
|
|
from providers.codestral import CodestralProvider
|
|
|
|
return CodestralProvider(config)
|
|
|
|
|
|
def _create_deepseek(config: ProviderConfig, _settings: Settings) -> BaseProvider:
|
|
from providers.deepseek import DeepSeekProvider
|
|
|
|
return DeepSeekProvider(config)
|
|
|
|
|
|
def _create_lmstudio(config: ProviderConfig, _settings: Settings) -> BaseProvider:
|
|
from providers.lmstudio import LMStudioProvider
|
|
|
|
return LMStudioProvider(config)
|
|
|
|
|
|
def _create_llamacpp(config: ProviderConfig, _settings: Settings) -> BaseProvider:
|
|
from providers.llamacpp import LlamaCppProvider
|
|
|
|
return LlamaCppProvider(config)
|
|
|
|
|
|
def _create_ollama(config: ProviderConfig, _settings: Settings) -> BaseProvider:
|
|
from providers.ollama import OllamaProvider
|
|
|
|
return OllamaProvider(config)
|
|
|
|
|
|
def _create_kimi(config: ProviderConfig, _settings: Settings) -> BaseProvider:
|
|
from providers.kimi import KimiProvider
|
|
|
|
return KimiProvider(config)
|
|
|
|
|
|
def _create_wafer(config: ProviderConfig, _settings: Settings) -> BaseProvider:
|
|
from providers.wafer import WaferProvider
|
|
|
|
return WaferProvider(config)
|
|
|
|
|
|
def _create_minimax(config: ProviderConfig, _settings: Settings) -> BaseProvider:
|
|
from providers.minimax import MiniMaxProvider
|
|
|
|
return MiniMaxProvider(config)
|
|
|
|
|
|
def _create_opencode(config: ProviderConfig, _settings: Settings) -> BaseProvider:
|
|
from providers.opencode import OpenCodeProvider
|
|
|
|
return OpenCodeProvider(config)
|
|
|
|
|
|
def _create_opencode_go(config: ProviderConfig, _settings: Settings) -> BaseProvider:
|
|
from providers.opencode import OpenCodeProvider
|
|
|
|
return OpenCodeProvider(config, provider_name="OPENCODE_GO")
|
|
|
|
|
|
def _create_vercel(config: ProviderConfig, _settings: Settings) -> BaseProvider:
|
|
from providers.vercel import VercelProvider
|
|
|
|
return VercelProvider(config)
|
|
|
|
|
|
def _create_huggingface(config: ProviderConfig, _settings: Settings) -> BaseProvider:
|
|
from providers.huggingface import HuggingFaceProvider
|
|
|
|
return HuggingFaceProvider(config)
|
|
|
|
|
|
def _create_cohere(config: ProviderConfig, _settings: Settings) -> BaseProvider:
|
|
from providers.cohere import CohereProvider
|
|
|
|
return CohereProvider(config)
|
|
|
|
|
|
def _create_zai(config: ProviderConfig, _settings: Settings) -> BaseProvider:
|
|
from providers.zai import ZaiProvider
|
|
|
|
return ZaiProvider(config)
|
|
|
|
|
|
def _create_fireworks(config: ProviderConfig, _settings: Settings) -> BaseProvider:
|
|
from providers.fireworks import FireworksProvider
|
|
|
|
return FireworksProvider(config)
|
|
|
|
|
|
def _create_cloudflare(config: ProviderConfig, settings: Settings) -> BaseProvider:
|
|
from providers.cloudflare import CloudflareProvider
|
|
|
|
return CloudflareProvider(config, account_id=settings.cloudflare_account_id)
|
|
|
|
|
|
def _create_gemini(config: ProviderConfig, _settings: Settings) -> BaseProvider:
|
|
from providers.gemini import GeminiProvider
|
|
|
|
return GeminiProvider(config)
|
|
|
|
|
|
def _create_groq(config: ProviderConfig, _settings: Settings) -> BaseProvider:
|
|
from providers.groq import GroqProvider
|
|
|
|
return GroqProvider(config)
|
|
|
|
|
|
def _create_cerebras(config: ProviderConfig, _settings: Settings) -> BaseProvider:
|
|
from providers.cerebras import CerebrasProvider
|
|
|
|
return CerebrasProvider(config)
|
|
|
|
|
|
PROVIDER_FACTORIES: dict[str, ProviderFactory] = {
|
|
"nvidia_nim": _create_nvidia_nim,
|
|
"open_router": _create_open_router,
|
|
"gemini": _create_gemini,
|
|
"deepseek": _create_deepseek,
|
|
"mistral": _create_mistral,
|
|
"mistral_codestral": _create_mistral_codestral,
|
|
"opencode": _create_opencode,
|
|
"opencode_go": _create_opencode_go,
|
|
"vercel": _create_vercel,
|
|
"huggingface": _create_huggingface,
|
|
"cohere": _create_cohere,
|
|
"wafer": _create_wafer,
|
|
"kimi": _create_kimi,
|
|
"minimax": _create_minimax,
|
|
"cerebras": _create_cerebras,
|
|
"groq": _create_groq,
|
|
"fireworks": _create_fireworks,
|
|
"cloudflare": _create_cloudflare,
|
|
"zai": _create_zai,
|
|
"lmstudio": _create_lmstudio,
|
|
"llamacpp": _create_llamacpp,
|
|
"ollama": _create_ollama,
|
|
}
|
|
|
|
if set(PROVIDER_CATALOG) != set(SUPPORTED_PROVIDER_IDS) or set(
|
|
PROVIDER_FACTORIES
|
|
) != set(SUPPORTED_PROVIDER_IDS):
|
|
raise AssertionError(
|
|
"PROVIDER_CATALOG, PROVIDER_FACTORIES, and SUPPORTED_PROVIDER_IDS are out of sync: "
|
|
f"catalog={set(PROVIDER_CATALOG)!r} factories={set(PROVIDER_FACTORIES)!r} "
|
|
f"ids={set(SUPPORTED_PROVIDER_IDS)!r}"
|
|
)
|
|
|
|
|
|
def create_provider(provider_id: str, settings: Settings) -> BaseProvider:
|
|
"""Create a provider instance for a supported provider id."""
|
|
descriptor = PROVIDER_CATALOG.get(provider_id)
|
|
if descriptor is None:
|
|
supported = "', '".join(PROVIDER_CATALOG)
|
|
raise UnknownProviderTypeError(
|
|
f"Unknown provider_type: '{provider_id}'. Supported: '{supported}'"
|
|
)
|
|
|
|
factory = PROVIDER_FACTORIES.get(provider_id)
|
|
if factory is None:
|
|
raise AssertionError(f"Unhandled provider descriptor: {provider_id}")
|
|
return factory(build_provider_config(descriptor, settings), settings)
|