.NET [Breaking] Simplify and Refactor ChatclientAgentOptions Ctor + Instructions (#1517)

* Point AgentOptions.Instructions to ChatOptions

* Update tests and checks

* Update xml docs

* Removal of agentOptions.Instructions in favor of chatOptions.Instructions

* Instructions and tool check consistency

* Instructions and tool check consistency

* Address comment

* Update .github/upgrades/prompts/SemanticKernelToAgentFramework.md

Co-authored-by: westey <164392973+westey-m@users.noreply.github.com>

* Address PR Comment

* Update latest changes to comply with the PR proposal

* Address feedback

* Update dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgentTests.cs

Co-authored-by: westey <164392973+westey-m@users.noreply.github.com>

* Address instructions

* Update declarative to use promptAgent.Instrucitons with chatOptions.Instructions

---------

Co-authored-by: westey <164392973+westey-m@users.noreply.github.com>
Co-authored-by: Mark Wallace <127216156+markwallace-microsoft@users.noreply.github.com>
This commit is contained in:
Roger Barreto
2025-12-03 13:39:47 +00:00
committed by GitHub
Unverified
parent e9ab514696
commit 6232dd8305
53 changed files with 393 additions and 365 deletions
@@ -1636,4 +1636,4 @@ The property mapping guide from a `AutoFunctionInvocationContext` to a `Function
| Result | Use `return` from the delegate |
| Terminate | Terminate |
| CancellationToken | provided via argument to middleware delegate |
| Arguments | Arguments |
| Arguments | Arguments |
@@ -32,7 +32,7 @@ AIAgent agent = new AzureOpenAIClient(
.GetChatClient(deploymentName)
.CreateAIAgent(new ChatClientAgentOptions
{
Instructions = "You are good at telling jokes.",
ChatOptions = new() { Instructions = "You are good at telling jokes." },
Name = "Joker",
AIContextProviderFactory = (ctx) => new ChatHistoryMemoryProvider(
vectorStore,
@@ -30,7 +30,7 @@ AIAgent agent = new AzureOpenAIClient(
.GetChatClient(deploymentName)
.CreateAIAgent(new ChatClientAgentOptions()
{
Instructions = "You are a friendly travel assistant. Use known memories about the user when responding, and do not invent details.",
ChatOptions = new() { Instructions = "You are a friendly travel assistant. Use known memories about the user when responding, and do not invent details." },
AIContextProviderFactory = ctx => ctx.SerializedState.ValueKind is not JsonValueKind.Null and not JsonValueKind.Undefined
// If each thread should have its own Mem0 scope, you can create a new id per thread here:
// ? new Mem0Provider(mem0HttpClient, new Mem0ProviderScope() { ThreadId = Guid.NewGuid().ToString() })
@@ -33,7 +33,7 @@ ChatClient chatClient = new AzureOpenAIClient(
// and its storage to that user id.
AIAgent agent = chatClient.CreateAIAgent(new ChatClientAgentOptions()
{
Instructions = "You are a friendly assistant. Always address the user by their name.",
ChatOptions = new() { Instructions = "You are a friendly assistant. Always address the user by their name." },
AIContextProviderFactory = ctx => new UserInfoMemory(chatClient.AsIChatClient(), ctx.SerializedState, ctx.JsonSerializerOptions)
});
@@ -62,7 +62,7 @@ AIAgent agent = azureOpenAIClient
.GetChatClient(deploymentName)
.CreateAIAgent(new ChatClientAgentOptions
{
Instructions = "You are a helpful support specialist for Contoso Outdoors. Answer questions using the provided context and cite the source document when available.",
ChatOptions = new() { Instructions = "You are a helpful support specialist for Contoso Outdoors. Answer questions using the provided context and cite the source document when available." },
AIContextProviderFactory = ctx => new TextSearchProvider(SearchAdapter, ctx.SerializedState, ctx.JsonSerializerOptions, textSearchOptions)
});
@@ -71,7 +71,7 @@ AIAgent agent = azureOpenAIClient
.GetChatClient(deploymentName)
.CreateAIAgent(new ChatClientAgentOptions
{
Instructions = "You are a helpful support specialist for the Microsoft Agent Framework. Answer questions using the provided context and cite the source document when available. Keep responses brief.",
ChatOptions = new() { Instructions = "You are a helpful support specialist for the Microsoft Agent Framework. Answer questions using the provided context and cite the source document when available. Keep responses brief." },
AIContextProviderFactory = ctx => new TextSearchProvider(SearchAdapter, ctx.SerializedState, ctx.JsonSerializerOptions, textSearchOptions)
});
@@ -29,7 +29,7 @@ AIAgent agent = new AzureOpenAIClient(
.GetChatClient(deploymentName)
.CreateAIAgent(new ChatClientAgentOptions
{
Instructions = "You are a helpful support specialist for Contoso Outdoors. Answer questions using the provided context and cite the source document when available.",
ChatOptions = new() { Instructions = "You are a helpful support specialist for Contoso Outdoors. Answer questions using the provided context and cite the source document when available." },
AIContextProviderFactory = ctx => new TextSearchProvider(MockSearchAsync, ctx.SerializedState, ctx.JsonSerializerOptions, textSearchOptions)
});
@@ -22,7 +22,7 @@ ChatClient chatClient = new AzureOpenAIClient(
.GetChatClient(deploymentName);
// Create the ChatClientAgent with the specified name and instructions.
ChatClientAgent agent = chatClient.CreateAIAgent(new ChatClientAgentOptions(name: "HelpfulAssistant", instructions: "You are a helpful assistant."));
ChatClientAgent agent = chatClient.CreateAIAgent(name: "HelpfulAssistant", instructions: "You are a helpful assistant.");
// Set PersonInfo as the type parameter of RunAsync method to specify the expected structured output from the agent and invoke the agent with some unstructured input.
AgentRunResponse<PersonInfo> response = await agent.RunAsync<PersonInfo>("Please provide information about John Smith, who is a 35-year-old software engineer.");
@@ -34,12 +34,10 @@ Console.WriteLine($"Age: {response.Result.Age}");
Console.WriteLine($"Occupation: {response.Result.Occupation}");
// Create the ChatClientAgent with the specified name, instructions, and expected structured output the agent should produce.
ChatClientAgent agentWithPersonInfo = chatClient.CreateAIAgent(new ChatClientAgentOptions(name: "HelpfulAssistant", instructions: "You are a helpful assistant.")
ChatClientAgent agentWithPersonInfo = chatClient.CreateAIAgent(new ChatClientAgentOptions()
{
ChatOptions = new()
{
ResponseFormat = Microsoft.Extensions.AI.ChatResponseFormat.ForJsonSchema<PersonInfo>()
}
Name = "HelpfulAssistant",
ChatOptions = new() { Instructions = "You are a helpful assistant.", ResponseFormat = Microsoft.Extensions.AI.ChatResponseFormat.ForJsonSchema<PersonInfo>() }
});
// Invoke the agent with some unstructured input while streaming, to extract the structured information from.
@@ -28,7 +28,7 @@ AIAgent agent = new AzureOpenAIClient(
.GetChatClient(deploymentName)
.CreateAIAgent(new ChatClientAgentOptions
{
Instructions = "You are good at telling jokes.",
ChatOptions = new() { Instructions = "You are good at telling jokes." },
Name = "Joker",
ChatMessageStoreFactory = ctx =>
{
@@ -18,8 +18,7 @@ var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
// Add agent options to the service collection.
builder.Services.AddSingleton(
new ChatClientAgentOptions(instructions: "You are good at telling jokes.", name: "Joker"));
builder.Services.AddSingleton(new ChatClientAgentOptions() { Name = "Joker", ChatOptions = new() { Instructions = "You are good at telling jokes." } });
// Add a chat client to the service collection.
builder.Services.AddKeyedChatClient("AzureOpenAI", (sp) => new AzureOpenAIClient(
@@ -21,7 +21,7 @@ AIAgent agent = new AzureOpenAIClient(
.GetChatClient(deploymentName)
.CreateAIAgent(new ChatClientAgentOptions
{
Instructions = "You are good at telling jokes.",
ChatOptions = new() { Instructions = "You are good at telling jokes." },
Name = "Joker",
ChatMessageStoreFactory = ctx => new InMemoryChatMessageStore(new MessageCountingChatReducer(2), ctx.SerializedState, ctx.JsonSerializerOptions)
});
@@ -24,10 +24,12 @@ AIProjectClient aiProjectClient = new(new Uri(endpoint), new AzureCliCredential(
// Create ChatClientAgent directly
ChatClientAgent agent = await aiProjectClient.CreateAIAgentAsync(
model: deploymentName,
new ChatClientAgentOptions(name: AssistantName, instructions: AssistantInstructions)
new ChatClientAgentOptions()
{
Name = AssistantName,
ChatOptions = new()
{
Instructions = AssistantInstructions,
ResponseFormat = Microsoft.Extensions.AI.ChatResponseFormat.ForJsonSchema<PersonInfo>()
}
});
@@ -44,10 +46,12 @@ Console.WriteLine($"Occupation: {response.Result.Occupation}");
// Create the ChatClientAgent with the specified name, instructions, and expected structured output the agent should produce.
ChatClientAgent agentWithPersonInfo = aiProjectClient.CreateAIAgent(
model: deploymentName,
new ChatClientAgentOptions(name: AssistantName, instructions: AssistantInstructions)
new ChatClientAgentOptions()
{
Name = AssistantName,
ChatOptions = new()
{
Instructions = AssistantInstructions,
ResponseFormat = Microsoft.Extensions.AI.ChatResponseFormat.ForJsonSchema<PersonInfo>()
}
});
@@ -34,9 +34,9 @@ AIAgent agent = await persistentAgentsClient.CreateAIAgentAsync(
options: new()
{
Name = "MicrosoftLearnAgent",
Instructions = "You answer questions by searching the Microsoft Learn content only.",
ChatOptions = new()
{
Instructions = "You answer questions by searching the Microsoft Learn content only.",
Tools = [mcpTool]
},
});
@@ -67,9 +67,9 @@ AIAgent agentWithRequiredApproval = await persistentAgentsClient.CreateAIAgentAs
options: new()
{
Name = "MicrosoftLearnAgentWithApproval",
Instructions = "You answer questions by searching the Microsoft Learn content only.",
ChatOptions = new()
{
Instructions = "You answer questions by searching the Microsoft Learn content only.",
Tools = [mcpToolWithApproval]
},
});
@@ -118,10 +118,11 @@ internal sealed class SloganWriterExecutor : Executor
/// <param name="chatClient">The chat client to use for the AI agent.</param>
public SloganWriterExecutor(string id, IChatClient chatClient) : base(id)
{
ChatClientAgentOptions agentOptions = new(instructions: "You are a professional slogan writer. You will be given a task to create a slogan.")
ChatClientAgentOptions agentOptions = new()
{
ChatOptions = new()
{
Instructions = "You are a professional slogan writer. You will be given a task to create a slogan.",
ResponseFormat = ChatResponseFormat.ForJsonSchema<SloganResult>()
}
};
@@ -193,10 +194,11 @@ internal sealed class FeedbackExecutor : Executor<SloganResult>
/// <param name="chatClient">The chat client to use for the AI agent.</param>
public FeedbackExecutor(string id, IChatClient chatClient) : base(id)
{
ChatClientAgentOptions agentOptions = new(instructions: "You are a professional editor. You will be given a slogan and the task it is meant to accomplish.")
ChatClientAgentOptions agentOptions = new()
{
ChatOptions = new()
{
Instructions = "You are a professional editor. You will be given a slogan and the task it is meant to accomplish.",
ResponseFormat = ChatResponseFormat.ForJsonSchema<FeedbackResult>()
}
};
@@ -85,10 +85,11 @@ public static class Program
/// </summary>
/// <returns>A ChatClientAgent configured for spam detection</returns>
private static ChatClientAgent GetSpamDetectionAgent(IChatClient chatClient) =>
new(chatClient, new ChatClientAgentOptions(instructions: "You are a spam detection assistant that identifies spam emails.")
new(chatClient, new ChatClientAgentOptions()
{
ChatOptions = new()
{
Instructions = "You are a spam detection assistant that identifies spam emails.",
ResponseFormat = ChatResponseFormat.ForJsonSchema<DetectionResult>()
}
});
@@ -98,10 +99,11 @@ public static class Program
/// </summary>
/// <returns>A ChatClientAgent configured for email assistance</returns>
private static ChatClientAgent GetEmailAssistantAgent(IChatClient chatClient) =>
new(chatClient, new ChatClientAgentOptions(instructions: "You are an email assistant that helps users draft responses to emails with professionalism.")
new(chatClient, new ChatClientAgentOptions()
{
ChatOptions = new()
{
Instructions = "You are an email assistant that helps users draft responses to emails with professionalism.",
ResponseFormat = ChatResponseFormat.ForJsonSchema<EmailResponse>()
}
});
@@ -100,10 +100,11 @@ public static class Program
/// </summary>
/// <returns>A ChatClientAgent configured for spam detection</returns>
private static ChatClientAgent GetSpamDetectionAgent(IChatClient chatClient) =>
new(chatClient, new ChatClientAgentOptions(instructions: "You are a spam detection assistant that identifies spam emails. Be less confident in your assessments.")
new(chatClient, new ChatClientAgentOptions()
{
ChatOptions = new()
{
Instructions = "You are a spam detection assistant that identifies spam emails. Be less confident in your assessments.",
ResponseFormat = ChatResponseFormat.ForJsonSchema<DetectionResult>()
}
});
@@ -113,10 +114,11 @@ public static class Program
/// </summary>
/// <returns>A ChatClientAgent configured for email assistance</returns>
private static ChatClientAgent GetEmailAssistantAgent(IChatClient chatClient) =>
new(chatClient, new ChatClientAgentOptions(instructions: "You are an email assistant that helps users draft responses to emails with professionalism.")
new(chatClient, new ChatClientAgentOptions()
{
ChatOptions = new()
{
Instructions = "You are an email assistant that helps users draft responses to emails with professionalism.",
ResponseFormat = ChatResponseFormat.ForJsonSchema<EmailResponse>()
}
});
@@ -140,10 +140,11 @@ public static class Program
/// </summary>
/// <returns>A ChatClientAgent configured for email analysis</returns>
private static ChatClientAgent GetEmailAnalysisAgent(IChatClient chatClient) =>
new(chatClient, new ChatClientAgentOptions(instructions: "You are a spam detection assistant that identifies spam emails.")
new(chatClient, new ChatClientAgentOptions()
{
ChatOptions = new()
{
Instructions = "You are a spam detection assistant that identifies spam emails.",
ResponseFormat = ChatResponseFormat.ForJsonSchema<AnalysisResult>()
}
});
@@ -153,10 +154,11 @@ public static class Program
/// </summary>
/// <returns>A ChatClientAgent configured for email assistance</returns>
private static ChatClientAgent GetEmailAssistantAgent(IChatClient chatClient) =>
new(chatClient, new ChatClientAgentOptions(instructions: "You are an email assistant that helps users draft responses to emails with professionalism.")
new(chatClient, new ChatClientAgentOptions()
{
ChatOptions = new()
{
Instructions = "You are an email assistant that helps users draft responses to emails with professionalism.",
ResponseFormat = ChatResponseFormat.ForJsonSchema<EmailResponse>()
}
});
@@ -166,10 +168,11 @@ public static class Program
/// </summary>
/// <returns>A ChatClientAgent configured for email summarization</returns>
private static ChatClientAgent GetEmailSummaryAgent(IChatClient chatClient) =>
new(chatClient, new ChatClientAgentOptions(instructions: "You are an assistant that helps users summarize emails.")
new(chatClient, new ChatClientAgentOptions()
{
ChatOptions = new()
{
Instructions = "You are an assistant that helps users summarize emails.",
ResponseFormat = ChatResponseFormat.ForJsonSchema<EmailSummary>()
}
});
@@ -285,19 +285,19 @@ internal sealed class CriticExecutor : Executor<ChatMessage, CriticDecision>
this._agent = new ChatClientAgent(chatClient, new ChatClientAgentOptions
{
Name = "Critic",
Instructions = """
You are a constructive critic. Review the content and provide specific feedback.
Always try to provide actionable suggestions for improvement and strive to identify improvement points.
Only approve if the content is high quality, clear, and meets the original requirements and you see no improvement points.
Provide your decision as structured output with:
- approved: true if content is good, false if revisions needed
- feedback: specific improvements needed (empty if approved)
Be concise but specific in your feedback.
""",
ChatOptions = new()
{
Instructions = """
You are a constructive critic. Review the content and provide specific feedback.
Always try to provide actionable suggestions for improvement and strive to identify improvement points.
Only approve if the content is high quality, clear, and meets the original requirements and you see no improvement points.
Provide your decision as structured output with:
- approved: true if content is good, false if revisions needed
- feedback: specific improvements needed (empty if approved)
Be concise but specific in your feedback.
""",
ResponseFormat = ChatResponseFormat.ForJsonSchema<CriticDecision>()
}
});
@@ -33,9 +33,9 @@ public class WeatherForecastAgent : DelegatingAIAgent
new ChatClientAgentOptions()
{
Name = AgentName,
Instructions = AgentInstructions,
ChatOptions = new ChatOptions()
{
Instructions = AgentInstructions,
Tools = [new ApprovalRequiredAIFunction(AIFunctionFactory.Create(GetWeather))],
// We want the agent to return structured output in a known format
// so that we can easily create adaptive cards from the response.
@@ -45,14 +45,20 @@ public static class AnthropicBetaServiceExtensions
{
var options = new ChatClientAgentOptions
{
Instructions = instructions,
Name = name,
Description = description,
};
if (!string.IsNullOrWhiteSpace(instructions))
{
options.ChatOptions ??= new();
options.ChatOptions.Instructions = instructions;
}
if (tools is { Count: > 0 })
{
options.ChatOptions = new ChatOptions { Tools = tools };
options.ChatOptions ??= new();
options.ChatOptions.Tools = tools;
}
var chatClient = betaService.AsIChatClient(model, defaultMaxTokens ?? DefaultMaxTokens);
@@ -45,14 +45,20 @@ public static class AnthropicClientExtensions
{
var options = new ChatClientAgentOptions
{
Instructions = instructions,
Name = name,
Description = description,
};
if (!string.IsNullOrWhiteSpace(instructions))
{
options.ChatOptions ??= new();
options.ChatOptions.Instructions = instructions;
}
if (tools is { Count: > 0 })
{
options.ChatOptions = new ChatOptions { Tools = tools };
options.ChatOptions ??= new();
options.ChatOptions.Tools = tools;
}
var chatClient = client.AsIChatClient(model, defaultMaxTokens ?? DefaultMaxTokens);
@@ -67,12 +67,17 @@ public static class PersistentAgentsClientExtensions
chatClient = clientFactory(chatClient);
}
if (!string.IsNullOrWhiteSpace(persistentAgentMetadata.Instructions) && chatOptions?.Instructions is null)
{
chatOptions ??= new ChatOptions();
chatOptions.Instructions = persistentAgentMetadata.Instructions;
}
return new ChatClientAgent(chatClient, options: new()
{
Id = persistentAgentMetadata.Id,
Name = persistentAgentMetadata.Name,
Description = persistentAgentMetadata.Description,
Instructions = persistentAgentMetadata.Instructions,
ChatOptions = chatOptions
}, services: services);
}
@@ -207,12 +212,17 @@ public static class PersistentAgentsClientExtensions
chatClient = clientFactory(chatClient);
}
if (!string.IsNullOrWhiteSpace(persistentAgentMetadata.Instructions) && options.ChatOptions?.Instructions is null)
{
options.ChatOptions ??= new ChatOptions();
options.ChatOptions.Instructions = persistentAgentMetadata.Instructions;
}
var agentOptions = new ChatClientAgentOptions()
{
Id = persistentAgentMetadata.Id,
Name = options.Name ?? persistentAgentMetadata.Name,
Description = options.Description ?? persistentAgentMetadata.Description,
Instructions = options.Instructions ?? persistentAgentMetadata.Instructions,
ChatOptions = options.ChatOptions,
AIContextProviderFactory = options.AIContextProviderFactory,
ChatMessageStoreFactory = options.ChatMessageStoreFactory,
@@ -453,7 +463,7 @@ public static class PersistentAgentsClientExtensions
model: model,
name: options.Name,
description: options.Description,
instructions: options.Instructions,
instructions: options.ChatOptions?.Instructions,
tools: toolDefinitionsAndResources.ToolDefinitions,
toolResources: toolDefinitionsAndResources.ToolResources,
temperature: null,
@@ -513,7 +523,7 @@ public static class PersistentAgentsClientExtensions
model: model,
name: options.Name,
description: options.Description,
instructions: options.Instructions,
instructions: options.ChatOptions?.Instructions,
tools: toolDefinitionsAndResources.ToolDefinitions,
toolResources: toolDefinitionsAndResources.ToolResources,
temperature: null,
@@ -393,7 +393,7 @@ public static partial class AzureAIProjectChatClientExtensions
PromptAgentDefinition agentDefinition = new(model)
{
Instructions = options.Instructions,
Instructions = options.ChatOptions?.Instructions,
Temperature = options.ChatOptions?.Temperature,
TopP = options.ChatOptions?.TopP,
TextOptions = new() { TextFormat = ToOpenAIResponseTextFormat(options.ChatOptions?.ResponseFormat, options.ChatOptions) }
@@ -459,7 +459,7 @@ public static partial class AzureAIProjectChatClientExtensions
PromptAgentDefinition agentDefinition = new(model)
{
Instructions = options.Instructions,
Instructions = options.ChatOptions?.Instructions,
Temperature = options.ChatOptions?.Temperature,
TopP = options.ChatOptions?.TopP,
TextOptions = new() { TextFormat = ToOpenAIResponseTextFormat(options.ChatOptions?.ResponseFormat, options.ChatOptions) }
@@ -822,10 +822,9 @@ public static partial class AzureAIProjectChatClientExtensions
if (agentDefinition is PromptAgentDefinition promptAgentDefinition)
{
agentOptions.ChatOptions ??= chatOptions?.Clone() ?? new();
agentOptions.Instructions = promptAgentDefinition.Instructions;
agentOptions.ChatOptions.Instructions = promptAgentDefinition.Instructions;
agentOptions.ChatOptions.Temperature = promptAgentDefinition.Temperature;
agentOptions.ChatOptions.TopP = promptAgentDefinition.TopP;
agentOptions.ChatOptions.Instructions = promptAgentDefinition.Instructions;
}
if (agentTools is { Count: > 0 })
@@ -38,7 +38,6 @@ public sealed class ChatClientPromptAgentFactory : PromptAgentFactory
{
Name = promptAgent.Name,
Description = promptAgent.Description,
Instructions = promptAgent.Instructions?.ToTemplateString(),
ChatOptions = promptAgent.GetChatOptions(this.Engine, this._functions),
};
@@ -35,7 +35,7 @@ public static class PromptAgentExtensions
return new ChatOptions()
{
Instructions = promptAgent.ResponseInstructions?.ToTemplateString(),
Instructions = promptAgent.Instructions?.ToTemplateString(),
Temperature = (float?)modelOptions?.Temperature?.Eval(engine),
MaxOutputTokens = (int?)modelOptions?.MaxOutputTokens?.Eval(engine),
TopP = (float?)modelOptions?.TopP?.Eval(engine),
@@ -77,12 +77,17 @@ public static class OpenAIAssistantClientExtensions
chatClient = clientFactory(chatClient);
}
if (!string.IsNullOrWhiteSpace(assistantMetadata.Instructions) && chatOptions?.Instructions is null)
{
chatOptions ??= new ChatOptions();
chatOptions.Instructions = assistantMetadata.Instructions;
}
return new ChatClientAgent(chatClient, options: new()
{
Id = assistantMetadata.Id,
Name = assistantMetadata.Name,
Description = assistantMetadata.Description,
Instructions = assistantMetadata.Instructions,
ChatOptions = chatOptions
}, services: services);
}
@@ -215,12 +220,17 @@ public static class OpenAIAssistantClientExtensions
chatClient = clientFactory(chatClient);
}
if (string.IsNullOrWhiteSpace(options.ChatOptions?.Instructions) && !string.IsNullOrWhiteSpace(assistantMetadata.Instructions))
{
options.ChatOptions ??= new ChatOptions();
options.ChatOptions.Instructions = assistantMetadata.Instructions;
}
var mergedOptions = new ChatClientAgentOptions()
{
Id = assistantMetadata.Id,
Name = options.Name ?? assistantMetadata.Name,
Description = options.Description ?? assistantMetadata.Description,
Instructions = options.Instructions ?? assistantMetadata.Instructions,
ChatOptions = options.ChatOptions,
AIContextProviderFactory = options.AIContextProviderFactory,
ChatMessageStoreFactory = options.ChatMessageStoreFactory,
@@ -339,10 +349,10 @@ public static class OpenAIAssistantClientExtensions
{
Name = name,
Description = description,
Instructions = instructions,
ChatOptions = tools is null ? null : new ChatOptions()
ChatOptions = tools is null && string.IsNullOrWhiteSpace(instructions) ? null : new ChatOptions()
{
Tools = tools,
Instructions = instructions
}
},
clientFactory,
@@ -377,7 +387,7 @@ public static class OpenAIAssistantClientExtensions
{
Name = options.Name,
Description = options.Description,
Instructions = options.Instructions,
Instructions = options.ChatOptions?.Instructions,
};
// Convert AITools to ToolDefinitions and ToolResources
@@ -443,10 +453,10 @@ public static class OpenAIAssistantClientExtensions
{
Name = name,
Description = description,
Instructions = instructions,
ChatOptions = tools is null ? null : new ChatOptions()
ChatOptions = tools is null && string.IsNullOrWhiteSpace(instructions) ? null : new ChatOptions()
{
Tools = tools,
Instructions = instructions,
}
},
clientFactory,
@@ -484,7 +494,7 @@ public static class OpenAIAssistantClientExtensions
{
Name = options.Name,
Description = options.Description,
Instructions = options.Instructions,
Instructions = options.ChatOptions?.Instructions,
};
// Convert AITools to ToolDefinitions and ToolResources
@@ -47,9 +47,9 @@ public static class OpenAIChatClientExtensions
{
Name = name,
Description = description,
Instructions = instructions,
ChatOptions = tools is null ? null : new ChatOptions()
ChatOptions = tools is null && string.IsNullOrWhiteSpace(instructions) ? null : new ChatOptions()
{
Instructions = instructions,
Tools = tools,
}
},
@@ -50,9 +50,9 @@ public static class OpenAIResponseClientExtensions
{
Name = name,
Description = description,
Instructions = instructions,
ChatOptions = tools is null ? null : new ChatOptions()
ChatOptions = tools is null && string.IsNullOrWhiteSpace(instructions) ? null : new ChatOptions()
{
Instructions = instructions,
Tools = tools,
}
},
@@ -32,7 +32,7 @@ public class OpenAIChatClientAgent : DelegatingAIAgent
{
Name = name,
Description = description,
Instructions = instructions,
ChatOptions = new ChatOptions() { Instructions = instructions },
}, loggerFactory)
{
}
@@ -32,7 +32,7 @@ public class OpenAIResponseClientAgent : DelegatingAIAgent
{
Name = name,
Description = description,
Instructions = instructions,
ChatOptions = new ChatOptions() { Instructions = instructions },
}, loggerFactory)
{
}
@@ -59,13 +59,13 @@ public sealed partial class ChatClientAgent : AIAgent
chatClient,
new ChatClientAgentOptions
{
Name = name,
Description = description,
Instructions = instructions,
ChatOptions = tools is null ? null : new ChatOptions
ChatOptions = (tools is null && string.IsNullOrWhiteSpace(instructions)) ? null : new ChatOptions
{
Tools = tools,
}
Instructions = instructions
},
Name = name,
Description = description
},
loggerFactory,
services)
@@ -141,7 +141,7 @@ public sealed partial class ChatClientAgent : AIAgent
/// These instructions are typically provided to the AI model as system messages to establish
/// the context and expected behavior for the agent's responses.
/// </remarks>
public string? Instructions => this._agentOptions?.Instructions;
public string? Instructions => this._agentOptions?.ChatOptions?.Instructions;
/// <summary>
/// Gets of the default <see cref="ChatOptions"/> used by the agent.
@@ -492,7 +492,6 @@ public sealed partial class ChatClientAgent : AIAgent
requestChatOptions.AllowMultipleToolCalls ??= this._agentOptions.ChatOptions.AllowMultipleToolCalls;
requestChatOptions.ConversationId ??= this._agentOptions.ChatOptions.ConversationId;
requestChatOptions.FrequencyPenalty ??= this._agentOptions.ChatOptions.FrequencyPenalty;
requestChatOptions.Instructions ??= this._agentOptions.ChatOptions.Instructions;
requestChatOptions.MaxOutputTokens ??= this._agentOptions.ChatOptions.MaxOutputTokens;
requestChatOptions.ModelId ??= this._agentOptions.ChatOptions.ModelId;
requestChatOptions.PresencePenalty ??= this._agentOptions.ChatOptions.PresencePenalty;
@@ -503,6 +502,13 @@ public sealed partial class ChatClientAgent : AIAgent
requestChatOptions.TopK ??= this._agentOptions.ChatOptions.TopK;
requestChatOptions.ToolMode ??= this._agentOptions.ChatOptions.ToolMode;
// Merge instructions by concatenating them if both are present.
requestChatOptions.Instructions = !string.IsNullOrWhiteSpace(requestChatOptions.Instructions) && !string.IsNullOrWhiteSpace(this.Instructions)
? $"{this.Instructions}\n{requestChatOptions.Instructions}"
: (!string.IsNullOrWhiteSpace(requestChatOptions.Instructions)
? requestChatOptions.Instructions
: this.Instructions);
// Merge only the additional properties from the agent if they are not already set in the request options.
if (requestChatOptions.AdditionalProperties is not null && this._agentOptions.ChatOptions.AdditionalProperties is not null)
{
@@ -685,12 +691,6 @@ public sealed partial class ChatClientAgent : AIAgent
""");
}
if (!string.IsNullOrWhiteSpace(this.Instructions))
{
chatOptions ??= new();
chatOptions.Instructions = string.IsNullOrWhiteSpace(chatOptions.Instructions) ? this.Instructions : $"{this.Instructions}\n{chatOptions.Instructions}";
}
// Only create or update ChatOptions if we have an id on the thread and we don't have the same one already in ChatOptions.
if (!string.IsNullOrWhiteSpace(typedThread.ConversationId) && typedThread.ConversationId != chatOptions?.ConversationId)
{
@@ -1,7 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Collections.Generic;
using System.Text.Json;
using Microsoft.Extensions.AI;
@@ -17,35 +16,6 @@ namespace Microsoft.Agents.AI;
/// </remarks>
public class ChatClientAgentOptions
{
/// <summary>
/// Initializes a new instance of the <see cref="ChatClientAgentOptions"/> class.
/// </summary>
public ChatClientAgentOptions()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ChatClientAgentOptions"/> class with the specified parameters.
/// </summary>
/// <remarks>If <paramref name="tools"/> is provided, a new <see cref="ChatOptions"/> instance is created
/// with the specified instructions and tools.</remarks>
/// <param name="instructions">The instructions or guidelines for the chat client agent. Can be <see langword="null"/> if not specified.</param>
/// <param name="name">The name of the chat client agent. Can be <see langword="null"/> if not specified.</param>
/// <param name="description">The description of the chat client agent. Can be <see langword="null"/> if not specified.</param>
/// <param name="tools">A list of <see cref="AITool"/> instances available to the chat client agent. Can be <see langword="null"/> if no
/// tools are specified.</param>
public ChatClientAgentOptions(string? instructions, string? name = null, string? description = null, IList<AITool>? tools = null)
{
this.Name = name;
this.Instructions = instructions;
this.Description = description;
if (tools is not null)
{
(this.ChatOptions ??= new()).Tools = tools;
}
}
/// <summary>
/// Gets or sets the agent id.
/// </summary>
@@ -56,11 +26,6 @@ public class ChatClientAgentOptions
/// </summary>
public string? Name { get; set; }
/// <summary>
/// Gets or sets the agent instructions.
/// </summary>
public string? Instructions { get; set; }
/// <summary>
/// Gets or sets the agent description.
/// </summary>
@@ -106,7 +71,6 @@ public class ChatClientAgentOptions
{
Id = this.Id,
Name = this.Name,
Instructions = this.Instructions,
Description = this.Description,
ChatOptions = this.ChatOptions?.Clone(),
ChatMessageStoreFactory = this.ChatMessageStoreFactory,
@@ -84,8 +84,7 @@ public class AnthropicChatCompletionFixture : IChatClientAgentFixture
return Task.FromResult(new ChatClientAgent(chatClient, options: new()
{
Name = name,
Instructions = instructions,
ChatOptions = new() { Tools = aiTools }
ChatOptions = new() { Instructions = instructions, Tools = aiTools }
}));
}
@@ -37,16 +37,20 @@ public class AIProjectClientCreateTests
{
"CreateWithChatClientAgentOptionsAsync" => await this._client.CreateAIAgentAsync(
model: s_config.DeploymentName,
options: new ChatClientAgentOptions(
instructions: AgentInstructions,
name: AgentName,
description: AgentDescription)),
options: new ChatClientAgentOptions()
{
Name = AgentName,
Description = AgentDescription,
ChatOptions = new() { Instructions = AgentInstructions }
}),
"CreateWithChatClientAgentOptionsSync" => this._client.CreateAIAgent(
model: s_config.DeploymentName,
options: new ChatClientAgentOptions(
instructions: AgentInstructions,
name: AgentName,
description: AgentDescription)),
options: new ChatClientAgentOptions()
{
Name = AgentName,
Description = AgentDescription,
ChatOptions = new() { Instructions = AgentInstructions }
}),
"CreateWithFoundryOptionsAsync" => await this._client.CreateAIAgentAsync(
name: AgentName,
creationOptions: new AgentVersionCreationOptions(new PromptAgentDefinition(s_config.DeploymentName) { Instructions = AgentInstructions }) { Description = AgentDescription }),
@@ -239,16 +243,18 @@ public class AIProjectClientCreateTests
{
"CreateWithChatClientAgentOptionsAsync" => await this._client.CreateAIAgentAsync(
model: s_config.DeploymentName,
options: new ChatClientAgentOptions(
name: AgentName,
instructions: AgentInstructions,
tools: [weatherFunction])),
options: new ChatClientAgentOptions()
{
Name = AgentName,
ChatOptions = new() { Instructions = AgentInstructions, Tools = [weatherFunction] }
}),
"CreateWithChatClientAgentOptionsSync" => this._client.CreateAIAgent(
s_config.DeploymentName,
options: new ChatClientAgentOptions(
name: AgentName,
instructions: AgentInstructions,
tools: [weatherFunction])),
options: new ChatClientAgentOptions()
{
Name = AgentName,
ChatOptions = new() { Instructions = AgentInstructions, Tools = [weatherFunction] }
}),
_ => throw new InvalidOperationException($"Unknown create mechanism: {createMechanism}")
};
@@ -34,16 +34,20 @@ public class AzureAIAgentsPersistentCreateTests
{
"CreateWithChatClientAgentOptionsAsync" => await this._persistentAgentsClient.CreateAIAgentAsync(
s_config.DeploymentName,
options: new ChatClientAgentOptions(
instructions: AgentInstructions,
name: AgentName,
description: AgentDescription)),
options: new ChatClientAgentOptions()
{
ChatOptions = new() { Instructions = AgentInstructions },
Name = AgentName,
Description = AgentDescription
}),
"CreateWithChatClientAgentOptionsSync" => this._persistentAgentsClient.CreateAIAgent(
s_config.DeploymentName,
options: new ChatClientAgentOptions(
instructions: AgentInstructions,
name: AgentName,
description: AgentDescription)),
options: new ChatClientAgentOptions()
{
ChatOptions = new() { Instructions = AgentInstructions },
Name = AgentName,
Description = AgentDescription
}),
"CreateWithFoundryOptionsAsync" => await this._persistentAgentsClient.CreateAIAgentAsync(
s_config.DeploymentName,
instructions: AgentInstructions,
@@ -109,14 +113,24 @@ public class AzureAIAgentsPersistentCreateTests
{
"CreateWithChatClientAgentOptionsAsync" => await this._persistentAgentsClient.CreateAIAgentAsync(
s_config.DeploymentName,
options: new ChatClientAgentOptions(
instructions: AgentInstructions,
tools: [new HostedFileSearchTool() { Inputs = [new HostedVectorStoreContent(vectorStoreMetadata.Value.Id)] }])),
options: new ChatClientAgentOptions()
{
ChatOptions = new()
{
Instructions = AgentInstructions,
Tools = [new HostedFileSearchTool() { Inputs = [new HostedVectorStoreContent(vectorStoreMetadata.Value.Id)] }]
}
}),
"CreateWithChatClientAgentOptionsSync" => this._persistentAgentsClient.CreateAIAgent(
s_config.DeploymentName,
options: new ChatClientAgentOptions(
instructions: AgentInstructions,
tools: [new HostedFileSearchTool() { Inputs = [new HostedVectorStoreContent(vectorStoreMetadata.Value.Id)] }])),
options: new ChatClientAgentOptions()
{
ChatOptions = new()
{
Instructions = AgentInstructions,
Tools = [new HostedFileSearchTool() { Inputs = [new HostedVectorStoreContent(vectorStoreMetadata.Value.Id)] }]
}
}),
"CreateWithFoundryOptionsAsync" => await this._persistentAgentsClient.CreateAIAgentAsync(
s_config.DeploymentName,
instructions: AgentInstructions,
@@ -179,15 +193,24 @@ public class AzureAIAgentsPersistentCreateTests
// Hosted tool path (tools supplied via ChatClientAgentOptions)
"CreateWithChatClientAgentOptionsAsync" => await this._persistentAgentsClient.CreateAIAgentAsync(
s_config.DeploymentName,
options: new ChatClientAgentOptions(
instructions: AgentInstructions,
tools: [new HostedCodeInterpreterTool() { Inputs = [new HostedFileContent(uploadedCodeFile.Id)] }])),
options: new ChatClientAgentOptions()
{
ChatOptions = new()
{
Instructions = AgentInstructions,
Tools = [new HostedCodeInterpreterTool() { Inputs = [new HostedFileContent(uploadedCodeFile.Id)] }]
}
}),
"CreateWithChatClientAgentOptionsSync" => this._persistentAgentsClient.CreateAIAgent(
s_config.DeploymentName,
options: new ChatClientAgentOptions(
instructions: AgentInstructions,
tools: [new HostedCodeInterpreterTool() { Inputs = [new HostedFileContent(uploadedCodeFile.Id)] }])),
// Foundry (definitions + resources provided directly)
options: new ChatClientAgentOptions()
{
ChatOptions = new()
{
Instructions = AgentInstructions,
Tools = [new HostedCodeInterpreterTool() { Inputs = [new HostedFileContent(uploadedCodeFile.Id)] }]
}
}),
"CreateWithFoundryOptionsAsync" => await this._persistentAgentsClient.CreateAIAgentAsync(
s_config.DeploymentName,
instructions: AgentInstructions,
@@ -232,14 +255,24 @@ public class AzureAIAgentsPersistentCreateTests
{
"CreateWithChatClientAgentOptionsAsync" => await this._persistentAgentsClient.CreateAIAgentAsync(
s_config.DeploymentName,
options: new ChatClientAgentOptions(
instructions: AgentInstructions,
tools: [weatherFunction])),
options: new ChatClientAgentOptions()
{
ChatOptions = new()
{
Instructions = AgentInstructions,
Tools = [weatherFunction]
}
}),
"CreateWithChatClientAgentOptionsSync" => this._persistentAgentsClient.CreateAIAgent(
s_config.DeploymentName,
options: new ChatClientAgentOptions(
instructions: AgentInstructions,
tools: [weatherFunction])),
options: new ChatClientAgentOptions()
{
ChatOptions = new()
{
Instructions = AgentInstructions,
Tools = [weatherFunction]
}
}),
_ => throw new InvalidOperationException($"Unknown create mechanism: {createMechanism}")
};
@@ -91,7 +91,7 @@ public sealed class AnthropicBetaServiceExtensionsTests
{
Name = "Test Agent",
Description = "Test description",
Instructions = "Test instructions"
ChatOptions = new() { Instructions = "Test instructions" }
};
// Act
@@ -158,7 +158,7 @@ public sealed class AnthropicClientExtensionsTests
{
Name = "Test Agent",
Description = "Test description",
Instructions = "Test instructions"
ChatOptions = new() { Instructions = "Test instructions" }
};
// Act
@@ -310,7 +310,7 @@ public sealed class PersistentAgentsClientExtensionsTests
{
Name = "Override Name",
Description = "Override Description",
Instructions = "Override Instructions"
ChatOptions = new() { Instructions = "Override Instructions" }
};
// Act
@@ -337,7 +337,7 @@ public sealed class PersistentAgentsClientExtensionsTests
{
Name = "Override Name",
Description = "Override Description",
Instructions = "Override Instructions"
ChatOptions = new() { Instructions = "Override Instructions" }
};
// Act
@@ -386,7 +386,7 @@ public sealed class PersistentAgentsClientExtensionsTests
{
Name = "Override Name",
Description = "Override Description",
Instructions = "Override Instructions"
ChatOptions = new() { Instructions = "Override Instructions" }
};
// Act
@@ -413,7 +413,7 @@ public sealed class PersistentAgentsClientExtensionsTests
{
Name = "Override Name",
Description = "Override Description",
Instructions = "Override Instructions"
ChatOptions = new() { Instructions = "Override Instructions" }
};
// Act
@@ -557,7 +557,7 @@ public sealed class PersistentAgentsClientExtensionsTests
{
Name = "Test Agent",
Description = "Test description",
Instructions = "Test instructions"
ChatOptions = new() { Instructions = "Test instructions" }
};
// Act
@@ -584,7 +584,7 @@ public sealed class PersistentAgentsClientExtensionsTests
{
Name = "Test Agent",
Description = "Test description",
Instructions = "Test instructions"
ChatOptions = new() { Instructions = "Test instructions" }
};
// Act
@@ -752,7 +752,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
var options = new ChatClientAgentOptions
{
Name = "test-agent",
Instructions = "Test instructions"
ChatOptions = new() { Instructions = "Test instructions" }
};
// Act
@@ -775,7 +775,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
var options = new ChatClientAgentOptions
{
Name = "test-agent",
Instructions = "Test instructions"
ChatOptions = new() { Instructions = "Test instructions" }
};
TestChatClient? testChatClient = null;
@@ -803,7 +803,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
var options = new ChatClientAgentOptions
{
Name = "test-agent",
Instructions = "Test instructions"
ChatOptions = new() { Instructions = "Test instructions" }
};
// Act
@@ -826,7 +826,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
var options = new ChatClientAgentOptions
{
Name = "test-agent",
Instructions = "Test instructions"
ChatOptions = new() { Instructions = "Test instructions" }
};
TestChatClient? testChatClient = null;
@@ -1575,8 +1575,8 @@ public sealed class AzureAIProjectChatClientExtensionsTests
var options = new ChatClientAgentOptions
{
Name = "test-agent",
Instructions = "Custom instructions",
Description = "Custom description"
Description = "Custom description",
ChatOptions = new ChatOptions { Instructions = "Custom instructions" }
};
// Act
@@ -1610,8 +1610,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
var options = new ChatClientAgentOptions
{
Name = "test-agent",
Instructions = "Test",
ChatOptions = new ChatOptions { Tools = tools }
ChatOptions = new ChatOptions { Instructions = "Test", Tools = tools }
};
// Act
@@ -49,8 +49,7 @@ public class AzureAIProjectChatClientTests
new ChatClientAgentOptions
{
Name = "test-agent",
Instructions = "Test instructions",
ChatOptions = new() { ConversationId = "conv_12345" }
ChatOptions = new() { Instructions = "Test instructions", ConversationId = "conv_12345" }
});
// Act
@@ -99,7 +98,7 @@ public class AzureAIProjectChatClientTests
new ChatClientAgentOptions
{
Name = "test-agent",
Instructions = "Test instructions",
ChatOptions = new() { Instructions = "Test instructions" },
});
// Act
@@ -148,8 +147,7 @@ public class AzureAIProjectChatClientTests
new ChatClientAgentOptions
{
Name = "test-agent",
Instructions = "Test instructions",
ChatOptions = new() { ConversationId = "conv_should_not_use_default" }
ChatOptions = new() { Instructions = "Test instructions", ConversationId = "conv_should_not_use_default" }
});
// Act
@@ -198,7 +196,7 @@ public class AzureAIProjectChatClientTests
new ChatClientAgentOptions
{
Name = "test-agent",
Instructions = "Test instructions",
ChatOptions = new() { Instructions = "Test instructions" },
});
// Act
@@ -70,7 +70,7 @@ public sealed class ChatClientAgentFactoryTests
Assert.IsType<ChatClientAgent>(agent);
var chatClientAgent = agent as ChatClientAgent;
Assert.NotNull(chatClientAgent?.ChatOptions);
Assert.Equal("Provide detailed and accurate responses.", chatClientAgent?.ChatOptions?.Instructions);
Assert.Equal("You are a helpful assistant.", chatClientAgent?.ChatOptions?.Instructions);
Assert.Equal(0.7F, chatClientAgent?.ChatOptions?.Temperature);
Assert.Equal(0.7F, chatClientAgent?.ChatOptions?.FrequencyPenalty);
Assert.Equal(1024, chatClientAgent?.ChatOptions?.MaxOutputTokens);
@@ -92,7 +92,7 @@ public sealed class OpenAIAssistantClientExtensionsTests
{
Name = "Test Agent",
Description = "Test description",
Instructions = "Test instructions"
ChatOptions = new() { Instructions = "Test instructions" }
};
// Act
@@ -223,7 +223,7 @@ public sealed class OpenAIAssistantClientExtensionsTests
{
Name = "Override Name",
Description = "Override Description",
Instructions = "Override Instructions"
ChatOptions = new() { Instructions = "Override Instructions" }
};
// Act
@@ -250,7 +250,7 @@ public sealed class OpenAIAssistantClientExtensionsTests
{
Name = "Override Name",
Description = "Override Description",
Instructions = "Override Instructions"
ChatOptions = new() { Instructions = "Override Instructions" }
};
// Act
@@ -299,7 +299,7 @@ public sealed class OpenAIAssistantClientExtensionsTests
{
Name = "Override Name",
Description = "Override Description",
Instructions = "Override Instructions"
ChatOptions = new() { Instructions = "Override Instructions" }
};
// Act
@@ -326,7 +326,7 @@ public sealed class OpenAIAssistantClientExtensionsTests
{
Name = "Override Name",
Description = "Override Description",
Instructions = "Override Instructions"
ChatOptions = new() { Instructions = "Override Instructions" }
};
// Act
@@ -498,7 +498,7 @@ public sealed class OpenAIAssistantClientExtensionsTests
var options = new ChatClientAgentOptions
{
Name = "Test Agent",
Instructions = "Test instructions"
ChatOptions = new() { Instructions = "Test instructions" }
};
// Act
@@ -130,7 +130,7 @@ public sealed class OpenAIChatClientExtensionsTests
{
Name = "Test Agent",
Description = "Test description",
Instructions = "Test instructions"
ChatOptions = new() { Instructions = "Test instructions" }
};
// Act
@@ -208,7 +208,7 @@ public sealed class OpenAIResponseClientExtensionsTests
var options = new ChatClientAgentOptions
{
Name = "Test Agent",
Instructions = "Test instructions"
ChatOptions = new() { Instructions = "Test instructions" }
};
// Act
@@ -19,7 +19,6 @@ public class ChatClientAgentOptionsTests
// Assert
Assert.Null(options.Name);
Assert.Null(options.Instructions);
Assert.Null(options.Description);
Assert.Null(options.ChatOptions);
Assert.Null(options.ChatMessageStoreFactory);
@@ -27,90 +26,44 @@ public class ChatClientAgentOptionsTests
}
[Fact]
public void ParameterizedConstructor_WithNullValues_SetsPropertiesCorrectly()
public void Constructor_WithNullValues_SetsPropertiesCorrectly()
{
// Act
var options = new ChatClientAgentOptions(
instructions: null,
name: null,
description: null,
tools: null);
var options = new ChatClientAgentOptions() { Name = null, Description = null, ChatOptions = new() { Tools = null, Instructions = null } };
// Assert
Assert.Null(options.Name);
Assert.Null(options.Instructions);
Assert.Null(options.Description);
Assert.Null(options.ChatOptions);
Assert.Null(options.AIContextProviderFactory);
Assert.Null(options.ChatMessageStoreFactory);
Assert.NotNull(options.ChatOptions);
Assert.Null(options.ChatOptions.Instructions);
Assert.Null(options.ChatOptions.Tools);
}
[Fact]
public void ParameterizedConstructor_WithInstructionsOnly_SetsChatOptionsWithInstructions()
{
// Arrange
const string Instructions = "Test instructions";
// Act
var options = new ChatClientAgentOptions(
instructions: Instructions,
name: null,
description: null,
tools: null);
// Assert
Assert.Null(options.Name);
Assert.Equal(Instructions, options.Instructions);
Assert.Null(options.Description);
Assert.Null(options.ChatOptions);
}
[Fact]
public void ParameterizedConstructor_WithToolsOnly_SetsChatOptionsWithTools()
public void Constructor_WithToolsOnly_SetsChatOptionsWithTools()
{
// Arrange
var tools = new List<AITool> { AIFunctionFactory.Create(() => "test") };
// Act
var options = new ChatClientAgentOptions(
instructions: null,
name: null,
description: null,
tools: tools);
var options = new ChatClientAgentOptions()
{
Name = null,
Description = null,
ChatOptions = new() { Tools = tools }
};
// Assert
Assert.Null(options.Name);
Assert.Null(options.Instructions);
Assert.Null(options.Description);
Assert.NotNull(options.ChatOptions);
Assert.Null(options.ChatOptions.Instructions);
Assert.Same(tools, options.ChatOptions.Tools);
AssertSameTools(tools, options.ChatOptions.Tools);
}
[Fact]
public void ParameterizedConstructor_WithInstructionsAndTools_SetsChatOptionsWithBoth()
{
// Arrange
const string Instructions = "Test instructions";
var tools = new List<AITool> { AIFunctionFactory.Create(() => "test") };
// Act
var options = new ChatClientAgentOptions(
instructions: Instructions,
name: null,
description: null,
tools: tools);
// Assert
Assert.Null(options.Name);
Assert.Equal(Instructions, options.Instructions);
Assert.Null(options.Description);
Assert.NotNull(options.ChatOptions);
Assert.Null(options.ChatOptions.Instructions);
Assert.Same(tools, options.ChatOptions.Tools);
}
[Fact]
public void ParameterizedConstructor_WithAllParameters_SetsAllPropertiesCorrectly()
public void Constructor_WithAllParameters_SetsAllPropertiesCorrectly()
{
// Arrange
const string Instructions = "Test instructions";
@@ -119,38 +72,37 @@ public class ChatClientAgentOptionsTests
var tools = new List<AITool> { AIFunctionFactory.Create(() => "test") };
// Act
var options = new ChatClientAgentOptions(
instructions: Instructions,
name: Name,
description: Description,
tools: tools);
var options = new ChatClientAgentOptions()
{
Name = Name,
Description = Description,
ChatOptions = new() { Tools = tools, Instructions = Instructions }
};
// Assert
Assert.Equal(Name, options.Name);
Assert.Equal(Instructions, options.Instructions);
Assert.Equal(Instructions, options.ChatOptions.Instructions);
Assert.Equal(Description, options.Description);
Assert.NotNull(options.ChatOptions);
Assert.Null(options.ChatOptions.Instructions);
Assert.Same(tools, options.ChatOptions.Tools);
AssertSameTools(tools, options.ChatOptions.Tools);
}
[Fact]
public void ParameterizedConstructor_WithNameAndDescriptionOnly_DoesNotCreateChatOptions()
public void Constructor_WithNameAndDescriptionOnly_DoesNotCreateChatOptions()
{
// Arrange
const string Name = "Test name";
const string Description = "Test description";
// Act
var options = new ChatClientAgentOptions(
instructions: null,
name: Name,
description: Description,
tools: null);
var options = new ChatClientAgentOptions()
{
Name = Name,
Description = Description,
};
// Assert
Assert.Equal(Name, options.Name);
Assert.Null(options.Instructions);
Assert.Equal(Description, options.Description);
Assert.Null(options.ChatOptions);
}
@@ -159,7 +111,6 @@ public class ChatClientAgentOptionsTests
public void Clone_CreatesDeepCopyWithSameValues()
{
// Arrange
const string Instructions = "Test instructions";
const string Name = "Test name";
const string Description = "Test description";
var tools = new List<AITool> { AIFunctionFactory.Create(() => "test") };
@@ -171,8 +122,11 @@ public class ChatClientAgentOptionsTests
ChatClientAgentOptions.AIContextProviderFactoryContext ctx) =>
new Mock<AIContextProvider>().Object;
var original = new ChatClientAgentOptions(Instructions, Name, Description, tools)
var original = new ChatClientAgentOptions()
{
Name = Name,
Description = Description,
ChatOptions = new() { Tools = tools },
Id = "test-id",
ChatMessageStoreFactory = ChatMessageStoreFactory,
AIContextProviderFactory = AIContextProviderFactory
@@ -185,7 +139,6 @@ public class ChatClientAgentOptionsTests
Assert.NotSame(original, clone);
Assert.Equal(original.Id, clone.Id);
Assert.Equal(original.Name, clone.Name);
Assert.Equal(original.Instructions, clone.Instructions);
Assert.Equal(original.Description, clone.Description);
Assert.Same(original.ChatMessageStoreFactory, clone.ChatMessageStoreFactory);
Assert.Same(original.AIContextProviderFactory, clone.AIContextProviderFactory);
@@ -197,14 +150,13 @@ public class ChatClientAgentOptionsTests
}
[Fact]
public void Clone_WithNullChatOptions_ClonesCorrectly()
public void Clone_WithoutProvidingChatOptions_ClonesCorrectly()
{
// Arrange
var original = new ChatClientAgentOptions
{
Id = "test-id",
Name = "Test name",
Instructions = "Test instructions",
Description = "Test description"
};
@@ -215,10 +167,19 @@ public class ChatClientAgentOptionsTests
Assert.NotSame(original, clone);
Assert.Equal(original.Id, clone.Id);
Assert.Equal(original.Name, clone.Name);
Assert.Equal(original.Instructions, clone.Instructions);
Assert.Equal(original.Description, clone.Description);
Assert.Null(clone.ChatOptions);
Assert.Null(original.ChatOptions);
Assert.Null(clone.ChatMessageStoreFactory);
Assert.Null(clone.AIContextProviderFactory);
}
private static void AssertSameTools(IList<AITool>? expected, IList<AITool>? actual)
{
var index = 0;
foreach (var tool in expected ?? [])
{
Assert.Same(tool, actual?[index]);
index++;
}
}
}
@@ -31,7 +31,7 @@ public partial class ChatClientAgentTests
Id = "test-agent-id",
Name = "test name",
Description = "test description",
Instructions = "test instructions",
ChatOptions = new() { Instructions = "test instructions" },
});
// Assert
@@ -65,7 +65,7 @@ public partial class ChatClientAgentTests
ChatClientAgent agent =
new(mockService.Object, options: new()
{
Instructions = "test instructions"
ChatOptions = new() { Instructions = "base instructions" },
});
// Act
@@ -99,7 +99,7 @@ public partial class ChatClientAgentTests
{
// Arrange
var chatClient = new Mock<IChatClient>().Object;
ChatClientAgent agent = new(chatClient, options: new() { Instructions = "test instructions" });
ChatClientAgent agent = new(chatClient, options: new() { ChatOptions = new() { Instructions = "test instructions" } });
// Act & Assert
await Assert.ThrowsAsync<ArgumentNullException>(() => agent.RunAsync((IReadOnlyCollection<ChatMessage>)null!));
@@ -120,7 +120,7 @@ public partial class ChatClientAgentTests
It.Is<ChatOptions>(opts => opts.MaxOutputTokens == 100),
It.IsAny<CancellationToken>())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { ChatOptions = new() { Instructions = "test instructions" } });
// Act
await agent.RunAsync([new(ChatRole.User, "test")], options: new ChatClientAgentRunOptions(chatOptions));
@@ -181,7 +181,7 @@ public partial class ChatClientAgentTests
capturedMessages.AddRange(msgs))
.ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "base instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { ChatOptions = new() { Instructions = "base instructions" } });
var runOptions = new AgentRunOptions();
// Act
@@ -212,7 +212,7 @@ public partial class ChatClientAgentTests
It.IsAny<ChatOptions>(),
It.IsAny<CancellationToken>())).ReturnsAsync(new ChatResponse(responseMessages));
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions", Name = authorName });
ChatClientAgent agent = new(mockService.Object, options: new() { ChatOptions = new() { Instructions = "test instructions" }, Name = authorName });
// Act
var result = await agent.RunAsync([new(ChatRole.User, "test")]);
@@ -239,7 +239,7 @@ public partial class ChatClientAgentTests
capturedMessages.AddRange(msgs))
.ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { ChatOptions = new() { Instructions = "test instructions" } });
// Create a thread using the agent's GetNewThread method
var thread = agent.GetNewThread();
@@ -270,7 +270,7 @@ public partial class ChatClientAgentTests
capturedMessages.AddRange(msgs))
.ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = null });
ChatClientAgent agent = new(mockService.Object, options: new() { ChatOptions = new() { Instructions = null } });
// Act
await agent.RunAsync([new(ChatRole.User, "test message")]);
@@ -300,7 +300,7 @@ public partial class ChatClientAgentTests
capturedMessages.AddRange(msgs))
.ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { ChatOptions = new() { Instructions = "test instructions" } });
// Act
await agent.RunAsync([]);
@@ -326,7 +326,7 @@ public partial class ChatClientAgentTests
It.Is<ChatOptions>(opts => opts.ConversationId == "ConvId"),
It.IsAny<CancellationToken>())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]) { ConversationId = "ConvId" });
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { ChatOptions = new() { Instructions = "test instructions" } });
ChatClientAgentThread thread = new() { ConversationId = "ConvId" };
@@ -346,7 +346,7 @@ public partial class ChatClientAgentTests
var chatOptions = new ChatOptions { ConversationId = "ConvId" };
Mock<IChatClient> mockService = new();
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { ChatOptions = new() { Instructions = "test instructions" } });
ChatClientAgentThread thread = new() { ConversationId = "ThreadId" };
@@ -369,7 +369,7 @@ public partial class ChatClientAgentTests
It.Is<ChatOptions>(opts => opts.MaxOutputTokens == 100 && opts.ConversationId == "ConvId"),
It.IsAny<CancellationToken>())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]) { ConversationId = "ConvId" });
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { ChatOptions = new() { Instructions = "test instructions" } });
ChatClientAgentThread thread = new() { ConversationId = "ConvId" };
@@ -394,7 +394,7 @@ public partial class ChatClientAgentTests
It.IsAny<ChatOptions>(),
It.IsAny<CancellationToken>())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { ChatOptions = new() { Instructions = "test instructions" } });
ChatClientAgentThread thread = new() { ConversationId = "ConvId" };
@@ -415,7 +415,7 @@ public partial class ChatClientAgentTests
It.IsAny<IEnumerable<ChatMessage>>(),
It.IsAny<ChatOptions>(),
It.IsAny<CancellationToken>())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]) { ConversationId = "ConvId" });
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { ChatOptions = new() { Instructions = "test instructions" } });
ChatClientAgentThread thread = new();
// Act
@@ -442,7 +442,7 @@ public partial class ChatClientAgentTests
mockFactory.Setup(f => f(It.IsAny<ChatClientAgentOptions.ChatMessageStoreFactoryContext>())).Returns(new InMemoryChatMessageStore());
ChatClientAgent agent = new(mockService.Object, options: new()
{
Instructions = "test instructions",
ChatOptions = new() { Instructions = "test instructions" },
ChatMessageStoreFactory = mockFactory.Object
});
@@ -473,7 +473,7 @@ public partial class ChatClientAgentTests
It.IsAny<CancellationToken>())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
ChatClientAgent agent = new(mockService.Object, options: new()
{
Instructions = "test instructions",
ChatOptions = new() { Instructions = "test instructions" },
});
// Act
@@ -508,7 +508,7 @@ public partial class ChatClientAgentTests
ChatClientAgent agent = new(mockService.Object, options: new()
{
Instructions = "test instructions",
ChatOptions = new() { Instructions = "test instructions" },
ChatMessageStoreFactory = mockFactory.Object
});
@@ -539,7 +539,7 @@ public partial class ChatClientAgentTests
mockFactory.Setup(f => f(It.IsAny<ChatClientAgentOptions.ChatMessageStoreFactoryContext>())).Returns(new InMemoryChatMessageStore());
ChatClientAgent agent = new(mockService.Object, options: new()
{
Instructions = "test instructions",
ChatOptions = new() { Instructions = "test instructions" },
ChatMessageStoreFactory = mockFactory.Object
});
@@ -592,7 +592,7 @@ public partial class ChatClientAgentTests
.Setup(p => p.InvokedAsync(It.IsAny<AIContextProvider.InvokedContext>(), It.IsAny<CancellationToken>()))
.Returns(new ValueTask());
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "base instructions", AIContextProviderFactory = _ => mockProvider.Object, ChatOptions = new() { Tools = [AIFunctionFactory.Create(() => { }, "base function")] } });
ChatClientAgent agent = new(mockService.Object, options: new() { AIContextProviderFactory = _ => mockProvider.Object, ChatOptions = new() { Instructions = "base instructions", Tools = [AIFunctionFactory.Create(() => { }, "base function")] } });
// Act
var thread = agent.GetNewThread() as ChatClientAgentThread;
@@ -654,7 +654,7 @@ public partial class ChatClientAgentTests
.Setup(p => p.InvokedAsync(It.IsAny<AIContextProvider.InvokedContext>(), It.IsAny<CancellationToken>()))
.Returns(new ValueTask());
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "base instructions", AIContextProviderFactory = _ => mockProvider.Object, ChatOptions = new() { Tools = [AIFunctionFactory.Create(() => { }, "base function")] } });
ChatClientAgent agent = new(mockService.Object, options: new() { AIContextProviderFactory = _ => mockProvider.Object, ChatOptions = new() { Instructions = "base instructions", Tools = [AIFunctionFactory.Create(() => { }, "base function")] } });
// Act
await Assert.ThrowsAsync<InvalidOperationException>(() => agent.RunAsync(requestMessages));
@@ -700,7 +700,7 @@ public partial class ChatClientAgentTests
.Setup(p => p.InvokingAsync(It.IsAny<AIContextProvider.InvokingContext>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new AIContext());
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "base instructions", AIContextProviderFactory = _ => mockProvider.Object, ChatOptions = new() { Tools = [AIFunctionFactory.Create(() => { }, "base function")] } });
ChatClientAgent agent = new(mockService.Object, options: new() { AIContextProviderFactory = _ => mockProvider.Object, ChatOptions = new() { Instructions = "base instructions", Tools = [AIFunctionFactory.Create(() => { }, "base function")] } });
// Act
await agent.RunAsync([new(ChatRole.User, "user message")]);
@@ -907,7 +907,7 @@ public partial class ChatClientAgentTests
{
// Arrange
var chatClient = new Mock<IChatClient>().Object;
var metadata = new ChatClientAgentOptions { Instructions = "You are a helpful assistant" };
var metadata = new ChatClientAgentOptions { ChatOptions = new() { Instructions = "You are a helpful assistant" } };
ChatClientAgent agent = new(chatClient, metadata);
// Act & Assert
@@ -936,7 +936,7 @@ public partial class ChatClientAgentTests
{
// Arrange
var chatClient = new Mock<IChatClient>().Object;
var metadata = new ChatClientAgentOptions { Instructions = null };
var metadata = new ChatClientAgentOptions { ChatOptions = new() { Instructions = null } };
ChatClientAgent agent = new(chatClient, metadata);
// Act & Assert
@@ -967,10 +967,10 @@ public partial class ChatClientAgentTests
}
/// <summary>
/// Verify that ChatOptions property returns null when no params are provided that require a ChatOptions instance.
/// Verify that ChatOptions is created with instructions when instructions are provided and no tools are provided.
/// </summary>
[Fact]
public void ChatOptionsReturnsNullWhenConstructorToolsNotProvided()
public void ChatOptionsCreatedWithInstructionsEvenWhenConstructorToolsNotProvided()
{
// Arrange
var chatClient = new Mock<IChatClient>().Object;
@@ -980,7 +980,8 @@ public partial class ChatClientAgentTests
Assert.Equal("TestInstructions", agent.Instructions);
Assert.Equal("TestName", agent.Name);
Assert.Equal("TestDescription", agent.Description);
Assert.Null(agent.ChatOptions);
Assert.NotNull(agent.ChatOptions);
Assert.Equal("TestInstructions", agent.ChatOptions.Instructions);
}
#endregion
@@ -1071,7 +1072,7 @@ public partial class ChatClientAgentTests
public async Task ChatOptionsMergingUsesAgentOptionsWhenRequestHasNoneAsync()
{
// Arrange
var agentChatOptions = new ChatOptions { MaxOutputTokens = 100, Temperature = 0.7f };
var agentChatOptions = new ChatOptions { MaxOutputTokens = 100, Temperature = 0.7f, Instructions = "test instructions" };
Mock<IChatClient> mockService = new();
ChatOptions? capturedChatOptions = null;
mockService.Setup(
@@ -1085,7 +1086,6 @@ public partial class ChatClientAgentTests
ChatClientAgent agent = new(mockService.Object, options: new()
{
Instructions = "test instructions",
ChatOptions = agentChatOptions
});
var messages = new List<ChatMessage> { new(ChatRole.User, "test") };
@@ -1114,7 +1114,7 @@ public partial class ChatClientAgentTests
capturedChatOptions = opts)
.ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
ChatClientAgent agent = new(mockService.Object, options: new("test instructions"));
ChatClientAgent agent = new(mockService.Object, options: new() { ChatOptions = new() { Instructions = "test instructions" } });
var messages = new List<ChatMessage> { new(ChatRole.User, "test") };
// Act
@@ -1167,6 +1167,7 @@ public partial class ChatClientAgentTests
// Arrange
var agentChatOptions = new ChatOptions
{
Instructions = "test instructions",
MaxOutputTokens = 100,
Temperature = 0.7f,
TopP = 0.9f,
@@ -1204,7 +1205,6 @@ public partial class ChatClientAgentTests
ChatClientAgent agent = new(mockService.Object, options: new()
{
Instructions = "test instructions",
ChatOptions = agentChatOptions
});
var messages = new List<ChatMessage> { new(ChatRole.User, "test") };
@@ -1263,6 +1263,7 @@ public partial class ChatClientAgentTests
var agentChatOptions = new ChatOptions
{
Instructions = "test instructions",
Tools = [agentTool]
};
var requestChatOptions = new ChatOptions
@@ -1283,7 +1284,6 @@ public partial class ChatClientAgentTests
ChatClientAgent agent = new(mockService.Object, options: new()
{
Instructions = "test instructions",
ChatOptions = agentChatOptions
});
var messages = new List<ChatMessage> { new(ChatRole.User, "test") };
@@ -1312,6 +1312,7 @@ public partial class ChatClientAgentTests
var agentChatOptions = new ChatOptions
{
Instructions = "test instructions",
Tools = [agentTool]
};
var requestChatOptions = new ChatOptions
@@ -1333,7 +1334,6 @@ public partial class ChatClientAgentTests
ChatClientAgent agent = new(mockService.Object, options: new()
{
Instructions = "test instructions",
ChatOptions = agentChatOptions
});
var messages = new List<ChatMessage> { new(ChatRole.User, "test") };
@@ -1360,6 +1360,7 @@ public partial class ChatClientAgentTests
// Arrange
var agentChatOptions = new ChatOptions
{
Instructions = "test instructions",
RawRepresentationFactory = _ => agentSetting
};
var requestChatOptions = new ChatOptions
@@ -1380,7 +1381,6 @@ public partial class ChatClientAgentTests
ChatClientAgent agent = new(mockService.Object, options: new()
{
Instructions = "test instructions",
ChatOptions = agentChatOptions
});
var messages = new List<ChatMessage> { new(ChatRole.User, "test") };
@@ -1436,7 +1436,7 @@ public partial class ChatClientAgentTests
TopK = 50,
PresencePenalty = 0.1f,
FrequencyPenalty = 0.2f,
Instructions = "test instructions\nrequest instructions",
Instructions = "agent instructions\nrequest instructions",
ModelId = "agent-model",
Seed = 12345,
ConversationId = "agent-conversation",
@@ -1459,7 +1459,6 @@ public partial class ChatClientAgentTests
ChatClientAgent agent = new(mockService.Object, options: new()
{
Instructions = "test instructions",
ChatOptions = agentChatOptions
});
var messages = new List<ChatMessage> { new(ChatRole.User, "test") };
@@ -1509,7 +1508,7 @@ public partial class ChatClientAgentTests
{
Id = "test-agent-id",
Name = "TestAgent",
Instructions = "Test instructions"
ChatOptions = new() { Instructions = "Test instructions" }
});
// Act
@@ -1532,7 +1531,7 @@ public partial class ChatClientAgentTests
var mockChatClient = new Mock<IChatClient>();
var agent = new ChatClientAgent(mockChatClient.Object, new ChatClientAgentOptions
{
Instructions = "Test instructions"
ChatOptions = new() { Instructions = "Test instructions" }
});
// Act
@@ -1556,7 +1555,7 @@ public partial class ChatClientAgentTests
var mockChatClient = new Mock<IChatClient>();
var agent = new ChatClientAgent(mockChatClient.Object, new ChatClientAgentOptions
{
Instructions = "Test instructions"
ChatOptions = new() { Instructions = "Test instructions" }
});
// Act
@@ -1582,7 +1581,7 @@ public partial class ChatClientAgentTests
var agent = new ChatClientAgent(mockChatClient.Object, new ChatClientAgentOptions
{
Instructions = "Test instructions"
ChatOptions = new() { Instructions = "Test instructions" }
});
// Act
@@ -1606,7 +1605,7 @@ public partial class ChatClientAgentTests
var agent = new ChatClientAgent(mockChatClient.Object, new ChatClientAgentOptions
{
Instructions = "Test instructions"
ChatOptions = new() { Instructions = "Test instructions" }
});
// Act
@@ -1632,7 +1631,7 @@ public partial class ChatClientAgentTests
var agent = new ChatClientAgent(mockChatClient.Object, new ChatClientAgentOptions
{
Instructions = "Test instructions"
ChatOptions = new() { Instructions = "Test instructions" }
});
// Act
@@ -1661,7 +1660,7 @@ public partial class ChatClientAgentTests
var agent = new ChatClientAgent(mockChatClient.Object, new ChatClientAgentOptions
{
Instructions = "Test instructions"
ChatOptions = new() { Instructions = "Test instructions" }
});
// Act
@@ -1694,7 +1693,7 @@ public partial class ChatClientAgentTests
{
Id = "test-agent-id",
Name = "TestAgent",
Instructions = "Test instructions"
ChatOptions = new() { Instructions = "Test instructions" }
});
// Act
@@ -1721,7 +1720,7 @@ public partial class ChatClientAgentTests
var agent = new ChatClientAgent(mockChatClient.Object, new ChatClientAgentOptions
{
Instructions = "Test instructions"
ChatOptions = new() { Instructions = "Test instructions" }
});
// Act
@@ -1756,12 +1755,12 @@ public partial class ChatClientAgentTests
var chatClientAgent1 = new ChatClientAgent(mockChatClient1.Object, new ChatClientAgentOptions
{
Instructions = "Test instructions 1"
ChatOptions = new() { Instructions = "Test instructions 1" }
});
var chatClientAgent2 = new ChatClientAgent(mockChatClient2.Object, new ChatClientAgentOptions
{
Instructions = "Test instructions 2"
ChatOptions = new() { Instructions = "Test instructions 2" }
});
// Act
@@ -1796,7 +1795,7 @@ public partial class ChatClientAgentTests
var mockChatClient = new Mock<IChatClient>();
var agent = new ChatClientAgent(mockChatClient.Object, new ChatClientAgentOptions
{
Instructions = "Test instructions"
ChatOptions = new() { Instructions = "Test instructions" }
});
// Act
@@ -1820,7 +1819,7 @@ public partial class ChatClientAgentTests
var mockChatClient = new Mock<IChatClient>();
var agent = new ChatClientAgent(mockChatClient.Object, new ChatClientAgentOptions
{
Instructions = "Test instructions"
ChatOptions = new() { Instructions = "Test instructions" }
});
// Act
@@ -1845,7 +1844,7 @@ public partial class ChatClientAgentTests
var mockChatClient = new Mock<IChatClient>();
var agent = new ChatClientAgent(mockChatClient.Object, new ChatClientAgentOptions
{
Instructions = "Test instructions"
ChatOptions = new() { Instructions = "Test instructions" }
});
// Act - Request IChatClient with a service key (base.GetService will return null due to serviceKey)
@@ -1870,7 +1869,7 @@ public partial class ChatClientAgentTests
mockChatClient.Setup(c => c.GetService(typeof(string), "some-key")).Returns("test-result");
var agent = new ChatClientAgent(mockChatClient.Object, new ChatClientAgentOptions
{
Instructions = "Test instructions"
ChatOptions = new() { Instructions = "Test instructions" }
});
// Act - Request string with a service key (base.GetService will return null due to serviceKey)
@@ -1911,7 +1910,7 @@ public partial class ChatClientAgentTests
ChatClientAgent agent =
new(mockService.Object, options: new()
{
Instructions = "test instructions"
ChatOptions = new() { Instructions = "test instructions" }
});
// Act
@@ -1958,7 +1957,7 @@ public partial class ChatClientAgentTests
mockFactory.Setup(f => f(It.IsAny<ChatClientAgentOptions.ChatMessageStoreFactoryContext>())).Returns(new InMemoryChatMessageStore());
ChatClientAgent agent = new(mockService.Object, options: new()
{
Instructions = "test instructions",
ChatOptions = new() { Instructions = "test instructions" },
ChatMessageStoreFactory = mockFactory.Object
});
@@ -1996,7 +1995,7 @@ public partial class ChatClientAgentTests
mockFactory.Setup(f => f(It.IsAny<ChatClientAgentOptions.ChatMessageStoreFactoryContext>())).Returns(new InMemoryChatMessageStore());
ChatClientAgent agent = new(mockService.Object, options: new()
{
Instructions = "test instructions",
ChatOptions = new() { Instructions = "test instructions" },
ChatMessageStoreFactory = mockFactory.Object
});
@@ -2049,7 +2048,7 @@ public partial class ChatClientAgentTests
.Setup(p => p.InvokedAsync(It.IsAny<AIContextProvider.InvokedContext>(), It.IsAny<CancellationToken>()))
.Returns(new ValueTask());
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "base instructions", AIContextProviderFactory = _ => mockProvider.Object, ChatOptions = new() { Tools = [AIFunctionFactory.Create(() => { }, "base function")] } });
ChatClientAgent agent = new(mockService.Object, options: new() { ChatOptions = new() { Instructions = "base instructions", Tools = [AIFunctionFactory.Create(() => { }, "base function")] }, AIContextProviderFactory = _ => mockProvider.Object });
// Act
var thread = agent.GetNewThread() as ChatClientAgentThread;
@@ -2112,7 +2111,7 @@ public partial class ChatClientAgentTests
.Setup(p => p.InvokedAsync(It.IsAny<AIContextProvider.InvokedContext>(), It.IsAny<CancellationToken>()))
.Returns(new ValueTask());
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "base instructions", AIContextProviderFactory = _ => mockProvider.Object, ChatOptions = new() { Tools = [AIFunctionFactory.Create(() => { }, "base function")] } });
ChatClientAgent agent = new(mockService.Object, options: new() { ChatOptions = new() { Instructions = "base instructions", Tools = [AIFunctionFactory.Create(() => { }, "base function")] }, AIContextProviderFactory = _ => mockProvider.Object });
// Act
await Assert.ThrowsAsync<InvalidOperationException>(async () =>
@@ -20,7 +20,7 @@ public class ChatClientAgent_DeserializeThreadTests
var factoryCalled = false;
var agent = new ChatClientAgent(mockChatClient.Object, new ChatClientAgentOptions
{
Instructions = "Test instructions",
ChatOptions = new() { Instructions = "Test instructions" },
AIContextProviderFactory = _ =>
{
factoryCalled = true;
@@ -53,7 +53,7 @@ public class ChatClientAgent_DeserializeThreadTests
var factoryCalled = false;
var agent = new ChatClientAgent(mockChatClient.Object, new ChatClientAgentOptions
{
Instructions = "Test instructions",
ChatOptions = new() { Instructions = "Test instructions" },
ChatMessageStoreFactory = _ =>
{
factoryCalled = true;
@@ -19,7 +19,7 @@ public class ChatClientAgent_GetNewThreadTests
var factoryCalled = false;
var agent = new ChatClientAgent(mockChatClient.Object, new ChatClientAgentOptions
{
Instructions = "Test instructions",
ChatOptions = new() { Instructions = "Test instructions" },
AIContextProviderFactory = _ =>
{
factoryCalled = true;
@@ -46,7 +46,7 @@ public class ChatClientAgent_GetNewThreadTests
var factoryCalled = false;
var agent = new ChatClientAgent(mockChatClient.Object, new ChatClientAgentOptions
{
Instructions = "Test instructions",
ChatOptions = new() { Instructions = "Test instructions" },
ChatMessageStoreFactory = _ =>
{
factoryCalled = true;
@@ -90,7 +90,7 @@ public sealed class ChatClientBuilderExtensionsTests
{
Name = "AgentWithOptions",
Description = "Desc",
Instructions = "Instr",
ChatOptions = new() { Instructions = "Instr" },
UseProvidedChatClientAsIs = true
};
@@ -115,7 +115,7 @@ public sealed class ChatClientBuilderExtensionsTests
var options = new ChatClientAgentOptions
{
Name = "ServiceAgent",
Instructions = "Service instructions"
ChatOptions = new() { Instructions = "Service instructions" }
};
// Act
@@ -148,7 +148,7 @@ public sealed class ChatClientBuilderExtensionsTests
ChatClientBuilder builder = null!;
// Act & Assert
Assert.Throws<ArgumentNullException>(() => builder.BuildAIAgent(options: new() { Instructions = "instructions" }));
Assert.Throws<ArgumentNullException>(() => builder.BuildAIAgent(options: new() { ChatOptions = new() { Instructions = "instructions" } }));
}
[Fact]
@@ -166,7 +166,7 @@ public sealed class ChatClientBuilderExtensionsTests
var agent = builder.BuildAIAgent(
new ChatClientAgentOptions
{
Instructions = "Middleware test",
ChatOptions = new() { Instructions = "Middleware test" },
UseProvidedChatClientAsIs = true
}
);
@@ -57,7 +57,7 @@ public sealed class ChatClientExtensionsTests
{
Name = "AgentWithOptions",
Description = "Desc",
Instructions = "Instr",
ChatOptions = new() { Instructions = "Instr" },
UseProvidedChatClientAsIs = true
};
@@ -89,6 +89,6 @@ public sealed class ChatClientExtensionsTests
IChatClient chatClient = null!;
// Act & Assert
Assert.Throws<ArgumentNullException>(() => chatClient.CreateAIAgent(options: new() { Instructions = "instructions" }));
Assert.Throws<ArgumentNullException>(() => chatClient.CreateAIAgent(options: new() { ChatOptions = new() { Instructions = "instructions" } }));
}
}
@@ -37,14 +37,24 @@ public class OpenAIAssistantClientExtensionsTests
{
"CreateWithChatClientAgentOptionsAsync" => await this._assistantClient.CreateAIAgentAsync(
model: s_config.ChatModelId!,
options: new ChatClientAgentOptions(
instructions: AgentInstructions,
tools: [weatherFunction])),
options: new ChatClientAgentOptions()
{
ChatOptions = new()
{
Instructions = AgentInstructions,
Tools = [weatherFunction]
}
}),
"CreateWithChatClientAgentOptionsSync" => this._assistantClient.CreateAIAgent(
model: s_config.ChatModelId!,
options: new ChatClientAgentOptions(
instructions: AgentInstructions,
tools: [weatherFunction])),
options: new ChatClientAgentOptions()
{
ChatOptions = new()
{
Instructions = AgentInstructions,
Tools = [weatherFunction]
}
}),
"CreateWithParamsAsync" => await this._assistantClient.CreateAIAgentAsync(
model: s_config.ChatModelId!,
instructions: AgentInstructions,
@@ -94,14 +104,24 @@ public class OpenAIAssistantClientExtensionsTests
{
"CreateWithChatClientAgentOptionsAsync" => await this._assistantClient.CreateAIAgentAsync(
model: s_config.ChatModelId!,
options: new ChatClientAgentOptions(
instructions: Instructions,
tools: [codeInterpreterTool])),
options: new ChatClientAgentOptions()
{
ChatOptions = new()
{
Instructions = Instructions,
Tools = [codeInterpreterTool]
}
}),
"CreateWithChatClientAgentOptionsSync" => this._assistantClient.CreateAIAgent(
model: s_config.ChatModelId!,
options: new ChatClientAgentOptions(
instructions: Instructions,
tools: [codeInterpreterTool])),
options: new ChatClientAgentOptions()
{
ChatOptions = new()
{
Instructions = Instructions,
Tools = [codeInterpreterTool]
}
}),
"CreateWithParamsAsync" => await this._assistantClient.CreateAIAgentAsync(
model: s_config.ChatModelId!,
instructions: Instructions,
@@ -159,14 +179,24 @@ public class OpenAIAssistantClientExtensionsTests
{
"CreateWithChatClientAgentOptionsAsync" => await this._assistantClient.CreateAIAgentAsync(
model: s_config.ChatModelId!,
options: new ChatClientAgentOptions(
instructions: Instructions,
tools: [fileSearchTool])),
options: new ChatClientAgentOptions()
{
ChatOptions = new()
{
Instructions = Instructions,
Tools = [fileSearchTool]
}
}),
"CreateWithChatClientAgentOptionsSync" => this._assistantClient.CreateAIAgent(
model: s_config.ChatModelId!,
options: new ChatClientAgentOptions(
instructions: Instructions,
tools: [fileSearchTool])),
options: new ChatClientAgentOptions()
{
ChatOptions = new()
{
Instructions = Instructions,
Tools = [fileSearchTool]
}
}),
"CreateWithParamsAsync" => await this._assistantClient.CreateAIAgentAsync(
model: s_config.ChatModelId!,
instructions: Instructions,
@@ -47,8 +47,7 @@ public class OpenAIChatCompletionFixture : IChatClientAgentFixture
return Task.FromResult(new ChatClientAgent(chatClient, options: new()
{
Name = name,
Instructions = instructions,
ChatOptions = new() { Tools = aiTools }
ChatOptions = new() { Instructions = instructions, Tools = aiTools }
}));
}
@@ -73,9 +73,9 @@ public class OpenAIResponseFixture(bool store) : IChatClientAgentFixture
options: new()
{
Name = name,
Instructions = instructions,
ChatOptions = new ChatOptions
{
Instructions = instructions,
Tools = aiTools,
RawRepresentationFactory = new Func<IChatClient, object>(_ => new ResponseCreationOptions() { StoredOutputEnabled = store })
},