Skip to content

Commit 17a37fc

Browse files
Merge branch 'main' into copilot/fix-env-variable-loading
2 parents c5a77b9 + c3eca25 commit 17a37fc

File tree

6 files changed

+64
-28
lines changed

6 files changed

+64
-28
lines changed

dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step15_ComputerUse/Program.cs

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,6 @@ private static async Task InvokeComputerUseAgentAsync(AIAgent agent)
8686
AllowBackgroundResponses = true,
8787
};
8888

89-
AgentSession session = await agent.CreateSessionAsync();
90-
9189
ChatMessage message = new(ChatRole.User, [
9290
new TextContent("I need you to help me search for 'OpenAI news'. Please type 'OpenAI news' and submit the search. Once you see search results, the task is complete."),
9391
new DataContent(new BinaryData(screenshots["browser_search"]), "image/png")
@@ -96,14 +94,18 @@ private static async Task InvokeComputerUseAgentAsync(AIAgent agent)
9694
// Initial request with screenshot - start with Bing search page
9795
Console.WriteLine("Starting computer automation session (initial screenshot: cua_browser_search.png)...");
9896

97+
// IMPORTANT: Computer-use with the Azure Agents API differs from the vanilla OpenAI Responses API.
98+
// The Azure Agents API rejects requests that include previous_response_id alongside
99+
// computer_call_output items. To work around this, each call uses a fresh session (avoiding
100+
// previous_response_id) and re-sends the full conversation context as input items instead.
101+
AgentSession session = await agent.CreateSessionAsync();
99102
AgentResponse response = await agent.RunAsync(message, session: session, options: runOptions);
100103

101104
// Main interaction loop
102105
const int MaxIterations = 10;
103106
int iteration = 0;
104107
// Initialize state machine
105108
SearchState currentState = SearchState.Initial;
106-
string initialCallId = string.Empty;
107109

108110
while (true)
109111
{
@@ -119,6 +121,9 @@ private static async Task InvokeComputerUseAgentAsync(AIAgent agent)
119121
response = await agent.RunAsync(session, runOptions);
120122
}
121123

124+
// Clear the continuation token so the next RunAsync call is a fresh request.
125+
runOptions.ContinuationToken = null;
126+
122127
Console.WriteLine($"Agent response received (ID: {response.ResponseId})");
123128

124129
if (iteration >= MaxIterations)
@@ -148,12 +153,6 @@ private static async Task InvokeComputerUseAgentAsync(AIAgent agent)
148153
ComputerCallAction action = firstComputerCall.Action;
149154
string currentCallId = firstComputerCall.CallId;
150155

151-
// Set the initial computer call ID for tracking and subsequent responses.
152-
if (string.IsNullOrEmpty(initialCallId))
153-
{
154-
initialCallId = currentCallId;
155-
}
156-
157156
Console.WriteLine($"Processing computer call (ID: {currentCallId})");
158157

159158
// Simulate executing the action and taking a screenshot
@@ -162,16 +161,31 @@ private static async Task InvokeComputerUseAgentAsync(AIAgent agent)
162161

163162
Console.WriteLine("Sending action result back to agent...");
164163

165-
AIContent content = new()
164+
// Build the follow-up messages with full conversation context.
165+
// The Azure Agents API rejects previous_response_id when computer_call_output items are
166+
// present, so we must re-send all prior output items (reasoning, computer_call, etc.)
167+
// as input items alongside the computer_call_output to maintain conversation continuity.
168+
List<ChatMessage> followUpMessages = [];
169+
170+
// Re-send all response output items as an assistant message so the API has full context
171+
List<AIContent> priorOutputContents = response.Messages
172+
.SelectMany(m => m.Contents)
173+
.ToList();
174+
followUpMessages.Add(new ChatMessage(ChatRole.Assistant, priorOutputContents));
175+
176+
// Add the computer_call_output as a user message
177+
AIContent callOutput = new()
166178
{
167179
RawRepresentation = new ComputerCallOutputResponseItem(
168-
initialCallId,
180+
currentCallId,
169181
output: ComputerCallOutput.CreateScreenshotOutput(new BinaryData(screenInfo.ImageBytes), "image/png"))
170182
};
183+
followUpMessages.Add(new ChatMessage(ChatRole.User, [callOutput]));
171184

172-
// Follow-up message with action result and new screenshot
173-
message = new(ChatRole.User, [content]);
174-
response = await agent.RunAsync(message, session: session, options: runOptions);
185+
// Create a fresh session so ConversationId does not carry over a previous_response_id.
186+
// Without this, the Azure Agents API returns an error when computer_call_output is present.
187+
session = await agent.CreateSessionAsync();
188+
response = await agent.RunAsync(followUpMessages, session: session, options: runOptions);
175189
}
176190
}
177191
}

dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step15_ComputerUse/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@
22

33
This sample demonstrates how to use the computer use tool with AI agents. The computer use tool allows agents to interact with a computer environment by viewing the screen, controlling the mouse and keyboard, and performing various actions to help complete tasks.
44

