microsoft/teams.net

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v2.0.0-preview.6

Branches

Tags

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

Clone

HTTPS

Download ZIP

Libraries/Microsoft.Teams.AI.Models.OpenAI/OpenAIChatModel.Send.cs

173lines · modecode

1using System.Text;
2using System.Text.Json;
3
4using Microsoft.Teams.AI.Messages;
5using Microsoft.Teams.AI.Models.OpenAI.Builders;
6
7using OpenAI.Chat;
8
9namespace Microsoft.Teams.AI.Models.OpenAI;
10
11public partial class OpenAIChatModel
12{
13 public async Task<IMessage> Send(IMessage message, ChatCompletionOptions? options = null, CancellationToken cancellationToken = default)
14 {
15 var res = await Send(message, new ChatModelOptions<ChatCompletionOptions>()
16 {
17 Functions = [],
18 Messages = []
19 }, cancellationToken);
20
21 return res;
22 }
23
24 public async Task<ModelMessage<string>> Send(IMessage message, ChatModelOptions<ChatCompletionOptions> options, CancellationToken cancellationToken = default)
25 {
26 var messages = await CallFunctions(message, options, cancellationToken);
27 var chatMessages = messages.Select(m => m.ToOpenAI()).ToList();
28
29 if (options.Prompt is not null)
30 {
31 chatMessages.Insert(0, options.Prompt.ToOpenAI());
32 }
33
34 var tools = options.Functions.Select(function => function.ToOpenAI()).ToArray();
35
36 try
37 {
38 var requestOptions = options.Options ?? new ChatCompletionOptions();
39
40 foreach (var tool in tools)
41 {
42 requestOptions.Tools.Add(tool);
43 }
44
45 var result = await ChatClient.CompleteChatAsync(
46 chatMessages,
47 requestOptions,
48 CancellationToken.None
49 );
50
51 var modelMessage = ChatMessage.CreateAssistantMessage(result.Value).ToTeams();
52
53 if (modelMessage.HasFunctionCalls)
54 {
55 return await Send(modelMessage, options, cancellationToken);
56 }
57
58 messages.Add(modelMessage);
59 return modelMessage;
60 }
61 catch (Exception ex)
62 {
63 Logger.Error(ex.ToString());
64 throw new Exception("chat completion error", ex);
65 }
66 }
67
68 public async Task<ModelMessage<string>> Send(IMessage message, ChatModelOptions<ChatCompletionOptions> options, IStream stream, CancellationToken cancellationToken = default)
69 {
70 var messages = await CallFunctions(message, options, cancellationToken);
71 var chatMessages = messages.Select(m => m.ToOpenAI()).ToList();
72
73 if (options.Prompt is not null)
74 {
75 chatMessages.Insert(0, options.Prompt.ToOpenAI());
76 }
77
78 var tools = options.Functions.Select(function => function.ToOpenAI()).ToArray();
79
80 try
81 {
82 var requestOptions = options.Options ?? new ChatCompletionOptions();
83
84 foreach (var tool in tools)
85 {
86 requestOptions.Tools.Add(tool);
87 }
88
89 var res = ChatClient.CompleteChatStreamingAsync(chatMessages, requestOptions, CancellationToken.None);
90 var content = new StringBuilder();
91 var toolCalls = new StreamingChatToolCallsBuilder();
92
93 await foreach (var chunk in res)
94 {
95 var delta = new StringBuilder();
96
97 foreach (var update in chunk.ContentUpdate)
98 {
99 delta.Append(update.Text);
100 }
101
102 foreach (var update in chunk.ToolCallUpdates)
103 {
104 toolCalls.Append(update);
105 }
106
107 content.Append(delta);
108 stream.Emit(delta.ToString());
109
110 if (chunk.FinishReason == ChatFinishReason.ToolCalls)
111 {
112 var input = ChatMessage.CreateAssistantMessage(toolCalls.Build()).ToTeams();
113 return await Send(input, options, stream, cancellationToken);
114 }
115 else if (chunk.FinishReason == ChatFinishReason.Length)
116 {
117 throw new NotImplementedException("Incomplete model output due to MaxTokens parameter or token limit exceeded.");
118 }
119 else if (chunk.FinishReason == ChatFinishReason.ContentFilter)
120 {
121 throw new NotImplementedException("Omitted content due to a content filter flag.");
122 }
123 }
124
125 var modelMessage = ChatMessage.CreateAssistantMessage(content.ToString()).ToTeams();
126 messages.Add(modelMessage);
127 return modelMessage;
128 }
129 catch (Exception ex)
130 {
131 Logger.Error(ex.ToString());
132 throw new Exception("chat completion error", ex);
133 }
134 }
135
136 protected async Task<IList<IMessage>> CallFunctions(IMessage message, ChatModelOptions<ChatCompletionOptions> options, CancellationToken cancellationToken = default)
137 {
138 var messages = options.Messages;
139 messages.Add(message);
140
141 if (message is ModelMessage<string> modelMessage && modelMessage.HasFunctionCalls)
142 {
143 foreach (var call in modelMessage.FunctionCalls ?? [])
144 {
145 var logger = Logger.Child($"Tools.{call.Name}");
146 logger.Debug(call.Arguments);
147 string? content;
148
149 try
150 {
151 var args = call.Parse() ?? new Dictionary<string, object?>();
152 var res = await options.Invoke(call, cancellationToken);
153
154 content = res is string asString ? asString : JsonSerializer.Serialize(res);
155 logger.Debug(content);
156 }
157 catch (Exception ex)
158 {
159 logger.Error(ex.ToString());
160 content = ex.Message;
161 }
162
163 messages.Add(new FunctionMessage()
164 {
165 FunctionId = call.Id,
166 Content = content
167 });
168 }
169 }
170
171 return messages;
172 }
173}