summaryrefslogtreecommitdiffhomepage
path: root/.rules/plan/07-15-model-catalog.md
diff options
context:
space:
mode:
Diffstat (limited to '.rules/plan/07-15-model-catalog.md')
-rw-r--r--.rules/plan/07-15-model-catalog.md136
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`.