How It Works#
This page describes the architecture and internal flow of an audio request through the microservice.
Architecture#
At a high level, the Audio Analyzer is a FastAPI service that accepts an
audio upload, splits it into chunks with FFmpeg, runs each chunk through an
ASR backend, and (optionally) runs a sentiment model in parallel. Results
are aggregated per session and returned as a single JSON response, an
OpenAI-compatible SSE stream, or an NDJSON event stream. The service also
accepts a continuous live audio feed over WebSocket (/v1/realtime), where
voice activity detection — rather than file boundaries — decides where each
transcribed utterance ends.
%%{init: {
'theme': 'base',
'themeVariables': {
'fontFamily': '"IntelOne Display", "Intel Clear", "Inter", "Segoe UI", Arial, sans-serif',
'fontSize': '14px',
'primaryColor': '#0068B5',
'primaryTextColor': '#FFFFFF',
'primaryBorderColor': '#00377C',
'lineColor': '#00377C',
'secondaryColor': '#EEF3F8',
'tertiaryColor': '#F7F8FA',
'background': '#FFFFFF',
'mainBkg': '#FFFFFF',
'clusterBkg': '#F7F8FA',
'clusterBorder': '#0068B5',
'edgeLabelBackground': '#FFFFFF',
'noteBkgColor': '#F7F8FA',
'noteTextColor': '#3A3A3A'
}
}}%%
flowchart LR
Client([Client])
subgraph Service["Audio Analyzer (FastAPI, :8010)"]
API["API Layer<br/>(transcription / realtime / health / devices)"]
Pipeline["Pipeline Orchestrator<br/>(pipeline.py)"]
VAD["Realtime Buffer + VAD<br/>(utils/pcm_audio.py)"]
Pre["Preprocessing<br/>(FFmpeg: decode, chunk, denoise)"]
ASR["ASR Backend<br/>(openai | openvino | whispercpp)"]
Sent["Sentiment Backend<br/>(openvino | pytorch)"]
Session[("Session Store<br/>storage/<session_id>/")]
end
Models[("Model Cache<br/>models/")]
Device{{"Inference Device<br/>CPU / GPU"}}
Client -- "POST /v1/audio/transcriptions{,/stream}" --> API
Client -- "WS /v1/realtime (PCM16 frames)" --> API
API --> VAD
VAD -- "committed utterance" --> Pipeline
API --> Pipeline
Pipeline --> Pre
Pre --> ASR
Pre --> Sent
ASR --> Device
Sent --> Device
ASR --> Pipeline
Sent --> Pipeline
Pipeline <--> Session
ASR -. loads .-> Models
Sent -. loads .-> Models
Pipeline -- "JSON response / NDJSON events<br/>X-Session-ID header" --> Client
classDef client fill:#FFFFFF,stroke:#0068B5,stroke-width:2px,color:#3A3A3A;
classDef core fill:#0068B5,stroke:#00377C,stroke-width:1.5px,color:#FFFFFF;
classDef backend fill:#00A3F4,stroke:#00377C,stroke-width:1.5px,color:#FFFFFF;
classDef store fill:#6C6C6C,stroke:#0068B5,stroke-width:1.5px,color:#FFFFFF;
classDef device fill:#00C7FD,stroke:#00377C,stroke-width:1.5px,color:#3A3A3A;
class Client client;
class API,Pipeline,Pre,VAD core;
class ASR,Sent backend;
class Session,Models store;
class Device device;
style Service fill:#F7F8FA,stroke:#0068B5,stroke-width:1.5px,color:#3A3A3A;
Key planes:
API layer — request validation, session header handling, response shaping (single JSON, OpenAI-compatible SSE, or NDJSON), and the realtime WebSocket event protocol.
Pipeline orchestrator — drives preprocessing, ASR, and sentiment; aggregates per-chunk results into a session-level summary.
Backends — pluggable ASR and sentiment implementations selected via config; each backend handles its own model loading and device placement.
Session store — per-session directory holding chunk files and metadata; enables multi-upload continuation via
session_id.
Request Flow#
The service supports two distinct ingestion modes: file-based (a finite upload, transcribed chunk by chunk) and live (an open-ended PCM stream over WebSocket, transcribed utterance by utterance).
File-based ingestion#
Upload — A client sends an audio file to
POST /v1/audio/transcriptions(single JSON response, or OpenAI-compatible SSE whenstream=true) orPOST /v1/audio/transcriptions/stream(NDJSON event stream).Session resolution — If
session_idis supplied, the service reuses the existing session directory understorage/<session_id>/. Otherwise, it creates a new session and returns the id in theX-Session-IDresponse header.Preprocessing — FFmpeg decodes the upload and produces audio chunks under the configured
audio_preprocessing.chunk_dir. Chunk size, silence detection, and optional denoising are controlled by theaudio_preprocessingconfig section.ASR inference — Each chunk is transcribed by the configured ASR backend (
openai,openvino, orwhispercpp) on the configured device (typicallyCPU;GPUis available only for supported OpenVINO™ paths).Sentiment (optional) — When
sentiment.enabledis true, the service runs the configured sentiment model (openvinoorpytorch) and aggregates a session-level summary.Response — The non-streaming endpoint returns a final response object. The NDJSON endpoint emits
transcription.chunkevents as each chunk completes and a finaltranscription.completedevent. Withstream=true,POST /v1/audio/transcriptionsinstead emits OpenAI-compatible SSE (transcript.text.delta→transcript.text.done→[DONE]).Cleanup — If
pipeline.delete_chunks_after_useis true, temporary chunk files are removed after processing. Session metadata remains understorage/<session_id>/.
Live ingestion (WebSocket)#
WS /v1/realtime accepts a continuous audio feed rather than a finite
upload, so chunking is driven by speech activity instead of file boundaries:
Connect — The client opens the socket and receives
transcription_session.createddescribing the negotiated audio format (PCM16, mono, 16 kHz by default).Append — The client pushes base64 PCM16 frames via
input_audio_buffer.appendinto a server-side rolling buffer.Turn detection — An energy-based VAD watches the buffer and emits
speech_started/speech_stopped. A sustained silence run closes the utterance; clients may instead disable VAD and commit boundaries explicitly withinput_audio_buffer.commit.Transcription — Each committed utterance is written to a temporary WAV and pushed through the same ASR pipeline used by file ingestion, so backend, device, and sentiment configuration behave identically. Transcription is serialized per socket so results stay in order, and runs in a thread pool to keep the event loop responsive.
Emit — The service sends
conversation.item.input_audio_transcription.deltafollowed by.completedfor each utterance. Because the socket’s segments share one session, the session transcript accumulates across the whole connection.
Live ingestion deliberately does not capture from a local microphone: the service is containerized and has no reliable host audio device, so the client owns capture and the service owns transcription.
Components#
api/— FastAPI routers:openai_endpoints.py(OpenAI-compatible transcription and SSE),realtime_endpoints.py(live WebSocket streaming), andcustom_endpoints.py(health, devices, and VSS-compatible routes).pipeline.py— Orchestrates preprocessing, ASR, and sentiment.components/— Backend implementations for ASR and sentiment providers.utils/— Audio utilities (includingpcm_audio.pyfor PCM/VAD andsubtitle_format.pyfor SRT/VTT), config loading, and session helpers.dto/— Request and response data models.
Configuration Surface#
All runtime behavior is driven by config.yaml, shared by both standalone
and container runs, with targeted overrides via AUDIO_ANALYZER__...
environment variables. See the Configuration Guide for the
full list of fields.