Architecture Handlers
A minimal, extensible Python library providing a base class (ArchitectureHandler) for building handler components that accept inputs, optionally perform network requests, and publish results via thread-safe queues. Ships with concrete handlers for AI/agent tasks: speech-to-text, text-to-speech, face recognition, gesture generation, LLM agent interaction, and Ollama chat.
Package: scs-architecture-handlers · Python: >= 3.10 · License: MIT
Table of Contents
- Features
- Installation
- Handlers Overview
- Design Decisions
- Usage
- Extension Points
- Configuration Reference
- Stats & Monitoring
- Development
- License
Features
- Threaded processing loop — consumes items from an input queue, processes them, and writes results to a result queue.
- Pluggable core logic — override
generate_results()or supplygenerate_results_callbackin config. - Template methods —
validate_item,preprocess_item,postprocess_resultfor easy adaptation. - Optional type checking —
expected_typecatches mismatched inputs at the boundary. - Dropping policy — bounded queues with configurable capacity; drops are counted and logged.
- Runtime instrumentation — timestamps and counters (processed/dropped/errors/network errors) via
stats(). - Async wrappers — all sync methods have
async_*counterparts (powered byasyncio.to_thread). - Hook methods — 9 instrumentation hooks (
_hook_before_feed,_hook_after_generate, etc.) for monitoring. - Non-blocking callbacks — dedicated callback worker thread so user callbacks never stall the hot path.
- Singleton sync core — async wrappers delegate to the same sync implementation; no code duplication.
Installation
From PyPI
pip install scs-architecture-handlers
from scs_architecture_handlers.base_handler import ArchitectureHandler
Development setup
# Clone the repository
git clone <repo-url>
cd architecture-handlers
# Create a virtual environment (Python >= 3.10)
python3.11 -m venv .venv
source .venv/bin/activate
# Install dependencies (using pip)
pip install -r requirements.txt
# Or using uv
uv sync
Handlers Overview
| Handler | File | Purpose |
|---|---|---|
ArchitectureHandler |
base_handler.py |
Extensible base class for all handlers. |
FaceRecognitionHandler |
face_recognition_handler.py |
Posts camera frames to a /detect FastAPI endpoint. Optional webcam auto-capture mode. |
GestureGenerationHandler |
gesture_generation_handler.py |
Sends audio chunks to a /generate gesture FastAPI for realtime beat-driven gesture synthesis. |
LLMAgentHandler |
llm_agent_handler.py |
Sends text or person data to an LLM agent FastAPI service. |
STTHandler |
stt_handler.py |
Speech-to-text via RealtimeSTT.AudioToTextRecorderClient (WebSocket). |
RealtimeSTTHandler (v2) |
stt_handler_v2.py |
Speech-to-text via WebRTC / aiortc with _AudioTrackProxy and _MicrophoneCapturer. |
F5TTSHandler |
f5tts_handler.py |
Text-to-speech via an F5-TTS FastAPI /synthesize endpoint. |
MaverickAgentHandler |
maverick_agent_handler.py |
Orchestrator: TTS + gesture generation + Unity visualizer streaming via WebSocket. |
OllamaChatAgent |
ollama_handler.py |
Stateful chat wrapper around ollama.Client with conversation history, save/load, and error types. |
Design Decisions
-
Single sync core, async wrappers on top. All async methods delegate to the sync implementation via
asyncio.to_thread. This means there is exactly one implementation of the processing logic — no duplicated sync/async code paths. -
Template Method pattern. The base class defines the skeleton of the feed → validate → preprocess → generate → postprocess → publish pipeline. Subclasses override only the steps they need (
validate_item,preprocess_item,generate_results,postprocess_result). -
Bounded queues with backpressure. Both input and result queues have configurable max sizes. When a queue is full, items are silently dropped and
dropped_countis incremented. This prevents unbounded memory growth under load. -
Dedicated async callback thread. External callbacks (e.g., database writes, network calls) can be scheduled via
_emit_async_callbackto run on a separate worker thread, keeping the main processing path fast and predictable. -
Hook-based instrumentation. All major lifecycle events fire hook methods. Subclasses and monitoring tools can override these hooks without modifying the core pipeline.
-
Dry-run mode in all network handlers. Every handler that performs network I/O supports a
dry_runflag that returns stub results without making HTTP calls. This makes testing and offline development straightforward. -
Weak reference finalizer. A
weakref.finalizeensures background threads are joined when the handler is garbage-collected, preventing orphaned threads.
Usage
1) Direct synchronous generation
Subclass ArchitectureHandler and override generate_results:
from scs_architecture_handlers.base_handler import ArchitectureHandler
class EchoHandler(ArchitectureHandler):
def generate_results(self, item, extra):
return {"echo": item, "meta": extra}
h = EchoHandler(run_as_thread=False)
out = h.generate("hello", tag=1)
print(out) # {"echo": "hello", "meta": {"tag": 1}}
h.cleanup()
2) Threaded pipeline
Use the internal worker thread to consume items from the input queue and push processed results to the result queue:
import time
from scs_architecture_handlers.base_handler import ArchitectureHandler
class Doubler(ArchitectureHandler):
def generate_results(self, item, extra):
return item * 2
h = Doubler(run_as_thread=True, disable_thread=False)
h.feed(10)
print(h.get_result(timeout=1.0)) # 20
h.cleanup()
3) Async wrappers
All public sync methods have async counterparts:
import asyncio
from scs_architecture_handlers.base_handler import ArchitectureHandler
class EchoHandler(ArchitectureHandler):
def generate_results(self, item, extra):
return {"echo": item}
async def main():
h = EchoHandler(run_as_thread=False)
out = await h.async_generate("async-hello")
print(out) # {"echo": "async-hello"}
await h.async_cleanup()
asyncio.run(main())
Available async wrappers: async_feed, async_get_result, async_generate, async_start, async_cleanup.
4) Callbacks & non-blocking execution
Schedule external callbacks on a dedicated thread via _emit_async_callback:
import time
from scs_architecture_handlers.base_handler import ArchitectureHandler
def on_result(item, extra):
time.sleep(0.1)
print("callback got:", item)
class NonBlockingHandler(ArchitectureHandler):
def generate_results(self, item, extra):
result = {"value": str(item).upper(), "extra": extra}
self._emit_async_callback(result, extra)
return result
h = NonBlockingHandler(
run_as_thread=False,
disable_thread=False,
generate_results_callback=on_result,
callback_queue_size=8,
)
for i in range(5):
out = h.generate(f"msg-{i}")
assert out["value"] == f"MSG-{i}"
time.sleep(0.5)
h.cleanup()
Notes:
- If the callback queue fills, new tasks are dropped and
dropped_countincrements. - For producer-style handlers (e.g., microphone/STT), use
_emit_async_callbackinside the production path.
Extension Points
Override these template methods to adapt the handler:
| Method | Purpose |
|---|---|
validate_item(item, extra) -> bool |
Return False to drop invalid items early (shape/content/type). |
preprocess_item(item, extra) -> (item, extra) |
Normalize or transform raw input before generate_results. |
generate_results(item, extra) -> Any |
Core computation (required — must override or supply callback). |
postprocess_result(item, extra, result) -> Any |
Final transformation (e.g., map indices to labels). |
_prepare_request_payload(item, extra) -> dict |
Build structured payload for network I/O. |
_perform_request(payload) -> Any |
Implement actual network request (override this, call via perform_request). |
Instrumentation hooks (optional overrides):
| Hook | When it fires |
|---|---|
_hook_before_feed / _hook_after_feed |
Before/after feed() |
_hook_before_generate / _hook_after_generate |
Before/after generate_results() |
_hook_before_network_call / _hook_after_network_call |
Before/after a network request |
_hook_after_result |
When a result is retrieved via get_result() |
_hook_on_error |
When generation raises an exception |
_hook_network_error |
When a network call fails |
Configuration Reference
| Key | Default | Description |
|---|---|---|
host |
None |
Remote host (optional). |
port |
None |
Remote port (optional). |
auth |
None |
Authentication token/credentials (optional). |
run_as_thread |
True |
Start internal worker threads. |
disable_thread |
False |
Force-disable threading even if run_as_thread is True. |
max_queue_size |
128 |
Capacity for the input queue. |
result_queue_size |
max_queue_size |
Capacity for the result queue. |
verbose |
False |
Enable INFO-level logging (default is WARNING). |
expected_type |
None |
Type or tuple for basic validation in validate_item. |
generate_results_callback |
None |
External callback alternative to subclassing generate_results. |
network_timeout |
None |
Optional timeout for network requests. |
client_id_prefix |
"client" |
Prefix for auto-generated client ID. |
callback_queue_size |
128 |
Capacity for the async callback queue (used with _emit_async_callback). |
Stats & Monitoring
stats = handler.stats()
print(stats)
# {
# "client_id": "client-...",
# "created_at": ...,
# "last_feed_at": ...,
# "last_generate_at": ...,
# "last_result_at": ...,
# "last_network_call_at": ...,
# "input_queue_size": 0,
# "result_queue_size": 0,
# "run_as_thread": True,
# "processed_count": 42,
# "dropped_count": 0,
# "error_count": 0,
# "network_error_count": 0,
# }
All counters are updated automatically by the default hook implementations. Override hooks for custom monitoring.
Development
Setup
python3.11 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
Running Tests
Tests use pytest with pytest-asyncio and pytest-timeout. They mock external services so no live servers are needed.
pytest -q
# or
pytest tests/
Individual test files:
| Test File | Tests For |
|---|---|
tests/test_base_handler.py |
Core ArchitectureHandler (init, feed, generate, threading, hooks, callbacks, cleanup, stats) |
tests/test_f5tts_handler.py |
F5TTSHandler |
tests/test_face_recognition_handler.py |
FaceRecognitionHandler |
tests/test_gesture_generation_handler.py |
GestureGenerationHandler |
tests/test_llm_agent_handler.py |
LLMAgentHandler |
tests/test_maverick_agent_handler.py |
MaverickAgentHandler |
tests/test_stt_handler.py |
STTHandler (v1 / RealtimeSTT) |
tests/test_realtime_stt_handler.py |
STTHandler (v2 / WebRTC) |
System Dependencies
On Debian/Ubuntu, you may need:
sudo apt-get update
sudo apt-get install -y portaudio19-dev
CI/CD & Releasing to PyPI
The project uses GitLab CI (.gitlab-ci.yml) with four stages:
- test — runs
pytest -qon all branches and merge requests. - secret-detection — GitLab Secret Detection template.
- build — builds sdist+wheel on version tags.
- deploy — publishes to PyPI via Twine on version tags.
To release a new version:
# 1. Update version in pyproject.toml
# 2. Commit and tag
git tag v0.2.6
git push origin v0.2.6
The deploy job verifies the tag matches pyproject.toml version automatically.
Prerequisites:
- Set a protected, masked CI variable
PYPI_TOKENwith your PyPI API token in GitLab → Settings → CI/CD → Variables. - Optionally override
PYPI_REPOSITORY_URLto point at TestPyPI for testing.
License
MIT
Metadata
Release files for scs-architecture-handlers 0.2.8
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| scs_architecture_handlers-0.2.8.tar.gz | 60.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| scs_architecture_handlers-0.2.8-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 113.0 kB
Release files / scs_architecture_handlers-0.2.8.tar.gz
| Download URL | scs_architecture_handlers-0.2.8.tar.gz |
|---|---|
| Size | 60.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
87d5680ce727a77c2708beaa165c4493f681b23e28ba9e0777064a74c866168a
|
|
BLAKE2b-256 checksum How to use checksums |
bf2d253b8feb2ed99782fcbf364bc9ea11e755bcff0b053621c024caa50e0327
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.15
|
Release files / scs_architecture_handlers-0.2.8-py3-none-any.whl
| Download URL | scs_architecture_handlers-0.2.8-py3-none-any.whl |
|---|---|
| Size | 53.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
909fc287ff36308e10bf3a741773beeb8aab87f34f5e26aaf634895cff84224e
|
|
BLAKE2b-256 checksum How to use checksums |
a02c34f2029d41860d2e3d5c1d677b7bc30f9d39034f5bfae09809623d385471
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.15
|