Skip to content

Commit

Permalink
Added OpenAI.Plugin files and configurations
Browse files Browse the repository at this point in the history
  • Loading branch information
cmendible committed Apr 10, 2024
1 parent 8ddc512 commit 6e528aa
Show file tree
Hide file tree
Showing 27 changed files with 537 additions and 22 deletions.
16 changes: 16 additions & 0 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
DOCS_IMAGE_NAME: aihub-prepdocs
PLUGIN_IMAGE_NAME: aihub-plugin
AIHUB_ARTIFACT_NAME: aihub-tf-module

jobs:
Expand Down Expand Up @@ -105,6 +106,21 @@ jobs:
tags: ${{ env.REGISTRY }}/${{ env.GITHUB_REPOSITORY_LOWER_CASE }}/${{ env.DOCS_IMAGE_NAME }}:${{ env.MINVERVERSIONOVERRIDE }}
labels: ${{ steps.meta-docs.outputs.labels }}

- name: Extract metadata (tags, labels) for plugin Docker
id: meta-plugin
uses: docker/metadata-action@v3
with:
images: ${{ env.REGISTRY }}/${{ env.PLUGIN_IMAGE_NAME }}

- name: Build and push Docker image for plugin
uses: docker/build-push-action@v3
with:
context: ./src/OpenAI.Plugin/
file: ./src/OpenAI.Plugin/Dockerfile
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ env.REGISTRY }}/${{ env.GITHUB_REPOSITORY_LOWER_CASE }}/${{ env.PLUGIN_IMAGE_NAME }}:${{ env.MINVERVERSIONOVERRIDE }}
labels: ${{ steps.meta-plugin.outputs.labels }}

- name: Upload Artifacts
uses: actions/upload-artifact@v3
with:
Expand Down
19 changes: 0 additions & 19 deletions infra/.terraform.lock.hcl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions infra/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ locals {
ca_chat_name = "${var.ca_chat_name}${local.name_sufix}"
ca_prep_docs_name = "${var.ca_prep_docs_name}${local.name_sufix}"
ca_aihub_name = "${var.ca_aihub_name}${local.name_sufix}"
func_name = "plugin${local.sufix}"
}

resource "azurerm_resource_group" "rg" {
Expand Down Expand Up @@ -229,3 +230,18 @@ module "ca_aihub" {
enable_entra_id_authentication = var.enable_entra_id_authentication
image_name = var.ca_aihub_image
}

module "plugin" {
source = "./modules/ca-plugin"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
resource_group_id = azurerm_resource_group.rg.id
func_name = local.func_name
image_name = var.ca_plugin_image
cae_id = module.cae.cae_id
cae_default_domain = module.cae.default_domain
appi_instrumentation_key = module.appi.appi_key
openai_key = module.openai.openai_key
openai_model = module.openai.gpt_deployment_name
openai_endpoint = module.openai.openai_endpoint
}
74 changes: 74 additions & 0 deletions infra/modules/ca-plugin/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
resource "azurerm_storage_account" "sa" {
name = "stfunc${var.func_name}"
location = var.location
resource_group_name = var.resource_group_name
account_tier = "Standard"
account_replication_type = "LRS"
enable_https_traffic_only = true
}

resource "azapi_resource" "ca_function" {
schema_validation_enabled = false
name = "func-${var.func_name}"
location = var.location
parent_id = var.resource_group_id
type = "Microsoft.Web/sites@2023-01-01"
body = jsonencode({
kind = "functionapp,linux,container,azurecontainerapps"
properties : {
language = "dotnet-isolated"
managedEnvironmentId = "${var.cae_id}"
siteConfig = {
linuxFxVersion = "DOCKER|cmendibl3/aoai-plugin:0.8.0"
appSettings = [
{
name = "AzureWebJobsStorage"
value = azurerm_storage_account.sa.primary_connection_string
},
{
name = "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING"
value = azurerm_storage_account.sa.primary_connection_string
},
{
name = "APPINSIGHTS_INSTRUMENTATIONKEY"
value = var.appi_instrumentation_key
},
{
name = "APPLICATIONINSIGHTS_CONNECTION_STRING"
value = "InstrumentationKey=${var.appi_instrumentation_key}"
},
{
name = "FUNCTIONS_WORKER_RUNTIME"
value = "dotnet-isolated"
},
{
name = "FUNCTIONS_EXTENSION_VERSION"
value = "~4"
},
{
name = "MODEL_ID"
value = var.openai_model
},
{
name = "API_KEY"
value = var.openai_key
},
{
name = "ENDPOINT"
value = var.openai_endpoint
},
{
name = "OpenApi__HostNames"
value = "https://func-${var.func_name}.${var.cae_default_domain}/api"
}
]
}
workloadProfileName = "Consumption"
resourceConfig = {
cpu = 1
memory = "2Gi"
}
httpsOnly = false
}
})
}
Empty file.
20 changes: 20 additions & 0 deletions infra/modules/ca-plugin/providers.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
terraform {
required_version = ">= 1.1.8"
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "3.87.0"
}
azapi = {
source = "Azure/azapi"
}
}
}

