Commit c58b8046 authored by Kantz's avatar Kantz
Browse files

readme to english and comments in api

parent bf7cc21d
# Der Tutor
# The Tutor
Dies ist ein Tutor-Bot für ein digitales Lehrwerk. Die App unterstützt aktüll vier Orchestratoren:
This is an LLM-based tutor app for a digital course script. The app currently supports four orchestrators:
1. `qa` für Fragen zu den Inhalten des Materials
2. `tutor` für einen offeneren Tutor-Dialog
3. `task` für Aufgaben aus dem Lehrwerk
4. `socratic` für einen sokratischen Dialog zu einem Thema
1. `qa` for questions about the course material
2. `tutor` for a more open tutoring dialog
3. `task` for working on tasks from the course material
4. `socratic` for a Socratic dialog on a topic
Nicht für alle Themen ist bereits Material vorhanden.
Material is not available for every topic yet.
## Voraussetzungen
## Prerequisites
- Python für das Backend
- Node.js 22.x oder neür für das Frontend
- pnpm in der Version aus `frontend/package.json`
- Eine Postgres-Datenbank mit `pgvector`
- Inhaltsdateien unter `backend/sources/lecture_script`
- Für `task` zusätzlich Aufgaben unter `backend/sources/tasks`
- Ein konfigurierter LLM-Provider über `LLM_PROVIDER`
- Ein konfigurierter Embedding-Provider über `EMBEDDING_PROVIDER`
- Python for the backend
- Node.js 22.x or newer for the frontend
- `pnpm` in the version pinned in `frontend/package.json`
- A Postgres database with `pgvector`
- Content files under `backend/sources/lecture_script`
- For `task`, additional task files under `backend/sources/tasks`
- A configured LLM provider via `LLM_PROVIDER`
- A configured embedding provider via `EMBEDDING_PROVIDER`
Unterstützte LLM-Provider:
Supported LLM providers:
- `openai`
- `gwdg`
- `mistral`
- `ollama`
Unterstützte Embedding-Provider:
Supported embedding providers:
- `sentence-transformer`
- `openai`
......@@ -35,7 +35,7 @@ Unterstützte Embedding-Provider:
## Setup
Konfiguriere zürst `backend/.env` anhand von `backend/.env-example`.
Configure `math-tutor/backend/.env` based on `math-tutor/backend/.env-example`.
Backend:
......@@ -54,32 +54,31 @@ corepack enable
pnpm install --frozen-lockfile
```
## Wichtige Konfiguration
## Important Configuration
Backend-Variablen in `math-tutor/backend/.env`:
Backend variables in `math-tutor/backend/.env`:
- `POSTGRES_URL`
- `FRONTEND_URL`
- `ORCHESTRATOR`
- `LLM_PROVIDER`
- `EMBEDDING_PROVIDER`
Je nach Provider werden weitere Variablen aus `backend/.env-example` benötigt, zum Beispiel:
Depending on the provider, you also need more variables from `backend/.env-example`, for example:
- `OPENAI_BASE_URL`, `OPENAI_API_KEY`, `OPENAI_CHAT_MODEL`
- `GWDG_BASE_URL`, `GWDG_API_KEY`, `GWDG_CHAT_MODEL`
- `MISTRAL_API_KEY`, `MISTRAL_CHAT_MODEL`
- `OLLAMA_URL`, `OLLAMA_MODEL`
Frontend-Variablen in `math-tutor/frontend/.env`:
Frontend variables in `math-tutor/frontend/.env`:
- `VITE_FRONTEND_LANG`
- `VITE_SHOW_CHATS_BUTTON`
- optional `VITE_API_BASE_URL` für den Dev-Proxy
- `VITE_SHOW_ARCHIVED_CHATS_BUTTON`
- optional `VITE_API_BASE_URL` for the dev proxy target
## Anwendung starten
## Run the App
Das Backend führt beim Start Readiness-Checks, Datenbankinitialisierung für LLM-Quotas und ein Embedding-Warmup aus. Stelle deshalb sicher, dass Datenbank und Provider-Konfiguration erreichbar sind.
On startup, the backend runs readiness checks, initializes database state for LLM quotas, and warms up embeddings. Make sure the database and provider configuration are reachable before starting it.
Backend:
......@@ -96,11 +95,11 @@ cd math-tutor/frontend
pnpm run dev
```
Das Vite-Dev-Frontend läuft auf `http://localhost:5173`. API-Aufrufe gehen im Development über den Vite-Proxy an das Backend.
The Vite dev server runs on `http://localhost:5173`. In development, API calls are proxied to the backend through Vite.
## Dev-Proxy
## Dev Proxy
Das Frontend ruft APIs relativ über `/api/...` auf. Im Development leitet Vite diese Reqüsts an das Backend weiter.
The frontend sends API requests to `/api/...`. In development, Vite forwards them to the backend.
Optional in `math-tutor/frontend/.env`:
......@@ -108,47 +107,47 @@ Optional in `math-tutor/frontend/.env`:
VITE_API_BASE_URL="http://localhost:8000"
```
Ohne diese Variable wird standardmäßig `http://localhost:8000` verwendet.
If you do not set it, Vite uses `http://localhost:8000` by default.
## Task Deep Links
Ein Aufgaben-Chat kann direkt per URL geöffnet werden:
You can open a task chat directly by URL:
`/chat?orchestrator=task&file_id=<task_file_id>&task_id=<task_id>`
Beispiel:
Example:
`http://localhost:5173/chat?orchestrator=task&file_id=abc-formel-anwenden&task_id=03`
Hinweise:
Notes:
- `file_id` muss im Aufgabenkatalog vorhanden sein.
- `task_id` muss in dieser Datei vorhanden sein.
- Bei ungültigen Werten fällt das Frontend auf `/select-task` zurück.
- Der Deep Link sperrt die Auswahl nicht daürhaft; Benutzer können später weiter wechseln.
- `file_id` must exist in the task catalog.
- `task_id` must exist inside that file.
- If the values are invalid, the frontend falls back to `/select-task`.
- The deep link does not lock the selection permanently; users can still switch tasks afterwards.
## Socratic Deep Links
Ein sokratischer Chat kann direkt per URL geöffnet werden:
You can open a Socratic chat directly by URL:
`/chat?orchestrator=socratic&topic_key=<topic_key>`
Beispiel:
Example:
`http://localhost:5173/chat?orchestrator=socratic&topic_key=klammerrechnung`
Hinweise:
Notes:
- `topic_key` muss im sokratischen Themenkatalog vorhanden sein.
- Die App verwendet normalisierte Themen-Schlüssel in der URL.
- Bei ungültigen Werten fällt das Frontend auf `/select-socratic` zurück.
- Der Deep Link sperrt die Themenauswahl nicht daürhaft.
- `topic_key` must exist in the Socratic topic catalog.
- The app uses normalized topic keys in the URL.
- If the values are invalid, the frontend falls back to `/select-socratic`.
- The deep link does not lock the topic selection permanently.
## Datenbank und Retrieval
## Database and Retrieval
Stelle sicher, dass `POSTGRES_URL` und die benötigten Embedding-Provider-Variablen in `backend/.env` gesetzt sind.
Make sure `POSTGRES_URL` and the required embedding provider variables are set in `backend/.env`.
Beispiel:
Example:
```env
POSTGRES_URL=postgresql://user:pass@host:5432/db
......@@ -157,7 +156,7 @@ SENTENCE_TRANSFORMER_MODEL=jinaai/jina-embeddings-v5-text-small-retrieval
EMBEDDING_DIM=512
```
Schema initialisieren:
Initialize the schema:
```powershell
cd math-tutor/backend
......@@ -165,7 +164,7 @@ cd math-tutor/backend
python -m scripts.retrieval_cli init-db
```
Material einbetten:
Ingest the material:
```powershell
cd math-tutor/backend
......@@ -173,46 +172,44 @@ cd math-tutor/backend
python -m scripts.retrieval_cli ingest --base sources/lecture_script --clear
```
Die Ingestion erwartet innerhalb von `sources/lecture_script` die aktüllen Unterordner:
The ingest step currently expects these subfolders inside `sources/lecture_script`:
- `sections`
- `subsections`
- `subsubsections`
- `childs`
Retrieval über die CLI:
Run retrieval from the CLI:
```powershell
cd math-tutor/backend
.\.venv\Scripts\Activate.ps1
python -m scripts.retrieval_cli qüry --q "Was ist eine Teilmenge?" --k 8 --expand
python -m scripts.retrieval_cli query --q "Was ist eine Teilmenge?" --k 8 --expand
```
## Orchestrator- und Retrieval-Konfiguration
## Orchestrator and Retrieval Configuration
Der Standard-Orchestrator wird über `ORCHESTRATOR` in `backend/.env` festgelegt. Verfügbare Werte sind:
Available orchestrators are:
- `qa`
- `tutor`
- `task`
- `socratic`
Die registrierten Orchestratoren liegen unter `backend/app/deterministic_services/orchestrators/`.
If a chat request does not specify an orchestrator, the backend falls back to `qa`.
Die zentralen Retrieval-Defaults liegen aktüll in:
Registered orchestrators live in `backend/app/deterministic_services/orchestrators/`.
The main retrieval defaults currently live in:
- `backend/app/deterministic_services/orchestrators/orchestrator_base.py`
- `backend/app/deterministic_services/retrieval_store.py`
Die API für die Orchestrator-Konfiguration liegt in:
- `backend/app/api/orchestrator.py`
## Tests
Die Tests liegen unter `backend/test`.
Tests live under `backend/test`.
Ein einfacher Gesamtlauf ist:
A simple full run is:
```powershell
cd math-tutor/backend
......@@ -220,7 +217,7 @@ cd math-tutor/backend
python -m unittest discover test
```
Einzelne Tests können weiterhin direkt als Modul gestartet werden, zum Beispiel:
You can still run individual test modules directly, for example:
```powershell
cd math-tutor/backend
......@@ -228,7 +225,7 @@ cd math-tutor/backend
python -m test.generate_socratic_chats_test
```
## Sokratische Initial-Prompts generieren
## Generate Socratic Initial Prompts
```powershell
cd math-tutor/backend
......@@ -238,26 +235,26 @@ python -m scripts.generate_socratic_chats --source-root sources/lecture_script -
## Docker Compose Template
Der aktülle Compose-Stack in `math-tutor/docker/docker-compose.yaml` ist ein Deployment-Template für einen externen Docker-Network-Namen `web`.
The current Compose stack in `math-tutor/docker/docker-compose.yaml` is a deployment template for an external Docker network named `web`.
Aktüll definiert die Datei:
It currently defines:
- einen Service `backend-openai`
- einen Service `frontend-openai`
- keine direkten Port-Mappings nach außen
- ein externes Netzwerk `web`
- a `backend-openai` service
- a `frontend-openai` service
- no direct public port mappings
- an external `web` network
Starten:
Start it with:
```powershell
cd math-tutor/docker
docker compose up --build -d
```
Wichtige Hinweise:
Notes:
- Das Compose-Template erwartet `backend/.env-openai`.
- Das Frontend wird im Container von Nginx auf Port `3000` ausgeliefert.
- Nginx im Frontend leitet `/api` an `http://backend-openai:8000` weiter.
- Ohne eigene Port-Mappings oder einen vorgelagerten Reverse Proxy ist die App nicht automatisch unter `http://localhost:80` erreichbar.
- Die Datei `math-tutor/docker/nginx.conf` existiert nicht; die verwendete Nginx-Konfiguration liegt unter `math-tutor/frontend/nginx.conf`.
- The Compose template expects `backend/.env-openai`.
- The frontend container is served by Nginx on port `3000`.
- Nginx proxies `/api` to `http://backend-openai:8000`.
- Without your own port mappings or an upstream reverse proxy, the app is not automatically reachable at `http://localhost:80`.
- `math-tutor/docker/nginx.conf` does not exist; the active Nginx config is `math-tutor/frontend/nginx.conf`.
# API Endpoint for the Canvas component. Images are saved at ./storage/drawings and extracting LaTeX using Mathpix.
from __future__ import annotations
import base64
......@@ -11,8 +13,12 @@ from fastapi import APIRouter, HTTPException
from pydantic import BaseModel, Field
from mpxpy.mathpix_client import MathpixClient
DRAWINGS_DIR = Path(__file__).resolve().parents[1] / "storage" / "drawings"
DRAWINGS_DIR.mkdir(parents=True, exist_ok=True)
router = APIRouter()
# Settings are set in the .env and loaded by config.py
settings = config.get_mathpix_settings()
mathpix_client = None
......@@ -48,9 +54,6 @@ def save_canvas(request: CanvasSaveRequest) -> CanvasSaveResponse:
raise HTTPException(
status_code=400, detail="invalid base64 payload") from exc
drawings_dir = Path(__file__).resolve().parents[1] / "storage" / "drawings"
drawings_dir.mkdir(parents=True, exist_ok=True)
timestamp = int(time.time())
safe_hint = request.filename_hint or "drawing"
safe_hint = "".join(
......@@ -59,8 +62,8 @@ def save_canvas(request: CanvasSaveRequest) -> CanvasSaveResponse:
safe_hint = "drawing"
base_name = f"{safe_hint}-{timestamp}"
img_path = drawings_dir / f"{base_name}.png"
txt_path = drawings_dir / f"{base_name}.txt"
img_path = DRAWINGS_DIR / f"{base_name}.png"
txt_path = DRAWINGS_DIR / f"{base_name}.txt"
with img_path.open("wb") as handle:
handle.write(raw)
......
# API Endpoint for the chat interactions. This includes handling user messages, generating assistant replies, and managing chat archives.
from __future__ import annotations
import logging
......@@ -116,6 +118,49 @@ def chat(request: ChatRequest) -> ChatResponse:
return ChatResponse(reply=reply, sources=sources)
# Initializes a chat session with a Socratic-style prompt based on the selected topic. This sets up the context and provides the first assistant message.
@router.post("/api/chat/bootstrap-socratic", response_model=SocraticBootstrapResponse)
def bootstrap_socratic(request: SocraticBootstrapRequest) -> SocraticBootstrapResponse:
chat_id = context_store.get_chat_id([], draft=request.draft)
sheet = context_store.load_sheet(chat_id)
if not sheet:
sheet = context_store.context_store_new.init_sheet(chat_id, [])
try:
topic_entry = socratic_oranisator.get_topic_entry(request.topic_key)
except ValueError as exc:
raise HTTPException(status_code=404, detail="topic not found") from exc
task_catalog.set_selected_topic(sheet, {}, topic_entry)
refs = task_catalog.get_selected_topic_parent_refs(sheet)
sources = retrieval_store.retrieve_for_parent_refs(
pg_url=config.get_postgres_url(),
parent_refs=refs,
)
context_store.set_sources(sheet, sources)
context_store.set_initialized(sheet, True)
context_store.save_sheet(sheet)
try:
reply = socratic_oranisator.get_initial_message(request.topic_key)
except ValueError as exc:
logger.exception("Socratic bootstrap prompt lookup failed")
raise HTTPException(status_code=500, detail=str(exc)) from exc
context_store.append_history_message(sheet, "assistant", reply)
context_store.save_sheet(sheet)
return SocraticBootstrapResponse(
reply=reply,
sources=[source.model_dump() for source in sources],
)
# ---
# Archive chats.
# Activatable via VITE_SHOW_ARCHIVED_CHATS_BUTTON="true" in frontend .env.
# Chat are achived in logs/chat_sessions/archive.jsonl
# ---
@router.get("/api/chat/archives", response_model=List[ChatArchiveSummary])
def list_archives(limit: int = Query(20, ge=1, le=200)) -> List[ChatArchiveSummary]:
......@@ -183,40 +228,3 @@ def archive_chat(request: ChatRequest) -> ChatArchiveResponse:
raise HTTPException(status_code=502, detail="chat archive failed") from exc
return ChatArchiveResponse(status="ok", chat_id=chat_id)
@router.post("/api/chat/bootstrap-socratic", response_model=SocraticBootstrapResponse)
def bootstrap_socratic(request: SocraticBootstrapRequest) -> SocraticBootstrapResponse:
chat_id = context_store.get_chat_id([], draft=request.draft)
sheet = context_store.load_sheet(chat_id)
if not sheet:
sheet = context_store.context_store_new.init_sheet(chat_id, [])
try:
topic_entry = socratic_oranisator.get_topic_entry(request.topic_key)
except ValueError as exc:
raise HTTPException(status_code=404, detail="topic not found") from exc
task_catalog.set_selected_topic(sheet, {}, topic_entry)
refs = task_catalog.get_selected_topic_parent_refs(sheet)
sources = retrieval_store.retrieve_for_parent_refs(
pg_url=config.get_postgres_url(),
parent_refs=refs,
)
context_store.set_sources(sheet, sources)
context_store.set_initialized(sheet, True)
context_store.save_sheet(sheet)
try:
reply = socratic_oranisator.get_initial_message(request.topic_key)
except ValueError as exc:
logger.exception("Socratic bootstrap prompt lookup failed")
raise HTTPException(status_code=500, detail=str(exc)) from exc
context_store.append_history_message(sheet, "assistant", reply)
context_store.save_sheet(sheet)
return SocraticBootstrapResponse(
reply=reply,
sources=[source.model_dump() for source in sources],
)
# API endpoints to reveal the context or soulution chat history and draft.
from __future__ import annotations
from typing import List, Optional
......@@ -23,6 +25,7 @@ class ContextRequest(BaseModel):
class DraftRequest(BaseModel):
draft: str = Field(..., min_length=1)
# gives the retrieval stored in the context store.
@router.post("/api/context/retrieval")
def get_retrieval_context(request: ContextRequest) -> List[dict]:
......@@ -41,6 +44,8 @@ def get_retrieval_context(request: ContextRequest) -> List[dict]:
return [source.model_dump() for source in sources]
# reveals the set solution in the task-mode.
@router.post("/api/context/solution")
def get_context_solution(request: DraftRequest) -> dict[str, str]:
sheet = context_store.load_sheet(request.draft)
......
# This module implements health and readiness checks for the application, including checks for LLM providers, MCP server, PostgreSQL database, and LLM quota.
from __future__ import annotations
import logging
......
# API endpoints for listing available tasks, getting task details, and selecting tasks or topics for the current session.
# Benennung der Funktionen aktuell noch unklar.
from __future__ import annotations
from typing import List
......
VITE_FRONTEND_LANG="de"
VITE_SHOW_CHATS_BUTTON="true"
VITE_SHOW_ARCHIVED_CHATS_BUTTON="true"
......@@ -166,7 +166,7 @@ const applyRetrievedSources = (
};
export default function ChatPage() {
const showChatsButton = String(import.meta.env.VITE_SHOW_CHATS_BUTTON ?? "true")
const showChatsButton = String(import.meta.env.VITE_SHOW_ARCHIVED_CHATS_BUTTON ?? "true")
.trim()
.toLowerCase() !== "false";
const navigate = useNavigate();
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment