Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
f3ae332
refactor(providers): give every provider decision one home
0xKT Aug 9, 2026
95e7e37
fix(providers): keep codex sse error details for retry classification
0xKT Aug 9, 2026
46e8538
fix(*): wire the agent model into skill forge rewriter and gate
0xKT Aug 9, 2026
f885247
fix(*): resolve the context window through one ladder in providers
0xKT Aug 9, 2026
4cb4496
fix(providers): recover reasoning from an orphaned closing think tag
0xKT Aug 9, 2026
79f5d96
fix(*): refuse wizard vendors a bare api key cannot configure
0xKT Aug 9, 2026
cc481b9
test(providers): pin api_key forwarding into acompletion kwargs
0xKT Aug 9, 2026
ea9fdd2
fix(providers): merge user extra_body with wire routing instead of ov…
0xKT Aug 9, 2026
70b92f6
docs(readme): invite vendor partnerships in the contributing section
0xKT Aug 9, 2026
336eb8f
test(cli): pin picker-registry parity and wizard prefill defaults
0xKT Aug 9, 2026
85d4544
fix(tui): replay a signal swallowed during an oauth handoff
0xKT Aug 9, 2026
b13d33a
fix(providers): keep fallback hops on wires that can actually serve them
0xKT Aug 9, 2026
0d9c784
chore(providers): let ruff format pick the quote style in the refusal…
0xKT Aug 9, 2026
d614355
fix(providers): stop masking chat errors as assistant text in stream …
0xKT Aug 9, 2026
30e7c9e
fix(providers): classify a rendered 404 body as model_unavailable
0xKT Aug 9, 2026
88bdba0
fix(providers): only veto fallback hops both identities are certain a…
0xKT Aug 9, 2026
cfdeb19
fix(providers): let explicit model_overrides win over shipped extra_body
0xKT Aug 9, 2026
24cc9e7
feat(*): declare provider endpoints and resolve every spelling in one…
0xKT Aug 9, 2026
98b736a
fix(providers): resolve the construction-time window without the network
0xKT Aug 9, 2026
4976265
refactor(cli): move the wizard's everos cluster into its own module
0xKT Aug 9, 2026
dd81d0f
fix(providers): only recover orphan think tags from parser-less backends
0xKT Aug 9, 2026
7fd28a9
feat(providers): rotate and fail over across a provider's endpoints
0xKT Aug 9, 2026
73a3906
refactor(*): keep pin wording out of the window ladder terms
0xKT Aug 9, 2026
bdf7aaa
refactor(cli): move the wizard's channel cluster into its own module
0xKT Aug 9, 2026
cdc2ec6
feat(*): route multi-endpoint sections through the rotor
0xKT Aug 9, 2026
5c94ea1
test(providers): argue the two new key-reading files into the guard
0xKT Aug 9, 2026
b8baa25
feat(*): manage provider endpoints from the cli
0xKT Aug 9, 2026
c2f53c9
feat(*): manage provider endpoints from the tui
0xKT Aug 9, 2026
b59f492
fix(providers): rotate endpoints on auth failures too
0xKT Aug 9, 2026
42b7945
fix(*): survive duck-typed providers and argue the split into the wir…
0xKT Aug 9, 2026
70e2624
test(providers): count the rotor as the sixth concrete backend
0xKT Aug 9, 2026
ede6bf1
chore(providers): drop issue references from source comments
0xKT Aug 9, 2026
69f6a54
fix(providers): stop reading an unresolved identity as self-hosted
0xKT Aug 9, 2026
28ed608
docs(context): register the provider endpoint term
0xKT Aug 9, 2026
2f7c3af
fix(*): carry endpoint material and hooks through the assembly layer
0xKT Aug 9, 2026
f24bc26
fix(*): redact endpoint keys and align the display and validation faces
0xKT Aug 9, 2026
102827d
fix(config): stop keyless endpoints reading as set and invalid sectio…
0xKT Aug 9, 2026
018ae5e
fix(*): render an invalid section's error instead of a traceback
0xKT Aug 10, 2026
60adc24
fix(providers): match 404 as a token and harden the catalog cache read
0xKT Aug 10, 2026
f541bf8
fix(providers): keep bedrock caching and knn content intact
0xKT Aug 10, 2026
c8e26a7
fix(*): make every credential gate read the shape requests actually use
0xKT Aug 10, 2026
bb2a109
fix(*): keep the event loop and startup free of the window ladder's w…
0xKT Aug 10, 2026
704f2fa
fix(providers): only a real list is the endpoints shape the gate reads
0xKT Aug 10, 2026
008635c
fix(*): close the reviewer's second-round confirmations
0xKT Aug 10, 2026
e61224d
chore(providers): restore the blank line ruff format wants
0xKT Aug 10, 2026
075c6a0
test(providers): skip interpreter teardown in the litellm-import probe
0xKT Aug 10, 2026
7c4253e
fix(providers): classify azure non-200 at the source, drop the 404 te…
0xKT Aug 10, 2026
4633d73
fix(providers): expose model_overrides through the endpoint rotor
0xKT Aug 10, 2026
3d04d83
docs(agent): refresh_context_window docstring matches its write set
0xKT Aug 10, 2026
cbe24b8
fix(providers): endpoint entries inherit the flat api_base and extra_…
0xKT Aug 10, 2026
c3c7baa
fix(providers): a spec's shipped default address satisfies the creden…
0xKT Aug 10, 2026
4cbb9f0
fix(config): list_providers mirrors the endpoint precedence
0xKT Aug 10, 2026
47e7196
fix(config): endpoint writes require a key for key-credential providers
0xKT Aug 10, 2026
262aa2f
fix(config): redact extra_headers values in endpoint display faces
0xKT Aug 10, 2026
156cd34
perf(tui_rpc): read the config once for all picker rows
0xKT Aug 10, 2026
9335b8d
fix(providers): codex non-200 carries the live status into classifica…
0xKT Aug 10, 2026
6704f17
fix(config): endpoint display shows the resolved address, not the raw…
0xKT Aug 10, 2026
c443b82
fix(providers): one source for which spec defaults the reader serves
0xKT Aug 10, 2026
cad1d38
fix(tui_rpc): the picker demands an address only when the gate does
0xKT Aug 10, 2026
306a05d
fix(config): redact the section-level extra_headers values as well
0xKT Aug 10, 2026
23aef6c
fix(tui_rpc): save_key names every missing requirement at once
0xKT Aug 10, 2026
f888d86
fix(providers): the key hint names endpoint add when endpoints exist
0xKT Aug 10, 2026
8192538
perf(tui_rpc): hoist the remaining per-row config read
0xKT Aug 10, 2026
74aa491
fix(config): list surfaces the endpoint count for local deployments too
0xKT Aug 10, 2026
ec63e06
refactor(providers): drop the no-op field whitelist in _present
0xKT Aug 10, 2026
c9d1085
docs(*): inheritance is per field, and the write set is not a fixed list
0xKT Aug 10, 2026
4fb3e97
test(config): rewrap an overlong line ruff-format flagged
0xKT Aug 10, 2026
b472901
test(providers): pin codex's wire-404 classification at the raise site
0xKT Aug 10, 2026
849adbc
fix(config): the oauth guard forbids credential fields, not display s…
0xKT Aug 10, 2026
c671602
test(providers): guard the gate's spec-default source with a syntheti…
0xKT Aug 10, 2026
5fad57c
fix(providers): the hint swap checks the endpoints shape like the gate
0xKT Aug 10, 2026
a7beb01
test(providers): pin the hint's endpoints-shape guard
0xKT Aug 10, 2026
b825060
fix(providers): a stale disk catalog no longer suppresses the warm
0xKT Aug 10, 2026
ec51874
fix(providers): caller-chosen names survive the lazy proxy and cover …
0xKT Aug 10, 2026
e9967f7
fix(agent): a provider switch invalidates the image verdict and says so
0xKT Aug 10, 2026
9ce23f3
fix(agent): the context window follows the adopted pair, not the RPC …
0xKT Aug 10, 2026
d298588
test(providers): sort three imports the seam repoint left unsorted
0xKT Aug 10, 2026
acd6b8c
fix(*): close the final review round's residue
0xKT Aug 10, 2026
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
91 changes: 91 additions & 0 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,97 @@ An LLM vendor adapter (`providers/`: Anthropic, OpenAI, Gemini, …), shared by
agent loop and the Curator.
_Avoid_: conflating provider (vendor) with model (a model name a provider serves)

