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/Assistants/Example02_FunctionCalling.cs

193lines · modecode

1using NUnit.Framework;
2using OpenAI.Assistants;
3using System;
4using System.ClientModel;
5using System.Collections.Generic;
6using System.Text.Json;
7using System.Threading;
8
9namespace OpenAI.Examples;
10
11public partial class AssistantExamples
12{
13 [Test]
14 public void Example02_FunctionCalling()
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#pragma warning disable OPENAI001
65 AssistantClient client = new(Environment.GetEnvironmentVariable("OPENAI_API_KEY"));
66
67 #region
68 // Create an assistant that can call the function tools.
69 AssistantCreationOptions assistantOptions = new()
70 {
71 Name = "Example: Function Calling",
72 Instructions =
73 "Don't make assumptions about what values to plug into functions."
74 + " Ask for clarification if a user request is ambiguous.",
75 Tools = { getLocationTool, getWeatherTool },
76 };
77
78 Assistant assistant = client.CreateAssistant("gpt-4-turbo", assistantOptions);
79 #endregion
80
81 #region
82 // Create a thread with an initial user message and run it.
83 ThreadCreationOptions threadOptions = new()
84 {
85 InitialMessages = { new ThreadInitializationMessage(["What's the weather like today?"]), },
86 };
87
88 ThreadRun run = client.CreateThreadAndRun(assistant.Id, threadOptions);
89 #endregion
90
91 #region
92 // Poll the run until it is no longer queued or in progress.
93 while (!run.Status.IsTerminal)
94 {
95 Thread.Sleep(TimeSpan.FromSeconds(1));
96 run = client.GetRun(run.ThreadId, run.Id);
97
98 // If the run requires action, resolve them.
99 if (run.Status == RunStatus.RequiresAction)
100 {
101 List<ToolOutput> toolOutputs = [];
102
103 foreach (RequiredAction action in run.RequiredActions)
104 {
105 switch (action.FunctionName)
106 {
107 case GetCurrentLocationFunctionName:
108 {
109 string toolResult = GetCurrentLocation();
110 toolOutputs.Add(new ToolOutput(action.ToolCallId, toolResult));
111 break;
112 }
113
114 case GetCurrentWeatherFunctionName:
115 {
116 // The arguments that the model wants to use to call the function are specified as a
117 // stringified JSON object based on the schema defined in the tool definition. Note that
118 // the model may hallucinate arguments too. Consequently, it is important to do the
119 // appropriate parsing and validation before calling the function.
120 using JsonDocument argumentsJson = JsonDocument.Parse(action.FunctionArguments);
121 bool hasLocation = argumentsJson.RootElement.TryGetProperty("location", out JsonElement location);
122 bool hasUnit = argumentsJson.RootElement.TryGetProperty("unit", out JsonElement unit);
123
124 if (!hasLocation)
125 {
126 throw new ArgumentNullException(nameof(location), "The location argument is required.");
127 }
128
129 string toolResult = hasUnit
130 ? GetCurrentWeather(location.GetString(), unit.GetString())
131 : GetCurrentWeather(location.GetString());
132 toolOutputs.Add(new ToolOutput(action.ToolCallId, toolResult));
133 break;
134 }
135
136 default:
137 {
138 // Handle other or unexpected calls.
139 throw new NotImplementedException();
140 }
141 }
142 }
143
144 // Submit the tool outputs to the assistant, which returns the run to the queued state.
145 run = client.SubmitToolOutputsToRun(run.ThreadId, run.Id, toolOutputs);
146 }
147 }
148 #endregion
149
150 #region
151 // With the run complete, list the messages and display their content
152 if (run.Status == RunStatus.Completed)
153 {
154 PageableCollection<ThreadMessage> messages
155 = client.GetMessages(run.ThreadId, resultOrder: ListOrder.OldestFirst);
156
157 foreach (ThreadMessage message in messages)
158 {
159 Console.WriteLine($"[{message.Role.ToString().ToUpper()}]: ");
160 foreach (MessageContent contentItem in message.Content)
161 {
162 Console.WriteLine($"{contentItem.Text}");
163
164 if (contentItem.ImageFileId is not null)
165 {
166 Console.WriteLine($" <Image File ID> {contentItem.ImageFileId}");
167 }
168
169 // Include annotations, if any.
170 if (contentItem.TextAnnotations.Count > 0)
171 {
172 Console.WriteLine();
173 foreach (TextAnnotation annotation in contentItem.TextAnnotations)
174 {
175 Console.WriteLine($"* File ID used by file_search: {annotation.InputFileId}");
176 Console.WriteLine($"* file_search quote from file: {annotation.InputQuote}");
177 Console.WriteLine($"* File ID created by code_interpreter: {annotation.OutputFileId}");
178 Console.WriteLine($"* Text to replace: {annotation.TextToReplace}");
179 Console.WriteLine($"* Message content index range: {annotation.StartIndex}-{annotation.EndIndex}");
180 }
181 }
182
183 }
184 Console.WriteLine();
185 }
186 }
187 else
188 {
189 throw new NotImplementedException(run.Status.ToString());
190 }
191 #endregion
192 }
193}