fastrag.serve.cli#

An interactive terminal client for the serving API, built on Typer.

It is pure httpx against the running server — no browser, no EventSource. That is the point: all three streaming endpoints are POST, so a browser’s EventSource cannot be used, and this client implements the fetch-equivalent reader that replaces it (the SSE parsing is written out rather than imported, so the public surface can’t drag in anything but httpx).

The token is a credential. It is read from FASTRAG_SESSION_TOKEN if set, else from a token file (default ~/.fastrag/session, created 0600 on first run), else minted on the fly by a single POST /v1/sessions, which mints a session when the request carries none, and is saved for later runs. Every request after that is header-authenticated. On a server built with bearer_tokens=False that route is a 404; the client then stays tokenless and rides the session cookie its own jar holds. The token is never logged; /token is the explicit opt-in to printing it.

History. The chat id is the client’s whole memory of a conversation: it is read off the message_start frame of the first answer and reused as the POST /v1/chats/{chat_id}/messages target for every later turn, so the server replays the prior turns into the model. /new (or /delete) clears it, and only then does the next question open a fresh chat. Each turn’s prior messages are sent to the model (so a follow-up pronoun resolves against earlier turns) but not to the retriever — the retriever only ever sees the current question. A pronoun may therefore resolve in the answer while the citations do not follow it.

Edit semantics. /retry and /edit POST to an existing message id and stream the regenerate endpoint. The serving app validates the request and streams a fresh answer, appending it as the latest assistant message rather than truncating the run to that point — the store that backs the app has no range-delete primitive, so the chat history grows instead of being rewound. A long chat may therefore accumulate the retried turns; /open shows them all.

Commands: /new, /chats, /open <id>, /retry, /edit <text>, /delete, /token, /help, /quit. Ctrl-C during a stream closes the response and returns to the prompt; the server persists finish_reason: "interrupted" and /open afterwards shows the partial answer.

Wire contract: this client only speaks HTTP to the side the app was started with in mind — it never issues a request for a route that doesn’t exist and never changes the server’s behaviour. If a route or event name looks wrong, this client is wrong; the server’s contract is owned by fastrag.serve.

Functions

load_token([token_file])

Return the token, if one is already known: env, then file.

parse_sse(text)

Parse one raw SSE body into (event, data) pairs.

save_token(token[, token_file])

Persist token 0600 — it is a live credential.

Classes

CLI(backend_url, *[, token_file, token, ...])

An interactive terminal client for the serving API, built on Typer.

class fastrag.serve.cli.CLI(backend_url, *, token_file=None, token=None, transport=None)[source]#

Bases: object

An interactive terminal client for the serving API, built on Typer.

CLI(backend_url=...) returns an object; calling it (cli()) runs the interactive loop. backend_url is the base URL of a serving app started from create_app(). token_file points at where the session token is read from and written to (default ~/.fastrag/session); token short-circuits all token discovery by supplying the credential directly.

Typer is imported lazily inside the constructor, so merely importing this class needs no serving extra. Only constructing a CLI does.

Parameters:
  • backend_url (str)

  • token_file (Path | str | None)

  • token (str | None)

  • transport (httpx.AsyncBaseTransport | None)

property token: str | None#

The current session token (bare credential — handle with care).

fastrag.serve.cli.parse_sse(text)[source]#

Parse one raw SSE body into (event, data) pairs.

A pure, deterministic reimplementation of the reader — line by line on the blank-line boundary, skipping : keep-alive comments, and dispatching separately. It is factored out of the streaming path so it can be unit-tested with no server and no network. Each data block is parsed as JSON; a data block with no lines yields {}.

Parameters:

text (str)

Return type:

list[tuple[str, dict[str, Any]]]

fastrag.serve.cli.load_token(token_file=None)[source]#

Return the token, if one is already known: env, then file.

FASTRAG_SESSION_TOKEN wins over the token file. Returns None when neither holds one — the caller then mints on first request.

Parameters:

token_file (Path | None)

Return type:

str | None

fastrag.serve.cli.save_token(token, token_file=None)[source]#

Persist token 0600 — it is a live credential.

Writes to token_file (default ~/.fastrag/session), creating the parent directory, with mode 0600. Returns the path that was written.

Parameters:
  • token (str)

  • token_file (Path | None)

Return type:

Path