Skip to main content
AgentOS clones a fresh agent for each POST /api/chat, so concurrent callers never share chat_history and the agent you passed into AgentOS(agents=[…]) is never mutated by request traffic.

Quick Start

1

Serve an Agent

2

Chat With a Session

Supply a session_id to continue the same conversation across calls. Omit it to make each call a fresh session.
Two concurrent clients using different session_ids never see each other’s transcripts.

How It Works

Each request resolves a per-request agent, resets its transcript, binds the session, and runs one turn. The /chat handler and the /invoke router share one session-isolation contract: both call _supports_session_isolation → _clone_agent → bind_session(agent, session_id) from praisonai.api.agent_invoke. Any behaviour change to session binding lands in bind_session() and both surfaces pick it up together.

Session Contract

The response model is unchanged: {response, agent_name, session_id}. Only the internal binding of session_id and the concurrency semantics changed.
An agent configured with handoffs=[…] is not cloned per request. It stays on the shared template, so chat_history is shared and session_id is not bound. This is deliberate — cloning would drop the handoffs. Use a session-aware backend (e.g. praisonai_mcp.serve_agents) if you need both handoffs and session isolation.
If cloning fails, /api/chat returns HTTP 500 with detail "Failed to isolate agent for session: {reason}". It is retry-safe and typically indicates a non-copyable custom object on the agent (e.g. a live socket).
bind_session() fails loudly. If the clone’s chat_history setter raises (e.g. a custom chat_history backed by a locked store), the whole request returns HTTP 500 with detail "Failed to isolate agent for session: Cannot reset chat_history for session isolation: ...". This is deliberate: silently continuing would leave the clone holding the template’s previous transcript and leak it to whoever the incoming session_id belongs to. If you see this in production, replace the offending chat_history descriptor with a plain list assignment path.

When Cloning Applies

Only real Agent instances that expose the per-session machinery are cloned. Plain mocks and lightweight callables fall back to the shared template.

Best Practices

Supplying the same session_id on each turn is how a client continues one conversation. Omitting it starts a fresh session on a fresh clone every time.
Agents with handoffs stay on the shared template and are not session-isolated over /chat. Reach for serve_agents([...]) when you need handoffs and per-session isolation together.
A 500 — Failed to isolate agent for session means the clone step raised, usually from a non-copyable object on the agent. It is safe to retry after removing the offending object.
This clone model isolates concurrent in-process requests. It does not persist sessions across process restarts — use a session store backend for durable history.
bind_session() treats a raised setter as a fatal cross-tenant hazard and refuses the request. If you attach a custom chat_history descriptor (e.g. one that locks a backing store on assignment), make its setter always succeed — either accept [] unconditionally or replace it with a plain attribute.

Serve Agents

serve_agents([...]) — per-session isolation with handoff support, useful for comparison

PraisonAI Call

The call server that hosts the n8n agent-invoke router