codingjoe/VoIP:面向 AI 时代的异步 VoIP Python 库
codingjoe/VoIP
codingjoe/VoIP 是一个面向 AI 时代的异步 VoIP Python 库,目前处于早期开发阶段,可能存在破坏性变更。它内置 MCP server,可让任意 MCP 客户端代为拨打电话,并支持通过 Ollama 与本地 agent 对话。
Documentation |
Issues |
Changelog |
Funding ♥
Async VoIP Python library for the AI age.
Warning
This library is in early development and may contain breaking changes. Use with caution.
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 echoEach 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.
来源:GitHub · github.com