chore: import upstream snapshot with attribution
dotnet-build-and-test / dotnet-test-functions (push) Has been cancelled
dotnet-build-and-test / paths-filter (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Debug, windows-latest, net9.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net8.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-foundry-hosted-it (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test-check (push) Has been cancelled
dotnet-build-and-test / Integration Test Report (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
dotnet-build-and-test / dotnet-test-functions (push) Has been cancelled
dotnet-build-and-test / paths-filter (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Debug, windows-latest, net9.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net8.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-foundry-hosted-it (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test-check (push) Has been cancelled
dotnet-build-and-test / Integration Test Report (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
This commit is contained in:
+129
@@ -0,0 +1,129 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.AI;
|
||||
|
||||
namespace Microsoft.Agents.AI;
|
||||
|
||||
/// <summary>
|
||||
/// An <see cref="AIAgent"/> that invokes a remote agent hosted with the Invocations protocol
|
||||
/// by sending plain-text HTTP POST requests to the <c>/invocations</c> endpoint.
|
||||
/// </summary>
|
||||
public sealed class InvocationsAIAgent : AIAgent
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly Uri _invocationsUri;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InvocationsAIAgent"/> class.
|
||||
/// </summary>
|
||||
/// <param name="agentEndpoint">
|
||||
/// The base URI of the hosted agent (e.g., <c>http://localhost:8089</c>).
|
||||
/// The <c>/invocations</c> path is appended automatically.
|
||||
/// </param>
|
||||
/// <param name="httpClient">Optional <see cref="HttpClient"/> to use. If <see langword="null"/>, a new instance is created.</param>
|
||||
/// <param name="name">Optional name for the agent.</param>
|
||||
/// <param name="description">Optional description for the agent.</param>
|
||||
public InvocationsAIAgent(
|
||||
Uri agentEndpoint,
|
||||
HttpClient? httpClient = null,
|
||||
string? name = null,
|
||||
string? description = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(agentEndpoint);
|
||||
|
||||
this._httpClient = httpClient ?? new HttpClient();
|
||||
|
||||
// Ensure the base URI ends with a slash so that combining works correctly.
|
||||
var baseUri = agentEndpoint.AbsoluteUri.EndsWith('/')
|
||||
? agentEndpoint
|
||||
: new Uri(agentEndpoint.AbsoluteUri + "/");
|
||||
this._invocationsUri = new Uri(baseUri, "invocations");
|
||||
|
||||
this.Name = name ?? "invocations-agent";
|
||||
this.Description = description ?? "An agent that calls a remote Invocations protocol endpoint.";
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string? Name { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string? Description { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async Task<AgentResponse> RunCoreAsync(
|
||||
IEnumerable<ChatMessage> messages,
|
||||
AgentSession? session = null,
|
||||
AgentRunOptions? options = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var inputText = GetLastUserText(messages);
|
||||
var responseText = await this.SendInvocationAsync(inputText, cancellationToken).ConfigureAwait(false);
|
||||
return new AgentResponse(new ChatMessage(ChatRole.Assistant, responseText));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async IAsyncEnumerable<AgentResponseUpdate> RunCoreStreamingAsync(
|
||||
IEnumerable<ChatMessage> messages,
|
||||
AgentSession? session = null,
|
||||
AgentRunOptions? options = null,
|
||||
[EnumeratorCancellation] CancellationToken cancellationToken = default)
|
||||
{
|
||||
// The Invocations protocol returns a complete response (no SSE streaming),
|
||||
// so we yield a single update with the full text.
|
||||
var inputText = GetLastUserText(messages);
|
||||
var responseText = await this.SendInvocationAsync(inputText, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
yield return new AgentResponseUpdate
|
||||
{
|
||||
Role = ChatRole.Assistant,
|
||||
Contents = [new TextContent(responseText)],
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override ValueTask<AgentSession> CreateSessionCoreAsync(CancellationToken cancellationToken = default)
|
||||
=> new(new InvocationsAgentSession());
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override ValueTask<JsonElement> SerializeSessionCoreAsync(
|
||||
AgentSession session,
|
||||
JsonSerializerOptions? jsonSerializerOptions = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
=> new(JsonSerializer.SerializeToElement(new { }, jsonSerializerOptions));
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override ValueTask<AgentSession> DeserializeSessionCoreAsync(
|
||||
JsonElement serializedState,
|
||||
JsonSerializerOptions? jsonSerializerOptions = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
=> new(new InvocationsAgentSession());
|
||||
|
||||
private async Task<string> SendInvocationAsync(string input, CancellationToken cancellationToken)
|
||||
{
|
||||
using var content = new StringContent(input, System.Text.Encoding.UTF8, "text/plain");
|
||||
using var response = await this._httpClient.PostAsync(this._invocationsUri, content, cancellationToken).ConfigureAwait(false);
|
||||
response.EnsureSuccessStatusCode();
|
||||
return await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static string GetLastUserText(IEnumerable<ChatMessage> messages)
|
||||
{
|
||||
string? lastUserText = null;
|
||||
foreach (var message in messages)
|
||||
{
|
||||
if (message.Role == ChatRole.User)
|
||||
{
|
||||
lastUserText = message.Text;
|
||||
}
|
||||
}
|
||||
|
||||
return lastUserText ?? string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Minimal session for the invocations agent. No state is persisted.
|
||||
/// </summary>
|
||||
private sealed class InvocationsAgentSession : AgentSession;
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using DotNetEnv;
|
||||
using Microsoft.Agents.AI;
|
||||
|
||||
// Load .env file if present (for local development)
|
||||
Env.TraversePath().Load();
|
||||
|
||||
Uri agentEndpoint = new(Environment.GetEnvironmentVariable("AGENT_ENDPOINT")
|
||||
?? "http://localhost:8088");
|
||||
|
||||
// Create an agent that calls the remote Invocations endpoint.
|
||||
InvocationsAIAgent agent = new(agentEndpoint);
|
||||
|
||||
// REPL
|
||||
Console.ForegroundColor = ConsoleColor.Cyan;
|
||||
Console.WriteLine($"""
|
||||
══════════════════════════════════════════════════════════
|
||||
Simple Invocations Agent Sample
|
||||
Connected to: {agentEndpoint}
|
||||
Type a message or 'quit' to exit
|
||||
══════════════════════════════════════════════════════════
|
||||
""");
|
||||
Console.ResetColor();
|
||||
Console.WriteLine();
|
||||
|
||||
while (true)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Green;
|
||||
Console.Write("You> ");
|
||||
Console.ResetColor();
|
||||
|
||||
string? input = Console.ReadLine();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(input)) { continue; }
|
||||
if (input.Equals("quit", StringComparison.OrdinalIgnoreCase)) { break; }
|
||||
|
||||
try
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Yellow;
|
||||
Console.Write("Agent> ");
|
||||
Console.ResetColor();
|
||||
|
||||
await foreach (var update in agent.RunStreamingAsync(input))
|
||||
{
|
||||
Console.Write(update);
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine($"Error: {ex.Message}");
|
||||
Console.ResetColor();
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
Console.WriteLine("Goodbye!");
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>net10.0</TargetFrameworks>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<CentralPackageTransitivePinningEnabled>false</CentralPackageTransitivePinningEnabled>
|
||||
<RootNamespace>SimpleInvocationsAgentClient</RootNamespace>
|
||||
<AssemblyName>simple-invocations-agent-client</AssemblyName>
|
||||
<NoWarn>$(NoWarn);NU1605</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DotNetEnv" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\..\..\src\Microsoft.Agents.AI.Abstractions\Microsoft.Agents.AI.Abstractions.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user