refactor(ui): delete legacy static website and refresh documentation

This commit is contained in:
2026-10-06 17:23:20 -03:00
parent f91001169c
commit 179f5ac7db
39 changed files with 102 additions and 16723 deletions
+4 -7
View File
@@ -42,17 +42,15 @@ audio. It is not an unconditional byte-for-byte tunnel.
| Source | Responsibility |
|---|---|
| [server.py](../../s2s-client/server.py) | Configuration, static serving, WebSocket relay, context loading, ApiPilot, reasoning, search, queue and usage routes |
| [server.py](../../s2s-client/server.py) | Configuration, documentation serving, WebSocket relay, context loading, ApiPilot, reasoning, search and queue routes |
| [start_ui.sh](../../start_ui.sh) | Dependency setup, ports, readiness, reload processes, and shutdown |
| [vite.config.ts](../../s2s-frontend/vite.config.ts) | Public development endpoint, HMR, allowed hosts, and backend routing |
| [App.tsx](../../s2s-frontend/src/App.tsx) | React layout, voice controls, dialogs, profile and reports sidebar |
| [controller.ts](../../s2s-frontend/src/realtime/controller.ts) | Application state, settings, context, queue, tools and external reasoning |
| [client.ts](../../s2s-frontend/src/realtime/client.ts) | Realtime protocol, session lifecycle, audio and transcript events |
| [components](../../s2s-frontend/src/components) | Chat, account, settings and voice components |
| [components](../../s2s-frontend/src/components) | Chat, caller profile, settings and voice components |
| [audio](../../s2s-frontend/src/audio) | TypeScript capture/playback worklets, PCM codec, and visualizer |
| [legacy-theme.css](../../s2s-frontend/src/styles/legacy-theme.css) | Retained themes and responsive visual styles alongside Tailwind |
| [auth.py](../../s2s-client/auth.py) | Hugging Face OAuth and caller identity/tier |
| [limiter.py](../../s2s-client/limiter.py) | SQLite daily talk-time reservations and reconciliation |
| [flows.json](../../s2s-reasoning-engine/flows.json) | Node-RED reasoning and assistant-response logging |
## Server routes
@@ -68,15 +66,14 @@ audio. It is not an unconditional byte-for-byte tunnel.
| `POST /api/apipilot/execute` | Validated tool execution returning a `success/data/error` envelope |
| `POST /api/search` | Serper web search proxy |
| `/api/session`, `/api/queue/*` | Load-balancer session and waiting-queue proxies |
| `/api/me`, `/api/session/heartbeat`, `/api/session/end` | Identity, reservation extension and usage reconciliation |
The public root serves the React UI. `/__vite_hmr` carries Vite hot updates;
`/v1/realtime` remains the speech connection. `/docs` remains the FastAPI API
explorer, while project documentation files are served in the `/docs/`
subdirectories. Markdown remains
source text, and directories without `index.html` are not browsable pages.
The existing static client remains in `s2s-client` for direct FastAPI and Docker
usage; its static responses retain the existing no-cache headers.
FastAPI serves backend and documentation routes only. The frontend source and
assets live in `s2s-frontend` and are served by Vite.
## Audio path
+2 -2
View File
@@ -3,8 +3,8 @@
Context reaches the assistant through browser-built instructions, server-patched
realtime session instructions, and HTTP reasoning context. The implementation is
in [server.py](../../s2s-client/server.py),
[main.js](../../s2s-client/main.js), and the
[WebSocket client](../../s2s-client/ws/s2s-ws-client.js).
[context.ts](../../s2s-frontend/src/lib/context.ts), and the
[WebSocket client](../../s2s-frontend/src/realtime/client.ts).
## Independent controls
+3 -3
View File
@@ -1,8 +1,8 @@
# Design language
This guide describes the current browser UI. Use
[style.css](../../s2s-client/style.css) as the token/selector reference and
[index.html](../../s2s-client/index.html) for the theme-specific structure.
[legacy-theme.css](../../s2s-frontend/src/styles/legacy-theme.css) as the token/selector
reference and [App.tsx](../../s2s-frontend/src/App.tsx) for the theme-specific structure.
Earlier documentation described a monochrome-only dark UI; the current design
also uses aurora gradients, glass surfaces, and a separate light theme.
@@ -36,7 +36,7 @@ selector overrides rather than a second stylesheet.
| Listening/user speaking | Cyan (`--listening`) |
| Assistant speaking | Violet (`--speaking`) |
| Processing | Amber (`--processing`) |
| Ready/authenticated | Green (`--success`) |
| Ready/success | Green (`--success`) |
| Error | Red (`--error`) |
`--voice-user`, `--voice-assistant` and `--voice-tool` connect transcript roles
+1 -1
View File
@@ -34,7 +34,7 @@ names shown in a deployment are configuration examples, not definitions.
| Queue | Users waiting for an available compute slot; waiting is not yet an active conversation |
| Ticket | A held queue position returned by the load balancer |
| Position | The displayed place in the queue; distinct from a time estimate |
| Claim | The transition from a waiting ticket to an allocated realtime session and usage reservation |
| Claim | The transition from a waiting ticket to an allocated realtime session |
| At capacity | A full waiting queue that cannot accept another ticket |
| Barge-in | User speech interrupts an active assistant response, coordinating playback clearing and backend cancellation |
| Log-only reasoning | External transcript processing that observes/logs the turn without replacing native speech output |
+10 -11
View File
@@ -15,15 +15,15 @@ The mode is normalized case-insensitively. `NODERED` and `NODE-RED` resolve to
## Shared instruction assembly
In [main.js](../../s2s-client/main.js), `effectiveReasoningInstructions()` starts
with `pinnedPrompt || settings.instructions`. `/api/config` pins
In [context.ts](../../s2s-frontend/src/lib/context.ts), `buildInstructions()` starts
with `config.primaryPrompt || settings.instructions`. `/api/config` pins
`S2S_PRIMARY_PROMPT` when non-empty; otherwise the settings prompt or
`DEFAULT_INSTRUCTIONS` is used. The current server does not fetch a remote agent
profile to choose this base prompt.
The browser adds caller context when `injectProfile` is enabled, custom RAG text
when the operator has applied it, and an available ApiPilot catalog block.
`effectiveInstructions()` adds `OPENAI_RENDER_HINT` only in `OPENAI` mode.
`speechInstructions()` adds the speech-rendering hint only in `OPENAI` mode.
The WebSocket client packages these instructions in `session.update.session`;
the local proxy may then add server-resolved context. Details belong in
[context injection](context-injection.md).
@@ -49,8 +49,8 @@ S2S_REASONING_TYPE=NODE_RED
S2S_REASONING_NODE_RED_URL=http://localhost:1881
```
`maybeRunOpenAiReasoning()` handles both external modes despite its historical
function name. It runs after a complete user transcript and posts a body with
`VoiceController.reason()` in [controller.ts](../../s2s-frontend/src/realtime/controller.ts)
handles both external modes. It runs after a complete user transcript and posts a body with
`transcript`, `instructions`, `language`, `user_context`, `phoneNumber`, and
`episode_id`. The server builds context and forwards the reasoning fields to
Node-RED with a 30-second timeout.
@@ -84,10 +84,9 @@ browser's native `session.tools` catalog as OpenAI Chat Completions `tools`.
For a non-log-only answer, the browser cancels/suppresses the active native
response and appends the external assistant text. It uses browser
`SpeechSynthesis` when available. Its compatibility fallback sends
`[VOCERO_RENDER_REPLY]` plus the answer to the realtime backend and requests a
response. The render hint asks the model to read only the answer after that
marker. This instruction is model behavior, not a guarantee of exact playback.
`SpeechSynthesis` when available. The React client adds the answer to the realtime
conversation as an assistant item; without browser speech synthesis it remains
visible as text.
External reasoning adds an HTTP operation to each completed transcript and has
separate provider token usage. It is not the speech pipeline's `responses-api`
@@ -99,8 +98,8 @@ realtime backend instead.
| Goal | Source |
|---|---|
| Change the base agent prompt | `S2S_PRIMARY_PROMPT` or browser settings when unpinned |
| Change browser context assembly | `effectiveReasoningInstructions()` in `main.js` |
| Change browser context assembly | `buildInstructions()` in `s2s-frontend/src/lib/context.ts` |
| Change HTTP provider messages | `reasoning()` in `server.py` |
| Change external-answer rendering | `maybeRunOpenAiReasoning()` in `main.js` |
| Change external-answer rendering | `VoiceController.reason()` and `RealtimeClient.renderExternalAnswer()` |
| Change Node-RED processing | `s2s-reasoning-engine/flows.json` |
| Change native model/tool generation | `speech_to_speech/LLM` and realtime handlers |
+3 -2
View File
@@ -48,8 +48,9 @@ For a native call, the backend emits:
```
`arguments` is a JSON-encoded string. The
[WebSocket client](../../s2s-client/ws/s2s-ws-client.js) parses it and dispatches
`toolcall`; [main.js](../../s2s-client/main.js) runs the supported executor.
[WebSocket client](../../s2s-frontend/src/realtime/client.ts) validates the event and
dispatches `tool`; [controller.ts](../../s2s-frontend/src/realtime/controller.ts)
parses the arguments and runs the supported executor.
Web search uses `POST /api/search`, keeping the deployment's Serper key on the
server. Tool output returns to the model as:
+10 -8
View File
@@ -78,7 +78,7 @@ code before claiming a root cause is resolved.
`S2S_DEBUG_PROMPT=true` enables complete prompt logs, including caller information
and conversation text. Use summaries when full payloads are unnecessary. The
browser's application functions are module-scoped; do not assume names such as
`effectiveInstructions()` are available directly on `window` in DevTools.
`buildInstructions()` are available directly on `window` in DevTools.
## Short gaps or clipped words around tool results
@@ -119,15 +119,17 @@ microphone/audio permissions. If automatic startup fails, use the unlocker
gesture and inspect the permission error rather than increasing the timer
without evidence.
## Read-only source checks
## Local validation
From the repository root, useful syntax checks are:
From the repository root, run the frontend checks and local backend fixtures:
```bash
node --check s2s-client/main.js
node --check s2s-client/ws/s2s-ws-client.js
python -m py_compile s2s-client/server.py
npm --prefix s2s-frontend run check
npm --prefix s2s-frontend run build
.venv/bin/python -m unittest discover -s s2s-client/tests -v
```
Syntax checks do not validate microphone access, network services, model output,
or audio quality. Collect a fresh browser/backend reproduction for those paths.
These checks cover frontend contracts and local session/queue/WebSocket
fixtures. They do not validate deployed services, real microphone hardware,
model output, or audio quality. Use a local browser/backend reproduction for
those paths before deployment.
+17 -18
View File
@@ -70,7 +70,7 @@ See [LLM](../speech/llm.md), [STT](../speech/stt.md), and [TTS](../speech/tts.md
|---|---|---|---|
| `SPEECH_TO_SPEECH_URL` set | Direct to pinned URL | Locked | Disabled |
| `S2S_PROXY_TARGET` set, no direct URL | Same-origin `/v1/realtime` proxy | Locked | Disabled |
| `LOAD_BALANCER_URL` set, neither above set | Per-session compute URL from `/api/session` | Hidden | Queue enabled; metering requires `SPACE_ID` too |
| `LOAD_BALANCER_URL` set, neither above set | Per-session compute URL from `/api/session` | Hidden | Allocation, queue polling and cancellation |
| None set | User-provided realtime URL | Editable | Disabled |
Direct URLs may be full `ws(s)://.../v1/realtime` addresses or host values that
@@ -84,8 +84,9 @@ server; it is not a universally available local service.
## Docker
This existing image serves the retained static client. It does not run the
React/Vite development launcher.
This image serves FastAPI backend routes and shared documentation. Run the
React frontend separately with Vite and point `S2S_BACKEND_URL` at the exposed
backend address. The image does not run the development launcher or serve a UI.
Use the project root as the build context:
@@ -144,24 +145,22 @@ The warmup is a timer, not proof that all upstream context requests have
finished. Browser permission and audio policies can still require a gesture.
The greeting uses the same `hola vocero` input as wake-word handling.
## Queue and daily usage
## Queue and session lifecycle
Load-balancer mode proxies session acquisition and queue polling. Waiting does
not reserve conversation time; a daily budget is reserved when a compute slot
is granted. The server enables metering only with both `LOAD_BALANCER_URL` and
`SPACE_ID` and with neither pinned direct/proxy mode active.
Load-balancer mode proxies `POST /api/session`. A free compute slot returns a
grant containing `session_id`, `connect_url`, and `pending_timeout_s`. Otherwise
the browser receives a queue ticket and polls `GET /api/queue/{queue_id}` until
a slot is granted. The browser offers a timed join action after waiting.
| Environment setting | Default | Purpose |
|---|---|---|
| `LIMIT_ANON_SEC` | 300 | Anonymous seconds per UTC day |
| `LIMIT_FREE_SEC` | 600 | Signed-in non-PRO seconds per UTC day |
| `UNLIMITED_ORGS` | Adds to built-in organizations | Additional unlimited organization members |
| `USAGE_HASH_SECRET` | Random if unset | Identity-key hashing and anonymous cookie signing |
`DELETE /api/queue/{queue_id}` cancels a waiting ticket. On session stop or page
exit, the browser uses `POST /api/queue/end` through `sendBeacon`, falling back
to a keepalive fetch. Expired tickets and missed join deadlines require a new
attempt. Full queues return `503` with `state: "at_capacity"`.
PRO members are unlimited. Built-in unlimited organizations are `cerebras`,
`HuggingFaceM4`, `smolagents`, and `pollen-robotics`; organization matching is
case-insensitive. See [auth.py](../../s2s-client/auth.py) and
[limiter.py](../../s2s-client/limiter.py) for the current enforcement behavior.
Once connected, session teardown closes the microphone, audio graph and
WebSocket. The 30-second inactivity timeout remains active when the assistant
is not speaking. Caller-profile lookup and upstream authorization operate
independently of allocation and use their existing configured endpoints.
For external transcript processing, configure the selected
[reasoning mode](reasoning.md). For connection, context and audio diagnostics,