From 5f7c34ac72d6c2e0714d7e08dcc33d529a94f618 Mon Sep 17 00:00:00 2001 From: Xiaoyun Zhang Date: Mon, 13 May 2024 14:58:50 -0700 Subject: [PATCH 1/2] [.Net] Remove Workflow class && bump version to 0.0.14 (#2675) * remove workflow class * bump version to 0.0.14 --- dotnet/eng/MetaInfo.props | 2 +- dotnet/src/AutoGen.Core/GroupChat/Graph.cs | 13 ------------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/dotnet/eng/MetaInfo.props b/dotnet/eng/MetaInfo.props index 8aff3c60226..0444dadfd5e 100644 --- a/dotnet/eng/MetaInfo.props +++ b/dotnet/eng/MetaInfo.props @@ -1,7 +1,7 @@ - 0.0.13 + 0.0.14 AutoGen https://microsoft.github.io/autogen-for-net/ https://github.com/microsoft/autogen diff --git a/dotnet/src/AutoGen.Core/GroupChat/Graph.cs b/dotnet/src/AutoGen.Core/GroupChat/Graph.cs index 78d92508611..02f4da50bae 100644 --- a/dotnet/src/AutoGen.Core/GroupChat/Graph.cs +++ b/dotnet/src/AutoGen.Core/GroupChat/Graph.cs @@ -8,19 +8,6 @@ namespace AutoGen.Core; -/// -/// Obsolete: please use -/// -[Obsolete("please use Graph")] -public class Workflow : Graph -{ - [Obsolete("please use Graph")] - public Workflow(IEnumerable transitions) - : base(transitions) - { - } -} - public class Graph { private readonly List transitions = new List(); From ba82c1d566234566770cadcf997ecb19aa22cada Mon Sep 17 00:00:00 2001 From: Xiaoyun Zhang Date: Mon, 13 May 2024 20:40:26 -0700 Subject: [PATCH 2/2] [.Net] Fix #2660 and add tests for AutoGen.DotnetInteractive (#2676) * update * fix 2660 * remove unnecessary feed --- dotnet/AutoGen.sln | 9 +- dotnet/NuGet.config | 2 - dotnet/eng/Version.props | 2 +- .../AutoGen.DotnetInteractive.csproj | 8 +- .../DotnetInteractiveFunction.cs | 71 +++++++------- .../InteractiveService.cs | 97 +++++++++---------- .../dotnet-tools.json | 2 +- .../AutoGen.DotnetInteractive.Tests.csproj | 24 +++++ .../DotnetInteractiveServiceTest.cs | 82 ++++++++++++++++ dotnet/website/articles/Installation.md | 6 -- dotnet/website/articles/Run-dotnet-code.md | 6 -- 11 files changed, 203 insertions(+), 106 deletions(-) create mode 100644 dotnet/test/AutoGen.DotnetInteractive.Tests/AutoGen.DotnetInteractive.Tests.csproj create mode 100644 dotnet/test/AutoGen.DotnetInteractive.Tests/DotnetInteractiveServiceTest.cs diff --git a/dotnet/AutoGen.sln b/dotnet/AutoGen.sln index b46b8091cf5..33f723e54f8 100644 --- a/dotnet/AutoGen.sln +++ b/dotnet/AutoGen.sln @@ -33,7 +33,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutoGen.Mistral", "src\Auto EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutoGen.Mistral.Tests", "test\AutoGen.Mistral.Tests\AutoGen.Mistral.Tests.csproj", "{15441693-3659-4868-B6C1-B106F52FF3BA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoGen.SemanticKernel.Tests", "test\AutoGen.SemanticKernel.Tests\AutoGen.SemanticKernel.Tests.csproj", "{1DFABC4A-8458-4875-8DCB-59F3802DAC65}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutoGen.SemanticKernel.Tests", "test\AutoGen.SemanticKernel.Tests\AutoGen.SemanticKernel.Tests.csproj", "{1DFABC4A-8458-4875-8DCB-59F3802DAC65}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoGen.DotnetInteractive.Tests", "test\AutoGen.DotnetInteractive.Tests\AutoGen.DotnetInteractive.Tests.csproj", "{B61388CA-DC73-4B7F-A7B2-7B9A86C9229E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -93,6 +95,10 @@ Global {1DFABC4A-8458-4875-8DCB-59F3802DAC65}.Debug|Any CPU.Build.0 = Debug|Any CPU {1DFABC4A-8458-4875-8DCB-59F3802DAC65}.Release|Any CPU.ActiveCfg = Release|Any CPU {1DFABC4A-8458-4875-8DCB-59F3802DAC65}.Release|Any CPU.Build.0 = Release|Any CPU + {B61388CA-DC73-4B7F-A7B2-7B9A86C9229E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B61388CA-DC73-4B7F-A7B2-7B9A86C9229E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B61388CA-DC73-4B7F-A7B2-7B9A86C9229E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B61388CA-DC73-4B7F-A7B2-7B9A86C9229E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -111,6 +117,7 @@ Global {6585D1A4-3D97-4D76-A688-1933B61AEB19} = {18BF8DD7-0585-48BF-8F97-AD333080CE06} {15441693-3659-4868-B6C1-B106F52FF3BA} = {F823671B-3ECA-4AE6-86DA-25E920D3FE64} {1DFABC4A-8458-4875-8DCB-59F3802DAC65} = {F823671B-3ECA-4AE6-86DA-25E920D3FE64} + {B61388CA-DC73-4B7F-A7B2-7B9A86C9229E} = {F823671B-3ECA-4AE6-86DA-25E920D3FE64} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {93384647-528D-46C8-922C-8DB36A382F0B} diff --git a/dotnet/NuGet.config b/dotnet/NuGet.config index 2eb25136c6a..1d0cf4c2bc7 100644 --- a/dotnet/NuGet.config +++ b/dotnet/NuGet.config @@ -2,8 +2,6 @@ - - diff --git a/dotnet/eng/Version.props b/dotnet/eng/Version.props index b9fc4367194..ae213015471 100644 --- a/dotnet/eng/Version.props +++ b/dotnet/eng/Version.props @@ -10,7 +10,7 @@ 6.8.0 2.4.2 17.7.0 - 1.0.0-beta.23523.2 + 1.0.0-beta.24229.4 8.0.0 4.0.0 diff --git a/dotnet/src/AutoGen.DotnetInteractive/AutoGen.DotnetInteractive.csproj b/dotnet/src/AutoGen.DotnetInteractive/AutoGen.DotnetInteractive.csproj index 57fcb1fce16..72c67fe7801 100644 --- a/dotnet/src/AutoGen.DotnetInteractive/AutoGen.DotnetInteractive.csproj +++ b/dotnet/src/AutoGen.DotnetInteractive/AutoGen.DotnetInteractive.csproj @@ -19,7 +19,7 @@ - + @@ -27,14 +27,12 @@ - - + - - + diff --git a/dotnet/src/AutoGen.DotnetInteractive/DotnetInteractiveFunction.cs b/dotnet/src/AutoGen.DotnetInteractive/DotnetInteractiveFunction.cs index 5587694882c..bb5504cd548 100644 --- a/dotnet/src/AutoGen.DotnetInteractive/DotnetInteractiveFunction.cs +++ b/dotnet/src/AutoGen.DotnetInteractive/DotnetInteractiveFunction.cs @@ -12,57 +12,58 @@ namespace AutoGen.DotnetInteractive; public class DotnetInteractiveFunction : IDisposable { private readonly InteractiveService? _interactiveService = null; - private string? _notebookPath; + private string _notebookPath; private readonly KernelInfoCollection _kernelInfoCollection = new KernelInfoCollection(); + /// + /// Create an instance of " + /// + /// interactive service to use. + /// notebook path if provided. public DotnetInteractiveFunction(InteractiveService interactiveService, string? notebookPath = null, bool continueFromExistingNotebook = false) { this._interactiveService = interactiveService; - this._notebookPath = notebookPath; + this._notebookPath = notebookPath ?? Path.GetTempPath() + "notebook.ipynb"; this._kernelInfoCollection.Add(new KernelInfo("csharp")); this._kernelInfoCollection.Add(new KernelInfo("markdown")); - - if (this._notebookPath != null) + if (continueFromExistingNotebook == false) { - if (continueFromExistingNotebook == false) + // remove existing notebook + if (File.Exists(this._notebookPath)) { - // remove existing notebook - if (File.Exists(this._notebookPath)) - { - File.Delete(this._notebookPath); - } + File.Delete(this._notebookPath); + } - var document = new InteractiveDocument(); + var document = new InteractiveDocument(); - using var stream = File.OpenWrite(_notebookPath); - Notebook.Write(document, stream, this._kernelInfoCollection); - stream.Flush(); - stream.Dispose(); - } - else if (continueFromExistingNotebook == true && File.Exists(this._notebookPath)) + using var stream = File.OpenWrite(_notebookPath); + Notebook.Write(document, stream, this._kernelInfoCollection); + stream.Flush(); + stream.Dispose(); + } + else if (continueFromExistingNotebook == true && File.Exists(this._notebookPath)) + { + // load existing notebook + using var readStream = File.OpenRead(this._notebookPath); + var document = Notebook.Read(readStream, this._kernelInfoCollection); + foreach (var cell in document.Elements) { - // load existing notebook - using var readStream = File.OpenRead(this._notebookPath); - var document = Notebook.Read(readStream, this._kernelInfoCollection); - foreach (var cell in document.Elements) + if (cell.KernelName == "csharp") { - if (cell.KernelName == "csharp") - { - var code = cell.Contents; - this._interactiveService.SubmitCSharpCodeAsync(code, default).Wait(); - } + var code = cell.Contents; + this._interactiveService.SubmitCSharpCodeAsync(code, default).Wait(); } } - else - { - // create an empty notebook - var document = new InteractiveDocument(); + } + else + { + // create an empty notebook + var document = new InteractiveDocument(); - using var stream = File.OpenWrite(_notebookPath); - Notebook.Write(document, stream, this._kernelInfoCollection); - stream.Flush(); - stream.Dispose(); - } + using var stream = File.OpenWrite(_notebookPath); + Notebook.Write(document, stream, this._kernelInfoCollection); + stream.Flush(); + stream.Dispose(); } } diff --git a/dotnet/src/AutoGen.DotnetInteractive/InteractiveService.cs b/dotnet/src/AutoGen.DotnetInteractive/InteractiveService.cs index 0dc34f24e44..7490b64e126 100644 --- a/dotnet/src/AutoGen.DotnetInteractive/InteractiveService.cs +++ b/dotnet/src/AutoGen.DotnetInteractive/InteractiveService.cs @@ -5,7 +5,6 @@ using System.Reactive.Linq; using System.Reflection; using Microsoft.DotNet.Interactive; -using Microsoft.DotNet.Interactive.App.Connection; using Microsoft.DotNet.Interactive.Commands; using Microsoft.DotNet.Interactive.Connection; using Microsoft.DotNet.Interactive.Events; @@ -41,7 +40,7 @@ public InteractiveService(string installingDirectory) public async Task StartAsync(string workingDirectory, CancellationToken ct = default) { - this.kernel = await this.CreateKernelAsync(workingDirectory, ct); + this.kernel = await this.CreateKernelAsync(workingDirectory, true, ct); return true; } @@ -84,7 +83,51 @@ public async Task StartAsync(string workingDirectory, CancellationToken ct return await this.SubmitCommandAsync(command, ct); } - private async Task CreateKernelAsync(string workingDirectory, CancellationToken ct = default) + public bool RestoreDotnetInteractive() + { + this.WriteLine("Restore dotnet interactive tool"); + // write RestoreInteractive.config from embedded resource to this.workingDirectory + var assembly = Assembly.GetAssembly(typeof(InteractiveService))!; + var resourceName = "AutoGen.DotnetInteractive.RestoreInteractive.config"; + using (var stream = assembly.GetManifestResourceStream(resourceName)!) + using (var fileStream = File.Create(Path.Combine(this.installingDirectory, "RestoreInteractive.config"))) + { + stream.CopyTo(fileStream); + } + + // write dotnet-tool.json from embedded resource to this.workingDirectory + + resourceName = "AutoGen.DotnetInteractive.dotnet-tools.json"; + using (var stream2 = assembly.GetManifestResourceStream(resourceName)!) + using (var fileStream2 = File.Create(Path.Combine(this.installingDirectory, "dotnet-tools.json"))) + { + stream2.CopyTo(fileStream2); + } + + var psi = new ProcessStartInfo + { + FileName = "dotnet", + Arguments = $"tool restore --configfile RestoreInteractive.config", + WorkingDirectory = this.installingDirectory, + RedirectStandardInput = true, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true, + }; + + using var process = new Process { StartInfo = psi }; + process.OutputDataReceived += this.PrintProcessOutput; + process.ErrorDataReceived += this.PrintProcessOutput; + process.Start(); + process.BeginErrorReadLine(); + process.BeginOutputReadLine(); + process.WaitForExit(); + + return process.ExitCode == 0; + } + + private async Task CreateKernelAsync(string workingDirectory, bool restoreWhenFail = true, CancellationToken ct = default) { try { @@ -139,13 +182,13 @@ await rootProxyKernel.SendAsync( return compositeKernel; } - catch (CommandLineInvocationException ex) when (ex.Message.Contains("Cannot find a tool in the manifest file that has a command named 'dotnet-interactive'")) + catch (CommandLineInvocationException) when (restoreWhenFail) { var success = this.RestoreDotnetInteractive(); if (success) { - return await this.CreateKernelAsync(workingDirectory, ct); + return await this.CreateKernelAsync(workingDirectory, false, ct); } throw; @@ -176,50 +219,6 @@ private void WriteLine(string data) this.Output?.Invoke(this, data); } - private bool RestoreDotnetInteractive() - { - this.WriteLine("Restore dotnet interactive tool"); - // write RestoreInteractive.config from embedded resource to this.workingDirectory - var assembly = Assembly.GetAssembly(typeof(InteractiveService))!; - var resourceName = "AutoGen.DotnetInteractive.RestoreInteractive.config"; - using (var stream = assembly.GetManifestResourceStream(resourceName)!) - using (var fileStream = File.Create(Path.Combine(this.installingDirectory, "RestoreInteractive.config"))) - { - stream.CopyTo(fileStream); - } - - // write dotnet-tool.json from embedded resource to this.workingDirectory - - resourceName = "AutoGen.DotnetInteractive.dotnet-tools.json"; - using (var stream2 = assembly.GetManifestResourceStream(resourceName)!) - using (var fileStream2 = File.Create(Path.Combine(this.installingDirectory, "dotnet-tools.json"))) - { - stream2.CopyTo(fileStream2); - } - - var psi = new ProcessStartInfo - { - FileName = "dotnet", - Arguments = $"tool restore --configfile RestoreInteractive.config", - WorkingDirectory = this.installingDirectory, - RedirectStandardInput = true, - RedirectStandardOutput = true, - RedirectStandardError = true, - UseShellExecute = false, - CreateNoWindow = true, - }; - - using var process = new Process { StartInfo = psi }; - process.OutputDataReceived += this.PrintProcessOutput; - process.ErrorDataReceived += this.PrintProcessOutput; - process.Start(); - process.BeginErrorReadLine(); - process.BeginOutputReadLine(); - process.WaitForExit(); - - return process.ExitCode == 0; - } - private void PrintProcessOutput(object sender, DataReceivedEventArgs e) { if (!string.IsNullOrEmpty(e.Data)) diff --git a/dotnet/src/AutoGen.DotnetInteractive/dotnet-tools.json b/dotnet/src/AutoGen.DotnetInteractive/dotnet-tools.json index b2677b61678..12b09e61cae 100644 --- a/dotnet/src/AutoGen.DotnetInteractive/dotnet-tools.json +++ b/dotnet/src/AutoGen.DotnetInteractive/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "Microsoft.dotnet-interactive": { - "version": "1.0.431302", + "version": "1.0.522904", "commands": [ "dotnet-interactive" ] diff --git a/dotnet/test/AutoGen.DotnetInteractive.Tests/AutoGen.DotnetInteractive.Tests.csproj b/dotnet/test/AutoGen.DotnetInteractive.Tests/AutoGen.DotnetInteractive.Tests.csproj new file mode 100644 index 00000000000..cf2c24eaf78 --- /dev/null +++ b/dotnet/test/AutoGen.DotnetInteractive.Tests/AutoGen.DotnetInteractive.Tests.csproj @@ -0,0 +1,24 @@ + + + + $(TestTargetFramework) + enable + false + True + + + + + + + + + + + + + + + + + diff --git a/dotnet/test/AutoGen.DotnetInteractive.Tests/DotnetInteractiveServiceTest.cs b/dotnet/test/AutoGen.DotnetInteractive.Tests/DotnetInteractiveServiceTest.cs new file mode 100644 index 00000000000..0e36053c45e --- /dev/null +++ b/dotnet/test/AutoGen.DotnetInteractive.Tests/DotnetInteractiveServiceTest.cs @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// DotnetInteractiveServiceTest.cs + +using FluentAssertions; +using Xunit; +using Xunit.Abstractions; + +namespace AutoGen.DotnetInteractive.Tests; + +public class DotnetInteractiveServiceTest : IDisposable +{ + private ITestOutputHelper _output; + private InteractiveService _interactiveService; + private string _workingDir; + + public DotnetInteractiveServiceTest(ITestOutputHelper output) + { + _output = output; + _workingDir = Path.Combine(Path.GetTempPath(), "test", Path.GetRandomFileName()); + if (!Directory.Exists(_workingDir)) + { + Directory.CreateDirectory(_workingDir); + } + + _interactiveService = new InteractiveService(_workingDir); + _interactiveService.StartAsync(_workingDir, default).Wait(); + } + + public void Dispose() + { + _interactiveService.Dispose(); + } + + [Fact] + public async Task ItRunCSharpCodeSnippetTestsAsync() + { + var cts = new CancellationTokenSource(); + var isRunning = await _interactiveService.StartAsync(_workingDir, cts.Token); + + isRunning.Should().BeTrue(); + + _interactiveService.IsRunning().Should().BeTrue(); + + // test code snippet + var hello_world = @" +Console.WriteLine(""hello world""); +"; + + await this.TestCSharpCodeSnippet(_interactiveService, hello_world, "hello world"); + await this.TestCSharpCodeSnippet( + _interactiveService, + code: @" +Console.WriteLine(""hello world"" +", + expectedOutput: "Error: (2,32): error CS1026: ) expected"); + + await this.TestCSharpCodeSnippet( + service: _interactiveService, + code: "throw new Exception();", + expectedOutput: "Error: System.Exception: Exception of type 'System.Exception' was thrown"); + } + + [Fact] + public async Task ItRunPowershellScriptTestsAsync() + { + // test power shell + var ps = @"Write-Output ""hello world"""; + await this.TestPowershellCodeSnippet(_interactiveService, ps, "hello world"); + } + + private async Task TestPowershellCodeSnippet(InteractiveService service, string code, string expectedOutput) + { + var result = await service.SubmitPowershellCodeAsync(code, CancellationToken.None); + result.Should().StartWith(expectedOutput); + } + + private async Task TestCSharpCodeSnippet(InteractiveService service, string code, string expectedOutput) + { + var result = await service.SubmitCSharpCodeAsync(code, CancellationToken.None); + result.Should().StartWith(expectedOutput); + } +} diff --git a/dotnet/website/articles/Installation.md b/dotnet/website/articles/Installation.md index a31998e5d93..59699a957d6 100644 --- a/dotnet/website/articles/Installation.md +++ b/dotnet/website/articles/Installation.md @@ -4,12 +4,6 @@ AutoGen.Net provides the following packages, you can choose to install one or more of them based on your needs: -> [!Note] -> The `AutoGen.DotnetInteractive` has a dependency on `Microsoft.DotNet.Interactive.VisualStudio` which is not available on nuget.org. To restore the dependency, you need to add the following package source to your project: -> ```bash -> https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json -> ``` - - `AutoGen`: The one-in-all package. This package has dependencies over `AutoGen.Core`, `AutoGen.OpenAI`, `AutoGen.LMStudio`, `AutoGen.SemanticKernel` and `AutoGen.SourceGenerator`. - `AutoGen.Core`: The core package, this package provides the abstraction for message type, agent and group chat. - `AutoGen.OpenAI`: This package provides the integration agents over openai models. diff --git a/dotnet/website/articles/Run-dotnet-code.md b/dotnet/website/articles/Run-dotnet-code.md index 2cc247d37e3..e3d8fa78a0b 100644 --- a/dotnet/website/articles/Run-dotnet-code.md +++ b/dotnet/website/articles/Run-dotnet-code.md @@ -19,12 +19,6 @@ For example, in data analysis scenario, agent can resolve tasks like "What is th ## How to run dotnet code snippet? The built-in feature of running dotnet code snippet is provided by [dotnet-interactive](https://github.com/dotnet/interactive). To run dotnet code snippet, you need to install the following package to your project, which provides the intergraion with dotnet-interactive: -> [!Note] -> The `AutoGen.DotnetInteractive` has a dependency on `Microsoft.DotNet.Interactive.VisualStudio` which is not available on nuget.org. To restore the dependency, you need to add the following package source to your project: -> ```bash -> https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json -> ``` - ```xml ```