# Changelog All notable changes to AI-RPG are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [1.0.3] — 2026-06-20 ### Fixed - **app/config.py**: fixed `SettingsError: error parsing value for field "cors_origins" from source "EnvSettingsSource"` that crashed the backend on startup when `CORS_ORIGINS` was set as a comma-separated string (e.g. `http://localhost:8080,http://localhost:5173,http://localhost`). - Root cause: pydantic-settings v2 by default tries to JSON-parse complex-typed env vars before applying field validators. The comma-separated string isn't valid JSON, so parsing failed before our `@field_validator(mode="before")` could split it. - Fix: declared `cors_origins: Annotated[list[str], NoDecode]` — `NoDecode` is a pydantic-settings marker that disables JSON pre-parsing, so the raw string reaches our `_split_cors` validator unchanged. - **requirements.txt**: bumped `pydantic` 2.7.1 → 2.9.2 and `pydantic-settings` 2.2.1 → 2.7.0. The `NoDecode` annotation was only introduced in pydantic-settings 2.6+, so the older versions couldn't support the fix above. All 68 unit tests still pass with the new versions. ## [1.0.2] — 2026-06-20 ### Fixed - **requirements.txt**: added missing `email-validator==2.2.0` dependency. Pydantic's `EmailStr` type (used in `RegisterRequest`, `AdminRegisterRequest`, `LoginRequest.user.email`, `UserPublic.email`, `TokenResponse.user.email`) requires this package at runtime, but it's not bundled with pydantic itself. Without it the backend crashed at startup with `ImportError: email-validator is not installed, run pip install pydantic[email]`. Also removed a duplicate `httpx==0.27.0` line. ## [1.0.1] — 2026-06-20 ### Fixed - **docker-compose.yml**: removed obsolete `version: "3.9"` (caused warning in modern Docker Compose). - **docker-compose.yml**: fixed frontend build context — was `./frontend` (broke `COPY deploy/nginx.conf` and `COPY frontend/package*.json` in Dockerfile.frontend). Now correctly `.` (project root) with `dockerfile: deploy/Dockerfile.frontend`. - **docker-compose.yml**: fixed frontend port mapping — was `5173:5173` but the frontend container is nginx on port 80. Now `8080:80` so the app is accessible at `http://localhost:8080`. - **docker-compose.yml**: added `extra_hosts: ["host.docker.internal:host-gateway"]` to the backend service — enables `LLM_API_URL=http://host.docker.internal:11434/v1` to work on Linux hosts (not just Docker Desktop on Mac/Windows). - **docker-compose.yml**: added `VITE_API_BASE_URL: /api` build arg for the frontend service — bakes the relative `/api` URL into the Vite bundle so the browser uses the same origin + nginx proxies `/api` → `backend:8000`. - **deploy/Dockerfile.frontend**: added `ARG VITE_API_BASE_URL=/api` + `ENV` so the build arg actually gets baked into the Vite bundle. - **deploy/nginx.conf**: extended SSE timeouts from 300s → 600s; added `proxy_send_timeout`; added `Upgrade`/`Connection` headers for future WebSocket support; added gzip for static assets. - **.env.example**: clarified `VITE_API_BASE_URL` — only used by local `npm run dev`, ignored by docker build (which uses `/api` relative). Removed the misleading `http://localhost/api` default. - **frontend/src/lib/api.ts**: now respects `VITE_API_BASE_URL` env var with fallback to relative `/api`. Works both for local dev (point at separate backend) and Docker (nginx proxy). - **frontend/src/vite-env.d.ts**: added Vite env type declarations so TypeScript knows about `import.meta.env.VITE_API_BASE_URL`. - **app/api/deps.py**: added `?access_token=` query parameter fallback for SSE endpoints. Native `EventSource` cannot send `Authorization` headers, so the frontend SSE client passes the token via query string. Without this fix, all SSE endpoints (`/iterate/stream`, `/builder/stream`, `/editor/stream`) returned 401. - **.dockerignore**: added at project root — excludes `node_modules/`, `__pycache__/`, `.venv/`, `data/`, `.git/`, etc. from Docker build contexts (faster builds, smaller context transfer). ## [1.0.0] — 2026-06-20 ### Added — Sprint 1: Foundation - docker-compose with 4 services: db (PostgreSQL 15), qdrant (1.9), backend (FastAPI/uvicorn), frontend (Vite dev / nginx prod). - SQLAlchemy 2.x async models for all 10 tables: `users`, `settings`, `world_presets`, `worlds`, `entities`, `steps`, `step_tool_calls`, `deferred_triggers`, `story_entries`, `llm_call_logs`. - Alembic-equivalent idempotent migration `001_initial_schema.py` (creates all tables, no pgvector). - `init_qdrant.py` creates collections `entities` and `story_entries` with payload indexes on `world_id`, `entity_type`, `entry_type`, `deleted`, `created_at`. - Settings seed: 33 default keys (llm.*, embeddings.*, context.*, qdrant.*, game.*, ui.*, admin.setup_token). - 2 builtin presets: Classic Fantasy, Deep Space Outpost. - `GET /api/health` returns `{status, db, qdrant, llm, embeddings, version}`. ### Added — Sprint 2: Auth + Admin - `POST /api/register` (only if at least one admin exists), `POST /api/register/admin?token=`, `POST /api/auth/login` (email or username), `POST /api/auth/refresh`, `POST /api/auth/logout`, `GET /api/auth/me`. - JWT (access + refresh) with python-jose, password hashing with passlib[bcrypt]. - Password strength validation: ≥8 chars, ≥1 letter, ≥1 digit, blacklist of trivial passwords. - Anti-enumeration: same `401 invalid_credentials` for "user not found" and "wrong password". - Admin setup token: auto-generated on first startup, printed in logs. - `GET/PATCH /api/admin/settings` with secret masking. - `GET /api/admin/llm-logs` with filters (world_id, stage, status, pagination) + detail view. - `GET /api/admin/users`, `PATCH /api/admin/users/{id}` (admin/active toggle). - `GET /api/admin/stats` (users, worlds, steps, avg LLM latency). - Diagnostic endpoints: `POST /api/admin/test/llm`, `/test/llm-tools`, `/test/embeddings`, `/test/embeddings/probe-dimension`, `/embeddings/recreate-collections`. - `POST /api/admin/upload-icon` (multipart, favicon/logo/og_image, ≤1MB, PNG/SVG/JPG/WebP/ICO). ### Added — Sprint 3: World Builder - `LlmClient` — OpenAI-compatible chat completions client with retry (3 attempts, exp backoff), streaming, tool-calling, separate-transaction logging. - `MockLlmClient` — replay-based mock for tests/dev when no LLM configured. - `app/prompts/` — 11 stage prompts in English (world_builder_schema/env/entities, world_editor, orchestrator_phase1/2/3_summary/3_suggest, intro_scene, subagent, summary). - `POST /api/worlds` creates draft world + returns SSE URL. - `GET /api/sessions/worlds/{id}/builder/stream` runs the 4-step builder flow: schemas → environment → entities (tool-calling loop) → intro scene (with submit_step + suggest_actions). - If preset_id provided, schemas/environment copied from preset (skip first LLM call). ### Added — Sprint 4: World Editor - `POST /api/worlds/{id}/edit` body `{instruction}` → SSE URL. - `GET /api/sessions/worlds/{id}/editor/stream?instruction=` runs LLM with world_editor tools. - `propose_changes` tool returns diff, `ask_user` emits clarification event, `comment_to_user` for chat. - Optimistic locking via `updated_at` on `PATCH /api/worlds/{id}` (409 state_conflict on mismatch). - `PATCH /api/worlds/{id}` for direct JSON edits. - `DELETE /api/worlds/{id}` soft-delete (status='archived'). ### Added — Sprint 5: Orchestrator - `POST /api/sessions/worlds/{id}/iterate` body `{action, action_source}` creates new step + returns SSE URL. - `GET /api/sessions/worlds/{id}/iterate/stream?step_id=` runs the 3-phase orchestrator: - **Phase 1**: tool-calling loop with `submit_plan` terminator, max 8 substeps (configurable). - **Phase 2**: writer LLM call with `submit_step`, streams `scene_chunk` events. - **Phase 3**: persist + deferred triggers + summary (if history > threshold) + suggest_actions. - `POST /api/sessions/worlds/{id}/retry` soft-deletes last step + creates new one with same action. - `POST /api/sessions/worlds/{id}/rollback` soft-deletes last step. - 17 game tools: entity_create/get/list/update/delete, env_update/get, update_plot_rails, advance_time, schedule_trigger, calc (with dice), random_choice (deterministic), rag_query/add, run_subagent, submit_plan, submit_step, suggest_actions. - 4 schema tools: schema_add_type/add_field/remove_field/modify_field. ### Added — Sprint 6: Frontend polish - React 18 + Vite 5 + TypeScript 5 + Tailwind CSS 3 + zustand 4 + react-i18next 14. - Dark theme by default (Tailwind `dark:` class on ``). - 8 pages: Login, Register, AdminRegister, WorldsList, WorldBuilder, WorldEdit, Play, Admin. - 23 components: 9 UI primitives, 5 session, 3 worlds, 6 admin. - 5 zustand stores: auth, ui, worlds, session, toast. - i18n bundles in English + Russian (complete). - Native EventSource SSE client with reconnect + Last-Event-ID. - Mobile-first responsive layout. - TypeScript strict mode, 0 type errors, `npm run build` succeeds (340 KB JS / 22 KB CSS). ### Added — Sprint 7: RAG + Triggers + Summary + Context - `app/core/rag.py` — two-stage retrieval: Qdrant vector search → PostgreSQL hydration. - `HashEmbedder` (offline, deterministic) and `OpenAIEmbedder` (OpenAI-compatible API) implementations. - Embedder provider selection via `embeddings.provider` setting (`offline_hash` | `openai`). - Auto-fallback: `embeddings.api_url` falls back to `llm.api_url` if empty (and same for api_key). - Embedder cache with `reset_embedder_cache()` (called on settings update). - `rag_query` returns empty list on embedder/Qdrant failure (non-blocking). - `rag_add` saves story entry with `embedding_status='pending'` if embedding fails (background indexer can retry). - `index_entity` helper for entity vectors. - World isolation via Qdrant payload filter `world_id`. - `_cleanup_qdrant(world_id)` deletes all points for a world on world delete (best-effort). - Deferred triggers: Phase 3.1 fires triggers where `fire_at <= current_time`, appends summary to scene_text, marks `is_fired=true`. - Summary: Phase 3.2 generates summary when `len(recent_steps) > compression_threshold_messages`, stores as StoryEntry with `metadata.type=summary`. - Context manager: builds messages with system prompt + optional summary + last N guaranteed messages + current action. ### Added — Sprint 8: Production - `README.md` with quickstart, architecture diagram, API overview, testing instructions. - `pytest.ini` + 65+ unit tests across 8 test files. - `CHANGELOG.md` (this file). - `docker-compose.yml` with healthchecks for db and qdrant. - `deploy/Dockerfile.backend`, `deploy/Dockerfile.frontend`, `deploy/nginx.conf`. - `.env.example` with all 24 env vars documented. - `.gitignore` for Python, Node, env, IDE, data dirs. - Frontend production build verified (`npm run build` produces `dist/`).