feat(aihubmix): sync the model list with the gateway catalog - #3871
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
The new Gemini image-generation IDs are not registered in the provider’s image-model configuration, preventing image output setup.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds 55 AIHubMix top-100 LLM catalog entries, registers them, and validates their schemas.
Changes:
- Adds model definitions across 11 families.
- Updates catalog ordering and regression tests.
- Bumps plugin version to
0.0.47.
File summaries
| File | Description |
|---|---|
models/aihubmix/models/llm/auto.yaml |
Adds Smart Router. |
models/aihubmix/models/llm/gpt-6-astra.yaml |
Adds GPT-6 Astra. |
models/aihubmix/models/llm/gpt-5.6-sol-disc.yaml |
Adds GPT-5.6 Sol Disc. |
models/aihubmix/models/llm/gpt-5.5-pro.yaml |
Adds GPT-5.5 Pro. |
models/aihubmix/models/llm/gpt-oss-20b-free.yaml |
Adds free GPT-OSS. |
models/aihubmix/models/llm/ox-alpha.yaml |
Adds Ox Alpha. |
models/aihubmix/models/llm/gemini-3.8-flash-free.yaml |
Adds free Gemini 3.8 Flash. |
models/aihubmix/models/llm/gemini-3.7-flash-free.yaml |
Adds free Gemini 3.7 Flash. |
models/aihubmix/models/llm/gemini-3.6-flash-free.yaml |
Adds free Gemini 3.6 Flash. |
models/aihubmix/models/llm/gemini-3.5-flash-lite-free.yaml |
Adds free Gemini 3.5 Lite. |
models/aihubmix/models/llm/gemini-3.1-flash-lite.yaml |
Adds Gemini 3.1 Lite. |
models/aihubmix/models/llm/gemini-3.1-flash-lite-nothink.yaml |
Adds non-thinking variant. |
models/aihubmix/models/llm/gemini-3.1-flash-image.yaml |
Adds Flash Image. |
models/aihubmix/models/llm/gemini-3.1-flash-lite-image.yaml |
Adds Flash-Lite Image. |
models/aihubmix/models/llm/gemini-3-pro-image.yaml |
Adds Pro Image. |
models/aihubmix/models/llm/deepseek-v4.1-flash.yaml |
Adds DeepSeek V4.1 Flash. |
models/aihubmix/models/llm/deepseek-v4-flash-vision-exp.yaml |
Adds vision variant. |
models/aihubmix/models/llm/deepseek-v4-flash-0731-fast.yaml |
Adds fast dated variant. |
models/aihubmix/models/llm/coding-glm-5.3.yaml |
Adds Coding GLM 5.3. |
models/aihubmix/models/llm/coding-glm-5.3-free.yaml |
Adds free GLM 5.3. |
models/aihubmix/models/llm/coding-glm-5.2.yaml |
Adds Coding GLM 5.2. |
models/aihubmix/models/llm/coding-glm-5.2-free.yaml |
Adds free GLM 5.2. |
models/aihubmix/models/llm/glm-5.2-fast-preview.yaml |
Adds GLM fast preview. |
models/aihubmix/models/llm/qwen3.8-max-2026-09-02.yaml |
Adds dated Qwen Max. |
models/aihubmix/models/llm/qwen3.8-flash.yaml |
Adds Qwen Flash. |
models/aihubmix/models/llm/hy4-preview.yaml |
Adds Hunyuan 4 preview. |
models/aihubmix/models/llm/hy3-free.yaml |
Adds free Hunyuan 3. |
models/aihubmix/models/llm/hy3-preview.yaml |
Adds Hunyuan 3 preview. |
models/aihubmix/models/llm/coding-kimi-k3.yaml |
Adds Coding Kimi K3. |
models/aihubmix/models/llm/coding-kimi-k3-free.yaml |
Adds free Kimi K3. |
models/aihubmix/models/llm/minimax-m2.7-free.yaml |
Adds free MiniMax M2.7. |
models/aihubmix/models/llm/ernie-5.1.yaml |
Adds ERNIE reasoning controls. |
models/aihubmix/models/llm/mercury-2.5-preview.yaml |
Adds Mercury preview. |
models/aihubmix/models/llm/mai-thinking-1.yaml |
Adds MAI Thinking. |
models/aihubmix/models/llm/agnes-3.0-flash.yaml |
Adds Agnes 3.0 Flash. |
models/aihubmix/models/llm/agnes-2.5-flash.yaml |
Adds Agnes 2.5 Flash. |
models/aihubmix/models/llm/agnes-2.5-pro.yaml |
Adds Agnes 2.5 Pro. |
models/aihubmix/models/llm/agnes-2.5-pro-alpha.yaml |
Adds Agnes Pro Alpha. |
models/aihubmix/models/llm/longcat-2.0.yaml |
Adds LongCat 2.0. |
models/aihubmix/models/llm/dots-3-note-preview-free.yaml |
Adds free Dots preview. |
models/aihubmix/models/llm/lfm-2.5-2.6b-free.yaml |
Adds free LFM model. |
models/aihubmix/models/llm/laguna-xs-2.1-free.yaml |
Adds free Laguna XS. |
models/aihubmix/models/llm/north-mini-code-free.yaml |
Adds free North Mini Code. |
models/aihubmix/models/llm/ling-3.0-flash-free.yaml |
Adds free Ling Flash. |
models/aihubmix/models/llm/ling-3.0-tiny-free.yaml |
Adds free Ling Tiny. |
models/aihubmix/models/llm/nemotron-3-super-120b-a12b-free.yaml |
Adds free Nemotron Super. |
models/aihubmix/models/llm/nemotron-3-ultra-550b-a55b-free.yaml |
Adds free Nemotron Ultra. |
models/aihubmix/models/llm/nemotron-3-nano-omni-30b-a3b-reasoning-free.yaml |
Adds free Nano Omni. |
models/aihubmix/models/llm/nemotron-3.5-lightning-free.yaml |
Adds free Lightning. |
models/aihubmix/models/llm/nemotron-3.5-content-safety-free.yaml |
Adds safety model. |
models/aihubmix/models/llm/nemotron-nano-9b-v2-free.yaml |
Adds free Nano 9B. |
models/aihubmix/models/llm/nemotron-nano-12b-v2-vl-free.yaml |
Adds free Nano VL. |
models/aihubmix/models/llm/muse-spark-1.1.yaml |
Adds Muse Spark 1.1. |
models/aihubmix/models/llm/muse-spark-1.2.yaml |
Adds Muse Spark 1.2. |
models/aihubmix/models/llm/muse-spark-1.3.yaml |
Adds Muse Spark 1.3. |
models/aihubmix/models/llm/_position.yaml |
Registers models in catalog order. |
models/aihubmix/tests/test_top100_models.py |
Adds schema regressions. |
models/aihubmix/manifest.yaml |
Bumps plugin version. |
Review details
- Files reviewed: 58/58 changed files
- Comments generated: 1
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Realign the predefined model list with the gateway catalog
(/api/v1/models?type=llm): add the chat models the plugin was missing from
the catalog's top 100, drop the models that are no longer served, drop the
free-tier aliases, and register every remaining model in _position.yaml.
215 LLM entries, all of them registered, no orphans.
Added (32) — the top-100 entries the plugin lacked:
auto, gpt-6-astra, gpt-5.6-sol-disc, gpt-5.5-pro, ox-alpha,
gemini-3.1-flash-lite (+ -nothink, -image, -lite-image), gemini-3-pro-image,
deepseek-v4.1-flash, deepseek-v4-flash-vision-exp, deepseek-v4-flash-0731-fast,
coding-glm-5.3, coding-glm-5.2, glm-5.2-fast-preview,
qwen3.8-max-2026-09-02, qwen3.8-flash, hy4-preview, hy3-preview,
coding-kimi-k3, ernie-5.1, mercury-2.5-preview, mai-thinking-1,
agnes-3.0-flash, agnes-2.5-flash, agnes-2.5-pro, agnes-2.5-pro-alpha,
longcat-2.0, muse-spark-1.1, muse-spark-1.2, muse-spark-1.3.
Parameter rules were probed against the production gateway rather than
copied from the catalog, since an HTTP 200 does not prove a parameter took
effect (reasoning knobs were compared via
usage.completion_tokens_details.reasoning_tokens):
- gpt-6-astra: reasoning_effort is restricted to low/medium/high/xhigh
("minimal" is rejected with HTTP 400; "none"/"max" silently degrade), and
it takes the enable_stream opt-out like the other new OpenAI models.
- ernie-5.1: exposes reasoning_effort instead of a boolean `thinking`,
which upstream rejects with "cannot unmarshal bool into ... model.Thinking"
(same class of bug as langgenius#3427).
Removed — retired models (11), each confirmed unusable on the live gateway
and absent from the catalog:
gpt-4.5-preview ("has been retired"), gemini-2.0-flash ("no longer
available"), gemini-2.5-flash-preview-04-17 (+ -nothink),
deepseek-v3.2-speciale (upstream 404), deepseek-ai/DeepSeek-V3-0324,
aihubmix-DeepSeek-R1, aihubmix-Llama-3-3-70B-Instruct, both chutesai
Llama 4 entries and meta-llama/llama-4-scout-17b-16e-instruct
(no_available_channel / model_not_found).
Removed — free-tier aliases (37, e.g. gpt-4.1-free, gemini-3-flash-preview-free,
the nemotron *-free variants). They share one account-wide free quota and
start failing as soon as it is exhausted, so they are a poor fit for a
predefined model list.
Also registers 10 live models that had never been listed in _position.yaml
(gemini-2.5-pro and its preview/search variants, gemini-2.5-flash-image,
minimax-m2.5, minimax-m2.5-highspeed, qwen3-vl-235b-a22b-instruct,
qwen-plus-latest).
Image-generation (mai-image-*), audio (gpt-audio-1.5) and transcription
(gpt-4o-transcribe-diarize) entries from the top 100 are intentionally not
added: they are not served on /v1/chat/completions.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
b42abce to
fa66fbe
Compare
There was a problem hiding this comment.
🟡 Changes recommended
It omits 23 advertised free models and leaves three Gemini image models unable to request image output.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 51/51 changed files
- Comments generated: 4
- Review effort level: Balanced
`llm.py` routes every `gemini*` id to `GoogleLargeLanguageModel`, which only asks for image output when the id is listed in `IMAGE_GENERATION_MODELS`. The three image models added in this branch were absent from that set, so they answered with text only. Add them to the allowlist, and expose `aspect_ratio` / `resolution` in their YAML with the options each one actually accepts. The values were probed live against the gateway: gemini-3-pro-image rejects 4:1 and 8:1, and gemini-3.1-flash-lite-image rejects 2K and 4K. `_set_image_config` also had to learn 4:1 and 8:1, otherwise the new options would have been dropped before the request left the plugin. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
🔵 Needs a closer look
The retired-model test does not validate registry removal when schema filenames differ from gateway model IDs.
Review details
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
models/aihubmix/tests/test_top100_models.py:135
- These six values are schema filenames, but
_position.yamlstores their different gateway model IDs (for example,deepseek-r1-aihubmix.yamlis registered asaihubmix-DeepSeek-R1). Therefore eachmodel not in positionsassertion is vacuously true and would not catch a stale registry entry. Track filename and model ID separately so the removal test covers both artifacts.
- Files reviewed: 52/52 changed files
- Comments generated: 0 new
- Review effort level: Balanced
The previous pass filled parameter_rules from vendor docs and sibling YAMLs. The authoritative source is the public projection published at https://aihubmix.com/model-data/index.json (schema 2.0.0), which states per model the officially supported enum, default and limits per protocol. Realign 23 model YAMLs to it and pin the facts down with tests. reasoning_effort now carries each model's official enum and default: gpt-6-astra gains max; gpt-5.5-pro, glm-5.2-fast-preview, mercury-2.5-preview, ox-alpha and muse-spark-1.1/1.2/1.3 gain the rule they were missing; coding-glm-5.3, coding-kimi-k3, qwen3.8-flash and the three deepseek-v4 flash models switch to the documented default; hy3 and hy4-preview take their own enums (no_think/low/high and none/high). Limits follow specs: qwen3.8-max* context 1000000 and max output 131072, qwen3.8-flash and glm-5.2* max output 131072, hy3/hy4-preview gain the max_tokens rule they lacked. Knobs the projection does not expose are dropped after confirming on the gateway that a 200 does not mean they took effect: gpt-5.6-sol-disc reasoning_mode, gpt-5.5-pro temperature/top_p (reasoning-only model, no chat-completions surface), glm-5.2-fast-preview boolean thinking (still returns reasoning_content) and thinking_budget. agnes-2.5/3.0-flash instead gain the official enable_thinking toggle, verified end to end. Pricing is unchanged and still comes from /api/v1/models?type=llm. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
An ineffective GPT-6 option and stale Qwen assertions must be corrected before approval.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
models/aihubmix/tests/test_top100_models.py:142
- For these six aliases, the tuple contains the filename stem, but
_position.yamlcontains each schema'smodelvalue (for exampleaihubmix-DeepSeek-R1anddeepseek-ai/DeepSeek-V3-0324). Consequently the position assertion would still pass if any retired model ID were accidentally left registered. Track filename and model ID separately so the regression test covers both removals.
- Files reviewed: 55/55 changed files
- Comments generated: 2
- Review effort level: Balanced
… object
Two plumbing defects the public projection turned up in llm.py.
gpt-5.5-pro lists only openai.responses and anthropic.messages, so unlike
gpt-5.5 it has no chat-completions surface, yet RESPONSE_SERIES_COMPATIBILITY
matched neither of its prefixes and the plugin sent it over chat completions.
Add it to the tuple so it goes through openai_response.py like gpt-5-pro and
gpt-5.6; its YAML already carries only rules that surface translates
(reasoning_effort, max_tokens, response_format, json_schema).
The thinking toggle was worse. parameter_rules can only express scalars, so
24 models ship a boolean `thinking`, but every vendor's chat-completions
field is an object. Sending the bare boolean fails outright on the gateway:
doubao-seed-2-0-pro/-lite 400 Mismatch type openapi.Thinking with value bool
kimi-k2.5 400 'thinking' must be json_object
coding-glm-5 400 JSON parse error: ... $Thinking
DeepSeek-V3.2-Exp-Think 400 cannot unmarshal bool into ... model.Thinking
ernie-5.0-thinking-exp 400 cannot unmarshal bool into ... model.Thinking
glm-5.2 200, ignored - 531 reasoning tokens still billed
_normalize_thinking_switch converts it to {"type": "enabled"|"disabled"} on
the OpenAI-compatible branch only, so anthropic.py keeps consuming the
boolean itself and Gemini keeps going through google.py. With the object
form the same calls return 200 and thinking really goes off (glm-5.2 drops
from 645 reasoning tokens to none, doubao to 0, coding-glm-5 to 14).
gemini-3-pro-preview needed the third variant: google.py reads thinking_mode,
so its `thinking` rule was inert. Rename it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
🔵 Needs a closer look
The existing Qwen 3.8 Max configuration test still asserts the old limits and will fail in the full test suite.
Review details
Suppressed comments (1)
models/aihubmix/models/llm/qwen3.8-max.yaml:15
- This change leaves
tests/test_qwen38_max_config.py:11-12asserting the old991_000context and128_000output limit, so the full AIHubMix test suite fails even though the new focused test passes. Update both existing expectations to1_000_000and131_072.
- Files reviewed: 57/57 changed files
- Comments generated: 0 new
- Review effort level: Balanced
…efinitions The earlier pass read only specs.reasoning_options (the projection's top-level summary of reasoning knobs) and missed domains[].capabilities[].protocols[].fields[], where each protocol's own field definitions carry the authoritative enum. Every model is now compared against the fields[] of the protocol it actually routes to in llm.py, and each change below is backed by live gateway behaviour, not just an HTTP 200. gpt-5.6-sol-disc (responses): restore reasoning_mode - deleting it in 59da4ce was wrong. A bogus value 400s with "Supported values are: 'standard' and 'pro'", and mode=pro spends 69 reasoning tokens where standard spends 0. Also add reasoning_summary (auto/concise/detailed; streaming with detailed produced 79 reasoning_summary_text deltas, which openai_response.py already parses) and reasoning_context (auto/current_turn/all_turns, left without a default so the server-side all_turns still applies). glm-5.2: add the missing reasoning_effort (none/minimal/low/medium/high/xhigh/max, default max). none returns zero reasoning tokens and no reasoning_content; minimal and max both reason. deepseek-v4.1-flash, deepseek-v4-flash-vision-exp, deepseek-v4-flash-0731-fast: drop the invented reasoning_effort=none and expose the official chat_completions thinking toggle instead. It is a boolean here because llm.py now normalises it into {"type": "enabled"|"disabled"}; turning it off stops reasoning tokens. json_schema is offered only where the gateway honours it. It 400s with "This response_format type is unavailable now" on deepseek-v4.1-flash and -vision-exp, and is silently ignored on coding-glm-5.2 and coding-glm-5.3, so those four drop the rule, the structured-output feature flag and the json_object -only response_format. glm-5.2, glm-5.2-fast-preview and deepseek-v4-flash-0731-fast keep it - they really do return the schema. gemini-3.1-flash-lite-nothink deliberately keeps no reasoning_effort even though the projection lists one: minimal, high and a bogus value all return 200 with zero reasoning tokens, so that enum belongs to the base model, not this id. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
🔵 Needs a closer look
The updated Qwen limits break an existing test, and several retirement assertions check filenames instead of registered model IDs.
Review details
Suppressed comments (2)
Previously missed (1) — in code that hasn't changed since the last review.
models/aihubmix/tests/test_top100_models.py:142
- These entries are filenames, not the model IDs stored in
_position.yaml, so the second assertion never verifies removal ofaihubmix-DeepSeek-R1,aihubmix-Llama-3-3-70B-Instruct,deepseek-ai/DeepSeek-V3-0324, or the three namespaced Llama IDs. Represent each retired item as(filename, model_id)and assert the filename is absent while the actual model ID is absent from positions.
models/aihubmix/models/llm/qwen3.8-max.yaml:15
- The full plugin test suite now has contradictory assertions:
tests/test_qwen38_max_config.py:11-12still requires this model's context and output limits to be991_000and128_000. With these updated values that pre-existing test fails, even though the new projection test only covers the dated variant. Update the existing test to assert1_000_000and131_072(and include this base model in the projection table) before merging.
- Files reviewed: 57/57 changed files
- Comments generated: 0 new
- Review effort level: Balanced
A full behavioural sweep over every parameter rule in this PR - the whole reasoning_effort enum of 13 models, json_schema on 25, and the thinking switch on 11 - turned up four rules the gateway does not honour and two it honours better than the YAML claimed. reasoning_effort is not validated on the chat-completions surface: a bogus level returns 200 and the model reasons anyway on every model tried, so the only usable evidence is the reasoning-token count. Only the OpenAI-native surface validates - gpt-5.5-pro 400s with "Supported values are: 'medium', 'high', and 'xhigh'", which is exactly what its rule already offers. Removed: - mercury-2.5-preview reasoning_effort. Inert across six calls; `instant`, the level that should spend the least, spent the most (864/872 against 794 for high). - ernie-5.1 and ox-alpha json_schema. Both answer a schema-constrained request in prose; json_object works, so response_format keeps that option only. - mai-thinking-1 json_schema and response_format. Every structured format 400s with "Structured `response_format` is not enabled for model". Restored, now that llm.py converts a boolean thinking into the official object: - glm-5.2-fast-preview thinking and thinking_budget. Disabled, it spends no reasoning tokens at all (333 when enabled); a budget of 64 spends exactly 64. Dropping them in 59da4ce was based on the projection alone and was wrong. coding-glm-5.2 keeps its enum - the levels do separate (low ~20 tokens against 57-59 for high/xhigh/max) - but `none` does not stop thinking: it still spends ~40 reasoning tokens and returns reasoning_content across two samples. The help text now says so; this is the same gateway-side drift already filed for hy3-preview / hy4-preview. Not verifiable this round, left as the projection has them: coding-kimi-k3 (400 "cannot be served at the moment"), hy3-preview json_schema (502 upstream, twice) and muse-spark-1.1/1.2/1.3 (403 on this key). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The gateway's `auto` id is a router, not a model: it picks a different upstream per request, so the context window, the feature flags and above all the price listed in its YAML are an average that matches no actual call. A Dify user picking it gets unpredictable billing with no way to tell what answered. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
🔵 Needs a closer look
An existing Qwen regression test now fails, and supported structured-output metadata remains incomplete.
Review details
Suppressed comments (2)
Previously missed (1) — in code that hasn't changed since the last review.
models/aihubmix/tests/test_top100_models.py:388
- These three models keep working
json_schemarules, but none of their schemas declares thestructured-outputfeature. That contradicts this PR's own supported/unsupported distinction (the preceding branch removes and asserts the flag when schemas are not honored) and leaves Dify capability discovery reporting structured output as unsupported. Add the feature to these supported models and assert its presence here.
models/aihubmix/models/llm/qwen3.8-max.yaml:15
- This catalog update makes the existing
tests/test_qwen38_max_config.pyfail: lines 11–12 still require a991_000context and128_000maximum output. Update that regression test to the new1_000_000/131_072values (and ideally include this base model in the new projection tables) so the full AIHubMix test suite remains green.
- Files reviewed: 56/56 changed files
- Comments generated: 0 new
- Review effort level: Balanced
There was a problem hiding this comment.
🟡 Changes recommended
The existing Qwen regression test now fails, and catalog-count and retired-registration assertions need correction.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (2)
Previously missed (1) — in code that hasn't changed since the last review.
models/aihubmix/tests/test_top100_models.py:144
- For these six retired models, the filename differs from the model ID that
_position.yamlstores (for example,deepseek-r1-aihubmixvs.aihubmix-DeepSeek-R1). As a result,assert model not in positionsis vacuous and would still pass if the retired registry entries were reintroduced. Track both filename and model ID explicitly.
models/aihubmix/models/llm/qwen3.8-max.yaml:15
- These catalog updates leave the pre-existing
tests/test_qwen38_max_config.pyasserting991_000and128_000, so the plugin's full test suite now fails even though the new test expects these values. Update that regression test to1_000_000and131_072in the same change.
- Files reviewed: 56/56 changed files
- Comments generated: 1
- Review effort level: Balanced
- tests/test_qwen38_max_config.py asserted the pre-update 991,000 context and 128,000 output cap for qwen3.8-max, so the full suite failed against the new catalog values. /api/v1/models now reports 1,000,000 / 131,072; pricing is unchanged. - gpt-6-astra drops `max` from reasoning_effort. The plugin dispatches this id over chat/completions, and there `max` spends exactly as many reasoning tokens as `medium` (6 vs 6 over four runs each) while `xhigh` really escalates (21-43). On /responses `max` does work (215-380 vs 143-184 for `xhigh`), but that is not the surface this schema drives. - test_retired_models_are_gone now tracks (filename, registered id) pairs. Six retired schemas are filed under a name that differs from the id `_position.yaml` registers (deepseek-r1-aihubmix.yaml declares `model: aihubmix-DeepSeek-R1`), so the registry assertion was vacuous. - Seventeen schemas offered `json_schema` without declaring the `structured-output` feature Dify reads for capability discovery. The flag now follows the rule, locked by a new test. 24 tests pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The PR text still claimed 32 additions and 215 registered ids from before the `auto` smart-router entry was dropped; the branch actually adds 31 and leaves 214. The registration test now pins both numbers and cross-checks the registry against the declared `model:` ids on disk in both directions, so a schema added without a position entry (or the reverse) fails here instead of at runtime. The comparison keys on the declared id rather than the filename, since several schemas are filed under a flattened name, and skips the five embedding/rerank schemas that sit in this directory but belong to other position files. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
🔵 Needs a closer look
The implementation contains 31 additions and 214 registrations, contradicting the advertised totals of 32 and 215.
Review details
Suppressed comments (1)
models/aihubmix/tests/test_top100_models.py:8
NEW_MODELScontains 31 IDs, and the resulting_position.yamlcontains 214 entries, while the PR contract states 32 additions and 215 total entries. The “Added (32)” table also names only 31 models, so this test currently codifies the mismatch instead of detecting the advertised total. Please either add the missing catalog model and register it, or correct the stated scope/counts and add an explicit total-count assertion.
- Files reviewed: 57/57 changed files
- Comments generated: 0 new
- Review effort level: Balanced
|
Note for reviewers: the latest Copilot review (2026-09-11T13:29:46Z) is stale — it ran against Its finding was the count mismatch (31 additions / 214 registered ids vs the advertised 32 / 215). That is fixed in assert len(NEW_MODELS) == 31
assert len(positions) == 214
assert len(positions) == len(set(positions))
...
assert set(positions) - declared == set()
assert declared - set(positions) == set()The last two close the gap in both directions, so a schema added without a All 8 review threads on this PR are resolved, and the PyYAML-only suite is green (24 passed). I do not have permission to re-request a Copilot review on this repository, so the bot will pick up the current head on the next push. 🤖 Generated with Claude Code |
There was a problem hiding this comment.
🔵 Needs a closer look
The large gateway-dependent catalog update includes unverified models and unresolved validation-test gaps.
Review details
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
models/aihubmix/tests/test_top100_models.py:467
- The validation output in the PR description reports
22 passed, but this module now contains 23 uniquely named, non-skipped test functions, including this final regression test. That output therefore predates the current test file; please rerun the current suite and update the validation result so the latest plumbing change is actually verified.
- Files reviewed: 57/57 changed files
- Comments generated: 1
- Review effort level: Balanced
The declared ids were collapsed into a set before any assertion ran, so two schema files declaring the same `model:` would have satisfied every check - the duplicate the test exists to catch. They are now kept as a list long enough to assert uniqueness, then compared against the registry as a set. Verified by dropping a second file with an existing id in place: the test fails on the new assertion and passed before it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The README stopped at the screenshot, so a user who needed more than the four config fields had nowhere to go. Adds the API-key console link, the step-by-step Dify integration guide (English and Chinese), and a Links section pointing at the site and the docs root. Links live in the README rather than in `manifest.yaml` / `provider/aihubmix.yaml` descriptions: no model plugin in this repo puts a URL in either field, and both render as plain text, so a URL there would not be clickable. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Summary
Realigns the AIHubMix plugin's predefined model list with the gateway catalog (
/api/v1/models?type=llm):*-free)_position.yamlResult: 214 LLM entries, every one of them registered in
_position.yaml, no orphaned files, no duplicates.manifest.yaml0.0.46 → 0.0.47.Added (31)
gpt-6-astra,gpt-5.6-sol-disc,gpt-5.5-pro,ox-alphagemini-3.1-flash-lite,gemini-3.1-flash-lite-nothink,gemini-3.1-flash-image,gemini-3.1-flash-lite-image,gemini-3-pro-imagedeepseek-v4.1-flash,deepseek-v4-flash-vision-exp,deepseek-v4-flash-0731-fastcoding-glm-5.3,coding-glm-5.2,glm-5.2-fast-previewqwen3.8-max-2026-09-02,qwen3.8-flashhy4-preview,hy3-preview,coding-kimi-k3ernie-5.1,mercury-2.5-preview,mai-thinking-1,agnes-3.0-flash,agnes-2.5-flash,agnes-2.5-pro,agnes-2.5-pro-alpha,longcat-2.0,muse-spark-1.1,muse-spark-1.2,muse-spark-1.3Validation
Two sources, each used for what it is authoritative about:
/api/v1/models?type=llm. Every price was diffed against it (31/31 exact, unit0.000001throughout).Every rule was then probed live against the production gateway, because an HTTP 200 does not prove a parameter took effect. Reasoning knobs were verified by comparing
usage.completion_tokens_details.reasoning_tokensand the presence ofmessage.reasoning_contentacross values.Two defects were caught this way:
gpt-6-astra—reasoning_effort: minimalreturns HTTP 400 listing the accepted values. The rule exposeslow/medium/high/xhigh. The projection's enum also hasmax, but the plugin dispatches this id overchat/completions, and on that surfacemaxspends exactly as many reasoning tokens asmedium(6 vs 6 over four runs each) whilexhighreally escalates (21–43). On/responsesmaxdoes work (215–380 against 143–184 forxhigh), so this is a surface-specific drift, not a bad enum. The model also gets theenable_streamopt-out used by the other new OpenAI models (streaming requires a KYC-verified org).ernie-5.1— a booleanthinkingis rejected upstream (json: cannot unmarshal bool into struct field ... model.Thinking), the same class of bug fixed forhy3in feat(aihubmix): add qwen3.7-plus/step-3.7-flash and fix hy3 reasoning control #3427. It exposesreasoning_effortinstead, verified effective (none→ 0 reasoning tokens).26 of the 31 returned a full completion on the test account. Five did not and are included anyway because they are listed in the catalog's top 100, with schemas following their validated family siblings:
coding-kimi-k3(no_available_channelon three attempts),hy3-preview(HTTP 502, Tencent upstream reported abnormal) andmuse-spark-1.1/1.2/1.3(HTTP 403 — the test account is access-restricted for that family).Removed — retired models (11)
Each is absent from the catalog and confirmed unusable against the live gateway:
gpt-4.5-previewmodel_retired— "has been retired… use gpt-4.1 instead"gemini-2.0-flashgemini-2.5-flash-preview-04-17gemini-2.5-flash-preview-04-17-nothinkdeepseek-v3.2-specialedeepseek-ai/DeepSeek-V3-0324no_available_channelaihubmix-DeepSeek-R1no_available_channelaihubmix-Llama-3-3-70B-Instructno_available_channelchutesai/Llama-4-Maverick-17B-128E-Instruct-FP8no_available_channelchutesai/Llama-4-Scout-17B-16E-Instructno_available_channelmeta-llama/llama-4-scout-17b-16e-instructmodel_not_foundOlder models that are missing from the catalog but still answer were deliberately kept:
claude-3-5-haiku/sonnet-20241022,claude-opus-4-20250514,claude-opus-4-1-20250805,claude-sonnet-4-20250514,DeepSeek-V3.2-Exp(-Think),deepseek-reasoner,deepseek-v3.2-fast,deepseek-v4-flash-think,doubao-seed-2-0-lite,gemini-3-pro-preview,gemini-3.1-flash-lite-preview,xiaomi-mimo-v2.5(-pro),gemini-embedding-2-preview.Removed — free-tier aliases (37)
All
*-freeentries (gpt-4.1-free,gemini-3-flash-preview-free,coding-glm-5-free, the nemotron*-freevariants, …). They draw on one account-wide free quota and start returning quota errors the moment it is used up, which makes them unreliable as predefined models — 9 of them already failed that way while probing this change.Also registered (10)
Live models that shipped without a
_position.yamlentry:gemini-2.5-pro,gemini-2.5-pro-preview-03-25,gemini-2.5-pro-preview-05-06, both*-searchvariants,gemini-2.5-flash-image,minimax-m2.5,minimax-m2.5-highspeed,qwen3-vl-235b-a22b-instruct,qwen-plus-latest.Intentionally not added (7)
Top-100 entries that are not chat models and are not served on
/v1/chat/completions:mai-image-2.5,mai-image-2.5-flash,mai-image-2.5-pro,mai-image-2.6,mai-image-2.6-flash— image generation (Requested path is not found); they belong to the image endpoint /tools/aihubmix_image.gpt-audio-1.5— requires an audiomodalitiesfield the OpenAI-compatible path cannot send.gpt-4o-transcribe-diarize— speech-to-text (The requested operation is unsupported);models/speech2text/is the right home.Gemini image models
Review flagged that the three new image models would have shipped text-only, and that was correct.
llm.pyroutes everygemini*id toGoogleLargeLanguageModel, which only asks for IMAGE output when the id is listed ingoogle.py'sIMAGE_GENERATION_MODELS; all three were missing from it, so_set_response_modalitiesand_set_image_configwere both skipped.They are now in the allowlist, and each YAML exposes
aspect_ratio/resolutionwith the options that model actually accepts — probed live against the gateway, dimensions read back off the returned JPEG:gemini-3-pro-image4:1and8:1return 400gemini-3.1-flash-image4:1,8:1gemini-3.1-flash-lite-image4:1,8:1_set_image_config's hardcoded ratio allowlist also had to learn4:1and8:1, otherwise the new YAML options would have been dropped before the request left the plugin. Noinclude_thoughtsrule was added:_set_thinking_configreturns early forIMAGE_GENERATION_MODELS, so that control would be inert.Parameter rules realigned to the public projection
A later pass rechecked all 23 affected YAMLs against the projection rather than against vendor docs and sibling files, and reconciled every difference.
reasoning_effortnow carries each model's own documented enum and default:gpt-6-astragpt-5.5-procoding-glm-5.2coding-glm-5.3coding-kimi-k3deepseek-v4.1-flash,deepseek-v4-flash-vision-exp,deepseek-v4-flash-0731-fastglm-5.2-fast-previewqwen3.8-max,qwen3.8-max-2026-09-02qwen3.8-flashhy3-previewhy4-previewmercury-2.5-previewox-alphamuse-spark-1.1/1.2/1.3Reading rule: the projection's
valuesis the documented enum and itsdefaultis the value the server applies when the field is omitted — itself an accepted wire value, confirmed live oncoding-glm-5.3(medium),qwen3.8-max-2026-09-02andhy3. So each YAML exposesvalues ∪ {default}and defaults to the projection default.Limits follow the projection's
specs:qwen3.8-max*context991000 → 1000000and max output128000 → 131072;qwen3.8-flashandglm-5.2*max output128000 → 131072;hy3-preview/hy4-previewgain themax_tokensrule they were missing (128000 / 64000).Knobs the projection does not expose were dropped, each after confirming on the gateway that the 200 was empty:
gpt-5.5-protemperature,top_pglm-5.2-fast-previewbooleanthinkingthinking: falsestill returnsreasoning_content; only the official object form disables it, whichparameter_rulescannot express —reasoning_effort: noneis the working off switchglm-5.2-fast-previewthinking_budgetbudget_tokensoption in the projectionagnes-2.5-flash/agnes-3.0-flashgained the projection's official thinking toggle instead: a booleanenable_thinking, whichllm.pysends aschat_template_kwargs.enable_thinking. Verified end to end — true → reasoning tokens andreasoning_content, false → neither.ernie-5.1keeps itsreasoning_effortrule even though the projection lists onlythinking_budgetfor it: the enum is verified effective on the gateway (none→ 0 reasoning tokens) while a booleanthinkingis rejected upstream.Enums taken from the projection's per-field definitions
The pass above read only
specs.reasoning_options— the projection's top-level summary of a model's reasoning knobs. The authoritative enum actually lives one level deeper, indomains[].capabilities[].protocols[].fields[]: each protocol lists its own fields withenum,defaultand anoverride.status(model-specific-enumwhen the set is narrowed for this model). A final pass compares every model against thefields[]of the protocol it actually routes to inllm.py, and each change below is backed by gateway behaviour, not just an HTTP 200.gpt-5.6-sol-discreasoning_modeis restored — dropping it above was wrong. It isofficial-model-levelonopenai.responses, and a bogus value 400s withSupported values are: 'standard' and 'pro'.mode: prospends 69 reasoning tokens wherestandardspends 0. Two sibling fields were missing entirely and are added:reasoning_summarydetailedproduced 79response.reasoning_summary_text.deltaevents, whichopenai_response.pyalready parsesreasoning_contextall_turnsstill appliesglm-5.2gained thereasoning_effortit was missing (none…max, defaultmax):none→ 0 reasoning tokens and noreasoning_content,minimal→ 294,max→ 243.The three deepseek flash models swap an invented enum for the official toggle. Their
reasoning_effortlisted anonethat the projection'sfields[]does not contain (low/high/max, defaultmedium); it is gone. The off switch is the field the projection really marksofficial-model-level, athinkingobject — shipped as a boolean becausellm.pynow normalises it into{"type": "enabled"|"disabled"}. Verified: enabled → 141/136/177 reasoning tokens, disabled → none.json_schemais now offered only where the gateway honours it. Probed with a strict schema andmax_tokens: 400:deepseek-v4.1-flash,deepseek-v4-flash-vision-expThis response_format type is unavailable nowstructured-outputflag and thejson_schemaresponse_formatoption removedcoding-glm-5.2,coding-glm-5.3Hello!, schema never appliedglm-5.2,glm-5.2-fast-preview,deepseek-v4-flash-0731-fast{"greeting": "Hello"}— enforcedgemini-3.1-flash-lite-nothinkdeliberately keeps noreasoning_efforteven though the projection lists one:minimal,highand a bogus value all return 200 with zero reasoning tokens. That enum belongs to the base model, not the-nothinkid.Fields present in the projection but deliberately not adopted:
tool_choice(the plugin sets it itself, and injecting it with no tools risks 400s);service_tier,prompt_cache_retentionandprompt_cache_options.*(nested and/or transport-billing knobs a flatparameter_rulesentry cannot express);messages[].content[].image_url.detailandinput[].phase(per-message, not per-model); single-value enums with no useful choice (mai-thinking-1reasoning_display: ['encrypted'],ernie-5.1web_search: ['auto']); and everything whose verdict isspec-onlyrather thanofficial-model-level.Every rule re-checked against gateway behaviour
A final sweep called each rule instead of trusting the projection: the full
reasoning_effortenum of 13 models,json_schemaon 25, and the thinking switch on 11.The first finding is about method.
reasoning_effortis not validated on the chat-completions surface —reasoning_effort: "zzz-not-a-level"returns 200 and the model reasons anyway on all 16 models tried, so a 200 says nothing and the reasoning-token count is the only usable evidence. Only the OpenAI-native surface validates:gpt-5.5-pro400s withSupported values are: 'medium', 'high', and 'xhigh'— exactly the three its rule already offers, andnone,minimalandloware each rejected by name.Removed — the gateway does not honour them:
mercury-2.5-previewreasoning_effortinstant, the level that should spend the least, spent the most (864 / 872 reasoning tokens against 794 forhigh)ernie-5.1,ox-alphajson_schemaHello! I am using English.);json_objectworks, soresponse_formatkeeps that option onlymai-thinking-1json_schema+response_formatStructured `response_format` is not enabled for modelRestored — the gateway honours them better than the YAML claimed.
glm-5.2-fast-previewgetsthinkingandthinking_budgetback: dropping them earlier was based on the projection plus a raw boolean that the vendor rejected, butllm.pynow sends the official object, and with it the switch spends no reasoning tokens when disabled (333 when enabled) while a budget of 64 spends exactly 64. The same measured wording is now onglm-5.2's two rules.coding-glm-5.2keeps its enum — the levels do separate (low~20 tokens against 57–59 forhigh/xhigh/max) — butnonedoes not stop thinking: ~40 reasoning tokens and areasoning_contentbody across two samples. The help text says so, and it joins thehy3-preview/hy4-previewdrift already filed as a separate task.Not verifiable this round, so left exactly as the projection has them:
coding-kimi-k3(400 cannot be served at the moment),hy3-previewjson_schema(502 upstream, twice) andmuse-spark-1.1/1.2/1.3(403 on this key).Plugin plumbing fixed alongside
The projection also exposed two defects in
llm.pythat no YAML change could reach.gpt-5.5-prowas going over the wrong protocol. Its projection entry lists onlyopenai.responsesandanthropic.messages— unlikegpt-5.5, it has no chat-completions surface.RESPONSE_SERIES_COMPATIBILITY = ("gpt-5-codex", "gpt-5-pro", "gpt-5.6", "o3-pro")matches by prefix and"gpt-5.5-pro"starts with none of them, so the plugin sent it to chat completions. It now routes throughopenai_response.pylike its siblings; its rules (reasoning_effort,max_tokens,response_format,json_schema) are exactly the ones that surface translates.The thinking toggle was broken on 24 models.
parameter_rulescan only express scalars, so those YAMLs ship a booleanthinking— but every vendor's chat-completions field is an object. The bare boolean fails on the gateway:"thinking": falsedoubao-seed-2-0-pro,doubao-seed-2-0-liteMismatch type openapi.Thinking with value boolkimi-k2.5'thinking' must be json_objectcoding-glm-5JSON parse error: ... ChatCompletionRequest$ThinkingDeepSeek-V3.2-Exp-Think,ernie-5.0-thinking-expcannot unmarshal bool into ... model.Thinkingglm-5.2llm.pynow converts it to the official{"type": "enabled"|"disabled"}on the OpenAI-compatible branch only, soanthropic.pykeeps consuming the boolean itself and Gemini keeps going throughgoogle.py. With the object form those same calls return 200 and thinking actually stops —glm-5.2645 reasoning tokens → none,doubao-seed-2-0-pro→ 0,coding-glm-5→ 14.gemini-3-pro-previewneeded the third variant:google.py's_set_thinking_configreadsthinking_mode, so itsthinkingrule was simply never read. Renamed.Documentation
The README stopped at the configuration screenshot, so it now also links the API-key console, the step-by-step Dify integration guide (with its Chinese version), and the AiHubMix site and docs root. The links live in the README rather than in the
manifest.yaml/provider/aihubmix.yamldescriptions, because no model plugin in this repo puts a URL in either field and both render as plain text.Release Notes
0.0.47 - catalog realignment: 31 new models, 11 retired ones removed, parameter rules rebuilt from AIHubMix's published schema
gpt-6-astra,gpt-5.6-sol-disc,gpt-5.5-pro,gemini-3.1-flash-lite,deepseek-v4.1-flash,coding-glm-5.3,qwen3.8-flash,hy4-preview,ernie-5.1and three Gemini image models.*-freealiases, which the gateway does not serve.gemini-3-pro-preview(the parameter was never read).json_schema) is now advertised only on the models where the gateway honours it.Change Type
Screenshots / Videos
*-freealiases the gateway refuses, and 10 shipped models invisible because they were never registeredgemini-3-pro-preview: the request returned 200 and kept billing reasoning tokensthinking: falseactually stops reasoning (glm-5.2645 reasoning tokens -> 0)gemini-3-pro-image,gemini-3.1-flash-image,gemini-3.1-flash-lite-imagereturn imagesLLM Plugin Checklist
Areas affected by this change (check all that apply)
Version
versioninmanifest.yaml(not the one undermeta) -0.0.46->0.0.47dify_plugin>=0.10.2is declared inpyproject.tomland locked inuv.lock(the pin this plugin already uses; the template's>=0.3.0,<0.6.0range predates the current SDK)Testing
This change is schema and metadata only: it adds and removes YAML model definitions and fixes three parameter-plumbing bugs in
llm.py/google.py. Validation was done directly against the production AIHubMix gateway plus the plugin's own unit tests, not through a Dify UI run - happy to record a local deployment run if a maintainer wants that before merge.Against the gateway - every rule was exercised with a real call and judged on observed behaviour rather than on HTTP 200: reasoning-token counts (
usage.completion_tokens_details.reasoning_tokens), the presence ofreasoning_content, the returned body shape forjson_schema, and the error text returned for deliberately bogus enum values. The per-model results are in the tables above. Four models could not be reached and fall back to the published schema:muse-spark-1.1/1.2/1.3(403, key-level restriction) andhy3-preview(502 from the Tencent upstream).Unit tests
tests/test_top100_models.py— asserts every added model parses, declaresllm/chat, uses the standard pricing unit and is registered in_position.yaml; that no*-freefile or position entry remains; that each retired model is gone from both; that the three image models are inIMAGE_GENERATION_MODELSand expose exactly the validated aspect-ratio/resolution options with no inert thinking switch; plus regression assertions for the two parameter defects above. Four further tests pin the realignment:reasoning_effortoptions/defaults and the context/output limits against a table transcribed from the projection, the absence of the inert knobs, and theagnesthinking toggle. Three more cover the plumbing: the responses-API routing ofgpt-5.5-pro, the boolean→object thinking conversion (the helper is lifted out ofllm.pyand exercised directly, so it runs withoutdify_plugin), and the Gemini rule rename. Three more pin the fields-level pass: thatgpt-5.6-sol-discexposes every official responses reasoning field with the projection's enum and thatopenai_response.pytranslates each one, that the deepseek flash trio turn thinking off through the official field, and thatjson_schemais offered only where the gateway honours it. The sweep above adds a fourth: the restoredglm-5.2-fast-previewswitch,mercury's inert enum staying out, andmai-thinking-1keeping no structured-output surface. Review follow-ups add three more: the registry cross-check (every_position.yamlid has a schema behind it and every llm schema is registered, compared on the declaredmodel:id rather than the filename, with the totals 31 added / 214 registered pinned and duplicate ids rejected), thestructured-outputfeature flag now tracking thejson_schemarule across the 17 schemas this PR gives it, andgpt-6-astraasserting the reducedreasoning_effortenum.tests/test_qwen38_max_config.pyis a pre-existing module this PR touches: the catalog now reports 1,000,000 context / 131,072 max output forqwen3.8-max(was 991,000 / 128,000), so its limit assertions were refreshed — pricing is unchanged.The whole
testjob was reproduced locally against this branch, packaged the way CI packages it:That covers all four test modules, including the two pre-existing ones that need
dify_plugin(test_september_models.py,test_opus5_parameters.py). The other steps of the job were run by hand too: manifest author islanggenius,0.0.47is not yet on the Marketplace (0.0.46is), novenv*directory,pyproject.tomlpresent,uv sync --frozenreports no lockfile drift, and theRelease Notessection above extracts non-empty. Only the two steps that need repository secrets or the CI-cloned.scripts/toolkit were skipped (Check Packaging,Check Plugin Install).🤖 Generated with Claude Code