Skip to content

Add hosted voice agent support - #9697

Open
JianW (v1212) wants to merge 19 commits into
Azure:mainfrom
v1212:users/wujia/hosted-voice-agent-support
Open

Add hosted voice agent support#9697
JianW (v1212) wants to merge 19 commits into
Azure:mainfrom
v1212:users/wujia/hosted-voice-agent-support

Conversation

@v1212

@v1212 JianW (v1212) commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes #9762.

Adds Hosted Voice Agent support to the azure.ai.agents extension while preserving the existing Hosted Agent and invocations_ws lifecycle.

A Hosted Voice Agent consists of:

  • a normal code-based Hosted Agent target using Voice Bridge over invocations_ws/1.0.0
  • a managed Voice wrapper that owns the Voice endpoint, VAD, STT, TTS, and configurable Voice input/output

The target follows the same code-agent package, provision, deploy, show, and delete patterns as other Hosted Agents. The wrapper is an additional explicit azure.ai.agent service so azd can distinguish Hosted Voice from a generic invocations_ws agent and developers can customize the Voice surface.

Cross-team design context and open questions are documented in the Draft Spec: https://github.com/coreai-microsoft/foundrysdk_specs/pull/322

User experience

Init from a sample azure.yaml

azd ai agent init -m .\path\to\azure.yaml
azd provision
azd deploy

After the sample is published, the local path can be replaced by the public GitHub URL for the sample azure.yaml.

Existing Hosted Voice azd project

cd <existing-hosted-voice-azd-project>
azd ai agent init
azd provision
azd deploy

When azure.yaml already exists, init reuses the existing project configuration. It does not regenerate target/wrapper services; the existing azure.yaml remains the deployment source of truth.

Sample azure.yaml

services:
  ai-project:
    host: azure.ai.project
    deployments:
      - name: gpt-4o-mini
        model:
          format: OpenAI
          name: gpt-4o-mini
          version: '2024-07-18'
        sku:
          name: GlobalStandard
          capacity: 10

  voice-hosted-agent-dotnet:
    host: azure.ai.agent
    kind: hosted
    project: src/voice-hosted-agent-dotnet
    language: csharp
    uses:
      - ai-project
    codeConfiguration:
      runtime: dotnet_10
      entryPoint: VoiceHostedAgent.dll
      dependencyResolution: bundled
    protocols:
      - protocol: invocations_ws
        version: 1.0.0
    metadata:
      voiceLiveCompatible: "true"
      bridgeProtocolVersion: "1.0"
    environmentVariables:
      - name: AZURE_OPENAI_ENDPOINT
        value: ${FOUNDRY_PROJECT_ENDPOINT}/openai/v1/responses
      - name: AZURE_OPENAI_DEPLOYMENT
        value: ${AZURE_AI_MODEL_DEPLOYMENT_NAME}
    container:
      resources:
        cpu: '1'
        memory: 2Gi

  voice-hosted-agent-dotnet-voice:
    host: azure.ai.agent
    kind: prompt-voice
    name: voice-hosted-agent-dotnet-voice
    uses:
      - ai-project
      - voice-hosted-agent-dotnet
    modelType: hosted_agent
    targetAgent:
      service: voice-hosted-agent-dotnet
      version: deployed
    store: false

targetAgent.service is an azd-local reference to the Hosted Agent service that runs the developer code and receives Voice Bridge turns. During deployment, azd resolves it to the deployed target name/version and creates or updates the managed wrapper.

Developers primarily edit target code. They can also configure wrapper-owned Voice settings such as audio formats, transcription, VAD, interruption behavior, output voice/locale/style/speed, modalities, storage, greeting, and avatar where supported.

Validation

  • full Go tests, vet, cspell, Linux/Windows lint, and cross-platform builds
  • init from an empty folder using sample azure.yaml
  • existing Hosted Voice azure.yaml project init reuse
  • West US 2 target and wrapper deployment
  • typed /help routed through the wrapper to target-owned Commands: output
  • real audio STT -> Hosted target -> synthesized output audio
  • Foundry service reads and latest sample Dashboard validation

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 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.

@v1212

Copy link
Copy Markdown
Contributor Author

Final validation status

All automated CI checks are green, including:

  • extension tests
  • cspell
  • go-fix
  • golangci-lint on Linux and Windows
  • provider verification
  • Linux, macOS, and Windows builds
  • Linux/macOS/Windows ARM64 cross-build validation
  • release artifact generation
  • SDL analysis
  • Azure SDK check enforcer: All checks passed

