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_FunctionCallingAsync.cs

193lines · modeblame

9f9f2936Jose Arriaga Maldonado2 years ago1using NUnit.Framework;
2using OpenAI.Assistants;
3using System;
4using System.ClientModel;
5using System.Collections.Generic;
6using System.Text.Json;
7using System.Threading.Tasks;
8
8cc6643fJose Arriaga Maldonado2 years ago9namespace OpenAI.Examples;
9f9f2936Jose Arriaga Maldonado2 years ago10
8cc6643fJose Arriaga Maldonado2 years ago11public partial class AssistantExamples
9f9f2936Jose Arriaga Maldonado2 years ago12{
13[Test]
8cc6643fJose Arriaga Maldonado2 years ago14public async Task Example02_FunctionCallingAsync()
9f9f2936Jose Arriaga Maldonado2 years ago15{
16#region
17string GetCurrentLocation()
18{
19// Call a location API here.
20return "San Francisco";
21}
22
23const string GetCurrentLocationFunctionName = "get_current_location";
24
25FunctionToolDefinition getLocationTool = new()
26{
27FunctionName = GetCurrentLocationFunctionName,
28Description = "Get the user's current location"
29};
30
31string GetCurrentWeather(string location, string unit = "celsius")
32{
33// Call a weather API here.
34return $"31 {unit}";
35}
36
37const string GetCurrentWeatherFunctionName = "get_current_weather";
38
39FunctionToolDefinition getWeatherTool = new()
40{
41FunctionName = GetCurrentWeatherFunctionName,
42Description = "Get the current weather in a given location",
43Parameters = 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
65AssistantClient client = new(Environment.GetEnvironmentVariable("OPENAI_API_KEY"));
66
67#region
68// Create an assistant that can call the function tools.
69AssistantCreationOptions assistantOptions = new()
70{
8cc6643fJose Arriaga Maldonado2 years ago71Name = "Example: Function Calling",
9f9f2936Jose Arriaga Maldonado2 years ago72Instructions =
73"Don't make assumptions about what values to plug into functions."
74+ " Ask for clarification if a user request is ambiguous.",
75Tools = { getLocationTool, getWeatherTool },
76};
77
78Assistant assistant = await client.CreateAssistantAsync("gpt-4-turbo", assistantOptions);
79#endregion
80
81#region
82// Create a thread with an initial user message and run it.
83ThreadCreationOptions threadOptions = new()
84{
85InitialMessages = { new ThreadInitializationMessage(["What's the weather like today?"]), },
86};
87
88ThreadRun run = await client.CreateThreadAndRunAsync(assistant.Id, threadOptions);
89#endregion
90
91#region
92// Poll the run until it is no longer queued or in progress.
93while (!run.Status.IsTerminal)
94{
95await Task.Delay(TimeSpan.FromSeconds(1));
96run = await client.GetRunAsync(run.ThreadId, run.Id);
97
98// If the run requires action, resolve them.
99if (run.Status == RunStatus.RequiresAction)
100{
101List<ToolOutput> toolOutputs = [];
102
103foreach (RequiredAction action in run.RequiredActions)
104{
105switch (action.FunctionName)
106{
107case GetCurrentLocationFunctionName:
108{
109string toolResult = GetCurrentLocation();
110toolOutputs.Add(new ToolOutput(action.ToolCallId, toolResult));
111break;
112}
113
114case 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.
120using JsonDocument argumentsJson = JsonDocument.Parse(action.FunctionArguments);
121bool hasLocation = argumentsJson.RootElement.TryGetProperty("location", out JsonElement location);
122bool hasUnit = argumentsJson.RootElement.TryGetProperty("unit", out JsonElement unit);
123
124if (!hasLocation)
125{
126throw new ArgumentNullException(nameof(location), "The location argument is required.");
127}
128
129string toolResult = hasUnit
130? GetCurrentWeather(location.GetString(), unit.GetString())
131: GetCurrentWeather(location.GetString());
132toolOutputs.Add(new ToolOutput(action.ToolCallId, toolResult));
133break;
134}
135
136default:
137{
138// Handle other or unexpected calls.
139throw new NotImplementedException();
140}
141}
142}
143
144// Submit the tool outputs to the assistant, which returns the run to the queued state.
145run = await client.SubmitToolOutputsToRunAsync(run.ThreadId, run.Id, toolOutputs);
146}
147}
148#endregion
149
150#region
151// With the run complete, list the messages and display their content
152if (run.Status == RunStatus.Completed)
153{
154AsyncPageableCollection<ThreadMessage> messages
155= client.GetMessagesAsync(run.ThreadId, resultOrder: ListOrder.OldestFirst);
156
157await foreach (ThreadMessage message in messages)
158{
159Console.WriteLine($"[{message.Role.ToString().ToUpper()}]: ");
160foreach (MessageContent contentItem in message.Content)
161{
162Console.WriteLine($"{contentItem.Text}");
163
164if (contentItem.ImageFileId is not null)
165{
166Console.WriteLine($" <Image File ID> {contentItem.ImageFileId}");
167}
168
169// Include annotations, if any.
170if (contentItem.TextAnnotations.Count > 0)
171{
172Console.WriteLine();
173foreach (TextAnnotation annotation in contentItem.TextAnnotations)
174{
175Console.WriteLine($"* File ID used by file_search: {annotation.InputFileId}");
176Console.WriteLine($"* file_search quote from file: {annotation.InputQuote}");
177Console.WriteLine($"* File ID created by code_interpreter: {annotation.OutputFileId}");
178Console.WriteLine($"* Text to replace: {annotation.TextToReplace}");
179Console.WriteLine($"* Message content index range: {annotation.StartIndex}-{annotation.EndIndex}");
180}
181}
182
183}
184Console.WriteLine();
185}
186}
187else
188{
189throw new NotImplementedException(run.Status.ToString());
190}
191#endregion
192}
193}