feat(phase6b): LLM analysis layer — DeepSeek + insights + cost ledger + budget guard
Schema: - insights (fingerprint dedup, occurrence_count, related_session_ids[], priority_score) - cost_ledger (per-call, cache_hit/miss/output token split + USD) - prompt_templates (versioned, per-tag, with JSON schema + tier + temperature) - budget_settings (global key/value: monthly_hard_cap_usd, daily_soft/hard, per_call_max, analysis_paused) Worker: - lib/deepseek.ts: Anthropic-compat endpoint client (deepseek-v4-flash | deepseek-v4-pro), usage→USD with V4 promo pricing, cache_read_input_tokens awareness, extractJson() helper - lib/budget.ts: checkBudget() returns active|soft_throttled|hard_paused|monthly_paused, forces flash tier on soft cap, halts on hard/monthly cap or analysis_paused - lib/prompts.ts: 5 seed templates (bug_triage P, ux_friction F, payment_issue P, onboarding_stuck F, provider_quality F) with embedded JSON schemas + Sase.tr context; pickPromptTag() maps session tags → prompt - lib/json-validate.ts: lightweight schema validator (no ajv dep) - lib/seed-runtime.ts: idempotent upsert of prompts + default budget settings on worker boot - lib/minio.ts: +getText() for compressed timeline fetch - jobs/analyze.ts: budget guard → 6h fingerprint cache (attach session to existing insight) → template lookup → severity-based tier override → DeepSeek call → JSON parse + validate → insert insight (or aggregate occurrence) → write cost_ledger - scheduler: analyze@*/4min on insight-pipeline queue UI: - /insights (was pipeline view) → now Insight Inbox: priority-sorted list w/ KPI strip (new/in_backlog/shipped/today $/month $), severity badges, link to detail - /insights/i/[id]: insight detail with structured body render, related sessions, per-session cost breakdown, raw JSON collapsible - /insights/costs: KPI cards (today, month, avg, cache hit), daily 30d bar table, by-model + by-prompt breakdowns, top 10 expensive, recent errors - /insights/pipeline: moved old session-pipeline view here - /insights/sessions/[id]: unchanged session timeline viewer Defaults: - monthly cap $30, daily soft $1.50 / hard $3, per-call $0.20, analysis_paused=false - Severity→Tier: P0/P1=pro, P2/P3/INFO=flash; budget soft-cap forces flash Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -217,3 +217,108 @@ model SessionCustomEvent {
|
||||
@@index([sessionId, timestamp])
|
||||
@@map("session_custom_events")
|
||||
}
|
||||
|
||||
// ---------- Phase 6b: LLM Analysis ----------
|
||||
|
||||
model PromptTemplate {
|
||||
id String @id @default(cuid())
|
||||
tag String
|
||||
version Int
|
||||
name String
|
||||
systemPrompt String @db.Text
|
||||
userPromptTemplate String @db.Text
|
||||
outputSchemaJson Json
|
||||
modelTier String // "flash" | "pro"
|
||||
maxOutputTokens Int @default(1500)
|
||||
temperature Float @default(0.3)
|
||||
active Boolean @default(true)
|
||||
performanceStats Json @default("{}")
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@unique([tag, version])
|
||||
@@map("prompt_templates")
|
||||
}
|
||||
|
||||
model Insight {
|
||||
id String @id @default(cuid())
|
||||
projectKey String
|
||||
type String
|
||||
severity String
|
||||
status String @default("new")
|
||||
fingerprint String
|
||||
title String
|
||||
body Json
|
||||
relatedSessionIds String[]
|
||||
occurrenceCount Int @default(1)
|
||||
uniqueUserCount Int @default(1)
|
||||
firstSeenAt DateTime
|
||||
lastSeenAt DateTime
|
||||
confidence Float?
|
||||
priorityScore Int @default(0)
|
||||
sourcePromptTag String
|
||||
sourcePromptVersion Int
|
||||
sourceModel String
|
||||
sourceCostUsd Float
|
||||
githubIssueUrl String?
|
||||
githubIssueId BigInt?
|
||||
githubIssueState String?
|
||||
shippedAt DateTime?
|
||||
validationStartedAt DateTime?
|
||||
validationPeriodDays Int @default(14)
|
||||
regressionDetected Boolean @default(false)
|
||||
validatedAt DateTime?
|
||||
founderNotes String? @db.Text
|
||||
founderSeverityOverride String?
|
||||
founderPriority Int?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@unique([projectKey, fingerprint])
|
||||
@@index([status, projectKey])
|
||||
@@index([severity, status])
|
||||
@@index([lastSeenAt(sort: Desc)])
|
||||
@@index([priorityScore(sort: Desc)])
|
||||
@@map("insights")
|
||||
}
|
||||
|
||||
model CostLedger {
|
||||
id String @id @default(cuid())
|
||||
insightId String?
|
||||
sessionId String?
|
||||
projectKey String
|
||||
promptTag String?
|
||||
promptVersion Int?
|
||||
provider String // "deepseek" | "anthropic" | "openrouter"
|
||||
model String
|
||||
tier String // "flash" | "pro"
|
||||
tokensInputCacheMiss Int
|
||||
tokensInputCacheHit Int
|
||||
tokensOutput Int
|
||||
costInputCacheMissUsd Float
|
||||
costInputCacheHitUsd Float
|
||||
costOutputUsd Float
|
||||
costTotalUsd Float
|
||||
cacheHitRatio Float
|
||||
callDurationMs Int?
|
||||
wasFallback Boolean @default(false)
|
||||
wasRetry Boolean @default(false)
|
||||
errorCode String?
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@index([createdAt])
|
||||
@@index([projectKey, createdAt])
|
||||
@@index([insightId])
|
||||
@@map("cost_ledger")
|
||||
}
|
||||
|
||||
model BudgetSetting {
|
||||
id String @id @default(cuid())
|
||||
projectKey String? // null = global
|
||||
settingKey String
|
||||
settingValue Json
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@unique([projectKey, settingKey])
|
||||
@@map("budget_settings")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user