openai/openai-dotnet

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
copilot/sub-pr-1011

Branches

Tags

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

Clone

HTTPS

Download ZIP

examples/Chat/Example03_FunctionCalling.cs

181lines · 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.FromBytes("""
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 """u8.ToArray())
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 [
62 new UserChatMessage("What's the weather like today?"),
63 ];
64
65 ChatCompletionOptions options = new()
66 {
67 Tools = { getCurrentLocationTool, getCurrentWeatherTool },
68 };
69 #endregion
70
71 #region
72 bool requiresAction;
73
74 do
75 {
76 requiresAction = false;
77 ChatCompletion completion = client.CompleteChat(messages, options);
78
79 switch (completion.FinishReason)
80 {
81 case ChatFinishReason.Stop:
82 {
83 // Add the assistant message to the conversation history.
84 messages.Add(new AssistantChatMessage(completion));
85 break;
86 }
87
88 case ChatFinishReason.ToolCalls:
89 {
90 // First, add the assistant message with tool calls to the conversation history.
91 messages.Add(new AssistantChatMessage(completion));
92
93 // Then, add a new tool message for each tool call that is resolved.
94 foreach (ChatToolCall toolCall in completion.ToolCalls)
95 {
96 switch (toolCall.FunctionName)
97 {
98 case nameof(GetCurrentLocation):
99 {
100 string toolResult = GetCurrentLocation();
101 messages.Add(new ToolChatMessage(toolCall.Id, toolResult));
102 break;
103 }
104
105 case nameof(GetCurrentWeather):
106 {
107 // The arguments that the model wants to use to call the function are specified as a
108 // stringified JSON object based on the schema defined in the tool definition. Note that
109 // the model may hallucinate arguments too. Consequently, it is important to do the
110 // appropriate parsing and validation before calling the function.
111 using JsonDocument argumentsJson = JsonDocument.Parse(toolCall.FunctionArguments);
112 bool hasLocation = argumentsJson.RootElement.TryGetProperty("location", out JsonElement location);
113 bool hasUnit = argumentsJson.RootElement.TryGetProperty("unit", out JsonElement unit);
114
115 if (!hasLocation)
116 {
117 throw new ArgumentNullException(nameof(location), "The location argument is required.");
118 }
119
120 string toolResult = hasUnit
121 ? GetCurrentWeather(location.GetString(), unit.GetString())
122 : GetCurrentWeather(location.GetString());
123 messages.Add(new ToolChatMessage(toolCall.Id, toolResult));
124 break;
125 }
126
127 default:
128 {
129 // Handle other unexpected calls.
130 throw new NotImplementedException();
131 }
132 }
133 }
134
135 requiresAction = true;
136 break;
137 }
138
139 case ChatFinishReason.Length:
140 throw new NotImplementedException("Incomplete model output due to MaxTokens parameter or token limit exceeded.");
141
142 case ChatFinishReason.ContentFilter:
143 throw new NotImplementedException("Omitted content due to a content filter flag.");
144
145 case ChatFinishReason.FunctionCall:
146 throw new NotImplementedException("Deprecated in favor of tool calls.");
147
148 default:
149 throw new NotImplementedException(completion.FinishReason.ToString());
150 }
151 } while (requiresAction);
152 #endregion
153
154 #region
155 foreach (ChatMessage message in messages)
156 {
157 switch (message)
158 {
159 case UserChatMessage userMessage:
160 Console.WriteLine($"[USER]:");
161 Console.WriteLine($"{userMessage.Content[0].Text}");
162 Console.WriteLine();
163 break;
164
165 case AssistantChatMessage assistantMessage when assistantMessage.Content.Count > 0:
166 Console.WriteLine($"[ASSISTANT]:");
167 Console.WriteLine($"{assistantMessage.Content[0].Text}");
168 Console.WriteLine();
169 break;
170
171 case ToolChatMessage:
172 // Do not print any tool messages; let the assistant summarize the tool results instead.
173 break;
174
175 default:
176 break;
177 }
178 }
179 #endregion
180 }
181}
182