pipecat-piopiy
Give your Pipecat agent a phone.
Piopiy is a communication platform: we provide the phone numbers, carry the calls, and host all the voice infrastructure. You build the agent with Pipecat and run it wherever you like; this package connects the two. Your agent answers calls to your Piopiy numbers, places calls through the Piopiy API, and mid-call it can hand the caller to a human or end the call. Nothing telephony-related to host or configure on your side.
Tested with Pipecat v1.8.1. Community-maintained by TeleCMI; not part of the Pipecat core.
Install
pip install pipecat-piopiy
# for the example, with Deepgram + OpenAI + Silero VAD:
pip install "pipecat-piopiy[example]"
Python 3.10+.
Use with a pipeline
from pipecat_piopiy import PiopiyRunner, PiopiyCallControl, piopiy_tools
from pipecat_piopiy.processors import PiopiyEventsProcessor
async def bot(transport, call):
control = PiopiyCallControl(call)
tools = piopiy_tools(control, transfer_number="919876543210",
transfer_caller_id="911203134087")
context = LLMContext(messages=[{"role": "system", "content": PROMPT}],
tools=tools.schemas)
aggregators = LLMContextAggregatorPair(
context, user_params=LLMUserAggregatorParams(vad_analyzer=SileroVADAnalyzer()))
pipeline = Pipeline([
transport.input(),
PiopiyEventsProcessor(call), # transfer progress -> frames + narration
stt, aggregators.user(), llm, tts,
transport.output(), aggregators.assistant(),
])
task = PipelineTask(pipeline)
@transport.event_handler("on_first_participant_joined")
async def on_caller_joined(_t, participant_id):
await task.queue_frames([LLMRunFrame()])
@transport.event_handler("on_participant_left")
async def on_caller_left(_t, participant_id, reason):
await task.cancel()
await PipelineRunner(handle_sigint=False).run(task)
PiopiyRunner().run(bot)
PiopiyRunner registers your process with Piopiy as the worker for your
agent. For every call, Piopiy hands it the call; the runner builds a
transport already connected to that call's audio and calls bot. The
caller is joined the moment the transport is connected. Use
PipelineRunner(handle_sigint=False): the runner owns the process signals.
Run the example
cd examples/foundational
cp .env.example .env # your agent id and token, the transfer number, your keys
python 01_piopiy_agent.py
Then call your agent: ring one of your Piopiy numbers, or place a call with
POST /v3/voice/agent/call. 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
Two values, both from your Piopiy dashboard:
| variable | what |
|---|---|
PIOPIY_AGENT_ID |
the agent this process serves |
PIOPIY_TOKEN |
your API token |
Optional: PIOPIY_MAX_SESSIONS (calls one process handles at once, default
10). PIOPIY_API_URL and PIOPIY_REGISTER exist only for regional or
private deployments; the public platform needs neither.
What the package gives you
PiopiyCall - the call in hand: call_id, direction, from_number,
to_number, agent_id, the variables you attached when placing the call,
and sip_account_id when the call came from a phone system you connected
to Piopiy, so one agent can tell your sites apart.
PiopiyCallControl - act on the 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 in conversation
with the agent; when the human answers the caller is handed over and the
agent leaves; if nobody answers the conversation simply continues. A
blind transfer hands the caller over at once. One transfer at a time per
call: a second one is refused with PiopiyAPIError(409, "transfer_in_progress").
piopiy_tools() - transfer_call and end_call as LLM function calls.
Each FunctionSchema carries its handler, so advertising tools.schemas on
the LLMContext is all the wiring. The destination is fixed in your code by
default so a caller cannot talk the agent into dialling anywhere else; pass
allow_model_destination=True to let the model choose. transfer_caller_id
is the number shown to the human being called; use one of your Piopiy
numbers.
PiopiyEventsProcessor - Piopiy tells the agent how a transfer is going.
The processor turns each update into a PiopiyTransferStatusFrame
(started, failed with a reason, completed) and, by default, speaks it:
"I'm connecting you now" as the human's phone rings, an apology if nobody
answers, plus a note into the LLM context so the model carries on sensibly.
Pass narrate=False to handle the frames yourself.
Notes
- Every action uses
PiopiyCall.call_id; the runner gives you the right one. - Accept timing is handled for you: if your process was too slow to join a call, the bot is cancelled so two agents never share one call.
- Pipecat changes quickly. This release is tested against v1.8.1; the pinned
range in
pyproject.tomlis>=1.8,<2.
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 pipecat_piopiy-0.2.2.tar.gz.
File metadata
- Download URL: pipecat_piopiy-0.2.2.tar.gz
- Upload date:
- Size: 18.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.12.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8eebdea31c3815111e49a9e2af437e418f0b505b14e11b1e5eb6bb8ef6151b76
|
|
| MD5 |
266fa5f1677b1ea45130f98afcca7787
|
|
| BLAKE2b-256 |
c4b1bcc028be029e761708f8f77e5184297f76dd37cce6326f287af7c61b9da8
|
File details
Details for the file pipecat_piopiy-0.2.2-py3-none-any.whl.
File metadata
- Download URL: pipecat_piopiy-0.2.2-py3-none-any.whl
- Upload date:
- Size: 15.7 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 |
b566dcc6b86c7dded22a3e9eda182064eafe450c0cce88cab046f2c371ce1fb3
|
|
| MD5 |
15763355d15459879126ac38532eeec5
|
|
| BLAKE2b-256 |
cf39060aa4abe0399782983096985dbc756f080522f434ffed38a9ad46694064
|