v0.2.8 operator dashboard with per-call audit log + cost tracking
This commit is contained in:
@@ -10,6 +10,10 @@
|
||||
// Same charge-once-per-job semantics: a Recap summarize job pairs
|
||||
// transcribe + analyze with the same X-Recap-Job-Id. The first call
|
||||
// (whichever endpoint) charges 1 credit; the second is free.
|
||||
//
|
||||
// Every outcome (success / quota-refused / backend-error) writes one
|
||||
// row to the audit log so the admin dashboard can compute cost,
|
||||
// margin, and speed metrics.
|
||||
|
||||
import express from "express";
|
||||
import { resolveLicense } from "../keysat-client.js";
|
||||
@@ -19,11 +23,14 @@ import { getConfigSnapshot, getTierQuotas } from "../config.js";
|
||||
import { createGeminiBackend } from "../backends/gemini.js";
|
||||
import { createHardwareBackend } from "../backends/hardware.js";
|
||||
import { envelope, errorEnvelope } from "./envelope.js";
|
||||
import { recordCall } from "../audit-log.js";
|
||||
import { calcGeminiCost } from "../pricing.js";
|
||||
|
||||
export function analyzeRouter() {
|
||||
const router = express.Router();
|
||||
|
||||
router.post("/analyze", express.json({ limit: "10mb" }), async (req, res) => {
|
||||
const t0 = Date.now();
|
||||
const installId = req.header("X-Recap-Install-Id");
|
||||
const jobId = req.header("X-Recap-Job-Id") || null;
|
||||
const auth = req.header("Authorization");
|
||||
@@ -65,6 +72,19 @@ export function analyzeRouter() {
|
||||
cfg.relay_analyze_backend_preference || "gemini_first";
|
||||
const plan = planBackend(row, quota, { hasHardware, preference });
|
||||
if (!plan.allowed) {
|
||||
await recordCall({
|
||||
install_id: installId,
|
||||
tier,
|
||||
pipeline: "analyze",
|
||||
backend: null,
|
||||
model: null,
|
||||
status: "refused",
|
||||
credit_charged: 0,
|
||||
duration_ms: Date.now() - t0,
|
||||
cost_usd: 0,
|
||||
job_id: jobId,
|
||||
error: plan.reason,
|
||||
});
|
||||
const e = await errorEnvelope({
|
||||
error: plan.reason,
|
||||
installId,
|
||||
@@ -98,6 +118,21 @@ export function analyzeRouter() {
|
||||
} catch (err) {
|
||||
if (reusedJob) refundJob(installId, jobId);
|
||||
console.error(`[relay/analyze] backend error: ${err?.message}`);
|
||||
await recordCall({
|
||||
install_id: installId,
|
||||
tier,
|
||||
pipeline: "analyze",
|
||||
backend: chosenBackend,
|
||||
model: chosenBackend === "gemini"
|
||||
? cfg.relay_gemini_analysis_model
|
||||
: cfg.relay_gemma_model,
|
||||
status: "error",
|
||||
credit_charged: 0,
|
||||
duration_ms: Date.now() - t0,
|
||||
cost_usd: 0,
|
||||
job_id: jobId,
|
||||
error: (err?.message || String(err)).slice(0, 200),
|
||||
});
|
||||
const e = await errorEnvelope({
|
||||
error: err?.message || "backend_error",
|
||||
installId,
|
||||
@@ -114,6 +149,28 @@ export function analyzeRouter() {
|
||||
creditCharged = 1;
|
||||
}
|
||||
|
||||
const costDetails =
|
||||
chosenBackend === "gemini" && result.usage
|
||||
? calcGeminiCost(result.model, result.usage)
|
||||
: {
|
||||
input_tokens: 0,
|
||||
output_tokens: 0,
|
||||
thinking_tokens: 0,
|
||||
cost_usd: 0,
|
||||
};
|
||||
await recordCall({
|
||||
install_id: installId,
|
||||
tier,
|
||||
pipeline: "analyze",
|
||||
backend: chosenBackend,
|
||||
model: result?.model || null,
|
||||
status: "success",
|
||||
credit_charged: creditCharged,
|
||||
duration_ms: Date.now() - t0,
|
||||
job_id: jobId,
|
||||
...costDetails,
|
||||
});
|
||||
|
||||
const body = await envelope({ result, installId, tier, creditCharged });
|
||||
res.json(body);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user