client sdk

This commit is contained in:
valenti
2026-10-21 17:56:04 +01:00
parent 1cafe3dd3c
commit d6bc2437c9
237 changed files with 5227 additions and 1019 deletions
+261
View File
@@ -5,6 +5,267 @@ Entries are written in English and ordered newest-first.
---
## 2026-10-21 — Feat: demo routes audio tool results to the external source
### Changed
- **Cue asset layout** — filler, start-filler and backchannel banks now live
under `<base>/<lang>/<voice_profile>/` instead of `<base>/<lang>/`, so
multiple TTS voice configurations can coexist on disk without overwriting one
another.
- **[`src/config.rs`](src/config.rs)** — added a resolved cue-asset voice
profile (`Config::cue_voice_profile()`), with optional explicit override via
`ASSET_VOICE_PROFILE`. When unset, the engine derives the profile from the
active voice setup (prefer `TTS_VOICE` when it is not `default`, otherwise
fall back to the reference-audio file stem, then to `default`).
- **[`src/assets.rs`](src/assets.rs)** / **[`src/main.rs`](src/main.rs)** /
**[`src/orchestrator/turn_manager.rs`](src/orchestrator/turn_manager.rs)** /
**[`src/ws/session.rs`](src/ws/session.rs)** — cue-bank bootstrap, runtime
language switching and cue metadata now all resolve the same
`<lang>/<voice_profile>` directory layout.
- **Legacy cue-bank migration** — when old single-voice banks still exist
directly under `<base>/<lang>/`, startup now copies their flat files
(`*.raw`, `texts.txt`, `.voice_signature`) into the active
`<base>/<lang>/<voice_profile>/` directory if the target files are missing.
The legacy files are kept in place.
- **[`run/refresh_voice_assets.sh`](run/refresh_voice_assets.sh)** — the voice
asset refresh script now targets the current cue voice profile and refreshes
directories under `<base>/<lang>/<voice_profile>/`, keeping other voice
profiles intact.
- **[`README.md`](README.md)** — documented the new cue-bank directory layout
and the optional `ASSET_VOICE_PROFILE` override.
- **[`sdk/demo.html`](sdk/demo.html)** — a tool definition may now carry an
extra `audio: true` flag (ignored by the backend, which only reads
`name`/`description`/`parameters`/`url`/`method`/`headers`). When the model
calls such a tool, the demo rebuilds the call's effective URL (arguments of
read-style verbs are appended as query parameters, mirroring the backend's
argument placement), writes it into the external audio source field and
starts playback on the engine's dedicated channel. `get_today_podcast_mp3` is
flagged this way, so a podcast request is played back instead of only being
returned to the model. The manual Play button now shares the same
`playExternalSource()` helper.
- **[`sdk/demo.html`](sdk/demo.html)** — the default session prompt and the
built-in tool descriptions now explicitly tell the model to call
`get_today_podcast_mp3` on the first relevant podcast/audio request and
`get_public_day_report` on the first relevant report/alerts request, to
reduce cases where the user has to repeat the same intent before tool calling
is triggered.
- **Markdown document mode** — when the user explicitly asks for a Markdown
document, the demo prompt now instructs the model to keep a very short spoken
preface outside the block and place the full document inside
`<chat_markdown>...</chat_markdown>`.
- **[`sdk/demo.html`](sdk/demo.html)** — audio tool results are queued on
`tool_call` and autoplay after a fixed post-`assistant_end` delay, rather
than waiting for the SDK player to become idle. This avoids false timeouts in
deployments where the engine keeps sending mixed 20 ms PCM frames
continuously, even during silence, while keeping the post-reply timing
deterministic.
- **[`src/tools.rs`](src/tools.rs)** / **[`src/orchestrator/turn_manager.rs`](src/orchestrator/turn_manager.rs)** —
the engine now strips `<chat_markdown>...</chat_markdown>` blocks out of the
spoken stream, emits them separately as `assistant_markdown` events, and
keeps the document text in the assistant history for follow-up turns.
- **[`sdk/demo.html`](sdk/demo.html)** — the SDK demo renders
`assistant_markdown` payloads as formatted Markdown bubbles in chat instead
of showing raw source text.
- **[`sdk/audio-agent-client.js`](sdk/audio-agent-client.js)** /
**[`sdk/audio-agent-client.d.ts`](sdk/audio-agent-client.d.ts)** — the SDK now
exposes the `assistant_markdown` event type for clients that want chat-only,
non-TTS Markdown documents.
- **[`sdk/audio-agent-client.js`](sdk/audio-agent-client.js)** /
**[`sdk/audio-agent-client.d.ts`](sdk/audio-agent-client.d.ts)** — `PcmPlayer`
now exposes `isIdle()` and emits a local `idle` event when its scheduled
audio has fully drained, giving browser clients a reliable post-playback
signal.
---
## 2026-10-21 — Chore: voice-asset refresh helper script
### Added
- **[`run/refresh_voice_assets.sh`](run/refresh_voice_assets.sh)** — bash helper
that reads the current `.env`, analyzes the active TTS voice / reference /
sampling / cue-text settings, computes a voice signature, inspects every cue
bank (`fillers`, `start_filler`, `backchannel`) across the discovered
languages, and decides which generated assets are stale.
- The script stores a per-directory `.voice_signature`, supports `--dry-run`,
`--force`, `--lang <code>`, `--base-lang-only` and `--no-restart`, deletes the
generated `*.raw` clips (plus `texts.txt` for text-driven banks) when a
refresh is needed, restarts `pm2-piperust`, and writes the updated signature
markers back to the asset directories.
---
## 2026-10-21 — Chore: SDK demo installs a default day-report tool
### Changed
- **[`sdk/demo.html`](sdk/demo.html)** — the demo now installs a default
runtime tool set through `POST /api/tools` right after connect:
`get_public_day_report`, mapped to
`GET https://aquachile.azurianlab.com/api/v1/day-report` with query
parameters `limit` and `date`, plus `get_today_podcast_mp3`, mapped to
`GET https://aquachile.azurianlab.com/api/v1/podcast/today/mp3` with query
parameter `download`. This lets the demo exercise real tool calling without
relying on an external `LLM_TOOLS_FILE`.
---
## 2026-10-21 — Feat: phone-login proxy (`POST /api/auth/phone-login`)
### Added
- **`POST /api/auth/phone-login`** ([`src/auth_api.rs`](src/auth_api.rs)) — a
server-side proxy that forwards `{ "phoneNumber": "…" }` to the AquaChile auth
API (`AUTH_API_BASE` + `AUTH_PHONE_LOGIN_PATH`) and returns its status and body
unchanged (on success: `sessionToken` + `user`). It exists because that API
rejects browser calls from the client origin (CORS preflight `400 Disallowed
CORS origin`, no `Access-Control-Allow-Origin`), so a direct `fetch` from the
demo is blocked; going through the engine makes the call same-origin. When the
request also carries a live `session_id`, the engine stores a sanitized user
profile (`name`, `email`, `businessRoles`, `permissionSet`, `centers`,
`permissions`) as hidden context for that session's LLM. Errors: `400` for an
empty/invalid body or missing `phoneNumber`, `404` for an unknown
`session_id`, `502` when the upstream is unreachable.
- Config `AUTH_API_BASE` (default `https://aquachile.azurianlab.com`) and
`AUTH_PHONE_LOGIN_PATH` (default `/api/v1/auth/phone-login`).
- Swagger UI (`/docs`) documents the endpoint (new `auth` tag,
`PhoneLoginRequest` schema).
### Changed
- **[JavaScript SDK](sdk/audio-agent-client.js)** — added
`phoneLogin(phoneNumber, sessionId?)` to `AudioAgentRest`; types updated in
[`sdk/audio-agent-client.d.ts`](sdk/audio-agent-client.d.ts) and the reference
in [`sdk/README.md`](sdk/README.md).
- **[`sdk/demo.html`](sdk/demo.html)** — at session start it reads `phoneNumber`
from the page URL (`?phoneNumber=+44…`) and authenticates through the engine
proxy, showing the resolved user in the header, a chat chip and the event log.
(Replaces the earlier direct cross-origin `fetch`, which the browser blocked.)
- **Hidden LLM user context** — the session user block now instructs the model
to treat `permissions`, `permissionSet`, `businessRoles` and `centers` as
access/domain scope, avoid fabricating unavailable access, constrain KPI /
report / alert interpretation to the verified user scope, and suggest next
steps aligned with the user's role.
---
## 2026-10-21 — Feat: per-session system prompt (`GET` / `POST /api/prompt`)
### Added
- **Server-assigned session id** — every WebSocket session is given an id
(`s-<epoch_ms>-<seq>`, the same stem used for its audit log) by a new
per-session prompt registry ([`src/prompt.rs`](src/prompt.rs)) and the id is
pushed to the client right after connect, before `cues`, as
`{"type":"session","id":"…"}` ([`src/ws/session.rs`](src/ws/session.rs)).
`SessionAudit::new` now receives that id instead of generating its own, so the
audit file and the live session share one identifier.
- **`GET` / `POST /api/prompt`** ([`src/prompt_api.rs`](src/prompt_api.rs)) — read
or override the LLM system prompt at runtime, **per session** (with a
`session_id`) or process-wide (without one). `GET` returns the effective prompt
plus metadata (`source`: `session` / `global` / `env` / `default` / `none`,
`has_override`, `direct_answers_applied`); `POST` installs an override
(`prompt: null` / `""` clears it). Overrides live **in memory only** — a
restart restores the configured default and a session override is dropped on
disconnect; an unknown session id returns `404`.
- The LLM turn now resolves the system prompt through the registry
([`src/orchestrator/turn_manager.rs`](src/orchestrator/turn_manager.rs)):
session override → process-wide override → `LLM_SYSTEM_PROMPT` → built-in
default. With `/api/prompt` never called the behaviour is unchanged.
- `Config::llm_system_prompt_from_env` records whether the configured prompt came
from `LLM_SYSTEM_PROMPT` (reported as `source: "env"`).
- Swagger UI (`/docs`) documents the new endpoint (new `prompt` tag, schemas
`PromptResponse` / `SetPromptRequest`).
### Changed
- **[JavaScript SDK](sdk/audio-agent-client.js)** — added the `session` event to
`CLIENT_EVENTS` and `getPrompt(sessionId?)` / `setPrompt(prompt, sessionId?)`
to `AudioAgentRest`; types updated in
[`sdk/audio-agent-client.d.ts`](sdk/audio-agent-client.d.ts) and the reference
in [`sdk/README.md`](sdk/README.md).
- **[`sdk/demo.html`](sdk/demo.html)** — captures the session id from the
`session` event and adds a **System prompt** panel to reload / override / clear
the prompt of the current session over REST. It also applies a built-in
`SESSION_PROMPT` (the Vocero / AquaChile assistant instructions) automatically
at the start of every session, right after the `session` event.
---
## 2026-10-21 — Chore: English-only code and documentation convention
### Changed
- **All code is now English-only.** Every comment, doc-comment, error/log string
and test description across `src/` and `sdk/` was reviewed and the remaining
Italian was translated — e.g. [`src/stt/tokenizer.rs`](src/stt/tokenizer.rs).
Per-language *content* is intentionally left as-is: the voice-cloning reading
phrases in [`src/customvoice.rs`](src/customvoice.rs) and the clip phrase
tables in [`src/lang.rs`](src/lang.rs) must stay in their own language.
- **Documentation is now English-only.** [`TOOL_CALLING.md`](TOOL_CALLING.md) and
[`sdk/README.md`](sdk/README.md) dropped their `Italiano` / `Español`
sections and [`README.md`](README.md) examples switched to English sample text.
This supersedes the previous IT/EN/ES documentation convention.
---
## 2026-10-21 — Feat: JavaScript client SDK + opt-in CORS
### Added
- **`sdk/audio-agent-client.js`** — a dependency-free, single-file ESM client
SDK (browser + Node) with types in
[`sdk/audio-agent-client.d.ts`](sdk/audio-agent-client.d.ts) and a
reference in [`sdk/README.md`](sdk/README.md). It exists so the
client can be **decoupled** from the engine and hosted on another server:
- `AudioAgentClient` — the realtime `/ws` session: `sendAudio()` (accepts
`Int16Array` / `Float32Array` / `ArrayBuffer` / view / `number[]`, normalized
to 16-bit LE mono 44.1 kHz), typed controls (`sendUserText`, `interrupt`,
`playAudioSource`, `stopAudioSource`, `setAudioSourceVolume`,
`setReference`, `clearReference`, `speak`), optional auto-reconnect with
exponential backoff, and one event per server message (`cues`, `user`,
`assistant_delta`, `assistant_end`, `cue`, `barge_in`, `tool_call`,
`audio_source`, `error`) plus a local `audio` event with the decoded frames.
- `AudioAgentRest` — typed REST wrapper: `health`, `getTools` / `setTools`,
`getCustomVoicePhrases` / `setCustomVoice` / `clearCustomVoice`,
`transcribe`, `openapi`; failures throw an error carrying `status`/`body`.
- Browser audio helpers `MicCapture` (mic → 882-sample `Int16Array` frames via
an inline AudioWorklet), `PcmPlayer` (jitter-buffered playback with
`flush()` for barge-in) and `AudioSession` (wires client + mic + player).
- PCM utilities `int16ArrayFromBytes`, `float32ToInt16`, `int16ToFloat32`,
`toInt16` and the `AUDIO` constants.
- **`GET /sdk/audio-agent-client.js`** ([`src/main.rs`](src/main.rs),
[`src/web/mod.rs`](src/web/mod.rs)) — serves the SDK (embedded at compile
time, like the HTML clients) so a client hosted elsewhere can fetch it.
- **`sdk/demo.html`** + **`GET /sdk/demo.html`** — a complete demo client built
on the SDK (English UI, like all the code in this project): connect/disconnect,
live session state, streaming
agent replies with word-level *karaoke* highlighting, user/agent chat bubbles,
inline chips for `cue` / `tool_call` / `audio_source` events, a barge-in
counter, a microphone toggle with a live level meter, a text-turn input, an
external audio source (play / stop / volume) and a raw event log. It imports
`./audio-agent-client.js`, so it works both served by the engine and copied to
another origin; it warns when opened outside a secure context (no mic on
`file://`).
- **Opt-in CORS** — `CORS_ALLOW_ORIGIN` (comma-separated origins, or a single
`*`) adds a [`tower-http`](https://docs.rs/tower-http) CORS layer
([`src/main.rs`](src/main.rs), [`src/config.rs`](src/config.rs)) for browser
clients on another origin. Empty (the default) adds **no** layer, so the
engine behaves exactly as before. The WebSocket `/ws` is unaffected (a
handshake is not a CORS request).
### Notes
- No build step for the SDK: copy the file, or import it from the engine origin.
- `tower-http` was already a transitive dependency; the `cors` feature adds no
new crates.
- Docs updated: [README.md](README.md#client-sdk-javascript) (new section, route
table, env table, feature bullet), [`.env.example`](.env.example), and the new
[`sdk/README.md`](sdk/README.md) (IT/EN/ES).
---
## 2026-10-21 — Feat: every HTTP verb for tools (list / get / add / update / delete / …)
### Changed