|
Download docs/API.md from Tamek/Mirai: direct link, hf CLI and curl.
- Browser
- Download file 7.44 kB
-
https://huggingface.co/spaces/Tamek/Mirai/resolve/main/docs/API.md
- Command line
-
hf download hf://spaces/Tamek/Mirai/docs/API.md
-
curl -L -o API.md https://huggingface.co/spaces/Tamek/Mirai/resolve/main/docs/API.md
7.44 kB
| # Mirai Backend API | |
| Canonical contract of the FastAPI service in `app/`. The web client never calls it directly: the | |
| Cloudflare Worker in the `mirai-chat` repo (`src/worker.js`) sits in front, validates and clamps | |
| requests, and adds the API key. Its browser-facing differences are listed in that repo's | |
| `SETUP_SERVER.md`. | |
| When you change a field, a limit or a status code here, update that file and the Worker constants | |
| in the same change (see [Limits](#limits-and-where-they-are-enforced)). | |
| ## Authentication | |
| | Env var | Effect | | |
| | --- | --- | | |
| | `SPACE_API_KEY` | When set, `/chat` and `/tts` require it as `Authorization: Bearer <key>` or `X-Api-Key: <key>`. Mismatch gives `401`. | | |
| | `REQUIRE_API_KEY=1` | Fail closed: if `SPACE_API_KEY` is missing, every `/chat` and `/tts` request gets `503 Server is not configured`. **Set this in production.** Without it and without a key, both endpoints are open to anyone who knows the Space URL. | | |
| `/health` never requires a key. CORS allows any origin for `GET` and `POST` (no credentials). | |
| `/docs`, `/redoc` and `/openapi.json` are off unless `ENABLE_DOCS=1`. | |
| ## `GET /health` | |
| ```json | |
| { "status": "ok", "model": "mirai_model-q4_k_m.gguf" } | |
| ``` | |
| | HTTP | `status` | Meaning | | |
| | --- | --- | --- | | |
| | 200 | `ok` | Model loaded and ready. | | |
| | 503 + `Retry-After: 15` | `loading` | Model still loading in the background thread. | | |
| | 503 + `Retry-After: 15` | `error` | Model failed to load (`model` may be empty; details are in the logs). | | |
| ## `POST /chat` | |
| Runs the LLM and returns the reply. Unknown request fields are ignored (the server never trusts | |
| client-supplied instructions). | |
| ### Request | |
| | Field | Type | Notes | | |
| | --- | --- | --- | | |
| | `message` | string, 1-2000 chars | Required. Special chat-template tokens and `User:`/`Mirai:` prefixes are stripped. | | |
| | `history` | `{role: "user" \| "assistant", content: string (max 2000)}[]`, max 24 items | Optional. Only the last 12 are considered, and old turns are dropped to fit `PROMPT_TOKEN_BUDGET`. | | |
| | `max_tokens` (alias `maxTokens`) | int | Optional. Clamped to **96-192**, default 128. | | |
| | `max_words` (alias `maxWords`) | int | Optional. Clamped to **10-60**, default 40. Becomes the length hint in the persona prompt. | | |
| | `tts` | bool, default `true` | `false` skips speech synthesis so the text returns as soon as the LLM finishes; fetch audio from `/tts`. | | |
| ### Response `200` | |
| ```json | |
| { | |
| "text": "Chào bạn! ...", | |
| "reply": "Chào bạn! ...", | |
| "emotion": "happy", | |
| "audio": null, | |
| "audioFormat": "mp3", | |
| "audioError": null, | |
| "lipSync": null, | |
| "sources": [], | |
| "meta": { "model": "...", "finish_reason": "stop", "truncated": false, "history_used": 4, | |
| "prompt_tokens": 512, "completion_tokens": 40, "latency_ms": 9000, "queue_ms": 0, | |
| "llm_ms": 8800, "first_token_ms": 4200, "tts_ms": 0 } | |
| } | |
| ``` | |
| | Field | Notes | | |
| | --- | --- | | |
| | `text`, `reply` | Same string. `reply` is kept for older clients; new code should read `text`. | | |
| | `emotion` | `happy`, `sad`, `angry`, `surprised`, `relaxed`, `neutral`, `shy`, `thinking`, or `null` when there is no clear signal (the client then leaves the face alone). A `[smile]`-style tag emitted by the model wins, otherwise keywords decide. Tags are removed from `text`. | | |
| | `audio` | Base64 MP3 or `null` (when `tts: false` or synthesis failed). | | |
| | `audioError` | `"tts_unavailable"` when synthesis failed, else `null`. TTS failure never fails the request. | | |
| | `lipSync` | See [Lip-sync payload](#lip-sync-payload), or `null`. | | |
| | `sources` | Always `[]` for now. Reserved. | | |
| | `meta.truncated` | `true` when generation hit the token limit or deadline; the text is cut back to the last whole sentence. | | |
| ### Errors | |
| Error bodies are `{ "detail": "<message>" }` (FastAPI). Through the Worker they become `{ "error": "<message>" }`. | |
| | HTTP | When | Retry-After | | |
| | --- | --- | --- | | |
| | 400 | Message empty after trimming/sanitizing. | | | |
| | 401 | Missing or wrong API key. | | | |
| | 413 | Prompt does not fit the context window because the **message** is too long. | | | |
| | 422 | Request fails schema validation (length limits, types). | | | |
| | 500 | Model failed to load, generation failed, or the **server prompt** alone exceeds `PROMPT_TOKEN_BUDGET` (a config problem, not the user's fault). | | | |
| | 503 | Model still loading, or queue full (one request generating plus one waiting). | 15 / 10 | | |
| ## `POST /tts` | |
| Request `{ "text": string, 1-1000 chars }`. Response: | |
| ```json | |
| { "audio": "<base64 mp3>", "audioFormat": "mp3", "audioError": null, "lipSync": { } } | |
| ``` | |
| On synthesis failure the status is still `200` with `audio: null` and `audioError: "tts_unavailable"`. | |
| `503` (+ `Retry-After: 5`) when 4 syntheses are already running. Voice is `vi-VN-HoaiMyNeural`; | |
| markup, URLs, `[tags]` and emoji are removed from the text before synthesis. | |
| ## Lip-sync payload | |
| ```json | |
| { | |
| "metadata": { "duration": 3.42, "text": "...", "source": "word", "version": 2 }, | |
| "mouthCues": [ | |
| { "start": 0.0, "end": 0.12, "value": { "A": 1.0 } }, | |
| { "start": 0.12, "end": 0.2, "value": {} } | |
| ] | |
| } | |
| ``` | |
| - `source` is `word`, `sentence` or `estimated`, depending on the timing marks edge-tts provided. | |
| The client only rescales cue time for non-`word` sources. | |
| - `value` maps vowel channels `A`, `I`, `U`, `E`, `O` to 0-1 weights (summing to at most 1). An empty object means mouth closed. | |
| - Cues are non-overlapping and ordered by `start`; times are seconds from the start of the audio. | |
| ## Limits and where they are enforced | |
| The same limit appears in several layers. Change all of them together. | |
| | Limit | Worker (`mirai-chat/src/worker.js`) | Backend | Client (`chatApi.js`) | | |
| | --- | --- | --- | --- | | |
| | `message` length | 1000 (silently truncated) | 2000 (422) | | | |
| | `history` items / chars each | 12 / 500 (truncated) | 24 / 2000 (422), uses last 12 | 12 (`HISTORY_MESSAGES`) | | |
| | `max_tokens` | cap 192, default 128 | clamp 96-192, default 128 | default 128 (`VITE_MIRAI_MAX_TOKENS`) | | |
| | `max_words` | cap 60, default 40 | clamp 10-60, default 40 | default 40 (`VITE_MIRAI_MAX_WORDS`) | | |
| | `/tts` text | 1000 | 1000 | | | |
| | Request body | 16 KB | | | | |
| | Timeouts | `/chat` upstream 110 s, `/tts` 30 s, `/health` 10 s | generation deadline `GEN_DEADLINE_SECONDS` (80), TTS 15 s | `/chat` 120 s, `/tts` 30 s, `/health` 5 s | | |
| Timeouts must stay ordered: backend < Worker < client, so the user gets a meaningful `504` instead of an abort. | |
| ## Configuration (backend env vars) | |
| | Var | Default | Purpose | | |
| | --- | --- | --- | | |
| | `MODEL_REPO`, `MODEL_FILE` | set in `Dockerfile` | GGUF model to download from Hugging Face. | | |
| | `CHAT_FORMAT` | `llama-3` | llama-cpp chat template; `auto` uses the one embedded in the GGUF. Use `chatml` for the Qwen3 model from `mirai-train`. | | |
| | `EMOTION_TAGS` | off | `1` adds the emotion-tag rule to the system prompt. Needed for models trained by `mirai-train`; leave off for the old Llama 3 model. | | |
| | `N_CTX` | 2048 | Context window. | | |
| | `PROMPT_TOKEN_BUDGET` | 900 | Extra cap on prompt tokens (slow CPU prefill). | | |
| | `N_THREADS`, `N_THREADS_BATCH`, `N_BATCH` | 2, = `N_THREADS`, 256 | CPU tuning. | | |
| | `TEMPERATURE` | 0.5 | Sampling temperature. | | |
| | `GEN_DEADLINE_SECONDS` | 80 | Hard stop for one generation. | | |
| | `KNOWLEDGE_HIDE_PREFIXES` | `Tên thật` | Lines of `app/data/knowledge_data.txt` starting with these (comma separated) are kept out of the prompt. | | |
| | `SPACE_API_KEY`, `REQUIRE_API_KEY`, `ENABLE_DOCS` | unset | See above. | | |