5+
> [!NOTE]
6+
> **Azure Agents API vs. vanilla OpenAI Responses API behavior:**
7+
> The Azure Agents API rejects requests that include `previous_response_id` alongside
8+
> `computer_call_output` items — unlike the vanilla OpenAI Responses API, which accepts them.
9+
> This sample works around the limitation by creating a **fresh session for each follow-up call**
10+
> (so no `previous_response_id` is carried over) and re-sending all prior response output items
11+
> (reasoning, computer_call, etc.) as input items to preserve full conversation context.
12+
> Additionally, the sample uses the **current** `CallId` from each computer call response
13+
> (not the initial one) and clears the `ContinuationToken` after polling completes to prevent
14+
> stale tokens from affecting subsequent requests.
15+
516
## What this sample demonstrates
617

718
- Creating agents with computer use capabilities

python/packages/devui/frontend/package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

python/packages/devui/frontend/yarn.lock

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2336,9 +2336,9 @@ tapable@^2.2.0:
23362336
integrity sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==
23372337

23382338
tar@^7.4.3:
2339-
version "7.5.3"
2340-
resolved "https://registry.yarnpkg.com/tar/-/tar-7.5.3.tgz#e1a41236e32446f75e63b720222112c4ffe5b3a1"
2341-
integrity sha512-ENg5JUHUm2rDD7IvKNFGzyElLXNjachNLp6RaGf4+JOgxXHkqA+gq81ZAMCUmtMtqBsoU62lcp6S27g1LCYGGQ==
2339+
version "7.5.9"
2340+
resolved "https://registry.yarnpkg.com/tar/-/tar-7.5.9.tgz#817ac12a54bc4362c51340875b8985d7dc9724b8"
2341+
integrity sha512-BTLcK0xsDh2+PUe9F6c2TlRp4zOOBMTkoQHQIWSIzI0R7KG46uEwq4OPk2W7bZcprBMsuaeFsqwYr7pjh6CuHg==
23422342
dependencies:
23432343
"@isaacs/fs-minipass" "^4.0.0"
23442344
chownr "^3.0.0"

python/samples/02-agents/conversations/redis_chat_message_store_session.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
from agent_framework.redis import RedisHistoryProvider
1010
from dotenv import load_dotenv
1111

12+
# Load environment variables from .env file
13+
load_dotenv()
14+
1215
"""
1316
Redis History Provider Session Example
1417
@@ -17,8 +20,10 @@
1720
with Redis as the backend data store.
1821
"""
1922

20-
# Load environment variables from .env file
21-
load_dotenv()
23+
24+
# Default Redis URL for local Redis Stack.
25+
# Override via the REDIS_URL environment variable for remote or authenticated instances.
26+
REDIS_URL = os.getenv("REDIS_URL", "redis://localhost:6379")
2227

2328

2429
async def example_manual_memory_store() -> None:
@@ -28,7 +33,7 @@ async def example_manual_memory_store() -> None:
2833
# Create Redis history provider
2934
redis_provider = RedisHistoryProvider(
3035
source_id="redis_basic_chat",
31-
redis_url="redis://localhost:6379",
36+
redis_url=REDIS_URL,
3237
)
3338

3439
# Create agent with Redis history provider
@@ -66,7 +71,7 @@ async def example_user_session_management() -> None:
6671
# Create Redis history provider for specific user session
6772
redis_provider = RedisHistoryProvider(
6873
source_id=f"redis_{user_id}",
69-
redis_url="redis://localhost:6379",
74+
redis_url=REDIS_URL,
7075
max_messages=10, # Keep only last 10 messages
7176
)
7277

@@ -107,7 +112,7 @@ async def example_conversation_persistence() -> None:
107112
print("--- Phase 1: Starting conversation ---")
108113
redis_provider = RedisHistoryProvider(
109114
source_id="redis_persistent_chat",
110-
redis_url="redis://localhost:6379",
115+
redis_url=REDIS_URL,
111116
)
112117

113118
agent = OpenAIChatClient().as_agent(
@@ -156,7 +161,7 @@ async def example_session_serialization() -> None:
156161

157162
redis_provider = RedisHistoryProvider(
158163
source_id="redis_serialization_chat",
159-
redis_url="redis://localhost:6379",
164+
redis_url=REDIS_URL,
160165
)
161166

162167
agent = OpenAIChatClient().as_agent(
@@ -198,7 +203,7 @@ async def example_message_limits() -> None:
198203
# Create provider with small message limit
199204
redis_provider = RedisHistoryProvider(
200205
source_id="redis_limited_chat",
201-
redis_url="redis://localhost:6379",
206+
redis_url=REDIS_URL,
202207
max_messages=3, # Keep only 3 most recent messages
203208
)
204209

@@ -233,7 +238,7 @@ async def main() -> None:
233238
print("Redis History Provider Examples")
234239
print("=" * 50)
235240
print("Prerequisites:")
236-
print("- Redis server running on localhost:6379")
241+
print("- Redis server running (set REDIS_URL env var or default localhost:6379)")
237242
print("- OPENAI_API_KEY environment variable set")
238243
print("=" * 50)
239244

0 commit comments

Comments
 (0)