diff options
Diffstat (limited to '.rules/plan/07-15-model-catalog.md')
| -rw-r--r-- | .rules/plan/07-15-model-catalog.md | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/.rules/plan/07-15-model-catalog.md b/.rules/plan/07-15-model-catalog.md new file mode 100644 index 0000000..c5a65b0 --- /dev/null +++ b/.rules/plan/07-15-model-catalog.md @@ -0,0 +1,136 @@ +# Phase 07 — Model catalog + +**Estimated time:** ~15 minutes +**Touches:** `lib/dispatch/adapter/minimax/model_catalog.rb`, +`lib/dispatch/adapter/minimax.rb` (only if `list_models` references the +catalog). + +## Goal + +Update `ModelCatalog.build` and `ModelCatalog.build_from_api` so they +reflect MiniMax's seven supported models and capabilities (per their +docs). The class shape stays the same so callers / specs keep working. + +MiniMax compatibility doc capability flags: + +- All 7 models support tool calls. +- All 7 models support streaming. +- Vision is NOT supported in any model (image / document content blocks + are explicitly listed as unsupported). +- `premium_request_multiplier` is `nil` (Token Plan is request-quota, + not multiplier-weighted). + +## Steps + +### 1. Update `lib/dispatch/adapter/minimax/model_catalog.rb` + +Two changes: + +- Set `supports_vision: false` (MiniMax does not accept image blocks). +- Drop the `"(unrated) #{display_name}"` formatting in + `build_from_api`. With every model in the bundled pricing table, the + `nil` branch is unreachable in practice, but keep the guard: + `display_name` should be returned verbatim. Unknown runtime models + should still produce a usable `ModelInfo`. + +Final shape: + +```ruby +# frozen_string_literal: true + +module Dispatch + module Adapter + class MiniMax < Base + module ModelCatalog + DEFAULT_CONTEXT_WINDOW = 204_800 + + module_function + + # Build a ModelInfo for a known MiniMax model id. + # + # @param id [String] + # @return [Dispatch::Adapter::ModelInfo] + def build(id) + ModelInfo.new( + id: id, + name: id, + max_context_tokens: PricingTable.context_window(id) || DEFAULT_CONTEXT_WINDOW, + supports_vision: false, + supports_tool_use: true, + supports_streaming: true, + premium_request_multiplier: nil, + pricing: PricingTable.lookup(id) + ) + end + + # Build a ModelInfo from a runtime API entry (from GET /v1/models). + # + # If the model id is in the bundled pricing table, the bundled + # context window is used. Display name from the API entry takes + # precedence over the id. + # + # @param api_entry [Hash] + # @return [Dispatch::Adapter::ModelInfo] + def build_from_api(api_entry) + id = api_entry["id"].to_s + display_name = api_entry["display_name"].to_s + display_name = id if display_name.empty? + + pricing = PricingTable.lookup(id) + context_win = PricingTable.context_window(id) || DEFAULT_CONTEXT_WINDOW + + ModelInfo.new( + id: id, + name: display_name, + max_context_tokens: context_win, + supports_vision: false, + supports_tool_use: true, + supports_streaming: true, + premium_request_multiplier: nil, + pricing: pricing + ) + end + end + end + end +end +``` + +### 2. Sanity-check `list_models` + +Find `def list_models` in `lib/dispatch/adapter/minimax.rb`. It probably +calls `ModelCatalog.build_from_api(entry)` for each runtime entry, then +merges with the hardcoded catalog. Confirm: + +- The runtime fetch hits `MODELS_PATH` (`/v1/models`). +- On any error (404, parse failure, network), it falls back to building + `ModelInfo` for each id from `PricingTable.known_ids` via + `ModelCatalog.build`. +- The result is a deduplicated array (by `id`). +- Caching (`@models_cache`, `@models_cache_at`) is intact. + +If MiniMax doesn't expose `/v1/models` (we don't know yet), the runtime +branch will return `nil` / `[]` and the hardcoded catalog will be used. +Both outcomes are acceptable. + +DO NOT add live network smoke testing here; live testing is post-plan. + +## Acceptance criteria + +- `ModelCatalog.build("MiniMax-M2.7")` returns a `ModelInfo` with: + - `id: "MiniMax-M2.7"` + - `max_context_tokens: 204_800` + - `supports_vision: false` + - `supports_tool_use: true` + - `supports_streaming: true` + - `pricing` with all four `*_per_mtok` fields zero. +- `ModelCatalog.build("not-a-real-model")` returns a `ModelInfo` with + `pricing: nil` and `max_context_tokens: 204_800` (the default). +- `bundle exec rubocop --autocorrect-all` exits 0. + +## Verification + +Run `run_tests` with `project_path=reference/dispatch-adapter-minimax`. +Rubocop must be clean. `model_catalog_spec.rb` failures are acceptable +(retargeted in phase 16). Any failure outside that file must be fixed +before calling `ask_for_next_plan`. |
