> ## Documentation Index
> Fetch the complete documentation index at: https://praison.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Engine API

> The local HTTP surface the Desktop app talks to on 127.0.0.1

The Desktop engine is a small HTTP server on loopback — document it here if you're wiring an alternate UI or an integration.

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent

# The engine wraps this agent. Everything below is how a UI talks to it.
agent = Agent(
    name="PraisonAI",
    instructions="You are a helpful assistant.",
)
agent.start("Hello")
```

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    Client[🌐 Any local client] --> Health[GET /health]
    Client --> Chat[POST /chat]
    Client --> Train[POST /train/start]
    Health --> Engine[🧠 Engine 127.0.0.1]
    Chat --> Engine
    Train --> Engine

    classDef client fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef route fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef engine fill:#189AB4,stroke:#7C90A0,color:#fff

    class Client client
    class Health,Chat,Train route
    class Engine engine
```

The engine binds to `127.0.0.1` only — **nothing leaves your machine** unless the model provider does. There is no auth because there is no remote surface: the loopback socket is the boundary.

## Quick Start

<Steps>
  <Step title="Find the port">
    The shell prints `PRAISONAI_PORT=<port>` on startup and writes it to the lockfile. Every route below is served from `http://127.0.0.1:<port>`.
  </Step>

  <Step title="Probe health">
    `GET /health` returns `{ok, version, data_dir}` — the version confirms it's the engine and not something else on the port.
  </Step>

  <Step title="Stream a chat">
    `POST /chat` returns a Server-Sent Events stream. See [Chat & Streaming](/docs/features/desktop/chat) for the event vocabulary.
  </Step>
</Steps>

***

## Routes

| Route                                | Purpose                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GET /health`                        | Liveness. Returns `ok`, `version`, and `data_dir` (the real data folder, honouring overrides).                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `POST /chat`                         | SSE token stream. Covered in [Chat & Streaming](/docs/features/desktop/chat).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `POST /approve/{approval_id}`        | Approve or deny a pending tool call. Body `{choice}`; a missing body means deny.                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `POST /mcp`, `GET /mcp`              | List and manage MCP servers (`add` / `remove` / `toggle`).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `GET /search?q=`                     | Full-text search across transcripts.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `POST /fork/{cid}/{idx}`             | Copy a transcript up to message `idx` into a new chat.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `DELETE /messages/{cid}/{idx}`       | Drop one exchange (a user turn and its reply).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `POST /project/{cid}`                | Set a conversation's project tag.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `DELETE /chats/{cid}`                | Delete a conversation. Reports the delete that actually happened: `200 {"ok": true}` on success (a missing file also counts, since `unlink(missing_ok=True)` succeeds), `400 {"ok": false, "error": …}` on an id that reduces to nothing (e.g. `/chats/..`), and `500 {"ok": false, "error": …}` on an `OSError` from `unlink()` (read-only or permission-changed data dir, a directory blocking the file, a synced folder mid-conflict). Answering `200` for every case is how a conversation closed on screen and reappeared in the sidebar on refresh. |
| `POST /train/start`                  | Start a fine-tune. `400` on an invalid method or column set. The single-GPU guard survives an engine restart: an adopted live run still counts, so a second start after a restart is refused with `409` rather than silently starting a second trainer beside the live one.                                                                                                                                                                                                                                                                               |
| `POST /train/stop/{run_id}`          | Stop a run. Stale-tab safe: stopping a run other than the live one is refused. An adopted run from a previous engine process is stopped through its persisted pid — the process group is signalled on POSIX (`killpg`) and the tree walked on Windows (`taskkill /T`). Quitting the engine runs the same kill before it exits, so the GPU is released whether the caller sends **Stop** or the engine receives **SIGTERM** (macOS/Linux only; Windows was already correct via `taskkill /T`).                                                             |
| `GET /train/progress?run=…&cursor=N` | SSE replay-then-follow. `400` on a malformed cursor.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `GET /train/status`                  | The live run plus its recent metrics.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `GET /train/runs`                    | Finished-run history (bounded — see caps below). Runs from previous engine processes are included: a live run reappears as `running` (its child process is adopted), an interrupted run reads as `failed`, a finished run reads as `done`.                                                                                                                                                                                                                                                                                                                |

***

## Status Codes

The engine returns real codes so a client can act, rather than dropping the connection.

| Code  | When                                                                                                                                                                                              |
| ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400` | Bad input — invalid training config, `?cursor=abc` on `/train/progress`, or an id that reduces to nothing on `DELETE /chats/`.                                                                    |
| `404` | No such conversation, message, version, or run.                                                                                                                                                   |
| `409` | A conflict — a training run is already live, or a stop named the wrong run.                                                                                                                       |
| `500` | A real server error, such as an unwritable runs directory, or an `OSError` from `unlink()` on `DELETE /chats/` (read-only data dir, a directory blocking the file, a synced folder mid-conflict). |

<Note>
  A malformed `/train/progress?cursor=abc` used to drop the connection with no response. It now returns `400 Bad Request` with `{"error": "cursor must be an integer"}`.
</Note>

<Note>
  `DELETE /chats/{cid}` used to answer `200 {"ok": true}` for every case, including permission errors and malformed ids. It now returns `400` or `500` with `{"ok": false, "error": …}` so a UI can react instead of blanking the transcript against a delete that never happened.
</Note>

***

## Bounded Deques

The training routes read from bounded in-memory buffers; the on-disk log is always the full record.

| Buffer                  | Cap                | Served by                      |
| ----------------------- | ------------------ | ------------------------------ |
| Run history             | `MAX_HISTORY=50`   | `GET /train/runs`              |
| Metric series (per run) | `MAX_METRICS=5000` | `GET /train/status` (last 500) |

***

## Best Practices

<AccordionGroup>
  <Accordion title="Read the port, don't guess it">
    The kernel assigns a free port at bind time. Read it from the `PRAISONAI_PORT=` line or the lockfile — never hardcode one.
  </Accordion>

  <Accordion title="Confirm the version on /health">
    `version` distinguishes the engine from anything else that answers on the port. Check it before trusting the rest of the surface.
  </Accordion>

  <Accordion title="Reconnect training with a cursor">
    `/train/progress` replays from `cursor` then follows. Store the last cursor you saw so a reconnect resumes exactly where it left off.
  </Accordion>
</AccordionGroup>

***

## Related

<CardGroup cols={2}>
  <Card title="Fine-Tuning" icon="list-check" href="/docs/features/desktop/fine-tune">
    The training subsystem the `/train/*` routes drive
  </Card>

  <Card title="Data & Privacy" icon="lock" href="/docs/features/desktop/data">
    Why the loopback socket is the whole boundary
  </Card>
</CardGroup>
