openai/openai-dotnet

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
OpenAI_2.1.0-beta.1

Branches

Tags

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

Clone

HTTPS

Download ZIP

examples/Assistants/Example02_FunctionCallingAsync.cs

191lines · modecode

1using NUnit.Framework;
2using OpenAI.Assistants;
3using System;
4using System.ClientModel;
5using System.Collections.Generic;
6using System.Text.Json;
7using System.Threading.Tasks;
8
9namespace OpenAI.Examples;
10
11public partial class AssistantExamples
12{
13 [Test]
14 public async Task Example02_FunctionCallingAsync()
15 {
16 #region
17 string GetCurrentLocation()
18 {
19 // Call a location API here.
20 return "San Francisco";
21 }
22
23 const string GetCurrentLocationFunctionName = "get_current_location";
24
25 FunctionToolDefinition getLocationTool = new()
26 {
27 FunctionName = GetCurrentLocationFunctionName,
28 Description = "Get the user's current location"
29 };
30
31 string GetCurrentWeather(string location, string unit = "celsius")
32 {
33 // Call a weather API here.
34 return $"31 {unit}";
35 }
36
37 const string GetCurrentWeatherFunctionName = "get_current_weather";
38
39 FunctionToolDefinition getWeatherTool = new()
40 {
41 FunctionName = GetCurrentWeatherFunctionName,
42 Description = "Get the current weather in a given location",
43 Parameters = BinaryData.FromString("""
44 {
45 "type": "object",
46 "properties": {
47 "location": {
48 "type": "string",
49 "description": "The city and state, e.g. Boston, MA"
50 },
51 "unit": {
52 "type": "string",
53 "enum": [ "celsius", "fahrenheit" ],
54 "description": "The temperature unit to use. Infer this from the specified location."
55 }
56 },
57 "required": [ "location" ]
58 }
59 """),
60 };
61 #endregion
62
63 // Assistants is a beta API and subject to change; acknowledge its experimental status by suppressing the matching warning.
64 AssistantClient client = new(Environment.GetEnvironmentVariable("OPENAI_API_KEY"));
65
66 #region
67 // Create an assistant that can call the function tools.
68 AssistantCreationOptions assistantOptions = new()
69 {
70 Name = "Example: Function Calling",
71 Instructions =
72 "Don't make assumptions about what values to plug into functions."
73 + " Ask for clarification if a user request is ambiguous.",
74 Tools = { getLocationTool, getWeatherTool },
75 };
76
77 Assistant assistant = await client.CreateAssistantAsync("gpt-4-turbo", assistantOptions);
78 #endregion
79
80 #region
81 // Create a thread with an initial user message and run it.
82 ThreadCreationOptions threadOptions = new()
83 {
84 InitialMessages = { "What's the weather like today?" }
85 };
86
87 ThreadRun run = await client.CreateThreadAndRunAsync(assistant.Id, threadOptions);
88 #endregion
89
90 #region
91 // Poll the run until it is no longer queued or in progress.
92 while (!run.Status.IsTerminal)
93 {
94 await Task.Delay(TimeSpan.FromSeconds(1));
95 run = await client.GetRunAsync(run.ThreadId, run.Id);
96
97 // If the run requires action, resolve them.
98 if (run.Status == RunStatus.RequiresAction)
99 {
100 List<ToolOutput> toolOutputs = [];
101
102 foreach (RequiredAction action in run.RequiredActions)
103 {
104 switch (action.FunctionName)
105 {
106 case GetCurrentLocationFunctionName:
107 {
108 string toolResult = GetCurrentLocation();
109 toolOutputs.Add(new ToolOutput(action.ToolCallId, toolResult));
110 break;
111 }
112
113 case GetCurrentWeatherFunctionName:
114 {
115 // The arguments that the model wants to use to call the function are specified as a
116 // stringified JSON object based on the schema defined in the tool definition. Note that
117 // the model may hallucinate arguments too. Consequently, it is important to do the
118 // appropriate parsing and validation before calling the function.
119 using JsonDocument argumentsJson = JsonDocument.Parse(action.FunctionArguments);
120 bool hasLocation = argumentsJson.RootElement.TryGetProperty("location", out JsonElement location);
121 bool hasUnit = argumentsJson.RootElement.TryGetProperty("unit", out JsonElement unit);
122
123 if (!hasLocation)
124 {
125 throw new ArgumentNullException(nameof(location), "The location argument is required.");
126 }
127
128 string toolResult = hasUnit
129 ? GetCurrentWeather(location.GetString(), unit.GetString())
130 : GetCurrentWeather(location.GetString());
131 toolOutputs.Add(new ToolOutput(action.ToolCallId, toolResult));
132 break;
133 }
134
135 default:
136 {
137 // Handle other or unexpected calls.
138 throw new NotImplementedException();
139 }
140 }
141 }
142
143 // Submit the tool outputs to the assistant, which returns the run to the queued state.
144 run = await client.SubmitToolOutputsToRunAsync(run.ThreadId, run.Id, toolOutputs);
145 }
146 }
147 #endregion
148
149 #region
150 // With the run complete, list the messages and display their content
151 if (run.Status == RunStatus.Completed)
152 {
153 AsyncCollectionResult<ThreadMessage> messages
154 = client.GetMessagesAsync(run.ThreadId, new MessageCollectionOptions() { Order = MessageCollectionOrder.Ascending });
155
156 await foreach (ThreadMessage message in messages)
157 {
158 Console.WriteLine($"[{message.Role.ToString().ToUpper()}]: ");
159 foreach (MessageContent contentItem in message.Content)
160 {
161 Console.WriteLine($"{contentItem.Text}");
162
163 if (contentItem.ImageFileId is not null)
164 {
165 Console.WriteLine($" <Image File ID> {contentItem.ImageFileId}");
166 }
167
168 // Include annotations, if any.
169 if (contentItem.TextAnnotations.Count > 0)
170 {
171 Console.WriteLine();
172 foreach (TextAnnotation annotation in contentItem.TextAnnotations)
173 {
174 Console.WriteLine($"* File ID used by file_search: {annotation.InputFileId}");
175 Console.WriteLine($"* File ID created by code_interpreter: {annotation.OutputFileId}");
176 Console.WriteLine($"* Text to replace: {annotation.TextToReplace}");
177 Console.WriteLine($"* Message content index range: {annotation.StartIndex}-{annotation.EndIndex}");
178 }
179 }
180
181 }
182 Console.WriteLine();
183 }
184 }
185 else
186 {
187 throw new NotImplementedException(run.Status.ToString());
188 }
189 #endregion
190 }
191}
192