Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[.Net] add DotnetInteractiveKernelBuilder to AutoGen.DotnetInteractive #3317

Merged
merged 7 commits into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
25 changes: 25 additions & 0 deletions .github/workflows/dotnet-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,24 @@ jobs:
fail-fast: false
matrix:
os: [ ubuntu-latest, macos-latest, windows-latest ]
python-version: ["3.11"]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
with:
lfs: true
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install jupyter and ipykernel
run: |
python -m pip install --upgrade pip
python -m pip install jupyter
python -m pip install ipykernel
- name: list available kernels
run: |
python -m jupyter kernelspec list
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
Expand Down Expand Up @@ -114,6 +127,18 @@ jobs:
- uses: actions/checkout@v4
with:
lfs: true
- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
python-version: 3.11
- name: Install jupyter and ipykernel
run: |
python -m pip install --upgrade pip
python -m pip install jupyter
python -m pip install ipykernel
- name: list available kernels
run: |
python -m jupyter kernelspec list
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
Expand Down
12 changes: 12 additions & 0 deletions .github/workflows/dotnet-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,18 @@ jobs:
- uses: actions/checkout@v4
with:
lfs: true
- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
python-version: 3.11
- name: Install jupyter and ipykernel
run: |
python -m pip install --upgrade pip
python -m pip install jupyter
python -m pip install ipykernel
- name: list available kernels
run: |
python -m jupyter kernelspec list
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
Expand Down
1 change: 1 addition & 0 deletions dotnet/eng/Version.props
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@
<MicrosoftASPNETCoreVersion>8.0.4</MicrosoftASPNETCoreVersion>
<GoogleCloudAPIPlatformVersion>3.0.0</GoogleCloudAPIPlatformVersion>
<JsonSchemaVersion>4.3.0.2</JsonSchemaVersion>
<PowershellSDKVersion>7.4.4</PowershellSDKVersion>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,45 @@
#region code_snippet_0_1
using AutoGen.Core;
using AutoGen.DotnetInteractive;
using AutoGen.DotnetInteractive.Extension;
#endregion code_snippet_0_1

