mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
.NET: Add agent hosting package and update sample (#296)
* Add agent hosting package and update sample * Review feedback and cleanup * Include the narrator * wip * wip * Remove workaround for empty state writes. * Handle changes to AgentThread. * One more. * Fix. --------- Co-authored-by: Aditya Mandaleeka <adityam@microsoft.com>
This commit is contained in:
co-authored by
Aditya Mandaleeka
parent
8dcc8533a6
commit
e7441ee29e
@@ -1,41 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Microsoft.Extensions.AI.Agents.Runtime;
|
||||
|
||||
/// <summary>
|
||||
/// Source-generated JSON type information for use by all agent runtime abstractions.
|
||||
/// </summary>
|
||||
[JsonSourceGenerationOptions(
|
||||
JsonSerializerDefaults.Web,
|
||||
UseStringEnumConverter = true,
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||
WriteIndented = false)]
|
||||
[JsonSerializable(typeof(ActorId))]
|
||||
[JsonSerializable(typeof(ActorMessage))]
|
||||
[JsonSerializable(typeof(ActorReadOperation))]
|
||||
[JsonSerializable(typeof(ActorReadOperationBatch))]
|
||||
[JsonSerializable(typeof(ActorReadResult))]
|
||||
[JsonSerializable(typeof(ActorRequest))]
|
||||
[JsonSerializable(typeof(ActorRequestMessage))]
|
||||
[JsonSerializable(typeof(ActorRequestUpdate))]
|
||||
[JsonSerializable(typeof(ActorResponse))]
|
||||
[JsonSerializable(typeof(ActorResponseMessage))]
|
||||
[JsonSerializable(typeof(ActorType))]
|
||||
[JsonSerializable(typeof(ActorWriteOperation))]
|
||||
[JsonSerializable(typeof(ActorWriteOperationBatch))]
|
||||
[JsonSerializable(typeof(GetValueOperation))]
|
||||
[JsonSerializable(typeof(GetValueResult))]
|
||||
[JsonSerializable(typeof(JsonElement))]
|
||||
[JsonSerializable(typeof(ListKeysOperation))]
|
||||
[JsonSerializable(typeof(ListKeysResult))]
|
||||
[JsonSerializable(typeof(ReadResponse))]
|
||||
[JsonSerializable(typeof(RemoveKeyOperation))]
|
||||
[JsonSerializable(typeof(RequestStatus))]
|
||||
[JsonSerializable(typeof(SendRequestOperation))]
|
||||
[JsonSerializable(typeof(SetValueOperation))]
|
||||
[JsonSerializable(typeof(UpdateRequestOperation))]
|
||||
[JsonSerializable(typeof(WriteResponse))]
|
||||
internal sealed partial class ActorJsonContext : JsonSerializerContext;
|
||||
@@ -33,4 +33,25 @@ public sealed class ActorResponse
|
||||
/// </summary>
|
||||
[JsonPropertyName("status")]
|
||||
public RequestStatus Status { get; init; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
string dataString;
|
||||
if (this.Data.ValueKind == JsonValueKind.Undefined)
|
||||
{
|
||||
dataString = "undefined";
|
||||
}
|
||||
else
|
||||
{
|
||||
var rawText = this.Data.GetRawText();
|
||||
dataString = rawText.Length switch
|
||||
{
|
||||
> 250 => $"{rawText.Substring(0, 250)}...",
|
||||
_ => rawText,
|
||||
};
|
||||
}
|
||||
|
||||
return $"ActorResponse(ActorId: {this.ActorId}, Status: {this.Status}, MessageId: {this.MessageId ?? "null"}, Data: {dataString})";
|
||||
}
|
||||
}
|
||||
|
||||
+15
-1
@@ -1,5 +1,6 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading;
|
||||
@@ -10,7 +11,7 @@ namespace Microsoft.Extensions.AI.Agents.Runtime;
|
||||
/// <summary>
|
||||
/// Represents a handle to an actor response, allowing retrieval of the response data and status updates.
|
||||
/// </summary>
|
||||
public abstract class ActorResponseHandle
|
||||
public abstract class ActorResponseHandle : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Attempts to get the response from the request if it is immediately available.
|
||||
@@ -43,4 +44,17 @@ public abstract class ActorResponseHandle
|
||||
/// <param name="cancellationToken">A token to cancel the watch operation.</param>
|
||||
/// <returns>An asynchronous enumerable of request updates.</returns>
|
||||
public abstract IAsyncEnumerable<ActorRequestUpdate> WatchUpdatesAsync(CancellationToken cancellationToken);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
this.Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes of the resources used by the <see cref="ActorResponseHandle"/> class.
|
||||
/// </summary>
|
||||
/// <param name="disposing">A boolean indicating whether the method is being called from the <see cref="Dispose()"/> method.</param>
|
||||
protected virtual void Dispose(bool disposing) { }
|
||||
}
|
||||
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Microsoft.Extensions.AI.Agents.Runtime;
|
||||
|
||||
/// <summary>Provides a collection of utility methods for working with JSON data in the context of actor runtime abstractions.</summary>
|
||||
public static partial class AgentRuntimeAbstractionsJsonUtilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the <see cref="JsonSerializerOptions"/> singleton used as the default in JSON serialization operations.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// For Native AOT or applications disabling <see cref="JsonSerializer.IsReflectionEnabledByDefault"/>, this instance
|
||||
/// includes source generated contracts for all common exchange types contained in this library.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// It additionally turns on the following settings:
|
||||
/// <list type="number">
|
||||
/// <item>Enables <see cref="JsonSerializerDefaults.Web"/> defaults.</item>
|
||||
/// <item>Enables <see cref="JsonIgnoreCondition.WhenWritingNull"/> as the default ignore condition for properties.</item>
|
||||
/// <item>Enables <see cref="JsonNumberHandling.AllowReadingFromString"/> as the default number handling for number types.</item>
|
||||
/// <item>Enables <see cref="JsonStringEnumConverter"/> for enum serialization.</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public static JsonSerializerOptions DefaultOptions { get; } = CreateDefaultOptions();
|
||||
|
||||
/// <summary>
|
||||
/// Creates default options to use for actor runtime-related serialization.
|
||||
/// </summary>
|
||||
/// <returns>The configured options.</returns>
|
||||
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL3050:RequiresDynamicCode", Justification = "Converter is guarded by IsReflectionEnabledByDefault check.")]
|
||||
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access", Justification = "Converter is guarded by IsReflectionEnabledByDefault check.")]
|
||||
private static JsonSerializerOptions CreateDefaultOptions()
|
||||
{
|
||||
// Copy the configuration from the source generated context.
|
||||
JsonSerializerOptions options = new(JsonContext.Default.Options);
|
||||
|
||||
options.MakeReadOnly();
|
||||
return options;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source-generated JSON type information for use by all agent runtime abstractions.
|
||||
/// </summary>
|
||||
[JsonSourceGenerationOptions(
|
||||
JsonSerializerDefaults.Web,
|
||||
UseStringEnumConverter = true,
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||
WriteIndented = false)]
|
||||
[JsonSerializable(typeof(ActorId))]
|
||||
[JsonSerializable(typeof(ActorMessage))]
|
||||
[JsonSerializable(typeof(ActorReadOperation))]
|
||||
[JsonSerializable(typeof(ActorReadOperationBatch))]
|
||||
[JsonSerializable(typeof(ActorReadResult))]
|
||||
[JsonSerializable(typeof(ActorRequest))]
|
||||
[JsonSerializable(typeof(ActorRequestMessage))]
|
||||
[JsonSerializable(typeof(ActorRequestUpdate))]
|
||||
[JsonSerializable(typeof(ActorResponse))]
|
||||
[JsonSerializable(typeof(ActorResponseMessage))]
|
||||
[JsonSerializable(typeof(ActorType))]
|
||||
[JsonSerializable(typeof(ActorWriteOperation))]
|
||||
[JsonSerializable(typeof(ActorWriteOperationBatch))]
|
||||
[JsonSerializable(typeof(GetValueOperation))]
|
||||
[JsonSerializable(typeof(GetValueResult))]
|
||||
[JsonSerializable(typeof(JsonElement))]
|
||||
[JsonSerializable(typeof(ListKeysOperation))]
|
||||
[JsonSerializable(typeof(ListKeysResult))]
|
||||
[JsonSerializable(typeof(ReadResponse))]
|
||||
[JsonSerializable(typeof(RemoveKeyOperation))]
|
||||
[JsonSerializable(typeof(RequestStatus))]
|
||||
[JsonSerializable(typeof(SendRequestOperation))]
|
||||
[JsonSerializable(typeof(SetValueOperation))]
|
||||
[JsonSerializable(typeof(UpdateRequestOperation))]
|
||||
[JsonSerializable(typeof(WriteResponse))]
|
||||
internal sealed partial class JsonContext : JsonSerializerContext;
|
||||
}
|
||||
@@ -4,6 +4,7 @@ using System;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.Shared.Diagnostics;
|
||||
|
||||
namespace Microsoft.Extensions.AI.Agents.Runtime;
|
||||
|
||||
@@ -13,15 +14,21 @@ namespace Microsoft.Extensions.AI.Agents.Runtime;
|
||||
[JsonConverter(typeof(Converter))]
|
||||
public readonly partial struct ActorType : IEquatable<ActorType>
|
||||
{
|
||||
#if NET7_0_OR_GREATER
|
||||
[System.Diagnostics.CodeAnalysis.StringSyntax("Regex")]
|
||||
#endif
|
||||
private const string AgentTypeValidationRegex = "^[a-zA-Z_][a-zA-Z._:\\-0-9]*$";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ActorId"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="type">The actor type.</param>
|
||||
public ActorType(string type)
|
||||
{
|
||||
if (!IsValid(type))
|
||||
Throw.IfNullOrEmpty(type);
|
||||
if (!IsValidType(type))
|
||||
{
|
||||
throw new ArgumentException($"Invalid type: '{type}'. Must be alphanumeric (a-z, 0-9, _) and cannot start with a number or contain spaces.");
|
||||
throw new ArgumentException($"Invalid type: '{type}'. Must start with a letter or underscore, and can only contain letters, dots, underscores, colons, hyphens, and numbers.", nameof(type));
|
||||
}
|
||||
|
||||
this.Name = type;
|
||||
@@ -59,16 +66,23 @@ public readonly partial struct ActorType : IEquatable<ActorType>
|
||||
public static bool operator !=(ActorType left, ActorType right) =>
|
||||
!(left == right);
|
||||
|
||||
internal static bool IsValid(string type) =>
|
||||
type is not null && TypeRegex().IsMatch(type);
|
||||
|
||||
#if NET
|
||||
[GeneratedRegex("^[a-zA-Z_][a-zA-Z_:0-9]*$")]
|
||||
#if NET7_0_OR_GREATER
|
||||
[GeneratedRegex(AgentTypeValidationRegex)]
|
||||
private static partial Regex TypeRegex();
|
||||
#else
|
||||
private static Regex TypeRegex() => new("^[a-zA-Z_][a-zA-Z_:0-9:]*$", RegexOptions.Compiled);
|
||||
private static readonly Regex s_typeRegex = new(AgentTypeValidationRegex, RegexOptions.Compiled);
|
||||
private static Regex TypeRegex() => s_typeRegex;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Validates whether the provided type string is a valid actor type.
|
||||
/// </summary>
|
||||
public static bool IsValidType(string type)
|
||||
{
|
||||
Throw.IfNullOrEmpty(type);
|
||||
return TypeRegex().IsMatch(type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// JSON converter for <see cref="ActorType"/>.
|
||||
/// </summary>
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
namespace Microsoft.Extensions.AI.Agents.Runtime;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for the <see cref="RequestStatus"/> enumeration.
|
||||
/// </summary>
|
||||
public static class RequestStatusExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines if the request status indicates that the request has terminated.
|
||||
/// </summary>
|
||||
/// <param name="status">The request status to check.</param>
|
||||
/// <returns><see langword="true"/> if the request has terminated; otherwise, <see langword="false"/>.</returns>
|
||||
public static bool IsTerminated(this RequestStatus status) => status != RequestStatus.Pending;
|
||||
}
|
||||
Reference in New Issue
Block a user