A Provider is described along four independent axes -- identity, connection, routing,
and what its models can do -- each with its own home. Mixing them in one record is what
left per-model facts nowhere to live and per-provider facts stated in several places at
once. The terms below name the pieces those axes are built from; they are properties of
a model or of a connection, not four synonyms for Provider.

**Model Ref**:
The canonical way a model is written down: `provider/model`, naming whoever serves it.
Usually that is the section it was configured under; where a Provider declares
`skip_prefixes` it may instead be the gateway already named in the id
(`openrouter/z-ai/glm-4.6` stored under `zai` keeps OpenRouter's name, because
OpenRouter is what serves it). Produced by `providers/wire.py::stored_model_id`, which
every surface that persists a choice goes through.
_Avoid_: "model id" for the stored form when the sent form is also in play — say Model
Ref or Wire Model.

**Merge Key**:
The identity of a Model Ref for comparison and de-duplication — the provider and the
vendor's own id, spelling-folded. Two refs naming one model share a Merge Key whatever
spelling either was written in.

**Wire Model**:
The form a Model Ref takes on the request: a LiteLLM route string, an Azure deployment
name, or a Codex slug. Derived, never stored, and derived in one place
(`providers/wire.py::wire_model`).
_Avoid_: treating the stored and sent forms as one string — they differ per provider.

