mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Enable more analyzers and various code tweaks/cleanup (#738)
This commit is contained in:
@@ -12,7 +12,6 @@ internal sealed class HostClientAgent
|
||||
{
|
||||
internal HostClientAgent(ILoggerFactory loggerFactory)
|
||||
{
|
||||
this._loggerFactory = loggerFactory;
|
||||
this._logger = loggerFactory.CreateLogger("HostClientAgent");
|
||||
}
|
||||
|
||||
@@ -23,7 +22,7 @@ internal sealed class HostClientAgent
|
||||
this._logger.LogInformation("Initializing Agent Framework agent with model: {ModelId}", modelId);
|
||||
|
||||
// Connect to the remote agents via A2A
|
||||
var createAgentTasks = agentUrls.Select(agentUrl => this.CreateAgentAsync(agentUrl));
|
||||
var createAgentTasks = agentUrls.Select(CreateAgentAsync);
|
||||
var agents = await Task.WhenAll(createAgentTasks);
|
||||
var tools = agents.Select(agent => (AITool)agent.AsAIFunction()).ToList();
|
||||
|
||||
@@ -45,10 +44,9 @@ internal sealed class HostClientAgent
|
||||
public AIAgent? Agent { get; private set; }
|
||||
|
||||
#region private
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
private async Task<AIAgent> CreateAgentAsync(string agentUri)
|
||||
private static async Task<AIAgent> CreateAgentAsync(string agentUri)
|
||||
{
|
||||
var url = new Uri(agentUri);
|
||||
var httpClient = new HttpClient
|
||||
|
||||
@@ -57,7 +57,7 @@ public static class Program
|
||||
continue;
|
||||
}
|
||||
|
||||
if (message == ":q" || message == "quit")
|
||||
if (message is ":q" or "quit")
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -20,10 +20,7 @@ public class Product
|
||||
this.Price = price;
|
||||
}
|
||||
|
||||
public decimal TotalPrice()
|
||||
{
|
||||
return this.Quantity * this.Price; // Total price for this product
|
||||
}
|
||||
public decimal TotalPrice() => this.Quantity * this.Price; // Total price for this product
|
||||
}
|
||||
|
||||
public class Invoice
|
||||
@@ -43,82 +40,78 @@ public class Invoice
|
||||
this.Products = products;
|
||||
}
|
||||
|
||||
public decimal TotalInvoicePrice()
|
||||
{
|
||||
return this.Products.Sum(product => product.TotalPrice()); // Total price of all products in the invoice
|
||||
}
|
||||
public decimal TotalInvoicePrice() => this.Products.Sum(product => product.TotalPrice()); // Total price of all products in the invoice
|
||||
}
|
||||
|
||||
public class InvoiceQuery
|
||||
{
|
||||
private readonly List<Invoice> _invoices;
|
||||
private static readonly Random s_random = new();
|
||||
|
||||
public InvoiceQuery()
|
||||
{
|
||||
// Extended mock data with quantities and prices
|
||||
this._invoices =
|
||||
[
|
||||
new("TICKET-XYZ987", "INV789", "Contoso", GetRandomDateWithinLastTwoMonths(), new List<Product>
|
||||
{
|
||||
new("TICKET-XYZ987", "INV789", "Contoso", GetRandomDateWithinLastTwoMonths(),
|
||||
[
|
||||
new("T-Shirts", 150, 10.00m),
|
||||
new("Hats", 200, 15.00m),
|
||||
new("Glasses", 300, 5.00m)
|
||||
}),
|
||||
new("TICKET-XYZ111", "INV111", "XStore", GetRandomDateWithinLastTwoMonths(), new List<Product>
|
||||
{
|
||||
]),
|
||||
new("TICKET-XYZ111", "INV111", "XStore", GetRandomDateWithinLastTwoMonths(),
|
||||
[
|
||||
new("T-Shirts", 2500, 12.00m),
|
||||
new("Hats", 1500, 8.00m),
|
||||
new("Glasses", 200, 20.00m)
|
||||
}),
|
||||
new("TICKET-XYZ222", "INV222", "Cymbal Direct", GetRandomDateWithinLastTwoMonths(), new List<Product>
|
||||
{
|
||||
]),
|
||||
new("TICKET-XYZ222", "INV222", "Cymbal Direct", GetRandomDateWithinLastTwoMonths(),
|
||||
[
|
||||
new("T-Shirts", 1200, 14.00m),
|
||||
new("Hats", 800, 7.00m),
|
||||
new("Glasses", 500, 25.00m)
|
||||
}),
|
||||
new("TICKET-XYZ333", "INV333", "Contoso", GetRandomDateWithinLastTwoMonths(), new List<Product>
|
||||
{
|
||||
]),
|
||||
new("TICKET-XYZ333", "INV333", "Contoso", GetRandomDateWithinLastTwoMonths(),
|
||||
[
|
||||
new("T-Shirts", 400, 11.00m),
|
||||
new("Hats", 600, 15.00m),
|
||||
new("Glasses", 700, 5.00m)
|
||||
}),
|
||||
new("TICKET-XYZ444", "INV444", "XStore", GetRandomDateWithinLastTwoMonths(), new List<Product>
|
||||
{
|
||||
]),
|
||||
new("TICKET-XYZ444", "INV444", "XStore", GetRandomDateWithinLastTwoMonths(),
|
||||
[
|
||||
new("T-Shirts", 800, 10.00m),
|
||||
new("Hats", 500, 18.00m),
|
||||
new("Glasses", 300, 22.00m)
|
||||
}),
|
||||
new("TICKET-XYZ555", "INV555", "Cymbal Direct", GetRandomDateWithinLastTwoMonths(), new List<Product>
|
||||
{
|
||||
]),
|
||||
new("TICKET-XYZ555", "INV555", "Cymbal Direct", GetRandomDateWithinLastTwoMonths(),
|
||||
[
|
||||
new("T-Shirts", 1100, 9.00m),
|
||||
new("Hats", 900, 12.00m),
|
||||
new("Glasses", 1200, 15.00m)
|
||||
}),
|
||||
new("TICKET-XYZ666", "INV666", "Contoso", GetRandomDateWithinLastTwoMonths(), new List<Product>
|
||||
{
|
||||
]),
|
||||
new("TICKET-XYZ666", "INV666", "Contoso", GetRandomDateWithinLastTwoMonths(),
|
||||
[
|
||||
new("T-Shirts", 2500, 8.00m),
|
||||
new("Hats", 1200, 10.00m),
|
||||
new("Glasses", 1000, 6.00m)
|
||||
}),
|
||||
new("TICKET-XYZ777", "INV777", "XStore", GetRandomDateWithinLastTwoMonths(), new List<Product>
|
||||
{
|
||||
]),
|
||||
new("TICKET-XYZ777", "INV777", "XStore", GetRandomDateWithinLastTwoMonths(),
|
||||
[
|
||||
new("T-Shirts", 1900, 13.00m),
|
||||
new("Hats", 1300, 16.00m),
|
||||
new("Glasses", 800, 19.00m)
|
||||
}),
|
||||
new("TICKET-XYZ888", "INV888", "Cymbal Direct", GetRandomDateWithinLastTwoMonths(), new List<Product>
|
||||
{
|
||||
]),
|
||||
new("TICKET-XYZ888", "INV888", "Cymbal Direct", GetRandomDateWithinLastTwoMonths(),
|
||||
[
|
||||
new("T-Shirts", 2200, 11.00m),
|
||||
new("Hats", 1700, 8.50m),
|
||||
new("Glasses", 600, 21.00m)
|
||||
}),
|
||||
new("TICKET-XYZ999", "INV999", "Contoso", GetRandomDateWithinLastTwoMonths(), new List<Product>
|
||||
{
|
||||
]),
|
||||
new("TICKET-XYZ999", "INV999", "Contoso", GetRandomDateWithinLastTwoMonths(),
|
||||
[
|
||||
new("T-Shirts", 1400, 10.50m),
|
||||
new("Hats", 1100, 9.00m),
|
||||
new("Glasses", 950, 12.00m)
|
||||
})
|
||||
])
|
||||
];
|
||||
}
|
||||
|
||||
@@ -132,9 +125,7 @@ public class InvoiceQuery
|
||||
|
||||
// Generate a random number of days between 0 and the total number of days in the range
|
||||
int totalDays = (endDate - startDate).Days;
|
||||
#pragma warning disable CA5394 // Do not use insecure randomness
|
||||
int randomDays = s_random.Next(0, totalDays + 1); // +1 to include the end date
|
||||
#pragma warning restore CA5394 // Do not use insecure randomness
|
||||
int randomDays = Random.Shared.Next(0, totalDays + 1); // +1 to include the end date
|
||||
|
||||
// Return the random date
|
||||
return startDate.AddDays(randomDays);
|
||||
|
||||
@@ -28,12 +28,12 @@ internal static class HttpActorProcessor
|
||||
return GetResult(response);
|
||||
}
|
||||
|
||||
if (streaming == true)
|
||||
if (streaming is true)
|
||||
{
|
||||
return new ActorUpdateStreamingResult(responseHandle);
|
||||
}
|
||||
|
||||
if (blocking == true)
|
||||
if (blocking is true)
|
||||
{
|
||||
response = await responseHandle.GetResponseAsync(cancellationToken);
|
||||
return GetResult(response);
|
||||
@@ -44,7 +44,7 @@ internal static class HttpActorProcessor
|
||||
ActorId = actorId,
|
||||
MessageId = messageId,
|
||||
Status = RequestStatus.Pending,
|
||||
Data = JsonDocument.Parse("{}").RootElement
|
||||
Data = JsonSerializer.Deserialize<JsonElement>("{}"),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -64,12 +64,12 @@ internal static class HttpActorProcessor
|
||||
return GetResult(response);
|
||||
}
|
||||
|
||||
if (streaming == true)
|
||||
if (streaming is true)
|
||||
{
|
||||
return new ActorUpdateStreamingResult(responseHandle);
|
||||
}
|
||||
|
||||
if (blocking == true)
|
||||
if (blocking is true)
|
||||
{
|
||||
response = await responseHandle.GetResponseAsync(cancellationToken);
|
||||
return GetResult(response);
|
||||
|
||||
@@ -83,10 +83,7 @@ builder.Services.AddCosmosActorStateStorage("actor-state-db", "ActorState");
|
||||
var app = builder.Build();
|
||||
|
||||
app.MapOpenApi();
|
||||
app.UseSwaggerUI(options =>
|
||||
{
|
||||
options.SwaggerEndpoint("/openapi/v1.json", "Agents API");
|
||||
});
|
||||
app.UseSwaggerUI(options => options.SwaggerEndpoint("/openapi/v1.json", "Agents API"));
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
app.UseExceptionHandler();
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ public class ChatClientConnectionInfo
|
||||
Enum.TryParse(providerValue, ignoreCase: true, out provider);
|
||||
}
|
||||
|
||||
if (endpoint is null && provider != ClientChatProvider.OpenAI || model is null || provider == ClientChatProvider.Unknown)
|
||||
if ((endpoint is null && provider != ClientChatProvider.OpenAI) || model is null || provider is ClientChatProvider.Unknown)
|
||||
{
|
||||
settings = null;
|
||||
return false;
|
||||
|
||||
+10
-24
@@ -36,19 +36,16 @@ public static class ChatClientExtensions
|
||||
return chatClientBuilder;
|
||||
}
|
||||
|
||||
private static ChatClientBuilder AddOpenAIClient(this IHostApplicationBuilder builder, string connectionName, ChatClientConnectionInfo connectionInfo)
|
||||
{
|
||||
return builder.AddOpenAIClient(connectionName, settings =>
|
||||
private static ChatClientBuilder AddOpenAIClient(this IHostApplicationBuilder builder, string connectionName, ChatClientConnectionInfo connectionInfo) =>
|
||||
builder.AddOpenAIClient(connectionName, settings =>
|
||||
{
|
||||
settings.Endpoint = connectionInfo.Endpoint;
|
||||
settings.Key = connectionInfo.AccessKey;
|
||||
})
|
||||
.AddChatClient(connectionInfo.SelectedModel);
|
||||
}
|
||||
|
||||
private static ChatClientBuilder AddAzureInferenceClient(this IHostApplicationBuilder builder, string connectionName, ChatClientConnectionInfo connectionInfo)
|
||||
{
|
||||
return builder.Services.AddChatClient(sp =>
|
||||
private static ChatClientBuilder AddAzureInferenceClient(this IHostApplicationBuilder builder, string connectionName, ChatClientConnectionInfo connectionInfo) =>
|
||||
builder.Services.AddChatClient(sp =>
|
||||
{
|
||||
var credential = new AzureKeyCredential(connectionInfo.AccessKey!);
|
||||
|
||||
@@ -56,16 +53,12 @@ public static class ChatClientExtensions
|
||||
|
||||
return client.AsIChatClient(connectionInfo.SelectedModel);
|
||||
});
|
||||
}
|
||||
|
||||
private static ChatClientBuilder AddOllamaClient(this IHostApplicationBuilder builder, string connectionName, ChatClientConnectionInfo connectionInfo)
|
||||
{
|
||||
var httpKey = $"{connectionName}_http";
|
||||
|
||||
builder.Services.AddHttpClient(httpKey, c =>
|
||||
{
|
||||
c.BaseAddress = connectionInfo.Endpoint;
|
||||
});
|
||||
builder.Services.AddHttpClient(httpKey, c => c.BaseAddress = connectionInfo.Endpoint);
|
||||
|
||||
return builder.Services.AddChatClient(sp =>
|
||||
{
|
||||
@@ -102,19 +95,16 @@ public static class ChatClientExtensions
|
||||
return chatClientBuilder;
|
||||
}
|
||||
|
||||
private static ChatClientBuilder AddKeyedOpenAIClient(this IHostApplicationBuilder builder, string connectionName, ChatClientConnectionInfo connectionInfo)
|
||||
{
|
||||
return builder.AddKeyedOpenAIClient(connectionName, settings =>
|
||||
private static ChatClientBuilder AddKeyedOpenAIClient(this IHostApplicationBuilder builder, string connectionName, ChatClientConnectionInfo connectionInfo) =>
|
||||
builder.AddKeyedOpenAIClient(connectionName, settings =>
|
||||
{
|
||||
settings.Endpoint = connectionInfo.Endpoint;
|
||||
settings.Key = connectionInfo.AccessKey;
|
||||
})
|
||||
.AddKeyedChatClient(connectionName, connectionInfo.SelectedModel);
|
||||
}
|
||||
|
||||
private static ChatClientBuilder AddKeyedAzureInferenceClient(this IHostApplicationBuilder builder, string connectionName, ChatClientConnectionInfo connectionInfo)
|
||||
{
|
||||
return builder.Services.AddKeyedChatClient(connectionName, sp =>
|
||||
private static ChatClientBuilder AddKeyedAzureInferenceClient(this IHostApplicationBuilder builder, string connectionName, ChatClientConnectionInfo connectionInfo) =>
|
||||
builder.Services.AddKeyedChatClient(connectionName, sp =>
|
||||
{
|
||||
var credential = new AzureKeyCredential(connectionInfo.AccessKey!);
|
||||
|
||||
@@ -122,16 +112,12 @@ public static class ChatClientExtensions
|
||||
|
||||
return client.AsIChatClient(connectionInfo.SelectedModel);
|
||||
});
|
||||
}
|
||||
|
||||
private static ChatClientBuilder AddKeyedOllamaClient(this IHostApplicationBuilder builder, string connectionName, ChatClientConnectionInfo connectionInfo)
|
||||
{
|
||||
var httpKey = $"{connectionName}_http";
|
||||
|
||||
builder.Services.AddHttpClient(httpKey, c =>
|
||||
{
|
||||
c.BaseAddress = connectionInfo.Endpoint;
|
||||
});
|
||||
builder.Services.AddHttpClient(httpKey, c => c.BaseAddress = connectionInfo.Endpoint);
|
||||
|
||||
return builder.Services.AddKeyedChatClient(connectionName, sp =>
|
||||
{
|
||||
|
||||
@@ -19,7 +19,7 @@ internal sealed class A2AActorClient : IActorClient
|
||||
|
||||
// because A2A sdk does not provide a client which can handle multiple agents, we need a client per agent
|
||||
// for this app the convention is "baseUri/<agentname>"
|
||||
private readonly ConcurrentDictionary<string, (A2AClient, A2ACardResolver)> _clients = new();
|
||||
private readonly ConcurrentDictionary<string, (A2AClient, A2ACardResolver)> _clients = [];
|
||||
|
||||
public A2AActorClient(ILogger logger, Uri baseUri)
|
||||
{
|
||||
@@ -35,10 +35,7 @@ internal sealed class A2AActorClient : IActorClient
|
||||
return a2aCardResolver.GetAgentCardAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public ValueTask<ActorResponseHandle> GetResponseAsync(ActorId actorId, string messageId, CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public ValueTask<ActorResponseHandle> GetResponseAsync(ActorId actorId, string messageId, CancellationToken cancellationToken) => throw new NotImplementedException();
|
||||
|
||||
public ValueTask<ActorResponseHandle> SendRequestAsync(ActorRequest request, CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -51,9 +48,8 @@ internal sealed class A2AActorClient : IActorClient
|
||||
private (A2AClient, A2ACardResolver) ResolveClient(ActorType agentName)
|
||||
=> this.ResolveClient(agentName.Name);
|
||||
|
||||
private (A2AClient, A2ACardResolver) ResolveClient(string agentName)
|
||||
{
|
||||
return this._clients.GetOrAdd(agentName, name =>
|
||||
private (A2AClient, A2ACardResolver) ResolveClient(string agentName) =>
|
||||
this._clients.GetOrAdd(agentName, name =>
|
||||
{
|
||||
var uri = new Uri($"{this._uri}/{name}/");
|
||||
var a2aClient = new A2AClient(uri);
|
||||
@@ -64,7 +60,6 @@ internal sealed class A2AActorClient : IActorClient
|
||||
this._logger.LogInformation("Built clients for agent {Agent} with baseUri {Uri}", name, uri);
|
||||
return (a2aClient, a2aCardResolver);
|
||||
});
|
||||
}
|
||||
|
||||
private sealed class A2AActorResponseHandle : ActorResponseHandle
|
||||
{
|
||||
@@ -77,20 +72,11 @@ internal sealed class A2AActorClient : IActorClient
|
||||
this._request = request;
|
||||
}
|
||||
|
||||
public override ValueTask CancelAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public override ValueTask CancelAsync(CancellationToken cancellationToken) => throw new NotImplementedException();
|
||||
|
||||
public override ValueTask<ActorResponse> GetResponseAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public override ValueTask<ActorResponse> GetResponseAsync(CancellationToken cancellationToken) => throw new NotImplementedException();
|
||||
|
||||
public override bool TryGetResponse([NotNullWhen(true)] out ActorResponse? response)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public override bool TryGetResponse([NotNullWhen(true)] out ActorResponse? response) => throw new NotImplementedException();
|
||||
|
||||
public override async IAsyncEnumerable<ActorRequestUpdate> WatchUpdatesAsync([EnumeratorCancellation] CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
@@ -31,8 +31,5 @@
|
||||
private string? requestId;
|
||||
private bool ShowRequestId => !string.IsNullOrEmpty(requestId);
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
requestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier;
|
||||
}
|
||||
protected override void OnInitialized() => requestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier;
|
||||
}
|
||||
|
||||
@@ -907,7 +907,7 @@
|
||||
A2A // Agent-to-Agent protocol
|
||||
}
|
||||
|
||||
private class Conversation
|
||||
private sealed class Conversation
|
||||
{
|
||||
public string SessionId { get; set; } = Guid.NewGuid().ToString();
|
||||
public string AgentName { get; set; } = "";
|
||||
@@ -944,32 +944,23 @@
|
||||
// Conversations start fresh on page load
|
||||
}
|
||||
|
||||
private string GetAgentIcon(string agentName)
|
||||
{
|
||||
return agentName?.ToLower() switch
|
||||
{
|
||||
"pirate" => "🏴☠️",
|
||||
"knights-and-knaves" => "⚔️",
|
||||
_ => "🤖"
|
||||
};
|
||||
}
|
||||
private string GetAgentIcon(string agentName) => agentName?.ToLower() switch
|
||||
{
|
||||
"pirate" => "🏴☠️",
|
||||
"knights-and-knaves" => "⚔️",
|
||||
_ => "🤖"
|
||||
};
|
||||
|
||||
private string GetAgentDisplayName(string agentName)
|
||||
{
|
||||
return agentName?.ToLower() switch
|
||||
{
|
||||
"pirate" => "Pirate",
|
||||
"knights-and-knaves" => "Knights & Knaves",
|
||||
_ => agentName ?? "Agent"
|
||||
};
|
||||
}
|
||||
private string GetAgentDisplayName(string agentName) => agentName?.ToLower() switch
|
||||
{
|
||||
"pirate" => "Pirate",
|
||||
"knights-and-knaves" => "Knights & Knaves",
|
||||
_ => agentName ?? "Agent"
|
||||
};
|
||||
|
||||
private void ToggleA2AExpanded()
|
||||
{
|
||||
isA2AExpanded = !isA2AExpanded;
|
||||
}
|
||||
private void ToggleA2AExpanded() => isA2AExpanded = !isA2AExpanded;
|
||||
|
||||
private async Task DiscoverAgentCard()
|
||||
private async Task DiscoverAgentCard()
|
||||
{
|
||||
if (string.IsNullOrEmpty(selectedAgentName) || isDiscoveringCard)
|
||||
return;
|
||||
|
||||
@@ -99,7 +99,7 @@ internal sealed class HttpActorClient(HttpClient httpClient) : IActorClient
|
||||
public override bool TryGetResponse([NotNullWhen(true)] out ActorResponse? response)
|
||||
{
|
||||
response = this._lastResponse;
|
||||
return response != null;
|
||||
return response is not null;
|
||||
}
|
||||
|
||||
public override async IAsyncEnumerable<ActorRequestUpdate> WatchUpdatesAsync([EnumeratorCancellation] CancellationToken cancellationToken)
|
||||
@@ -138,7 +138,7 @@ internal sealed class HttpActorClient(HttpClient httpClient) : IActorClient
|
||||
private static async IAsyncEnumerable<ActorRequestUpdate> EnumerateAsync(HttpResponseMessage responseMessage, [EnumeratorCancellation] CancellationToken cancellationToken)
|
||||
{
|
||||
var responseStream = await responseMessage.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||
var sseParser = SseParser.Create<ActorRequestUpdate?>(responseStream, (eventType, data) =>
|
||||
var sseParser = SseParser.Create(responseStream, (eventType, data) =>
|
||||
{
|
||||
if (eventType != "message")
|
||||
{
|
||||
@@ -159,26 +159,13 @@ internal sealed class HttpActorClient(HttpClient httpClient) : IActorClient
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<ActorResponse> ReadResponseAsync(HttpResponseMessage responseMessage, CancellationToken cancellationToken)
|
||||
{
|
||||
var response = await responseMessage.Content.ReadFromJsonAsync<ActorResponse>(AgentRuntimeJsonUtilities.DefaultOptions, cancellationToken).ConfigureAwait(false);
|
||||
if (response == null)
|
||||
{
|
||||
throw new InvalidOperationException($"No response found for actor '{actorId}' with message ID '{messageId}'.");
|
||||
}
|
||||
private async Task<ActorResponse> ReadResponseAsync(HttpResponseMessage responseMessage, CancellationToken cancellationToken) =>
|
||||
await responseMessage.Content.ReadFromJsonAsync<ActorResponse>(AgentRuntimeJsonUtilities.DefaultOptions, cancellationToken).ConfigureAwait(false) ??
|
||||
throw new InvalidOperationException($"No response found for actor '{actorId}' with message ID '{messageId}'.");
|
||||
|
||||
return response;
|
||||
}
|
||||
private static bool IsJsonResponse([NotNullWhen(true)] HttpResponseMessage? response) => response?.Content.Headers.ContentType?.MediaType == "application/json";
|
||||
|
||||
private static bool IsJsonResponse([NotNullWhen(true)] HttpResponseMessage? response)
|
||||
{
|
||||
return response?.Content.Headers.ContentType?.MediaType == "application/json";
|
||||
}
|
||||
|
||||
private static bool IsStreamingResponse([NotNullWhen(true)] HttpResponseMessage? response)
|
||||
{
|
||||
return response?.Content.Headers.ContentType?.MediaType == "text/event-stream";
|
||||
}
|
||||
private static bool IsStreamingResponse([NotNullWhen(true)] HttpResponseMessage? response) => response?.Content.Headers.ContentType?.MediaType == "text/event-stream";
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
|
||||
@@ -24,10 +24,7 @@ Uri a2aAddress = new("http://localhost:5390/a2a");
|
||||
|
||||
builder.Services.AddHttpClient<AgentDiscoveryClient>(client => client.BaseAddress = baseAddress);
|
||||
builder.Services.AddHttpClient<IActorClient, HttpActorClient>(client => client.BaseAddress = baseAddress);
|
||||
builder.Services.AddSingleton<A2AActorClient>(sp =>
|
||||
{
|
||||
return new A2AActorClient(sp.GetRequiredService<ILogger<A2AActorClient>>(), a2aAddress);
|
||||
});
|
||||
builder.Services.AddSingleton(sp => new A2AActorClient(sp.GetRequiredService<ILogger<A2AActorClient>>(), a2aAddress));
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
||||
.AddSource(SourceName) // Our custom activity source
|
||||
.AddSource("Microsoft.Extensions.AI.Agents") // Agent Framework telemetry
|
||||
.AddHttpClientInstrumentation() // Capture HTTP calls to OpenAI
|
||||
.AddOtlpExporter(options => { options.Endpoint = new Uri(otlpEndpoint); })
|
||||
.AddOtlpExporter(options => options.Endpoint = new Uri(otlpEndpoint))
|
||||
.Build();
|
||||
|
||||
// Setup metrics with resource and instrument name filtering
|
||||
@@ -52,7 +52,7 @@ using var meterProvider = Sdk.CreateMeterProviderBuilder()
|
||||
.AddMeter("Microsoft.Extensions.AI.Agents") // Agent Framework metrics
|
||||
.AddHttpClientInstrumentation() // HTTP client metrics
|
||||
.AddRuntimeInstrumentation() // .NET runtime metrics
|
||||
.AddOtlpExporter(options => { options.Endpoint = new Uri(otlpEndpoint); })
|
||||
.AddOtlpExporter(options => options.Endpoint = new Uri(otlpEndpoint))
|
||||
.Build();
|
||||
|
||||
// Setup structured logging with OpenTelemetry
|
||||
@@ -62,10 +62,7 @@ serviceCollection.AddLogging(loggingBuilder => loggingBuilder
|
||||
.AddOpenTelemetry(options =>
|
||||
{
|
||||
options.SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(ServiceName, serviceVersion: "1.0.0"));
|
||||
options.AddOtlpExporter(otlpOptions =>
|
||||
{
|
||||
otlpOptions.Endpoint = new Uri(otlpEndpoint);
|
||||
});
|
||||
options.AddOtlpExporter(otlpOptions => otlpOptions.Endpoint = new Uri(otlpEndpoint));
|
||||
options.IncludeScopes = true;
|
||||
options.IncludeFormattedMessage = true;
|
||||
}));
|
||||
@@ -97,7 +94,7 @@ var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT
|
||||
appLogger.LogInformation("OpenTelemetry Aspire Demo application started");
|
||||
|
||||
[Description("Get the weather for a given location.")]
|
||||
static async Task<string> GetWeather([Description("The location to get the weather for.")] string location)
|
||||
static async Task<string> GetWeatherAsync([Description("The location to get the weather for.")] string location)
|
||||
{
|
||||
await Task.Delay(2000);
|
||||
return $"The weather in {location} is cloudy with a high of 15°C.";
|
||||
@@ -109,7 +106,7 @@ using var instrumentedChatClient = new AzureOpenAIClient(new Uri(endpoint), new
|
||||
.AsIChatClient() // Converts a native OpenAI SDK ChatClient into a Microsoft.Extensions.AI.IChatClient
|
||||
.AsBuilder()
|
||||
.UseFunctionInvocation()
|
||||
.UseOpenTelemetry(loggerFactory: loggerFactory, sourceName: SourceName, (cfg) => { cfg.EnableSensitiveData = true; })
|
||||
.UseOpenTelemetry(loggerFactory: loggerFactory, sourceName: SourceName, (cfg) => cfg.EnableSensitiveData = true)
|
||||
.Build();
|
||||
|
||||
appLogger.LogInformation("Creating Agent with OpenTelemetry instrumentation");
|
||||
@@ -117,7 +114,7 @@ appLogger.LogInformation("Creating Agent with OpenTelemetry instrumentation");
|
||||
using var agent = new ChatClientAgent(instrumentedChatClient,
|
||||
name: "OpenTelemetryDemoAgent",
|
||||
instructions: "You are a helpful assistant that provides concise and informative responses.",
|
||||
tools: [AIFunctionFactory.Create(GetWeather)])
|
||||
tools: [AIFunctionFactory.Create(GetWeatherAsync)])
|
||||
.WithOpenTelemetry(loggerFactory, SourceName); // Enable telemetry on the agent
|
||||
|
||||
var thread = agent.GetNewThread();
|
||||
@@ -127,9 +124,10 @@ appLogger.LogInformation("Agent created successfully with ID: {AgentId}", agent.
|
||||
// Create a parent span for the entire agent session
|
||||
using var sessionActivity = activitySource.StartActivity("Agent Session");
|
||||
var sessionId = thread.ConversationId ?? Guid.NewGuid().ToString();
|
||||
sessionActivity?.SetTag("agent.name", "OpenTelemetryDemoAgent");
|
||||
sessionActivity?.SetTag("session.id", sessionId);
|
||||
sessionActivity?.SetTag("session.start_time", DateTimeOffset.UtcNow.ToString("O"));
|
||||
sessionActivity?
|
||||
.SetTag("agent.name", "OpenTelemetryDemoAgent")
|
||||
.SetTag("session.id", sessionId)
|
||||
.SetTag("session.start_time", DateTimeOffset.UtcNow.ToString("O"));
|
||||
|
||||
appLogger.LogInformation("Starting agent session with ID: {SessionId}", sessionId);
|
||||
using (appLogger.BeginScope(new Dictionary<string, object> { ["SessionId"] = sessionId, ["AgentName"] = "OpenTelemetryDemoAgent" }))
|
||||
@@ -152,11 +150,12 @@ using (appLogger.BeginScope(new Dictionary<string, object> { ["SessionId"] = ses
|
||||
|
||||
// Create a child span for each individual interaction
|
||||
using var activity = activitySource.StartActivity("Agent Interaction");
|
||||
activity?.SetTag("user.input", userInput);
|
||||
activity?.SetTag("agent.name", "OpenTelemetryDemoAgent");
|
||||
activity?.SetTag("interaction.number", interactionCount);
|
||||
activity?
|
||||
.SetTag("user.input", userInput)
|
||||
.SetTag("agent.name", "OpenTelemetryDemoAgent")
|
||||
.SetTag("interaction.number", interactionCount);
|
||||
|
||||
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
|
||||
try
|
||||
{
|
||||
@@ -197,9 +196,10 @@ using (appLogger.BeginScope(new Dictionary<string, object> { ["SessionId"] = ses
|
||||
responseTimeHistogram.Record(responseTime,
|
||||
new KeyValuePair<string, object?>("status", "error"));
|
||||
|
||||
activity?.SetTag("response.success", false);
|
||||
activity?.SetTag("error.message", ex.Message);
|
||||
activity?.SetStatus(ActivityStatusCode.Error, ex.Message);
|
||||
activity?
|
||||
.SetTag("response.success", false)
|
||||
.SetTag("error.message", ex.Message)
|
||||
.SetStatus(ActivityStatusCode.Error, ex.Message);
|
||||
|
||||
appLogger.LogError(ex, "Agent interaction #{InteractionNumber} failed after {ResponseTime:F2} seconds: {ErrorMessage}",
|
||||
interactionCount, responseTime, ex.Message);
|
||||
@@ -207,8 +207,9 @@ using (appLogger.BeginScope(new Dictionary<string, object> { ["SessionId"] = ses
|
||||
}
|
||||
|
||||
// Add session summary to the parent span
|
||||
sessionActivity?.SetTag("session.total_interactions", interactionCount);
|
||||
sessionActivity?.SetTag("session.end_time", DateTimeOffset.UtcNow.ToString("O"));
|
||||
sessionActivity?
|
||||
.SetTag("session.total_interactions", interactionCount)
|
||||
.SetTag("session.end_time", DateTimeOffset.UtcNow.ToString("O"));
|
||||
|
||||
appLogger.LogInformation("Agent session completed. Total interactions: {TotalInteractions}", interactionCount);
|
||||
} // End of logging scope
|
||||
|
||||
@@ -32,7 +32,7 @@ public class AgentSample(ITestOutputHelper output) : BaseSample(output)
|
||||
AzureAIAgentsPersistent
|
||||
}
|
||||
|
||||
protected IChatClient GetChatClient(ChatClientProviders provider, ChatClientAgentOptions? options = null)
|
||||
protected static IChatClient GetChatClient(ChatClientProviders provider, ChatClientAgentOptions? options = null)
|
||||
=> provider switch
|
||||
{
|
||||
ChatClientProviders.OpenAIChatCompletion => GetOpenAIChatClient(),
|
||||
@@ -46,14 +46,6 @@ public class AgentSample(ITestOutputHelper output) : BaseSample(output)
|
||||
_ => throw new NotSupportedException($"Provider {provider} is not supported.")
|
||||
};
|
||||
|
||||
protected ChatOptions? GetChatOptions(ChatClientProviders? provider)
|
||||
=> provider switch
|
||||
{
|
||||
ChatClientProviders.OpenAIResponses_InMemoryMessageThread => new() { RawRepresentationFactory = static (_) => new ResponseCreationOptions() { StoredOutputEnabled = false } },
|
||||
ChatClientProviders.OpenAIResponses_ConversationIdThread => new() { RawRepresentationFactory = static (_) => new ResponseCreationOptions() { StoredOutputEnabled = true } },
|
||||
_ => null
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// For providers that store the agent and the thread on the server side, this will clean and delete
|
||||
/// any sample agent and thread that was created during this execution.
|
||||
@@ -65,16 +57,14 @@ public class AgentSample(ITestOutputHelper output) : BaseSample(output)
|
||||
/// <remarks>
|
||||
/// Ideally for faster execution and potential cost savings, server-side agents should be reused.
|
||||
/// </remarks>
|
||||
protected Task AgentCleanUpAsync(ChatClientProviders provider, AIAgent agent, AgentThread? thread = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return provider switch
|
||||
protected static Task AgentCleanUpAsync(ChatClientProviders provider, AIAgent agent, AgentThread? thread = null, CancellationToken cancellationToken = default)
|
||||
=> provider switch
|
||||
{
|
||||
ChatClientProviders.AzureAIAgentsPersistent => AzureAIAgentsPersistentAgentCleanUpAsync(agent, thread, cancellationToken),
|
||||
ChatClientProviders.OpenAIAssistant => OpenAIAssistantCleanUpAgentAsync(agent, thread, cancellationToken),
|
||||
// For other remaining provider sample types, no cleanup is needed as they don't offer a server-side agent/thread clean-up API.
|
||||
_ => Task.CompletedTask
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a server-side agent identifier based on the specified provider and options.
|
||||
@@ -84,23 +74,21 @@ public class AgentSample(ITestOutputHelper output) : BaseSample(output)
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
|
||||
/// <returns>The identifier of the created agent, or <see langword="null"/> if the provider does not use server-side agents.</returns>
|
||||
/// <remarks>Some server-side agent providers require an agent id reference to be created before it can be invoked.</remarks>
|
||||
protected Task<string?> AgentCreateAsync(ChatClientProviders provider, ChatClientAgentOptions options, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return provider switch
|
||||
protected static Task<string?> AgentCreateAsync(ChatClientProviders provider, ChatClientAgentOptions options, CancellationToken cancellationToken = default)
|
||||
=> provider switch
|
||||
{
|
||||
ChatClientProviders.OpenAIAssistant => OpenAIAssistantCreateAgentAsync(options, cancellationToken),
|
||||
ChatClientProviders.AzureAIAgentsPersistent => AzureAIAgentsPersistentCreateAgentAsync(options, cancellationToken),
|
||||
_ => Task.FromResult<string?>(null)
|
||||
};
|
||||
}
|
||||
|
||||
#region Private GetChatClient
|
||||
|
||||
private IChatClient GetOpenAIChatClient()
|
||||
private static IChatClient GetOpenAIChatClient()
|
||||
=> new ChatClient(TestConfiguration.OpenAI.ChatModelId, TestConfiguration.OpenAI.ApiKey)
|
||||
.AsIChatClient();
|
||||
|
||||
private IChatClient GetAzureOpenAIChatClient()
|
||||
private static IChatClient GetAzureOpenAIChatClient()
|
||||
=> ((TestConfiguration.AzureOpenAI.ApiKey is null)
|
||||
// Use Azure CLI credentials if API key is not provided.
|
||||
? new AzureOpenAIClient(TestConfiguration.AzureOpenAI.Endpoint, new AzureCliCredential())
|
||||
@@ -108,23 +96,23 @@ public class AgentSample(ITestOutputHelper output) : BaseSample(output)
|
||||
.GetChatClient(TestConfiguration.AzureOpenAI.DeploymentName)
|
||||
.AsIChatClient();
|
||||
|
||||
private IChatClient GetOpenAIResponsesClient()
|
||||
private static IChatClient GetOpenAIResponsesClient()
|
||||
=> new OpenAIResponseClient(TestConfiguration.OpenAI.ChatModelId, TestConfiguration.OpenAI.ApiKey)
|
||||
.AsIChatClient();
|
||||
|
||||
private IChatClient GetAzureAIAgentPersistentClient(ChatClientAgentOptions options)
|
||||
private static IChatClient GetAzureAIAgentPersistentClient(ChatClientAgentOptions options)
|
||||
=> new PersistentAgentsClient(TestConfiguration.AzureAI.Endpoint, new AzureCliCredential()).AsNewIChatClient(options.Id!);
|
||||
|
||||
private IChatClient GetOpenAIAssistantChatClient(ChatClientAgentOptions options)
|
||||
private static IChatClient GetOpenAIAssistantChatClient(ChatClientAgentOptions options)
|
||||
=> new AssistantClient(TestConfiguration.OpenAI.ApiKey).AsIChatClient(options.Id!);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private AgentCreate
|
||||
|
||||
private async Task<string?> AzureAIAgentsPersistentCreateAgentAsync(ChatClientAgentOptions options, CancellationToken cancellationToken)
|
||||
private static async Task<string?> AzureAIAgentsPersistentCreateAgentAsync(ChatClientAgentOptions options, CancellationToken cancellationToken)
|
||||
{
|
||||
var persistentAgentsClient = new Azure.AI.Agents.Persistent.PersistentAgentsAdministrationClient(
|
||||
var persistentAgentsClient = new PersistentAgentsAdministrationClient(
|
||||
TestConfiguration.AzureAI.Endpoint,
|
||||
new AzureCliCredential());
|
||||
|
||||
@@ -138,9 +126,9 @@ public class AgentSample(ITestOutputHelper output) : BaseSample(output)
|
||||
return result?.Value.Id;
|
||||
}
|
||||
|
||||
private async Task<string?> OpenAIAssistantCreateAgentAsync(ChatClientAgentOptions options, CancellationToken cancellationToken)
|
||||
private static async Task<string?> OpenAIAssistantCreateAgentAsync(ChatClientAgentOptions options, CancellationToken cancellationToken)
|
||||
{
|
||||
var assistantClient = new OpenAI.Assistants.AssistantClient(TestConfiguration.OpenAI.ApiKey);
|
||||
var assistantClient = new AssistantClient(TestConfiguration.OpenAI.ApiKey);
|
||||
Assistant assistant = await assistantClient.CreateAssistantAsync(
|
||||
TestConfiguration.OpenAI.ChatModelId,
|
||||
new()
|
||||
@@ -157,7 +145,7 @@ public class AgentSample(ITestOutputHelper output) : BaseSample(output)
|
||||
|
||||
#region Private AgentCleanUp
|
||||
|
||||
private async Task AzureAIAgentsPersistentAgentCleanUpAsync(AIAgent agent, AgentThread? thread, CancellationToken cancellationToken)
|
||||
private static async Task AzureAIAgentsPersistentAgentCleanUpAsync(AIAgent agent, AgentThread? thread, CancellationToken cancellationToken)
|
||||
{
|
||||
var persistentAgentsClient = (agent as ChatClientAgent)?.ChatClient.GetService<PersistentAgentsClient>() ??
|
||||
throw new InvalidOperationException("The provided chat client is not a Persistent Agents Chat Client");
|
||||
@@ -171,7 +159,7 @@ public class AgentSample(ITestOutputHelper output) : BaseSample(output)
|
||||
}
|
||||
}
|
||||
|
||||
private async Task OpenAIAssistantCleanUpAgentAsync(AIAgent agent, AgentThread? thread, CancellationToken cancellationToken)
|
||||
private static async Task OpenAIAssistantCleanUpAgentAsync(AIAgent agent, AgentThread? thread, CancellationToken cancellationToken)
|
||||
{
|
||||
var assistantClient = (agent as ChatClientAgent)?.ChatClient
|
||||
.GetService<AssistantClient>()
|
||||
|
||||
+7
-7
@@ -18,11 +18,11 @@ public class ConcurrentOrchestration_Intro(ITestOutputHelper output) : Orchestra
|
||||
{
|
||||
// Define the agents
|
||||
ChatClientAgent physicist =
|
||||
this.CreateAgent(
|
||||
CreateAgent(
|
||||
instructions: "You are an expert in physics. You answer questions from a physics perspective.",
|
||||
description: "An expert in physics");
|
||||
ChatClientAgent chemist =
|
||||
this.CreateAgent(
|
||||
CreateAgent(
|
||||
instructions: "You are an expert in chemistry. You answer questions from a chemistry perspective.",
|
||||
description: "An expert in chemistry");
|
||||
|
||||
@@ -36,14 +36,14 @@ public class ConcurrentOrchestration_Intro(ITestOutputHelper output) : Orchestra
|
||||
new(physicist, chemist)
|
||||
{
|
||||
LoggerFactory = this.LoggerFactory,
|
||||
ResponseCallback = monitor.ResponseCallback,
|
||||
StreamingResponseCallback = streamedResponse ? monitor.StreamingResultCallback : null,
|
||||
ResponseCallback = monitor.ResponseCallbackAsync,
|
||||
StreamingResponseCallback = streamedResponse ? monitor.StreamingResultCallbackAsync : null,
|
||||
};
|
||||
|
||||
// Run the orchestration
|
||||
string input = "What is temperature?";
|
||||
Console.WriteLine($"\n# INPUT: {input}\n");
|
||||
AgentRunResponse result = await orchestration.RunAsync(input);
|
||||
const string Input = "What is temperature?";
|
||||
Console.WriteLine($"\n# INPUT: {Input}\n");
|
||||
AgentRunResponse result = await orchestration.RunAsync(Input);
|
||||
|
||||
Console.WriteLine($"\n# RESULT:\n{string.Join("\n\n", result.Messages.Select(r => $"{r.Text}"))}");
|
||||
|
||||
|
||||
+8
-8
@@ -19,15 +19,15 @@ public class ConcurrentOrchestration_With_StructuredOutput(ITestOutputHelper out
|
||||
{
|
||||
// Define the agents
|
||||
ChatClientAgent agent1 =
|
||||
this.CreateAgent(
|
||||
CreateAgent(
|
||||
instructions: "You are an expert in identifying themes in articles. Given an article, identify the main themes.",
|
||||
description: "An expert in identifying themes in articles");
|
||||
ChatClientAgent agent2 =
|
||||
this.CreateAgent(
|
||||
CreateAgent(
|
||||
instructions: "You are an expert in sentiment analysis. Given an article, identify the sentiment.",
|
||||
description: "An expert in sentiment analysis");
|
||||
ChatClientAgent agent3 =
|
||||
this.CreateAgent(
|
||||
CreateAgent(
|
||||
instructions: "You are an expert in entity recognition. Given an article, extract the entities.",
|
||||
description: "An expert in entity recognition");
|
||||
|
||||
@@ -35,11 +35,11 @@ public class ConcurrentOrchestration_With_StructuredOutput(ITestOutputHelper out
|
||||
ConcurrentOrchestration orchestration = new(agent1, agent2, agent3) { LoggerFactory = this.LoggerFactory };
|
||||
|
||||
// Run the orchestration
|
||||
const string resourceId = "Hamlet_full_play_summary.txt";
|
||||
string input = Resources.Read(resourceId);
|
||||
Console.WriteLine($"\n# INPUT: @{resourceId}\n");
|
||||
const string ResourceId = "Hamlet_full_play_summary.txt";
|
||||
string input = Resources.Read(ResourceId);
|
||||
Console.WriteLine($"\n# INPUT: @{ResourceId}\n");
|
||||
|
||||
var output = await orchestration.RunAsync<Analysis>(this.CreateChatClient(), input);
|
||||
var output = await orchestration.RunAsync<Analysis>(CreateChatClient(), input);
|
||||
Console.WriteLine($"\n# RESULT:\n{JsonSerializer.Serialize(output, s_options)}");
|
||||
}
|
||||
|
||||
@@ -50,5 +50,5 @@ public class ConcurrentOrchestration_With_StructuredOutput(ITestOutputHelper out
|
||||
public IList<string> Sentiments { get; set; } = [];
|
||||
public IList<string> Entities { get; set; } = [];
|
||||
}
|
||||
#pragma warning restore CA1812 // Avoid uninstantiated internal classes
|
||||
#pragma warning restore CA1812
|
||||
}
|
||||
|
||||
+7
-7
@@ -24,7 +24,7 @@ public class GroupChatOrchestration_Intro(ITestOutputHelper output) : Orchestrat
|
||||
{
|
||||
// Define the agents
|
||||
ChatClientAgent writer =
|
||||
this.CreateAgent(
|
||||
CreateAgent(
|
||||
name: "CopyWriter",
|
||||
description: "A copy writer",
|
||||
instructions:
|
||||
@@ -37,7 +37,7 @@ public class GroupChatOrchestration_Intro(ITestOutputHelper output) : Orchestrat
|
||||
Consider suggestions when refining an idea.
|
||||
""");
|
||||
ChatClientAgent editor =
|
||||
this.CreateAgent(
|
||||
CreateAgent(
|
||||
name: "Reviewer",
|
||||
description: "An editor.",
|
||||
instructions:
|
||||
@@ -62,13 +62,13 @@ public class GroupChatOrchestration_Intro(ITestOutputHelper output) : Orchestrat
|
||||
editor)
|
||||
{
|
||||
LoggerFactory = this.LoggerFactory,
|
||||
ResponseCallback = monitor.ResponseCallback,
|
||||
StreamingResponseCallback = streamedResponse ? monitor.StreamingResultCallback : null,
|
||||
ResponseCallback = monitor.ResponseCallbackAsync,
|
||||
StreamingResponseCallback = streamedResponse ? monitor.StreamingResultCallbackAsync : null,
|
||||
};
|
||||
|
||||
string input = "Create a slogon for a new eletric SUV that is affordable and fun to drive.";
|
||||
Console.WriteLine($"\n# INPUT: {input}\n");
|
||||
AgentRunResponse result = await orchestration.RunAsync(input);
|
||||
const string Input = "Create a slogon for a new eletric SUV that is affordable and fun to drive.";
|
||||
Console.WriteLine($"\n# INPUT: {Input}\n");
|
||||
AgentRunResponse result = await orchestration.RunAsync(Input);
|
||||
Console.WriteLine($"\n# RESULT: {result}");
|
||||
|
||||
this.DisplayHistory(monitor.History);
|
||||
|
||||
+19
-19
@@ -18,7 +18,7 @@ public class GroupChatOrchestration_With_AIManager(ITestOutputHelper output) : O
|
||||
{
|
||||
// Define the agents
|
||||
ChatClientAgent farmer =
|
||||
this.CreateAgent(
|
||||
CreateAgent(
|
||||
name: "Farmer",
|
||||
description: "A rural farmer from Southeast Asia.",
|
||||
instructions:
|
||||
@@ -29,7 +29,7 @@ public class GroupChatOrchestration_With_AIManager(ITestOutputHelper output) : O
|
||||
You are in a debate. Feel free to challenge the other participants with respect.
|
||||
""");
|
||||
ChatClientAgent developer =
|
||||
this.CreateAgent(
|
||||
CreateAgent(
|
||||
name: "Developer",
|
||||
description: "An urban software developer from the United States.",
|
||||
instructions:
|
||||
@@ -40,7 +40,7 @@ public class GroupChatOrchestration_With_AIManager(ITestOutputHelper output) : O
|
||||
You are in a debate. Feel free to challenge the other participants with respect.
|
||||
""");
|
||||
ChatClientAgent teacher =
|
||||
this.CreateAgent(
|
||||
CreateAgent(
|
||||
name: "Teacher",
|
||||
description: "A retired history teacher from Eastern Europe",
|
||||
instructions:
|
||||
@@ -51,7 +51,7 @@ public class GroupChatOrchestration_With_AIManager(ITestOutputHelper output) : O
|
||||
You are in a debate. Feel free to challenge the other participants with respect.
|
||||
""");
|
||||
ChatClientAgent activist =
|
||||
this.CreateAgent(
|
||||
CreateAgent(
|
||||
name: "Activist",
|
||||
description: "A young activist from South America.",
|
||||
instructions:
|
||||
@@ -61,7 +61,7 @@ public class GroupChatOrchestration_With_AIManager(ITestOutputHelper output) : O
|
||||
You are in a debate. Feel free to challenge the other participants with respect.
|
||||
""");
|
||||
ChatClientAgent spiritual =
|
||||
this.CreateAgent(
|
||||
CreateAgent(
|
||||
name: "SpiritualLeader",
|
||||
description: "A spiritual leader from the Middle East.",
|
||||
instructions:
|
||||
@@ -71,7 +71,7 @@ public class GroupChatOrchestration_With_AIManager(ITestOutputHelper output) : O
|
||||
You are in a debate. Feel free to challenge the other participants with respect.
|
||||
""");
|
||||
ChatClientAgent artist =
|
||||
this.CreateAgent(
|
||||
CreateAgent(
|
||||
name: "Artist",
|
||||
description: "An artist from Africa.",
|
||||
instructions:
|
||||
@@ -81,7 +81,7 @@ public class GroupChatOrchestration_With_AIManager(ITestOutputHelper output) : O
|
||||
You are in a debate. Feel free to challenge the other participants with respect.
|
||||
""");
|
||||
ChatClientAgent immigrant =
|
||||
this.CreateAgent(
|
||||
CreateAgent(
|
||||
name: "Immigrant",
|
||||
description: "An immigrant entrepreneur from Asia living in Canada.",
|
||||
instructions:
|
||||
@@ -92,7 +92,7 @@ public class GroupChatOrchestration_With_AIManager(ITestOutputHelper output) : O
|
||||
You are in a debate. Feel free to challenge the other participants with respect.
|
||||
""");
|
||||
ChatClientAgent doctor =
|
||||
this.CreateAgent(
|
||||
CreateAgent(
|
||||
name: "Doctor",
|
||||
description: "A doctor from Scandinavia.",
|
||||
instructions:
|
||||
@@ -108,12 +108,12 @@ public class GroupChatOrchestration_With_AIManager(ITestOutputHelper output) : O
|
||||
OrchestrationMonitor monitor = new();
|
||||
|
||||
// Define the orchestration
|
||||
const string topic = "What does a good life mean to you personally?";
|
||||
const string Topic = "What does a good life mean to you personally?";
|
||||
GroupChatOrchestration orchestration =
|
||||
new(
|
||||
new AIGroupChatManager(
|
||||
topic,
|
||||
this.CreateChatClient())
|
||||
Topic,
|
||||
CreateChatClient())
|
||||
{
|
||||
MaximumInvocationCount = 5
|
||||
},
|
||||
@@ -127,12 +127,12 @@ public class GroupChatOrchestration_With_AIManager(ITestOutputHelper output) : O
|
||||
doctor)
|
||||
{
|
||||
LoggerFactory = this.LoggerFactory,
|
||||
ResponseCallback = monitor.ResponseCallback,
|
||||
ResponseCallback = monitor.ResponseCallbackAsync,
|
||||
};
|
||||
|
||||
// Run the orchestration
|
||||
Console.WriteLine($"\n# INPUT: {topic}\n");
|
||||
AgentRunResponse result = await orchestration.RunAsync(topic);
|
||||
Console.WriteLine($"\n# INPUT: {Topic}\n");
|
||||
AgentRunResponse result = await orchestration.RunAsync(Topic);
|
||||
Console.WriteLine($"\n# RESULT: {result}");
|
||||
|
||||
this.DisplayHistory(monitor.History);
|
||||
@@ -167,21 +167,21 @@ public class GroupChatOrchestration_With_AIManager(ITestOutputHelper output) : O
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override ValueTask<GroupChatManagerResult<string>> FilterResults(IReadOnlyCollection<ChatMessage> history, CancellationToken cancellationToken = default) =>
|
||||
protected override ValueTask<GroupChatManagerResult<string>> FilterResultsAsync(IReadOnlyCollection<ChatMessage> history, CancellationToken cancellationToken = default) =>
|
||||
this.GetResponseAsync<string>(history, Prompts.Filter(topic), cancellationToken);
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override ValueTask<GroupChatManagerResult<string>> SelectNextAgent(IReadOnlyCollection<ChatMessage> history, GroupChatTeam team, CancellationToken cancellationToken = default) =>
|
||||
protected override ValueTask<GroupChatManagerResult<string>> SelectNextAgentAsync(IReadOnlyCollection<ChatMessage> history, GroupChatTeam team, CancellationToken cancellationToken = default) =>
|
||||
this.GetResponseAsync<string>(history, Prompts.Selection(topic, team.FormatList()), cancellationToken);
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override ValueTask<GroupChatManagerResult<bool>> ShouldRequestUserInput(IReadOnlyCollection<ChatMessage> history, CancellationToken cancellationToken = default) =>
|
||||
protected override ValueTask<GroupChatManagerResult<bool>> ShouldRequestUserInputAsync(IReadOnlyCollection<ChatMessage> history, CancellationToken cancellationToken = default) =>
|
||||
new(new GroupChatManagerResult<bool>(false) { Reason = "The AI group chat manager does not request user input." });
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async ValueTask<GroupChatManagerResult<bool>> ShouldTerminate(IReadOnlyCollection<ChatMessage> history, CancellationToken cancellationToken = default)
|
||||
protected override async ValueTask<GroupChatManagerResult<bool>> ShouldTerminateAsync(IReadOnlyCollection<ChatMessage> history, CancellationToken cancellationToken = default)
|
||||
{
|
||||
GroupChatManagerResult<bool> result = await base.ShouldTerminate(history, cancellationToken);
|
||||
GroupChatManagerResult<bool> result = await base.ShouldTerminateAsync(history, cancellationToken);
|
||||
if (!result.Value)
|
||||
{
|
||||
result = await this.GetResponseAsync<bool>(history, Prompts.Termination(topic), cancellationToken);
|
||||
|
||||
+7
-7
@@ -16,7 +16,7 @@ public class GroupChatOrchestration_With_HumanInTheLoop(ITestOutputHelper output
|
||||
{
|
||||
// Define the agents
|
||||
ChatClientAgent writer =
|
||||
this.CreateAgent(
|
||||
CreateAgent(
|
||||
name: "CopyWriter",
|
||||
description: "A copy writer",
|
||||
instructions:
|
||||
@@ -29,7 +29,7 @@ public class GroupChatOrchestration_With_HumanInTheLoop(ITestOutputHelper output
|
||||
Consider suggestions when refining an idea.
|
||||
""");
|
||||
ChatClientAgent editor =
|
||||
this.CreateAgent(
|
||||
CreateAgent(
|
||||
name: "Reviewer",
|
||||
description: "An editor.",
|
||||
instructions:
|
||||
@@ -63,13 +63,13 @@ public class GroupChatOrchestration_With_HumanInTheLoop(ITestOutputHelper output
|
||||
editor)
|
||||
{
|
||||
LoggerFactory = this.LoggerFactory,
|
||||
ResponseCallback = monitor.ResponseCallback,
|
||||
ResponseCallback = monitor.ResponseCallbackAsync,
|
||||
};
|
||||
|
||||
// Run the orchestration
|
||||
string input = "Create a slogon for a new eletric SUV that is affordable and fun to drive.";
|
||||
Console.WriteLine($"\n# INPUT: {input}\n");
|
||||
AgentRunResponse result = await orchestration.RunAsync(input);
|
||||
const string Input = "Create a slogon for a new eletric SUV that is affordable and fun to drive.";
|
||||
Console.WriteLine($"\n# INPUT: {Input}\n");
|
||||
AgentRunResponse result = await orchestration.RunAsync(Input);
|
||||
Console.WriteLine($"\n# RESULT: {result}");
|
||||
|
||||
this.DisplayHistory(monitor.History);
|
||||
@@ -84,7 +84,7 @@ public class GroupChatOrchestration_With_HumanInTheLoop(ITestOutputHelper output
|
||||
/// </remarks>
|
||||
private sealed class CustomRoundRobinGroupChatManager : RoundRobinGroupChatManager
|
||||
{
|
||||
protected override ValueTask<GroupChatManagerResult<bool>> ShouldRequestUserInput(IReadOnlyCollection<ChatMessage> history, CancellationToken cancellationToken = default)
|
||||
protected override ValueTask<GroupChatManagerResult<bool>> ShouldRequestUserInputAsync(IReadOnlyCollection<ChatMessage> history, CancellationToken cancellationToken = default)
|
||||
{
|
||||
string? lastAgent = history.LastOrDefault()?.AuthorName;
|
||||
|
||||
|
||||
+9
-9
@@ -20,24 +20,24 @@ public class HandoffOrchestration_Intro(ITestOutputHelper output) : Orchestratio
|
||||
{
|
||||
// Define the agents & tools
|
||||
ChatClientAgent triageAgent =
|
||||
this.CreateAgent(
|
||||
CreateAgent(
|
||||
instructions: "A customer support agent that triages issues.",
|
||||
name: "TriageAgent",
|
||||
description: "Handle customer requests.");
|
||||
ChatClientAgent statusAgent =
|
||||
this.CreateAgent(
|
||||
CreateAgent(
|
||||
name: "OrderStatusAgent",
|
||||
instructions: "Handle order status requests.",
|
||||
description: "A customer support agent that checks order status.",
|
||||
functions: AIFunctionFactory.Create(OrderFunctions.CheckOrderStatus));
|
||||
ChatClientAgent returnAgent =
|
||||
this.CreateAgent(
|
||||
CreateAgent(
|
||||
name: "OrderReturnAgent",
|
||||
instructions: "Handle order return requests.",
|
||||
description: "A customer support agent that handles order returns.",
|
||||
functions: AIFunctionFactory.Create(OrderFunctions.ProcessReturn));
|
||||
ChatClientAgent refundAgent =
|
||||
this.CreateAgent(
|
||||
CreateAgent(
|
||||
name: "OrderRefundAgent",
|
||||
instructions: "Handle order refund requests.",
|
||||
description: "A customer support agent that handles order refund.",
|
||||
@@ -49,7 +49,7 @@ public class HandoffOrchestration_Intro(ITestOutputHelper output) : Orchestratio
|
||||
OrchestrationMonitor monitor = new();
|
||||
// Define user responses for InteractiveCallback (since sample is not interactive)
|
||||
Queue<string> responses = new();
|
||||
string task = "I am a customer that needs help with my orders";
|
||||
const string Task = "I am a customer that needs help with my orders";
|
||||
responses.Enqueue("I'd like to track the status of my order");
|
||||
responses.Enqueue("My order ID is 123");
|
||||
responses.Enqueue("I want to return another order of mine");
|
||||
@@ -75,13 +75,13 @@ public class HandoffOrchestration_Intro(ITestOutputHelper output) : Orchestratio
|
||||
return new(input);
|
||||
},
|
||||
LoggerFactory = this.LoggerFactory,
|
||||
ResponseCallback = monitor.ResponseCallback,
|
||||
StreamingResponseCallback = streamedResponse ? monitor.StreamingResultCallback : null,
|
||||
ResponseCallback = monitor.ResponseCallbackAsync,
|
||||
StreamingResponseCallback = streamedResponse ? monitor.StreamingResultCallbackAsync : null,
|
||||
};
|
||||
|
||||
// Run the orchestration
|
||||
Console.WriteLine($"\n# INPUT:\n{task}\n");
|
||||
AgentRunResponse result = await orchestration.RunAsync(task);
|
||||
Console.WriteLine($"\n# INPUT:\n{Task}\n");
|
||||
AgentRunResponse result = await orchestration.RunAsync(Task);
|
||||
|
||||
Console.WriteLine($"\n# RESULT: {result}");
|
||||
|
||||
|
||||
+5
-8
@@ -22,18 +22,18 @@ public class HandoffOrchestration_With_StructuredInput(ITestOutputHelper output)
|
||||
|
||||
// Define the agents
|
||||
ChatClientAgent triageAgent =
|
||||
this.CreateAgent(
|
||||
CreateAgent(
|
||||
instructions: "Given a GitHub issue, triage it.",
|
||||
name: "TriageAgent",
|
||||
description: "An agent that triages GitHub issues");
|
||||
ChatClientAgent pythonAgent =
|
||||
this.CreateAgent(
|
||||
CreateAgent(
|
||||
instructions: "You are an agent that handles Python related GitHub issues.",
|
||||
name: "PythonAgent",
|
||||
description: "An agent that handles Python related issues",
|
||||
functions: githubAddLabelFunction);
|
||||
ChatClientAgent dotnetAgent =
|
||||
this.CreateAgent(
|
||||
CreateAgent(
|
||||
instructions: "You are an agent that handles .NET related GitHub issues.",
|
||||
name: "DotNetAgent",
|
||||
description: "An agent that handles .NET related issues",
|
||||
@@ -51,7 +51,7 @@ public class HandoffOrchestration_With_StructuredInput(ITestOutputHelper output)
|
||||
.Add(triageAgent, [dotnetAgent, pythonAgent]))
|
||||
{
|
||||
LoggerFactory = this.LoggerFactory,
|
||||
ResponseCallback = monitor.ResponseCallback,
|
||||
ResponseCallback = monitor.ResponseCallbackAsync,
|
||||
};
|
||||
|
||||
GithubIssue input =
|
||||
@@ -105,9 +105,6 @@ public class HandoffOrchestration_With_StructuredInput(ITestOutputHelper output)
|
||||
{
|
||||
public Dictionary<string, string[]> Labels { get; } = [];
|
||||
|
||||
public void AddLabels(string issueId, params string[] labels)
|
||||
{
|
||||
this.Labels[issueId] = labels;
|
||||
}
|
||||
public void AddLabels(string issueId, params string[] labels) => this.Labels[issueId] = labels;
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -67,14 +67,14 @@ public class SequentialOrchestration_Foundry_Agents(ITestOutputHelper output) :
|
||||
new(analystAgent, writerAgent, editorAgent)
|
||||
{
|
||||
LoggerFactory = this.LoggerFactory,
|
||||
ResponseCallback = monitor.ResponseCallback,
|
||||
StreamingResponseCallback = streamedResponse ? monitor.StreamingResultCallback : null,
|
||||
ResponseCallback = monitor.ResponseCallbackAsync,
|
||||
StreamingResponseCallback = streamedResponse ? monitor.StreamingResultCallbackAsync : null,
|
||||
};
|
||||
|
||||
// Run the orchestration
|
||||
string input = "An eco-friendly stainless steel water bottle that keeps drinks cold for 24 hours";
|
||||
Console.WriteLine($"\n# INPUT: {input}\n");
|
||||
AgentRunResponse result = await orchestration.RunAsync(input);
|
||||
const string Input = "An eco-friendly stainless steel water bottle that keeps drinks cold for 24 hours";
|
||||
Console.WriteLine($"\n# INPUT: {Input}\n");
|
||||
AgentRunResponse result = await orchestration.RunAsync(Input);
|
||||
Console.WriteLine($"\n# RESULT: {result}");
|
||||
|
||||
this.DisplayHistory(monitor.History);
|
||||
|
||||
+8
-8
@@ -19,7 +19,7 @@ public class SequentialOrchestration_Intro(ITestOutputHelper output) : Orchestra
|
||||
{
|
||||
// Define the agents
|
||||
ChatClientAgent analystAgent =
|
||||
this.CreateAgent(
|
||||
CreateAgent(
|
||||
name: "Analyst",
|
||||
instructions:
|
||||
"""
|
||||
@@ -30,7 +30,7 @@ public class SequentialOrchestration_Intro(ITestOutputHelper output) : Orchestra
|
||||
""",
|
||||
description: "A agent that extracts key concepts from a product description.");
|
||||
ChatClientAgent writerAgent =
|
||||
this.CreateAgent(
|
||||
CreateAgent(
|
||||
name: "copywriter",
|
||||
instructions:
|
||||
"""
|
||||
@@ -40,7 +40,7 @@ public class SequentialOrchestration_Intro(ITestOutputHelper output) : Orchestra
|
||||
""",
|
||||
description: "An agent that writes a marketing copy based on the extracted concepts.");
|
||||
ChatClientAgent editorAgent =
|
||||
this.CreateAgent(
|
||||
CreateAgent(
|
||||
name: "editor",
|
||||
instructions:
|
||||
"""
|
||||
@@ -58,14 +58,14 @@ public class SequentialOrchestration_Intro(ITestOutputHelper output) : Orchestra
|
||||
new(analystAgent, writerAgent, editorAgent)
|
||||
{
|
||||
LoggerFactory = this.LoggerFactory,
|
||||
ResponseCallback = monitor.ResponseCallback,
|
||||
StreamingResponseCallback = streamedResponse ? monitor.StreamingResultCallback : null,
|
||||
ResponseCallback = monitor.ResponseCallbackAsync,
|
||||
StreamingResponseCallback = streamedResponse ? monitor.StreamingResultCallbackAsync : null,
|
||||
};
|
||||
|
||||
// Run the orchestration
|
||||
string input = "An eco-friendly stainless steel water bottle that keeps drinks cold for 24 hours";
|
||||
Console.WriteLine($"\n# INPUT: {input}\n");
|
||||
AgentRunResponse result = await orchestration.RunAsync(input);
|
||||
const string Input = "An eco-friendly stainless steel water bottle that keeps drinks cold for 24 hours";
|
||||
Console.WriteLine($"\n# INPUT: {Input}\n");
|
||||
AgentRunResponse result = await orchestration.RunAsync(Input);
|
||||
Console.WriteLine($"\n# RESULT: {result}");
|
||||
|
||||
this.DisplayHistory(monitor.History);
|
||||
|
||||
+5
-5
@@ -63,14 +63,14 @@ public class SequentialOrchestration_Multi_Agent(ITestOutputHelper output) : Orc
|
||||
new(analystAgent, writerAgent, editorAgent)
|
||||
{
|
||||
LoggerFactory = this.LoggerFactory,
|
||||
ResponseCallback = monitor.ResponseCallback,
|
||||
StreamingResponseCallback = streamedResponse ? monitor.StreamingResultCallback : null,
|
||||
ResponseCallback = monitor.ResponseCallbackAsync,
|
||||
StreamingResponseCallback = streamedResponse ? monitor.StreamingResultCallbackAsync : null,
|
||||
};
|
||||
|
||||
// Run the orchestration
|
||||
string input = "An eco-friendly stainless steel water bottle that keeps drinks cold for 24 hours";
|
||||
Console.WriteLine($"\n# INPUT: {input}\n");
|
||||
AgentRunResponse result = await orchestration.RunAsync(input);
|
||||
const string Input = "An eco-friendly stainless steel water bottle that keeps drinks cold for 24 hours";
|
||||
Console.WriteLine($"\n# INPUT: {Input}\n");
|
||||
AgentRunResponse result = await orchestration.RunAsync(Input);
|
||||
Console.WriteLine($"\n# RESULT: {result}");
|
||||
|
||||
this.DisplayHistory(monitor.History);
|
||||
|
||||
+4
-4
@@ -16,7 +16,7 @@ public class SequentialOrchestration_With_Cancellation(ITestOutputHelper output)
|
||||
{
|
||||
// Define the agents
|
||||
ChatClientAgent agent =
|
||||
this.CreateAgent(
|
||||
CreateAgent(
|
||||
"""
|
||||
If the input message is a number, return the number incremented by one.
|
||||
""",
|
||||
@@ -26,10 +26,10 @@ public class SequentialOrchestration_With_Cancellation(ITestOutputHelper output)
|
||||
SequentialOrchestration orchestration = new(agent) { LoggerFactory = this.LoggerFactory };
|
||||
|
||||
// Run the orchestration
|
||||
string input = "42";
|
||||
Console.WriteLine($"\n# INPUT: {input}\n");
|
||||
const string Input = "42";
|
||||
Console.WriteLine($"\n# INPUT: {Input}\n");
|
||||
|
||||
OrchestratingAgentResponse result = await orchestration.RunAsync([new ChatMessage(ChatRole.User, input)]);
|
||||
OrchestratingAgentResponse result = await orchestration.RunAsync([new ChatMessage(ChatRole.User, Input)]);
|
||||
|
||||
result.Cancel();
|
||||
await Task.Delay(TimeSpan.FromSeconds(3));
|
||||
|
||||
@@ -20,7 +20,7 @@ ChatClientAgentOptions agentOptions = new(name: "HelpfulAssistant", instructions
|
||||
{
|
||||
ChatOptions = new()
|
||||
{
|
||||
ResponseFormat = ChatResponseFormatJson.ForJsonSchema(
|
||||
ResponseFormat = ChatResponseFormat.ForJsonSchema(
|
||||
schema: AIJsonUtilities.CreateJsonSchema(typeof(PersonInfo)),
|
||||
schemaName: "PersonInfo",
|
||||
schemaDescription: "Information about a person including their name, age, and occupation")
|
||||
|
||||
@@ -84,15 +84,13 @@ namespace SampleApp
|
||||
{
|
||||
this._vectorStore = vectorStore ?? throw new ArgumentNullException(nameof(vectorStore));
|
||||
|
||||
if (serializedStoreState.ValueKind == JsonValueKind.String)
|
||||
if (serializedStoreState.ValueKind is JsonValueKind.String)
|
||||
{
|
||||
// Here we can deserialize the thread id so that we can access the same messages as before the suspension.
|
||||
this._threadId = JsonSerializer.Deserialize<string>(serializedStoreState);
|
||||
this._threadId = serializedStoreState.Deserialize<string>();
|
||||
}
|
||||
}
|
||||
|
||||
public string? ThreadId => this._threadId;
|
||||
|
||||
public async Task AddMessagesAsync(IEnumerable<ChatMessage> messages, CancellationToken cancellationToken)
|
||||
{
|
||||
this._threadId ??= Guid.NewGuid().ToString();
|
||||
@@ -122,18 +120,15 @@ namespace SampleApp
|
||||
cancellationToken)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var messages = records
|
||||
.Select(x => JsonSerializer.Deserialize<ChatMessage>(x.SerializedMessage!)!)
|
||||
.ToList();
|
||||
var messages = records.ConvertAll(x => JsonSerializer.Deserialize<ChatMessage>(x.SerializedMessage!)!)
|
||||
;
|
||||
messages.Reverse();
|
||||
return messages;
|
||||
}
|
||||
|
||||
public ValueTask<JsonElement?> SerializeStateAsync(JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
public ValueTask<JsonElement?> SerializeStateAsync(JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) =>
|
||||
// We have to serialize the thread id, so that on deserialization we can retrieve the messages using the same thread id.
|
||||
return new ValueTask<JsonElement?>(JsonSerializer.SerializeToElement(this._threadId));
|
||||
}
|
||||
new(JsonSerializer.SerializeToElement(this._threadId));
|
||||
|
||||
/// <summary>
|
||||
/// The data structure used to store chat history items in the vector store.
|
||||
|
||||
@@ -69,7 +69,7 @@ internal sealed class SampleService(AIAgent agent, [FromKeyedServices("AppShutdo
|
||||
// Delay a little to allow the service to finish starting.
|
||||
await Task.Delay(100, cancellationToken);
|
||||
|
||||
while (cancellationToken.IsCancellationRequested is false)
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
Console.WriteLine("\nAgent: Ask me to tell you a joke about a specific topic. To exit just press Ctrl+C or enter without any input.\n");
|
||||
Console.Write("> ");
|
||||
@@ -83,7 +83,7 @@ internal sealed class SampleService(AIAgent agent, [FromKeyedServices("AppShutdo
|
||||
}
|
||||
|
||||
// Stream the output to the console as it is generated.
|
||||
await foreach (var update in agent.RunStreamingAsync(input, this._thread!, cancellationToken: cancellationToken))
|
||||
await foreach (var update in agent.RunStreamingAsync(input, this._thread, cancellationToken: cancellationToken))
|
||||
{
|
||||
Console.Write(update);
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ namespace SampleApp
|
||||
public UserInfoMemory(IChatClient chatClient, JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null)
|
||||
{
|
||||
this._chatClient = chatClient;
|
||||
this.UserInfo = JsonSerializer.Deserialize<UserInfo>(serializedState, jsonSerializerOptions) ?? new UserInfo();
|
||||
this.UserInfo = serializedState.Deserialize<UserInfo>(jsonSerializerOptions) ?? new UserInfo();
|
||||
}
|
||||
|
||||
public UserInfo UserInfo { get; set; }
|
||||
@@ -110,7 +110,7 @@ namespace SampleApp
|
||||
public override async ValueTask InvokedAsync(InvokedContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Try and extract the user name and age from the message if we don't have it already and it's a user message.
|
||||
if ((this.UserInfo.UserName == null || this.UserInfo.UserAge == null) && context.RequestMessages.Any(x => x.Role == ChatRole.User))
|
||||
if ((this.UserInfo.UserName is null || this.UserInfo.UserAge is null) && context.RequestMessages.Any(x => x.Role == ChatRole.User))
|
||||
{
|
||||
var result = await this._chatClient.GetResponseAsync<UserInfo>(
|
||||
context.RequestMessages,
|
||||
@@ -130,15 +130,15 @@ namespace SampleApp
|
||||
StringBuilder instructions = new();
|
||||
|
||||
// If we don't already know the user's name and age, add instructions to ask for them, otherwise just provide what we have to the context.
|
||||
instructions.AppendLine(
|
||||
this.UserInfo.UserName == null ?
|
||||
"Ask the user for their name and politely decline to answer any questions until they provide it." :
|
||||
$"The user's name is {this.UserInfo.UserName}.");
|
||||
|
||||
instructions.AppendLine(
|
||||
this.UserInfo.UserAge == null ?
|
||||
"Ask the user for their age and politely decline to answer any questions until they provide it." :
|
||||
$"The user's age is {this.UserInfo.UserAge}.");
|
||||
instructions
|
||||
.AppendLine(
|
||||
this.UserInfo.UserName is null ?
|
||||
"Ask the user for their name and politely decline to answer any questions until they provide it." :
|
||||
$"The user's name is {this.UserInfo.UserName}.")
|
||||
.AppendLine(
|
||||
this.UserInfo.UserAge is null ?
|
||||
"Ask the user for their age and politely decline to answer any questions until they provide it." :
|
||||
$"The user's age is {this.UserInfo.UserAge}.");
|
||||
|
||||
return new ValueTask<AIContext>(new AIContext
|
||||
{
|
||||
@@ -153,7 +153,7 @@ namespace SampleApp
|
||||
|
||||
public override ValueTask DeserializeAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
this.UserInfo = JsonSerializer.Deserialize<UserInfo>(serializedState, jsonSerializerOptions) ?? new UserInfo();
|
||||
this.UserInfo = serializedState.Deserialize<UserInfo>(jsonSerializerOptions) ?? new UserInfo();
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
@@ -13,7 +12,6 @@ using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Azure.AI.OpenAI;
|
||||
using Azure.Identity;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Microsoft.Extensions.AI.Agents;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using ModelContextProtocol.Client;
|
||||
@@ -30,10 +28,7 @@ using var sharedHandler = new SocketsHttpHandler
|
||||
};
|
||||
using var httpClient = new HttpClient(sharedHandler);
|
||||
|
||||
var consoleLoggerFactory = LoggerFactory.Create(builder =>
|
||||
{
|
||||
builder.AddConsole();
|
||||
});
|
||||
var consoleLoggerFactory = LoggerFactory.Create(builder => builder.AddConsole());
|
||||
|
||||
// Create SSE client transport for the MCP server
|
||||
var serverUrl = "http://localhost:7071/";
|
||||
@@ -59,7 +54,7 @@ AIAgent agent = new AzureOpenAIClient(
|
||||
new Uri(endpoint),
|
||||
new AzureCliCredential())
|
||||
.GetChatClient(deploymentName)
|
||||
.CreateAIAgent(instructions: "You answer questions related to the weather.", tools: [.. mcpTools.Select(mcpTool => (AITool)mcpTool)]);
|
||||
.CreateAIAgent(instructions: "You answer questions related to the weather.", tools: [.. mcpTools]);
|
||||
|
||||
// Invoke the agent and output the text result.
|
||||
Console.WriteLine(await agent.RunAsync("Get current weather alerts for New York?"));
|
||||
@@ -92,8 +87,8 @@ static async Task<string?> HandleAuthorizationUrlAsync(Uri authorizationUrl, Uri
|
||||
var code = query["code"];
|
||||
var error = query["error"];
|
||||
|
||||
string responseHtml = "<html><body><h1>Authentication complete</h1><p>You can close this window now.</p></body></html>";
|
||||
byte[] buffer = Encoding.UTF8.GetBytes(responseHtml);
|
||||
const string ResponseHtml = "<html><body><h1>Authentication complete</h1><p>You can close this window now.</p></body></html>";
|
||||
byte[] buffer = Encoding.UTF8.GetBytes(ResponseHtml);
|
||||
context.Response.ContentLength64 = buffer.Length;
|
||||
context.Response.ContentType = "text/html";
|
||||
context.Response.OutputStream.Write(buffer, 0, buffer.Length);
|
||||
|
||||
@@ -44,17 +44,16 @@ public static class Program
|
||||
var feedbackProvider = new FeedbackExecutor(chatClient);
|
||||
|
||||
// Build the workflow by adding executors and connecting them
|
||||
WorkflowBuilder builder = new(sloganWriter);
|
||||
builder.AddEdge(sloganWriter, feedbackProvider);
|
||||
builder.AddEdge(feedbackProvider, sloganWriter);
|
||||
var workflow = builder.Build<string>();
|
||||
var workflow = new WorkflowBuilder(sloganWriter)
|
||||
.AddEdge(sloganWriter, feedbackProvider)
|
||||
.AddEdge(feedbackProvider, sloganWriter)
|
||||
.Build<string>();
|
||||
|
||||
// Execute the workflow
|
||||
var ask = "Create a slogan for a new electric SUV that is affordable and fun to drive.";
|
||||
StreamingRun run = await InProcessExecution.StreamAsync(workflow, ask);
|
||||
StreamingRun run = await InProcessExecution.StreamAsync(workflow, "Create a slogan for a new electric SUV that is affordable and fun to drive.");
|
||||
await foreach (WorkflowEvent evt in run.WatchStreamAsync().ConfigureAwait(false))
|
||||
{
|
||||
if (evt is SloganGeneratedEvent || evt is FeedbackEvent)
|
||||
if (evt is SloganGeneratedEvent or FeedbackEvent)
|
||||
{
|
||||
// Custom events to allow us to monitor the progress of the workflow.
|
||||
Console.WriteLine($"{evt}");
|
||||
@@ -114,10 +113,6 @@ internal sealed class SloganWriterExecutor
|
||||
IMessageHandler<string, SloganResult>,
|
||||
IMessageHandler<FeedbackResult, SloganResult>
|
||||
{
|
||||
private const string Instruction = """
|
||||
You are a professional slogan writer. You will be given a task to create a slogan.
|
||||
""";
|
||||
|
||||
private readonly AIAgent _agent;
|
||||
private readonly AgentThread _thread;
|
||||
|
||||
@@ -127,13 +122,11 @@ internal sealed class SloganWriterExecutor
|
||||
/// <param name="chatClient">The chat client to use for the AI agent.</param>
|
||||
public SloganWriterExecutor(IChatClient chatClient)
|
||||
{
|
||||
var agentOptions = new ChatClientAgentOptions(instructions: Instruction)
|
||||
ChatClientAgentOptions agentOptions = new(instructions: "You are a professional slogan writer. You will be given a task to create a slogan.")
|
||||
{
|
||||
ChatOptions = new()
|
||||
{
|
||||
ResponseFormat = ChatResponseFormatJson.ForJsonSchema(
|
||||
schema: AIJsonUtilities.CreateJsonSchema(typeof(SloganResult))
|
||||
)
|
||||
ResponseFormat = ChatResponseFormat.ForJsonSchema(AIJsonUtilities.CreateJsonSchema(typeof(SloganResult)))
|
||||
}
|
||||
};
|
||||
|
||||
@@ -184,10 +177,6 @@ internal sealed class FeedbackEvent(FeedbackResult feedbackResult) : WorkflowEve
|
||||
/// </summary>
|
||||
internal sealed class FeedbackExecutor : ReflectingExecutor<FeedbackExecutor>, IMessageHandler<SloganResult>
|
||||
{
|
||||
private const string Instruction = """
|
||||
You are a professional editor. You will be given a slogan and the task it is meant to accomplish.
|
||||
""";
|
||||
|
||||
private readonly AIAgent _agent;
|
||||
private readonly AgentThread _thread;
|
||||
|
||||
@@ -195,7 +184,7 @@ internal sealed class FeedbackExecutor : ReflectingExecutor<FeedbackExecutor>, I
|
||||
|
||||
public int MaxAttempts { get; init; } = 3;
|
||||
|
||||
private int _attempts = 0;
|
||||
private int _attempts;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FeedbackExecutor"/> class.
|
||||
@@ -203,13 +192,11 @@ internal sealed class FeedbackExecutor : ReflectingExecutor<FeedbackExecutor>, I
|
||||
/// <param name="chatClient">The chat client to use for the AI agent.</param>
|
||||
public FeedbackExecutor(IChatClient chatClient)
|
||||
{
|
||||
var agentOptions = new ChatClientAgentOptions(instructions: Instruction)
|
||||
ChatClientAgentOptions agentOptions = new(instructions: "You are a professional editor. You will be given a slogan and the task it is meant to accomplish.")
|
||||
{
|
||||
ChatOptions = new()
|
||||
{
|
||||
ResponseFormat = ChatResponseFormatJson.ForJsonSchema(
|
||||
schema: AIJsonUtilities.CreateJsonSchema(typeof(FeedbackResult))
|
||||
)
|
||||
ResponseFormat = ChatResponseFormat.ForJsonSchema(AIJsonUtilities.CreateJsonSchema(typeof(FeedbackResult)))
|
||||
}
|
||||
};
|
||||
|
||||
@@ -229,11 +216,13 @@ internal sealed class FeedbackExecutor : ReflectingExecutor<FeedbackExecutor>, I
|
||||
var feedback = JsonSerializer.Deserialize<FeedbackResult>(response.Text) ?? throw new InvalidOperationException("Failed to deserialize feedback.");
|
||||
|
||||
await context.AddEventAsync(new FeedbackEvent(feedback));
|
||||
|
||||
if (feedback.Rating >= this.MinimumRating)
|
||||
{
|
||||
await context.AddEventAsync(new WorkflowCompletedEvent($"The following slogan was accepted:\n\n{message.Slogan}"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._attempts >= this.MaxAttempts)
|
||||
{
|
||||
await context.AddEventAsync(new WorkflowCompletedEvent($"The slogan was rejected after {this.MaxAttempts} attempts. Final slogan:\n\n{message.Slogan}"));
|
||||
|
||||
@@ -34,10 +34,10 @@ public static class Program
|
||||
AIAgent englishAgent = await GetTranslationAgentAsync("English", persistentAgentsClient, model);
|
||||
|
||||
// Build the workflow by adding executors and connecting them
|
||||
WorkflowBuilder builder = new(frenchAgent);
|
||||
builder.AddEdge(frenchAgent, spanishAgent);
|
||||
builder.AddEdge(spanishAgent, englishAgent);
|
||||
var workflow = builder.Build<ChatMessage>();
|
||||
var workflow = new WorkflowBuilder(frenchAgent)
|
||||
.AddEdge(frenchAgent, spanishAgent)
|
||||
.AddEdge(spanishAgent, englishAgent)
|
||||
.Build<ChatMessage>();
|
||||
|
||||
// Execute the workflow
|
||||
StreamingRun run = await InProcessExecution.StreamAsync(workflow, new ChatMessage(ChatRole.User, "Hello World!"));
|
||||
@@ -71,11 +71,10 @@ public static class Program
|
||||
PersistentAgentsClient persistentAgentsClient,
|
||||
string model)
|
||||
{
|
||||
string instructions = $"You are a translation assistant that translates the provided text to {targetLanguage}.";
|
||||
var agentMetadata = await persistentAgentsClient.Administration.CreateAgentAsync(
|
||||
model: model,
|
||||
name: $"{targetLanguage} Translator",
|
||||
instructions: instructions);
|
||||
instructions: $"You are a translation assistant that translates the provided text to {targetLanguage}.");
|
||||
|
||||
return await persistentAgentsClient.GetAIAgentAsync(agentMetadata.Value.Id);
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ public static class Program
|
||||
Dictionary<string, List<AgentRunResponseUpdate>> buffer = [];
|
||||
await foreach (AgentRunResponseUpdate update in agent.RunStreamingAsync(input, thread).ConfigureAwait(false))
|
||||
{
|
||||
if (update.MessageId == null)
|
||||
if (update.MessageId is null)
|
||||
{
|
||||
// skip updates that don't have a message ID
|
||||
continue;
|
||||
|
||||
@@ -26,11 +26,10 @@ internal static class WorkflowHelper
|
||||
AIAgent englishAgent = GetLanguageAgent("English", chatClient);
|
||||
|
||||
// Build the workflow by adding executors and connecting them
|
||||
WorkflowBuilder builder = new(startExecutor);
|
||||
builder.AddFanOutEdge(startExecutor, targets: [frenchAgent, englishAgent]);
|
||||
builder.AddFanInEdge(aggregationExecutor, sources: [frenchAgent, englishAgent]);
|
||||
|
||||
return builder.Build<List<ChatMessage>>();
|
||||
return new WorkflowBuilder(startExecutor)
|
||||
.AddFanOutEdge(startExecutor, targets: [frenchAgent, englishAgent])
|
||||
.AddFanInEdge(aggregationExecutor, sources: [frenchAgent, englishAgent])
|
||||
.Build<List<ChatMessage>>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -39,11 +38,8 @@ internal static class WorkflowHelper
|
||||
/// <param name="targetLanguage">The target language for translation</param>
|
||||
/// <param name="chatClient">The chat client to use for the agent</param>
|
||||
/// <returns>A ChatClientAgent configured for the specified language</returns>
|
||||
private static ChatClientAgent GetLanguageAgent(string targetLanguage, IChatClient chatClient)
|
||||
{
|
||||
string instructions = $"You're a helpful assistant who always responds in {targetLanguage}.";
|
||||
return new ChatClientAgent(chatClient, instructions, name: $"{targetLanguage}Agent");
|
||||
}
|
||||
private static ChatClientAgent GetLanguageAgent(string targetLanguage, IChatClient chatClient) =>
|
||||
new(chatClient, instructions: $"You're a helpful assistant who always responds in {targetLanguage}.", name: $"{targetLanguage}Agent");
|
||||
|
||||
/// <summary>
|
||||
/// Executor that starts the concurrent processing by sending messages to the agents.
|
||||
|
||||
+4
-4
@@ -51,7 +51,7 @@ public static class Program
|
||||
// Checkpoints are automatically created at the end of each super step when a
|
||||
// checkpoint manager is provided. You can store the checkpoint info for later use.
|
||||
CheckpointInfo? checkpoint = superStepCompletedEvt.CompletionInfo!.Checkpoint;
|
||||
if (checkpoint != null)
|
||||
if (checkpoint is not null)
|
||||
{
|
||||
checkpoints.Add(checkpoint);
|
||||
Console.WriteLine($"** Checkpoint created at step {checkpoints.Count}.");
|
||||
@@ -72,9 +72,9 @@ public static class Program
|
||||
|
||||
// Rehydrate a new workflow instance from a saved checkpoint and continue execution
|
||||
var newWorkflow = WorkflowHelper.GetWorkflow();
|
||||
var checkpointIndex = 5;
|
||||
Console.WriteLine($"\n\nHydrating a new workflow instance from the {checkpointIndex + 1}th checkpoint.");
|
||||
CheckpointInfo savedCheckpoint = checkpoints[checkpointIndex];
|
||||
const int CheckpointIndex = 5;
|
||||
Console.WriteLine($"\n\nHydrating a new workflow instance from the {CheckpointIndex + 1}th checkpoint.");
|
||||
CheckpointInfo savedCheckpoint = checkpoints[CheckpointIndex];
|
||||
|
||||
Checkpointed<StreamingRun> newCheckpointedRun = await InProcessExecution
|
||||
.StreamAsync(newWorkflow, NumberSignal.Init, checkpointManager)
|
||||
|
||||
+8
-18
@@ -23,12 +23,10 @@ internal static class WorkflowHelper
|
||||
JudgeExecutor judgeExecutor = new(42);
|
||||
|
||||
// Build the workflow by connecting executors in a loop
|
||||
var workflow = new WorkflowBuilder(guessNumberExecutor)
|
||||
return new WorkflowBuilder(guessNumberExecutor)
|
||||
.AddEdge(guessNumberExecutor, judgeExecutor)
|
||||
.AddEdge(judgeExecutor, guessNumberExecutor)
|
||||
.Build<NumberSignal>();
|
||||
|
||||
return workflow;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,19 +92,15 @@ internal sealed class GuessNumberExecutor() : ReflectingExecutor<GuessNumberExec
|
||||
/// Checkpoint the current state of the executor.
|
||||
/// This must be overridden to save any state that is needed to resume the executor.
|
||||
/// </summary>
|
||||
protected override ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken cancellation = default)
|
||||
{
|
||||
return context.QueueStateUpdateAsync(StateKey, (this.LowerBound, this.UpperBound));
|
||||
}
|
||||
protected override ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken cancellation = default) =>
|
||||
context.QueueStateUpdateAsync(StateKey, (this.LowerBound, this.UpperBound));
|
||||
|
||||
/// <summary>
|
||||
/// Restore the state of the executor from a checkpoint.
|
||||
/// This must be overridden to restore any state that was saved during checkpointing.
|
||||
/// </summary>
|
||||
protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellation = default)
|
||||
{
|
||||
protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellation = default) =>
|
||||
(this.LowerBound, this.UpperBound) = await context.ReadStateAsync<(int, int)>(StateKey).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -115,7 +109,7 @@ internal sealed class GuessNumberExecutor() : ReflectingExecutor<GuessNumberExec
|
||||
internal sealed class JudgeExecutor() : ReflectingExecutor<JudgeExecutor>("Judge"), IMessageHandler<int>
|
||||
{
|
||||
private readonly int _targetNumber;
|
||||
private int _tries = 0;
|
||||
private int _tries;
|
||||
private const string StateKey = "JudgeExecutorState";
|
||||
|
||||
/// <summary>
|
||||
@@ -149,17 +143,13 @@ internal sealed class JudgeExecutor() : ReflectingExecutor<JudgeExecutor>("Judge
|
||||
/// Checkpoint the current state of the executor.
|
||||
/// This must be overridden to save any state that is needed to resume the executor.
|
||||
/// </summary>
|
||||
protected override ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken cancellation = default)
|
||||
{
|
||||
return context.QueueStateUpdateAsync(StateKey, this._tries);
|
||||
}
|
||||
protected override ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken cancellation = default) =>
|
||||
context.QueueStateUpdateAsync(StateKey, this._tries);
|
||||
|
||||
/// <summary>
|
||||
/// Restore the state of the executor from a checkpoint.
|
||||
/// This must be overridden to restore any state that was saved during checkpointing.
|
||||
/// </summary>
|
||||
protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellation = default)
|
||||
{
|
||||
protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellation = default) =>
|
||||
this._tries = await context.ReadStateAsync<int>(StateKey).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ public static class Program
|
||||
// Checkpoints are automatically created at the end of each super step when a
|
||||
// checkpoint manager is provided. You can store the checkpoint info for later use.
|
||||
CheckpointInfo? checkpoint = superStepCompletedEvt.CompletionInfo!.Checkpoint;
|
||||
if (checkpoint != null)
|
||||
if (checkpoint is not null)
|
||||
{
|
||||
checkpoints.Add(checkpoint);
|
||||
Console.WriteLine($"** Checkpoint created at step {checkpoints.Count}.");
|
||||
@@ -70,9 +70,9 @@ public static class Program
|
||||
Console.WriteLine($"Number of checkpoints created: {checkpoints.Count}");
|
||||
|
||||
// Restoring from a checkpoint and resuming execution
|
||||
var checkpointIndex = 5;
|
||||
Console.WriteLine($"\n\nRestoring from the {checkpointIndex + 1}th checkpoint.");
|
||||
CheckpointInfo savedCheckpoint = checkpoints[checkpointIndex];
|
||||
const int CheckpointIndex = 5;
|
||||
Console.WriteLine($"\n\nRestoring from the {CheckpointIndex + 1}th checkpoint.");
|
||||
CheckpointInfo savedCheckpoint = checkpoints[CheckpointIndex];
|
||||
// Note that we are restoring the state directly to the same run instance.
|
||||
await checkpointedRun.RestoreCheckpointAsync(savedCheckpoint, CancellationToken.None).ConfigureAwait(false);
|
||||
await foreach (WorkflowEvent evt in checkpointedRun.Run.WatchStreamAsync().ConfigureAwait(false))
|
||||
|
||||
+8
-18
@@ -23,12 +23,10 @@ internal static class WorkflowHelper
|
||||
JudgeExecutor judgeExecutor = new(42);
|
||||
|
||||
// Build the workflow by connecting executors in a loop
|
||||
var workflow = new WorkflowBuilder(guessNumberExecutor)
|
||||
return new WorkflowBuilder(guessNumberExecutor)
|
||||
.AddEdge(guessNumberExecutor, judgeExecutor)
|
||||
.AddEdge(judgeExecutor, guessNumberExecutor)
|
||||
.Build<NumberSignal>();
|
||||
|
||||
return workflow;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,19 +92,15 @@ internal sealed class GuessNumberExecutor() : ReflectingExecutor<GuessNumberExec
|
||||
/// Checkpoint the current state of the executor.
|
||||
/// This must be overridden to save any state that is needed to resume the executor.
|
||||
/// </summary>
|
||||
protected override ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken cancellation = default)
|
||||
{
|
||||
return context.QueueStateUpdateAsync(StateKey, (this.LowerBound, this.UpperBound));
|
||||
}
|
||||
protected override ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken cancellation = default) =>
|
||||
context.QueueStateUpdateAsync(StateKey, (this.LowerBound, this.UpperBound));
|
||||
|
||||
/// <summary>
|
||||
/// Restore the state of the executor from a checkpoint.
|
||||
/// This must be overridden to restore any state that was saved during checkpointing.
|
||||
/// </summary>
|
||||
protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellation = default)
|
||||
{
|
||||
protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellation = default) =>
|
||||
(this.LowerBound, this.UpperBound) = await context.ReadStateAsync<(int, int)>(StateKey).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -115,7 +109,7 @@ internal sealed class GuessNumberExecutor() : ReflectingExecutor<GuessNumberExec
|
||||
internal sealed class JudgeExecutor() : ReflectingExecutor<JudgeExecutor>("Judge"), IMessageHandler<int>
|
||||
{
|
||||
private readonly int _targetNumber;
|
||||
private int _tries = 0;
|
||||
private int _tries;
|
||||
private const string StateKey = "JudgeExecutorState";
|
||||
|
||||
/// <summary>
|
||||
@@ -149,17 +143,13 @@ internal sealed class JudgeExecutor() : ReflectingExecutor<JudgeExecutor>("Judge
|
||||
/// Checkpoint the current state of the executor.
|
||||
/// This must be overridden to save any state that is needed to resume the executor.
|
||||
/// </summary>
|
||||
protected override ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken cancellation = default)
|
||||
{
|
||||
return context.QueueStateUpdateAsync(StateKey, this._tries);
|
||||
}
|
||||
protected override ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken cancellation = default) =>
|
||||
context.QueueStateUpdateAsync(StateKey, this._tries);
|
||||
|
||||
/// <summary>
|
||||
/// Restore the state of the executor from a checkpoint.
|
||||
/// This must be overridden to restore any state that was saved during checkpointing.
|
||||
/// </summary>
|
||||
protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellation = default)
|
||||
{
|
||||
protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellation = default) =>
|
||||
this._tries = await context.ReadStateAsync<int>(StateKey).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
+7
-7
@@ -57,7 +57,7 @@ public static class Program
|
||||
// Checkpoints are automatically created at the end of each super step when a
|
||||
// checkpoint manager is provided. You can store the checkpoint info for later use.
|
||||
CheckpointInfo? checkpoint = superStepCompletedEvt.CompletionInfo!.Checkpoint;
|
||||
if (checkpoint != null)
|
||||
if (checkpoint is not null)
|
||||
{
|
||||
checkpoints.Add(checkpoint);
|
||||
Console.WriteLine($"** Checkpoint created at step {checkpoints.Count}.");
|
||||
@@ -76,9 +76,9 @@ public static class Program
|
||||
Console.WriteLine($"Number of checkpoints created: {checkpoints.Count}");
|
||||
|
||||
// Restoring from a checkpoint and resuming execution
|
||||
var checkpointIndex = 1;
|
||||
Console.WriteLine($"\n\nRestoring from the {checkpointIndex + 1}th checkpoint.");
|
||||
CheckpointInfo savedCheckpoint = checkpoints[checkpointIndex];
|
||||
const int CheckpointIndex = 1;
|
||||
Console.WriteLine($"\n\nRestoring from the {CheckpointIndex + 1}th checkpoint.");
|
||||
CheckpointInfo savedCheckpoint = checkpoints[CheckpointIndex];
|
||||
// Note that we are restoring the state directly to the same run instance.
|
||||
await checkpointedRun.RestoreCheckpointAsync(savedCheckpoint, CancellationToken.None).ConfigureAwait(false);
|
||||
await foreach (WorkflowEvent evt in checkpointedRun.Run.WatchStreamAsync().ConfigureAwait(false))
|
||||
@@ -109,13 +109,13 @@ public static class Program
|
||||
{
|
||||
case NumberSignal.Init:
|
||||
int initialGuess = ReadIntegerFromConsole("Please provide your initial guess: ");
|
||||
return request.CreateResponse<int>(initialGuess);
|
||||
return request.CreateResponse(initialGuess);
|
||||
case NumberSignal.Above:
|
||||
int lowerGuess = ReadIntegerFromConsole($"You previously guessed {signal.Number} too large. Please provide a new guess: ");
|
||||
return request.CreateResponse<int>(lowerGuess);
|
||||
return request.CreateResponse(lowerGuess);
|
||||
case NumberSignal.Below:
|
||||
int higherGuess = ReadIntegerFromConsole($"You previously guessed {signal.Number} too small. Please provide a new guess: ");
|
||||
return request.CreateResponse<int>(higherGuess);
|
||||
return request.CreateResponse(higherGuess);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-11
@@ -20,12 +20,10 @@ internal static class WorkflowHelper
|
||||
JudgeExecutor judgeExecutor = new(42);
|
||||
|
||||
// Build the workflow by connecting executors in a loop
|
||||
var workflow = new WorkflowBuilder(numberInputPort)
|
||||
return new WorkflowBuilder(numberInputPort)
|
||||
.AddEdge(numberInputPort, judgeExecutor)
|
||||
.AddEdge(judgeExecutor, numberInputPort)
|
||||
.Build<SignalWithNumber>();
|
||||
|
||||
return workflow;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +58,7 @@ internal sealed class SignalWithNumber
|
||||
internal sealed class JudgeExecutor() : ReflectingExecutor<JudgeExecutor>("Judge"), IMessageHandler<int>
|
||||
{
|
||||
private readonly int _targetNumber;
|
||||
private int _tries = 0;
|
||||
private int _tries;
|
||||
private const string StateKey = "JudgeExecutorState";
|
||||
|
||||
/// <summary>
|
||||
@@ -94,17 +92,13 @@ internal sealed class JudgeExecutor() : ReflectingExecutor<JudgeExecutor>("Judge
|
||||
/// Checkpoint the current state of the executor.
|
||||
/// This must be overridden to save any state that is needed to resume the executor.
|
||||
/// </summary>
|
||||
protected override ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken cancellation = default)
|
||||
{
|
||||
return context.QueueStateUpdateAsync(StateKey, this._tries);
|
||||
}
|
||||
protected override ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken cancellation = default) =>
|
||||
context.QueueStateUpdateAsync(StateKey, this._tries);
|
||||
|
||||
/// <summary>
|
||||
/// Restore the state of the executor from a checkpoint.
|
||||
/// This must be overridden to restore any state that was saved during checkpointing.
|
||||
/// </summary>
|
||||
protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellation = default)
|
||||
{
|
||||
protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellation = default) =>
|
||||
this._tries = await context.ReadStateAsync<int>(StateKey).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,10 +56,10 @@ public static class Program
|
||||
var aggregationExecutor = new ConcurrentAggregationExecutor();
|
||||
|
||||
// Build the workflow by adding executors and connecting them
|
||||
WorkflowBuilder builder = new(startExecutor);
|
||||
builder.AddFanOutEdge(startExecutor, targets: [physicist, chemist]);
|
||||
builder.AddFanInEdge(aggregationExecutor, sources: [physicist, chemist]);
|
||||
var workflow = builder.Build<string>();
|
||||
var workflow = new WorkflowBuilder(startExecutor)
|
||||
.AddFanOutEdge(startExecutor, targets: [physicist, chemist])
|
||||
.AddFanInEdge(aggregationExecutor, sources: [physicist, chemist])
|
||||
.Build<string>();
|
||||
|
||||
// Execute the workflow in streaming mode
|
||||
StreamingRun run = await InProcessExecution.StreamAsync(workflow, "What is temperature?");
|
||||
|
||||
+17
-38
@@ -53,11 +53,11 @@ public static class Program
|
||||
var handleSpamExecutor = new HandleSpamExecutor();
|
||||
|
||||
// Build the workflow by adding executors and connecting them
|
||||
WorkflowBuilder builder = new(spamDetectionExecutor);
|
||||
builder.AddEdge(spamDetectionExecutor, emailAssistantExecutor, condition: GetCondition(expectedResult: false));
|
||||
builder.AddEdge(emailAssistantExecutor, sendEmailExecutor);
|
||||
builder.AddEdge(spamDetectionExecutor, handleSpamExecutor, condition: GetCondition(expectedResult: true));
|
||||
var workflow = builder.Build<ChatMessage>();
|
||||
var workflow = new WorkflowBuilder(spamDetectionExecutor)
|
||||
.AddEdge(spamDetectionExecutor, emailAssistantExecutor, condition: GetCondition(expectedResult: false))
|
||||
.AddEdge(emailAssistantExecutor, sendEmailExecutor)
|
||||
.AddEdge(spamDetectionExecutor, handleSpamExecutor, condition: GetCondition(expectedResult: true))
|
||||
.Build<ChatMessage>();
|
||||
|
||||
// Read a email from a text file
|
||||
string email = Resources.Read("spam.txt");
|
||||
@@ -79,53 +79,34 @@ public static class Program
|
||||
/// </summary>
|
||||
/// <param name="expectedResult">The expected spam detection result</param>
|
||||
/// <returns>A function that evaluates whether a message meets the expected result</returns>
|
||||
private static Func<object?, bool> GetCondition(bool expectedResult)
|
||||
{
|
||||
return detectionResult =>
|
||||
{
|
||||
return detectionResult is DetectionResult result && result.IsSpam == expectedResult;
|
||||
};
|
||||
}
|
||||
private static Func<object?, bool> GetCondition(bool expectedResult) =>
|
||||
detectionResult => detectionResult is DetectionResult result && result.IsSpam == expectedResult;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a spam detection agent.
|
||||
/// </summary>
|
||||
/// <returns>A ChatClientAgent configured for spam detection</returns>
|
||||
private static ChatClientAgent GetSpamDetectionAgent(IChatClient chatClient)
|
||||
{
|
||||
string instructions = "You are a spam detection assistant that identifies spam emails.";
|
||||
var agentOptions = new ChatClientAgentOptions(instructions: instructions)
|
||||
private static ChatClientAgent GetSpamDetectionAgent(IChatClient chatClient) =>
|
||||
new(chatClient, new ChatClientAgentOptions(instructions: "You are a spam detection assistant that identifies spam emails.")
|
||||
{
|
||||
ChatOptions = new()
|
||||
{
|
||||
ResponseFormat = ChatResponseFormatJson.ForJsonSchema(
|
||||
schema: AIJsonUtilities.CreateJsonSchema(typeof(DetectionResult))
|
||||
)
|
||||
ResponseFormat = ChatResponseFormat.ForJsonSchema(AIJsonUtilities.CreateJsonSchema(typeof(DetectionResult)))
|
||||
}
|
||||
};
|
||||
|
||||
return new ChatClientAgent(chatClient, agentOptions);
|
||||
}
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Creates an email assistant agent.
|
||||
/// </summary>
|
||||
/// <returns>A ChatClientAgent configured for email assistance</returns>
|
||||
private static ChatClientAgent GetEmailAssistantAgent(IChatClient chatClient)
|
||||
{
|
||||
string instructions = "You are an email assistant that helps users draft responses to emails with professionalism.";
|
||||
var agentOptions = new ChatClientAgentOptions(instructions: instructions)
|
||||
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.")
|
||||
{
|
||||
ChatOptions = new()
|
||||
{
|
||||
ResponseFormat = ChatResponseFormatJson.ForJsonSchema(
|
||||
schema: AIJsonUtilities.CreateJsonSchema(typeof(EmailResponse))
|
||||
)
|
||||
ResponseFormat = ChatResponseFormat.ForJsonSchema(AIJsonUtilities.CreateJsonSchema(typeof(EmailResponse)))
|
||||
}
|
||||
};
|
||||
|
||||
return new ChatClientAgent(chatClient, agentOptions);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -188,7 +169,7 @@ internal sealed class SpamDetectionExecutor : ReflectingExecutor<SpamDetectionEx
|
||||
EmailId = Guid.NewGuid().ToString(),
|
||||
EmailContent = message.Text
|
||||
};
|
||||
await context.QueueStateUpdateAsync<Email>(newEmail.EmailId, newEmail, scopeName: EmailStateConstants.EmailStateScope);
|
||||
await context.QueueStateUpdateAsync(newEmail.EmailId, newEmail, scopeName: EmailStateConstants.EmailStateScope);
|
||||
|
||||
// Invoke the agent
|
||||
var response = await this._spamDetectionAgent.RunAsync(message);
|
||||
@@ -252,10 +233,8 @@ internal sealed class SendEmailExecutor() : ReflectingExecutor<SendEmailExecutor
|
||||
/// <summary>
|
||||
/// Simulate the sending of an email.
|
||||
/// </summary>
|
||||
public async ValueTask HandleAsync(EmailResponse message, IWorkflowContext context)
|
||||
{
|
||||
public async ValueTask HandleAsync(EmailResponse message, IWorkflowContext context) =>
|
||||
await context.AddEventAsync(new WorkflowCompletedEvent($"Email sent: {message.Response}"));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -94,53 +94,33 @@ public static class Program
|
||||
/// </summary>
|
||||
/// <param name="expectedDecision">The expected spam detection decision</param>
|
||||
/// <returns>A function that evaluates whether a message meets the expected result</returns>
|
||||
private static Func<object?, bool> GetCondition(SpamDecision expectedDecision)
|
||||
{
|
||||
return detectionResult =>
|
||||
{
|
||||
return detectionResult is DetectionResult result && result.spamDecision == expectedDecision;
|
||||
};
|
||||
}
|
||||
private static Func<object?, bool> GetCondition(SpamDecision expectedDecision) => detectionResult => detectionResult is DetectionResult result && result.spamDecision == expectedDecision;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a spam detection agent.
|
||||
/// </summary>
|
||||
/// <returns>A ChatClientAgent configured for spam detection</returns>
|
||||
private static ChatClientAgent GetSpamDetectionAgent(IChatClient chatClient)
|
||||
{
|
||||
string instructions = "You are a spam detection assistant that identifies spam emails. Be less confident in your assessments.";
|
||||
var agentOptions = new ChatClientAgentOptions(instructions: instructions)
|
||||
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.")
|
||||
{
|
||||
ChatOptions = new()
|
||||
{
|
||||
ResponseFormat = ChatResponseFormatJson.ForJsonSchema(
|
||||
schema: AIJsonUtilities.CreateJsonSchema(typeof(DetectionResult))
|
||||
)
|
||||
ResponseFormat = ChatResponseFormat.ForJsonSchema(AIJsonUtilities.CreateJsonSchema(typeof(DetectionResult)))
|
||||
}
|
||||
};
|
||||
|
||||
return new ChatClientAgent(chatClient, agentOptions);
|
||||
}
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Creates an email assistant agent.
|
||||
/// </summary>
|
||||
/// <returns>A ChatClientAgent configured for email assistance</returns>
|
||||
private static ChatClientAgent GetEmailAssistantAgent(IChatClient chatClient)
|
||||
{
|
||||
string instructions = "You are an email assistant that helps users draft responses to emails with professionalism.";
|
||||
var agentOptions = new ChatClientAgentOptions(instructions: instructions)
|
||||
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.")
|
||||
{
|
||||
ChatOptions = new()
|
||||
{
|
||||
ResponseFormat = ChatResponseFormatJson.ForJsonSchema(
|
||||
schema: AIJsonUtilities.CreateJsonSchema(typeof(EmailResponse))
|
||||
)
|
||||
ResponseFormat = ChatResponseFormat.ForJsonSchema(AIJsonUtilities.CreateJsonSchema(typeof(EmailResponse)))
|
||||
}
|
||||
};
|
||||
|
||||
return new ChatClientAgent(chatClient, agentOptions);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -213,7 +193,7 @@ internal sealed class SpamDetectionExecutor : ReflectingExecutor<SpamDetectionEx
|
||||
EmailId = Guid.NewGuid().ToString(),
|
||||
EmailContent = message.Text
|
||||
};
|
||||
await context.QueueStateUpdateAsync<Email>(newEmail.EmailId, newEmail, scopeName: EmailStateConstants.EmailStateScope);
|
||||
await context.QueueStateUpdateAsync(newEmail.EmailId, newEmail, scopeName: EmailStateConstants.EmailStateScope);
|
||||
|
||||
// Invoke the agent
|
||||
var response = await this._spamDetectionAgent.RunAsync(message);
|
||||
@@ -276,10 +256,8 @@ internal sealed class SendEmailExecutor() : ReflectingExecutor<SendEmailExecutor
|
||||
/// <summary>
|
||||
/// Simulate the sending of an email.
|
||||
/// </summary>
|
||||
public async ValueTask HandleAsync(EmailResponse message, IWorkflowContext context)
|
||||
{
|
||||
public async ValueTask HandleAsync(EmailResponse message, IWorkflowContext context) =>
|
||||
await context.AddEventAsync(new WorkflowCompletedEvent($"Email sent: {message.Response}"));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
+16
-39
@@ -78,7 +78,7 @@ public static class Program
|
||||
.AddEdge<AnalysisResult>(
|
||||
emailAnalysisExecutor,
|
||||
databaseAccessExecutor,
|
||||
condition: analysisResult => analysisResult is not null && analysisResult.EmailLength <= LongEmailThreshold)
|
||||
condition: analysisResult => analysisResult?.EmailLength <= LongEmailThreshold)
|
||||
// Save the analysis result to the database with summary
|
||||
.AddEdge(emailSummaryExecutor, databaseAccessExecutor);
|
||||
var workflow = builder.Build<ChatMessage>();
|
||||
@@ -141,61 +141,40 @@ public static class Program
|
||||
/// Create an email analysis agent.
|
||||
/// </summary>
|
||||
/// <returns>A ChatClientAgent configured for email analysis</returns>
|
||||
private static ChatClientAgent GetEmailAnalysisAgent(IChatClient chatClient)
|
||||
{
|
||||
string instructions = "You are a spam detection assistant that identifies spam emails.";
|
||||
var agentOptions = new ChatClientAgentOptions(instructions: instructions)
|
||||
private static ChatClientAgent GetEmailAnalysisAgent(IChatClient chatClient) =>
|
||||
new(chatClient, new ChatClientAgentOptions(instructions: "You are a spam detection assistant that identifies spam emails.")
|
||||
{
|
||||
ChatOptions = new()
|
||||
{
|
||||
ResponseFormat = ChatResponseFormatJson.ForJsonSchema(
|
||||
schema: AIJsonUtilities.CreateJsonSchema(typeof(AnalysisResult))
|
||||
)
|
||||
ResponseFormat = ChatResponseFormat.ForJsonSchema(AIJsonUtilities.CreateJsonSchema(typeof(AnalysisResult)))
|
||||
}
|
||||
};
|
||||
|
||||
return new ChatClientAgent(chatClient, agentOptions);
|
||||
}
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Creates an email assistant agent.
|
||||
/// </summary>
|
||||
/// <returns>A ChatClientAgent configured for email assistance</returns>
|
||||
private static ChatClientAgent GetEmailAssistantAgent(IChatClient chatClient)
|
||||
{
|
||||
string instructions = "You are an email assistant that helps users draft responses to emails with professionalism.";
|
||||
var agentOptions = new ChatClientAgentOptions(instructions: instructions)
|
||||
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.")
|
||||
{
|
||||
ChatOptions = new()
|
||||
{
|
||||
ResponseFormat = ChatResponseFormatJson.ForJsonSchema(
|
||||
schema: AIJsonUtilities.CreateJsonSchema(typeof(EmailResponse))
|
||||
)
|
||||
ResponseFormat = ChatResponseFormat.ForJsonSchema(AIJsonUtilities.CreateJsonSchema(typeof(EmailResponse)))
|
||||
}
|
||||
};
|
||||
|
||||
return new ChatClientAgent(chatClient, agentOptions);
|
||||
}
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Creates an agent that summarizes emails.
|
||||
/// </summary>
|
||||
/// <returns>A ChatClientAgent configured for email summarization</returns>
|
||||
private static ChatClientAgent GetEmailSummaryAgent(IChatClient chatClient)
|
||||
{
|
||||
string instructions = "You are an assistant that helps users summarize emails.";
|
||||
var agentOptions = new ChatClientAgentOptions(instructions: instructions)
|
||||
private static ChatClientAgent GetEmailSummaryAgent(IChatClient chatClient) =>
|
||||
new(chatClient, new ChatClientAgentOptions(instructions: "You are an assistant that helps users summarize emails.")
|
||||
{
|
||||
ChatOptions = new()
|
||||
{
|
||||
ResponseFormat = ChatResponseFormatJson.ForJsonSchema(
|
||||
schema: AIJsonUtilities.CreateJsonSchema(typeof(EmailSummary))
|
||||
)
|
||||
ResponseFormat = ChatResponseFormat.ForJsonSchema(AIJsonUtilities.CreateJsonSchema(typeof(EmailSummary)))
|
||||
}
|
||||
};
|
||||
|
||||
return new ChatClientAgent(chatClient, agentOptions);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
internal static class EmailStateConstants
|
||||
@@ -271,7 +250,7 @@ internal sealed class EmailAnalysisExecutor : ReflectingExecutor<EmailAnalysisEx
|
||||
EmailId = Guid.NewGuid().ToString(),
|
||||
EmailContent = message.Text
|
||||
};
|
||||
await context.QueueStateUpdateAsync<Email>(newEmail.EmailId, newEmail, scopeName: EmailStateConstants.EmailStateScope);
|
||||
await context.QueueStateUpdateAsync(newEmail.EmailId, newEmail, scopeName: EmailStateConstants.EmailStateScope);
|
||||
|
||||
// Invoke the agent
|
||||
var response = await this._emailAnalysisAgent.RunAsync(message);
|
||||
@@ -335,10 +314,8 @@ internal sealed class SendEmailExecutor() : ReflectingExecutor<SendEmailExecutor
|
||||
/// <summary>
|
||||
/// Simulate the sending of an email.
|
||||
/// </summary>
|
||||
public async ValueTask HandleAsync(EmailResponse message, IWorkflowContext context)
|
||||
{
|
||||
public async ValueTask HandleAsync(EmailResponse message, IWorkflowContext context) =>
|
||||
await context.AddEventAsync(new WorkflowCompletedEvent($"Email sent: {message.Response}"));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -437,7 +414,7 @@ internal sealed class DatabaseAccessExecutor() : ReflectingExecutor<DatabaseAcce
|
||||
public async ValueTask HandleAsync(AnalysisResult message, IWorkflowContext context)
|
||||
{
|
||||
// 1. Save the email content
|
||||
var email = await context.ReadStateAsync<Email>(message.EmailId, scopeName: EmailStateConstants.EmailStateScope);
|
||||
await context.ReadStateAsync<Email>(message.EmailId, scopeName: EmailStateConstants.EmailStateScope);
|
||||
await Task.Delay(100); // Simulate database access delay
|
||||
|
||||
// 2. Save the analysis result
|
||||
|
||||
@@ -99,8 +99,8 @@ internal sealed class Program
|
||||
{
|
||||
Configuration = this.Configuration
|
||||
};
|
||||
Workflow<string> workflow = DeclarativeWorkflowBuilder.Build<string>(this.WorkflowFile, options);
|
||||
return workflow;
|
||||
|
||||
return DeclarativeWorkflowBuilder.Build<string>(this.WorkflowFile, options);
|
||||
}
|
||||
|
||||
private const string DefaultWorkflow = "HelloWorld.yaml";
|
||||
@@ -164,7 +164,7 @@ internal sealed class Program
|
||||
Debug.WriteLine($"REQUEST #{requestInfo.Request.RequestId}");
|
||||
if (response is not null)
|
||||
{
|
||||
ExternalResponse requestResponse = requestInfo.Request.CreateResponse<InputResponse>(response);
|
||||
ExternalResponse requestResponse = requestInfo.Request.CreateResponse(response);
|
||||
await run.Run.SendResponseAsync(requestResponse).ConfigureAwait(false);
|
||||
response = null;
|
||||
}
|
||||
@@ -257,7 +257,7 @@ internal sealed class Program
|
||||
private static InputResponse HandleExternalRequest(ExternalRequest request)
|
||||
{
|
||||
InputRequest? message = request.Data.As<InputRequest>();
|
||||
string? userInput = null;
|
||||
string? userInput;
|
||||
do
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.DarkGreen;
|
||||
|
||||
+4
-5
@@ -52,18 +52,17 @@ public static class Program
|
||||
{
|
||||
if (request.DataIs<NumberSignal>())
|
||||
{
|
||||
var signal = request.DataAs<NumberSignal>();
|
||||
switch (signal)
|
||||
switch (request.DataAs<NumberSignal>())
|
||||
{
|
||||
case NumberSignal.Init:
|
||||
int initialGuess = ReadIntegerFromConsole("Please provide your initial guess: ");
|
||||
return request.CreateResponse<int>(initialGuess);
|
||||
return request.CreateResponse(initialGuess);
|
||||
case NumberSignal.Above:
|
||||
int lowerGuess = ReadIntegerFromConsole("You previously guessed too large. Please provide a new guess: ");
|
||||
return request.CreateResponse<int>(lowerGuess);
|
||||
return request.CreateResponse(lowerGuess);
|
||||
case NumberSignal.Below:
|
||||
int higherGuess = ReadIntegerFromConsole("You previously guessed too small. Please provide a new guess: ");
|
||||
return request.CreateResponse<int>(higherGuess);
|
||||
return request.CreateResponse(higherGuess);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-4
@@ -19,12 +19,10 @@ internal static class WorkflowHelper
|
||||
JudgeExecutor judgeExecutor = new(42);
|
||||
|
||||
// Build the workflow by connecting executors in a loop
|
||||
var workflow = new WorkflowBuilder(numberInputPort)
|
||||
return new WorkflowBuilder(numberInputPort)
|
||||
.AddEdge(numberInputPort, judgeExecutor)
|
||||
.AddEdge(judgeExecutor, numberInputPort)
|
||||
.Build<NumberSignal>();
|
||||
|
||||
return workflow;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +42,7 @@ internal enum NumberSignal
|
||||
internal sealed class JudgeExecutor() : ReflectingExecutor<JudgeExecutor>("Judge"), IMessageHandler<int>
|
||||
{
|
||||
private readonly int _targetNumber;
|
||||
private int _tries = 0;
|
||||
private int _tries;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="JudgeExecutor"/> class.
|
||||
|
||||
@@ -108,7 +108,7 @@ internal sealed class GuessNumberExecutor : ReflectingExecutor<GuessNumberExecut
|
||||
internal sealed class JudgeExecutor : ReflectingExecutor<JudgeExecutor>, IMessageHandler<int>
|
||||
{
|
||||
private readonly int _targetNumber;
|
||||
private int _tries = 0;
|
||||
private int _tries;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="JudgeExecutor"/> class.
|
||||
|
||||
@@ -30,10 +30,10 @@ public static class Program
|
||||
var aggregate = new AggregationExecutor();
|
||||
|
||||
// Build the workflow by connecting executors sequentially
|
||||
WorkflowBuilder builder = new(fileRead);
|
||||
builder.AddFanOutEdge(fileRead, targets: [wordCount, paragraphCount]);
|
||||
builder.AddFanInEdge(aggregate, sources: [wordCount, paragraphCount]);
|
||||
var workflow = builder.Build<string>();
|
||||
var workflow = new WorkflowBuilder(fileRead)
|
||||
.AddFanOutEdge(fileRead, targets: [wordCount, paragraphCount])
|
||||
.AddFanInEdge(aggregate, sources: [wordCount, paragraphCount])
|
||||
.Build<string>();
|
||||
|
||||
// Execute the workflow with input data
|
||||
Run run = await InProcessExecution.RunAsync(workflow, "Lorem_Ipsum.txt");
|
||||
@@ -63,7 +63,7 @@ internal sealed class FileReadExecutor() : ReflectingExecutor<FileReadExecutor>(
|
||||
string fileContent = Resources.Read(message);
|
||||
// Store file content in a shared state for access by other executors
|
||||
string fileID = Guid.NewGuid().ToString();
|
||||
await context.QueueStateUpdateAsync<string>(fileID, fileContent, scopeName: FileContentStateConstants.FileContentStateScope);
|
||||
await context.QueueStateUpdateAsync(fileID, fileContent, scopeName: FileContentStateConstants.FileContentStateScope);
|
||||
|
||||
return fileID;
|
||||
}
|
||||
|
||||
+4
-10
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Agents.Workflows;
|
||||
using Microsoft.Agents.Workflows.Reflection;
|
||||
@@ -54,13 +55,8 @@ internal sealed class UppercaseExecutor() : ReflectingExecutor<UppercaseExecutor
|
||||
/// <param name="message">The input text to convert</param>
|
||||
/// <param name="context">Workflow context for accessing workflow services and adding events</param>
|
||||
/// <returns>The input text converted to uppercase</returns>
|
||||
public async ValueTask<string> HandleAsync(string message, IWorkflowContext context)
|
||||
{
|
||||
string result = message.ToUpperInvariant();
|
||||
|
||||
// The return value will be sent as a message along an edge to subsequent executors
|
||||
return result;
|
||||
}
|
||||
public async ValueTask<string> HandleAsync(string message, IWorkflowContext context) =>
|
||||
message.ToUpperInvariant(); // The return value will be sent as a message along an edge to subsequent executors
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -76,9 +72,7 @@ internal sealed class ReverseTextExecutor() : ReflectingExecutor<ReverseTextExec
|
||||
/// <returns>The input text reversed</returns>
|
||||
public async ValueTask<string> HandleAsync(string message, IWorkflowContext context)
|
||||
{
|
||||
char[] charArray = message.ToCharArray();
|
||||
System.Array.Reverse(charArray);
|
||||
string result = new(charArray);
|
||||
string result = string.Concat(message.Reverse());
|
||||
|
||||
// Signal that the workflow is complete
|
||||
await context.AddEventAsync(new WorkflowCompletedEvent(result)).ConfigureAwait(false);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Agents.Workflows;
|
||||
using Microsoft.Agents.Workflows.Reflection;
|
||||
@@ -53,13 +54,8 @@ internal sealed class UppercaseExecutor() : ReflectingExecutor<UppercaseExecutor
|
||||
/// <param name="message">The input text to convert</param>
|
||||
/// <param name="context">Workflow context for accessing workflow services and adding events</param>
|
||||
/// <returns>The input text converted to uppercase</returns>
|
||||
public async ValueTask<string> HandleAsync(string message, IWorkflowContext context)
|
||||
{
|
||||
string result = message.ToUpperInvariant();
|
||||
|
||||
// The return value will be sent as a message along an edge to subsequent executors
|
||||
return result;
|
||||
}
|
||||
public async ValueTask<string> HandleAsync(string message, IWorkflowContext context) =>
|
||||
message.ToUpperInvariant(); // The return value will be sent as a message along an edge to subsequent executors
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -75,9 +71,7 @@ internal sealed class ReverseTextExecutor() : ReflectingExecutor<ReverseTextExec
|
||||
/// <returns>The input text reversed</returns>
|
||||
public async ValueTask<string> HandleAsync(string message, IWorkflowContext context)
|
||||
{
|
||||
char[] charArray = message.ToCharArray();
|
||||
System.Array.Reverse(charArray);
|
||||
string result = new(charArray);
|
||||
string result = string.Concat(message.Reverse());
|
||||
|
||||
// Signal that the workflow is complete
|
||||
await context.AddEventAsync(new WorkflowCompletedEvent(result)).ConfigureAwait(false);
|
||||
|
||||
+7
-9
@@ -40,13 +40,14 @@ public static class Program
|
||||
AIAgent englishAgent = GetTranslationAgent("English", chatClient);
|
||||
|
||||
// Build the workflow by adding executors and connecting them
|
||||
WorkflowBuilder builder = new(frenchAgent);
|
||||
builder.AddEdge(frenchAgent, spanishAgent);
|
||||
builder.AddEdge(spanishAgent, englishAgent);
|
||||
var workflow = builder.Build<ChatMessage>();
|
||||
var workflow = new WorkflowBuilder(frenchAgent)
|
||||
.AddEdge(frenchAgent, spanishAgent)
|
||||
.AddEdge(spanishAgent, englishAgent)
|
||||
.Build<ChatMessage>();
|
||||
|
||||
// Execute the workflow
|
||||
StreamingRun run = await InProcessExecution.StreamAsync(workflow, new ChatMessage(ChatRole.User, "Hello World!"));
|
||||
|
||||
// Must send the turn token to trigger the agents.
|
||||
// The agents are wrapped as executors. When they receive messages,
|
||||
// they will cache the messages and only start processing when they receive a TurnToken.
|
||||
@@ -66,9 +67,6 @@ public static class Program
|
||||
/// <param name="targetLanguage">The target language for translation</param>
|
||||
/// <param name="chatClient">The chat client to use for the agent</param>
|
||||
/// <returns>A ChatClientAgent configured for the specified language</returns>
|
||||
private static ChatClientAgent GetTranslationAgent(string targetLanguage, IChatClient chatClient)
|
||||
{
|
||||
string instructions = $"You are a translation assistant that translates the provided text to {targetLanguage}.";
|
||||
return new ChatClientAgent(chatClient, instructions);
|
||||
}
|
||||
private static ChatClientAgent GetTranslationAgent(string targetLanguage, IChatClient chatClient) =>
|
||||
new(chatClient, $"You are a translation assistant that translates the provided text to {targetLanguage}.");
|
||||
}
|
||||
|
||||
@@ -14,10 +14,10 @@ var userInput = "Tell me a joke about a pirate.";
|
||||
|
||||
Console.WriteLine($"User Input: {userInput}");
|
||||
|
||||
await SKAgent();
|
||||
await AFAgent();
|
||||
await SKAgentAsync();
|
||||
await AFAgentAsync();
|
||||
|
||||
async Task SKAgent()
|
||||
async Task SKAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== SK Agent ===\n");
|
||||
|
||||
@@ -47,7 +47,7 @@ async Task SKAgent()
|
||||
await azureAgentClient.Administration.DeleteAgentAsync(agent.Id);
|
||||
}
|
||||
|
||||
async Task AFAgent()
|
||||
async Task AFAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== AF Agent ===\n");
|
||||
|
||||
|
||||
@@ -21,10 +21,10 @@ Console.WriteLine($"User Input: {userInput}");
|
||||
static string GetWeather([Description("The location to get the weather for.")] string location)
|
||||
=> $"The weather in {location} is cloudy with a high of 15°C.";
|
||||
|
||||
await SKAgent();
|
||||
await AFAgent();
|
||||
await SKAgentAsync();
|
||||
await AFAgentAsync();
|
||||
|
||||
async Task SKAgent()
|
||||
async Task SKAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== SK Agent ===\n");
|
||||
|
||||
@@ -59,7 +59,7 @@ async Task SKAgent()
|
||||
await azureAgentClient.Administration.DeleteAgentAsync(agent.Id);
|
||||
}
|
||||
|
||||
async Task AFAgent()
|
||||
async Task AFAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== AF Agent ===\n");
|
||||
|
||||
|
||||
+5
-7
@@ -16,10 +16,10 @@ var userInput = "Tell me a joke about a pirate.";
|
||||
|
||||
Console.WriteLine($"User Input: {userInput}");
|
||||
|
||||
await SKAgent();
|
||||
await AFAgent();
|
||||
await SKAgentAsync();
|
||||
await AFAgentAsync();
|
||||
|
||||
async Task SKAgent()
|
||||
async Task SKAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== SK Agent ===\n");
|
||||
|
||||
@@ -61,7 +61,7 @@ async Task SKAgent()
|
||||
await agent.Client.Administration.DeleteAgentAsync(agent.Id);
|
||||
}
|
||||
|
||||
async Task AFAgent()
|
||||
async Task AFAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== AF Agent ===\n");
|
||||
|
||||
@@ -71,12 +71,10 @@ async Task AFAgent()
|
||||
{
|
||||
var azureAgentClient = sp.GetRequiredService<PersistentAgentsClient>();
|
||||
|
||||
var aiAgent = azureAgentClient.CreateAIAgent(
|
||||
return azureAgentClient.CreateAIAgent(
|
||||
deploymentName,
|
||||
name: "GenerateStory",
|
||||
instructions: "You are good at telling jokes.");
|
||||
|
||||
return aiAgent;
|
||||
});
|
||||
|
||||
await using ServiceProvider serviceProvider = serviceCollection.BuildServiceProvider();
|
||||
|
||||
+4
-4
@@ -17,10 +17,10 @@ var userInput = "Create a python code file using the code interpreter tool with
|
||||
|
||||
Console.WriteLine($"User Input: {userInput}");
|
||||
|
||||
await SKAgent();
|
||||
await AFAgent();
|
||||
await SKAgentAsync();
|
||||
await AFAgentAsync();
|
||||
|
||||
async Task SKAgent()
|
||||
async Task SKAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== SK Agent ===\n");
|
||||
|
||||
@@ -65,7 +65,7 @@ async Task SKAgent()
|
||||
await azureAgentClient.Administration.DeleteAgentAsync(agent.Id);
|
||||
}
|
||||
|
||||
async Task AFAgent()
|
||||
async Task AFAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== AF Agent ===\n");
|
||||
|
||||
|
||||
+4
-4
@@ -15,10 +15,10 @@ var userInput = "Tell me a joke about a pirate.";
|
||||
|
||||
Console.WriteLine($"User Input: {userInput}");
|
||||
|
||||
await SKAgent();
|
||||
await AFAgent();
|
||||
await SKAgentAsync();
|
||||
await AFAgentAsync();
|
||||
|
||||
async Task SKAgent()
|
||||
async Task SKAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== SK Agent ===\n");
|
||||
|
||||
@@ -46,7 +46,7 @@ async Task SKAgent()
|
||||
}
|
||||
}
|
||||
|
||||
async Task AFAgent()
|
||||
async Task AFAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== AF Agent ===\n");
|
||||
|
||||
|
||||
+4
-4
@@ -42,10 +42,10 @@ var userInput =
|
||||
|
||||
Console.WriteLine($"User Input: {userInput}");
|
||||
|
||||
await SKAgent();
|
||||
await AFAgent();
|
||||
await SKAgentAsync();
|
||||
await AFAgentAsync();
|
||||
|
||||
async Task SKAgent()
|
||||
async Task SKAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== SK Agent ===\n");
|
||||
|
||||
@@ -111,7 +111,7 @@ async Task SKAgent()
|
||||
}
|
||||
}
|
||||
|
||||
async Task AFAgent()
|
||||
async Task AFAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== AF Agent ===\n");
|
||||
|
||||
|
||||
+4
-4
@@ -21,10 +21,10 @@ Console.WriteLine($"User Input: {userInput}");
|
||||
static string GetWeather([Description("The location to get the weather for.")] string location)
|
||||
=> $"The weather in {location} is cloudy with a high of 15°C.";
|
||||
|
||||
await SKAgent();
|
||||
await AFAgent();
|
||||
await SKAgentAsync();
|
||||
await AFAgentAsync();
|
||||
|
||||
async Task SKAgent()
|
||||
async Task SKAgentAsync()
|
||||
{
|
||||
OpenAIResponseAgent agent = new(new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential())
|
||||
.GetOpenAIResponseClient(deploymentName));
|
||||
@@ -43,7 +43,7 @@ async Task SKAgent()
|
||||
}
|
||||
}
|
||||
|
||||
async Task AFAgent()
|
||||
async Task AFAgentAsync()
|
||||
{
|
||||
var agent = new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential())
|
||||
.GetOpenAIResponseClient(deploymentName)
|
||||
|
||||
+4
-4
@@ -16,10 +16,10 @@ var userInput = "Tell me a joke about a pirate.";
|
||||
|
||||
Console.WriteLine($"User Input: {userInput}");
|
||||
|
||||
await SKAgent();
|
||||
await AFAgent();
|
||||
await SKAgentAsync();
|
||||
await AFAgentAsync();
|
||||
|
||||
async Task SKAgent()
|
||||
async Task SKAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== SK Agent ===\n");
|
||||
|
||||
@@ -39,7 +39,7 @@ async Task SKAgent()
|
||||
Console.WriteLine(result.Message);
|
||||
}
|
||||
|
||||
async Task AFAgent()
|
||||
async Task AFAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== AF Agent ===\n");
|
||||
|
||||
|
||||
@@ -12,10 +12,10 @@ var userInput = "Tell me a joke about a pirate.";
|
||||
|
||||
Console.WriteLine($"User Input: {userInput}");
|
||||
|
||||
await SKAgent();
|
||||
await AFAgent();
|
||||
await SKAgentAsync();
|
||||
await AFAgentAsync();
|
||||
|
||||
async Task SKAgent()
|
||||
async Task SKAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== SK Agent ===\n");
|
||||
|
||||
@@ -44,7 +44,7 @@ async Task SKAgent()
|
||||
}
|
||||
}
|
||||
|
||||
async Task AFAgent()
|
||||
async Task AFAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== AF Agent ===\n");
|
||||
|
||||
|
||||
@@ -17,10 +17,10 @@ Console.WriteLine($"User Input: {userInput}");
|
||||
static string GetWeather([Description("The location to get the weather for.")] string location)
|
||||
=> $"The weather in {location} is cloudy with a high of 15°C.";
|
||||
|
||||
await SKAgent();
|
||||
await AFAgent();
|
||||
await SKAgentAsync();
|
||||
await AFAgentAsync();
|
||||
|
||||
async Task SKAgent()
|
||||
async Task SKAgentAsync()
|
||||
{
|
||||
var builder = Kernel.CreateBuilder().AddOpenAIChatClient(modelId, apiKey);
|
||||
|
||||
@@ -40,7 +40,7 @@ async Task SKAgent()
|
||||
Console.WriteLine(result.Message);
|
||||
}
|
||||
|
||||
async Task AFAgent()
|
||||
async Task AFAgentAsync()
|
||||
{
|
||||
var agent = new OpenAIClient(apiKey).GetChatClient(modelId).CreateAIAgent(
|
||||
instructions: "You are a helpful assistant",
|
||||
|
||||
@@ -13,10 +13,10 @@ var userInput = "Tell me a joke about a pirate.";
|
||||
|
||||
Console.WriteLine($"User Input: {userInput}");
|
||||
|
||||
await SKAgent();
|
||||
await AFAgent();
|
||||
await SKAgentAsync();
|
||||
await AFAgentAsync();
|
||||
|
||||
async Task SKAgent()
|
||||
async Task SKAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== SK Agent ===\n");
|
||||
|
||||
@@ -36,7 +36,7 @@ async Task SKAgent()
|
||||
Console.WriteLine(result.Message);
|
||||
}
|
||||
|
||||
async Task AFAgent()
|
||||
async Task AFAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== AF Agent ===\n");
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#pragma warning disable OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
|
||||
|
||||
using Microsoft.Extensions.AI.Agents;
|
||||
using Microsoft.SemanticKernel;
|
||||
using Microsoft.SemanticKernel.Agents.OpenAI;
|
||||
using Microsoft.SemanticKernel.Connectors.OpenAI;
|
||||
using OpenAI;
|
||||
@@ -15,15 +14,13 @@ var userInput = "Tell me a joke about a pirate.";
|
||||
|
||||
Console.WriteLine($"User Input: {userInput}");
|
||||
|
||||
await SKAgent();
|
||||
await AFAgent();
|
||||
await SKAgentAsync();
|
||||
await AFAgentAsync();
|
||||
|
||||
async Task SKAgent()
|
||||
async Task SKAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== SK Agent ===\n");
|
||||
|
||||
var builder = Kernel.CreateBuilder().AddOpenAIChatClient(modelId, apiKey);
|
||||
|
||||
var assistantsClient = new AssistantClient(apiKey);
|
||||
|
||||
// Define the assistant
|
||||
@@ -53,7 +50,7 @@ async Task SKAgent()
|
||||
await assistantsClient.DeleteAssistantAsync(agent.Id);
|
||||
}
|
||||
|
||||
async Task AFAgent()
|
||||
async Task AFAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== AF Agent ===\n");
|
||||
|
||||
|
||||
@@ -22,10 +22,10 @@ static string GetWeather([Description("The location to get the weather for.")] s
|
||||
|
||||
Console.WriteLine($"User Input: {userInput}");
|
||||
|
||||
await SKAgent();
|
||||
await AFAgent();
|
||||
await SKAgentAsync();
|
||||
await AFAgentAsync();
|
||||
|
||||
async Task SKAgent()
|
||||
async Task SKAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== SK Agent ===\n");
|
||||
|
||||
@@ -67,7 +67,7 @@ async Task SKAgent()
|
||||
await assistantsClient.DeleteAssistantAsync(agent.Id);
|
||||
}
|
||||
|
||||
async Task AFAgent()
|
||||
async Task AFAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== AF Agent ===\n");
|
||||
|
||||
|
||||
+5
-7
@@ -16,10 +16,10 @@ var userInput = "Tell me a joke about a pirate.";
|
||||
|
||||
Console.WriteLine($"User Input: {userInput}");
|
||||
|
||||
await SKAgent();
|
||||
await AFAgent();
|
||||
await SKAgentAsync();
|
||||
await AFAgentAsync();
|
||||
|
||||
async Task SKAgent()
|
||||
async Task SKAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== SK Agent ===\n");
|
||||
|
||||
@@ -60,7 +60,7 @@ async Task SKAgent()
|
||||
await assistantsClient.DeleteAssistantAsync(agent.Id);
|
||||
}
|
||||
|
||||
async Task AFAgent()
|
||||
async Task AFAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== AF Agent ===\n");
|
||||
|
||||
@@ -70,9 +70,7 @@ async Task AFAgent()
|
||||
{
|
||||
var assistantClient = sp.GetRequiredService<AssistantClient>();
|
||||
|
||||
var agent = assistantClient.CreateAIAgent(modelId, name: "Joker", instructions: "You are good at telling jokes.");
|
||||
|
||||
return agent;
|
||||
return assistantClient.CreateAIAgent(modelId, name: "Joker", instructions: "You are good at telling jokes.");
|
||||
});
|
||||
|
||||
await using ServiceProvider serviceProvider = serviceCollection.BuildServiceProvider();
|
||||
|
||||
+4
-6
@@ -19,15 +19,13 @@ var assistantsClient = new AssistantClient(apiKey);
|
||||
|
||||
Console.WriteLine($"User Input: {userInput}");
|
||||
|
||||
await SKAgent();
|
||||
await AFAgent();
|
||||
await SKAgentAsync();
|
||||
await AFAgentAsync();
|
||||
|
||||
async Task SKAgent()
|
||||
async Task SKAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== SK Agent ===\n");
|
||||
|
||||
var builder = Kernel.CreateBuilder().AddOpenAIChatClient(modelId, apiKey);
|
||||
|
||||
// Define the assistant
|
||||
Assistant assistant = await assistantsClient.CreateAssistantAsync(modelId, enableCodeInterpreter: true);
|
||||
|
||||
@@ -71,7 +69,7 @@ async Task SKAgent()
|
||||
await assistantsClient.DeleteAssistantAsync(agent.Id);
|
||||
}
|
||||
|
||||
async Task AFAgent()
|
||||
async Task AFAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== AF Agent ===\n");
|
||||
|
||||
|
||||
@@ -13,10 +13,10 @@ var userInput = "Tell me a joke about a pirate.";
|
||||
|
||||
Console.WriteLine($"User Input: {userInput}");
|
||||
|
||||
await SKAgent();
|
||||
await AFAgent();
|
||||
await SKAgentAsync();
|
||||
await AFAgentAsync();
|
||||
|
||||
async Task SKAgent()
|
||||
async Task SKAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== SK Agent ===\n");
|
||||
|
||||
@@ -43,7 +43,7 @@ async Task SKAgent()
|
||||
}
|
||||
}
|
||||
|
||||
async Task AFAgent()
|
||||
async Task AFAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== AF Agent ===\n");
|
||||
|
||||
|
||||
+4
-4
@@ -40,10 +40,10 @@ var userInput =
|
||||
|
||||
Console.WriteLine($"User Input: {userInput}");
|
||||
|
||||
await SKAgent();
|
||||
await AFAgent();
|
||||
await SKAgentAsync();
|
||||
await AFAgentAsync();
|
||||
|
||||
async Task SKAgent()
|
||||
async Task SKAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== SK Agent ===\n");
|
||||
|
||||
@@ -108,7 +108,7 @@ async Task SKAgent()
|
||||
}
|
||||
}
|
||||
|
||||
async Task AFAgent()
|
||||
async Task AFAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== AF Agent ===\n");
|
||||
|
||||
|
||||
@@ -19,10 +19,10 @@ Console.WriteLine($"User Input: {userInput}");
|
||||
static string GetWeather([Description("The location to get the weather for.")] string location)
|
||||
=> $"The weather in {location} is cloudy with a high of 15°C.";
|
||||
|
||||
await SKAgent();
|
||||
await AFAgent();
|
||||
await SKAgentAsync();
|
||||
await AFAgentAsync();
|
||||
|
||||
async Task SKAgent()
|
||||
async Task SKAgentAsync()
|
||||
{
|
||||
var builder = Kernel.CreateBuilder().AddOpenAIChatClient(modelId, apiKey);
|
||||
|
||||
@@ -42,7 +42,7 @@ async Task SKAgent()
|
||||
}
|
||||
}
|
||||
|
||||
async Task AFAgent()
|
||||
async Task AFAgentAsync()
|
||||
{
|
||||
var agent = new OpenAIClient(apiKey).GetOpenAIResponseClient(modelId).CreateAIAgent(
|
||||
instructions: "You are a helpful assistant",
|
||||
|
||||
+4
-4
@@ -14,10 +14,10 @@ var userInput = "Tell me a joke about a pirate.";
|
||||
|
||||
Console.WriteLine($"User Input: {userInput}");
|
||||
|
||||
await SKAgent();
|
||||
await AFAgent();
|
||||
await SKAgentAsync();
|
||||
await AFAgentAsync();
|
||||
|
||||
async Task SKAgent()
|
||||
async Task SKAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== SK Agent ===\n");
|
||||
|
||||
@@ -36,7 +36,7 @@ async Task SKAgent()
|
||||
Console.WriteLine(result.Message);
|
||||
}
|
||||
|
||||
async Task AFAgent()
|
||||
async Task AFAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== AF Agent ===\n");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user