13. Model Catalog & Selection
1. Product rule
Users must be able to use market-available models broadly, not only a curated demo subset.
Therefore:
- Catalog is refreshable
- Custom model IDs are always allowed
- OpenAI-compatible gateways are first-class
- Search is global across enabled providers
2. Selection UX
Model picker fields
- search box
- provider filter
- capability filters: tools / vision / reasoning
- sort: recent / provider / name
Item display
- model display name
- model id
- provider name
- capability badges
- optional context window
Advanced
- “Use custom model ID”
- “Refresh catalog”
3. Recent models
Persist recent selected model refs:
type RecentModelRef = {
providerId: string
modelId: string
usedAt: string
}Show top N in picker.
4. Session model binding
Each session stores:
providerIdmodelIdthinkingLevel(off|minimal|low|medium|high|xhigh|max)
Changing model or thinking level mid-session affects subsequent turns only. The stored thinking preference survives restart; the effective request level is capability-clamped for the selected model at execution time.
For a newly created session, the renderer resolves the app default provider's current default-model capability. A reasoning-capable model starts at the highest canonical level in its published supportedThinkingLevels; a non-reasoning model or missing capability metadata starts at off. This is a creation default only and never rewrites an existing session's stored choice.
5. Capability warnings
If user selects model tagged without tools while in Agent mode:
- show non-blocking warning
- do not hard-block (vendor tags may be incomplete)
6. Refresh behavior
providers.refreshModels:
- query runtime-supported discovery endpoints
- merge into catalog cache
- keep user-defined models
- return counts: added/updated/failed providers
The desktop uses stale-while-revalidate for configured providers:
- hydrate each saved provider's last catalog from Rust-owned SQLite during renderer bootstrap
- render that catalog immediately in the composer picker and saved-provider edit dialog
- perform at most one live refresh per provider per renderer lifetime
- merge a successful response into SQLite and replace the renderer snapshot
- reset the renderer refresh marker after provider configuration changes so the next picker open revalidates the endpoint
7. Offline behavior
If refresh fails / offline:
- use cached catalog
- never clear an already-rendered cached list or flash an empty picker
- allow custom model id
- still allow providers with known model ids
8. Catalog item schema
type ModelCatalogItem = {
providerId: string
vendorKey: string
modelId: string
displayName: string
source: "bundled" | "discovered" | "user" | "recent"
capabilities: Array<
| "tools"
| "vision"
| "reasoning"
| "streaming"
| "json"
| "long_context"
>
contextWindow?: number
maxOutputTokens?: number
deprecated?: boolean
notes?: string
supportedThinkingLevels?: Array<
"off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max"
>
}9. Selection resolution order
When UI/search requests models for picker:
- recent models for enabled providers
- user-defined models
- discovered/refreshed cache
- bundled snapshot
- always include "custom model id" entry action
Deduplicate by (providerId, modelId) with priority: user > discovered > bundled > recent-only.
10. Default model policy
App-level default:
- first successfully tested provider + its default/recommended model
- if none configured, onboarding checklist requires provider setup before first agent run
Session-level:
- inherits app default at creation
- initializes thinking to the highest level published by the inherited model when it supports reasoning, otherwise
off - can override independently
11. Capability gating
| mode/feature | required capability |
|---|---|
| Agent mode tools | tools (warn if missing; hard-block only if runtime cannot function) |
| image input | vision |
| reasoning UI affordances | reasoning |
| structured repair helpers | json optional |
Warnings are non-blocking unless execution is impossible.
11.1 Reasoning capability resolution
- Resolve pi catalog metadata for the exact
(vendorKey, modelId)or a separator-bounded compatible-gateway alias. - The complete pi model record is authoritative; cached/discovered model capabilities and legacy provider overrides cannot replace its reasoning flag or thinking-level map.
- A free-form id absent from pi is an unknown generic model and exposes only
off; the UI cannot promote it to reasoning-capable. - The Composer renders the selector only when the resolved pi model supports reasoning and lists only the resolved
supportedThinkingLevels. - If a stored/requested level is unavailable, choose the nearest supported level by scanning upward first and then downward. Non-reasoning models always resolve to
off. - Changing to a non-reasoning provider persists
off; no unsupported level leaks into the next request.
12. Refresh strategy
- manual refresh button in settings/model picker
- optional refresh on provider create/test success
- no aggressive background polling in MVP
- refresh failures keep previous cache and surface non-fatal error
Electron decorates cached and freshly discovered model rows with the matching pi-ai model record when one exists. Its contextWindow is the authoritative value for the picker and context inspector because the same record is passed to the agent sidecar. Provider discovery remains the fallback for models absent from the pi-ai catalog.
13. Search behavior
- case-insensitive match on displayName, modelId, provider name, vendorKey
- capability filters are AND
- provider filter is exact providerId
- empty query shows recents + popular/bundled first
14. Acceptance criteria
- [ ] search finds models across multiple providers
- [ ] custom model id path works without catalog hit
- [ ] recent models surface in the picker
- [ ] refresh merges into cache and picker (never destructively replaces)
- [ ] restart hydrates the prior catalog before live refresh, and offline refresh keeps the cached picker populated
- [ ] capability badges visible
- [ ] session model change applies to next turn only
- [ ] a new session defaults a reasoning-capable inherited model to its highest published thinking level and otherwise defaults to
off - [ ] reasoning selector is capability-gated and pi-published sparse level sets clamp the same way in Composer, Electron main, and the pi sidecar
- [ ] provider settings and cached discovery cannot override a known pi model
- [ ] unknown free-form models remain runnable without invented capabilities
- [ ] pinned pi-ai ^0.82.1+ resolves
claude-opus-5(and gateway-compatible aliases) to the published 1M-context adaptive-thinking record without desktop overrides