pretheme
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
# Changelog — s2s-client (Azurian Vocero)
|
||||
|
||||
Tutte le modifiche rilevanti al progetto sono documentate in questo file.
|
||||
Formato basato su [Keep a Changelog](https://keepachangelog.com/it/1.1.0/).
|
||||
|
||||
---
|
||||
|
||||
## [2026-09-07]
|
||||
|
||||
### Aggiunto — Supporto `episode_id` (podcast episode inject)
|
||||
|
||||
Nuovo parametro query string `?episode_id=` affiancato a `?phoneNumber=` che
|
||||
abilita il caricamento e l'iniezione automatica di un episodio podcast come
|
||||
contesto PRIMARIO nel prompt LLM.
|
||||
|
||||
**URL di esempio:**
|
||||
```
|
||||
/?phoneNumber=%2B447432477433&episode_id=episode-19569f77-a06e-5ce5-a52f-6299dbd67d03
|
||||
```
|
||||
|
||||
**File modificati:**
|
||||
|
||||
- **`server.py`** ([server.py](file:///home/duxiter/s2s/s2s-client/server.py)):
|
||||
1. Aggiunta costante `PODCAST_EPISODE_HINT` ([L426-L429](file:///home/duxiter/s2s/s2s-client/server.py#L426-L429)) per l'hint semantico nel prompt.
|
||||
2. Aggiunta funzione `_resolve_episode_context(raw_value)` ([L475-L504](file:///home/duxiter/s2s/s2s-client/server.py#L475-L504)): normalizza la risposta API (dict/list/str, eventuale wrapper `{"result": …}`), la serializza in JSON (cap 24.000 chars) e la avvolge con i marker `<<<PODCAST_EPISODE_BEGIN/END>>>`.
|
||||
3. Aggiunta funzione asincrona `_fetch_podcast_episode(episode_id)` ([L1010-L1078](file:///home/duxiter/s2s/s2s-client/server.py#L1010-L1078)): GET plain verso
|
||||
`https://aquachile.azurianlab.com/api/v1/podcast/episodes/{episode_id}`
|
||||
(timeout 15s, follow redirects, error handling soft — non rompe mai il flusso, restituisce `None` su fail).
|
||||
4. Estesa `_augment_instructions_with_profile(...)` ([L1137-L1181](file:///home/duxiter/s2s/s2s-client/server.py#L1137-L1181)) con nuovo parametro `episode_context_text`; l'ordine di inject ora è (più specifico → più generico): **episode → profile → reports → summary**.
|
||||
5. Estesa `_patch_client_session_update_with_profile(...)` ([L1184-L1306](file:///home/duxiter/s2s/s2s-client/server.py#L1184-L1306)) con supporto a `episode_context_text` e logging dedicato `[INJECT-EPISODE]` / `[DEBUG-EPISODE]`.
|
||||
6. WebSocket connect handler ([L1791-L1959](file:///home/duxiter/s2s/s2s-client/server.py#L1791-L1959)):
|
||||
- legge `episode_id` da query params assieme a `phoneNumber`;
|
||||
- esegue fetch episodio + resolve PRIMA di profile/reports/summary;
|
||||
- salva `injected_episode_context` e `inject_episode_id` nel `session_ctx` condiviso.
|
||||
7. `_client_to_upstream` ([L2649-L2661](file:///home/duxiter/s2s/s2s-client/server.py#L2649-L2661)): passa `ctx_episode_context` al patch di `session.update`.
|
||||
8. Endpoint `POST /api/reasoning`:
|
||||
- esteso modello `ReasoningRequest` con campo `episode_id: str | None = None` ([L1309-L1323](file:///home/duxiter/s2s/s2s-client/server.py#L1309-L1323));
|
||||
- nel handler ([L1411-L1577](file:///home/duxiter/s2s/s2s-client/server.py#L1411-L1577)) fetch episodio indipendente da phone;
|
||||
- `inject_episode_text` viene **prepend** a `user_context_parts` (prima di tutti);
|
||||
- inoltro a NODE_RED / OPENAI con logging esteso (`episodeIdLen`).
|
||||
|
||||
- **`main.js`** ([main.js](file:///home/duxiter/s2s/s2s-client/main.js)):
|
||||
1. Lettura da query string: `const voceroEpisodeId = pageParams.get("episode_id")` ([L676-L680](file:///home/duxiter/s2s/s2s-client/main.js#L676-L680)).
|
||||
2. `fetchOpenAiReasoningAnswer` ([L1337-L1364](file:///home/duxiter/s2s/s2s-client/main.js#L1337-L1364)): campo `episode_id` aggiunto al body POST `/api/reasoning`, logKey e `console.info` estesi.
|
||||
3. Costruzione URL WebSocket ([L2323-L2342](file:///home/duxiter/s2s/s2s-client/main.js#L2323-L2342)): `episode_id` appeso a query params sia con API `URL.searchParams` sia nel fallback stringa (concat con `&`).
|
||||
|
||||
---
|
||||
|
||||
### Aggiunto — `S2S_START_SESSION_MODE`: 3 modalità di start sessione
|
||||
|
||||
Env var letta dal server e propagata al client via `GET /api/config.startSessionMode`.
|
||||
Scopo: governare se e come il microfono si auto-accende e se il modello saluta
|
||||
per primo (flow simile a wake-word). Valori supportati:
|
||||
|
||||
| Valore | Descrizione |
|
||||
|---|---|
|
||||
| *(vuoto) / default* | Start manuale: solo tap sull'Orb / pulsante Start |
|
||||
| **`TALK`** | Dopo 2s overlay fullscreen "Tocca ovunque per iniziare" (policy safe per Safari/iOS `getUserMedia`). Click/touch/tasto dentro la user gesture → `doStart()`. WS client inietta bootstrap `"hola vocero"` + `requestResponse()`. |
|
||||
| **`TALKFIRST`** | Dopo 2s **tenta `doStart()` DIRETTAMENTE SENZA click**, pre-caricando `pendingWakeWordConfirmationText = "hola vocero"` (stessa variabile della wake-word vera). Se i permessi mic sono già "allow always" funziona senza click. Se fallisce (policy Safari/iOS) → fallback sullo stesso unlocker di TALK con messaggio "permesso microfono". |
|
||||
|
||||
**File modificati:**
|
||||
|
||||
- **`server.py`** ([server.py](file:///home/duxiter/s2s/s2s-client/server.py)):
|
||||
1. Lettura env: `S2S_START_SESSION_MODE = os.environ.get(...)`, `.strip().upper()` ([L150](file:///home/duxiter/s2s/s2s-client/server.py#L150)).
|
||||
2. Banner `[STARTUP]` esteso con campo `startSessionMode=` ([L196-L212](file:///home/duxiter/s2s/s2s-client/server.py#L196-L212)).
|
||||
3. Endpoint `GET /api/config` → nuovo campo `startSessionMode` + log `[INTERNAL] /api/config` ([L1383-L1400](file:///home/duxiter/s2s/s2s-client/server.py#L1383-L1400)).
|
||||
|
||||
- **`index.html`** ([index.html](file:///home/duxiter/s2s/s2s-client/index.html#L32-L39)):
|
||||
- Aggiunto nuovo `#talk-unlocker` overlay fullscreen (z-index 9999), `hidden` di default, con card "Preparando Vocero… / Toca en cualquier lugar para empezar a hablar" + spinner gradient.
|
||||
|
||||
- **`style.css`** ([style.css](file:///home/duxiter/s2s/s2s-client/style.css#L145-L215)):
|
||||
- Classi `.talk-unlocker`, `.talk-unlocker-hint`, `.talk-unlocker-spinner`, `.talk-unlocker-title`, `.talk-unlocker-sub` + animazioni fade-in e pop-up card.
|
||||
|
||||
- **`main.js`** ([main.js](file:///home/duxiter/s2s/s2s-client/main.js)):
|
||||
1. Variabile globale `startSessionMode = ""` + commento ([L628-L632](file:///home/duxiter/s2s/s2s-client/main.js#L628-L632)).
|
||||
2. Parse dentro `fetchConfig()` → uppercase, log in `reportDebug("B",…)` ([L2309-L2320](file:///home/duxiter/s2s/s2s-client/main.js#L2309-L2320)).
|
||||
3. Passaggio a WS client → `startWithResponse: startSessionMode === "TALK"` ([L2683-L2692](file:///home/duxiter/s2s/s2s-client/main.js#L2683-L2692)).
|
||||
4. Blocco scheduling auto-start ([L2976-L3083](file:///home/duxiter/s2s/s2s-client/main.js#L2976-L3083)):
|
||||
- `bootstrapWithAutoStart()`: `await fetchConfig()` → `maybeScheduleAutoStart()`;
|
||||
- `setTimeout(2000 ms)` warmup per finire fetch di episode/profile/reports;
|
||||
- `showTalkUnlocker(fallbackHint)`: gestisce label custom nel caso fallback TALKFIRST;
|
||||
- **`tryTalkFirstDirect()` (TALKFIRST)**: set `pendingWakeWordConfirmationText="hola vocero"` + `doStart()` + try/catch con teardown e fallback unlocker;
|
||||
- listener unlocker click/touchstart/keydown → `startTalkFromGesture()` con `createResumedAudioContext()` dentro la gesture (per iOS silent-free).
|
||||
5. `onClientStatus()` ([L2878-L2932](file:///home/duxiter/s2s/s2s-client/main.js#L2878-L2932)) aggiunto `hideTalkUnlocker()` in `connecting/queued/your-turn/connected`.
|
||||
|
||||
- **`ws/s2s-ws-client.js`** ([s2s-ws-client.js](file:///home/duxiter/s2s/s2s-client/ws/s2s-ws-client.js)):
|
||||
1. Costruttore: nuove proprietà `_startWithResponse`, `_startWithResponseText` (default `"hola vocero"`), `_initialResponseSent` guardia one-shot ([L163-L178](file:///home/duxiter/s2s/s2s-client/ws/s2s-ws-client.js#L163-L178)).
|
||||
2. Handler `session.updated` ([L688-L713](file:///home/duxiter/s2s/s2s-client/ws/s2s-ws-client.js#L688-L713)): **Solo per `TALK`** (non TALKFIRST, che usa invece `pendingWakeWordConfirmationText`): `this.sendUserText("hola vocero") + this.requestResponse()` — stesso identico code path della wake-word.
|
||||
3. `requestResponse` / `_createResponseNow` ripristinati puliti (solo payload opzionale `image`; nessun shaped `response.create`).
|
||||
|
||||
---
|
||||
|
||||
### Modificato — `#reports-sidebar-toggle` visibile solo con `?debug=true`
|
||||
|
||||
Il bottone "reports" nella topbar (toggle del pannello laterale reports) è ora
|
||||
`data-debug-only="true"` — viene quindi mostrato solo se l'URL contiene
|
||||
`?debug=true`, analogamente al cluster `.topbar-right`.
|
||||
|
||||
**File modificati:**
|
||||
|
||||
- **`index.html`** → `#reports-sidebar-toggle` ([index.html](file:///home/duxiter/s2s/s2s-client/index.html#L47-L50)): aggiunto `data-debug-only="true"`.
|
||||
- **`main.js`** → `applyDebugOnlyVisibilityGate()` ([L48-L109](file:///home/duxiter/s2s/s2s-client/main.js#L48-L109)): esteso per iterare *tutti* gli elementi `[data-debug-only="true"]` (non solo `.topbar-right`); riga 1227: `reportsSidebarToggle.hidden = false` gated a `qs.get("debug") === "true"` ([L1227-L1246](file:///home/duxiter/s2s/s2s-client/main.js#L1227-L1246)) + log rosso `🚫 FORZATO HIDDEN` quando manca debug=true.
|
||||
- **`style.css`** → regola di sicurezza già presente: `[data-debug-only]:not([data-debug-enabled]) { display:none !important; visibility:hidden; opacity:0 }` ([L532-L537](file:///home/duxiter/s2s/s2s-client/style.css#L532-L537)).
|
||||
|
||||
---
|
||||
|
||||
## [2026-09-04]
|
||||
|
||||
### Modificato — Chat sempre visibile + integrazione seamless
|
||||
|
||||
Riorganizzato il layout per mostrare la chat testuale in modo permanente e
|
||||
integrato visivamente con l'Orb nella parte superiore dello stage.
|
||||
|
||||
- Rimosso pulsante toggle chat nella topbar (`#chat-btn`) e relativo overlay.
|
||||
- Stage con l'Orb nella parte superiore; nuovo `#chat-panel` a seguire (cronologia
|
||||
+ form input fisso sopra il footer sticky).
|
||||
- Aggiornata logica in `main.js` e `ui/chat.js` per l'invio testo tramite
|
||||
`client.sendUserText` e auto-scroll senza controlli di visibilità pannello.
|
||||
- Versione banner deploy: **2026-09-03 14:40**.
|
||||
|
||||
### Risolto — Stabilità integrazione chat mobile
|
||||
|
||||
- Errore `Missing element: #chat-btn` risolto con selettori non-strict
|
||||
(`document.querySelector`) per elementi opzionali rimossi.
|
||||
- Refactoring estetico "seamless": chat senza cornici, ombre o sfondi
|
||||
(`background:transparent`, `box-shadow:none`).
|
||||
- **Layout mobile:**
|
||||
- padding incrementato in `#app` (56px) e `.chat-history` (96px) per evitare
|
||||
sovrapposizioni col footer sticky;
|
||||
- margini fissi bolle: `margin-right:22px` (Assistant), `margin-left:6px` (User).
|
||||
- **Auto-scroll streaming:** implementato `_scrollToBottom` con catena di retry
|
||||
multipli (`requestAnimationFrame` + `setTimeout`) per compensare il line-wrap
|
||||
asincrono durante l'arrivo dei token; `overflow-anchor:none` + `scroll-behavior:auto !important`.
|
||||
|
||||
### Aggiunto — Avatar utente iniziali + modale profilo
|
||||
|
||||
- Sostituito testo nome utente nell'header con avatar circolare 40×40px
|
||||
(gradient brand) contenente le iniziali calcolate (es. "AV").
|
||||
- Nuova `<dialog id="user-profile-dialog">` in `index.html` che si apre al click
|
||||
sull'avatar: mostra email, telefono, ruolo, azienda, con supporto a shape
|
||||
annidate del payload profilo.
|
||||
- Chiusura modale: tasto ESC, click fuori card, pulsante `×` dedicato.
|
||||
|
||||
---
|
||||
|
||||
## [Note di formato]
|
||||
|
||||
- **Cap campo prompt episodio:** 24.000 caratteri (transcript lunghi).
|
||||
- **Cap profilo / summary:** 1.600 caratteri ciascuno.
|
||||
- **Cap reports list:** 12.000 caratteri.
|
||||
- **Ordine inject prompt LLM (WS session.update + reasoning):**
|
||||
1. Episodio podcast corrente (PRIMARIO)
|
||||
2. Profilo chiamante (identity)
|
||||
3. Lista reports / script podcast schedulati
|
||||
4. Summary ultima conversazione
|
||||
- Tutti i blocchi sono marcati con `<<<NOME_BLOCCO_BEGIN/END>>>` per ispezione log.
|
||||
Reference in New Issue
Block a user