REST API#
The persona manager registers three HTTP endpoints on Jupyter Server (see
extension.py). All are Jupyter Server handlers, so they require the
usual authentication — include the XSRF token / auth cookie that JupyterLab uses
for its own API calls. Paths below are relative to the server’s base_url.
Method |
Path |
Purpose |
|---|---|---|
|
|
Send a one-shot message to a persona and get its reply |
|
|
Interrupt in-progress replies in a chat |
|
|
Fetch a persona’s avatar image |
Note
These endpoints are backend infrastructure. In normal use, the chat UI talks to
personas over the collaborative YChat document and the Yjs awareness
channel, not over REST — awareness is how a persona’s model, settings, usage, and
typing status reach the browser. The message and cancel endpoints exist for
one-shot / programmatic messaging and for the interrupt button.
POST /api/ai/message/<persona_name>#
Route a single message to the persona whose defaults
matches <persona_name> (URL-encoded), in a fresh temporary chat room, and return
the concatenated reply. Implemented by MessageHandler.
Path parameter
persona_name— the persona’s display name (itsdefaults.name), URL-encoded. Note this matches on name, not on the personaid.
Request body (JSON)
Field |
Type |
Required |
Description |
|---|---|---|---|
|
|
✓ |
The message text to send |
|
|
Optional message metadata, e.g. a |
curl -X POST "$BASE_URL/api/ai/message/MyPersona" \
-H "Content-Type: application/json" \
-H "Authorization: token $JUPYTER_TOKEN" \
-d '{"message": "Hello!"}'
Response 200 (JSON)
{ "response": "…the persona's reply text…" }
The handler creates a temporary room, sends the message mentioning the target
persona, waits for the persona to finish writing (its isWriting awareness flag
clears), then returns all message bodies in the room joined together. If the
persona is still writing after 120 seconds, the wait times out and whatever
has been written so far is returned.
Errors
Status |
When |
|---|---|
|
Body is not valid JSON, or |
|
No persona with that name is found |
POST /api/ai/personas/cancel#
Interrupt in-progress replies in a specific chat. Implemented by
CancelHandler. The chat UI’s interrupt button POSTs here.
Query parameter
chat_path(required) — the path of the chat file, resolved to a room via the file-ID manager.
Request: no body needed.
curl -X POST "$BASE_URL/api/ai/personas/cancel?chat_path=my-chat.chat" \
-H "Authorization: token $JUPYTER_TOKEN"
For each persona in that chat that is currently
processing, the handler calls
cancel_response(). Idle personas are
skipped (a cancel with no active reply is out of spec for some backends, e.g.
ACP’s session/cancel). A persona whose cancel_response raises is logged and
skipped, not surfaced as an error.
Response 200 (JSON) — the IDs of the personas that were asked to cancel:
{
"status": "cancelled",
"cancelled": ["jupyter-ai-personas::my_package::MyPersona"]
}
Errors
Status |
When |
|---|---|
|
|
|
No chat/room found for |
|
The server’s |
GET /api/ai/avatars/<persona_id>#
Serve a persona’s avatar image. Implemented by AvatarHandler. This
is the URL that avatar_path
returns and that appears as avatar_url on the persona’s chat user — the browser
requests it directly; you rarely call it by hand.
Path parameter
persona_id— the personaid(URL-encoded).
Response 200 — the raw image bytes, with Content-Type guessed from the
file extension (SVG, PNG, or JPG). The file is resolved from a module-level cache
built when personas are initialized/refreshed, keyed by persona ID (an O(1)
lookup), from each persona’s defaults.avatar_path.
Errors
Status |
When |
|---|---|
|
No avatar is cached for that persona ID |
|
The avatar file exceeds the 5 MB limit |
|
The file can’t be read |