Skip to content

solvi.remote

What every remote model shares (solvi.llm, solvi.systemone, solvi.generate, the chart proposer): the endpoint, the key in the header only, retries, token counts and one policy for HTTP errors.

What every remote model of solvi shares — solvi.llm (an OpenAI-compatible chat server), solvi.systemone (a System One service), solvi.generate (a generating model on an llm client) and the chart proposer: an http(s) endpoint, the API key in the Authorization header only (never in a message, a trace or a repr), retries with backoff, token counts, and one policy for errors:

  • HTTP 401, 403, 404 and any other 4xx except 400 / 413 / 422 — a wrong key, model or URL: RemoteError is raised (llm's LLMError and systemone's SystemOneError are its subclasses), never an escalation that looks like an answer;
  • 400, 413, 422 — the server refused this request's input: Refused; a decider escalates that one decision;
  • 408, 409, 429, 5xx, network errors, timeouts, a broken connection — retried (backoff · 2^k seconds); after the retries NoAnswer: a decider escalates, and the decision is not cached (asked again next time).

usage counts tokens under one set of names whatever the server calls them: input_tokens (prompt_tokens), output_tokens (completion_tokens), reasoning_tokens (completion_tokens_details.reasoning_tokens).

RemoteError

Bases: RuntimeError

The server refused the request itself (a wrong key, model or URL: HTTP 401, 403, 404 and other client errors except 400 / 413 / 422) — a configuration error, raised rather than escalated. The message has the endpoint, never the key.

Refused

Refused(msg, code=None, why='')

Bases: Exception

The server refused this request's input (HTTP 400 / 413 / 422): the reason, for the escalation.

NoAnswer

Bases: Exception

The server did not answer after the retries: the reason, for the escalation.

RemoteClient

RemoteClient(base_url, model, api_key=None, *, path='', timeout=60.0, retries=2, backoff=1.0, headers=None, opener=None, sleep=None)

An http(s) endpoint with retries (see the module docs). Subclasses set service (the words an escalation uses: "the LLM server", "the System One service") and error (the RemoteError subclass raised).

send

send(body, refused=None)

POST body with the retries → the response. refused(code, why): called on HTTP 400 / 413 / 422 — return a new body to try instead (solvi.llm's reply-format ladder), or None to give up (Refused). RemoteError for a configuration error, NoAnswer after the retries.

count

count(usage)

Add a reply's usage to self.usage → the tokens it gives, by the shared names.

endpoint

endpoint(url)

A URL without credentials, query or fragment — what the trace may show.

error_text

error_text(e)

The body of an HTTP error → its message: the JSON error's "message" (or a string "detail", as FastAPI and Jeeves send), else the text. A gateway that wraps the upstream provider's error (OpenRouter: "Provider returned error" with the real cause in error.metadata.raw) → the message and that cause. Whitespace runs collapsed, at most WHY_CHARS characters.

tokens

tokens(u)

A reply's usage object → {input_tokens, output_tokens, reasoning_tokens} (the ones it gives), whatever the server names them.