Repository navigation
feat(coach): live model lists per provider + iOS #140 model catalog and per-model reasoning levels - #120
Merged
Conversation
…nd per-model reasoning levels Settings now reads each cloud provider's own model list once the provider is selected with a saved key (OpenAI /v1/models, Gemini v1beta/models, OpenRouter /api/v1/models, MiniMax /v1/models), shows it under the curated picks, and flags a stored model the provider no longer lists or is retiring, instead of every coach turn failing on a stale preset. The stored model stays a free string; the presets remain the fallback when the list can't be fetched. Ports iOS #140 (8833ee0): - presets: GPT-6 Luna / Sol (default) / Astra; OpenRouter Claude 5.5, GPT-6, Gemini 4 Argon / 3.8 Flash, DeepSeek V4.1; MiniMax drops M2.5/M2.7 (retiring Nov 18 2026). Gemini keeps the rolling -latest aliases (issue #22), which iOS doesn't use. - ReasoningEffort none..max with each model's accepted levels; the stored effort is dropped when the selected model rejects it (an HTTP 400 on every turn otherwise). OpenRouter models outside the presets use the live `reasoning.supported_efforts`. - Gemini sends the level as thinkingConfig.thinkingLevel (3+ only; 2.x rejects it). - Prices for the new models, checked against OpenRouter's live listing.
- A cancelled or superseded model-list fetch could land after a newer one (CancellationException was swallowed into a Failure, and Refresh wasn't cancelled on a key change). One load job at a time now, and cancellation propagates. - Offline / backend-proxy modes send OpenAI requests; they resolve reasoning effort by the OpenAI rules again instead of always dropping it. - Gemini page tokens are URL-encoded. - MiniMax presets are never flagged as unlisted (whether /v1/models lists the -highspeed variants is unverified); a retired M2.5/M2.7 choice still is.
…ng them Reported on an unknown build and phone: the Activity tile cut off steps and distance and lost the calories row. The tile already sizes to its content (6be5d1c), but nothing guaranteed it when the grid row ends up shorter than the values column. The column now reports its full height as before, and if it still gets less, it is drawn scaled down to fit (anchored left, centred) rather than clipped. The rings already shrink to the height they get.
…e's loading state The superseded job's finally could run after its replacement had set loading, so the picker showed the old status (and a Refresh button) while the new fetch was still in flight.
# Conflicts: # docs/ios-sync.md
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.
Ports iOS #140 (coach model catalog,
8833ee0) and adds what the iOS change doesn't have: the coach now reads each provider's own model list instead of relying only on preset model names that go stale.Live model lists
Once a cloud provider is selected and its key is saved, Settings fetches the provider's model list (
CoachModelCatalog):GET /v1/modelsGET v1beta/models(paged, key in header)generateContent, with display namesGET /api/v1/modelsGET /v1/modelsThe picker shows the curated presets first, then "All models from …" with a filter (OpenRouter lists ~390 tool-capable models). Under the picker:
The listing is advisory: the stored model stays a free string, and the presets are the fallback when the list can't be fetched. It is cached in memory for 6 h, keyed per provider and key. All four endpoints were checked to exist; OpenRouter's fields were read from the live response.
From iOS #140
ReasoningEffort, none to max). The Reasoning picker lists only the levels the selected model accepts. The stored level is dropped when the model rejects it (ReasoningEfforts.resolve, applied inCoachClientResolver.coachSettingsand meal analysis), because an unsupported level is an HTTP 400 on every turn. OpenRouter models outside the presets use the livereasoning.supported_efforts. The picker is hidden for MiniMax and local, whose clients send noreasoningfield.thinkingConfig.thinkingLevel, only for Gemini 3 and later, since 2.x rejects it. Before this, Android ignored the setting for Gemini.Android differences
-latestaliases (issue Chat Issues Gemini Model Old #22) instead of iOS's versioned slugs.thoughtSignature: Android already echoes it back.Review
An independent read-only review found no blocking issues. Fixed in
189fecf:Left as is, and noted in the code: for a custom OpenRouter model, the live effort levels apply only after Settings has fetched the list in that session. Until then the stored effort is sent unchanged, as before.
Also: Today Activity tile clipping (
4621312)A user reported that the Activity tile cut off steps and distance and dropped calories. We don't know their build or phone, and I couldn't reproduce it: the tile has sized to its content since
6be5d1c. So the values column now makes clipping impossible. It still reports its full height so the tile grows when it can; if it gets less space anyway, it's drawn scaled down to fit instead of cut off. Not checked on a device.Tests
./gradlew :app:testDebugUnitTest: 1519 tests, 0 failures. NewCoachModelCatalogTestcovers:GeminiClientTestcoversthinkingLevel. Existing fixtures were updated for the new defaults.Not checked on a device, and no live call was made with a real key.
🤖 Generated with Claude Code