Skip to content

Support prompt voice telephony bindings - #9804

Open
JianW (v1212) wants to merge 12 commits into
Azure:mainfrom
v1212:users/wujia/prompt-voice-agent-telephony-bindings
Open

Support prompt voice telephony bindings#9804
JianW (v1212) wants to merge 12 commits into
Azure:mainfrom
v1212:users/wujia/prompt-voice-agent-telephony-bindings

Conversation

@v1212

@v1212 JianW (v1212) commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds azure.yaml support for Foundry-side telephony bindings on prompt voice agents.
  • Keeps the user experience nearly unchanged: users still run azd ai agent init, azd provision, azd deploy, and azd down; no new command is introduced.
  • Limits telephony binding support to kind: prompt-voice; other agent types reject telephony configuration.

Spec

What changes for users

Users can optionally add phone-number bindings to a prompt voice agent service:

services:
  support-voice:
    host: azure.ai.agent
    kind: prompt-voice
    name: support-voice
    model:
      id: gpt-realtime
    telephony:
      bindings:
        - provider: acs
          identifier: "28:orgid:<resource-account-object-id>"
          connection: telephony-acs
        - provider: twilio
          identifier: "+14255550123"
          connection: telephony-twilio

Then they continue to use the normal lifecycle:

azd provision
azd deploy support-voice

Scope

  • This PR only creates Foundry-side telephony bindings after a prompt voice agent deploy succeeds.
  • It does not purchase phone numbers, create Teams/TPE resources, create Twilio numbers, bootstrap Event Subscription auth/apps, or automate cross-tenant/admin telephony setup.
  • Provider-side resources and project connections are prerequisites provided by the user/admin.
  • agent_version and arbitrary provider_config are intentionally not exposed because the current service contract does not support version-pinned bindings and unknown provider config fields are ignored.

Validation

  • provider must be acs or twilio.
  • identifier and connection are required.
  • ACS identifiers support 28:orgid:<guid> for TPE or 4:+<E.164> for ACS-purchased numbers.
  • Twilio identifiers use +<E.164>.
  • provider: acs is mapped to the service canonical provider azure-communication-service.
  • telephony on non-prompt-voice services is rejected unless an explicit AGENT_DEFINITION_PATH override is routing the deploy to a different agent definition.

Implementation

  • Adds prompt voice telephony YAML models, schema, and inline azure.yaml round-trip support.
  • Adds telephony binding API models and agent-scoped create/get operations.
  • Runs binding creation after successful prompt voice deploy and agent version validation.
  • Existing bindings are treated as create-only: matching bindings are accepted; drift is reported as an error rather than silently ignored.
  • Handles ACS binding GET responses that return the binding identity/status but omit request fields like identifier/connection name.
  • Adds tests for validation, round-trip, API request path/body, provider mapping, drift detection, and prompt-voice-only detection.

Test plan and private package

  • Test guide/package: D:\jwshare\adc-hosted-agent\ws-endpoint\voice-telephony-binding-private-test-share-pr9804-win-amd64.zip
  • The test guide uses the prepared TTL 3 Days Foundry project and telephony-twilio connection.
  • It also records reference steps for preparing Foundry Twilio and ACS connections.
  • Cleanup explicitly deletes telephony bindings before deleting agents because the service currently has a known issue where deleting an agent may leave bindings behind.

Validation run

  • go test ./... -count=1
  • go build ./...
  • cspell lint 'extensions/**/*.go' 'extensions/**/*.md' --config ./.vscode/cspell.yaml --no-progress
  • Twilio E2E using Support prompt voice telephony bindings #9804 azd deploy passed in TTL 3 Days: provider=twilio, dedicated/test numbers bound through telephony-twilio, binding status active, and real inbound calls reached prompt voice agents.
  • A long-lived demo agent was created: wujia-voice-agent-telephony-demo0904, phone +1 978-627-7446, binding twilio:+19786277446.
  • ACS/TPE Foundry-side setup in TTL passed: ACS resource, telephony-acs connection, project MI RBAC, and ACS binding status active.
  • ACS/TPE full call E2E is blocked on provider-side Event Subscription webhook validation for the TTL Foundry callback; this is outside the azd binding YAML scope and is tracked in the linked spec/report.

Fixes #9803

@azure-pipelines

Copy link
Copy Markdown
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.

@github-actions github-actions Bot added the ext-agents azure.ai.agents extension label Sep 1, 2026
@azure-pipelines

Copy link
Copy Markdown
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.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 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, while desiredID is twilio:+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.

Comment thread cli/azd/extensions/azure.ai.agents/internal/pkg/agents/agent_yaml/parse.go Outdated
Comment thread cli/azd/extensions/azure.ai.agents/internal/project/service_target_agent.go Outdated
Copilot AI review requested due to automatic review settings September 4, 2026 05:39

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 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 $ref file contains telephony bypasses the prompt-voice restriction: kind resolution expands the reference, but serviceHasTelephony sees only $ref, and the hosted conversion later drops the field. Validate the resolved effective properties (and add a $ref regression 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 the lll check will fail. Split the MustCompile call 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

Comment thread cli/azd/extensions/azure.ai.agents/internal/project/service_target_agent.go Outdated
Comment thread cli/azd/extensions/azure.ai.agents/schemas/azure.ai.agent.json
Copilot AI review requested due to automatic review settings September 4, 2026 07:51

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 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

  • agentDefinitionPath is also populated for an ordinary service-directory agent.yaml (lines 470–479), so this condition bypasses the non-voice rejection for every legacy file-based service, not only for an explicit AGENT_DEFINITION_PATH override. Check the environment override itself so a hosted service cannot silently ignore telephony.
	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 causing golangci-lint to 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 causing golangci-lint to 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

Comment thread cli/azd/extensions/azure.ai.agents/internal/project/service_target_agent.go Outdated
Comment thread cli/azd/extensions/azure.ai.agents/internal/project/service_target_agent.go Outdated
Comment thread cli/azd/extensions/azure.ai.agents/README.md
Copilot AI review requested due to automatic review settings September 4, 2026 08:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 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 lll limit (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

Comment thread cli/azd/extensions/azure.ai.agents/internal/project/service_target_agent.go Outdated
Comment thread cli/azd/extensions/azure.ai.agents/internal/pkg/agents/agent_yaml/parse.go Outdated
Copilot AI review requested due to automatic review settings September 4, 2026 10:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 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 telephony when kind is hosted, despite documenting it as prompt-voice-only. Add a root allOf condition requiring kind: prompt-voice whenever telephony is 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), so lll will reject it. Split the initializer and condition across lines.
  • Files reviewed: 13/13 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

Copilot AI review requested due to automatic review settings September 4, 2026 12:23

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 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 terminal failed state makes azd deploy report 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

Comment thread cli/azd/extensions/azure.ai.agents/internal/project/service_target_agent.go Outdated
Copilot AI review requested due to automatic review settings September 4, 2026 12:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 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.DeepEqual compares the concrete types inside these dynamic maps rather than their JSON values. An on-disk agent.yaml decodes a numeric transfer-target property as an integer, while the GET response decodes the same JSON number as float64, 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:148
  • telephony is declared unconditionally, and none of the root allOf rules requires kind: 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 of telephony that requires kind to be prompt-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-sdk-automation

Copy link
Copy Markdown
Contributor

azure.ai.agents PR build

Note

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"
  • Version: 1.0.0-beta.14.pr.9804.6789986
  • Merge commit: b91104e

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ext-agents azure.ai.agents extension

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support prompt voice agent telephony bindings in azure.yaml

3 participants