openai/openai-dotnet

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
OpenAI_2.10.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

examples/Assistants/Example02_FunctionCalling.cs

195lines · 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;
8
8cc6643fJose Arriaga Maldonado2 years ago9namespace OpenAI.Examples;
9f9f2936Jose Arriaga Maldonado2 years ago10
5dce104aJose Arriaga Maldonado1 years ago11// This example uses experimental APIs which are subject to change. To use experimental APIs,
12// please acknowledge their experimental status by suppressing the corresponding warning.
13#pragma warning disable OPENAI001
14
8cc6643fJose Arriaga Maldonado2 years ago15public partial class AssistantExamples
9f9f2936Jose Arriaga Maldonado2 years ago16{
17[Test]
8cc6643fJose Arriaga Maldonado2 years ago18public void Example02_FunctionCalling()
9f9f2936Jose Arriaga Maldonado2 years ago19{
20#region
21string GetCurrentLocation()
22{
23// Call a location API here.
24return "San Francisco";
25}
26
27const string GetCurrentLocationFunctionName = "get_current_location";
28
86407c80Jose Arriaga Maldonado1 years ago29FunctionToolDefinition getLocationTool = new(GetCurrentLocationFunctionName)
9f9f2936Jose Arriaga Maldonado2 years ago30{
31Description = "Get the user's current location"
32};
33
34string GetCurrentWeather(string location, string unit = "celsius")
35{
36// Call a weather API here.
37return $"31 {unit}";
38}
39
40const string GetCurrentWeatherFunctionName = "get_current_weather";
41
86407c80Jose Arriaga Maldonado1 years ago42FunctionToolDefinition getWeatherTool = new(GetCurrentWeatherFunctionName)
9f9f2936Jose Arriaga Maldonado2 years ago43{
44Description = "Get the current weather in a given location",
45Parameters = BinaryData.FromString("""
46{
47"type": "object",
48"properties": {
49"location": {
50"type": "string",
51"description": "The city and state, e.g. Boston, MA"
52},
53"unit": {
54"type": "string",
55"enum": [ "celsius", "fahrenheit" ],
56"description": "The temperature unit to use. Infer this from the specified location."
57}
58},
59"required": [ "location" ]
60}
61"""),
62};
63#endregion
64
65// Assistants is a beta API and subject to change; acknowledge its experimental status by suppressing the matching warning.
66AssistantClient client = new(Environment.GetEnvironmentVariable("OPENAI_API_KEY"));
67
68#region
69// Create an assistant that can call the function tools.
70AssistantCreationOptions assistantOptions = new()
71{
8cc6643fJose Arriaga Maldonado2 years ago72Name = "Example: Function Calling",
9f9f2936Jose Arriaga Maldonado2 years ago73Instructions =
74"Don't make assumptions about what values to plug into functions."
75+ " Ask for clarification if a user request is ambiguous.",
76Tools = { getLocationTool, getWeatherTool },
77};
78
79Assistant assistant = client.CreateAssistant("gpt-4-turbo", assistantOptions);
80#endregion
81
82#region
83// Create a thread with an initial user message and run it.
84ThreadCreationOptions threadOptions = new()
85{
1c40de67Krzysztof Cwalina2 years ago86InitialMessages = { "What's the weather like today?" }
9f9f2936Jose Arriaga Maldonado2 years ago87};
88
89ThreadRun run = client.CreateThreadAndRun(assistant.Id, threadOptions);
90#endregion
91
92#region
93// Poll the run until it is no longer queued or in progress.
94while (!run.Status.IsTerminal)
95{
96Thread.Sleep(TimeSpan.FromSeconds(1));
97run = client.GetRun(run.ThreadId, run.Id);
98
99// If the run requires action, resolve them.
100if (run.Status == RunStatus.RequiresAction)
101{
102List<ToolOutput> toolOutputs = [];
103
104foreach (RequiredAction action in run.RequiredActions)
105{
106switch (action.FunctionName)
107{
108case GetCurrentLocationFunctionName:
109{
110string toolResult = GetCurrentLocation();
111toolOutputs.Add(new ToolOutput(action.ToolCallId, toolResult));
112break;
113}
114
115case GetCurrentWeatherFunctionName:
116{
117// The arguments that the model wants to use to call the function are specified as a
118// stringified JSON object based on the schema defined in the tool definition. Note that
119// the model may hallucinate arguments too. Consequently, it is important to do the
120// appropriate parsing and validation before calling the function.
121using JsonDocument argumentsJson = JsonDocument.Parse(action.FunctionArguments);
122bool hasLocation = argumentsJson.RootElement.TryGetProperty("location", out JsonElement location);
123bool hasUnit = argumentsJson.RootElement.TryGetProperty("unit", out JsonElement unit);
124
125if (!hasLocation)
126{
127throw new ArgumentNullException(nameof(location), "The location argument is required.");
128}
129
130string toolResult = hasUnit
131? GetCurrentWeather(location.GetString(), unit.GetString())
132: GetCurrentWeather(location.GetString());
133toolOutputs.Add(new ToolOutput(action.ToolCallId, toolResult));
134break;
135}
136
137default:
138{
139// Handle other or unexpected calls.
140throw new NotImplementedException();
141}
142}
143}
144
145// Submit the tool outputs to the assistant, which returns the run to the queued state.
146run = client.SubmitToolOutputsToRun(run.ThreadId, run.Id, toolOutputs);
147}
148}
149#endregion
150
151#region
152// With the run complete, list the messages and display their content
153if (run.Status == RunStatus.Completed)
154{
2ab1a942Jose Arriaga Maldonado1 years ago155CollectionResult<ThreadMessage> messages
156= client.GetMessages(run.ThreadId, new MessageCollectionOptions() { Order = MessageCollectionOrder.Ascending });
b0f9e5c3Jose Arriaga Maldonado1 years ago157
9f9f2936Jose Arriaga Maldonado2 years ago158foreach (ThreadMessage message in messages)
159{
160Console.WriteLine($"[{message.Role.ToString().ToUpper()}]: ");
161foreach (MessageContent contentItem in message.Content)
162{
163Console.WriteLine($"{contentItem.Text}");
164
165if (contentItem.ImageFileId is not null)
166{
167Console.WriteLine($" <Image File ID> {contentItem.ImageFileId}");
168}
169
170// Include annotations, if any.
171if (contentItem.TextAnnotations.Count > 0)
172{
173Console.WriteLine();
174foreach (TextAnnotation annotation in contentItem.TextAnnotations)
175{
176Console.WriteLine($"* File ID used by file_search: {annotation.InputFileId}");
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}
5dce104aJose Arriaga Maldonado1 years ago194
195#pragma warning restore OPENAI001