-
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
16 changed files
with
549 additions
and
65 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,197 @@ | ||
```csharp | ||
using var api = GetAuthenticatedClient(); | ||
|
||
OpenAIFile favoriteNumberFile = await api.Files.CreateFileAsync( | ||
file: """ | ||
This file contains the favorite numbers for individuals. | ||
John Doe: 14 | ||
Bob Doe: 32 | ||
Jane Doe: 44 | ||
"""u8.ToArray(), | ||
filename: "favorite_numbers.txt", | ||
purpose: CreateFileRequestPurpose.Assistants); | ||
|
||
var service = new AllToolsService(); | ||
IList<ChatCompletionTool> tools = service.AsTools().AsOpenAiTools(); | ||
|
||
AssistantObject assistant = await api.Assistants.CreateAssistantAsync( | ||
model: CreateAssistantRequestModel.Gpt4o, | ||
instructions: "Use functions to resolve family relations into the names of people. Use file search to " | ||
+ " look up the favorite numbers of people. Use code interpreter to create graphs of lines.", | ||
tools: tools | ||
.Select(x => new ToolsItem2(new AssistantToolsFunction | ||
{ | ||
Function = x.Function, | ||
})) | ||
.Concat([ | ||
new AssistantToolsFileSearch(), | ||
new AssistantToolsCode() | ||
]) | ||
.ToArray(), | ||
toolResources: new CreateAssistantRequestToolResources | ||
{ | ||
FileSearch = new CreateAssistantRequestToolResourcesFileSearch | ||
{ | ||
VectorStores = new List<CreateAssistantRequestToolResourcesFileSearchVectorStore> | ||
{ | ||
new() | ||
{ | ||
FileIds = [favoriteNumberFile.Id], | ||
}, | ||
}, | ||
}, | ||
}); | ||
|
||
RunObject run = await api.Assistants.CreateThreadAndRunAsync( | ||
assistantId: assistant.Id, | ||
new CreateThreadRequest | ||
{ | ||
Messages = [ | ||
"Create a graph of a line with a slope that's my father's favorite number " | ||
+ "and an offset that's my mother's favorite number.", | ||
"Include people's names in your response and cite where you found them." | ||
], | ||
}); | ||
|
||
// Poll the run until it is no longer queued or in progress. | ||
while (run.Status is RunObjectStatus.Queued or RunObjectStatus.InProgress or RunObjectStatus.RequiresAction) | ||
{ | ||
await Task.Delay(TimeSpan.FromSeconds(1)); | ||
run = await api.Assistants.GetRunAsync(run.ThreadId, run.Id); | ||
|
||
// If the run requires action, resolve them. | ||
if (run.Status == RunObjectStatus.RequiresAction) | ||
{ | ||
List<SubmitToolOutputsRunRequestToolOutput> toolOutputs = []; | ||
|
||
foreach (RunToolCallObject toolCall in run.RequiredAction?.SubmitToolOutputs.ToolCalls ?? []) | ||
{ | ||
var json = await service.CallAsync( | ||
functionName: toolCall.Function.Name, | ||
argumentsAsJson: toolCall.Function.Arguments); | ||
toolOutputs.Add(new SubmitToolOutputsRunRequestToolOutput | ||
{ | ||
ToolCallId = toolCall.Id, | ||
Output = json, | ||
}); | ||
} | ||
|
||
// Submit the tool outputs to the assistant, which returns the run to the queued state. | ||
run = await api.Assistants.SubmitToolOuputsToRunAsync( | ||
threadId: run.ThreadId, | ||
runId: run.Id, | ||
toolOutputs: toolOutputs); | ||
} | ||
} | ||
|
||
// With the run complete, list the messages and display their content | ||
if (run.Status == RunObjectStatus.Completed) | ||
{ | ||
ListMessagesResponse messages | ||
= await api.Assistants.ListMessagesAsync(run.ThreadId, order: ListMessagesOrder.Asc); | ||
|
||
foreach (MessageObject message in messages.Data) | ||
{ | ||
Console.WriteLine($"[{message.Role.ToString().ToUpper()}]: "); | ||
foreach (ContentItem2 contentItem in message.Content) | ||
{ | ||
if (contentItem.MessageImageFileObject is {} imageFile) | ||
{ | ||
OpenAIFile imageInfo = await api.Files.RetrieveFileAsync(imageFile.ImageFile.FileId); | ||
byte[] imageBytes = await api.Files.DownloadFileAsync(imageFile.ImageFile.FileId); | ||
|
||
FileInfo fileInfo = new($"{imageInfo.Filename}.png"); | ||
|
||
await File.WriteAllBytesAsync(fileInfo.FullName, imageBytes); | ||
|
||
Console.WriteLine($"<image: {new Uri(fileInfo.FullName).AbsoluteUri}>"); | ||
} | ||
if (contentItem.MessageImageUrlObject is {} imageUrl) | ||
{ | ||
Console.WriteLine($" <Image URL> {imageUrl.ImageUrl.Url}"); | ||
} | ||
if (contentItem.MessageTextObject is {} text) | ||
{ | ||
Console.WriteLine($"{text.Text.Value}"); | ||
|
||
// Include annotations, if any. | ||
if (text.Text.Annotations.Count > 0) | ||
{ | ||
Console.WriteLine(); | ||
foreach (AnnotationsItem annotation in text.Text.Annotations) | ||
{ | ||
if (annotation.MessageContentTextFileCitationObject is {} fileCitation) | ||
{ | ||
Console.WriteLine($"* File citation, file ID: {fileCitation.FileCitation.FileId}"); | ||
Console.WriteLine($"* Text to replace: {fileCitation.Text}"); | ||
Console.WriteLine($"* Message content index range: {fileCitation.StartIndex}-{fileCitation.EndIndex}"); | ||
} | ||
if (annotation.MessageContentTextFilePathObject is {} filePath) | ||
{ | ||
Console.WriteLine($"* File output, new file ID: {filePath.FilePath.FileId}"); | ||
Console.WriteLine($"* Text to replace: {filePath.Text}"); | ||
Console.WriteLine($"* Message content index range: {filePath.StartIndex}-{filePath.EndIndex}"); | ||
} | ||
} | ||
} | ||
} | ||
if (contentItem.MessageRefusalObject is {} refusal) | ||
{ | ||
Console.WriteLine($"Refusal: {refusal.Refusal}"); | ||
} | ||
} | ||
Console.WriteLine(); | ||
} | ||
|
||
|
||
// List run steps for details about tool calls | ||
ListRunStepsResponse runSteps = await api.Assistants.ListRunStepsAsync( | ||
threadId: run.ThreadId, | ||
runId: run.Id, | ||
order: ListRunStepsOrder.Asc); | ||
foreach (RunStepObject step in runSteps.Data) | ||
{ | ||
Console.WriteLine($"Run step: {step.Status}"); | ||
foreach (var toolCall in step.StepDetails.ToolCalls?.ToolCalls ?? []) | ||
{ | ||
if (toolCall.RunStepDetailsCodeObject is {} codeInterpreter) | ||
{ | ||
Console.WriteLine($" --> Tool call: {codeInterpreter.Type}"); | ||
foreach (var output in codeInterpreter.CodeInterpreter.Outputs) | ||
{ | ||
if (output.Logs is {} logs) | ||
{ | ||
Console.WriteLine($" --> Output: {logs.Logs}"); | ||
} | ||
if (output.Image is {} image) | ||
{ | ||
Console.WriteLine($" --> Output: {image.Image.FileId}"); | ||
} | ||
} | ||
} | ||
if (toolCall.RunStepDetailsFileSearchObject is {} fileSearch) | ||
{ | ||
Console.WriteLine($" --> Tool call: {fileSearch.Type}"); | ||
foreach (var output in fileSearch.FileSearch.Results ?? []) | ||
{ | ||
Console.WriteLine($" --> Output: {output.FileId}"); | ||
} | ||
} | ||
if (toolCall.RunStepDetailsFunctionObject is {} tool) | ||
{ | ||
Console.WriteLine($" --> Tool call: {tool.Type}"); | ||
} | ||
} | ||
} | ||
} | ||
else | ||
{ | ||
throw new NotImplementedException(run.Status.ToString()); | ||
} | ||
|
||
// Optionally, delete any persistent resources you no longer need. | ||
_ = await api.Assistants.DeleteThreadAsync(run.ThreadId); | ||
_ = await api.Assistants.DeleteAssistantAsync(assistant.Id); | ||
_ = await api.Files.DeleteFileAsync(favoriteNumberFile.Id); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
```csharp | ||
[GenerateJsonSchema(Strict = false)] | ||
public interface IAllToolsService | ||
{ | ||
[Description("Provided a family relation type like 'father' or 'mother', " | ||
+ "gets the name of the related person from the user.")] | ||
public string GetNameOfFamilyMember( | ||
[Description("The relation to the user to query, e.g. 'mother' or 'father'")] | ||
string relation); | ||
} | ||
|
||
public class AllToolsService : IAllToolsService | ||
{ | ||
public string GetNameOfFamilyMember(string relation) | ||
{ | ||
return relation switch | ||
{ | ||
not null when relation.Contains("father") => "John Doe", | ||
not null when relation.Contains("mother") => "Jane Doe", | ||
_ => throw new ArgumentException(relation, nameof(relation)) | ||
}; | ||
} | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.