Metadata-Version: 2.5
Name: tring
Version: 0.1.0
Summary: Open-source voice agent stack: one agent contract, any runtime (cascade / speech-to-speech / hybrid), local-first, cost-transparent.
Project-URL: Homepage, https://github.com/anzal1/tring
Author: Anzal Hussain Abidi
License-Expression: MIT
License-File: LICENSE
Keywords: agents,llm,speech,stt,telephony,tts,voice
Classifier: Development Status :: 3 - Alpha
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
Requires-Python: >=3.11
Requires-Dist: pydantic>=2.7
Requires-Dist: pyyaml>=6.0
Provides-Extra: cloud
Requires-Dist: httpx>=0.27; extra == 'cloud'
Requires-Dist: websockets>=12.0; extra == 'cloud'
Provides-Extra: dev
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Requires-Dist: types-pyyaml; extra == 'dev'
Provides-Extra: local
Requires-Dist: faster-whisper>=1.0; extra == 'local'
Requires-Dist: httpx>=0.27; extra == 'local'
Requires-Dist: kokoro-onnx>=0.4; extra == 'local'
Requires-Dist: numpy>=1.26; extra == 'local'
Provides-Extra: transports
Requires-Dist: websockets>=12.0; extra == 'transports'
Description-Content-Type: text/markdown

# Tring

**Open-source voice agent stack. One agent contract, any runtime, honest costs.**

Tring is the sound of an arriving call. It lets you define a voice agent once and run it on any of
three architectures — a cascaded pipeline (STT → LLM → TTS), a speech-to-speech
model, or a hybrid of the two — without rewriting anything. It ships with a
fully local, $0-per-minute pipeline as a first-class citizen, and it tells you
exactly what every call costs.

> Built from lessons learned running voice agents across hundreds of thousands
> of production telephony calls, in multiple languages, where callers
> code-switch mid-sentence and two seconds of silence means a hangup.

## Why Tring

- **One contract, three runtimes.** `AgentSpec` is plain YAML. Switching a
  customer from a cascade to speech-to-speech is a config change: same tools,
  same analytics, same cost attribution.
- **Local-first.** faster-whisper for STT, Ollama for the LLM, Kokoro for TTS.
  Production-tuned turn-taking and interruption handling with zero cloud
  dependencies. Cloud providers are opt-in upgrades.
- **Latency as correctness.** In voice, dead air is a conversational error.
  Tring's primitives make it structurally impossible rather than patching it:
  - *Tool-call choreography* — the model cannot emit a tool call without
    declaring what to say while it runs (`waiting_message`, `spoken_mode`,
    `post_tool_response` are schema-required fields).
  - *Speak-while-thinking* — a character-level streaming parser flushes the
    `speak` field to TTS while the tool call is still being generated.
  - *Interruption reconciliation* — TTS generates faster than audio plays; on
    a barge-in, Tring annotates context so the model never references words
    the caller never actually heard.
  - *Cache-safe language lock* — the language directive is re-asserted every
    turn without breaking prompt-cache byte-stability.
- **Honest cost accounting.** Every metered unit carries an `estimated` flag.
  Per-call, per-connected-minute, per-conversation, and per-outcome roll-ups,
  so you optimize the denominator instead of the rate.
- **Per-language provider routing.** Real callers code-switch. The best STT
  engine for one language is often the wrong one for another; Tring routes
  STT/TTS/LLM per agent *and* per detected language.

## Quickstart

```bash
pip install tring[local]
```

```yaml
# agent.yaml
name: front-desk
persona: |
  You are a friendly front-desk assistant for a dental clinic.
language:
  primary: en
runtime:
  mode: cascade
  routing:
    default: { stt: faster_whisper, llm: ollama, tts: kokoro }
```

```python
from tring import AgentSpec, CallSession
from tring.runtimes.cascade import CascadeRuntime

agent = AgentSpec.from_yaml("agent.yaml")
session = CallSession(agent)
runtime = CascadeRuntime(session)
```

See [`examples/`](examples/) for the full local quickstart, the console
dev loop, and cost reporting.

## Status

Early alpha. The core contracts, primitives, cost meter, and local cascade
runtime are usable; speech-to-speech and hybrid adapters, telephony ingress
(SIP), and the hosted dashboard are on the [roadmap](docs/ROADMAP.md).

## Architecture

See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).

## License

MIT © Anzal Hussain Abidi
