diff --git a/.github/labeler.yml b/.github/labeler.yml index d27aa07f5..638974f9f 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -12,7 +12,7 @@ dependencies: # Add 'deployment' label to any change within the 'deploy' directory deployment: - - deploy/**/* + - scripts/deploy/**/* # Add 'documentation' label to any change of '.md' files documentation: diff --git a/.github/workflows/copilot-chat-package.yml b/.github/workflows/copilot-chat-package.yml index 5b999b089..7f02a25bb 100644 --- a/.github/workflows/copilot-chat-package.yml +++ b/.github/workflows/copilot-chat-package.yml @@ -6,9 +6,9 @@ name: copilot-chat-package on: pull_request: - branches: [ "main" ] + branches: ["main"] merge_group: - branches: [ "main" ] + branches: ["main"] concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} @@ -20,36 +20,36 @@ permissions: jobs: copilot-chat-package: strategy: - fail-fast: false - matrix: - include: - - { dotnet: '6.0', configuration: Release, os: ubuntu-latest } + fail-fast: false + matrix: + include: + - { dotnet: "6.0", configuration: Release, os: ubuntu-latest } runs-on: ${{ matrix.os }} env: NUGET_CERT_REVOCATION_MODE: offline steps: - - uses: actions/checkout@v3 - with: - clean: true - - - name: Pull container dotnet/sdk:${{ matrix.dotnet }} - run: docker pull mcr.microsoft.com/dotnet/sdk:${{ matrix.dotnet }} - - - name: Package Copilot Chat WebAPI - run: | - chmod +x $(pwd)/deploy/package-webapi.sh; - docker run --rm -v $(pwd):/app -w /app -e GITHUB_ACTIONS='true' mcr.microsoft.com/dotnet/sdk:${{ matrix.dotnet }} /bin/sh -c "/app/deploy/package-webapi.sh --no-zip"; - - - name: Set version tag - id: versiontag - run: | - VERSION_TAG="$(date +'%Y%m%d').${{ github.run_number }}.${{ github.run_attempt }}" - echo $VERSION_TAG - echo "versiontag=$VERSION_TAG" >> $GITHUB_OUTPUT - - - name: Upload package to artifacts - uses: actions/upload-artifact@v3 - with: - name: copilotchat-webapi-${{ steps.versiontag.outputs.versiontag }} - path: ./deploy/publish + - uses: actions/checkout@v3 + with: + clean: true + + - name: Pull container dotnet/sdk:${{ matrix.dotnet }} + run: docker pull mcr.microsoft.com/dotnet/sdk:${{ matrix.dotnet }} + + - name: Package Copilot Chat WebAPI + run: | + chmod +x $(pwd)/scripts/deploy/package-webapi.sh; + docker run --rm -v $(pwd):/app -w /app -e GITHUB_ACTIONS='true' mcr.microsoft.com/dotnet/sdk:${{ matrix.dotnet }} /bin/sh -c "/app/scripts/deploy/package-webapi.sh --no-zip"; + + - name: Set version tag + id: versiontag + run: | + VERSION_TAG="$(date +'%Y%m%d').${{ github.run_number }}.${{ github.run_attempt }}" + echo $VERSION_TAG + echo "versiontag=$VERSION_TAG" >> $GITHUB_OUTPUT + + - name: Upload package to artifacts + uses: actions/upload-artifact@v3 + with: + name: copilotchat-webapi-${{ steps.versiontag.outputs.versiontag }} + path: ./scripts/deploy/publish diff --git a/.github/workflows/copilot-deploy-frontend.yml b/.github/workflows/copilot-deploy-frontend.yml index 532ba8784..311ad371d 100644 --- a/.github/workflows/copilot-deploy-frontend.yml +++ b/.github/workflows/copilot-deploy-frontend.yml @@ -71,4 +71,4 @@ jobs: - name: Deploy SWA run: | - deploy/deploy-webapp.sh --subscription ${{secrets.AZURE_SUBSCRIPTION_ID}} --resource-group ${{vars.CC_DEPLOYMENT_GROUP_NAME}} --deployment-name ${{inputs.DEPLOYMENT_NAME}} --application-id ${{vars.APPLICATION_CLIENT_ID}} --authority ${{secrets.APPLICATION_AUTHORITY}} --no-redirect + scripts/deploy/deploy-webapp.sh --subscription ${{secrets.AZURE_SUBSCRIPTION_ID}} --resource-group ${{vars.CC_DEPLOYMENT_GROUP_NAME}} --deployment-name ${{inputs.DEPLOYMENT_NAME}} --application-id ${{vars.APPLICATION_CLIENT_ID}} --authority ${{secrets.APPLICATION_AUTHORITY}} --no-redirect diff --git a/.github/workflows/copilot-deploy-infra.yml b/.github/workflows/copilot-deploy-infra.yml index f7c44865d..a1722d031 100644 --- a/.github/workflows/copilot-deploy-infra.yml +++ b/.github/workflows/copilot-deploy-infra.yml @@ -61,4 +61,4 @@ jobs: inlineScript: | AI_SERVICE_KEY=$(az cognitiveservices account keys list --name ${{vars.AZUREOPENAI__NAME}} --resource-group ${{vars.AZUREOPENAI_DEPLOYMENT_GROUP_NAME}} | jq -r '.key1') echo "::add-mask::$AI_SERVICE_KEY" - deploy/deploy-azure.sh --subscription ${{secrets.AZURE_SUBSCRIPTION_ID}} --web-api-key ${{secrets.WEB_API_KEY}} --resource-group ${{vars.CC_DEPLOYMENT_GROUP_NAME}} --deployment-name ${{steps.deployment-id.outputs.deployment_name}} --region ${{vars.CC_DEPLOYMENT_REGION}} --ai-service AzureOpenAI --ai-endpoint ${{secrets.AZURE_OPENAI_ENDPOINT}} --ai-service-key $AI_SERVICE_KEY --app-service-sku ${{vars.WEBAPP_API_SKU}} --no-deploy-package --debug-deployment + scripts/deploy/deploy-azure.sh --subscription ${{secrets.AZURE_SUBSCRIPTION_ID}} --web-api-key ${{secrets.WEB_API_KEY}} --resource-group ${{vars.CC_DEPLOYMENT_GROUP_NAME}} --deployment-name ${{steps.deployment-id.outputs.deployment_name}} --region ${{vars.CC_DEPLOYMENT_REGION}} --ai-service AzureOpenAI --ai-endpoint ${{secrets.AZURE_OPENAI_ENDPOINT}} --ai-service-key $AI_SERVICE_KEY --app-service-sku ${{vars.WEBAPP_API_SKU}} --no-deploy-package --debug-deployment diff --git a/.gitignore b/.gitignore index b6e361a29..f09d4d638 100644 --- a/.gitignore +++ b/.gitignore @@ -480,4 +480,7 @@ webapp/build/ webapp/node_modules/ # Custom plugin files used in webapp for testing -webapp/public/.well-known* \ No newline at end of file +webapp/public/.well-known* + +# Auto-generated solution file from Visual Studio +webapi/CopilotChatWebApi.sln \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..8495ea143 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + "version": "0.2.0", + "configurations": [ + + { + "name": "Debug CopilotChatWebApi", + "type": "coreclr", + "preLaunchTask": "build (CopilotChatWebApi)", + "request": "launch", + "program": "${workspaceFolder}/webapi/bin/Debug/net6.0/CopilotChatWebApi.dll", + "cwd": "${workspaceFolder}/webapi", + "env": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index e5940b7ea..67e6a3b4b 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -83,7 +83,7 @@ "type": "process", "args": [ "build", - "${workspaceFolder}/CopilotChatWebApi.csproj", + "${workspaceFolder}/webapi/CopilotChatWebApi.csproj", "/property:GenerateFullPaths=true", "/consoleloggerparameters:NoSummary", "/property:DebugType=portable" diff --git a/CopilotChat.sln b/CopilotChat.sln index 1135cca43..69ecdfff2 100644 --- a/CopilotChat.sln +++ b/CopilotChat.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.6.33706.43 @@ -22,4 +22,4 @@ Global GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {971570D3-60EA-4EE4-980C-0BDA3E66E741} EndGlobalSection -EndGlobal +EndGlobal \ No newline at end of file diff --git a/deploy/README.md b/scripts/deploy/README.md similarity index 100% rename from deploy/README.md rename to scripts/deploy/README.md diff --git a/deploy/deploy-azure.ps1 b/scripts/deploy/deploy-azure.ps1 similarity index 100% rename from deploy/deploy-azure.ps1 rename to scripts/deploy/deploy-azure.ps1 diff --git a/deploy/deploy-azure.sh b/scripts/deploy/deploy-azure.sh old mode 100755 new mode 100644 similarity index 100% rename from deploy/deploy-azure.sh rename to scripts/deploy/deploy-azure.sh diff --git a/deploy/deploy-webapi.ps1 b/scripts/deploy/deploy-webapi.ps1 similarity index 100% rename from deploy/deploy-webapi.ps1 rename to scripts/deploy/deploy-webapi.ps1 diff --git a/deploy/deploy-webapi.sh b/scripts/deploy/deploy-webapi.sh old mode 100755 new mode 100644 similarity index 100% rename from deploy/deploy-webapi.sh rename to scripts/deploy/deploy-webapi.sh diff --git a/deploy/deploy-webapp.ps1 b/scripts/deploy/deploy-webapp.ps1 similarity index 100% rename from deploy/deploy-webapp.ps1 rename to scripts/deploy/deploy-webapp.ps1 diff --git a/deploy/deploy-webapp.sh b/scripts/deploy/deploy-webapp.sh old mode 100755 new mode 100644 similarity index 100% rename from deploy/deploy-webapp.sh rename to scripts/deploy/deploy-webapp.sh diff --git a/deploy/main.bicep b/scripts/deploy/main.bicep similarity index 99% rename from deploy/main.bicep rename to scripts/deploy/main.bicep index c07259108..2434d58e7 100644 --- a/deploy/main.bicep +++ b/scripts/deploy/main.bicep @@ -271,7 +271,7 @@ resource appServiceWebConfig 'Microsoft.Web/sites/config@2022-09-01' = { value: 'Warning' } { - name: 'Logging:LogLevel:SemanticKernel.Service' + name: 'Logging:LogLevel:CopilotChat.WebApi' value: 'Warning' } { diff --git a/deploy/main.json b/scripts/deploy/main.json similarity index 99% rename from deploy/main.json rename to scripts/deploy/main.json index b059692f8..21a0c157d 100644 --- a/deploy/main.json +++ b/scripts/deploy/main.json @@ -380,7 +380,7 @@ "value": "Warning" }, { - "name": "Logging:LogLevel:SemanticKernel.Service", + "name": "Logging:LogLevel:CopilotChat.WebApi", "value": "Warning" }, { diff --git a/deploy/package-webapi.ps1 b/scripts/deploy/package-webapi.ps1 similarity index 86% rename from deploy/package-webapi.ps1 rename to scripts/deploy/package-webapi.ps1 index 3966b35c6..81ae21ccf 100644 --- a/deploy/package-webapi.ps1 +++ b/scripts/deploy/package-webapi.ps1 @@ -37,7 +37,7 @@ if (!(Test-Path $publishOutputDirectory)) { } Write-Host "Build configuration: $BuildConfiguration" -dotnet publish "$PSScriptRoot/../webapi/CopilotChatWebApi.csproj" --configuration $BuildConfiguration --framework $DotNetFramework --runtime $TargetRuntime --self-contained --output "$publishOutputDirectory" +dotnet publish "$PSScriptRoot/../../webapi/CopilotChatWebApi.csproj" --configuration $BuildConfiguration --framework $DotNetFramework --runtime $TargetRuntime --self-contained --output "$publishOutputDirectory" if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } diff --git a/deploy/package-webapi.sh b/scripts/deploy/package-webapi.sh similarity index 91% rename from deploy/package-webapi.sh rename to scripts/deploy/package-webapi.sh index c36040f84..f4fe48f19 100644 --- a/deploy/package-webapi.sh +++ b/scripts/deploy/package-webapi.sh @@ -72,7 +72,7 @@ if [[ ! -d "$PUBLISH_ZIP_DIRECTORY" ]]; then fi echo "Build configuration: $CONFIGURATION" -dotnet publish "$SCRIPT_ROOT/../webapi/CopilotChatWebApi.csproj" --configuration $CONFIGURATION --framework $DOTNET --runtime $RUNTIME --self-contained --output "$PUBLISH_OUTPUT_DIRECTORY" +dotnet publish "$SCRIPT_ROOT/../../webapi/CopilotChatWebApi.csproj" --configuration $CONFIGURATION --framework $DOTNET --runtime $RUNTIME --self-contained --output "$PUBLISH_OUTPUT_DIRECTORY" if [ $? -ne 0 ]; then exit 1 fi diff --git a/importdocument/Config.cs b/tools/importdocument/Config.cs similarity index 100% rename from importdocument/Config.cs rename to tools/importdocument/Config.cs diff --git a/importdocument/ImportDocument.csproj b/tools/importdocument/ImportDocument.csproj similarity index 100% rename from importdocument/ImportDocument.csproj rename to tools/importdocument/ImportDocument.csproj diff --git a/importdocument/Program.cs b/tools/importdocument/Program.cs similarity index 100% rename from importdocument/Program.cs rename to tools/importdocument/Program.cs diff --git a/importdocument/README.md b/tools/importdocument/README.md similarity index 96% rename from importdocument/README.md rename to tools/importdocument/README.md index e099d35ec..3c5180791 100644 --- a/importdocument/README.md +++ b/tools/importdocument/README.md @@ -11,15 +11,17 @@ Memories can be generated from conversations as well as imported from external s Importing documents enables Copilot Chat to have up-to-date knowledge of specific contexts, such as enterprise and personal data. ## Configure your environment + 1. A registered App in Azure Portal (https://learn.microsoft.com/azure/active-directory/develop/quickstart-register-app) - Select Mobile and desktop applications as platform type, and the Redirect URI will be `http://localhost` - Select **`Accounts in any organizational directory (Any Azure AD directory - Multitenant) - and personal Microsoft accounts (e.g. Skype, Xbox)`** as the supported account +and personal Microsoft accounts (e.g. Skype, Xbox)`** as the supported account type for this sample. - Note the **`Application (client) ID`** from your app registration. -2. Make sure the service is running. To start the service, see [here](../webapi/README.md). +2. Make sure the service is running. To start the service, see [here](../../webapi/README.md). ## Running the app + 1. Ensure the web api is running at `https://localhost:40443/`. 2. Configure the appsettings.json file under this folder root with the following variables and fill in with your information, where @@ -33,7 +35,7 @@ Importing documents enables Copilot Chat to have up-to-date knowledge of specifi all users will have access to: `dotnet run --files .\sample-docs\ms10k.txt` - + Or **Run** the following command to import a document to the app under a chat isolated document collection where only the chat session will have access to: diff --git a/importdocument/appsettings.json b/tools/importdocument/appsettings.json similarity index 100% rename from importdocument/appsettings.json rename to tools/importdocument/appsettings.json diff --git a/importdocument/sample-docs/Lorem_ipsum.pdf b/tools/importdocument/sample-docs/Lorem_ipsum.pdf similarity index 100% rename from importdocument/sample-docs/Lorem_ipsum.pdf rename to tools/importdocument/sample-docs/Lorem_ipsum.pdf diff --git a/importdocument/sample-docs/Microsoft-Responsible-AI-Standard-v2-General-Requirements.pdf b/tools/importdocument/sample-docs/Microsoft-Responsible-AI-Standard-v2-General-Requirements.pdf similarity index 100% rename from importdocument/sample-docs/Microsoft-Responsible-AI-Standard-v2-General-Requirements.pdf rename to tools/importdocument/sample-docs/Microsoft-Responsible-AI-Standard-v2-General-Requirements.pdf diff --git a/importdocument/sample-docs/ms10k.txt b/tools/importdocument/sample-docs/ms10k.txt similarity index 100% rename from importdocument/sample-docs/ms10k.txt rename to tools/importdocument/sample-docs/ms10k.txt diff --git a/webapi/Auth/ApiKeyAuthenticationHandler.cs b/webapi/Auth/ApiKeyAuthenticationHandler.cs index 550f862b1..e2dbeec6f 100644 --- a/webapi/Auth/ApiKeyAuthenticationHandler.cs +++ b/webapi/Auth/ApiKeyAuthenticationHandler.cs @@ -9,7 +9,7 @@ using Microsoft.Extensions.Options; using Microsoft.Extensions.Primitives; -namespace SemanticKernel.Service.Auth; +namespace CopilotChat.WebApi.Auth; /// /// Class implementing API key authentication. diff --git a/webapi/Auth/ApiKeyAuthenticationSchemeOptions.cs b/webapi/Auth/ApiKeyAuthenticationSchemeOptions.cs index 33e8943f2..b9c680977 100644 --- a/webapi/Auth/ApiKeyAuthenticationSchemeOptions.cs +++ b/webapi/Auth/ApiKeyAuthenticationSchemeOptions.cs @@ -2,7 +2,7 @@ using Microsoft.AspNetCore.Authentication; -namespace SemanticKernel.Service.Auth; +namespace CopilotChat.WebApi.Auth; /// /// Options for API key authentication. diff --git a/webapi/Auth/PassThroughAuthenticationHandler.cs b/webapi/Auth/PassThroughAuthenticationHandler.cs index e12320b89..0453865e7 100644 --- a/webapi/Auth/PassThroughAuthenticationHandler.cs +++ b/webapi/Auth/PassThroughAuthenticationHandler.cs @@ -7,7 +7,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -namespace SemanticKernel.Service.Auth; +namespace CopilotChat.WebApi.Auth; /// /// Class implementing "authentication" that lets all requests pass through. diff --git a/webapi/CopilotChat/Controllers/BotController.cs b/webapi/Controllers/BotController.cs similarity index 97% rename from webapi/CopilotChat/Controllers/BotController.cs rename to webapi/Controllers/BotController.cs index 3583b0cd3..7151766a6 100644 --- a/webapi/CopilotChat/Controllers/BotController.cs +++ b/webapi/Controllers/BotController.cs @@ -14,13 +14,13 @@ using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Memory; -using SemanticKernel.Service.CopilotChat.Extensions; -using SemanticKernel.Service.CopilotChat.Models; -using SemanticKernel.Service.CopilotChat.Options; -using SemanticKernel.Service.CopilotChat.Storage; -using SemanticKernel.Service.Options; +using CopilotChat.WebApi.Extensions; +using CopilotChat.WebApi.Options; +using CopilotChat.WebApi.Storage; +using CopilotChat.WebApi.Models.Storage; +using CopilotChat.WebApi.Models.Response; -namespace SemanticKernel.Service.CopilotChat.Controllers; +namespace CopilotChat.WebApi.Controllers; [ApiController] public class BotController : ControllerBase @@ -142,7 +142,7 @@ public async Task> UploadAsync( return this.CreatedAtAction( nameof(ChatHistoryController.GetChatSessionByIdAsync), - nameof(ChatHistoryController).Replace("Controller", "", StringComparison.OrdinalIgnoreCase), + nameof(ChatHistoryController).Replace("Controller", string.Empty, StringComparison.OrdinalIgnoreCase), new { chatId }, newChat); } @@ -208,7 +208,7 @@ private async Task GetMemoryRecordsAndAppendToEmbeddingsAsync( IKernel kernel, string collectionName, List>> embeddings, - string newCollectionName = "") + string? newCollectionName = null) { List collectionMemoryRecords; try diff --git a/webapi/CopilotChat/Controllers/ChatController.cs b/webapi/Controllers/ChatController.cs similarity index 92% rename from webapi/CopilotChat/Controllers/ChatController.cs rename to webapi/Controllers/ChatController.cs index c33d78bc9..85e9e4769 100644 --- a/webapi/CopilotChat/Controllers/ChatController.cs +++ b/webapi/Controllers/ChatController.cs @@ -24,13 +24,13 @@ using Microsoft.SemanticKernel.Skills.MsGraph.Connectors.Client; using Microsoft.SemanticKernel.Skills.OpenAPI.Authentication; using Microsoft.SemanticKernel.Skills.OpenAPI.Extensions; -using SemanticKernel.Service.CopilotChat.Hubs; -using SemanticKernel.Service.CopilotChat.Models; -using SemanticKernel.Service.CopilotChat.Skills.ChatSkills; -using SemanticKernel.Service.Diagnostics; -using SemanticKernel.Service.Models; +using CopilotChat.WebApi.Hubs; +using CopilotChat.WebApi.Skills.ChatSkills; +using CopilotChat.WebApi.Models.Response; +using CopilotChat.WebApi.Models.Request; +using CopilotChat.WebApi.Services; -namespace SemanticKernel.Service.CopilotChat.Controllers; +namespace CopilotChat.WebApi.Controllers; /// /// Controller responsible for handling chat messages and responses. @@ -116,7 +116,7 @@ public async Task ChatAsync( return this.BadRequest(string.Concat(aiException.Message, " - Detail: " + aiException.Detail)); } - return this.BadRequest(result.LastErrorDescription); + return this.BadRequest(result.LastException!.Message); } AskResult chatSkillAskResult = new() @@ -174,7 +174,7 @@ private async Task RegisterPlannerSkillsAsync(CopilotChatPlanner planner, Dictio this._logger.LogInformation("Registering Klarna plugin"); // Register the Klarna shopping ChatGPT plugin with the planner's kernel. There is no authentication required for this plugin. - await planner.Kernel.ImportChatGptPluginSkillFromUrlAsync("KlarnaShoppingPlugin", new Uri("https://www.klarna.com/.well-known/ai-plugin.json"), new OpenApiSkillExecutionParameters()); + await planner.Kernel.ImportAIPluginAsync("KlarnaShoppingPlugin", new Uri("https://www.klarna.com/.well-known/ai-plugin.json"), new OpenApiSkillExecutionParameters()); } // GitHub @@ -182,9 +182,9 @@ private async Task RegisterPlannerSkillsAsync(CopilotChatPlanner planner, Dictio { this._logger.LogInformation("Enabling GitHub plugin."); BearerAuthenticationProvider authenticationProvider = new(() => Task.FromResult(GithubAuthHeader)); - await planner.Kernel.ImportOpenApiSkillFromFileAsync( + await planner.Kernel.ImportAIPluginAsync( skillName: "GitHubPlugin", - filePath: Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!, "CopilotChat", "Skills", "OpenApiPlugins/GitHubPlugin/openapi.json"), + filePath: Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!, "Skills", "OpenApiPlugins/GitHubPlugin/openapi.json"), new OpenApiSkillExecutionParameters { AuthCallback = authenticationProvider.AuthenticateRequestAsync, @@ -198,9 +198,9 @@ await planner.Kernel.ImportOpenApiSkillFromFileAsync( var authenticationProvider = new BasicAuthenticationProvider(() => { return Task.FromResult(JiraAuthHeader); }); var hasServerUrlOverride = variables.TryGetValue("jira-server-url", out string? serverUrlOverride); - await planner.Kernel.ImportOpenApiSkillFromFileAsync( + await planner.Kernel.ImportAIPluginAsync( skillName: "JiraPlugin", - filePath: Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!, "CopilotChat", "Skills", "OpenApiPlugins/JiraPlugin/openapi.json"), + filePath: Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!, "Skills", "OpenApiPlugins/JiraPlugin/openapi.json"), new OpenApiSkillExecutionParameters { AuthCallback = authenticationProvider.AuthenticateRequestAsync, @@ -241,7 +241,7 @@ await planner.Kernel.ImportOpenApiSkillFromFileAsync( var requiresAuth = !plugin.AuthType.Equals("none", StringComparison.OrdinalIgnoreCase); BearerAuthenticationProvider authenticationProvider = new(() => Task.FromResult(PluginAuthValue)); - await planner.Kernel.ImportChatGptPluginSkillFromUrlAsync( + await planner.Kernel.ImportAIPluginAsync( $"{plugin.NameForModel}Plugin", uriBuilder.Uri, new OpenApiSkillExecutionParameters diff --git a/webapi/CopilotChat/Controllers/ChatHistoryController.cs b/webapi/Controllers/ChatHistoryController.cs similarity index 97% rename from webapi/CopilotChat/Controllers/ChatHistoryController.cs rename to webapi/Controllers/ChatHistoryController.cs index c9b930fdb..73539dc7f 100644 --- a/webapi/CopilotChat/Controllers/ChatHistoryController.cs +++ b/webapi/Controllers/ChatHistoryController.cs @@ -11,13 +11,15 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.SemanticKernel; -using SemanticKernel.Service.CopilotChat.Hubs; -using SemanticKernel.Service.CopilotChat.Models; -using SemanticKernel.Service.CopilotChat.Options; -using SemanticKernel.Service.CopilotChat.Skills; -using SemanticKernel.Service.CopilotChat.Storage; - -namespace SemanticKernel.Service.CopilotChat.Controllers; +using CopilotChat.WebApi.Hubs; +using CopilotChat.WebApi.Options; +using CopilotChat.WebApi.Skills; +using CopilotChat.WebApi.Storage; +using CopilotChat.WebApi.Models.Storage; +using CopilotChat.WebApi.Models.Request; +using CopilotChat.WebApi.Models.Response; + +namespace CopilotChat.WebApi.Controllers; /// /// Controller for chat history. diff --git a/webapi/CopilotChat/Controllers/ChatMemoryController.cs b/webapi/Controllers/ChatMemoryController.cs similarity index 93% rename from webapi/CopilotChat/Controllers/ChatMemoryController.cs rename to webapi/Controllers/ChatMemoryController.cs index 919660900..344a32e54 100644 --- a/webapi/CopilotChat/Controllers/ChatMemoryController.cs +++ b/webapi/Controllers/ChatMemoryController.cs @@ -10,11 +10,11 @@ using Microsoft.Extensions.Options; using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Memory; -using SemanticKernel.Service.CopilotChat.Options; -using SemanticKernel.Service.CopilotChat.Skills.ChatSkills; -using SemanticKernel.Service.CopilotChat.Storage; +using CopilotChat.WebApi.Options; +using CopilotChat.WebApi.Skills.ChatSkills; +using CopilotChat.WebApi.Storage; -namespace SemanticKernel.Service.CopilotChat.Controllers; +namespace CopilotChat.WebApi.Controllers; /// /// Controller for retrieving semantic memory data of chat sessions. @@ -114,7 +114,7 @@ private bool ValidateMemoryName(string memoryName) } /// - /// Sanitizes the log input by removing new line characters. + /// Sanitizes the log input by removing new line characters. /// This helps prevent log forgery attacks from malicious text. /// /// @@ -124,7 +124,7 @@ private bool ValidateMemoryName(string memoryName) /// The sanitized input. private string SanitizeLogInput(string input) { - return input.Replace(Environment.NewLine, "", StringComparison.Ordinal); + return input.Replace(Environment.NewLine, string.Empty, StringComparison.Ordinal); } # endregion diff --git a/webapi/CopilotChat/Controllers/ChatParticipantController.cs b/webapi/Controllers/ChatParticipantController.cs similarity index 95% rename from webapi/CopilotChat/Controllers/ChatParticipantController.cs rename to webapi/Controllers/ChatParticipantController.cs index b08ff8c1f..ce9d20052 100644 --- a/webapi/CopilotChat/Controllers/ChatParticipantController.cs +++ b/webapi/Controllers/ChatParticipantController.cs @@ -7,11 +7,11 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Logging; -using SemanticKernel.Service.CopilotChat.Hubs; -using SemanticKernel.Service.CopilotChat.Models; -using SemanticKernel.Service.CopilotChat.Storage; +using CopilotChat.WebApi.Hubs; +using CopilotChat.WebApi.Storage; +using CopilotChat.WebApi.Models.Storage; -namespace SemanticKernel.Service.CopilotChat.Controllers; +namespace CopilotChat.WebApi.Controllers; /// /// Controller for managing invitations and participants in a chat session. diff --git a/webapi/CopilotChat/Controllers/DocumentImportController.cs b/webapi/Controllers/DocumentImportController.cs similarity index 98% rename from webapi/CopilotChat/Controllers/DocumentImportController.cs rename to webapi/Controllers/DocumentImportController.cs index 731a790a9..916132713 100644 --- a/webapi/CopilotChat/Controllers/DocumentImportController.cs +++ b/webapi/Controllers/DocumentImportController.cs @@ -14,17 +14,18 @@ using Microsoft.Extensions.Options; using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.Text; -using SemanticKernel.Service.CopilotChat.Hubs; -using SemanticKernel.Service.CopilotChat.Models; -using SemanticKernel.Service.CopilotChat.Options; -using SemanticKernel.Service.CopilotChat.Skills; -using SemanticKernel.Service.CopilotChat.Storage; -using SemanticKernel.Service.Services; +using CopilotChat.WebApi.Hubs; +using CopilotChat.WebApi.Options; +using CopilotChat.WebApi.Skills; +using CopilotChat.WebApi.Storage; +using CopilotChat.WebApi.Services; using UglyToad.PdfPig; using UglyToad.PdfPig.DocumentLayoutAnalysis.TextExtractor; -using static SemanticKernel.Service.CopilotChat.Models.MemorySource; +using CopilotChat.WebApi.Models.Request; +using CopilotChat.WebApi.Models.Storage; +using CopilotChat.WebApi.Models.Response; -namespace SemanticKernel.Service.CopilotChat.Controllers; +namespace CopilotChat.WebApi.Controllers; /// /// Controller for importing documents. diff --git a/webapi/CopilotChat/Controllers/ServiceOptionsController.cs b/webapi/Controllers/ServiceOptionsController.cs similarity index 87% rename from webapi/CopilotChat/Controllers/ServiceOptionsController.cs rename to webapi/Controllers/ServiceOptionsController.cs index 4b79b2e5f..7a76f5219 100644 --- a/webapi/CopilotChat/Controllers/ServiceOptionsController.cs +++ b/webapi/Controllers/ServiceOptionsController.cs @@ -1,16 +1,15 @@ // Copyright (c) Microsoft. All rights reserved. using System; +using CopilotChat.WebApi.Models.Response; +using CopilotChat.WebApi.Options; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using SemanticKernel.Service.CopilotChat.Models; -using SemanticKernel.Service.Options; -using static SemanticKernel.Service.CopilotChat.Models.ServiceOptionsResponse; -namespace SemanticKernel.Service.CopilotChat.Controllers; +namespace CopilotChat.WebApi.Controllers; /// /// Controller responsible for returning the service options to the client. diff --git a/webapi/CopilotChat/Controllers/SpeechTokenController.cs b/webapi/Controllers/SpeechTokenController.cs similarity index 91% rename from webapi/CopilotChat/Controllers/SpeechTokenController.cs rename to webapi/Controllers/SpeechTokenController.cs index 8dea7b3b9..b19c30f10 100644 --- a/webapi/CopilotChat/Controllers/SpeechTokenController.cs +++ b/webapi/Controllers/SpeechTokenController.cs @@ -9,10 +9,10 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using SemanticKernel.Service.CopilotChat.Models; -using SemanticKernel.Service.CopilotChat.Options; +using CopilotChat.WebApi.Options; +using CopilotChat.WebApi.Models.Response; -namespace SemanticKernel.Service.CopilotChat.Controllers; +namespace CopilotChat.WebApi.Controllers; [Authorize] [ApiController] @@ -71,6 +71,6 @@ private async Task FetchTokenAsync(string fetchUri, string subscrip return new TokenResult { Token = token, ResponseCode = response.StatusCode }; } - return new TokenResult { Token = "", ResponseCode = HttpStatusCode.NotFound }; + return new TokenResult { ResponseCode = HttpStatusCode.NotFound }; } } diff --git a/webapi/CopilotChat/Extensions/SemanticKernelExtensions.cs b/webapi/CopilotChat/Extensions/SemanticKernelExtensions.cs deleted file mode 100644 index fe3c7ccf7..000000000 --- a/webapi/CopilotChat/Extensions/SemanticKernelExtensions.cs +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System; -using Microsoft.AspNetCore.SignalR; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Microsoft.SemanticKernel; -using Microsoft.SemanticKernel.Memory; -using SemanticKernel.Service.CopilotChat.Hubs; -using SemanticKernel.Service.CopilotChat.Options; -using SemanticKernel.Service.CopilotChat.Skills.ChatSkills; -using SemanticKernel.Service.CopilotChat.Storage; -using SemanticKernel.Service.Options; - -namespace SemanticKernel.Service.CopilotChat.Extensions; - -/// -/// Extension methods for registering Copilot Chat components to Semantic Kernel. -/// -public static class CopilotChatSemanticKernelExtensions -{ - /// - /// Add Planner services - /// - public static IServiceCollection AddCopilotChatPlannerServices(this IServiceCollection services) - { - IOptions? plannerOptions = services.BuildServiceProvider().GetService>(); - services.AddScoped(sp => - { - IKernel plannerKernel = Kernel.Builder - .WithLogger(sp.GetRequiredService>()) - .WithMemory(sp.GetRequiredService()) - // TODO: [sk Issue #2046] verify planner has AI service configured - .WithPlannerBackend(sp.GetRequiredService>().Value) - .Build(); - return new CopilotChatPlanner(plannerKernel, plannerOptions?.Value); - }); - - // Register Planner skills (AI plugins) here. - // TODO: [sk Issue #2046] Move planner skill registration from ChatController to this location. - - return services; - } - - /// - /// Register the Copilot chat skills with the kernel. - /// - public static IKernel RegisterCopilotChatSkills(this IKernel kernel, IServiceProvider sp) - { - // Chat skill - kernel.ImportSkill(new ChatSkill( - kernel: kernel, - chatMessageRepository: sp.GetRequiredService(), - chatSessionRepository: sp.GetRequiredService(), - messageRelayHubContext: sp.GetRequiredService>(), - promptOptions: sp.GetRequiredService>(), - documentImportOptions: sp.GetRequiredService>(), - planner: sp.GetRequiredService(), - logger: sp.GetRequiredService>()), - nameof(ChatSkill)); - - return kernel; - } - - /// - /// Add the completion backend to the kernel config for the planner. - /// - private static KernelBuilder WithPlannerBackend(this KernelBuilder kernelBuilder, AIServiceOptions options) - { - return options.Type switch - { - AIServiceOptions.AIServiceType.AzureOpenAI => kernelBuilder.WithAzureChatCompletionService(options.Models.Planner, options.Endpoint, options.Key), - AIServiceOptions.AIServiceType.OpenAI => kernelBuilder.WithOpenAIChatCompletionService(options.Models.Planner, options.Key), - _ => throw new ArgumentException($"Invalid {nameof(options.Type)} value in '{AIServiceOptions.PropertyName}' settings."), - }; - } -} diff --git a/webapi/CopilotChat/Models/Responses/ServiceOptionsResponse.cs b/webapi/CopilotChat/Models/Responses/ServiceOptionsResponse.cs deleted file mode 100644 index bd24ef4b2..000000000 --- a/webapi/CopilotChat/Models/Responses/ServiceOptionsResponse.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System.Collections.Generic; -using System.Linq; -using System.Text.Json.Serialization; - -namespace SemanticKernel.Service.CopilotChat.Models; - -public class ServiceOptionsResponse -{ - /// - /// Response to memoriesStoreType request. - /// - public class MemoriesStoreOptionResponse - { - /// - /// All the available memories store types. - /// - [JsonPropertyName("types")] - public IEnumerable Types { get; set; } = Enumerable.Empty(); - - /// - /// The selected memories store type. - /// - [JsonPropertyName("selectedType")] - public string SelectedType { get; set; } = string.Empty; - } - - /// - /// The memories store that is configured. - /// - [JsonPropertyName("memoriesStore")] - public MemoriesStoreOptionResponse MemoriesStore { get; set; } = new MemoriesStoreOptionResponse(); -} - diff --git a/webapi/CopilotChatWebApi.csproj b/webapi/CopilotChatWebApi.csproj index d925afce7..bd586a492 100644 --- a/webapi/CopilotChatWebApi.csproj +++ b/webapi/CopilotChatWebApi.csproj @@ -5,21 +5,21 @@ 10 enable disable - SemanticKernel.Service + CopilotChat.WebApi aspnet-SKWebApi-1581687a-bee4-40ea-a886-ce22524aea88 - - - - - - - - + + + + + + + + diff --git a/webapi/CopilotChatWebApi.sln b/webapi/CopilotChatWebApi.sln deleted file mode 100644 index 9d08ac20f..000000000 --- a/webapi/CopilotChatWebApi.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.5.33530.505 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CopilotChatWebApi", "CopilotChatWebApi.csproj", "{35CC3A68-E577-4B21-B94C-BF674F8FA505}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {35CC3A68-E577-4B21-B94C-BF674F8FA505}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {35CC3A68-E577-4B21-B94C-BF674F8FA505}.Debug|Any CPU.Build.0 = Debug|Any CPU - {35CC3A68-E577-4B21-B94C-BF674F8FA505}.Release|Any CPU.ActiveCfg = Release|Any CPU - {35CC3A68-E577-4B21-B94C-BF674F8FA505}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {1F60AC39-60D2-4CD2-B2FC-71E174DDFC1A} - EndGlobalSection -EndGlobal diff --git a/webapi/ConfigurationExtensions.cs b/webapi/Extensions/ConfigurationExtensions.cs similarity index 97% rename from webapi/ConfigurationExtensions.cs rename to webapi/Extensions/ConfigurationExtensions.cs index 81e7bf369..e89c1d75f 100644 --- a/webapi/ConfigurationExtensions.cs +++ b/webapi/Extensions/ConfigurationExtensions.cs @@ -6,7 +6,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; -namespace SemanticKernel.Service; +namespace CopilotChat.WebApi.Extensions; internal static class ConfigExtensions { diff --git a/webapi/Diagnostics/ExceptionExtensions.cs b/webapi/Extensions/ExceptionExtensions.cs similarity index 100% rename from webapi/Diagnostics/ExceptionExtensions.cs rename to webapi/Extensions/ExceptionExtensions.cs diff --git a/webapi/CopilotChat/Extensions/IAsyncEnumerableExtensions.cs b/webapi/Extensions/IAsyncEnumerableExtensions.cs similarity index 92% rename from webapi/CopilotChat/Extensions/IAsyncEnumerableExtensions.cs rename to webapi/Extensions/IAsyncEnumerableExtensions.cs index 033364acc..7eba934d5 100644 --- a/webapi/CopilotChat/Extensions/IAsyncEnumerableExtensions.cs +++ b/webapi/Extensions/IAsyncEnumerableExtensions.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Threading.Tasks; -namespace SemanticKernel.Service.CopilotChat.Extensions; +namespace CopilotChat.WebApi.Extensions; /// /// Extension methods for enabling async LINQ operations on IAsyncEnumerable sequence. diff --git a/webapi/SemanticKernelExtensions.cs b/webapi/Extensions/SemanticKernelExtensions.cs similarity index 72% rename from webapi/SemanticKernelExtensions.cs rename to webapi/Extensions/SemanticKernelExtensions.cs index 8c287b089..1c1e32136 100644 --- a/webapi/SemanticKernelExtensions.cs +++ b/webapi/Extensions/SemanticKernelExtensions.cs @@ -4,6 +4,7 @@ using System.IO; using System.Net.Http; using System.Threading.Tasks; +using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -16,11 +17,14 @@ using Microsoft.SemanticKernel.Memory; using Microsoft.SemanticKernel.Skills.Core; using Microsoft.SemanticKernel.TemplateEngine; -using SemanticKernel.Service.CopilotChat.Extensions; -using SemanticKernel.Service.Options; -using static SemanticKernel.Service.Options.MemoriesStoreOptions; +using CopilotChat.WebApi.Hubs; +using CopilotChat.WebApi.Options; +using CopilotChat.WebApi.Skills.ChatSkills; +using CopilotChat.WebApi.Storage; +using static CopilotChat.WebApi.Options.MemoriesStoreOptions; +using Microsoft.SemanticKernel.Orchestration; -namespace SemanticKernel.Service; +namespace CopilotChat.WebApi.Extensions; /// /// Extension methods for registering Semantic Kernel related services. @@ -60,13 +64,68 @@ internal static IServiceCollection AddSemanticKernelServices(this IServiceCollec return services; } + /// + /// Add Planner services + /// + public static IServiceCollection AddPlannerServices(this IServiceCollection services) + { + IOptions? plannerOptions = services.BuildServiceProvider().GetService>(); + services.AddScoped(sp => + { + IKernel plannerKernel = Kernel.Builder + .WithLogger(sp.GetRequiredService>()) + .WithMemory(sp.GetRequiredService()) + // TODO: [sk Issue #2046] verify planner has AI service configured + .WithPlannerBackend(sp.GetRequiredService>().Value) + .Build(); + return new CopilotChatPlanner(plannerKernel, plannerOptions?.Value); + }); + + // Register Planner skills (AI plugins) here. + // TODO: [sk Issue #2046] Move planner skill registration from ChatController to this location. + + return services; + } + + /// + /// Register the chat skill with the kernel. + /// + public static IKernel RegisterChatSkill(this IKernel kernel, IServiceProvider sp) + { + // Chat skill + kernel.ImportSkill(new ChatSkill( + kernel: kernel, + chatMessageRepository: sp.GetRequiredService(), + chatSessionRepository: sp.GetRequiredService(), + messageRelayHubContext: sp.GetRequiredService>(), + promptOptions: sp.GetRequiredService>(), + documentImportOptions: sp.GetRequiredService>(), + planner: sp.GetRequiredService(), + logger: sp.GetRequiredService>()), + nameof(ChatSkill)); + + return kernel; + } + + /// + /// Propagate exception from within semantic function + /// + public static void ThrowIfFailed(this SKContext context) + { + if (context.ErrorOccurred) + { + context.Logger.LogError(context.LastException, "{0}", context.LastException?.Message); + throw context.LastException!; + } + } + /// /// Register the skills with the kernel. /// private static Task RegisterSkillsAsync(IServiceProvider sp, IKernel kernel) { // Copilot chat skills - kernel.RegisterCopilotChatSkills(sp); + kernel.RegisterChatSkill(sp); // Time skill kernel.ImportSkill(new TimeSkill(), nameof(TimeSkill)); @@ -83,7 +142,7 @@ private static Task RegisterSkillsAsync(IServiceProvider sp, IKernel kernel) } catch (TemplateException e) { - kernel.Log.LogError("Could not load skill from {Directory}: {Message}", subDir, e.Message); + kernel.Logger.LogError("Could not load skill from {Directory}: {Message}", subDir, e.Message); } } } @@ -141,6 +200,7 @@ private static void AddSemanticTextMemory(this IServiceCollection services) return new AzureCognitiveSearchMemoryStore(config.AzureCognitiveSearch.Endpoint, config.AzureCognitiveSearch.Key); }); break; + case MemoriesStoreOptions.MemoriesStoreType.Chroma: if (config.Chroma == null) { @@ -203,6 +263,19 @@ private static KernelBuilder WithEmbeddingBackend(this KernelBuilder kernelBuild }; } + /// + /// Add the completion backend to the kernel config for the planner. + /// + private static KernelBuilder WithPlannerBackend(this KernelBuilder kernelBuilder, AIServiceOptions options) + { + return options.Type switch + { + AIServiceOptions.AIServiceType.AzureOpenAI => kernelBuilder.WithAzureChatCompletionService(options.Models.Planner, options.Endpoint, options.Key), + AIServiceOptions.AIServiceType.OpenAI => kernelBuilder.WithOpenAIChatCompletionService(options.Models.Planner, options.Key), + _ => throw new ArgumentException($"Invalid {nameof(options.Type)} value in '{AIServiceOptions.PropertyName}' settings."), + }; + } + /// /// Construct IEmbeddingGeneration from /// diff --git a/webapi/CopilotChat/Extensions/ServiceExtensions.cs b/webapi/Extensions/ServiceExtensions.cs similarity index 70% rename from webapi/CopilotChat/Extensions/ServiceExtensions.cs rename to webapi/Extensions/ServiceExtensions.cs index 11cafe19e..350fb913a 100644 --- a/webapi/CopilotChat/Extensions/ServiceExtensions.cs +++ b/webapi/Extensions/ServiceExtensions.cs @@ -5,17 +5,20 @@ using System.IO; using System.Reflection; using Azure; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -using SemanticKernel.Service.CopilotChat.Models; -using SemanticKernel.Service.CopilotChat.Options; -using SemanticKernel.Service.CopilotChat.Storage; -using SemanticKernel.Service.Options; -using SemanticKernel.Service.Services; +using Microsoft.Identity.Web; +using CopilotChat.WebApi.Auth; +using CopilotChat.WebApi.Options; +using CopilotChat.WebApi.Storage; +using CopilotChat.WebApi.Services; using Tesseract; +using CopilotChat.WebApi.Models.Storage; -namespace SemanticKernel.Service.CopilotChat.Extensions; +namespace CopilotChat.WebApi.Extensions; /// /// Extension methods for . @@ -26,60 +29,145 @@ public static class CopilotChatServiceExtensions /// /// Parse configuration into options. /// - public static IServiceCollection AddCopilotChatOptions(this IServiceCollection services, ConfigurationManager configuration) + public static IServiceCollection AddOptions(this IServiceCollection services, ConfigurationManager configuration) { - // AI service configurations for Copilot Chat. - // They are using the same configuration section as Semantic Kernel. - services.AddOptions(AIServiceOptions.PropertyName) + // General configuration + services.AddOptions() + .Bind(configuration.GetSection(ServiceOptions.PropertyName)) + .ValidateDataAnnotations() + .ValidateOnStart() + .PostConfigure(TrimStringProperties); + + // Default AI service configurations for Semantic Kernel + services.AddOptions() .Bind(configuration.GetSection(AIServiceOptions.PropertyName)) + .ValidateDataAnnotations() + .ValidateOnStart() + .PostConfigure(TrimStringProperties); + + // Authorization configuration + services.AddOptions() + .Bind(configuration.GetSection(AuthorizationOptions.PropertyName)) + .ValidateOnStart() + .ValidateDataAnnotations() + .PostConfigure(TrimStringProperties); + + // Memory store configuration + services.AddOptions() + .Bind(configuration.GetSection(MemoriesStoreOptions.PropertyName)) + .ValidateDataAnnotations() .ValidateOnStart() .PostConfigure(TrimStringProperties); // Chat log storage configuration services.AddOptions() .Bind(configuration.GetSection(ChatStoreOptions.PropertyName)) + .ValidateDataAnnotations() .ValidateOnStart() .PostConfigure(TrimStringProperties); // Azure speech token configuration services.AddOptions() .Bind(configuration.GetSection(AzureSpeechOptions.PropertyName)) + .ValidateDataAnnotations() .ValidateOnStart() .PostConfigure(TrimStringProperties); // Bot schema configuration services.AddOptions() .Bind(configuration.GetSection(BotSchemaOptions.PropertyName)) + .ValidateDataAnnotations() .ValidateOnStart() .PostConfigure(TrimStringProperties); // Document memory options services.AddOptions() .Bind(configuration.GetSection(DocumentMemoryOptions.PropertyName)) + .ValidateDataAnnotations() .ValidateOnStart() .PostConfigure(TrimStringProperties); // Chat prompt options services.AddOptions() .Bind(configuration.GetSection(PromptsOptions.PropertyName)) + .ValidateDataAnnotations() .ValidateOnStart() .PostConfigure(TrimStringProperties); // Planner options services.AddOptions() .Bind(configuration.GetSection(PlannerOptions.PropertyName)) + .ValidateDataAnnotations() .ValidateOnStart() .PostConfigure(TrimStringProperties); // OCR support options services.AddOptions() .Bind(configuration.GetSection(OcrSupportOptions.PropertyName)) + .ValidateDataAnnotations() .ValidateOnStart() .PostConfigure(TrimStringProperties); return services; } + /// + /// Add authorization services + /// + internal static IServiceCollection AddAuthorization(this IServiceCollection services, IConfiguration configuration) + { + AuthorizationOptions config = services.BuildServiceProvider().GetRequiredService>().Value; + switch (config.Type) + { + case AuthorizationOptions.AuthorizationType.AzureAd: + services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddMicrosoftIdentityWebApi(configuration.GetSection($"{AuthorizationOptions.PropertyName}:AzureAd")); + break; + + case AuthorizationOptions.AuthorizationType.ApiKey: + services.AddAuthentication(ApiKeyAuthenticationHandler.AuthenticationScheme) + .AddScheme( + ApiKeyAuthenticationHandler.AuthenticationScheme, + options => options.ApiKey = config.ApiKey); + break; + + case AuthorizationOptions.AuthorizationType.None: + services.AddAuthentication(PassThroughAuthenticationHandler.AuthenticationScheme) + .AddScheme( + authenticationScheme: PassThroughAuthenticationHandler.AuthenticationScheme, + configureOptions: null); + break; + + default: + throw new InvalidOperationException($"Invalid authorization type '{config.Type}'."); + } + + return services; + } + + /// + /// Add CORS settings. + /// + internal static IServiceCollection AddCorsPolicy(this IServiceCollection services) + { + IConfiguration configuration = services.BuildServiceProvider().GetRequiredService(); + string[] allowedOrigins = configuration.GetSection("AllowedOrigins").Get() ?? Array.Empty(); + if (allowedOrigins.Length > 0) + { + services.AddCors(options => + { + options.AddDefaultPolicy( + policy => + { + policy.WithOrigins(allowedOrigins) + .AllowAnyHeader(); + }); + }); + } + + return services; + } + /// /// Adds persistent OCR support service. /// diff --git a/webapi/CopilotChat/Hubs/MessageRelayHub.cs b/webapi/Hubs/MessageRelayHub.cs similarity index 97% rename from webapi/CopilotChat/Hubs/MessageRelayHub.cs rename to webapi/Hubs/MessageRelayHub.cs index 408e44b8e..f01233dbd 100644 --- a/webapi/CopilotChat/Hubs/MessageRelayHub.cs +++ b/webapi/Hubs/MessageRelayHub.cs @@ -4,7 +4,7 @@ using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Logging; -namespace SemanticKernel.Service.CopilotChat.Hubs; +namespace CopilotChat.WebApi.Hubs; /// /// Represents a chat hub for real-time communication. diff --git a/webapi/Models/Ask.cs b/webapi/Models/Request/Ask.cs similarity index 87% rename from webapi/Models/Ask.cs rename to webapi/Models/Request/Ask.cs index d72f95b30..cec903e37 100644 --- a/webapi/Models/Ask.cs +++ b/webapi/Models/Request/Ask.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; -namespace SemanticKernel.Service.Models; +namespace CopilotChat.WebApi.Models.Request; public class Ask { diff --git a/webapi/CopilotChat/Models/CreateChatParameters.cs b/webapi/Models/Request/CreateChatParameters.cs similarity index 90% rename from webapi/CopilotChat/Models/CreateChatParameters.cs rename to webapi/Models/Request/CreateChatParameters.cs index 5c25fcb13..f3ce51dc8 100644 --- a/webapi/CopilotChat/Models/CreateChatParameters.cs +++ b/webapi/Models/Request/CreateChatParameters.cs @@ -2,12 +2,13 @@ using System.Text.Json.Serialization; -namespace SemanticKernel.Service.CopilotChat.Models; +namespace CopilotChat.WebApi.Models.Request; /// /// Json body for creating a new chat session. /// public class CreateChatParameters + { /// /// Id of the user who sent this message. diff --git a/webapi/CopilotChat/Models/CustomPlugin.cs b/webapi/Models/Request/CustomPlugin.cs similarity index 96% rename from webapi/CopilotChat/Models/CustomPlugin.cs rename to webapi/Models/Request/CustomPlugin.cs index b3c3ebb12..b0872e4d0 100644 --- a/webapi/CopilotChat/Models/CustomPlugin.cs +++ b/webapi/Models/Request/CustomPlugin.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. using System.Text.Json.Serialization; -namespace SemanticKernel.Service.CopilotChat.Models; +namespace CopilotChat.WebApi.Models.Request; /// /// Custom plugin imported from ChatGPT Manifest file. diff --git a/webapi/CopilotChat/Models/DocumentData.cs b/webapi/Models/Request/DocumentData.cs similarity index 92% rename from webapi/CopilotChat/Models/DocumentData.cs rename to webapi/Models/Request/DocumentData.cs index 04f937df1..5eedba8d9 100644 --- a/webapi/CopilotChat/Models/DocumentData.cs +++ b/webapi/Models/Request/DocumentData.cs @@ -2,7 +2,7 @@ using System.Text.Json.Serialization; -namespace SemanticKernel.Service.CopilotChat.Models; +namespace CopilotChat.WebApi.Models.Request; public sealed class DocumentData { diff --git a/webapi/CopilotChat/Models/DocumentImportForm.cs b/webapi/Models/Request/DocumentImportForm.cs similarity index 96% rename from webapi/CopilotChat/Models/DocumentImportForm.cs rename to webapi/Models/Request/DocumentImportForm.cs index e259734b7..9d6768eb0 100644 --- a/webapi/CopilotChat/Models/DocumentImportForm.cs +++ b/webapi/Models/Request/DocumentImportForm.cs @@ -5,7 +5,7 @@ using System.Linq; using Microsoft.AspNetCore.Http; -namespace SemanticKernel.Service.CopilotChat.Models; +namespace CopilotChat.WebApi.Models.Request; /// /// Form for importing a document from a POST Http request. diff --git a/webapi/Models/AskResult.cs b/webapi/Models/Response/AskResult.cs similarity index 87% rename from webapi/Models/AskResult.cs rename to webapi/Models/Response/AskResult.cs index 27349b9a7..c6f4f6175 100644 --- a/webapi/Models/AskResult.cs +++ b/webapi/Models/Response/AskResult.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; -namespace SemanticKernel.Service.Models; +namespace CopilotChat.WebApi.Models.Response; public class AskResult { diff --git a/webapi/CopilotChat/Models/Bot.cs b/webapi/Models/Response/Bot.cs similarity index 93% rename from webapi/CopilotChat/Models/Bot.cs rename to webapi/Models/Response/Bot.cs index a5c760ffc..b51338746 100644 --- a/webapi/CopilotChat/Models/Bot.cs +++ b/webapi/Models/Response/Bot.cs @@ -2,9 +2,10 @@ using System.Collections.Generic; using Microsoft.SemanticKernel.Memory; -using SemanticKernel.Service.CopilotChat.Options; +using CopilotChat.WebApi.Options; +using CopilotChat.WebApi.Models.Storage; -namespace SemanticKernel.Service.CopilotChat.Models; +namespace CopilotChat.WebApi.Models.Response; /// /// The data model of a bot for portability. diff --git a/webapi/CopilotChat/Models/BotEmbeddingConfig.cs b/webapi/Models/Response/BotEmbeddingConfig.cs similarity index 88% rename from webapi/CopilotChat/Models/BotEmbeddingConfig.cs rename to webapi/Models/Response/BotEmbeddingConfig.cs index e8e4768a1..966efffd0 100644 --- a/webapi/CopilotChat/Models/BotEmbeddingConfig.cs +++ b/webapi/Models/Response/BotEmbeddingConfig.cs @@ -2,9 +2,9 @@ using System.ComponentModel.DataAnnotations; using System.Text.Json.Serialization; -using SemanticKernel.Service.Options; +using CopilotChat.WebApi.Options; -namespace SemanticKernel.Service.CopilotChat.Models; +namespace CopilotChat.WebApi.Models.Response; /// /// The embedding configuration of a bot. Used in the Bot object for portability. diff --git a/webapi/CopilotChat/Models/BotResponsePrompt.cs b/webapi/Models/Response/BotResponsePrompt.cs similarity index 98% rename from webapi/CopilotChat/Models/BotResponsePrompt.cs rename to webapi/Models/Response/BotResponsePrompt.cs index dfbd8a121..44b87c8a5 100644 --- a/webapi/CopilotChat/Models/BotResponsePrompt.cs +++ b/webapi/Models/Response/BotResponsePrompt.cs @@ -2,7 +2,7 @@ using System.Text.Json.Serialization; -namespace SemanticKernel.Service.CopilotChat.Models; +namespace CopilotChat.WebApi.Models.Response; /// /// The fianl prompt sent to generate bot response. diff --git a/webapi/CopilotChat/Models/Responses/CreateChatResponse.cs b/webapi/Models/Response/CreateChatResponse.cs similarity index 90% rename from webapi/CopilotChat/Models/Responses/CreateChatResponse.cs rename to webapi/Models/Response/CreateChatResponse.cs index d9fee9869..eeb4d4d1e 100644 --- a/webapi/CopilotChat/Models/Responses/CreateChatResponse.cs +++ b/webapi/Models/Response/CreateChatResponse.cs @@ -1,8 +1,9 @@ // Copyright (c) Microsoft. All rights reserved. using System.Text.Json.Serialization; +using CopilotChat.WebApi.Models.Storage; -namespace SemanticKernel.Service.CopilotChat.Models; +namespace CopilotChat.WebApi.Models.Response; /// /// Response object definition to the 'chatSession/create' request. diff --git a/webapi/CopilotChat/Models/DocumentMessageContent.cs b/webapi/Models/Response/DocumentMessageContent.cs similarity index 97% rename from webapi/CopilotChat/Models/DocumentMessageContent.cs rename to webapi/Models/Response/DocumentMessageContent.cs index ae5901489..0be35a024 100644 --- a/webapi/CopilotChat/Models/DocumentMessageContent.cs +++ b/webapi/Models/Response/DocumentMessageContent.cs @@ -4,8 +4,9 @@ using System.Linq; using System.Text.Json; using System.Text.Json.Serialization; +using CopilotChat.WebApi.Models.Request; -namespace SemanticKernel.Service.CopilotChat.Models; +namespace CopilotChat.WebApi.Models.Response; /// /// Value of `Content` for a `ChatMessage` of type `ChatMessageType.Document`. diff --git a/webapi/CopilotChat/Models/ProposedPlan.cs b/webapi/Models/Response/ProposedPlan.cs similarity index 95% rename from webapi/CopilotChat/Models/ProposedPlan.cs rename to webapi/Models/Response/ProposedPlan.cs index f84990b88..c8e580936 100644 --- a/webapi/CopilotChat/Models/ProposedPlan.cs +++ b/webapi/Models/Response/ProposedPlan.cs @@ -3,7 +3,7 @@ using System.Text.Json.Serialization; using Microsoft.SemanticKernel.Planning; -namespace SemanticKernel.Service.CopilotChat.Models; +namespace CopilotChat.WebApi.Models.Response; // Type of Plan public enum PlanType diff --git a/webapi/Models/Response/ServiceOptionsResponse.cs b/webapi/Models/Response/ServiceOptionsResponse.cs new file mode 100644 index 000000000..e6a124409 --- /dev/null +++ b/webapi/Models/Response/ServiceOptionsResponse.cs @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System.Collections.Generic; +using System.Linq; +using System.Text.Json.Serialization; + +namespace CopilotChat.WebApi.Models.Response; + +public class ServiceOptionsResponse +{ + /// + /// The memories store that is configured. + /// + [JsonPropertyName("memoriesStore")] + public MemoriesStoreOptionResponse MemoriesStore { get; set; } = new MemoriesStoreOptionResponse(); +} + +/// +/// Response to memoriesStoreType request. +/// +public class MemoriesStoreOptionResponse +{ + /// + /// All the available memories store types. + /// + [JsonPropertyName("types")] + public IEnumerable Types { get; set; } = Enumerable.Empty(); + + /// + /// The selected memories store type. + /// + [JsonPropertyName("selectedType")] + public string SelectedType { get; set; } = string.Empty; +} diff --git a/webapi/CopilotChat/Models/Responses/SpeechTokenResponse.cs b/webapi/Models/Response/SpeechTokenResponse.cs similarity index 85% rename from webapi/CopilotChat/Models/Responses/SpeechTokenResponse.cs rename to webapi/Models/Response/SpeechTokenResponse.cs index 977fe0de0..b71b91b03 100644 --- a/webapi/CopilotChat/Models/Responses/SpeechTokenResponse.cs +++ b/webapi/Models/Response/SpeechTokenResponse.cs @@ -1,6 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. -namespace SemanticKernel.Service.CopilotChat.Models; +namespace CopilotChat.WebApi.Models.Response; /// /// Token Response is a simple wrapper around the token and region diff --git a/webapi/CopilotChat/Models/ChatMessage.cs b/webapi/Models/Storage/ChatMessage.cs similarity index 97% rename from webapi/CopilotChat/Models/ChatMessage.cs rename to webapi/Models/Storage/ChatMessage.cs index 86c218ab6..c3cbf5d98 100644 --- a/webapi/CopilotChat/Models/ChatMessage.cs +++ b/webapi/Models/Storage/ChatMessage.cs @@ -5,9 +5,10 @@ using System.Globalization; using System.Text.Json; using System.Text.Json.Serialization; -using SemanticKernel.Service.CopilotChat.Storage; +using CopilotChat.WebApi.Models.Response; +using CopilotChat.WebApi.Storage; -namespace SemanticKernel.Service.CopilotChat.Models; +namespace CopilotChat.WebApi.Models.Storage; /// /// Information about a single chat message. @@ -133,7 +134,7 @@ public ChatMessage( string userName, string chatId, string content, - string prompt = "", + string? prompt = null, AuthorRoles authorRole = AuthorRoles.User, ChatMessageType type = ChatMessageType.Message, Dictionary? tokenUsage = null) @@ -144,7 +145,7 @@ public ChatMessage( this.ChatId = chatId; this.Content = content; this.Id = Guid.NewGuid().ToString(); - this.Prompt = prompt; + this.Prompt = prompt ?? string.Empty; this.AuthorRole = authorRole; this.Type = type; this.TokenUsage = tokenUsage; diff --git a/webapi/CopilotChat/Models/ChatParticipant.cs b/webapi/Models/Storage/ChatParticipant.cs similarity index 90% rename from webapi/CopilotChat/Models/ChatParticipant.cs rename to webapi/Models/Storage/ChatParticipant.cs index 3c28912b5..7006c9cf9 100644 --- a/webapi/CopilotChat/Models/ChatParticipant.cs +++ b/webapi/Models/Storage/ChatParticipant.cs @@ -2,9 +2,9 @@ using System; using System.Text.Json.Serialization; -using SemanticKernel.Service.CopilotChat.Storage; +using CopilotChat.WebApi.Storage; -namespace SemanticKernel.Service.CopilotChat.Models; +namespace CopilotChat.WebApi.Models.Storage; /// /// A chat participant is a user that is part of a chat. diff --git a/webapi/CopilotChat/Models/ChatSession.cs b/webapi/Models/Storage/ChatSession.cs similarity index 94% rename from webapi/CopilotChat/Models/ChatSession.cs rename to webapi/Models/Storage/ChatSession.cs index 25d315564..1ebba0c01 100644 --- a/webapi/CopilotChat/Models/ChatSession.cs +++ b/webapi/Models/Storage/ChatSession.cs @@ -2,9 +2,9 @@ using System; using System.Text.Json.Serialization; -using SemanticKernel.Service.CopilotChat.Storage; +using CopilotChat.WebApi.Storage; -namespace SemanticKernel.Service.CopilotChat.Models; +namespace CopilotChat.WebApi.Models.Storage; /// /// A chat session diff --git a/webapi/CopilotChat/Models/MemorySource.cs b/webapi/Models/Storage/MemorySource.cs similarity index 89% rename from webapi/CopilotChat/Models/MemorySource.cs rename to webapi/Models/Storage/MemorySource.cs index 2cbfcd284..1b11003aa 100644 --- a/webapi/CopilotChat/Models/MemorySource.cs +++ b/webapi/Models/Storage/MemorySource.cs @@ -2,24 +2,24 @@ using System; using System.Text.Json.Serialization; -using SemanticKernel.Service.CopilotChat.Storage; +using CopilotChat.WebApi.Storage; -namespace SemanticKernel.Service.CopilotChat.Models; +namespace CopilotChat.WebApi.Models.Storage; + +/// +/// Type of the memory source. +/// +public enum MemorySourceType +{ + // A file source. + File, +} /// /// The external memory source. /// public class MemorySource : IStorageEntity { - /// - /// Type of the memory source. - /// - public enum MemorySourceType - { - // A file source. - File, - } - /// /// Source ID that is persistent and unique. /// diff --git a/webapi/Options/AIServiceOptions.cs b/webapi/Options/AIServiceOptions.cs index 853c5ad09..8c3e58348 100644 --- a/webapi/Options/AIServiceOptions.cs +++ b/webapi/Options/AIServiceOptions.cs @@ -2,7 +2,7 @@ using System.ComponentModel.DataAnnotations; -namespace SemanticKernel.Service.Options; +namespace CopilotChat.WebApi.Options; /// /// Configuration options for AI services, such as Azure OpenAI and OpenAI. diff --git a/webapi/Options/AuthorizationOptions.cs b/webapi/Options/AuthorizationOptions.cs index d8ee6f702..0ee7d4f64 100644 --- a/webapi/Options/AuthorizationOptions.cs +++ b/webapi/Options/AuthorizationOptions.cs @@ -2,7 +2,7 @@ using System.ComponentModel.DataAnnotations; -namespace SemanticKernel.Service.Options; +namespace CopilotChat.WebApi.Options; /// /// Configuration options for authorizing to the service. diff --git a/webapi/Options/AzureCognitiveSearchOptions.cs b/webapi/Options/AzureCognitiveSearchOptions.cs index ee9707939..c965480df 100644 --- a/webapi/Options/AzureCognitiveSearchOptions.cs +++ b/webapi/Options/AzureCognitiveSearchOptions.cs @@ -2,7 +2,7 @@ using System.ComponentModel.DataAnnotations; -namespace SemanticKernel.Service.Options; +namespace CopilotChat.WebApi.Options; /// /// Configuration settings for connecting to Azure Cognitive Search. diff --git a/webapi/CopilotChat/Options/AzureFormRecognizerOptions.cs b/webapi/Options/AzureFormRecognizerOptions.cs similarity index 88% rename from webapi/CopilotChat/Options/AzureFormRecognizerOptions.cs rename to webapi/Options/AzureFormRecognizerOptions.cs index a3c47c727..ecbed6471 100644 --- a/webapi/CopilotChat/Options/AzureFormRecognizerOptions.cs +++ b/webapi/Options/AzureFormRecognizerOptions.cs @@ -1,9 +1,8 @@ // Copyright (c) Microsoft. All rights reserved. using System.ComponentModel.DataAnnotations; -using SemanticKernel.Service.Options; -namespace SemanticKernel.Service.CopilotChat.Options; +namespace CopilotChat.WebApi.Options; /// /// Configuration options for Azure Form Recognizer OCR support. diff --git a/webapi/CopilotChat/Options/AzureSpeechOptions.cs b/webapi/Options/AzureSpeechOptions.cs similarity index 91% rename from webapi/CopilotChat/Options/AzureSpeechOptions.cs rename to webapi/Options/AzureSpeechOptions.cs index 94a442d23..81d5bdd75 100644 --- a/webapi/CopilotChat/Options/AzureSpeechOptions.cs +++ b/webapi/Options/AzureSpeechOptions.cs @@ -1,6 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. -namespace SemanticKernel.Service.CopilotChat.Options; +namespace CopilotChat.WebApi.Options; /// /// Configuration options for Azure speech recognition. diff --git a/webapi/CopilotChat/Options/BotSchemaOptions.cs b/webapi/Options/BotSchemaOptions.cs similarity index 86% rename from webapi/CopilotChat/Options/BotSchemaOptions.cs rename to webapi/Options/BotSchemaOptions.cs index 193b34935..f8ee76cfd 100644 --- a/webapi/CopilotChat/Options/BotSchemaOptions.cs +++ b/webapi/Options/BotSchemaOptions.cs @@ -1,9 +1,8 @@ // Copyright (c) Microsoft. All rights reserved. using System.ComponentModel.DataAnnotations; -using SemanticKernel.Service.Options; -namespace SemanticKernel.Service.CopilotChat.Options; +namespace CopilotChat.WebApi.Options; /// /// Configuration options for the bot file schema that is supported by this application. diff --git a/webapi/CopilotChat/Options/ChatStoreOptions.cs b/webapi/Options/ChatStoreOptions.cs similarity index 93% rename from webapi/CopilotChat/Options/ChatStoreOptions.cs rename to webapi/Options/ChatStoreOptions.cs index d0a9860f5..27f25a209 100644 --- a/webapi/CopilotChat/Options/ChatStoreOptions.cs +++ b/webapi/Options/ChatStoreOptions.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. -using SemanticKernel.Service.Options; - -namespace SemanticKernel.Service.CopilotChat.Options; +namespace CopilotChat.WebApi.Options; /// /// Configuration settings for the chat store. diff --git a/webapi/CopilotChat/Options/CosmosOptions.cs b/webapi/Options/CosmosOptions.cs similarity index 93% rename from webapi/CopilotChat/Options/CosmosOptions.cs rename to webapi/Options/CosmosOptions.cs index a639dbf49..c0e20bba4 100644 --- a/webapi/CopilotChat/Options/CosmosOptions.cs +++ b/webapi/Options/CosmosOptions.cs @@ -1,9 +1,8 @@ // Copyright (c) Microsoft. All rights reserved. using System.ComponentModel.DataAnnotations; -using SemanticKernel.Service.Options; -namespace SemanticKernel.Service.CopilotChat.Options; +namespace CopilotChat.WebApi.Options; /// /// Configuration settings for connecting to Azure CosmosDB. diff --git a/webapi/CopilotChat/Options/DocumentMemoryOptions.cs b/webapi/Options/DocumentMemoryOptions.cs similarity index 95% rename from webapi/CopilotChat/Options/DocumentMemoryOptions.cs rename to webapi/Options/DocumentMemoryOptions.cs index 3df81b1cc..ce0c22426 100644 --- a/webapi/CopilotChat/Options/DocumentMemoryOptions.cs +++ b/webapi/Options/DocumentMemoryOptions.cs @@ -1,9 +1,8 @@ // Copyright (c) Microsoft. All rights reserved. using System.ComponentModel.DataAnnotations; -using SemanticKernel.Service.Options; -namespace SemanticKernel.Service.CopilotChat.Options; +namespace CopilotChat.WebApi.Options; /// /// Configuration options for handling memorized documents. diff --git a/webapi/CopilotChat/Options/FileSystemOptions.cs b/webapi/Options/FileSystemOptions.cs similarity index 81% rename from webapi/CopilotChat/Options/FileSystemOptions.cs rename to webapi/Options/FileSystemOptions.cs index e4bb92b6b..c6c62cadf 100644 --- a/webapi/CopilotChat/Options/FileSystemOptions.cs +++ b/webapi/Options/FileSystemOptions.cs @@ -1,9 +1,8 @@ // Copyright (c) Microsoft. All rights reserved. using System.ComponentModel.DataAnnotations; -using SemanticKernel.Service.Options; -namespace SemanticKernel.Service.CopilotChat.Options; +namespace CopilotChat.WebApi.Options; /// /// File system storage configuration. diff --git a/webapi/Options/MemoriesStoreOptions.cs b/webapi/Options/MemoriesStoreOptions.cs index 2d7a40610..7486b4cba 100644 --- a/webapi/Options/MemoriesStoreOptions.cs +++ b/webapi/Options/MemoriesStoreOptions.cs @@ -1,6 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. -namespace SemanticKernel.Service.Options; +namespace CopilotChat.WebApi.Options; /// /// Configuration settings for the memories store. diff --git a/webapi/Options/NotEmptyOrWhitespaceAttribute.cs b/webapi/Options/NotEmptyOrWhitespaceAttribute.cs index f314d9b6d..a38dd2857 100644 --- a/webapi/Options/NotEmptyOrWhitespaceAttribute.cs +++ b/webapi/Options/NotEmptyOrWhitespaceAttribute.cs @@ -3,7 +3,7 @@ using System; using System.ComponentModel.DataAnnotations; -namespace SemanticKernel.Service.Options; +namespace CopilotChat.WebApi.Options; /// /// If the string is set, it must not be empty or whitespace. diff --git a/webapi/CopilotChat/Options/OcrSupportOptions.cs b/webapi/Options/OcrSupportOptions.cs similarity index 92% rename from webapi/CopilotChat/Options/OcrSupportOptions.cs rename to webapi/Options/OcrSupportOptions.cs index 6667498c6..bfc90ecd5 100644 --- a/webapi/CopilotChat/Options/OcrSupportOptions.cs +++ b/webapi/Options/OcrSupportOptions.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. -using SemanticKernel.Service.Options; - -namespace SemanticKernel.Service.CopilotChat.Options; +namespace CopilotChat.WebApi.Options; /// /// Ocr Support Configuration Options diff --git a/webapi/CopilotChat/Options/PlannerOptions.cs b/webapi/Options/PlannerOptions.cs similarity index 95% rename from webapi/CopilotChat/Options/PlannerOptions.cs rename to webapi/Options/PlannerOptions.cs index 59dbd09f6..0e24cdbc6 100644 --- a/webapi/CopilotChat/Options/PlannerOptions.cs +++ b/webapi/Options/PlannerOptions.cs @@ -1,8 +1,8 @@ // Copyright (c) Microsoft. All rights reserved. using System.ComponentModel.DataAnnotations; -using SemanticKernel.Service.CopilotChat.Models; +using CopilotChat.WebApi.Models.Response; -namespace SemanticKernel.Service.CopilotChat.Options; +namespace CopilotChat.WebApi.Options; /// /// Configuration options for the planner. diff --git a/webapi/CopilotChat/Options/PromptsOptions.cs b/webapi/Options/PromptsOptions.cs similarity index 98% rename from webapi/CopilotChat/Options/PromptsOptions.cs rename to webapi/Options/PromptsOptions.cs index f11071e05..e66656a93 100644 --- a/webapi/CopilotChat/Options/PromptsOptions.cs +++ b/webapi/Options/PromptsOptions.cs @@ -2,9 +2,8 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using SemanticKernel.Service.Options; -namespace SemanticKernel.Service.CopilotChat.Options; +namespace CopilotChat.WebApi.Options; /// /// Configuration options for the chat diff --git a/webapi/Options/QdrantOptions.cs b/webapi/Options/QdrantOptions.cs index 95e30f99d..bc961a921 100644 --- a/webapi/Options/QdrantOptions.cs +++ b/webapi/Options/QdrantOptions.cs @@ -2,7 +2,7 @@ using System.ComponentModel.DataAnnotations; -namespace SemanticKernel.Service.Options; +namespace CopilotChat.WebApi.Options; /// /// Configuration settings for connecting to Qdrant. diff --git a/webapi/Options/RequiredOnPropertyValueAttribute.cs b/webapi/Options/RequiredOnPropertyValueAttribute.cs index 699574d02..a46a395a7 100644 --- a/webapi/Options/RequiredOnPropertyValueAttribute.cs +++ b/webapi/Options/RequiredOnPropertyValueAttribute.cs @@ -4,7 +4,7 @@ using System.ComponentModel.DataAnnotations; using System.Reflection; -namespace SemanticKernel.Service.Options; +namespace CopilotChat.WebApi.Options; /// /// If the other property is set to the expected value, then this property is required. diff --git a/webapi/Options/ServiceOptions.cs b/webapi/Options/ServiceOptions.cs index 31f7d6d81..475dbb1e2 100644 --- a/webapi/Options/ServiceOptions.cs +++ b/webapi/Options/ServiceOptions.cs @@ -2,7 +2,7 @@ using System.ComponentModel.DataAnnotations; -namespace SemanticKernel.Service.Options; +namespace CopilotChat.WebApi.Options; /// /// Configuration options for the CopilotChat service. diff --git a/webapi/CopilotChat/Options/TesseractOptions.cs b/webapi/Options/TesseractOptions.cs similarity index 87% rename from webapi/CopilotChat/Options/TesseractOptions.cs rename to webapi/Options/TesseractOptions.cs index 0fe50f104..493affff3 100644 --- a/webapi/CopilotChat/Options/TesseractOptions.cs +++ b/webapi/Options/TesseractOptions.cs @@ -1,9 +1,8 @@ // Copyright (c) Microsoft. All rights reserved. using System.ComponentModel.DataAnnotations; -using SemanticKernel.Service.Options; -namespace SemanticKernel.Service.CopilotChat.Options; +namespace CopilotChat.WebApi.Options; /// /// Configuration options for Tesseract OCR support. diff --git a/webapi/Options/VectorMemoryWebOptions.cs b/webapi/Options/VectorMemoryWebOptions.cs index 590014b5c..78b41e2e3 100644 --- a/webapi/Options/VectorMemoryWebOptions.cs +++ b/webapi/Options/VectorMemoryWebOptions.cs @@ -2,7 +2,7 @@ using System.ComponentModel.DataAnnotations; -namespace SemanticKernel.Service.Options; +namespace CopilotChat.WebApi.Options; /// /// Configuration settings for connecting to a vector memory web service. diff --git a/webapi/Program.cs b/webapi/Program.cs index 4f4a98863..9ecae88f0 100644 --- a/webapi/Program.cs +++ b/webapi/Program.cs @@ -12,12 +12,11 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using SemanticKernel.Service.CopilotChat.Extensions; -using SemanticKernel.Service.CopilotChat.Hubs; -using SemanticKernel.Service.Diagnostics; -using SemanticKernel.Service.Services; +using CopilotChat.WebApi.Extensions; +using CopilotChat.WebApi.Hubs; +using CopilotChat.WebApi.Services; -namespace SemanticKernel.Service; +namespace CopilotChat.WebApi; /// /// Copilot Chat Service @@ -37,18 +36,14 @@ public static async Task Main(string[] args) builder.Host.AddConfiguration(); builder.WebHost.UseUrls(); // Disables endpoint override warning message when using IConfiguration for Kestrel endpoint. - // Add in configuration options and Semantic Kernel services. + // Add in configuration options and required services. builder.Services .AddSingleton(sp => sp.GetRequiredService>()) // some services require an un-templated ILogger .AddOptions(builder.Configuration) - .AddSemanticKernelServices(); - - // Add CopilotChat services. - builder.Services - .AddCopilotChatOptions(builder.Configuration) - .AddCopilotChatPlannerServices() + .AddPlannerServices() .AddPersistentChatStore() - .AddPersistentOcrSupport(); + .AddPersistentOcrSupport() + .AddSemanticKernelServices(); // Add SignalR as the real time relay service builder.Services.AddSignalR(); @@ -70,7 +65,7 @@ public static async Task Main(string[] args) .AddAuthorization(builder.Configuration) .AddEndpointsApiExplorer() .AddSwaggerGen() - .AddCors() + .AddCorsPolicy() .AddControllers(); builder.Services.AddHealthChecks(); diff --git a/webapi/README.md b/webapi/README.md index af72ef6ca..05afa6b50 100644 --- a/webapi/README.md +++ b/webapi/README.md @@ -1,4 +1,4 @@ -# Semantic Kernel Service - CopilotChat +# Copilot Chat Web API This ASP.Net web application provides a web service hosting the Semantic Kernel, enabling secure and modular access to its features for the Copilot Chat application without embedding kernel code and settings, @@ -74,14 +74,14 @@ You can start the WebApi service using the command-line, Visual Studio Code, or # Enabling Sequential Planner -If you want to use SequentialPlanner (multi-step) instead ActionPlanner (single-step), we recommend using `gpt-4` or `gpt-3.5-turbo` as the planner model. **SequentialPlanner works best with `gpt-4`.** Using `gpt-3.5-turbo` will require with a relevancy filter. +If you want to use SequentialPlanner (multi-step) instead ActionPlanner (single-step), we recommend using `gpt-4` or `gpt-3.5-turbo` as the planner model. **SequentialPlanner works best with `gpt-4`.** Using `gpt-3.5-turbo` will require using a relevancy filter. To enable sequential planner, 1. In [./webapi/appsettings.json](appsettings.json), set `"Type": "Sequential"` under the `Planner` section. 1. Then, set your preferred Planner model (`gpt-4` or `gpt-3.5-turbo`) under the `AIService` configuration section. 1. If using `gpt-4`, no other changes are required. - 1. If using `gpt-3.5-turbo`: change [CopilotChatPlanner.cs](CopilotChat/Skills/ChatSkills/CopilotChatPlanner.cs) to initialize SequentialPlanner with a RelevancyThreshold\*. + 1. If using `gpt-3.5-turbo`: change [CopilotChatPlanner.cs](Skills/ChatSkills/CopilotChatPlanner.cs) to initialize SequentialPlanner with a RelevancyThreshold\*. - Add `using` statement to top of file: ``` using Microsoft.SemanticKernel.Planning.Sequential; diff --git a/webapi/ServiceExtensions.cs b/webapi/ServiceExtensions.cs deleted file mode 100644 index 06c0baaae..000000000 --- a/webapi/ServiceExtensions.cs +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System; -using System.Collections.Generic; -using System.Reflection; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authentication.JwtBearer; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using Microsoft.Identity.Web; -using SemanticKernel.Service.Auth; -using SemanticKernel.Service.Options; - -namespace SemanticKernel.Service; - -internal static class ServicesExtensions -{ - /// - /// Parse configuration into options. - /// - internal static IServiceCollection AddOptions(this IServiceCollection services, ConfigurationManager configuration) - { - // General configuration - services.AddOptions() - .Bind(configuration.GetSection(ServiceOptions.PropertyName)) - .ValidateDataAnnotations() - .ValidateOnStart() - .PostConfigure(TrimStringProperties); - - // Default AI service configurations for Semantic Kernel - services.AddOptions() - .Bind(configuration.GetSection(AIServiceOptions.PropertyName)) - .ValidateDataAnnotations() - .ValidateOnStart() - .PostConfigure(TrimStringProperties); - - // Authorization configuration - services.AddOptions() - .Bind(configuration.GetSection(AuthorizationOptions.PropertyName)) - .ValidateOnStart() - .ValidateDataAnnotations() - .PostConfigure(TrimStringProperties); - - // Memory store configuration - services.AddOptions() - .Bind(configuration.GetSection(MemoriesStoreOptions.PropertyName)) - .ValidateDataAnnotations() - .ValidateOnStart() - .PostConfigure(TrimStringProperties); - - return services; - } - - /// - /// Add CORS settings. - /// - internal static IServiceCollection AddCors(this IServiceCollection services) - { - IConfiguration configuration = services.BuildServiceProvider().GetRequiredService(); - string[] allowedOrigins = configuration.GetSection("AllowedOrigins").Get() ?? Array.Empty(); - if (allowedOrigins.Length > 0) - { - services.AddCors(options => - { - options.AddDefaultPolicy( - policy => - { - policy.WithOrigins(allowedOrigins) - .AllowAnyHeader(); - }); - }); - } - - return services; - } - - /// - /// Add authorization services - /// - internal static IServiceCollection AddAuthorization(this IServiceCollection services, IConfiguration configuration) - { - AuthorizationOptions config = services.BuildServiceProvider().GetRequiredService>().Value; - switch (config.Type) - { - case AuthorizationOptions.AuthorizationType.AzureAd: - services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) - .AddMicrosoftIdentityWebApi(configuration.GetSection($"{AuthorizationOptions.PropertyName}:AzureAd")); - break; - - case AuthorizationOptions.AuthorizationType.ApiKey: - services.AddAuthentication(ApiKeyAuthenticationHandler.AuthenticationScheme) - .AddScheme( - ApiKeyAuthenticationHandler.AuthenticationScheme, - options => options.ApiKey = config.ApiKey); - break; - - case AuthorizationOptions.AuthorizationType.None: - services.AddAuthentication(PassThroughAuthenticationHandler.AuthenticationScheme) - .AddScheme( - authenticationScheme: PassThroughAuthenticationHandler.AuthenticationScheme, - configureOptions: null); - break; - - default: - throw new InvalidOperationException($"Invalid authorization type '{config.Type}'."); - } - - return services; - } - - /// - /// Trim all string properties, recursively. - /// - private static void TrimStringProperties(T options) where T : class - { - Queue targets = new(); - targets.Enqueue(options); - - while (targets.Count > 0) - { - object target = targets.Dequeue(); - Type targetType = target.GetType(); - foreach (PropertyInfo property in targetType.GetProperties()) - { - // Skip enumerations - if (property.PropertyType.IsEnum) - { - continue; - } - - // Property is a built-in type, readable, and writable. - if (property.PropertyType.Namespace == "System" && - property.CanRead && - property.CanWrite) - { - // Property is a non-null string. - if (property.PropertyType == typeof(string) && - property.GetValue(target) != null) - { - property.SetValue(target, property.GetValue(target)!.ToString()!.Trim()); - } - } - else - { - // Property is a non-built-in and non-enum type - queue it for processing. - if (property.GetValue(target) != null) - { - targets.Enqueue(property.GetValue(target)!); - } - } - } - } - } -} diff --git a/webapi/Services/AppInsightsTelemetryService.cs b/webapi/Services/AppInsightsTelemetryService.cs index 62b9ab0b0..b424b8c1b 100644 --- a/webapi/Services/AppInsightsTelemetryService.cs +++ b/webapi/Services/AppInsightsTelemetryService.cs @@ -4,9 +4,8 @@ using System.Security.Claims; using Microsoft.ApplicationInsights; using Microsoft.AspNetCore.Http; -using SemanticKernel.Service.Diagnostics; -namespace SemanticKernel.Service.Services; +namespace CopilotChat.WebApi.Services; /// /// Implementation of the telemetry service interface for Azure Application Insights (AppInsights). diff --git a/webapi/Services/AppInsightsUserTelemetryInitializerService.cs b/webapi/Services/AppInsightsUserTelemetryInitializerService.cs index 49f3033a5..c0097f4d0 100644 --- a/webapi/Services/AppInsightsUserTelemetryInitializerService.cs +++ b/webapi/Services/AppInsightsUserTelemetryInitializerService.cs @@ -5,7 +5,7 @@ using Microsoft.ApplicationInsights.Extensibility; using Microsoft.AspNetCore.Http; -namespace SemanticKernel.Service.Services; +namespace CopilotChat.WebApi.Services; /// /// A telemetry initializer used by the TelemetryClient to fill in data for requests. diff --git a/webapi/Services/AzureFormRecognizerOcrEngine.cs b/webapi/Services/AzureFormRecognizerOcrEngine.cs index 8d0d2118f..f8c12fc39 100644 --- a/webapi/Services/AzureFormRecognizerOcrEngine.cs +++ b/webapi/Services/AzureFormRecognizerOcrEngine.cs @@ -10,7 +10,7 @@ using Azure.AI.FormRecognizer.Models; using Microsoft.AspNetCore.Http; -namespace SemanticKernel.Service.Services; +namespace CopilotChat.WebApi.Services; /// /// Wrapper for the Azure.AI.FormRecognizer. This allows Form Recognizer to be used as the OCR engine for reading text from files with an image MIME type. diff --git a/webapi/Services/IOcrEngine.cs b/webapi/Services/IOcrEngine.cs index bd1527676..e039d0980 100644 --- a/webapi/Services/IOcrEngine.cs +++ b/webapi/Services/IOcrEngine.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Http; -namespace SemanticKernel.Service.Services; +namespace CopilotChat.WebApi.Services; /// /// An OCR engine that can read in text from image MIME type files. diff --git a/webapi/Diagnostics/ITelemetryService.cs b/webapi/Services/ITelemetryService.cs similarity index 92% rename from webapi/Diagnostics/ITelemetryService.cs rename to webapi/Services/ITelemetryService.cs index 8c4724261..46c65a8e2 100644 --- a/webapi/Diagnostics/ITelemetryService.cs +++ b/webapi/Services/ITelemetryService.cs @@ -1,6 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. -namespace SemanticKernel.Service.Diagnostics; +namespace CopilotChat.WebApi.Services; /// /// Interface for common telemetry events to track actions across the semantic kernel. diff --git a/webapi/Services/NullOcrEngine.cs b/webapi/Services/NullOcrEngine.cs index 7b80d9fd5..c7892c9a1 100644 --- a/webapi/Services/NullOcrEngine.cs +++ b/webapi/Services/NullOcrEngine.cs @@ -4,7 +4,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Http; -namespace SemanticKernel.Service.Services; +namespace CopilotChat.WebApi.Services; /// /// Used as a placeholder implementation when "none" is set in the OcrSupport:Type field in the configuration. diff --git a/webapi/Services/TesseractEngineWrapper.cs b/webapi/Services/TesseractEngineWrapper.cs index 4f11d5a53..6ee7e401f 100644 --- a/webapi/Services/TesseractEngineWrapper.cs +++ b/webapi/Services/TesseractEngineWrapper.cs @@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Http; using Tesseract; -namespace SemanticKernel.Service.Services; +namespace CopilotChat.WebApi.Services; /// /// Wrapper for the TesseractEngine within the Tesseract OCR library. This is used to allow the TesseractEngine to be mocked in the event that the Tesseract language file is not installed. diff --git a/webapi/CopilotChat/Skills/ChatSkills/ChatSkill.cs b/webapi/Skills/ChatSkills/ChatSkill.cs similarity index 90% rename from webapi/CopilotChat/Skills/ChatSkills/ChatSkill.cs rename to webapi/Skills/ChatSkills/ChatSkill.cs index 2bd2ff504..749cb85a9 100644 --- a/webapi/CopilotChat/Skills/ChatSkills/ChatSkill.cs +++ b/webapi/Skills/ChatSkills/ChatSkill.cs @@ -15,16 +15,17 @@ using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.AI.ChatCompletion; using Microsoft.SemanticKernel.AI.TextCompletion; -using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Orchestration; using Microsoft.SemanticKernel.SkillDefinition; using Microsoft.SemanticKernel.TemplateEngine; -using SemanticKernel.Service.CopilotChat.Hubs; -using SemanticKernel.Service.CopilotChat.Models; -using SemanticKernel.Service.CopilotChat.Options; -using SemanticKernel.Service.CopilotChat.Storage; +using CopilotChat.WebApi.Hubs; +using CopilotChat.WebApi.Options; +using CopilotChat.WebApi.Storage; +using CopilotChat.WebApi.Models.Storage; +using CopilotChat.WebApi.Models.Response; +using CopilotChat.WebApi.Extensions; -namespace SemanticKernel.Service.CopilotChat.Skills.ChatSkills; +namespace CopilotChat.WebApi.Skills.ChatSkills; /// /// ChatSkill offers a more coherent chat experience by using memories @@ -150,12 +151,7 @@ public async Task ExtractUserIntentAsync(SKContext context) // Get token usage from ChatCompletion result and add to context TokenUtilities.GetFunctionTokenUsage(result, context, "SystemIntentExtraction"); - if (result.ErrorOccurred) - { - context.Log.LogError("{0}: {1}", result.LastErrorDescription, result.LastException); - context.Fail(result.LastErrorDescription); - return string.Empty; - } + result.ThrowIfFailed(); return $"User intent: {result}"; } @@ -196,12 +192,7 @@ public async Task ExtractAudienceAsync(SKContext context) // Get token usage from ChatCompletion result and add to context TokenUtilities.GetFunctionTokenUsage(result, context, "SystemAudienceExtraction"); - if (result.ErrorOccurred) - { - context.Log.LogError("{0}: {1}", result.LastErrorDescription, result.LastException); - context.Fail(result.LastErrorDescription); - return string.Empty; - } + result.ThrowIfFailed(); return $"List of participants: {result}"; } @@ -220,7 +211,7 @@ public async Task ExtractChatHistoryAsync( var remainingToken = tokenLimit; - string historyText = ""; + string historyText = string.Empty; foreach (var chatMessage in sortedMessages) { var formattedMessage = chatMessage.ToFormattedString(); @@ -274,7 +265,7 @@ public async Task ChatAsync( [Description("Previously proposed plan that is approved"), DefaultValue(null), SKName("proposedPlan")] string? planJson, [Description("ID of the response message for planner"), DefaultValue(null), SKName("responseMessageId")] string? messageId, SKContext context, - CancellationToken cancellationToken) + CancellationToken cancellationToken = default) { // Set the system description in the prompt options await this.SetSystemDescriptionAsync(chatId); @@ -296,7 +287,7 @@ public async Task ChatAsync( await this.UpdateChatMessageContentAsync(planJson, messageId); } - ChatMessage? chatMessage; + ChatMessage chatMessage; if (chatContext.Variables.ContainsKey("userCancelledPlan")) { // Save hardcoded response if user cancelled plan @@ -308,11 +299,6 @@ public async Task ChatAsync( chatMessage = await this.GetChatResponseAsync(chatId, userId, chatContext, cancellationToken); } - if (chatMessage == null) - { - context.Fail(chatContext.LastErrorDescription); - return context; - } context.Variables.Update(chatMessage.Content); if (chatMessage.TokenUsage != null) @@ -321,7 +307,7 @@ public async Task ChatAsync( } else { - context.Log.LogWarning("ChatSkill token usage unknown. Ensure token management has been implemented correctly."); + context.Logger.LogWarning("ChatSkill token usage unknown. Ensure token management has been implemented correctly."); } return context; @@ -336,23 +322,17 @@ public async Task ChatAsync( /// The user ID /// The SKContext. /// The created chat message containing the model-generated response. - private async Task GetChatResponseAsync(string chatId, string userId, SKContext chatContext, CancellationToken cancellationToken) + private async Task GetChatResponseAsync(string chatId, string userId, SKContext chatContext, CancellationToken cancellationToken) { // Get the audience await this.UpdateBotResponseStatusOnClient(chatId, "Extracting audience"); var audience = await this.GetAudienceAsync(chatContext); - if (chatContext.ErrorOccurred) - { - return null; - } + chatContext.ThrowIfFailed(); // Extract user intent from the conversation history. await this.UpdateBotResponseStatusOnClient(chatId, "Extracting user intent"); var userIntent = await this.GetUserIntentAsync(chatContext); - if (chatContext.ErrorOccurred) - { - return null; - } + chatContext.ThrowIfFailed(); chatContext.Variables.Set("audience", audience); chatContext.Variables.Set("userIntent", userIntent); @@ -365,10 +345,7 @@ public async Task ChatAsync( await this.UpdateBotResponseStatusOnClient(chatId, "Acquiring external information from planner"); var externalInformationTokenLimit = (int)(remainingToken * this._promptOptions.ExternalInformationContextWeight); var planResult = await this.AcquireExternalInformationAsync(chatContext, userIntent, externalInformationTokenLimit); - if (chatContext.ErrorOccurred) - { - return null; - } + chatContext.ThrowIfFailed(); // If plan is suggested, send back to user for approval before running var proposedPlan = this._externalInformationSkill.ProposedPlan; @@ -389,19 +366,12 @@ public async Task ChatAsync( await this.UpdateBotResponseStatusOnClient(chatId, "Extracting semantic and document memories"); var chatMemoriesTokenLimit = (int)(remainingToken * this._promptOptions.MemoriesResponseContextWeight); var documentContextTokenLimit = (int)(remainingToken * this._promptOptions.DocumentContextWeight); - string[] tasks; - try - { - tasks = await Task.WhenAll( - this._semanticChatMemorySkill.QueryMemoriesAsync(userIntent, chatId, chatMemoriesTokenLimit, this._kernel.Memory), - this._documentMemorySkill.QueryDocumentsAsync(userIntent, chatId, documentContextTokenLimit, this._kernel.Memory) - ); - } - catch (SKException ex) - { - chatContext.Fail(ex.Message, ex); - return null; - } + + string[] tasks = await Task.WhenAll( + this._semanticChatMemorySkill.QueryMemoriesAsync(userIntent, chatId, chatMemoriesTokenLimit, this._kernel.Memory), + this._documentMemorySkill.QueryDocumentsAsync(userIntent, chatId, documentContextTokenLimit, this._kernel.Memory) + ); + var chatMemories = tasks[0]; var documentMemories = tasks[1]; @@ -414,10 +384,7 @@ public async Task ChatAsync( { await this.UpdateBotResponseStatusOnClient(chatId, "Extracting chat history"); chatHistory = await this.ExtractChatHistoryAsync(chatId, chatHistoryTokenLimit); - if (chatContext.ErrorOccurred) - { - return null; - } + chatContext.ThrowIfFailed(); chatContextText = $"{chatContextText}\n{chatHistory}"; } @@ -428,7 +395,10 @@ public async Task ChatAsync( // Render the prompt var promptRenderer = new PromptTemplateEngine(); - var renderedPrompt = await promptRenderer.RenderAsync(this._promptOptions.SystemChatPrompt, chatContext, cancellationToken); + var renderedPrompt = await promptRenderer.RenderAsync( + this._promptOptions.SystemChatPrompt, + chatContext, + cancellationToken); chatContext.Variables.Set("prompt", renderedPrompt); // Need to extract this from the rendered prompt because Time and Date are calculated during render @@ -436,12 +406,9 @@ public async Task ChatAsync( var promptView = new BotResponsePrompt(renderedPrompt, this._promptOptions.SystemDescription, this._promptOptions.SystemResponse, audience, userIntent, chatMemories, documentMemories, planResult, chatHistory, systemChatContinuation); // Calculate token usage of prompt template - chatContext.Variables.Set(TokenUtilities.GetFunctionKey(chatContext.Log, "SystemMetaPrompt")!, TokenUtilities.TokenCount(renderedPrompt).ToString(CultureInfo.InvariantCulture)); + chatContext.Variables.Set(TokenUtilities.GetFunctionKey(chatContext.Logger, "SystemMetaPrompt")!, TokenUtilities.TokenCount(renderedPrompt).ToString(CultureInfo.InvariantCulture)); - if (chatContext.ErrorOccurred) - { - return null; - } + chatContext.ThrowIfFailed(); // Stream the response to the client await this.UpdateBotResponseStatusOnClient(chatId, "Generating bot response"); @@ -480,17 +447,14 @@ private async Task GetAudienceAsync(SKContext context) var audience = await this.ExtractAudienceAsync(audienceContext); // Copy token usage into original chat context - var functionKey = TokenUtilities.GetFunctionKey(context.Log, "SystemAudienceExtraction")!; + var functionKey = TokenUtilities.GetFunctionKey(context.Logger, "SystemAudienceExtraction")!; if (audienceContext.Variables.TryGetValue(functionKey, out string? tokenUsage)) { context.Variables.Set(functionKey, tokenUsage); } // Propagate the error - if (audienceContext.ErrorOccurred) - { - context.Fail(audienceContext.LastErrorDescription); - } + audienceContext.ThrowIfFailed(); return audience; } @@ -509,17 +473,13 @@ private async Task GetUserIntentAsync(SKContext context) userIntent = await this.ExtractUserIntentAsync(intentContext); // Copy token usage into original chat context - var functionKey = TokenUtilities.GetFunctionKey(context.Log, "SystemIntentExtraction")!; + var functionKey = TokenUtilities.GetFunctionKey(context.Logger, "SystemIntentExtraction")!; if (intentContext.Variables.TryGetValue(functionKey!, out string? tokenUsage)) { context.Variables.Set(functionKey!, tokenUsage); } - // Propagate the error - if (intentContext.ErrorOccurred) - { - context.Fail(intentContext.LastErrorDescription); - } + intentContext.ThrowIfFailed(); } return userIntent; @@ -534,7 +494,7 @@ private async Task GetUserIntentAsync(SKContext context) /// Maximum number of tokens. private Task QueryChatMemoriesAsync(SKContext context, string userIntent, int tokenLimit) { - return this._semanticChatMemorySkill.QueryMemoriesAsync(userIntent, context["chatId"], tokenLimit, this._kernel.Memory); + return this._semanticChatMemorySkill.QueryMemoriesAsync(userIntent, context.Variables["chatId"], tokenLimit, this._kernel.Memory); } /// @@ -546,7 +506,7 @@ private Task QueryChatMemoriesAsync(SKContext context, string userIntent /// Maximum number of tokens. private Task QueryDocumentsAsync(SKContext context, string userIntent, int tokenLimit) { - return this._documentMemorySkill.QueryDocumentsAsync(userIntent, context["chatId"], tokenLimit, this._kernel.Memory); + return this._documentMemorySkill.QueryDocumentsAsync(userIntent, context.Variables["chatId"], tokenLimit, this._kernel.Memory); } /// @@ -564,10 +524,7 @@ private async Task AcquireExternalInformationAsync(SKContext context, st var plan = await this._externalInformationSkill.AcquireExternalInformationAsync(userIntent, planContext); // Propagate the error - if (planContext.ErrorOccurred) - { - context.Fail(planContext.LastErrorDescription); - } + planContext.ThrowIfFailed(); return plan; } @@ -593,7 +550,7 @@ private async Task SaveNewMessageAsync(string message, string userI userName, chatId, message, - "", + string.Empty, ChatMessage.AuthorRoles.User, // Default to a standard message if the `type` is not recognized Enum.TryParse(type, out ChatMessage.ChatMessageType typeAsEnum) && Enum.IsDefined(typeof(ChatMessage.ChatMessageType), typeAsEnum) diff --git a/webapi/CopilotChat/Skills/ChatSkills/CopilotChatPlanner.cs b/webapi/Skills/ChatSkills/CopilotChatPlanner.cs similarity index 97% rename from webapi/CopilotChat/Skills/ChatSkills/CopilotChatPlanner.cs rename to webapi/Skills/ChatSkills/CopilotChatPlanner.cs index e3a764e20..45de1572d 100644 --- a/webapi/CopilotChat/Skills/ChatSkills/CopilotChatPlanner.cs +++ b/webapi/Skills/ChatSkills/CopilotChatPlanner.cs @@ -10,10 +10,10 @@ using Microsoft.SemanticKernel.Planning; using Microsoft.SemanticKernel.Planning.Sequential; using Microsoft.SemanticKernel.SkillDefinition; -using SemanticKernel.Service.CopilotChat.Models; -using SemanticKernel.Service.CopilotChat.Options; +using CopilotChat.WebApi.Options; +using CopilotChat.WebApi.Models.Response; -namespace SemanticKernel.Service.CopilotChat.Skills.ChatSkills; +namespace CopilotChat.WebApi.Skills.ChatSkills; /// /// A lightweight wrapper around a planner to allow for curating which skills are available to it. diff --git a/webapi/CopilotChat/Skills/ChatSkills/DocumentMemorySkill.cs b/webapi/Skills/ChatSkills/DocumentMemorySkill.cs similarity index 97% rename from webapi/CopilotChat/Skills/ChatSkills/DocumentMemorySkill.cs rename to webapi/Skills/ChatSkills/DocumentMemorySkill.cs index bf58c3b1a..d1832c447 100644 --- a/webapi/CopilotChat/Skills/ChatSkills/DocumentMemorySkill.cs +++ b/webapi/Skills/ChatSkills/DocumentMemorySkill.cs @@ -9,9 +9,9 @@ using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Memory; using Microsoft.SemanticKernel.SkillDefinition; -using SemanticKernel.Service.CopilotChat.Options; +using CopilotChat.WebApi.Options; -namespace SemanticKernel.Service.CopilotChat.Skills.ChatSkills; +namespace CopilotChat.WebApi.Skills.ChatSkills; /// /// This skill provides the functions to query the document memory. diff --git a/webapi/CopilotChat/Skills/ChatSkills/ExternalInformationSkill.cs b/webapi/Skills/ChatSkills/ExternalInformationSkill.cs similarity index 94% rename from webapi/CopilotChat/Skills/ChatSkills/ExternalInformationSkill.cs rename to webapi/Skills/ChatSkills/ExternalInformationSkill.cs index dfc48ce8e..48020c2de 100644 --- a/webapi/CopilotChat/Skills/ChatSkills/ExternalInformationSkill.cs +++ b/webapi/Skills/ChatSkills/ExternalInformationSkill.cs @@ -15,12 +15,12 @@ using Microsoft.SemanticKernel.Orchestration; using Microsoft.SemanticKernel.Planning; using Microsoft.SemanticKernel.SkillDefinition; -using SemanticKernel.Service.CopilotChat.Models; -using SemanticKernel.Service.CopilotChat.Options; -using SemanticKernel.Service.CopilotChat.Skills.OpenApiPlugins.GitHubPlugin.Model; -using SemanticKernel.Service.CopilotChat.Skills.OpenApiPlugins.JiraPlugin.Model; +using CopilotChat.WebApi.Options; +using CopilotChat.WebApi.Skills.OpenApiPlugins.GitHubPlugin.Model; +using CopilotChat.WebApi.Skills.OpenApiPlugins.JiraPlugin.Model; +using CopilotChat.WebApi.Models.Response; -namespace SemanticKernel.Service.CopilotChat.Skills.ChatSkills; +namespace CopilotChat.WebApi.Skills.ChatSkills; /// /// This skill provides the functions to acquire external information. @@ -95,7 +95,7 @@ public async Task AcquireExternalInformationAsync( string planJson = JsonSerializer.Serialize(deserializedPlan.Plan); // Reload the plan with the planner's kernel so // it has full context to be executed - var newPlanContext = new SKContext(null, this._planner.Kernel.Skills, this._planner.Kernel.Log); + var newPlanContext = new SKContext(null, this._planner.Kernel.Skills, this._planner.Kernel.Logger); var plan = Plan.FromJson(planJson, newPlanContext); // Invoke plan @@ -103,7 +103,7 @@ public async Task AcquireExternalInformationAsync( var functionsUsed = $"FUNCTIONS EXECUTED: {string.Join("; ", this.GetPlanSteps(plan))}."; int tokenLimit = - int.Parse(context["tokenLimit"], new NumberFormatInfo()) - + int.Parse(context.Variables["tokenLimit"], new NumberFormatInfo()) - TokenUtilities.TokenCount(PromptPreamble) - TokenUtilities.TokenCount(PromptPostamble) - TokenUtilities.TokenCount(functionsUsed) - @@ -139,7 +139,7 @@ public async Task AcquireExternalInformationAsync( { // TODO: [Issue #2256] Remove InvalidPlan retry logic once Core team stabilizes planner try { - plan = await this._planner.CreatePlanAsync($"Given the following context, accomplish the user intent.\nContext:\n{contextString}\nUser Intent:{userIntent}", context.Log); + plan = await this._planner.CreatePlanAsync($"Given the following context, accomplish the user intent.\nContext:\n{contextString}\nUser Intent:{userIntent}", context.Logger); } catch (Exception e) when (this.IsRetriableError(e)) { @@ -149,7 +149,7 @@ public async Task AcquireExternalInformationAsync( retriesAvail = e is PlanningException ? 0 : retriesAvail--; // Retry plan creation if LLM returned response that doesn't contain valid plan (invalid XML or JSON). - context.Log.LogWarning("Retrying CreatePlan on error: {0}", e.Message); + context.Logger.LogWarning("Retrying CreatePlan on error: {0}", e.Message); continue; } throw; @@ -241,11 +241,11 @@ private bool TryExtractJsonFromOpenApiPlanResult(SKContext context, string openA } catch (JsonException) { - context.Log.LogDebug("Unable to extract JSON from planner response, it is likely not from an OpenAPI skill."); + context.Logger.LogDebug("Unable to extract JSON from planner response, it is likely not from an OpenAPI skill."); } catch (InvalidOperationException) { - context.Log.LogDebug("Unable to extract JSON from planner response, it may already be proper JSON."); + context.Logger.LogDebug("Unable to extract JSON from planner response, it may already be proper JSON."); } json = string.Empty; @@ -289,7 +289,7 @@ private string OptimizeOpenApiSkillJson(string jsonContent, int tokenLimit, Plan // Some APIs will return a JSON response with one property key representing an embedded answer. // Extract this value for further processing - string resultsDescriptor = ""; + string resultsDescriptor = string.Empty; if (document.RootElement.ValueKind == JsonValueKind.Object) { diff --git a/webapi/CopilotChat/Skills/ChatSkills/SemanticChatMemory.cs b/webapi/Skills/ChatSkills/SemanticChatMemory.cs similarity index 96% rename from webapi/CopilotChat/Skills/ChatSkills/SemanticChatMemory.cs rename to webapi/Skills/ChatSkills/SemanticChatMemory.cs index 889af6f2c..4aa82d726 100644 --- a/webapi/CopilotChat/Skills/ChatSkills/SemanticChatMemory.cs +++ b/webapi/Skills/ChatSkills/SemanticChatMemory.cs @@ -5,7 +5,7 @@ using System.Text.Json; using System.Text.Json.Serialization; -namespace SemanticKernel.Service.CopilotChat.Skills.ChatSkills; +namespace CopilotChat.WebApi.Skills.ChatSkills; /// /// A collection of semantic chat memory. diff --git a/webapi/CopilotChat/Skills/ChatSkills/SemanticChatMemoryExtractor.cs b/webapi/Skills/ChatSkills/SemanticChatMemoryExtractor.cs similarity index 96% rename from webapi/CopilotChat/Skills/ChatSkills/SemanticChatMemoryExtractor.cs rename to webapi/Skills/ChatSkills/SemanticChatMemoryExtractor.cs index 76d24939e..53319b2ff 100644 --- a/webapi/CopilotChat/Skills/ChatSkills/SemanticChatMemoryExtractor.cs +++ b/webapi/Skills/ChatSkills/SemanticChatMemoryExtractor.cs @@ -11,10 +11,10 @@ using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Memory; using Microsoft.SemanticKernel.Orchestration; -using SemanticKernel.Service.CopilotChat.Extensions; -using SemanticKernel.Service.CopilotChat.Options; +using CopilotChat.WebApi.Extensions; +using CopilotChat.WebApi.Options; -namespace SemanticKernel.Service.CopilotChat.Skills.ChatSkills; +namespace CopilotChat.WebApi.Skills.ChatSkills; /// /// Helper class to extract and create semantic memory from chat history. @@ -64,7 +64,7 @@ internal static async Task ExtractSemanticChatMemoryAsync( { // Skip semantic memory extraction for this item if it fails. // We cannot rely on the model to response with perfect Json each time. - context.Log.LogInformation("Unable to extract semantic memory for {0}: {1}. Continuing...", memoryName, ex.Message); + context.Logger.LogInformation("Unable to extract semantic memory for {0}: {1}. Continuing...", memoryName, ex.Message); continue; } } diff --git a/webapi/CopilotChat/Skills/ChatSkills/SemanticChatMemoryItem.cs b/webapi/Skills/ChatSkills/SemanticChatMemoryItem.cs similarity index 94% rename from webapi/CopilotChat/Skills/ChatSkills/SemanticChatMemoryItem.cs rename to webapi/Skills/ChatSkills/SemanticChatMemoryItem.cs index 1eef7ab91..e19ecd486 100644 --- a/webapi/CopilotChat/Skills/ChatSkills/SemanticChatMemoryItem.cs +++ b/webapi/Skills/ChatSkills/SemanticChatMemoryItem.cs @@ -2,7 +2,7 @@ using System.Text.Json.Serialization; -namespace SemanticKernel.Service.CopilotChat.Skills.ChatSkills; +namespace CopilotChat.WebApi.Skills.ChatSkills; /// /// A single entry in the chat memory. diff --git a/webapi/CopilotChat/Skills/ChatSkills/SemanticChatMemorySkill.cs b/webapi/Skills/ChatSkills/SemanticChatMemorySkill.cs similarity index 95% rename from webapi/CopilotChat/Skills/ChatSkills/SemanticChatMemorySkill.cs rename to webapi/Skills/ChatSkills/SemanticChatMemorySkill.cs index 308dd597b..fa8bff8e7 100644 --- a/webapi/CopilotChat/Skills/ChatSkills/SemanticChatMemorySkill.cs +++ b/webapi/Skills/ChatSkills/SemanticChatMemorySkill.cs @@ -10,11 +10,11 @@ using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Memory; using Microsoft.SemanticKernel.SkillDefinition; -using SemanticKernel.Service.CopilotChat.Models; -using SemanticKernel.Service.CopilotChat.Options; -using SemanticKernel.Service.CopilotChat.Storage; +using CopilotChat.WebApi.Options; +using CopilotChat.WebApi.Storage; +using CopilotChat.WebApi.Models.Storage; -namespace SemanticKernel.Service.CopilotChat.Skills.ChatSkills; +namespace CopilotChat.WebApi.Skills.ChatSkills; /// /// This skill provides the functions to query the semantic chat memory. @@ -89,7 +89,7 @@ public async Task QueryMemoriesAsync( relevantMemories = relevantMemories.OrderByDescending(m => m.Relevance).ToList(); - string memoryText = ""; + string memoryText = string.Empty; foreach (var memory in relevantMemories) { var tokenCount = TokenUtilities.TokenCount(memory.Metadata.Text); diff --git a/webapi/CopilotChat/Skills/OpenApiPlugins/GitHubPlugin/Model/Label.cs b/webapi/Skills/OpenApiPlugins/GitHubPlugin/Model/Label.cs similarity index 92% rename from webapi/CopilotChat/Skills/OpenApiPlugins/GitHubPlugin/Model/Label.cs rename to webapi/Skills/OpenApiPlugins/GitHubPlugin/Model/Label.cs index ab43d0bc0..48b1f3957 100644 --- a/webapi/CopilotChat/Skills/OpenApiPlugins/GitHubPlugin/Model/Label.cs +++ b/webapi/Skills/OpenApiPlugins/GitHubPlugin/Model/Label.cs @@ -2,7 +2,7 @@ using System.Text.Json.Serialization; -namespace SemanticKernel.Service.CopilotChat.Skills.OpenApiPlugins.GitHubPlugin.Model; +namespace CopilotChat.WebApi.Skills.OpenApiPlugins.GitHubPlugin.Model; /// /// Represents a pull request label. diff --git a/webapi/CopilotChat/Skills/OpenApiPlugins/GitHubPlugin/Model/PullRequest.cs b/webapi/Skills/OpenApiPlugins/GitHubPlugin/Model/PullRequest.cs similarity index 97% rename from webapi/CopilotChat/Skills/OpenApiPlugins/GitHubPlugin/Model/PullRequest.cs rename to webapi/Skills/OpenApiPlugins/GitHubPlugin/Model/PullRequest.cs index 07766497b..1d5ecaab5 100644 --- a/webapi/CopilotChat/Skills/OpenApiPlugins/GitHubPlugin/Model/PullRequest.cs +++ b/webapi/Skills/OpenApiPlugins/GitHubPlugin/Model/PullRequest.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using System.Text.Json.Serialization; -namespace SemanticKernel.Service.CopilotChat.Skills.OpenApiPlugins.GitHubPlugin.Model; +namespace CopilotChat.WebApi.Skills.OpenApiPlugins.GitHubPlugin.Model; /// /// Represents a GitHub Pull Request. diff --git a/webapi/CopilotChat/Skills/OpenApiPlugins/GitHubPlugin/Model/Repo.cs b/webapi/Skills/OpenApiPlugins/GitHubPlugin/Model/Repo.cs similarity index 91% rename from webapi/CopilotChat/Skills/OpenApiPlugins/GitHubPlugin/Model/Repo.cs rename to webapi/Skills/OpenApiPlugins/GitHubPlugin/Model/Repo.cs index 9352b3d20..4ccb6828b 100644 --- a/webapi/CopilotChat/Skills/OpenApiPlugins/GitHubPlugin/Model/Repo.cs +++ b/webapi/Skills/OpenApiPlugins/GitHubPlugin/Model/Repo.cs @@ -2,7 +2,7 @@ using System.Text.Json.Serialization; -namespace SemanticKernel.Service.CopilotChat.Skills.OpenApiPlugins.GitHubPlugin.Model; +namespace CopilotChat.WebApi.Skills.OpenApiPlugins.GitHubPlugin.Model; /// /// Represents a GitHub Repo. diff --git a/webapi/CopilotChat/Skills/OpenApiPlugins/GitHubPlugin/Model/User.cs b/webapi/Skills/OpenApiPlugins/GitHubPlugin/Model/User.cs similarity index 94% rename from webapi/CopilotChat/Skills/OpenApiPlugins/GitHubPlugin/Model/User.cs rename to webapi/Skills/OpenApiPlugins/GitHubPlugin/Model/User.cs index f263cbaf5..61941f891 100644 --- a/webapi/CopilotChat/Skills/OpenApiPlugins/GitHubPlugin/Model/User.cs +++ b/webapi/Skills/OpenApiPlugins/GitHubPlugin/Model/User.cs @@ -2,7 +2,7 @@ using System.Text.Json.Serialization; -namespace SemanticKernel.Service.CopilotChat.Skills.OpenApiPlugins.GitHubPlugin.Model; +namespace CopilotChat.WebApi.Skills.OpenApiPlugins.GitHubPlugin.Model; /// /// Represents a user on GitHub. diff --git a/webapi/CopilotChat/Skills/OpenApiPlugins/GitHubPlugin/openapi.json b/webapi/Skills/OpenApiPlugins/GitHubPlugin/openapi.json similarity index 100% rename from webapi/CopilotChat/Skills/OpenApiPlugins/GitHubPlugin/openapi.json rename to webapi/Skills/OpenApiPlugins/GitHubPlugin/openapi.json diff --git a/webapi/CopilotChat/Skills/OpenApiPlugins/JiraPlugin/Model/CommentAuthor.cs b/webapi/Skills/OpenApiPlugins/JiraPlugin/Model/CommentAuthor.cs similarity index 88% rename from webapi/CopilotChat/Skills/OpenApiPlugins/JiraPlugin/Model/CommentAuthor.cs rename to webapi/Skills/OpenApiPlugins/JiraPlugin/Model/CommentAuthor.cs index fe5135d6e..429f414e4 100644 --- a/webapi/CopilotChat/Skills/OpenApiPlugins/JiraPlugin/Model/CommentAuthor.cs +++ b/webapi/Skills/OpenApiPlugins/JiraPlugin/Model/CommentAuthor.cs @@ -2,7 +2,7 @@ using System.Text.Json.Serialization; -namespace SemanticKernel.Service.CopilotChat.Skills.OpenApiPlugins.JiraPlugin.Model; +namespace CopilotChat.WebApi.Skills.OpenApiPlugins.JiraPlugin.Model; /// /// Represents the Author of a comment. diff --git a/webapi/CopilotChat/Skills/OpenApiPlugins/JiraPlugin/Model/CommentResponse.cs b/webapi/Skills/OpenApiPlugins/JiraPlugin/Model/CommentResponse.cs similarity index 90% rename from webapi/CopilotChat/Skills/OpenApiPlugins/JiraPlugin/Model/CommentResponse.cs rename to webapi/Skills/OpenApiPlugins/JiraPlugin/Model/CommentResponse.cs index 4678665c2..f8b18b1bc 100644 --- a/webapi/CopilotChat/Skills/OpenApiPlugins/JiraPlugin/Model/CommentResponse.cs +++ b/webapi/Skills/OpenApiPlugins/JiraPlugin/Model/CommentResponse.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Text.Json.Serialization; -namespace SemanticKernel.Service.CopilotChat.Skills.OpenApiPlugins.JiraPlugin.Model; +namespace CopilotChat.WebApi.Skills.OpenApiPlugins.JiraPlugin.Model; /// /// Represents a the list of comments that make up a CommentResponse. diff --git a/webapi/CopilotChat/Skills/OpenApiPlugins/JiraPlugin/Model/IndividualComments.cs b/webapi/Skills/OpenApiPlugins/JiraPlugin/Model/IndividualComments.cs similarity index 91% rename from webapi/CopilotChat/Skills/OpenApiPlugins/JiraPlugin/Model/IndividualComments.cs rename to webapi/Skills/OpenApiPlugins/JiraPlugin/Model/IndividualComments.cs index 191042ad1..d8f191136 100644 --- a/webapi/CopilotChat/Skills/OpenApiPlugins/JiraPlugin/Model/IndividualComments.cs +++ b/webapi/Skills/OpenApiPlugins/JiraPlugin/Model/IndividualComments.cs @@ -2,7 +2,7 @@ using System.Text.Json.Serialization; -namespace SemanticKernel.Service.CopilotChat.Skills.OpenApiPlugins.JiraPlugin.Model; +namespace CopilotChat.WebApi.Skills.OpenApiPlugins.JiraPlugin.Model; /// /// Represents an individual comment on an issue in jira. diff --git a/webapi/CopilotChat/Skills/OpenApiPlugins/JiraPlugin/Model/IssueResponse.cs b/webapi/Skills/OpenApiPlugins/JiraPlugin/Model/IssueResponse.cs similarity index 94% rename from webapi/CopilotChat/Skills/OpenApiPlugins/JiraPlugin/Model/IssueResponse.cs rename to webapi/Skills/OpenApiPlugins/JiraPlugin/Model/IssueResponse.cs index 62da29642..2b32e6ac1 100644 --- a/webapi/CopilotChat/Skills/OpenApiPlugins/JiraPlugin/Model/IssueResponse.cs +++ b/webapi/Skills/OpenApiPlugins/JiraPlugin/Model/IssueResponse.cs @@ -2,7 +2,7 @@ using System.Text.Json.Serialization; -namespace SemanticKernel.Service.CopilotChat.Skills.OpenApiPlugins.JiraPlugin.Model; +namespace CopilotChat.WebApi.Skills.OpenApiPlugins.JiraPlugin.Model; /// /// Represents a the trimmed down response for retrieving an issue from jira. diff --git a/webapi/CopilotChat/Skills/OpenApiPlugins/JiraPlugin/Model/IssueResponseFIeld.cs b/webapi/Skills/OpenApiPlugins/JiraPlugin/Model/IssueResponseFIeld.cs similarity index 95% rename from webapi/CopilotChat/Skills/OpenApiPlugins/JiraPlugin/Model/IssueResponseFIeld.cs rename to webapi/Skills/OpenApiPlugins/JiraPlugin/Model/IssueResponseFIeld.cs index f81b12a2a..9a949da69 100644 --- a/webapi/CopilotChat/Skills/OpenApiPlugins/JiraPlugin/Model/IssueResponseFIeld.cs +++ b/webapi/Skills/OpenApiPlugins/JiraPlugin/Model/IssueResponseFIeld.cs @@ -2,7 +2,7 @@ using System.Text.Json.Serialization; -namespace SemanticKernel.Service.CopilotChat.Skills.OpenApiPlugins.JiraPlugin.Model; +namespace CopilotChat.WebApi.Skills.OpenApiPlugins.JiraPlugin.Model; /// /// Represents the fields that make up an IssueResponse. diff --git a/webapi/CopilotChat/Skills/OpenApiPlugins/JiraPlugin/openapi.json b/webapi/Skills/OpenApiPlugins/JiraPlugin/openapi.json similarity index 100% rename from webapi/CopilotChat/Skills/OpenApiPlugins/JiraPlugin/openapi.json rename to webapi/Skills/OpenApiPlugins/JiraPlugin/openapi.json diff --git a/webapi/CopilotChat/Skills/OpenApiPlugins/README.md b/webapi/Skills/OpenApiPlugins/README.md similarity index 99% rename from webapi/CopilotChat/Skills/OpenApiPlugins/README.md rename to webapi/Skills/OpenApiPlugins/README.md index 069e05d74..99d9a8535 100644 --- a/webapi/CopilotChat/Skills/OpenApiPlugins/README.md +++ b/webapi/Skills/OpenApiPlugins/README.md @@ -14,7 +14,7 @@ This description is extracted from GitHub's official OpenAPI descriptions for th The OpenAPI spec at [https://www.klarna.com/.well-known/ai-plugin.json](https://www.klarna.com/.well-known/ai-plugin.json) defines the APIs for Klarna Shopping's ChatGPT AI plugin operations. This definition was retrieved using Klarna's official ChatGPT plugin hosted at https://www.klarna.com/.well-known/ai-plugin.json. -Serving the OpenAPI definition from the repo is a workaround for Klarna's ChatGPT plugin sometimes returning a 403 when requested from CopilotChat. +Serving the OpenAPI definition from the repo is a workaround for Klarna's ChatGPT plugin sometimes returning a 403 when requested from CopilotChat.WebApi. ## JiraPlugin diff --git a/webapi/CopilotChat/Skills/TokenUtilities.cs b/webapi/Skills/TokenUtilities.cs similarity index 90% rename from webapi/CopilotChat/Skills/TokenUtilities.cs rename to webapi/Skills/TokenUtilities.cs index b463cec86..66dafeacd 100644 --- a/webapi/CopilotChat/Skills/TokenUtilities.cs +++ b/webapi/Skills/TokenUtilities.cs @@ -6,10 +6,11 @@ using System.Linq; using Azure.AI.OpenAI; using Microsoft.Extensions.Logging; +using Microsoft.SemanticKernel.Connectors.AI.OpenAI.AzureSdk; using Microsoft.SemanticKernel.Connectors.AI.OpenAI.Tokenizers; using Microsoft.SemanticKernel.Orchestration; -namespace SemanticKernel.Service.CopilotChat.Skills; +namespace CopilotChat.WebApi.Skills; /// /// Utility methods for token management. @@ -60,13 +61,13 @@ internal static Dictionary EmptyTokenUsages() /// /// Gets the total token usage from a Chat or Text Completion result context and adds it as a variable to response context. /// - /// Result context of chat completion + /// Result context from chat model /// Context maintained during response generation. /// Name of the function that invoked the chat completion. /// true if token usage is found in result context; otherwise, false. internal static void GetFunctionTokenUsage(SKContext result, SKContext chatContext, string? functionName = null) { - var functionKey = GetFunctionKey(chatContext.Log, functionName); + var functionKey = GetFunctionKey(chatContext.Logger, functionName); if (functionKey == null) { return; @@ -74,11 +75,11 @@ internal static void GetFunctionTokenUsage(SKContext result, SKContext chatConte if (result.ModelResults == null || result.ModelResults.Count == 0) { - chatContext.Log.LogError("Unable to determine token usage for {0}", functionKey); + chatContext.Logger.LogError("Unable to determine token usage for {0}", functionKey); return; } - var tokenUsage = result.ModelResults.First().GetResult().Usage.TotalTokens; + var tokenUsage = result.ModelResults.First().GetResult().Usage.TotalTokens; chatContext.Variables.Set(functionKey!, tokenUsage.ToString(CultureInfo.InvariantCulture)); } diff --git a/webapi/CopilotChat/Storage/ChatMemorySourceRepository.cs b/webapi/Storage/ChatMemorySourceRepository.cs similarity index 93% rename from webapi/CopilotChat/Storage/ChatMemorySourceRepository.cs rename to webapi/Storage/ChatMemorySourceRepository.cs index 6c3f53f2f..7444edc2c 100644 --- a/webapi/CopilotChat/Storage/ChatMemorySourceRepository.cs +++ b/webapi/Storage/ChatMemorySourceRepository.cs @@ -3,9 +3,9 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using SemanticKernel.Service.CopilotChat.Models; +using CopilotChat.WebApi.Models.Storage; -namespace SemanticKernel.Service.CopilotChat.Storage; +namespace CopilotChat.WebApi.Storage; /// /// A repository for chat messages. diff --git a/webapi/CopilotChat/Storage/ChatMessageRepository.cs b/webapi/Storage/ChatMessageRepository.cs similarity index 93% rename from webapi/CopilotChat/Storage/ChatMessageRepository.cs rename to webapi/Storage/ChatMessageRepository.cs index ec4bb9516..cfc0b0d08 100644 --- a/webapi/CopilotChat/Storage/ChatMessageRepository.cs +++ b/webapi/Storage/ChatMessageRepository.cs @@ -3,9 +3,9 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using SemanticKernel.Service.CopilotChat.Models; +using CopilotChat.WebApi.Models.Storage; -namespace SemanticKernel.Service.CopilotChat.Storage; +namespace CopilotChat.WebApi.Storage; /// /// A repository for chat messages. diff --git a/webapi/CopilotChat/Storage/ChatParticipantRepository.cs b/webapi/Storage/ChatParticipantRepository.cs similarity index 95% rename from webapi/CopilotChat/Storage/ChatParticipantRepository.cs rename to webapi/Storage/ChatParticipantRepository.cs index 95d575786..313041b76 100644 --- a/webapi/CopilotChat/Storage/ChatParticipantRepository.cs +++ b/webapi/Storage/ChatParticipantRepository.cs @@ -3,9 +3,9 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using SemanticKernel.Service.CopilotChat.Models; +using CopilotChat.WebApi.Models.Storage; -namespace SemanticKernel.Service.CopilotChat.Storage; +namespace CopilotChat.WebApi.Storage; /// /// A repository for chat sessions. diff --git a/webapi/CopilotChat/Storage/ChatSessionRepository.cs b/webapi/Storage/ChatSessionRepository.cs similarity index 82% rename from webapi/CopilotChat/Storage/ChatSessionRepository.cs rename to webapi/Storage/ChatSessionRepository.cs index b1fcc9660..3875fb051 100644 --- a/webapi/CopilotChat/Storage/ChatSessionRepository.cs +++ b/webapi/Storage/ChatSessionRepository.cs @@ -1,8 +1,8 @@ // Copyright (c) Microsoft. All rights reserved. -using SemanticKernel.Service.CopilotChat.Models; +using CopilotChat.WebApi.Models.Storage; -namespace SemanticKernel.Service.CopilotChat.Storage; +namespace CopilotChat.WebApi.Storage; /// /// A repository for chat sessions. diff --git a/webapi/CopilotChat/Storage/CosmosDbContext.cs b/webapi/Storage/CosmosDbContext.cs similarity index 98% rename from webapi/CopilotChat/Storage/CosmosDbContext.cs rename to webapi/Storage/CosmosDbContext.cs index 26aed983e..6a8e6264d 100644 --- a/webapi/CopilotChat/Storage/CosmosDbContext.cs +++ b/webapi/Storage/CosmosDbContext.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; using Microsoft.Azure.Cosmos; -namespace SemanticKernel.Service.CopilotChat.Storage; +namespace CopilotChat.WebApi.Storage; /// /// A storage context that stores entities in a CosmosDB container. diff --git a/webapi/CopilotChat/Storage/FileSystemContext.cs b/webapi/Storage/FileSystemContext.cs similarity index 98% rename from webapi/CopilotChat/Storage/FileSystemContext.cs rename to webapi/Storage/FileSystemContext.cs index 843d7ab56..8946231d7 100644 --- a/webapi/CopilotChat/Storage/FileSystemContext.cs +++ b/webapi/Storage/FileSystemContext.cs @@ -8,7 +8,7 @@ using System.Text.Json; using System.Threading.Tasks; -namespace SemanticKernel.Service.CopilotChat.Storage; +namespace CopilotChat.WebApi.Storage; /// /// A storage context that stores entities on disk. diff --git a/webapi/CopilotChat/Storage/IRepository.cs b/webapi/Storage/IRepository.cs similarity index 96% rename from webapi/CopilotChat/Storage/IRepository.cs rename to webapi/Storage/IRepository.cs index c5c977b57..fb2e1e7f4 100644 --- a/webapi/CopilotChat/Storage/IRepository.cs +++ b/webapi/Storage/IRepository.cs @@ -3,7 +3,7 @@ using System; using System.Threading.Tasks; -namespace SemanticKernel.Service.CopilotChat.Storage; +namespace CopilotChat.WebApi.Storage; /// /// Defines the basic CRUD operations for a repository. diff --git a/webapi/CopilotChat/Storage/IStorageContext.cs b/webapi/Storage/IStorageContext.cs similarity index 95% rename from webapi/CopilotChat/Storage/IStorageContext.cs rename to webapi/Storage/IStorageContext.cs index 8892908d9..54e350d37 100644 --- a/webapi/CopilotChat/Storage/IStorageContext.cs +++ b/webapi/Storage/IStorageContext.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using System.Threading.Tasks; -namespace SemanticKernel.Service.CopilotChat.Storage; +namespace CopilotChat.WebApi.Storage; /// /// Defines the basic CRUD operations for a storage context. diff --git a/webapi/CopilotChat/Storage/IStorageEntity.cs b/webapi/Storage/IStorageEntity.cs similarity index 68% rename from webapi/CopilotChat/Storage/IStorageEntity.cs rename to webapi/Storage/IStorageEntity.cs index e77ea851e..e1aa6ab72 100644 --- a/webapi/CopilotChat/Storage/IStorageEntity.cs +++ b/webapi/Storage/IStorageEntity.cs @@ -1,6 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. -namespace SemanticKernel.Service.CopilotChat.Storage; +namespace CopilotChat.WebApi.Storage; public interface IStorageEntity { diff --git a/webapi/CopilotChat/Storage/Repository.cs b/webapi/Storage/Repository.cs similarity index 96% rename from webapi/CopilotChat/Storage/Repository.cs rename to webapi/Storage/Repository.cs index 4136b6fca..d6add3cad 100644 --- a/webapi/CopilotChat/Storage/Repository.cs +++ b/webapi/Storage/Repository.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using System.Threading.Tasks; -namespace SemanticKernel.Service.CopilotChat.Storage; +namespace CopilotChat.WebApi.Storage; /// /// Defines the basic CRUD operations for a repository. diff --git a/webapi/CopilotChat/Storage/VolatileContext.cs b/webapi/Storage/VolatileContext.cs similarity index 97% rename from webapi/CopilotChat/Storage/VolatileContext.cs rename to webapi/Storage/VolatileContext.cs index d50ac9fad..4cf7a82aa 100644 --- a/webapi/CopilotChat/Storage/VolatileContext.cs +++ b/webapi/Storage/VolatileContext.cs @@ -7,7 +7,7 @@ using System.Linq; using System.Threading.Tasks; -namespace SemanticKernel.Service.CopilotChat.Storage; +namespace CopilotChat.WebApi.Storage; /// /// A storage context that stores entities in memory. diff --git a/webapi/appsettings.json b/webapi/appsettings.json index acc1542b7..256434543 100644 --- a/webapi/appsettings.json +++ b/webapi/appsettings.json @@ -230,7 +230,7 @@ "Logging": { "LogLevel": { "Default": "Warning", - "SemanticKernel.Service": "Information", + "CopilotChat.WebApi": "Information", "Microsoft.SemanticKernel": "Information", "Microsoft.AspNetCore.Hosting": "Information", "Microsoft.Hosting.Lifetime": "Information"