# Configurazione dei modelli in Pi: built-in, utente, progetto Pi (il coding agent che orchestra il workflow NL→SQL) può risolvere un `provider/model` in tre modi diversi. Non sono alternativi: coesistono, e la scelta di quale usare dipende da **quanto è standard l'endpoint** e da **quanto deve essere ampia la visibilità** del modello (tutti i progetti vs. un progetto solo). ## Credenziali nel backend container In produzione configurare una sola sorgente generica: il bundle `THT_SECRETS_FILE` con la voce `THT_MODEL_API_KEY` (predefinito della distribuzione unificata), oppure `THT_MODEL_API_KEY_FILE` come secret file assoluto. Non mettere il valore della chiave in `.env`. `PiProcessManager` rilegge e valida la sorgente per ogni processo, normalizza il provider selezionato e passa al solo child Pi la variabile nativa appropriata (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GEMINI_API_KEY`, `ZAI_API_KEY`, ecc.). Il percorso generico, le chiavi di provider non selezionati e il vecchio `PI_PROVIDER_API_KEY` vengono rimossi dall'ambiente del child. Provider locali come `ollama`, `lmstudio` e `aritmolab` continuano senza chiave; un provider hosted non mappato o un secret mancante/non sicuro fallisce prima dello spawn con errore sanitizzato. La sorgente generica supporta soltanto provider con una singola chiave: `ant-ling`, `anthropic`, `cerebras`, `deepseek`, `fireworks`, `github-copilot`, `google` (anche tramite alias `gemini`), `google-vertex` in modalità API key, `groq`, `huggingface`, `kimi-coding`, `minimax`, `minimax-cn`, `mistral`, `moonshotai`, `moonshotai-cn`, `nvidia`, `openai`, `opencode`, `opencode-go`, `openrouter`, `together`, `vercel-ai-gateway`, `xai`, i quattro provider `xiaomi*`, `zai` e `zai-coding-cn`. I provider composti `amazon-bedrock`, `azure-openai-responses`, `cloudflare-workers-ai` e `cloudflare-ai-gateway` non sono rappresentabili da un solo file. La selezione fallisce prima dello spawn (anche durante l'elenco modelli); tutte le credenziali ambientali AWS, Azure e Cloudflare restano comunque rimosse. Servirà una futura configurazione dedicata per provider per supportare questi bundle senza ambiguità. ## I tre livelli di provenienza di un modello ### 1. Built-in (compilato dentro Pi) Pi viene distribuito con un elenco di modelli già noti (`models.generated.js` dentro il pacchetto `@earendil-works/pi-ai`): Anthropic, OpenAI, Google, e anche provider di terze parti con API pubblica ben nota come DeepSeek. Per questi **non serve alcuna configurazione**: bastano le credenziali (env var o `pi auth`). `deepseek/deepseek-v4-pro` è così: è nella build di Pi perché `api.deepseek.com` è un'API pubblica documentata, non un endpoint interno. ### 2. `models.json` a livello utente (`~/.pi/agent/models.json`) Per un endpoint **OpenAI-compatible** che non è tra i built-in — ma che non richiede nessuna logica di trasporto speciale — basta *dichiararlo*: baseUrl, apiKey, lista modelli. Questo file esiste **solo a livello utente**: non c'è un equivalente project-level (un `./.pi/models.json` non viene letto). Nelle installazioni gestite da ThothII il file deve essere interamente dichiarativo. ThothII rifiuta ricorsivamente qualsiasi valore JSON che inizi con `!`, anche dentro `headers`, `models`, `modelOverrides`, `compat`, array o campi non ancora conosciuti. Pi 0.80.3 tratterebbe quel prefisso come un comando shell al momento della richiesta; questa forma non è ammessa né dall'elenco gestito dei modelli né dallo smoke isolato. L'errore restituito è fisso e non include comando, percorso o secret. Per i secret usare un riferimento ambiente come `"$ZAI_API_KEY"` o `"${ZAI_API_KEY}"`. Il backend può popolare la variabile nativa del solo provider selezionato leggendo `THT_MODEL_API_KEY_FILE`, oppure dal bundle `THT_SECRETS_FILE` (`THT_MODEL_API_KEY`); in alternativa le credenziali possono arrivare dal file protetto montato con `PI_AUTH_FILE`, omettendo `apiKey` da `models.json`. Non esiste una sintassi di riferimento diretto a un secret file dentro `models.json`: con le sorgenti `THT_MODEL_*` il file viene letto da ThothII e trasformato nella variabile ambiente del child Pi; `PI_AUTH_FILE` viene invece montato come archivio credenziali Pi protetto. Per un punto esclamativo letterale iniziale, la sintassi Pi dichiarativa è `$!`, non `!`. Esempio reale in uso su questa macchina — GLM (provider `zai`): ```json { "providers": { "zai": { "baseUrl": "https://api.z.ai/api/coding/paas/v4", "api": "openai-completions", "apiKey": "$ZAI_API_KEY", "models": [ { "id": "glm-5.2", "name": "GLM-5.2", "reasoning": true, "contextWindow": 200000, "maxTokens": 131072 } ] } } } ``` Essendo a livello utente, GLM è visibile da **qualsiasi progetto**. ### 3. Estensione (`.pi/extensions/*.js`, utente o progetto) Quando l'endpoint richiede codice — ad esempio conversione di eventi (thinking→text), un `streamSimple` custom, o comunque logica che un file dichiarativo non può esprimere — serve un'estensione Pi che chiama `pi.registerProvider(...)`. Le estensioni possono vivere sia in `~/.pi/agent/extensions/` (tutti i progetti) sia in `/.pi/extensions/` (solo quel progetto, se Pi viene lanciato con quella cwd). Esempio reale — il provider AritmoLab (Qwen), interno all'ospedale, usato solo da ThothII: [harness/.pi/extensions/aritmolab-provider.js](../../harness/.pi/extensions/aritmolab-provider.js). ## Tabella riassuntiva (stato attuale di questa macchina) | Modello | Livello | Perché | Visibilità | |---|---|---|---| | `deepseek/deepseek-v4-pro` | Built-in Pi | API pubblica nota, già nella build | Tutti i progetti | | `zai/glm-5.2` | `~/.pi/agent/models.json` | Endpoint OpenAI-compatible custom (z.ai), nessuna logica speciale | Tutti i progetti | | `aritmolab/qwen3.6-35b-a3b` | Estensione di progetto | Endpoint interno ospedaliero + conversione eventi thinking→text custom | Solo ThothII (cwd=`harness/`) | ## Come scegliere il livello giusto per un nuovo modello 1. **L'endpoint è un'API pubblica già nota a Pi?** → niente da fare, verifica con `pi --list-models`. 2. **È OpenAI-compatible, nessuna logica custom, e deve essere visibile ovunque?** → `~/.pi/agent/models.json`. 3. **Serve codice custom (auth non standard, conversione eventi, trasporto non-OpenAI) oppure deve restare visibile a un solo progetto?** → estensione, in `~/.pi/agent/extensions/` (globale) o `/.pi/extensions/` (locale). --- ## Ambito utente vs. progetto — riepilogo generale Oltre ai modelli, Pi carica altre risorse da due alberi paralleli: `~/.pi/agent/` (utente) e `/.pi/` (progetto, risolto in base alla directory da cui viene lanciato `pi`). | File/Directory | Livello utente | Livello progetto | Auto-discovery | Precedenza | |---|---|---|---|---| | `models.json` | `~/.pi/agent/models.json` | non supportato | no | solo utente | | `settings.json` | `~/.pi/agent/settings.json` | `./.pi/settings.json` | no | progetto sovrascrive utente | | `extensions/` | `~/.pi/agent/extensions/` | `./.pi/extensions/` | sì (`.ts`/`.js`) | uniti (progetto + utente) | | `prompts/` | `~/.pi/agent/prompts/` | `./.pi/prompts/` | sì (`.md`) | uniti | | `themes/` | `~/.pi/agent/themes/` | `./.pi/themes/` | sì (`.json`) | progetto preferito | | `skills/` | `~/.pi/agent/skills/` | `./.pi/skills/` | sì (`.md`) | uniti | ### Esempio reale: ThothII ``` harness/.pi/ ├── extensions/ │ ├── aritmolab-provider.js ← provider LLM solo-progetto │ ├── tht-gate.js ← gate human-in-the-loop │ ├── reserved-labels.mjs ← utility condivisa (NON auto-caricata; .mjs ignorato) │ └── gate/ ← modulo usato da tht-gate.js ├── settings.json ← (opzionale) override delle impostazioni utente └── themes/ └── thothii-mono.json ← tema del progetto ``` ### Comportamento rispetto alla cwd La directory da cui lanci `pi` determina quale `.pi/` di progetto viene trovata: ```bash # Da harness/ — trova harness/.pi/extensions/aritmolab-provider.js cd /path/to/ThothII/harness pi --model aritmolab/qwen3.6-35b-a3b "..." # Dalla radice di ThothII — nessun .pi/ trovato lì o nei genitori, solo config utente cd /path/to/ThothII pi --model aritmolab/qwen3.6-35b-a3b "..." # ❌ Error: model not found ``` Il backend di ThothII (`PiProcessManager`, `list-models.ts`, `model-matrix.mjs`) lancia sempre `pi` con `cwd: harnessDir`, per questo Qwen è visibile in produzione. --- ## Trappola nell'auto-discovery: `.mjs` viene ignorato Il pattern di auto-discovery delle estensioni in Pi è **`/\.(ts|js)$/`** — non include `.mjs`. ``` .pi/extensions/ ├── my-extension.js ✅ auto-caricata ├── my-extension.ts ✅ auto-caricata ├── my-extension.mjs ❌ ignorata silenziosamente (non combacia col pattern) └── shared-module.mjs ✅ va bene per moduli helper (deliberatamente non caricato come estensione) ``` Se serve un modulo condiviso importato da un'estensione, usa `.mjs` proprio per evitare che venga trattato come estensione a sé. --- ## Verifica ### Elenco modelli ```bash pi --list-models ``` Mostra i built-in + i modelli utente da `models.json`. **Non mostra** i provider registrati da estensione (come AritmoLab) — quelli vanno verificati con la cwd giusta. ### Verifica che un'estensione sia caricata ```bash cd harness # o la cwd rilevante per il progetto pi --mode rpc # poi: {"type": "get_available_models", "id": "1"} ``` La risposta RPC include tutti i modelli disponibili, inclusi quelli da estensione. --- ## Troubleshooting | Problema | Causa | Soluzione | |---|---|---| | `Model "X/Y" not found` lanciando da fuori progetto | Il modello è registrato da un'estensione locale, non visibile fuori dalla cwd giusta | Lancia `pi` dalla directory di progetto corretta (es. `harness/`) | | Estensione non caricata pur essendo nella cartella giusta | File `.mjs` invece di `.js`/`.ts` | Rinomina in `.js` | | Impostazioni di progetto non applicate | `settings.json` di progetto ha errori di sintassi, o si sta lanciando `pi` dalla cwd sbagliata | Valida il JSON, controlla la cwd |