microsoft/teams.net

Public

mirrored fromhttps://github.com/microsoft/teams.netAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
87729f780a04a94c40e3816a99f39edf8c0379b7

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

Samples/Samples.AI/Handlers/FeedbackHandler.cs

167lines · modecode

1using System.Collections.Concurrent;
2
3using Microsoft.Teams.AI.Models.OpenAI;
4using Microsoft.Teams.AI.Prompts;
5using Microsoft.Teams.AI.Templates;
6using Microsoft.Teams.Api.Activities.Invokes;
7using Microsoft.Teams.Apps;
8
9namespace Samples.AI.Handlers;
10
11/// <summary>
12/// Handles feedback loop functionality - sending AI messages with feedback buttons
13/// and processing feedback submissions
14/// </summary>
15public static class FeedbackHandler
16{
17 /// <summary>
18 /// Storage for feedback data. In production, this would be persisted in a database.
19 /// </summary>
20 public static readonly ConcurrentDictionary<string, FeedbackData> StoredFeedbackByMessageId = new();
21
22 /// <summary>
23 /// Handles the feedback loop command - sends an AI response with feedback buttons
24 /// </summary>
25 public static async Task HandleFeedbackLoop(OpenAIChatModel model, IContext<Microsoft.Teams.Api.Activities.MessageActivity> context, CancellationToken cancellationToken = default)
26 {
27 context.Log.Info($"[HANDLER] Feedback loop handler invoked with query: {context.Activity.Text}");
28
29 var prompt = new OpenAIChatPrompt(model, new ChatPromptOptions
30 {
31 Instructions = new StringTemplate("You are a helpful assistant.")
32 });
33
34 context.Log.Info("[HANDLER] Sending query to AI model...");
35 var result = await prompt.Send(context.Activity.Text, cancellationToken);
36
37 if (result.Content != null)
38 {
39 context.Log.Info($"[HANDLER] AI response received: {result.Content}");
40
41 // Create message with AI generated indicator and feedback buttons
42 var messageActivity = new Microsoft.Teams.Api.Activities.MessageActivity
43 {
44 Text = result.Content,
45 }
46 .AddAIGenerated()
47 .AddFeedback(); // This adds the thumbs up/down buttons
48
49 context.Log.Info("[HANDLER] Sending message with feedback buttons");
50 var sentActivity = await context.Send(messageActivity, cancellationToken);
51
52 // Store the feedback data for later retrieval
53 if (sentActivity.Id != null)
54 {
55 StoredFeedbackByMessageId[sentActivity.Id] = new FeedbackData
56 {
57 IncomingMessage = context.Activity.Text ?? string.Empty,
58 OutgoingMessage = result.Content,
59 Likes = 0,
60 Dislikes = 0,
61 Feedbacks = new List<string>()
62 };
63 context.Log.Info($"[HANDLER] Stored feedback data for message ID: {sentActivity.Id}");
64 }
65 }
66 else
67 {
68 context.Log.Warn("[HANDLER] AI did not generate a response");
69 await context.Reply("I did not generate a response.", cancellationToken);
70 }
71 }
72
73 /// <summary>
74 /// Handles feedback submissions from users
75 /// </summary>
76 public static void HandleFeedbackSubmission(IContext<Messages.SubmitActionActivity> context)
77 {
78 context.Log.Info($"[HANDLER] Feedback submission received for activity ID: {context.Activity.Id}");
79
80 if (context.Activity.Value?.ActionValue == null)
81 {
82 context.Log.Warn("[HANDLER] No action value found in feedback submission");
83 return;
84 }
85
86 context.Log.Info($"[HANDLER] Raw ActionValue: {System.Text.Json.JsonSerializer.Serialize(context.Activity.Value.ActionValue)}");
87
88 // Deserialize ActionValue to a dictionary
89 var actionValueJson = System.Text.Json.JsonSerializer.Serialize(context.Activity.Value.ActionValue);
90 var actionValue = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, string>>(actionValueJson);
91
92 if (actionValue == null)
93 {
94 context.Log.Warn("[HANDLER] Could not parse action value");
95 return;
96 }
97
98 // Extract reaction (like/dislike) and feedback JSON string
99 var reaction = actionValue.ContainsKey("reaction") ? actionValue["reaction"] : null;
100 var feedbackJsonString = actionValue.ContainsKey("feedback") ? actionValue["feedback"] : null;
101
102 // Parse the feedback JSON string to extract feedbackText
103 string? feedbackText = null;
104 if (!string.IsNullOrEmpty(feedbackJsonString))
105 {
106 try
107 {
108 var feedbackObj = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, string>>(feedbackJsonString);
109 feedbackText = feedbackObj?.ContainsKey("feedbackText") == true ? feedbackObj["feedbackText"] : null;
110 }
111 catch (System.Text.Json.JsonException ex)
112 {
113 context.Log.Warn($"[HANDLER] Failed to parse feedback JSON: {ex.Message}");
114 }
115 }
116
117 context.Log.Info($"[HANDLER] Reaction: {reaction}, Feedback Text: {feedbackText}");
118
119 if (context.Activity.ReplyToId == null)
120 {
121 context.Log.Warn($"[HANDLER] No replyToId found for message ID {context.Activity.Id}");
122 return;
123 }
124
125 // Update stored feedback
126 if (StoredFeedbackByMessageId.TryGetValue(context.Activity.ReplyToId, out var existingFeedback))
127 {
128 if (reaction == "like")
129 {
130 existingFeedback.Likes++;
131 context.Log.Info($"[HANDLER] Incremented likes to {existingFeedback.Likes}");
132 }
133 else if (reaction == "dislike")
134 {
135 existingFeedback.Dislikes++;
136 context.Log.Info($"[HANDLER] Incremented dislikes to {existingFeedback.Dislikes}");
137 }
138
139 if (feedbackText != null)
140 {
141 existingFeedback.Feedbacks.Add(feedbackText);
142 context.Log.Info($"[HANDLER] Added feedback text: '{feedbackText}'. Total feedbacks: {existingFeedback.Feedbacks.Count}");
143 }
144
145 // Log feedback summary
146 context.Log.Info($"[HANDLER] Feedback summary for message {context.Activity.ReplyToId}: " +
147 $"Likes={existingFeedback.Likes}, Dislikes={existingFeedback.Dislikes}, " +
148 $"Feedbacks={existingFeedback.Feedbacks.Count}");
149 }
150 else
151 {
152 context.Log.Warn($"[HANDLER] No feedback data found for message ID {context.Activity.ReplyToId}");
153 }
154 }
155}
156
157/// <summary>
158/// Data structure for storing feedback information
159/// </summary>
160public class FeedbackData
161{
162 public string IncomingMessage { get; set; } = string.Empty;
163 public string OutgoingMessage { get; set; } = string.Empty;
164 public int Likes { get; set; }
165 public int Dislikes { get; set; }
166 public List<string> Feedbacks { get; set; } = new();
167}