Spaces:
Runtime error
Runtime error
Download apps/web/src/api/slip-queue.ts from ClaraSu/llm-router: direct link, hf CLI and curl.
- Browser
- Download file 4.8 kB
-
https://huggingface.co/spaces/ClaraSu/llm-router/resolve/main/apps/web/src/api/slip-queue.ts
- Command line
-
hf download hf://spaces/ClaraSu/llm-router/apps/web/src/api/slip-queue.ts
-
curl -L -o slip-queue.ts https://huggingface.co/spaces/ClaraSu/llm-router/resolve/main/apps/web/src/api/slip-queue.ts
4.8 kB
| import { apiFetch } from './client' | |
| export type SlipStatus = 'pending' | 'processing' | 'success' | 'needs_review' | 'failed' | 'confirmed' | 'duplicate' | |
| // Mirrors ParsedExpense (apps/api/src/services/expense/ports/index.ts) | |
| // exactly — narrower ParsedExpenseResult (./expenses, the older | |
| // /v1/expense/parse response) doesn't carry `reference`/`template_id`. | |
| export interface SlipParsedExpense { | |
| amount: number | |
| currency: string | |
| date: string | null | |
| merchant: string | null | |
| category: string | null | |
| confidence: number | |
| raw_description: string | |
| reference: string | null | |
| template_id?: string | null | |
| } | |
| export interface SlipQueueItem { | |
| id: string | |
| image_base64: string | null | |
| image_url: string | null | |
| status: SlipStatus | |
| retry_count: number | |
| parsed_result: SlipParsedExpense | null | |
| error_message: string | null | |
| // Best-candidate template match info, present whenever a template match | |
| // was attempted (regardless of whether it cleared the confidence bar) — | |
| // see slip-queue-match-review-ui. | |
| match_confidence: number | null | |
| match_template_id: string | null | |
| created_at: string | |
| processed_at: string | null | |
| } | |
| export interface EnqueueSlipsResult { | |
| succeeded: SlipQueueItem[] | |
| failed: Array<{ error: string }> | |
| } | |
| export async function enqueueSlips(images: string[]): Promise<EnqueueSlipsResult> { | |
| const res = await apiFetch('/v1/slip-queue', { | |
| method: 'POST', | |
| body: JSON.stringify({ images, format: 'json' }), | |
| }) | |
| if (!res.ok) { | |
| const err = await res.json() as { error?: string } | |
| throw new Error(err.error ?? 'Failed to enqueue slips') | |
| } | |
| return res.json() as Promise<EnqueueSlipsResult> | |
| } | |
| export interface SlipQueueCursor { | |
| createdAt: string | |
| id: string | |
| } | |
| export interface SlipQueuePage { | |
| items: SlipQueueItem[] | |
| hasMore: boolean | |
| } | |
| export async function listSlipQueuePage(status: SlipStatus, opts: { limit?: number; after?: SlipQueueCursor } = {}): Promise<SlipQueuePage> { | |
| const params = new URLSearchParams({ status, limit: String(opts.limit ?? 30) }) | |
| if (opts.after) { | |
| params.set('after_created_at', opts.after.createdAt) | |
| params.set('after_id', opts.after.id) | |
| } | |
| const res = await apiFetch(`/v1/slip-queue?${params.toString()}`) | |
| if (!res.ok) throw new Error('Failed to load slip queue') | |
| return res.json() as Promise<SlipQueuePage> | |
| } | |
| export async function getSlipQueueCounts(): Promise<Record<SlipStatus, number>> { | |
| const res = await apiFetch('/v1/slip-queue/counts') | |
| if (!res.ok) throw new Error('Failed to load slip queue counts') | |
| return res.json() as Promise<Record<SlipStatus, number>> | |
| } | |
| export async function retrySlip(id: string): Promise<SlipQueueItem> { | |
| const res = await apiFetch(`/v1/slip-queue/${id}/retry`, { method: 'POST' }) | |
| if (!res.ok) { | |
| const err = await res.json() as { error?: string } | |
| throw new Error(err.error ?? 'Failed to retry slip') | |
| } | |
| return res.json() as Promise<SlipQueueItem> | |
| } | |
| export interface SlipConfirmOverrides { | |
| date?: string | |
| merchant?: string | |
| currency?: string | |
| description?: string | |
| shares?: Array<{ person_id: string; amount: number }> | |
| } | |
| export interface SlipConfirmDetails extends SlipConfirmOverrides { | |
| category_id: string | |
| } | |
| export async function confirmSlip(id: string, categoryId: string, overrides?: SlipConfirmOverrides): Promise<void> { | |
| const res = await apiFetch(`/v1/slip-queue/${id}/confirm`, { | |
| method: 'POST', | |
| body: JSON.stringify({ category_id: categoryId, ...overrides }), | |
| }) | |
| if (!res.ok) { | |
| const err = await res.json() as { error?: string } | |
| throw new Error(err.error ?? 'Failed to confirm slip') | |
| } | |
| } | |
| export interface ApplyMatchOverride { | |
| parsed_result: SlipParsedExpense | |
| match_confidence: number | |
| match_template_id: string | null | |
| } | |
| // POST /v1/slip-queue/:id/apply-match — resolves a needs_review slip via one | |
| // of slip-queue-match-review-ui's two option boxes. No `override` reuses the | |
| // slip's own already-persisted match ("use matched template"); an `override` | |
| // carries a freshly-taught template's match result instead ("set up a new | |
| // template"). Either way the slip lands on 'success', same as today's | |
| // confident-auto-match path. | |
| export async function applySlipMatch(id: string, override?: ApplyMatchOverride): Promise<SlipQueueItem> { | |
| const res = await apiFetch(`/v1/slip-queue/${id}/apply-match`, { | |
| method: 'POST', | |
| body: JSON.stringify(override ?? {}), | |
| }) | |
| if (!res.ok) { | |
| const err = await res.json() as { error?: string } | |
| throw new Error(err.error ?? 'Failed to apply the template match') | |
| } | |
| return res.json() as Promise<SlipQueueItem> | |
| } | |
| export async function discardSlip(id: string): Promise<void> { | |
| const res = await apiFetch(`/v1/slip-queue/${id}`, { method: 'DELETE' }) | |
| if (!res.ok) throw new Error('Failed to discard slip') | |
| } | |