Manual regression validation also passed:

  • Scenario 1: init -m from sample -> target/wrapper deploy -> show/doctor -> typed /help -> real audio -> cleanup
  • Scenario 2: init -m into existing azd project -> existing services preserved -> service-specific deploy -> typed /help -> real audio -> cleanup
  • target-owned /help returned Commands: through the wrapper
  • audio STT recognized Hello. and returned output audio

Review status:

  • unresolved review threads: 0
  • inline review comments: 0
  • pending reviews: 0

The PR remains Draft and therefore still requires normal human review/approval before merge.

@v1212

Copy link
Copy Markdown
Contributor Author

Synced with latest #9634 Voice API surface

PR #9697 now contains the latest #9634 head (4c2447bb3) through the updated #9655 stack.

Hosted Voice adaptation completed:

  • migrated wrapper create/update to the replacement unified Voice API surface
  • retained strict kind=voice, model_type=hosted_agent, and pinned target_agent
  • retained target status/project/protocol/Voice Bridge validation
  • retained init-from-sample and existing-project workflows
  • added explicit output voice locale precedence regression coverage

Validation:

  • go test ./...: PASS
  • go vet ./...: PASS
  • cspell: PASS
  • Linux/Windows golangci-lint: PASS
  • cross-platform build/test pipeline: PASS
  • West US 2 deploy against current TTL project: PASS
  • wrapper /help returned target-owned Commands: response
  • real audio: STT Hello., non-empty response, 5 output audio frames
  • wrapper and target cleaned up

Updated private package:
D:\jwshare\adc-hosted-agent\ws-endpoint\hosted-voice-agent-private-test-share-win-amd64.zip

SHA256:
344fff0c60221e848b1021899a71a95708b37f428f4a6a79d49fcb05a7f2fbfa

Private bundle commit: aba916c6adcf6a58e8ff4d25857bf3d182898872.

@v1212

Copy link
Copy Markdown
Contributor Author

Final standard-manifest regression result — PASS

Hosted Voice init now matches the existing Hosted Agent / invocations_ws experience.

Scenario 1: empty folder + manifest

  • Standard Agent Manifest used (not a custom descriptor).
  • azd ai agent init -m <manifest> created a new azd project and copied the sample under src/voice-hosted-agent-dotnet.
  • Manifest-provided .NET 10, VoiceHostedAgent.dll, bundled dependency mode, invocations_ws/1.0.0, resources, and Voice Bridge metadata were preserved.
  • Target and wrapper were generated.
  • azd deploy --all succeeded.
  • Wrapper /help returned target-owned Commands:.
  • Real audio: STT Hello., non-empty response, 5 output audio frames.

Scenario 2: existing source/project

  • Standard manifest was present in agents/voice under an existing valid azd project.
  • Plain azd ai agent init (no -m) detected the manifest.
  • Parent azure.yaml was updated; existing unrelated service remained unchanged.
  • Target path was agents/voice; no nested azure.yaml was created.
  • Wrapper reused the existing azure.ai.project service and referenced the target.

CI / quality

  • All GitHub checks passed.
  • Linux and Windows golangci-lint passed.
  • Unit/functional tests passed.
  • Linux/macOS/Windows builds and ARM64 validation passed.
  • Azure SDK check enforcer: All checks passed.
  • Review threads: 0.

Private package

D:\jwshare\adc-hosted-agent\ws-endpoint\hosted-voice-agent-private-test-share-win-amd64.zip

SHA256:
763f46f045afb50115970d5f63ed61085d72e67ea5d2b285abeaf746842ac8b2

The public GitHub sample manifest URL remains pending publication. The private package contains the validated standard manifest and sample source.

@v1212

Copy link
Copy Markdown
Contributor Author

Renamed private test package

The final private test ZIP now includes the PR number in its filename:

D:\jwshare\adc-hosted-agent\ws-endpoint\hosted-voice-agent-pr9697-private-test-share-win-amd64.zip

SHA256:

ad1b69db3a738d7033eea16a0128c18d5f4a9cb36dcae83d120d228bab77b2c0

Checksum file:

D:\jwshare\adc-hosted-agent\ws-endpoint\hosted-voice-agent-pr9697-private-test-share-win-amd64.zip.sha256

The previous non-PR-numbered ZIP path has been removed.

@v1212

Copy link
Copy Markdown
Contributor Author

Updated rigorous private test package

D:\jwshare\adc-hosted-agent\ws-endpoint\hosted-voice-agent-pr9697-private-test-share-win-amd64.zip

SHA256:

4d2824539da75715dcebf10abe287f9232324116c1bc3f030d711d97b451b6d7

