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,212 @@
|
||||
# Development Guide
|
||||
|
||||
## Come Aggiungere un Nuovo Tool
|
||||
|
||||
1. Apri `src/engine/tools.py`
|
||||
2. Scrivi una funzione con `@tool` decorator:
|
||||
|
||||
```python
|
||||
@tool
|
||||
def perception_check(character_id: int, dc: int, modifier: int = 0) -> ToolResult:
|
||||
char = storage.get_character(character_id)
|
||||
if not char:
|
||||
return {"success": False, "tool": "perception-check", "message": "PG non trovato."}
|
||||
atk = dice.roll("d20", modifier=modifier)
|
||||
passed = atk["total"] >= dc
|
||||
return {
|
||||
"success": True,
|
||||
"tool": "perception-check",
|
||||
"character_id": character_id,
|
||||
"roll": atk["total"],
|
||||
"dc": dc,
|
||||
"passed": passed,
|
||||
"message": f"Percezione: d20+{modifier} → {atk['total']} vs CD {dc} → {'Successo!' if passed else 'Fallimento.'}",
|
||||
}
|
||||
```
|
||||
|
||||
3. Aggiungi il tool a `_KNOWN_TOOLS` in `src/engine/prompts.py`:
|
||||
```python
|
||||
"perception_check": {"character_id": "id pg", "dc": "CD percezione", "modifier": "bonus/penalità"},
|
||||
```
|
||||
|
||||
4. Aggiungi un esempio a `TOOL_EXAMPLES` in `prompts.py`:
|
||||
```python
|
||||
- Tiro percezione CD 12 → {"tool": "perception_check", "params": {"character_id": 1, "dc": 12}, "narration": "Tendi l'orecchio..."}
|
||||
```
|
||||
|
||||
5. **Fatto.** Il tool viene automaticamente registrato in `TOOL_REGISTRY`.
|
||||
|
||||
## Come Aggiungere un Provider LLM
|
||||
|
||||
1. Apri `src/engine/providers.py`
|
||||
2. Crea una nuova classe che estende `LLMProvider`:
|
||||
```python
|
||||
class AnthropicProvider(LLMProvider):
|
||||
def __init__(self, api_key: str, model: str, timeout: int = 60):
|
||||
self.api_key = api_key
|
||||
self.model = model
|
||||
self.timeout = timeout
|
||||
|
||||
def ask(self, prompt: str, system: str = "") -> str:
|
||||
# Implementa chiamata API Anthropic qui
|
||||
...
|
||||
```
|
||||
|
||||
3. Registra in `_registry`:
|
||||
```python
|
||||
_registry = {
|
||||
"ollama": OllamaProvider,
|
||||
"openrouter": OpenRouterProvider,
|
||||
"local": LocalProvider,
|
||||
"anthropic": AnthropicProvider, # <-- aggiungi
|
||||
}
|
||||
```
|
||||
|
||||
4. Aggiungi il case in `create_provider()`:
|
||||
```python
|
||||
if cfg.kind == "anthropic":
|
||||
return cls(cfg.anthropic_api_key, cfg.anthropic_model, cfg.timeout)
|
||||
```
|
||||
|
||||
5. Aggiungi configurazione in `config.py`:
|
||||
```python
|
||||
anthropic_api_key: str = ""
|
||||
anthropic_model: str = "claude-sonnet-4-20250514"
|
||||
```
|
||||
|
||||
## Come Aggiungere una Nuova Area all'Avventura
|
||||
|
||||
1. Apri `data/adventure/avventura.json`
|
||||
2. Aggiungi area all'array `areas`:
|
||||
```json
|
||||
{
|
||||
"id": "1.10",
|
||||
"name": "Biblioteca Arcana",
|
||||
"order": 10,
|
||||
"description": "...",
|
||||
"atmosphere": "...",
|
||||
"connections": ["1.9"],
|
||||
"features": ["Scaffali polverosi", "Globo luminoso fluttuante"],
|
||||
"puzzles": [...],
|
||||
"traps": [],
|
||||
"monsters": [{"ref_id": "ancient_spirit", "count": 1, "tactics": "...", "ambush": false}],
|
||||
"items": [
|
||||
{"id": "scroll_of_truth", "name": "Pergamena della Verità", "description": "...", "type": "consumable", "magical": true}
|
||||
],
|
||||
"treasure": []
|
||||
}
|
||||
```
|
||||
|
||||
3. Aggiungi stat block del mostro in `monsters`:
|
||||
```json
|
||||
{
|
||||
"id": "ancient_spirit",
|
||||
"name": "Spirito Antico",
|
||||
"level": 3,
|
||||
"stats": {"ac": 14, "hp": 18, "attack": "d20+3", "damage": "1d6+1", "saves": {"Tempra": 2, "Riflessi": 4, "Volontà": 6}, "move": "9m (volando)"},
|
||||
"special": "Intangibile, colpito solo da magia o argento",
|
||||
"xp": 100,
|
||||
"loot": []
|
||||
}
|
||||
```
|
||||
|
||||
4. Riavvia il server. La nuova area viene caricata automaticamente.
|
||||
|
||||
## Come Aggiungere una Tabella Crit/Fumble
|
||||
|
||||
1. Aggiungi entry a `data/crit_fumble_tables.json`
|
||||
2. Riavvia il server (ricarica da JSON se DB vuoto)
|
||||
3. Se il DB è già popolato, cancella `data/dcc_rpg.db` (verrà ricreato)
|
||||
|
||||
## Test
|
||||
|
||||
```bash
|
||||
# Test dadi
|
||||
python src/engine/dice.py
|
||||
|
||||
# Test tools (con setup DB)
|
||||
python src/engine/tools.py
|
||||
|
||||
# Test LLM parsing
|
||||
python src/engine/llm.py
|
||||
|
||||
# Test DB completo
|
||||
python src/engine/db.py
|
||||
|
||||
# Avvia CLI interattiva
|
||||
python -c "
|
||||
from src.engine import dm, db
|
||||
db.init_db()
|
||||
camp_id = db.create_campaign('Test', 'Avventura')
|
||||
char_id = db.create_character(camp_id, 'Eroe', 'guerriero', 'soldato')
|
||||
db.update_character(char_id, level=1, str=15, agi=11, sta=14, per=9, int=8, lck=10, hp_current=12, hp_max=12, ac=15)
|
||||
dm.interactive(camp_id)
|
||||
"
|
||||
```
|
||||
|
||||
### Test esistenti (`tests/`)
|
||||
- `test_dice.py` — test modulo dadi (pytest)
|
||||
- `test_tools.py` — test tool execution
|
||||
- `test_integration.py` — test flusso dm_cycle
|
||||
- `test_websocket.py` — test WebSocket lifecycle
|
||||
|
||||
Per eseguire:
|
||||
```bash
|
||||
python -m pytest tests/ -v
|
||||
```
|
||||
|
||||
## Known LSP Issues (non bloccanti)
|
||||
|
||||
Il LSP segnala questi errori, ma non impediscono l'esecuzione:
|
||||
|
||||
| File | Errore | Causa |
|
||||
|---|---|---|
|
||||
| `db.py:762` | `Object of type "None" is not subscriptable` | `cursor.fetchone()` in `get_current_area` può restituire None |
|
||||
| `test_dice.py:47` | `output_die not in ValidationError` | `DiceChainResult` e `ValidationError` condividono TypedDict — meglio `@dataclass` |
|
||||
| `tests/test_websocket.py:8` | `Import pytest could not be resolved` | pytest non installato nell'ambiente LSP |
|
||||
| `game_context.py:29` | `get_current_area not in db` | LSP non riconosce funzioni dinamicamente caricate |
|
||||
|
||||
## Estendere il Frontend
|
||||
|
||||
Il frontend è un singolo file HTML (`src/web/static/index.html`) con:
|
||||
- CSS: tema scuro sword & sorcery con variabili CSS
|
||||
- JS: ~300 righe di vanilla JS (nessun framework)
|
||||
- WebSocket: connessione persistente, auto-reconnect
|
||||
|
||||
Per modificare:
|
||||
- **Stile**: modifica le variabili CSS in `:root` e classi dei componenti
|
||||
- **Nuova sezione sidebar**: aggiungi div in HTML + funzione render in JS
|
||||
- **Nuovo tipo di messaggio**: aggiungi case a `handleMessage()`
|
||||
|
||||
## Guida alla Conoscenza Base (knowledge/)
|
||||
|
||||
I 15 file in `knowledge/` contengono le regole DCC in formato markdown italiano.
|
||||
Non sono usati direttamente dal runtime (i dati sono in DB/JSON/code),
|
||||
ma sono la fonte di verità per lo sviluppo.
|
||||
|
||||
Per rigenerare prompt alla luce delle regole:
|
||||
1. Consulta i file `knowledge/01_meccanica_base.md` → `knowledge/15_dadi.md`
|
||||
2. Adatta `prompts.py` e `tools.py` di conseguenza
|
||||
|
||||
## Checklist Prossimi Miglioramenti
|
||||
|
||||
**Priorità alta:**
|
||||
- [ ] Aggiungere requirements.txt o pyproject.toml
|
||||
- [ ] Aggiungere Dockerfile + docker-compose.yml
|
||||
- [ ] Script start.sh per avvio con un comando
|
||||
- [ ] Supporto per più PG contemporaneamente
|
||||
- [ ] Persistenza PF multipli mostri per area
|
||||
|
||||
**Priorità media:**
|
||||
- [ ] Test completi per ogni tool
|
||||
- [ ] Tavola incantesimi dal Reference Booklet
|
||||
- [ ] Abilità ladro complete (dado dedicato per livello)
|
||||
- [ ] Scacciare non-morti (Chierico)
|
||||
- [ ] Equipaggiamento e armi dal knowledge base
|
||||
|
||||
**Priorità bassa:**
|
||||
- [ ] OCR Core Rulebook (498 pagine immagini, ~8h stimate)
|
||||
- [ ] RAG + embedding per knowledge base DCC
|
||||
- [ ] Multi-giocatore (più PG in contemporanea)
|
||||
- [ ] Inventory drag & drop nel frontend
|
||||
- [ ] Mappa interattiva delle aree
|
||||
Reference in new issue
Block a user