**Auth Method**:
One way of connecting to a Provider: what credential material it needs (as an AND of
OR-groups), how that material is obtained, where it is kept, and how it is verified.
A Provider may declare several and is usable when any one is satisfied.
`providers/auth.py::credential_status` answers "is this Provider usable", and is the
only place that may: seven surfaces once decided it independently and disagreed with
each other on the two configurations that made the rewrite necessary.
_Avoid_: "credential kind" for the whole shape — that names only the material.

**Model Row**:
One model as a person reads it: a Model Ref plus a label and a description, tagged with
the source that supplied them. Display only — nothing shaping a request reads a Model
Row (`providers/catalog.py`).
_Avoid_: confusing it with what a model can *do*. Whether a request may carry
`cache_control` blocks is a Prompt Cache Breakpoint question, not a Model Row one.

**Model Overlay**:
What a user states about a model no catalogue carries — a label and a description for a
self-hosted deployment. Beats the catalogue for the fields it sets.

**Prompt Cache Breakpoint**:
An Anthropic-shaped `cache_control` marker placed on a request so the prefix before it is
cached. Whether one may be placed is **(wire x model family)**: the wire has to have
somewhere to carry the field (`ProviderSpec.supports_prompt_caching`, a property of the
API being spoken) *and* the model's vendor has to be the one that reads it. A gateway
accepting the field is not the same as its upstream honouring it -- OpenRouter carries it
for every model it fronts and forwards it to vendors that bill the prompt twice.
Decided once, in `providers/prompt_cache.py`, which every marker asks.
_Avoid_: reading LiteLLM's per-model `supports_prompt_caching`, which answers "does this
model cache at all" -- a different question, and the one that produced the doubled bill.

**Token Rates**:
What a model costs per token, and separately how much context it holds. Both are facts
about a Provider's catalogue, so both are resolved in `providers/rates.py` rather than by
whoever is about to report a number. The two are deliberately sourced differently: rates
price a call after it happened, so the ladder may reach a community-maintained catalogue;
a context window sizes trimming and therefore shapes the *next* request, so only the
tables that also route may answer it. The window walks its own ladder
(`effective_context_window`): an explicitly configured value wins outright, then the
model's real window, then the module's documented fallback -- and a gauge that cannot
resolve the real window reports 0 so the UI shows its empty state rather than a number
that is nobody's.
_Avoid_: "pricing" for the resolution -- that names the arithmetic on top
(`token_wise/pricing.py`), which is a different module for a reason.

**Provider Pin**:
`agents.defaults.provider`: an explicit override of the Provider a Model Ref names.
Every surface that changes the model rewrites it by one rule
(`providers/pin.py::resolve`), because a pin left behind routes the new model to the old
vendor with the old vendor's key.
_Avoid_: reading it as a provider *signal* -- a pinned name says which section to ask
about, never that the section holds credentials.

**Provider Endpoint**:
One url/key/headers group a provider section offers, of possibly several
(`ProviderConfig.endpoints`, resolved through `providers/endpoints.py::provider_endpoints`
whichever spelling the section used -- explicit list, Gemini's `api_key_list`, or the
flat fields). Several endpoints on one section mean several accounts on the same vendor;
`EndpointRotorProvider` spreads and fails over across them.
_Avoid_: two same-sounding neighbors. Routing's `ModelEndpoint` (`RoutingConfig.models`)
keys by *model* and picks a backend per request; a Provider Endpoint keys by *account*
under one provider. And a bare `api_base` is one endpoint's address, not the endpoint --
an endpoint is the whole credential group under a label.

