You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
As dev I'm using AI a bit everywhere, not only Azure. Is it supose to work just the same let's say by switch the AzureOpenAIClient to OpenAIClient and specifying my Endpoint?
ex:
// Create the agent using Azure OpenAI.// AIAgent agent = new AzureOpenAIClient(// new Uri(endpoint),// new AzureCliCredential())// .GetChatClient(deploymentName)// .CreateAIAgent(agentOptions);AIAgentagent=newOpenAIClient(newApiKeyCredential(API_KEY),newOpenAIClientOptions{Endpoint=newUri(endpoint)}).GetChatClient(deploymentName).CreateAIAgent(agentOptions);varresponse=awaitagent.RunAsync("Please provide information about John Smith, who is a 35-year-old software engineer.");
There's no need for it to be Azure at all. AIAgent is just an abstract base class, which could be backed by anything. And ChatClientAgent, as one of the most prominent derivations, is just backed by an IChatClient, which has implementations for OpenAI, AWS, Gemini, Anthropic, ONNX Runtime GenAI, Hugging Face, and so on.
I'm having mixed results using a IChatClient implementation from the Anthropic.SDK nuget package. It's mostly works, but when a TurnToken is sent to the agent based on it, it's causing a call to GetStreamingResponseAsync with an empty list of messages which ends up causing an error.
When I switch to using a OpenAI based IChatClient backed by GitHub Models that call to GetStreamingResponseAsync doesn't happen.
I'm going to try to do some more debugging on Monday, but curious if anyone else is seeing something similar.
I'll plan to either post my findings or at least sample code to reproduce the issue here early next week.
Here is some sample code that shows the problem I'm seeing. When trying to create a sequential workflow using WorkflowBuilder and the agent based on Anthropic.SDK a System.Reflection.TargetInvocationException: 'Error invoking handler for Microsoft.Agents.AI.Workflows.TurnToken' is thrown. I can see that when using AgentWorkflowBuilder there is a 3rd executor in the workflow, and my code using WorkflowBuilder only results in a two executer workflow, but that it still works when the underlying IChatClient instances are OpenAIClient I don't think that's the issue.
usingAnthropic.SDK;usingAnthropic.SDK.Constants;usingMicrosoft.Agents.AI;usingMicrosoft.Agents.AI.Workflows;usingMicrosoft.Extensions.AI;usingMicrosoft.Extensions.Configuration;usingMicrosoft.Extensions.DependencyInjection;usingMicrosoft.Extensions.Hosting;usingOpenAI;usingSystem.ClientModel;varhost=Host.CreateApplicationBuilder(newHostApplicationBuilderSettings{EnvironmentName="Development"}).Build();varconfig=host.Services.GetRequiredService<IConfiguration>();vargithubModelsKey=config["GitHubModels:ApiKey"];varopenAIClient=newOpenAIClient(newApiKeyCredential(githubModelsKey),newOpenAIClientOptions{Endpoint=newUri("https://models.inference.ai.azure.com")}).GetChatClient("gpt-4o-mini").AsIChatClient();varanthropicKey=config["Anthropic:ApiKey"];varanthropicClient=newAnthropicClient(newAPIAuthentication(anthropicKey)).Messages.AsBuilder().ConfigureOptions(options =>{options.ModelId=AnthropicModels.Claude35Haiku;options.MaxOutputTokens=512;}).Build();// Define agents using both clientsvaropenAIJoker=newChatClientAgent(openAIClient,instructions:"You like to tell jokes.",name:"Joker");varopenAIAnnoyer=newChatClientAgent(openAIClient,instructions:"You like to explain jokes.",name:"Annoyer");varanthropicJoker=newChatClientAgent(anthropicClient,instructions:"You like to tell jokes.",name:"Joker");varanthropicAnnoyer=newChatClientAgent(anthropicClient,instructions:"You like to explain jokes.",name:"Annoyer");// Define workflows using AgentWorkflowBuildervaropenAIAgentWorkflow=AgentWorkflowBuilder.BuildSequential(openAIJoker,openAIAnnoyer);varanthropicAgentWorkflow=AgentWorkflowBuilder.BuildSequential(anthropicJoker,anthropicAnnoyer);// Define workflows using WorkflowBuildervaropenAIWorkflow=newWorkflowBuilder(openAIJoker).AddEdge(openAIJoker,openAIAnnoyer).Build();varanthropicWorkflow=newWorkflowBuilder(anthropicJoker).AddEdge(anthropicJoker,anthropicAnnoyer).Build();varprompt="Tell me a joke about cats.";// The first three workConsole.WriteLine("OpenAI using Workflow from AgentWorkflowBuilder:");varopenAIAgentResponse=await(awaitopenAIAgentWorkflow.AsAgentAsync()).RunAsync(prompt);Console.WriteLine(openAIAgentResponse.Text);Console.WriteLine();Console.WriteLine("Anthropic using Workflow from AgentWorkflowBuilder:");varanthropicAgentResponse=await(awaitanthropicAgentWorkflow.AsAgentAsync()).RunAsync(prompt);Console.WriteLine(anthropicAgentResponse.Text);Console.WriteLine();Console.WriteLine("OpenAI using Workflow from WorkflowBuilder:");varopenAIWorkflowResponse=await(awaitopenAIWorkflow.AsAgentAsync()).RunAsync(prompt);Console.WriteLine(openAIWorkflowResponse.Text);Console.WriteLine();// But this one throws an exception:Console.WriteLine("Anthropic using Workflow from WorkflowBuilder:");varanthropicWorkflowResponse=await(awaitanthropicWorkflow.AsAgentAsync()).RunAsync(prompt);Console.WriteLine(anthropicWorkflowResponse.Text);Console.WriteLine();
I believe I've found the beginning of tracking down where the problem I'm having is coming from. In AIAgentHostExector.TakeTurnAsync there is a check to ignore any updates that don't have a MessageId set. What's I'm seeing is that when the underlying provider is from Anthropic.SDK the MessageId is always null even when the update is relevant and has content. Because all the response updates are ignored, it ends up sending a TurnToken message with no preceding ChatMessage message.
I'm not sure if that if statement is making a bad assumption about how providers use MessageId, the Anthropic.SDK code isn't, but should be passing along a MessageId, if Anthropic's API just doesn't have this concept of MessageId and so the Anthropic.SDK code should be generating one to be compliant with IChatClient.
AgentWorkflowBuilder.BuildSequential doesn't use AIAgentHostExecutor, because AIAgentHostExecutor currently has a few issues (one of which you noted):
It's ignoring messages that don't have a MessageId set. westey (@westey-m), you were following up on this one I believe?
It's only forwarding on the output messages from the agent but not the input messages, which means the next agent in the conversation doesn't see the full history.
An agent is seeing all output from the previous agent as "Assistant" messages, which can confuse some providers, as the content may not be in a format they understand.
.AddHandler<ChatMessage[]>((messages,_)=>this._pendingMessages.AddRange(messages))// TODO: Remove once https://github.com/microsoft/agent-framework/issues/782 is addressed
.AddHandler<List<ChatMessage>>((messages,_)=>this._pendingMessages.AddRange(messages))// TODO: Remove once https://github.com/microsoft/agent-framework/issues/782 is addressed
that was put in place temporarily to address those issues. I'd like to see us address those issues in AIAgentHostExecutor and then this variation can be removed. cc: Jacob Alber (@lokitoth)
I will find time today to try out your repro and see what's going on.
Thanks Stephen Toub (@stephentoub). I just checked my logs for the streaming responses when using Anthropic's API vs GitHub Models and it looks like Anthropic's response only includes an id property for the message_start event, but not the content_block_delta events, but I don't know if that makes it correct for the updates to not have their MessageId property set, or if Anthropic.SDK should be using the id from the message_start one each update object.
Tao Chen (@TaoChenOSU), it looks like there are some issues with filtering out the updates without message ids for some providers.
I remember we were saying the right fix is to switch to combining updates into messages using the IEnumerable<AgentRunResponseUpdate>.ToAgentRunResponse extension method instead of the custom code currently in Workflows. Is that right?
The issue we were facing was that there could be potentially multiple messages from the agent per run invocation. Without relying on the message ID, we couldn't correctly identify the updates that belong to the same message and build the fully formed message.
I saw that IEnumerable<AgentRunResponseUpdate>.ToAgentRunResponse actually handled that. Could you confirm?
The issue we were facing was that there could be potentially multiple messages from the agent per run invocation. Without relying on the message ID, we couldn't correctly identify the updates that belong to the same message and build the fully formed message.
I saw that IEnumerable<AgentRunResponseUpdate>.ToAgentRunResponse actually handled that. Could you confirm?
ToAgentRunResponse looks for changes in message ID or role to help identify message boundaries (we should also consider updating it to factor in author name).
The issue should have been resolved by PR #1439. Could you please try running your code again using the latest release when it comes out and let us know if it resolves the issue?
The issue should have been resolved by PR #1439. Could you please try running your code again using the latest release when it comes out and let us know if it resolves the issue?
I updated the code to the 1.0.0-preview.25.10.28.1 version of Microsoft.Agents.AI packages and Anthropic.SDK 5.8.0 and all the cases work now. Thanks.
Activity
stephentoub commentedon Oct 3, 2025
There's no need for it to be Azure at all. AIAgent is just an abstract base class, which could be backed by anything. And ChatClientAgent, as one of the most prominent derivations, is just backed by an IChatClient, which has implementations for OpenAI, AWS, Gemini, Anthropic, ONNX Runtime GenAI, Hugging Face, and so on.
https://github.com/microsoft/agent-framework/tree/main/dotnet/samples/GettingStarted/AgentProviders has examples with various providers.
And https://github.com/microsoft/agent-framework/blob/main/dotnet/samples/GettingStarted/Workflows/_Foundational/05_MultiModelService/Program.cs provides an example of multiple providers working together in a workflow.
In your example, yes, you can use OpenAIClient, with whatever Endpoint you want.
mikeholczer commentedon Oct 3, 2025
I'm having mixed results using a IChatClient implementation from the Anthropic.SDK nuget package. It's mostly works, but when a TurnToken is sent to the agent based on it, it's causing a call to GetStreamingResponseAsync with an empty list of messages which ends up causing an error.
When I switch to using a OpenAI based IChatClient backed by GitHub Models that call to GetStreamingResponseAsync doesn't happen.
I'm going to try to do some more debugging on Monday, but curious if anyone else is seeing something similar.
I'll plan to either post my findings or at least sample code to reproduce the issue here early next week.
stephentoub commentedon Oct 4, 2025
That'd be great. There may be some small issues to fix in workflows, in Anthropic.SDK, or both.
fboucher commentedon Oct 4, 2025
I found may issue :')
I had a
Azure.AI.OpenAIin the mix I think there was namespace confusion or something. It works now.mikeholczer commentedon Oct 4, 2025
Here is some sample code that shows the problem I'm seeing. When trying to create a sequential workflow using
WorkflowBuilderand the agent based onAnthropic.SDKaSystem.Reflection.TargetInvocationException: 'Error invoking handler for Microsoft.Agents.AI.Workflows.TurnToken'is thrown. I can see that when usingAgentWorkflowBuilderthere is a 3rd executor in the workflow, and my code usingWorkflowBuilderonly results in a two executer workflow, but that it still works when the underlyingIChatClientinstances areOpenAIClientI don't think that's the issue.mikeholczer commentedon Oct 6, 2025
I believe I've found the beginning of tracking down where the problem I'm having is coming from. In
AIAgentHostExector.TakeTurnAsyncthere is a check to ignore any updates that don't have aMessageIdset. What's I'm seeing is that when the underlying provider is fromAnthropic.SDKtheMessageIdis alwaysnulleven when the update is relevant and has content. Because all the response updates are ignored, it ends up sending a TurnToken message with no preceding ChatMessage message.agent-framework/dotnet/src/Microsoft.Agents.AI.Workflows/Specialized/AIAgentHostExecutor.cs
Lines 52 to 66 in 6285419
I'm not sure if that if statement is making a bad assumption about how providers use
MessageId, theAnthropic.SDKcode isn't, but should be passing along aMessageId, if Anthropic's API just doesn't have this concept ofMessageIdand so theAnthropic.SDKcode should be generating one to be compliant withIChatClient.stephentoub commentedon Oct 6, 2025
Thanks, Michael Holczer (@mikeholczer).
AgentWorkflowBuilder.BuildSequential doesn't use AIAgentHostExecutor, because AIAgentHostExecutor currently has a few issues (one of which you noted):
Instead, it's using this one:
agent-framework/dotnet/src/Microsoft.Agents.AI.Workflows/AgentWorkflowBuilder.cs
Lines 147 to 193 in 6285419
that was put in place temporarily to address those issues. I'd like to see us address those issues in AIAgentHostExecutor and then this variation can be removed. cc: Jacob Alber (@lokitoth)
I will find time today to try out your repro and see what's going on.
mikeholczer commentedon Oct 6, 2025
Thanks Stephen Toub (@stephentoub). I just checked my logs for the streaming responses when using Anthropic's API vs GitHub Models and it looks like Anthropic's response only includes an
idproperty for themessage_startevent, but not thecontent_block_deltaevents, but I don't know if that makes it correct for the updates to not have theirMessageIdproperty set, or ifAnthropic.SDKshould be using theidfrom themessage_startone each update object.westey-m commentedon Oct 6, 2025
Tao Chen (@TaoChenOSU), it looks like there are some issues with filtering out the updates without message ids for some providers.
I remember we were saying the right fix is to switch to combining updates into messages using the
IEnumerable<AgentRunResponseUpdate>.ToAgentRunResponseextension method instead of the custom code currently in Workflows. Is that right?TaoChenOSU commentedon Oct 7, 2025
The issue we were facing was that there could be potentially multiple messages from the agent per run invocation. Without relying on the message ID, we couldn't correctly identify the updates that belong to the same message and build the fully formed message.
I saw that
IEnumerable<AgentRunResponseUpdate>.ToAgentRunResponseactually handled that. Could you confirm?stephentoub commentedon Oct 7, 2025
ToAgentRunResponse looks for changes in message ID or role to help identify message boundaries (we should also consider updating it to factor in author name).
TaoChenOSU commentedon Oct 21, 2025
Hi Michael Holczer (@mikeholczer),
The issue should have been resolved by PR #1439. Could you please try running your code again using the latest release when it comes out and let us know if it resolves the issue?
mikeholczer commentedon Oct 22, 2025
Tao Chen (@TaoChenOSU) Sure. Any idea when that's likely to be?
mikeholczer commentedon Oct 31, 2025
I updated the code to the 1.0.0-preview.25.10.28.1 version of Microsoft.Agents.AI packages and Anthropic.SDK 5.8.0 and all the cases work now. Thanks.
jochenkirstaetter commentedon Dec 5, 2025
Hi Roger Barreto (@rogerbarreto)
Thanks for the consideration of the community-driven NuGet package.
Cheers