image-tool / frontend /src /api /client.ts
JunyoungK7's picture
Claude Sonnet 4.6
Deploy to HF Spaces: Docker setup, CPU inference, env-gated config
a3953cb
Raw History Blame Contribute Delete
4.54 kB
export const API_BASE = import.meta.env.VITE_API_BASE ?? "http://localhost:8000";
// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------
export interface StepSummary {
step_number: number;
status: "pending" | "processing" | "completed" | "failed";
original_image_url: string | null;
result_image_url: string | null;
error_message: string | null;
}
// ---------------------------------------------------------------------------
// Upload
// ---------------------------------------------------------------------------
/**
* Upload an image file to the backend.
* POSTs as multipart/form-data; backend applies EXIF rotation, saves step 0,
* and returns the job_id.
*/
export async function uploadImage(file: File): Promise<{ job_id: string }> {
const formData = new FormData();
formData.append("file", file);
const response = await fetch(`${API_BASE}/upload`, {
method: "POST",
body: formData,
});
if (!response.ok) {
let detail = `HTTP ${response.status}`;
try {
const json = await response.json();
if (json?.detail) detail = json.detail;
} catch { /* ignore */ }
throw new Error(detail);
}
return response.json() as Promise<{ job_id: string }>;
}
// ---------------------------------------------------------------------------
// Generate
// ---------------------------------------------------------------------------
/**
* POST /generate — send the mask JSON to the backend and start inpainting.
*
* The backend creates a new step (truncating any forward history if needed),
* starts inference in a BackgroundTask, and returns immediately.
*/
export async function generateMask(
jobId: string,
maskJson: object,
maskSource: "manual" | "ocr" | "mixed" = "manual",
): Promise<{ job_id: string; step_number: number; status: string }> {
const response = await fetch(`${API_BASE}/generate`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
job_id: jobId,
mask_source: maskSource,
mask_json: maskJson,
}),
});
if (!response.ok) {
let detail = `HTTP ${response.status}`;
try {
const json = await response.json();
if (json?.detail) detail = json.detail;
} catch { /* ignore */ }
throw new Error(detail);
}
return response.json() as Promise<{ job_id: string; step_number: number; status: string }>;
}
// ---------------------------------------------------------------------------
// Result polling
// ---------------------------------------------------------------------------
/**
* GET /result/{job_id} — poll current job state.
*
* Returns top-level `status` / `error_message` for the current step
* (backward-compatible with the polling hook), plus the full `steps[]` array
* and `current_step` for the multi-step history navigator.
*/
export async function getJobResult(jobId: string): Promise<{
job_id: string;
current_step: number;
status: "pending" | "processing" | "completed" | "failed";
result_image_url: string | null;
original_image_url: string | null;
error_message: string | null;
steps: StepSummary[];
}> {
const response = await fetch(`${API_BASE}/result/${jobId}`);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
}
// ---------------------------------------------------------------------------
// Step navigation
// ---------------------------------------------------------------------------
/**
* POST /jobs/{job_id}/goto-step — set job.current_step to stepNumber.
*
* Only completed steps may be navigated to. Truncation of forward history
* happens lazily on the next POST /generate call.
*/
export async function goToStep(
jobId: string,
stepNumber: number,
): Promise<{ job_id: string; current_step: number }> {
const response = await fetch(`${API_BASE}/jobs/${jobId}/goto-step`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ step_number: stepNumber }),
});
if (!response.ok) {
let detail = `HTTP ${response.status}`;
try {
const json = await response.json();
if (json?.detail) detail = json.detail;
} catch { /* ignore */ }
throw new Error(detail);
}
return response.json() as Promise<{ job_id: string; current_step: number }>;
}