test / docs /API.md
Gazia Aziz
Backend API documentation
702c725
|
Raw History Blame Contribute Delete
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).

Endpoints

GET /scenarios

All available scenarios, for a scenario picker UI.

Response 200 β€” ScenariosResponse

{
  "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)

{ "scenario_id": "onboarding_report" }

Response 201 β€” NewChatResponse

{
  "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

{
  "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

{ "message": "string" }

Response 200 β€” ChatResponse

{
  "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

{ "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

{
  "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

{
  "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

{ "status": "ok", "active_sessions": 3 }

Errors

All error responses use FastAPI's default shape:

{ "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.