Metadata-Version: 2.4
Name: VoIP
Version: 0.2.3
Summary: Python asyncio library for VoIP calls.
Keywords: VoIP,SIP,RTP,WebRTC,STUN,asyncio
Author-email: Johannes Maron <johannes@maron.family>
Requires-Python: >=3.13
Description-Content-Type: text/markdown
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Information Technology
Classifier: Intended Audience :: Telecommunications Industry
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Framework :: AsyncIO
Classifier: Topic :: Communications :: Internet Phone
Classifier: Topic :: Communications :: Telephony
Classifier: Topic :: Multimedia :: Sound/Audio
Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: System :: Networking
Classifier: Topic :: Home Automation
License-File: LICENSE
Requires-Dist: cryptography
Requires-Dist: faster-whisper ; extra == "ai"
Requires-Dist: numpy ; extra == "ai"
Requires-Dist: av ; extra == "ai"
Requires-Dist: ollama ; extra == "ai"
Requires-Dist: pocket-tts ; extra == "ai"
Requires-Dist: numpy ; extra == "audio"
Requires-Dist: click ; extra == "cli"
Requires-Dist: pygments ; extra == "cli"
Requires-Dist: faster-whisper ; extra == "cli"
Requires-Dist: numpy ; extra == "cli"
Requires-Dist: ollama ; extra == "cli"
Requires-Dist: pocket-tts ; extra == "cli"
Requires-Dist: numpy ; extra == "hd-audio"
Requires-Dist: av ; extra == "hd-audio"
Requires-Dist: Pygments ; extra == "pygments"
Project-URL: Changelog, https://github.com/codingjoe/VoIP/releases
Project-URL: Documentation, https://codingjoe.dev/VoIP/
Project-URL: Funding, https://github.com/sponsors/codingjoe
Project-URL: Homepage, https://github.com/codingjoe/VoIP
Project-URL: Issues, https://github.com/codingjoe/VoIP/issues
Project-URL: Releasenotes, https://github.com/codingjoe/VoIP/releases/latest
Project-URL: Source, https://github.com/codingjoe/VoIP
Provides-Extra: ai
Provides-Extra: audio
Provides-Extra: cli
Provides-Extra: hd-audio
Provides-Extra: pygments

<p align="center">
  <picture>
    <source media="(prefers-color-scheme: dark)" srcset="https://github.com/codingjoe/VoIP/raw/main/docs/images/logo-dark.svg">
    <source media="(prefers-color-scheme: light)" srcset="https://github.com/codingjoe/VoIP/raw/main/docs/images/logo-light.svg">
    <img alt="Python VoIP" src="https://github.com/codingjoe/VoIP/raw/main/docs/images/logo-light.svg">
  </picture>
<br>
  <a href="https://codingjoe.dev/VoIP">Documentation</a> |
  <a href="https://github.com/codingjoe/VoIP/issues/new/choose">Issues</a> |
  <a href="https://github.com/codingjoe/VoIP/releases">Changelog</a> |
  <a href="https://github.com/sponsors/codingjoe">Funding</a> ♥
</p>

# Python VoIP

Async VoIP Python library for the AI age.

> [!WARNING]
> This library is in early development and may contain breaking changes. Use with caution.

## Usage

### CLI

Answer calls and transcribe them live from the terminal:

```console
SIP_PASSWORD=******** uvx 'voip[cli]' sip sips:alice@sip.example.com transcribe
```

A simple echo server can be started with:

````console
```console
SIP_PASSWORD=******** uvx 'voip[cli]' sip sips:alice@sip.example.com echo
````

You can also talk to a local agent (needs [Ollama]):

```console
SIP_PASSWORD=******** uvx 'voip[cli]' sip sips:alice@sip.example.com agent
```

### Python API

```console
uv add voip[audio,ai,pygments]
```

Subclass `TranscribeCall` and override `transcription_received` to handle results.
Pass it as `call_class` when answering an incoming call:

```python
import asyncio
import ssl
from voip.ai import TranscribeCall
from voip.sip.protocol import SIP


class MyCall(TranscribeCall):
    def transcription_received(self, text: str) -> None:
        print(f"[{self.caller}] {text}")


class MySession(SIP):
    def call_received(self, request) -> None:
        asyncio.create_task(self.answer(request=request, call_class=MyCall))


async def main():
    loop = asyncio.get_running_loop()
    ssl_context = ssl.create_default_context()
    await loop.create_connection(
        lambda: MySession(
            aor="sips:alice@example.com",
            username="alice",
            password="secret",
        ),
        host="sip.example.com",
        port=5061,
        ssl=ssl_context,
    )
    await asyncio.Future()


asyncio.run(main())
```

For raw audio access without transcription, subclass `AudioCall` and override
`audio_received(self, audio: np.ndarray)` instead.

[ollama]: https://ollama.com/

