refactor: rename API and frontend projects to Vocero
Rename the project directories, Python distribution and import package, and npm package. Update launcher, Docker, tooling, tests, lockfiles, and documentation while preserving existing API contracts. Make AGENTS.md self-contained and remove external skill and workflow references.
This commit is contained in:
+6
-6
@@ -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/
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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 <http://localhost:8420/> 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)
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
|
||||
+21
-21
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
+19
-12
@@ -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
|
||||
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
+11
-11
@@ -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 <http://localhost:8420/>, 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
|
||||
|
||||
+12
-12
@@ -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")
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -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.
|
||||
@@ -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"]
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Vendored
@@ -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"
|
||||
@@ -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
|
||||
@@ -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 = {
|
||||
Generated
+39
-39
@@ -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"
|
||||
@@ -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)
|
||||
+6
-6
@@ -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()
|
||||
|
||||
+6
-6
@@ -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()
|
||||
|
||||
+5
-5
@@ -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()
|
||||
|
||||
+5
-5
@@ -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()
|
||||
|
||||
+5
-5
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
+2
-2
@@ -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
|
||||
@@ -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:
|
||||
@@ -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)
|
||||
+2
-2
@@ -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)`.
|
||||
+2
-2
@@ -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:
|
||||
@@ -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:
|
||||
@@ -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:
|
||||
+3
-3
@@ -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 = (
|
||||
+9
-9
@@ -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]:
|
||||
+1
-1
@@ -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)
|
||||
@@ -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
|
||||
```
|
||||
+28
-7
@@ -64,21 +64,21 @@ async function waitForFile(filename: string) {
|
||||
|
||||
async function launcher(context: TestContext, env: Record<string, string> = {}) {
|
||||
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"]) {
|
||||
@@ -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",
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "s2s-frontend",
|
||||
"name": "vocero-frontend",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
Vendored
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user