Add realtime voice agent samples - #50410
Conversation
|
One or more custom setup steps configured for this repository failed during this Copilot code review run: Setup steps run before each review. If the review above is missing context, or no review was posted at all, the failing step above may be the cause. See the workflow run for failure details, fix your setup steps configuration, and re-request a review. Note You can configure setup steps for Copilot code review separately from Copilot cloud agent with a |
There was a problem hiding this comment.
🟡 Changes recommended
One or more issues must be addressed before approval.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds preview voice-agent lifecycle, persistence, realtime text/audio, and function-tool samples for azure-ai-agents, with README documentation.
Changes:
- Adds synchronous and asynchronous voice-agent samples.
- Adds realtime WebSocket, audio playback, persistence, and tool examples.
- Documents clients, configuration, and sample links in the README.
File summaries
| File | Description |
|---|---|
| sdk/ai/azure-ai-agents/src/samples/java/com/azure/ai/agents/voice/VoiceAgentWithToolsSample.java | Updated as part of this pull request. |
| sdk/ai/azure-ai-agents/src/samples/java/com/azure/ai/agents/voice/VoiceAgentVersionsSample.java | Updated as part of this pull request. |
| sdk/ai/azure-ai-agents/src/samples/java/com/azure/ai/agents/voice/VoiceAgentSampleUtils.java | Updated as part of this pull request. |
| sdk/ai/azure-ai-agents/src/samples/java/com/azure/ai/agents/voice/VoiceAgentRealtimeSampleUtils.java | Updated as part of this pull request. |
| sdk/ai/azure-ai-agents/src/samples/java/com/azure/ai/agents/voice/VoiceAgentReadConversationSample.java | Updated as part of this pull request. |
| sdk/ai/azure-ai-agents/src/samples/java/com/azure/ai/agents/voice/VoiceAgentReadConversationAudioSample.java | Updated as part of this pull request. |
| sdk/ai/azure-ai-agents/src/samples/java/com/azure/ai/agents/voice/VoiceAgentLiveTextConversationSample.java | Updated as part of this pull request. |
| sdk/ai/azure-ai-agents/src/samples/java/com/azure/ai/agents/voice/VoiceAgentLiveTextConversationAsyncSample.java | Updated as part of this pull request. |
| sdk/ai/azure-ai-agents/src/samples/java/com/azure/ai/agents/voice/VoiceAgentLiveFunctionToolSample.java | Updated as part of this pull request. |
| sdk/ai/azure-ai-agents/src/samples/java/com/azure/ai/agents/voice/VoiceAgentLiveAudioConversationAsyncSample.java | Updated as part of this pull request. |
| sdk/ai/azure-ai-agents/src/samples/java/com/azure/ai/agents/voice/VoiceAgentGenerateSample.java | Updated as part of this pull request. |
| sdk/ai/azure-ai-agents/src/samples/java/com/azure/ai/agents/voice/VoiceAgentBasicSample.java | Updated as part of this pull request. |
| sdk/ai/azure-ai-agents/src/samples/java/com/azure/ai/agents/voice/VoiceAgentBasicAsyncSample.java | Updated as part of this pull request. |
| sdk/ai/azure-ai-agents/README.md | Updated as part of this pull request. |
Review details
Suppressed comments (12)
sdk/ai/azure-ai-agents/README.md:961
- This snippet only handles
RealtimeServerEventResponseTextDelta, but the lifecycle samples create voice agents with audio output (and audio is the voice definition default). Following the documented setup therefore reachesresponse.donewithout printing the agent reply; either configure the example agent/response forTEXTor handle the audio/transcript events and document that requirement.
if (event instanceof RealtimeServerEventResponseTextDelta) {
System.out.print(((RealtimeServerEventResponseTextDelta) event).getDelta());
sdk/ai/azure-ai-agents/README.md:190
- The paragraph above says every
Beta*Clientis built fromAgentsClientBuilder.beta(), butBetaAgentEndpointConversationsClientis not exposed by that sub-builder; the actual API isbuilder.buildBetaAgentEndpointConversationsClient()(and the async equivalent). As written, this table sends readers to a nonexistent construction path for this newly documented client.
| `BetaAgentEndpointConversationsClient` | `VoiceAgents=V1Preview` |
sdk/ai/azure-ai-agents/src/samples/java/com/azure/ai/agents/voice/VoiceAgentBasicAsyncSample.java:61
- The normal and error cleanup paths delete the entire caller-selected agent, although
createAgentVersioncan add a version to an agent that already exists. ReusingFOUNDRY_VOICE_AGENT_NAMEtherefore destroys the existing agent instead of only cleaning up this sample's resources; use a unique/owned name or delete only resources created here.
.then(client.deleteAgent(agentName))
.onErrorResume(error -> client.deleteAgent(agentName)
.onErrorResume(cleanupError -> Mono.empty())
.then(Mono.error(error)))
sdk/ai/azure-ai-agents/src/samples/java/com/azure/ai/agents/voice/VoiceAgentBasicSample.java:64
- This cleanup deletes the entire
agentName, butFOUNDRY_VOICE_AGENT_NAMEis caller-configurable andcreateAgentVersiononly adds a version to the named agent; it does not prove that the sample owns that agent. If the name already exists, the sample removes the caller's existing agent and all of its versions. Use a unique default/ownership check and only delete an agent created by this sample.
} finally {
client.deleteAgent(agentName);
System.out.println("Deleted voice agent: " + agentName);
sdk/ai/azure-ai-agents/src/samples/java/com/azure/ai/agents/voice/VoiceAgentLiveAudioConversationAsyncSample.java:164
- This declaration is 122 characters long, exceeding the repository's 120-character Checkstyle limit, so the sample will fail the style check. Wrap the initializer onto the next line.
private final AudioFormat format = new AudioFormat(VoiceAgentRealtimeSampleUtils.SAMPLE_RATE, 16, 1, true, false);
sdk/ai/azure-ai-agents/src/samples/java/com/azure/ai/agents/voice/VoiceAgentLiveAudioConversationAsyncSample.java:261
- The worker threads are only interrupted after the microphone and speaker have been stopped, closed, and nulled. A playback worker can already have taken a queued chunk and then execute
speaker.write(...)against the closed/null field, producing an uncaught exception during normal shutdown;close()also returns without waiting for either worker to terminate. Stop and join the workers before releasing the lines, with safe visibility for the shared fields.
if (captureThread != null) {
captureThread.interrupt();
}
if (playbackThread != null) {
playbackThread.interrupt();
sdk/ai/azure-ai-agents/src/samples/java/com/azure/ai/agents/voice/VoiceAgentLiveAudioConversationAsyncSample.java:104
- This error handler deletes
agentNameeven for failures fromgenerateAgent, before the pipeline has established that it owns that resource. With a caller-supplied name, a generation/auth/network failure can therefore delete an unrelated existing agent; track ownership after successful creation and skip cleanup unless this run created the agent.
.then(Mono.defer(() -> cleanupAgent(agents, agentName, keepAgent)))
.onErrorResume(error -> Mono.defer(() -> cleanupAgent(agents, agentName, keepAgent))
.onErrorResume(cleanupError -> Mono.empty())
.then(Mono.error(error)))
sdk/ai/azure-ai-agents/src/samples/java/com/azure/ai/agents/voice/VoiceAgentLiveFunctionToolSample.java:113
- This
finallydeletes the entire caller-selected agent even whencreateAgentVersionmerely adds a version to an existing agent. A user who pointsFOUNDRY_VOICE_AGENT_NAMEat an existing resource will lose that agent and its other versions; use a unique/owned name or clean up only the version created here.
} finally {
agents.deleteAgent(agentName);
System.out.println("Deleted voice agent: " + agentName);
sdk/ai/azure-ai-agents/src/samples/java/com/azure/ai/agents/voice/VoiceAgentLiveTextConversationAsyncSample.java:92
- This error handler deletes
agentNameeven for failures fromgenerateAgent, before the pipeline has established that it owns that resource. With a caller-supplied name, a generation/auth/network failure can therefore delete an unrelated existing agent; track ownership after successful creation and skip cleanup unless this run created the agent.
.then(Mono.defer(() -> cleanupAgent(agents, agentName, keepAgent)))
.onErrorResume(error -> Mono.defer(() -> cleanupAgent(agents, agentName, keepAgent))
.onErrorResume(cleanupError -> Mono.empty())
.then(Mono.error(error)))
sdk/ai/azure-ai-agents/src/samples/java/com/azure/ai/agents/voice/VoiceAgentRealtimeSampleUtils.java:124
SpeakerPlayer.playperforms a potentially blockingSourceDataLine.write. InVoiceAgentLiveTextConversationAsyncSample, this method is called directly from thereceiveEvents()subscriber, and the async WebSocket emits events from its Netty inbound callback. A stalled or slow speaker can therefore block the WebSocket event loop, preventing response frames such asresponse.donefrom being consumed and making the sample hang or overflow the session buffer. Move playback to a dedicated worker/queue or shift the subscriber off the event loop.
line.write(pcm, 0, pcm.length);
sdk/ai/azure-ai-agents/src/samples/java/com/azure/ai/agents/voice/VoiceAgentVersionsSample.java:66
- This unconditional cleanup deletes the entire caller-selected agent, but
createAgentVersioncan add a version to an existing agent. IfFOUNDRY_VOICE_AGENT_NAMEpoints at an existing resource, the versions sample removes that resource and all of its other versions; use a unique/owned name or clean up only versions created by the sample.
} finally {
client.deleteAgent(agentName);
sdk/ai/azure-ai-agents/src/samples/java/com/azure/ai/agents/voice/VoiceAgentWithToolsSample.java:108
agentCreatedonly records that the version call succeeded; it does not establish that this sample created the agent. When the configured name already exists, this code adds a version and then deletes the pre-existing agent infinally. Use a unique/owned name or preserve existing agents and remove only the sample's version.
} finally {
if (agentCreated) {
client.deleteAgent(agentName);
}
- Files reviewed: 14/14 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
cb05d1d to
766aa06
Compare
|
Azure Pipelines: Successfully started running 278 pipeline(s). 36 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
1 similar comment
|
Azure Pipelines: Successfully started running 278 pipeline(s). 36 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
aab3a8e to
8d0c7dc
Compare
8d0c7dc to
4425d7a
Compare
4425d7a to
8bf6170
Compare
8bf6170 to
c558890
Compare
c558890 to
eba1b01
Compare
eba1b01 to
47aa527
Compare
47aa527 to
62cc033
Compare
ea5191f to
caca7ea
Compare
caca7ea to
b482fb9
Compare
b482fb9 to
34ece4b
Compare
34ece4b to
32a7817
Compare
4ce5825 to
7f514e1
Compare
7f514e1 to
91fd3ef
Compare
91fd3ef to
b97bf92
Compare
Description
Top PR in stack #50413, based on #50409. Adds focused synchronous and asynchronous voice-agent samples and links them from the Agents README.
Examples cover agent lifecycle and versions, generated definitions, tool configuration, persisted conversations/audio, realtime text, realtime audio, and client-side function tools.
Stack:
Validation:
mvn -q -f sdk/ai/azure-ai-agents/pom.xml -DskipTests test-compile checkstyle:checkAll SDK Contribution checklist:
General Guidelines and Best Practices
Testing Guidelines