Skip to content

Add Hubris provider - #6352

Open
Aimagine-life wants to merge 7 commits into
anomalyco:devfrom
Aimagine-life:provider/hubris
Open

Aimagine-life wants to merge 7 commits into
anomalyco:devfrom
Aimagine-life:provider/hubris

Conversation

@Aimagine-life

@Aimagine-life Aimagine-life commented Sep 5, 2026

Copy link
Copy Markdown

Provider

Hubris (https://hubris.pw) — an OpenAI-compatible LLM gateway billed in Russian rubles, serving OpenAI, Anthropic, Google, DeepSeek, Qwen, Z.ai, Moonshot, xAI, MiniMax and other models behind a single API key.

name = "Hubris"
env = ["HUBRIS_API_KEY"]
npm = "@ai-sdk/openai-compatible"
api = "https://api.hubris.pw/v1"
doc = "https://hubris.pw/models"

Same shape as the other OpenAI-compatible relays (e.g. providers/requesty). Logo is a currentColor SVG with a square viewBox.

Sync module

packages/core/src/sync/providers/hubris.ts, registered in sync/index.ts (aggregators group) and documented in sync.md:

  • Source: the public catalog behind https://hubris.pw/models (https://hubris.pw/api/internal/models/catalog?limit=1000, no auth).
  • Hubris prices are RUB. They are converted to USD per 1M tokens at the Bank of Russia official daily rate (https://www.cbr.ru/scripts/XML_daily.asp, JSON mirror cbr-xml-daily.ru as fallback). Previously synced USD prices are kept while the converted value drifts less than 3 %, so the hourly sync does not churn every file on FX noise; real price changes still update.
  • Every entry factors onto lab metadata via base_model (resolveModelMetadataBaseModel, same as OpenRouter/Requesty). Models without a models/ entry are skipped and listed in the sync notices (trackMissingModels: false, no issue spam); :batch variants, ~ aliases and non-chat models (image/video/speech/embeddings) are skipped.
  • reasoning / tool_call / structured_output come from the catalog's supportedParameters; reasoning models get the relay controls (effort none/low/medium/high/max + budget_tokens), as for Requesty. limit.context comes from the catalog, limit.output is inherited (falls back to context when the lab entry has none, like Requesty).

Models

185 provider models, all override-only on top of existing lab metadata — the 26 hand-picked ones from the first commit plus everything else in the Hubris catalog that resolves to a models/ entry (Anthropic Claude 3.5–5 / Fable, OpenAI GPT-4 → GPT-6 Astra / o-series / gpt-oss, Gemini 2.5–3.8, DeepSeek V3–V4, Qwen 3–3.8, GLM 4.5–5.3, Kimi K2–K3, Grok 4.3–4.6, MiniMax M2–M3, Mistral, Llama, Nemotron, Perplexity Sonar, …).

Model ids are the ids the gateway accepts verbatim (vendor/model, OpenRouter-style), so file paths use the gateway's vendor prefixes (z-ai/, x-ai/, qwen/) while base_model resolves to the lab ids (zhipuai/, xai/, alibaba/).

Validation

  • bun models:sync hubris → 159 created / 8 updated on top of the hand-authored files, then bun models:sync hubris --dry-run → clean (0/0/0).
  • bun validate passes with the new provider included.

🤖 Generated with Claude Code

Hubris (https://hubris.pw) is an OpenAI-compatible LLM gateway billed in
Russian rubles. Adds provider.toml (openai-compatible, HUBRIS_API_KEY,
https://api.hubris.pw/v1), a currentColor logo, and 26 of the most used
models as override-only entries on top of the existing lab metadata.

Prices are the public RUB per-million rates converted to USD at the
Bank of Russia rate on 2026-09-05 (86.5857 RUB/USD).
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Action items

  • [high] [violation] providers/hubris/models/anthropic/claude-opus-5.toml:3 - Check: Multi-model relay reasoning_options must follow lab + same-surface peer baselines; do not invent a universal effort list or budget_tokens. Why: Hubris is an OpenAI-compatible multi-lab gateway, but almost every reasoner is stamped with the same effort = ["none","low","medium","high","max"] plus bare budget_tokens. That contradicts first-party/OpenRouter baselines across the catalog (e.g. Claude Opus 5 / Fable / Opus 4.8: effort low|medium|high|xhigh|max, no budget; Claude Haiku 4.5: budget_tokens only; DeepSeek V4: toggle + high|max or low|high|max, no budget; GPT-5.5: none|low|medium|high|xhigh effort-only; GPT-5.6: includes xhigh/max as lab; Gemini 3.5: minimal|low|medium|high; Gemini 3.7/3.8: low|medium|high; Gemini 2.5 Flash: toggle + budget; Qwen3.8: toggle + lab effort/budget shape; GLM 5.3: always-on low|high|max; Kimi K3: toggle + low|high|max; Grok 4.6: low|medium|high|xhigh; MiniMax M3: toggle only). Bogus budget_tokens is explicitly disallowed for GPT effort-only, Claude 4.7+ adaptive effort, and DeepSeek V4. Action: For each reasoner, author host-accurate reasoning_options from the lab entry and established relay peers (OpenRouter-class), not Requesty’s blanket template. Drop invented budget_tokens unless this host documents a real reasoning-budget field; use toggle only with a leading wire-path comment when on/off is a separate control and effort does not already include none.
  • [high] [violation] providers/hubris/models/deepseek/deepseek-v4-pro-0813.toml:3 - Check: DeepSeek V4 / GLM / Kimi / Qwen / MiniMax relay controls must not be rewritten as GPT-style L/M/H(+none)+budget. Why: These families have narrower or different native surfaces (toggle+high|max, always-on effort, toggle+budget, toggle only). The shared Hubris block misrepresents caller controls and will mislead clients. Action: Replace the shared block on deepseek-v4-*, z-ai/glm-5.3*, moonshotai/kimi-k3, qwen/qwen3.8-*, and minimax/minimax-m3 with the lab/peer option sets for each model (and document any Hubris-specific wire fields in a leading comment when using toggle).
  • [medium] [violation] providers/hubris/models/anthropic/claude-fable-5.1.toml:10 - Check: Non-USD pricing must note FX conversion in a leading top-of-file comment. Why: Costs are RUB→USD conversions, but every model TOML starts at bare base_model/[cost] with no rate/date header. AGENTS.md requires the conversion rate and date above the first key so the USD figures remain auditable after sync/comment stripping. Action: Add a leading comment on each priced file (or a shared provider-level header pattern on every model file) stating Bank of Russia 86.5857 RUB/USD on 2026-09-05 (or the actual rate/date used) and that values are USD/MTok.

Syncs providers/hubris from the public Hubris catalog. Prices are RUB and
are converted at the Bank of Russia official daily rate (XML feed, JSON
mirror as fallback); previously synced USD prices are kept while the
converted value drifts less than 3% so daily FX moves do not churn files.
Every model factors onto lab metadata via base_model; models without a
models/ entry are skipped and listed in the notices. Catalog grows from
the 26 hand-picked models to 185.
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Action items

  • [high] [violation] packages/core/src/sync/providers/hubris.ts:1036 - Check: Multi-model relay reasoning_options must follow lab + same-surface peer baselines; do not invent a universal effort enum or budget_tokens (AGENTS.md Reasoning options; audit skill Step 2/4). Why: reasoningOptions() hardcodes effort = none|low|medium|high|max plus bare budget_tokens for every catalog reasoner, then rewrites ~100 Hubris model files. That contradicts first-party/peer controls: DeepSeek V4 is toggle + high/max (no budget); GPT-5.x is effort-only (often including minimal/xhigh, never budget); Claude 4.7+/Opus 5 is adaptive effort without invented none+budget; Gemini 2.5 Flash is toggle/budget (not L–H effort); GLM-5.3 is low/high/max always-on; DeepSeek R1 peers use []. Downstream clients get false controls. Copying Requesty’s same shortcut does not satisfy policy. Action: Replace the blanket mapper with per-model (or per-family) options from the lab entry and established OpenAI-compatible peers; only emit budget_tokens when this host exposes a real reasoning-budget field; drop invented levels; re-sync all Hubris reasoners; document the host wire fields in sync.md / the PR body.

Reasoning options now come from the OpenRouter entry for the same route
(same wire surface), falling back to the lab's first-party entry, instead
of a templated effort list; reasoners with no resolvable controls are
skipped. Every model file gets a leading FX header with the Bank of
Russia rate and date used for the RUB->USD conversion, plus wire-path
comments for the emitted controls.
@Aimagine-life

Copy link
Copy Markdown
Author

Addressed the review items in the latest commit:

  • Reasoning controls — no more templated effort+budget_tokens. The sync now takes reasoning_options from the OpenRouter entry for the same route (same id, else same base_model) — Hubris forwards the OpenRouter-style reasoning object, so that is the same-surface peer — falling back to the lab's first-party entry, then to locally authored options; a reasoner with none of those is skipped for manual authoring. Result per the examples raised: Claude Opus 5 / Fable / Opus 4.8 → toggle + low|medium|high|xhigh|max, Claude Haiku 4.5 → toggle, DeepSeek V4 → toggle + low|high|max, GPT-5.5 → none|low|medium|high|xhigh, Gemini 2.5 Flash → toggle, GLM 5.3 → low|high|max, Kimi K3 → toggle + low|high|max, MiniMax M3 → toggle. All 185 files re-synced from the module, so hand-authored blocks are gone.
  • FX header — every model file now starts with # FX: … Bank of Russia official rate 86.5857 RUB/USD (2026-09-05) …, written by the sync (authoritativeHeaders), followed by the wire-path comment for the emitted controls (reasoning.enabled / reasoning.effort / reasoning.max_tokens). The header is kept together with the prices while the converted values drift less than 3 %, and refreshed with the new rate/date when they actually change.

bun models:sync hubris --dry-run is clean after the run and bun validate passes.

@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Action items

  • [medium] [violation] packages/core/src/sync/providers/hubris.ts:4899 - Check: Sync modules that skip source models must surface them in notices when they claim to (see sync.md / peer providers like Merge Gateway). Why: sourceID returns chat model IDs for every skipped translate (undefined from missing base_model or unresolved reasoning_options), but there is no skippedNotice. The runner only emits provider.skippedNotice?.(skippedRemote), so those IDs are dropped silently—contradicting the PR/sync.md claim that missing lab/control cases are “listed in the sync notices.” Action: Add a skippedNotice (and optionally distinguish no-metadata vs no-reasoning-controls) so skipped remote IDs appear in the sync report.
  • [medium] [violation] packages/core/src/sync/providers/hubris.ts:4867 - Check: When base_model maps a distinct host route onto a shared lab model, the provider entry must override name (OpenRouter does this via shouldPreserveFactoredName / display_name). Why: Catalog displayName is parsed but never written. Routes such as openai/gpt-5.6-luna-probase_model = "openai/gpt-5.6-luna" (and sol/terra-pro) therefore inherit the non-Pro lab name (“GPT-5.6 Luna”) while the host ID and OpenRouter peer are Pro-labeled. Action: When the resolved base_model slug differs from the Hubris model id (or always when displayName differs from the lab name), pass name: model.displayName into factorBaseModel overrides so Pro/alias routes keep distinct display names.

Skipped remote ids are reported through skippedNotice, grouped by reason
(no lab metadata / no resolvable reasoning controls). Routes whose slug
differs from the lab model (gpt-5.6-*-pro) keep their own display name.
@Aimagine-life

Copy link
Copy Markdown
Author

Both follow-ups addressed in the latest commit:

  • Skip noticesskippedNotice added; skipped remote ids are now grouped by reason in the sync report (Skipped N Hubris model(s) with no lab metadata: … / … with no resolvable reasoning controls: …). Current run: 136 catalog routes without a models/ entry are listed, none skipped for reasoning controls.
  • Route names — when the route slug differs from the resolved lab slug (the gpt-5.6-*-pro aliases), name is written from the catalog display name (name = "GPT-5.6 Luna Pro" etc.); routes that only differ by punctuation (claude-haiku-4.5claude-haiku-4-5) keep the inherited lab name.

bun models:sync hubris --dry-run clean, bun validate green.

@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Action items

  • [medium] [violation] packages/core/src/sync/providers/hubris.ts (JSON FX fallback in fetchUsdRate) - Check: RUB→USD conversion must yield a valid YYYY-MM-DD rate date (same contract as the XML path / UsdRate schema). Why: parseCbrXmlUsdRate converts CBR’s DD.MM.YYYY into ISO, but the JSON mirror path does date: json.Date.slice(0, 10) with no conversion. cbr-xml-daily-style Date values are DD.MM.YYYY, which fail UsdRate’s /^\d{4}-\d{2}-\d{2}$/ check and abort the whole Hubris sync whenever the primary XML feed errors. Action: Normalize the JSON mirror date the same way as the XML parser (or parse a field that is already ISO), and add a focused unit test covering both FX sources.

@Aimagine-life

Copy link
Copy Markdown
Author

Fixed: the JSON-mirror fallback now parses its ISO timestamp with Date and emits the same YYYY-MM-DD as the XML path, validated through the shared UsdRate schema (invalid dates throw instead of slipping through).

@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Action items

  • [high] [violation] packages/core/src/sync/providers/hubris.ts:8454 - Check: Sync skip must not delete files that should stay for manual authoring (sync.md runner contract; MissingReasoningOptionsError retention path). Why: When reasoning is true and resolveReasoningOptions returns undefined, buildHubrisModel returns { skip: "no resolvable reasoning controls" } and translateModel yields undefined. The runner then omits that ID from the desired set and deletes the local TOML on the next sync (Hubris does not set deleteMissing: false). That contradicts the “left for manual authoring” comment and will wipe any hand-authored reasoner that lacks OpenRouter/lab peers. Action: Preserve those IDs (throw MissingReasoningOptionsError, keep/return the existing model, or otherwise exclude them from delete-missing), matching Cloudflare AI Gateway / other syncs.
  • [medium] [violation] packages/core/src/sync/providers/hubris.ts:8563 - Check: FX header date must be the Bank of Russia publication day in YYYY-MM-DD, matching the XML path. Why: The JSON-mirror fallback does new Date(json.Date).toISOString().slice(0, 10). CBR mirror timestamps are typically …T…+03:00; UTC conversion can move the calendar day (e.g. early Moscow morning → previous UTC date), so reused FX headers/prices can be labeled with the wrong date. Action: Parse the mirror date as a calendar date (e.g. take the leading YYYY-MM-DD / local components) without UTC toISOString day-shifting, same contract as the XML parser.

Reasoning models with no OpenRouter/lab controls now raise
MissingReasoningOptionsError so the runner keeps any existing file and
reports them instead of deleting. The JSON-mirror fallback takes the
Bank of Russia calendar day as written instead of a UTC-shifted date.
@Aimagine-life

Copy link
Copy Markdown
Author

Both addressed in the latest commits:

  • Retention — a reasoner with no OpenRouter/lab reasoning_options now throws MissingReasoningOptionsError instead of being dropped from the desired set, so the runner keeps any existing TOML and lists the model in the report (same path as Cloudflare AI Gateway). skippedNotice is now only about routes without lab metadata.
  • Mirror date — the JSON fallback takes the calendar day as written in the mirror's Moscow-time timestamp (/^\d{4}-\d{2}-\d{2}/), no Date/UTC conversion, so the FX header can't slip to the previous day.

Dry-run clean, bun validate green.

@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

No actionable findings.

@github-actions github-actions Bot added the reviewer: ready Automated review found no actionable items label Sep 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

reviewer: ready Automated review found no actionable items

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant