fix(assistant): optional unreachable providers degrade health; add provider setup state #62

Merged
KofTwentyTwo merged 1 commits from feature/assistant-optional-provider-health into develop 2026-10-16 16:51:20 +00:00
KofTwentyTwo commented 2026-10-16 16:44:18 +00:00 (Migrated from github.com)

Why

On kof22 the Admin > Health component assistant showed DOWN because provider 2, "lore (local GPU)", is enabled but unreachable while the owner is still building it. It is not the default; Claude is the default and is UP. Central health rolls a component up as the worst of its registered checks: any DOWN makes the component DOWN, and only INFO severity caps DOWN at DEGRADED, so WARNING changes nothing. The DOWN came from assistant.provider.2 and assistant.model.1..4.

What

  • Optional providers degrade. A provider is optional when it is not the default and not the only route for any configured routing. "Configured routing" means an enabled profile's simple/complex model, a budget downgrade model or a knowledge source's embedding model, where that model has no fallback on another usable provider. The fallback can be an explicit fallback chain, or for LOCAL providers another LOCAL provider of the same type (the same pool FallbackChain uses).
    • When an optional provider is unreachable, its provider and model checks report DEGRADED with a message such as: Provider 'lore (local GPU)' is unreachable; it is optional (not the default and not the only route), so requests use 'Claude'.
    • The default provider and sole routes stay DOWN, with details required=<reason>.
    • If the routes cannot be read, the outcome stays DOWN.
  • The breaker still refuses unreachable providers. A failed provider probe carries reachable=false. ProviderHealth.beforeCall now refuses a provider whose fresh central outcome is DEGRADED but unreachable. Before this change it trusted any DEGRADED outcome.
  • Setup state. New assistant_provider.setup_state field, labelled "Setup state", with values Active and Setting up; empty counts as Active. It is a normal editable field in the Provider section, with help in the bundle.
    • A provider that is Setting up is never routed to: not as the default (even if marked default), and not through profile models, fallbacks, budget downgrades or embeddings.
    • Its provider and model health checks are not supplied, so they are unregistered and excluded from the rollup. A check resolved before the change reports UP "Not set up yet; checks are skipped".
    • Test connection and Discover models still work on it.
  • DDL: a guarded ADD COLUMN IF NOT EXISTS block at the end of docs/schema-postgres.sql, plus UPGRADE.md and CHANGELOG entries. Hosts must apply it before deploying.

No frontend change: the field comes from metadata.

Tests

AssistantHealthChecksTest has 6 new tests:

  • an optional provider is DEGRADED while the default is DOWN
  • model checks follow their provider
  • sole routes (profile routing, budget downgrade, and the last usable provider) stay DOWN, and an explicit fallback makes the provider optional again
  • the local pool counts as a fallback
  • Setting up skips checks and routing, skips a "default" provider that is Setting up, and rejects invalid values
  • pass-through cases

./mvnw verify is green locally, including SchemaForeignKeyPostgresTest running the DDL on Postgres.

🤖 Generated with Claude Code

## Why On kof22 the Admin > Health component `assistant` showed DOWN because provider 2, "lore (local GPU)", is enabled but unreachable while the owner is still building it. It is not the default; Claude is the default and is UP. Central health rolls a component up as the worst of its registered checks: any DOWN makes the component DOWN, and only `INFO` severity caps DOWN at DEGRADED, so `WARNING` changes nothing. The DOWN came from `assistant.provider.2` and `assistant.model.1..4`. ## What - **Optional providers degrade.** A provider is optional when it is not the default and not the only route for any configured routing. "Configured routing" means an enabled profile's simple/complex model, a budget downgrade model or a knowledge source's embedding model, where that model has no fallback on another usable provider. The fallback can be an explicit fallback chain, or for LOCAL providers another LOCAL provider of the same type (the same pool `FallbackChain` uses). - When an optional provider is unreachable, its provider and model checks report **DEGRADED** with a message such as: `Provider 'lore (local GPU)' is unreachable; it is optional (not the default and not the only route), so requests use 'Claude'`. - The default provider and sole routes stay **DOWN**, with details `required=<reason>`. - If the routes cannot be read, the outcome stays DOWN. - **The breaker still refuses unreachable providers.** A failed provider probe carries `reachable=false`. `ProviderHealth.beforeCall` now refuses a provider whose fresh central outcome is DEGRADED but unreachable. Before this change it trusted any DEGRADED outcome. - **Setup state.** New `assistant_provider.setup_state` field, labelled "Setup state", with values Active and Setting up; empty counts as Active. It is a normal editable field in the Provider section, with help in the bundle. - A provider that is **Setting up** is never routed to: not as the default (even if marked default), and not through profile models, fallbacks, budget downgrades or embeddings. - Its provider and model health checks are not supplied, so they are unregistered and excluded from the rollup. A check resolved before the change reports UP "Not set up yet; checks are skipped". - Test connection and Discover models still work on it. - **DDL:** a guarded `ADD COLUMN IF NOT EXISTS` block at the end of `docs/schema-postgres.sql`, plus UPGRADE.md and CHANGELOG entries. Hosts must apply it before deploying. No frontend change: the field comes from metadata. ## Tests `AssistantHealthChecksTest` has 6 new tests: - an optional provider is DEGRADED while the default is DOWN - model checks follow their provider - sole routes (profile routing, budget downgrade, and the last usable provider) stay DOWN, and an explicit fallback makes the provider optional again - the local pool counts as a fallback - Setting up skips checks and routing, skips a "default" provider that is Setting up, and rejects invalid values - pass-through cases `./mvnw verify` is green locally, including `SchemaForeignKeyPostgresTest` running the DDL on Postgres. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.