feat(intake): cut out backgrounds in the worker, never in the upload (#281)
Wires removeBackgroundsForItem into draftQueued, gated on the submitter's remove_background intent recorded on item_drafts. The step runs after the draft is committed and catches for itself, so an unreachable or erroring sidecar never turns a draft that was written correctly into a failed one — the photo simply keeps its original, and the admin's per-photo control in the review queue is still there to do it by hand. It is awaited, unlike the notification below it, so a sweep that has returned has finished its work; nothing on the request path waits on it. Adds backend/tests/integration/draftingBackgroundRemoval.integration.test.ts as a new file rather than extending drafting.integration.test.ts, because that suite has never produced a successful draft and therefore has no draftListing mock — adding one there would be file-wide and would change what its existing tests exercise. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,7 @@ import { getAnthropicClient } from './anthropicClient';
|
||||
import { draftListing } from './draftListing';
|
||||
import { applyDraft } from './applyDraft';
|
||||
import { notifyDraftReady } from './notifyDraft';
|
||||
import { removeBackgroundsForItem } from './backgroundRemoval';
|
||||
|
||||
/**
|
||||
* Turns queued submissions into drafts.
|
||||
@@ -19,6 +20,13 @@ import { notifyDraftReady } from './notifyDraft';
|
||||
* The photos are often the only copy of an item no longer in the sender's
|
||||
* hands, so every failure below leaves the row and its images intact and merely
|
||||
* undrafted. Nothing in this file deletes anything.
|
||||
*
|
||||
* Background removal (#281) follows drafting rather than running on its own
|
||||
* pass. That couples the two: an environment with no ANTHROPIC_API_KEY drafts
|
||||
* nothing, so it cuts out nothing either. That is the intended trade — a
|
||||
* separate pass would re-attempt an unreachable sidecar on every sweep for a
|
||||
* row that is going to sit at 'queued' indefinitely — and the admin's per-photo
|
||||
* control in the review queue is the way to do it by hand meanwhile.
|
||||
*/
|
||||
|
||||
/** Three tries, then it waits for a person rather than burning money on a loop. */
|
||||
@@ -32,6 +40,7 @@ type Photo = { mediaType: string; base64: string };
|
||||
interface QueuedRow {
|
||||
item_id: number;
|
||||
submitter_note: string | null;
|
||||
remove_background: boolean;
|
||||
}
|
||||
|
||||
export interface SweepResult {
|
||||
@@ -119,7 +128,7 @@ async function draftOne(
|
||||
|
||||
export async function draftQueued(limit = DEFAULT_BATCH): Promise<SweepResult> {
|
||||
const { rows } = await pool.query<QueuedRow>(
|
||||
`SELECT item_id, submitter_note FROM item_drafts
|
||||
`SELECT item_id, submitter_note, remove_background FROM item_drafts
|
||||
WHERE state = 'queued' AND attempts < $2
|
||||
ORDER BY created_at
|
||||
LIMIT $1`,
|
||||
@@ -146,6 +155,22 @@ export async function draftQueued(limit = DEFAULT_BATCH): Promise<SweepResult> {
|
||||
await draftOne(client, row.item_id, row.submitter_note, photos);
|
||||
drafted++;
|
||||
|
||||
// Deliberately after the draft is committed, and catching for itself.
|
||||
//
|
||||
// This is the sender's tick from the submission page, honoured here so
|
||||
// they never waited for it — and a failure must not mark a draft that was
|
||||
// written correctly as failed. The photo keeps its original in that case,
|
||||
// and the admin's per-photo control is still there to do it by hand.
|
||||
//
|
||||
// Awaited, unlike the notification below, so a sweep that has returned
|
||||
// has finished its work. Nothing is waiting on this: the worker is off
|
||||
// the request path, which is the whole reason drafting lives here.
|
||||
if (row.remove_background) {
|
||||
await removeBackgroundsForItem(row.item_id).catch((err) =>
|
||||
console.error(`[drafting] background removal for item ${row.item_id}:`, err)
|
||||
);
|
||||
}
|
||||
|
||||
// Fire and forget, and deliberately after the draft is committed. A mail
|
||||
// failure must never mark a draft that was written correctly as failed —
|
||||
// the queue is what the admin actually works from, and the email is a
|
||||
|
||||
Reference in New Issue
Block a user