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/
|
.npm/
|
||||||
package-lock.json
|
package-lock.json
|
||||||
!speech_to_speech/package-lock.json
|
!speech_to_speech/package-lock.json
|
||||||
!s2s-frontend/package-lock.json
|
!vocero-frontend/package-lock.json
|
||||||
s2s-frontend/.eslintcache
|
vocero-frontend/.eslintcache
|
||||||
yarn-error.log
|
yarn-error.log
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
@@ -125,10 +125,10 @@ CVS/
|
|||||||
# ─────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────
|
||||||
# Client backend and speech-service runtime artifacts
|
# Client backend and speech-service runtime artifacts
|
||||||
# ─────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────
|
||||||
s2s-client/.cache/
|
vocero-api/.cache/
|
||||||
s2s-client/uploads/
|
vocero-api/uploads/
|
||||||
s2s-client/sessions/
|
vocero-api/sessions/
|
||||||
s2s-client/*.log
|
vocero-api/*.log
|
||||||
speech_to_speech/*.log
|
speech_to_speech/*.log
|
||||||
speech_to_speech/sessions/
|
speech_to_speech/sessions/
|
||||||
speech_to_speech/.cache/
|
speech_to_speech/.cache/
|
||||||
|
|||||||
@@ -3,12 +3,13 @@
|
|||||||
## Scope and working conventions
|
## Scope and working conventions
|
||||||
|
|
||||||
This repository is Vocero: a browser voice client, its FastAPI integration
|
This repository is Vocero: a browser voice client, its FastAPI integration
|
||||||
server, and a speech pipeline. Use the code, commands,
|
server, and a speech pipeline. Keep this guide self-contained and base its
|
||||||
configuration, and documentation within this checkout. Do not assume other
|
instructions on this checkout's code, configuration, documentation, and tools.
|
||||||
repositories or developer-specific directories are available.
|
|
||||||
|
|
||||||
All file and directory references in this guide are relative to the repository
|
All file and directory references in this guide must point within the repository
|
||||||
root. HTTP routes mentioned below are URLs, not filesystem paths.
|
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.
|
- Check the current branch, worktree, and working-tree changes before editing.
|
||||||
Honor the checkout requested by the user, including direct work on `main`.
|
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.
|
assuming a tool is missing. Do not install system-wide packages unnecessarily.
|
||||||
- When commits are requested, make coherent, scoped Conventional Commits as work
|
- When commits are requested, make coherent, scoped Conventional Commits as work
|
||||||
progresses. Avoid excessive micro-commits and unrelated cleanup.
|
progresses. Avoid excessive micro-commits and unrelated cleanup.
|
||||||
- Invoke `work-on-feature` only when explicitly requested. For a requested PR,
|
- For a requested pull request, describe the problem, resulting behavior,
|
||||||
follow the available `pr-description` skill exactly and put verification
|
validation, and relevant limitations. Create issues or post comments only
|
||||||
details in the handoff when its format excludes them. Issues are optional;
|
when requested.
|
||||||
create native Gitea issues or post comments only when requested.
|
|
||||||
- When asked to merge locally, use the requested rebase/fast-forward flow;
|
- When asked to merge locally, use the requested rebase/fast-forward flow;
|
||||||
preserve a recovery ref before rewriting and verify retained patches. A local
|
preserve a recovery ref before rewriting and verify retained patches. A local
|
||||||
merge does not imply a push, deployment, or restart. Report those separately.
|
merge does not imply a push, deployment, or restart. Report those separately.
|
||||||
- Treat old conversations and proposals as context. Verify claims against the
|
- Treat historical proposals in `docs/proposals/` as context. Verify their
|
||||||
current code and distinguish proposed authentication/deployment changes from
|
claims against current code and distinguish proposed authentication or
|
||||||
implemented behavior.
|
deployment changes from implemented behavior.
|
||||||
|
|
||||||
## Project map
|
## Project map
|
||||||
|
|
||||||
| Location | Responsibility |
|
| Location | Responsibility |
|
||||||
| ---------------------------------- | ------------------------------------------------------------------------------------------- |
|
| ---------------------------------- | ------------------------------------------------------------------------------------------- |
|
||||||
| `s2s-client/` | The complete uv project: manifests, environment, Python tooling, tests, and FastAPI package |
|
| `vocero-api/` | 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 |
|
| `vocero-api/vocero_api/main.py` | Application assembly, documentation mount, and retained redirects |
|
||||||
| `s2s-client/s2s_client/api/` | HTTP routes, route registration, and response validation helper |
|
| `vocero-api/vocero_api/api/` | HTTP routes, route registration, and response validation helper |
|
||||||
| `s2s-client/s2s_client/models/` | Pydantic request and response contracts |
|
| `vocero-api/vocero_api/models/` | Pydantic request and response contracts |
|
||||||
| `s2s-client/s2s_client/core/` | Environment parsing, logging, public URLs, and startup diagnostics |
|
| `vocero-api/vocero_api/core/` | Environment parsing, logging, public URLs, and startup diagnostics |
|
||||||
| `s2s-client/s2s_client/providers/` | ApiPilot and caller/profile/report HTTP integrations |
|
| `vocero-api/vocero_api/providers/` | ApiPilot and caller/profile/report HTTP integrations |
|
||||||
| `s2s-client/s2s_client/services/` | Prompt/context construction, reasoning, and tool-context rendering |
|
| `vocero-api/vocero_api/services/` | Prompt/context construction, reasoning, and tool-context rendering |
|
||||||
| `s2s-client/s2s_client/realtime/` | WebSocket relay, event handling, marker filtering, and audio forwarding |
|
| `vocero-api/vocero_api/realtime/` | WebSocket relay, event handling, marker filtering, and audio forwarding |
|
||||||
| `s2s-frontend/` | React, TypeScript, Tailwind, and Vite browser application |
|
| `vocero-frontend/` | React, TypeScript, Tailwind, and Vite browser application |
|
||||||
| `speech_to_speech/` | Separately run VAD → STT → LLM → TTS pipeline and realtime API |
|
| `speech_to_speech/` | Separately run VAD → STT → LLM → TTS pipeline and realtime API |
|
||||||
| `start_ui.sh` | Dependency sync and supervision of FastAPI plus Vite |
|
| `start_ui.sh` | Dependency sync and supervision of FastAPI plus Vite |
|
||||||
| `docs/` | Maintained technical documentation, examples, and historical proposals |
|
| `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
|
implementation belongs in the package. Keep HTTP handlers focused on transport
|
||||||
and reuse providers/services for shared behavior.
|
and reuse providers/services for shared behavior.
|
||||||
|
|
||||||
@@ -62,37 +62,37 @@ Load-balancer allocation/queues and upstream caller authentication remain.
|
|||||||
|
|
||||||
## Tooling and checks
|
## Tooling and checks
|
||||||
|
|
||||||
The uv project must stay inside `s2s-client/`, including
|
The uv project must stay inside `vocero-api/`, including
|
||||||
`s2s-client/pyproject.toml`, `s2s-client/uv.lock`, `s2s-client/.python-version`,
|
`vocero-api/pyproject.toml`, `vocero-api/uv.lock`, `vocero-api/.python-version`,
|
||||||
`s2s-client/.venv/`, `s2s-client/Makefile`, and
|
`vocero-api/.venv/`, `vocero-api/Makefile`, and
|
||||||
`s2s-client/.pre-commit-config.yaml`.
|
`vocero-api/.pre-commit-config.yaml`.
|
||||||
Use Python 3.12 and the uv version required by `tool.uv.required-version`
|
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;
|
(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.
|
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.
|
changing the required version.
|
||||||
|
|
||||||
The frontend uses npm and `s2s-frontend/package-lock.json`. Its Node requirement is
|
The frontend uses npm and `vocero-frontend/package-lock.json`. Its Node requirement is
|
||||||
`^22.18.0 || >=24.0.0`; consult `s2s-frontend/package.json` for current scripts.
|
`^22.18.0 || >=24.0.0`; consult `vocero-frontend/package.json` for current scripts.
|
||||||
Keep application code and supported configuration files in TypeScript.
|
Keep application code and supported configuration files in TypeScript.
|
||||||
|
|
||||||
Commands below run from the repository root:
|
Commands below run from the repository root:
|
||||||
|
|
||||||
| Command | Purpose |
|
| Command | Purpose |
|
||||||
| ------------------------------------- | --------------------------------------------------------------------- |
|
| ------------------------------------- | --------------------------------------------------------------------- |
|
||||||
| `make -C s2s-client install` | Sync the locked Python environment and development tools |
|
| `make -C vocero-api install` | Sync the locked Python environment and development tools |
|
||||||
| `make -C s2s-client format` | Apply Ruff fixes and formatting |
|
| `make -C vocero-api format` | Apply Ruff fixes and formatting |
|
||||||
| `make -C s2s-client check` | Check lock freshness, formatting, Ruff lint, mypy, and pytest |
|
| `make -C vocero-api 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 vocero-api 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 |
|
| `make -C vocero-api test-image` | Build and smoke-test the actual backend Docker image |
|
||||||
| `npm --prefix s2s-frontend ci` | Install locked frontend dependencies |
|
| `npm --prefix vocero-frontend ci` | Install locked frontend dependencies |
|
||||||
| `npm --prefix s2s-frontend run check` | Check formatting, lint, TypeScript, Vitest, and launcher integration |
|
| `npm --prefix vocero-frontend run check` | Check formatting, lint, TypeScript, Vitest, and launcher integration |
|
||||||
| `npm --prefix s2s-frontend run build` | Type-check and build frontend assets |
|
| `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,
|
Python uses Ruff with a 120-character line limit, double quotes, import ordering,
|
||||||
and typed application functions checked by mypy. Frontend formatting and lint
|
and typed application functions checked by mypy. Frontend formatting and lint
|
||||||
rules live in `s2s-frontend/prettier.config.ts` and
|
rules live in `vocero-frontend/prettier.config.ts` and
|
||||||
`s2s-frontend/eslint.config.ts`; use those tools rather than introducing
|
`vocero-frontend/eslint.config.ts`; use those tools rather than introducing
|
||||||
competing conventions.
|
competing conventions.
|
||||||
|
|
||||||
Run checks appropriate to the change: backend checks for Python/API work,
|
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
|
- Response models validate fields constructed by this service. Upstream-owned
|
||||||
values remain permissive where the old parser was permissive: `Any`, optional
|
values remain permissive where the old parser was permissive: `Any`, optional
|
||||||
fields, open objects, and arbitrary profile JSON are deliberate boundaries.
|
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
|
with Pydantic, then serializes the original payload. Do not substitute
|
||||||
`model_dump()` or automatic response-model serialization: those can insert
|
`model_dump()` or automatic response-model serialization: those can insert
|
||||||
defaults, filter unknown keys,
|
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
|
- Preserve environment/dotenv parsing, default flags, and import-time behavior
|
||||||
during refactors. Do not introduce stricter configuration validation or change
|
during refactors. Do not introduce stricter configuration validation or change
|
||||||
authentication behavior as part of a tooling change.
|
authentication behavior as part of a tooling change.
|
||||||
- `s2s-client/tests/fixtures/legacy-contracts.json` captures the server before the
|
- `vocero-api/tests/fixtures/legacy-contracts.json` captures the server before the
|
||||||
migration at `3d42df5`. `s2s-client/tests/test_compatibility.py` compares HTTP
|
migration at `3d42df5`. `vocero-api/tests/test_compatibility.py` compares HTTP
|
||||||
response bytes, statuses, selected headers, outgoing requests, request schemas,
|
response bytes, statuses, selected headers, outgoing requests, request schemas,
|
||||||
and realtime frames. Do not regenerate captures merely to make a refactor pass.
|
and realtime frames. Do not regenerate captures merely to make a refactor pass.
|
||||||
- Known reasoning failures, ApiPilot authentication/cache quirks, and relay
|
- 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
|
## Browser and realtime behavior
|
||||||
|
|
||||||
- React renders the UI; `s2s-frontend/src/realtime/controller.ts` owns observable
|
- React renders the UI; `vocero-frontend/src/realtime/controller.ts` owns observable
|
||||||
application state, `s2s-frontend/src/realtime/client.ts` owns protocol/session
|
application state, `vocero-frontend/src/realtime/client.ts` owns protocol/session
|
||||||
lifecycle, and `s2s-frontend/src/audio/` owns the audio graph and worklets.
|
lifecycle, and `vocero-frontend/src/audio/` owns the audio graph and worklets.
|
||||||
Keep high-frequency audio/WebSocket work outside React rendering and preserve
|
Keep high-frequency audio/WebSocket work outside React rendering and preserve
|
||||||
cleanup across unmounts and reconnects.
|
cleanup across unmounts and reconnects.
|
||||||
- Preserve the existing 16 kHz PCM16 audio path, worklet registration, interruption
|
- 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
|
server/provider calls; do not assume a vector database or a completed magic-link
|
||||||
authentication flow exists.
|
authentication flow exists.
|
||||||
- Assistant Markdown is parsed with Marked and sanitized with DOMPurify in
|
- 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
|
raw message text. Keep sanitization and unsafe-link regression coverage when
|
||||||
changing it.
|
changing it.
|
||||||
- Reuse the existing design tokens, Tailwind integration, and
|
- 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
|
narrow screens, keyboard focus, dialog dismissal, and voice-state feedback for
|
||||||
UI changes.
|
UI changes.
|
||||||
Preserve intentional `?debug=true` controls and browser audio-permission flows.
|
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
|
## Startup and deployment boundaries
|
||||||
|
|
||||||
`./start_ui.sh` sources `.env`, syncs `s2s-client/.venv/` with
|
`./start_ui.sh` loads the optional root `.env`, syncs `vocero-api/.venv/` with
|
||||||
`uv sync --project s2s-client --locked`, and installs frontend packages when the
|
`uv sync --project vocero-api --locked`, and installs frontend packages when the
|
||||||
manifests change.
|
manifests change.
|
||||||
It starts Vite on `0.0.0.0:8420` and FastAPI on `127.0.0.1:8421`, with
|
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
|
`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`.
|
and `OPENAI`; unknown `S2S_REASONING_TYPE` values fall back to `PROXY`.
|
||||||
|
|
||||||
The backend Docker image serves APIs, WebSockets, and root `docs/`, not the React
|
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,
|
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.
|
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.
|
without language suffixes. Historical proposals do not define current behavior.
|
||||||
- Reserve root Markdown for established files such as `README.md` and `AGENTS.md`.
|
- 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.
|
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
|
- 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 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
|
direct Python startup uses `load_dotenv(..., override=False)`. Preserve both
|
||||||
behaviors and avoid importing live configuration when a test can isolate it.
|
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 |
|
| Component | Purpose |
|
||||||
|---|---|
|
|---|---|
|
||||||
| [s2s-frontend](s2s-frontend) | React, TypeScript, and Tailwind browser UI, served by Vite with hot reload |
|
| [vocero-frontend](vocero-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-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 |
|
| [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
|
Start with [client setup and usage](docs/client/usage.md), then read the
|
||||||
[architecture](docs/client/architecture.md) and [documentation index](docs/README.md).
|
[architecture](docs/client/architecture.md) and [documentation index](docs/README.md).
|
||||||
The speech backend needs its own installed model and runtime dependencies;
|
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
|
## 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
|
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 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`.
|
and forwards HTTP/WebSocket backend routes to FastAPI on `127.0.0.1:8421`.
|
||||||
Both servers reload during development; stopping the launcher stops both.
|
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
|
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,
|
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
|
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
|
The Docker image serves backend routes and shared documentation. The React
|
||||||
frontend runs separately through Vite. To build the backend image, use the
|
frontend runs separately through Vite. To build the backend image, use the
|
||||||
repository root as the Docker build context so documentation is included:
|
repository root as the Docker build context so documentation is included:
|
||||||
|
|
||||||
```bash
|
```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 \
|
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
|
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
|
## Project references
|
||||||
|
|
||||||
- [Change history](s2s-client/CHANGELOG.md)
|
- [Change history](vocero-api/CHANGELOG.md)
|
||||||
- [Backend development and compatibility](docs/client/development.md)
|
- [Backend development and compatibility](docs/client/development.md)
|
||||||
- [Realtime protocol and pipeline](docs/speech/realtime-api.md)
|
- [Realtime protocol and pipeline](docs/speech/realtime-api.md)
|
||||||
- [Context injection](docs/client/context-injection.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)
|
- [Design language](client/design.md)
|
||||||
- [Glossary](client/glossary.md)
|
- [Glossary](client/glossary.md)
|
||||||
- [Troubleshooting](client/troubleshooting.md)
|
- [Troubleshooting](client/troubleshooting.md)
|
||||||
- [Change history](../s2s-client/CHANGELOG.md)
|
- [Change history](../vocero-api/CHANGELOG.md)
|
||||||
|
|
||||||
## Speech backend
|
## Speech backend
|
||||||
|
|
||||||
|
|||||||
+21
-21
@@ -1,7 +1,7 @@
|
|||||||
# Client architecture
|
# Client architecture
|
||||||
|
|
||||||
The [React browser client](../../s2s-frontend) uses the
|
The [React browser client](../../vocero-frontend) uses the
|
||||||
[FastAPI backend](../../s2s-client) for HTTP APIs and a speech WebSocket relay.
|
[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
|
`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;
|
loopback port `8421`. Vite forwards backend routes and provides hot reload;
|
||||||
Uvicorn reloads Python edits. The launcher supervises both processes.
|
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 |
|
| Source | Responsibility |
|
||||||
|---|---|
|
|---|---|
|
||||||
| [server.py](../../s2s-client/server.py) | Compatibility entry point for existing `server:app` commands |
|
| [server.py](../../vocero-api/server.py) | Compatibility entry point for existing `server:app` commands |
|
||||||
| [main.py](../../s2s-client/s2s_client/main.py) | FastAPI assembly, documentation serving and redirects |
|
| [main.py](../../vocero-api/vocero_api/main.py) | FastAPI assembly, documentation serving and redirects |
|
||||||
| [api](../../s2s-client/s2s_client/api) | HTTP route registration, handlers and compatible response validation |
|
| [api](../../vocero-api/vocero_api/api) | HTTP route registration, handlers and compatible response validation |
|
||||||
| [models](../../s2s-client/s2s_client/models) | Pydantic request and response contracts |
|
| [models](../../vocero-api/vocero_api/models) | Pydantic request and response contracts |
|
||||||
| [core](../../s2s-client/s2s_client/core) | Existing environment parsing, logging, URL helpers and diagnostics |
|
| [core](../../vocero-api/vocero_api/core) | Existing environment parsing, logging, URL helpers and diagnostics |
|
||||||
| [providers](../../s2s-client/s2s_client/providers) | Caller context and ApiPilot HTTP integrations |
|
| [providers](../../vocero-api/vocero_api/providers) | Caller context and ApiPilot HTTP integrations |
|
||||||
| [services](../../s2s-client/s2s_client/services) | Context construction, reasoning and tool-context rendering |
|
| [services](../../vocero-api/vocero_api/services) | Context construction, reasoning and tool-context rendering |
|
||||||
| [realtime](../../s2s-client/s2s_client/realtime) | WebSocket relay, events, markers and audio handling |
|
| [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 |
|
| [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 |
|
| [vite.config.ts](../../vocero-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 |
|
| [App.tsx](../../vocero-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 |
|
| [controller.ts](../../vocero-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 |
|
| [client.ts](../../vocero-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 |
|
| [components](../../vocero-frontend/src/components) | Chat, caller profile, settings and voice components |
|
||||||
| [audio](../../s2s-frontend/src/audio) | TypeScript capture/playback worklets, PCM codec, and visualizer |
|
| [audio](../../vocero-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 |
|
| [legacy-theme.css](../../vocero-frontend/src/styles/legacy-theme.css) | Retained themes and responsive visual styles alongside Tailwind |
|
||||||
|
|
||||||
## Server routes
|
## Server routes
|
||||||
|
|
||||||
@@ -79,9 +79,9 @@ explorer, while project documentation files are served in the `/docs/`
|
|||||||
subdirectories. Markdown remains
|
subdirectories. Markdown remains
|
||||||
source text, and directories without `index.html` are not browsable pages.
|
source text, and directories without `index.html` are not browsable pages.
|
||||||
FastAPI serves backend and documentation routes only. The frontend source and
|
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
|
describe service-owned structure and retain loose upstream values. See
|
||||||
[development and compatibility](development.md) before tightening a contract.
|
[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
|
1. The browser captures microphone audio with echo cancellation, noise
|
||||||
suppression and automatic gain-control settings.
|
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.
|
mono PCM16. The browser sends base64 `input_audio_buffer.append` frames.
|
||||||
3. The backend processes VAD → STT → LLM → TTS and emits realtime events.
|
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
|
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.
|
resamples and fades playback edges. Input speech can clear playback for barge-in.
|
||||||
|
|
||||||
Audio worklets are loaded through `AudioWorklet.addModule`; keep their processor
|
Audio worklets are loaded through `AudioWorklet.addModule`; keep their processor
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
Context reaches the assistant through browser-built instructions, server-patched
|
Context reaches the assistant through browser-built instructions, server-patched
|
||||||
realtime session instructions, and HTTP reasoning context. The implementation is
|
realtime session instructions, and HTTP reasoning context. The implementation is
|
||||||
in the server's [context services](../../s2s-client/s2s_client/services/context.py),
|
in the server's [context services](../../vocero-api/vocero_api/services/context.py),
|
||||||
[realtime proxy](../../s2s-client/s2s_client/realtime/proxy.py),
|
[realtime proxy](../../vocero-api/vocero_api/realtime/proxy.py),
|
||||||
[context.ts](../../s2s-frontend/src/lib/context.ts), and the
|
[context.ts](../../vocero-frontend/src/lib/context.ts), and the
|
||||||
[WebSocket client](../../s2s-frontend/src/realtime/client.ts).
|
[WebSocket client](../../vocero-frontend/src/realtime/client.ts).
|
||||||
|
|
||||||
## Independent controls
|
## Independent controls
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# Design language
|
# Design language
|
||||||
|
|
||||||
This guide describes the current browser UI. Use
|
This guide describes the current browser UI. Use
|
||||||
[legacy-theme.css](../../s2s-frontend/src/styles/legacy-theme.css) as the token/selector
|
[legacy-theme.css](../../vocero-frontend/src/styles/legacy-theme.css) as the token/selector
|
||||||
reference and [App.tsx](../../s2s-frontend/src/App.tsx) for the theme-specific structure.
|
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
|
Earlier documentation described a monochrome-only dark UI; the current design
|
||||||
also uses aurora gradients, glass surfaces, and a separate light theme.
|
also uses aurora gradients, glass surfaces, and a separate light theme.
|
||||||
|
|
||||||
|
|||||||
+19
-12
@@ -1,11 +1,18 @@
|
|||||||
# Backend development and compatibility
|
# 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
|
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
|
own tooling. Runtime dependency versions match the environment used to capture
|
||||||
the pre-migration server; framework upgrades require a separate compatibility
|
the pre-migration server; framework upgrades require a separate compatibility
|
||||||
review.
|
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
|
Pytest filters only the known `anyio.abc.BlockingPortal` deprecation emitted by
|
||||||
Starlette's test client ([upstream issue](https://github.com/Kludex/starlette/issues/3497)).
|
Starlette's test client ([upstream issue](https://github.com/Kludex/starlette/issues/3497)).
|
||||||
The filter matches the exact message, warning category, and module; other
|
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:
|
From the repository root:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make -C s2s-client install # uv sync --locked, including development tools
|
make -C vocero-api install # uv sync --locked, including development tools
|
||||||
make -C s2s-client format # Ruff safe fixes and formatting
|
make -C vocero-api format # Ruff safe fixes and formatting
|
||||||
make -C s2s-client check # lock freshness, format, lint, mypy, pytest
|
make -C vocero-api check # lock freshness, format, lint, mypy, pytest
|
||||||
make -C s2s-client test-launcher # Bash supervisor integration fixtures
|
make -C vocero-api test-launcher # Bash supervisor integration fixtures
|
||||||
make -C s2s-client test-image # Docker build and isolated runtime smoke test
|
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
|
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
|
readiness checks, and process-group shutdown. Offline starts require the
|
||||||
locked packages and Python runtime to have been installed or cached already.
|
locked packages and Python runtime to have been installed or cached already.
|
||||||
|
|
||||||
Optional hooks use `make -C s2s-client hooks-install`; the configuration stays
|
Optional hooks use `make -C vocero-api hooks-install`; the configuration stays
|
||||||
in `s2s-client/.pre-commit-config.yaml`. `make -C s2s-client hooks-run` runs the
|
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
|
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`
|
only to the client service. The server entry points are `vocero_api.main:app`
|
||||||
and the retained `server:app` shim; use `--app-dir s2s-client` from the root.
|
and the retained `server:app` shim; use `--app-dir vocero-api` from the root.
|
||||||
|
|
||||||
## Schema boundaries
|
## Schema boundaries
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ The mode is normalized case-insensitively. Unknown values resolve to `PROXY`.
|
|||||||
|
|
||||||
## Shared instruction assembly
|
## 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
|
with `config.primaryPrompt || settings.instructions`. `/api/config` pins
|
||||||
`S2S_PRIMARY_PROMPT` when non-empty; otherwise the settings prompt or
|
`S2S_PRIMARY_PROMPT` when non-empty; otherwise the settings prompt or
|
||||||
`DEFAULT_INSTRUCTIONS` is used. The current server does not fetch a remote agent
|
`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
|
`OPENAI_ORGANIZATION` and `OPENAI_PROJECT` are optional headers. The model value
|
||||||
is a runtime default, not a recommendation about provider availability.
|
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`,
|
runs after a complete user transcript and posts a body with `transcript`,
|
||||||
`instructions`, `language`, `user_context`, `phoneNumber`, and `episode_id`.
|
`instructions`, `language`, `user_context`, `phoneNumber`, and `episode_id`.
|
||||||
The server combines instructions and context into model messages, requests a
|
The server combines instructions and context into model messages, requests a
|
||||||
@@ -76,7 +76,7 @@ realtime backend instead.
|
|||||||
| Goal | Source |
|
| Goal | Source |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Change the base agent prompt | `S2S_PRIMARY_PROMPT` or browser settings when unpinned |
|
| 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 browser context assembly | `buildInstructions()` in `vocero-frontend/src/lib/context.ts` |
|
||||||
| Change HTTP provider messages | `reasoning()` in `s2s-client/s2s_client/services/reasoning.py` |
|
| Change HTTP provider messages | `reasoning()` in `vocero-api/vocero_api/services/reasoning.py` |
|
||||||
| Change external-answer rendering | `VoiceController.reason()` and `RealtimeClient.renderExternalAnswer()` |
|
| Change external-answer rendering | `VoiceController.reason()` and `RealtimeClient.renderExternalAnswer()` |
|
||||||
| Change native model/tool generation | `speech_to_speech/LLM` and realtime handlers |
|
| 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
|
`arguments` is a JSON-encoded string. The
|
||||||
[WebSocket client](../../s2s-frontend/src/realtime/client.ts) validates the event and
|
[WebSocket client](../../vocero-frontend/src/realtime/client.ts) validates the event and
|
||||||
dispatches `tool`; [controller.ts](../../s2s-frontend/src/realtime/controller.ts)
|
dispatches `tool`; [controller.ts](../../vocero-frontend/src/realtime/controller.ts)
|
||||||
parses the arguments and runs the supported executor.
|
parses the arguments and runs the supported executor.
|
||||||
Web search uses `POST /api/search`, keeping the deployment's Serper key on the
|
Web search uses `POST /api/search`, keeping the deployment's Serper key on the
|
||||||
server. Tool output returns to the model as:
|
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
|
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
|
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
|
dependencies are reused until their manifests change. Changes to root
|
||||||
`.env` or launcher settings require a restart.
|
`.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:
|
From the repository root, run the frontend checks and local backend fixtures:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm --prefix s2s-frontend run check
|
npm --prefix vocero-frontend run check
|
||||||
npm --prefix s2s-frontend run build
|
npm --prefix vocero-frontend run build
|
||||||
make -C s2s-client check
|
make -C vocero-api check
|
||||||
```
|
```
|
||||||
|
|
||||||
These checks cover frontend contracts and local session/queue/WebSocket
|
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
|
S2S_PROXY_TARGET=ws://localhost:8765/v1/realtime ./start_ui.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
The launcher loads the root `.env`, syncs `s2s-client/.venv` from the committed
|
The launcher loads the root `.env`, syncs `vocero-api/.venv` from the committed
|
||||||
`s2s-client/uv.lock`, and installs the locked
|
`vocero-api/uv.lock`, and installs the locked
|
||||||
frontend dependencies when needed. Vite serves the React frontend on port
|
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`.
|
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
|
`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,
|
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
|
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
|
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:
|
To install and start the backend manually:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
uv sync --project s2s-client --locked
|
uv sync --project vocero-api --locked
|
||||||
S2S_PROXY_TARGET=ws://localhost:8765/v1/realtime \
|
S2S_PROXY_TARGET=ws://localhost:8765/v1/realtime \
|
||||||
uv run --project s2s-client --locked uvicorn --app-dir s2s-client s2s_client.main:app \
|
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 s2s-client
|
--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
|
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.
|
See [development and compatibility](development.md) for checks and schema rules.
|
||||||
|
|
||||||
Open <http://localhost:8420/>, tap the orb/microphone control, grant microphone
|
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:
|
Use the project root as the build context:
|
||||||
|
|
||||||
```bash
|
```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 \
|
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
|
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
|
appropriate existing network and use that service's hostname. An upstream
|
||||||
bound only to host loopback is not reachable via an arbitrary container name.
|
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/`,
|
explorer; documentation lives under `/docs/client/`, `/docs/speech/`,
|
||||||
`/docs/adr/`, `/docs/examples/`, and `/docs/proposals/`. Legacy `/project/` and
|
`/docs/adr/`, `/docs/examples/`, and `/docs/proposals/`. Legacy `/project/` and
|
||||||
`/public/docs.html` redirect to the historical proposal. Markdown is served as a
|
`/public/docs.html` redirect to the historical proposal. Markdown is served as a
|
||||||
|
|||||||
+12
-12
@@ -2,9 +2,9 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
CLIENT_DIR="$ROOT_DIR/s2s-client"
|
API_DIR="$ROOT_DIR/vocero-api"
|
||||||
VENV_DIR="$CLIENT_DIR/.venv"
|
VENV_DIR="$API_DIR/.venv"
|
||||||
FRONTEND_DIR="$ROOT_DIR/s2s-frontend"
|
FRONTEND_DIR="$ROOT_DIR/vocero-frontend"
|
||||||
if [ -f "$ROOT_DIR/.env" ]; then
|
if [ -f "$ROOT_DIR/.env" ]; then
|
||||||
set -a
|
set -a
|
||||||
source "$ROOT_DIR/.env"
|
source "$ROOT_DIR/.env"
|
||||||
@@ -28,7 +28,7 @@ for required_command in uv node npm setsid curl; do
|
|||||||
done
|
done
|
||||||
|
|
||||||
if ! node -e 'const [major, minor] = process.versions.node.split(".").map(Number); process.exit((major === 22 && minor >= 18) || major >= 24 ? 0 : 1)'; then
|
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
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -46,19 +46,19 @@ if [ "$UI_PORT" = "$S2S_BACKEND_PORT" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -f "$CLIENT_DIR/pyproject.toml" ] || [ ! -f "$CLIENT_DIR/uv.lock" ]; then
|
if [ ! -f "$API_DIR/pyproject.toml" ] || [ ! -f "$API_DIR/uv.lock" ]; then
|
||||||
echo "Error: s2s-client/pyproject.toml and s2s-client/uv.lock are required."
|
echo "Error: vocero-api/pyproject.toml and vocero-api/uv.lock are required."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# uv manages only the client environment; an existing root speech venv is separate.
|
# uv manages only the API environment; an existing root speech venv is separate.
|
||||||
UV_PROJECT_ENVIRONMENT="$VENV_DIR" uv sync --project "$CLIENT_DIR" --locked
|
UV_PROJECT_ENVIRONMENT="$VENV_DIR" uv sync --project "$API_DIR" --locked
|
||||||
|
|
||||||
# Keep restarts offline when the installed frontend manifests have not changed.
|
# Keep restarts offline when the installed frontend manifests have not changed.
|
||||||
if [ ! -f "$FRONTEND_DIR/node_modules/vite/bin/vite.js" ] || \
|
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-lock.json" "$FRONTEND_DIR/node_modules/.s2s-package-lock.json" || \
|
||||||
! cmp -s "$FRONTEND_DIR/package.json" "$FRONTEND_DIR/node_modules/.s2s-package.json"; then
|
! 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
|
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-lock.json" "$FRONTEND_DIR/node_modules/.s2s-package-lock.json"
|
||||||
cp "$FRONTEND_DIR/package.json" "$FRONTEND_DIR/node_modules/.s2s-package.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 "----------------------------------------------------"
|
echo "----------------------------------------------------"
|
||||||
|
|
||||||
cd "$ROOT_DIR"
|
cd "$ROOT_DIR"
|
||||||
setsid "$VENV_DIR/bin/python" -m uvicorn s2s_client.main:app \
|
setsid "$VENV_DIR/bin/python" -m uvicorn vocero_api.main:app \
|
||||||
--app-dir "$CLIENT_DIR" \
|
--app-dir "$API_DIR" \
|
||||||
--host 127.0.0.1 --port "$S2S_BACKEND_PORT" \
|
--host 127.0.0.1 --port "$S2S_BACKEND_PORT" \
|
||||||
--reload --reload-dir "$CLIENT_DIR" &
|
--reload --reload-dir "$API_DIR" &
|
||||||
backend_pid=$!
|
backend_pid=$!
|
||||||
child_pids+=("$backend_pid")
|
child_pids+=("$backend_pid")
|
||||||
|
|
||||||
|
|||||||
@@ -7,21 +7,21 @@ repos:
|
|||||||
hooks:
|
hooks:
|
||||||
- id: ruff-check
|
- id: ruff-check
|
||||||
name: Ruff lint
|
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
|
language: unsupported
|
||||||
types_or: [python, pyi]
|
types_or: [python, pyi]
|
||||||
files: ^s2s-client/
|
files: ^vocero-api/
|
||||||
require_serial: true
|
require_serial: true
|
||||||
- id: ruff-format
|
- id: ruff-format
|
||||||
name: Ruff formatting
|
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
|
language: unsupported
|
||||||
types_or: [python, pyi]
|
types_or: [python, pyi]
|
||||||
files: ^s2s-client/
|
files: ^vocero-api/
|
||||||
require_serial: true
|
require_serial: true
|
||||||
- id: mypy
|
- id: mypy
|
||||||
name: Python types
|
name: Python types
|
||||||
entry: uv run --directory s2s-client --locked mypy
|
entry: uv run --directory vocero-api --locked mypy
|
||||||
language: unsupported
|
language: unsupported
|
||||||
pass_filenames: false
|
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
|
All significant client changes are recorded here. This English history
|
||||||
supersedes the former Italian and Spanish versions. Historical entries describe
|
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
|
### 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
|
- Removed the optional workflow reasoning service, its configuration, transcript
|
||||||
forwarding, and browser integration. Reasoning supports `PROXY` and `OPENAI`;
|
forwarding, and browser integration. Reasoning supports `PROXY` and `OPENAI`;
|
||||||
unsupported mode values fall back to `PROXY`.
|
unsupported mode values fall back to `PROXY`.
|
||||||
- Filter the specific Starlette TestClient / AnyIO `BlockingPortal` deprecation
|
- Filter the specific Starlette TestClient / AnyIO `BlockingPortal` deprecation
|
||||||
in pytest while retaining the runtime versions used for compatibility captures.
|
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
|
- 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,
|
lockfile. Split the FastAPI service into configuration, route, provider,
|
||||||
context/reasoning, and realtime modules while retaining `server:app` startup.
|
context/reasoning, and realtime modules while retaining `server:app` startup.
|
||||||
- Added Pydantic response contracts with permissive upstream fields and original
|
- Added Pydantic response contracts with permissive upstream fields and original
|
||||||
payload serialization. Captured HTTP, request-schema, and realtime regression
|
payload serialization. Captured HTTP, request-schema, and realtime regression
|
||||||
fixtures preserve existing API behavior, including known failures.
|
fixtures preserve existing API behavior, including known failures.
|
||||||
- Removed the legacy browser site in favor of the React application in
|
- 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
|
- Retired daily talk-time accounting and the associated Hugging Face account
|
||||||
UI. Load-balancer allocation, queue lifecycle, caller profiles, upstream
|
UI. Load-balancer allocation, queue lifecycle, caller profiles, upstream
|
||||||
authentication, and voice-session cleanup remain available.
|
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.
|
# Serve backend APIs, the WebSocket proxy, and shared project documentation.
|
||||||
# Run the React/Vite frontend separately; this image contains no browser UI.
|
# Run the React/Vite frontend separately; this image contains no browser UI.
|
||||||
# HF Spaces (sdk: docker) routes traffic to $PORT, default 7860.
|
# 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 \
|
ENV UV_LINK_MODE=copy \
|
||||||
UV_PYTHON_DOWNLOADS=never \
|
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
|
RUN uv sync --locked --no-dev --no-install-project
|
||||||
|
|
||||||
COPY s2s-client/README.md s2s-client/server.py ./
|
COPY vocero-api/README.md vocero-api/server.py ./
|
||||||
COPY s2s-client/s2s_client/ ./s2s_client/
|
COPY vocero-api/vocero_api/ ./vocero_api/
|
||||||
COPY docs/ /app/docs/
|
COPY docs/ /app/docs/
|
||||||
RUN uv sync --locked --no-dev
|
RUN uv sync --locked --no-dev
|
||||||
|
|
||||||
EXPOSE 7860
|
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
|
../start_ui.sh
|
||||||
|
|
||||||
hooks-install:
|
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:
|
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:
|
test-launcher:
|
||||||
npm --prefix ../s2s-frontend run test:launcher
|
npm --prefix ../vocero-frontend run test:launcher
|
||||||
|
|
||||||
test-image:
|
test-image:
|
||||||
bash scripts/test_docker_image.sh
|
bash scripts/test_docker_image.sh
|
||||||
@@ -9,15 +9,15 @@ pinned: false
|
|||||||
short_description: Voice chat over WebSocket against a HF speech-to-speech
|
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,
|
This service handles configuration, caller context, upstream authentication,
|
||||||
search, reasoning, ApiPilot tools, and optional load-balancer allocation.
|
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
|
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.
|
or through Docker exposes only backend routes and shared documentation.
|
||||||
|
|
||||||
The complete [usage guide](../docs/client/usage.md) covers local startup,
|
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:
|
Build from the repository root so the container includes the documentation:
|
||||||
|
|
||||||
```bash
|
```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`,
|
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:
|
3.12 (uv can provision Python). From the repository root:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make -C s2s-client install
|
make -C vocero-api install
|
||||||
make -C s2s-client check
|
make -C vocero-api check
|
||||||
```
|
```
|
||||||
|
|
||||||
Tests isolate environment configuration, use HTTP fixtures for allocation and
|
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
|
See [backend development and compatibility](../docs/client/development.md) for
|
||||||
formatting, typing, schema policy, optional Git hooks, and deployment checks.
|
formatting, typing, schema policy, optional Git hooks, and deployment checks.
|
||||||
`uvicorn server:app --app-dir s2s-client` remains a supported entry point;
|
`uvicorn server:app --app-dir vocero-api` remains a supported entry point;
|
||||||
the implementation now lives in `s2s_client/`.
|
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
|
FastAPI's API explorer remains at `/docs`. Project documents are served as files below
|
||||||
`/docs/client/`, `/docs/speech/`, `/docs/adr/`, `/docs/examples/`, and
|
`/docs/client/`, `/docs/speech/`, `/docs/adr/`, `/docs/examples/`, and
|
||||||
@@ -3,7 +3,7 @@ requires = ["setuptools>=68", "wheel"]
|
|||||||
build-backend = "setuptools.build_meta"
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "aquaportal-s2s-client"
|
name = "vocero-api"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
description = "FastAPI integration and realtime proxy for the Vocero frontend"
|
description = "FastAPI integration and realtime proxy for the Vocero frontend"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
@@ -29,12 +29,12 @@ dev = [
|
|||||||
required-version = "==0.12.5"
|
required-version = "==0.12.5"
|
||||||
|
|
||||||
[tool.setuptools.packages.find]
|
[tool.setuptools.packages.find]
|
||||||
include = ["s2s_client*"]
|
include = ["vocero_api*"]
|
||||||
|
|
||||||
[tool.ruff]
|
[tool.ruff]
|
||||||
target-version = "py312"
|
target-version = "py312"
|
||||||
line-length = 120
|
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]
|
[tool.ruff.lint]
|
||||||
select = ["E4", "E7", "E9", "F", "I", "B", "UP"]
|
select = ["E4", "E7", "E9", "F", "I", "B", "UP"]
|
||||||
@@ -48,7 +48,7 @@ line-ending = "auto"
|
|||||||
[tool.mypy]
|
[tool.mypy]
|
||||||
python_version = "3.12"
|
python_version = "3.12"
|
||||||
plugins = ["pydantic.mypy"]
|
plugins = ["pydantic.mypy"]
|
||||||
files = ["s2s_client", "server.py"]
|
files = ["vocero_api", "server.py"]
|
||||||
check_untyped_defs = true
|
check_untyped_defs = true
|
||||||
disallow_untyped_defs = true
|
disallow_untyped_defs = true
|
||||||
no_implicit_optional = true
|
no_implicit_optional = true
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
IMAGE="s2s-client-smoke:$(date +%s)-$$"
|
IMAGE="vocero-api-smoke:$(date +%s)-$$"
|
||||||
container_id=""
|
container_id=""
|
||||||
cleanup() {
|
cleanup() {
|
||||||
if [ -n "$container_id" ]; then
|
if [ -n "$container_id" ]; then
|
||||||
@@ -13,7 +13,7 @@ cleanup() {
|
|||||||
}
|
}
|
||||||
trap cleanup EXIT
|
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")"
|
container_id="$(docker run --detach --publish 127.0.0.1::7860 "$IMAGE")"
|
||||||
port="$(docker port "$container_id" 7860/tcp)"
|
port="$(docker port "$container_id" 7860/tcp)"
|
||||||
ready=false
|
ready=false
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
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))
|
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
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
with patch.dict(os.environ, {}, clear=True), patch("dotenv.load_dotenv", return_value=False):
|
with patch.dict(os.environ, {}, clear=True), patch("dotenv.load_dotenv", return_value=False):
|
||||||
from s2s_client import main as server
|
from vocero_api import main as server
|
||||||
from s2s_client.core import config as settings
|
from vocero_api.core import config as settings
|
||||||
from s2s_client.providers import apipilot
|
from vocero_api.providers import apipilot
|
||||||
from s2s_client.realtime import proxy
|
from vocero_api.realtime import proxy
|
||||||
from s2s_client.services import context
|
from vocero_api.services import context
|
||||||
|
|
||||||
ASYNC_CLIENT = httpx.AsyncClient
|
ASYNC_CLIENT = httpx.AsyncClient
|
||||||
FIXTURE = Path(__file__).parent / "fixtures" / "legacy-contracts.json"
|
FIXTURE = Path(__file__).parent / "fixtures" / "legacy-contracts.json"
|
||||||
@@ -4,9 +4,9 @@ import pytest
|
|||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
from pydantic import ValidationError
|
from pydantic import ValidationError
|
||||||
|
|
||||||
from s2s_client.api.responses import validated_json_response
|
from vocero_api.api.responses import validated_json_response
|
||||||
from s2s_client.models.requests import ApipilotExecuteRequest, QueueEndPayload, SearchRequest
|
from vocero_api.models.requests import ApipilotExecuteRequest, QueueEndPayload, SearchRequest
|
||||||
from s2s_client.models.responses import (
|
from vocero_api.models.responses import (
|
||||||
AllocationResponse,
|
AllocationResponse,
|
||||||
QueueCancellationResponse,
|
QueueCancellationResponse,
|
||||||
ReasoningResponse,
|
ReasoningResponse,
|
||||||
@@ -58,7 +58,7 @@ def test_request_models_keep_legacy_extra_field_and_argument_behavior():
|
|||||||
|
|
||||||
def test_http_routes_publish_response_schemas():
|
def test_http_routes_publish_response_schemas():
|
||||||
# Import the app only after the compatibility suite's isolated configuration.
|
# 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()
|
spec = app.openapi()
|
||||||
for path, operations in spec["paths"].items():
|
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"]
|
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(
|
@pytest.mark.parametrize(
|
||||||
("value", "expected"),
|
("value", "expected"),
|
||||||
[
|
[
|
||||||
@@ -85,6 +92,6 @@ def test_http_routes_publish_response_schemas():
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_reasoning_configuration_preserves_normalization_and_native_fallback(value, expected):
|
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
|
assert _normalize_reasoning_type(value) == expected
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
"""Backend contract checks with local HTTP fixtures and a loopback voice server.
|
"""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
|
import json
|
||||||
@@ -16,10 +16,10 @@ from websockets.sync.server import serve
|
|||||||
|
|
||||||
# Never consume deployment credentials or contact services from a developer's .env.
|
# 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):
|
with patch.dict(os.environ, {}, clear=True), patch("dotenv.load_dotenv", return_value=False):
|
||||||
from s2s_client import main as server
|
from vocero_api import main as server
|
||||||
from s2s_client.core import config as settings
|
from vocero_api.core import config as settings
|
||||||
from s2s_client.providers import apipilot, caller
|
from vocero_api.providers import apipilot, caller
|
||||||
from s2s_client.services import tool_context
|
from vocero_api.services import tool_context
|
||||||
|
|
||||||
|
|
||||||
GRANT = {
|
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" },
|
{ 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]]
|
[[package]]
|
||||||
name = "certifi"
|
name = "certifi"
|
||||||
version = "2026.7.22"
|
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" },
|
{ 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]]
|
[[package]]
|
||||||
name = "watchfiles"
|
name = "watchfiles"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
|
|
||||||
from s2s_client.api.routes import apipilot, caller, config, search, sessions
|
from vocero_api.api.routes import apipilot, caller, config, search, sessions
|
||||||
from s2s_client.models.responses import ReasoningResponse
|
from vocero_api.models.responses import ReasoningResponse
|
||||||
from s2s_client.realtime.proxy import realtime_proxy
|
from vocero_api.realtime.proxy import realtime_proxy
|
||||||
from s2s_client.services.reasoning import reasoning
|
from vocero_api.services.reasoning import reasoning
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
router.include_router(config.router)
|
router.include_router(config.router)
|
||||||
+6
-6
@@ -3,12 +3,12 @@ from __future__ import annotations
|
|||||||
from fastapi import APIRouter, HTTPException, Request
|
from fastapi import APIRouter, HTTPException, Request
|
||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
|
|
||||||
from s2s_client.api.responses import validated_json_response
|
from vocero_api.api.responses import validated_json_response
|
||||||
from s2s_client.core import config as settings
|
from vocero_api.core import config as settings
|
||||||
from s2s_client.core import logging as app_logging
|
from vocero_api.core import logging as app_logging
|
||||||
from s2s_client.models import requests as models_requests
|
from vocero_api.models import requests as models_requests
|
||||||
from s2s_client.models.responses import ToolCatalogResponse, ToolExecutionResponse
|
from vocero_api.models.responses import ToolCatalogResponse, ToolExecutionResponse
|
||||||
from s2s_client.providers import apipilot as providers_apipilot
|
from vocero_api.providers import apipilot as providers_apipilot
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
+6
-6
@@ -6,12 +6,12 @@ import httpx
|
|||||||
from fastapi import APIRouter, HTTPException
|
from fastapi import APIRouter, HTTPException
|
||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
|
|
||||||
from s2s_client.api.responses import validated_json_response
|
from vocero_api.api.responses import validated_json_response
|
||||||
from s2s_client.core import config as settings
|
from vocero_api.core import config as settings
|
||||||
from s2s_client.core import logging as app_logging
|
from vocero_api.core import logging as app_logging
|
||||||
from s2s_client.models.responses import ReportsResponse, UserProfileResponse
|
from vocero_api.models.responses import ReportsResponse, UserProfileResponse
|
||||||
from s2s_client.providers import caller as providers_caller
|
from vocero_api.providers import caller as providers_caller
|
||||||
from s2s_client.services import context as services_context
|
from vocero_api.services import context as services_context
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
+5
-5
@@ -3,11 +3,11 @@ from __future__ import annotations
|
|||||||
from fastapi import APIRouter, Request
|
from fastapi import APIRouter, Request
|
||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
|
|
||||||
from s2s_client.api.responses import validated_json_response
|
from vocero_api.api.responses import validated_json_response
|
||||||
from s2s_client.core import config as settings
|
from vocero_api.core import config as settings
|
||||||
from s2s_client.core import logging as app_logging
|
from vocero_api.core import logging as app_logging
|
||||||
from s2s_client.core import urls as core_urls
|
from vocero_api.core import urls as core_urls
|
||||||
from s2s_client.models.responses import ConfigResponse
|
from vocero_api.models.responses import ConfigResponse
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
+5
-5
@@ -6,11 +6,11 @@ import httpx
|
|||||||
from fastapi import APIRouter, HTTPException
|
from fastapi import APIRouter, HTTPException
|
||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
|
|
||||||
from s2s_client.api.responses import validated_json_response
|
from vocero_api.api.responses import validated_json_response
|
||||||
from s2s_client.core import config as settings
|
from vocero_api.core import config as settings
|
||||||
from s2s_client.core import logging as app_logging
|
from vocero_api.core import logging as app_logging
|
||||||
from s2s_client.models import requests as models_requests
|
from vocero_api.models import requests as models_requests
|
||||||
from s2s_client.models.responses import SearchResponse
|
from vocero_api.models.responses import SearchResponse
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
+5
-5
@@ -6,11 +6,11 @@ import httpx
|
|||||||
from fastapi import APIRouter, HTTPException, Request
|
from fastapi import APIRouter, HTTPException, Request
|
||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
|
|
||||||
from s2s_client.api.responses import validated_json_response
|
from vocero_api.api.responses import validated_json_response
|
||||||
from s2s_client.core import config as settings
|
from vocero_api.core import config as settings
|
||||||
from s2s_client.core import logging as app_logging
|
from vocero_api.core import logging as app_logging
|
||||||
from s2s_client.models.requests import QueueEndPayload
|
from vocero_api.models.requests import QueueEndPayload
|
||||||
from s2s_client.models.responses import AllocationResponse, QueueCancellationResponse
|
from vocero_api.models.responses import AllocationResponse, QueueCancellationResponse
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@@ -4,7 +4,7 @@ import os
|
|||||||
|
|
||||||
from dotenv import load_dotenv
|
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__), "..", "..", ".."))
|
ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", ".."))
|
||||||
load_dotenv(os.path.join(ROOT_DIR, ".env"), override=False)
|
load_dotenv(os.path.join(ROOT_DIR, ".env"), override=False)
|
||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from s2s_client.core import config as settings
|
from vocero_api.core import config as settings
|
||||||
from s2s_client.core import logging as app_logging
|
from vocero_api.core import logging as app_logging
|
||||||
|
|
||||||
|
|
||||||
# Resolve the execution URL for the startup banner using the same normalization
|
# 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 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:
|
def _url_with_query(url: str, **params: str) -> str:
|
||||||
@@ -6,8 +6,8 @@ from fastapi import FastAPI
|
|||||||
from fastapi.responses import RedirectResponse
|
from fastapi.responses import RedirectResponse
|
||||||
from fastapi.staticfiles import StaticFiles
|
from fastapi.staticfiles import StaticFiles
|
||||||
|
|
||||||
from s2s_client.api.router import router
|
from vocero_api.api.router import router
|
||||||
from s2s_client.core import config, diagnostics # noqa: F401 - retain the startup banner
|
from vocero_api.core import config, diagnostics # noqa: F401 - retain the startup banner
|
||||||
|
|
||||||
app = FastAPI(title="s2s-demo")
|
app = FastAPI(title="s2s-demo")
|
||||||
app.include_router(router)
|
app.include_router(router)
|
||||||
+2
-2
@@ -9,8 +9,8 @@ from typing import Any, cast
|
|||||||
import httpx
|
import httpx
|
||||||
from fastapi import HTTPException, Request
|
from fastapi import HTTPException, Request
|
||||||
|
|
||||||
from s2s_client.core import config as settings
|
from vocero_api.core import config as settings
|
||||||
from s2s_client.core import logging as app_logging
|
from vocero_api.core import logging as app_logging
|
||||||
|
|
||||||
# In-process cache and asynchronous lock: one caller refreshes while others
|
# In-process cache and asynchronous lock: one caller refreshes while others
|
||||||
# read the stale entry. `_APIPILOT_CACHE[auth_fingerprint] = (fetched_at_monotonic, tools_list)`.
|
# 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
|
import httpx
|
||||||
|
|
||||||
from s2s_client.core import config as settings
|
from vocero_api.core import config as settings
|
||||||
from s2s_client.core import logging as app_logging
|
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:
|
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
|
import re
|
||||||
from typing import Any, cast
|
from typing import Any, cast
|
||||||
|
|
||||||
from s2s_client.core import logging as app_logging
|
from vocero_api.core import logging as app_logging
|
||||||
from s2s_client.services import context as services_context
|
from vocero_api.services import context as services_context
|
||||||
from s2s_client.services import tool_context as services_tool_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:
|
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.asyncio.client import ClientConnection
|
||||||
from websockets.typing import Subprotocol
|
from websockets.typing import Subprotocol
|
||||||
|
|
||||||
from s2s_client.core import config as settings
|
from vocero_api.core import config as settings
|
||||||
from s2s_client.core import logging as app_logging
|
from vocero_api.core import logging as app_logging
|
||||||
from s2s_client.core import urls as core_urls
|
from vocero_api.core import urls as core_urls
|
||||||
from s2s_client.providers import apipilot as providers_apipilot
|
from vocero_api.providers import apipilot as providers_apipilot
|
||||||
from s2s_client.providers import caller as providers_caller
|
from vocero_api.providers import caller as providers_caller
|
||||||
from s2s_client.realtime import events as realtime_events
|
from vocero_api.realtime import events as realtime_events
|
||||||
from s2s_client.services import context as services_context
|
from vocero_api.services import context as services_context
|
||||||
from s2s_client.services import tool_context as services_tool_context
|
from vocero_api.services import tool_context as services_tool_context
|
||||||
|
|
||||||
|
|
||||||
async def realtime_proxy(ws: WebSocket) -> None:
|
async def realtime_proxy(ws: WebSocket) -> None:
|
||||||
+3
-3
@@ -4,9 +4,9 @@ import datetime as _dt_module
|
|||||||
import json
|
import json
|
||||||
from typing import Any, cast
|
from typing import Any, cast
|
||||||
|
|
||||||
from s2s_client.core import config as settings
|
from vocero_api.core import config as settings
|
||||||
from s2s_client.core import logging as app_logging
|
from vocero_api.core import logging as app_logging
|
||||||
from s2s_client.services import tool_context as services_tool_context
|
from vocero_api.services import tool_context as services_tool_context
|
||||||
|
|
||||||
VOCERO_USER_CONTEXT_HINT = " Caller profile resolved from the page phoneNumber: "
|
VOCERO_USER_CONTEXT_HINT = " Caller profile resolved from the page phoneNumber: "
|
||||||
REPORTS_LIST_HINT = (
|
REPORTS_LIST_HINT = (
|
||||||
+9
-9
@@ -6,15 +6,15 @@ import httpx
|
|||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
|
|
||||||
from s2s_client.api.responses import validated_json_response
|
from vocero_api.api.responses import validated_json_response
|
||||||
from s2s_client.core import config as settings
|
from vocero_api.core import config as settings
|
||||||
from s2s_client.core import logging as app_logging
|
from vocero_api.core import logging as app_logging
|
||||||
from s2s_client.models import requests as models_requests
|
from vocero_api.models import requests as models_requests
|
||||||
from s2s_client.models.responses import ReasoningResponse
|
from vocero_api.models.responses import ReasoningResponse
|
||||||
from s2s_client.providers import apipilot as providers_apipilot
|
from vocero_api.providers import apipilot as providers_apipilot
|
||||||
from s2s_client.providers import caller as providers_caller
|
from vocero_api.providers import caller as providers_caller
|
||||||
from s2s_client.services import context as services_context
|
from vocero_api.services import context as services_context
|
||||||
from s2s_client.services import tool_context as services_tool_context
|
from vocero_api.services import tool_context as services_tool_context
|
||||||
|
|
||||||
|
|
||||||
def _openai_headers() -> dict[str, str]:
|
def _openai_headers() -> dict[str, str]:
|
||||||
+1
-1
@@ -4,7 +4,7 @@ import json
|
|||||||
import re
|
import re
|
||||||
from typing import cast
|
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)
|
# APIPILOT — Reasoning engine integration (two passes)
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
# Vocero React frontend
|
# Vocero React frontend
|
||||||
|
|
||||||
React 19, TypeScript, Tailwind CSS 4, and Vite 8 client for the existing
|
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.
|
rendering and state, including the realtime WebSocket and audio integration.
|
||||||
|
|
||||||
## Run during development
|
## 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
|
Add `?debug=true` to access settings, tools, and custom RAG context. Existing
|
||||||
`phoneNumber` and `episode_id` query parameters are supported.
|
`phoneNumber` and `episode_id` query parameters are supported.
|
||||||
|
|
||||||
The launcher requires Python 3 with venv support, Node.js 22.18 or later on the
|
The launcher requires uv 0.12.5, Node.js 22.18 or later on the 22.x line or
|
||||||
22.x line or Node.js 24+, npm, Bash 4.4+, `setsid` (util-linux), and `curl`.
|
Node.js 24+, npm, Bash 4.4+, `setsid` (util-linux), and `curl`.
|
||||||
It creates `.venv` and installs missing Python dependencies. It runs
|
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
|
`npm ci --include=dev` on first start and when either frontend package manifest
|
||||||
changes. Subsequent restarts reuse the installed dependencies.
|
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
|
must differ. Occupied ports fail startup rather than silently selecting a new
|
||||||
endpoint. The launcher waits for FastAPI before starting Vite, stops both
|
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.
|
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
|
The Vite child explicitly uses `NODE_ENV=development`, including when a process
|
||||||
manager sets `NODE_ENV=production`, so React Fast Refresh remains enabled.
|
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
|
## Staging with hot reload
|
||||||
|
|
||||||
PM2 can continue running the root `start_ui.sh` as its existing `s2s_ui` process.
|
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`
|
Ensure uv and the Node/npm versions above are on PM2's PATH and deploy `vocero-frontend`
|
||||||
alongside `s2s-client` and `docs`. The reverse proxy still targets port `8420`.
|
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:
|
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:
|
[client usage guide](../docs/client/usage.md), then run:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cd s2s-frontend
|
cd vocero-frontend
|
||||||
npm ci
|
npm ci
|
||||||
npm run dev
|
npm run dev
|
||||||
```
|
```
|
||||||
+28
-7
@@ -64,21 +64,21 @@ async function waitForFile(filename: string) {
|
|||||||
|
|
||||||
async function launcher(context: TestContext, env: Record<string, string> = {}) {
|
async function launcher(context: TestContext, env: Record<string, string> = {}) {
|
||||||
const root = await mkdtemp(join(tmpdir(), "s2s-launcher-"));
|
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");
|
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(root, "bin"));
|
||||||
await mkdir(join(modules, "vite/bin"), { recursive: true });
|
await mkdir(join(modules, "vite/bin"), { recursive: true });
|
||||||
await copyFile(new URL("../../start_ui.sh", import.meta.url), join(root, "start_ui.sh"));
|
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, "vocero-api/pyproject.toml"), "");
|
||||||
await writeFile(join(root, "s2s-client/uv.lock"), "");
|
await writeFile(join(root, "vocero-api/uv.lock"), "");
|
||||||
await writeFile(
|
await writeFile(
|
||||||
join(root, "bin/uv"),
|
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',
|
'#!/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 },
|
{ mode: 0o755 },
|
||||||
);
|
);
|
||||||
await writeFile(
|
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',
|
'#!/bin/bash\nif [ "$1" = "-c" ]; then exit 0; fi\nexec node "$S2S_TEST_ROOT/child.mjs" backend "$@"\n',
|
||||||
{ mode: 0o755 },
|
{ mode: 0o755 },
|
||||||
);
|
);
|
||||||
@@ -146,9 +146,30 @@ for (const signal of ["SIGINT", "SIGTERM"] as const) {
|
|||||||
assert.equal(run.output().includes("Installing"), false);
|
assert.equal(run.output().includes("Installing"), false);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
await readFile(join(run.root, "uv-args"), "utf8"),
|
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);
|
run.child.kill(signal);
|
||||||
assert.equal(await run.exited, signal === "SIGINT" ? 130 : 143);
|
assert.equal(await run.exited, signal === "SIGINT" ? 130 : 143);
|
||||||
for (const role of ["backend", "frontend"]) {
|
for (const role of ["backend", "frontend"]) {
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "s2s-frontend",
|
"name": "vocero-frontend",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "s2s-frontend",
|
"name": "vocero-frontend",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dompurify": "^3.4.16",
|
"dompurify": "^3.4.16",
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "s2s-frontend",
|
"name": "vocero-frontend",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"type": "module",
|
"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