Stream useful partial progress to users
A product pattern for streaming AI work without flooding the interface with raw model internals.
Open source docReal workflow example
A legal operations tool generates a first-pass contract review. The work takes 45 seconds because the system reads the document, extracts clauses, checks policy conflicts, drafts comments, and validates the final output.
Raw token streaming makes the interface noisy and fragile. Useful streaming shows milestones: uploaded, parsing, extracting clauses, checking policy, drafting comments, validating, ready for review. The user sees progress that maps to the workflow, not the model's internal sentence construction.
The final review still appears only after validation completes.
Implementation approach
Name the progress states before wiring streaming. Good states reflect user concerns: what is being read, what is being checked, what can fail, and when the output becomes actionable.
Stream milestone events from the server. Partial text can be helpful for long drafts, but it should be visually distinct from approved output. Disable send, publish, approve, and export actions until validation finishes.
Persist the final response and validation result. A streamed response that disappears on refresh is not a production workflow.
Code or config snippet
type ProgressEvent =
| { type: "status"; label: "Parsing document" }
| { type: "status"; label: "Extracting clauses" }
| { type: "status"; label: "Checking policy" }
| { type: "preview"; text: string }
| { type: "validated"; runId: string };
function sendProgress(controller: ReadableStreamDefaultController, event: ProgressEvent) {
controller.enqueue(`data: ${JSON.stringify(event)}\n\n`);
}
sendProgress(controller, { type: "status", label: "Checking policy" });
const response = await openai.responses.create({
model: "gpt-4.1-mini",
input,
stream: true,
});
Mistakes to avoid
- Streaming raw tokens into the same visual space as final approved content.
- Showing a spinner for a multi-step workflow with no milestone feedback.
- Enabling irreversible actions before final validation.
- Losing the completed response when the user refreshes.
- Treating progress messages as a substitute for error states.
Ready checklist
- Progress states are named in product language.
- Partial output is visually distinct from final output.
- Server sends milestone events for long steps.
- Final validation runs before actions unlock.
- Completed output is persisted.
- Failure and retry states are visible.
