feat: add ai-kit health tracking for both AI stacks, exposed via /api/health - #417
Merged
Conversation
…/health evig has two independent AI provider systems -- src/lib/hirn/providers (DB-backed, admin-configurable, per-user; powers Hirn chat) and src/lib/ai/providers.ts (env-based cascade; powers form-assist, protocol/task/vote advisors, smart product entry, blog translation). Both are correctly NOT adopting ai-kit's chain/model layer -- each already owns a model registry (context windows, tool/vision support, per-token cost) that ai-kit does not model, the same reasoning documented in fleet/SHARED.md for kivvi and orangecat. What both lacked was persistent health state. /api/health has checked only the database and Meilisearch since it existed -- an AI outage was invisible to it, the same blind spot that took down botsmann's chat this session (a friendly fallback and a database-only health check that both kept reporting "healthy"). - src/lib/hirn/health.ts + src/lib/ai/health.ts (new): two SEPARATE ai-kit createHealthTracker instances -- a bug isolated to one stack should show as one named service going down, not get averaged into a single "AI" bucket. - hirn/providers/index.ts: new getChatResponse() wraps getDefaultChatProvider + .chat() + health recording in one step. Three call sites (chat.ts, the public chat route, deliverables/ai.ts) used to duplicate that pair with nothing recording whether it worked; all three now call getChatResponse instead. - ai/providers.ts: callWithFallback/callVisionWithFallback already had solid cascade + error-categorization logic (Groq -> OpenRouter -> Ollama, auth/rate_limit/network/timeout reasons) -- untouched. Two lines added at each function's existing return points to record the outcome, using the already-exported buildFailureMessage for a readable lastError. - /api/health/route.ts: adds hirn and aiTools as named services, mapped onto the existing healthy/degraded/unhealthy vocabulary. "unknown" (nothing attempted since restart) reads as healthy, and -- matching the file's own existing rule for Meilisearch -- an AI outage can only ever push the overall status to degraded, never to the unhealthy/503 that's reserved for the database. A dead API key is not fixed by a restart, so it must never fail whatever gate decides whether to kill this process. - jest.config.js: ai-kit is ESM-only, Jest runs CJS -- added to both transformIgnorePatterns allowlists (the static one and next/jest's own generated one), the exact two-place fix botsmann's jest.config already documents for the same package. - One existing test (chat.test.ts) mocked getDefaultChatProvider directly; updated to mock getChatResponse, the function chat.ts actually calls now. No other existing test needed changes. - Fixes a stale catomean org reference on @fleet/ai-forms while editing this dependency block. ai-kit bumped to v0.5.0. Full verify green: eslint (whole repo), lint:umlauts, compliance:ci (SSOT/i18n/hardcoded-strings/leaks, 0 new violations), typecheck, 7775 tests (40 new), production build. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…acking # Conflicts: # package-lock.json # package.json
1 task
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
src/lib/hirn/providers(DB-backed, admin-configurable, per-user) andsrc/lib/ai/providers.ts(env-based cascade for form-assist/advisors/blog-translate). Both correctly stay OFF ai-kit's chain/model layer — each already owns a model registryai-kitdoesn't model, same reasoningfleet/SHARED.mddocuments for kivvi and orangecat./api/healthhas only ever checked database + Meilisearch — an AI outage was invisible to it, the exact blind spot that took down botsmann's chat this session.src/lib/hirn/health.ts+src/lib/ai/health.ts— two separateai-kitcreateHealthTrackerinstances (a bug isolated to one stack shows as one named service, not an averaged bucket).hirn/providers/index.ts: newgetChatResponse()consolidates 3 call sites that duplicatedgetDefaultChatProvider()+.chat()with nothing recording the outcome.ai/providers.ts:callWithFallback/callVisionWithFallback's existing cascade logic is untouched — 2 lines added at each function's existing return points./api/health/route.ts: addshirnandaiToolsas named services. An AI outage can only push overall status todegraded, neverunhealthy/503 — that's reserved for the database, since a dead API key isn't fixed by a restart.jest.config.js:ai-kitis ESM-only; added to bothtransformIgnorePatternsallowlists, the same two-place fix botsmann's jest.config already documents.catomeanorg ref on@fleet/ai-forms.Test plan
lint:umlauts,compliance:ci(0 new violations), typecheck, 7775 tests (40 new), production build