openai/openai-dotnet

Public

mirrored from https://github.com/openai/openai-dotnetAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
OpenAI_2.0.0-beta.1

Branches

Tags

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

Clone

HTTPS

Download ZIP

examples/Chat/Example03_FunctionCalling.cs

186lines · modecode

1using NUnit.Framework;
2using OpenAI.Chat;
3using System;
4using System.Collections.Generic;
5using System.Text.Json;
6
7namespace OpenAI.Examples;
8
9public partial class ChatExamples
10{
11 #region
12 private static string GetCurrentLocation()
13 {
14 // Call the location API here.
15 return "San Francisco";
16 }
17
18 private static string GetCurrentWeather(string location, string unit = "celsius")
19 {
20 // Call the weather API here.
21 return $"31 {unit}";
22 }
23 #endregion
24
25 #region
26 private static readonly ChatTool getCurrentLocationTool = ChatTool.CreateFunctionTool(
27 functionName: nameof(GetCurrentLocation),
28 functionDescription: "Get the user's current location"
29 );
30
31 private static readonly ChatTool getCurrentWeatherTool = ChatTool.CreateFunctionTool(
32 functionName: nameof(GetCurrentWeather),
33 functionDescription: "Get the current weather in a given location",
34 functionParameters: BinaryData.FromString("""
35 {
36 "type": "object",
37 "properties": {
38 "location": {
39 "type": "string",
40 "description": "The city and state, e.g. Boston, MA"
41 },
42 "unit": {
43 "type": "string",
44 "enum": [ "celsius", "fahrenheit" ],
45 "description": "The temperature unit to use. Infer this from the specified location."
46 }
47 },
48 "required": [ "location" ]
49 }
50 """)
51 );
52 #endregion
53
54 [Test]
55 public void Example03_FunctionCalling()
56 {
57 ChatClient client = new("gpt-4-turbo", Environment.GetEnvironmentVariable("OPENAI_API_KEY"));
58
59 #region
60 List<ChatMessage> messages = [
61 new UserChatMessage("What's the weather like today?"),
62 ];
63
64 ChatCompletionOptions options = new()
65 {
66 Tools = { getCurrentLocationTool, getCurrentWeatherTool },
67 };
68 #endregion
69
70 #region
71 bool requiresAction;
72
73 do
74 {
75 requiresAction = false;
76 ChatCompletion chatCompletion = client.CompleteChat(messages, options);
77
78 switch (chatCompletion.FinishReason)
79 {
80 case ChatFinishReason.Stop:
81 {
82 // Add the assistant message to the conversation history.
83 messages.Add(new AssistantChatMessage(chatCompletion));
84 break;
85 }
86
87 case ChatFinishReason.ToolCalls:
88 {
89 // First, add the assistant message with tool calls to the conversation history.
90 messages.Add(new AssistantChatMessage(chatCompletion));
91
92 // Then, add a new tool message for each tool call that is resolved.
93 foreach (ChatToolCall toolCall in chatCompletion.ToolCalls)
94 {
95 switch (toolCall.FunctionName)
96 {
97 case nameof(GetCurrentLocation):
98 {
99 string toolResult = GetCurrentLocation();
100 messages.Add(new ToolChatMessage(toolCall.Id, toolResult));
101 break;
102 }
103
104 case nameof(GetCurrentWeather):
105 {
106 // The arguments that the model wants to use to call the function are specified as a
107 // stringified JSON object based on the schema defined in the tool definition. Note that
108 // the model may hallucinate arguments too. Consequently, it is important to do the
109 // appropriate parsing and validation before calling the function.
110 using JsonDocument argumentsJson = JsonDocument.Parse(toolCall.FunctionArguments);
111 bool hasLocation = argumentsJson.RootElement.TryGetProperty("location", out JsonElement location);
112 bool hasUnit = argumentsJson.RootElement.TryGetProperty("unit", out JsonElement unit);
113
114 if (!hasLocation)
115 {
116 throw new ArgumentNullException(nameof(location), "The location argument is required.");
117 }
118
119 string toolResult = hasUnit
120 ? GetCurrentWeather(location.GetString(), unit.GetString())
121 : GetCurrentWeather(location.GetString());
122 messages.Add(new ToolChatMessage(toolCall.Id, toolResult));
123 break;
124 }
125
126 default:
127 {
128 // Handle other unexpected calls.
129 throw new NotImplementedException();
130 }
131 }
132 }
133
134 requiresAction = true;
135 break;
136 }
137
138 case ChatFinishReason.Length:
139 throw new NotImplementedException("Incomplete model output due to MaxTokens parameter or token limit exceeded.");
140
141 case ChatFinishReason.ContentFilter:
142 throw new NotImplementedException("Omitted content due to a content filter flag.");
143
144 case ChatFinishReason.FunctionCall:
145 throw new NotImplementedException("Deprecated in favor of tool calls.");
146
147 default:
148 throw new NotImplementedException(chatCompletion.FinishReason.ToString());
149 }
150 } while (requiresAction);
151 #endregion
152
153 #region
154 foreach (ChatMessage requestMessage in messages)
155 {
156 switch (requestMessage)
157 {
158 case SystemChatMessage systemMessage:
159 Console.WriteLine($"[SYSTEM]:");
160 Console.WriteLine($"{systemMessage.Content[0].Text}");
161 Console.WriteLine();
162 break;
163
164 case UserChatMessage userMessage:
165 Console.WriteLine($"[USER]:");
166 Console.WriteLine($"{userMessage.Content[0].Text}");
167 Console.WriteLine();
168 break;
169
170 case AssistantChatMessage assistantMessage when assistantMessage.Content.Count > 0:
171 Console.WriteLine($"[ASSISTANT]:");
172 Console.WriteLine($"{assistantMessage.Content[0].Text}");
173 Console.WriteLine();
174 break;
175
176 case ToolChatMessage:
177 // Do not print any tool messages; let the assistant summarize the tool results instead.
178 break;
179
180 default:
181 break;
182 }
183 }
184 #endregion
185 }
186}