workshop/dotnet/App/backend/Controllers/ChatController.cs (118 lines of code) (raw):
using Core.Utilities.Models;
using Core.Utilities.Config;
using Core.Utilities.Extensions;
// Add import required for StockService
using Microsoft.SemanticKernel;
// Add ChatCompletion import
using Microsoft.SemanticKernel.ChatCompletion;
// Add import for Agents
using Microsoft.SemanticKernel.Agents.AzureAI;
using Microsoft.SemanticKernel.Agents;
// Temporarily added to enable Semantic Kernel tracing
using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.AspNetCore.Mvc;
using Azure;
namespace Controllers;
[ApiController]
[Route("sk")]
public class ChatController : ControllerBase {
private readonly Kernel _kernel;
private AzureAIAgent _stockSentimentAgent;
private AgentsClient _agentsClient;
private readonly string _connectionString;
private readonly string _groundingWithBingConnectionId;
private readonly string _deploymentName;
private readonly string _managedIdentityClientId;
public ChatController(Kernel kernel)
{
_kernel = kernel;
_connectionString = AISettingsProvider.GetSettings().AIFoundryProject.ConnectionString;
_groundingWithBingConnectionId = AISettingsProvider.GetSettings().AIFoundryProject.GroundingWithBingConnectionId;
_deploymentName = AISettingsProvider.GetSettings().AIFoundryProject.DeploymentName;
_managedIdentityClientId = AISettingsProvider.GetSettings().ManagedIdentity?.ClientId;
_agentsClient = GetAgentsClient().Result;
_stockSentimentAgent = GetAzureAIAgent().Result;
}
/// <summary>
/// Get StockSemanticAgent instance
/// </summary>
/// <returns></returns>
private async Task<AzureAIAgent> GetAzureAIAgent()
{
var credential = GetDefaultAzureCredential();
var projectClient = new AIProjectClient(_connectionString, credential);
var clientProvider = AzureAIClientProvider.FromConnectionString(_connectionString, credential);
ConnectionResponse bingConnection = await projectClient.GetConnectionsClient().GetConnectionAsync(_groundingWithBingConnectionId);
var connectionId = bingConnection.Id;
ToolConnectionList connectionList = new ToolConnectionList
{
ConnectionList = { new ToolConnection(connectionId) }
};
BingGroundingToolDefinition bingGroundingTool = new BingGroundingToolDefinition(connectionList);
var definition = await _agentsClient.CreateAgentAsync(
_deploymentName,
instructions:
"""
Your responsibility is to find the stock sentiment for a given Stock, emitting advice in a creative and funny tone.
RULES:
- Report a stock sentiment scale from 1 to 10 where stock sentiment is 1 for sell and 10 for buy.
- Only use current data reputable sources such as Yahoo Finance, MarketWatch, Fidelity and similar.
- Provide the stock sentiment scale in your response and a recommendation to buy, hold or sell.
- Include the reasoning behind your recommendation.
- Be sure to cite the source of the information.
""",
tools:
[
bingGroundingTool
]);
var agent = new AzureAIAgent(definition, clientProvider)
{
Kernel = _kernel,
};
return agent;
}
/// <summary>
/// Get AgentsClient instance
/// </summary>
/// <returns></returns>
private async Task<AgentsClient> GetAgentsClient()
{
var clientProvider = AzureAIClientProvider.FromConnectionString(_connectionString, GetDefaultAzureCredential());
return clientProvider.Client.GetAgentsClient();
}
private DefaultAzureCredential GetDefaultAzureCredential()
{
// Conditionally set the Azure credentials because a managed identity client is required if you're running in ACA but not locally
var credential = string.IsNullOrEmpty(_managedIdentityClientId) ?
new DefaultAzureCredential()
: new DefaultAzureCredential(new DefaultAzureCredentialOptions
{
ManagedIdentityClientId = _managedIdentityClientId
});
return credential;
}
[HttpPost("/chat")]
public async Task<ChatResponse> ReplyAsync([FromBody]ChatRequest request)
{
var chatHistory = new ChatHistory();
if (request.MessageHistory.Count == 0) {
chatHistory.AddSystemMessage("You are a friendly financial advisor who only emits financial advice in a creative and funny tone.");
}
else {
chatHistory = request.ToChatHistory();
}
// Initialize fullMessage variable and add user input to chat history
string fullMessage = "";
if (request.InputMessage != null)
{
chatHistory.AddUserMessage(request.InputMessage);
// Create a thread for the agent conversation.
AgentThread thread = await _agentsClient.CreateThreadAsync();
ChatMessageContent message = new(AuthorRole.User, request.InputMessage);
await _stockSentimentAgent.AddChatMessageAsync(thread.Id, message);
await foreach (ChatMessageContent response in _stockSentimentAgent.InvokeAsync(thread.Id))
{
// Include TextContent (via ChatMessageContent.Content), if present.
string contentExpression = string.IsNullOrWhiteSpace(response.Content) ? string.Empty : response.Content;
chatHistory.AddAssistantMessage(contentExpression);
fullMessage += contentExpression;
// Provide visibility for inner content (that isn't TextContent).
foreach (KernelContent item in response.Items)
{
if (item is AnnotationContent annotation)
{
var annotationExpression = ($"{annotation.Quote}: File #{annotation.FileId}");
chatHistory.AddAssistantMessage(annotationExpression);
fullMessage += annotationExpression;
}
}
}
}
var chatResponse = new ChatResponse(fullMessage, chatHistory.FromChatHistory());
return chatResponse;
}
}