Files
Timmy-time-dashboard/pyproject.toml
Alexander Whitestone 317669a21e
Some checks failed
Tests / lint (pull_request) Failing after 32s
Tests / test (pull_request) Has been skipped
feat: add Mumble voice bridge for Alexander–Timmy co-play audio (#858)
Implements a bidirectional Mumble voice bridge so Alexander and Timmy can
have voice conversations during co-play sessions, with audio piped to the
stream.

Changes:
- src/integrations/mumble/bridge.py — MumbleBridge class with:
  - Connect/disconnect lifecycle (graceful degradation when pymumble-py3 absent)
  - speak(text) — converts Timmy's TTS output to PCM and sends to Mumble
  - send_audio(pcm) — raw 16-bit 48 kHz mono PCM transmit
  - push_to_talk() context manager for PTT mode
  - VAD mode (voice activity detection with configurable RMS threshold)
  - on_audio callbacks for audio received from Alexander's mic
  - Pure-Python resampling / stereo-to-mono (no audioop, Python 3.13+ compat)
  - Piper TTS → pyttsx3 TTS fallback chain
- src/config.py — 9 new Mumble settings (host, port, user, password,
  channel, audio_mode, vad_threshold, silence_ms, enabled)
- pyproject.toml — pymumble-py3 optional dep + mumble extras group
- docker-compose.yml — mumblevoip/mumble-server under mumble profile
  (ports 64738 TCP+UDP; start with: docker compose --profile mumble up)
- tests/integrations/test_mumble_bridge.py — 39 unit tests
- src/integrations/CLAUDE.md — document mumble module

Fixes #858

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 22:18:01 -04:00

170 lines
5.4 KiB
TOML

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
[tool.poetry]
name = "timmy-time"
version = "1.0.0"
description = "Mission Control for sovereign AI agents"
readme = "README.md"
license = "MIT"
authors = ["Alexander Whitestone"]
homepage = "http://localhost:3000/rockachopa/Timmy-time-dashboard"
repository = "http://localhost:3000/rockachopa/Timmy-time-dashboard"
packages = [
{ include = "config.py", from = "src" },
{ include = "bannerlord", from = "src" },
{ include = "brain", from = "src" },
{ include = "dashboard", from = "src" },
{ include = "infrastructure", from = "src" },
{ include = "integrations", from = "src" },
{ include = "spark", from = "src" },
{ include = "timmy", from = "src" },
{ include = "timmy_serve", from = "src" },
{ include = "timmyctl", from = "src" },
]
[tool.poetry.dependencies]
python = ">=3.11,<4"
agno = { version = ">=2.0.0,<3.0", extras = ["sqlite"] }
ollama = ">=0.3.0,<1.0"
openai = ">=1.0.0"
fastapi = ">=0.115.0,<1.0"
uvicorn = { version = ">=0.32.0,<1.0", extras = ["standard"] }
jinja2 = ">=3.1.0"
httpx = ">=0.27.0"
python-multipart = ">=0.0.12"
typer = ">=0.12.0"
rich = ">=13.0.0"
pydantic-settings = ">=2.0.0,<3.0"
mcp = ">=1.0.0"
# Optional extras
redis = { version = ">=5.0.0", optional = true }
celery = { version = ">=5.3.0", extras = ["redis"], optional = true }
python-telegram-bot = { version = ">=21.0", optional = true }
"discord.py" = { version = ">=2.3.0", optional = true }
airllm = { version = ">=2.9.0", optional = true }
pyttsx3 = { version = ">=2.90", optional = true }
openai-whisper = { version = ">=20231117", optional = true }
piper-tts = { version = ">=1.2.0", optional = true }
sounddevice = { version = ">=0.4.6", optional = true }
pymumble-py3 = { version = ">=1.0", optional = true }
sentence-transformers = { version = ">=2.0.0", optional = true }
numpy = { version = ">=1.24.0", optional = true }
requests = { version = ">=2.31.0", optional = true }
trafilatura = { version = ">=1.6.0", optional = true }
GitPython = { version = ">=3.1.40", optional = true }
pytest = { version = ">=8.0.0", optional = true }
pytest-asyncio = { version = ">=0.24.0", optional = true }
pytest-cov = { version = ">=5.0.0", optional = true }
pytest-timeout = { version = ">=2.3.0", optional = true }
selenium = { version = ">=4.20.0", optional = true }
pytest-randomly = { version = ">=3.16.0", optional = true }
pytest-xdist = { version = ">=3.5.0", optional = true }
anthropic = "^0.86.0"
opencv-python = "^4.13.0.92"
[tool.poetry.extras]
telegram = ["python-telegram-bot"]
discord = ["discord.py"]
bigbrain = ["airllm"]
voice = ["pyttsx3", "openai-whisper", "piper-tts", "sounddevice"]
mumble = ["pymumble-py3"]
celery = ["celery"]
embeddings = ["sentence-transformers", "numpy"]
git = ["GitPython"]
research = ["requests", "trafilatura", "google-search-results"]
dev = ["pytest", "pytest-asyncio", "pytest-cov", "pytest-timeout", "pytest-randomly", "pytest-xdist", "selenium"]
[tool.poetry.group.dev.dependencies]
pytest = ">=8.0.0"
pytest-asyncio = ">=0.24.0"
pytest-cov = ">=5.0.0"
pytest-timeout = ">=2.3.0"
selenium = ">=4.20.0"
pytest-randomly = "^4.0.1"
pytest-xdist = "^3.8.0"
ruff = ">=0.8.0"
mypy = ">=1.0.0"
[tool.poetry.scripts]
timmy = "timmy.cli:main"
timmy-serve = "timmy_serve.cli:main"
timmyctl = "timmyctl.cli:main"
[tool.pytest.ini_options]
testpaths = ["tests"]
pythonpath = ["src", "tests"]
asyncio_mode = "auto"
asyncio_default_fixture_loop_scope = "function"
timeout = 30
timeout_method = "signal"
timeout_func_only = false
addopts = "-v --tb=short --strict-markers --disable-warnings --durations=10 --cov-fail-under=60"
markers = [
"unit: Unit tests (fast, no I/O)",
"integration: Integration tests (may use SQLite)",
"functional: Functional tests (real HTTP requests, no mocking)",
"e2e: End-to-end tests (full system, may be slow)",
"dashboard: Dashboard route tests",
"slow: Tests that take >1 second",
"selenium: Requires Selenium and Chrome (browser automation)",
"docker: Requires Docker and docker-compose",
"ollama: Requires Ollama service running",
"external_api: Requires external API access",
"skip_ci: Skip in CI environment (local development only)",
]
[tool.ruff]
line-length = 100
target-version = "py311"
src = ["src", "tests"]
[tool.ruff.lint]
select = ["E", "F", "I", "UP", "B", "S"]
ignore = [
# Mapped from existing bandit skips: B101→S101, B104→S104, etc.
"S101", "S104", "S307", "S310", "S324", "S601", "S608",
# Project patterns: graceful degradation (try/except pass), FastAPI Depends()
"S110", "S112", "B008",
# Subprocess usage in scripts/infrastructure
"S603", "S607",
# Non-cryptographic random is fine for non-security contexts
"S311",
# Line length handled by formatter; long strings/URLs can't always be broken
"E501",
]
[tool.ruff.lint.isort]
known-first-party = ["config", "dashboard", "infrastructure", "integrations", "spark", "timmy", "timmy_serve"]
[tool.ruff.lint.per-file-ignores]
"tests/**" = ["S"]
[tool.coverage.run]
source = ["src"]
omit = [
"*/tests/*",
]
[tool.coverage.report]
show_missing = true
skip_empty = true
precision = 1
exclude_lines = [
"pragma: no cover",
"if __name__ == .__main__.",
"if TYPE_CHECKING:",
"raise NotImplementedError",
"@abstractmethod",
]
# Fail CI if coverage drops below this threshold
fail_under = 73
[tool.coverage.html]
directory = "htmlcov"
[tool.coverage.xml]
output = "coverage.xml"