The Test Plan now includes:

  • exact interactive prompts for both init scenarios
  • explicit gpt-4o-mini model/deployment guidance and invalid choices to avoid
  • independent scenario policy: either scenario for focused testing; both for release regression
  • CLI/service-definition validation
  • deterministic typed /help and real-audio E2E
  • Microsoft Foundry Portal target, wrapper binding, Playground, and microphone validation
  • latest sample Dashboard from voice-first-agent-dev commit a6eaa70c6854581bcaa7cb4290501d3263b971a6
  • Dashboard /config and /agents API checks plus browser typed/microphone validation
  • separate evidence and verdict sections for each scenario

Package validation:

  • latest .NET sample restore passed with bundled Directory.Packages.props
  • latest Dashboard starts and resolves the selected West US 2 backend
  • Dashboard reports Hosted mode and discovers agents
  • package-local localhost Origin guard smoke passed: external Origin rejected with 403; localhost accepted
  • secret/cached artifact scan clean
  • final package readiness review: READY

@v1212
JianW (v1212) force-pushed the users/wujia/hosted-voice-agent-support branch from 20ea3c8 to f9eca38 Compare August 27, 2026 07:06
@v1212
JianW (v1212) marked this pull request as ready for review August 27, 2026 07:06
Copilot AI balanced review requested due to automatic review settings August 27, 2026 07:06
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 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.

Pull request overview

Adds hosted voice wrappers to the Azure AI Agents extension, integrating them with manifest initialization, dependency ordering, deployment validation, and cleanup.

Changes:

  • Adds hosted-agent voice schema and API mappings.
  • Resolves and validates deployed hosted targets through uses.
  • Extends initialization workflows, documentation, and tests.

Reviewed changes

Copilot reviewed 25 out of 25 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
schemas/azure.ai.agent.json Defines hosted voice configuration.
README.md Documents hosted voice usage.
internal/project/service_target_agent.go Deploys and validates wrappers.
internal/project/service_target_agent_test.go Tests inline round-tripping.
internal/project/hosted_voice_target_test.go Tests target resolution and validation.
internal/project/foundry_dependencies.go Resolves deployed target dependencies.
internal/project/agent_policies_test.go Updates voice policy validation fixture.
internal/project/agent_definition.go Maps hosted voice fields.
internal/pkg/agents/agent_yaml/yaml.go Adds authoring model types.
internal/pkg/agents/agent_yaml/parse.go Validates hosted voice manifests.
internal/pkg/agents/agent_yaml/parse_voice_test.go Tests manifest validation.
internal/pkg/agents/agent_yaml/map.go Builds hosted wrapper API requests.
internal/pkg/agents/agent_yaml/map_voice_test.go Tests request serialization.
internal/pkg/agents/agent_api/models.go Adds wire-model target references.
internal/cmd/listen.go Deep-clones service configuration.
internal/cmd/init.go Integrates hosted voice initialization.
internal/cmd/init_test.go Tests manifest code configuration.
internal/cmd/init_hosted_voice.go Detects compatible manifests.
internal/cmd/init_hosted_voice_test.go Tests manifest detection.
internal/cmd/init_from_templates_helpers.go Adds the interactive init choice.
internal/cmd/init_from_templates_helpers_test.go Tests hosted voice selection.
internal/cmd/init_from_code.go Generates target and wrapper services.
internal/cmd/init_from_code_test.go Tests wrapper generation.
internal/cmd/delete.go Cleans target pin variables.
internal/cmd/delete_test.go Tests marker cleanup.

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread cli/azd/extensions/azure.ai.agents/README.md Outdated
Comment thread cli/azd/extensions/azure.ai.agents/README.md Outdated
Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/init.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/internal/cmd/init_from_code.go Outdated
Copilot AI review requested due to automatic review settings August 27, 2026 07:35

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.

Pull request overview

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

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

cli/azd/extensions/azure.ai.agents/internal/cmd/init_hosted_voice.go:42

  • fmt.Sprint accepts voiceLiveCompatible: true as compatible, but the deployment mapper only copies string metadata values (agent_yaml/map.go:899-905). Such a manifest gets a wrapper, then deploys the target without this marker and the wrapper fails its post-deploy compatibility check. Normalize these marker values to strings before deployment, or reject non-string metadata during detection.
	voiceCompatible := strings.EqualFold(strings.TrimSpace(fmt.Sprint(metadata["voiceLiveCompatible"])), "true")
	bridgeVersion := strings.TrimSpace(fmt.Sprint(metadata["bridgeProtocolVersion"]))

Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/init.go Outdated
Copilot AI review requested due to automatic review settings August 27, 2026 07:48

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.

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated 2 comments.

