Repository navigation
Feat/claude model selection - #11
Merged
emanuilo merged 17 commits intoOct 5, 2026
Merged
Conversation
The previous range capped at 0.1.x, which has no `effort` option. 0.2.x exposes it as Optional[Literal["low","medium","high","xhigh","max"]], which the upcoming model/effort selection needs. Verified against the installed 0.2.152 that every ClaudeAgentOptions field this project uses still exists: env, model, system_prompt, setting_sources, mcp_servers, allowed_tools, disallowed_tools, permission_mode, max_turns. Test suite is unchanged by the bump: 178 passed, 4 failed. The same four failures (tests/backends/test_mcp_bridge.py) occur on main before this commit. They are Windows-only and unrelated: mcp_bridge.py passes fd= to uvicorn, which resolves it via socket.fromfd(..., AF_UNIX, ...), and AF_UNIX does not exist on Windows.
Adds memclaw/anthropic_models.py, which asks /v1/models what the configured credential can reach and reads each model's effort support off its capability tree. The setup wizard will build its model picker from this, so a model Anthropic releases needs no code change here. Shaped after openai_health.py: probe an API, return a small dataclass report, leave presentation to the caller. Notes on the pieces that are easy to get wrong: - Auth headers differ by credential. An OAuth token sent as x-api-key is rejected 401, so subscription and API-key modes build separate headers. - Effort levels are filtered through SDK_EFFORT_LEVELS, mirroring what ClaudeAgentOptions.effort accepts. A level Anthropic ships before our SDK understands it is dropped rather than passed through. - Results are cached for 24h in ~/.memclaw/models_cache.json. A missing, malformed or future-dated cache falls back to the network instead of raising; the wizard must never break over a cache file. - The endpoint pages at 20 by default, so limit=100 is requested. Verified against the live API with a subscription token: 11 models, correctly ordered newest first. Effort support turns out to be graded rather than all-or-nothing, which the capability tree handles for free: Sonnet 4.6 supports low/medium/high/max but not xhigh, Opus 4.5 stops at high, and Haiku 4.5 and Sonnet 4.5 report no effort support at all. 23 new tests, all HTTP mocked; the suite still needs no keys and no network. Full run: 201 passed, plus the 4 pre-existing Windows-only mcp_bridge failures that also fail on main.
Two new MemclawConfig fields alongside cursor_model, populated from the environment the same way CURSOR_MODEL already is. Empty means "use the built-in default", so nothing changes for an existing install until the wizard starts writing these keys. They sit in their own Claude block rather than inside the Cursor one, to keep the two backends visually separate. 204 passed, plus the 4 pre-existing Windows-only mcp_bridge failures.
The Claude backend's wizard_setup now follows the credential question with a model picker built from the live /v1/models list, and an effort picker holding only the levels that model actually accepts. A model that supports no effort skips the second question entirely and drops any level left over from an earlier choice, so an unsupported value can never reach the SDK. Fetching the list is a convenience, not a requirement: any failure prints one warning and keeps the current model, so the wizard always finishes.
Both run_one_shot and run_turn now take the model and effort off the config instead of the hardcoded constant. _MODEL stays as the fallback for an unset value, and an effort the SDK does not recognise resolves to None, which makes the SDK omit the flag rather than pass it to the CLI. Both are resolved once in __init__, mirroring how the Cursor backend resolves its model. Verified against the real SDK, not just the mocks: a one-shot turn succeeds with effort=high on Sonnet 4.6 and with effort unset on Haiku 4.5.
The panel now reports which model a turn will actually use and which effort level goes with it, resolved through the same helpers the backend uses, so status cannot drift from runtime. Both lines are skipped when Cursor is the active backend — naming a Claude model there would describe something that is not going to run. "default" means no level is set and the model decides.
Adds the cases the spec asks for that the backend tests did not yet have: an empty ANTHROPIC_MODEL falls back to the built-in default, an effort level the SDK does not know is ignored, both values reach ClaudeAgentOptions, and a failed model fetch warns once instead of raising out of the wizard.
The autouse fixture already stripped the credential env vars, but not ANTHROPIC_MODEL/ANTHROPIC_EFFORT. Importing memclaw.config loads ~/.memclaw/.env into os.environ, so once a developer had run the wizard, the three default-fallback tests asserted against that developer's own model instead of the built-in default.
emanuilo
self-requested a review
September 23, 2026 01:33
emanuilo
reviewed
Sep 23, 2026
emanuilo
reviewed
Sep 23, 2026
emanuilo
reviewed
Sep 23, 2026
emanuilo
previously approved these changes
Sep 23, 2026
emanuilo
reviewed
Oct 4, 2026
emanuilo
reviewed
Oct 4, 2026
emanuilo
reviewed
Oct 4, 2026
emanuilo
reviewed
Oct 4, 2026
emanuilo
reviewed
Oct 4, 2026
emanuilo
reviewed
Oct 4, 2026
emanuilo
reviewed
Oct 4, 2026
emanuilo
reviewed
Oct 4, 2026
emanuilo
reviewed
Oct 4, 2026
emanuilo
reviewed
Oct 4, 2026
anthropic_models.py and backends/claude.py imported from each other, so both sides needed in-function imports. fetch_models() now takes the auth mode and credential as plain strings, like probe_openai(api_key), so the module no longer needs anything from claude.py and lives next to it as backends/claude_models.py. All imports in claude.py are back at the top. This also removes _probe_config, which built a whole MemclawConfig (and its folders and files) just to carry one credential. The model-list cache is gone: it could serve a list fetched with a previous credential, and the picker only runs from `memclaw configure`. The OAuth beta header is dropped too, since /v1/models doesn't need it.
The Claude CLI reads ANTHROPIC_MODEL itself, so someone who exports it for Claude Code would silently change Memclaw's model too. The new names match CURSOR_MODEL, and a test pins that ANTHROPIC_MODEL is ignored.
The status command checked for the Claude backend by name and imported its private helpers, which base.py says nothing outside a backend should need. Backends now implement status_rows(config) -> [(label, value)]: Claude returns model and effort, Cursor returns its model, and status prints whatever the active backend gives it.
If the current model wasn't an exact id in the fetched list (an alias like `opus`, or a value set only in the shell), the picker fell back to the newest model, so pressing Enter changed the model. The current model now gets its own row and stays the default; keeping it leaves both CLAUDE_MODEL and CLAUDE_EFFORT untouched. Like CURSOR_MODEL, a value exported only in the shell counts as current. The Panel + numbered rows + Prompt.ask pattern repeated by the platform, backend, auth, model and effort pickers now lives in one helper, memclaw.prompts.choose. It sits outside setup.py so backends can import it at module level.
The fallback cost still used Sonnet prices, which is wrong once the model can be Opus or Haiku. Prices are now looked up per model (by id prefix, so dated snapshots match); an unknown model is estimated at the default model's price. The SDK normally reports total_cost_usd itself, so this only covers turns where it doesn't. Also renames _MODEL to _DEFAULT_MODEL to match cursor.py and refreshes the out-of-date "Sonnet 4 pricing" and "claude-agent-sdk 0.1.x" notes.
The env-isolation fixture and config helper were copied between test files, and the copy didn't clear the model/effort vars. They now live in tests/conftest.py as isolate_claude_env and the claude_config factory, used by the Claude backend, config and status tests. The model-discovery tests no longer need either, since fetch_models takes plain strings. Adds end-to-end tests for ClaudeAgentBackend.wizard_setup: auth choice, credential, model and effort, including a fetch failure that must still save the credential. The "(spec x.y)" references are gone, since that spec isn't in the repo.
The env-var table rows already cover the details.
memclaw imports mcp directly (mcp_tools.py, mcp_bridge.py) but never declared it, so CI's unpinned pip install pulled mcp 2.x, which removed the low-level Server decorators.
emanuilo
approved these changes
Oct 5, 2026
Owner
|
Its great now! Thanks a lot! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds Claude model and effort-level selection to Memclaw.
Until now, the Claude backend always ran a hardcoded
claude-sonnet-4-6with no way to change the model or how deeply it thinks. The setup wizard now asks both questions, and the answers are stored as ordinary config values.What changed
Live model discovery
memclaw/anthropic_models.py/v1/modelsusing the credential the user just entered, so a newly released model appears without a Memclaw release. Both credential types are supported (OAuth bearer token andx-api-key); they are not interchangeable, so the auth header is chosen per mode.claude-agent-sdkaccepts (low/medium/high/xhigh/max), so a level a future API adds drops out on its own instead of reaching the SDK.~/.memclaw/models_cache.jsonfor 24h so repeatedmemclaw configureruns don't re-hit the API. A missing, stale, or malformed cache silently falls back to the network.Wizard
memclaw/backends/claude.pymemclaw configurecannot finish.Config and runtime
Adds
ANTHROPIC_MODELandANTHROPIC_EFFORTsettings, resolved from the config file or the environment like every other value.Both settings are optional:
The backend passes the resolved model and effort to
ClaudeAgentOptions.memclaw statusshows the active model and effort, but only when Claude is the active backend — printing them under Cursor would name a model that isn't going to run.Dependency
claude-agent-sdkto>=0.2.139,<0.3, the first version exposingClaudeAgentOptions.effort.Tests
New coverage for:
ANTHROPIC_MODELcannot change the resultNotes
Defaults are unchanged: an existing install that accepts every prompt keeps running the same model it ran before.
README documents both variables and the new wizard steps.