namespace AutoGen.BasicSample.CodeSnippet;
public class RunCodeSnippetCodeSnippet
{
public async Task CodeSnippet1()
{
IAgent agent = default;
IAgent agent = new DefaultReplyAgent("agent", "Hello World");

#region code_snippet_1_1
var workingDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
Directory.CreateDirectory(workingDirectory);
var interactiveService = new InteractiveService(installingDirectory: workingDirectory);
await interactiveService.StartAsync(workingDirectory: workingDirectory);
var kernel = DotnetInteractiveKernelBuilder
.CreateDefaultBuilder() // add C# and F# kernels
.Build();
#endregion code_snippet_1_1

#region code_snippet_1_2
// register dotnet code block execution hook to an arbitrary agent
var dotnetCodeAgent = agent.RegisterDotnetCodeBlockExectionHook(interactiveService: interactiveService);
// register middleware to execute code block
var dotnetCodeAgent = agent
.RegisterMiddleware(async (msgs, option, innerAgent, ct) =>
{
var lastMessage = msgs.LastOrDefault();
if (lastMessage == null || lastMessage.GetContent() is null)
{
return await innerAgent.GenerateReplyAsync(msgs, option, ct);
}

if (lastMessage.ExtractCodeBlock("```csharp", "```") is string codeSnippet)
{
// execute code snippet
var result = await kernel.RunSubmitCodeCommandAsync(codeSnippet, "csharp");
return new TextMessage(Role.Assistant, result, from: agent.Name);
}
else
{
// no code block found, invoke next agent
return await innerAgent.GenerateReplyAsync(msgs, option, ct);
}
});

var codeSnippet = @"
```csharp
Expand All @@ -44,5 +64,17 @@ public async Task CodeSnippet1()
```
";
#endregion code_snippet_1_3

#region code_snippet_1_4
var pythonKernel = DotnetInteractiveKernelBuilder
.CreateDefaultBuilder()
.AddPythonKernel(venv: "python3")
.Build();

var pythonCode = """
print('Hello from Python!')
""";
var result = await pythonKernel.RunSubmitCodeCommandAsync(pythonCode, "python3");
#endregion code_snippet_1_4
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using AutoGen.BasicSample;
using AutoGen.Core;
using AutoGen.DotnetInteractive;
using AutoGen.DotnetInteractive.Extension;
using AutoGen.OpenAI;
using FluentAssertions;

Expand All @@ -14,32 +15,13 @@ public static async Task RunAsync()
{
var instance = new Example04_Dynamic_GroupChat_Coding_Task();

// setup dotnet interactive
var workDir = Path.Combine(Path.GetTempPath(), "InteractiveService");
if (!Directory.Exists(workDir))
{
Directory.CreateDirectory(workDir);
}

using var service = new InteractiveService(workDir);
var dotnetInteractiveFunctions = new DotnetInteractiveFunction(service);

var result = Path.Combine(workDir, "result.txt");
if (File.Exists(result))
{
File.Delete(result);
}

await service.StartAsync(workDir, default);
var kernel = DotnetInteractiveKernelBuilder
.CreateDefaultBuilder()
.AddPythonKernel("python3")
.Build();

var gptConfig = LLMConfiguration.GetAzureOpenAIGPT3_5_Turbo();

var helperAgent = new GPTAgent(
name: "helper",
systemMessage: "You are a helpful AI assistant",
temperature: 0f,
config: gptConfig);

var groupAdmin = new GPTAgent(
name: "groupAdmin",
systemMessage: "You are the admin of the group chat",
Expand All @@ -56,8 +38,8 @@ public static async Task RunAsync()
systemMessage: """
You are a manager who takes coding problem from user and resolve problem by splitting them into small tasks and assign each task to the most appropriate agent.
Here's available agents who you can assign task to:
- coder: write dotnet code to resolve task
- runner: run dotnet code from coder
- coder: write python code to resolve task
- runner: run python code from coder

The workflow is as follows:
- You take the coding problem from user
Expand All @@ -83,15 +65,7 @@ You are a manager who takes coding problem from user and resolve problem by spli

Once the coding problem is resolved, summarize each steps and results and send the summary to the user using the following format:
```summary
{
"problem": "{coding problem}",
"steps": [
{
"step": "{step}",
"result": "{result}"
}
]
}
@user, <summary of the task>
```

Your reply must contain one of [task|ask|summary] to indicate the type of your message.
Expand All @@ -110,19 +84,16 @@ Your reply must contain one of [task|ask|summary] to indicate the type of your m
// The nuget agent install nuget packages if there's any.
var coderAgent = new GPTAgent(
name: "coder",
systemMessage: @"You act as dotnet coder, you write dotnet code to resolve task. Once you finish writing code, ask runner to run the code for you.
systemMessage: @"You act as python coder, you write python code to resolve task. Once you finish writing code, ask runner to run the code for you.

Here're some rules to follow on writing dotnet code:
- put code between ```csharp and ```
- When creating http client, use `var httpClient = new HttpClient()`. Don't use `using var httpClient = new HttpClient()` because it will cause error when running the code.
- Try to use `var` instead of explicit type.
- Try avoid using external library, use .NET Core library instead.
- Use top level statement to write code.
- put code between ```python and ```
- Try avoid using external library
- Always print out the result to console. Don't write code that doesn't print out anything.

If you need to install nuget packages, put nuget packages in the following format:
```nuget
nuget_package_name
Use the following format to install pip package:
```python
%pip install <package_name>
```

If your code is incorrect, Fix the error and send the code again.
Expand All @@ -143,10 +114,8 @@ Your reply must contain one of [task|ask|summary] to indicate the type of your m
name: "reviewer",
systemMessage: """
You are a code reviewer who reviews code from coder. You need to check if the code satisfy the following conditions:
- The reply from coder contains at least one code block, e.g ```csharp and ```
- There's only one code block and it's csharp code block
- The code block is not inside a main function. a.k.a top level statement
- The code block is not using declaration when creating http client
- The reply from coder contains at least one code block, e.g ```python and ```
- There's only one code block and it's python code block

You don't check the code style, only check if the code satisfy the above conditions.

Expand All @@ -173,14 +142,32 @@ Your reply must contain one of [task|ask|summary] to indicate the type of your m
// The runner agent will run the code block from coder's reply.
// It runs dotnet code using dotnet interactive service hook.
// It also truncate the output if the output is too long.
var runner = new AssistantAgent(
var runner = new DefaultReplyAgent(
name: "runner",
defaultReply: "No code available, coder, write code please")
.RegisterDotnetCodeBlockExectionHook(interactiveService: service)
.RegisterMiddleware(async (msgs, option, agent, ct) =>
{
var mostRecentCoderMessage = msgs.LastOrDefault(x => x.From == "coder") ?? throw new Exception("No coder message found");
return await agent.GenerateReplyAsync(new[] { mostRecentCoderMessage }, option, ct);

if (mostRecentCoderMessage.ExtractCodeBlock("```python", "```") is string code)
{
var result = await kernel.RunSubmitCodeCommandAsync(code, "python");
// only keep the first 500 characters
if (result.Length > 500)
{
result = result.Substring(0, 500);
}
result = $"""
# [CODE_BLOCK_EXECUTION_RESULT]
{result}
""";

return new TextMessage(Role.Assistant, result, from: agent.Name);
}
else
{
return await agent.GenerateReplyAsync(msgs, option, ct);
}
})
.RegisterPrintMessage();

Expand Down Expand Up @@ -251,18 +238,27 @@ Your reply must contain one of [task|ask|summary] to indicate the type of your m
workflow: workflow);

// task 1: retrieve the most recent pr from mlnet and save it in result.txt
var groupChatManager = new GroupChatManager(groupChat);
await userProxy.SendAsync(groupChatManager, "Retrieve the most recent pr from mlnet and save it in result.txt", maxRound: 30);
File.Exists(result).Should().BeTrue();

// task 2: calculate the 39th fibonacci number
var answer = 63245986;
// clear the result file
File.Delete(result);
var task = """
retrieve the most recent pr from mlnet and save it in result.txt
""";
var chatHistory = new List<IMessage>
{
new TextMessage(Role.Assistant, task)
{
From = userProxy.Name
}
};
await foreach (var message in groupChat.SendAsync(chatHistory, maxRound: 10))
{
if (message.From == admin.Name && message.GetContent().Contains("```summary"))
{
// Task complete!
break;
}
}

var conversationHistory = await userProxy.InitiateChatAsync(groupChatManager, "What's the 39th of fibonacci number? Save the result in result.txt", maxRound: 10);
// check if the result file is created
var result = "result.txt";
File.Exists(result).Should().BeTrue();
var resultContent = File.ReadAllText(result);
resultContent.Should().Contain(answer.ToString());
}
}
Loading
Loading