240 lines
16 KiB
Markdown
240 lines
16 KiB
Markdown
# Repository guidance
|
|
|
|
## Scope and working conventions
|
|
|
|
This repository is Vocero: a browser voice client, its FastAPI integration
|
|
server, a speech pipeline, and optional Node-RED flows. Use the code, commands,
|
|
configuration, and documentation within this checkout. Do not assume other
|
|
repositories or developer-specific directories are available.
|
|
|
|
All file and directory references in this guide are relative to the repository
|
|
root. HTTP routes mentioned below are URLs, not filesystem paths.
|
|
|
|
- Check the current branch, worktree, and working-tree changes before editing.
|
|
Honor the checkout requested by the user, including direct work on `main`.
|
|
Use an isolated worktree when requested and preserve other worktrees.
|
|
- An analysis-only or planning request authorizes inspection, not implementation.
|
|
Return findings with source locations. Present plans in the conversation
|
|
unless the user asks to save a file; follow any explicit approval gate.
|
|
- Write clear explanations and ask one conceptual question at a time. Carry out
|
|
already authorized work without repeatedly asking for permission.
|
|
- Use the native patch/edit tool for manual changes. Prefer `rg`, `fd`, `jq`,
|
|
`git`, and standard CLI tools for inspection; check availability before
|
|
assuming a tool is missing. Do not install system-wide packages unnecessarily.
|
|
- When commits are requested, make coherent, scoped Conventional Commits as work
|
|
progresses. Avoid excessive micro-commits and unrelated cleanup.
|
|
- Invoke `work-on-feature` only when explicitly requested. For a requested PR,
|
|
follow the available `pr-description` skill exactly and put verification
|
|
details in the handoff when its format excludes them. Issues are optional;
|
|
create native Gitea issues or post comments only when requested.
|
|
- When asked to merge locally, use the requested rebase/fast-forward flow;
|
|
preserve a recovery ref before rewriting and verify retained patches. A local
|
|
merge does not imply a push, deployment, or restart. Report those separately.
|
|
- Treat old conversations and proposals as context. Verify claims against the
|
|
current code and distinguish proposed authentication/deployment changes from
|
|
implemented behavior.
|
|
|
|
## Project map
|
|
|
|
| Location | Responsibility |
|
|
| ---------------------------------- | ------------------------------------------------------------------------------------------- |
|
|
| `s2s-client/` | The complete uv project: manifests, environment, Python tooling, tests, and FastAPI package |
|
|
| `s2s-client/s2s_client/main.py` | Application assembly, documentation mount, and retained redirects |
|
|
| `s2s-client/s2s_client/api/` | HTTP routes, route registration, and response validation helper |
|
|
| `s2s-client/s2s_client/models/` | Pydantic request and response contracts |
|
|
| `s2s-client/s2s_client/core/` | Environment parsing, logging, public URLs, and startup diagnostics |
|
|
| `s2s-client/s2s_client/providers/` | ApiPilot and caller/profile/report HTTP integrations |
|
|
| `s2s-client/s2s_client/services/` | Prompt/context construction, reasoning, and tool-context rendering |
|
|
| `s2s-client/s2s_client/realtime/` | WebSocket relay, event handling, marker filtering, and audio forwarding |
|
|
| `s2s-frontend/` | React, TypeScript, Tailwind, and Vite browser application |
|
|
| `speech_to_speech/` | Separately run VAD → STT → LLM → TTS pipeline and realtime API |
|
|
| `s2s-reasoning-engine/` | Optional Node-RED reasoning/logging service |
|
|
| `start_ui.sh` | Dependency sync and supervision of FastAPI plus Vite |
|
|
| `docs/` | Maintained technical documentation, examples, and historical proposals |
|
|
|
|
`s2s-client/server.py` is a compatibility shim for `server:app`; new backend
|
|
implementation belongs in the package. Keep HTTP handlers focused on transport
|
|
and reuse providers/services for shared behavior.
|
|
|
|
The old static browser site, daily talk-time quotas, quota database/accounting,
|
|
and Hugging Face account UI were deliberately removed. Do not restore their
|
|
files, routes, dependencies, or compatibility layers as incidental cleanup.
|
|
Load-balancer allocation/queues and upstream caller authentication remain.
|
|
|
|
## Tooling and checks
|
|
|
|
The uv project must stay inside `s2s-client/`, including
|
|
`s2s-client/pyproject.toml`, `s2s-client/uv.lock`, `s2s-client/.python-version`,
|
|
`s2s-client/.venv/`, `s2s-client/Makefile`, and
|
|
`s2s-client/.pre-commit-config.yaml`.
|
|
Use Python 3.12 and the uv version required by `tool.uv.required-version`
|
|
(currently 0.12.5). Update dependencies through uv and commit the lockfile;
|
|
do not use pip to modify this environment or move it to the repository root.
|
|
Keep uv pins in `s2s-client/Dockerfile` and setup documentation aligned when
|
|
changing the required version.
|
|
|
|
The frontend uses npm and `s2s-frontend/package-lock.json`. Its Node requirement is
|
|
`^22.18.0 || >=24.0.0`; consult `s2s-frontend/package.json` for current scripts.
|
|
Keep application code and supported configuration files in TypeScript.
|
|
|
|
Commands below run from the repository root:
|
|
|
|
| Command | Purpose |
|
|
| ------------------------------------- | --------------------------------------------------------------------- |
|
|
| `make -C s2s-client install` | Sync the locked Python environment and development tools |
|
|
| `make -C s2s-client format` | Apply Ruff fixes and formatting |
|
|
| `make -C s2s-client check` | Check lock freshness, formatting, Ruff lint, mypy, and pytest |
|
|
| `make -C s2s-client test-launcher` | Test readiness, failures, dependency sync, and process-group shutdown |
|
|
| `make -C s2s-client test-image` | Build and smoke-test the actual backend Docker image |
|
|
| `npm --prefix s2s-frontend ci` | Install locked frontend dependencies |
|
|
| `npm --prefix s2s-frontend run check` | Check formatting, lint, TypeScript, Vitest, and launcher integration |
|
|
| `npm --prefix s2s-frontend run build` | Type-check and build frontend assets |
|
|
|
|
Python uses Ruff with a 120-character line limit, double quotes, import ordering,
|
|
and typed application functions checked by mypy. Frontend formatting and lint
|
|
rules live in `s2s-frontend/prettier.config.ts` and
|
|
`s2s-frontend/eslint.config.ts`; use those tools rather than introducing
|
|
competing conventions.
|
|
|
|
Run checks appropriate to the change: backend checks for Python/API work,
|
|
frontend checks and build for browser work, launcher integration for supervisor
|
|
changes, and image tests for packaging/Docker changes. Documentation-only work
|
|
normally needs link/path and diff checks, not service startup. Wait for commands
|
|
to finish before reporting success, and state skipped coverage or blockers.
|
|
|
|
Tests should use mock upstream HTTP and local WebSocket fixtures, isolating
|
|
environment configuration. Do not rely on deployed services, real credentials,
|
|
or model downloads for client tests. The client gate does not test the separate
|
|
speech pipeline, microphone hardware, or real model output. That pipeline has
|
|
no complete dependency manifest or general test gate in this checkout; inspect
|
|
the selected backend and explain the verification limits for speech changes.
|
|
|
|
## API and compatibility rules
|
|
|
|
Read [backend development and compatibility](docs/client/development.md) before
|
|
refactoring the server or changing schemas. Existing wire behavior is the
|
|
baseline, including behavior that looks unsafe, inconsistent, or buggy.
|
|
Intentional bug fixes or contract changes need their own scope and regression
|
|
evidence; do not bundle them into tooling or structural refactors.
|
|
|
|
- Preserve paths, methods, statuses, error shapes, headers, field names,
|
|
omissions, coercion, authentication precedence, and outgoing provider calls.
|
|
Trace both frontend consumers and provider parsing; a permissive consumer is
|
|
not evidence that an upstream schema can safely be tightened.
|
|
- Retain existing request-model defaults and extra-field behavior. Do not make
|
|
new fields required, forbid previously accepted extras, or normalize loosely
|
|
parsed provider values without evidence and an intentional behavior change.
|
|
- Response models validate fields constructed by this service. Upstream-owned
|
|
values remain permissive where the old parser was permissive: `Any`, optional
|
|
fields, open objects, and arbitrary profile JSON are deliberate boundaries.
|
|
- `validated_json_response` in `s2s-client/s2s_client/api/responses.py` validates
|
|
with Pydantic, then serializes the original payload. Do not substitute
|
|
`model_dump()` or automatic response-model serialization: those can insert
|
|
defaults, filter unknown keys,
|
|
coerce values, or alter serialization. Keep OpenAPI declarations aligned with
|
|
the actual validation boundary.
|
|
- Preserve environment/dotenv parsing, default flags, and import-time behavior
|
|
during refactors. Do not introduce stricter configuration validation or change
|
|
authentication behavior as part of a tooling change.
|
|
- `s2s-client/tests/fixtures/legacy-contracts.json` captures the server before the
|
|
migration at `3d42df5`. `s2s-client/tests/test_compatibility.py` compares HTTP
|
|
response bytes, statuses, selected headers, outgoing requests, request schemas,
|
|
and realtime frames. Do not regenerate captures merely to make a refactor pass.
|
|
- Known reasoning failures, ApiPilot authentication/cache quirks, and relay
|
|
behavior are documented in the compatibility guide. Preserve narrow lint/type
|
|
exceptions that retain those behaviors until a deliberate fix is in scope.
|
|
- The pytest warning filter is only for Starlette TestClient's exact deprecated
|
|
AnyIO `BlockingPortal` alias. Do not broaden warning suppression or upgrade
|
|
runtime frameworks just to silence that warning without a compatibility review.
|
|
|
|
## Browser and realtime behavior
|
|
|
|
- React renders the UI; `s2s-frontend/src/realtime/controller.ts` owns observable
|
|
application state, `s2s-frontend/src/realtime/client.ts` owns protocol/session
|
|
lifecycle, and `s2s-frontend/src/audio/` owns the audio graph and worklets.
|
|
Keep high-frequency audio/WebSocket work outside React rendering and preserve
|
|
cleanup across unmounts and reconnects.
|
|
- Preserve the existing 16 kHz PCM16 audio path, worklet registration, interruption
|
|
behavior, and teardown. Session startup initializes audio, waits for
|
|
`session.created`, and sends `session.update` before audio or the greeting.
|
|
A `session.updated` acknowledgment is optional; requiring it previously broke
|
|
startup with this speech service.
|
|
- Preserve query parameters such as `phoneNumber` and `episode_id`, existing
|
|
`s2s.ws.*` preference keys, session-only RAG, and TALK/TALKFIRST behavior unless
|
|
the task explicitly changes them. Context comes from browser instructions and
|
|
server/provider calls; do not assume a vector database or a completed magic-link
|
|
authentication flow exists.
|
|
- Assistant Markdown is parsed with Marked and sanitized with DOMPurify in
|
|
`s2s-frontend/src/lib/messages.ts`. User text stays literal, and exports retain
|
|
raw message text. Keep sanitization and unsafe-link regression coverage when
|
|
changing it.
|
|
- Reuse the existing design tokens, Tailwind integration, and
|
|
`s2s-frontend/src/styles/legacy-theme.css`. Verify both light and dark themes,
|
|
narrow screens, keyboard focus, dialog dismissal, and voice-state feedback for
|
|
UI changes.
|
|
Preserve intentional `?debug=true` controls and browser audio-permission flows.
|
|
- User-visible times use `America/Santiago` where no saved zone exists; avoid
|
|
browser-local timezone assumptions. Keep raw timestamps in their existing API
|
|
and export formats.
|
|
|
|
## Startup and deployment boundaries
|
|
|
|
`./start_ui.sh` sources `.env`, syncs `s2s-client/.venv/` with
|
|
`uv sync --project s2s-client --locked`, and installs frontend packages when the
|
|
manifests change.
|
|
It starts Vite on `0.0.0.0:8420` and FastAPI on `127.0.0.1:8421`, with
|
|
`UI_HOST`, `UI_PORT`, and `S2S_BACKEND_PORT` overrides. Use distinct ports for
|
|
concurrent worktrees. Preserve readiness checks, failure propagation, and
|
|
shutdown of both process groups, including Uvicorn's reload worker.
|
|
|
|
Staging intentionally retains Vite HMR and Uvicorn reload. The Vite child uses
|
|
`NODE_ENV=development` even under PM2. Do not replace this with static production
|
|
serving unless that deployment change is requested. Vite proxies `/api`,
|
|
`/v1/realtime`, and documentation routes to FastAPI; preserve public Host headers
|
|
used to construct WebSocket URLs. Remote staging needs explicit
|
|
`S2S_DEV_ALLOWED_HOSTS` hostnames and reverse-proxy WebSocket forwarding for both
|
|
`/v1/realtime` and `/__vite_hmr`.
|
|
|
|
The established PM2 process name is `s2s_ui`. When it points to this launcher's
|
|
deployed checkout, `pm2 restart s2s_ui --update-env` also performs the locked uv
|
|
sync; a separate sync is optional to prepare dependencies before restarting.
|
|
Ensure the required uv and Node/npm are on that process's PATH. Verify the live
|
|
PM2 script path and environment before making claims about a deployment.
|
|
|
|
The launcher does not start or restart `speech_to_speech/` or Node-RED. A configured
|
|
speech URL does not prove which source revision or implementation runs there.
|
|
`SPEECH_TO_SPEECH_URL` pins a direct browser connection; `S2S_PROXY_TARGET` enables
|
|
the local relay. Either disables load-balancer mode. The Node-RED flows in
|
|
`s2s-reasoning-engine/flows.json` are log-only; preserve the meaning of `log_only`
|
|
responses.
|
|
|
|
The backend Docker image serves APIs, WebSockets, and root `docs/`, not the React
|
|
UI. Build with the repository root as context and `s2s-client/Dockerfile`; the
|
|
image listens on 7860. FastAPI's root remains 404, `/docs` is the API explorer,
|
|
and project documentation is served below `/docs/` with retained legacy redirects.
|
|
|
|
## Language, documentation, and secrets
|
|
|
|
- Use English for code, identifiers, comments, logs, internal instructions, and
|
|
developer documentation. Spanish is the default for visible UI, spoken output,
|
|
and user-facing API messages. Preserve literal wake phrases and protocol names;
|
|
translating an existing response or prompt can change a contract.
|
|
- Keep maintained documentation under `docs/client/`, `docs/speech/`, `docs/adr/`,
|
|
`docs/examples/`, or `docs/proposals/`. Update existing documents first and keep
|
|
`docs/README.md` current when adding a document. Use one canonical English file
|
|
without language suffixes. Historical proposals do not define current behavior.
|
|
- Reserve root Markdown for established files such as `README.md` and `AGENTS.md`.
|
|
Keep task summaries in the conversation unless a saved artifact is requested.
|
|
Record notable changes under `Unreleased` in `s2s-client/CHANGELOG.md`.
|
|
- Keep credentials and deployment values out of commits and examples. Never copy
|
|
the root `.env` into the frontend or expose backend secrets through `VITE_*`.
|
|
The launcher sources `.env` before startup, so it can override shell values;
|
|
direct Python startup uses `load_dotenv(..., override=False)`. Preserve both
|
|
behaviors and avoid importing live configuration when a test can isolate it.
|
|
|
|
Start with [usage](docs/client/usage.md), [architecture](docs/client/architecture.md),
|
|
[development](docs/client/development.md), and [troubleshooting](docs/client/troubleshooting.md).
|
|
For speech work, read [the realtime protocol](docs/speech/realtime-api.md) and the
|
|
relevant [STT](docs/speech/stt.md), [LLM](docs/speech/llm.md), or
|
|
[TTS](docs/speech/tts.md) guide before changing pipeline behavior.
|