microsoft/teams.net

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
copilot/close-pull-request

Branches

Tags

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

Clone

HTTPS

Download ZIP

Libraries/Microsoft.Teams.AI/Prompts/ChatPrompt/ChatPrompt.cs

235lines · modecode

1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT License.
3
4using System.Reflection;
5
6using Microsoft.Teams.AI.Annotations;
7using Microsoft.Teams.AI.Messages;
8using Microsoft.Teams.AI.Models;
9using Microsoft.Teams.Common.Extensions;
10using Microsoft.Teams.Common.Logging;
11
12namespace Microsoft.Teams.AI.Prompts;
13
14/// <summary>
15/// a prompt that can send/receive text
16/// messages and expose chat model specific
17/// features like streaming/functions
18/// </summary>
19public interface IChatPrompt : IPrompt
20{
21 /// <summary>
22 /// the message history
23 /// </summary>
24 public IList<IMessage> Messages { get; }
25
26 /// <summary>
27 /// the collection of registered functions
28 /// </summary>
29 public FunctionCollection Functions { get; }
30}
31
32/// <summary>
33/// a prompt that can send/receive text
34/// messages and expose chat model specific
35/// features like streaming/functions
36/// </summary>
37public interface IChatPrompt<TOptions> : IChatPrompt
38{
39 /// <summary>
40 /// register an error handler
41 /// </summary>
42 public IChatPrompt<TOptions> OnError(Action<Exception> onError);
43
44 /// <summary>
45 /// register an error handler
46 /// </summary>
47 public IChatPrompt<TOptions> OnError(Func<Exception, Task> onError);
48
49 /// <summary>
50 /// send a message via the prompt using string content
51 /// </summary>
52 /// <param name="text">the message text</param>
53 /// <param name="options">the request options</param>
54 /// <param name="onChunk">
55 /// the stream chunk handler (if notnull streaming is enabled)
56 /// </param>
57 /// <returns>the models response</returns>
58 public Task<ModelMessage<string>> Send(string text, RequestOptions? options = null, OnStreamChunk? onChunk = null, CancellationToken cancellationToken = default);
59
60 /// <summary>
61 /// send a message via the prompt using content blocks
62 /// </summary>
63 /// <param name="content">the message content</param>
64 /// <param name="options">the request options</param>
65 /// <param name="onChunk">
66 /// the stream chunk handler (if notnull streaming is enabled)
67 /// </param>
68 /// <returns>the models response</returns>
69 public Task<ModelMessage<string>> Send(IContent[] content, RequestOptions? options = null, OnStreamChunk? onChunk = null, CancellationToken cancellationToken = default);
70
71 /// <summary>
72 /// send a message via the prompt
73 /// </summary>
74 /// <param name="message">the message to send</param>
75 /// <param name="options">the request options</param>
76 /// <param name="onChunk">
77 /// the stream chunk handler (if notnull streaming is enabled)
78 /// </param>
79 /// <returns>the models response</returns>
80 public Task<ModelMessage<string>> Send(UserMessage<string> message, RequestOptions? options = null, OnStreamChunk? onChunk = null, CancellationToken cancellationToken = default);
81
82 /// <summary>
83 /// send a message via the prompt
84 /// </summary>
85 /// <param name="message">the message to send</param>
86 /// <param name="options">the request options</param>
87 /// <param name="onChunk">
88 /// the stream chunk handler (if notnull streaming is enabled)
89 /// </param>
90 /// <returns>the models response</returns>
91 public Task<ModelMessage<string>> Send(UserMessage<IEnumerable<IContent>> message, RequestOptions? options = null, OnStreamChunk? onChunk = null, CancellationToken cancellationToken = default);
92
93 /// <summary>
94 /// options to send when invoking a prompt
95 /// </summary>
96 public class RequestOptions
97 {
98 /// <summary>
99 /// the conversation history
100 /// </summary>
101 public IList<IMessage>? Messages { get; set; }
102
103 /// <summary>
104 /// the model request options
105 /// </summary>
106 public TOptions? Request { get; set; }
107 }
108}
109
110/// <summary>
111/// a prompt that can send/receive text
112/// messages and expose chat model specific
113/// features like streaming/functions
114/// </summary>
115public partial class ChatPrompt<TOptions> : IChatPrompt<TOptions>
116{
117 public string Name { get; private set; }
118 public string Description { get; private set; }
119 public IList<IMessage> Messages { get; private set; }
120 public FunctionCollection Functions { get; private set; }
121
122 protected IChatModel<TOptions> Model { get; }
123 protected ITemplate? Template { get; }
124 protected ILogger Logger { get; }
125 protected IList<IChatPlugin> Plugins { get; }
126 protected event EventHandler<Exception> ErrorEvent;
127
128 public ChatPrompt(IChatModel<TOptions> model, ChatPromptOptions? options = null)
129 {
130 options ??= new();
131 Name = options.Name ?? "Chat";
132 Description = options.Description ?? "an agent you can chat with";
133 Model = model;
134 Template = options.Instructions;
135 Messages = options.Messages ?? [];
136 Functions = new();
137 Logger = (options.Logger ?? new ConsoleLogger()).Child($"AI.{Name}");
138 Plugins = [];
139 ErrorEvent = (_, ex) => Logger.Error(ex);
140 }
141
142 public ChatPrompt(ChatPrompt<TOptions> prompt)
143 {
144 Name = prompt.Name;
145 Description = prompt.Description;
146 Messages = prompt.Messages;
147 Functions = prompt.Functions;
148 Model = prompt.Model;
149 Template = prompt.Template;
150 Logger = prompt.Logger;
151 Plugins = prompt.Plugins;
152 ErrorEvent = prompt.ErrorEvent;
153 }
154
155 public ChatPrompt(string name, ChatPrompt<TOptions> prompt)
156 {
157 Name = name;
158 Description = prompt.Description;
159 Messages = prompt.Messages;
160 Functions = prompt.Functions;
161 Model = prompt.Model;
162 Template = prompt.Template;
163 Logger = prompt.Logger.Peer(name);
164 Plugins = prompt.Plugins;
165 ErrorEvent = prompt.ErrorEvent;
166 }
167
168 /// <summary>
169 /// create a ChatPrompt from any class
170 /// utilizing the ChatPromptAttribute
171 /// </summary>
172 /// <param name="model">the model to use</param>
173 /// <param name="value">the class instance to use</param>
174 /// <returns>a ChatPrompt</returns>
175 public static ChatPrompt<TOptions> From<T>(IChatModel<TOptions> model, T value, ChatPromptOptions? options = null) where T : class
176 {
177 var type = value.GetType();
178 var promptAttribute = type.GetCustomAttribute<PromptAttribute>();
179 var nameAttribute = type.GetCustomAttribute<Prompt.NameAttribute>();
180 var descriptionAttribute = type.GetCustomAttribute<Prompt.DescriptionAttribute>();
181 var instructionsAttribute = type.GetCustomAttribute<Prompt.InstructionsAttribute>();
182
183 if (promptAttribute is null)
184 {
185 throw new Exception("only types utilizing the ChatPromptAttribute can be turned into a ChatPrompt");
186 }
187
188 var name = promptAttribute.Name ?? nameAttribute?.Name ?? type.Name;
189 var description = promptAttribute.Description ?? descriptionAttribute?.Description;
190 var instructions = promptAttribute.Instructions ?? instructionsAttribute?.Instructions;
191 options ??= new();
192 options.WithName(name);
193
194 if (description is not null)
195 {
196 options = options.WithDescription(description);
197 }
198
199 if (instructions is not null)
200 {
201 options = options.WithInstructions(instructions);
202 }
203
204 var prompt = new ChatPrompt<TOptions>(model, options);
205
206 foreach (var method in type.GetMethods())
207 {
208 var functionAttribute = method.GetCustomAttribute<FunctionAttribute>();
209 var functionDescriptionAttribute = method.GetCustomAttribute<Annotations.Function.DescriptionAttribute>();
210
211 if (functionAttribute is null) continue;
212
213 var function = new Function(
214 functionAttribute.Name ?? method.Name,
215 functionAttribute.Description ?? functionDescriptionAttribute?.Description,
216 method.CreateDelegate(value)
217 );
218
219 prompt.Function(function);
220 }
221
222 foreach (var fields in type.GetFields())
223 {
224 var chatPluginAttribute = fields.GetCustomAttribute<ChatPluginAttribute>();
225 if (chatPluginAttribute is null) continue;
226 var plugin = fields.GetValue(value);
227 if (plugin is IChatPlugin chatPlugin)
228 {
229 prompt.Plugin(chatPlugin);
230 }
231 }
232
233 return prompt;
234 }
235}