Support prompt voice telephony bindings - #9804
Conversation
|
Azure Pipelines: Successfully started running 1 pipeline(s). 22 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
|
Azure Pipelines: Successfully started running 2 pipeline(s). 20 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
🟡 Changes recommended
Binding normalization, regional routing, legacy validation, and lint failures need correction.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds declarative Foundry telephony bindings for prompt voice agents.
Changes:
- Adds telephony YAML models, schema, validation, and round-trip support.
- Adds binding create/get API operations and drift detection.
- Creates bindings after successful voice-agent deployment.
File summaries
| File | Description |
|---|---|
cspell.yaml |
Adds telephony terminology. |
internal/exterrors/codes.go |
Adds binding operation codes. |
internal/pkg/agents/agent_api/models.go |
Defines binding API models. |
internal/pkg/agents/agent_api/operations.go |
Implements binding API calls. |
internal/pkg/agents/agent_api/operations_test.go |
Tests binding requests. |
internal/pkg/agents/agent_yaml/parse.go |
Validates telephony configuration. |
internal/pkg/agents/agent_yaml/parse_voice_test.go |
Tests validation behavior. |
internal/pkg/agents/agent_yaml/yaml.go |
Defines authoring models. |
internal/project/agent_definition.go |
Supports inline round trips. |
internal/project/service_target_agent.go |
Integrates binding deployment. |
internal/project/service_target_agent_test.go |
Tests detection and matching. |
schemas/azure.ai.agent.json |
Publishes the configuration schema. |
Review details
Suppressed comments (2)
cli/azd/extensions/azure.ai.agents/internal/project/service_target_agent.go:2507
- These binding requests drop
AZURE_VOICE_OVERRIDDEN_HOST. The surrounding voice-agent GET/create/update calls propagate that header because some deployments must bypass public APIM; in those deployments the agent operation can succeed and the subsequent telephony GET/create can route incorrectly. Pass the resolved override through both telephony client operations.
remoteBinding, getErr := agentClient.GetTelephonyBinding(
ctx,
agentObject.Name,
bindingID,
agent_api.TelephonyBindingAPIVersion,
cli/azd/extensions/azure.ai.agents/internal/project/service_target_agent.go:2557
- Normalize the response ID before comparing it. The new GET test models the service response as
twilio:%2B14255550123, whiledesiredIDistwilio:+14255550123; this comparison therefore reports drift for an unchanged Twilio binding on every redeploy.
desiredID := fmt.Sprintf("%s:%s", strings.TrimSpace(desired.Provider), strings.TrimSpace(desired.Identifier))
if strings.TrimSpace(remote.ID) != "" && strings.TrimSpace(remote.ID) != desiredID {
return false
- Files reviewed: 12/12 changed files
- Comments generated: 3
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
🟡 Changes recommended
Referenced non-voice definitions can bypass telephony validation, and an added Go line violates the enforced line-length limit.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (2)
Previously missed (1) — in code that hasn't changed since the last review.
cli/azd/extensions/azure.ai.agents/internal/project/service_target_agent.go:1409
- This checks only the raw property bags, so a hosted service whose
$reffile containstelephonybypasses the prompt-voice restriction: kind resolution expands the reference, butserviceHasTelephonysees only$ref, and the hosted conversion later drops the field. Validate the resolved effective properties (and add a$refregression case) before entering the non-voice deploy path.
cli/azd/extensions/azure.ai.agents/internal/pkg/agents/agent_yaml/parse.go:20
- This 133-character Go line exceeds the repository's enforced 125-character limit (
cli/azd/AGENTS.md:115-126), so thelllcheck will fail. Split theMustCompilecall across lines.
var acsTpeRawIDPattern = regexp.MustCompile(`^28:orgid:[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$`)
- Files reviewed: 12/12 changed files
- Comments generated: 2
- Review effort level: Balanced
There was a problem hiding this comment.
🟡 Changes recommended
Non-voice validation and drift detection have correctness gaps, and several added Go lines fail the enforced line-length lint rule.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (3)
cli/azd/extensions/azure.ai.agents/internal/project/service_target_agent.go:1409
agentDefinitionPathis also populated for an ordinary service-directoryagent.yaml(lines 470–479), so this condition bypasses the non-voice rejection for every legacy file-based service, not only for an explicitAGENT_DEFINITION_PATHoverride. Check the environment override itself so a hosted service cannot silently ignoretelephony.
if !isVoice && p.agentDefinitionPath == "" && serviceHasTelephony(serviceConfig) {
cli/azd/extensions/azure.ai.agents/internal/pkg/agents/agent_yaml/parse.go:20
- This regex declaration is 133 columns, exceeding the enforced 125-column Go limit (
cli/azd/AGENTS.md:115-126) and causinggolangci-lintto fail.
var acsTpeRawIDPattern = regexp.MustCompile(`^28:orgid:[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$`)
cli/azd/extensions/azure.ai.agents/internal/pkg/agents/agent_yaml/parse_voice_test.go:365
- These three added rows are 157–179 columns, exceeding the enforced 125-column Go limit (
cli/azd/AGENTS.md:115-126) and causinggolangci-lintto fail. Expand each table entry across fields.
{name: "bad provider", yaml: "telephony:\n bindings:\n - provider: sip\n identifier: +14255550123\n connection: c", want: "provider must be acs or twilio"},
{name: "bad twilio id", yaml: "telephony:\n bindings:\n - provider: twilio\n identifier: not-a-number\n connection: c", want: "identifier must be +<E.164>"},
{name: "missing connection", yaml: "telephony:\n bindings:\n - provider: acs\n identifier: 4:+14255550123", want: "connection is required"},
- Files reviewed: 13/13 changed files
- Comments generated: 5
- Review effort level: Balanced
There was a problem hiding this comment.
🟡 Changes recommended
The legacy definition-path guard can silently bypass validation, and several added Go lines violate the enforced line-length limit.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (1)
cli/azd/extensions/azure.ai.agents/internal/project/service_target_agent.go:2574
- This added line is 126 characters after tab expansion and fails the repository's enforced 125-character
llllimit (cli/azd/AGENTS.md:115-126). Split the initializer condition across lines.
if remoteConnection := telephonyBindingConnection(remote); remoteConnection != strings.TrimSpace(desired.ConnectionName) {
- Files reviewed: 13/13 changed files
- Comments generated: 3
- Review effort level: Balanced
There was a problem hiding this comment.
🔵 Needs a closer look
Duplicate bindings, incomplete schema enforcement, and a lint failure remain unresolved.
Review details
Suppressed comments (3)
Previously missed (3) — in code that hasn't changed since the last review.
cli/azd/extensions/azure.ai.agents/internal/pkg/agents/agent_yaml/parse.go:581
- Duplicate provider/identifier pairs currently pass validation. If two entries use different connections, deployment creates the first binding and then reports drift on the second, leaving a partially applied configuration that cannot converge without manual cleanup. Reject duplicate normalized identities before making API calls.
cli/azd/extensions/azure.ai.agents/schemas/azure.ai.agent.json:148 - The schema still accepts
telephonywhenkindishosted, despite documenting it as prompt-voice-only. Add a rootallOfcondition requiringkind: prompt-voicewhenevertelephonyis present so editor/schema validation matches deploy-time validation.
cli/azd/extensions/azure.ai.agents/internal/project/service_target_agent.go:2586 - This line is 126 columns after tab expansion and exceeds the repository's enforced 125-column Go limit (
cli/azd/AGENTS.md:115-126), solllwill reject it. Split the initializer and condition across lines.
- Files reviewed: 13/13 changed files
- Comments generated: 0 new
- Review effort level: Balanced
There was a problem hiding this comment.
🟡 Changes recommended
Failed remote bindings can be accepted as successful, and one added line violates the enforced Go line-length limit.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
cli/azd/extensions/azure.ai.agents/internal/project/service_target_agent.go:2524
- A matching binding is accepted without checking
remoteBinding.Status, so a binding in a terminalfailedstate makesazd deployreport success even though calls cannot route. Reject failed bindings with actionable cleanup guidance, and handle any documented transitional statuses before continuing; add a regression test for the failed-status case.
- Files reviewed: 13/13 changed files
- Comments generated: 1
- Review effort level: Balanced
There was a problem hiding this comment.
🔵 Needs a closer look
Schema enforcement and transfer-target drift comparison have correctness gaps.
Review details
Suppressed comments (2)
Previously missed (2) — in code that hasn't changed since the last review.
cli/azd/extensions/azure.ai.agents/internal/project/service_target_agent.go:2591
reflect.DeepEqualcompares the concrete types inside these dynamic maps rather than their JSON values. An on-diskagent.yamldecodes a numeric transfer-target property as an integer, while the GET response decodes the same JSON number asfloat64, so an unchanged binding allowed by the schema is reported as drift. Compare canonical JSON representations or introduce a typed transfer-target model, and cover the on-disk numeric round trip.
cli/azd/extensions/azure.ai.agents/schemas/azure.ai.agent.json:148telephonyis declared unconditionally, and none of the rootallOfrules requireskind: prompt-voice, so schema validation still accepts a hosted service with this property even though deploy rejects it. Add a conditional rule for the presence oftelephonythat requireskindto beprompt-voice, together with a schema regression test, as is already done for other kind-specific properties.
- Files reviewed: 13/13 changed files
- Comments generated: 0 new
- Review effort level: Balanced
azure.ai.agents PR buildNote This is an unsigned development build. Install it only if you trust this PR. Install the extension: azd ext install "https://azuresdkartifacts.z5.web.core.windows.net/azd/extensions/pr/9804/azure-ai-agents.zip"
|
Summary
azure.yamlsupport for Foundry-side telephony bindings on prompt voice agents.azd ai agent init,azd provision,azd deploy, andazd down; no new command is introduced.kind: prompt-voice; other agent types rejecttelephonyconfiguration.Spec
What changes for users
Users can optionally add phone-number bindings to a prompt voice agent service:
Then they continue to use the normal lifecycle:
Scope
agent_versionand arbitraryprovider_configare intentionally not exposed because the current service contract does not support version-pinned bindings and unknown provider config fields are ignored.Validation
providermust beacsortwilio.identifierandconnectionare required.28:orgid:<guid>for TPE or4:+<E.164>for ACS-purchased numbers.+<E.164>.provider: acsis mapped to the service canonical providerazure-communication-service.telephonyon non-prompt-voiceservices is rejected unless an explicitAGENT_DEFINITION_PATHoverride is routing the deploy to a different agent definition.Implementation
azure.yamlround-trip support.Test plan and private package
D:\jwshare\adc-hosted-agent\ws-endpoint\voice-telephony-binding-private-test-share-pr9804-win-amd64.ziptelephony-twilioconnection.Validation run
go test ./... -count=1go build ./...cspell lint 'extensions/**/*.go' 'extensions/**/*.md' --config ./.vscode/cspell.yaml --no-progressprovider=twilio, dedicated/test numbers bound throughtelephony-twilio, binding statusactive, and real inbound calls reached prompt voice agents.wujia-voice-agent-telephony-demo0904, phone+1 978-627-7446, bindingtwilio:+19786277446.telephony-acsconnection, project MI RBAC, and ACS binding statusactive.Fixes #9803