Prompt Progress

Interim progress of a prompt, for clients that stream the prompt api’s response.

A prompt can take a while: the LLM may call tools, plugins and MCP servers, and each tool call adds another round trip to the LLM. A client that sends Accept: text/event-stream to a LLMClient’s prompt api receives Server-Sent Events (SSE) while the prompt runs, followed by the same JSON that a non-streaming client receives. See smarter.apps.llmclient.api.v1.views.base.LLMClientApiBaseViewSet.

The prompt itself runs synchronously, in a worker thread, exactly as it does for a non-streaming client. Its progress comes from the signals that the prompt already sends (chat_request, llm_tool_requested, chat_plugin_called, mcpclient_tool_called, …). The receivers in this module forward them to the sink of the prompt that sent them, which is held in a contextvars.ContextVar, so that concurrent prompts never see each other’s events.

SSE payload format

  • retry: 3000 is sent once, first.

  • event: progress with data: {"type": ..., "message": ..., ...} for each step.

  • : keepalive comments while the prompt is idle, e.g. waiting on the LLM.

  • event: result with data: {"status": <http status>, "response": <the api's json>}, last.

class smarter.apps.prompt.progress.PromptProgressEvents[source]

Bases: object

The type of each progress event.

LLM_REQUEST = 'llm_request'
MCP_TOOL_CALLED = 'mcp_tool_called'
MCP_TOOL_FAILED = 'mcp_tool_failed'
MCP_TOOL_RESPONDED = 'mcp_tool_responded'
PLUGIN_CALLED = 'plugin_called'
TOOL_REQUESTED = 'tool_requested'
TOOL_RESPONDED = 'tool_responded'
smarter.apps.prompt.progress.emit(event_type, message, **detail)[source]

Send a progress event to the current prompt’s sink, if it has one.

A prompt without a streaming client has no sink, so this does nothing. A failing sink is logged, and never fails the prompt.

Parameters:
  • event_type (str) – One of PromptProgressEvents.

  • message (str) – A short, human-readable description of the step.

  • detail (Any) – Other JSON-serializable fields of the event.

Return type:

None

smarter.apps.prompt.progress.progress_sink(sink)[source]

Send the progress events of the prompt that runs in this context to sink.

Parameters:

sink (Callable[[dict[str, Any]], None]) – Called with each progress event, a JSON-serializable dict.

Return type:

Generator[None, None, None]

async smarter.apps.prompt.progress.prompt_event_stream(complete, on_error)[source]

Run a prompt in a worker thread, and yield its progress, then its result, as SSE frames.

Parameters:
  • complete (Callable[[], HttpResponse]) – Runs the prompt synchronously, and returns the prompt api’s JSON response.

  • on_error (Callable[[Exception], HttpResponse]) – Returns the prompt api’s JSON error response for an exception that complete raised.

Yields:

SSE frames: progress events and keepalives, then the result.

Return type:

AsyncIterator[str]

smarter.apps.prompt.progress.prompt_event_stream_response(complete, on_error)[source]

The prompt api’s response as Server-Sent Events.

See prompt_event_stream().

Parameters:
  • complete (Callable[[], HttpResponse]) – Runs the prompt synchronously, and returns the prompt api’s JSON response.

  • on_error (Callable[[Exception], HttpResponse]) – Returns the prompt api’s JSON error response for an exception that complete raised.

Returns:

A streaming text/event-stream response, whose http status is always 200. The prompt’s own status is in its result event.

Return type:

StreamingHttpResponse

smarter.apps.prompt.progress.wants_event_stream(request)[source]

True if the client asked for the prompt api’s response as Server-Sent Events.

Parameters:

request (HttpRequest) – The prompt api’s request.

Returns:

True if the request’s Accept header includes text/event-stream.

Return type:

bool