### TUI-RPC

**TUI-RPC**:
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,9 @@ Raven is early, and useful contributions are welcome across runtime
architecture, TUI polish, provider support, memory workflows, proactivity,
benchmarks, documentation, and issue reports.

Model vendors interested in a first-party integration or an open-source
partnership (as MiniMax already has) are welcome to open an issue and say so.

Before opening a PR:

1. Read [AGENTS.md](AGENTS.md).
Expand Down
7 changes: 5 additions & 2 deletions benchmarks/clawbench/stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,10 @@ def __init__(
restrict_to_workspace: bool,
) -> None:
from raven.agent.loop import AgentLoop
from raven.cli.commands import _make_provider
from raven.cli._helpers import make_provider as _make_provider
from raven.config.loader import load_config, set_config_path
from raven.config.raven import ContextConfig
from raven.providers.rates import effective_context_window
from raven.session.manager import SessionManager

workspace.mkdir(parents=True, exist_ok=True)
Expand All @@ -126,7 +127,9 @@ def __init__(
self.config.agents.defaults.workspace = str(workspace.resolve())
self.provider = UsageTrackingProvider(_make_provider(self.config))
self.model = self.config.agents.defaults.model
self.context_window = int(context_window or self.config.agents.defaults.context_window_tokens)
self.context_window = effective_context_window(
self.model, context_window or self.config.agents.defaults.context_window_tokens
)
self.curator_model = curator_model or self.model
self.session_id = session_id
self.previous_totals = dict(self.provider.accumulated)
Expand Down
40 changes: 11 additions & 29 deletions benchmarks/pinchbench/direct/raven_executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -330,38 +330,20 @@ def _make_benchmark_provider(model: str, api_key: str, api_base: str, provider_n


def _estimate_cost_usd(model: str, prompt_tokens: int, completion_tokens: int) -> float | None:
"""Estimate USD cost using LiteLLM's pricing database with manual fallbacks.
"""Estimate USD cost for one call, or None when no source prices the model.

Falls back to _fallback_pricing for models not yet in LiteLLM's DB.
Returns None if the model is unknown to both.
A thin wrapper on purpose. This carried its own two-tier copy of the
resolution -- LiteLLM, then a hand-written table -- while the shipped ladder
grew a live gateway table and a per-vendor catalogue between them, so a
benchmark could report a figure the product would not. There is one answer to
"what does this cost" and it lives with the provider.
"""
# Manual fallback pricing ($/token) for models absent from LiteLLM's DB.
# Source: OpenRouter model pages (as of 2026-03).
_fallback_pricing: Dict[str, tuple[float, float]] = {
"z-ai/glm-4.5-air": (0.13e-6, 0.85e-6), # $0.13/$0.85 per 1M tokens
}

try:
import litellm

# LiteLLM expects "openrouter/<provider>/<model>" format for OpenRouter models.
or_model = f"openrouter/{model}" if not model.startswith("openrouter/") else model
prompt_cost, completion_cost = litellm.cost_per_token(
model=or_model,
prompt_tokens=prompt_tokens,
completion_tokens=completion_tokens,
)
return prompt_cost + completion_cost
except Exception:
pass

# Fallback for models not in LiteLLM DB
base_model = model.removeprefix("openrouter/")
if base_model in _fallback_pricing:
p_per_tok, c_per_tok = _fallback_pricing[base_model]
return p_per_tok * prompt_tokens + c_per_tok * completion_tokens
from raven.token_wise.pricing import estimate_cost_usd

return None
# Benchmarks name OpenRouter models bare; the ladder is keyed by stored ids,
# which name their provider.
stored = model if model.startswith("openrouter/") else f"openrouter/{model}"
return estimate_cost_usd(stored, prompt_tokens, completion_tokens)


async def _run_turn_text(agent, message: str, *, session_key: str, chat_id: str) -> str:
Expand Down
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ include = [
"raven/templates/**/*.md",
"raven/skills/**/*.md",
"raven/skills/**/*.sh",
# Bundled model-label snapshot, so a fresh install labels models offline.
"raven/providers/data/*.json",
# Tracing dashboard viewer (dependency-free Node server + static client).
"raven/tracing/viewer/**/*.js",
"raven/tracing/viewer/**/*.css",
Expand Down
Loading
Loading