Skip to content

Commit

Permalink
docs: .NET Documentation (#5039)
Browse files Browse the repository at this point in the history
* docs: Getting Started for .NET

* docs: Move .NET documentation under .NET-specific folder

* docs: update packages for refactor

---------
  • Loading branch information
lokitoth authored Jan 16, 2025
1 parent 5495a02 commit b02965e
Show file tree
Hide file tree
Showing 4 changed files with 252 additions and 0 deletions.
30 changes: 30 additions & 0 deletions docs/dotnet/user-guide/core-user-guide/defining-message-types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Defining Message Types

Messages are currently required to be Protocol Buffers. To define them, it is necessary to include the Protocol Buffers compiler, through the `Grpc.Tools` package. In your `.csproj` file, add/edit:

```xml
<PackageReference Include="Grpc.Tools" PrivateAssets="All" />
```

Then create an include a `.proto` file in the project:

```xml
<ItemGroup>
<Protobuf Include="messages.proto" GrpcServices="Client;Server" Link="messages.proto" />
</ItemGroup>
```

Then define your messages as specified in the [Protocol Buffers Language Guide](https://protobuf.dev/programming-guides/proto3/)

```proto
syntax = "proto3";
package HelloAgents;
option csharp_namespace = "AgentsProtocol";
message TextMessage {
string Source = 1;
string Content = 2;
}
```
46 changes: 46 additions & 0 deletions docs/dotnet/user-guide/core-user-guide/differences-python.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Differences from Python

## Agents Self-Interact

When an agent sends a message of a type to which it also listens:

```csharp
[TopicSubscription("default")]
public class MyAgent(
IAgentWorker worker,
[FromKeyedServices("EventTypes")] EventTypes typeRegistry
) :
Agent(worker, typeRegistry),
IHandle<Message>
{
async Task SomeInternalFunctionAsync()
{
Message m;

// ...
await this.PublishMessageAsync(m);
}

public async Task Handle(Message message)
{
// will receive messages sent by SomeInternalFunctionAsync()
}
}
```

Tracked by [#4998](https://github.com/microsoft/autogen/issues/4998)

## 'Local' Runtime is Multithreaded

Unlike the `single_threaded_runtime`, the InProcess (`local: true`) runtime for .NET is multi-threaded, so messages will process in arbitrary order across agents. This means that an agent may process messages sent after the termination request has been made unless checking for termination using the `IHostApplicationLifecycle` service.

## No equivalent to 'stop_when_idle()'

Agents need to request termination explicitly, as there is no meaningful 'idle' state.

## All message types need to be Protocol Buffers

See (linkto: defining-message-types.md) for instructions on defining messages

Tracked by [#4695](https://github.com/microsoft/autogen/issues/4695)
143 changes: 143 additions & 0 deletions docs/dotnet/user-guide/core-user-guide/getting-started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# Quick Start

Before diving into the core APIs, let’s start with a simple example of two agents that count down from 10 to 1.

We first define the agent classes and their respective procedures for handling messages. We create two agent classes: `Modifier` and `Checker`. The `Modifier` agent modifies a number that is given and the `Check` agent checks the value against a condition. We also define a pair of
messages in a .proto file which will be generated into the message types that will be passed
between the agents.

```proto
syntax = "proto3";
package HelloAgents;
option csharp_namespace = "Microsoft.Autogen.Samples.CountAgent.Protocol";
message CountMessage {
int32 Content = 1;
}
message CountUpdate {
int32 NewCount = 1;
}
```

We create two messages to ensure we have tick-tock behaviour between the agents; if we used a single type, then both agents would receive the other agents' message as well as self-sent messages. (Note: this is a behaviour difference from Python; Issue#4998)

In the project file, we add

```xml
<ItemGroup>
<PackageReference Include="Grpc.Tools" PrivateAssets="All" />
</ItemGroup>

<ItemGroup>
<Protobuf Include="messages.proto" GrpcServices="Client;Server" Link="messages.proto" />
</ItemGroup>
```

This will ensure the message classes are available for our agents to send/receive.

Now we will define the agents:

```csharp
[TopicSubscription("default")]
public class Modifier(
IAgentWorker worker,
[FromKeyedServices("EventTypes")] EventTypes typeRegistry,
ModifyF modifyFunc
) :
Agent(worker, typeRegistry),
IHandle<CountMessage>
{
public async Task Handle(CountMessage item)
{
// handling code
}
}
```

The `TopicSubscription` attribute defines the set of topics the agents will listen to. Topics (see here) are useful for separaating different logical chains of agent communications.

The first two parameters to the constructor, `IAgentWorker` and `EventTypes` are automatically made available through dependency injection to the workers. (We do not allow direct construction of workers in Autogen.Core: see here for FAQ), and need to be passed on to the base class.

Other parameters are also made available through dependency injection (see here).

Agents register for messages by implementing the `IHandle<MessageType>` interface:

```csharp
public async Task Handle(CountMessage item)
{
int newValue = modifyFunc(item.Content);
Console.WriteLine($"{SEPARATOR_LINE}\nModifier:\nModified {item.Content} to {newValue}");

CountUpdate updateMessage = new CountUpdate { NewCount = newValue };

await this.PublishMessageAsync(updateMessage);
}
```

The `Modifier` agent receives a `CountMessage` indicating the current count, modifies it using the injected `ModifyF modifyFunc`, and publishes the `CountUpdate` message.

The `Checker` agent is defines similarly:

```csharp
[TopicSubscription("default")]
public class Checker(
IAgentWorker worker,
[FromKeyedServices("EventTypes")] EventTypes typeRegistry,
IHostApplicationLifetime hostApplicationLifetime,
TerminationF runUntilFunc
) :
Agent(worker, typeRegistry),
IHandle<CountUpdate>
{
public Task Handle(CountUpdate item)
{
if (!runUntilFunc(item.NewCount))
{
Console.WriteLine($"{SEPARATOR_LINE}\nChecker:\n{item.NewCount} passed the check, continue.");
await this.PublishMessageAsync(new CountMessage { Content = item.NewCount });
}
else
{
Console.WriteLine($"{SEPARATOR_LINE}\nChecker:\n{item.NewCount} failed the check, stopping.");
hostApplicationLifetime.StopApplication();
}
}
}
```

The `Checker` continues the count when `runUntilFunc` has not triggered by publishing a new `CountMessage` with the updated count; if termination is desired, it will request it by calling `hostApplicationLifetime.StopApplication()`.

You might have already noticed, the agents’ logic, whether it is using model or code executor, is completely decoupled from how messages are delivered. This is the core idea: the framework provides a communication infrastructure, and the agents are responsible for their own logic. We call the communication infrastructure an Agent Runtime.

Agent runtime is a key concept of this framework. Besides delivering messages, it also manages agents’ lifecycle. So the creation of agents are handled by the runtime.

The following code shows how to register and run the agents using the local (InProcess) runtime:

```csharp
// Define the counting logic
using ModifyF = System.Func<int, int>;
using TerminationF = System.Func<int, bool>;

ModifyF modifyFunc = (int x) => x - 1;
TerminationF runUntilFunc = (int x) =>
{
return x <= 1;
};

// Register the services
WebApplicationBuilder? builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton(modifyFunc);
builder.Services.AddSingleton(runUntilFunc);

// Send the initial count to the agents app, running on the `local` runtime, and pass through the registered services via the application `builder`
var app = await AgentsApp.PublishMessageAsync("default", new CountMessage
{
Content = 10
}, local: true, builder: builder).ConfigureAwait(false);

// Run until application shutdown
await app.WaitForShutdownAsync();
```
33 changes: 33 additions & 0 deletions docs/dotnet/user-guide/core-user-guide/installation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Installation

## Add via `<ProjectReference>`

```
<ProjectReference Include="<path>/<to>/Contracts/Microsoft.AutoGen.Contracts.csproj" />
<ProjectReference Include="<path>/<to>/Core/Microsoft.AutoGen.Core.csproj" />
```

<!-- (TODO: Move to bottom) -->

## These will only work after we release the package:

## Install via `.NET cli`

```
> dotnet add package Microsoft.AutoGen.Contracts --version 0.4.0
> dotnet add package Microsoft.AutoGen.Core --version 0.4.0
```

## Install via `Package Manager`

```
PM> NuGet\Install-Package Microsoft.AutoGen.Contracts -Version 0.4.0
PM> NuGet\Install-Package Microsoft.AutoGen.Core -Version 0.4.0
```

## Add via `<PackageReference>`

```
<PackageReference Include="Microsoft.AutoGen.Contracts" Version="0.2.1" />
<PackageReference Include="Microsoft.AutoGen.Core" Version="0.2.1" />
```

0 comments on commit b02965e

Please sign in to comment.