initial relay scaffold
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
// Job-id deduplication. Recap mints a UUID per summarize job (the
|
||||
// transcribe + analyze pair) and sends it in X-Recap-Job-Id on every
|
||||
// relay call. The first call with a given (install_id, job_id) tuple
|
||||
// reserves a credit; subsequent calls with the same tuple are free
|
||||
// until the job_id expires (1 hour).
|
||||
//
|
||||
// Stored in-memory only — not persisted across restarts because (a)
|
||||
// a restart breaks all in-flight Recap streams anyway and (b) the
|
||||
// worst-case outcome of a "lost reservation" is the user being
|
||||
// charged for a single retry, which is acceptable.
|
||||
|
||||
const JOB_TTL_MS = 60 * 60 * 1000; // 1 hour
|
||||
|
||||
// Map<install_id|job_id, { backend, tier, charged_at, refunded }>
|
||||
const jobs = new Map();
|
||||
|
||||
function key(installId, jobId) {
|
||||
return `${installId}|${jobId}`;
|
||||
}
|
||||
|
||||
// On a new request: returns { charged: true } if this is the first call
|
||||
// for the job (caller must commit a credit), or { charged: false,
|
||||
// backend, tier } if it's a retry/follow-up.
|
||||
export function lookupJob(installId, jobId) {
|
||||
if (!installId || !jobId) return null;
|
||||
pruneExpired();
|
||||
const k = key(installId, jobId);
|
||||
const existing = jobs.get(k);
|
||||
if (existing && !existing.refunded) return existing;
|
||||
return null;
|
||||
}
|
||||
|
||||
// Mark a job as having been charged. Idempotent — second call for the
|
||||
// same (install_id, job_id) is a no-op.
|
||||
export function markJobCharged(installId, jobId, { backend, tier }) {
|
||||
if (!installId || !jobId) return;
|
||||
pruneExpired();
|
||||
const k = key(installId, jobId);
|
||||
if (jobs.has(k) && !jobs.get(k).refunded) return;
|
||||
jobs.set(k, {
|
||||
backend,
|
||||
tier,
|
||||
charged_at: Date.now(),
|
||||
refunded: false,
|
||||
});
|
||||
}
|
||||
|
||||
// Refund a previously charged credit for a failed job. Future calls
|
||||
// with the same job_id will be treated as new (since the reservation
|
||||
// is no longer valid).
|
||||
export function refundJob(installId, jobId) {
|
||||
if (!installId || !jobId) return;
|
||||
const k = key(installId, jobId);
|
||||
const existing = jobs.get(k);
|
||||
if (existing) existing.refunded = true;
|
||||
}
|
||||
|
||||
function pruneExpired() {
|
||||
const cutoff = Date.now() - JOB_TTL_MS;
|
||||
for (const [k, v] of jobs) {
|
||||
if (v.charged_at < cutoff) jobs.delete(k);
|
||||
}
|
||||
}
|
||||
|
||||
export function snapshotJobs() {
|
||||
pruneExpired();
|
||||
return Array.from(jobs.entries()).map(([k, v]) => ({ key: k, ...v }));
|
||||
}
|
||||
Reference in New Issue
Block a user