microsoft/teams.net

Public

mirrored from https://github.com/microsoft/teams.netAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
df2202d1bf4bccd37efa56c709f911109c0d9ec5

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