livekit-piopiy
Piopiy telephony for LiveKit Agents. Your LiveKit agent takes real phone calls on the Piopiy platform by TeleCMI: calls the Piopiy API places, calls arriving on your numbers, and calls arriving from your PBX over SIP Connect. Mid-call it can transfer the caller to a human, warm or blind, and hang up.
Piopiy bridges each call into a LiveKit room and hands your worker the room
and a per-call token. Your AgentSession runs in that room; this package
supplies the rest: taking the call, knowing who is calling, acting on the
call, and hearing about transfers.
Tested with livekit-agents v1.7.1. Maintained by TeleCMI.
Install
pip install livekit-piopiy
# for the example, with Deepgram + OpenAI + Silero VAD:
pip install "livekit-piopiy[example]"
Python 3.10+. Depends on livekit-agents and piopiy-agent, the
framework-agnostic Piopiy worker SDK.
Use with an AgentSession
from livekit.agents import Agent, AgentSession
from livekit.plugins import deepgram, openai, silero
from livekit_piopiy import PiopiyRunner, PiopiyCallControl, piopiy_tools, attach_transfer_status
async def bot(room, call):
control = PiopiyCallControl(call)
tools = piopiy_tools(control, transfer_number="919876543210",
transfer_caller_id="911203134087")
agent = Agent(instructions=PROMPT, tools=tools.tools)
session = AgentSession(stt=deepgram.STT(model="nova-3", language="en"),
llm=openai.LLM(model="gpt-4o-mini"),
tts=deepgram.TTS(), vad=silero.VAD.load())
attach_transfer_status(call, session) # narrates transfer progress
await session.start(agent=agent, room=room)
session.generate_reply(instructions="Greet the caller.")
await call.wait_for_end()
await session.aclose()
PiopiyRunner().run(bot)
PiopiyRunner connects to Piopiy as a worker for your agent. For every call
it connects an rtc.Room with the token the platform minted, accepts the
call (which is when the caller is bridged in), and calls bot(room, call).
The room is disconnected when bot returns; call.wait_for_end() returns
when the caller leaves.
This is the "bring your own worker" model: your process runs anywhere with
outbound internet, no LiveKit Cloud agent dispatch is involved, and the
livekit-agents CLI worker is not used.
Run the example
cd examples/foundational
cp .env.example .env # agent id, token, API base, transfer number, keys
python 01_piopiy_agent.py
Then call your agent: place a call with POST /v3/voice/ai/call, ring one of
your numbers mapped to the agent, or dial the agent id from a PBX registered
over SIP Connect. Ask for a person to see a warm transfer, ask for "the billing
line" to see a blind transfer, and say goodbye to see it hang up.
Configuration
| variable | what |
|---|---|
PIOPIY_AGENT_ID |
the agent this worker serves, from the dashboard |
PIOPIY_TOKEN |
the Bearer token, the same one that creates calls |
PIOPIY_API_URL |
the platform's /v3 base URL, as given in your account |
PIOPIY_REGISTER |
optional; host:port of the worker register |
PIOPIY_TLS |
optional; false to talk to the register without TLS (development) |
PIOPIY_MAX_SESSIONS |
calls one process handles at once |
What the package gives you
PiopiyCall - call_id, direction, from_number, to_number,
agent_id, variables, sip_account_id on SIP Connect calls, and
wait_for_end().
PiopiyCallControl (from piopiy-agent) - actions on the live call:
result = await control.warm_transfer(to_number="9198...", transfer_summary="Refund on order A-1042")
result = await control.blind_transfer(to_number="9198...", caller_id="9112...")
await control.hangup(reason="resolved")
verdict = await control.wait_for_transfer(result.request_id) # queued -> completed | failed
A warm transfer rings the human while the caller stays with the agent; on
answer the caller is handed over and the agent leaves; if nobody answers the
conversation continues. A blind transfer hands the caller over at once. One
transfer at a time per call: a second is refused with
PiopiyAPIError(409, "transfer_in_progress").
piopiy_tools() - transfer_call and end_call as LiveKit function
tools (tools.tools). The destination is fixed in code; pass
transfer_caller_id for the DID to present, which SIP Connect calls require.
attach_transfer_status() - the platform pushes every transfer's progress
into the room as transfer_status data messages. This speaks "I'm connecting
you now" as the target rings, an apology if it fails, and adds a note to the
agent's chat context so the model carries on. Pass narrate=False and
on_status= to handle it yourself.
Notes
- Every action uses the call's customer leg, which
PiopiyCall.call_idis. - SIP Connect calls consume no phone number, so a transfer to a phone from one
needs
transfer_caller_id(a DID you own). - Accept timing is handled for you: the runner accepts right after the room connects; a join past the platform's deadline leaves the room and does not run the bot, so two agents never share a call.
- The Pipecat equivalent is
pipecat-piopiy.
License
MIT. Copyright TeleCMI.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file livekit_piopiy-0.1.0.tar.gz.
File metadata
- Download URL: livekit_piopiy-0.1.0.tar.gz
- Upload date:
- Size: 15.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.12.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
410394f44e9b3d9d00096ae98237ed1dd806c2e9beefcb8e006072b626b412bf
|
|
| MD5 |
cc1fca1898ed0dfdf2aa7ee365203fcc
|
|
| BLAKE2b-256 |
74b4307824b828fec079beecc4c5bc1dc3de0adee2da271f2d54985f2684928a
|
File details
Details for the file livekit_piopiy-0.1.0-py3-none-any.whl.
File metadata
- Download URL: livekit_piopiy-0.1.0-py3-none-any.whl
- Upload date:
- Size: 13.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.12.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d5ecf1154063a6456f8ee5234c77c1c3278cb6b901888a05cfc99c8d847d6efc
|
|
| MD5 |
e585e04312269b499ae54c33e5101cd3
|
|
| BLAKE2b-256 |
44112ae13cfb018431cde903a3f6ecf55db9077944a25d8313abf06bf220cb6b
|