diff --git a/dotnet/Directory.Packages.props b/dotnet/Directory.Packages.props index 75106b5fcb..41fd7e9ef3 100644 --- a/dotnet/Directory.Packages.props +++ b/dotnet/Directory.Packages.props @@ -112,6 +112,7 @@ + diff --git a/dotnet/src/Microsoft.Agents.AI.Hyperlight/HyperlightCodeActProvider.cs b/dotnet/src/Microsoft.Agents.AI.Hyperlight/HyperlightCodeActProvider.cs index 3065a0a893..e70a25033f 100644 --- a/dotnet/src/Microsoft.Agents.AI.Hyperlight/HyperlightCodeActProvider.cs +++ b/dotnet/src/Microsoft.Agents.AI.Hyperlight/HyperlightCodeActProvider.cs @@ -68,8 +68,9 @@ public sealed class HyperlightCodeActProvider : AIContextProvider, IDisposable /// Optional configuration options for the provider. When the provider /// uses the defaults of (the /// backend with no tools, mounts, or allow-list entries). - /// Use to target a Wasm - /// guest module instead. + /// Use for the bundled Python + /// guest module, or for a + /// custom Wasm guest. /// public HyperlightCodeActProvider(HyperlightCodeActProviderOptions? options = null) { diff --git a/dotnet/src/Microsoft.Agents.AI.Hyperlight/HyperlightCodeActProviderOptions.cs b/dotnet/src/Microsoft.Agents.AI.Hyperlight/HyperlightCodeActProviderOptions.cs index 93e5a09c39..32f61cbf7b 100644 --- a/dotnet/src/Microsoft.Agents.AI.Hyperlight/HyperlightCodeActProviderOptions.cs +++ b/dotnet/src/Microsoft.Agents.AI.Hyperlight/HyperlightCodeActProviderOptions.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using HyperlightSandbox.Api; +using HyperlightSandbox.Guest.Python; using Microsoft.Extensions.AI; using Microsoft.Shared.Diagnostics; @@ -12,8 +13,9 @@ namespace Microsoft.Agents.AI.Hyperlight; /// . /// /// -/// Use the and -/// factory methods to construct an instance with the desired sandbox backend. +/// Use the , , +/// and factory methods to construct an instance +/// with the desired sandbox backend. /// The parameterless constructor is equivalent to . /// public sealed class HyperlightCodeActProviderOptions @@ -34,7 +36,17 @@ private HyperlightCodeActProviderOptions(SandboxBackend backend, string? moduleP } /// - /// Creates options targeting the backend. + /// Creates options targeting the backend + /// with the bundled Python guest module from the + /// Hyperlight.HyperlightSandbox.Guest.Python NuGet package. + /// No explicit module path needs to be provided by the caller. + /// + public static HyperlightCodeActProviderOptions CreateForPython() + => new(SandboxBackend.Wasm, PythonGuestModule.GetModulePath()); + + /// + /// Creates options targeting the backend + /// with a custom guest module. /// /// Path to the guest module (.wasm or .aot file). public static HyperlightCodeActProviderOptions CreateForWasm(string modulePath) @@ -53,7 +65,8 @@ public static HyperlightCodeActProviderOptions CreateForJavaScript() /// /// Gets the path to the guest module. Set when the options were created via - /// ; otherwise. + /// or ; + /// when using the built-in JavaScript backend. /// public string? ModulePath { get; } diff --git a/dotnet/src/Microsoft.Agents.AI.Hyperlight/Microsoft.Agents.AI.Hyperlight.csproj b/dotnet/src/Microsoft.Agents.AI.Hyperlight/Microsoft.Agents.AI.Hyperlight.csproj index a6fd3d9c9e..af7800c0c6 100644 --- a/dotnet/src/Microsoft.Agents.AI.Hyperlight/Microsoft.Agents.AI.Hyperlight.csproj +++ b/dotnet/src/Microsoft.Agents.AI.Hyperlight/Microsoft.Agents.AI.Hyperlight.csproj @@ -13,6 +13,7 @@ + diff --git a/dotnet/src/Microsoft.Agents.AI.Hyperlight/README.md b/dotnet/src/Microsoft.Agents.AI.Hyperlight/README.md index 5f6efdb0f6..f875443560 100644 --- a/dotnet/src/Microsoft.Agents.AI.Hyperlight/README.md +++ b/dotnet/src/Microsoft.Agents.AI.Hyperlight/README.md @@ -25,15 +25,46 @@ Both surfaces support: * Snapshot/restore per run so the guest starts from a known clean state every invocation. +## Quick Start — Bundled Python Guest + +The simplest way to get started is with the bundled Python guest module. +This package includes +[`Hyperlight.HyperlightSandbox.Guest.Python`](https://www.nuget.org/packages/Hyperlight.HyperlightSandbox.Guest.Python), +so no separate module path is needed: + +```csharp +using Microsoft.Agents.AI.Hyperlight; + +// Use the bundled Python guest — no module path required +var options = HyperlightCodeActProviderOptions.CreateForPython(); +using var provider = new HyperlightCodeActProvider(options); +``` + +Or with the standalone function: + +```csharp +using var executeCode = new HyperlightExecuteCodeFunction( + HyperlightCodeActProviderOptions.CreateForPython()); +``` + +## Other Backends + +```csharp +// Built-in JavaScript (QuickJS) — default +var jsOptions = HyperlightCodeActProviderOptions.CreateForJavaScript(); + +// Custom Wasm guest module. See the Hyperlight Sandbox docs for creating a custom Wasm module. +var wasmOptions = HyperlightCodeActProviderOptions.CreateForWasm("/path/to/guest.aot"); +``` + ## Requirements -* The `Hyperlight.HyperlightSandbox.Api` NuGet package, published from the - `src/sdk/dotnet` SDK in [hyperlight-dev/hyperlight-sandbox](https://github.com/hyperlight-dev/hyperlight-sandbox) - (the .NET API was added in [PR #46](https://github.com/hyperlight-dev/hyperlight-sandbox/pull/46), - now merged). Until the package is published to nuget.org the project - restore will fail; this project is intentionally `IsPackable=false` in - the meantime. -* A Hyperlight Python guest module when using `SandboxBackend.Wasm`. +* The [`Hyperlight.HyperlightSandbox.Api`](https://www.nuget.org/packages/Hyperlight.HyperlightSandbox.Api) + and [`Hyperlight.HyperlightSandbox.Guest.Python`](https://www.nuget.org/packages/Hyperlight.HyperlightSandbox.Guest.Python) + NuGet packages (included as dependencies). +* A hypervisor: [KVM](https://help.ubuntu.com/community/KVM/Installation) (Linux), + [MSHV](https://github.com/rust-vmm/mshv), or + [Hyper-V](https://learn.microsoft.com/en-us/windows-server/virtualization/hyper-v/get-started/Install-Hyper-V) (Windows). ## Status diff --git a/dotnet/tests/Microsoft.Agents.AI.Hyperlight.UnitTests/HyperlightCodeActProviderTests.cs b/dotnet/tests/Microsoft.Agents.AI.Hyperlight.UnitTests/HyperlightCodeActProviderTests.cs index eb8d941ea5..7fae5746e2 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Hyperlight.UnitTests/HyperlightCodeActProviderTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Hyperlight.UnitTests/HyperlightCodeActProviderTests.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System.IO; using System.Linq; using Microsoft.Extensions.AI; @@ -170,4 +171,71 @@ public void Dispose_IsIdempotentAndBlocksFurtherAddTools() // Assert Assert.Throws(() => provider.AddTools(tool)); } + + [Fact] + public void CreateForPython_SetsWasmBackendAndBundledGuest() + { + // Act + var options = HyperlightCodeActProviderOptions.CreateForPython(); + + // Assert + Assert.Equal(HyperlightSandbox.Api.SandboxBackend.Wasm, options.Backend); + Assert.False(string.IsNullOrWhiteSpace(options.ModulePath)); + Assert.False(string.IsNullOrWhiteSpace(Path.GetFileName(options.ModulePath))); + } + + [Fact] + public void CreateForWasm_SetsWasmBackendAndCustomPath() + { + // Arrange + var modulePath = Path.Combine("path", "to", "guest.aot"); + + // Act + var options = HyperlightCodeActProviderOptions.CreateForWasm(modulePath); + + // Assert + Assert.Equal(HyperlightSandbox.Api.SandboxBackend.Wasm, options.Backend); + Assert.Equal(modulePath, options.ModulePath); + } + + [Fact] + public void CreateForJavaScript_SetsJavaScriptBackend() + { + // Act + var options = HyperlightCodeActProviderOptions.CreateForJavaScript(); + + // Assert + Assert.Equal(HyperlightSandbox.Api.SandboxBackend.JavaScript, options.Backend); + Assert.Null(options.ModulePath); + } + + [Fact] + public void DefaultCtor_EquivalentToCreateForJavaScript() + { + // Act + var options = new HyperlightCodeActProviderOptions(); + + // Assert + Assert.Equal(HyperlightSandbox.Api.SandboxBackend.JavaScript, options.Backend); + Assert.Null(options.ModulePath); + } + + [Fact] + public void Ctor_WithPythonOptions_SeedsFromOptions() + { + // Arrange + var tool = AIFunctionFactory.Create(() => "x", name: "x"); + var options = HyperlightCodeActProviderOptions.CreateForPython(); + options.Tools = new[] { tool }; + options.FileMounts = new[] { new FileMount("/h", "/m") }; + options.AllowedDomains = new[] { new AllowedDomain("https://a") }; + + // Act + using var provider = new HyperlightCodeActProvider(options); + + // Assert + Assert.Single(provider.GetTools()); + Assert.Single(provider.GetFileMounts()); + Assert.Single(provider.GetAllowedDomains()); + } }