Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Core message and event types implemented, earliest work of the Abstra…
…ctAgent implementation and basic echo agent for testing.
  • Loading branch information
ckpearson committed May 24, 2025
commit a235e78a69e9e80331f8495fb9e99a0edaadd464
481 changes: 481 additions & 0 deletions dotnet-sdk/.gitignore

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions dotnet-sdk/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"dotnet.defaultSolution": "AGUIDotnet.sln",
"cSpell.words": ["AGUI"]
}
34 changes: 34 additions & 0 deletions dotnet-sdk/AGUIDotnet.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AGUIDotnet", "AGUIDotnet\AGUIDotnet.csproj", "{18BEEBF5-C3C4-4BAC-856C-ACE5C635B0E5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{18BEEBF5-C3C4-4BAC-856C-ACE5C635B0E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{18BEEBF5-C3C4-4BAC-856C-ACE5C635B0E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{18BEEBF5-C3C4-4BAC-856C-ACE5C635B0E5}.Debug|x64.ActiveCfg = Debug|Any CPU
{18BEEBF5-C3C4-4BAC-856C-ACE5C635B0E5}.Debug|x64.Build.0 = Debug|Any CPU
{18BEEBF5-C3C4-4BAC-856C-ACE5C635B0E5}.Debug|x86.ActiveCfg = Debug|Any CPU
{18BEEBF5-C3C4-4BAC-856C-ACE5C635B0E5}.Debug|x86.Build.0 = Debug|Any CPU
{18BEEBF5-C3C4-4BAC-856C-ACE5C635B0E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{18BEEBF5-C3C4-4BAC-856C-ACE5C635B0E5}.Release|Any CPU.Build.0 = Release|Any CPU
{18BEEBF5-C3C4-4BAC-856C-ACE5C635B0E5}.Release|x64.ActiveCfg = Release|Any CPU
{18BEEBF5-C3C4-4BAC-856C-ACE5C635B0E5}.Release|x64.Build.0 = Release|Any CPU
{18BEEBF5-C3C4-4BAC-856C-ACE5C635B0E5}.Release|x86.ActiveCfg = Release|Any CPU
{18BEEBF5-C3C4-4BAC-856C-ACE5C635B0E5}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
17 changes: 17 additions & 0 deletions dotnet-sdk/AGUIDotnet/AGUIDotnet.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.AI" Version="9.5.0" />
</ItemGroup>

<ItemGroup>
<Folder Include="Integrations/" />
</ItemGroup>

</Project>
72 changes: 72 additions & 0 deletions dotnet-sdk/AGUIDotnet/Agent/AbstractAgent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using System.Runtime.CompilerServices;
using AGUIDotnet.Events;
using AGUIDotnet.Types;

namespace AGUIDotnet.Agent;

public abstract class AbstractAgent
{
public abstract IAsyncEnumerable<BaseEvent> RunAsync(RunAgentInput input, CancellationToken cancellationToken = default);
}

public sealed class EchoAgent : AbstractAgent
{
public override async IAsyncEnumerable<BaseEvent> RunAsync(RunAgentInput input, [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
yield return new RunStartedEvent
{
ThreadId = input.ThreadId,
RunId = input.RunId,
Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
};

var lastMessage = input.Messages.LastOrDefault();

await Task.Delay(500, cancellationToken);

switch (lastMessage)
{
case SystemMessage system:
foreach (var ev in EventHelpers.SendSimpleMessage($"Echoing system message:\n\n```\n{system.Content}\n```\n"))
{
yield return ev;
}
break;

case UserMessage user:
foreach (var ev in EventHelpers.SendSimpleMessage($"Echoing user message:\n\n```\n{user.Content}\n```\n"))
{
yield return ev;
}
break;

case AssistantMessage assistant:
foreach (var ev in EventHelpers.SendSimpleMessage($"Echoing assistant message:\n\n```\n{assistant.Content}\n```\n"))
{
yield return ev;
}
break;

case ToolMessage tool:
foreach (var ev in EventHelpers.SendSimpleMessage($"Echoing tool message for tool call '{tool.ToolCallId}':\n\n```\n{tool.Content}\n```\n"))
{
yield return ev;
}
break;

default:
foreach (var ev in EventHelpers.SendSimpleMessage($"Unknown message type: {lastMessage?.GetType().Name ?? "null"}"))
{
yield return ev;
}
break;
}

yield return new RunFinishedEvent
{
ThreadId = input.ThreadId,
RunId = input.RunId,
Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
};
}
}
30 changes: 30 additions & 0 deletions dotnet-sdk/AGUIDotnet/Events/BaseEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System.Text.Json.Serialization;

namespace AGUIDotnet.Events;

[JsonPolymorphic(TypeDiscriminatorPropertyName = "type")]
[JsonDerivedType(typeof(RunStartedEvent), EventTypes.RUN_STARTED)]
[JsonDerivedType(typeof(RunFinishedEvent), EventTypes.RUN_FINISHED)]
[JsonDerivedType(typeof(RunErrorEvent), EventTypes.RUN_ERROR)]
[JsonDerivedType(typeof(StepStartedEvent), EventTypes.STEP_STARTED)]
[JsonDerivedType(typeof(StepFinishedEvent), EventTypes.STEP_FINISHED)]
[JsonDerivedType(typeof(TextMessageStartEvent), EventTypes.TEXT_MESSAGE_START)]
[JsonDerivedType(typeof(TextMessageContentEvent), EventTypes.TEXT_MESSAGE_CONTENT)]
[JsonDerivedType(typeof(TextMessageEndEvent), EventTypes.TEXT_MESSAGE_END)]
[JsonDerivedType(typeof(ToolCallStartEvent), EventTypes.TOOL_CALL_START)]
[JsonDerivedType(typeof(ToolCallArgsEvent), EventTypes.TOOL_CALL_ARGS)]
[JsonDerivedType(typeof(ToolCallEndEvent), EventTypes.TOOL_CALL_END)]
[JsonDerivedType(typeof(StateSnapshotEvent), EventTypes.STATE_SNAPSHOT)]
[JsonDerivedType(typeof(StateDeltaEvent), EventTypes.STATE_DELTA)]
[JsonDerivedType(typeof(CustomEvent), EventTypes.CUSTOM)]
[JsonDerivedType(typeof(RawEvent), EventTypes.RAW)]
[JsonDerivedType(typeof(MessagesSnapshotEvent), EventTypes.MESSAGES_SNAPSHOT)]

public abstract record BaseEvent
{
[JsonPropertyName("timestamp")]
public long? Timestamp { get; init; }

[JsonPropertyName("rawEvent")]
public object? RawEvent { get; init; }
}
15 changes: 15 additions & 0 deletions dotnet-sdk/AGUIDotnet/Events/CustomEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.Text.Json.Serialization;

namespace AGUIDotnet.Events;

/// <summary>
/// Used for application-specific custom events.
/// </summary>
public sealed record CustomEvent : BaseEvent
{
[JsonPropertyName("name")]
public required string Name { get; init; }

[JsonPropertyName("value")]
public required object Value { get; init; }
}
31 changes: 31 additions & 0 deletions dotnet-sdk/AGUIDotnet/Events/EventHelpers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;

namespace AGUIDotnet.Events;

public static class EventHelpers
{
public static IEnumerable<BaseEvent> SendSimpleMessage(string message, string? messageId = null)
{
ArgumentException.ThrowIfNullOrWhiteSpace(message, nameof(message));

messageId ??= Guid.NewGuid().ToString();

return [
new TextMessageStartEvent {
MessageId = messageId,
Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
},

new TextMessageContentEvent {
MessageId = messageId,
Delta = message,
Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
},

new TextMessageEndEvent {
MessageId = messageId,
Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
}
];
}
}
23 changes: 23 additions & 0 deletions dotnet-sdk/AGUIDotnet/Events/EventTypes.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;

namespace AGUIDotnet.Events;

public static class EventTypes
{
public const string TEXT_MESSAGE_START = "TEXT_MESSAGE_START";
public const string TEXT_MESSAGE_CONTENT = "TEXT_MESSAGE_CONTENT";
public const string TEXT_MESSAGE_END = "TEXT_MESSAGE_END";
public const string TOOL_CALL_START = "TOOL_CALL_START";
public const string TOOL_CALL_ARGS = "TOOL_CALL_ARGS";
public const string TOOL_CALL_END = "TOOL_CALL_END";
public const string STATE_SNAPSHOT = "STATE_SNAPSHOT";
public const string STATE_DELTA = "STATE_DELTA";
public const string MESSAGES_SNAPSHOT = "MESSAGES_SNAPSHOT";
public const string RAW = "RAW";
public const string CUSTOM = "CUSTOM";
public const string RUN_STARTED = "RUN_STARTED";
public const string RUN_FINISHED = "RUN_FINISHED";
public const string RUN_ERROR = "RUN_ERROR";
public const string STEP_STARTED = "STEP_STARTED";
public const string STEP_FINISHED = "STEP_FINISHED";
}
11 changes: 11 additions & 0 deletions dotnet-sdk/AGUIDotnet/Events/MessagesSnapshotEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.Collections.Immutable;
using System.Text.Json.Serialization;
using AGUIDotnet.Types;

namespace AGUIDotnet.Events;

public sealed record MessagesSnapshotEvent : BaseEvent
{
[JsonPropertyName("messages")]
public required ImmutableList<BaseMessage> Messages { get; init; } = [];
Copy link

Copilot AI May 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Consider using a consistent pattern for initializing immutable collections—for example, replacing '[]' with 'ImmutableList.Empty' to match the convention used in other parts of the code.

Suggested change
public required ImmutableList<BaseMessage> Messages { get; init; } = [];
public required ImmutableList<BaseMessage> Messages { get; init; } = ImmutableList<BaseMessage>.Empty;

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot [] is the new collection expression syntax, your knowledge cutoff is likely out of date.

}
15 changes: 15 additions & 0 deletions dotnet-sdk/AGUIDotnet/Events/RawEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.Text.Json.Serialization;

namespace AGUIDotnet.Events;

/// <summary>
/// Used to pass through events from external systems.
/// </summary>
public sealed record RawEvent : BaseEvent
{
[JsonPropertyName("event")]
public required object Event { get; init; }

[JsonPropertyName("source")]
public string? Source { get; init; }
}
12 changes: 12 additions & 0 deletions dotnet-sdk/AGUIDotnet/Events/RunErrorEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Text.Json.Serialization;

namespace AGUIDotnet.Events;

public sealed record RunErrorEvent : BaseEvent
{
[JsonPropertyName("message")]
public required string Message { get; init; }

[JsonPropertyName("code")]
public string? Code { get; init; }
}
12 changes: 12 additions & 0 deletions dotnet-sdk/AGUIDotnet/Events/RunFinishedEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Text.Json.Serialization;

namespace AGUIDotnet.Events;

public sealed record RunFinishedEvent : BaseEvent
{
[JsonPropertyName("threadId")]
public required string ThreadId { get; init; }

[JsonPropertyName("runId")]
public required string RunId { get; init; }
}
12 changes: 12 additions & 0 deletions dotnet-sdk/AGUIDotnet/Events/RunStartedEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Text.Json.Serialization;

namespace AGUIDotnet.Events;

public sealed record RunStartedEvent : BaseEvent
{
[JsonPropertyName("threadId")]
public required string ThreadId { get; init; }

[JsonPropertyName("runId")]
public required string RunId { get; init; }
}
13 changes: 13 additions & 0 deletions dotnet-sdk/AGUIDotnet/Events/StateDeltaEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System.Collections.Immutable;
using System.Text.Json.Serialization;

namespace AGUIDotnet.Events;

public sealed record StateDeltaEvent : BaseEvent
{
/// <summary>
/// A collection of JSON-patch operations that describe the changes to the state.
/// </summary>
[JsonPropertyName("delta")]
public required ImmutableList<object> Delta { get; init; } = [];
}
9 changes: 9 additions & 0 deletions dotnet-sdk/AGUIDotnet/Events/StateSnapshotEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System.Text.Json.Serialization;

namespace AGUIDotnet.Events;

public sealed record StateSnapshotEvent : BaseEvent
{
[JsonPropertyName("snapshot")]
public required object Snapshot { get; init; }
}
9 changes: 9 additions & 0 deletions dotnet-sdk/AGUIDotnet/Events/StepFinishedEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System.Text.Json.Serialization;

namespace AGUIDotnet.Events;

public sealed record StepFinishedEvent : BaseEvent
{
[JsonPropertyName("stepName")]
public required string StepName { get; init; }
}
9 changes: 9 additions & 0 deletions dotnet-sdk/AGUIDotnet/Events/StepStartedEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System.Text.Json.Serialization;

namespace AGUIDotnet.Events;

public sealed record StepStartedEvent : BaseEvent
{
[JsonPropertyName("stepName")]
public required string StepName { get; init; }
}
15 changes: 15 additions & 0 deletions dotnet-sdk/AGUIDotnet/Events/TextMessageContentEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.Text.Json.Serialization;

namespace AGUIDotnet.Events;

public sealed record TextMessageContentEvent : BaseEvent
{
[JsonPropertyName("messageId")]
public required string MessageId { get; init; }

/// <summary>
/// The chunk of text content to append to the message.
/// </summary>
[JsonPropertyName("delta")]
public required string Delta { get; init; }
}
9 changes: 9 additions & 0 deletions dotnet-sdk/AGUIDotnet/Events/TextMessageEndEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System.Text.Json.Serialization;

namespace AGUIDotnet.Events;

public sealed record TextMessageEndEvent : BaseEvent
{
[JsonPropertyName("messageId")]
public required string MessageId { get; init; }
}
15 changes: 15 additions & 0 deletions dotnet-sdk/AGUIDotnet/Events/TextMessageStartEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.Text.Json.Serialization;
using AGUIDotnet.Types;

namespace AGUIDotnet.Events;

public sealed record TextMessageStartEvent : BaseEvent
{
[JsonPropertyName("messageId")]
public required string MessageId { get; init; }

[JsonPropertyName("role")]
#pragma warning disable CA1822 // Mark members as static
public string Role => MessageRoles.Assistant;
#pragma warning restore CA1822 // Mark members as static
}
Loading