Suppressed comments (5)

Previously missed (3) — in code that hasn't changed since the last review.

cli/azd/extensions/azure.ai.agents/internal/project/foundry_dependencies.go:465

  • This added line is 126 characters and will fail the repository's 125-character lll check (cli/azd/AGENTS.md:115-126). Wrap the call before merging.
		return nil, fmt.Errorf("hosted voice target service %q is deployed to a different Foundry project", targetServiceName)

cli/azd/extensions/azure.ai.agents/internal/pkg/agents/agent_yaml/parse.go:456

  • This added line is 130 characters and exceeds the repository's 125-character Go limit (cli/azd/AGENTS.md:115-126), so lll will reject it.

This issue also appears in the following locations of the same file:

  • line 458
  • line 467
							errors = append(errors, "template.target_agent.service is required when model_type is 'hosted_agent'")

cli/azd/extensions/azure.ai.agents/internal/pkg/agents/agent_yaml/map.go:765

  • This added line is 192 characters and exceeds the repository's 125-character Go limit (cli/azd/AGENTS.md:115-126), so lll will reject it.
			return nil, fmt.Errorf("model, instructions, structured_inputs, tools, tool_choice, parallel_tool_calls, max_output_tokens, include, and handoff belong to the target hosted agent")

cli/azd/extensions/azure.ai.agents/internal/pkg/agents/agent_yaml/parse.go:458

  • This condition is 131 characters and exceeds the repository's 125-character Go limit (cli/azd/AGENTS.md:115-126). Split it so the enforced lll check passes.
						if agent.TargetAgent != nil && agent.TargetAgent.Version != "" && agent.TargetAgent.Version != "deployed" {

cli/azd/extensions/azure.ai.agents/internal/pkg/agents/agent_yaml/parse.go:467

  • This added line is 202 characters and exceeds the repository's 125-character Go limit (cli/azd/AGENTS.md:115-126), so lll will reject it.
							errors = append(errors, "instructions, structured_inputs, tools, tool_choice, parallel_tool_calls, max_output_tokens, include, and handoff belong to the target hosted agent")

Comment thread cli/azd/extensions/azure.ai.agents/internal/pkg/agents/agent_yaml/yaml.go Outdated
Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/init_from_code.go Outdated
Copilot AI review requested due to automatic review settings August 27, 2026 08:06

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

Both documented initialization paths omit wrapper generation, and adoption can misclassify supported service shapes.

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/cmd/init.go:2096

  • The manifest-driven flow still persists only the hosted target. After this merge it falls through to addToProject, which adds one hosted service and never creates the prompt-voice wrapper documented in the README and required by #9762. Add hosted-voice manifest detection and create the wrapper (including its project/target uses edges) before init completes.
				if hostedAgent, ok := agentManifest.Template.(agent_yaml.ContainerAgent); ok &&
  • Files reviewed: 21/21 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/init_adopt.go Outdated
Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/init_from_code.go
Copilot AI review requested due to automatic review settings September 2, 2026 07:57
@v1212
JianW (v1212) force-pushed the users/wujia/hosted-voice-agent-support branch from 51644a5 to 0429bfb Compare September 2, 2026 07:57

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

Hosted Voice initialization and validation still have blocking gaps.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 21/21 changed files
  • Comments generated: 3
  • Review effort level: Balanced

Comment thread cli/azd/extensions/azure.ai.agents/README.md Outdated
Comment thread cli/azd/extensions/azure.ai.agents/schemas/azure.ai.agent.json Outdated
Copilot AI review requested due to automatic review settings September 2, 2026 08:16

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

Adoption can misclassify referenced or legacy voice wrappers and overwrite them with hosted deployment settings.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 21/21 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/init_adopt.go Outdated
Copilot AI review requested due to automatic review settings September 2, 2026 08:49

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

Wrapper-level protocols currently pass schema validation but are silently discarded during deployment.

Review details

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

cli/azd/extensions/azure.ai.agents/schemas/azure.ai.agent.json:229

  • Reject protocols as a target-owned field here. The schema currently accepts wrapper-level protocols, but AgentDefinitionInline.toVoiceAgent omits Protocols, so azd silently discards the setting even though the hosted target is the component that must declare invocations_ws/1.0.0. Add matching schema and Go validation before the inline/standalone definitions are converted.
  • Files reviewed: 21/21 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

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 Hosted Voice Agents in azd

4 participants