From 5a71f9b36ab0a6435c3c4c4e3b1ae230032786ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Mon, 6 Apr 2026 00:40:05 +0200 Subject: [PATCH] refactor(insights): use provider abstraction in all 7 insight generators Replace direct OpenAI API calls with resolveProvider() in all insight generators (general, blood-pressure, weight, pulse, bmi, medication- compliance, mood). Each generator now uses domain-specific prompts from @/lib/ai/prompts/ and the AIProvider.generateCompletion() interface. hasKey -> hasProvider in return signatures and all UI consumers. Remove openaiKeyEncrypted references from data reset routes. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/app/api/admin/data/route.ts | 1 - src/app/api/insights/comprehensive/route.ts | 4 +- src/app/api/settings/data/route.ts | 1 - src/app/insights/page.tsx | 28 ++-- src/app/settings/page.tsx | 15 +- src/components/insights/insights-card.tsx | 5 +- src/lib/ai/prompts/mood.ts | 21 +++ src/lib/insights/blood-pressure-status.ts | 135 ++++------------- src/lib/insights/bmi-status.ts | 137 ++++------------- src/lib/insights/general-status.ts | 133 ++++------------- .../insights/medication-compliance-status.ts | 125 +++------------- src/lib/insights/mood-status.ts | 129 +++------------- src/lib/insights/pulse-status.ts | 141 ++++-------------- src/lib/insights/weight-status.ts | 127 +++------------- src/lib/jobs/reminder-worker.ts | 6 - 15 files changed, 217 insertions(+), 791 deletions(-) create mode 100644 src/lib/ai/prompts/mood.ts diff --git a/src/app/api/admin/data/route.ts b/src/app/api/admin/data/route.ts index b8e9e4c2c..11837eda0 100644 --- a/src/app/api/admin/data/route.ts +++ b/src/app/api/admin/data/route.ts @@ -51,7 +51,6 @@ export const DELETE = apiHandler(async (request: NextRequest) => { heightCm: null, dateOfBirth: null, gender: null, - openaiKeyEncrypted: null, insightsPrivacyMode: "aggregated", insightsCachedAt: null, insightsCachedText: null, diff --git a/src/app/api/insights/comprehensive/route.ts b/src/app/api/insights/comprehensive/route.ts index ff4b7be62..0fa7477b3 100644 --- a/src/app/api/insights/comprehensive/route.ts +++ b/src/app/api/insights/comprehensive/route.ts @@ -1,4 +1,5 @@ import { prisma } from "@/lib/db"; +import { resolveProvider } from "@/lib/ai/provider"; import { apiSuccess } from "@/lib/api-response"; import { summarize, type DataPoint } from "@/lib/analytics/trends"; import { getBpTargets } from "@/lib/analytics/bp-targets"; @@ -32,7 +33,6 @@ export const GET = apiHandler(async () => { select: { heightCm: true, dateOfBirth: true, - openaiKeyEncrypted: true, }, }); @@ -371,7 +371,7 @@ export const GET = apiHandler(async () => { moodPulseScatterData, medications: medCompliance, alerts, - hasOpenAiKey: !!dbUser?.openaiKeyEncrypted, + hasProvider: (await resolveProvider(userId)).type !== "none", dataSpanDays, totalMeasurements: allMeasurements.length, }); diff --git a/src/app/api/settings/data/route.ts b/src/app/api/settings/data/route.ts index 7f3c43082..9eface9d7 100644 --- a/src/app/api/settings/data/route.ts +++ b/src/app/api/settings/data/route.ts @@ -71,7 +71,6 @@ export const DELETE = apiHandler(async (request: NextRequest) => { heightCm: null, dateOfBirth: null, gender: null, - openaiKeyEncrypted: null, insightsPrivacyMode: "aggregated", insightsCachedAt: null, insightsCachedText: null, diff --git a/src/app/insights/page.tsx b/src/app/insights/page.tsx index 707324f0f..4dea98fed 100644 --- a/src/app/insights/page.tsx +++ b/src/app/insights/page.tsx @@ -130,49 +130,49 @@ interface AnalyticsData { } interface GeneralStatusData { - hasKey: boolean; + hasProvider: boolean; text: string | null; cached: boolean; updatedAt: string | null; } interface BloodPressureStatusData { - hasKey: boolean; + hasProvider: boolean; text: string | null; cached: boolean; updatedAt: string | null; } interface WeightStatusData { - hasKey: boolean; + hasProvider: boolean; text: string | null; cached: boolean; updatedAt: string | null; } interface PulseStatusData { - hasKey: boolean; + hasProvider: boolean; text: string | null; cached: boolean; updatedAt: string | null; } interface BmiStatusData { - hasKey: boolean; + hasProvider: boolean; text: string | null; cached: boolean; updatedAt: string | null; } interface MoodStatusData { - hasKey: boolean; + hasProvider: boolean; text: string | null; cached: boolean; updatedAt: string | null; } interface MedicationComplianceStatusData { - hasKey: boolean; + hasProvider: boolean; summary: string | null; medications: Array<{ medicationId: string; @@ -941,7 +941,7 @@ export default function InsightsPage() {

{generalStatus.text}

- ) : !generalStatus?.hasKey ? ( + ) : !generalStatus?.hasProvider ? (

{t("insights.generalStatusNoKey")}

@@ -1229,7 +1229,7 @@ export default function InsightsPage() {

{bloodPressureStatus.text}

- ) : !bloodPressureStatus?.hasKey ? ( + ) : !bloodPressureStatus?.hasProvider ? (

{t("insights.bloodPressureStatusNoKey")}

@@ -1478,7 +1478,7 @@ export default function InsightsPage() {

{weightStatus.text}

- ) : !weightStatus?.hasKey ? ( + ) : !weightStatus?.hasProvider ? (

{t("insights.weightStatusNoKey")}

@@ -1528,7 +1528,7 @@ export default function InsightsPage() {

{pulseStatus.text}

- ) : !pulseStatus?.hasKey ? ( + ) : !pulseStatus?.hasProvider ? (

{t("insights.pulseStatusNoKey")}

@@ -1573,7 +1573,7 @@ export default function InsightsPage() {

{moodStatus.text}

- ) : !moodStatus?.hasKey ? ( + ) : !moodStatus?.hasProvider ? (

{t("insights.moodStatusNoKey")}

@@ -1684,7 +1684,7 @@ export default function InsightsPage() {

{medicationComplianceStatus.summary}

- ) : !medicationComplianceStatus?.hasKey ? ( + ) : !medicationComplianceStatus?.hasProvider ? (

{t("insights.medicationComplianceStatusNoKey")}

@@ -1740,7 +1740,7 @@ export default function InsightsPage() {

{bmiStatus.text}

- ) : !bmiStatus?.hasKey ? ( + ) : !bmiStatus?.hasProvider ? (

{t("insights.bmiStatusNoKey")}

diff --git a/src/app/settings/page.tsx b/src/app/settings/page.tsx index 971901f97..2a528cd55 100644 --- a/src/app/settings/page.tsx +++ b/src/app/settings/page.tsx @@ -2640,7 +2640,8 @@ function InsightsSettingsSection({ if (!res.ok) return null; const json = await res.json(); return json.data as { - hasKey: boolean; + codexStatus: string; + hasAdminKey: boolean; privacyMode: string; lastInsightAt: string | null; }; @@ -2730,7 +2731,7 @@ function InsightsSettingsSection({

{t("settings.kiInsights")}

- {settings?.hasKey && ( + {(settings?.codexStatus === "connected" || settings?.hasAdminKey) && ( {t("settings.configured")} @@ -2775,7 +2776,7 @@ function InsightsSettingsSection({ value={apiKey} onChange={(e) => setApiKey(e.target.value)} placeholder={ - settings?.hasKey ? t("settings.apiKeyStored") : "sk-..." + (settings?.codexStatus === "connected" || settings?.hasAdminKey) ? t("settings.apiKeyStored") : "sk-..." } className="flex-1" /> @@ -2785,9 +2786,9 @@ function InsightsSettingsSection({ ) : ( )} - {settings?.hasKey ? t("common.replace") : t("common.save")} + {(settings?.codexStatus === "connected" || settings?.hasAdminKey) ? t("common.replace") : t("common.save")} - {settings?.hasKey && ( + {(settings?.codexStatus === "connected" || settings?.hasAdminKey) && (