Spaces:
Sleeping
Sleeping
|
Download docs/API.md from AnkurGoswami/test: direct link, hf CLI and curl.
- Browser
- Download file 6.81 kB
-
https://huggingface.co/spaces/AnkurGoswami/test/resolve/main/docs/API.md
- Command line
-
hf download hf://spaces/AnkurGoswami/test/docs/API.md
-
curl -L -o API.md https://huggingface.co/spaces/AnkurGoswami/test/resolve/main/docs/API.md
6.81 kB
| # ALAS Backend API | |
| FastAPI backend for the ALAS workplace soft-skills coaching app (`main.py`). It drives an LLM-played character through a scripted scenario, extracts live coaching notes as the conversation progresses, and produces a rubric-based assessment on demand. | |
| - **Base URL (local):** `http://localhost:8000` | |
| - **Run:** `uv run uvicorn main:app --reload --port 8000` | |
| - **Interactive docs:** `/docs` (Swagger UI) and `/redoc`, auto-generated by FastAPI | |
| - **Content type:** `application/json` for all requests/responses except `/chats/{chat_id}/stream`, which returns `text/event-stream` | |
| ## Session model | |
| State is kept **in memory** (`store: dict[chat_id -> session]`) β there is no database, and all sessions are lost on server restart. A session holds: | |
| | Field | Description | | |
| |---|---| | |
| | `agent` | `CharacterAgent` instance (LangChain-backed) that plays the AI role and holds conversation history | | |
| | `scenario_id` | The scenario this chat was started with | | |
| | `messages` | Ordered list of `{role, text, turn}` β the full transcript | | |
| | `notes` | Accumulated coaching notes, appended asynchronously | | |
| | `pending_turns` | Trainee turn numbers whose note extraction is still running in the background | | |
| A session must be created via `POST /chats` before any of the `/chats/{chat_id}/...` endpoints can be used, and every endpoint that takes a `chat_id` returns **404** if it doesn't exist (see [Errors](#errors)). | |
| ## Endpoints | |
| ### `GET /scenarios` | |
| All available scenarios, for a scenario picker UI. | |
| **Response 200** β `ScenariosResponse` | |
| ```json | |
| { | |
| "scenarios": [ | |
| { | |
| "id": "onboarding_report", | |
| "title": "string", | |
| "skill_area": "string", | |
| "ai_name": "string", | |
| "ai_role": "string", | |
| "learner_role": "string", | |
| "learning_goal": "string", | |
| "has_autoplay": true | |
| } | |
| ] | |
| } | |
| ``` | |
| Known scenario ids in this repo: `onboarding_report` (default), `coaching_employee`, `guest_price_complaint`. | |
| --- | |
| ### `POST /chats` | |
| Start a new chat session for a scenario. Returns a fresh `chat_id` and any scripted opening message from the AI character. | |
| **Request body** β `NewChatRequest` (optional; defaults to `onboarding_report`) | |
| ```json | |
| { "scenario_id": "onboarding_report" } | |
| ``` | |
| **Response 201** β `NewChatResponse` | |
| ```json | |
| { | |
| "chat_id": "uuid", | |
| "scenario_id": "onboarding_report", | |
| "messages": [ | |
| { "role": "manager", "text": "string", "turn": 0 } | |
| ] | |
| } | |
| ``` | |
| `messages` is empty if the scenario doesn't open with an AI line (`opens == "learner"`). | |
| **Errors** | |
| - `400` β unknown `scenario_id` | |
| --- | |
| ### `GET /chats/{chat_id}` | |
| Retrieve the full message history for a session. | |
| **Response 200** β `ChatHistoryResponse` | |
| ```json | |
| { | |
| "chat_id": "uuid", | |
| "messages": [ | |
| { "role": "trainee", "text": "string", "turn": 1 }, | |
| { "role": "manager", "text": "string", "turn": 1 } | |
| ] | |
| } | |
| ``` | |
| **Errors** | |
| - `404` β unknown `chat_id` | |
| --- | |
| ### `POST /chats/{chat_id}/message` | |
| Send a trainee message and get the full AI reply back in one blocking response. Note extraction for this turn is kicked off as a FastAPI background task (non-blocking). | |
| **Request body** β `ChatRequest` | |
| ```json | |
| { "message": "string" } | |
| ``` | |
| **Response 200** β `ChatResponse` | |
| ```json | |
| { | |
| "chat_id": "uuid", | |
| "reply": "string", | |
| "turn": 1, | |
| "messages": [ { "role": "trainee|manager", "text": "string", "turn": 1 } ] | |
| } | |
| ``` | |
| **Errors** | |
| - `404` β unknown `chat_id` | |
| --- | |
| ### `POST /chats/{chat_id}/stream` | |
| Same as `/message`, but streams the AI reply token-by-token over **Server-Sent Events**. | |
| **Request body** β `ChatRequest` (same as above) | |
| **Response 200** β `text/event-stream` | |
| ``` | |
| data: Hel | |
| data: lo | |
| data: ! | |
| data: [DONE] | |
| ``` | |
| Each `data:` line is a raw token chunk (not JSON β concatenate to reassemble the reply). The stream always ends with a `data: [DONE]` sentinel. Note extraction is scheduled on a background thread once streaming completes and is *not* reflected in this response β poll `GET /chats/{chat_id}/notes` and watch `pending_turns`. | |
| **Errors** | |
| - `404` β unknown `chat_id` | |
| --- | |
| ### `GET /skills` | |
| The rubric's skill list, in display order. Same set for every scenario β lets a UI render empty sections before any notes exist. | |
| **Response 200** β `SkillsResponse` | |
| ```json | |
| { "skills": [ { "slug": "string", "name": "string", "order": 0 } ] } | |
| ``` | |
| --- | |
| ### `GET /chats/{chat_id}/notes` | |
| Flat, insertion-ordered list of coaching notes extracted so far. Grouping by skill is left to the caller. | |
| **Response 200** β `NotesResponse` | |
| ```json | |
| { | |
| "chat_id": "uuid", | |
| "notes": [ | |
| { "skill": "string", "note": "string", "evidence": "string", "turn": 1 } | |
| ], | |
| "pending_turns": [2] | |
| } | |
| ``` | |
| `pending_turns` lists trainee turns whose extraction is still running in the background β poll this endpoint until a turn drops out of the list to know its notes (possibly none) are final. | |
| **Errors** | |
| - `404` β unknown `chat_id` | |
| --- | |
| ### `POST /chats/{chat_id}/assessment` | |
| Score the conversation so far against the rubric's skills. The result is **cached on the session** β repeat calls for the same `chat_id` return the same assessment without re-invoking the LLM. | |
| **Response 200** β `Assessment` | |
| ```json | |
| { | |
| "overall_summary": "string", | |
| "grades": [ | |
| { | |
| "skill": "string", | |
| "level": "Low | Medium | High", | |
| "reasoning": "string", | |
| "evidence_turns": [1, 3] | |
| } | |
| ], | |
| "key_moment": "string", | |
| "coaching_suggestion": "string", | |
| "readiness_score": 7 | |
| } | |
| ``` | |
| **Errors** | |
| - `400` β no messages yet to assess, or no rubric skills configured for the scenario | |
| - `404` β unknown `chat_id` | |
| --- | |
| ### `DELETE /chats/{chat_id}` | |
| Drop a session and free its in-memory state. | |
| **Response** β `204 No Content` | |
| **Errors** | |
| - `404` β unknown `chat_id` | |
| --- | |
| ### `GET /health` | |
| Liveness/readiness probe. | |
| **Response 200** | |
| ```json | |
| { "status": "ok", "active_sessions": 3 } | |
| ``` | |
| ## Errors | |
| All error responses use FastAPI's default shape: | |
| ```json | |
| { "detail": "chat_id '...' not found" } | |
| ``` | |
| | Status | Meaning | | |
| |---|---| | |
| | 400 | Bad request β unknown `scenario_id`, empty conversation on assessment, or no rubric configured | | |
| | 404 | `chat_id` not found (session expired, deleted, or server restarted) | | |
| | 422 | Request body failed Pydantic validation (e.g. missing `message` field) | | |
| ## Typical flow | |
| 1. `GET /scenarios` β let the user pick a scenario | |
| 2. `POST /chats` β get a `chat_id` | |
| 3. Repeat: `POST /chats/{chat_id}/message` (or `/stream`) for each trainee turn | |
| 4. `GET /chats/{chat_id}/notes` β poll for live coaching notes, checking `pending_turns` | |
| 5. `POST /chats/{chat_id}/assessment` β final rubric-based score once the conversation is done | |
| 6. `DELETE /chats/{chat_id}` β clean up when finished | |
| A minimal reference client for this exact flow lives in [api_client.py](api_client.py). | |