Skip to content

.Net: Add Sessions (Code Interpreter) Core Plugin and Demo Project #6160

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

Merged
Merged
Show file tree
Hide file tree
Changes from 6 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
2 changes: 1 addition & 1 deletion dotnet/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<PackageVersion Include="Azure.AI.ContentSafety" Version="1.0.0" />
<PackageVersion Include="Azure.AI.OpenAI" Version="1.0.0-beta.15" />
<PackageVersion Include="Azure.AI.OpenAI.Assistants" Version="1.0.0-beta.3" />
<PackageVersion Include="Azure.Identity" Version="1.11.2" />
<PackageVersion Include="Azure.Identity" Version="1.11.3" />
<PackageVersion Include="Azure.Monitor.OpenTelemetry.Exporter" Version="1.2.0" />
<PackageVersion Include="Azure.Search.Documents" Version="11.5.1" />
<PackageVersion Include="Handlebars.Net.Helpers" Version="2.4.1.5" />
Expand Down
14 changes: 13 additions & 1 deletion dotnet/SK-dotnet.sln
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,9 @@ EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Agents.OpenAI", "src\Agents\OpenAI\Agents.OpenAI.csproj", "{644A2F10-324D-429E-A1A3-887EAE64207F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Demos", "Demos", "{5D4C0700-BBB5-418F-A7B2-F392B9A18263}"
ProjectSection(SolutionItems) = preProject
samples\Demos\README.md = samples\Demos\README.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LearnResources", "samples\LearnResources\LearnResources.csproj", "{B04C26BC-A933-4A53-BE17-7875EB12E012}"
EndProject
Expand Down Expand Up @@ -287,7 +290,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ContentSafety", "samples\De
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Concepts", "samples\Concepts\Concepts.csproj", "{925B1185-8B58-4E2D-95C9-4CA0BA9364E5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FunctionInvocationApproval", "samples\Demos\FunctionInvocationApproval\FunctionInvocationApproval.csproj", "{6B56D8EE-9991-43E3-90B2-B8F5C5CE77C2}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FunctionInvocationApproval", "samples\Demos\FunctionInvocationApproval\FunctionInvocationApproval.csproj", "{6B56D8EE-9991-43E3-90B2-B8F5C5CE77C2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodeInterpreterPlugin", "samples\Demos\CodeInterpreterPlugin\CodeInterpreterPlugin.csproj", "{3ED53702-0E53-473A-A0F4-645DB33541C2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -680,6 +685,12 @@ Global
{6B56D8EE-9991-43E3-90B2-B8F5C5CE77C2}.Publish|Any CPU.Build.0 = Debug|Any CPU
{6B56D8EE-9991-43E3-90B2-B8F5C5CE77C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6B56D8EE-9991-43E3-90B2-B8F5C5CE77C2}.Release|Any CPU.Build.0 = Release|Any CPU
{3ED53702-0E53-473A-A0F4-645DB33541C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3ED53702-0E53-473A-A0F4-645DB33541C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3ED53702-0E53-473A-A0F4-645DB33541C2}.Publish|Any CPU.ActiveCfg = Debug|Any CPU
{3ED53702-0E53-473A-A0F4-645DB33541C2}.Publish|Any CPU.Build.0 = Debug|Any CPU
{3ED53702-0E53-473A-A0F4-645DB33541C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3ED53702-0E53-473A-A0F4-645DB33541C2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -775,6 +786,7 @@ Global
{6EF9663D-976C-4A27-B8D3-8B1E63BA3BF2} = {5D4C0700-BBB5-418F-A7B2-F392B9A18263}
{925B1185-8B58-4E2D-95C9-4CA0BA9364E5} = {FA3720F1-C99A-49B2-9577-A940257098BF}
{6B56D8EE-9991-43E3-90B2-B8F5C5CE77C2} = {5D4C0700-BBB5-418F-A7B2-F392B9A18263}
{3ED53702-0E53-473A-A0F4-645DB33541C2} = {5D4C0700-BBB5-418F-A7B2-F392B9A18263}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {FBDC56A3-86AD-4323-AA0F-201E59123B83}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<UserSecretsId>5ee045b0-aea3-4f08-8d31-32d1a6f8fed0</UserSecretsId>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Azure.Identity" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
<PackageReference Include="Microsoft.Extensions.Http" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\src\Connectors\Connectors.OpenAI\Connectors.OpenAI.csproj" />
<ProjectReference Include="..\..\..\src\Plugins\Plugins.Core\Plugins.Core.csproj" />
<ProjectReference Include="..\..\..\src\SemanticKernel.Abstractions\SemanticKernel.Abstractions.csproj" />
<ProjectReference Include="..\..\..\src\SemanticKernel.Core\SemanticKernel.Core.csproj" />
</ItemGroup>
</Project>
108 changes: 108 additions & 0 deletions dotnet/samples/Demos/CodeInterpreterPlugin/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Copyright (c) Microsoft. All rights reserved.

using System.Text;
using Azure.Identity;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Microsoft.SemanticKernel.Plugins.Core.CodeInterpreter;

#pragma warning disable SKEXP0050 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.

var configuration = new ConfigurationBuilder()
.AddUserSecrets<Program>()
.AddEnvironmentVariables()
.Build();

var apiKey = configuration["OpenAI:ApiKey"];
var modelId = configuration["OpenAI:ChatModelId"];
var endpoint = configuration["AzureContainerApps:Endpoint"];

// Cached token for the Azure Container Apps service
string? cachedToken = null;

// Logger for program scope
ILogger logger = NullLogger.Instance;

ArgumentNullException.ThrowIfNull(apiKey);
ArgumentNullException.ThrowIfNull(modelId);
ArgumentNullException.ThrowIfNull(endpoint);

/// <summary>
/// Acquire a token for the Azure Container Apps service
/// </summary>
async Task<string> TokenProvider()
{
if (cachedToken is null)
{
string resource = "https://acasessions.io/.default";
var credential = new InteractiveBrowserCredential();

// Attempt to get the token
var accessToken = await credential.GetTokenAsync(new Azure.Core.TokenRequestContext([resource])).ConfigureAwait(false);
if (logger.IsEnabled(LogLevel.Information))
{
logger.LogInformation("Access token obtained successfully");
}
cachedToken = accessToken.Token;
}

return cachedToken;
}

var settings = new SessionPythonSettings()
{
Endpoint = new Uri(endpoint),
SessionId = Guid.NewGuid().ToString()
};

Console.WriteLine("=== Code Interpreter With Azure Container Apps Plugin Demo ===");

var builder =
Kernel.CreateBuilder()
.AddOpenAIChatCompletion(modelId, apiKey);

// Change the log level to Trace to see more detailed logs
builder.Services.AddLogging(loggingBuilder => loggingBuilder.AddConsole().SetMinimumLevel(LogLevel.Information));
builder.Services.AddHttpClient();
builder.Services.AddSingleton((sp)
=> new SessionsPythonPlugin(
settings,
TokenProvider,
sp.GetRequiredService<IHttpClientFactory>(),
sp.GetRequiredService<ILoggerFactory>()));
var kernel = builder.Build();

logger = kernel.GetRequiredService<ILoggerFactory>().CreateLogger<Program>();
kernel.Plugins.AddFromObject(kernel.GetRequiredService<SessionsPythonPlugin>());
var chatCompletion = kernel.GetRequiredService<IChatCompletionService>();

var chatHistory = new ChatHistory();

StringBuilder fullAssistantContent = new();

do
{
Console.Write("\nUser: ");
var input = Console.ReadLine();
if (string.IsNullOrWhiteSpace(input)) { break; }

chatHistory.AddUserMessage(input);

Console.WriteLine("Assistant: ");
fullAssistantContent.Clear();
await foreach (var content in chatCompletion.GetStreamingChatMessageContentsAsync(
chatHistory,
new OpenAIPromptExecutionSettings { ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions },
kernel)
.ConfigureAwait(false))
{
Console.Write(content.Content);
fullAssistantContent.Append(content.Content);
}
chatHistory.AddAssistantMessage(fullAssistantContent.ToString());
} while (true);
33 changes: 33 additions & 0 deletions dotnet/samples/Demos/CodeInterpreterPlugin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Semantic Kernel - Code Interpreter Plugin with Azure Container Apps

This example demonstrates how to do AI Code Interpretetion using a Plugin with Azure Container Apps to execute python code in a container.

## Configuring Secrets

The example require credentials to access OpenAI and Azure Container Apps (ACA)

If you have set up those credentials as secrets within Secret Manager or through environment variables for other samples from the solution in which this project is found, they will be re-used.

### To set your secrets with Secret Manager:

```
dotnet user-secrets init

dotnet user-secrets set "OpenAI:ApiKey" "..."
dotnet user-secrets set "OpenAI:ChatModelId" "gpt-3.5-turbo" # or any other function callable model.

dotnet user-secrets set "AzureContainerApps:Endpoint" " .. endpoint .. "
```

### To set your secrets with environment variables

Use these names:

```
# OpenAI
OpenAI__ApiKey
OpenAI__ChatModelId

# Azure Container Apps
AzureContainerApps__Endpoint
```
3 changes: 2 additions & 1 deletion dotnet/samples/Demos/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ Demonstration applications that leverage the usage of one or many SK features
| Create Chat GPT Plugin | A simple plugin that uses OpenAI GPT-3 to chat |
| Home Automation | This example demonstrates a few dependency injection patterns that can be used with Semantic Kernel. |
| HuggingFace Image to Text | In this demonstration the application uses Semantic Kernel's HuggingFace ImageToText Service to fetch a descriptive analysis of the clicked image. |
| Telemetry With Application Insights | Demo on how an application can be configured to send Semantic Kernel telemetry to Application Insights. |
| Telemetry With Application Insights | Demo on how an application can be configured to send Semantic Kernel telemetry to Application Insights. |
| Code Interpreter Plugin | A plugin that leverages Azure Container Apps service to execute python code. |
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using System.ComponentModel;
using System.Text.Json.Serialization;

namespace Microsoft.SemanticKernel.Plugins.Core.CodeInterpreter;

/// <summary>
/// Settings for a Python session.
/// </summary>
public class SessionPythonSettings
{
/// <summary>
/// Determines if the input should be sanitized.
/// </summary>
[JsonIgnore]
public bool SanitizeInput { get; set; }

/// <summary>
/// The target endpoint.
/// </summary>
[JsonIgnore]
public Uri? Endpoint { get; init; }

/// <summary>
/// The session identifier.
/// </summary>
[JsonPropertyName("identifier")]
public string? SessionId { get; init; }

/// <summary>
/// Code input type.
/// </summary>
[JsonPropertyName("codeInputType")]
public CodeInputTypeSetting CodeInputType { get; set; }

/// <summary>
/// Code execution type.
/// </summary>
[JsonPropertyName("executionType")]
public CodeExecutionTypeSetting CodeExecutionType { get; set; }

/// <summary>
/// Timeout in seconds for the code execution.
/// </summary>
[JsonPropertyName("timeoutInSeconds")]
public int TimeoutInSeconds { get; set; } = 100;

/// <summary>
/// The Python code to execute.
/// </summary>
[JsonPropertyName("pythonCode")]
public string? PythonCode { get; set; }

/// <summary>
/// Code input type.
/// </summary>
[Description("Code input type.")]
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum CodeInputTypeSetting
{
/// <summary>
/// Code is provided as a inline string.
/// </summary>
[Description("Code is provided as a inline string.")]
[JsonPropertyName("inline")]
Inline
}

/// <summary>
/// Code input type.
/// </summary>
[Description("Code input type.")]
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum CodeExecutionTypeSetting
{
/// <summary>
/// Code is provided as a inline string.
/// </summary>
[Description("Code is provided as a inline string.")]
[JsonPropertyName("synchronous")]
Synchronous
}

internal SessionPythonSettings CloneForRequest(string pythonCode)
{
return new SessionPythonSettings
{
SanitizeInput = this.SanitizeInput,
SessionId = this.SessionId,
CodeInputType = this.CodeInputType,
CodeExecutionType = this.CodeExecutionType,
TimeoutInSeconds = this.TimeoutInSeconds,
PythonCode = pythonCode
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using System.ComponentModel;
using System.Text.Json.Serialization;

namespace Microsoft.SemanticKernel.Plugins.Core.CodeInterpreter;

/// <summary>
/// Metadata for a file in the session.
/// </summary>
public class SessionRemoteFileMetadata
{
/// <summary>
/// Initializes a new instance of the SessionRemoteFileMetadata class.
/// </summary>
[JsonConstructor]
public SessionRemoteFileMetadata(string filename, int size)
{
this.Filename = filename;
this.Size = size;
}

/// <summary>
/// The filename relative to `/mnt/data`.
/// </summary>
[Description("The filename relative to `/mnt/data`.")]
[JsonPropertyName("filename")]
public string Filename { get; set; }

/// <summary>
/// The size of the file in bytes.
/// </summary>
[Description("The size of the file in bytes.")]
[JsonPropertyName("size")]
public int Size { get; set; }

/// <summary>
/// The last modified time.
/// </summary>
[Description("Last modified time.")]
[JsonPropertyName("last_modified_time")]
public DateTime? LastModifiedTime { get; set; }

/// <summary>
/// The full path of the file.
/// </summary>
[Description("The full path of the file.")]
public string FullPath => $"/mnt/data/{this.Filename}";
}
Loading
Loading