microsoft/teams.net
Publicmirrored fromhttps://github.com/microsoft/teams.netAvailable
Samples/Samples.AI/Program.cs
159lines · modecode
| 1 | using System.Text.RegularExpressions; |
| 2 | using Microsoft.Teams.Apps.Extensions; |
| 3 | using Microsoft.Teams.Apps.Activities; |
| 4 | using Microsoft.Teams.Apps.Activities.Invokes; |
| 5 | using Microsoft.Teams.Plugins.AspNetCore.Extensions; |
| 6 | using Microsoft.Teams.Plugins.AspNetCore.DevTools.Extensions; |
| 7 | using Microsoft.Teams.AI.Models.OpenAI; |
| 8 | using Microsoft.Teams.AI.Prompts; |
| 9 | using Microsoft.Teams.AI.Templates; |
| 10 | using Azure.AI.OpenAI; |
| 11 | using System.ClientModel; |
| 12 | using Microsoft.Teams.Api.Activities; |
| 13 | using Samples.AI.Handlers; |
| 14 | |
| 15 | var builder = WebApplication.CreateBuilder(args); |
| 16 | |
| 17 | // Configuration |
| 18 | var azureOpenAIModel = builder.Configuration["AzureOpenAIModel"] ?? throw new InvalidOperationException("AzureOpenAIModel not configured"); |
| 19 | var azureOpenAIEndpoint = builder.Configuration["AzureOpenAIEndpoint"] ?? throw new InvalidOperationException("AzureOpenAIEndpoint not configured"); |
| 20 | var azureOpenAIKey = builder.Configuration["AzureOpenAIKey"] ?? throw new InvalidOperationException("AzureOpenAIKey not configured"); |
| 21 | |
| 22 | var azureOpenAI = new AzureOpenAIClient( |
| 23 | new Uri(azureOpenAIEndpoint), |
| 24 | new ApiKeyCredential(azureOpenAIKey) |
| 25 | ); |
| 26 | |
| 27 | // Register AI Model as singleton |
| 28 | var aiModel = new OpenAIChatModel(azureOpenAIModel, azureOpenAI); |
| 29 | |
| 30 | builder.AddTeams().AddTeamsDevTools(); |
| 31 | var app = builder.Build(); |
| 32 | |
| 33 | var teamsApp = app.UseTeams(); |
| 34 | |
| 35 | // Simple chat handler - "hi" command |
| 36 | teamsApp.OnMessage(@"^hi$", async (context) => |
| 37 | { |
| 38 | context.Log.Info($"[COMMAND] 'hi' command invoked by user: {context.Activity.From.Name}"); |
| 39 | |
| 40 | var prompt = new OpenAIChatPrompt(aiModel, new ChatPromptOptions |
| 41 | { |
| 42 | Instructions = new StringTemplate("You are a friendly assistant who talks like a pirate") |
| 43 | }); |
| 44 | |
| 45 | context.Log.Info("[COMMAND] Sending 'hi' message to AI with pirate personality..."); |
| 46 | var result = await prompt.Send(context.Activity.Text); |
| 47 | if (result.Content != null) |
| 48 | { |
| 49 | context.Log.Info($"[COMMAND] AI response: {result.Content}"); |
| 50 | var messageActivity = new MessageActivity |
| 51 | { |
| 52 | Text = result.Content, |
| 53 | }.AddAIGenerated(); |
| 54 | await context.Send(messageActivity); |
| 55 | } |
| 56 | }); |
| 57 | |
| 58 | // Pokemon command handler |
| 59 | teamsApp.OnMessage(@"^pokemon\s+(.+)", async (context) => |
| 60 | { |
| 61 | context.Log.Info($"[COMMAND] 'pokemon' command invoked: {context.Activity.Text}"); |
| 62 | var match = Regex.Match(context.Activity.Text ?? "", @"^pokemon\s+(.+)", RegexOptions.IgnoreCase); |
| 63 | if (match.Success) |
| 64 | { |
| 65 | var pokemonName = match.Groups[1].Value.Trim(); |
| 66 | context.Log.Info($"[COMMAND] Extracted pokemon name: '{pokemonName}'"); |
| 67 | context.Activity.Text = pokemonName; |
| 68 | await FunctionCallingHandler.HandlePokemonSearch(aiModel, context); |
| 69 | } |
| 70 | }); |
| 71 | |
| 72 | |
| 73 | // Streaming handler |
| 74 | teamsApp.OnMessage(@"^stream\s+(.+)", async (context) => |
| 75 | { |
| 76 | context.Log.Info($"[COMMAND] 'stream' command invoked: {context.Activity.Text}"); |
| 77 | var match = Regex.Match(context.Activity.Text ?? "", @"^stream\s+(.+)", RegexOptions.IgnoreCase); |
| 78 | if (match.Success) |
| 79 | { |
| 80 | var query = match.Groups[1].Value.Trim(); |
| 81 | context.Log.Info($"[COMMAND] Extracted query for streaming: '{query}'"); |
| 82 | var prompt = new OpenAIChatPrompt(aiModel, new ChatPromptOptions |
| 83 | { |
| 84 | Instructions = new StringTemplate("You are a friendly assistant who responds in extremely verbose language") |
| 85 | }); |
| 86 | |
| 87 | context.Log.Info("[COMMAND] Sending streaming request to AI..."); |
| 88 | var result = await prompt.Send(query, (chunk) => |
| 89 | { |
| 90 | context.Log.Info($"[STREAM] Chunk received: {chunk}"); |
| 91 | context.Stream.Emit(chunk); |
| 92 | return Task.CompletedTask; |
| 93 | }); |
| 94 | } |
| 95 | }); |
| 96 | |
| 97 | // Citations handler |
| 98 | teamsApp.OnMessage(@"^citations?\b", async (context) => |
| 99 | { |
| 100 | context.Log.Info($"[COMMAND] 'citations' command invoked: {context.Activity.Text}"); |
| 101 | await CitationsHandler.HandleCitationsDemo(context); |
| 102 | }); |
| 103 | |
| 104 | // Feedback loop handler |
| 105 | teamsApp.OnMessage(@"^feedback\s+(.+)", async (context) => |
| 106 | { |
| 107 | context.Log.Info($"[COMMAND] 'feedback' command invoked: {context.Activity.Text}"); |
| 108 | var match = Regex.Match(context.Activity.Text ?? "", @"^feedback\s+(.+)", RegexOptions.IgnoreCase); |
| 109 | if (match.Success) |
| 110 | { |
| 111 | var query = match.Groups[1].Value.Trim(); |
| 112 | context.Log.Info($"[COMMAND] Extracted query for feedback: '{query}'"); |
| 113 | context.Activity.Text = query; |
| 114 | await FeedbackHandler.HandleFeedbackLoop(aiModel, context); |
| 115 | } |
| 116 | }); |
| 117 | |
| 118 | // Memory clear handler |
| 119 | teamsApp.OnMessage(@"^memory\s+clear\b", async (context) => |
| 120 | { |
| 121 | context.Log.Info($"[COMMAND] 'memory clear' command invoked for conversation: {context.Activity.Conversation.Id}"); |
| 122 | await MemoryManagementHandler.ClearConversationMemory(context.Activity.Conversation.Id); |
| 123 | await context.Reply("🧠 Memory cleared!"); |
| 124 | }); |
| 125 | |
| 126 | // Prompt-based handler (declarative style) |
| 127 | teamsApp.OnMessage(@"^/weather\b", async (context) => |
| 128 | { |
| 129 | context.Log.Info($"[COMMAND] '/weather' command invoked: {context.Activity.Text}"); |
| 130 | var prompt = OpenAIChatPrompt.From(aiModel, new Samples.AI.Prompts.WeatherPrompt(context.ToActivityType())); |
| 131 | var result = await prompt.Send(context.Activity.Text); |
| 132 | if (!string.IsNullOrEmpty(result.Content)) |
| 133 | { |
| 134 | context.Log.Info($"[COMMAND] AI response: {result.Content}"); |
| 135 | var messageActivity = new MessageActivity { Text = result.Content }.AddAIGenerated(); |
| 136 | await context.Send(messageActivity); |
| 137 | } |
| 138 | else |
| 139 | { |
| 140 | await context.Reply("Sorry I could not figure it out"); |
| 141 | } |
| 142 | }); |
| 143 | |
| 144 | // Feedback submission handler |
| 145 | teamsApp.OnFeedback((context) => |
| 146 | { |
| 147 | context.Log.Info($"[HANDLER] Feedback submission received"); |
| 148 | FeedbackHandler.HandleFeedbackSubmission(context); |
| 149 | return Task.CompletedTask; |
| 150 | }); |
| 151 | |
| 152 | // Fallback stateful conversation handler |
| 153 | teamsApp.OnMessage(async (context) => |
| 154 | { |
| 155 | context.Log.Info($"[FALLBACK] Fallback handler invoked (no command matched): {context.Activity.Text}"); |
| 156 | await MemoryManagementHandler.HandleStatefulConversation(aiModel, context); |
| 157 | }); |
| 158 | |
| 159 | app.Run(); |