Chat & Streaming

Send messages, stream replies, and read tool cards in the Desktop app

Every reply streams live from your local agent β€” text, reasoning, tool cards, and usage all arrive as typed events over 127.0.0.1.

python
from praisonaiagents import Agent

agent = Agent(
    name="Assistant",
    instructions="You are a helpful assistant.",
)
# The Desktop app streams this agent's reply token-by-token.
agent.start("Write a haiku about the sea", stream=True)
Diagram

πŸ‘€ You

πŸ’¬ Message

🧠 Engine

πŸ“‘ SSE Events

βœ… Rendered Turn

Quick Start

  1. Send your first message

    Type in the composer and press Enter. The reply streams in as the agent produces it.

  2. Watch the events render

    Text, a live reasoning panel, tool cards, and a usage line all appear in the same turn as they stream.

  3. Act on the turn

    Hover a turn to Copy, Regenerate, Fork, or Delete it.


How It Works

The Desktop app opens POST /chat and streams these 11 event types straight into the turn β€” you write no client code for it.

python
from praisonaiagents import Agent

agent = Agent(
    name="Assistant",
    instructions="You are a helpful assistant.",
)
# The Desktop app opens POST /chat and streams these 11 event types
# straight into the turn β€” you write no client code for it.
agent.start("Summarize today's notes", stream=True)

The engine sends Server-Sent Events on POST /chat.

Diagram
πŸ€– Agent🧠 Engine🌐 AppπŸ‘€ YouπŸ€– Agent🧠 Engine🌐 AppπŸ‘€ Youtype message + EnterPOST /chat (prompt)agent.start(stream=True)chunksstart / delta / tool_call / usage / endstreamed turn

Each event maps to one visible piece of the turn:

Diagram

πŸ’¬ Send

🟒 start

🧠 reasoning

✍️ delta

πŸ”§ tool_drafting

πŸ”¨ tool_call

πŸ”‘ approval_request

πŸ“¦ tool_result

πŸ“Š usage

βœ… end

Every event carries a msg_id that scopes it to a specific assistant message; start additionally carries a run_id that scopes it to a specific run, so a POST /approve/{aid} or POST /cancel sent later addresses the right run and not whichever one happens to be pending. The vocabulary is exactly the 11 events below β€” a StreamProtocolVocabulary test in the engine fails CI if a new emitter or a new documented event ever drifts from that list, so this table stays a faithful mirror of the wire.

Every event the stream emits (from engine/server.py), with the exact payload a client sees on the SSE wire:

EventPayloadRendered as
start{msg_id, run_id}New assistant turn container
reasoning{msg_id, text} (incremental)Live “Thinking… Xs” panel (collapses to “Thought for Xs”)
delta{msg_id, text}Streamed text into the current turn
tool_drafting{msg_id, name}“Preparing tool…” state / argument preview inside a tool card
tool_call{msg_id, call_id, name, args}Tool card (running dot)
tool_result{msg_id, call_id, name, ok, output, seconds}Tool card result pane (tail-truncated with Show all + Copy output)
approval_request{msg_id, approval_id, call_id, name, args}Approval card (Allow / Always allow / Deny) β€” human-in-the-loop gate
usage{msg_id, chars, seconds, ttft}Chars, seconds, time-to-first-token under the reply
cancelled{msg_id, run_id}“Stopped” indicator
error{msg_id, message, kind}Typed error card β€” auth / rate_limit / no_answer / empty / internal
end{msg_id, user_index, assistant_index, versions, active}Finalizes the turn

Every event carries msg_id so the client can address a specific message rather than assuming the last one is live. Clients integrating with the SSE stream should handle all 11 events β€” a client written against a subset silently ignores the rest, and approval_request in particular will block the run invisibly until the 300 s approval timeout if the client does not render it.


Capabilities

Reasoning panel

When the model emits reasoning, a live “Thinking… Xs” panel shows it and collapses to “Thought for Xs” when the turn ends. Toggle it with Show reasoning and Collapse reasoning by default in Settings.

Tool cards

A tool_call opens a card with a running dot; tool_drafting previews the arguments; tool_result fills the result pane. Long output is tail-truncated with Show all and Copy output. Tool cards remain visible even if the model produces no follow-up answer β€” a no_answer banner then explains what didn’t come back.

Attachments

Drop a file and the composer branches on its MIME type β€” images and PDFs reach a vision model as bytes, everything else folds into the prompt as text.

Diagram

image/* or application/pdf

anything else

πŸ“Ž File dropped

MIME type?

πŸ–ΌοΈ base64 data: URL
β†’ agent.start attachments=

πŸ“„ Clip to 100 kB
β†’ fold into prompt

🧠 Vision model sees bytes

Drag-drop or use the + button. Chips show each file’s size. Limits: 2 MB max, up to 5 files.

  • **Images (image/*) and PDFs** are read as base64 and passed to the agent as attachments=[...], so a vision-capable model (gpt-4o, claude-3-, gemini-) sees the image itself β€” not a text description of it. Point the Desktop app at a text-only model and the image is ignored silently.
  • Text files are clipped to 100 kB of text and folded into the prompt, so long pastes condense into an attachment instead of filling the context.

Only text is stored in the turn’s history; the image bytes are ephemeral to the turn.

Cancel, regenerate, fork

Stop cancels a live run β€” the client is told explicitly with a cancelled event rather than inferring it from silence. Cancelling with Stop no longer double-reports as “No output” β€” a cancelled event is the only banner the user sees. Per-turn hover actions cover Copy, Regenerate, Fork (POST /fork/{cid}/{idx}), and Delete message (DELETE /messages/{cid}/{idx}).


Message Versions

Regenerating an answer keeps the previous one as a prior version on the same message, so you can compare without copying anything out.

Diagram

πŸ’¬ Answer v1

πŸ” Regenerate

πŸ’¬ Answer v2

β—€ 2/2 β–Ά picker

  • Regenerate sends POST /chat with regenerate_of set to the message index, so the new answer is filed as another version instead of a duplicate question.
  • A < 1/2 > picker appears on hover once a message has more than one version, opening at the newest.
  • Stepping through the picker calls POST /version/{cid}/{idx}/{n} and repaints the chosen answer.
  • In the server payload every assistant row exposes versions, active, version_count, and version_active.

Message Queue

Typing while a turn is running queues your next message instead of interrupting it.

  • The queued message sends automatically when the current turn finishes.
  • Stop clears the queue rather than sending it β€” cancelling means “not this either”.

In-App Confirmation Dialogs

Destructive actions β€” delete conversation, delete-all, delete message β€” route through an in-app modal, not the browser’s window.confirm.


Keyboard Shortcuts

KeyAction
⌘NNew chat
⌘KSearch all conversations
⌘,Open settings
EnterSend
Shift+EnterNewline
EscClose overlay

Related