What
The per-user default-model role vocabulary is declared in four places and no two agree.
| where |
roles |
frontend/src/features/chat-ai-settings/types.ts:42 |
chat, composer, planner, embedding, rerank, critic |
frontend/src/features/settings/api.ts:99-113 |
chat, composer, planner, embedding, rerank |
DefaultModelsCard.tsx — rows actually rendered |
chat, composer, planner, rerank |
backend get_default_model(...) |
chat, composer, embedding, critic, distill |
- resolved but never settable:
critic, distill
- settable but never resolved:
planner, rerank
- declared but no row:
embedding
Why it matters
services/chat-service/app/routers/evaluate.py:171-180 refuses to score and tells the user, verbatim:
"Set a critic model in Settings › Chat & AI › default models."
That row does not exist. The instruction the product gives cannot be followed.
services/chat-service/app/routers/internal.py:570-571 falls distill back to chat the same silent way, so a reasoning model is handed a distill job and returns a blank completion.
Impact on the suite
Two E2E results were attributed to a hardware limit ("only one strong model fits in memory") that is not the cause:
composition-generate — SKIPPED, needs a distinct critic
assistant-endofday — FAILS, needs a non-reasoning distill model
Both are role-resolution failures, not memory failures.
Shape of the fix
One authoritative role list, and one settings page that renders every role in it, each defaulting to not set → fall back. The backend already has this shape (user_default_models keyed by role, resolved server-side); what is missing is a single source of truth for the role list and a surface that covers it. 53 frontend components currently pick a model independently.
What
The per-user default-model role vocabulary is declared in four places and no two agree.
frontend/src/features/chat-ai-settings/types.ts:42frontend/src/features/settings/api.ts:99-113DefaultModelsCard.tsx— rows actually renderedget_default_model(...)critic,distillplanner,rerankembeddingWhy it matters
services/chat-service/app/routers/evaluate.py:171-180refuses to score and tells the user, verbatim:That row does not exist. The instruction the product gives cannot be followed.
services/chat-service/app/routers/internal.py:570-571fallsdistillback tochatthe same silent way, so a reasoning model is handed a distill job and returns a blank completion.Impact on the suite
Two E2E results were attributed to a hardware limit ("only one strong model fits in memory") that is not the cause:
composition-generate— SKIPPED, needs a distinct criticassistant-endofday— FAILS, needs a non-reasoning distill modelBoth are role-resolution failures, not memory failures.
Shape of the fix
One authoritative role list, and one settings page that renders every role in it, each defaulting to not set → fall back. The backend already has this shape (
user_default_modelskeyed by role, resolved server-side); what is missing is a single source of truth for the role list and a surface that covers it. 53 frontend components currently pick a model independently.