diff --git a/.gitignore b/.gitignore index f2b3fda..4ea628b 100644 --- a/.gitignore +++ b/.gitignore @@ -38,8 +38,8 @@ node_modules/ .npm/ package-lock.json !speech_to_speech/package-lock.json -!s2s-frontend/package-lock.json -s2s-frontend/.eslintcache +!vocero-frontend/package-lock.json +vocero-frontend/.eslintcache yarn-error.log npm-debug.log* yarn-debug.log* @@ -125,10 +125,10 @@ CVS/ # ───────────────────────────────────────────────────────────── # Client backend and speech-service runtime artifacts # ───────────────────────────────────────────────────────────── -s2s-client/.cache/ -s2s-client/uploads/ -s2s-client/sessions/ -s2s-client/*.log +vocero-api/.cache/ +vocero-api/uploads/ +vocero-api/sessions/ +vocero-api/*.log speech_to_speech/*.log speech_to_speech/sessions/ speech_to_speech/.cache/ diff --git a/AGENTS.md b/AGENTS.md index 405a325..7fce268 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3,12 +3,13 @@ ## Scope and working conventions This repository is Vocero: a browser voice client, its FastAPI integration -server, and a speech pipeline. Use the code, commands, -configuration, and documentation within this checkout. Do not assume other -repositories or developer-specific directories are available. +server, and a speech pipeline. Keep this guide self-contained and base its +instructions on this checkout's code, configuration, documentation, and tools. -All file and directory references in this guide are relative to the repository -root. HTTP routes mentioned below are URLs, not filesystem paths. +All file and directory references in this guide must point within the repository +and be relative to its root. Commands may use the project's command-line tools, +such as `rg`, `git`, `uv`, `npm`, Docker, and PM2. 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`. @@ -23,35 +24,34 @@ root. HTTP routes mentioned below are URLs, not filesystem paths. 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. +- For a requested pull request, describe the problem, resulting behavior, + validation, and relevant limitations. Create 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. +- Treat historical proposals in `docs/proposals/` as context. Verify their + claims against current code and distinguish proposed authentication or + 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 | +| `vocero-api/` | The complete uv project: manifests, environment, Python tooling, tests, and FastAPI package | +| `vocero-api/vocero_api/main.py` | Application assembly, documentation mount, and retained redirects | +| `vocero-api/vocero_api/api/` | HTTP routes, route registration, and response validation helper | +| `vocero-api/vocero_api/models/` | Pydantic request and response contracts | +| `vocero-api/vocero_api/core/` | Environment parsing, logging, public URLs, and startup diagnostics | +| `vocero-api/vocero_api/providers/` | ApiPilot and caller/profile/report HTTP integrations | +| `vocero-api/vocero_api/services/` | Prompt/context construction, reasoning, and tool-context rendering | +| `vocero-api/vocero_api/realtime/` | WebSocket relay, event handling, marker filtering, and audio forwarding | +| `vocero-frontend/` | React, TypeScript, Tailwind, and Vite browser application | | `speech_to_speech/` | Separately run VAD → STT → LLM → TTS pipeline and realtime API | | `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 +`vocero-api/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. @@ -62,37 +62,37 @@ 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`. +The uv project must stay inside `vocero-api/`, including +`vocero-api/pyproject.toml`, `vocero-api/uv.lock`, `vocero-api/.python-version`, +`vocero-api/.venv/`, `vocero-api/Makefile`, and +`vocero-api/.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 +Keep uv pins in `vocero-api/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. +The frontend uses npm and `vocero-frontend/package-lock.json`. Its Node requirement is +`^22.18.0 || >=24.0.0`; consult `vocero-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 | +| `make -C vocero-api install` | Sync the locked Python environment and development tools | +| `make -C vocero-api format` | Apply Ruff fixes and formatting | +| `make -C vocero-api check` | Check lock freshness, formatting, Ruff lint, mypy, and pytest | +| `make -C vocero-api test-launcher` | Test readiness, failures, dependency sync, and process-group shutdown | +| `make -C vocero-api test-image` | Build and smoke-test the actual backend Docker image | +| `npm --prefix vocero-frontend ci` | Install locked frontend dependencies | +| `npm --prefix vocero-frontend run check` | Check formatting, lint, TypeScript, Vitest, and launcher integration | +| `npm --prefix vocero-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 +rules live in `vocero-frontend/prettier.config.ts` and +`vocero-frontend/eslint.config.ts`; use those tools rather than introducing competing conventions. Run checks appropriate to the change: backend checks for Python/API work, @@ -126,7 +126,7 @@ evidence; do not bundle them into tooling or structural refactors. - 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 +- `validated_json_response` in `vocero-api/vocero_api/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, @@ -135,8 +135,8 @@ evidence; do not bundle them into tooling or structural refactors. - 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 +- `vocero-api/tests/fixtures/legacy-contracts.json` captures the server before the + migration at `3d42df5`. `vocero-api/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 @@ -148,9 +148,9 @@ evidence; do not bundle them into tooling or structural refactors. ## 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. +- React renders the UI; `vocero-frontend/src/realtime/controller.ts` owns observable + application state, `vocero-frontend/src/realtime/client.ts` owns protocol/session + lifecycle, and `vocero-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 @@ -164,11 +164,11 @@ evidence; do not bundle them into tooling or structural refactors. 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 + `vocero-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, + `vocero-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. @@ -178,8 +178,8 @@ evidence; do not bundle them into tooling or structural refactors. ## 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 +`./start_ui.sh` loads the optional root `.env`, syncs `vocero-api/.venv/` with +`uv sync --project vocero-api --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 @@ -207,7 +207,7 @@ the local relay. Either disables load-balancer mode. Reasoning supports `PROXY` and `OPENAI`; unknown `S2S_REASONING_TYPE` values fall back to `PROXY`. 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 +UI. Build with the repository root as context and `vocero-api/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. @@ -223,10 +223,10 @@ and project documentation is served below `/docs/` with retained legacy redirect 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`. + Record notable changes under `Unreleased` in `vocero-api/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; + The launcher loads `.env` when present, so its values 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. diff --git a/README.md b/README.md index 702b693..48ba8b2 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ documentation, source identifiers, comments, and internal instructions use Engli | Component | Purpose | |---|---| -| [s2s-frontend](s2s-frontend) | React, TypeScript, and Tailwind browser UI, served by Vite with hot reload | -| [s2s-client](s2s-client) | FastAPI backend, realtime WebSocket proxy, search and ApiPilot bridges | +| [vocero-frontend](vocero-frontend) | React, TypeScript, and Tailwind browser UI, served by Vite with hot reload | +| [vocero-api](vocero-api) | FastAPI backend, realtime WebSocket proxy, search and ApiPilot bridges | | [speech_to_speech](speech_to_speech) | Queue-driven VAD, speech recognition, language model, and speech synthesis pipeline | Start with [client setup and usage](docs/client/usage.md), then read the [architecture](docs/client/architecture.md) and [documentation index](docs/README.md). The speech backend needs its own installed model and runtime dependencies; -The uv project in `s2s-client/` installs only the UI server dependencies. +The uv project in `vocero-api/` installs only the UI server dependencies. ## Run the browser client @@ -24,7 +24,7 @@ S2S_PROXY_TARGET=ws://localhost:8765/v1/realtime ./start_ui.sh ``` Open and allow microphone access. The launcher creates -`s2s-client/.venv`, syncs locked Python and frontend dependencies, +`vocero-api/.venv`, syncs locked Python and frontend dependencies, and loads the root `.env` if present. Vite listens on `UI_PORT` (default `8420`) and forwards HTTP/WebSocket backend routes to FastAPI on `127.0.0.1:8421`. Both servers reload during development; stopping the launcher stops both. @@ -33,16 +33,16 @@ Leave `SPEECH_TO_SPEECH_URL` unset to use the same-origin speech proxy. The launcher requires uv 0.12.5 (which provisions Python 3.12), Node.js 22.18+ on the 22.x line or Node.js 24+, npm, Bash 4.4+, `setsid`, and `curl`. For staging hostnames, set `S2S_DEV_ALLOWED_HOSTS` and forward the Vite HMR WebSocket through the existing -HTTPS reverse proxy. See [development setup](s2s-frontend/README.md). +HTTPS reverse proxy. See [development setup](vocero-frontend/README.md). The Docker image serves backend routes and shared documentation. The React frontend runs separately through Vite. To build the backend image, use the repository root as the Docker build context so documentation is included: ```bash -docker build -f s2s-client/Dockerfile -t s2s-client . +docker build -f vocero-api/Dockerfile -t vocero-api . docker run --rm -p 7860:7860 \ - -e SPEECH_TO_SPEECH_URL=ws://localhost:8765/v1/realtime s2s-client + -e SPEECH_TO_SPEECH_URL=ws://localhost:8765/v1/realtime vocero-api ``` The direct WebSocket URL is resolved by the user's browser. For a server-side @@ -50,7 +50,7 @@ proxy deployment, supply a backend address reachable from the container instead. ## Project references -- [Change history](s2s-client/CHANGELOG.md) +- [Change history](vocero-api/CHANGELOG.md) - [Backend development and compatibility](docs/client/development.md) - [Realtime protocol and pipeline](docs/speech/realtime-api.md) - [Context injection](docs/client/context-injection.md) diff --git a/docs/README.md b/docs/README.md index c667713..234c10d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -15,7 +15,7 @@ do not define the current implementation. - [Design language](client/design.md) - [Glossary](client/glossary.md) - [Troubleshooting](client/troubleshooting.md) -- [Change history](../s2s-client/CHANGELOG.md) +- [Change history](../vocero-api/CHANGELOG.md) ## Speech backend diff --git a/docs/client/architecture.md b/docs/client/architecture.md index b4a1d56..a573884 100644 --- a/docs/client/architecture.md +++ b/docs/client/architecture.md @@ -1,7 +1,7 @@ # Client architecture -The [React browser client](../../s2s-frontend) uses the -[FastAPI backend](../../s2s-client) for HTTP APIs and a speech WebSocket relay. +The [React browser client](../../vocero-frontend) uses the +[FastAPI backend](../../vocero-api) for HTTP APIs and a speech WebSocket relay. `start_ui.sh` serves Vite on the existing public port `8420` and FastAPI on loopback port `8421`. Vite forwards backend routes and provides hot reload; Uvicorn reloads Python edits. The launcher supervises both processes. @@ -42,22 +42,22 @@ audio. It is not an unconditional byte-for-byte tunnel. | Source | Responsibility | |---|---| -| [server.py](../../s2s-client/server.py) | Compatibility entry point for existing `server:app` commands | -| [main.py](../../s2s-client/s2s_client/main.py) | FastAPI assembly, documentation serving and redirects | -| [api](../../s2s-client/s2s_client/api) | HTTP route registration, handlers and compatible response validation | -| [models](../../s2s-client/s2s_client/models) | Pydantic request and response contracts | -| [core](../../s2s-client/s2s_client/core) | Existing environment parsing, logging, URL helpers and diagnostics | -| [providers](../../s2s-client/s2s_client/providers) | Caller context and ApiPilot HTTP integrations | -| [services](../../s2s-client/s2s_client/services) | Context construction, reasoning and tool-context rendering | -| [realtime](../../s2s-client/s2s_client/realtime) | WebSocket relay, events, markers and audio handling | +| [server.py](../../vocero-api/server.py) | Compatibility entry point for existing `server:app` commands | +| [main.py](../../vocero-api/vocero_api/main.py) | FastAPI assembly, documentation serving and redirects | +| [api](../../vocero-api/vocero_api/api) | HTTP route registration, handlers and compatible response validation | +| [models](../../vocero-api/vocero_api/models) | Pydantic request and response contracts | +| [core](../../vocero-api/vocero_api/core) | Existing environment parsing, logging, URL helpers and diagnostics | +| [providers](../../vocero-api/vocero_api/providers) | Caller context and ApiPilot HTTP integrations | +| [services](../../vocero-api/vocero_api/services) | Context construction, reasoning and tool-context rendering | +| [realtime](../../vocero-api/vocero_api/realtime) | WebSocket relay, events, markers and audio handling | | [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, 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 | +| [vite.config.ts](../../vocero-frontend/vite.config.ts) | Public development endpoint, HMR, allowed hosts, and backend routing | +| [App.tsx](../../vocero-frontend/src/App.tsx) | React layout, voice controls, dialogs, profile and reports sidebar | +| [controller.ts](../../vocero-frontend/src/realtime/controller.ts) | Application state, settings, context, queue, tools and external reasoning | +| [client.ts](../../vocero-frontend/src/realtime/client.ts) | Realtime protocol, session lifecycle, audio and transcript events | +| [components](../../vocero-frontend/src/components) | Chat, caller profile, settings and voice components | +| [audio](../../vocero-frontend/src/audio) | TypeScript capture/playback worklets, PCM codec, and visualizer | +| [legacy-theme.css](../../vocero-frontend/src/styles/legacy-theme.css) | Retained themes and responsive visual styles alongside Tailwind | ## Server routes @@ -79,9 +79,9 @@ explorer, while project documentation files are served in the `/docs/` subdirectories. Markdown remains source text, and directories without `index.html` are not browsable pages. FastAPI serves backend and documentation routes only. The frontend source and -assets live in `s2s-frontend` and are served by Vite. +assets live in `vocero-frontend` and are served by Vite. -The uv project and its Python tools are scoped to `s2s-client/`. Response models +The uv project and its Python tools are scoped to `vocero-api/`. Response models describe service-owned structure and retain loose upstream values. See [development and compatibility](development.md) before tightening a contract. @@ -89,11 +89,11 @@ describe service-owned structure and retain loose upstream values. See 1. The browser captures microphone audio with echo cancellation, noise suppression and automatic gain-control settings. -2. [mic-capture.worklet.ts](../../s2s-frontend/src/audio/mic-capture.worklet.ts) resamples to 16 kHz +2. [mic-capture.worklet.ts](../../vocero-frontend/src/audio/mic-capture.worklet.ts) resamples to 16 kHz mono PCM16. The browser sends base64 `input_audio_buffer.append` frames. 3. The backend processes VAD → STT → LLM → TTS and emits realtime events. 4. Assistant PCM16 audio at the current 16 kHz protocol rate is decoded and sent to - [audio-playback.worklet.ts](../../s2s-frontend/src/audio/audio-playback.worklet.ts), which buffers, + [audio-playback.worklet.ts](../../vocero-frontend/src/audio/audio-playback.worklet.ts), which buffers, resamples and fades playback edges. Input speech can clear playback for barge-in. Audio worklets are loaded through `AudioWorklet.addModule`; keep their processor diff --git a/docs/client/context-injection.md b/docs/client/context-injection.md index 8a6e62e..a10bb08 100644 --- a/docs/client/context-injection.md +++ b/docs/client/context-injection.md @@ -2,10 +2,10 @@ Context reaches the assistant through browser-built instructions, server-patched realtime session instructions, and HTTP reasoning context. The implementation is -in the server's [context services](../../s2s-client/s2s_client/services/context.py), -[realtime proxy](../../s2s-client/s2s_client/realtime/proxy.py), -[context.ts](../../s2s-frontend/src/lib/context.ts), and the -[WebSocket client](../../s2s-frontend/src/realtime/client.ts). +in the server's [context services](../../vocero-api/vocero_api/services/context.py), +[realtime proxy](../../vocero-api/vocero_api/realtime/proxy.py), +[context.ts](../../vocero-frontend/src/lib/context.ts), and the +[WebSocket client](../../vocero-frontend/src/realtime/client.ts). ## Independent controls diff --git a/docs/client/design.md b/docs/client/design.md index f2a6fb5..11e4944 100644 --- a/docs/client/design.md +++ b/docs/client/design.md @@ -1,8 +1,8 @@ # Design language This guide describes the current browser UI. Use -[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. +[legacy-theme.css](../../vocero-frontend/src/styles/legacy-theme.css) as the token/selector +reference and [App.tsx](../../vocero-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. diff --git a/docs/client/development.md b/docs/client/development.md index f5068a7..b68a5d3 100644 --- a/docs/client/development.md +++ b/docs/client/development.md @@ -1,11 +1,18 @@ # Backend development and compatibility -The FastAPI project lives entirely in `s2s-client/`. It uses uv 0.12.5, Python +The FastAPI project lives entirely in `vocero-api/`. It uses uv 0.12.5, Python 3.12, and a committed `uv.lock`. The frontend and speech pipeline keep their own tooling. Runtime dependency versions match the environment used to capture the pre-migration server; framework upgrades require a separate compatibility review. +The Python distribution is named `vocero-api`, and its import package is +`vocero_api`. The browser package and directory are both named `vocero-frontend`. +`start_ui.sh` creates the API environment at its current path on the next start; +do not move an existing virtual environment because its executables contain +absolute paths. Reinstall optional Git hooks after a project-directory rename +with `make -C vocero-api hooks-install`. + Pytest filters only the known `anyio.abc.BlockingPortal` deprecation emitted by Starlette's test client ([upstream issue](https://github.com/Kludex/starlette/issues/3497)). The filter matches the exact message, warning category, and module; other @@ -17,24 +24,24 @@ the deprecated alias. Runtime dependencies and warning handling are unchanged. From the repository root: ```bash -make -C s2s-client install # uv sync --locked, including development tools -make -C s2s-client format # Ruff safe fixes and formatting -make -C s2s-client check # lock freshness, format, lint, mypy, pytest -make -C s2s-client test-launcher # Bash supervisor integration fixtures -make -C s2s-client test-image # Docker build and isolated runtime smoke test +make -C vocero-api install # uv sync --locked, including development tools +make -C vocero-api format # Ruff safe fixes and formatting +make -C vocero-api check # lock freshness, format, lint, mypy, pytest +make -C vocero-api test-launcher # Bash supervisor integration fixtures +make -C vocero-api test-image # Docker build and isolated runtime smoke test ``` -Run `uv lock` inside `s2s-client/` when intentionally changing dependencies. +Run `uv lock` inside `vocero-api/` when intentionally changing dependencies. Do not install server packages into a shared root environment. The launcher -syncs `s2s-client/.venv` and retains root `.env` loading, port defaults, +syncs `vocero-api/.venv` and retains root `.env` loading, port defaults, readiness checks, and process-group shutdown. Offline starts require the locked packages and Python runtime to have been installed or cached already. -Optional hooks use `make -C s2s-client hooks-install`; the configuration stays -in `s2s-client/.pre-commit-config.yaml`. `make -C s2s-client hooks-run` runs the +Optional hooks use `make -C vocero-api hooks-install`; the configuration stays +in `vocero-api/.pre-commit-config.yaml`. `make -C vocero-api hooks-run` runs the same hooks without installing them. Hooks and Python quality checks apply -only to the client service. The server entry points are `s2s_client.main:app` -and the retained `server:app` shim; use `--app-dir s2s-client` from the root. +only to the client service. The server entry points are `vocero_api.main:app` +and the retained `server:app` shim; use `--app-dir vocero-api` from the root. ## Schema boundaries diff --git a/docs/client/reasoning.md b/docs/client/reasoning.md index fe69a0b..46d727a 100644 --- a/docs/client/reasoning.md +++ b/docs/client/reasoning.md @@ -13,7 +13,7 @@ The mode is normalized case-insensitively. Unknown values resolve to `PROXY`. ## Shared instruction assembly -In [context.ts](../../s2s-frontend/src/lib/context.ts), `buildInstructions()` starts +In [context.ts](../../vocero-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 @@ -50,7 +50,7 @@ OPENAI_REASONING_MODEL=gpt-4.1-mini `OPENAI_ORGANIZATION` and `OPENAI_PROJECT` are optional headers. The model value is a runtime default, not a recommendation about provider availability. -`VoiceController.reason()` in [controller.ts](../../s2s-frontend/src/realtime/controller.ts) +`VoiceController.reason()` in [controller.ts](../../vocero-frontend/src/realtime/controller.ts) runs after a complete user transcript and posts a body with `transcript`, `instructions`, `language`, `user_context`, `phoneNumber`, and `episode_id`. The server combines instructions and context into model messages, requests a @@ -76,7 +76,7 @@ realtime backend instead. | Goal | Source | |---|---| | Change the base agent prompt | `S2S_PRIMARY_PROMPT` or browser settings when unpinned | -| Change browser context assembly | `buildInstructions()` in `s2s-frontend/src/lib/context.ts` | -| Change HTTP provider messages | `reasoning()` in `s2s-client/s2s_client/services/reasoning.py` | +| Change browser context assembly | `buildInstructions()` in `vocero-frontend/src/lib/context.ts` | +| Change HTTP provider messages | `reasoning()` in `vocero-api/vocero_api/services/reasoning.py` | | Change external-answer rendering | `VoiceController.reason()` and `RealtimeClient.renderExternalAnswer()` | | Change native model/tool generation | `speech_to_speech/LLM` and realtime handlers | diff --git a/docs/client/tool-calling.md b/docs/client/tool-calling.md index ec5767e..9d1300c 100644 --- a/docs/client/tool-calling.md +++ b/docs/client/tool-calling.md @@ -48,8 +48,8 @@ For a native call, the backend emits: ``` `arguments` is a JSON-encoded string. The -[WebSocket client](../../s2s-frontend/src/realtime/client.ts) validates the event and -dispatches `tool`; [controller.ts](../../s2s-frontend/src/realtime/controller.ts) +[WebSocket client](../../vocero-frontend/src/realtime/client.ts) validates the event and +dispatches `tool`; [controller.ts](../../vocero-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: diff --git a/docs/client/troubleshooting.md b/docs/client/troubleshooting.md index 05bb4ed..aa46a92 100644 --- a/docs/client/troubleshooting.md +++ b/docs/client/troubleshooting.md @@ -37,7 +37,7 @@ the separate `/v1/realtime` WebSocket on the same public origin. For a startup dependency error, check uv 0.12.5 and Node/npm on the process manager's PATH, plus `setsid`/`curl`. uv manages Python 3.12 in -`s2s-client/.venv` and checks the committed lock on every start. Frontend +`vocero-api/.venv` and checks the committed lock on every start. Frontend dependencies are reused until their manifests change. Changes to root `.env` or launcher settings require a restart. @@ -123,9 +123,9 @@ without evidence. From the repository root, run the frontend checks and local backend fixtures: ```bash -npm --prefix s2s-frontend run check -npm --prefix s2s-frontend run build -make -C s2s-client check +npm --prefix vocero-frontend run check +npm --prefix vocero-frontend run build +make -C vocero-api check ``` These checks cover frontend contracts and local session/queue/WebSocket diff --git a/docs/client/usage.md b/docs/client/usage.md index 933a29d..12764ef 100644 --- a/docs/client/usage.md +++ b/docs/client/usage.md @@ -12,8 +12,8 @@ From the repository root: S2S_PROXY_TARGET=ws://localhost:8765/v1/realtime ./start_ui.sh ``` -The launcher loads the root `.env`, syncs `s2s-client/.venv` from the committed -`s2s-client/uv.lock`, and installs the locked +The launcher loads the root `.env`, syncs `vocero-api/.venv` from the committed +`vocero-api/uv.lock`, and installs the locked frontend dependencies when needed. Vite serves the React frontend on port 8420 and proxies the backend routes to Uvicorn on `127.0.0.1:8421`. `UI_PORT` overrides the public port; `S2S_BACKEND_PORT` overrides the internal @@ -24,21 +24,21 @@ the script, values in that file can replace variables supplied on the command li Use Node.js 22.18+ on the 22.x line or Node.js 24+, npm, uv 0.12.5, Bash 4.4+, `setsid`, and `curl`. Remote staging access also needs the exact hostname in `S2S_DEV_ALLOWED_HOSTS`, HTTPS, and WebSocket forwarding for speech -and Vite HMR. See [frontend development setup](../../s2s-frontend/README.md). +and Vite HMR. See [frontend development setup](../../vocero-frontend/README.md). To install and start the backend manually: ```bash -uv sync --project s2s-client --locked +uv sync --project vocero-api --locked S2S_PROXY_TARGET=ws://localhost:8765/v1/realtime \ - uv run --project s2s-client --locked uvicorn --app-dir s2s-client s2s_client.main:app \ - --host 127.0.0.1 --port 8421 --reload --reload-dir s2s-client + uv run --project vocero-api --locked uvicorn --app-dir vocero-api vocero_api.main:app \ + --host 127.0.0.1 --port 8421 --reload --reload-dir vocero-api ``` -Then, in a second terminal, run `cd s2s-frontend`, `npm ci`, and `npm run dev`. +Then, in a second terminal, run `cd vocero-frontend`, `npm ci`, and `npm run dev`. uv provisions Python 3.12 when needed. The Python environment belongs only to -`s2s-client`; an existing root `.venv` for speech dependencies is left alone. +`vocero-api`; an existing root `.venv` for speech dependencies is left alone. See [development and compatibility](development.md) for checks and schema rules. Open , tap the orb/microphone control, grant microphone @@ -94,9 +94,9 @@ backend address. The image does not run the development launcher or serve a UI. Use the project root as the build context: ```bash -docker build -f s2s-client/Dockerfile -t s2s-client . +docker build -f vocero-api/Dockerfile -t vocero-api . docker run --rm -p 7860:7860 \ - -e SPEECH_TO_SPEECH_URL=ws://localhost:8765/v1/realtime s2s-client + -e SPEECH_TO_SPEECH_URL=ws://localhost:8765/v1/realtime vocero-api ``` For the proxy path, replace the direct setting with a server-reachable @@ -104,7 +104,7 @@ For the proxy path, replace the direct setting with a server-reachable appropriate existing network and use that service's hostname. An upstream bound only to host loopback is not reachable via an arbitrary container name. -The image includes `s2s-client` and root `docs`. `/docs` is still the FastAPI API +The image includes `vocero-api` and root `docs`. `/docs` is still the FastAPI API explorer; documentation lives under `/docs/client/`, `/docs/speech/`, `/docs/adr/`, `/docs/examples/`, and `/docs/proposals/`. Legacy `/project/` and `/public/docs.html` redirect to the historical proposal. Markdown is served as a diff --git a/start_ui.sh b/start_ui.sh index 2554da4..71d2e90 100755 --- a/start_ui.sh +++ b/start_ui.sh @@ -2,9 +2,9 @@ set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -CLIENT_DIR="$ROOT_DIR/s2s-client" -VENV_DIR="$CLIENT_DIR/.venv" -FRONTEND_DIR="$ROOT_DIR/s2s-frontend" +API_DIR="$ROOT_DIR/vocero-api" +VENV_DIR="$API_DIR/.venv" +FRONTEND_DIR="$ROOT_DIR/vocero-frontend" if [ -f "$ROOT_DIR/.env" ]; then set -a source "$ROOT_DIR/.env" @@ -28,7 +28,7 @@ for required_command in uv node npm setsid curl; do done if ! node -e 'const [major, minor] = process.versions.node.split(".").map(Number); process.exit((major === 22 && minor >= 18) || major >= 24 ? 0 : 1)'; then - echo "Error: s2s-frontend requires Node.js 22.18+ on the 22.x line, or Node.js 24+." + echo "Error: vocero-frontend requires Node.js 22.18+ on the 22.x line, or Node.js 24+." exit 1 fi @@ -46,19 +46,19 @@ if [ "$UI_PORT" = "$S2S_BACKEND_PORT" ]; then exit 1 fi -if [ ! -f "$CLIENT_DIR/pyproject.toml" ] || [ ! -f "$CLIENT_DIR/uv.lock" ]; then - echo "Error: s2s-client/pyproject.toml and s2s-client/uv.lock are required." +if [ ! -f "$API_DIR/pyproject.toml" ] || [ ! -f "$API_DIR/uv.lock" ]; then + echo "Error: vocero-api/pyproject.toml and vocero-api/uv.lock are required." exit 1 fi -# uv manages only the client environment; an existing root speech venv is separate. -UV_PROJECT_ENVIRONMENT="$VENV_DIR" uv sync --project "$CLIENT_DIR" --locked +# uv manages only the API environment; an existing root speech venv is separate. +UV_PROJECT_ENVIRONMENT="$VENV_DIR" uv sync --project "$API_DIR" --locked # Keep restarts offline when the installed frontend manifests have not changed. if [ ! -f "$FRONTEND_DIR/node_modules/vite/bin/vite.js" ] || \ ! cmp -s "$FRONTEND_DIR/package-lock.json" "$FRONTEND_DIR/node_modules/.s2s-package-lock.json" || \ ! cmp -s "$FRONTEND_DIR/package.json" "$FRONTEND_DIR/node_modules/.s2s-package.json"; then - echo "Installing frontend dependencies from s2s-frontend/package-lock.json..." + echo "Installing frontend dependencies from vocero-frontend/package-lock.json..." npm --prefix "$FRONTEND_DIR" ci --include=dev cp "$FRONTEND_DIR/package-lock.json" "$FRONTEND_DIR/node_modules/.s2s-package-lock.json" cp "$FRONTEND_DIR/package.json" "$FRONTEND_DIR/node_modules/.s2s-package.json" @@ -109,10 +109,10 @@ echo " Press Ctrl+C in this terminal to stop both servers." echo "----------------------------------------------------" cd "$ROOT_DIR" -setsid "$VENV_DIR/bin/python" -m uvicorn s2s_client.main:app \ - --app-dir "$CLIENT_DIR" \ +setsid "$VENV_DIR/bin/python" -m uvicorn vocero_api.main:app \ + --app-dir "$API_DIR" \ --host 127.0.0.1 --port "$S2S_BACKEND_PORT" \ - --reload --reload-dir "$CLIENT_DIR" & + --reload --reload-dir "$API_DIR" & backend_pid=$! child_pids+=("$backend_pid") diff --git a/s2s-client/.dockerignore b/vocero-api/.dockerignore similarity index 100% rename from s2s-client/.dockerignore rename to vocero-api/.dockerignore diff --git a/s2s-client/.gitignore b/vocero-api/.gitignore similarity index 100% rename from s2s-client/.gitignore rename to vocero-api/.gitignore diff --git a/s2s-client/.pre-commit-config.yaml b/vocero-api/.pre-commit-config.yaml similarity index 65% rename from s2s-client/.pre-commit-config.yaml rename to vocero-api/.pre-commit-config.yaml index 59ed36d..6663347 100644 --- a/s2s-client/.pre-commit-config.yaml +++ b/vocero-api/.pre-commit-config.yaml @@ -7,21 +7,21 @@ repos: hooks: - id: ruff-check name: Ruff lint - entry: uv run --project s2s-client --locked ruff check --force-exclude + entry: uv run --project vocero-api --locked ruff check --force-exclude language: unsupported types_or: [python, pyi] - files: ^s2s-client/ + files: ^vocero-api/ require_serial: true - id: ruff-format name: Ruff formatting - entry: uv run --project s2s-client --locked ruff format --check --force-exclude + entry: uv run --project vocero-api --locked ruff format --check --force-exclude language: unsupported types_or: [python, pyi] - files: ^s2s-client/ + files: ^vocero-api/ require_serial: true - id: mypy name: Python types - entry: uv run --directory s2s-client --locked mypy + entry: uv run --directory vocero-api --locked mypy language: unsupported pass_filenames: false - files: ^s2s-client/(s2s_client/|server\.py|pyproject\.toml|uv\.lock) + files: ^vocero-api/(vocero_api/|server\.py|pyproject\.toml|uv\.lock) diff --git a/s2s-client/.python-version b/vocero-api/.python-version similarity index 100% rename from s2s-client/.python-version rename to vocero-api/.python-version diff --git a/s2s-client/CHANGELOG.md b/vocero-api/CHANGELOG.md similarity index 95% rename from s2s-client/CHANGELOG.md rename to vocero-api/CHANGELOG.md index 3b97d9f..1f03219 100644 --- a/s2s-client/CHANGELOG.md +++ b/vocero-api/CHANGELOG.md @@ -1,4 +1,4 @@ -# Changelog — s2s-client (Azurian Vocero) +# Changelog — vocero-api (Azurian Vocero) All significant client changes are recorded here. This English history supersedes the former Italian and Spanish versions. Historical entries describe @@ -9,20 +9,25 @@ the implementation at the recorded date; current operating instructions live in ### Changed +- Renamed the API and browser projects to `vocero-api/` and `vocero-frontend/`. + Python uses the `vocero-api` distribution and `vocero_api` import package; + npm uses `vocero-frontend`. Updated the launcher, Docker setup, tooling, + tests, and documentation while preserving HTTP/WebSocket contracts and + existing environment variables, browser preferences, and PM2 process names. - Removed the optional workflow reasoning service, its configuration, transcript forwarding, and browser integration. Reasoning supports `PROXY` and `OPENAI`; unsupported mode values fall back to `PROXY`. - Filter the specific Starlette TestClient / AnyIO `BlockingPortal` deprecation in pytest while retaining the runtime versions used for compatibility captures. - Added a uv-managed Python 3.12 environment and Ruff, mypy, and pytest quality - checks, all contained in `s2s-client/`. Startup and Docker use the committed + checks, all contained in `vocero-api/`. Startup and Docker use the committed lockfile. Split the FastAPI service into configuration, route, provider, context/reasoning, and realtime modules while retaining `server:app` startup. - Added Pydantic response contracts with permissive upstream fields and original payload serialization. Captured HTTP, request-schema, and realtime regression fixtures preserve existing API behavior, including known failures. - Removed the legacy browser site in favor of the React application in - `s2s-frontend`. FastAPI now serves only backend and documentation routes. + `vocero-frontend`. FastAPI now serves only backend and documentation routes. - Retired daily talk-time accounting and the associated Hugging Face account UI. Load-balancer allocation, queue lifecycle, caller profiles, upstream authentication, and voice-session cleanup remain available. diff --git a/s2s-client/Dockerfile b/vocero-api/Dockerfile similarity index 57% rename from s2s-client/Dockerfile rename to vocero-api/Dockerfile index 4d43a41..c633983 100644 --- a/s2s-client/Dockerfile +++ b/vocero-api/Dockerfile @@ -1,4 +1,4 @@ -# Build from the project root: docker build -f s2s-client/Dockerfile -t s2s-client . +# Build from the project root: docker build -f vocero-api/Dockerfile -t vocero-api . # Serve backend APIs, the WebSocket proxy, and shared project documentation. # Run the React/Vite frontend separately; this image contains no browser UI. # HF Spaces (sdk: docker) routes traffic to $PORT, default 7860. @@ -8,18 +8,18 @@ COPY --from=ghcr.io/astral-sh/uv:0.12.5 /uv /uvx /bin/ ENV UV_LINK_MODE=copy \ UV_PYTHON_DOWNLOADS=never \ - PATH="/app/s2s-client/.venv/bin:$PATH" + PATH="/app/vocero-api/.venv/bin:$PATH" -WORKDIR /app/s2s-client +WORKDIR /app/vocero-api -COPY s2s-client/pyproject.toml s2s-client/uv.lock ./ +COPY vocero-api/pyproject.toml vocero-api/uv.lock ./ RUN uv sync --locked --no-dev --no-install-project -COPY s2s-client/README.md s2s-client/server.py ./ -COPY s2s-client/s2s_client/ ./s2s_client/ +COPY vocero-api/README.md vocero-api/server.py ./ +COPY vocero-api/vocero_api/ ./vocero_api/ COPY docs/ /app/docs/ RUN uv sync --locked --no-dev EXPOSE 7860 -CMD ["uvicorn", "s2s_client.main:app", "--host", "0.0.0.0", "--port", "7860"] +CMD ["uvicorn", "vocero_api.main:app", "--host", "0.0.0.0", "--port", "7860"] diff --git a/s2s-client/Makefile b/vocero-api/Makefile similarity index 86% rename from s2s-client/Makefile rename to vocero-api/Makefile index 87a0d0c..b342ef4 100644 --- a/s2s-client/Makefile +++ b/vocero-api/Makefile @@ -48,13 +48,13 @@ dev: ../start_ui.sh hooks-install: - $(UV_RUN) pre-commit install --install-hooks --config s2s-client/.pre-commit-config.yaml + $(UV_RUN) pre-commit install --install-hooks --config vocero-api/.pre-commit-config.yaml hooks-run: - $(UV_RUN) pre-commit run --all-files --config s2s-client/.pre-commit-config.yaml + $(UV_RUN) pre-commit run --all-files --config vocero-api/.pre-commit-config.yaml test-launcher: - npm --prefix ../s2s-frontend run test:launcher + npm --prefix ../vocero-frontend run test:launcher test-image: bash scripts/test_docker_image.sh diff --git a/s2s-client/README.md b/vocero-api/README.md similarity index 81% rename from s2s-client/README.md rename to vocero-api/README.md index 2d7cf86..ece8538 100644 --- a/s2s-client/README.md +++ b/vocero-api/README.md @@ -9,15 +9,15 @@ pinned: false short_description: Voice chat over WebSocket against a HF speech-to-speech --- -# Realtime voice backend +# Vocero API -FastAPI APIs and WebSocket relay for the [React voice frontend](../s2s-frontend). +FastAPI APIs and WebSocket relay for the [React voice frontend](../vocero-frontend). This service handles configuration, caller context, upstream authentication, search, reasoning, ApiPilot tools, and optional load-balancer allocation. -The root `start_ui.sh` now serves the [React frontend](../s2s-frontend) with +The root `start_ui.sh` now serves the [React frontend](../vocero-frontend) with Vite on public port `8420`, proxying backend requests here on loopback port -`8421`. The browser UI lives entirely in `s2s-frontend`; running FastAPI directly +`8421`. The browser UI lives entirely in `vocero-frontend`; running FastAPI directly or through Docker exposes only backend routes and shared documentation. The complete [usage guide](../docs/client/usage.md) covers local startup, @@ -28,7 +28,7 @@ and [change history](CHANGELOG.md) for implementation details and history. Build from the repository root so the container includes the documentation: ```bash -docker build -f s2s-client/Dockerfile -t s2s-client . +docker build -f vocero-api/Dockerfile -t vocero-api . ``` The Python project is contained in this directory: `pyproject.toml`, `uv.lock`, @@ -36,8 +36,8 @@ The Python project is contained in this directory: `pyproject.toml`, `uv.lock`, 3.12 (uv can provision Python). From the repository root: ```bash -make -C s2s-client install -make -C s2s-client check +make -C vocero-api install +make -C vocero-api check ``` Tests isolate environment configuration, use HTTP fixtures for allocation and @@ -46,8 +46,9 @@ frames, keepalive, and teardown. No deployed services or database are needed. See [backend development and compatibility](../docs/client/development.md) for formatting, typing, schema policy, optional Git hooks, and deployment checks. -`uvicorn server:app --app-dir s2s-client` remains a supported entry point; -the implementation now lives in `s2s_client/`. +`uvicorn server:app --app-dir vocero-api` remains a supported entry point; +the implementation lives in the `vocero_api/` package. Its Python distribution +name is `vocero-api`. FastAPI's API explorer remains at `/docs`. Project documents are served as files below `/docs/client/`, `/docs/speech/`, `/docs/adr/`, `/docs/examples/`, and diff --git a/s2s-client/pyproject.toml b/vocero-api/pyproject.toml similarity index 91% rename from s2s-client/pyproject.toml rename to vocero-api/pyproject.toml index 295d6e3..b8dcebc 100644 --- a/s2s-client/pyproject.toml +++ b/vocero-api/pyproject.toml @@ -3,7 +3,7 @@ requires = ["setuptools>=68", "wheel"] build-backend = "setuptools.build_meta" [project] -name = "aquaportal-s2s-client" +name = "vocero-api" version = "0.1.0" description = "FastAPI integration and realtime proxy for the Vocero frontend" readme = "README.md" @@ -29,12 +29,12 @@ dev = [ required-version = "==0.12.5" [tool.setuptools.packages.find] -include = ["s2s_client*"] +include = ["vocero_api*"] [tool.ruff] target-version = "py312" line-length = 120 -include = ["pyproject.toml", "s2s_client/**/*.py", "server.py", "tests/**/*.py", "scripts/**/*.py"] +include = ["pyproject.toml", "vocero_api/**/*.py", "server.py", "tests/**/*.py", "scripts/**/*.py"] [tool.ruff.lint] select = ["E4", "E7", "E9", "F", "I", "B", "UP"] @@ -48,7 +48,7 @@ line-ending = "auto" [tool.mypy] python_version = "3.12" plugins = ["pydantic.mypy"] -files = ["s2s_client", "server.py"] +files = ["vocero_api", "server.py"] check_untyped_defs = true disallow_untyped_defs = true no_implicit_optional = true diff --git a/s2s-client/scripts/test_docker_image.sh b/vocero-api/scripts/test_docker_image.sh similarity index 94% rename from s2s-client/scripts/test_docker_image.sh rename to vocero-api/scripts/test_docker_image.sh index ec0e598..b41e7f0 100644 --- a/s2s-client/scripts/test_docker_image.sh +++ b/vocero-api/scripts/test_docker_image.sh @@ -3,7 +3,7 @@ set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" -IMAGE="s2s-client-smoke:$(date +%s)-$$" +IMAGE="vocero-api-smoke:$(date +%s)-$$" container_id="" cleanup() { if [ -n "$container_id" ]; then @@ -13,7 +13,7 @@ cleanup() { } trap cleanup EXIT -docker build --file "$ROOT_DIR/s2s-client/Dockerfile" --tag "$IMAGE" "$ROOT_DIR" +docker build --file "$ROOT_DIR/vocero-api/Dockerfile" --tag "$IMAGE" "$ROOT_DIR" container_id="$(docker run --detach --publish 127.0.0.1::7860 "$IMAGE")" port="$(docker port "$container_id" 7860/tcp)" ready=false diff --git a/s2s-client/server.py b/vocero-api/server.py similarity index 62% rename from s2s-client/server.py rename to vocero-api/server.py index d35bdba..c98494c 100644 --- a/s2s-client/server.py +++ b/vocero-api/server.py @@ -3,7 +3,7 @@ import sys from pathlib import Path -# Also support --app-dir /path/to/s2s-client when launched outside the checkout. +# Also support --app-dir /path/to/vocero-api when launched outside the checkout. sys.path.insert(0, str(Path(__file__).resolve().parent)) -from s2s_client.main import app as app # noqa: E402 +from vocero_api.main import app as app # noqa: E402 diff --git a/s2s-client/tests/fixtures/legacy-contracts.json b/vocero-api/tests/fixtures/legacy-contracts.json similarity index 100% rename from s2s-client/tests/fixtures/legacy-contracts.json rename to vocero-api/tests/fixtures/legacy-contracts.json diff --git a/s2s-client/tests/test_compatibility.py b/vocero-api/tests/test_compatibility.py similarity index 98% rename from s2s-client/tests/test_compatibility.py rename to vocero-api/tests/test_compatibility.py index b329c7b..76bdc41 100644 --- a/s2s-client/tests/test_compatibility.py +++ b/vocero-api/tests/test_compatibility.py @@ -17,11 +17,11 @@ import httpx from fastapi.testclient import TestClient with patch.dict(os.environ, {}, clear=True), patch("dotenv.load_dotenv", return_value=False): - from s2s_client import main as server - from s2s_client.core import config as settings - from s2s_client.providers import apipilot - from s2s_client.realtime import proxy - from s2s_client.services import context + from vocero_api import main as server + from vocero_api.core import config as settings + from vocero_api.providers import apipilot + from vocero_api.realtime import proxy + from vocero_api.services import context ASYNC_CLIENT = httpx.AsyncClient FIXTURE = Path(__file__).parent / "fixtures" / "legacy-contracts.json" diff --git a/s2s-client/tests/test_models.py b/vocero-api/tests/test_models.py similarity index 89% rename from s2s-client/tests/test_models.py rename to vocero-api/tests/test_models.py index 35f2b8d..8cfa411 100644 --- a/s2s-client/tests/test_models.py +++ b/vocero-api/tests/test_models.py @@ -4,9 +4,9 @@ import pytest from fastapi.responses import JSONResponse from pydantic import ValidationError -from s2s_client.api.responses import validated_json_response -from s2s_client.models.requests import ApipilotExecuteRequest, QueueEndPayload, SearchRequest -from s2s_client.models.responses import ( +from vocero_api.api.responses import validated_json_response +from vocero_api.models.requests import ApipilotExecuteRequest, QueueEndPayload, SearchRequest +from vocero_api.models.responses import ( AllocationResponse, QueueCancellationResponse, ReasoningResponse, @@ -58,7 +58,7 @@ def test_request_models_keep_legacy_extra_field_and_argument_behavior(): def test_http_routes_publish_response_schemas(): # Import the app only after the compatibility suite's isolated configuration. - from s2s_client.main import app + from vocero_api.main import app spec = app.openapi() for path, operations in spec["paths"].items(): @@ -73,6 +73,13 @@ def test_http_routes_publish_response_schemas(): assert config["properties"]["reasoningType"]["enum"] == ["PROXY", "OPENAI"] +def test_legacy_entry_point_exports_the_renamed_application(): + import server + from vocero_api.main import app + + assert server.app is app + + @pytest.mark.parametrize( ("value", "expected"), [ @@ -85,6 +92,6 @@ def test_http_routes_publish_response_schemas(): ], ) def test_reasoning_configuration_preserves_normalization_and_native_fallback(value, expected): - from s2s_client.core.config import _normalize_reasoning_type + from vocero_api.core.config import _normalize_reasoning_type assert _normalize_reasoning_type(value) == expected diff --git a/s2s-client/tests/test_server.py b/vocero-api/tests/test_server.py similarity index 98% rename from s2s-client/tests/test_server.py rename to vocero-api/tests/test_server.py index 3c9fe3b..4fe0a24 100644 --- a/s2s-client/tests/test_server.py +++ b/vocero-api/tests/test_server.py @@ -1,6 +1,6 @@ """Backend contract checks with local HTTP fixtures and a loopback voice server. -Run from s2s-client: uv run --locked pytest tests +Run from vocero-api: uv run --locked pytest tests """ import json @@ -16,10 +16,10 @@ from websockets.sync.server import serve # Never consume deployment credentials or contact services from a developer's .env. with patch.dict(os.environ, {}, clear=True), patch("dotenv.load_dotenv", return_value=False): - from s2s_client import main as server - from s2s_client.core import config as settings - from s2s_client.providers import apipilot, caller - from s2s_client.services import tool_context + from vocero_api import main as server + from vocero_api.core import config as settings + from vocero_api.providers import apipilot, caller + from vocero_api.services import tool_context GRANT = { diff --git a/s2s-client/uv.lock b/vocero-api/uv.lock similarity index 99% rename from s2s-client/uv.lock rename to vocero-api/uv.lock index 2bd99b4..5b37630 100644 --- a/s2s-client/uv.lock +++ b/vocero-api/uv.lock @@ -24,45 +24,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/12/b8/4bd346e22b28902df4d651910f5242c28d84e4a5c2435ca5c3f797ed7e2e/anyio-4.15.1-py3-none-any.whl", hash = "sha256:6152fdbbf9a77fdec97731721bebf7c4c44f7c29b424b0065826173efc7ed101", size = 132079, upload-time = "2026-09-05T10:42:37.923Z" }, ] -[[package]] -name = "aquaportal-s2s-client" -version = "0.1.0" -source = { editable = "." } -dependencies = [ - { name = "fastapi" }, - { name = "httpx" }, - { name = "pydantic" }, - { name = "python-dotenv" }, - { name = "uvicorn", extra = ["standard"] }, - { name = "websockets" }, -] - -[package.dev-dependencies] -dev = [ - { name = "mypy" }, - { name = "pre-commit" }, - { name = "pytest" }, - { name = "ruff" }, -] - -[package.metadata] -requires-dist = [ - { name = "fastapi", specifier = "==0.115.6" }, - { name = "httpx", specifier = "==0.28.1" }, - { name = "pydantic", specifier = "==2.13.5" }, - { name = "python-dotenv", specifier = "==1.2.2" }, - { name = "uvicorn", extras = ["standard"], specifier = "==0.34.0" }, - { name = "websockets", specifier = "==17.2" }, -] - -[package.metadata.requires-dev] -dev = [ - { name = "mypy", specifier = ">=1.16,<2" }, - { name = "pre-commit", specifier = ">=4.4,<5" }, - { name = "pytest", specifier = ">=9,<10" }, - { name = "ruff", specifier = ">=0.12,<1" }, -] - [[package]] name = "certifi" version = "2026.7.22" @@ -546,6 +507,45 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/df/93/084f10ebbecdedf7902ed68fbecc903f0e9c5742155bfffb05eccbc071da/virtualenv-21.14.5-py3-none-any.whl", hash = "sha256:b0651e0174982bba17cc6f0aaa2ef496730cb85a3f35eb5d1119d1c904f7d1da", size = 5487482, upload-time = "2026-10-02T17:42:23.38Z" }, ] +[[package]] +name = "vocero-api" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "fastapi" }, + { name = "httpx" }, + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "uvicorn", extra = ["standard"] }, + { name = "websockets" }, +] + +[package.dev-dependencies] +dev = [ + { name = "mypy" }, + { name = "pre-commit" }, + { name = "pytest" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "fastapi", specifier = "==0.115.6" }, + { name = "httpx", specifier = "==0.28.1" }, + { name = "pydantic", specifier = "==2.13.5" }, + { name = "python-dotenv", specifier = "==1.2.2" }, + { name = "uvicorn", extras = ["standard"], specifier = "==0.34.0" }, + { name = "websockets", specifier = "==17.2" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "mypy", specifier = ">=1.16,<2" }, + { name = "pre-commit", specifier = ">=4.4,<5" }, + { name = "pytest", specifier = ">=9,<10" }, + { name = "ruff", specifier = ">=0.12,<1" }, +] + [[package]] name = "watchfiles" version = "1.3.0" diff --git a/s2s-client/s2s_client/__init__.py b/vocero-api/vocero_api/__init__.py similarity index 100% rename from s2s-client/s2s_client/__init__.py rename to vocero-api/vocero_api/__init__.py diff --git a/s2s-client/s2s_client/api/__init__.py b/vocero-api/vocero_api/api/__init__.py similarity index 100% rename from s2s-client/s2s_client/api/__init__.py rename to vocero-api/vocero_api/api/__init__.py diff --git a/s2s-client/s2s_client/api/responses.py b/vocero-api/vocero_api/api/responses.py similarity index 100% rename from s2s-client/s2s_client/api/responses.py rename to vocero-api/vocero_api/api/responses.py diff --git a/s2s-client/s2s_client/api/router.py b/vocero-api/vocero_api/api/router.py similarity index 68% rename from s2s-client/s2s_client/api/router.py rename to vocero-api/vocero_api/api/router.py index 20d4881..e1f1157 100644 --- a/s2s-client/s2s_client/api/router.py +++ b/vocero-api/vocero_api/api/router.py @@ -2,10 +2,10 @@ from fastapi import APIRouter -from s2s_client.api.routes import apipilot, caller, config, search, sessions -from s2s_client.models.responses import ReasoningResponse -from s2s_client.realtime.proxy import realtime_proxy -from s2s_client.services.reasoning import reasoning +from vocero_api.api.routes import apipilot, caller, config, search, sessions +from vocero_api.models.responses import ReasoningResponse +from vocero_api.realtime.proxy import realtime_proxy +from vocero_api.services.reasoning import reasoning router = APIRouter() router.include_router(config.router) diff --git a/s2s-client/s2s_client/api/routes/__init__.py b/vocero-api/vocero_api/api/routes/__init__.py similarity index 100% rename from s2s-client/s2s_client/api/routes/__init__.py rename to vocero-api/vocero_api/api/routes/__init__.py diff --git a/s2s-client/s2s_client/api/routes/apipilot.py b/vocero-api/vocero_api/api/routes/apipilot.py similarity index 94% rename from s2s-client/s2s_client/api/routes/apipilot.py rename to vocero-api/vocero_api/api/routes/apipilot.py index e704da1..a918006 100644 --- a/s2s-client/s2s_client/api/routes/apipilot.py +++ b/vocero-api/vocero_api/api/routes/apipilot.py @@ -3,12 +3,12 @@ from __future__ import annotations from fastapi import APIRouter, HTTPException, Request from fastapi.responses import JSONResponse -from s2s_client.api.responses import validated_json_response -from s2s_client.core import config as settings -from s2s_client.core import logging as app_logging -from s2s_client.models import requests as models_requests -from s2s_client.models.responses import ToolCatalogResponse, ToolExecutionResponse -from s2s_client.providers import apipilot as providers_apipilot +from vocero_api.api.responses import validated_json_response +from vocero_api.core import config as settings +from vocero_api.core import logging as app_logging +from vocero_api.models import requests as models_requests +from vocero_api.models.responses import ToolCatalogResponse, ToolExecutionResponse +from vocero_api.providers import apipilot as providers_apipilot router = APIRouter() diff --git a/s2s-client/s2s_client/api/routes/caller.py b/vocero-api/vocero_api/api/routes/caller.py similarity index 97% rename from s2s-client/s2s_client/api/routes/caller.py rename to vocero-api/vocero_api/api/routes/caller.py index 0f96047..a8cbc0f 100644 --- a/s2s-client/s2s_client/api/routes/caller.py +++ b/vocero-api/vocero_api/api/routes/caller.py @@ -6,12 +6,12 @@ import httpx from fastapi import APIRouter, HTTPException from fastapi.responses import JSONResponse -from s2s_client.api.responses import validated_json_response -from s2s_client.core import config as settings -from s2s_client.core import logging as app_logging -from s2s_client.models.responses import ReportsResponse, UserProfileResponse -from s2s_client.providers import caller as providers_caller -from s2s_client.services import context as services_context +from vocero_api.api.responses import validated_json_response +from vocero_api.core import config as settings +from vocero_api.core import logging as app_logging +from vocero_api.models.responses import ReportsResponse, UserProfileResponse +from vocero_api.providers import caller as providers_caller +from vocero_api.services import context as services_context router = APIRouter() diff --git a/s2s-client/s2s_client/api/routes/config.py b/vocero-api/vocero_api/api/routes/config.py similarity index 88% rename from s2s-client/s2s_client/api/routes/config.py rename to vocero-api/vocero_api/api/routes/config.py index 576fb56..57d973e 100644 --- a/s2s-client/s2s_client/api/routes/config.py +++ b/vocero-api/vocero_api/api/routes/config.py @@ -3,11 +3,11 @@ from __future__ import annotations from fastapi import APIRouter, Request from fastapi.responses import JSONResponse -from s2s_client.api.responses import validated_json_response -from s2s_client.core import config as settings -from s2s_client.core import logging as app_logging -from s2s_client.core import urls as core_urls -from s2s_client.models.responses import ConfigResponse +from vocero_api.api.responses import validated_json_response +from vocero_api.core import config as settings +from vocero_api.core import logging as app_logging +from vocero_api.core import urls as core_urls +from vocero_api.models.responses import ConfigResponse router = APIRouter() diff --git a/s2s-client/s2s_client/api/routes/search.py b/vocero-api/vocero_api/api/routes/search.py similarity index 92% rename from s2s-client/s2s_client/api/routes/search.py rename to vocero-api/vocero_api/api/routes/search.py index bb7ae6e..5097b42 100644 --- a/s2s-client/s2s_client/api/routes/search.py +++ b/vocero-api/vocero_api/api/routes/search.py @@ -6,11 +6,11 @@ import httpx from fastapi import APIRouter, HTTPException from fastapi.responses import JSONResponse -from s2s_client.api.responses import validated_json_response -from s2s_client.core import config as settings -from s2s_client.core import logging as app_logging -from s2s_client.models import requests as models_requests -from s2s_client.models.responses import SearchResponse +from vocero_api.api.responses import validated_json_response +from vocero_api.core import config as settings +from vocero_api.core import logging as app_logging +from vocero_api.models import requests as models_requests +from vocero_api.models.responses import SearchResponse router = APIRouter() diff --git a/s2s-client/s2s_client/api/routes/sessions.py b/vocero-api/vocero_api/api/routes/sessions.py similarity index 96% rename from s2s-client/s2s_client/api/routes/sessions.py rename to vocero-api/vocero_api/api/routes/sessions.py index a401359..e4f0a9c 100644 --- a/s2s-client/s2s_client/api/routes/sessions.py +++ b/vocero-api/vocero_api/api/routes/sessions.py @@ -6,11 +6,11 @@ import httpx from fastapi import APIRouter, HTTPException, Request from fastapi.responses import JSONResponse -from s2s_client.api.responses import validated_json_response -from s2s_client.core import config as settings -from s2s_client.core import logging as app_logging -from s2s_client.models.requests import QueueEndPayload -from s2s_client.models.responses import AllocationResponse, QueueCancellationResponse +from vocero_api.api.responses import validated_json_response +from vocero_api.core import config as settings +from vocero_api.core import logging as app_logging +from vocero_api.models.requests import QueueEndPayload +from vocero_api.models.responses import AllocationResponse, QueueCancellationResponse router = APIRouter() diff --git a/s2s-client/s2s_client/core/__init__.py b/vocero-api/vocero_api/core/__init__.py similarity index 100% rename from s2s-client/s2s_client/core/__init__.py rename to vocero-api/vocero_api/core/__init__.py diff --git a/s2s-client/s2s_client/core/config.py b/vocero-api/vocero_api/core/config.py similarity index 99% rename from s2s-client/s2s_client/core/config.py rename to vocero-api/vocero_api/core/config.py index 26e2e64..3f13be6 100644 --- a/s2s-client/s2s_client/core/config.py +++ b/vocero-api/vocero_api/core/config.py @@ -4,7 +4,7 @@ import os from dotenv import load_dotenv -from s2s_client.core import logging as app_logging +from vocero_api.core import logging as app_logging ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..")) load_dotenv(os.path.join(ROOT_DIR, ".env"), override=False) diff --git a/s2s-client/s2s_client/core/diagnostics.py b/vocero-api/vocero_api/core/diagnostics.py similarity index 96% rename from s2s-client/s2s_client/core/diagnostics.py rename to vocero-api/vocero_api/core/diagnostics.py index 846650f..042c89e 100644 --- a/s2s-client/s2s_client/core/diagnostics.py +++ b/vocero-api/vocero_api/core/diagnostics.py @@ -1,7 +1,7 @@ from __future__ import annotations -from s2s_client.core import config as settings -from s2s_client.core import logging as app_logging +from vocero_api.core import config as settings +from vocero_api.core import logging as app_logging # Resolve the execution URL for the startup banner using the same normalization diff --git a/s2s-client/s2s_client/core/logging.py b/vocero-api/vocero_api/core/logging.py similarity index 100% rename from s2s-client/s2s_client/core/logging.py rename to vocero-api/vocero_api/core/logging.py diff --git a/s2s-client/s2s_client/core/urls.py b/vocero-api/vocero_api/core/urls.py similarity index 97% rename from s2s-client/s2s_client/core/urls.py rename to vocero-api/vocero_api/core/urls.py index 9cff9ec..74bbee3 100644 --- a/s2s-client/s2s_client/core/urls.py +++ b/vocero-api/vocero_api/core/urls.py @@ -4,7 +4,7 @@ from urllib.parse import parse_qsl, urlencode, urlsplit, urlunsplit from fastapi import Request, WebSocket -from s2s_client.core import config as settings +from vocero_api.core import config as settings def _url_with_query(url: str, **params: str) -> str: diff --git a/s2s-client/s2s_client/main.py b/vocero-api/vocero_api/main.py similarity index 88% rename from s2s-client/s2s_client/main.py rename to vocero-api/vocero_api/main.py index 3ea5866..25bfc9b 100644 --- a/s2s-client/s2s_client/main.py +++ b/vocero-api/vocero_api/main.py @@ -6,8 +6,8 @@ from fastapi import FastAPI from fastapi.responses import RedirectResponse from fastapi.staticfiles import StaticFiles -from s2s_client.api.router import router -from s2s_client.core import config, diagnostics # noqa: F401 - retain the startup banner +from vocero_api.api.router import router +from vocero_api.core import config, diagnostics # noqa: F401 - retain the startup banner app = FastAPI(title="s2s-demo") app.include_router(router) diff --git a/s2s-client/s2s_client/models/__init__.py b/vocero-api/vocero_api/models/__init__.py similarity index 100% rename from s2s-client/s2s_client/models/__init__.py rename to vocero-api/vocero_api/models/__init__.py diff --git a/s2s-client/s2s_client/models/requests.py b/vocero-api/vocero_api/models/requests.py similarity index 100% rename from s2s-client/s2s_client/models/requests.py rename to vocero-api/vocero_api/models/requests.py diff --git a/s2s-client/s2s_client/models/responses.py b/vocero-api/vocero_api/models/responses.py similarity index 100% rename from s2s-client/s2s_client/models/responses.py rename to vocero-api/vocero_api/models/responses.py diff --git a/s2s-client/s2s_client/providers/__init__.py b/vocero-api/vocero_api/providers/__init__.py similarity index 100% rename from s2s-client/s2s_client/providers/__init__.py rename to vocero-api/vocero_api/providers/__init__.py diff --git a/s2s-client/s2s_client/providers/apipilot.py b/vocero-api/vocero_api/providers/apipilot.py similarity index 99% rename from s2s-client/s2s_client/providers/apipilot.py rename to vocero-api/vocero_api/providers/apipilot.py index 50e1494..16c801f 100644 --- a/s2s-client/s2s_client/providers/apipilot.py +++ b/vocero-api/vocero_api/providers/apipilot.py @@ -9,8 +9,8 @@ from typing import Any, cast import httpx from fastapi import HTTPException, Request -from s2s_client.core import config as settings -from s2s_client.core import logging as app_logging +from vocero_api.core import config as settings +from vocero_api.core import logging as app_logging # In-process cache and asynchronous lock: one caller refreshes while others # read the stale entry. `_APIPILOT_CACHE[auth_fingerprint] = (fetched_at_monotonic, tools_list)`. diff --git a/s2s-client/s2s_client/providers/caller.py b/vocero-api/vocero_api/providers/caller.py similarity index 99% rename from s2s-client/s2s_client/providers/caller.py rename to vocero-api/vocero_api/providers/caller.py index 453abd4..b3bc689 100644 --- a/s2s-client/s2s_client/providers/caller.py +++ b/vocero-api/vocero_api/providers/caller.py @@ -6,8 +6,8 @@ from urllib.parse import urlencode import httpx -from s2s_client.core import config as settings -from s2s_client.core import logging as app_logging +from vocero_api.core import config as settings +from vocero_api.core import logging as app_logging async def _fetch_reports_list(phone_number: str | None = None, auth_token: str | None = None) -> list | dict | None: diff --git a/s2s-client/s2s_client/realtime/__init__.py b/vocero-api/vocero_api/realtime/__init__.py similarity index 100% rename from s2s-client/s2s_client/realtime/__init__.py rename to vocero-api/vocero_api/realtime/__init__.py diff --git a/s2s-client/s2s_client/realtime/events.py b/vocero-api/vocero_api/realtime/events.py similarity index 99% rename from s2s-client/s2s_client/realtime/events.py rename to vocero-api/vocero_api/realtime/events.py index 2da301b..e4a57ff 100644 --- a/s2s-client/s2s_client/realtime/events.py +++ b/vocero-api/vocero_api/realtime/events.py @@ -4,9 +4,9 @@ import json import re from typing import Any, cast -from s2s_client.core import logging as app_logging -from s2s_client.services import context as services_context -from s2s_client.services import tool_context as services_tool_context +from vocero_api.core import logging as app_logging +from vocero_api.services import context as services_context +from vocero_api.services import tool_context as services_tool_context async def _report_server_debug(hypothesis_id: str, location: str, msg: str, data: dict | None = None) -> None: diff --git a/s2s-client/s2s_client/realtime/proxy.py b/vocero-api/vocero_api/realtime/proxy.py similarity index 99% rename from s2s-client/s2s_client/realtime/proxy.py rename to vocero-api/vocero_api/realtime/proxy.py index 05afea7..2fdeaed 100644 --- a/s2s-client/s2s_client/realtime/proxy.py +++ b/vocero-api/vocero_api/realtime/proxy.py @@ -10,14 +10,14 @@ from fastapi import WebSocket, WebSocketDisconnect from websockets.asyncio.client import ClientConnection from websockets.typing import Subprotocol -from s2s_client.core import config as settings -from s2s_client.core import logging as app_logging -from s2s_client.core import urls as core_urls -from s2s_client.providers import apipilot as providers_apipilot -from s2s_client.providers import caller as providers_caller -from s2s_client.realtime import events as realtime_events -from s2s_client.services import context as services_context -from s2s_client.services import tool_context as services_tool_context +from vocero_api.core import config as settings +from vocero_api.core import logging as app_logging +from vocero_api.core import urls as core_urls +from vocero_api.providers import apipilot as providers_apipilot +from vocero_api.providers import caller as providers_caller +from vocero_api.realtime import events as realtime_events +from vocero_api.services import context as services_context +from vocero_api.services import tool_context as services_tool_context async def realtime_proxy(ws: WebSocket) -> None: diff --git a/s2s-client/s2s_client/services/__init__.py b/vocero-api/vocero_api/services/__init__.py similarity index 100% rename from s2s-client/s2s_client/services/__init__.py rename to vocero-api/vocero_api/services/__init__.py diff --git a/s2s-client/s2s_client/services/context.py b/vocero-api/vocero_api/services/context.py similarity index 99% rename from s2s-client/s2s_client/services/context.py rename to vocero-api/vocero_api/services/context.py index 853392b..3bb603f 100644 --- a/s2s-client/s2s_client/services/context.py +++ b/vocero-api/vocero_api/services/context.py @@ -4,9 +4,9 @@ import datetime as _dt_module import json from typing import Any, cast -from s2s_client.core import config as settings -from s2s_client.core import logging as app_logging -from s2s_client.services import tool_context as services_tool_context +from vocero_api.core import config as settings +from vocero_api.core import logging as app_logging +from vocero_api.services import tool_context as services_tool_context VOCERO_USER_CONTEXT_HINT = " Caller profile resolved from the page phoneNumber: " REPORTS_LIST_HINT = ( diff --git a/s2s-client/s2s_client/services/reasoning.py b/vocero-api/vocero_api/services/reasoning.py similarity index 98% rename from s2s-client/s2s_client/services/reasoning.py rename to vocero-api/vocero_api/services/reasoning.py index 288d63b..43b67c2 100644 --- a/s2s-client/s2s_client/services/reasoning.py +++ b/vocero-api/vocero_api/services/reasoning.py @@ -6,15 +6,15 @@ import httpx from fastapi import HTTPException from fastapi.responses import JSONResponse -from s2s_client.api.responses import validated_json_response -from s2s_client.core import config as settings -from s2s_client.core import logging as app_logging -from s2s_client.models import requests as models_requests -from s2s_client.models.responses import ReasoningResponse -from s2s_client.providers import apipilot as providers_apipilot -from s2s_client.providers import caller as providers_caller -from s2s_client.services import context as services_context -from s2s_client.services import tool_context as services_tool_context +from vocero_api.api.responses import validated_json_response +from vocero_api.core import config as settings +from vocero_api.core import logging as app_logging +from vocero_api.models import requests as models_requests +from vocero_api.models.responses import ReasoningResponse +from vocero_api.providers import apipilot as providers_apipilot +from vocero_api.providers import caller as providers_caller +from vocero_api.services import context as services_context +from vocero_api.services import tool_context as services_tool_context def _openai_headers() -> dict[str, str]: diff --git a/s2s-client/s2s_client/services/tool_context.py b/vocero-api/vocero_api/services/tool_context.py similarity index 99% rename from s2s-client/s2s_client/services/tool_context.py rename to vocero-api/vocero_api/services/tool_context.py index 2b0fb79..3011332 100644 --- a/s2s-client/s2s_client/services/tool_context.py +++ b/vocero-api/vocero_api/services/tool_context.py @@ -4,7 +4,7 @@ import json import re from typing import cast -from s2s_client.core import logging as app_logging +from vocero_api.core import logging as app_logging # --------------------------------------------------------------------------- # APIPILOT — Reasoning engine integration (two passes) diff --git a/s2s-frontend/.editorconfig b/vocero-frontend/.editorconfig similarity index 100% rename from s2s-frontend/.editorconfig rename to vocero-frontend/.editorconfig diff --git a/s2s-frontend/.env.example b/vocero-frontend/.env.example similarity index 100% rename from s2s-frontend/.env.example rename to vocero-frontend/.env.example diff --git a/s2s-frontend/.gitignore b/vocero-frontend/.gitignore similarity index 100% rename from s2s-frontend/.gitignore rename to vocero-frontend/.gitignore diff --git a/s2s-frontend/.prettierignore b/vocero-frontend/.prettierignore similarity index 100% rename from s2s-frontend/.prettierignore rename to vocero-frontend/.prettierignore diff --git a/s2s-frontend/README.md b/vocero-frontend/README.md similarity index 94% rename from s2s-frontend/README.md rename to vocero-frontend/README.md index 03bafef..4ad7d9a 100644 --- a/s2s-frontend/README.md +++ b/vocero-frontend/README.md @@ -1,7 +1,7 @@ # Vocero React frontend React 19, TypeScript, Tailwind CSS 4, and Vite 8 client for the existing -`s2s-client` FastAPI backend. This is a client-side application with React-owned +`vocero-api` FastAPI backend. This is a client-side application with React-owned rendering and state, including the realtime WebSocket and audio integration. ## Run during development @@ -10,9 +10,9 @@ From the repository root, run `./start_ui.sh`. Open `http://localhost:8420`. Add `?debug=true` to access settings, tools, and custom RAG context. Existing `phoneNumber` and `episode_id` query parameters are supported. -The launcher requires Python 3 with venv support, Node.js 22.18 or later on the -22.x line or Node.js 24+, npm, Bash 4.4+, `setsid` (util-linux), and `curl`. -It creates `.venv` and installs missing Python dependencies. It runs +The launcher requires uv 0.12.5, Node.js 22.18 or later on the 22.x line or +Node.js 24+, npm, Bash 4.4+, `setsid` (util-linux), and `curl`. +It provisions Python 3.12 and syncs `vocero-api/.venv` from the API lockfile. It runs `npm ci --include=dev` on first start and when either frontend package manifest changes. Subsequent restarts reuse the installed dependencies. @@ -26,7 +26,7 @@ changes. Subsequent restarts reuse the installed dependencies. must differ. Occupied ports fail startup rather than silently selecting a new endpoint. The launcher waits for FastAPI before starting Vite, stops both process groups on Ctrl+C/SIGTERM, and exits with a failure if either server exits. -Uvicorn watches only `s2s-client`, avoiding frontend and dependency directories. +Uvicorn watches only `vocero-api`, avoiding frontend and dependency directories. The Vite child explicitly uses `NODE_ENV=development`, including when a process manager sets `NODE_ENV=production`, so React Fast Refresh remains enabled. @@ -49,8 +49,8 @@ variables, which are exposed to browser code. ## Staging with hot reload PM2 can continue running the root `start_ui.sh` as its existing `s2s_ui` process. -Ensure the Node/npm versions above are on PM2's PATH and deploy `s2s-frontend` -alongside `s2s-client` and `docs`. The reverse proxy still targets port `8420`. +Ensure uv and the Node/npm versions above are on PM2's PATH and deploy `vocero-frontend` +alongside `vocero-api` and `docs`. The reverse proxy still targets port `8420`. Set the exact public hostname in the root `.env`, without scheme or port: @@ -74,7 +74,7 @@ Start FastAPI on `127.0.0.1:8421` as described in the [client usage guide](../docs/client/usage.md), then run: ```sh -cd s2s-frontend +cd vocero-frontend npm ci npm run dev ``` diff --git a/s2s-frontend/config/backend-proxy.ts b/vocero-frontend/config/backend-proxy.ts similarity index 100% rename from s2s-frontend/config/backend-proxy.ts rename to vocero-frontend/config/backend-proxy.ts diff --git a/s2s-frontend/config/launcher.integration.ts b/vocero-frontend/config/launcher.integration.ts similarity index 89% rename from s2s-frontend/config/launcher.integration.ts rename to vocero-frontend/config/launcher.integration.ts index 314e1b1..972d9ef 100644 --- a/s2s-frontend/config/launcher.integration.ts +++ b/vocero-frontend/config/launcher.integration.ts @@ -64,21 +64,21 @@ async function waitForFile(filename: string) { async function launcher(context: TestContext, env: Record = {}) { const root = await mkdtemp(join(tmpdir(), "s2s-launcher-")); - const frontend = join(root, "s2s-frontend"); + const frontend = join(root, "vocero-frontend"); const modules = join(frontend, "node_modules"); - await mkdir(join(root, "s2s-client/.venv/bin"), { recursive: true }); + await mkdir(join(root, "vocero-api/.venv/bin"), { recursive: true }); await mkdir(join(root, "bin")); await mkdir(join(modules, "vite/bin"), { recursive: true }); await copyFile(new URL("../../start_ui.sh", import.meta.url), join(root, "start_ui.sh")); - await writeFile(join(root, "s2s-client/pyproject.toml"), ""); - await writeFile(join(root, "s2s-client/uv.lock"), ""); + await writeFile(join(root, "vocero-api/pyproject.toml"), ""); + await writeFile(join(root, "vocero-api/uv.lock"), ""); await writeFile( join(root, "bin/uv"), '#!/bin/bash\nprintf "%s\\n" "$@" > "$S2S_TEST_ROOT/uv-args"\nprintf "%s" "$UV_PROJECT_ENVIRONMENT" > "$S2S_TEST_ROOT/uv-environment"\nexit "${S2S_TEST_UV_EXIT:-0}"\n', { mode: 0o755 }, ); await writeFile( - join(root, "s2s-client/.venv/bin/python"), + join(root, "vocero-api/.venv/bin/python"), '#!/bin/bash\nif [ "$1" = "-c" ]; then exit 0; fi\nexec node "$S2S_TEST_ROOT/child.mjs" backend "$@"\n', { mode: 0o755 }, ); @@ -146,9 +146,30 @@ for (const signal of ["SIGINT", "SIGTERM"] as const) { assert.equal(run.output().includes("Installing"), false); assert.equal( await readFile(join(run.root, "uv-args"), "utf8"), - `sync\n--project\n${run.root}/s2s-client\n--locked\n`, + `sync\n--project\n${run.root}/vocero-api\n--locked\n`, ); - assert.equal(await readFile(join(run.root, "uv-environment"), "utf8"), `${run.root}/s2s-client/.venv`); + assert.equal(await readFile(join(run.root, "uv-environment"), "utf8"), `${run.root}/vocero-api/.venv`); + const backendMetadata: unknown = JSON.parse(await readFile(join(run.root, "backend.json"), "utf8")); + assert.deepEqual(backendMetadata, { + args: [ + "backend", + "-m", + "uvicorn", + "vocero_api.main:app", + "--app-dir", + `${run.root}/vocero-api`, + "--host", + "127.0.0.1", + "--port", + String(run.backendPort), + "--reload", + "--reload-dir", + `${run.root}/vocero-api`, + ], + target: `http://127.0.0.1:${run.backendPort}`, + hosts: "s2s.test", + nodeEnv: "production", + }); run.child.kill(signal); assert.equal(await run.exited, signal === "SIGINT" ? 130 : 143); for (const role of ["backend", "frontend"]) { diff --git a/s2s-frontend/eslint.config.ts b/vocero-frontend/eslint.config.ts similarity index 100% rename from s2s-frontend/eslint.config.ts rename to vocero-frontend/eslint.config.ts diff --git a/s2s-frontend/index.html b/vocero-frontend/index.html similarity index 100% rename from s2s-frontend/index.html rename to vocero-frontend/index.html diff --git a/s2s-frontend/package-lock.json b/vocero-frontend/package-lock.json similarity index 99% rename from s2s-frontend/package-lock.json rename to vocero-frontend/package-lock.json index e6de408..cb07230 100644 --- a/s2s-frontend/package-lock.json +++ b/vocero-frontend/package-lock.json @@ -1,11 +1,11 @@ { - "name": "s2s-frontend", + "name": "vocero-frontend", "version": "0.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "s2s-frontend", + "name": "vocero-frontend", "version": "0.0.0", "dependencies": { "dompurify": "^3.4.16", diff --git a/s2s-frontend/package.json b/vocero-frontend/package.json similarity index 98% rename from s2s-frontend/package.json rename to vocero-frontend/package.json index 795f737..eee75c0 100644 --- a/s2s-frontend/package.json +++ b/vocero-frontend/package.json @@ -1,5 +1,5 @@ { - "name": "s2s-frontend", + "name": "vocero-frontend", "private": true, "version": "0.0.0", "type": "module", diff --git a/s2s-frontend/prettier.config.ts b/vocero-frontend/prettier.config.ts similarity index 100% rename from s2s-frontend/prettier.config.ts rename to vocero-frontend/prettier.config.ts diff --git a/s2s-frontend/src/App.test.tsx b/vocero-frontend/src/App.test.tsx similarity index 100% rename from s2s-frontend/src/App.test.tsx rename to vocero-frontend/src/App.test.tsx diff --git a/s2s-frontend/src/App.tsx b/vocero-frontend/src/App.tsx similarity index 100% rename from s2s-frontend/src/App.tsx rename to vocero-frontend/src/App.tsx diff --git a/s2s-frontend/src/audio/audio-playback.worklet.ts b/vocero-frontend/src/audio/audio-playback.worklet.ts similarity index 100% rename from s2s-frontend/src/audio/audio-playback.worklet.ts rename to vocero-frontend/src/audio/audio-playback.worklet.ts diff --git a/s2s-frontend/src/audio/codec.ts b/vocero-frontend/src/audio/codec.ts similarity index 100% rename from s2s-frontend/src/audio/codec.ts rename to vocero-frontend/src/audio/codec.ts diff --git a/s2s-frontend/src/audio/engine.ts b/vocero-frontend/src/audio/engine.ts similarity index 100% rename from s2s-frontend/src/audio/engine.ts rename to vocero-frontend/src/audio/engine.ts diff --git a/s2s-frontend/src/audio/mic-capture.worklet.ts b/vocero-frontend/src/audio/mic-capture.worklet.ts similarity index 100% rename from s2s-frontend/src/audio/mic-capture.worklet.ts rename to vocero-frontend/src/audio/mic-capture.worklet.ts diff --git a/s2s-frontend/src/audio/worklet-globals.d.ts b/vocero-frontend/src/audio/worklet-globals.d.ts similarity index 100% rename from s2s-frontend/src/audio/worklet-globals.d.ts rename to vocero-frontend/src/audio/worklet-globals.d.ts diff --git a/s2s-frontend/src/components/Bubbles.tsx b/vocero-frontend/src/components/Bubbles.tsx similarity index 100% rename from s2s-frontend/src/components/Bubbles.tsx rename to vocero-frontend/src/components/Bubbles.tsx diff --git a/s2s-frontend/src/components/Conversation.tsx b/vocero-frontend/src/components/Conversation.tsx similarity index 100% rename from s2s-frontend/src/components/Conversation.tsx rename to vocero-frontend/src/components/Conversation.tsx diff --git a/s2s-frontend/src/components/Dialog.tsx b/vocero-frontend/src/components/Dialog.tsx similarity index 100% rename from s2s-frontend/src/components/Dialog.tsx rename to vocero-frontend/src/components/Dialog.tsx diff --git a/s2s-frontend/src/components/SettingsDialogs.tsx b/vocero-frontend/src/components/SettingsDialogs.tsx similarity index 100% rename from s2s-frontend/src/components/SettingsDialogs.tsx rename to vocero-frontend/src/components/SettingsDialogs.tsx diff --git a/s2s-frontend/src/components/VoiceStage.tsx b/vocero-frontend/src/components/VoiceStage.tsx similarity index 100% rename from s2s-frontend/src/components/VoiceStage.tsx rename to vocero-frontend/src/components/VoiceStage.tsx diff --git a/s2s-frontend/src/hooks/use-wake-word.ts b/vocero-frontend/src/hooks/use-wake-word.ts similarity index 100% rename from s2s-frontend/src/hooks/use-wake-word.ts rename to vocero-frontend/src/hooks/use-wake-word.ts diff --git a/s2s-frontend/src/lib/api.ts b/vocero-frontend/src/lib/api.ts similarity index 100% rename from s2s-frontend/src/lib/api.ts rename to vocero-frontend/src/lib/api.ts diff --git a/s2s-frontend/src/lib/context.ts b/vocero-frontend/src/lib/context.ts similarity index 100% rename from s2s-frontend/src/lib/context.ts rename to vocero-frontend/src/lib/context.ts diff --git a/s2s-frontend/src/lib/contracts.ts b/vocero-frontend/src/lib/contracts.ts similarity index 100% rename from s2s-frontend/src/lib/contracts.ts rename to vocero-frontend/src/lib/contracts.ts diff --git a/s2s-frontend/src/lib/messages.test.ts b/vocero-frontend/src/lib/messages.test.ts similarity index 100% rename from s2s-frontend/src/lib/messages.test.ts rename to vocero-frontend/src/lib/messages.test.ts diff --git a/s2s-frontend/src/lib/messages.ts b/vocero-frontend/src/lib/messages.ts similarity index 100% rename from s2s-frontend/src/lib/messages.ts rename to vocero-frontend/src/lib/messages.ts diff --git a/s2s-frontend/src/lib/profile.ts b/vocero-frontend/src/lib/profile.ts similarity index 100% rename from s2s-frontend/src/lib/profile.ts rename to vocero-frontend/src/lib/profile.ts diff --git a/s2s-frontend/src/lib/reports.ts b/vocero-frontend/src/lib/reports.ts similarity index 100% rename from s2s-frontend/src/lib/reports.ts rename to vocero-frontend/src/lib/reports.ts diff --git a/s2s-frontend/src/lib/settings.ts b/vocero-frontend/src/lib/settings.ts similarity index 100% rename from s2s-frontend/src/lib/settings.ts rename to vocero-frontend/src/lib/settings.ts diff --git a/s2s-frontend/src/main.tsx b/vocero-frontend/src/main.tsx similarity index 100% rename from s2s-frontend/src/main.tsx rename to vocero-frontend/src/main.tsx diff --git a/s2s-frontend/src/realtime/client.test.ts b/vocero-frontend/src/realtime/client.test.ts similarity index 100% rename from s2s-frontend/src/realtime/client.test.ts rename to vocero-frontend/src/realtime/client.test.ts diff --git a/s2s-frontend/src/realtime/client.ts b/vocero-frontend/src/realtime/client.ts similarity index 100% rename from s2s-frontend/src/realtime/client.ts rename to vocero-frontend/src/realtime/client.ts diff --git a/s2s-frontend/src/realtime/controller.test.ts b/vocero-frontend/src/realtime/controller.test.ts similarity index 100% rename from s2s-frontend/src/realtime/controller.test.ts rename to vocero-frontend/src/realtime/controller.test.ts diff --git a/s2s-frontend/src/realtime/controller.ts b/vocero-frontend/src/realtime/controller.ts similarity index 100% rename from s2s-frontend/src/realtime/controller.ts rename to vocero-frontend/src/realtime/controller.ts diff --git a/s2s-frontend/src/styles.css b/vocero-frontend/src/styles.css similarity index 100% rename from s2s-frontend/src/styles.css rename to vocero-frontend/src/styles.css diff --git a/s2s-frontend/src/styles/legacy-theme.css b/vocero-frontend/src/styles/legacy-theme.css similarity index 100% rename from s2s-frontend/src/styles/legacy-theme.css rename to vocero-frontend/src/styles/legacy-theme.css diff --git a/s2s-frontend/src/test/backend-proxy.test.ts b/vocero-frontend/src/test/backend-proxy.test.ts similarity index 100% rename from s2s-frontend/src/test/backend-proxy.test.ts rename to vocero-frontend/src/test/backend-proxy.test.ts diff --git a/s2s-frontend/src/test/setup.ts b/vocero-frontend/src/test/setup.ts similarity index 100% rename from s2s-frontend/src/test/setup.ts rename to vocero-frontend/src/test/setup.ts diff --git a/s2s-frontend/tsconfig.app.json b/vocero-frontend/tsconfig.app.json similarity index 100% rename from s2s-frontend/tsconfig.app.json rename to vocero-frontend/tsconfig.app.json diff --git a/s2s-frontend/tsconfig.json b/vocero-frontend/tsconfig.json similarity index 100% rename from s2s-frontend/tsconfig.json rename to vocero-frontend/tsconfig.json diff --git a/s2s-frontend/tsconfig.node.json b/vocero-frontend/tsconfig.node.json similarity index 100% rename from s2s-frontend/tsconfig.node.json rename to vocero-frontend/tsconfig.node.json diff --git a/s2s-frontend/vite.config.ts b/vocero-frontend/vite.config.ts similarity index 100% rename from s2s-frontend/vite.config.ts rename to vocero-frontend/vite.config.ts diff --git a/s2s-frontend/vitest.config.ts b/vocero-frontend/vitest.config.ts similarity index 100% rename from s2s-frontend/vitest.config.ts rename to vocero-frontend/vitest.config.ts