Skip to content

refactor(voice): extract ConversationRelay TwiML logic into twiml.py - #113

Open
xinghaohuang91 wants to merge 2 commits into
mainfrom
refactor/extract-twiml-builder
Open

refactor(voice): extract ConversationRelay TwiML logic into twiml.py#113
xinghaohuang91 wants to merge 2 commits into
mainfrom
refactor/extract-twiml-builder

Conversation

@xinghaohuang91

Copy link
Copy Markdown
Contributor

Summary

  • VoiceChannel owned every TwiML layering/resolution decision inline (_build_twiml_options, _overlay_fields, _resolve_action_url, _resolve_websocket_url, _resolve_default_action_url), coupling the channel to ConversationRelay's TwiML shape.
  • Moves all of it into a new TwiMLBuilderConversationRelay in twiml.py, constructed from TACConfig + VoiceChannelConfig, with a single build() entry point.
  • VoiceChannel now just constructs the builder in __init__ and calls build() at its two TwiML call sites (handle_incoming_call, initiate_outbound_conversation).
  • Pure move — no logic changes, no public API changes. Method bodies are unchanged apart from the necessary renames (self.tac.configself.tac_config, self.configself.channel_config).
  • This is groundwork for a follow-up PR that splits ConversationRelay support out of VoiceChannel into a pluggable provider — this extraction keeps that PR's diff small by giving it a self-contained unit to move, instead of moving tangled logic and standing up the new abstraction in the same change.

Known minor, low-risk behavior nuance (documented, not fixed here): in initiate_outbound_conversation, TwiML rendering now happens just before the try block instead of inside it, so an exception from TwiML rendering itself (very unlikely — fields are already validated by this point) would skip the "Failed to initiate outbound call" log line but still propagate the same way.

Test plan

  • make lint / make type-check / make check all pass
  • 876 existing tests pass unmodified
  • tests/ and getting_started/ have zero diff — public API and examples untouched

VoiceChannel owned every TwiML layering/resolution decision inline
(_build_twiml_options, _overlay_fields, _resolve_action_url,
_resolve_websocket_url, _resolve_default_action_url), which coupled the
channel tightly to ConversationRelay's TwiML shape. Move all of it into
a new TwiMLBuilderConversationRelay, constructed from TACConfig and
VoiceChannelConfig, with a single build() entry point. VoiceChannel now
just constructs the builder and calls build() at its two TwiML call sites.

No logic changes — method bodies moved as-is, only necessary renames
(self.tac.config -> self.tac_config, self.config -> self.channel_config).
Public API and TwiMLOptions naming are untouched in this PR.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 26, 2026 17:20

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Refactors the Voice channel’s ConversationRelay TwiML composition so VoiceChannel no longer owns option layering and URL resolution, by extracting that logic into a dedicated builder in twiml.py. This reduces coupling between the channel implementation and the ConversationRelay TwiML shape, setting up future provider/plugin work without changing behavior.

Changes:

  • Introduced TwiMLBuilderConversationRelay (in twiml.py) to encapsulate TwiML option layering and action/WebSocket URL resolution behind a single build() entry point.
  • Updated VoiceChannel to construct and use the builder at its inbound/outbound TwiML call sites.
  • Updated VoiceChannelConfig.default_twiml_options documentation to point at the new overlay logic location.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
src/tac/channels/voice/twiml.py Adds TwiMLBuilderConversationRelay to centralize TwiML layering + URL resolution and reuse existing generate_twiml().
src/tac/channels/voice/channel.py Replaces inline TwiML layering/resolution with builder usage for inbound and outbound TwiML generation.
src/tac/channels/voice/config.py Updates the default_twiml_options doc reference to the extracted overlay implementation.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/tac/channels/voice/config.py Outdated
_resolve_websocket_url and _resolve_default_action_url each had exactly
one caller — fold them into build() and _resolve_action_url respectively.
No logic changes.

Also fixes a doc reference flagged by review: point at the fully-qualified
tac.channels.voice.twiml.TwiMLBuilderConversationRelay path instead of the
ambiguous twiml.TwiMLBuilderConversationRelay.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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.

3 participants