User Tools

Site Tools


wiki:ai:chatbot-helpdesk-sentiment

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
wiki:ai:chatbot-helpdesk-sentiment [2025/06/24 20:09] ddehamerwiki:ai:chatbot-helpdesk-sentiment [2025/07/02 21:10] (current) zhein
Line 1: Line 1:
-====== AI Chatbot wit Sentiment and In Kind Responses ======+====== AI Chatbot with Sentiment and In Kind Responses ====== 
  
 ===== Requirements ===== ===== Requirements =====
Line 12: Line 13:
  
 ===== Prerequisites ===== ===== Prerequisites =====
 +
 Install These Install These
-<code>+ 
 +<code ->
 brew install dotnet-sdk #This will be version 9 which will not work but gives you the rest of the stuff needed. brew install dotnet-sdk #This will be version 9 which will not work but gives you the rest of the stuff needed.
 brew install azure-cli brew install azure-cli
 </code> </code>
  
-Assuming you already have created a Resource Group named don-test-rg, an OpenAI deployment named  and deployed a gpt model named gpt-4.1.+Assuming you already have created a Resource Group named don-test-rg, an OpenAI deployment named don-openai-useast, and deployed a gpt model named gpt-4.1 inside openai.  Those all require the portal.
  
-Install this from the webpage and follow directions. +Install this from the webpage and follow directions. [[https://dotnet.microsoft.com/en-us/download/dotnet/8.0|dotnet 8.x]]
-[[https://dotnet.microsoft.com/en-us/download/dotnet/8.0|dotnet 8.x]]+
  
-Then these +Create a folder in your workspace and save the files at the end of this document.  
-<code>+ 
 +Open a terminal, set your folder to the one where the files are saved, then run these commands: 
 + 
 +These commands are only needed for Windows users: 
 +<code -> 
 +dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org 
 +npm install -g azure-functions-core-tools@4 --unsafe-perm true 
 +dotnet nuget locals all --clear 
 +dotnet restore 
 +</code> 
 + 
 +The rest of the instructions are for every OS: 
 + 
 +<code ->
 dotnet add package Microsoft.Azure.Functions.Worker.Extensions.OpenApi --version 1.4.0 dotnet add package Microsoft.Azure.Functions.Worker.Extensions.OpenApi --version 1.4.0
 dotnet --list-sdks dotnet --list-sdks
Line 32: Line 47:
  
 Create Storage Account Create Storage Account
-<code>+ 
 +<code ->
 az storage account create \ az storage account create \
   --name dehamerfuncstorage \   --name dehamerfuncstorage \
Line 41: Line 57:
  
 Create Function Plan for Windows Create Function Plan for Windows
-<code>+ 
 +<code ->
 az functionapp plan create \ az functionapp plan create \
   --resource-group don-test-rg \   --resource-group don-test-rg \
-  --name donfuncplan \+  --name dehamerfuncplan \
   --location eastus \   --location eastus \
   --sku B1 \   --sku B1 \
Line 51: Line 68:
  
 Create Function App Create Function App
-<code>+ 
 +<code ->
 az functionapp create \ az functionapp create \
   --resource-group don-test-rg \   --resource-group don-test-rg \
   --name dehamerfuncapp \   --name dehamerfuncapp \
-  --plan donfuncplan \+  --plan dehamerfuncplan \
   --storage-account dehamerfunctstorage \   --storage-account dehamerfunctstorage \
   --runtime dotnet-isolated \   --runtime dotnet-isolated \
Line 63: Line 81:
  
 Create TexAnalytics Create TexAnalytics
-<code>+ 
 +<code ->
 az cognitiveservices account create \ az cognitiveservices account create \
   --name dehamersentimentai \   --name dehamersentimentai \
Line 73: Line 92:
  
 az cognitiveservices account update \ az cognitiveservices account update \
-  --name donsentimentai \+  --name dehamersentimentai \
   --resource-group don-test-rg \   --resource-group don-test-rg \
   --set properties.publicNetworkAccess=Enabled   --set properties.publicNetworkAccess=Enabled
Line 79: Line 98:
  
 Get keys and endpoint for export and app settings Get keys and endpoint for export and app settings
-<code>+ 
 +<code ->
 export TEXT_ANALYTICS_ENDPOINT=`az cognitiveservices account show \  export TEXT_ANALYTICS_ENDPOINT=`az cognitiveservices account show \ 
   --name dehamersentimentai \   --name dehamersentimentai \
Line 86: Line 106:
  
 export TEXT_ANALYTICS_KEY=`az cognitiveservices account keys list \ export TEXT_ANALYTICS_KEY=`az cognitiveservices account keys list \
-  --name dehamersentimentai \+  --name dehamersentamentai \
   --resource-group don-test-rg \   --resource-group don-test-rg \
   --query "key1" -o tsv`   --query "key1" -o tsv`
Line 103: Line 123:
  
 export OPENAI_ENDPOINT=https://don-openai-useast.openai.azure.com/ export OPENAI_ENDPOINT=https://don-openai-useast.openai.azure.com/
 +
 +export OPENAI_DEPLOYMENT=gpt-4.1
  
 az functionapp config appsettings set \ az functionapp config appsettings set \
-  --name donfuncapp \+  --name dehamerfuncapp \
   --resource-group don-test-rg \   --resource-group don-test-rg \
   --settings \   --settings \
   OPENAI_KEY=$OPENAI_KEY \   OPENAI_KEY=$OPENAI_KEY \
   OPENAI_ENDPOINT=$OPENAI_ENDPOINT   OPENAI_ENDPOINT=$OPENAI_ENDPOINT
 +  OPENAI_DEPLOYMENT=$OPENAI_DEPLOYMENT
      
 #Confirm they were set #Confirm they were set
Line 119: Line 142:
  
 Create a directory to store the functionapp files Create a directory to store the functionapp files
-<code>+ 
 +<code ->
 mkdir ~/FeedbackFunctionDotNet mkdir ~/FeedbackFunctionDotNet
 cd ~/FeedbackFunctionDotNet cd ~/FeedbackFunctionDotNet
 </code> </code>
  
-Save the FeedbackFunction.cs, FeedbackFunction.csproj, global.json, host.json, and local.settings.json into the FeedBackFunctionDotNet directory.+Save the FeedbackFunction.cs, FeedbackFunction.csproj, global.json, host.json, local.settings.json, and program.cs into the FeedBackFunctionDotNet directory.
  
 <code - FeedbackFunction.cs> <code - FeedbackFunction.cs>
Line 178: Line 202:
  
         // OpenAI Setup         // OpenAI Setup
-        var openaiEndpoint = Environment.GetEnvironmentVariable("OPENAI_API_ENDPOINT")?.TrimEnd('/'); +        var openaiEndpoint = Environment.GetEnvironmentVariable("OPENAI_ENDPOINT")?.TrimEnd('/'); 
-        var openaiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY");+        var openaiKey = Environment.GetEnvironmentVariable("OPENAI_KEY");
         var deployment = Environment.GetEnvironmentVariable("OPENAI_DEPLOYMENT");         var deployment = Environment.GetEnvironmentVariable("OPENAI_DEPLOYMENT");
  
Line 264: Line 288:
  
 Make sure this matches the version of dotnet 8.x you install. Make sure this matches the version of dotnet 8.x you install.
 +
 <code - global.json> <code - global.json>
 { {
Line 287: Line 312:
 } }
 </code> </code>
 +
 +<code - program.cs>
 +using Microsoft.Extensions.Hosting;
 +using Microsoft.Extensions.DependencyInjection;
 +
 +var host = new HostBuilder()
 +    .ConfigureFunctionsWorkerDefaults()
 +    .Build();
 +
 +host.Run();
 +</code>
 +
 +From the FeedbackFunctionDonnet directory:
 +
 +<code ->
 +dotnet clean
 +dotnet publish -c Release -o publish
 +cd publish
 +zip -r ../publish.zip .
 +cd ..
 +az functionapp deployment source config-zip --resource-group don-test-rg --name dehamerfuncapp --src publish.zip
 +</code>
 +
 +After running you will see additional files in the FeedbackFunctionDotnet directory.  This is normal.
 +
 +<code ->
 +ls
 +bin                     FeedbackFunction.csproj host.json               obj                     publish
 +FeedbackFunction.cs     global.json             local.settings.json     Program.cs              publish.zip
 +</code>
 +
 +To test if it is working
 +
 +<code ->
 +export FUNCTION_KEY=`az functionapp function keys list \
 +  --name dehamerfuncapp \
 +  --resource-group don-test-rg \
 +  --function-name dehamerfeedbackfunction --query "default" | sed -e s:\"::`
 +
 +curl -X POST https://dehamerfuncapp.azurewebsites.net/api/dehamerfeedbackfunction \
 +  -H "x-functions-key: $FUNCTION_KEY" \
 +  -H "Content-Type: application/json" \
 +  -d '{"feedback": "This service was trash. I want my time back."}'
 +</code>
 +
 +You should see something like:
 +
 +<code ->
 +{"sentiment":"negative","message":"Oh, absolutely\u2014because we totally specialize in time travel, right? Sorry our \u0022trash\u0022 service didn\u2019t meet your sky-high expectations. Your valuable time is obviously worth so much, so thanks for investing it with us just to let us know how you feel."}%
 +</code>
 +
 +Confirmation tests
 +
 +<code ->
 +az functionapp function list --name dehamerfuncapp --resource-group don-test-rg --query "[].{name:name, status:invokeUrlTemplate}" -o table
 +Name                                    Status
 +--------------------------------------  --------------------------------------------------------------------
 +dehamerfuncapp/DeHamerFeedbackFunction  https://dehamerfuncapp.azurewebsites.net/api/dehamerfeedbackfunction
 +</code>
 +
 +<code ->
 +az functionapp show --name dehamerfuncapp --resource-group don-test-rg --query "enabledHostNames"
 +[
 +  "dehamerfuncapp.azurewebsites.net",
 +  "dehamerfuncapp.scm.azurewebsites.net"
 +]
 +</code>
 +
 +<code ->
 +az functionapp function list \
 +  --name dehamerfuncapp \
 +  --resource-group don-test-rg \
 +  --query "[].invokeUrlTemplate" \
 +  --output tsv
 +
 +https://dehamerfuncapp.azurewebsites.net/api/dehamerfeedbackfunction
 +</code>
 +
 +<code ->
 +az functionapp function show \
 +  --name dehamerfuncapp \
 +  --resource-group don-test-rg \
 +  --function-name dehamerfeedbackfunction \
 +  --query "isDisabled"
 +
 +false
 +</code>
 +
 +If true
 +
 +<code ->
 +az functionapp function update \
 +  --name dehamerfuncapp \
 +  --resource-group don-test-rg \
 +  --function-name dehamerfeedbackfunction \
 +  --set isDisabled=false
 +</code>
 +
 +Tailing logs if enabled.  Notice it says webapp, not functionapp.  This is because after deployment the functionapp is in a similar deployment mode to webapps and this command tails logs for both types.  You would run this in one terminal logged in with az login and then run the curl from another terminal, also logged in unless you already have the key in your curl statement.
 +
 +<code ->
 +az webapp log tail \
 +  --name dehamerfuncapp \
 +  --resource-group don-test-rg
 +</code>
 +
 +If you want it to autostart after issues.
 +
 +<code ->
 +#Optional
 +az webapp config set \
 +  --name dehamerfuncapp \
 +  --resource-group don-test-rg \
 +  --always-on true
 +</code>
 +
 +Get a list of your functionapps
 +
 +<code ->
 +az functionapp function list \
 +  --name dehamerfuncapp \
 +  --resource-group don-test-rg \
 +  --output table
 +
 +Href                                                                              InvokeUrlTemplate                                                     IsDisabled    Language         Location    Name                                    ResourceGroup    ScriptHref                                                                            TestData    TestDataHref
 +--------------------------------------------------------------------------------  --------------------------------------------------------------------  ------------  ---------------  ----------  --------------------------------------  ---------------  ------------------------------------------------------------------------------------  ----------  --------------------------------------------------------------------------------------------------------
 +https://dehamerfuncapp.azurewebsites.net/admin/functions/DeHamerFeedbackFunction  https://dehamerfuncapp.azurewebsites.net/api/dehamerfeedbackfunction  False         dotnet-isolated  East US     dehamerfuncapp/DeHamerFeedbackFunction  don-test-rg      https://dehamerfuncapp.azurewebsites.net/admin/vfs/site/wwwroot/FeedbackFunction.dll              https://dehamerfuncapp.azurewebsites.net/admin/vfs/data/Functions/sampledata/DeHamerFeedbackFunction.dat
 +</code>
 +
 +Only do this for testing.  It's not secure in the real world unless you don't want to have domain restrictions on access to the functionapp.  I had to do it so I could test from my home webserver.
 +
 +<code ->
 +az functionapp cors add \
 +  --name dehamerfuncapp \
 +  --resource-group don-test-rg \
 +  --allowed-origins "*"
 +</code>
 +
 +If you have a webserver that you trust, you can create this webpage and test it:
 +
 +<code - index.html>
 +<!DOCTYPE html>
 +<html lang="en">
 +<head>
 +  <meta charset="UTF-8" />
 +  <title>Dennis Feedback Page</title>
 +  <style>
 +    body {
 +      background-color: white;
 +      color: red;
 +      font-family: Arial, sans-serif;
 +      margin: 40px;
 +    }
 +
 +    header {
 +      display: flex;
 +      align-items: center;
 +      margin-bottom: 40px;
 +    }
 +
 +    header img {
 +      height: 50px;
 +      margin-right: 20px;
 +    }
 +
 +    h1 {
 +      font-size: 2em;
 +    }
 +
 +    textarea {
 +      width: 100%;
 +      height: 100px;
 +      font-size: 1em;
 +      padding: 10px;
 +    }
 +
 +    button {
 +      margin-top: 10px;
 +      padding: 10px 20px;
 +      font-size: 1em;
 +      background-color: red;
 +      color: white;
 +      border: none;
 +      cursor: pointer;
 +    }
 +
 +    #response {
 +      margin-top: 20px;
 +      font-weight: bold;
 +      white-space: pre-line;
 +    }
 +  </style>
 +</head>
 +<body>
 +  <header>
 +    <img src="https://webobjects2.cdw.com/is/image/CDW/cdw23-logo-no-tag-88x48?$transparent$" alt="CDW Logo" />
 +    <h1>Dennis Feedback Page</h1>
 +  </header>
 +
 +  <form id="feedbackForm">
 +    <label for="feedback">Enter your feedback:</label><br>
 +    <textarea id="feedback" name="feedback" required></textarea><br>
 +    <button type="submit">Submit</button>
 +  </form>
 +
 +  <div id="response"></div>
 +
 +  <script>
 +    document.getElementById('feedbackForm').addEventListener('submit', async function (e) {
 +      e.preventDefault();
 +
 +      const feedback = document.getElementById('feedback').value;
 +      const responseDiv = document.getElementById('response');
 +
 +      responseDiv.textContent = "Processing...";
 +
 +      try {
 +        const res = await fetch('https://dehamerfuncapp.azurewebsites.net/api/dehamerfeedbackfunction', {
 +          method: 'POST',
 +          headers: {
 +            'Content-Type': 'application/json'
 +          },
 +          body: JSON.stringify({ feedback })
 +        });
 +
 +        if (!res.ok) {
 +          throw new Error("API error: " + res.status);
 +        }
 +
 +        const data = await res.json();
 +        responseDiv.innerHTML = `<strong>Sentiment:</strong> ${data.sentiment}<br><strong>Response:</strong> ${data.message}`;
 +      } catch (err) {
 +        responseDiv.textContent = "Error: " + err.message;
 +      }
 +    });
 +  </script>
 +</body>
 +</html>
 +</code>
 +
 +===== Dennis Additions =====
 +
 +==== Azure CLI vs Azure Portal Instructions ====
 +
 +| Task | Azure CLI Command | Azure Portal Steps |
 +| Create Resource Group | az group create --name don-test-rg --location eastus | Go to Azure Portal > Resource Groups > Create |
 +| Create Storage Account | az storage account create --name dehamerfuncstorage --location eastus --resource-group don-test-rg --sku Standard_LRS | Go to Storage Accounts > Create |
 +| Create Function App | az functionapp create --resource-group don-test-rg --consumption-plan-location eastus --runtime dotnet-isolated --functions-version 4 --name dehamerfuncapp --storage-account dehamerfuncstorage | Go to Function Apps > Create > Choose .NET Isolated |
 +| Deploy Code via Zip | az functionapp deployment source config-zip --src publish.zip --name dehamerfuncapp --resource-group don-test-rg | Go to Function App > Deployment Center > Zip Deploy |
 +| Create Cognitive Services Resource | az cognitiveservices account create --name dehamersentimentai --resource-group don-test-rg --kind TextAnalytics --sku S --location eastus --yes | Search 'Cognitive Services' > Create > Choose Text Analytics |
 +| Set App Settings | az functionapp config appsettings set --name dehamerfuncapp --resource-group don-test-rg --settings KEY=value | Go to Configuration > Application Settings > Add New |
 +
 +==== Successful Deployment Steps ====
 +
 +After resolving early build issues and switching from Linux to a Windows-hosted function plan due to SDK compatibility, the deployment succeeded with the following workflow:
 +
 +- Rewrote the function class to eliminate naming conflicts\\ - Set AuthorizationLevel to Anonymous to permit open feedback submission\\ - Used curl to verify responses from the deployed function\\ - Created an HTML frontend to POST feedback to the function endpoint
 +
 +==== Architectural Diagram ====
 +
 +{{:wiki:ai:chatgpt_image_jun_24_2025_05_00_18_pm.png}}\\ \\
 +
 +[[ai_knowledge|AI Knowledge]]
 +
 +
wiki/ai/chatbot-helpdesk-sentiment.1750795789.txt.gz · Last modified: by ddehamer