From 66d142f7f6de37613c42be5940728bbee84eb34a Mon Sep 17 00:00:00 2001 From: Mark Whitby Date: Mon, 13 May 2024 19:24:01 +0100 Subject: [PATCH 1/2] Updates to 04-deploy-ai --- .../04-ACS/acs-sk-csharp.ipynb | 19 +- .../aais-lc-python-api/README.md | 253 +++++++++++++ .../aais-lc-python}/Dockerfile | 0 .../aais-lc-python}/README.md | 2 +- .../aais-lc-python-api/aais-lc-python/main.py | 101 +++++ .../aais-lc-python/requirements.txt | 11 + .../aais-sk-csharp-api/README.md | 356 ++++++++++++++++++ .../aais-sk-csharp/Dockerfile | 22 ++ .../CustomPlugin/GetMovieInfo}/config.json | 4 +- .../CustomPlugin/GetMovieInfo}/skprompt.txt | 0 .../aais-sk-csharp/Program.cs | 132 +++++++ .../Properties/launchSettings.json | 0 .../aais-sk-csharp/aais-sk-csharp.csproj} | 12 +- .../aais-sk-csharp/aais-sk-csharp.sln} | 0 .../aais-sk-csharp/appsettings.json | 19 + .../acs-lc-python-api/README.md | 222 ----------- .../acs-lc-python/.env.example | 12 - .../acs-lc-python-api/acs-lc-python/main.py | 112 ------ .../acs-lc-python/requirements.txt | 9 - .../acs-sk-csharp-api/README.md | 316 ---------------- .../acs-sk-csharp/Dockerfile | 22 -- .../acs-sk-csharp/Program.cs | 147 -------- .../appsettings.Development.json | 8 - .../acs-sk-csharp/appsettings.json | 21 -- .../.chainlit/translations/en-US.json | 231 ++++++++++++ .../02-frontend-ui/chainlitagent-ui/README.md | 14 +- .../02-frontend-ui/chainlitagent-ui/app.py | 7 +- .../chainlitagent-ui/chainlit.md | 2 +- .../chainlitagent-ui/requirements.txt | 8 +- 29 files changed, 1167 insertions(+), 895 deletions(-) create mode 100644 labs/04-deploy-ai/01-backend-api/aais-lc-python-api/README.md rename labs/04-deploy-ai/01-backend-api/{acs-lc-python-api/acs-lc-python => aais-lc-python-api/aais-lc-python}/Dockerfile (100%) rename labs/04-deploy-ai/01-backend-api/{acs-lc-python-api/acs-lc-python => aais-lc-python-api/aais-lc-python}/README.md (91%) create mode 100644 labs/04-deploy-ai/01-backend-api/aais-lc-python-api/aais-lc-python/main.py create mode 100644 labs/04-deploy-ai/01-backend-api/aais-lc-python-api/aais-lc-python/requirements.txt create mode 100644 labs/04-deploy-ai/01-backend-api/aais-sk-csharp-api/README.md create mode 100644 labs/04-deploy-ai/01-backend-api/aais-sk-csharp-api/aais-sk-csharp/Dockerfile rename labs/04-deploy-ai/01-backend-api/{acs-sk-csharp-api/acs-sk-csharp/Plugins/CustomPlugin/GetIntent => aais-sk-csharp-api/aais-sk-csharp/Plugins/CustomPlugin/GetMovieInfo}/config.json (88%) rename labs/04-deploy-ai/01-backend-api/{acs-sk-csharp-api/acs-sk-csharp/Plugins/CustomPlugin/GetIntent => aais-sk-csharp-api/aais-sk-csharp/Plugins/CustomPlugin/GetMovieInfo}/skprompt.txt (100%) create mode 100644 labs/04-deploy-ai/01-backend-api/aais-sk-csharp-api/aais-sk-csharp/Program.cs rename labs/04-deploy-ai/01-backend-api/{acs-sk-csharp-api/acs-sk-csharp => aais-sk-csharp-api/aais-sk-csharp}/Properties/launchSettings.json (100%) rename labs/04-deploy-ai/01-backend-api/{acs-sk-csharp-api/acs-sk-csharp/acs-sk-csharp.csproj => aais-sk-csharp-api/aais-sk-csharp/aais-sk-csharp.csproj} (72%) rename labs/04-deploy-ai/01-backend-api/{acs-sk-csharp-api/acs-sk-csharp/acs-sk-csharp.sln => aais-sk-csharp-api/aais-sk-csharp/aais-sk-csharp.sln} (100%) create mode 100644 labs/04-deploy-ai/01-backend-api/aais-sk-csharp-api/aais-sk-csharp/appsettings.json delete mode 100644 labs/04-deploy-ai/01-backend-api/acs-lc-python-api/README.md delete mode 100644 labs/04-deploy-ai/01-backend-api/acs-lc-python-api/acs-lc-python/.env.example delete mode 100644 labs/04-deploy-ai/01-backend-api/acs-lc-python-api/acs-lc-python/main.py delete mode 100644 labs/04-deploy-ai/01-backend-api/acs-lc-python-api/acs-lc-python/requirements.txt delete mode 100644 labs/04-deploy-ai/01-backend-api/acs-sk-csharp-api/README.md delete mode 100644 labs/04-deploy-ai/01-backend-api/acs-sk-csharp-api/acs-sk-csharp/Dockerfile delete mode 100644 labs/04-deploy-ai/01-backend-api/acs-sk-csharp-api/acs-sk-csharp/Program.cs delete mode 100644 labs/04-deploy-ai/01-backend-api/acs-sk-csharp-api/acs-sk-csharp/appsettings.Development.json delete mode 100644 labs/04-deploy-ai/01-backend-api/acs-sk-csharp-api/acs-sk-csharp/appsettings.json create mode 100644 labs/04-deploy-ai/02-frontend-ui/chainlitagent-ui/.chainlit/translations/en-US.json diff --git a/labs/03-orchestration/04-ACS/acs-sk-csharp.ipynb b/labs/03-orchestration/04-ACS/acs-sk-csharp.ipynb index 30dc3d2..fbf79df 100644 --- a/labs/03-orchestration/04-ACS/acs-sk-csharp.ipynb +++ b/labs/03-orchestration/04-ACS/acs-sk-csharp.ipynb @@ -1070,9 +1070,9 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" + "display_name": ".NET (C#)", + "language": "C#", + "name": ".net-csharp" }, "language_info": { "codemirror_mode": { @@ -1086,7 +1086,18 @@ "pygments_lexer": "ipython3", "version": "3.11.6" }, - "orig_nbformat": 4 + "orig_nbformat": 4, + "polyglot_notebook": { + "kernelInfo": { + "defaultKernelName": "csharp", + "items": [ + { + "aliases": [], + "name": "csharp" + } + ] + } + } }, "nbformat": 4, "nbformat_minor": 2 diff --git a/labs/04-deploy-ai/01-backend-api/aais-lc-python-api/README.md b/labs/04-deploy-ai/01-backend-api/aais-lc-python-api/README.md new file mode 100644 index 0000000..774cc49 --- /dev/null +++ b/labs/04-deploy-ai/01-backend-api/aais-lc-python-api/README.md @@ -0,0 +1,253 @@ +# 04 - Deploy ACS Langchain Python API + +In this folder you will find a sample AI App that is built using Python, Langchain and Azure AI Search. + +The entire solution is in this folder, but we also have all the step by step instructions so you can see how it was built. + +## Complete Solution + +To test the version of the app in this folder, you should just be able to run the command below. It should read the environment variable values from the `.env` file located in the root of this repository, so if you've already configured that there shouldn't be anything else to do. Otherwise, you'll need to fill in the `.env` file with the necessary values. + +You can run the app using the command below. + +```bash +uvicorn main:app --reload --host=0.0.0.0 --port=5291 +``` + +## Step by Step Instructions + +### Create Python Project and Solution + +```bash +mkdir aais-lc-python +cd aais-lc-python +``` + +### Add Dependencies + +```bash +echo "azure-core==1.30.1 +azure-identity==1.16.0 +azure-search-documents==11.4.0 +fastapi==0.110.1 +uvicorn==0.25.0 +openai==1.27.0 +langchain==0.1.19 +langchain-openai==0.1.3 +tiktoken==0.6.0 +python-dotenv==1.0.1 +chainlit==1.0.506" > requirements.txt +``` + +```bash +pip install -r requirements.txt +``` + +### Create main.py + +```bash +echo "" > main.py +``` + +The first thing we need to do is to add some import and from statements to the `main.py` file. + +```python +import os +import logging +from langchain_openai import AzureOpenAIEmbeddings, AzureChatOpenAI +from dotenv import load_dotenv +from fastapi import FastAPI +from pydantic import BaseModel +from fastapi.responses import JSONResponse, HTMLResponse +from langchain.prompts import PromptTemplate +from azure.search.documents import SearchClient +from azure.search.documents.models import VectorizedQuery +from azure.core.credentials import AzureKeyCredential +from langchain.chains import LLMChain +from langchain_core.output_parsers import StrOutputParser +``` + +We configure some logging so we can see what's happening behind the scenes + +```python +logging.basicConfig(format='%(levelname)-10s%(message)s', level=logging.INFO) +``` + +Next we read in the environment variables. + +```python +if load_dotenv(): + logging.info("Azure OpenAI Endpoint: " + os.getenv("AZURE_OPENAI_ENDPOINT")) + logging.info("Azure AI Search: " + os.getenv("AZURE_AI_SEARCH_SERVICE_NAME")) +else: + print("No file .env found") +``` + +Now we add the Embeddings and ChatCompletion instances of Azure OpenAI that we will be using. + +```python +# Create an Embeddings Instance of Azure OpenAI +azure_openai_embeddings = AzureOpenAIEmbeddings( + azure_deployment = os.getenv("AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME"), + openai_api_version = os.getenv("OPENAI_EMBEDDING_API_VERSION"), + model= os.getenv("AZURE_OPENAI_EMBEDDING_MODEL") +) + +azure_openai = AzureChatOpenAI( + azure_deployment = os.getenv("AZURE_OPENAI_COMPLETION_DEPLOYMENT_NAME"), + temperature=0.1, + max_tokens=500 +) +logging.info('Completed creation of embedding and completion instances.') +``` + +Next we start the app. + +```python +# Start the App +app = FastAPI() +``` + +Next we define the class and routing methods that will be used. + +```python +class CompletionRequest(BaseModel): + Question: str + +class CompletionResponse(BaseModel): + completion: str +``` + +This route is for the root of the app, it will return a link to the Swagger UI. + +```python +@app.get("/", response_class=HTMLResponse) +def read_root(): + return "Swagger Endpoint: docs" +``` + +This route is for the completion endpoint, it will take a question and return a completion. The question is first passed to Azure AI Search to get the top 5 results, then the question and results are passed to Azure OpenAI to get a completion. + +```python +@app.post("/completion/", response_class=JSONResponse) +def execute_completion(request: CompletionRequest): + # Ask the question + # The question is being passed in via the message body. + # request: CompletionRequest + + # Create a prompt template with variables, note the curly braces + prompt = PromptTemplate( + input_variables=["original_question","search_results"], + template=""" + Question: {original_question} + + Do not use any other data. + Only use the movie data below when responding. + {search_results} + """, + ) + + # Search Vector Store + search_client = SearchClient( + os.getenv("AZURE_AI_SEARCH_ENDPOINT"), + os.getenv("AZURE_AI_SEARCH_INDEX_NAME"), + AzureKeyCredential(os.getenv("AZURE_AI_SEARCH_API_KEY")) + ) + + vector = VectorizedQuery(vector=azure_openai_embeddings.embed_query(request.Question), k_nearest_neighbors=5, fields="vector") + + results = list(search_client.search( + search_text=request.Question, + query_type="semantic", + semantic_configuration_name="movies-semantic-config", + include_total_count=True, + vector_queries=[vector], + select=["title","genre","overview","tagline","release_date","popularity","vote_average","vote_count","runtime","revenue","original_language"], + top=5 + )) + + output_parser = StrOutputParser() + chain = prompt | azure_openai | output_parser + response = chain.invoke({"original_question": request.Question, "search_results": results}) + logging.info("Response from LLM: " + response) + return CompletionResponse(completion = response) +``` + +### Test the App + +Now that we have all the code in place let's run it. + +Remember to create a `.env` file in the same folder as the `main.py` file and add the environment variables and values we used from the `.env` file in the Jupyter notebooks. + +```bash +uvicorn main:app --reload --host=0.0.0.0 --port=5291 +``` + +Once the app is started, open a browser and navigate to http://127.0.0.1:5291/docs +>**Note:** the port number may be different to `5291`, so double check the output from the `uvicorn main:app` command. + +Click on the "POST /completion" endpoint, click on "Try it out", enter a Prompt, "List the movies about ships on the water.", then click on "Execute". + +### Build and Test Docker Image + +Let's now package the solution into a Docker Image so it can be deployed to a container service like Azure Kubernetes Serivce (AKS) or Azure Container Apps (ACA). + +First you'll need to create a Dockerfile + +```bash +echo "" > Dockerfile +``` + +Next, paste the following into the Dockerfile + +```Dockerfile +FROM python:3.11-slim AS builder +WORKDIR /app +ENV PYTHONDONTWRITEBYTECODE 1 +ENV PYTHONUNBUFFERED 1 +RUN apt-get update && apt-get install -y \ + build-essential \ + curl \ + software-properties-common \ + git \ + && rm -rf /var/lib/apt/lists/* +# Create a virtualenv to keep dependencies together +RUN python -m venv /opt/venv +ENV PATH="/opt/venv/bin:$PATH" +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Stage 2 - Copy only necessary files to the runner stage +FROM python:3.11-slim +WORKDIR /app +COPY --from=builder /opt/venv /opt/venv +ENV PATH="/opt/venv/bin:$PATH" +COPY main.py . +EXPOSE 5291 +CMD ["uvicorn", "main:app", "--host=0.0.0.0", "--port=5291"] +``` + +Next, we build the Docker Image. + +```bash +docker build -t aais-lc-python:v1 . +``` + +Finally, we can test the image. We pass in the environment variable values as we don't want to have sensitive information embedded directly into the image. + +```bash +docker run -it --rm \ + --name aaislcpython \ + -p 5291:5291 \ + -e AZURE_OPENAI_API_KEY="" \ + -e AZURE_OPENAI_ENDPOINT="" \ + -e OPENAI_API_VERSION="2024-03-01-preview" \ + -e AZURE_OPENAI_COMPLETION_DEPLOYMENT_NAME="" \ + -e AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME="" \ + -e AZURE_OPENAI_EMBEDDING_MODEL="" \ + -e AZURE_AI_SEARCH_SERVICE_NAME="" \ + -e AZURE_AI_SEARCH_ENDPOINT="Swagger Endpoint: docs" + +@app.post("/completion/", response_class=JSONResponse) +def execute_completion(request: CompletionRequest): + # Ask the question + # The question is being passed in via the message body. + # request: CompletionRequest + + # Create a prompt template with variables, note the curly braces + prompt = PromptTemplate( + input_variables=["original_question","search_results"], + template=""" + Question: {original_question} + + Do not use any other data. + Only use the movie data below when responding. + {search_results} + """, + ) + + # Search Vector Store + search_client = SearchClient( + azure_ai_search_endpoint, + azure_ai_search_index_name, + AzureKeyCredential(azure_ai_search_api_key) + ) + + vector = VectorizedQuery(vector=azure_openai_embeddings.embed_query(request.Question), k_nearest_neighbors=5, fields="vector") + + results = list(search_client.search( + search_text=request.Question, + query_type="semantic", + semantic_configuration_name="movies-semantic-config", + include_total_count=True, + vector_queries=[vector], + select=["title","genre","overview","tagline","release_date","popularity","vote_average","vote_count","runtime","revenue","original_language"], + top=5 + )) + + output_parser = StrOutputParser() + chain = prompt | azure_openai | output_parser + response = chain.invoke({"original_question": request.Question, "search_results": results}) + logging.info("Response from LLM: " + response) + return CompletionResponse(completion = response) diff --git a/labs/04-deploy-ai/01-backend-api/aais-lc-python-api/aais-lc-python/requirements.txt b/labs/04-deploy-ai/01-backend-api/aais-lc-python-api/aais-lc-python/requirements.txt new file mode 100644 index 0000000..3166900 --- /dev/null +++ b/labs/04-deploy-ai/01-backend-api/aais-lc-python-api/aais-lc-python/requirements.txt @@ -0,0 +1,11 @@ +azure-core==1.30.1 +azure-identity==1.16.0 +azure-search-documents==11.4.0 +fastapi==0.110.1 +uvicorn==0.25.0 +openai==1.27.0 +langchain==0.1.19 +langchain-openai==0.1.3 +tiktoken==0.6.0 +python-dotenv==1.0.1 +chainlit==1.0.506 \ No newline at end of file diff --git a/labs/04-deploy-ai/01-backend-api/aais-sk-csharp-api/README.md b/labs/04-deploy-ai/01-backend-api/aais-sk-csharp-api/README.md new file mode 100644 index 0000000..3534588 --- /dev/null +++ b/labs/04-deploy-ai/01-backend-api/aais-sk-csharp-api/README.md @@ -0,0 +1,356 @@ +# 04 - Deploy Azure AI Search Semantic Kernel C# API + +In this folder you will find a sample AI App that is built using C#, Semantic Kernel and Azure AI Search. + +The entire solution is in this folder, but we also have all the step by step instructions so you can see how it was built. + +## Complete Solution + +1. To test locally fill in `appsettings.json` with the same values from the .env file that was used earlier for the Jupyter Notebook based labs. +2. Build and Run the App + +```bash +dotnet run +``` + +## Step by Step Instructions + +### Create C# Project and Solution + +```bash +mkdir aais-sk-csharp +cd aais-sk-csharp + +dotnet new webapi --use-minimal-apis +dotnet new solution +dotnet sln aais-sk-csharp.sln add aais-sk-csharp.csproj +``` + +### Add Dependencies + +```bash +dotnet add aais-sk-csharp.csproj package Azure.Core --version "1.39.0" +dotnet add aais-sk-csharp.csproj package Azure.AI.OpenAI --version "1.0.0-beta.16" +dotnet add aais-sk-csharp.csproj package Microsoft.SemanticKernel --version "1.10.0" +dotnet add aais-sk-csharp.csproj package Microsoft.SemanticKernel.Connectors.OpenAI --version "1.10.0" +dotnet add aais-sk-csharp.csproj package Azure.Search.Documents --version "11.5.0-beta.5" +``` + +### Update Program.cs + +The first thing we need to do is to add some Using statements to the `Program.cs` file. Add the following right at the top of this file. + +```csharp +using System.Text; +using Microsoft.SemanticKernel; +using Microsoft.SemanticKernel.Connectors.OpenAI; +using Azure; +using Azure.Search.Documents; +using Azure.Search.Documents.Models; +using Azure.Search.Documents.Indexes; +using Azure.AI.OpenAI; +using Microsoft.AspNetCore.Mvc; +using Microsoft.SemanticKernel.ChatCompletion; +``` + +Next we read in the variables. Because this is ASP.NET Core we will switch from the DotEnv package we used in earlier labs to native ASP.NET Core Configuration. + +```csharp +var builder = WebApplication.CreateBuilder(args); + +// Load values into variables +var config = builder.Configuration; +var azure_openai_api_key = config["AZURE_OPENAI_API_KEY"] ?? String.Empty; +var azure_openai_endpoint = config["AZURE_OPENAI_ENDPOINT"] ?? String.Empty; +var openai_api_version = config["OPENAI_API_VERSION"] ?? String.Empty; +var azure_openai_completion_deployment_name = config["AZURE_OPENAI_COMPLETION_DEPLOYMENT_NAME"] ?? String.Empty; +var azure_openai_embedding_deployment_name = config["AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME"] ?? String.Empty; +var azure_ai_search_name = config["AZURE_AI_SEARCH_SERVICE_NAME"] ?? String.Empty; +var azure_ai_search_endpoint = config["AZURE_AI_SEARCH_ENDPOINT"] ?? String.Empty; +var azure_ai_search_index_name = config["AZURE_AI_SEARCH_INDEX_NAME"] ?? String.Empty; +var azure_ai_search_api_key = config["AZURE_AI_SEARCH_API_KEY"] ?? String.Empty; +Console.WriteLine("Configuration loaded."); +``` + +Next we add the Semantic Kernel to the ASP.NET Core dependency injection container. Insert these lines after the configuration code above. + +```csharp +// Add Semantic Kernel service to the container. +// Add in configuration options and required services. +builder.Services.AddSingleton(sp => sp.GetRequiredService>()); // some services require an un-templated ILogger + +builder.Services.AddSingleton(sp => +{ + return new AzureOpenAIChatCompletionService(azure_openai_completion_deployment_name, azure_openai_endpoint, azure_openai_api_key); +}); + +builder.Services.AddAzureOpenAIChatCompletion(azure_openai_completion_deployment_name, azure_openai_endpoint, azure_openai_api_key); +builder.Services.AddKernel(); +``` + +You can remove the section of code that defines `var summaries` as we don't need it for this sample. + +Next we swap out the `app.MapGet` `weatherforecast` function with our own. + +```csharp +// Configure Routing +app.MapPost("/completion", async ([FromServices] Kernel kernel, [FromBody] CompletionRequest request) => +{ + try + { + // Read values from .env file + // These are loaded during startup, see above for details. + + // Setup Semantic Kernel + // This has already been setup as part of the ASP.NET Core dependency injection setup + // and is passed into this function as a parameter. + // [FromServices] Kernel kernel + + // Ask the question + // The question is being passed in via the message body. + // [FromBody] CompletionRequest request + + // The PromptTemplate which was setup as an inline function in the earlier labs has been moved + // into the Plugins directory so it is easier to manage and configure. This gives the ability to mount updated + // prompt files into a container without having to rewrite the source code. + var pluginsDirectory = Path.Combine(System.IO.Directory.GetCurrentDirectory(), "Plugins"); + var customPlugin = kernel.ImportPluginFromPromptDirectory(Path.Combine(pluginsDirectory, "CustomPlugin")); + Console.WriteLine("Plugin GetMovieInfo loaded."); + + // Get Embedding for the original question + OpenAIClient azureOpenAIClient = new OpenAIClient(new Uri(azure_openai_endpoint),new AzureKeyCredential(azure_openai_api_key)); + float[] queryEmbedding = azureOpenAIClient.GetEmbeddings(new EmbeddingsOptions(azure_openai_embedding_deployment_name, new List() { request.Question })).Value.Data[0].Embedding.ToArray(); + + Console.WriteLine("Embedding of original question has been completed."); + + // Search Vector Store + + string semanticSearchConfigName = "movies-semantic-config"; + + SearchOptions searchOptions = new SearchOptions + { + QueryType = SearchQueryType.Semantic, + SemanticConfigurationName = semanticSearchConfigName, + VectorQueries = { new RawVectorQuery() { Vector = queryEmbedding, KNearestNeighborsCount = 5, Fields = { "vector" } } }, + Size = 5, + Select = { "title", "genre" }, + }; + + AzureKeyCredential indexCredential = new AzureKeyCredential(azure_ai_search_api_key); + SearchIndexClient indexClient = new SearchIndexClient(new Uri(azure_ai_search_endpoint), indexCredential); + SearchClient searchClient = indexClient.GetSearchClient(azure_ai_search_index_name); + + //Perform the search + SearchResults response = searchClient.Search(request.Question, searchOptions); + Pageable> results = response.GetResults(); + + // Create string from the results + + StringBuilder stringBuilderResults = new StringBuilder(); + foreach (SearchResult result in results) + { + stringBuilderResults.AppendLine($"{result.Document["title"]}"); + }; + + Console.WriteLine(stringBuilderResults.ToString()); + + Console.WriteLine("Searching of Vector Store has been completed."); + + // Build the Prompt and Execute against the Azure OpenAI to get the completion + + var completion = await kernel.InvokeAsync(customPlugin["GetMovieInfo"], new () { { "original_question", request.Question }, { "search_results", stringBuilderResults.ToString() }}); + Console.WriteLine("Implementation of RAG using SK, C# and Azure Cognitive Search has been completed."); + Console.WriteLine(completion.ToString()); + return new CompletionResponse(completion.ToString()); + } + catch (Exception exc) + { + Console.WriteLine($"Error: {exc.Message}"); + return new CompletionResponse("Something unexpected happened."); + } +}) +.WithName("Completion") +.WithOpenApi(); +``` + +We'll replace the `app.Run()` with the async version. + +```csharp +// Start the Process +await app.RunAsync(); +``` + +We don't need the `WeatherForecast` record so we can remove that from the `Program.cs` file. We'll replace it with the following two records that are used for the question and completion. + +```csharp +public record CompletionRequest (string Question) {} + +public record CompletionResponse (string completion) {} +``` + +### Create a Plug In + +With Semantic Kernel, we can define a prompt within a file and then use that file as a plug in. This allows us to define the prompt in a file and then mount that file into a container without having to change the source code. + +In the root of the project create a folder called `Plugins` and in that folder create another folder called `CustomPlugin`. + +```bash +mkdir Plugins +cd Plugins +mkdir CustomPlugin +``` + +The Plug In that we're going to create will be called `GetMovieInfo`, so let's create a folder for that too. + +```bash +cd CustomPlugin +mkdir GetMovieInfo +``` + +Under the `GetMovieInfo` folder we will create two files. One file will provide the template for the prompt that we want to use with Azure OpenAI. The other file will provide the configuration parameters. + +Create a file called `skprompt.txt` and add the following text. + +```text +Question: {{$original_question}} + +Do not use any other data. +Only use the movie data below when responding. +{{$search_results}} +``` + +Next, create a file named `config.json` and add the following. + +```json +{ + "schema": 1, + "type": "completion", + "description": "Answers questions about provided movie data.", + "completion": { + "max_tokens": 500, + "temperature": 0.1, + "top_p": 0.5, + "presence_penalty": 0.0, + "frequency_penalty": 0.0 + }, + "input": { + "parameters": [ + { + "name": "original_question", + "description": "The user's request.", + "defaultValue": "" + }, + { + "name": "search_results", + "description": "Vector Search results from Azure AI Search.", + "defaultValue": "" + } + ] + } +} +``` + +When you've completed the above steps, your folder structure should look like this. + +```text +aais-sk-csharp +├── Plugins +│ ├── CustomPlugin +│ │ ├── GetMovieInfo +│ │ │ ├── config.json +│ │ │ ├── skprompt.txt +``` + +### Test the App + +You'll first need to update the `appsettings.json` file to provide the values needed. After `AllowedHosts` add the following and replace the values with your own. + +```json + "OPENAI_API_VERSION": "2024-03-01-preview", + "AZURE_OPENAI_API_KEY": "", + "AZURE_OPENAI_ENDPOINT": "", + "AZURE_OPENAI_COMPLETION_MODEL": "", + "AZURE_OPENAI_COMPLETION_DEPLOYMENT_NAME": "", + "AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME": "", + "AZURE_AI_SEARCH_SERVICE_NAME": "", + "AZURE_AI_SEARCH_ENDPOINT": "", + "AZURE_AI_SEARCH_INDEX_NAME": "", + "AZURE_AI_SEARCH_API_KEY": "" + ``` + +We'll also need to update the `aais-sk-csharp.csproj` file to include the `Plugins` directory in the build. By default, the `dotnet build` command will ignore `.txt` files, and we need the `skprompt.txt` file to be included in the build. + +```xml + + + PreserveNewest + + +``` + +Next we can compile and run the app. + +```csharp +dotnet run +``` + +Once the app is started, open a browser and navigate to http://127.0.0.1:5291/swagger/index.html +>**Note:** the port number may be different to `5291`, so double check the output from the `dotnet run` command. + +Click on the "POST /completion" endpoint, click on "Try it out", enter a Prompt, "List the movies about ships on the water.", then click on "Execute". + +### Build and Test Docker Image + +Let's now package the solution into a Docker Image so it can be deployed to a container service like Azure Kubernetes Serivce (AKS) or Azure Container Apps (ACA). + +In the root of the project create a file called `Dockerfile` and add the following. + +```dockerfile +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base +WORKDIR /app +EXPOSE 5291 +ENV ASPNETCORE_URLS=http://+:5291 + +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +ARG configuration=Release +WORKDIR /src +COPY ["aais-sk-csharp.csproj", "."] +RUN dotnet restore "aais-sk-csharp.csproj" +COPY ["Program.cs", "."] +COPY ["Plugins/", "./Plugins/"] +RUN dotnet build "aais-sk-csharp.csproj" -c $configuration -o /app/build + +FROM build AS publish +ARG configuration=Release +RUN dotnet publish "aais-sk-csharp.csproj" -c $configuration -o /app/publish + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "aais-sk-csharp.dll"] +``` + +Now run the following command to build the Docker image. + +```bash +docker build -t aais-sk-csharp:v1 . +``` + +We can then test the image and be sure to set the environment variables so they override the values in the appsettings.json file. We don't want to have sensitive information embedded directly into the image. + +```bash +docker run -it --rm \ + --name aaisskcsharp \ + -p 5291:5291 \ + -e AZURE_OPENAI_API_KEY="" \ + -e AZURE_OPENAI_ENDPOINT="" \ + -e OPENAI_API_VERSION="2024-03-01-preview" \ + -e AZURE_OPENAI_COMPLETION_MODEL="" \ + -e AZURE_OPENAI_COMPLETION_DEPLOYMENT_NAME="" \ + -e AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME="" \ + -e AZURE_AI_SEARCH_SERVICE_NAME="" \ + -e AZURE_AI_SEARCH_ENDPOINT="(sp => sp.GetRequiredService>()); // some services require an un-templated ILogger + +builder.Services.AddSingleton(sp => +{ + return new AzureOpenAIChatCompletionService(azure_openai_completion_deployment_name, azure_openai_endpoint, azure_openai_api_key); +}); + +builder.Services.AddAzureOpenAIChatCompletion(azure_openai_completion_deployment_name, azure_openai_endpoint, azure_openai_api_key); +builder.Services.AddKernel(); +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +app.UseSwagger(); +app.UseSwaggerUI(); +app.UseHttpsRedirection(); + +// Configure Routing +app.MapPost("/completion", async ([FromServices] Kernel kernel, [FromBody] CompletionRequest request) => +{ + try + { + // Read values from .env file + // These are loaded during startup, see above for details. + + // Setup Semantic Kernel + // This has already been setup as part of the ASP.NET Core dependency injection setup + // and is passed into this function as a parameter. + // [FromServices] Kernel kernel + + // Ask the question + // The question is being passed in via the message body. + // [FromBody] CompletionRequest request + + // The PromptTemplate which was setup as an inline function in the earlier labs has been moved + // into the Plugins directory so it is easier to manage and configure. This gives the ability to mount updated + // prompt files into a container without having to rewrite the source code. + var pluginsDirectory = Path.Combine(System.IO.Directory.GetCurrentDirectory(), "Plugins"); + var customPlugin = kernel.ImportPluginFromPromptDirectory(Path.Combine(pluginsDirectory, "CustomPlugin")); + Console.WriteLine("Plugin GetMovieInfo loaded."); + + // Get Embedding for the original question + OpenAIClient azureOpenAIClient = new OpenAIClient(new Uri(azure_openai_endpoint),new AzureKeyCredential(azure_openai_api_key)); + float[] queryEmbedding = azureOpenAIClient.GetEmbeddings(new EmbeddingsOptions(azure_openai_embedding_deployment_name, new List() { request.Question })).Value.Data[0].Embedding.ToArray(); + + Console.WriteLine("Embedding of original question has been completed."); + + // Search Vector Store + + string semanticSearchConfigName = "movies-semantic-config"; + + SearchOptions searchOptions = new SearchOptions + { + QueryType = SearchQueryType.Semantic, + SemanticConfigurationName = semanticSearchConfigName, + VectorQueries = { new RawVectorQuery() { Vector = queryEmbedding, KNearestNeighborsCount = 5, Fields = { "vector" } } }, + Size = 5, + Select = { "title", "genre" }, + }; + + AzureKeyCredential indexCredential = new AzureKeyCredential(azure_ai_search_api_key); + SearchIndexClient indexClient = new SearchIndexClient(new Uri(azure_ai_search_endpoint), indexCredential); + SearchClient searchClient = indexClient.GetSearchClient(azure_ai_search_index_name); + + //Perform the search + SearchResults response = searchClient.Search(request.Question, searchOptions); + Pageable> results = response.GetResults(); + + // Create string from the results + + StringBuilder stringBuilderResults = new StringBuilder(); + foreach (SearchResult result in results) + { + stringBuilderResults.AppendLine($"{result.Document["title"]}"); + }; + + Console.WriteLine(stringBuilderResults.ToString()); + + Console.WriteLine("Searching of Vector Store has been completed."); + + // Build the Prompt and Execute against the Azure OpenAI to get the completion + + var completion = await kernel.InvokeAsync(customPlugin["GetMovieInfo"], new () { { "original_question", request.Question }, { "search_results", stringBuilderResults.ToString() }}); + Console.WriteLine("Implementation of RAG using SK, C# and Azure Cognitive Search has been completed."); + Console.WriteLine(completion.ToString()); + return new CompletionResponse(completion.ToString()); + } + catch (Exception exc) + { + Console.WriteLine($"Error: {exc.Message}"); + return new CompletionResponse("Something unexpected happened."); + } +}) +.WithName("Completion") +.WithOpenApi(); + +// Start the Process +await app.RunAsync(); + +public record CompletionRequest (string Question) {} + +public record CompletionResponse (string completion) {} \ No newline at end of file diff --git a/labs/04-deploy-ai/01-backend-api/acs-sk-csharp-api/acs-sk-csharp/Properties/launchSettings.json b/labs/04-deploy-ai/01-backend-api/aais-sk-csharp-api/aais-sk-csharp/Properties/launchSettings.json similarity index 100% rename from labs/04-deploy-ai/01-backend-api/acs-sk-csharp-api/acs-sk-csharp/Properties/launchSettings.json rename to labs/04-deploy-ai/01-backend-api/aais-sk-csharp-api/aais-sk-csharp/Properties/launchSettings.json diff --git a/labs/04-deploy-ai/01-backend-api/acs-sk-csharp-api/acs-sk-csharp/acs-sk-csharp.csproj b/labs/04-deploy-ai/01-backend-api/aais-sk-csharp-api/aais-sk-csharp/aais-sk-csharp.csproj similarity index 72% rename from labs/04-deploy-ai/01-backend-api/acs-sk-csharp-api/acs-sk-csharp/acs-sk-csharp.csproj rename to labs/04-deploy-ai/01-backend-api/aais-sk-csharp-api/aais-sk-csharp/aais-sk-csharp.csproj index 000bf67..54a083e 100644 --- a/labs/04-deploy-ai/01-backend-api/acs-sk-csharp-api/acs-sk-csharp/acs-sk-csharp.csproj +++ b/labs/04-deploy-ai/01-backend-api/aais-sk-csharp-api/aais-sk-csharp/aais-sk-csharp.csproj @@ -1,20 +1,20 @@ - net7.0 + net8.0 enable enable acs_sk_csharp - - - + + + - - + + diff --git a/labs/04-deploy-ai/01-backend-api/acs-sk-csharp-api/acs-sk-csharp/acs-sk-csharp.sln b/labs/04-deploy-ai/01-backend-api/aais-sk-csharp-api/aais-sk-csharp/aais-sk-csharp.sln similarity index 100% rename from labs/04-deploy-ai/01-backend-api/acs-sk-csharp-api/acs-sk-csharp/acs-sk-csharp.sln rename to labs/04-deploy-ai/01-backend-api/aais-sk-csharp-api/aais-sk-csharp/aais-sk-csharp.sln diff --git a/labs/04-deploy-ai/01-backend-api/aais-sk-csharp-api/aais-sk-csharp/appsettings.json b/labs/04-deploy-ai/01-backend-api/aais-sk-csharp-api/aais-sk-csharp/appsettings.json new file mode 100644 index 0000000..f086fb1 --- /dev/null +++ b/labs/04-deploy-ai/01-backend-api/aais-sk-csharp-api/aais-sk-csharp/appsettings.json @@ -0,0 +1,19 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "OPENAI_API_VERSION": "2024-03-01-preview", + "AZURE_OPENAI_API_KEY": "", + "AZURE_OPENAI_ENDPOINT": "", + "AZURE_OPENAI_COMPLETION_MODEL": "", + "AZURE_OPENAI_COMPLETION_DEPLOYMENT_NAME": "", + "AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME": "", + "AZURE_AI_SEARCH_SERVICE_NAME": "", + "AZURE_AI_SEARCH_ENDPOINT": "", + "AZURE_AI_SEARCH_INDEX_NAME": "", + "AZURE_AI_SEARCH_API_KEY": "" +} \ No newline at end of file diff --git a/labs/04-deploy-ai/01-backend-api/acs-lc-python-api/README.md b/labs/04-deploy-ai/01-backend-api/acs-lc-python-api/README.md deleted file mode 100644 index c49bd98..0000000 --- a/labs/04-deploy-ai/01-backend-api/acs-lc-python-api/README.md +++ /dev/null @@ -1,222 +0,0 @@ -# 04 - Deploy ACS Langchain Python API - -In this folder you will find a sample AI App that is built using Python, Langchain and Azure Cognitive Search. - -The entire solution is in this folder, but we also have all the step by step instructions so you can see how it was built. - -## Complete Solution - -1. To test locally fill in `.env` with the same values from the .env file that was used for the Jupyter Notebooks. -2. Build and Run the App - -```bash -uvicorn main:app --reload --host=0.0.0.0 --port=5291 -``` - -## Step by Step Instructions - -### Create Python Project and Solution - -```bash -mkdir acs-lc-python -cd acs-lc-python -``` - -### Add Dependencies - -```bash -echo "azure-core==1.29.3 -azure-identity==1.14.0 -azure-search-documents==11.4.0b8 -fastapi==0.99.1 -uvicorn==0.23.2 -openai==0.27.9 -langchain==0.0.312 -tiktoken==0.4.0 -python-dotenv==1.0.0" > requirements.txt -``` - -```bash -pip install -r requirements.txt -``` - -### Create main.py - -```bash -echo "" > main.py -``` - -The first thing we need to do is to add some import and from statements to the `main.py` file. - -```python -import os -from langchain.llms import AzureOpenAI -from langchain.embeddings.openai import OpenAIEmbeddings -from langchain.chat_models import AzureChatOpenAI -from langchain.prompts import PromptTemplate -from langchain.chains import LLMChain -from dotenv import load_dotenv -from fastapi import FastAPI -from fastapi.responses import JSONResponse, HTMLResponse -from pydantic import BaseModel -from azure.core.credentials import AzureKeyCredential -from azure.search.documents.models import Vector -from azure.search.documents import SearchClient -``` - -Next we read in the environment variables the same way we did in the notebook. - -```python -# Load environment variables -if load_dotenv(): - print("Found OpenAPI Base Endpoint: " + os.getenv("OPENAI_API_BASE")) -else: - print("No file .env found") - -openai_api_type = os.getenv("OPENAI_API_TYPE") -openai_api_key = os.getenv("OPENAI_API_KEY") -openai_api_base = os.getenv("OPENAI_API_BASE") -openai_api_version = os.getenv("OPENAI_API_VERSION") -deployment_name = os.getenv("AZURE_OPENAI_COMPLETION_DEPLOYMENT_NAME") -embedding_name = os.getenv("AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME") -acs_service_name = os.getenv("AZURE_COGNITIVE_SEARCH_SERVICE_NAME") -acs_endpoint_name = os.getenv("AZURE_COGNITIVE_SEARCH_ENDPOINT_NAME") -acs_index_name = os.getenv("AZURE_COGNITIVE_SEARCH_INDEX_NAME") -acs_api_key = os.getenv("AZURE_COGNITIVE_SEARCH_API_KEY") -print("openai_api_type = " + openai_api_type) -print("Configuration loaded.") -``` - -Next we add the Embeddings and ChatCompletion instances of Azure OpenAI that we will be using. - -```python -# Create an Embeddings Instance of Azure OpenAI -embeddings = OpenAIEmbeddings( - model="text-embedding-ada-002", - deployment=embedding_name, - openai_api_type = openai_api_type, - openai_api_version = openai_api_version, - openai_api_base = openai_api_base, - openai_api_key = openai_api_key -) -# Create a Completion Instance of Azure OpenAI -llm = AzureChatOpenAI( - model="gpt-3.5-turbo", - deployment_name = deployment_name, - openai_api_type = openai_api_type, - openai_api_version = openai_api_version, - openai_api_base = openai_api_base, - openai_api_key = openai_api_key, - temperature=0.1, - max_tokens=500 -) -print('Completed creation of embedding and completion instances.') -``` - -Next we start the app. - -```python -# Start the App -app = FastAPI() -``` - -Next we define the class and routing methods that will be used. - -```python -class CompletionRequest(BaseModel): - Question: str - -class CompletionResponse(BaseModel): - Completion: str - -@app.get("/", response_class=HTMLResponse) -def read_root(): - return "Swagger Endpoint: docs" - -@app.post("/completion/", response_class=JSONResponse) -def execute_completion(request: CompletionRequest): - # Ask the question - # The question is being passed in via the message body. - # request: CompletionRequest - - # Create a prompt template with variables, note the curly braces - prompt = PromptTemplate( - input_variables=["original_question","search_results"], - template=""" - Question: {original_question} - - Do not use any other data. - Only use the movie data below when responding. - {search_results} - """, - ) - - # Get Embedding for the original question - question_embedded=embeddings.embed_query(request.Question) - - # Search Vector Store - search_client = SearchClient( - acs_endpoint_name, - acs_index_name, - AzureKeyCredential(acs_api_key) - ) - vector = Vector( - value=question_embedded, - k=5, - fields="content_vector" - ) - results = list(search_client.search( - search_text="", - include_total_count=True, - vectors=[vector], - select=["content"], - )) - - # Build the Prompt and Execute against the Azure OpenAI to get the completion - chain = LLMChain(llm=llm, prompt=prompt, verbose=True) - response = chain.run({"original_question": request.Question, "search_results": results}) - print(response) - return CompletionResponse(Completion = response) -``` - -### Test the App - -Now that we have all the code in place let's run it. - -```bash -uvicorn main:app --reload --host=0.0.0.0 --port=5291 -``` - -Once the app is started, open a browser and navigate to http://127.0.0.1:5291/docs ->**Note:** the port number may be different to `5291`, so double check the output from the `uvicorn main:app` command. - -Click on the "POST /completion" endpoint, click on "Try it out", enter a Prompt, "List the movies about ships on the water.", then click on "Execute". - -### Build and Test Docker Image - -Let's now package the solution into a Docker Image so it can be deployed to a container service like Azure Kubernetes Serivce (AKS) or Azure Container Apps (ACA). - -```bash -docker build -t acs-lc-python:v1 . -``` - -We can then test the image and be sure to set the environment variables so they override the values in the appsettings.json file. We don't want to have sensitive information embedded directly into the image. - -```bash -docker run -it --rm \ - --name acslcpython \ - -p 5291:5291 \ - -e OPENAI_API_TYPE="Set this to "azure" for API key authentication or "azure_ad" for Azure AD authentication>", \ - -e OPENAI_API_KEY="" \ - -e OPENAI_API_BASE="" \ - -e OPENAI_API_VERSION="2023-05-15" \ - -e OPENAI_COMPLETION_MODEL="" \ - -e AZURE_TENANT_ID="" \ - -e AZURE_OPENAI_COMPLETION_DEPLOYMENT_NAME="" \ - -e AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME="" \ - -e AZURE_COGNITIVE_SEARCH_SERVICE_NAME="" \ - -e AZURE_COGNITIVE_SEARCH_ENDPOINT_NAME="" -OPENAI_API_KEY = "" -OPENAI_API_BASE = "" -OPENAI_API_VERSION = "2023-05-15" -OPENAI_COMPLETION_MODEL = "" -AZURE_TENANT_ID = "" -AZURE_OPENAI_COMPLETION_DEPLOYMENT_NAME = "" -AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME = "" -AZURE_COGNITIVE_SEARCH_SERVICE_NAME = "" -AZURE_COGNITIVE_SEARCH_ENDPOINT_NAME = "Swagger Endpoint: docs" - -@app.post("/completion/", response_class=JSONResponse) -def execute_completion(request: CompletionRequest): - # Ask the question - # The question is being passed in via the message body. - # request: CompletionRequest - - # Create a prompt template with variables, note the curly braces - prompt = PromptTemplate( - input_variables=["original_question","search_results"], - template=""" - Question: {original_question} - - Do not use any other data. - Only use the movie data below when responding. - {search_results} - """, - ) - - # Get Embedding for the original question - question_embedded=embeddings.embed_query(request.Question) - - # Search Vector Store - search_client = SearchClient( - acs_endpoint_name, - acs_index_name, - AzureKeyCredential(acs_api_key) - ) - vector = Vector( - value=question_embedded, - k=5, - fields="content_vector" - ) - results = list(search_client.search( - search_text="", - include_total_count=True, - vectors=[vector], - select=["content"], - )) - - # Build the Prompt and Execute against the Azure OpenAI to get the completion - chain = LLMChain(llm=llm, prompt=prompt, verbose=True) - response = chain.run({"original_question": request.Question, "search_results": results}) - print(response) - return CompletionResponse(Completion = response) diff --git a/labs/04-deploy-ai/01-backend-api/acs-lc-python-api/acs-lc-python/requirements.txt b/labs/04-deploy-ai/01-backend-api/acs-lc-python-api/acs-lc-python/requirements.txt deleted file mode 100644 index 99d61ce..0000000 --- a/labs/04-deploy-ai/01-backend-api/acs-lc-python-api/acs-lc-python/requirements.txt +++ /dev/null @@ -1,9 +0,0 @@ -azure-core==1.29.3 -azure-identity==1.14.0 -azure-search-documents==11.4.0b8 -fastapi==0.99.1 -uvicorn==0.23.2 -openai==0.27.9 -langchain==0.0.329 -tiktoken==0.4.0 -python-dotenv==1.0.0 \ No newline at end of file diff --git a/labs/04-deploy-ai/01-backend-api/acs-sk-csharp-api/README.md b/labs/04-deploy-ai/01-backend-api/acs-sk-csharp-api/README.md deleted file mode 100644 index 0c4e63c..0000000 --- a/labs/04-deploy-ai/01-backend-api/acs-sk-csharp-api/README.md +++ /dev/null @@ -1,316 +0,0 @@ -# 04 - Deploy ACS Semantic Kernel C# API - -In this folder you will find a sample AI App that is built using C#, Semantic Kernel and Azure Cognitive Search. - -The entire solution is in this folder, but we also have all the step by step instructions so you can see how it was built. - -## Complete Solution - -1. To test locally fill in `appsettings.json` with the same values from the .env file that was used for the Jupyter Notebooks. -2. Build and Run the App - -```bash -dotnet run -``` - -## Step by Step Instructions - -### Create C# Project and Solution - -```bash -mkdir acs-sk-csharp -cd acs-sk-csharp - -dotnet new webapi --use-minimal-apis -dotnet new solution -dotnet sln acs-sk-csharp.sln add acs-sk-csharp.csproj -``` - -### Add Dependencies - -```bash -dotnet add acs-sk-csharp.csproj package Azure.Core --version "1.35.0" -dotnet add acs-sk-csharp.csproj package Azure.AI.OpenAI --version "1.0.0-beta.7" -dotnet add acs-sk-csharp.csproj package Microsoft.SemanticKernel --version "0.24.230918.1-preview" -dotnet add acs-sk-csharp.csproj package Microsoft.SemanticKernel.Abstractions --version "0.24.230918.1-preview" -dotnet add acs-sk-csharp.csproj package Azure.Search.Documents --version "11.5.0-beta.4" -``` - -### Update Program.cs - -The first thing we need to do is to add some Using statements to the `Program.cs` file. - -```csharp -using System.Text; -using Microsoft.SemanticKernel; -using Microsoft.SemanticKernel.Orchestration; -using Azure; -using Azure.Search.Documents; -using Azure.Search.Documents.Models; -using Azure.AI.OpenAI; -using Microsoft.AspNetCore.Mvc; -``` - -Next we read in the variables. Because this is ASP.NET Core we will switch from the DotEnv package we used in earlier labs to native ASP.NET Core Configuration. - -```csharp -var builder = WebApplication.CreateBuilder(args); - -// Load values into variables -var config = builder.Configuration; -var openai_api_type = config["OPENAI_API_TYPE"] ?? String.Empty; -var openai_api_key = config["OPENAI_API_KEY"] ?? String.Empty; -var openai_api_base = config["OPENAI_API_BASE"] ?? String.Empty; -var openai_api_version = config["OPENAI_API_VERSION"] ?? String.Empty; -var deployment_name = config["AZURE_OPENAI_COMPLETION_DEPLOYMENT_NAME"] ?? String.Empty; -var embedding_name = config["AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME"] ?? String.Empty; -var acs_service_name = config["AZURE_COGNITIVE_SEARCH_SERVICE_NAME"] ?? String.Empty; -var acs_endpoint_name = config["AZURE_COGNITIVE_SEARCH_ENDPOINT_NAME"] ?? String.Empty; -var acs_index_name = config["AZURE_COGNITIVE_SEARCH_INDEX_NAME"] ?? String.Empty; -var acs_api_key = config["AZURE_COGNITIVE_SEARCH_API_KEY"] ?? String.Empty; -Console.WriteLine($"openai_api_type = {openai_api_type}"); -Console.WriteLine("Configuration loaded."); -``` - -Next we add the Semantic Kernel to the ASP.NET Core dependency injection container. - -```csharp -// Add Semantic Kernel service to the container. -// Add in configuration options and required services. -builder.Services.AddSingleton(sp => sp.GetRequiredService>()); // some services require an un-templated ILogger -builder.Services.AddScoped(sp => -{ - // Setup Semantic Kernel - IKernel kernel = Kernel.Builder - .WithLoggerFactory(sp.GetRequiredService()) - .WithAzureChatCompletionService(deployment_name, openai_api_base, openai_api_key) - .WithAzureTextEmbeddingGenerationService(embedding_name, openai_api_base, openai_api_key) - .Build(); - - Console.WriteLine("SK Kernel with ChatCompletion and EmbeddingsGeneration services created."); - - return kernel; -}); -``` - -Next we swap out the `app.MapGet` `weatherforecast` function with our own. - -```csharp -// Configure Routing -app.MapPost("/completion", async ([FromServices] IKernel kernel, [FromBody] CompletionRequest request) => -{ - try - { - // Read values from .env file - // These are loaded during startup, see above for details. - - // Setup Semantic Kernel - // This has already been setup as part of the ASP.NET Core dependency injection setup - // and is passed into this function as a parameter. - // [FromServices] IKernel kernel - - // Ask the question - // The question is being passed in via the message body. - // [FromBody] string question - - // Configure a prompt as a plug in. We'll define the path to the plug in here and setup - // the plug in later. - var pluginsDirectory = Path.Combine(System.IO.Directory.GetCurrentDirectory(), "Plugins"); - var customPlugin = kernel.ImportSemanticSkillFromDirectory(pluginsDirectory, "CustomPlugin"); - - Console.WriteLine("Semantic Function GetIntent with SK has been completed."); - - // Get Embedding for the original question - OpenAIClient azureOpenAIClient = new OpenAIClient(new Uri(openai_api_base),new AzureKeyCredential(openai_api_key)); - float[] questionEmbedding = azureOpenAIClient.GetEmbeddings(embedding_name, new EmbeddingsOptions(request.Question)).Value.Data[0].Embedding.ToArray(); - - Console.WriteLine("Embedding of original question has been completed."); - - // Search Vector Store - SearchOptions searchOptions = new SearchOptions - { - // Filter to only Content greater than or equal our preference - // Filter = SearchFilter.Create($"Content ge {content}"), - // OrderBy = { "Content desc" } // Sort by Content from high to low - // Size = 5, // Take only 5 results - // Select = { "id", "content", "content_vector" }, // Which fields to return - Vectors = { new() { Value = questionEmbedding, KNearestNeighborsCount = 5, Fields = { "content_vector" } } }, // Vector Search - Size = 5, - Select = { "id", "content" }, - }; - - // Note the search text is null and the vector search is filled in. - AzureKeyCredential credential = new AzureKeyCredential(acs_api_key); - SearchClient searchClient = new SearchClient(new Uri(acs_endpoint_name), acs_index_name, credential); - SearchResults response = searchClient.Search(null, searchOptions); - Pageable> results = response.GetResults(); - // Create string from the results - StringBuilder stringBuilderResults = new StringBuilder(); - foreach (SearchResult result in results) - { - stringBuilderResults.AppendLine($"{result.Document["content"]}"); - }; - - Console.WriteLine("Searching of Vector Store has been completed."); - - // Build the Prompt and Execute against the Azure OpenAI to get the completion - // Initialize the prompt variables - ContextVariables variables = new ContextVariables - { - ["original_question"] = request.Question, - ["search_results"] = stringBuilderResults.ToString() - }; - // Use SK Chaining to Invoke Semantic Function - string completion = (await kernel.RunAsync(variables, customPlugin["GetIntent"])).Result; - Console.WriteLine(completion); - - Console.WriteLine("Implementation of RAG using SK, C# and Azure Cognitive Search has been completed."); - - return new CompletionResponse(completion); - } - catch (Exception exc) - { - Console.WriteLine($"Error: {exc.Message}"); - return new CompletionResponse("Something unexpected happened."); - } -}) -.WithName("Completion") -.WithOpenApi(); - -// Start the Process -await app.RunAsync(); - -public record CompletionRequest (string Question) {} - -public record CompletionResponse (string Completion) {} -``` - -Note at the end of the last section of code, we replaced the app.Run() from the original code with the async version. - -```csharp -await app.RunAsync(); -``` - -Also, as we've replaced the `weatherforecast` function, we can remove the `WeatherForecast` record. So, you can delete the following lines which should be at the end of the `Program.cs` file. - -```csharp -record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary) -{ - public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); -} -``` - -### Create a Plug In - -Next we will create a plug in that will be used to define the prompt that will be sent to the Azure OpenAI API. - -In the root of the project create a folder called `Plugins` and in that folder create another folder called `CustomPlugin`. - -```bash -mkdir Plugins -cd Plugins -mkdir CustomPlugin -``` - -The Plug In that we're going to create will be called `GetIntent`, so let's create a folder for that too. - -```bash -cd CustomPlugin -mkdir GetIntent -``` - -Under the `GetIntent` folder we will create two files. One file will provide the template for the prompt that we want to use with Azure OpenAI. The other file will provide the configuration parameters. - -Create a file called `skprompt.txt` and add the following text. - -```text -Question: {{$original_question}} - -Do not use any other data. -Only use the movie data below when responding. -{{$search_results}} -``` - -Next, create a file named `config.json` and add the following. - -```json -{ - "schema": 1, - "type": "completion", - "description": "Gets the intent of the user.", - "completion": { - "max_tokens": 500, - "temperature": 0.1, - "top_p": 0.5, - "presence_penalty": 0.0, - "frequency_penalty": 0.0 - }, - "input": { - "parameters": [ - { - "name": "original_question", - "description": "The user's request.", - "defaultValue": "" - }, - { - "name": "search_results", - "description": "Vector Search results from Azure Cognitive Search.", - "defaultValue": "" - } - ] - } -} -``` - -When you've completed the above steps, your folder structure should look like this. - -```text -acs-sk-csharp -├── Plugins -│ ├── CustomPlugin -│ │ ├── GetIntent -│ │ │ ├── config.json -│ │ │ ├── skprompt.txt -``` - -### Test the App - -Now that we have all the code in place let's compile and run it. - -```csharp -dotnet run -``` - -Once the app is started, open a browser and navigate to http://127.0.0.1:5291/swagger/index.html ->**Note:** the port number may be different to `5291`, so double check the output from the `dotnet run` command. - -Click on the "POST /completion" endpoint, click on "Try it out", enter a Prompt, "List the movies about ships on the water.", then click on "Execute". - -### Build and Test Docker Image - -Let's now package the solution into a Docker Image so it can be deployed to a container service like Azure Kubernetes Serivce (AKS) or Azure Container Apps (ACA). - -```bash -docker build -t acs-sk-csharp:v1 . -``` - -We can then test the image and be sure to set the environment variables so they override the values in the appsettings.json file. We don't want to have sensitive information embedded directly into the image. - -```bash -docker run -it --rm \ - --name acsskcsharp \ - -p 5291:5291 \ - -e OPENAI_API_TYPE="Set this to "azure" for API key authentication or "azure_ad" for Azure AD authentication>", \ - -e OPENAI_API_KEY="" \ - -e OPENAI_API_BASE="" \ - -e OPENAI_API_VERSION="2023-05-15" \ - -e OPENAI_COMPLETION_MODEL="" \ - -e AZURE_TENANT_ID="" \ - -e AZURE_OPENAI_COMPLETION_DEPLOYMENT_NAME="" \ - -e AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME="" \ - -e AZURE_COGNITIVE_SEARCH_SERVICE_NAME="" \ - -e AZURE_COGNITIVE_SEARCH_ENDPOINT_NAME="(sp => sp.GetRequiredService>()); // some services require an un-templated ILogger -builder.Services.AddScoped(sp => -{ - // Setup Semantic Kernel - IKernel kernel = Kernel.Builder - .WithLoggerFactory(sp.GetRequiredService()) - .WithAzureChatCompletionService(deployment_name, openai_api_base, openai_api_key) - .WithAzureTextEmbeddingGenerationService(embedding_name, openai_api_base, openai_api_key) - .Build(); - - Console.WriteLine("SK Kernel with ChatCompletion and EmbeddingsGeneration services created."); - - return kernel; -}); - -// Add services to the container. -// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle -builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddSwaggerGen(); - -var app = builder.Build(); - -// Configure the HTTP request pipeline. -app.UseSwagger(); -app.UseSwaggerUI(); -app.UseHttpsRedirection(); - -// Configure Routing -app.MapPost("/completion", async ([FromServices] IKernel kernel, [FromBody] CompletionRequest request) => -{ - try - { - // Read values from .env file - // These are loaded during startup, see above for details. - - // Setup Semantic Kernel - // This has already been setup as part of the ASP.NET Core dependency injection setup - // and is passed into this function as a parameter. - // [FromServices] IKernel kernel - - // Ask the question - // The question is being passed in via the message body. - // [FromBody] string question - - // Create a prompt template with variables, note the double curly braces with dollar sign for the variables. - // The PromptTemplate which was setup as inline SemanticFunction in the Polyglot notebook setup has been moved - // into the Plugins directory so it is easier to manage and configure. Picture the ability to mount updated - // prompt files into a container without having to rewrite the source code. - var pluginsDirectory = Path.Combine(System.IO.Directory.GetCurrentDirectory(), "Plugins"); - var customPlugin = kernel.ImportSemanticSkillFromDirectory(pluginsDirectory, "CustomPlugin"); - - Console.WriteLine("Semantic Function GetIntent with SK has been completed."); - - // Get Embedding for the original question - OpenAIClient azureOpenAIClient = new OpenAIClient(new Uri(openai_api_base),new AzureKeyCredential(openai_api_key)); - float[] questionEmbedding = azureOpenAIClient.GetEmbeddings(embedding_name, new EmbeddingsOptions(request.Question)).Value.Data[0].Embedding.ToArray(); - - Console.WriteLine("Embedding of original question has been completed."); - - // Search Vector Store - SearchOptions searchOptions = new SearchOptions - { - // Filter to only Content greater than or equal our preference - // Filter = SearchFilter.Create($"Content ge {content}"), - // OrderBy = { "Content desc" } // Sort by Content from high to low - // Size = 5, // Take only 5 results - // Select = { "id", "content", "content_vector" }, // Which fields to return - Vectors = { new() { Value = questionEmbedding, KNearestNeighborsCount = 5, Fields = { "content_vector" } } }, // Vector Search - Size = 5, - Select = { "id", "content" }, - }; - - // Note the search text is null and the vector search is filled in. - AzureKeyCredential credential = new AzureKeyCredential(acs_api_key); - SearchClient searchClient = new SearchClient(new Uri(acs_endpoint_name), acs_index_name, credential); - SearchResults response = searchClient.Search(null, searchOptions); - Pageable> results = response.GetResults(); - // Create string from the results - StringBuilder stringBuilderResults = new StringBuilder(); - foreach (SearchResult result in results) - { - stringBuilderResults.AppendLine($"{result.Document["content"]}"); - }; - - Console.WriteLine("Searching of Vector Store has been completed."); - - // Build the Prompt and Execute against the Azure OpenAI to get the completion - // Initialize the prompt variables - ContextVariables variables = new ContextVariables - { - ["original_question"] = request.Question, - ["search_results"] = stringBuilderResults.ToString() - }; - // Use SK Chaining to Invoke Semantic Function - string completion = (await kernel.RunAsync(variables, customPlugin["GetIntent"])).Result; - Console.WriteLine(completion); - - Console.WriteLine("Implementation of RAG using SK, C# and Azure Cognitive Search has been completed."); - - return new CompletionResponse(completion); - } - catch (Exception exc) - { - Console.WriteLine($"Error: {exc.Message}"); - return new CompletionResponse("Something unexpected happened."); - } -}) -.WithName("Completion") -.WithOpenApi(); - -// Start the Process -await app.RunAsync(); - -public record CompletionRequest (string Question) {} - -public record CompletionResponse (string Completion) {} \ No newline at end of file diff --git a/labs/04-deploy-ai/01-backend-api/acs-sk-csharp-api/acs-sk-csharp/appsettings.Development.json b/labs/04-deploy-ai/01-backend-api/acs-sk-csharp-api/acs-sk-csharp/appsettings.Development.json deleted file mode 100644 index ff66ba6..0000000 --- a/labs/04-deploy-ai/01-backend-api/acs-sk-csharp-api/acs-sk-csharp/appsettings.Development.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - } -} diff --git a/labs/04-deploy-ai/01-backend-api/acs-sk-csharp-api/acs-sk-csharp/appsettings.json b/labs/04-deploy-ai/01-backend-api/acs-sk-csharp-api/acs-sk-csharp/appsettings.json deleted file mode 100644 index 6460e4a..0000000 --- a/labs/04-deploy-ai/01-backend-api/acs-sk-csharp-api/acs-sk-csharp/appsettings.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - }, - "AllowedHosts": "*", - "OPENAI_API_TYPE": "Set this to "azure" for API key authentication or "azure_ad" for Azure AD authentication>", - "OPENAI_API_KEY": "", - "OPENAI_API_BASE": "", - "OPENAI_API_VERSION": "2023-05-15", - "OPENAI_COMPLETION_MODEL": "", - "AZURE_TENANT_ID": "", - "AZURE_OPENAI_COMPLETION_DEPLOYMENT_NAME": "", - "AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME": "", - "AZURE_COGNITIVE_SEARCH_SERVICE_NAME": "", - "AZURE_COGNITIVE_SEARCH_ENDPOINT_NAME": "", - "AZURE_COGNITIVE_SEARCH_API_KEY": "" -} \ No newline at end of file diff --git a/labs/04-deploy-ai/02-frontend-ui/chainlitagent-ui/.chainlit/translations/en-US.json b/labs/04-deploy-ai/02-frontend-ui/chainlitagent-ui/.chainlit/translations/en-US.json new file mode 100644 index 0000000..0bca720 --- /dev/null +++ b/labs/04-deploy-ai/02-frontend-ui/chainlitagent-ui/.chainlit/translations/en-US.json @@ -0,0 +1,231 @@ +{ + "components": { + "atoms": { + "buttons": { + "userButton": { + "menu": { + "settings": "Settings", + "settingsKey": "S", + "APIKeys": "API Keys", + "logout": "Logout" + } + } + } + }, + "molecules": { + "newChatButton": { + "newChat": "New Chat" + }, + "tasklist": { + "TaskList": { + "title": "\ud83d\uddd2\ufe0f Task List", + "loading": "Loading...", + "error": "An error occured" + } + }, + "attachments": { + "cancelUpload": "Cancel upload", + "removeAttachment": "Remove attachment" + }, + "newChatDialog": { + "createNewChat": "Create new chat?", + "clearChat": "This will clear the current messages and start a new chat.", + "cancel": "Cancel", + "confirm": "Confirm" + }, + "settingsModal": { + "settings": "Settings", + "expandMessages": "Expand Messages", + "hideChainOfThought": "Hide Chain of Thought", + "darkMode": "Dark Mode" + }, + "detailsButton": { + "using": "Using", + "running": "Running", + "took_one": "Took {{count}} step", + "took_other": "Took {{count}} steps" + }, + "auth": { + "authLogin": { + "title": "Login to access the app.", + "form": { + "email": "Email address", + "password": "Password", + "noAccount": "Don't have an account?", + "alreadyHaveAccount": "Already have an account?", + "signup": "Sign Up", + "signin": "Sign In", + "or": "OR", + "continue": "Continue", + "forgotPassword": "Forgot password?", + "passwordMustContain": "Your password must contain:", + "emailRequired": "email is a required field", + "passwordRequired": "password is a required field" + }, + "error": { + "default": "Unable to sign in.", + "signin": "Try signing in with a different account.", + "oauthsignin": "Try signing in with a different account.", + "redirect_uri_mismatch": "The redirect URI is not matching the oauth app configuration.", + "oauthcallbackerror": "Try signing in with a different account.", + "oauthcreateaccount": "Try signing in with a different account.", + "emailcreateaccount": "Try signing in with a different account.", + "callback": "Try signing in with a different account.", + "oauthaccountnotlinked": "To confirm your identity, sign in with the same account you used originally.", + "emailsignin": "The e-mail could not be sent.", + "emailverify": "Please verify your email, a new email has been sent.", + "credentialssignin": "Sign in failed. Check the details you provided are correct.", + "sessionrequired": "Please sign in to access this page." + } + }, + "authVerifyEmail": { + "almostThere": "You're almost there! We've sent an email to ", + "verifyEmailLink": "Please click on the link in that email to complete your signup.", + "didNotReceive": "Can't find the email?", + "resendEmail": "Resend email", + "goBack": "Go Back", + "emailSent": "Email sent successfully.", + "verifyEmail": "Verify your email address" + }, + "providerButton": { + "continue": "Continue with {{provider}}", + "signup": "Sign up with {{provider}}" + }, + "authResetPassword": { + "newPasswordRequired": "New password is a required field", + "passwordsMustMatch": "Passwords must match", + "confirmPasswordRequired": "Confirm password is a required field", + "newPassword": "New password", + "confirmPassword": "Confirm password", + "resetPassword": "Reset Password" + }, + "authForgotPassword": { + "email": "Email address", + "emailRequired": "email is a required field", + "emailSent": "Please check the email address {{email}} for instructions to reset your password.", + "enterEmail": "Enter your email address and we will send you instructions to reset your password.", + "resendEmail": "Resend email", + "continue": "Continue", + "goBack": "Go Back" + } + } + }, + "organisms": { + "chat": { + "history": { + "index": { + "showHistory": "Show history", + "lastInputs": "Last Inputs", + "noInputs": "Such empty...", + "loading": "Loading..." + } + }, + "inputBox": { + "input": { + "placeholder": "Type your message here..." + }, + "speechButton": { + "start": "Start recording", + "stop": "Stop recording" + }, + "SubmitButton": { + "sendMessage": "Send message", + "stopTask": "Stop Task" + }, + "UploadButton": { + "attachFiles": "Attach files" + }, + "waterMark": { + "text": "Built with" + } + }, + "Messages": { + "index": { + "running": "Running", + "executedSuccessfully": "executed successfully", + "failed": "failed", + "feedbackUpdated": "Feedback updated", + "updating": "Updating" + } + }, + "dropScreen": { + "dropYourFilesHere": "Drop your files here" + }, + "index": { + "failedToUpload": "Failed to upload", + "cancelledUploadOf": "Cancelled upload of", + "couldNotReachServer": "Could not reach the server", + "continuingChat": "Continuing previous chat" + }, + "settings": { + "settingsPanel": "Settings panel", + "reset": "Reset", + "cancel": "Cancel", + "confirm": "Confirm" + } + }, + "threadHistory": { + "sidebar": { + "filters": { + "FeedbackSelect": { + "feedbackAll": "Feedback: All", + "feedbackPositive": "Feedback: Positive", + "feedbackNegative": "Feedback: Negative" + }, + "SearchBar": { + "search": "Search" + } + }, + "DeleteThreadButton": { + "confirmMessage": "This will delete the thread as well as it's messages and elements.", + "cancel": "Cancel", + "confirm": "Confirm", + "deletingChat": "Deleting chat", + "chatDeleted": "Chat deleted" + }, + "index": { + "pastChats": "Past Chats" + }, + "ThreadList": { + "empty": "Empty...", + "today": "Today", + "yesterday": "Yesterday", + "previous7days": "Previous 7 days", + "previous30days": "Previous 30 days" + }, + "TriggerButton": { + "closeSidebar": "Close sidebar", + "openSidebar": "Open sidebar" + } + }, + "Thread": { + "backToChat": "Go back to chat", + "chatCreatedOn": "This chat was created on" + } + }, + "header": { + "chat": "Chat", + "readme": "Readme" + } + } + }, + "hooks": { + "useLLMProviders": { + "failedToFetchProviders": "Failed to fetch providers:" + } + }, + "pages": { + "Design": {}, + "Env": { + "savedSuccessfully": "Saved successfully", + "requiredApiKeys": "Required API Keys", + "requiredApiKeysInfo": "To use this app, the following API keys are required. The keys are stored on your device's local storage." + }, + "Page": { + "notPartOfProject": "You are not part of this project." + }, + "ResumeButton": { + "resumeChat": "Resume Chat" + } + } +} \ No newline at end of file diff --git a/labs/04-deploy-ai/02-frontend-ui/chainlitagent-ui/README.md b/labs/04-deploy-ai/02-frontend-ui/chainlitagent-ui/README.md index 62a1e73..d77ca53 100644 --- a/labs/04-deploy-ai/02-frontend-ui/chainlitagent-ui/README.md +++ b/labs/04-deploy-ai/02-frontend-ui/chainlitagent-ui/README.md @@ -1,8 +1,14 @@ # Building an app with Chainlit -The following section will demonstrate how to build a sample AI App using Chainlit. The official docs can be found here: https://docs.chainlit.io +The following section will demonstrate how to build a sample AI App using Chainlit. Chainlit is a tool that allows you to build AI applications with ease, by providing a simple interface to interact with AI models. -Here are the steps at a high-level. +The official docs can be found here: https://docs.chainlit.io. + +The app in this folder will connect to one of the backend applications in the `01-backend-api` folder. You can use either the Python/Langchain or .NET/Semantic Kernel version, either will work. + +Start one of the backend applications first, test and ensure the backend is working before starting the frontend. + +To run the chainlit application, follow the steps below: 1. Install any requirements @@ -16,5 +22,5 @@ pip install -r requirements.txt chainlit run app.py -w ``` -3. Open a browser and navigate to http://localhost:8000/ -4. Enter a prompt in the box at the bottom of the screen and hit "Enter". \ No newline at end of file +3. After a few moments, a browser window should open automatically. If not, go to a browser and navigate to http://localhost:8000/ +4. Enter a prompt in the box at the bottom of the screen and hit "Enter". Remember that the backend has been configured to answer questions about the movies that we uploaded to Azure AI Search. \ No newline at end of file diff --git a/labs/04-deploy-ai/02-frontend-ui/chainlitagent-ui/app.py b/labs/04-deploy-ai/02-frontend-ui/chainlitagent-ui/app.py index acee2c0..0f3f1a7 100644 --- a/labs/04-deploy-ai/02-frontend-ui/chainlitagent-ui/app.py +++ b/labs/04-deploy-ai/02-frontend-ui/chainlitagent-ui/app.py @@ -24,17 +24,18 @@ def main(): @cl.on_message -async def main(message: str): +async def main(message: cl.Message): # Retrieve any objects or information from the user session welcome = cl.user_session.get("welcome") # type: welcome # Call the backend api httprequest asynchronously # encoded_data = urllib.parse.urlencode(message).encode("utf-8") + print (message.content) headers = {'accept': 'application/json', 'content-type': 'application/json'} async with aiohttp.ClientSession(headers=headers) as session: async with session.post( - url=backend_api_base + "/completion", - data='{ "question": "' + message + '"}' + url=backend_api_base + "/completion/", + data='{ "Question": "' + message.content + '"}' ) as response: res = await response.text() json_response = json.loads(res) diff --git a/labs/04-deploy-ai/02-frontend-ui/chainlitagent-ui/chainlit.md b/labs/04-deploy-ai/02-frontend-ui/chainlitagent-ui/chainlit.md index a19a928..38d41b9 100644 --- a/labs/04-deploy-ai/02-frontend-ui/chainlitagent-ui/chainlit.md +++ b/labs/04-deploy-ai/02-frontend-ui/chainlitagent-ui/chainlit.md @@ -7,6 +7,6 @@ Hi there, Developer! 👋 We're excited to have you on board. Chainlit is a powe - **Documentation:** Get started with our comprehensive [Chainlit Documentation](https://docs.chainlit.io) 📚 - **Discord Community:** Join our friendly [Chainlit Discord](https://discord.gg/ZThrUxbAYw) to ask questions, share your projects, and connect with other developers! 💬 -This is sample UI for the Azure Cognitive Search + AI Orchestrator + Retrieval Augmented Generation (RAG) pattern API deployed in the previous step. This UI takes your question, sends it to the back-end API, and then displays the result from the back-end API which is the LLM completion. +This is a sample UI for the Azure AI Search + AI Orchestrator + Retrieval Augmented Generation (RAG) pattern API deployed in the previous step. This UI takes your question, sends it to the back-end API, and then displays the result from the back-end API which is the LLM completion. Sample question for the movie data that has been vectorized: **List the movies about ships on the water.** diff --git a/labs/04-deploy-ai/02-frontend-ui/chainlitagent-ui/requirements.txt b/labs/04-deploy-ai/02-frontend-ui/chainlitagent-ui/requirements.txt index b8e6d80..7b7f163 100644 --- a/labs/04-deploy-ai/02-frontend-ui/chainlitagent-ui/requirements.txt +++ b/labs/04-deploy-ai/02-frontend-ui/chainlitagent-ui/requirements.txt @@ -1,5 +1,3 @@ -chainlit==0.6.402 -fastapi==0.99.1 -python-dotenv==1.0.0 -asyncio==3.4.3 -aiohttp==3.9.0 \ No newline at end of file +chainlit==1.0.506 +fastapi==0.110.1 +python-dotenv==1.0.1 From 4b14f328101c3d68b189fddd965752959216f857 Mon Sep 17 00:00:00 2001 From: Mark Whitby Date: Mon, 13 May 2024 19:28:02 +0100 Subject: [PATCH 2/2] Updating build workflows for python / sk apps --- .github/workflows/build-acs-lc-python-api.yml | 4 ++-- .github/workflows/build-acs-sk-csharp-api.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-acs-lc-python-api.yml b/.github/workflows/build-acs-lc-python-api.yml index 43d39ba..57f5367 100644 --- a/.github/workflows/build-acs-lc-python-api.yml +++ b/.github/workflows/build-acs-lc-python-api.yml @@ -9,10 +9,10 @@ on: defaults: run: - working-directory: labs/04-deploy-ai/01-backend-api/acs-lc-python-api/acs-lc-python + working-directory: labs/04-deploy-ai/01-backend-api/aais-lc-python-api/aais-lc-python env: - IMAGE_NAME: acs-lc-python-api + IMAGE_NAME: aais-lc-python-api jobs: diff --git a/.github/workflows/build-acs-sk-csharp-api.yml b/.github/workflows/build-acs-sk-csharp-api.yml index 2cace66..29636ad 100644 --- a/.github/workflows/build-acs-sk-csharp-api.yml +++ b/.github/workflows/build-acs-sk-csharp-api.yml @@ -9,10 +9,10 @@ on: defaults: run: - working-directory: labs/04-deploy-ai/01-backend-api/acs-sk-csharp-api/acs-sk-csharp + working-directory: labs/04-deploy-ai/01-backend-api/aais-sk-csharp-api/aais-sk-csharp env: - IMAGE_NAME: acs-sk-csharp-api + IMAGE_NAME: aais-sk-csharp-api jobs: