( documentId: number, costCents: number, inputTokens: number, outputTokens: number, modelId?: string, )
| 274 | } |
| 275 | |
| 276 | async function recordCost( |
| 277 | documentId: number, |
| 278 | costCents: number, |
| 279 | inputTokens: number, |
| 280 | outputTokens: number, |
| 281 | modelId?: string, |
| 282 | ): Promise<void> { |
| 283 | const today = new Date().toISOString().slice(0, 10); |
| 284 | const config = getModelConfig(modelId); |
| 285 | |
| 286 | await db.insert(budgetTracking).values({ |
| 287 | date: today, |
| 288 | model: config.model, |
| 289 | inputTokens, |
| 290 | outputTokens, |
| 291 | costCents: Math.round(costCents * 100) / 100, |
| 292 | documentId, |
| 293 | jobType: "ai_analysis", |
| 294 | }); |
| 295 | |
| 296 | // Update per-document cost |
| 297 | const existing = await db |
| 298 | .select({ aiCostCents: documents.aiCostCents }) |
| 299 | .from(documents) |
| 300 | .where(eq(documents.id, documentId)) |
| 301 | .limit(1); |
| 302 | |
| 303 | const currentCost = existing[0]?.aiCostCents ?? 0; |
| 304 | await db |
| 305 | .update(documents) |
| 306 | .set({ aiCostCents: currentCost + Math.ceil(costCents) }) |
| 307 | .where(eq(documents.id, documentId)); |
| 308 | } |
| 309 | |
| 310 | function formatETA(progress: BatchProgress): string { |
| 311 | if (progress.completed === 0) return "calculating..."; |
no test coverage detected