Skip to content

codingjoe/VoIP

Python VoIP
Documentation | Issues | Changelog | Funding

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.

Sponsors

Sponsors

Usage

To get started, you will need a SIP account. One is usually included with ISP. Check your ISP's documentation or router for details.

You will need a SIP AOR (URI), which looks like this:

sip:USER:PASSWORD@SIP_SERVER

Note

This library defaults to UDP transport on port 5060 for sip: URIs, which is the most widely supported configuration. To use TCP or TLS, add an explicit transport parameter, e.g. sip:user@host;transport=TCP or sips:user@host (SIPS always uses TLS on port 5061).

CLI

A simple echo call can be started with:

uvx 'voip[cli]' sip sip:alice:********@sip.example.com echo

Each command supports an optional --dial argument to initiate an outbound call instead of waiting for an inbound one.

To dial a number, say a message, and hang up automatically:

uvx 'voip[cli]' sip sip:alice:********@sip.example.com say sip:+15551234567@sip.example.com "Your package has arrived."

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

uvx 'voip[cli]' sip sip:alice:********@sip.example.com agent --initial-prompt "Hi, I am looking for a Mr. Ron, first name Mo?"

MCP

The voip package ships a ready-made Model Context Protocol (MCP) server that exposes tools to make phone calls on your behalf to any MCP client.

{
  "mcpServers": {
    "VoIP": {
      "type": "stdio",
      "command": "uvx",
      "args": [
        "voip[mcp]",
        "mcp"
      ],
      "env": {
        "SIP_AOR": "sip:number:password@example.com"
      }
    }
  }
}

Python API

uv add voip[audio,ai,pygments]

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

import asyncio
import dataclasses

from voip.ai import TranscribeCall
from voip.sip.protocol import SIP
from voip.sip.types import SipURI
from voip.sip.transactions import InviteTransaction
from voip.rtp import RealtimeTransportProtocol
from faster_whisper import WhisperModel


@dataclasses.dataclass(kw_only=True, slots=True)
class TranscribingCall(TranscribeCall):
    def transcription_received(self, text) -> None:
        print(text)


class TranscribeInviteTransaction(InviteTransaction):
    def invite_received(self, request) -> None:
        self.ringing()
        self.answer(
            session_class=TranscribingCall,
            stt_model=WhisperModel("kyutai/stt-1b-en_fr-trfs", device="cuda"),
        )


async def main():
    loop = asyncio.get_running_loop()
    _, rtp_protocol = await loop.create_datagram_endpoint(
        RealtimeTransportProtocol,
        local_addr=("0.0.0.0", 0),
    )
    await loop.create_connection(
        lambda: SIP(
            rtp=rtp_protocol,
            aor=SipURI.parse("sip:alice:********@example.com"),
            transaction_class=TranscribeInviteTransaction,
        ),
        host="sip.example.com",
        port=5060,
    )
    await asyncio.Future()


asyncio.run(main())

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

About

Python VoIP library of the AI age

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Contributors

Languages