import type { ClaudeJobKind, ClaudeJobStatus, VerifyResult } from '@prisma/client' export type JobWithRelations = { id: string kind: ClaudeJobKind status: ClaudeJobStatus taskCode: string | null taskTitle: string | null ideaCode: string | null ideaTitle: string | null sprintGoal: string | null sprintCode: string | null productName: string productCode: string | null storyCode: string | null pbiCode: string | null modelId: string | null inputTokens: number | null outputTokens: number | null cacheReadTokens: number | null cacheWriteTokens: number | null costUsd: number | null branch: string | null prUrl: string | null error: string | null summary: string | null description: string | null verifyResult: VerifyResult | null startedAt: Date | null finishedAt: Date | null createdAt: Date sprintRunId: string | null } export const JOB_INCLUDE = { task: { select: { code: true, title: true, description: true, implementation_plan: true, story: { select: { code: true, pbi: { select: { code: true } } } }, }, }, idea: { select: { code: true, title: true, description: true, grill_md: true, plan_md: true } }, product: { select: { name: true, code: true } }, sprint_run: { include: { sprint: { select: { sprint_goal: true, code: true } } } }, } as const export type RawJob = { id: string kind: ClaudeJobKind status: ClaudeJobStatus model_id: string | null input_tokens: number | null output_tokens: number | null cache_read_tokens: number | null cache_write_tokens: number | null branch: string | null pr_url: string | null error: string | null summary: string | null verify_result: VerifyResult | null started_at: Date | null finished_at: Date | null created_at: Date sprint_run_id: string | null task: { code: string | null title: string description: string | null implementation_plan: string | null story: { code: string; pbi: { code: string } } | null } | null idea: { code: string | null title: string description: string | null grill_md: string | null plan_md: string | null } | null product: { name: string; code: string | null } sprint_run: { sprint: { sprint_goal: string; code: string } } | null } export type PriceRow = { model_id: string input_price_per_1m: { toString: () => string } output_price_per_1m: { toString: () => string } cache_read_price_per_1m: { toString: () => string } cache_write_price_per_1m: { toString: () => string } } export function buildPriceMap(prices: PriceRow[]): Map { return new Map(prices.map((p) => [p.model_id, p])) } export function pickDescription(j: RawJob): string | null { switch (j.kind) { case 'TASK_IMPLEMENTATION': return j.task?.implementation_plan ?? j.task?.description ?? null case 'IDEA_GRILL': return j.idea?.grill_md ?? j.idea?.description ?? null case 'IDEA_MAKE_PLAN': return j.idea?.plan_md ?? j.idea?.description ?? null case 'PLAN_CHAT': return j.idea?.description ?? null case 'SPRINT_IMPLEMENTATION': return null default: return null } } export function computeCost(j: RawJob, priceMap: Map): number | null { if (!j.model_id) return null const p = priceMap.get(j.model_id) if (!p || j.input_tokens == null) return null return ( ((j.input_tokens ?? 0) * Number(p.input_price_per_1m.toString())) / 1_000_000 + ((j.output_tokens ?? 0) * Number(p.output_price_per_1m.toString())) / 1_000_000 + ((j.cache_read_tokens ?? 0) * Number(p.cache_read_price_per_1m.toString())) / 1_000_000 + ((j.cache_write_tokens ?? 0) * Number(p.cache_write_price_per_1m.toString())) / 1_000_000 ) } export function mapJob(j: RawJob, priceMap: Map): JobWithRelations { return { id: j.id, kind: j.kind, status: j.status, taskCode: j.task?.code ?? null, taskTitle: j.task?.title ?? null, ideaCode: j.idea?.code ?? null, ideaTitle: j.idea?.title ?? null, sprintGoal: j.sprint_run?.sprint.sprint_goal ?? null, sprintCode: j.sprint_run?.sprint.code ?? null, productName: j.product.name, productCode: j.product.code ?? null, storyCode: j.task?.story?.code ?? null, pbiCode: j.task?.story?.pbi?.code ?? null, modelId: j.model_id, inputTokens: j.input_tokens, outputTokens: j.output_tokens, cacheReadTokens: j.cache_read_tokens, cacheWriteTokens: j.cache_write_tokens, costUsd: computeCost(j, priceMap), branch: j.branch, prUrl: j.pr_url, error: j.error, summary: j.summary, description: pickDescription(j), verifyResult: j.verify_result, startedAt: j.started_at, finishedAt: j.finished_at, createdAt: j.created_at, sprintRunId: j.sprint_run_id, } }