fix: JSON repair, WS thread pool, switch to DeepSeek V4 Flash, funnel Lv0
LLM fixes: - _try_repair_json() — ripara JSON malformato (apici, virgole trailing) - _strip_json_from_text() — rimuove residui JSON dalla narration - parse_tool_call() — rimuove troncamento 200 caratteri dal fallback DM fixes: - Fallback euristico quando params vuoti (dm.py) - Estrazione narrazione da dm_override/narrate params.params.text Prompt (prompts.py): - Vietati termini tecnici/scientifici/anacronistici (HPLC, laser, ...) - Aggiunto vocabolario fantasy obbligatorio Server (server.py): - dm_cycle in ThreadPoolExecutor per non bloccare WS ping - WebSocketDisconnect gestiti - Rimosso import inesistente db_schema Frontend (index.html): - Nascosti tool name dalla chat - Personaggio -> PG, connessioni aree con nomi Infrastruttura: - Switch da openrouter/free a DeepSeek V4 Flash via OpenCode Go - pyproject.toml, Dockerfile, docker-compose.yml, start.sh - .env auto-load (start.sh) - Funnel Lv0 (funnel.py) — creazione PG livello 0 Docs: architecture, database, setup, game mechanics, codebase ref
This commit is contained in:
1 parent
6b048dc0cc
commit
eacc946868
23 files changed
+1786
-61
No files matched your search
@@ -0,0 +1,216 @@
|
||||
# Codebase Reference
|
||||
|
||||
## Directory Layout
|
||||
|
||||
```
|
||||
rpg/
|
||||
├── src/
|
||||
│ ├── engine/
|
||||
│ │ ├── __init__.py # Public API del modulo engine
|
||||
│ │ ├── config.py # ProviderConfig, load_from_env()
|
||||
│ │ ├── db.py # SQLite schema, CRUD, adventure data access
|
||||
│ │ ├── dice.py # DCC dice system (roll, dice_chain)
|
||||
│ │ ├── dm.py # DM cycle orchestrator + CLI interactive
|
||||
│ │ ├── game_context.py # GameContext dataclass (campagna + stato)
|
||||
│ │ ├── heuristic_fallback.py# Keyword parser per fallback LLM
|
||||
│ │ ├── llm.py # LLM abstraction, ask(), parse_tool_call()
|
||||
│ │ ├── prompts.py # System prompt, context builder, tool docs
|
||||
│ │ ├── providers.py # Ollama/OpenRouter/Local providers
|
||||
│ │ └── tools.py # @tool registry, 10 Approved Tools
|
||||
│ │
|
||||
│ └── web/
|
||||
│ ├── __init__.py
|
||||
│ ├── server.py # FastAPI app, REST, WebSocket, main()
|
||||
│ └── static/
|
||||
│ └── index.html # SPA frontend (888 righe CSS+JS+HTML)
|
||||
│
|
||||
├── data/
|
||||
│ ├── dcc_rpg.db # SQLite database (auto-creato)
|
||||
│ ├── adventure/
|
||||
│ │ └── avventura.json # Avventura machine-readable (9 aree)
|
||||
│ └── crit_fumble_tables.json # Tabelle critici e fumble (207 righe)
|
||||
│
|
||||
├── docs/
|
||||
│ └── plans/
|
||||
│ └── 2026-06-25-dcc-rpg-design.md # Design doc originale (314 righe)
|
||||
│
|
||||
├── docs_dev/ # Documentazione sviluppo (questo)
|
||||
├── knowledge/ # 15 file KB (regole DCC, ~3708 righe)
|
||||
├── input/ # PDF sorgente, reference booklet
|
||||
├── ocr_full/ # OCR output (58K parole)
|
||||
├── graphify-out/ # Grafo concettuale
|
||||
└── tests/ # Test (pytest)
|
||||
├── test_dice.py
|
||||
├── test_tools.py
|
||||
├── test_integration.py
|
||||
└── test_websocket.py
|
||||
```
|
||||
|
||||
## Modulo: `src/engine/config.py`
|
||||
|
||||
### `ProviderKind` = `"ollama" | "openrouter" | "local"`
|
||||
|
||||
### `ProviderConfig` (dataclass)
|
||||
| Campo | Default | Env Var |
|
||||
|---|---|---|
|
||||
| `kind` | `"openrouter"` | `DCC_LLM_PROVIDER` |
|
||||
| `ollama_base_url` | `http://192.168.0.50:11434` | `DCC_OLLAMA_URL` |
|
||||
| `ollama_model` | `gemma2:9b` | `DCC_OLLAMA_MODEL` |
|
||||
| `openrouter_api_key` | `""` | `DCC_OPENROUTER_KEY` |
|
||||
| `openrouter_model` | `"openrouter/free"` | `DCC_OPENROUTER_MODEL` |
|
||||
| `openrouter_free_fallback` | `True` | `DCC_OR_FREE_FALLBACK` |
|
||||
| `local_base_url` | `http://192.168.0.240/v1` | `DCC_LOCAL_URL` |
|
||||
| `local_api_key` | `""` | `DCC_LOCAL_KEY` |
|
||||
| `local_model` | `"big-pickle"` | `DCC_LOCAL_MODEL` |
|
||||
| `timeout` | `60` | `DCC_LLM_TIMEOUT` |
|
||||
|
||||
## Modulo: `src/engine/dice.py`
|
||||
|
||||
### `DICE_CHAIN`
|
||||
```
|
||||
d3 → d4 → d5 → d6 → d7 → d8 → d10 → d12 → d14 → d16 → d20 → d24 → d30
|
||||
```
|
||||
|
||||
### `SPECIAL_DICE`
|
||||
`d%` (100), `d100` (100), `d0` (0), `d1` (1), `d2` (2)
|
||||
|
||||
### API
|
||||
| Funzione | Parametri | Returns |
|
||||
|---|---|---|
|
||||
| `roll(die, count=1, modifier=0)` | die: str | `RollResult {die, count, modifier, results[], total, crit, fumble, raw}` |
|
||||
| `dice_chain(die, steps)` | steps: int (+=su, -=giù) | `DiceChainResult {input_die, steps, output_die, chain}` o `ValidationError` |
|
||||
| `roll_str(raw)` | "2d20+3" | `RollResult` |
|
||||
| `validate_die(die)` | die: str | `str | None` (normalizzato o None) |
|
||||
|
||||
### Regole crit/fumble
|
||||
- Solo su `d20`: 20 = crit, 1 = fumble
|
||||
- Critico: moltiplica danno, tiro su tabella critici per classe
|
||||
- Fumble: tiro su tabella fumble (dado dipende da armatura)
|
||||
|
||||
## Modulo: `src/engine/tools.py`
|
||||
|
||||
### `@tool` decorator
|
||||
Ogni funzione decorata viene automaticamente registrata in `TOOL_REGISTRY`.
|
||||
|
||||
### `run(tool_name, **kwargs)` — dispatcher centralizzato
|
||||
Cerca in `TOOL_REGISTRY`, chiama la funzione, cattura eccezioni.
|
||||
|
||||
### 10 Approved Tools
|
||||
|
||||
| Tool | Funzione | Parametri Chiave |
|
||||
|---|---|---|
|
||||
| `roll_dice` | Tira dado/i | `die`, `count`, `modifier` |
|
||||
| `dice_chain` | Risolve catena dadi | `die`, `steps` |
|
||||
| `combat` | Ciclo combattimento completo | `character_id`, `target_ac`, `attack_bonus`, `damage_die`, `mighty_deed_die`, `monster_id`, `armor_type` |
|
||||
| `ability_check` | Prova abilità | `character_id`, `dc`, `modifier` |
|
||||
| `saving_throw` | Tiro salvezza | `character_id`, `save_type` (Tempra/Riflessi/Volontà), `dc` |
|
||||
| `movement` | Movimento | `distance_m`, `terrain`, `stealth` |
|
||||
| `spellcasting` | Incantesimo | `character_id`, `spell_name`, `spell_check`, `dc` |
|
||||
| `update_inventory` | Gestione inventario | `character_id`, `item`, `action` (add/remove/equip/unequip/list) |
|
||||
| `award_xp` | Assegna PE | `character_id`, `xp` |
|
||||
| `dm_override` | DM override | `command`, `params` |
|
||||
|
||||
### Tool `combat` — Dettaglio flusso
|
||||
1. Carica PG e mostro (con PF persistenti da combat_state)
|
||||
2. Tiro iniziativa (d20)
|
||||
3. Tiro attacco (d20 + bonus)
|
||||
4. Mighty Deed (se guerriero, dado d3/d4/d5 aggiuntivo)
|
||||
5. Crit/Fumble su 20/1 naturale
|
||||
6. Lookup tabella critici (per classe/livello) o fumble (per armatura)
|
||||
7. Calcolo danno + danno extra da critico
|
||||
8. Persistenza PF mostro in combat_state
|
||||
|
||||
### Tool `spellcasting` — Tier
|
||||
| Tiro | Risultato |
|
||||
|---|---|
|
||||
| 1 | Fallimento critico (corruzione!) |
|
||||
| < DC | Fallimento |
|
||||
| ≤ 11 | Successo minore (effetto base) |
|
||||
| ≤ 17 | Successo moderato (effetto migliorato) |
|
||||
| ≤ 19 | Successo maggiore (effetto potente) |
|
||||
| 20+ | Successo massimo (effetto devastante) |
|
||||
|
||||
## Modulo: `src/engine/providers.py`
|
||||
|
||||
### Provider Classes
|
||||
| Classe | Endpoint | Auth |
|
||||
|---|---|---|
|
||||
| `OllamaProvider` | `{base_url}/api/generate` | Nessuna |
|
||||
| `OpenRouterProvider` | `openrouter.ai/api/v1/chat/completions` | Bearer API key |
|
||||
| `LocalProvider` | `{base_url}/chat/completions` | Bearer key (opzionale) |
|
||||
|
||||
### `create_provider(cfg)` → Factory
|
||||
Legge `cfg.kind`, istanzia provider corretto con parametri appropriati.
|
||||
|
||||
## Modulo: `src/engine/dm.py`
|
||||
|
||||
### `dm_cycle(campaign_id, player_action)` → ciclo principale
|
||||
1. `GameContext.load(campaign_id)` — assembla contesto
|
||||
2. `_llm_decision(ctx, action)` — LLM + fallback
|
||||
3. `tools.run(tool_name, **params)` — esecuzione
|
||||
4. `db.advance_turn()` + `db.log_turn()` — persistenza
|
||||
5. Return response dict
|
||||
|
||||
### `interactive(campaign_id)` — CLI interattiva
|
||||
Loop input/output con prompt `⚔️ », comandi: esci, stato
|
||||
|
||||
## Modulo: `src/engine/prompts.py`
|
||||
|
||||
### `SYSTEM_PROMPT` — Prompt di sistema (~60 righe)
|
||||
- Stile narrazione
|
||||
- Meccaniche base DCC
|
||||
- Tool disponibili con parametri (json)
|
||||
- Esempi di chiamata
|
||||
- Formato risposta obbligatorio
|
||||
|
||||
### `build_prompt(ctx, player_action, current_area_id)` → prompt completo
|
||||
- `format_context(ctx)`: contesto campagna
|
||||
- `format_area_context(area_id)`: descrizione area attuale
|
||||
- Azione giocatore
|
||||
|
||||
## Modulo: `src/engine/db.py`
|
||||
|
||||
Vedi `database-schema.md` per schema completo.
|
||||
|
||||
### Lifecycle
|
||||
1. `init_db()` → init_schema + migrate + populate_from_json
|
||||
2. Connessioni thread-local con `threading.local()`
|
||||
3. WAL mode + busy_timeout per concorrenza
|
||||
|
||||
## Modulo: `src/web/server.py`
|
||||
|
||||
### Entry Point
|
||||
```bash
|
||||
python -m src.web.server
|
||||
# oppure uvicorn src.web.server:app --host 0.0.0.0 --port 8300
|
||||
```
|
||||
|
||||
### REST Endpoints
|
||||
| Metodo | Path | Descrizione |
|
||||
|---|---|---|
|
||||
| GET | `/api/campaigns` | Lista campagne |
|
||||
| GET | `/api/campaign/{id}` | Stato campagna completo |
|
||||
| POST | `/api/campaign?name=...` | Crea campagna |
|
||||
| POST | `/api/campaign/{id}/character?name=...&char_class=...` | Aggiunge PG default |
|
||||
| GET | `/health` | Health check |
|
||||
| GET | `/` | Frontend SPA |
|
||||
|
||||
### WebSocket `/ws/{campaign_id}`
|
||||
| Messaggio | Direzione | Descrizione |
|
||||
|---|---|---|
|
||||
| `{"type": "action", "payload": "..."}` | Client→Server | Azione giocatore |
|
||||
| `{"type": "ping"}` | Client→Server | Keepalive |
|
||||
| `{"type": "state", "campaign": {...}}` | Server→Client | Stato completo |
|
||||
| `{"type": "response", "turn": N, ...}` | Server→Client | Risultato turno |
|
||||
| `{"type": "pong"}` | Server→Client | Keepalive response |
|
||||
| `{"type": "error", "message": "..."}` | Server→Client | Errore |
|
||||
|
||||
## File: `src/web/static/index.html`
|
||||
|
||||
Single-page application (~888 righe) con:
|
||||
- **Welcome screen**: lista campagne, creazione nuova
|
||||
- **Sidebar** (320px): scheda PG (statistiche, HP bar), missioni, mappa aree
|
||||
- **Chat area**: messaggi player/DM con stili differenziati
|
||||
- **Input bar**: campo testo + bottone invio
|
||||
- **WebSocket** con auto-reconnect ogni 3s
|
||||
- Tema scuro "sword & sorcery" con font Cinzel + Cormorant Garamond
|
||||
Reference in new issue
Block a user