Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@ class ProvidersConfig(_Base):
forge: ProviderConfig = Field(default_factory=ProviderConfig)
requesty: ProviderConfig = Field(default_factory=ProviderConfig)
opper: ProviderConfig = Field(default_factory=ProviderConfig)
api_route: ProviderConfig = Field(default_factory=ProviderConfig)
bedrock: ProviderConfig = Field(default_factory=ProviderConfig)
anthropic: ProviderConfig = Field(default_factory=ProviderConfig)
openai: ProviderConfig = Field(default_factory=ProviderConfig)
Expand Down
11 changes: 11 additions & 0 deletions core/providers/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,17 @@ def _matches_declared_endpoint(self, api_base: str | None) -> bool:
# ``provider/model`` id like ``azure/gpt-5.5`` deliberately pins one
# route, so the prefix must survive rather than be stripped.
),
ProviderSpec(
name="api_route",
keywords=("api_route", "api-route"),
env_key="API_ROUTE_API_KEY",
display_name="API Route",
backend="openai_compat",
is_gateway=True,
endpoint_class="gateway",
detect_by_base_keyword="api-route.com",
default_api_base="https://global.api-route.com/v1",
),
ProviderSpec(
name="forge",
keywords=("forge",),
Expand Down
14 changes: 14 additions & 0 deletions docs/guide/models.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,20 @@ deepcode provider test my-openrouter --model MODEL_ID
This sends a short request to check that the model responds. Desktop/Web offer
the same check through **Save and verify model**.

For API Route, choose **API Route** in Desktop/Web or add a connection from the
terminal. It uses the OpenAI-compatible endpoint at
`https://global.api-route.com/v1`:

```console
deepcode provider set my-api-route --template api_route --api-key-env API_ROUTE_API_KEY
deepcode provider models my-api-route --refresh
deepcode provider test my-api-route --model MODEL_ID
```

Set `API_ROUTE_API_KEY` in your environment and replace `MODEL_ID` with an exact
ID returned by the catalog refresh. See the [API Route quickstart](https://www.api-route.com/docs/quickstart)
for key setup.

When trying a new model server, you can also check streaming and tool calls:

```console
Expand Down
53 changes: 53 additions & 0 deletions tests/test_api_route_provider.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
"""API Route uses the shared OpenAI-compatible gateway path."""

from __future__ import annotations

import sys
from pathlib import Path

import pytest

ROOT = Path(__file__).resolve().parents[1]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))

from core.config import ConfigError, DeepCodeConfig, make_llm_provider
from core.providers.openai_compat import OpenAICompatProvider
from core.providers.registry import find_by_name


def _config(api_key: str | None) -> DeepCodeConfig:
return DeepCodeConfig.model_validate(
{
"agents": {"defaults": {"provider": "api_route", "model": "gpt-5.5"}},
"providers": {"api_route": {"apiKey": api_key}},
}
)


def test_api_route_is_a_named_gateway() -> None:
spec = find_by_name("api_route")

assert spec is not None
assert spec.display_name == "API Route"
assert spec.env_key == "API_ROUTE_API_KEY"
assert spec.default_api_base == "https://global.api-route.com/v1"
assert spec.backend == "openai_compat"
assert spec.resolve_endpoint_class(spec.default_api_base) == "gateway"
assert spec.resolve_endpoint_class("https://other.example/v1") == "unknown"
assert spec.detect_by_key_prefix == ""
assert spec.strip_model_prefix is False


def test_api_route_connection_uses_its_key_and_default_endpoint() -> None:
provider = make_llm_provider(_config("test-api-route-key"))

assert isinstance(provider, OpenAICompatProvider)
assert provider.api_key == "test-api-route-key"
assert provider.api_base == "https://global.api-route.com/v1"
assert provider.default_model == "gpt-5.5"


def test_api_route_requires_its_own_key() -> None:
with pytest.raises(ConfigError, match=r"providers\.api_route\.apiKey"):
make_llm_provider(_config(None))
1 change: 1 addition & 0 deletions tests/test_provider_egress.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ def test_invalid_policy_entry_fails_closed():
("openrouter", "gateway"),
("requesty", "gateway"),
("opper", "gateway"),
("api_route", "gateway"),
("forge", "gateway"),
("zhipu", "aggregator"),
("dashscope", "aggregator"),
Expand Down