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
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,9 @@ Wizard defaults reflect the project's per-phase recommendations (stronger reason
| `anthropic` | [console.anthropic.com](https://console.anthropic.com/settings/keys) | Reference adapter. NOT included in Claude Pro / Max subscriptions — separate billing. |
| `openai` | [platform.openai.com](https://platform.openai.com/api-keys) | NOT included in ChatGPT / Codex subscriptions — separate billing. |
| `google` | [aistudio.google.com](https://aistudio.google.com/apikey) | NOT included in Gemini Advanced — separate billing. |
| `openrouter` | [openrouter.ai](https://openrouter.ai/settings/keys) | Gateway to many providers with one key and one prepaid balance (also reads `OPENROUTER_API_KEY`). Model IDs are `vendor/model` slugs (`anthropic/claude-sonnet-4.6`, `openai/gpt-4o-mini`, ...) — browse them at [openrouter.ai/models](https://openrouter.ai/models). Not offered by the setup wizard yet; configure by hand — full guide: [`utilities/llm/providers/OPENROUTER.md`](libs/openant-core/utilities/llm/providers/OPENROUTER.md). |

All three support tool calling, so any of them can drive the `enhance` and `verify` phases that use the agentic tool-use loop.
All four support tool calling, so any of them can drive the `enhance` and `verify` phases that use the agentic tool-use loop.

#### Quick path for Anthropic-only setups

Expand Down Expand Up @@ -128,7 +129,7 @@ The wizard writes `~/.config/openant/config.json` for you, but you can edit it d
}
```

Providers accept a custom `base_url` for OpenAI-compatible / Anthropic-compatible proxies (OpenRouter, vLLM, Bedrock, internal gateways). The `openant-default` config (Claude across all phases) is built in and always available regardless of file contents.
Providers accept a custom `base_url` for OpenAI-compatible / Anthropic-compatible proxies (vLLM, Bedrock, internal gateways); OpenRouter has its own first-class `openrouter` provider type. The `openant-default` config (Claude across all phases) is built in and always available regardless of file contents.

#### Adding a new provider adapter

Expand Down
90 changes: 90 additions & 0 deletions config/models.json
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,96 @@
"price": {"input": 0.075, "output": 0.30},
"source": "openant shipped pricing table (utilities/model_config.py); not re-verified against live Google pricing",
"retrieved": "2026-07-23"
},
{
"id": "anthropic/claude-opus-4.8", "provider": "openrouter", "status": "current",
"price": {"input": 5.00, "output": 25.00},
"source": "verified against the live OpenRouter catalogue (GET https://openrouter.ai/api/v1/models), per-token rates converted to per-1M; OpenRouter's rate, which may differ from the direct provider's",
"retrieved": "2026-08-01"
},
{
"id": "anthropic/claude-sonnet-4.6", "provider": "openrouter", "status": "current",
"price": {"input": 3.00, "output": 15.00},
"source": "verified against the live OpenRouter catalogue (GET https://openrouter.ai/api/v1/models), per-token rates converted to per-1M; OpenRouter's rate, which may differ from the direct provider's",
"retrieved": "2026-08-01"
},
{
"id": "anthropic/claude-haiku-4.5", "provider": "openrouter", "status": "current",
"price": {"input": 1.00, "output": 5.00},
"source": "verified against the live OpenRouter catalogue (GET https://openrouter.ai/api/v1/models), per-token rates converted to per-1M; OpenRouter's rate, which may differ from the direct provider's",
"retrieved": "2026-08-01"
},
{
"id": "openai/gpt-4o", "provider": "openrouter", "status": "current",
"price": {"input": 2.50, "output": 10.00},
"source": "verified against the live OpenRouter catalogue (GET https://openrouter.ai/api/v1/models), per-token rates converted to per-1M; OpenRouter's rate, which may differ from the direct provider's",
"retrieved": "2026-08-01"
},
{
"id": "openai/gpt-4o-mini", "provider": "openrouter", "status": "current",
"price": {"input": 0.15, "output": 0.60},
"source": "verified against the live OpenRouter catalogue (GET https://openrouter.ai/api/v1/models), per-token rates converted to per-1M; OpenRouter's rate, which may differ from the direct provider's",
"retrieved": "2026-08-01"
},
{
"id": "openai/gpt-4.1", "provider": "openrouter", "status": "current",
"price": {"input": 2.00, "output": 8.00},
"source": "verified against the live OpenRouter catalogue (GET https://openrouter.ai/api/v1/models), per-token rates converted to per-1M; OpenRouter's rate, which may differ from the direct provider's",
"retrieved": "2026-08-01"
},
{
"id": "openai/gpt-4.1-mini", "provider": "openrouter", "status": "current",
"price": {"input": 0.40, "output": 1.60},
"source": "verified against the live OpenRouter catalogue (GET https://openrouter.ai/api/v1/models), per-token rates converted to per-1M; OpenRouter's rate, which may differ from the direct provider's",
"retrieved": "2026-08-01"
},
{
"id": "openai/gpt-4.1-nano", "provider": "openrouter", "status": "current",
"price": {"input": 0.10, "output": 0.40},
"source": "verified against the live OpenRouter catalogue (GET https://openrouter.ai/api/v1/models), per-token rates converted to per-1M; OpenRouter's rate, which may differ from the direct provider's",
"retrieved": "2026-08-01"
},
{
"id": "openai/o1", "provider": "openrouter", "status": "current",
"price": {"input": 15.00, "output": 60.00},
"source": "verified against the live OpenRouter catalogue (GET https://openrouter.ai/api/v1/models), per-token rates converted to per-1M; OpenRouter's rate, which may differ from the direct provider's",
"retrieved": "2026-08-01"
},
{
"id": "openai/o3", "provider": "openrouter", "status": "current",
"price": {"input": 2.00, "output": 8.00},
"source": "verified against the live OpenRouter catalogue (GET https://openrouter.ai/api/v1/models), per-token rates converted to per-1M; OpenRouter's rate, which may differ from the direct provider's",
"retrieved": "2026-08-01"
},
{
"id": "openai/o3-mini", "provider": "openrouter", "status": "current",
"price": {"input": 1.10, "output": 4.40},
"source": "verified against the live OpenRouter catalogue (GET https://openrouter.ai/api/v1/models), per-token rates converted to per-1M; OpenRouter's rate, which may differ from the direct provider's",
"retrieved": "2026-08-01"
},
{
"id": "openai/o4-mini", "provider": "openrouter", "status": "current",
"price": {"input": 1.10, "output": 4.40},
"source": "verified against the live OpenRouter catalogue (GET https://openrouter.ai/api/v1/models), per-token rates converted to per-1M; OpenRouter's rate, which may differ from the direct provider's",
"retrieved": "2026-08-01"
},
{
"id": "google/gemini-2.5-pro", "provider": "openrouter", "status": "current",
"price": {"input": 1.25, "output": 10.00},
"source": "verified against the live OpenRouter catalogue (GET https://openrouter.ai/api/v1/models), per-token rates converted to per-1M; OpenRouter's rate, which may differ from the direct provider's",
"retrieved": "2026-08-01"
},
{
"id": "google/gemini-2.5-flash", "provider": "openrouter", "status": "current",
"price": {"input": 0.30, "output": 2.50},
"source": "verified against the live OpenRouter catalogue (GET https://openrouter.ai/api/v1/models), per-token rates converted to per-1M; OpenRouter's rate, which may differ from the direct provider's",
"retrieved": "2026-08-01"
},
{
"id": "google/gemini-2.5-flash-lite", "provider": "openrouter", "status": "current",
"price": {"input": 0.10, "output": 0.40},
"source": "verified against the live OpenRouter catalogue (GET https://openrouter.ai/api/v1/models), per-token rates converted to per-1M; OpenRouter's rate, which may differ from the direct provider's",
"retrieved": "2026-08-01"
}
]
}
2 changes: 1 addition & 1 deletion libs/openant-core/core/model_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
_CONFIG_REL = Path("config") / "models.json"
_SEARCH_LEVELS = 6
_VALID_STATUS = frozenset({"current", "retired", "unknown"})
_VALID_PROVIDERS = frozenset({"anthropic", "openai", "google"})
_VALID_PROVIDERS = frozenset({"anthropic", "openai", "google", "openrouter"})


def _search_upward(start: Path) -> Path | None:
Expand Down
43 changes: 43 additions & 0 deletions libs/openant-core/tests/_llm_factories/openrouter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""Scenario factory for the OpenRouter adapter contract tests.

OpenRouter speaks OpenAI's Chat Completions wire format through the same
``openai`` SDK, so the scripted fake-client behaviors are shared with
the OpenAI factory — this module reuses its scenario handlers verbatim
and only swaps the adapter under construction. What differs between the
two adapters (default base_url, ``OPENROUTER_API_KEY`` fallback,
attribution headers, 400-invalid-model / 402-credits / 403-moderation /
finish_reason-"error" mapping) is covered by the dedicated
``tests/test_llm_openrouter_adapter.py``.

See ``tests/test_llm_adapter_contract.py`` for the scenario catalogue.
"""

from __future__ import annotations

from typing import Any
from unittest.mock import MagicMock

import openai

from utilities.llm import LLMAdapter
from utilities.llm.providers.openrouter import OpenRouterAdapter

from .openai import _SCENARIO_HANDLERS as SCENARIO_HANDLERS


def make_adapter(scenario: str) -> LLMAdapter:
"""Build an OpenRouterAdapter whose SDK is scripted for ``scenario``."""
if scenario not in SCENARIO_HANDLERS:
raise KeyError(f"Unknown scenario: {scenario!r}")

handler = SCENARIO_HANDLERS[scenario]

def side_effect(**kwargs: Any) -> Any:
return handler(kwargs)

fake_client = MagicMock(spec=openai.OpenAI)
fake_client.chat = MagicMock()
fake_client.chat.completions = MagicMock()
fake_client.chat.completions.create = MagicMock(side_effect=side_effect)

return OpenRouterAdapter(_client=fake_client)
7 changes: 7 additions & 0 deletions libs/openant-core/tests/test_llm_adapter_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,19 @@ def _google_factory():
return make_adapter


def _openrouter_factory():
from tests._llm_factories.openrouter import make_adapter

return make_adapter


# Each row: (display_name, scenario_factory_callable)
# Add a row when registering a new adapter.
ADAPTERS: list[tuple[str, Callable[[str], LLMAdapter]]] = [
("anthropic", _anthropic_factory()),
("openai", _openai_factory()),
("google", _google_factory()),
("openrouter", _openrouter_factory()),
]


Expand Down
Loading
Loading