Skip to content

Replace Relay Realtime SDK examples on TTS provider pages with Server SDK examples - #876

Merged
Devon-White merged 4 commits into
mainfrom
devon/tts-pages-server-sdk
Oct 29, 2026
Merged

Devon-White merged 4 commits into
mainfrom
devon/tts-pages-server-sdk

Conversation

@Devon-White

@Devon-White Devon-White commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • Replaces the deprecated "Relay Realtime SDK" tab (Node.js @signalwire/realtime-api call.playTTS() fragment) on seven TTS provider pages with a WebSocket (Relay) tab. The tab holds complete Python and TypeScript Relay client handlers that answer a call and play the page's greeting with that provider's voice string.
  • Puts the Server SDK first in the SWML tab. Each of the two existing YAML documents (the ai agent with languages, and set: say_voice + play) now follows Python and TypeScript SWMLService servers that serve that same document. The YAML stays as it was.
  • Leaves voice IDs, greeting text, YAML, and the Call Flow Builder tab unchanged. The rest of each page is untouched, and so is TTS/index.mdx (open PRs fix(swml): align the SWML method reference with platform behavior #852 and Add Speak text on a call guide #789 change it).

Pages:

Closes #880
Refs #600

Verification

All checks ran against the pinned signalwire-sdk==3.4.1 and @signalwire/sdk@2.0.5, installed in a scratch directory.

  • Python samples: py_compile and pyflakes pass on all 21.
  • TypeScript samples: tsc --strict passes on all 21. This also confirms that the one @ts-expect-error is needed, because an unused directive fails the compile.
  • SWML servers (28 runs): I ran each of the 28 servers, Python under python and TypeScript as .mjs under node, and fetched /swml with basic auth. Every document matches the YAML beside it. A request without credentials gets 401.
  • Relay samples: Both languages build the nested tts item. I sent the calling.play request from each SDK through a stub client. Both send the same payload, {"type": "tts", "params": {"text": ..., "voice": "<engine>.<voice>"}}. That is the same payload the deprecated @signalwire/realtime-api playTTS({ text, voice }) built.
  • Links: /docs/server-sdks, plus /docs/platform/voice/make-and-receive-calls#answer-a-call-via-swml and #answer-a-call-via-websocket-relay, checked by hand against the slugs and headings.
  • yarn fern-md-check: all MDX files are valid.

Notes for reviewers

  • Not tested on a live call. I didn't place a call against a real Space with these samples, so I haven't heard any provider's voice played through them. The Relay payload was checked with a stub client, and the SWML servers were checked by fetching their documents.
  • Type workaround in the TypeScript ai sample. In 2.0.5 the ai verb's prompt is typed string | Array, but the verb needs { text }. The sample uses a commented // @ts-expect-error above the prompt line, so it still type-checks.
  • Why SWMLService and not AgentBase for the AI agent. The Python AgentBase.add_language() splits amazon.Ruth:neural into voice, engine, and model, and engine is deprecated in the SWML reference. SWMLService sends the voice string exactly as written.
  • No playTTS() in the pinned TypeScript SDK. The TypeScript Server SDK reference documents playTTS(), but @signalwire/sdk@2.0.5 doesn't have it. Python 3.4.1 does have play_tts(), but both Relay samples use play() with the same nested { type: "tts", params: { text, voice } } item, so the two tabs match each other and the Relay wire format (the same choice speak-text.mdx makes).
  • Small mismatch with the YAML. The builders emit play: { url: "say:..." }. The YAML uses the play: "say:..." string shorthand. Both mean the same thing.
  • Pins kept on purpose. The registries now have newer releases (signalwire-sdk 3.7.5, @signalwire/sdk 3.7.1). I kept the house pins because bumps go to every guide in one PR.
  • Tabs stay. These are provider reference pages, not capability guides, so I kept them. The Server SDK leads inside the SWML tab, and WebSocket (Relay) is now the second tab.

… SDK examples

Swap the deprecated @signalwire/realtime-api tab on the Azure, Cartesia,
Deepgram, ElevenLabs, Google Cloud, OpenAI, and Amazon Polly pages for
Python and TypeScript Relay client samples, and lead each SWML example
with a Server SDK SWMLService server ahead of the existing YAML.

Refs #600
@github-actions

github-actions Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

hey-august
hey-august previously approved these changes Oct 29, 2026

@hey-august hey-august left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. I re-ran the checks against signalwire-sdk==3.4.1 and @signalwire/sdk@2.0.5:

  • All 21 Python samples compile, and all 21 TypeScript samples pass tsc --strict as ES modules.
  • All 28 SWML servers return the same document as the YAML beside them, and a request without credentials gets 401.
  • The Relay payload matches what mod_infrastructure accepts: play[].params takes text and voice (relay_apis.c:1265). The voice string goes through the same lookup the SWML say: path uses, and strings with : or - (amazon.Ruth:neural, the Cartesia UUID) pass validation.
  • The links and anchors resolve.

Two optional nits:

  1. Python 3.4.1 has call.play_tts(text, voice=..., on_completed=...) (relay/call.py:580). It sends the same request and is closer to the old playTTS(). Only the TypeScript SDK lacks it.
  2. The Python Relay sample nests params, but the TypeScript one uses the flat item. The SDK converts both to the same request, but the two tabs look like they use different structures. Using one form in both would fix that.

hey-august and others added 3 commits October 29, 2026 10:01
# Conflicts:
#	fern/products/platform/pages/calling/voice/TTS/azure.mdx
#	fern/products/platform/pages/calling/voice/TTS/cartesia.mdx
#	fern/products/platform/pages/calling/voice/TTS/deepgram.mdx
#	fern/products/platform/pages/calling/voice/TTS/elevenlabs.mdx
#	fern/products/platform/pages/calling/voice/TTS/google.mdx
#	fern/products/platform/pages/calling/voice/TTS/openai.mdx
#	fern/products/platform/pages/calling/voice/TTS/polly.mdx
Every TTS provider page now builds the TypeScript `tts` play item as
`{ type: "tts", params: { text, voice } }`, matching the Python tab and
the Relay wire format. @signalwire/sdk 2.0.5 passes an item that already
has `params` through unchanged, so the request is the same as before.

Refs #876.
@Devon-White

Devon-White commented Oct 29, 2026 •

Copy link
Copy Markdown
Collaborator Author

@hey-august Thanks for the re-run and the approval. I took nit 2 and kept play for nit 1. I also merged origin/main (12f743b). The only conflict was main's switch to /docs/swml/reference/calling/ai in the languages link on each page. I kept main's links and this PR's code.

  1. play_tts() in Python: no change. You're right that signalwire-sdk 3.4.1 has call.play_tts(text, voice=..., on_completed=...). It wraps the same [{"type": "tts", "params": {...}}] request. But @signalwire/sdk 2.0.5 has no playTTS(); it only shows up in a doc comment. Our convention for Relay samples is the same play command in both languages, so the two tabs read line for line, and speak-text.mdx (Add Speak text on a call guide #789) does the same. Switching only Python would bring back the mismatch nit 2 is about. I added this to the PR body's notes.

  2. One play item form: fixed in 5e3db38. On all seven pages the TypeScript Relay sample now uses the nested { type: "tts", params: { text, voice } } item, the same as the Python tab, speak-text.mdx, and the Relay wire format. In 2.0.5, play() passes an item that already has params through unchanged and only reshapes the flat form, so the request doesn't change. play() takes Record<string, unknown>[], so the nested form type-checks without a cast.

Checks, against signalwire-sdk==3.4.1 and @signalwire/sdk@2.0.5 installed in a scratch directory:

  • tsc --strict passes on all 21 TypeScript samples, as ES modules. py_compile and pyflakes pass on all 21 Python samples. All 14 YAML blocks parse.
  • Stub client: for each page, the TypeScript play request carries the same play array as the Python sample, for example [{"type": "tts", "params": {"text": "...", "voice": "amazon.Ruth:neural"}}].
  • yarn fern-md-check: all MDX files are valid.
  • I checked /docs/swml/reference/calling/ai/languages#use-voice-strings by hand against the page slug and its "Use voice strings" heading.

@Devon-White
Devon-White merged commit 699b565 into main Oct 29, 2026
2 checks passed
@Devon-White
Devon-White deleted the devon/tts-pages-server-sdk branch October 29, 2026 17:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Replace the Relay Realtime SDK tab on the TTS provider pages with Server SDK examples

2 participants