feat(doctor): surface per-model reasoning-effort support and context-window limits - #302
Merged
Jason Robert (jrob5756) merged 2 commits intoJul 15, 2026
Merged
Conversation
…window limits Add an optional AgentProvider.get_model_capabilities() hook (mirrors the existing get_max_prompt_tokens / get_model_pricing pattern) that reports, per model: which reasoning.effort levels it accepts, its default effort, and its prompt/output/context-window token limits. - Copilot implements the hook via client.list_models() (reasoning fields live at the top level of Model, not nested under capabilities). - Claude implements it via the existing thinking-model heuristic plus get_max_prompt_tokens (Anthropic exposes no output/total-context split). - Other providers inherit the base None default, rendered as n/a. diagnostics.ProviderDiagnostic.models changes from list[str] to list[ModelDiagnostic] (id + capability fields), populated per-model with per-model failure isolation so one bad model does not drop the rest. conductor doctor --models now renders a separate per-provider Models detail table (Model / Reasoning efforts / Default / Prompt / Output / Context); the Providers table Models column shows a count instead of raw ids. The JSON models field is now a list of capability objects. Also fixes a latent bug found while implementing this: Copilot's _validate_reasoning_effort_for_model read capabilities.supported_reasoning_efforts, but the installed SDK exposes that field at the top level of Model, so the per-model reasoning-effort check (including max-rejection) was silently a no-op against the real SDK. Closes #301 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ab369757-28a7-4bee-ab9d-6c7eab2f6543
- diagnostics._build_model_diagnostics: the per-model try/except only wrapped the get_model_capabilities call, not the subsequent attribute reads on its result. A malformed capabilities object escaped the guard and wiped out every already-built model for that provider. Now the whole per-model construction is guarded, and simplified to build ModelDiagnostic kwargs from ModelCapabilityInfo.to_dict() instead of copying each field by hand. - claude.get_model_capabilities: split the reasoning-effort heuristic and the max_prompt_tokens lookup into independently-guarded try/except blocks, so the method upholds "never raise" standalone rather than relying on its caller's defensive catch. Also fixed a misleading "dashboard use case" reference in the docstring. - diagnostics.ModelDiagnostic: made frozen, matching its sibling value objects (ModelCapabilityInfo, ModelPricing, CredentialEnvVar) with no functional cost since it is always built in one step. - docs/cli-reference.md, CHANGELOG.md: corrected an inaccurate claim that claude-agent-sdk / hermes / openai-agents render n/a per field in a Models detail table row. They do not implement list_models either, so they never get a detail table at all -- only n/a in the Providers table summary. - test_copilot.py: reverted unused kwargs on TestReasoningEffort._make_model left over from before the get_model_capabilities tests split into their own helper class. - Added regression tests: mixed success/failure model ordering, a malformed capabilities object, supported_reasoning_efforts=[] render as "none" in the doctor CLI, and Claude's reasoning fields staying populated when the SDK is unavailable or model is not a string. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ab369757-28a7-4bee-ab9d-6c7eab2f6543
Jason Robert (jrob5756)
marked this pull request as ready for review
July 15, 2026 20:44
Jason Robert (jrob5756)
deleted the
feature/301-doctor-model-capabilities
branch
July 15, 2026 20:45
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
Extends
conductor doctor --models(and the underlyingProviderDiagnostic/--jsonoutput) to surface, per model:reasoning.effortlevels a model accepts, plus its default.Approach
AgentProvider.get_model_capabilities(model)hook (mirrors the existingget_max_prompt_tokens/get_model_pricingpattern inproviders/base.py; base default returnsNone, never raises).client.list_models()— reasoning fields live at the top level ofModel, not nested undercapabilities.is_claude_thinking_model) plusget_max_prompt_tokens; Anthropic exposes no output/total-context split, so those fields stayNone.Nonedefault, rendered asn/a.ProviderDiagnostic.modelschanged fromlist[str]tolist[ModelDiagnostic](id + capability fields), populated per-model with per-model failure isolation so one bad model does not drop the rest of the list.conductor doctor --modelsnow renders a separate per-provider Models detail table (Model / Reasoning efforts / Default / Prompt / Output / Context); the Providers table's Models column shows a count instead of raw ids. The JSONmodelsfield is now a list of capability objects (not plain id strings — this is a breaking shape change for--jsonconsumers).Bug fix (found while implementing)
CopilotProvider._validate_reasoning_effort_for_modelreadcapabilities.supported_reasoning_efforts, but the installed SDK (github-copilot-sdk>=1.0.0) exposes that field at the top level ofModel. The lookup always returnedNone, so per-model reasoning-effort validation (including themax-rejection behavior from #299) was silently a no-op against the real SDK. Fixed the read location; updated the test mock shape to match the real SDK.Testing
uv run ruff check src tests/ruff format --check— clean.uv run ty check src— clean (one pre-existing, unrelated warning).uv run pytest -m "not install_scripts"— 3942 passed, 21 skipped, 1 pre-existing failure unrelated to this change (test_copilot_large_write.py, fails identically onmain— hits a live backend that rejects an unavailable model).test_base.py,test_copilot.py,test_claude.py,test_diagnostics.py,test_doctor.py.Docs
Updated
docs/cli-reference.md(new "Per-model capabilities" section) andCHANGELOG.md(Added + Fixed entries).Closes #301