Compare commits

...

13 Commits

Author SHA1 Message Date
James Hush
b6da5c18b7 Add changelog for #4389 2026-04-30 14:38:30 +08:00
James Hush
4b6881b81d fix(aws): surface fatal errors on missing/invalid credentials
AWS services were silently failing on bad credentials. Nova Sonic was the
worst offender: no audio, no clear log, and an InvalidStateError from
awscrt at shutdown that masked the real cause.

Changes:
- Nova Sonic: connect failure now pushes a fatal ErrorFrame with a
  "check AWS credentials and region" hint. _disconnect wraps stream and
  session-end cleanup so a partially-initialized stream no longer raises
  InvalidStateError on top of the real error.
- Bedrock LLM and Polly TTS: branch on botocore ClientError. Auth-class
  codes (UnrecognizedClientException, InvalidSignatureException,
  AccessDeniedException, ExpiredTokenException, InvalidAccessKeyId,
  SignatureDoesNotMatch, MissingAuthenticationTokenException, AuthFailure)
  push fatal errors. Other client errors stay non-fatal (transient).
- Transcribe STT: _connect_websocket catch-all is now fatal, since
  presigned URL and websocket connect failures don't recover on retry.
2026-04-30 14:36:44 +08:00
Mark Backman
bfdd19464f Merge pull request #4385 from pipecat-ai/mb/runner-session-id
feat(runner): add session_id to RunnerArguments
2026-04-29 13:17:47 -04:00
Mark Backman
1a93ff52f1 Merge pull request #4386 from pipecat-ai/mb/update-soniox-model
feat(soniox): update default TTS model to tts-rt-v1
2026-04-29 13:17:09 -04:00
Mark Backman
6e2008a7a6 Add changelog for #4386 2026-04-29 11:09:38 -04:00
Mark Backman
da8d3a2d80 feat(soniox): update default TTS model to tts-rt-v1
Promotes the Soniox TTS default model from `tts-rt-v1-preview` to the
generally available `tts-rt-v1`.
2026-04-29 11:05:12 -04:00
Mark Backman
6b608e7e22 Add changelog for #4385 2026-04-29 09:53:42 -04:00
Mark Backman
924b9a9d8c feat(runner): add session_id to RunnerArguments
Adds a `session_id: str | None` field to `RunnerArguments` so bots can
log/trace a per-session identifier in local development the same way
they can in Pipecat Cloud (where it is provided via the
`x-daily-session-id` header).

The local runner now mints a UUID at every `*RunnerArguments`
construction site. For paths that already returned a `sessionId` to the
caller (Daily `/start`, dial-in webhook), a single UUID is now generated
and shared between `runner_args.session_id` and the response body
instead of being thrown away. The SmallWebRTC `/api/offer` endpoint
accepts an optional `session_id` so the `/sessions/{session_id}/...`
proxy can thread it through.

