Temporal in Frontend¶
The frontend does not run Temporal directly. It talks to a backend service that owns the Temporal workers and workflows. From this project’s perspective, Temporal is an orchestration engine behind a REST API.
The core integration lives in src/services/TemporalGenerationService/TemporalGenerationService.ts. That class is the gateway between your UI and Temporal-backed workflows.
Here’s what’s happening structurally.
First, Temporal is used to orchestrate long-running generation workflows — things like image generation, batch generation, mask edits, and video merge operations. Instead of doing generation synchronously, the frontend starts a workflow and then tracks it over time.
When a user triggers generation, the app:
- Builds a unique workflow ID (for example:
gen-${card.id}-${Date.now()}) -
Sends a POST request to backend endpoints like:
-
/workflows/start-with-refinement /workflows/batch/start-with-refinement/workflows/start/workflows/merge
These endpoints are wrappers around Temporal workflow executions.
So the flow looks like this:
User action → Frontend calls API → Backend starts Temporal workflow → Frontend polls for status → UI updates progressively.
Now let’s break down the main Temporal usage patterns.
Single generation workflow¶
When generating an image (or similar content), the service sends:
- userId
- projectId
- cardId
- workflowId
- workflowType (derived from card type and action type)
- taskQueue
- conversationId
The backend responds with a runId. The frontend then updates the card’s generation_started_at timestamp in PocketBase and begins tracking the workflow.
Batch generation¶
Batch generation calls /workflows/batch/start-with-refinement. The backend returns:
- batchId
- an array of workflowIds + runIds
The frontend then:
- Tracks each individual workflow
- Exposes aggregate batch status
- Maps backend results to file IDs when complete
This tells me Temporal is being used to fan out multiple child workflows and aggregate progress.
Mask edit generation¶
There’s a specialized flow for image editing with masks. This uses /workflows/start and sends a structured input object that includes:
- prompt
- modelId
- parameters (with actionType forced to "edit")
- referenceImages
- mask
- userId
- projectId
This implies the backend workflow definition expects a structured input payload — likely passed as the workflow input parameter in Temporal.
Polling instead of push¶
There’s no WebSocket or server-sent events here. The frontend polls every ~2 seconds (configurable) using:
/workflows/{workflowId}/status
Polling stops when the status is:
- completed
- failed
- cancelled
- terminated
The service tracks:
- poll intervals
- consecutive errors
- failed workflows
- listeners per workflow
If too many polling errors occur, it marks the workflow as failed and stops trying.
That’s a deliberate resilience layer in the client.
Status mapping¶
Temporal’s native status values (numeric or string) are mapped to simplified frontend statuses:
running completed failed cancelled terminated
Numeric codes like:
- 2 → completed
- 3 → failed
- 4 → cancelled
are converted via mapTemporalStatus().
So the frontend is abstracting Temporal SDK status enums into UI-friendly states.
Workflow management features¶
The service also supports:
- Canceling a workflow (
POST /workflows/{workflowId}/cancel) - Fetching active workflows for a user
- Fetching workflow history
- Subscribing to updates via internal listener sets
- Starting a merge workflow for video export
That last one (/workflows/merge) is clearly another Temporal workflow type used for post-production style operations.
Summary¶
Temporal is being used as:
- A durable orchestrator for AI/media generation
- A fan-out engine for batch jobs
- A coordinator for multi-step workflows (likely refinement → generation → storage → result)
- A state machine whose lifecycle is reflected in the UI
The frontend treats it as a job engine — start job, poll job, display progress, handle completion.
Important design implications
- The frontend never directly uses the Temporal SDK.
- All Temporal interaction is abstracted behind a backend HTTP API.
- Workflow IDs are generated client-side.
- The backend likely uses those workflow IDs as Temporal workflow IDs (which allows deterministic idempotency).
- Polling is the synchronization mechanism — no event streaming.