provider "azurerm" {
features {
cognitive_account {
purge_soft_delete_on_destroy = true
}
}
}
11 changes: 11 additions & 0 deletions infra/modules/ca-plugin/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
variable "resource_group_name" {}
variable "resource_group_id" {}
variable "location" {}
variable "func_name" {}
variable "image_name" {}
variable "cae_id" {}
variable "cae_default_domain" {}
variable "appi_instrumentation_key" {}
variable "openai_key" {}
variable "openai_model" {}
variable "openai_endpoint" {}
2 changes: 1 addition & 1 deletion infra/modules/cae/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ output "cae_id" {
value = azapi_resource.cae.id
}

output "defaultDomain" {
output "default_domain" {
value = jsondecode(azapi_resource.cae.output).properties.defaultDomain
}

4 changes: 4 additions & 0 deletions infra/modules/openai/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,7 @@ output "gpt4_deployment_model_name" {
output "embedding_deployment_name" {
value = azurerm_cognitive_deployment.embedding.name
}

output "openai_key" {
value = azurerm_cognitive_account.openai.primary_access_key
}
8 changes: 6 additions & 2 deletions infra/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,15 @@ variable "ca_chat_image" {
}

variable "ca_prep_docs_image" {
default = "ghcr.io/azure/aihub/aihub-prepdocs:1.0.6"
default = "ghcr.io/azure/aihub/aihub-prepdocs:1.0.8"
}

variable "ca_plugin_image" {
default = "ghcr.io/azure/aihub/aihub-plugin:1.0.8"
}

variable "ca_aihub_image" {
default = "ghcr.io/azure/aihub/aihub:1.0.6"
default = "ghcr.io/azure/aihub/aihub:1.0.8"
}

variable "use_random_suffix" {
Expand Down
1 change: 1 addition & 0 deletions src/OpenAI.Plugin/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
local.settings.json
23 changes: 23 additions & 0 deletions src/OpenAI.Plugin/AIPluginJson.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
public class AIPluginJson
{
[Function("GetAIPluginJson")]
public HttpResponseData Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = ".well-known/ai-plugin.json")] HttpRequestData req)
{
var currentDomain = $"{req.Url.Scheme}://{req.Url.Host}:{req.Url.Port}/api";

HttpResponseData response = req.CreateResponse(HttpStatusCode.OK);
response.Headers.Add("Content-Type", "application/json");

var settings = AIPluginSettings.FromFile();

// serialize app settings to json using System.Text.Json
var json = System.Text.Json.JsonSerializer.Serialize(settings);

// replace {url} with the current domain
json = json.Replace("{url}", currentDomain, StringComparison.OrdinalIgnoreCase);

response.WriteString(json);

return response;
}
}
73 changes: 73 additions & 0 deletions src/OpenAI.Plugin/CallTranscriptPlugin.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
using System.Net;
using System.Text.Json;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Attributes;
using Microsoft.Extensions.Logging;
using Models;

