airi/apps/server
RainbowBird 9a182e0b68
Some checks are pending
Cloudflare Workers (server-dev) / Deploy - stage-web (server-dev) (push) Waiting to run
feat(server,stage-ui): bidirectional streaming TTS + audio path refactor
Why:
- Add a real bidirectional streaming TTS path: raw LLM tokens are
  forwarded to the upstream model (Volcengine v3 via the unspeech ws
  bridge) without client-side segmentation, so the model owns sentence
  splitting and audio chunks play as they arrive.
- Move audio endpoints out of /api/v1/openai/. `/audio/voices`,
  `/audio/models`, `/audio/voices/streaming` are not real OpenAI public
  APIs, and the streaming TTS surface has nothing to do with OpenAI —
  keeping them under /openai/ mislabelled the contract.
- Introduce `capabilities.speech.transport` on ProviderDefinition so
  future streaming providers (ElevenLabs / Cartesia / OpenAI Realtime)
  opt in without touching Stage.vue or the session factory.
- Unify Stage.vue's TTS path through a single StageTtsSession so the
  chat-orchestrator hooks no longer branch on provider id.

What:
- apps/server: new ws proxy /api/v1/audio/speech/ws bridges client ↔
  unspeech with auth, pre-flight flux check, billing from upstream
  session.finished.usage, OTel spans.
- apps/server: audio routes moved from /api/v1/openai/audio/* to
  /api/v1/audio/* (hard cutover; 404 sentinel tests added).
- apps/server: new /api/v1/audio/voices/streaming proxy reads voices
  from unspeech /api/voices?provider=volcengine.
- apps/server: new STREAMING_TTS_UPSTREAM configKV entry +
  scripts/seed-streaming-tts.ts.
- stage-ui: new libs/speech/streaming-pipeline.ts opens one ws per LLM
  intent (appendText / finish / cancel + onSentence / onError / onDone).
- stage-ui: new libs/speech/tts-session.ts — StageTtsSession interface
  with segmenter and streaming adapters; factory dispatches by
  capabilities.speech.transport instead of hard-coded provider id.
- stage-ui: providerOfficialSpeechStreaming with capabilities.speech =
  { transport: 'bidirectional-ws' }; settings page with model/voice
  picker + ws-based preview.
- stage-ui: Stage.vue chat hooks collapsed to a single currentSession;
  hot-swap watcher cancels mid-session on provider/voice/model change;
  unmount cancels and drains playback.

Tests:
- 9 streaming-pipeline tests (happy path / buffered / error / cancel /
  truncation)
- 11 tts-session tests (factory branch coverage + adapter contracts)
- 4 audio-speech-ws route tests (forwarding / billing / pre-flight /
  config-missing)
- 3 legacy-path 404 sentinels in v1 route tests
- Verification doc updated to reflect automated coverage.
2026-05-17 01:35:40 +08:00
..
docs feat(server,stage-ui): bidirectional streaming TTS + audio path refactor 2026-05-17 01:35:40 +08:00
drizzle refactor(server): drop redis stream + worker role (#1792) 2026-05-08 21:14:01 +08:00
otel refactor(server): llm router config sync subscriber 2026-05-16 03:59:19 +08:00
production/railway fix(docker): enhance server-schema build process with error handling 2026-05-08 21:59:36 +08:00
scripts feat(server,stage-ui): bidirectional streaming TTS + audio path refactor 2026-05-17 01:35:40 +08:00
sql fix(server): docker-compose fix 2026-01-05 16:26:44 +08:00
src feat(server,stage-ui): bidirectional streaming TTS + audio path refactor 2026-05-17 01:35:40 +08:00
tests/verifications test(server): integration test 2026-05-16 17:42:19 +08:00
.env feat(server): finalize in-process LLM/TTS router cutover 2026-05-16 03:21:24 +08:00
CLAUDE.md feat(server): finalize in-process LLM/TTS router cutover 2026-05-16 03:21:24 +08:00
docker-compose.otel.yml feat(server): replace legacy health endpoints with K8s-style /livez and /readyz probes 2026-05-15 19:02:57 +08:00
docker-compose.yml feat(server): replace legacy health endpoints with K8s-style /livez and /readyz probes 2026-05-15 19:02:57 +08:00
Dockerfile fix(docker): enhance server-schema build process with error handling 2026-05-08 21:59:36 +08:00
drizzle.config.ts fix(server): database with duplicated index 2026-01-05 16:27:10 +08:00
instrumentation.ts feat(server/otel): restructure observability metrics and add active sessions gauge 2026-05-12 23:10:13 +08:00
package.json feat(server): stream tts provider 2026-05-16 17:29:40 +08:00
railway.toml revert(server/docker): restore assets distribution image 2026-04-28 00:54:13 +08:00
README.md feat(server): env-based trusted origins for Capacitor dev (#1763) 2026-05-14 16:17:13 +08:00
tsconfig.json feat(server/otel): restructure observability metrics and add active sessions gauge 2026-05-12 23:10:13 +08:00
vitest.config.ts feat(ui-server-auth): init server auth page 2026-04-03 01:26:38 +08:00

@proj-airi/server

HTTP and WebSocket backend for AIRI. This app owns auth, billing, chat synchronization, gateway forwarding, and server-side observability export.

What It Does

  • Serves the Hono-based API and WebSocket endpoints.
  • Uses Postgres as the source of truth for users, billing, and durable state.
  • Uses Redis for cache, KV, Pub/Sub, and Streams.
  • Forwards GenAI requests to the configured upstream gateway and records billing from usage.
  • Exports traces, metrics, and logs through OpenTelemetry.

How To Use It

Install dependencies from the repo root and run scoped commands:

pnpm -F @proj-airi/server typecheck
pnpm -F @proj-airi/server exec vitest run
pnpm -F @proj-airi/server build

For local observability infrastructure, use:

docker compose -f apps/server/docker-compose.otel.yml up -d

ADDITIONAL_TRUSTED_ORIGINS (LAN / Capacitor dev)

When the mobile dev server uses a non-localhost origin (for example https://10.x.x.x:5273 from cap copy ios / capacitor.config.json), set ADDITIONAL_TRUSTED_ORIGINS in apps/server/.env.local to a comma-separated list of exact origins (parsed and normalized at startup). Example:

ADDITIONAL_TRUSTED_ORIGINS=https://10.0.0.129:5273,https://198.18.0.1:5273

Restart the API server after changing this variable.