microsoft/teams.net

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
8e11e5e231765b6fbeae91b4033a354e2340d0af

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

176lines · modecode

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