namespace OpenAI.Plugin
{
public class CallTranscriptPlugin
{
private static readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
private readonly ILogger _logger;
private readonly Kernel _kernel;
private readonly string _endpoint = Environment.GetEnvironmentVariable("ENDPOINT")!;
private readonly string _deploymentName = Environment.GetEnvironmentVariable("MODEL_ID")!;
private readonly string _subscriptionKey = Environment.GetEnvironmentVariable("API_KEY")!;

public CallTranscriptPlugin(ILoggerFactory loggerFactory, Kernel kernel)
{
_logger = loggerFactory.CreateLogger<CallTranscriptPlugin>();
_kernel = kernel;
}

[Function("Call Transcript Plugin")]
[OpenApiOperation(operationId: "CallTranscriptPlugin", tags: new[] { "CallTranscriptPlugin" }, Description = "Used to analyze a call given the transcript and a prompt")]
[OpenApiRequestBody("application/json", typeof(ExecuteFunctionRequest), Description = "Variables to use when executing the specified function.", Required = true)]
[OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "application/json", bodyType: typeof(ExecuteFunctionResponse), Description = "Returns the response from the AI.")]
[OpenApiResponseWithBody(statusCode: HttpStatusCode.BadRequest, contentType: "application/json", bodyType: typeof(ErrorResponse), Description = "Returned if the request body is invalid.")]
[OpenApiResponseWithBody(statusCode: HttpStatusCode.NotFound, contentType: "application/json", bodyType: typeof(ErrorResponse), Description = "Returned if the semantic function could not be found.")]
public async Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "plugins/transcript")] HttpRequestData req,
FunctionContext executionContext)
{
_logger.LogInformation("C# HTTP trigger function processed a request.");

#pragma warning disable CA1062
var functionRequest = await JsonSerializer.DeserializeAsync<ExecuteFunctionRequest>(req.Body, _jsonOptions).ConfigureAwait(false);
#pragma warning disable CA1062
if (functionRequest == null)
{
return await CreateResponseAsync(req, HttpStatusCode.BadRequest, new ErrorResponse() { Message = $"Invalid request body {functionRequest}" }).ConfigureAwait(false);
}

try
{
var context = new KernelArguments
{
{ "transcript", functionRequest.Transcript }
};

var result = await _kernel.InvokeAsync("Prompts", "CallAnalyzer", context).ConfigureAwait(false);

return await CreateResponseAsync(
req,
HttpStatusCode.OK,
new ExecuteFunctionResponse() { Response = result.ToString() }).ConfigureAwait(false);
}
catch (Exception ex)
{
return await CreateResponseAsync(req, HttpStatusCode.BadRequest, new ErrorResponse() { Message = ex.Message }).ConfigureAwait(false);
}
}

private static async Task<HttpResponseData> CreateResponseAsync(HttpRequestData requestData, HttpStatusCode statusCode, object responseBody)
{
var responseData = requestData.CreateResponse(statusCode);
await responseData.WriteAsJsonAsync(responseBody).ConfigureAwait(false);
return responseData;
}
}
}
12 changes: 12 additions & 0 deletions src/OpenAI.Plugin/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS installer-env

COPY . /src/dotnet-function-app
RUN cd /src/dotnet-function-app && \
mkdir -p /home/site/wwwroot && \
dotnet publish *.csproj --output /home/site/wwwroot

FROM mcr.microsoft.com/azure-functions/dotnet-isolated:4.0-dotnet-isolated8.0
ENV AzureWebJobsScriptRoot=/home/site/wwwroot \
AzureFunctionsJobHost__Logging__Console__IsEnabled=true

COPY --from=installer-env ["/home/site/wwwroot", "/home/site/wwwroot"]
19 changes: 19 additions & 0 deletions src/OpenAI.Plugin/GlobalUsing.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
global using Microsoft.SemanticKernel;
global using Microsoft.Extensions.DependencyInjection;
global using Microsoft.Extensions.Logging;
global using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
global using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations;
global using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums;
global using Microsoft.Extensions.Hosting;
global using Microsoft.OpenApi.Models;
global using System.Text.Json.Serialization;
global using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Attributes;
global using Microsoft.Extensions.Configuration;
global using System.Net;
global using System.Reflection;
global using Microsoft.Azure.Functions.Worker;
global using Microsoft.Azure.Functions.Worker.Http;
global using Azure.AI.OpenAI;
global using Azure;
global using Azure.Identity;
global using Models;
Loading

0 comments on commit 6e528aa

Please sign in to comment.