Repository navigation
feat: AI server status in the sidebar - #276
TheBlackSide243 wants to merge 3 commits into
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewer's GuideImplements an authenticated AI health-status endpoint backed by cached existing health checks, then surfaces its online, missing-model, offline, or disabled state in desktop and mobile sidebars with localized labels and tooltip details. Sequence diagram for sidebar AI status pollingsequenceDiagram
actor User
participant Sidebar
participant ReactQuery
participant API
participant AIService
User->>Sidebar: Open authenticated app
Sidebar->>ReactQuery: useAIStatus()
ReactQuery->>API: GET /api/v1/ai/status
API->>API: get_settings()
alt AI disabled
API-->>ReactQuery: AIStatusResponse(status=disabled)
else Status cache valid
API-->>ReactQuery: Cached AIStatusResponse
else Cache expired
API->>AIService: check_health()
AIService-->>API: Endpoint health and available models
API->>API: summarize(raw)
API-->>ReactQuery: AIStatusResponse
end
ReactQuery-->>Sidebar: Render AIStatusDot
Sidebar-->>User: Status dot and tooltip details
ReactQuery->>API: GET /api/v1/ai/status every 60 seconds
State diagram for AI server status indicatorstateDiagram-v2
[*] --> disabled
[*] --> unknown
disabled --> disabled: AI disabled
unknown --> online: status=online
unknown --> model_missing: status=model_missing
unknown --> offline: status=offline
online --> online: healthy models available
model_missing --> model_missing: configured model unavailable
offline --> offline: no healthy endpoint
online --> model_missing: configured model becomes unavailable
model_missing --> online: all configured models available
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 3 issues
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="backend/app/api/ai_status.py" line_range="44" />
<code_context>
+ )
+ endpoint = healthy[0]
+ available = endpoint.get("available_models") or []
+ wanted = [m for m in (endpoint.get("vision_model"), endpoint.get("text_model")) if m]
+ missing = sorted({m for m in wanted if not _matches(m, available)})
+ return AIStatusResponse(
</code_context>
<issue_to_address>
**Disabled capability causes false warning**
When only one AI capability is enabled and the configured model for the disabled capability is unavailable, `summarize()` includes both configured models in `wanted` regardless of which capabilities are enabled, so the disabled capability’s missing model sets `model_missing` and the sidebar shows a false warning.
Include only models for enabled AI capabilities when computing `missing`.
</issue_to_address>
### Comment 2
<location path="backend/app/api/ai_status.py" line_range="45" />
<code_context>
+ endpoint = healthy[0]
+ available = endpoint.get("available_models") or []
+ wanted = [m for m in (endpoint.get("vision_model"), endpoint.get("text_model")) if m]
+ missing = sorted({m for m in wanted if not _matches(m, available)})
+ return AIStatusResponse(
+ status="model_missing" if missing else "online",
</code_context>
<issue_to_address>
**Model rotations appear missing**
When either configured model setting contains multiple comma-separated rotation models, `_matches()` compares the entire comma-separated configuration value as one model name, so a server with any of the configured rotation models is reported as missing them all; the sidebar shows the wrong status and model detail.
Parse configured rotation lists and report missing models only when none of the configured alternatives is available.
</issue_to_address>
### Comment 3
<location path="backend/tests/test_ai_status.py" line_range="61" />
<code_context>
+ ):
+ response = await client.get("/api/v1/ai/status", headers=auth_headers)
+ ai_status._cache.clear()
+ assert response.status_code == 200
+ assert response.json()["status"] in ("offline", "disabled")
</code_context>
<issue_to_address>
**Disabled status passes enabled test**
When the endpoint returns `disabled` even though AI is enabled, `test_endpoint_needs_sign_in_and_answers` accepts `disabled` at line 62 and never asserts that the mocked `check_health` is called, so an endpoint that always reports disabled passes even with AI enabled.
Set AI enabled explicitly in this test, assert the health check is called, and require the expected `offline` status.
</issue_to_address>Sourcery assessment
Approval pending. 3 findings to address first.
Blocking findings: backend/app/api/ai_status.py:44, backend/app/api/ai_status.py:45, backend/tests/test_ai_status.py:61
| ) | ||
| endpoint = healthy[0] | ||
| available = endpoint.get("available_models") or [] | ||
| wanted = [m for m in (endpoint.get("vision_model"), endpoint.get("text_model")) if m] |
There was a problem hiding this comment.
🟡 Medium · Disabled capability causes false warning
When only one AI capability is enabled and the configured model for the disabled capability is unavailable, summarize() includes both configured models in wanted regardless of which capabilities are enabled, so the disabled capability’s missing model sets model_missing and the sidebar shows a false warning.
Include only models for enabled AI capabilities when computing missing.
Prompt for AI agents
In `backend/app/api/ai_status.py` at line 44:
**Disabled capability causes false warning**
When only one AI capability is enabled and the configured model for the disabled capability is unavailable, `summarize()` includes both configured models in `wanted` regardless of which capabilities are enabled, so the disabled capability’s missing model sets `model_missing` and the sidebar shows a false warning.
Include only models for enabled AI capabilities when computing `missing`.| endpoint = healthy[0] | ||
| available = endpoint.get("available_models") or [] | ||
| wanted = [m for m in (endpoint.get("vision_model"), endpoint.get("text_model")) if m] | ||
| missing = sorted({m for m in wanted if not _matches(m, available)}) |
There was a problem hiding this comment.
🟡 Medium · Model rotations appear missing
When either configured model setting contains multiple comma-separated rotation models, _matches() compares the entire comma-separated configuration value as one model name, so a server with any of the configured rotation models is reported as missing them all; the sidebar shows the wrong status and model detail.
Parse configured rotation lists and report missing models only when none of the configured alternatives is available.
Prompt for AI agents
In `backend/app/api/ai_status.py` at line 45:
**Model rotations appear missing**
When either configured model setting contains multiple comma-separated rotation models, `_matches()` compares the entire comma-separated configuration value as one model name, so a server with any of the configured rotation models is reported as missing them all; the sidebar shows the wrong status and model detail.
Parse configured rotation lists and report missing models only when none of the configured alternatives is available.| ): | ||
| response = await client.get("/api/v1/ai/status", headers=auth_headers) | ||
| ai_status._cache.clear() | ||
| assert response.status_code == 200 |
There was a problem hiding this comment.
🟡 Medium · Disabled status passes enabled test
When the endpoint returns disabled even though AI is enabled, test_endpoint_needs_sign_in_and_answers accepts disabled at line 62 and never asserts that the mocked check_health is called, so an endpoint that always reports disabled passes even with AI enabled.
Set AI enabled explicitly in this test, assert the health check is called, and require the expected offline status.
Prompt for AI agents
In `backend/tests/test_ai_status.py` at line 61:
**Disabled status passes enabled test**
When the endpoint returns `disabled` even though AI is enabled, `test_endpoint_needs_sign_in_and_answers` accepts `disabled` at line 62 and never asserts that the mocked `check_health` is called, so an endpoint that always reports disabled passes even with AI enabled.
Set AI enabled explicitly in this test, assert the health check is called, and require the expected `offline` status.- A model setting that lists several models (a rotation) counts as present when any of them is. - The model of a switched-off capability is not reported missing. - The endpoint test enables AI, requires 'offline' and checks the probe ran. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Settings > AI lets each user pick their own endpoints and models; tagging, suggestions and pairings already honour them. This feature now does too, through services/user_ai.ai_service_for, with the server's AI_* settings as the fallback. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Description
Adds a small status dot at the bottom of the sidebar telling whether the AI server is reachable
and has the configured models (online / offline / model missing / AI turned off), with the
details in a tooltip. Useful with a self-hosted Ollama that may be asleep or missing a model.
GET /api/v1/ai/statusreuses the existing health check and lists the configured models theserver does not have; cached for 20 s so the sidebar polling stays cheap.
This is one of a set of small, independent PRs (each branches from
mainand can be reviewed ordropped on its own); they only overlap in the nav/router registration lines.
Type of Change
Checklist
Testing
ruff check,ruff format --check, migration heads check and the full pytest suite on top of currentmain(2029 passed).npm run lint,i18n:check,vocab:check,tsc --noEmitand vitest (693 passed).Test Environment
🤖 Generated with Claude Code
Summary by Sourcery
Show the reachability and model readiness of each user's configured AI service in the sidebar.
New Features:
Enhancements:
Tests: