Skip to content

feat(aihubmix): sync the full model catalog from the endpoint - #6670

Closed
0genlab wants to merge 20 commits into
anomalyco:devfrom
0genlab:feat/aihubmix-sync
Closed

0genlab wants to merge 20 commits into
anomalyco:devfrom
0genlab:feat/aihubmix-sync

Conversation

@0genlab

@0genlab 0genlab commented Sep 10, 2026

Copy link
Copy Markdown

Adds a fully interface-driven sync adapter for AIHubMix, an aggregator that
relays ~408 LLM routes from 26 model labs. Everything is read from the public
catalog endpoint — no credentials, no per-model overrides in the adapter.

GET https://aihubmix.com/api/v1/models?type=llm

This PR ships the adapter only

An earlier revision carried the 299 generated providers/aihubmix/models/*.toml
files alongside the adapter. That pushed the diff past GitHub's 300-file ceiling,
gh pr diff --patch answered HTTP 406, and pr-reviewer.yml died in its
context step before the agent ever started — so no revision of the branch could
reach a review at all.

The catalog files are now dropped from the PR. Once the adapter merges,
sync-models.yml regenerates them on automation/sync-models-aihubmix, a branch
the reviewer workflow already skips by design. The diff here is 13 files: the
adapter, three framework/test files, sync.md, and eight stale alias files this
revision removes (below).

Answers to the review findings

toggle alongside an effort list containing none — folded, per AGENTS.md

34 of 408 routes publish both {type = "toggle"} and a graded effort list
containing none. That is not a defect in the source: AIHubMix is a
multi-dialect gateway, and it accepts whichever off switch the caller's SDK
speaks, mapping each onto the vendor's real control rather than erroring.
providers/aihubmix/provider.toml on dev already documents all four paths:

  • Raw Chat — $.reasoning_effort = "none"|"minimal"|…|"xhigh"; "none" disables
  • Raw Messages — $.thinking.type = "enabled"|"disabled"|"adaptive"
  • Raw Gemini — $.generationConfig.thinkingConfig, thinkingBudget: 0 off
  • Raw Responses — $.reasoning.effort

Verified live against glm-5.2 on 2026-09-11 with a fixed prompt: both
reasoning_effort: "none" and enable_thinking: false independently produce 0
reasoning characters, sending both together is coherent rather than an error,
minimal (217 chars) and max (328) are distinct live tiers — and a bogus
reasoning_effort: "turbo" returns HTTP 200 mapped to a real tier, not 400.

But the catalog spells this one way, and AGENTS.md is explicit: graded effort
that already carries none stands alone, with no toggle. That is a shape
rule applied uniformly — not per-model knowledge — so the adapter now folds it,
and the dialects that reach the same off state are named in the file header,
which is where AGENTS.md says they belong:

# Off is effort=none; graded levels — no toggle. The same off elsewhere:
# $.enable_thinking = true|false on the OpenAI-compatible /v1/chat/completions path (verified live 2026-09-11);
# $.thinking.type = "enabled"|"disabled"|"adaptive" on /v1/messages; $.generationConfig.thinkingConfig on the Gemini path.
# https://docs.aihubmix.com/cn/api/unified-inference
reasoning_options = [{ type = "effort", values = ["none", "minimal", "high"] }]

A toggle whose effort list has no off value is untouched — { effort = ["high", "max"] } + toggle still writes both. Regression tests cover both shapes,
including the no_thinknone alias path.

(An earlier comment of mine on this PR said this shape "is being fixed at the
source". That was my own inference and it was wrong; it is documented, intended
gateway behaviour. The catalog-side normalisation above is the right place to
reconcile it.)

Wire path for the toggle — fixed

The reviewer was right that {type = "toggle"} on its own does not tell a caller
which field to send, and that a sync rewrites the file whole and would erase a
hand-written header. translateModel now emits the header itself whenever the
model carries a reasoning control — the same shape the OpenRouter adapter uses —
so the wire path survives every rewrite instead of depending on a human re-adding
it.

A sync must not delete what only the file records — fixed

The endpoint has no surface for several things a provider file carries, and the
adapter rebuilt each object from the endpoint answer alone. All of these now
carry through from the authored file, the way the Anthropic, OpenRouter and
Merge Gateway adapters do:

field what would have been lost
experimental, provider the [experimental.modes.fast] block and its nested request body on gpt-5.4, gpt-5.4-mini, gpt-5.5
limit.input the 922k input cap the catalog models and the endpoint does not
budget_tokens min/max the endpoint states a budget exists but never its range, so a bare option on a base_model file would replace the lab's real bounds with an unbounded control
input_audio, output_audio, cost.reasoning per-tier audio rates on the two doubao-seed-2-0 routes and gemini-2.5-flash

Two more in the same family:

  • Echoed cache_read. 6 models and 4 context tiers repeat the input price in
    cache_read. An omitted price field already means "no such rate" for this
    endpoint, so an echoed one is now read the same way rather than published as a
    discount it is not.
  • Narrowed modalities. The endpoint under-reports: text,image for
    kimi-k2.5, whose lab entry records video, and text for
    qwen3.8-2.4t-a95b, whose own file notes a live 200 on image input. The
    endpoint list is now unioned with what the file recorded — it can widen a
    route, never silently narrow one. (58 of 60 existing files already match the
    live list exactly; pdf in particular is served now, 53 routes including
    gpt-5.4 as text,image,pdf. The two gaps are reported upstream.)

A create must not override lab metadata off — fixed

The same class of bug one level deeper, and the more dangerous half of it:
dev carries 77 aihubmix files, so most of the catalog arrives as a create
with no file to fall back on. Anything read from existing is simply absent on
the run that writes the other 180.

what was wrong evidence now
reasoning / tool_call fell back to false the endpoint never sends false: 107 of 408 routes omit reasoning, 100 omit tool_call, 0 deny either a missing flag stays undefined, so the lab value is inherited
modalities unioned only with the file 14 creates would have written a narrowing override — gpt-4o, gpt-4.1, o3, claude-opus-4-1, gpt-5.5-pro, gpt-6-astra losing pdf; six qwen3.5/qwen3.6 routes losing audio; mimo-v2-omni losing pdf the union now includes the lab entry the relay factors onto, then the file
a retained header could outlive its options a folded toggle kept advertising a toggle authoritativeHeaders: true

authoritativeHeaders on its own would have deleted the price citations, source
links and live-test records humans wrote into the same comment block — seven
files on dev carry one. translateModel now reads the existing header
(one new optional accessor on the sync context) and supersedes only the
wire-path lines it authors; everything else is carried through verbatim.

Under-reported relay limits written as authoritative overrides — fixed

The reviewer was right and my earlier answer here was wrong. I had measured only
existing files against their lab entry and reported "two of 206"; the actual
exposure is on the creates, where there is no file. Re-measured across every
entry the adapter writes: 25 narrowing [limit] overrides, not two.

They are not all the same thing, and that is the whole fix. Most are the endpoint
restating a binary window in decimal — eight glm routes quote 204800 as 200000,
four quote 1048576 as 1000000 — which is not the host narrowing anything. A
handful are real host caps. A ratio separates them without any per-model table: a
decimal restatement of a binary window loses at most 1000/1024 per K unit, so
1000³/1024³ (three nested unit swaps) is the floor of what a restatement can
explain. A stated limit below an accepted one but at or above that ratio resolves
to the accepted value and writes no override.

before after
narrowing [limit] overrides written 25 7

The 18 retired: 8 glm routes at 204800→200000 (0.977), 4 at 1048576→1000000
(0.954), mimo-v2-omni 262144→256000, three MiniMax files an earlier sync had
already written at 131072→128000/128100, and qwen3.6-plus-preview-free at
65536→65535.

The 7 that survive are an order of magnitude away from a unit swap and are kept:
grok-code-fast-1 caps output at 10000 of a 256000 window (0.039), four Kimi
routes at 32768 of 262144 (0.125), gpt-5-chat-latest at 16384 of 128000 and a
128000 context of the lab's 400000 (0.320).

Two details the measurement forced:

  • The test is applied to whichever side states the limit, not to the endpoint
    quote, which is what retires the three MiniMax restatements already sitting in
    files on dev.
  • When the endpoint quotes nothing, the fallback is the authored value, not
    the lab's. An authored limit is not a stale copy of the lab's but a narrower one
    on purpose — kimi-k2.5 serves 32768 of a 262144 window — and widening it to
    the lab's would have deleted a real cap. My first attempt did exactly that.

A full standalone entry for a model a lab built — fixed

AGENTS.md is explicit: if the provider did not create the model, the provider
entry must use base_model. The adapter honoured that whenever the lab entry
existed, but fell through to the standalone path when it did not — writing a
complete third-party TOML for, say, a DeepSeek model into providers/aihubmix/.

A standalone entry is now only authored where the response names no vendor.
A named lab means the relay belongs on base_model, so the relay is skipped and
reported for a human to add models/<lab>/<id>.toml, after which it factors with
no change to the adapter.

before after
standalone full definitions written 51 7
new standalone creates for models a lab built 38 0

The 7 remaining are pre-existing files on dev being updated, not new creates.
81 of 407 routes name a vendor whose lab entry does not exist yet — 38 of them
described completely enough that the endpoint answer alone would have satisfied
the standalone guard. Those 81 now open deduped [missing-model] issues instead
(next section).

A standalone file already in the repo keeps being updated rather than frozen:
what it should have been is upstream's call, and freezing it would only stall its
prices.

Skips that produced notices nobody acts on — fixed

skipCreates is unset for aihubmix because creates do run, and the runner only
fed skippedRemote into the missing-model issue path when skipCreates === true.
So every unresolved relay produced a line in the sync report and nothing else.

SyncProvider gains trackMissingModels?: boolean — implied by skipCreates,
and settable on its own for exactly this case: a provider that creates most
models but still cannot write some of them. aihubmix sets it, and each skipped
relay now opens one deduped [missing-model] issue naming the file to add.

Blast radius checked: ofox and pioneer are the only other providers with the
flag and both already set skipCreates: true, so no existing provider's
behaviour changes.

budget_tokens bounds copied from a lab or peer entry — cannot be done

The suggestion was to copy min/max from the same model's first-party or peer
provider entry when the endpoint sends a bare budget. There is nowhere to copy
from, at the schema level: ModelMetadata (packages/core/src/schema.ts)
has no reasoning_options field at all, so a models/<lab>/<model>.toml entry
cannot state a budget range. Measured across the current catalog: 0 of 370
models/ entries carry a reasoning_options key, and the only four files that
mention it at all do so in a comment saying cost and reasoning_options are
"intentionally left to the provider files".

That is also the right boundary rather than an accident. A budget range is a
property of the host's API — what AIHubMix will accept on the wire — not of the
model, so copying another provider's range in would assert something about
AIHubMix that no source states.

What the adapter does instead, unchanged from the last round: when a file already
carries min/max, they are carried through rather than replaced with an
unbounded control. On a create with no file, the bare budget is written as the
endpoint states it, because the alternative is inventing bounds. The 103 bare
budgets are reported upstream.

Intersecting a relay's options with its lab/peer baseline — declined

The suggestion was to narrow a relay's advertised tiers against what its lab
model or sibling routes advertise. That would put per-model knowledge back into
the adapter — the exact thing this rewrite removed (the developer_id → lab
table and the prefix/suffix strip list). The gateway's tier support is a property
of the gateway, not of the lab: AIHubMix deliberately accepts tiers the upstream
vendor does not name and maps them. An intersection would silently drop tiers
that demonstrably work. Where the endpoint is wrong, the fix belongs on the
endpoint; two such fixes have already shipped there during this PR.

Eight stale alias files removed

alicloud-glm-5.1, zai-glm-5.1, deep-deepseek-v4-pro,
deep-deepseek-v4-flash, alicloud-deepseek-v4-pro,
alicloud-deepseek-v4-flash, xiaomi-mimo-v2.5 and xiaomi-mimo-v2.5-pro are
deleted from providers/aihubmix/models/.

An earlier revision of this PR kept them and described them as "routing aliases
served but not listed". That was the wrong reading. The prefix on each is a
channel namealicloud, zai, deep, xiaomi are the upstream channel
the gateway routes through, not a distinct model — and each one answers HTTP 200
by relaying to a model already in this catalog, echoing that model's ID back in
the response. They duplicate glm-5.1, deepseek-v4-pro, deepseek-v4-flash
and mimo-v2.5 under a channel name.

"Callable" is therefore not the catalog boundary; the endpoint's main model list
is. Several hundred further routes are callable on the gateway without appearing
in it. The eight files predate that boundary being enforced.

A route can still rotate out of the list for a spell without being retired, so
deleteMissing stays false — but the missing-file notice now names both
readings so the next occurrence gets judged rather than kept by default.

What the interface-driven rewrite removed

The adapter originally landed 232 models, resolving relays to lab metadata with
a hand-maintained developer_id → lab table plus a list of routing prefixes and
suffixes to strip. Both are gone. AIHubMix now serves vendor, variant_of
and open_weights on the endpoint itself, so nothing about a relay is inferred
from its ID or mirrored in this repo any more — the adapter reads the catalog's
own answer. That change is what brings the catalog to 211.

first revision now
aihubmix models in the built catalog 232 211
entries using base_model 217 204
standalone full definitions 15 7
lab mapping 21 hand-maintained developer_id entries vendor from the endpoint
relay → base matching 16 prefixes + 10 suffixes stripped from the ID variant_of from the endpoint
open_weights not served; every standalone create blocked served for 289 of 408

How models are matched to lab metadata

The endpoint answers both halves of the lookup itself:

  • vendor names the lab that built the model (292 of 408 routes).
  • variant_of names the AIHubMix ID this entry is a routing variant of, and
    variant_kind labels it (pricing-tier 26, channel-tier 24,
    mode-preset 18, deprecated-alias 8).

A relay is looked up under its own ID first, then under each variant_of hop,
nearest first. Nearest-first matters: qwen3.8-max-preview is declared a
variant of qwen3.8-max and both are published lab models, so the relay factors
onto the preview it actually serves rather than onto the root of its chain.

Following a declared chain also reaches relays no string rule could have:

relay resolves to why a prefix/suffix rule can't
ox-alpha zhipuai/glm-5.3-flash codename shares no substring with the model
grok-code-fast-1 xai/grok-build-0.1 renamed upstream
cohere-command-a cohere/command-a-03-2025 the date tag is the lab's, not the relay's
grok-4-fast-reasoning xai/grok-4.3 deprecated-alias: the old ID now routes to 4.3

The one thing left in the adapter is VENDOR_LABS, four entries for the labs
the two registries spell differently (zhipu/zhipuai, moonshot/moonshotai,
bytedance/bytedance-seed, meituan-longcat/meituan). It maps namespaces;
no entry in it decides what a model is or which lab built it.

Lookups stay case-folded: AIHubMix lowercases every relay ID while labs keep
their own casing, so minimax-m2 has to reach minimax/MiniMax-M2.

Dated release tags are deliberately left on. gemini-2.5-pro-preview-06-05 is a
pinned snapshot, not google/gemini-2.5-pro — and the endpoint does not declare
it a variant of one, which is now the actual test rather than a judgement call
in the adapter.

A resolved base means the entry inherits open_weights, knowledge, license
and the rest from models/, and records only what actually differs — which is
why 204 of 211 entries are three or four lines long.

Standalone entries are skipped, not guessed

A standalone entry is only legal where the response names no vendor (above),
and it still has to carry release_date, open_weights and both limits itself.
The adapter skips anything that satisfies neither condition and reports it
through skippedNotice and a deduped issue rather than inventing a value.

open_weights used to block every standalone create because the endpoint did
not serve it. It now does, for 289 of 408 routes, which is what lets the adapter
create standalone entries at all. The guard also checks limits now: the endpoint
sends max_output: 0 for a ceiling it does not know, and without that check
those models reach the writer and fail ModelBase validation.

What is still uncovered, entirely on the endpoint side:

  • 116 routes carry no vendor, so they cannot be factored
  • 104 date to no release_date
  • 119 state no open_weights
  • 104 quote max_output: 0

Every one of these shrinks as AIHubMix backfills its own catalog. The adapter
needs no change for any of them.

Sentinel values in the source data

Two signals mean "unknown", not "zero" or "huge", and are read as absent so the
field falls through to the base model:

signal count why
max_output: 0 104 a servable model cannot emit zero tokens
max_output >= context_length 36 the context window quoted a second time, leaving no room for the prompt

No value is invented — the field is simply omitted and inherited.

Reasoning options

The endpoint carries an extra default key inside reasoning_options[] that
ReasoningOption does not accept, and two non-standard effort spellings,
no_think and instant. The adapter drops the unknown key and maps the
spellings onto none / minimal, filtering anything else outside
ReasoningEffortValue, and folds a toggle into a graded effort list that already
carries none. These are shape-level normalisations — vocabulary and option
shape, not per-model judgement. All four source quirks are reported upstream to
AIHubMix.

Deletion policy

deleteMissing is false and trackMissingModels is true. AIHubMix rotates
routes in and out; a transient absence should not delete a catalog entry. The
sourceID hook returns undefined for retire_stage === "deprecated" so
retired routes are marked, not removed.

Three framework changes

  • formatToml now emits input_audio / output_audio inside cost tiers. Both
    are already in Cost, but were silently dropped when writing tiered pricing.
  • translateModel's context gains an optional header(id) accessor, so a
    provider that owns its header can refresh the block it generates without
    discarding the notes a human wrote around it. Optional, so no other provider
    changes.
  • SyncProvider gains trackMissingModels?: boolean, so a provider that creates
    models but still skips the ones it cannot write opens issues for those skips.
    Implied by skipCreates; the two providers that would be affected already set
    it. The issue body wording is corrected alongside it — it asserted the provider
    used skipCreates, which is no longer true of every caller.

Verification

  • bun run validate — exit 0, aihubmix 211 models in the built catalog
  • bun test packages/core/test/sync.test.ts — 219 pass, 2 fail (both
    pre-existing on dev: DeepInfra modalities and an LLM Gateway case-variant
    assertion that only fails on case-insensitive filesystems). Three new tests
    cover the limit-restatement rule, the standalone refusal, and the
    create-but-skip issue path.
  • bunx tsc --noEmit -p packages/core — unchanged from the dev baseline
  • full sync from the dev baseline — 142 created, 69 updated, 0 removed;
    running it again — 0 created, 0 updated, 0 removed (idempotent)
  • built-catalog diff against dev — no provider other than aihubmix changes

Note for maintainers on the sync gate

aihubmix is not in REVIEWED_REASONING_PROVIDERS, and most of its routes are
reasoning models, so the hourly sync PR will be classified unsafe on nearly
every run and wait for a human. That is the right default for a new adapter —
happy to leave it there until it has a track record.

🤖 Generated with Claude Code

chenxue and others added 3 commits September 9, 2026 11:14
`CostTier` extends `Cost`, so `input_audio` and `output_audio` are valid on a
tier, but `formatToml` only emitted them for the top-level `[cost]` table.
Any sync that rewrote a model with tiered audio rates silently dropped them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
AIHubMix has had no sync module, so its 77 models were only ever refreshed by
hand-written PRs. The last one landed 2026-08-31, which is why prices have
drifted and new relays never arrive on their own.

The endpoint (`https://aihubmix.com/api/v1/models?type=llm`, no auth) is
authoritative for pricing and deprecation status only, matching the Ofox
scope. Token limits and modalities are deliberately not synced: the endpoint
reports each relay's conservative defaults rather than the upstream model's
capabilities. It caps `context_length` per relay (Claude Opus 4.6 is listed at
200K against its 1M window), quotes `max_output` per default request, and never
lists `pdf` even for models that accept PDFs.

`cache_read` is ignored when it equals `input`: the endpoint echoes the input
price for models with no cached rate configured, which covers 35 of the 301
priced entries at a nonzero price (plus 51 free models reporting 0 across the
board, where the guard is a no-op). Taking the echoed value literally would
have set Gemini 3.1 Flash Lite to $0.25 against the $0.025 that 26 other
providers list.

The first run updates 17 models. Beyond precision refinements it corrects real
drift: GPT-5.6 Luna to OpenAI's own $0.20/$1.20 (was $1/$6), Sol and Terra to
their current cuts, Gemini 3.5 Flash's `cache_read` from $1.50 to $0.15 (the
authored value had the same echoed-input bug), and the Coding MiMo v2.5 output
rates onto Xiaomi's actual 2:1 ratio.

New relays are not created automatically (`skipCreates`) since AIHubMix serves
roughly 400 upstream models against this hand-verified subset; each missing ID
opens a deduped issue instead. Routing aliases such as `alicloud-glm-5.1` are
served but unlisted, so local files absent from the response are retained.

The Gemini 2.5 Flash thinking-budget comment moves to the file header, which is
the only comment block `formatToml` preserves.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
AIHubMix now serves capabilities, limits, modalities and reasoning
controls alongside pricing, so the adapter reads all of them instead of
treating the endpoint as authoritative for cost and status alone.

Relays are factored onto the lab metadata they serve: `developer_id`
maps a relay to its lab, and routing prefixes (`coding-`, `alicloud-`)
and suffixes (`-free`, `-think`, `-nothink`) select a mode rather than a
different model, so they are stripped when resolving the base. A relay
then records only what it actually changes. With bases resolving, new
IDs no longer need to be held back, so `skipCreates` is dropped and 155
relays are created.

Three source quirks are handled in translation rather than written
through: `reasoning_options[]` carries an AIHubMix-only `default` key
the strict schema rejects, two effort levels are spelled `no_think` and
`instant`, and `max_output: 0` means "unknown" rather than a real
ceiling for 102 of 415 models.

A relay with neither resolvable lab metadata nor the release_date and
open_weights a standalone entry requires is reported rather than written
with invented values.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Action items

  • [high] [violation] packages/core/src/sync/providers/aihubmix.ts:1700 - Check: Reasoning options must match lab/peer baseline; never pair toggle with effort that already includes none; never invent full effort enums or budget_tokens. Why: reasoningOptions() blindly forwards endpoint toggle/effort/budget_tokens after aliasing no_thinknone. That produces invalid shapes (toggle+none), full-schema dumps (nonemax), and budgets on hosts/models that only have effort or no control. Action: Normalize after parse: drop toggle when effort contains none; intersect effort with lab/same-surface peers (not the full enum); emit budget_tokens only for families that actually expose a reasoning budget on this path; add regression tests for these cases.
  • [high] [violation] providers/aihubmix/models/glm-5.2.toml:8 - Check: GLM-5.2 baseline is lab high/max (Zhipu maps lower aliases); no toggle+none. Why: New options are toggle + full ["none","minimal","low","medium","high","xhigh","max"], contradicting providers/zhipuai/models/glm-5.2.toml and AGENTS anti-patterns. Same shape on coding-glm-5.2.toml / coding-glm-5.2-free.toml. Action: Use lab-aligned options (typically effort high/max, or effort-with-none only if this host truly uses effort-as-off and then no toggle); fix coding variants the same way.
  • [high] [violation] providers/aihubmix/models/minimax-m2.7.toml:1 - Check: Non-lab hosts must base_model when the lab model is nameable; relay reasoning_options must not invent controls the lab marks as none. Why: Lab metadata exists as minimax/MiniMax-M2.7 and OpenRouter factors onto it with reasoning_options = [], but AIHubMix keeps a full standalone file and invents effort + budget_tokens. Same for coding-minimax-m2.7*.toml. Sync resolveBaseModel only exact-matches after prefix/suffix strip, so minimax-m2.7 never hits MiniMax-M2.7. Action: Add canonical ID aliases (or case-insensitive match) so these resolve to minimax/MiniMax-M2.7, factor override-only files, and keep reasoning_options = [] unless AIHubMix docs prove real caller controls.
  • [high] [violation] providers/aihubmix/models/doubao-seed-2-0-pro.toml:1 - Check: Third-party relays of lab models must use base_model; options must not be toggle+none full enums. Why: Lab metadata is bytedance-seed/seed-2.0-pro (and siblings), but these stay fully inline and now use toggle + ["none","minimal",…,"max"]. Resolver never maps doubao-seed-2-0-*seed-2.0-*. Action: Add doubao/seed ID mapping, factor onto bytedance-seed/…, and author only real AIHubMix deltas (cost/limits/options).
  • [high] [violation] providers/aihubmix/models/gpt-5-mini.toml:2 - Check: On relays, [] means no caller control—not missing data; GPT-5.x lab/peers expose effort. Why: New entries stamp reasoning_options = [] for models whose first-party files use effort (e.g. GPT-5 Mini/Nano minimal|low|medium|high, o1 low|medium|high). That understates controls consumers can send. Also gpt-5-codex.toml, gpt-5-nano.toml, gpt-5.1-codex-max.toml, o1.toml, o1-pro.toml, and similar. Action: Copy lab/peer effort sets (or document verified no-control); stop defaulting empty from a sparse endpoint payload.
  • [high] [violation] providers/aihubmix/models/hy3.toml:4 - Check: toggle must not appear alongside effort values that include none. Why: hy3.toml, hy3-free.toml, hy3-preview.toml, and hy4-preview.toml all author toggle + effort containing none (and hy4 also adds bare budget_tokens). Action: Prefer effort-only with none, or toggle + graded levels without none; drop unverified budgets.
  • [high] [violation] providers/aihubmix/models/gemini-3.5-flash.toml:7 - Check: budget_tokens only when this host/lab path has a real reasoning budget. Why: Lab Gemini 3.5 Flash is effort-only; the PR adds bare budget_tokens across many Gemini 2.5/3.x relays (and MiniMax). Provider.toml even notes Responses has no reasoning-token budget. Action: Emit budget only for verified budget surfaces (legacy Gemini thinkingBudget where true, older Claude extended thinking, some Qwen); strip elsewhere.
  • [medium] [violation] providers/aihubmix/models/deepseek-v4-pro-0813.toml:12 - Check: DeepSeek V4 Pro baseline is toggle + high/max (lab maps low→high). Why: Effort widened to ["low","high","max"] against providers/deepseek/models/deepseek-v4-pro.toml and the file’s own prior high|max comment. Action: Keep Pro at high/max (Flash may keep low); restore leading wire comments stripped by sync.
  • [medium] [violation] providers/aihubmix/models/glm-5.3.toml:5 - Check: Lab GLM-5.3 is always-on reasoner with effort low|high|max only. Why: PR adds toggle while Zhipu documents thinking cannot be disabled. Action: Match lab (effort only, no toggle) unless AIHubMix proves a separate off control.
  • [medium] [violation] providers/aihubmix/models/claude-opus-4-6.toml:7 - Check: Every toggle needs a leading top-of-file wire-path comment. Why: Sync re-serialization dropped the prior Messages/thinking.type comments across Claude and other toggle models, leaving toggles undocumented. Action: Preserve/re-emit a leading header (or adapter-owned header) with the exact AIHubMix wire path for each toggle family.
  • [medium] [possible mistake] packages/core/src/sync/providers/aihubmix.ts:1740 - Check: Do not treat echoed cache_read == input as a real cache rate. Why: Patch 2 guarded that echo (e.g. Gemini Flash Lite 10× overstate); full-catalog sync removes the guard and clears authored cache rates whenever the field is omitted. Action: Reintroduce the equal-to-input ignore (and only clear cache fields when the endpoint explicitly signals no cache), with tests.
  • [low] [possible mistake] providers/aihubmix/models/command-a-plus-05-2026.toml:3 - Check: Cohere command options/budgets vs lab peers. Why: toggle + effort none|high + bare budget_tokens looks like an unfiltered endpoint dump rather than a verified Cohere control surface. Action: Align with lab/peer Cohere reasoning controls or provide host-specific evidence before keeping this shape.

chenxue and others added 2 commits September 10, 2026 15:38
AIHubMix lowercases every relay ID while labs keep their own casing, so
`minimax-m2` never matched `minimax/MiniMax-M2` and the whole MiniMax
line fell through to the standalone path. The lab index is now
case-folded, and `nvidia-`/`bai-` join the routing prefixes with
`-highspeed`, `-fast` and `-latest` joining the suffixes. 24 relays that
previously had no resolvable base now factor onto one.

The endpoint signals an unknown output ceiling three ways: 0, the value
of `context_length` (51 of 415 models, which would leave no room for the
prompt), and a value above the window (6 models, up to 10x). All three
are read as absent so the base model's real ceiling shows through.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`cc-`, `mm-`, `aihubmix-`, `aihub-` and `ahm-` are AIHubMix's own
namespaces, and `cloudflare-`/`deepinfra-` name the upstream compute a
relay routes to, the same way `alicloud-` already did. Stripping them
resolves 16 more relays onto the lab metadata they serve.

`cc-minimax-m2` and `cc-MiniMax-M2` are one route under two spellings
and would claim filenames differing only in case, so the response is
deduplicated on the folded ID, keeping the last record whole.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Action items

  • [high] [violation] packages/core/src/sync/providers/aihubmix.ts:1700 - Check: Relay reasoning_options must follow lab/peer baselines (no full-enum dumps, no toggle+none, no invented budget_tokens). Why: reasoningOptions() only renames/filters endpoint values and never normalizes against lab/peers. That produces invalid shapes at scale: toggle + effort including none (e.g. Doubao, Qwen3.5/3.7, GLM-5.2), full nonemax dumps where labs are narrower (Qwen3.5 Plus is toggle+budget only; GLM-5.2 is high/max; DeepSeek V4 Pro is toggle+high/max), and bare budget_tokens on MiniMax/Claude paths the lab does not expose that way. The unit test even locks in effort with none plus toggle. Action: Normalize endpoint options against the resolved lab entry and same-surface peers (or drop untrusted endpoint options and keep/authored lab-faithful controls); reject toggle when effort already has none; never emit full schema enums or budget without a real host budget field.
  • [high] [violation] providers/aihubmix/models/deepseek-v4-pro-0813.toml:12 - Check: DeepSeek V4 Pro effort must match lab (toggle + high/max). Why: Sync rewrote this from the previously validated high/max set to ["low", "high", "max"], and new deepseek-v4-pro.toml does the same. Lab Pro maps low→high and does not advertise low. Action: Restore values = ["high", "max"] for Pro (Flash may keep low only if this host truly accepts it), and stop the translator from widening Pro’s set.
  • [high] [violation] providers/aihubmix/models/coding-minimax-m2.toml:6 - Check: MiniMax M2/M2.x relays must not invent caller controls the lab marks as always-on ([]). Why: First-party MiniMax M2–M2.7 use reasoning_options = [], but AIHubMix now authors effort L/M/H plus bare budget_tokens on coding/cc/mm MiniMax routes. That claims graded effort and a reasoning budget the lab API does not expose. Action: Use [] (or only controls AIHubMix docs prove for these routes); remove invented effort/budget from all MiniMax M2/M2.x AIHubMix files.
  • [high] [violation] providers/aihubmix/models/glm-5.2.toml:11 - Check: GLM-5.2 effort must match lab baseline high/max, not a full enum + toggle. Why: Zhipu lab documents effective levels as high/max (with none/minimal as skip-thinking aliases mapped away). The PR replaces that with toggle + the full nonemax list on glm-5.2 and coding/cloudflare variants. Action: Author lab-faithful options (effort high/max, or the exact AIHubMix wire set if narrower/different and documented); do not dump the full enum.
  • [high] [violation] providers/aihubmix/models/qwen3.5-plus.toml / qwen3.7-flash.toml:10 - Check: Qwen chat reasoners on relays should copy lab toggle (+ budget), not invent graded full-enum effort. Why: Alibaba first-party Qwen3.5/3.7 Plus/Flash are toggle + budget_tokens. The PR adds the full effort ladder (often with none alongside toggle) on many Qwen3.5/3.6/3.7/3.8 routes. Action: Align with lab/peers: keep toggle (+ budget when the host exposes it); drop invented L–max effort unless AIHubMix documents a distinct effort API for those IDs.
  • [high] [violation] providers/aihubmix/models/minimax-m2.7.toml:1 - Check: Provider models with reasoning = true (including via base_model) must set reasoning_options. Why: After factoring onto minimax/MiniMax-M2.7, this file (and sibling coding MiniMax M2.7 routes that lost explicit options in later patches) inherits reasoning = true from lab metadata that has no reasoning_options, and the provider file no longer declares any. That leaves resolved reasoners without controls. Action: Set provider reasoning_options explicitly (lab-faithful [] for M2.7, or real host controls if proven) on every factored MiniMax reasoner.
  • [medium] [violation] packages/core/src/sync/providers/aihubmix.ts:1700 - Check: Every toggle needs a leading top-of-file wire-path comment. Why: Sync emits hundreds of toggle entries but never sets header (unlike OpenRouter/Merge Gateway). Sync serialization strips mid-file comments, so toggles ship without the required wire documentation. Action: Return a leading header from translateModel with the exact AIHubMix toggle (and effort/budget) request fields once those are verified.
  • [medium] [possible mistake] providers/aihubmix/models/Qwen/QwQ-32B.toml:2 - Check: Do not override lab reasoning = true to false without host evidence. Why: Lab models/alibaba/qwq-32b.toml is a reasoner; this relay forces reasoning = false, which would hide reasoning capability if the endpoint is wrong. Action: Verify AIHubMix’s flag for this ID; if it reasons, drop the override (and author options); if it truly does not, cite host evidence in the PR.
  • [medium] [possible mistake] packages/core/src/sync/providers/aihubmix.ts:1600 - Check: Do not treat incomplete endpoint modalities as authoritative overrides of lab PDF/audio support. Why: Earlier AIHubMix notes said the endpoint omits pdf even when PDFs are accepted; the new adapter still writes endpoint modalities through factorBaseModel, and several Gemini/Claude files drop pdf (and audio rates) relative to lab/prior authored data. Action: Only override modalities when the endpoint is complete for that model, or preserve authored/lab richer input sets (especially pdf) when the API list is a known under-report.
  • [low] [possible mistake] packages/core/src/sync/providers/aihubmix.ts:1750 - Check: cache_read equal to input may still mean “not quoted” rather than a real cache price. Why: Patch 2 documented the echo-input bug (e.g. Gemini Flash Lite 10× overstatement) and guarded it; the full-catalog rewrite removed that guard and now trusts raw cache_read. Action: Confirm the endpoint no longer echoes input into cache_read; if it still does, restore the equal-to-input ignore (or equivalent) so false cache prices are not published.

AIHubMix fixed 14 routes that had quoted `max_output` equal to
`context_length`, plus two `context_length` values rounded to 131_000.
Every corrected value matches what the other providers in the catalog
already record for the same model.

Nine files change and all nine shrink: the endpoint now agrees with the
lab metadata, so the factored entries stop recording an override.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Action items

  • [high] [violation] packages/core/src/sync/providers/aihubmix.ts:1700 - Check: toggle must not coexist with effort that already includes none. Why: reasoningOptions() passes the endpoint list through unchanged, so many relays land invalid dual off-controls, e.g. coding-glm-5.2.toml, cloudflare-glm-5.2.toml, glm-5.2-fast-preview.toml, command-a-plus-05-2026.toml, and the Doubao Seed 2.0 family (toggle + values containing none). Action: When effort includes none, drop toggle (effort-only). Add a regression test covering that normalization.
  • [high] [violation] packages/core/src/sync/providers/aihubmix.ts:1700 - Check: Relay reasoning_options must follow lab + same-surface peers, not dump the full effort enum or invent controls. Why: Lab/peer baselines are much narrower, but the adapter writes broad invented sets. Examples: GLM-5.2 lab is high/max only, yet AIHubMix writes full nonemax (+ toggle); MiniMax M2/M2.7 lab and OpenRouter use [], yet AIHubMix invents L/M/H effort + budget_tokens; Gemini 3.5/3.7 Flash lab is effort-only, yet AIHubMix adds budget_tokens; DeepSeek V4 lab is high/max, yet AIHubMix adds low. Action: Normalize from lab/peer baselines (or verified AIHubMix wire docs), not raw endpoint enums. Do not invent budget_tokens unless this host exposes a real reasoning-budget field.
  • [high] [violation] packages/core/src/sync/providers/aihubmix.ts:1586 - Check: Every toggle needs a leading top-of-file wire-path comment. Why: Unlike OpenRouter / LLM Gateway / Merge Gateway, this adapter never returns a header for toggle models, so sync strips any prior comments and leaves dozens of toggle files undocumented (Claude, GLM, DeepSeek, MiniMax M3, etc.). Action: Emit a stable AIHubMix toggle header (exact request path) from translateModel whenever options include toggle.
  • [high] [possible mistake] packages/core/src/sync/providers/aihubmix.ts:1737 - Check: Do not treat echoed cache_read == input as a real cached rate. Why: Patch 2 documented that AIHubMix echoes input into cache_read for models with no cache price (and that taking it literally overstated Gemini 3.1 Flash Lite ~10×). Patch 3 removed that guard and now writes cache_read whenever present, so future syncs can republish false cache pricing. Action: Restore the equal-to-input ignore (or equivalent) and keep the regression test that previously locked this behavior.
  • [medium] [possible mistake] providers/aihubmix/models/coding-kimi-k3.toml:15 - Check: Output limits must not be whole-context sentinels. Why: output = 1_048_576 matches the lab context window (lab output is 131_072), and several MiniMax coding routes still force output = 204_800 equal to context. Patch 4 only drops max_output when API context is present and quoted >= context; missing/zero context still keeps the bad ceiling. Action: Tighten the sentinel (e.g. also compare against resolved base context) and re-sync so these overrides disappear when they match the window.
  • [medium] [possible mistake] providers/aihubmix/models/Qwen/QwQ-32B.toml:2 - Check: Do not flip lab reasoning off from a thin relay flag. Why: Lab models/alibaba/qwq-32b.toml is reasoning = true (always-on reasoner), but the new relay sets reasoning = false, which drops reasoning capability for this route. Action: Verify AIHubMix’s actual behavior for this ID; if it still reasons, remove the override (and set reasoning_options = [] if there is no caller control).

chenxue and others added 2 commits September 10, 2026 17:50
AIHubMix pins snapshot dates onto relay IDs (`gemini-2.5-pro-preview-06-05`)
while labs name the model itself (`google/gemini-2.5-pro`), so the tag has to
come off before the ID can match. Peel routing and date affixes to a fixed
point instead of one pass per rule, since they stack — `coding-gemini-2.5-pro-
preview-05-06-search` carries three, with the date wedged between two of them.

The date patterns are anchored and validate real month and day ranges so
`llama2-70b-4096` keeps its context size and `-13-45` stays attached to
nothing. The unstripped ID is still tried first, so a lab that genuinely
carries a date in its name (`cohere/command-a-03-2025`) still wins.

Also map developer_id 34 (muse-spark) and 35 (laguna) to the labs that
publish them. 36, 37, 43 and 25 have no lab directory in models/ at all,
so mapping them would not resolve anything.

27 relays now resolve to a base model: 299 of 415 source models covered,
up from 272.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The notes predated case-folded lookups, vanity prefixes, the second limit
sentinel and the date-tag rules, and quoted counts from an older snapshot
of the endpoint.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Action items

  • [critical] [violation] packages/core/src/sync/providers/aihubmix.ts:1700 - Check: AGENTS.md Reasoning options — never pair toggle with effort that already includes none; never dump the full effort enum; baseline = lab + same-surface peers. Why: reasoningOptions() passes the endpoint through after alias cleanup only. That lands invalid shapes across the catalog, e.g. coding-glm-5.2.toml / glm-5.2.toml (toggle + nonemax while Zhipu lab is effort high/max only), qwen3.5-*.toml / qwen3.7-*.toml (toggle + full nonemax + budget_tokens while Alibaba Qwen3.5 Plus is toggle + budget_tokens only), and Doubao seed files with the same toggle+none pattern. Action: Normalize before write: if effort contains none, drop toggle; replace full-enum dumps with lab/peer effort sets for that model; re-sync so existing TOMLs pick up the fix.
  • [high] [violation] packages/core/src/sync/providers/aihubmix.ts:1579 - Check: Every toggle needs a leading top-of-file wire comment (AGENTS.md). Why: Unlike OpenRouter/Merge Gateway, translateModel never sets header, so new/updated toggle models ship without a wire path (only rare preserved headers such as qwen3.7-flash keep one). Action: Emit an authoritative header documenting AIHubMix’s real toggle field(s) whenever reasoning_options includes toggle, and re-sync.
  • [high] [violation] providers/aihubmix/models/minimax-m2.toml:6 - Check: Relay reasoning_options must match lab/peer controls; do not invent budget_tokens or graded effort. Why: First-party MiniMax M2/M2.1/M2.5/M2.7 use reasoning_options = [] (always-on, no caller control). AIHubMix writes effort nonehigh plus bare budget_tokens on minimax-m2.toml, coding-minimax-m2*.toml, cc-minimax-m2*.toml, etc. Action: For those bases, author [] (or omit only if inheritance is correct) unless AIHubMix docs prove a real control surface; strip invented budgets from the adapter path.
  • [high] [violation] providers/aihubmix/models/Qwen/QwQ-32B.toml:2 - Check: Do not override a lab reasoner to reasoning = false without host evidence. Why: models/alibaba/qwq-32b.toml and Alibaba peers mark QwQ as reasoning = true (always-on). This relay sets reasoning = false, which drops reasoning capability for a known reasoner. Action: Remove the false override (inherit lab true + appropriate reasoning_options, typically []) unless AIHubMix documents a non-reasoning route.
  • [high] [possible mistake] providers/aihubmix/models/gemini-2.5-flash.toml:4 - Check: Gemini 2.5 Flash baseline is lab/peer toggle + budget_tokens (with verified bounds), not a free-form effort list. Why: Lab Google entry is toggle + budget min = 0 / max = 24_576. Synced AIHubMix drops toggle, invents effort none/minimal/low/medium/high, and keeps unbounded budget_tokens (same pattern on lite/preview/nothink/search variants). Action: Align with Google + AIHubMix wire docs (restore toggle + budget bounds, drop invented effort unless the host truly exposes it).
  • [medium] [violation] packages/core/src/sync/providers/aihubmix.ts:208 - Check: max_output equal to (or above) context_length must not become a lasting limit.output override. Why: Sentinel handling sets maxOutput to undefined, then falls back to existing?.limit?.output, so prior bad values stick—e.g. minimax-m2.7.toml / coding-minimax-m2.7*.toml still have output = 204_800 matching the window, leaving no prompt room vs lab ~128k. Action: Do not fall back to an existing output that is ≥ context; clear the override so lab output inherits.
  • [medium] [possible mistake] providers/aihubmix/models/deepseek-v4-pro.toml:8 - Check: DeepSeek V4 Pro baseline is toggle + effort high/max (lab maps low→high). Why: Lab DeepSeek Pro is high/max; this PR expands Pro (and deepseek-v4-pro-0813) to low/high/max. Flash legitimately includes low; Pro should not without host-specific proof. Action: Match lab Pro (high/max) or cite AIHubMix evidence that low is a distinct wire level on this host.
  • [medium] [possible mistake] providers/aihubmix/models/claude-opus-4-6.toml:8 - Check: Claude 4.6/4.7 controls vs Anthropic lab + prior AIHubMix wire comments. Why: Lab Opus 4.6 is effort lowmax + budget_tokens min = 1_024 (no toggle); Opus 4.7 is effort-only adaptive (no budget). Synced files add toggle, keep bare budget_tokens on 4.6 without min, and drop the previous AIHubMix wire comments that documented adaptive vs budget. Action: Reconcile with AIHubMix’s Messages surface: keep only real controls, restore min if budget remains, document toggle wire path or drop toggle if off is not a separate field.

chenxue and others added 2 commits September 10, 2026 18:00
Follows the revert: a dated snapshot is its own model, and jiekou, nano-gpt,
kilo and openrouter all write those IDs standalone rather than factoring them
onto the undated lab entry.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Action items

  • [high] [violation] packages/core/src/sync/providers/aihubmix.ts:1700 - Check: Reasoning options must not pair toggle with effort that already includes none. Why: reasoningOptions() passthroughs AIHubMix payload as-is (and the unit test locks toggle + values = ["none", "minimal", "high"]). That shape is an AGENTS.md / audit-skill violation and was written into many new TOMLs (coding-glm-5.2.toml, glm-5.2.toml, command-a-plus-05-2026.toml, Doubao Seed 2.0*, etc.). Action: After mapping aliases, drop toggle when effort contains none (or drop none if this host truly has a separate on/off wire control); update the normalizing test; re-sync so catalog files stop carrying both.
  • [high] [violation] packages/core/src/sync/providers/aihubmix.ts:1700 - Check: Relay reasoning_options baseline = lab + same-surface peers, not a full enum dump / invented controls. Why: Trusting the endpoint invents controls contradicted by first-party peers: DeepSeek V4* gains low (lab is high|max); MiniMax M2.x gains L/M/H + budget_tokens while lab/OpenRouter use []; GLM-5.2 dumps none…max + toggle (lab is effort high|max only, with aliases mapping onto those); GLM-5.3 gains toggle though lab documents always-on thinking; Claude Opus/Sonnet 4.6–4.8 gain toggle (and 4.6 keeps budget_tokens without bounds) while Anthropic first-party is effort-only (budget only on older tiers). Action: Prefer lab/peer option sets (or sanitize endpoint values against them); never invent budget_tokens without a real reasoning-budget field; re-sync affected families.
  • [high] [violation] packages/core/src/sync/providers/aihubmix.ts:218 - Check: Every toggle needs a leading top-of-file wire-path comment. Why: OpenRouter/Merge Gateway emit a toggle header on translate; AIHubMix never does, so newly created toggle models (coding-glm-*.toml, deepseek-v4-*.toml, Claude*, GLM*, etc.) land without the required wire comment, and sync strips any mid-file comments. Action: Emit an authoritative leading header documenting AIHubMix’s exact toggle (and effort/budget) request fields when toggle is present, then re-sync.
  • [medium] [possible mistake] providers/aihubmix/models/coding-minimax-m2.7.toml:18 - Check: Do not treat AIHubMix max_output == context_length as a real output ceiling when it is a sentinel. Why: Patch 04 correctly treats that equality as absent for new factoring, but several MiniMax entries still override limit.output = 204_800 (equal to context) after earlier syncs, while first-party MiniMax M2.7 is 131_072. Action: Re-sync (or clear those overrides) so sentinel-equal ceilings inherit the lab value unless the host truly differs.
  • [medium] [possible mistake] providers/aihubmix/models/command-a-plus-05-2026.toml:7 - Check: Do not combine Cohere native budget with OpenAI-compat effort=none on one host entry. Why: Lab Cohere documents either native thinking.token_budget or compat reasoning_effort = none|high, not toggle + none/high + budget_tokens together. The AIHubMix entry merges all three. Action: Pick the control surface this host actually exposes and match the lab shape for that surface only.
  • [low] [possible mistake] sync.md (AIHubMix Notes) - Check: Docs must match the final adapter after the date-tag revert. Why: Intermediate docs claimed peeling dated snapshot tags; the final note correctly says they stay, but the PR body still describes peeling -preview-05-06 / -2026-01-23. Action: Align the PR description with the final behavior so reviewers do not re-introduce date stripping.

chenxue and others added 2 commits September 11, 2026 20:19
…ant_of

AIHubMix now serves `vendor`, `variant_of` and `open_weights`, so nothing about
a relay has to be inferred from its ID or mirrored in this repo any more.

- `vendor` replaces the hand-maintained `developer_id` table. `VENDOR_LABS` is
  all that is left of it: the four labs the two registries spell differently.
- `variant_of` replaces the routing prefix/suffix lists. A relay is looked up
  under its own ID first and then under each declared hop, nearest first, so
  `qwen3.8-max-preview` factors onto the preview rather than its chain root.
  Following the declared chain also reaches relays no string rule could —
  `ox-alpha` onto `zhipuai/glm-5.3-flash`, `grok-code-fast-1` onto
  `xai/grok-build-0.1`, `cohere-command-a` onto `cohere/command-a-03-2025`.
- `open_weights` is served for 289 of 408 models, which unblocks standalone
  creates. A standalone entry also needs limits, so the skip guard now checks
  them; without it the endpoint's 0-output models fail catalog validation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The pull request carried 299 generated `providers/aihubmix/models`
files alongside the adapter, which pushed the diff past GitHub's
300-file ceiling. `gh pr diff --patch` then answered HTTP 406, and the
reviewer workflow died in its context step before the agent ever ran --
so no revision of this branch could earn `reviewer: ready`.

Ship the adapter alone. Once it merges, `sync-models.yml` regenerates
the catalog on `automation/sync-models-aihubmix`, a branch the reviewer
workflow already skips by design.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@0genlab

0genlab commented Sep 11, 2026

Copy link
Copy Markdown
Author

The reviewer workflow has not seen this branch since 10 Sep

Flagging a mechanical problem rather than a review finding, because it silently swallows every revision pushed here.

pr-reviewer.yml prepares its context with:

gh pr diff "$PR_NUMBER" --repo "$GITHUB_REPOSITORY" --patch --color never > .pr-review/diff.patch

This PR carried 304 files, and that call answers:

could not find pull request diff: HTTP 406: Sorry, the diff exceeded the maximum
number of files (300). Consider using 'List pull requests files' API or locally
cloning the repository instead.
PullRequest.diff too_large

set -euo pipefail turns that into a failed job, so the review check goes red in ~10s and the reviewer agent is never started. The five Action-items comments above all predate that: they belong to pushes on 10 Sep, when the diff was still under the ceiling. The newest revision has no comment at all — not because it is clean, but because nothing looked at it. And since mark-pr-ready lives inside the agent that never runs, no revision of a 300+ file branch can ever earn reviewer: ready, however good it is.

Two things follow.

On our side — the PR is now 4 files. I have dropped the 299 generated providers/aihubmix/models entries and kept only the adapter:

M  packages/core/src/sync/index.ts
A  packages/core/src/sync/providers/aihubmix.ts
M  packages/core/test/sync.test.ts
M  sync.md

The catalog belongs to the automation anyway: once this merges, sync-models.yml regenerates those files on automation/sync-models-aihubmix, which pr-reviewer.yml already skips by design. Routing the data through the channel you built for it is strictly better than hand-carrying it here, and it gets the adapter itself under human review where it belongs.

On yours — the workflow is worth hardening. Any PR touching a large provider will hit this, and it fails in the least helpful way: red check, no comment, no label, no indication that the reviewer was never invoked. Your own error message names both fixes; the checkout is already there, so the local one is close at hand:

git fetch origin "$BASE_SHA" "$HEAD_SHA"
git diff "$BASE_SHA".."$HEAD_SHA" > .pr-review/diff.patch

Worth considering separately: at minimum, a distinct failure message so "the reviewer could not read this PR" stops looking like "the reviewer found problems".

On the reasoning_options findings

The earlier comments are not stale on the merits, and I am not asking you to disregard them. The recurring one — toggle alongside an effort list that already contains none — is real, and I have reproduced it: 31 models come off the AIHubMix endpoint carrying both controls, e.g.

"glm-5.2": [{"type": "toggle"},
            {"type": "effort", "values": ["none", "minimal", "low", "medium", "high", "xhigh", "max"], "default": "max"}]

The adapter is a faithful pass-through, so the shape is ours, not the adapter's invention. That makes it something to fix at the source rather than to paper over on the way in, and it is being fixed there; the corrected shapes will arrive through the sync automation rather than as hand-edits here.

One part of the suggested remedy I would push back on. Intersecting effort values against lab or same-surface peer baselines would mean hard-coding per-model knowledge in the adapter — "GLM-5.2 is high/max only" — which is exactly what an endpoint-driven provider exists to avoid, and what goes stale the moment the upstream changes. Structural normalization is a different matter and I have no objection to it: dropping toggle when effort already carries none is a rule about shape, not about any particular model, and it holds wherever it is applied.

🤖 Generated with Claude Code

@0genlab

0genlab commented Sep 11, 2026

Copy link
Copy Markdown
Author

Correcting myself on toggle + none

In my previous comment I said the overlapping controls were ours to fix at the source. That was my own inference, and it was wrong — I checked with the people who built the gateway, then verified it against the live API. The overlap is deliberate, and I should not have characterised it as a defect on the way to being repaired.

AIHubMix is an aggregator. Its clients do not arrive speaking one dialect: some are written against OpenAI's reasoning_effort, some against Zhipu/Qwen's enable_thinking, some against a thinking-budget field. The gateway accepts all of them and maps whatever it receives onto the underlying vendor's actual control, deliberately resolving rather than rejecting. toggle and an effort list containing none are therefore not two competing off-switches that slipped through — they are the same off state reachable from two SDK dialects, both of which real traffic uses.

Measured just now against glm-5.2 through https://aihubmix.com/v1/chat/completions, single trivial prompt, reading reasoning_content length and usage.completion_tokens_details.reasoning_tokens:

request reasoning chars reasoning tokens
reasoning_effort: "none" 0
enable_thinking: false 0
both together 0
reasoning_effort: "minimal" 217 64
reasoning_effort: "max" 328 92
enable_thinking: true 384 117
reasoning_effort: "turbo" (not a real tier) 302 89

Three things fall out. Both off-switches genuinely turn thinking off, independently. Supplying both is coherent rather than contradictory — no error, no conflict, the same off state. And an unrecognised effort value returns 200 having been mapped to a sensible tier instead of 400 — which is the design intent stated plainly: accommodate the dialect the caller happens to speak, do not punish them for it.

So the endpoint is not reporting a malformed capability. It is reporting a wider control surface than a first-party model has, because a relay genuinely has one.

That leaves a real question about your catalog rather than about our data, and it is yours to settle. The AGENTS.md rule reads naturally for a first-party model, where one control surface exists and a second is evidence of a mistake. For a relay in front of many labs, "both dialects are accepted" is simply true, and the current schema has no way to say it — so it comes out looking like the anti-pattern. If you would rather the catalog carry the narrower first-party shape, say which of the two you want and I will make the adapter emit that consistently; I would just rather not have it recorded as a bug we are fixing, because it is neither.

One thing I still agree with from the earlier reviews, for the avoidance of doubt: I would not want the adapter intersecting effort values against per-model lab baselines. That is hand-maintained model knowledge in a code path whose whole purpose is to not have any, and it goes stale the moment an upstream adds a tier.

A note for later, not a request now

Once the adapter is reviewed and merged and the catalog has synced a few times, I would like to follow the path #4840, #4668, #4849 and #5078 took and ask for aihubmix in REVIEWED_REASONING_PROVIDERS. Not yet — that trust should be earned by the sync output looking right for a while first, and I would rather it be your call on evidence than a favour bundled into this PR. Flagging the intent so it is not a surprise.

🤖 Generated with Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

Action items

  • [high] [violation] packages/core/src/sync/providers/aihubmix.ts - Check: toggle must not be paired with effort that already includes none (AGENTS.md Reasoning options; same rule as CrossModel/Eden AI/LLM Gateway). Why: reasoningOptions() copies endpoint toggle and effort independently. The new unit test locks in { effort: [none, …], toggle }, which the schema/docs treat as invalid (off is already effort=none). Automation will write that dual form across hundreds of relays. Action: When effort values include none, drop toggle (or fold toggle into none only if the host has no separate on/off field). Update the normalizing test to assert the legal shape.
  • [high] [violation] packages/core/src/sync/providers/aihubmix.ts - Check: Every toggle needs a leading top-of-file wire-path comment; sync peers set header on create (OpenRouter, LLM Gateway, Merge Gateway). Why: translateModel never returns header. New creates (and rewrites that lose hand headers) will land toggles with no wire comment, which formatToml cannot restore. Action: When the translated model includes toggle, return a leading header documenting AIHubMix’s real control(s) (Chat/Messages/Gemini as appropriate, matching providers/aihubmix/provider.toml).
  • [medium] [possible mistake] packages/core/src/sync/providers/aihubmix.ts - Check: Relay reasoning_options baseline = lab + same-surface peers, not a raw endpoint dump (AGENTS.md / audit skill). Why: The adapter trusts AIHubMix’s option list wholesale (aliases only). Intermediate catalog output already showed DeepSeek V4 as toggle + low/high/max (lab/OpenRouter peers are high/max or high/xhigh, no low) and broad nonemax + budget_tokens stacks on Qwen/Doubao-style relays. That will republish on the first automation sync. Action: Prefer lab/peer option sets for known bases (or strip levels the host cannot mean), and only keep budget_tokens when this host path actually exposes a reasoning budget—not a full-enum passthrough. Add tests for at least DeepSeek V4 and one GPT-style effort-only model.

A sync rewrites the model file whole, so any header a human wrote on it
is lost the first time the model changes. AIHubMix reaches the same
thinking toggle from four dialects -- `enable_thinking` on the
OpenAI-compatible path, `thinking.type` on `/v1/messages`,
`generationConfig.thinkingConfig` on the Gemini path -- so `toggle`
alone does not tell a caller which field to send.

Emit the header from `translateModel` whenever the model carries a
toggle, the way the OpenRouter adapter already does, so the wire path
survives the rewrite.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@0genlab 0genlab closed this Sep 11, 2026
@0genlab 0genlab reopened this Sep 11, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Action items

  • [high] [violation] packages/core/src/sync/providers/aihubmix.ts:reasoningOptions - Check: AGENTS.md / audit skill — when effort includes none and other graded levels, author only effort (no toggle). Why: reasoningOptions flat-maps endpoint options and keeps both { type: "toggle" } and effort values that include none (including after no_thinknone). That is the documented anti-pattern; peer aggregators (OpenRouter, CrossModel, EmpirioLabs, Eden AI, LLM Gateway) reconcile it. First automation sync will write that shape across many relays. Multi-dialect off-switches belong in the toggle wire-path header, not as contradictory option rows. Action: When any effort list includes none, drop toggle (or fold off into none only). Add a regression test for toggle + none / no_think.
  • [high] [violation] packages/core/src/sync/providers/aihubmix.ts:buildCost - Check: Preserve provider cost keys the endpoint does not model. Why: Final buildCost returns only input / output / cache_read / cache_write / tiers and drops authored input_audio, output_audio, and cost.reasoning. Existing AIHubMix entries (e.g. doubao-seed-2-0-lite-260428, doubao-seed-2-0-mini-260428, gemini-2.5-flash) carry input_audio (including per-tier). The Pricing schema does not read audio rates, so the next sync will wipe them. Peer adapters keep input_audio / reasoning from existing when the API omits them. Action: When the endpoint returns a partial price object, preserve authored input_audio, output_audio, reasoning, and matching tier audio fields the API does not quote; add a test that a priced sync keeps authored input_audio.

chenxue and others added 2 commits September 11, 2026 21:53
The catalog endpoint carries text and cache rates only -- no audio and no
reasoning rate, at the top level or inside a tier. `buildCost` rebuilt
the cost object from that answer alone, so the next sync would have
wiped the `input_audio` already authored on `doubao-seed-2-0-lite`,
`doubao-seed-2-0-mini` and `gemini-2.5-flash`.

Carry the authored audio and reasoning rates through, matching tiers by
context size, the way the other gateway adapters do.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
AIHubMix accepts whichever off switch the caller's SDK speaks --
`reasoning_effort: "none"` on the Chat path, `enable_thinking: false`,
`thinking.type: "disabled"` on `/v1/messages`, `thinkingBudget: 0` on
the Gemini path -- and maps each onto the vendor's real control instead
of rejecting it. 34 of 408 routes therefore publish both a toggle and a
graded effort list carrying `none`.

The catalog spells that one way: `AGENTS.md` says graded effort that
already includes `none` stands alone, with no toggle. Fold it, and name
the dialects that reach the same off state in the file header, which is
where that belongs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@0genlab 0genlab closed this Sep 11, 2026
@0genlab 0genlab reopened this Sep 11, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Action items

  • [high] [violation] packages/core/src/sync/providers/aihubmix.ts - Check: Hand-authored provider-only fields must survive sync rewrites. Why: buildAihubmixModel never passes experimental or provider into factorBaseModel / the standalone object (peer adapters such as merge-gateway, anthropic, nano-gpt, digitalocean do). On the first automation run this drops [experimental.modes.fast] (and its nested provider body) from gpt-5.4, gpt-5.4-mini, and gpt-5.5. Action: Preserve existing?.experimental and existing?.provider on both the factored and standalone paths, with a regression test that an authored fast-mode block is still present after translate.
  • [high] [possible mistake] packages/core/src/sync/providers/aihubmix.ts (buildCost) - Check: Do not publish echoed full-price cache_read as a real cache discount. Why: The early adapter ignored cache_read when it equaled input (and documented ~35 priced rows doing this, e.g. Gemini Flash Lite 10× overstatement). The full-catalog rewrite removed that guard and now writes cache_read: price(pricing.cache_read) unconditionally; CrossModel still requires a genuine discount (cache_read < input). Action: Restore a genuine-discount / non-echo rule for top-level and tier cache_read (and the matching test), or cite endpoint evidence that equal cache_read is intentional paid cache pricing.
  • [high] [possible mistake] packages/core/src/sync/providers/aihubmix.ts (modalities) - Check: Relay modalities must not silently strip capabilities the host already accepts. Why: The first AIHubMix notes said the endpoint never lists pdf even for PDF-capable routes; the final adapter treats input_modalities as authoritative and only falls back when the string is empty. That will replace curated pdf (and any other missing modality) with a narrower list on every rewrite, as intermediate generated files already did. Action: Union endpoint modalities with authored/lab modalities when the API omits known inputs (at least pdf), or prove the live catalog now lists every accepted modality for those routes.
  • [medium] [violation] packages/core/src/sync/providers/aihubmix.ts (limit) - Check: Preserve provider limit.input the way other gateway syncs do. Why: Limit is only { context, output }. OpenRouter/CrossModel keep existing?.limit?.input. Standalone or mismatched-context rewrites can drop authored input caps (e.g. GPT 922k input) that the endpoint does not model. Action: Set input: existing?.limit?.input (and cover it in tests) so input caps are not wiped when context/output refresh.
  • [medium] [possible mistake] packages/core/src/sync/providers/aihubmix.ts (reasoningOptions) - Check: Do not strip verified budget_tokens bounds. Why: Every budget_tokens option is reduced to { type: "budget_tokens" }, discarding min/max. When that object is written on a base_model file it overrides richer lab/authored bounds (e.g. Claude min = 1_024) with an unbounded control. Action: Forward endpoint min/max when present; otherwise leave budget_tokens unset so inheritance/preserveReasoningOptions can keep authored bounds—add a test that an existing min is not replaced by a bare { type = "budget_tokens" }.

The endpoint has no surface for several things a provider file carries,
and the adapter rebuilt each object from the endpoint answer alone, so
the first automation run would have dropped them:

- `experimental` and `provider` -- the `[experimental.modes.fast]` block
  and its nested request body on `gpt-5.4`, `gpt-5.4-mini`, `gpt-5.5`
- `limit.input` -- the 922k input cap the catalog models and AIHubMix
  does not
- reasoning budget bounds -- the endpoint states that a budget exists
  but never its range, so a bare `{ type = "budget_tokens" }` written
  onto a `base_model` file would override the lab's real `min`/`max`
  with an unbounded control

Carry all four through from the authored file, the way the Anthropic,
OpenRouter and Merge Gateway adapters do.

Six models and four context tiers also repeat the input price in
`cache_read`, which is how the endpoint spells "no cache discount"
rather than a real rate; an omitted field already means "no such rate"
here, so an echoed one is now read the same way instead of publishing a
full-price read as a 10x discount.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The catalog under-reports what a route accepts. It lists `text,image`
for `kimi-k2.5`, whose lab entry and this repo both record video, and
`text` for `qwen3.8-2.4t-a95b`, whose own file carries a note that live
image input returned 200 on 2026-08-31. Treating the endpoint as
authoritative would delete both on the first sync.

Union the endpoint list with what the file recorded instead. A modality
the endpoint adds still lands; one it never listed is removed by editing
the file, which is where it came from. Both gaps are reported upstream.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@0genlab 0genlab closed this Sep 11, 2026
@0genlab 0genlab reopened this Sep 11, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Action items

  • [high] [violation] packages/core/src/sync/providers/aihubmix.ts (build shared flags) - Check: Provider base_model files must only record real host deltas; omitted API fields must not invent lab-contradicting overrides. Why: reasoning and tool_call fall back to false when the endpoint omits them (model.* ?? existing?.* ?? false). On creates (and any update without a prior local flag), that materializes reasoning = false / tool_call = false even when factorBaseModel would otherwise inherit true from lab metadata—permanently disabling reasoners/tools in the catalog. Action: Drop the false defaults; treat missing endpoint booleans as unknown (undefined) so factorBaseModel can inherit lab values, and only write an override when the API explicitly sends a boolean.
  • [high] [violation] packages/core/src/sync/providers/aihubmix.ts (modalities / create path) - Check: Relay modality overrides must not silently narrow lab capabilities the host still accepts. Why: Patch 17 unions endpoint modalities only with the existing provider file. New creates have no file, so a narrow endpoint list (e.g. text,image while models/<lab>/… includes video/pdf) becomes a lasting [modalities] override via factorBaseModel—exactly the under-report failure the PR already documents for kimi-k2.5 / qwen3.8. Action: When a base_model resolves, union endpoint modalities with that lab entry’s modalities (e.g. via modelMetadata(base)), not only with existing.modalities; keep file-only widening as an additional merge for updates.
  • [medium] [possible mistake] packages/core/src/sync/providers/aihubmix.ts (limit mapping) - Check: Do not publish relay-conservative ceilings as hard provider limits when they understate the upstream model. Why: Context/output still treat the endpoint as authoritative (context / max_output[limit]). Earlier design and sync.md history called out caps like Claude Opus context at 200K vs a 1M lab window; if that still happens, first automation write will bake the cap in as an override. Action: Verify live catalog limits against lab peers for a few flagship relays; if under-reporting remains, inherit lab limits unless the endpoint value is a confirmed real host ceiling (or only override when strictly greater / explicitly documented as the served window).
  • [medium] [violation] packages/core/src/sync/providers/aihubmix.ts (translateModel header + provider flags) - Check: Every toggle needs a leading wire-path comment that survives sync rewrites (AGENTS.md Reasoning options). Why: Headers are emitted, but the runner keeps any pre-existing leading comment unless authoritativeHeaders is set (header = existing.header || translatedHeader). Files that already have a source/pricing header never receive TOGGLE_HEADER / FOLDED_HEADER, and stale headers are never refreshed when folding toggle into effort=none. Action: Set authoritativeHeaders: true for AIHubMix (or merge toggle/folded wire comments into retained headers) so the dialect documentation always lands on toggle and folded-none models.
  • [low] [possible mistake] sync.md (AIHubMix Notes) - Check: Sync docs should match final adapter behavior. Why: Notes stop at the vendor/variant_of revision and still describe omitted cache prices as always clearing authored rates and modalities/limits as fully authoritative, omitting later behavior (echoed cache_read drop, audio/reasoning price preservation, toggle fold, modality widen-only, budget bound carry). Action: Update the AIHubMix section so automation operators know which fields are API-owned vs file-preserved.

The endpoint never sends `false` for a capability it does not know: 107 of
408 routes omit `reasoning` and 100 omit `tool_call`, and no route sends
`false` at all. Reading a missing flag as `false` wrote an override that
disabled a reasoner the lab entry declares.

Modalities had the same shape of bug one level down. The union added in the
previous commit merged the endpoint's list with the existing file, but `dev`
carries only 77 aihubmix files, so most of the catalog arrives as a create
with no file to merge against — 14 creates in the current listing would have
written a narrowing override (`gpt-4o` losing pdf, `qwen3.5-27b` losing
audio). The union now also includes the lab entry the relay factors onto.

Headers were retained rather than refreshed, so a wire path could outlive the
options it documents and a folded toggle kept advertising a toggle.
`authoritativeHeaders` fixes that but would have deleted the price citations
and live-test records humans wrote in the same block, so translateModel now
reads the existing header and supersedes only the wire-path lines it authors.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@0genlab 0genlab closed this Sep 11, 2026
@0genlab 0genlab reopened this Sep 11, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Action items

  • [high] [violation] packages/core/src/sync/providers/aihubmix.ts - Check: Non-lab hosts must use base_model; nameable lab models must not be authored as full standalone provider definitions. Why: When vendor is set but no matching models/<lab>/… entry exists, resolveBaseModel fails and the build falls through to the standalone path whenever the endpoint supplies name / release_date / open_weights / limits. That writes a complete third-party TOML for a lab model instead of adding lab metadata and factoring onto it. Action: If vendor identifies a lab model, never emit a full standalone entry. Skip (and surface via missing-model/skip notices) until models/<lab>/<id>.toml exists, then factor with base_model.
  • [high] [violation] packages/core/src/sync/providers/aihubmix.ts - Check: Do not publish under-reported relay limits as authoritative overrides (same class of bug already fixed for modalities). Why: The endpoint still caps context_length and quotes conservative/0/backfilled max_output (e.g. Claude Opus 4.6 at 200K vs a 1M lab window). The final adapter treats those values as owned fields and will write narrowing [limit] overrides on creates and updates, while modalities were correctly changed to only widen via lab/file union. Action: Stop treating endpoint limits as always authoritative. Prefer lab/existing ceilings when the endpoint under-reports (omit override when equal to lab after factoring; keep authored wider limits; only write a limit delta when the host truly restricts the window).
  • [medium] [possible mistake] packages/core/src/sync/providers/aihubmix.ts - Check: Missing/incomplete remote models should be trackable when trackMissingModels is enabled. Why: skipCreates is unset so creates run, but incomplete translates only append to skippedRemote. Runner issue opens use skippedRemote only when skipCreates === true, so unresolved relays (missing lab metadata, incomplete standalone fields) produce notices only and no deduped [missing-model] issues. Action: Either enable skipCreates for the incomplete subset and keep tracking, or feed unresolved sourceIDs into the missing-model issue path so lab gaps are actionable.
  • [medium] [possible mistake] packages/core/src/sync/providers/aihubmix.ts - Check: budget_tokens must not invent unbounded controls that shadow real bounds. Why: Endpoint budgets are bare; patch 16 preserves authored min/max only when a local file already has them. New creates still emit { type = "budget_tokens" } with no bounds, which can override peer/lab-accurate ranges on the first automation write. Action: On create, omit budget_tokens unless bounds are known, or copy bounds from the same-model first-party/peer provider entry before writing.

Four review findings, all fixed at the adapter's shape rules rather than with
per-model tables.

- Limits no longer publish a decimal restatement of a binary window as a
  narrowing override. A stated limit below an accepted one but at or above
  1000³/1024³ resolves to the accepted value; genuine host caps still land.
  25 narrowing overrides become 7, and three MiniMax restatements an earlier
  sync wrote into files are retired.
- A full standalone entry is only authored where the response names no vendor.
  A named lab means the relay belongs on base_model, so 38 would-be standalone
  creates for lab models become skips that name the file a human must add.
- SyncProvider gains trackMissingModels, so a provider that creates models but
  still skips the ones it cannot write opens deduped [missing-model] issues
  instead of notices nobody acts on.
- budget_tokens bounds cannot be copied from a lab entry: ModelMetadata has no
  reasoning_options field, so there is no such baseline to shadow. The comment
  records why rather than adding a fallback that could never fire.

Also drops eight channel-alias files (alicloud-glm-5.1, zai-glm-5.1, the four
deepseek-v4 channel routes, two xiaomi-mimo-v2.5 routes). Each relays to a model
already in the catalog and echoes that model's ID back; the endpoint's main model
list, not callability, is the catalog boundary.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Action items

  • [high] [violation] packages/core/src/sync/providers/aihubmix.ts - Check: Factored provider models must keep real display-name deltas (AGENTS.md override-only / different display name). Why: buildAihubmixModel computes name = model.model_name ?? existing?.name but the factorBaseModel(...) call only passes name: existing?.name (and shared never includes name). New creates therefore never write the endpoint label, so routes like coding-* / *-free inherit the bare lab name (e.g. “GLM-4.6”) instead of “Coding GLM 4.6 (free)”. Intermediate generated TOMLs already showed this (factored coding/free files with no name). Eden AI’s sync passes an explicit displayName for the same reason. Action: Pass the resolved endpoint/existing name into factorBaseModel (e.g. name, relying on inheritedOverride to drop matches with the lab). Add a test that a create for coding-… / …-free keeps a distinct model_name.
  • [medium] [possible mistake] packages/core/src/sync/providers/aihubmix.ts (WIRE_PATH_LINE / composeHeader) - Check: authoritativeHeaders must refresh only adapter-owned wire-path lines and preserve human price/source notes (sync.md AIHubMix notes). Why: WIRE_PATH_LINE is unanchored alternatives (…|\$\.|thinkingConfig|docs\.aihubmix\.com\/cn\/api), so any header line containing $. or a docs.aihubmix.com/cn/api URL is dropped—even a pricing/source citation that only links the docs. Several hand-authored aihubmix headers cite that docs host. Action: Match only the adapter’s authored wire-path prefixes (e.g. # Toggle:, # Off is effort=none, # Effort:, # Budget:) instead of bare $. / docs-host substrings, and add a regression test that a docs-linked citation survives while a stale toggle line is replaced.

…an notes

Limits
- The unit-restatement test now compares a ratio instead of a direction. The
  endpoint restates 204800 as 200000 and 1000000 as 1048576, and neither is the
  host stating a different window; checking only the narrowing side left 20
  routes writing an override that states no difference at all.
- The accepted value is looked for in the lab entry first and only then in the
  provider file, so a restatement resolves to the spelling that makes the
  override disappear. Resolving file-first pinned 10 imprecise numbers forever
  (qwen3.7-flash's 991000 for the lab's 1000000).
- Whatever the restatement resolves to is clamped to the lab's window, after the
  resolution rather than instead of it: a relay cannot serve a wider window than
  the model it relays, and an endpoint quoting back the file's own stale ceiling
  is only caught by a later clamp (grok-4.5 held 1000000 against a lab 500000).

Header
- The two wire-path lines this adapter owns are dropped whether or not a derived
  block replaces them. Keeping them when nothing is derived left a route
  advertising a toggle it no longer had, and no later sync could tell.
- A superseded line is recognised by its opening on the trimmed line, so an
  indented `  # Toggle:` no longer outlives its block.
- Eight provider files carried human notes mid-body, which a sync drops; moved
  above the first key as AGENTS.md requires.

Names
- The endpoint label is compared on the bare ID, which is what resolved the base
  model, so 10 namespaced routes stop taking a redundant storefront override.
- A blank label is skipped rather than written: ModelBase.name is min(1), and
  writing one through aborted the whole provider's sync at validation.

normalizeModelSlug is exported from openrouter.ts, which already serves as the
shared helper module for the other provider adapters.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@0genlab 0genlab closed this Sep 14, 2026
@0genlab

0genlab commented Sep 14, 2026

Copy link
Copy Markdown
Author

Closed in favour of a clean PR: #7042.

Same branch, same head commit — the history here carried 299 generated catalog files through several revisions, which is what kept pr-reviewer.yml from ever reaching the diff (HTTP 406 on a 300+ file patch). #7042 opens at 22 files with the review findings answered in the body.

All four findings from the review are addressed there, including the one about narrowing [limit] overrides — the endpoint turned out to agree with the lab on claude-opus-4-8 (1000000/128000) and the narrowing 200000/32000 was the provider file's own. Limits are now resolved against the lab window and clamped to it.

@github-actions

Copy link
Copy Markdown
Contributor

Action items

  • [high] [violation] providers/aihubmix/models/xiaomi-mimo-v2.5.toml:1 - Check: Non-lab host catalog completeness / “alias duplicates an ID already in the catalog.” Why: This PR deletes xiaomi-mimo-v2.5, xiaomi-mimo-v2.5-pro, alicloud-glm-5.1, zai-glm-5.1, and the four DeepSeek channel aliases, claiming each duplicates a listed main ID already in the catalog. On dev those main IDs are not present (mimo-v2.5, mimo-v2.5-pro, glm-5.1, deepseek-v4-pro, deepseek-v4-flash are absent; only coding/free or dated snapshots remain), and this PR does not add them. After merge, the paid mainline routes disappear until a later automation run (which this workflow does not guarantee in the same merge). Action: Keep these files until the listed canonical IDs are written in-repo, or include the endpoint’s main IDs (mimo-v2.5, glm-5.1, deepseek-v4-pro, …) in this PR so deletions are true duplicates.
  • [medium] [possible mistake] packages/core/src/sync/providers/aihubmix.ts (reasoning path via runner preserveReasoningOptions) - Check: Relay reasoning = true must not get reasoning_options = [] from uncertainty (AGENTS.md / audit skill). Why: When the endpoint marks a model as reasoning but omits or yields no usable reasoning_options, the adapter leaves options unset and the shared runner stamps [] on creates (lab metadata has no reasoning_options to inherit). On a multi-model gateway that is how “no caller control” is published, not “unknown,” and it can shadow real lab/peer controls (Eden AI skips those creates instead). Action: If options cannot be derived from the endpoint (or a known peer/lab mapping), skip the create and let trackMissingModels open an issue rather than writing []; only emit [] when the host affirmatively has no control.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant