Files
kite/.planning/phases/07-redo-and-optimize-llm-integration/07-05-SUMMARY.md
T
curo1305 1f0808c303 docs(07-05): add plan summary — Tasks 1-3 complete, Task 4 human checkpoint pending
- Backend GET/PUT /api/admin/ai-config + test-connection + load_provider_config_by_id
- Frontend getAiConfig/saveAiConfig/testAiConnection helpers + AdminAiConfigTab system section
- DocumentCard classification_failed badge + Re-analyze button
- 131 frontend tests pass, 369 backend tests pass
2026-06-04 23:24:09 +02:00

8.3 KiB

phase, plan, subsystem, tags, dependency_graph, tech_stack, key_files, decisions, metrics
phase plan subsystem tags dependency_graph tech_stack key_files decisions metrics
07-redo-and-optimize-llm-integration 05 ai-admin-frontend
admin
ai-providers
frontend
classification-failed
vitest
requires provides affects
07-04
GET/PUT /api/admin/ai-config
GET /api/admin/ai-config/test-connection
load_provider_config_by_id
AdminAiConfigTab System AI Providers section
DocumentCard classification_failed badge + Re-analyze button
backend/api/admin.py
backend/services/ai_config.py
frontend/src/api/client.js
frontend/src/components/admin/AdminAiConfigTab.vue
frontend/src/components/documents/DocumentCard.vue
added patterns
_ai_config_to_dict whitelist helper (mirrors _user_to_dict pattern)
atomic UPDATE SET is_active = (provider_id = target) for single-active invariant
write-only API key field (never pre-filled from server)
Vue accordion with formByProvider reactive map
created modified
backend/tests/test_admin_ai_config.py
frontend/tests/api.spec.js
frontend/tests/DocumentCard.spec.js
backend/api/admin.py
backend/services/ai_config.py
frontend/src/api/client.js
frontend/src/components/admin/AdminAiConfigTab.vue
frontend/src/components/documents/DocumentCard.vue
test-connection endpoint returns 200 ok=false for provider failures (not 5xx) so UI always gets structured status
GET /ai-config synthesises stub entries for all 10 PROVIDER_DEFAULTS providers even before any DB rows exist
api_key field in PUT: None=no change, ''=clear, non-empty=encrypt+store (three-way semantics)
AdminAiConfigTab PROVIDER_DEFAULTS mirrored in frontend JS — no /api/ai/defaults endpoint needed
duration completed tasks_completed tasks_total files_changed
~45 minutes 2026-06-04T21:23:15Z 3 3 7

Phase 7 Plan 05: Admin AI Providers Panel + Classification Failed Badge Summary

Admin AI provider configuration panel (GET/PUT/test-connection) with api_key_enc whitelist + atomic is_active flip; DocumentCard classification-failed badge driving POST /classify re-queue.

Tasks Completed

Task Name Commit Files
1 Admin AI-config backend + load_provider_config_by_id e678930 backend/api/admin.py, backend/services/ai_config.py, backend/tests/test_admin_ai_config.py
2 Frontend client helpers + AdminAiConfigTab system section + Vitest 0db412d frontend/src/api/client.js, frontend/src/components/admin/AdminAiConfigTab.vue, frontend/tests/api.spec.js
3 DocumentCard classification_failed badge + Re-analyze + Vitest c45d9e4 frontend/src/components/documents/DocumentCard.vue, frontend/tests/DocumentCard.spec.js

What Was Built

Task 1 — Backend admin AI-config endpoints

backend/services/ai_config.py gained load_provider_config_by_id(session, provider_id) — mirrors load_provider_config but filters by provider_id only (no is_active check), so the test-connection endpoint can test inactive rows.

backend/api/admin.py received:

  • _ai_config_to_dict(row) — whitelist helper returning {provider_id, base_url, model_name, context_chars, is_active, has_api_key, updated_at}. Explicitly excludes api_key_enc (T-07-01).
  • SystemAiConfigUpdate(BaseModel) — Pydantic model with extra="forbid" and a provider_id validator against PROVIDER_DEFAULTS keys (T-07-13).
  • GET /api/admin/ai-config — returns all 10 providers (DB rows + synthesised stubs from PROVIDER_DEFAULTS for unconfigured providers). Never exposes api_key_enc.
  • PUT /api/admin/ai-config — upsert with three-way api_key semantics (None=no change, ""=clear, non-empty=HKDF-encrypt). Atomic is_active flip via single UPDATE SET is_active = (provider_id = :target). Writes audit log with fields_changed only (T-07-14). Requires get_current_admin (T-07-15).
  • GET /api/admin/ai-config/test-connection?provider_id=X — calls load_provider_config_by_id, builds provider, calls health_check(). Returns {ok: bool} as 200 — never 5xx for provider failures.

backend/tests/test_admin_ai_config.py promoted from xfail stubs to three passing tests:

  • test_get_never_returns_key — asserts api_key_enc and plaintext key absent from GET response
  • test_put_writes_active_provider — asserts exactly 1 row with is_active=True after two PUTs
  • test_put_admin_only — asserts non-admin PUT returns 403

Task 2 — Frontend client helpers + AdminAiConfigTab system section

frontend/src/api/client.js gained:

  • getAiConfig() — GET /api/admin/ai-config
  • saveAiConfig(body) — PUT with JSON body
  • testAiConnection(providerId) — GET with encodeURIComponent-encoded provider_id

frontend/src/components/admin/AdminAiConfigTab.vue extended:

  • New <section> "System AI Providers (Global)" placed above the existing per-user assignment table
  • Per-provider accordion (all 10 from PROVIDER_DEFAULTS)
  • Write-only API key field (placeholder "(unchanged)" or "(not set)" — never pre-filled)
  • Base URL, model name, context_chars inputs with PROVIDER_DEFAULTS as placeholders
  • "Set Active", "Save", "Test Connection" buttons per provider
  • Inline OK/Failed badges for test results (auto-clear after 3s)
  • Existing users, configs, saveConfig, providers variables and per-user table left completely untouched (Pitfall 6 compliance)

frontend/tests/api.spec.js — 4 Vitest tests verifying fetch URL, method, headers, body, and encodeURIComponent for provider_id.

Task 3 — DocumentCard classification_failed badge

frontend/src/components/documents/DocumentCard.vue:

  • Red pill badge "Classification failed" when doc.status === 'classification_failed'
  • "Re-analyze" button with @click.stop (does not open doc on click)
  • On click: calls classifyDocument(props.doc.id), emits 'reclassified' upward
  • Spinner text "Re-analyzing…" while in flight; resets after 500ms
  • Existing v-if="doc.is_shared" block preserved unchanged

frontend/tests/DocumentCard.spec.js — 4 Vitest tests: badge renders for classification_failed, no badge for ready/processing, and reanalyze() calls classifyDocument with correct id and emits reclassified.

Test Results

  • Backend: 369 passed, 6 skipped, 7 xfailed (1 pre-existing test_extractor.py ModuleNotFoundError excluded)
  • Frontend: 131 passed (16 test files), build exits 0

Deviations from Plan

None — plan executed exactly as written. The existing AiConfigUpdate per-user model was renamed to UserAiConfigUpdate to avoid conflict with the new SystemAiConfigUpdate. This is a clean rename with no behavioral change (the PATCH /users/{user_id}/ai-config endpoint is unaffected).

Threat Mitigations Applied

Threat ID Mitigation
T-07-01 _ai_config_to_dict whitelist; test asserts api_key_enc never in GET response
T-07-03 Single UPDATE SET is_active = (provider_id = target) atomic flip; test asserts COUNT=1
T-07-13 extra="forbid" on SystemAiConfigUpdate; provider_id validated against PROVIDER_DEFAULTS
T-07-14 Audit metadata contains only provider_id + fields_changed list, never api_key value
T-07-15 All 3 endpoints carry Depends(get_current_admin); test asserts 403 for regular users

Known Stubs

None — all features are fully wired.

Threat Flags

None — no new security-relevant surface beyond what is described in the plan's threat model.

Self-Check: PASSED

Files exist:

  • backend/api/admin.py — contains _ai_config_to_dict, SystemAiConfigUpdate, GET /ai-config, PUT /ai-config, test-connection
  • backend/services/ai_config.py — contains load_provider_config_by_id
  • frontend/src/api/client.js — contains getAiConfig, saveAiConfig, testAiConnection
  • frontend/src/components/admin/AdminAiConfigTab.vue — contains "System AI Providers"
  • frontend/src/components/documents/DocumentCard.vue — contains classification_failed, Re-analyze
  • frontend/tests/api.spec.js — exists
  • frontend/tests/DocumentCard.spec.js — exists

Commits exist:

  • e678930 — Task 1 (backend endpoints)
  • 0db412d — Task 2 (frontend client + AdminAiConfigTab)
  • c45d9e4 — Task 3 (DocumentCard + Vitest)