refactor(voice): extract ConversationRelay TwiML logic into twiml.py - #113
Open
xinghaohuang91 wants to merge 2 commits into
Open
refactor(voice): extract ConversationRelay TwiML logic into twiml.py#113xinghaohuang91 wants to merge 2 commits into
xinghaohuang91 wants to merge 2 commits into
Conversation
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>
xinghaohuang91
requested review from
ryanrouleau and
wenzhu1587
as code owners
August 26, 2026 17:20
There was a problem hiding this comment.
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(intwiml.py) to encapsulate TwiML option layering and action/WebSocket URL resolution behind a singlebuild()entry point. - Updated
VoiceChannelto construct and use the builder at its inbound/outbound TwiML call sites. - Updated
VoiceChannelConfig.default_twiml_optionsdocumentation 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.
_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>
3 tasks
ryanrouleau
approved these changes
Aug 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
VoiceChannelowned 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.TwiMLBuilderConversationRelayintwiml.py, constructed fromTACConfig+VoiceChannelConfig, with a singlebuild()entry point.VoiceChannelnow just constructs the builder in__init__and callsbuild()at its two TwiML call sites (handle_incoming_call,initiate_outbound_conversation).self.tac.config→self.tac_config,self.config→self.channel_config).VoiceChannelinto 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 thetryblock 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 checkall passtests/andgetting_started/have zero diff — public API and examples untouched