Skip to content

feat: AI server status in the sidebar - #276

Open
TheBlackSide243 wants to merge 3 commits into
Anyesh:mainfrom
TheBlackSide243:pr/ai-status
Open

TheBlackSide243 wants to merge 3 commits into
Anyesh:mainfrom
TheBlackSide243:pr/ai-status

Conversation

@TheBlackSide243

@TheBlackSide243 TheBlackSide243 commented Oct 18, 2026 •

Copy link
Copy Markdown

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/status reuses the existing health check and lists the configured models the
    server does not have; cached for 20 s so the sidebar polling stays cheap.
  • Translations for all 8 locales (nav namespace).

This is one of a set of small, independent PRs (each branches from main and can be reviewed or
dropped on its own); they only overlap in the nav/router registration lines.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Refactoring (no functional changes)
  • CI/CD or build changes

Checklist

  • I have read the CONTRIBUTING guide
  • My code follows the project's coding style
  • I have added tests that prove my fix/feature works
  • New and existing tests pass locally
  • I have updated documentation as needed
  • My changes don't introduce new warnings or errors

Testing

  • Backend: ruff check, ruff format --check, migration heads check and the full pytest suite on top of current main (2029 passed).
  • Frontend: npm run lint, i18n:check, vocab:check, tsc --noEmit and vitest (693 passed).
  • Running on my own self-hosted instance (Docker Compose on TrueNAS, Ollama).

Test Environment

  • Docker Compose
  • Kubernetes
  • Local development

🤖 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:

  • Add an authenticated AI status endpoint that reports server availability and missing configured models for each user.
  • Display the AI status in desktop and mobile sidebars with localized labels and tooltip details.
  • Support user-specific AI endpoint configuration when checking service health.

Enhancements:

  • Include optional Ollama in-memory model information without treating unloaded models as unavailable.
  • Cache backend status checks to reduce repeated health probes while sidebar polling.

Tests:

  • Add coverage for AI status classification, model rotations, disabled capabilities, Ollama loading information, authentication, and user-specific endpoints.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sourcery-ai

sourcery-ai Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Reviewer's Guide

Implements 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 polling

sequenceDiagram
    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
Loading

State diagram for AI server status indicator

stateDiagram-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
Loading

File-Level Changes

Change Details Files
Adds an authenticated, cached AI status API that derives server availability and configured-model presence from the existing health check.
  • Introduces status values for online, missing models, offline, and disabled AI configurations.
  • Matches configured Ollama model names against available tagged or untagged names.
  • Caches health results for 20 seconds and registers the new endpoint.
  • Adds unit and endpoint coverage for status summarization and authentication.
backend/app/api/ai_status.py
backend/app/api/router.py
backend/tests/test_ai_status.py
Adds a React Query client hook and sidebar status indicator with polling, visual state colors, and model details in a tooltip.
  • Polls the authenticated status endpoint every minute with a dedicated query key.
  • Renders the indicator in desktop and mobile sidebars, including an animated online state.
  • Displays localized labels and contextual details for missing, configured, or unknown models.
frontend/components/ai-status-dot.tsx
frontend/components/mobile-sidebar.tsx
frontend/components/sidebar.tsx
frontend/lib/hooks/query-keys.ts
frontend/lib/hooks/use-ai-status.ts
Adds localized navigation strings for the AI status indicator across all supported locales.
  • Provides translations for each status label and tooltip detail.
frontend/messages/de/nav.json
frontend/messages/en/nav.json
frontend/messages/fr/nav.json
frontend/messages/it/nav.json
frontend/messages/ja/nav.json
frontend/messages/ko/nav.json
frontend/messages/zh-CN/nav.json
frontend/messages/zh-TW/nav.json

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@github-actions github-actions Bot added javascript Pull requests that update javascript code python Pull requests that update python code labels Oct 18, 2026

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread backend/app/api/ai_status.py Outdated
)
endpoint = healthy[0]
available = endpoint.get("available_models") or []
wanted = [m for m in (endpoint.get("vision_model"), endpoint.get("text_model")) if m]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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)})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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.

TheBlackSide243 and others added 2 commits October 18, 2026 22:21
- 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

javascript Pull requests that update javascript code python Pull requests that update python code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants