Skip to content

Commit

Permalink
Merge branch 'microsoft:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
bkrabach authored Jan 2, 2025
2 parents 76c2fb7 + ebb7ede commit bc1c627
Show file tree
Hide file tree
Showing 129 changed files with 8,115 additions and 5,190 deletions.
5 changes: 3 additions & 2 deletions aspire-orchestrator/Aspire.AppHost/Aspire.AppHost.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@
</ItemGroup>

<ItemGroup>
<ProjectReference
Include="..\..\examples\dotnet\dotnet-03-simple-chatbot\dotnet-03-simple-chatbot.csproj" />
<ProjectReference Include="..\..\examples\dotnet\dotnet-01-echo-bot\dotnet-01-echo-bot.csproj" />
<ProjectReference Include="..\..\examples\dotnet\dotnet-02-message-types-demo\dotnet-02-message-types-demo.csproj" />
<ProjectReference Include="..\..\examples\dotnet\dotnet-03-simple-chatbot\dotnet-03-simple-chatbot.csproj" />
<ProjectReference Include="..\Aspire.Extensions\Aspire.Extensions.csproj" IsAspireProjectResource="false" />
</ItemGroup>

Expand Down
149 changes: 114 additions & 35 deletions aspire-orchestrator/Aspire.AppHost/Program.cs
Original file line number Diff line number Diff line change
@@ -1,39 +1,118 @@
// Copyright (c) Microsoft. All rights reserved.

var builder = DistributedApplication.CreateBuilder(args);

var authority = builder.AddParameterFromConfiguration("authority", "EntraId:Authority");
var clientId = builder.AddParameterFromConfiguration("clientId", "EntraId:ClientId");

// Workbench backend
var workbenchService = builder.AddWorkbenchService("workbenchservice", projectDirectory: Path.Combine("..", "..", "workbench-service"), clientId: clientId);
var workbenchServiceEndpoint = workbenchService.GetSemanticWorkbenchEndpoint(builder.ExecutionContext.IsPublishMode);

// Workbench frontend
var workbenchApp = builder.AddViteApp("workbenchapp", workingDirectory: Path.Combine("..", "..", "workbench-app"), packageManager: "pnpm")
.WithPnpmPackageInstallation()
.WithEnvironment(name: "VITE_SEMANTIC_WORKBENCH_SERVICE_URL", workbenchServiceEndpoint)
.WaitFor(workbenchService)
.PublishAsDockerFile([
new DockerBuildArg("VITE_SEMANTIC_WORKBENCH_CLIENT_ID", clientId.Resource.Value),
new DockerBuildArg("VITE_SEMANTIC_WORKBENCH_AUTHORITY", authority.Resource.Value),
]);

// Sample Python agent
builder.AddAssistantApp("skill-assistant", projectDirectory: Path.Combine("..", "..", "assistants", "skill-assistant"), assistantModuleName: "skill-assistant")
.WithEnvironment(name: "assistant__workbench_service_url", workbenchServiceEndpoint);

// Sample .NET agent
var simpleChatBot = builder.AddProject<Projects.dotnet_03_simple_chatbot>("simple-chatbot-dotnet")
.WithHttpEndpoint()
.WaitFor(workbenchService)
.WithEnvironment(name: "Workbench__WorkbenchEndpoint", workbenchServiceEndpoint);

simpleChatBot.WithEnvironment("Workbench__ConnectorHost", $"{simpleChatBot.GetEndpoint("http")}");

if (!builder.ExecutionContext.IsPublishMode)
using Aspire.Hosting.Extensions;
using Projects;

internal static class Program
{
workbenchApp.WithHttpsEndpoint(env: "PORT");
}
public static void Main(string[] args)
{
var builder = DistributedApplication.CreateBuilder(args);

builder
.AddSemanticWorkbench(out IResourceBuilder<ExecutableResource> workbenchBackend, out EndpointReference workbenchEndpoint)
.AddPythonAssistant("skill-assistant", workbenchEndpoint)
.AddDotnetExample<dotnet_03_simple_chatbot>("simple-chatbot-dotnet", workbenchBackend, workbenchEndpoint);

// When running locally
if (!builder.ExecutionContext.IsPublishMode)
{
builder
.AddPythonExample("python-01-echo-bot", workbenchEndpoint)
.AddPythonExample("python-02-simple-chatbot", workbenchEndpoint)
.AddPythonExample("python-03-multimodel-chatbot", workbenchEndpoint)
.AddPythonAssistant("explorer-assistant", workbenchEndpoint)
.AddPythonAssistant("guided-conversation-assistant", workbenchEndpoint)
.AddPythonAssistant("prospector-assistant", workbenchEndpoint)
.AddDotnetExample<dotnet_01_echo_bot>("echo-bot-dotnet", workbenchBackend, workbenchEndpoint)
.AddDotnetExample<dotnet_02_message_types_demo>("sw-tutorial-bot-dotnet", workbenchBackend, workbenchEndpoint);
}

builder
.ShowDashboardUrl(true)
.LaunchDashboard(delay: 5000)
.Build()
.Run();
}

/// <summary>
/// Add the workbench frontend and backend components
/// </summary>
private static IDistributedApplicationBuilder AddSemanticWorkbench(this IDistributedApplicationBuilder builder,
out IResourceBuilder<ExecutableResource> workbenchBackend, out EndpointReference workbenchServiceEndpoint)
{
var authority = builder.AddParameterFromConfiguration("authority", "EntraId:Authority");
var clientId = builder.AddParameterFromConfiguration("clientId", "EntraId:ClientId");

// Workbench backend
workbenchBackend = builder.AddWorkbenchService(
name: "workbenchservice",
projectDirectory: Path.Combine("..", "..", "workbench-service"),
clientId: clientId);

workbenchServiceEndpoint = workbenchBackend.GetSemanticWorkbenchEndpoint(builder.ExecutionContext.IsPublishMode);

// Workbench frontend
var workbenchApp = builder.AddViteApp(
name: "workbenchapp",
workingDirectory: Path.Combine("..", "..", "workbench-app"),
packageManager: "pnpm")
.WithPnpmPackageInstallation()
.WithEnvironment(name: "VITE_SEMANTIC_WORKBENCH_SERVICE_URL", workbenchServiceEndpoint)
.WaitFor(workbenchBackend)
.PublishAsDockerFile([
new DockerBuildArg("VITE_SEMANTIC_WORKBENCH_CLIENT_ID", clientId.Resource.Value),
new DockerBuildArg("VITE_SEMANTIC_WORKBENCH_AUTHORITY", authority.Resource.Value),
]);

builder.Build().Run();
// When running locally
if (!builder.ExecutionContext.IsPublishMode)
{
if (!int.TryParse(builder.Configuration["Workbench:AppPort"], out var appPort)) { appPort = 4000; }
workbenchApp.WithHttpsEndpoint(port: appPort, env: "PORT", isProxied: false);
}

return builder;
}

private static IDistributedApplicationBuilder AddPythonAssistant(this IDistributedApplicationBuilder builder,
string name, EndpointReference workbenchServiceEndpoint)
{
builder
.AddAssistantUvPythonApp(
name: name,
projectDirectory: Path.Combine("..", "..", "assistants", name),
assistantModuleName: name)
.WithEnvironment(name: "assistant__workbench_service_url", workbenchServiceEndpoint);

return builder;
}

private static IDistributedApplicationBuilder AddPythonExample(this IDistributedApplicationBuilder builder,
string name, EndpointReference workbenchServiceEndpoint)
{
builder
.AddAssistantUvPythonApp(
name: name,
projectDirectory: Path.Combine("..", "..", "examples", "python", name),
assistantModuleName: "assistant")
.WithEnvironment(name: "assistant__workbench_service_url", workbenchServiceEndpoint);

return builder;
}

// .NET Agent Example 1
private static IDistributedApplicationBuilder AddDotnetExample<T>(this IDistributedApplicationBuilder builder,
string name, IResourceBuilder<ExecutableResource> workbenchBackend, EndpointReference workbenchServiceEndpoint) where T : IProjectMetadata, new()
{
var agent = builder
.AddProject<T>(name: name)
.WithHttpEndpoint()
.WaitFor(workbenchBackend)
.WithEnvironment(name: "Workbench__WorkbenchEndpoint", workbenchServiceEndpoint);

agent.WithEnvironment("Workbench__ConnectorHost", $"{agent.GetEndpoint("http")}");

return builder;
}
}
10 changes: 8 additions & 2 deletions aspire-orchestrator/Aspire.AppHost/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,14 @@
"Aspire.Hosting.Dcp": "Warning"
}
},
"Workbench": {
// Used only when running Aspire locally, ie not in the cloud
// Port 4000 is the default port used also when not using Aspire.
// Using the same port allows to keep cookies and other local settings.
"AppPort": 4000
},
"EntraID": {
"ClientId": "22cb77c3-ca98-4a26-b4db-ac4dcecba690",
"Authority": "https://login.microsoftonline.com/common"
"Authority": "https://login.microsoftonline.com/common",
"ClientId": "22cb77c3-ca98-4a26-b4db-ac4dcecba690"
}
}
40 changes: 40 additions & 0 deletions aspire-orchestrator/Aspire.Extensions/Dashboard.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright (c) Microsoft. All rights reserved.

using System.Diagnostics;

namespace Aspire.Hosting.Extensions;

public static class Dashboard
{
/// <summary>
/// Show Aspire dashboard URL before the logging start.
/// </summary>
public static IDistributedApplicationBuilder ShowDashboardUrl(this IDistributedApplicationBuilder builder, bool withStyle = false)
{
Console.WriteLine(withStyle
? $"\u001b[1mAspire dashboard URL: {GetUrl(builder)}\u001b[0m\n\n"
: $"Aspire dashboard URL: {GetUrl(builder)}\n\n");
return builder;
}

/// <summary>
/// Wait 5 seconds and automatically open the browser (when using 'dotnet run' the browser doesn't open)
/// </summary>
public static IDistributedApplicationBuilder LaunchDashboard(this IDistributedApplicationBuilder builder, int delay = 5000)
{
Task.Run(async () =>
{
await Task.Delay(delay).ConfigureAwait(false);
Process.Start(new ProcessStartInfo { FileName = GetUrl(builder), UseShellExecute = true });
});

return builder;
}

private static string GetUrl(IDistributedApplicationBuilder builder)
{
string token = builder.Configuration["AppHost:BrowserToken"] ?? string.Empty;
string url = builder.Configuration["ASPNETCORE_URLS"]?.Split(";")[0] ?? throw new ArgumentException("ASPNETCORE_URLS is empty");
return $"{url}/login?t={token}";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public static IResourceBuilder<UvAppResource> AddUvApp(
private static IResourceBuilder<UvAppResource> AddUvApp(this IDistributedApplicationBuilder builder,
string name,
string scriptPath,
string projectDirectory,
string? projectDirectory,
string virtualEnvironmentPath,
params string[] args)
{
Expand All @@ -32,7 +32,7 @@ private static IResourceBuilder<UvAppResource> AddUvApp(this IDistributedApplica

string wd = projectDirectory ?? Path.Combine("..", name);

projectDirectory = PathNormalizer.NormalizePathForCurrentPlatform(Path.Combine(builder.AppHostDirectory, wd));
projectDirectory = Path.Combine(builder.AppHostDirectory, wd).NormalizePathForCurrentPlatform();

var virtualEnvironment = new VirtualEnvironment(Path.IsPathRooted(virtualEnvironmentPath)
? virtualEnvironmentPath
Expand All @@ -43,8 +43,8 @@ private static IResourceBuilder<UvAppResource> AddUvApp(this IDistributedApplica
// var projectExecutable = instrumentationExecutable ?? pythonExecutable;

string[] allArgs = args is { Length: > 0 }
? ["run", scriptPath, .. args]
: ["run", scriptPath];
? ["run", "--frozen", scriptPath, .. args]
: ["run", "--frozen", scriptPath];

var projectResource = new UvAppResource(name, projectDirectory);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,22 @@ public static IResourceBuilder<ExecutableResource> AddWorkbenchService(
{
ArgumentNullException.ThrowIfNull(builder);

var workbenchService = builder.AddUvApp(name, projectDirectory, "start-semantic-workbench-service", scriptArgs)
var workbenchService = builder
.AddUvApp(name, projectDirectory, "start-semantic-workbench-service", scriptArgs)
.PublishAsDockerImage(dockerContext: Path.Combine("..", ".."),
dockerFilePath: Path.Combine("workbench-service", "Dockerfile"),
configure: new(configure => configure
.WithBuildArg("SSHD_ENABLED", "false")))
.WithEnvironment(name: "WORKBENCH__AUTH__ALLOWED_APP_ID", clientId.Resource.Value);

if (builder.ExecutionContext.IsPublishMode)
{
// When running on Azure
workbenchService.WithHttpsEndpoint(port: 3000);
}
else
{
// When running locally
workbenchService.WithHttpEndpoint(env: "PORT");
}

Expand All @@ -40,27 +44,31 @@ public static EndpointReference GetSemanticWorkbenchEndpoint(this IResourceBuild
return workbenchService.GetEndpoint(isPublishMode ? "https" : "http");
}

public static IResourceBuilder<ExecutableResource> AddAssistantApp(
public static IResourceBuilder<ExecutableResource> AddAssistantUvPythonApp(
this IDistributedApplicationBuilder builder,
string name,
string projectDirectory,
string assistantModuleName)
{
ArgumentNullException.ThrowIfNull(builder);

var assistant = builder.AddUvApp(name, projectDirectory, "start-assistant")
var assistant = builder
.AddUvApp(name, projectDirectory, "start-assistant")
.PublishAsDockerImage(dockerContext: Path.Combine("..", ".."),
dockerFilePath: Path.Combine("tools", "docker", "Dockerfile.assistant"),
configure: new(configure => configure
.WithBuildArg("package", assistantModuleName)
.WithBuildArg("app", $"assistant.{assistantModuleName.Replace('-', '_')}:app")
));

if (builder.ExecutionContext.IsPublishMode)
{
// When running on Azure
assistant.WithHttpEndpoint(port: 3001, env: "ASSISTANT__PORT");
}
else
{
// When running locally
assistant.WithHttpEndpoint(env: "ASSISTANT__PORT");
}

Expand Down
Loading

0 comments on commit bc1c627

Please sign in to comment.