This is the prerequisite step for collapsing pipecat-cloud's
`SessionArguments` / `*SessionArguments` hierarchy onto the upstream
runner types.
2026-04-29 09:45:55 -04:00
Aleix Conchillo Flaqué
9411c4b67e Merge pull request #4382 from pipecat-ai/aleix/unfill-changelog-script
chore(changelog): add release-changelog.py and fix (PR line indentation in towncrier template
2026-04-28 13:18:49 -07:00
Mark Backman
ac5eb97670 Merge pull request #4384 from pipecat-ai/mb/nvidia-remove-riva-ref
Update README to remove NVIDIA references to RIVA
2026-04-28 13:18:36 -04:00
Mark Backman
3034f8bb3b Update README to remove NVIDIA references to RIVA 2026-04-28 12:42:58 -04:00
Aleix Conchillo Flaqué
60c66eda48 chore(towncrier): indent (PR ref line by two spaces in template
So the rendered changelog has the (PR [...]) line aligned as a list
continuation under its bullet. Verified with both short and wrapped
entries via `towncrier build --draft`.
2026-04-27 15:07:53 -07:00
Aleix Conchillo Flaqué
ea3585146c chore(scripts): add release-changelog.py
Adds a script to unfill (single-line) entry paragraphs in CHANGELOG.md
while keeping `(PR [...])` on its own continuation line.
2026-04-27 15:07:53 -07:00
13 changed files with 275 additions and 40 deletions

View File

@@ -89,20 +89,20 @@ Catch new features, interviews, and how-tos on our [Pipecat TV](https://www.yout
## 🧩 Available services
| Category | Services |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Speech-to-Text | [AssemblyAI](https://docs.pipecat.ai/api-reference/server/services/stt/assemblyai), [AWS](https://docs.pipecat.ai/api-reference/server/services/stt/aws), [Azure](https://docs.pipecat.ai/api-reference/server/services/stt/azure), [Cartesia](https://docs.pipecat.ai/api-reference/server/services/stt/cartesia), [Deepgram](https://docs.pipecat.ai/api-reference/server/services/stt/deepgram), [ElevenLabs](https://docs.pipecat.ai/api-reference/server/services/stt/elevenlabs), [Fal Wizper](https://docs.pipecat.ai/api-reference/server/services/stt/fal), [Gladia](https://docs.pipecat.ai/api-reference/server/services/stt/gladia), [Google](https://docs.pipecat.ai/api-reference/server/services/stt/google), [Gradium](https://docs.pipecat.ai/api-reference/server/services/stt/gradium), [Groq (Whisper)](https://docs.pipecat.ai/api-reference/server/services/stt/groq), [Mistral](https://docs.pipecat.ai/api-reference/server/services/stt/mistral), [NVIDIA Riva](https://docs.pipecat.ai/api-reference/server/services/stt/riva), [OpenAI (Whisper)](https://docs.pipecat.ai/api-reference/server/services/stt/openai), [Sarvam](https://docs.pipecat.ai/api-reference/server/services/stt/sarvam), [Soniox](https://docs.pipecat.ai/api-reference/server/services/stt/soniox), [Speechmatics](https://docs.pipecat.ai/api-reference/server/services/stt/speechmatics), [Whisper](https://docs.pipecat.ai/api-reference/server/services/stt/whisper), [xAI](https://docs.pipecat.ai/api-reference/server/services/stt/xai) |
| LLMs | [Anthropic](https://docs.pipecat.ai/api-reference/server/services/llm/anthropic), [AWS](https://docs.pipecat.ai/api-reference/server/services/llm/aws), [Azure](https://docs.pipecat.ai/api-reference/server/services/llm/azure), [Cerebras](https://docs.pipecat.ai/api-reference/server/services/llm/cerebras), [DeepSeek](https://docs.pipecat.ai/api-reference/server/services/llm/deepseek), [Fireworks AI](https://docs.pipecat.ai/api-reference/server/services/llm/fireworks), [Gemini](https://docs.pipecat.ai/api-reference/server/services/llm/gemini), [Grok](https://docs.pipecat.ai/api-reference/server/services/llm/grok), [Groq](https://docs.pipecat.ai/api-reference/server/services/llm/groq), [Mistral](https://docs.pipecat.ai/api-reference/server/services/llm/mistral), [Nebius](https://docs.pipecat.ai/api-reference/server/services/llm/nebius), [Novita](https://docs.pipecat.ai/api-reference/server/services/llm/novita), [NVIDIA NIM](https://docs.pipecat.ai/api-reference/server/services/llm/nvidia), [Ollama](https://docs.pipecat.ai/api-reference/server/services/llm/ollama), [OpenAI](https://docs.pipecat.ai/api-reference/server/services/llm/openai), [OpenAI Responses](https://docs.pipecat.ai/api-reference/server/services/llm/openai-responses), [OpenRouter](https://docs.pipecat.ai/api-reference/server/services/llm/openrouter), [Perplexity](https://docs.pipecat.ai/api-reference/server/services/llm/perplexity), [Qwen](https://docs.pipecat.ai/api-reference/server/services/llm/qwen), [SambaNova](https://docs.pipecat.ai/api-reference/server/services/llm/sambanova), [Sarvam](https://docs.pipecat.ai/api-reference/server/services/llm/sarvam), [Together AI](https://docs.pipecat.ai/api-reference/server/services/llm/together) |
| Text-to-Speech | [Async](https://docs.pipecat.ai/api-reference/server/services/tts/asyncai), [AWS](https://docs.pipecat.ai/api-reference/server/services/tts/aws), [Azure](https://docs.pipecat.ai/api-reference/server/services/tts/azure), [Camb AI](https://docs.pipecat.ai/api-reference/server/services/tts/camb), [Cartesia](https://docs.pipecat.ai/api-reference/server/services/tts/cartesia), [Deepgram](https://docs.pipecat.ai/api-reference/server/services/tts/deepgram), [ElevenLabs](https://docs.pipecat.ai/api-reference/server/services/tts/elevenlabs), [Fish](https://docs.pipecat.ai/api-reference/server/services/tts/fish), [Google](https://docs.pipecat.ai/api-reference/server/services/tts/google), [Gradium](https://docs.pipecat.ai/api-reference/server/services/tts/gradium), [Groq](https://docs.pipecat.ai/api-reference/server/services/tts/groq), [Hume](https://docs.pipecat.ai/api-reference/server/services/tts/hume), [Inworld](https://docs.pipecat.ai/api-reference/server/services/tts/inworld), [Kokoro](https://docs.pipecat.ai/api-reference/server/services/tts/kokoro), [LMNT](https://docs.pipecat.ai/api-reference/server/services/tts/lmnt), [MiniMax](https://docs.pipecat.ai/api-reference/server/services/tts/minimax), [Mistral](https://docs.pipecat.ai/api-reference/server/services/tts/mistral), [Neuphonic](https://docs.pipecat.ai/api-reference/server/services/tts/neuphonic), [NVIDIA Riva](https://docs.pipecat.ai/api-reference/server/services/tts/riva), [OpenAI](https://docs.pipecat.ai/api-reference/server/services/tts/openai), [Piper](https://docs.pipecat.ai/api-reference/server/services/tts/piper), [Resemble](https://docs.pipecat.ai/api-reference/server/services/tts/resemble), [Rime](https://docs.pipecat.ai/api-reference/server/services/tts/rime), [Sarvam](https://docs.pipecat.ai/api-reference/server/services/tts/sarvam), [Smallest](https://docs.pipecat.ai/api-reference/server/services/tts/smallest), [Soniox](https://docs.pipecat.ai/api-reference/server/services/tts/soniox), [Speechmatics](https://docs.pipecat.ai/api-reference/server/services/tts/speechmatics), [xAI](https://docs.pipecat.ai/api-reference/server/services/tts/xai), [XTTS](https://docs.pipecat.ai/api-reference/server/services/tts/xtts) |
| Speech-to-Speech | [AWS Nova Sonic](https://docs.pipecat.ai/api-reference/server/services/s2s/aws), [Gemini Multimodal Live](https://docs.pipecat.ai/api-reference/server/services/s2s/gemini), [Grok Voice Agent](https://docs.pipecat.ai/api-reference/server/services/s2s/grok), [OpenAI Realtime](https://docs.pipecat.ai/api-reference/server/services/s2s/openai), [Ultravox](https://docs.pipecat.ai/api-reference/server/services/s2s/ultravox), |
| Transport | [Daily (WebRTC)](https://docs.pipecat.ai/api-reference/server/services/transport/daily), [FastAPI Websocket](https://docs.pipecat.ai/api-reference/server/services/transport/fastapi-websocket), [LiveKit (WebRTC)](https://docs.pipecat.ai/api-reference/server/services/transport/livekit), [SmallWebRTCTransport](https://docs.pipecat.ai/api-reference/server/services/transport/small-webrtc), [WebSocket Server](https://docs.pipecat.ai/api-reference/server/services/transport/websocket-server), [WhatsApp](https://docs.pipecat.ai/api-reference/server/services/transport/whatsapp), Local |
| Serializers | [Exotel](https://docs.pipecat.ai/api-reference/server/services/serializers/exotel), [Genesys](https://docs.pipecat.ai/api-reference/server/services/serializers/genesys), [Plivo](https://docs.pipecat.ai/api-reference/server/services/serializers/plivo), [Twilio](https://docs.pipecat.ai/api-reference/server/services/serializers/twilio), [Telnyx](https://docs.pipecat.ai/api-reference/server/services/serializers/telnyx), [Vonage](https://docs.pipecat.ai/api-reference/server/services/serializers/vonage) |
| Video | [HeyGen](https://docs.pipecat.ai/api-reference/server/services/video/heygen), [LemonSlice](https://docs.pipecat.ai/api-reference/server/services/transport/lemonslice), [Tavus](https://docs.pipecat.ai/api-reference/server/services/video/tavus), [Simli](https://docs.pipecat.ai/api-reference/server/services/video/simli) |
| Memory | [mem0](https://docs.pipecat.ai/api-reference/server/services/memory/mem0) |
| Vision & Image | [fal](https://docs.pipecat.ai/api-reference/server/services/image-generation/fal), [Google Imagen](https://docs.pipecat.ai/api-reference/server/services/image-generation/google-imagen), [Moondream](https://docs.pipecat.ai/api-reference/server/services/vision/moondream) |
| Audio Processing | [Silero VAD](https://docs.pipecat.ai/api-reference/server/utilities/audio/silero-vad-analyzer), [Krisp Viva](https://docs.pipecat.ai/guides/features/krisp-viva), [Koala](https://docs.pipecat.ai/api-reference/server/utilities/audio/koala-filter), [ai-coustics](https://docs.pipecat.ai/api-reference/server/utilities/audio/aic-filter), [RNNoise](https://docs.pipecat.ai/api-reference/server/utilities/audio/rnnoise-filter) |
| Analytics & Metrics | [OpenTelemetry](https://docs.pipecat.ai/api-reference/server/utilities/opentelemetry), [Sentry](https://docs.pipecat.ai/api-reference/server/services/analytics/sentry) |
| Community | [Browse community integrations →](https://docs.pipecat.ai/api-reference/server/services/community-integrations) |
| Category | Services |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Speech-to-Text | [AssemblyAI](https://docs.pipecat.ai/api-reference/server/services/stt/assemblyai), [AWS](https://docs.pipecat.ai/api-reference/server/services/stt/aws), [Azure](https://docs.pipecat.ai/api-reference/server/services/stt/azure), [Cartesia](https://docs.pipecat.ai/api-reference/server/services/stt/cartesia), [Deepgram](https://docs.pipecat.ai/api-reference/server/services/stt/deepgram), [ElevenLabs](https://docs.pipecat.ai/api-reference/server/services/stt/elevenlabs), [Fal Wizper](https://docs.pipecat.ai/api-reference/server/services/stt/fal), [Gladia](https://docs.pipecat.ai/api-reference/server/services/stt/gladia), [Google](https://docs.pipecat.ai/api-reference/server/services/stt/google), [Gradium](https://docs.pipecat.ai/api-reference/server/services/stt/gradium), [Groq (Whisper)](https://docs.pipecat.ai/api-reference/server/services/stt/groq), [Mistral](https://docs.pipecat.ai/api-reference/server/services/stt/mistral), [NVIDIA](https://docs.pipecat.ai/api-reference/server/services/stt/nvidia), [OpenAI (Whisper)](https://docs.pipecat.ai/api-reference/server/services/stt/openai), [Sarvam](https://docs.pipecat.ai/api-reference/server/services/stt/sarvam), [Soniox](https://docs.pipecat.ai/api-reference/server/services/stt/soniox), [Speechmatics](https://docs.pipecat.ai/api-reference/server/services/stt/speechmatics), [Whisper](https://docs.pipecat.ai/api-reference/server/services/stt/whisper), [xAI](https://docs.pipecat.ai/api-reference/server/services/stt/xai) |
| LLMs | [Anthropic](https://docs.pipecat.ai/api-reference/server/services/llm/anthropic), [AWS](https://docs.pipecat.ai/api-reference/server/services/llm/aws), [Azure](https://docs.pipecat.ai/api-reference/server/services/llm/azure), [Cerebras](https://docs.pipecat.ai/api-reference/server/services/llm/cerebras), [DeepSeek](https://docs.pipecat.ai/api-reference/server/services/llm/deepseek), [Fireworks AI](https://docs.pipecat.ai/api-reference/server/services/llm/fireworks), [Gemini](https://docs.pipecat.ai/api-reference/server/services/llm/gemini), [Grok](https://docs.pipecat.ai/api-reference/server/services/llm/grok), [Groq](https://docs.pipecat.ai/api-reference/server/services/llm/groq), [Mistral](https://docs.pipecat.ai/api-reference/server/services/llm/mistral), [Nebius](https://docs.pipecat.ai/api-reference/server/services/llm/nebius), [Novita](https://docs.pipecat.ai/api-reference/server/services/llm/novita), [NVIDIA NIM](https://docs.pipecat.ai/api-reference/server/services/llm/nvidia), [Ollama](https://docs.pipecat.ai/api-reference/server/services/llm/ollama), [OpenAI](https://docs.pipecat.ai/api-reference/server/services/llm/openai), [OpenAI Responses](https://docs.pipecat.ai/api-reference/server/services/llm/openai-responses), [OpenRouter](https://docs.pipecat.ai/api-reference/server/services/llm/openrouter), [Perplexity](https://docs.pipecat.ai/api-reference/server/services/llm/perplexity), [Qwen](https://docs.pipecat.ai/api-reference/server/services/llm/qwen), [SambaNova](https://docs.pipecat.ai/api-reference/server/services/llm/sambanova), [Sarvam](https://docs.pipecat.ai/api-reference/server/services/llm/sarvam), [Together AI](https://docs.pipecat.ai/api-reference/server/services/llm/together) |
| Text-to-Speech | [Async](https://docs.pipecat.ai/api-reference/server/services/tts/asyncai), [AWS](https://docs.pipecat.ai/api-reference/server/services/tts/aws), [Azure](https://docs.pipecat.ai/api-reference/server/services/tts/azure), [Camb AI](https://docs.pipecat.ai/api-reference/server/services/tts/camb), [Cartesia](https://docs.pipecat.ai/api-reference/server/services/tts/cartesia), [Deepgram](https://docs.pipecat.ai/api-reference/server/services/tts/deepgram), [ElevenLabs](https://docs.pipecat.ai/api-reference/server/services/tts/elevenlabs), [Fish](https://docs.pipecat.ai/api-reference/server/services/tts/fish), [Google](https://docs.pipecat.ai/api-reference/server/services/tts/google), [Gradium](https://docs.pipecat.ai/api-reference/server/services/tts/gradium), [Groq](https://docs.pipecat.ai/api-reference/server/services/tts/groq), [Hume](https://docs.pipecat.ai/api-reference/server/services/tts/hume), [Inworld](https://docs.pipecat.ai/api-reference/server/services/tts/inworld), [Kokoro](https://docs.pipecat.ai/api-reference/server/services/tts/kokoro), [LMNT](https://docs.pipecat.ai/api-reference/server/services/tts/lmnt), [MiniMax](https://docs.pipecat.ai/api-reference/server/services/tts/minimax), [Mistral](https://docs.pipecat.ai/api-reference/server/services/tts/mistral), [Neuphonic](https://docs.pipecat.ai/api-reference/server/services/tts/neuphonic), [NVIDIA](https://docs.pipecat.ai/api-reference/server/services/tts/nvidia), [OpenAI](https://docs.pipecat.ai/api-reference/server/services/tts/openai), [Piper](https://docs.pipecat.ai/api-reference/server/services/tts/piper), [Resemble](https://docs.pipecat.ai/api-reference/server/services/tts/resemble), [Rime](https://docs.pipecat.ai/api-reference/server/services/tts/rime), [Sarvam](https://docs.pipecat.ai/api-reference/server/services/tts/sarvam), [Smallest](https://docs.pipecat.ai/api-reference/server/services/tts/smallest), [Soniox](https://docs.pipecat.ai/api-reference/server/services/tts/soniox), [Speechmatics](https://docs.pipecat.ai/api-reference/server/services/tts/speechmatics), [xAI](https://docs.pipecat.ai/api-reference/server/services/tts/xai), [XTTS](https://docs.pipecat.ai/api-reference/server/services/tts/xtts) |
| Speech-to-Speech | [AWS Nova Sonic](https://docs.pipecat.ai/api-reference/server/services/s2s/aws), [Gemini Multimodal Live](https://docs.pipecat.ai/api-reference/server/services/s2s/gemini), [Grok Voice Agent](https://docs.pipecat.ai/api-reference/server/services/s2s/grok), [OpenAI Realtime](https://docs.pipecat.ai/api-reference/server/services/s2s/openai), [Ultravox](https://docs.pipecat.ai/api-reference/server/services/s2s/ultravox), |
| Transport | [Daily (WebRTC)](https://docs.pipecat.ai/api-reference/server/services/transport/daily), [FastAPI Websocket](https://docs.pipecat.ai/api-reference/server/services/transport/fastapi-websocket), [LiveKit (WebRTC)](https://docs.pipecat.ai/api-reference/server/services/transport/livekit), [SmallWebRTCTransport](https://docs.pipecat.ai/api-reference/server/services/transport/small-webrtc), [WebSocket Server](https://docs.pipecat.ai/api-reference/server/services/transport/websocket-server), [WhatsApp](https://docs.pipecat.ai/api-reference/server/services/transport/whatsapp), Local |
| Serializers | [Exotel](https://docs.pipecat.ai/api-reference/server/services/serializers/exotel), [Genesys](https://docs.pipecat.ai/api-reference/server/services/serializers/genesys), [Plivo](https://docs.pipecat.ai/api-reference/server/services/serializers/plivo), [Twilio](https://docs.pipecat.ai/api-reference/server/services/serializers/twilio), [Telnyx](https://docs.pipecat.ai/api-reference/server/services/serializers/telnyx), [Vonage](https://docs.pipecat.ai/api-reference/server/services/serializers/vonage) |
| Video | [HeyGen](https://docs.pipecat.ai/api-reference/server/services/video/heygen), [LemonSlice](https://docs.pipecat.ai/api-reference/server/services/transport/lemonslice), [Tavus](https://docs.pipecat.ai/api-reference/server/services/video/tavus), [Simli](https://docs.pipecat.ai/api-reference/server/services/video/simli) |
| Memory | [mem0](https://docs.pipecat.ai/api-reference/server/services/memory/mem0) |
| Vision & Image | [fal](https://docs.pipecat.ai/api-reference/server/services/image-generation/fal), [Google Imagen](https://docs.pipecat.ai/api-reference/server/services/image-generation/google-imagen), [Moondream](https://docs.pipecat.ai/api-reference/server/services/vision/moondream) |
| Audio Processing | [Silero VAD](https://docs.pipecat.ai/api-reference/server/utilities/audio/silero-vad-analyzer), [Krisp Viva](https://docs.pipecat.ai/guides/features/krisp-viva), [Koala](https://docs.pipecat.ai/api-reference/server/utilities/audio/koala-filter), [ai-coustics](https://docs.pipecat.ai/api-reference/server/utilities/audio/aic-filter), [RNNoise](https://docs.pipecat.ai/api-reference/server/utilities/audio/rnnoise-filter) |
| Analytics & Metrics | [OpenTelemetry](https://docs.pipecat.ai/api-reference/server/utilities/opentelemetry), [Sentry](https://docs.pipecat.ai/api-reference/server/services/analytics/sentry) |
| Community | [Browse community integrations →](https://docs.pipecat.ai/api-reference/server/services/community-integrations) |
📚 [View full services documentation →](https://docs.pipecat.ai/api-reference/server/services/supported-services)

1
changelog/4385.added.md Normal file
View File

@@ -0,0 +1 @@
- Added a `session_id` field to `RunnerArguments` so bots can log or trace a per-session identifier in local development the same way they can in Pipecat Cloud. The development runner now mints a UUID at every construction site, and paths that already returned a `sessionId` to the caller (Daily `/start`, dial-in webhook) share that same UUID with the runner args instead of generating two. The SmallWebRTC `/api/offer` endpoint also accepts an optional `session_id` query parameter so the `/sessions/{session_id}/...` proxy can thread it through.

View File

@@ -0,0 +1 @@
- Updated the default `SonioxTTSService` model from `tts-rt-v1-preview` to the generally available `tts-rt-v1`.

9
changelog/4389.fixed.md Normal file
View File

@@ -0,0 +1,9 @@
- Fixed AWS services failing silently on missing or invalid credentials.
`AWSNovaSonicLLMService`, `AWSBedrockLLMService`, `AWSPollyTTSService`,
and `AWSTranscribeSTTService` now push a fatal `ErrorFrame` with a
"check AWS credentials and region" hint on auth-class failures, so the
pipeline cancels promptly instead of continuing to run with no output.
- Fixed `AWSNovaSonicLLMService._disconnect` raising `InvalidStateError`
from `awscrt/aio/http.py` when cleanup ran on a stream from a failed
`invoke_model_with_bidirectional_stream` call. The error was masking
the real connect-time auth failure in the logs.

View File

@@ -5,7 +5,7 @@
{% for text, values in sections[section][category].items() %}
{{ text }}
(PR {{ values|join(', ') }})
(PR {{ values|join(', ') }})
{% endfor %}
{% endfor %}

96
scripts/release-changelog.py Executable file
View File

@@ -0,0 +1,96 @@
#!/usr/bin/env python3
"""Generate release notes from CHANGELOG.md for use in GitHub Releases (or anywhere).
Usage::
python scripts/release-changelog.py VERSION
python scripts/release-changelog.py 1.1.0
python scripts/release-changelog.py --file path/to/CHANGELOG.md 1.1.0
Extracts the requested ``## [VERSION]`` section (heading and ``### …``
subheadings included) and prints it to stdout. The only transformation
applied is collapsing each entry paragraph onto a single line, so it is
suitable for pasting into release notes that don't need 80-column wrapping.
``(PR [...])`` references stay on their own two-space-indented continuation
line. The input file is never modified.
Every paragraph is unfilled. Indentation is preserved — each logical line
(bullets, sub-bullets, and the ``(PR [...])`` continuation) keeps its
leading whitespace; only the wrapped continuation lines that follow them
get joined back. Code-block paragraphs (triple-backtick fences, or deeply
indented blocks with no list markers around) are passed through untouched.
"""
from __future__ import annotations
import argparse
import re
import sys
from pathlib import Path
DEFAULT_CHANGELOG = Path(__file__).resolve().parent.parent / "CHANGELOG.md"
PARAGRAPH_SPLIT_RE = re.compile(r"(\n[ \t]*\n+)")
CONTINUATION_RE = re.compile(r"\n(?![ \t]*[-*+] )[ \t]+")
PR_RE = re.compile(r" \(PR \[")
CODE_FENCE_RE = re.compile(r"^\s*```")
DEEP_INDENT_RE = re.compile(r"^ {4,}")
LIST_MARKER_RE = re.compile(r"^[ \t]*[-*+] ")
SECTION_HEADING_RE = re.compile(r"^## \[", re.MULTILINE)
def is_code_block_paragraph(paragraph: str) -> bool:
lines = paragraph.splitlines()
if any(CODE_FENCE_RE.match(line) for line in lines):
return True
# In a list paragraph, 4-space indent is a sub-bullet continuation, not
# code. Only treat deep-indented lines as code when no bullets are around
# to claim them.
if any(LIST_MARKER_RE.match(line) for line in lines):
return False
return any(DEEP_INDENT_RE.match(line) for line in lines)
def unfill_entry(paragraph: str) -> str:
joined = CONTINUATION_RE.sub(" ", paragraph)
return PR_RE.sub("\n (PR [", joined, count=1)
def process(text: str) -> str:
parts = PARAGRAPH_SPLIT_RE.split(text)
for i in range(0, len(parts), 2):
para = parts[i]
if para and not is_code_block_paragraph(para):
parts[i] = unfill_entry(para)
return "".join(parts)
def extract_section(text: str, version: str) -> str:
head_re = re.compile(rf"^## \[{re.escape(version)}\]", re.MULTILINE)
m = head_re.search(text)
if not m:
sys.exit(f"error: version {version!r} not found")
nxt = SECTION_HEADING_RE.search(text, m.end())
end = nxt.start() if nxt else len(text)
return text[m.start() : end]
def main() -> None:
summary = (__doc__ or "").splitlines()[0]
parser = argparse.ArgumentParser(description=summary)
parser.add_argument("version", help="Version to extract (e.g. 1.1.0).")
parser.add_argument(
"--file",
type=Path,
default=DEFAULT_CHANGELOG,
help=f"Path to the changelog (default: {DEFAULT_CHANGELOG}).",
)
args = parser.parse_args()
text = args.file.read_text()
section = extract_section(text, args.version)
sys.stdout.write(process(section))
if __name__ == "__main__":
main()

View File

@@ -180,7 +180,7 @@ async def _run_telephony_bot(websocket: WebSocket, args: argparse.Namespace):
bot_module = _get_bot_module()
# Just pass the WebSocket - let the bot handle parsing
runner_args = WebSocketRunnerArguments(websocket=websocket)
runner_args = WebSocketRunnerArguments(websocket=websocket, session_id=str(uuid.uuid4()))
runner_args.cli_args = args
await bot_module.bot(runner_args)
@@ -267,15 +267,25 @@ def _setup_webrtc_routes(app: FastAPI, args: argparse.Namespace):
)
@app.post("/api/offer")
async def offer(request: SmallWebRTCRequest, background_tasks: BackgroundTasks):
async def offer(
request: SmallWebRTCRequest,
background_tasks: BackgroundTasks,
session_id: str | None = None,
):
"""Handle WebRTC offer requests via SmallWebRTCRequestHandler."""
# When called via the /sessions/{session_id}/api/offer proxy the
# session_id is threaded through; for direct /api/offer calls we mint
# one so bots see a stable identifier in either path.
resolved_session_id = session_id or str(uuid.uuid4())
# Prepare runner arguments with the callback to run your bot
async def webrtc_connection_callback(connection: SmallWebRTCConnection):
bot_module = _get_bot_module()
runner_args = SmallWebRTCRunnerArguments(
webrtc_connection=connection, body=request.request_data
webrtc_connection=connection,
body=request.request_data,
session_id=resolved_session_id,
)
runner_args.cli_args = args
background_tasks.add_task(bot_module.bot, runner_args)
@@ -343,7 +353,7 @@ def _setup_webrtc_routes(app: FastAPI, args: argparse.Namespace):
or request_data.get("requestData")
or active_session,
)
return await offer(webrtc_request, background_tasks)
return await offer(webrtc_request, background_tasks, session_id=session_id)
elif request.method == HTTPMethod.PATCH.value:
patch_request = SmallWebRTCPatchRequest(
pc_id=request_data["pc_id"],
@@ -490,7 +500,9 @@ def _setup_whatsapp_routes(app: FastAPI, args: argparse.Namespace):
connection: The established WebRTC connection
"""
bot_module = _get_bot_module()
runner_args = SmallWebRTCRunnerArguments(webrtc_connection=connection)
runner_args = SmallWebRTCRunnerArguments(
webrtc_connection=connection, session_id=str(uuid.uuid4())
)
runner_args.cli_args = args
background_tasks.add_task(bot_module.bot, runner_args)
@@ -554,7 +566,9 @@ def _setup_daily_routes(app: FastAPI, args: argparse.Namespace):
# Start the bot in the background with empty body for GET requests
bot_module = _get_bot_module()
runner_args = DailyRunnerArguments(room_url=room_url, token=token)
runner_args = DailyRunnerArguments(
room_url=room_url, token=token, session_id=str(uuid.uuid4())
)
runner_args.cli_args = args
asyncio.create_task(bot_module.bot(runner_args))
return RedirectResponse(room_url)
@@ -590,6 +604,7 @@ def _setup_daily_routes(app: FastAPI, args: argparse.Namespace):
existing_room_url = os.getenv("DAILY_ROOM_URL")
session_id = str(uuid.uuid4())
result = None
# Configure room if:
@@ -638,14 +653,16 @@ def _setup_daily_routes(app: FastAPI, args: argparse.Namespace):
room_properties=room_properties,
token_properties=token_properties,
)
runner_args = DailyRunnerArguments(room_url=room_url, token=token, body=body)
runner_args = DailyRunnerArguments(
room_url=room_url, token=token, body=body, session_id=session_id
)
result = {
"dailyRoom": room_url,
"dailyToken": token,
"sessionId": str(uuid.uuid4()),
"sessionId": session_id,
}
else:
runner_args = RunnerArguments(body=body)
runner_args = RunnerArguments(body=body, session_id=session_id)
# Update CLI args.
runner_args.cli_args = args
@@ -749,20 +766,21 @@ def _setup_daily_routes(app: FastAPI, args: argparse.Namespace):
daily_api_url=daily_api_url,
)
# Generate session ID for both the runner args and the response
session_id = str(uuid.uuid4())
# Start bot with dial-in context
bot_module = _get_bot_module()
runner_args = DailyRunnerArguments(
room_url=room_config.room_url,
token=room_config.token,
body=request_body.model_dump(),
session_id=session_id,
)
runner_args.cli_args = args
asyncio.create_task(bot_module.bot(runner_args))
# Generate session ID
session_id = str(uuid.uuid4())
# Return response matching Pipecat Cloud format
return {
"dailyRoom": room_config.room_url,
@@ -838,7 +856,9 @@ async def _run_daily_direct(args: argparse.Namespace):
room_url, token = await configure(session, room_exp_duration=PIPECAT_ROOM_EXP_HOURS)
# Direct connections have no request body, so use empty dict
runner_args = DailyRunnerArguments(room_url=room_url, token=token)
runner_args = DailyRunnerArguments(
room_url=room_url, token=token, session_id=str(uuid.uuid4())
)
runner_args.handle_sigint = True
runner_args.cli_args = args

View File

@@ -58,13 +58,25 @@ class DailyDialinRequest(BaseModel):
@dataclass
class RunnerArguments:
"""Base class for runner session arguments."""
"""Base class for runner session arguments.
Parameters:
handle_sigint: Whether the bot should install a SIGINT handler.
handle_sigterm: Whether the bot should install a SIGTERM handler.
pipeline_idle_timeout_secs: Seconds the pipeline may stay idle before
shutting down.
body: Optional request body data passed from the runner entry point.
session_id: Identifier for this bot session.
cli_args: Parsed CLI arguments from the runner, when launched via the
development runner.
"""
# Use kw_only so subclasses don't need to worry about ordering.
handle_sigint: bool = field(init=False, kw_only=True)
handle_sigterm: bool = field(init=False, kw_only=True)
pipeline_idle_timeout_secs: int = field(init=False, kw_only=True)
body: Any | None = field(default_factory=dict, kw_only=True)
session_id: str | None = field(default=None, kw_only=True)
cli_args: argparse.Namespace | None = field(default=None, init=False, kw_only=True)
def __post_init__(self):

View File

@@ -43,7 +43,7 @@ from pipecat.utils.tracing.service_decorators import traced_llm
try:
import aioboto3
from botocore.config import Config
from botocore.exceptions import ReadTimeoutError
from botocore.exceptions import ClientError, ReadTimeoutError
except ModuleNotFoundError as e:
logger.error(f"Exception: {e}")
logger.error(
@@ -52,6 +52,23 @@ except ModuleNotFoundError as e:
raise Exception(f"Missing module: {e}")
# AWS error codes that indicate the service won't work until creds/region are
# fixed. We treat these as fatal so the pipeline stops instead of silently
# degrading.
_AWS_AUTH_ERROR_CODES = frozenset(
{
"UnrecognizedClientException",
"InvalidSignatureException",
"AccessDeniedException",
"ExpiredTokenException",
"InvalidAccessKeyId",
"SignatureDoesNotMatch",
"MissingAuthenticationTokenException",
"AuthFailure",
}
)
@dataclass
class AWSBedrockLLMSettings(LLMSettings):
"""Settings for AWSBedrockLLMService.
@@ -555,6 +572,20 @@ class AWSBedrockLLMService(LLMService):
raise
except (TimeoutError, ReadTimeoutError):
await self._call_event_handler("on_completion_timeout")
except ClientError as e:
error_code = e.response.get("Error", {}).get("Code", "")
if error_code in _AWS_AUTH_ERROR_CODES:
await self.push_error(
error_msg=(
"AWS Bedrock authentication failed. "
"Check AWS credentials and region. "
f"Underlying error: {e}"
),
exception=e,
fatal=True,
)
else:
await self.push_error(error_msg=f"AWS Bedrock client error: {e}", exception=e)
except Exception as e:
await self.push_error(error_msg=f"Unknown error occurred: {e}", exception=e)
finally:

View File

@@ -602,7 +602,19 @@ class AWSNovaSonicLLMService(LLMService):
self._ready_to_send_context = True
await self._finish_connecting_if_context_available()
except Exception as e:
await self.push_error(error_msg=f"Initialization error: {e}", exception=e)
# Connect-time failures (most commonly bad/missing AWS credentials or
# an unsupported region) leave the bidirectional stream in a partial
# state and produce no audio output. Treat them as fatal so the
# pipeline cancels with a clear ERROR rather than continuing silently.
await self.push_error(
error_msg=(
"AWS Nova Sonic failed to start. "
"Check AWS credentials and region. "
f"Underlying error: {e}"
),
exception=e,
fatal=True,
)
await self._disconnect()
async def _process_completed_function_calls(self, send_new_results: bool):
@@ -703,17 +715,28 @@ class AWSNovaSonicLLMService(LLMService):
# NOTE: see explanation of HACK, below
self._disconnecting = True
# Clean up client
# Clean up client. If connect failed (e.g. bad credentials), the
# session may not have started, so end events can fail. Don't let
# that mask the real error or block cleanup.
if self._client:
await self._send_session_end_events()
try:
await self._send_session_end_events()
except Exception as e:
logger.debug(f"Ignoring error while sending session-end events: {e}")
self._client = None
# Clean up context
self._context = None
# Clean up stream
# Clean up stream. A stream from a failed
# invoke_model_with_bidirectional_stream call has an already-
# cancelled awscrt future; closing it raises InvalidStateError that
# otherwise drowns out the real connect error in the logs.
if self._stream:
await self._stream.close()
try:
await self._stream.close()
except Exception as e:
logger.debug(f"Ignoring error while closing partial stream: {e}")
self._stream = None
# NOTE: see explanation of HACK, below

View File

@@ -323,8 +323,18 @@ class AWSTranscribeSTTService(WebsocketSTTService):
await self._call_event_handler("on_connected")
logger.info(f"{self} Successfully connected to AWS Transcribe")
except Exception as e:
# Connect-time failures (most commonly bad/missing AWS credentials,
# an unsupported region, or a 403 from the presigned URL) won't
# recover on retry. Treat them as fatal so the pipeline cancels
# with a clear ERROR rather than silently producing no transcripts.
await self.push_error(
error_msg=f"Unable to connect to AWS Transcribe: {e}", exception=e
error_msg=(
"Unable to connect to AWS Transcribe. "
"Check AWS credentials and region. "
f"Underlying error: {e}"
),
exception=e,
fatal=True,
)
raise

View File

@@ -37,6 +37,23 @@ except ModuleNotFoundError as e:
raise Exception(f"Missing module: {e}")
# AWS error codes that indicate the service won't work until creds/region are
# fixed. We treat these as fatal so the pipeline stops instead of silently
# degrading.
_AWS_AUTH_ERROR_CODES = frozenset(
{
"UnrecognizedClientException",
"InvalidSignatureException",
"AccessDeniedException",
"ExpiredTokenException",
"InvalidAccessKeyId",
"SignatureDoesNotMatch",
"MissingAuthenticationTokenException",
"AuthFailure",
}
)
def language_to_aws_language(language: Language) -> str | None:
"""Convert a Language enum to AWS Polly language code.
@@ -366,6 +383,21 @@ class AWSPollyTTSService(TTSService):
frame = TTSAudioRawFrame(chunk, self.sample_rate, 1, context_id=context_id)
yield frame
except (BotoCoreError, ClientError) as error:
error_message = f"AWS Polly TTS error: {str(error)}"
yield ErrorFrame(error=error_message)
except ClientError as error:
error_code = error.response.get("Error", {}).get("Code", "")
if error_code in _AWS_AUTH_ERROR_CODES:
# Bad/missing credentials won't fix themselves between calls.
# Stop the pipeline so the failure surfaces clearly.
await self.push_error(
error_msg=(
"AWS Polly authentication failed. "
"Check AWS credentials and region. "
f"Underlying error: {error}"
),
exception=error,
fatal=True,
)
else:
yield ErrorFrame(error=f"AWS Polly TTS error: {error}")
except BotoCoreError as error:
yield ErrorFrame(error=f"AWS Polly TTS error: {error}")

View File

@@ -184,7 +184,7 @@ class SonioxTTSService(WebsocketTTSService):
"""
# Initialize default_settings
default_settings = self.Settings(
model="tts-rt-v1-preview",
model="tts-rt-v1",
voice="Adrian",
language=Language.EN,
)