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 · 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.
64AssistantClient client = new(Environment.GetEnvironmentVariable("OPENAI_API_KEY"));
65
66#region
67// Create an assistant that can call the function tools.
68AssistantCreationOptions assistantOptions = new()
69{
8cc6643fJose Arriaga Maldonado2 years ago70Name = "Example: Function Calling",
9f9f2936Jose Arriaga Maldonado2 years ago71Instructions =
72"Don't make assumptions about what values to plug into functions."
73+ " Ask for clarification if a user request is ambiguous.",
74Tools = { getLocationTool, getWeatherTool },
75};
76
77Assistant 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.
82ThreadCreationOptions threadOptions = new()
83{
1c40de67Krzysztof Cwalina2 years ago84InitialMessages = { "What's the weather like today?" }
9f9f2936Jose Arriaga Maldonado2 years ago85};
86
87ThreadRun 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.
92while (!run.Status.IsTerminal)
93{
94await Task.Delay(TimeSpan.FromSeconds(1));
95run = await client.GetRunAsync(run.ThreadId, run.Id);
96
97// If the run requires action, resolve them.
98if (run.Status == RunStatus.RequiresAction)
99{
100List<ToolOutput> toolOutputs = [];
101
102foreach (RequiredAction action in run.RequiredActions)
103{
104switch (action.FunctionName)
105{
106case GetCurrentLocationFunctionName:
107{
108string toolResult = GetCurrentLocation();
109toolOutputs.Add(new ToolOutput(action.ToolCallId, toolResult));
110break;
111}
112
113case 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.
119using JsonDocument argumentsJson = JsonDocument.Parse(action.FunctionArguments);
120bool hasLocation = argumentsJson.RootElement.TryGetProperty("location", out JsonElement location);
121bool hasUnit = argumentsJson.RootElement.TryGetProperty("unit", out JsonElement unit);
122
123if (!hasLocation)
124{
125throw new ArgumentNullException(nameof(location), "The location argument is required.");
126}
127
128string toolResult = hasUnit
129? GetCurrentWeather(location.GetString(), unit.GetString())
130: GetCurrentWeather(location.GetString());
131toolOutputs.Add(new ToolOutput(action.ToolCallId, toolResult));
132break;
133}
134
135default:
136{
137// Handle other or unexpected calls.
138throw new NotImplementedException();
139}
140}
141}
142
143// Submit the tool outputs to the assistant, which returns the run to the queued state.
144run = 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
151if (run.Status == RunStatus.Completed)
152{
2ab1a942Jose Arriaga Maldonado1 years ago153AsyncCollectionResult<ThreadMessage> messages
154= client.GetMessagesAsync(run.ThreadId, new MessageCollectionOptions() { Order = MessageCollectionOrder.Ascending });
155
9f9f2936Jose Arriaga Maldonado2 years ago156await foreach (ThreadMessage message in messages)
157{
158Console.WriteLine($"[{message.Role.ToString().ToUpper()}]: ");
159foreach (MessageContent contentItem in message.Content)
160{
161Console.WriteLine($"{contentItem.Text}");
162
163if (contentItem.ImageFileId is not null)
164{
165Console.WriteLine($" <Image File ID> {contentItem.ImageFileId}");
166}
167
168// Include annotations, if any.
169if (contentItem.TextAnnotations.Count > 0)
170{
171Console.WriteLine();
172foreach (TextAnnotation annotation in contentItem.TextAnnotations)
173{
174Console.WriteLine($"* File ID used by file_search: {annotation.InputFileId}");
175Console.WriteLine($"* File ID created by code_interpreter: {annotation.OutputFileId}");
176Console.WriteLine($"* Text to replace: {annotation.TextToReplace}");
177Console.WriteLine($"* Message content index range: {annotation.StartIndex}-{annotation.EndIndex}");
178}
179}
180
181}
182Console.WriteLine();
183}
184}
185else
186{
187throw new NotImplementedException(run.Status.ToString());
188}
189#endregion
190}
191}