Skip to content

Update long-running operation APIs to use LRO subclient pattern - streaming LRO subclient inherits from polling LRO subclient #118

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 115 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
115 commits
Select commit Hold shift + click to select a range
f4ab277
Add project reference to SCM; move to new paging types
annelo-msft Jun 25, 2024
38dd09d
wire return values and stub out implementations
annelo-msft Jun 25, 2024
17c4078
update page collection to use new page token contract
annelo-msft Jun 26, 2024
9cf0447
Update tests and samples
annelo-msft Jun 26, 2024
7679061
fix page token serialization logic
annelo-msft Jun 26, 2024
6acd48b
add async page collections
annelo-msft Jun 26, 2024
6327af6
Add rehydration overload
annelo-msft Jun 26, 2024
e0c14eb
Add async rehydration overload
annelo-msft Jun 26, 2024
2d46435
backup WIP on generalized implementation
annelo-msft Jun 26, 2024
bae625a
backup WIP on generalized implementation
annelo-msft Jun 26, 2024
03e17b6
remove Assistant-specific page collection implementations in favor of…
annelo-msft Jun 26, 2024
1f150ea
remove Assistant-specific page collection implementations in favor of…
annelo-msft Jun 26, 2024
6ba8dbd
Backup of generalized protocol paging idea
annelo-msft Jun 26, 2024
7c1f74d
remove Assistant-specific page result enumerator implementations in f…
annelo-msft Jun 26, 2024
bc2004e
take BinaryData instead of ClientToken, which cannot be constructed a…
annelo-msft Jun 26, 2024
6138636
rework rehydration methods to use new ClientToken pattern
annelo-msft Jun 26, 2024
bae34b9
add test for rehydration method
annelo-msft Jun 26, 2024
5283bc5
add a second test for rehydration method
annelo-msft Jun 26, 2024
e0305ea
SCM renames
annelo-msft Jun 26, 2024
64bea2b
updates from mini pr review
annelo-msft Jun 26, 2024
ea8db96
rework RequestOptions
annelo-msft Jun 26, 2024
bcfed80
bug fix
annelo-msft Jun 26, 2024
e365566
GetPageCore
annelo-msft Jun 27, 2024
1ed51af
backup of initial thinking on operation-specific page token idea
annelo-msft Jun 27, 2024
2ad6dc1
extend operation-specific page token idea to protocol methods
annelo-msft Jun 27, 2024
5b236a6
rename ClientToken to ContinuationToken
annelo-msft Jun 27, 2024
444bee0
nits: tidy
annelo-msft Jun 27, 2024
0060d5f
add test to illustrate convenience/protocol interop
annelo-msft Jun 28, 2024
0aca908
updates based on feedback from Krzysztof
annelo-msft Jul 1, 2024
c4a8a63
add protocol implementations of all Assistant-related paged methods
annelo-msft Jul 1, 2024
4a9d6af
add convenience implementations of Assistant-related paged methods
annelo-msft Jul 1, 2024
f91969f
move to CurrentPageToken instead of FirstPageToken
annelo-msft Jul 1, 2024
90eed11
starting idea around client as enumerator; backing up
annelo-msft Jul 1, 2024
313753e
more backup
annelo-msft Jul 1, 2024
a8e17e3
tidy up before taking inventory
annelo-msft Jul 1, 2024
c5261d1
backup prior to attempt to converge around single internal collection…
annelo-msft Jul 1, 2024
9c05fc2
more refining
annelo-msft Jul 1, 2024
209b71d
implement interface explicitly
annelo-msft Jul 1, 2024
b0d5f04
more tidy
annelo-msft Jul 1, 2024
c5b568d
Make it work for async
annelo-msft Jul 1, 2024
e07fb0b
restructure to look more like mini-client
annelo-msft Jul 2, 2024
f3c5e96
simplify
annelo-msft Jul 2, 2024
8b762e2
more nits and simplify
annelo-msft Jul 2, 2024
7cd2e92
nits
annelo-msft Jul 2, 2024
67d70fa
renames and a little rework
annelo-msft Jul 2, 2024
daab108
move page tokens
annelo-msft Jul 2, 2024
818b6bd
Implement remaining Assistants endpoings
annelo-msft Jul 2, 2024
34264b5
Implement vector store paginated endpoints
annelo-msft Jul 2, 2024
74e8b34
follow-ups to convenience and tests for vector stores
annelo-msft Jul 2, 2024
827cf2b
bug fix
annelo-msft Jul 2, 2024
891ccf5
temp bug fix
annelo-msft Jul 2, 2024
b15bd18
Proof of concept of PageEnumerator pattern for MessageCollection
annelo-msft Jul 2, 2024
34175dc
Refactor to PageEnumerator pattern
annelo-msft Jul 2, 2024
4a175d8
bug fix
annelo-msft Jul 2, 2024
8656f12
nit
annelo-msft Jul 2, 2024
4d2bf33
updates from SCM polish work
annelo-msft Jul 3, 2024
73faa95
Implement new LRO pattern on top of page collection implementatino
annelo-msft Jul 5, 2024
fc5d7ed
add missed files
annelo-msft Jul 5, 2024
529b399
add async overloads and rework poller a bit
annelo-msft Jul 5, 2024
a1422d1
fix some bugs
annelo-msft Jul 6, 2024
d338ec8
back up WIP
annelo-msft Jul 6, 2024
80dbcfa
more backup WIP
annelo-msft Jul 9, 2024
1ea8036
back up WIP
annelo-msft Jul 9, 2024
b2b3556
more backup of WIP - idea of protocol methods returning OperationResu…
annelo-msft Jul 10, 2024
415fff5
re-impl of polling LRO
annelo-msft Jul 10, 2024
c7d0870
nits
annelo-msft Jul 10, 2024
9ef8fc8
backup of streaming LRO WIP
annelo-msft Jul 10, 2024
e8886a3
intial e2e of async streaming ThreadRunLRO
annelo-msft Jul 10, 2024
279b3e7
backup while testing
annelo-msft Jul 10, 2024
4eeffaf
backup
annelo-msft Jul 11, 2024
e860fee
protocol operation implementation and polling test succeeds
annelo-msft Jul 11, 2024
aa94594
Streaming protocol implementation and test; one case open to solve still
annelo-msft Jul 11, 2024
46ffefc
handle IsCompleted for protocol/streaming by throwing, since we can't…
annelo-msft Jul 11, 2024
b1602b2
add test for streaming cancellation
annelo-msft Jul 11, 2024
05254c9
Initially implement StreamingThreadRunOperation.WaitAsync; with test …
annelo-msft Jul 11, 2024
4d5b0ef
add GetUpdatesStreaming method to StreamingThreadRunOperation
annelo-msft Jul 11, 2024
4482369
add SubmitToolOutputsToRunStreaming convenience method
annelo-msft Jul 11, 2024
5c384f3
add basic test for streaming SubmitToolOutput method; fix to Required…
annelo-msft Jul 12, 2024
67b0bdb
backup idea of ContinuableEnumerator
annelo-msft Jul 12, 2024
bd1722b
backup successful implementation of stream replacement ida
annelo-msft Jul 12, 2024
10ff848
add functional implementation of WaitAsync for streaming LRO
annelo-msft Jul 12, 2024
465b893
rewrite in terms of UpdateStatus
annelo-msft Jul 12, 2024
8f35fa6
move initialization to constructor
annelo-msft Jul 13, 2024
d8a6829
updates from SCM changes and some more comments and TODOs
annelo-msft Jul 13, 2024
f068567
Merge remote-tracking branch 'upstream/main' into oai-pageenumerator-…
annelo-msft Jul 15, 2024
4008584
move to SCM beta.5 package
annelo-msft Jul 15, 2024
b3d4fd7
Merge remote-tracking branch 'upstream/main' into oai-streamlroinherits
annelo-msft Jul 15, 2024
2e97ef7
move over update for protocol-convenience cast
annelo-msft Jul 15, 2024
1ee0486
some fixes
annelo-msft Jul 15, 2024
4ce3888
updates from SCM code
annelo-msft Jul 15, 2024
0622853
fix tests
annelo-msft Jul 15, 2024
e492b7c
remove internal protocol method implementations that have moved to in…
annelo-msft Jul 15, 2024
f811de1
nits
annelo-msft Jul 15, 2024
e7e3de7
add refdocs for paginated endpoint service methods
annelo-msft Jul 15, 2024
88319b8
add refdocs for options types
annelo-msft Jul 15, 2024
7118d13
add refdocs for collection rehydration overloads
annelo-msft Jul 15, 2024
e275801
add test and fix bugs
annelo-msft Jul 15, 2024
8d66ade
add a test for vector store
annelo-msft Jul 15, 2024
e1bdc4e
Merge remote-tracking branch 'upstream/main' into oai-streamlroinherits
annelo-msft Jul 16, 2024
8c77af5
Merge branch 'oai-streamlroinherits' of https://github.com/annelo-msf…
annelo-msft Jul 16, 2024
cfae419
updates from SCM changes
annelo-msft Jul 16, 2024
e99b2ba
Merge remote-tracking branch 'upstream/main' into oai-streamlroinherits
annelo-msft Jul 16, 2024
ae6eb9d
bug fix
annelo-msft Jul 16, 2024
d0417ae
back up WIP in adding convenience overloads to ThreadRunOperation min…
annelo-msft Jul 16, 2024
1666794
backup prior to running tests
annelo-msft Jul 16, 2024
422b594
add ReturnWhen parameter to protocol methods
annelo-msft Jul 16, 2024
1944863
bug fixes
annelo-msft Jul 16, 2024
6c637c7
Merge remote-tracking branch 'upstream/main' into oai-pageenumerator-…
annelo-msft Jul 16, 2024
0813f06
Merge branch 'oai-pageenumerator-pattern' into oai-streamlroinherits
annelo-msft Jul 16, 2024
aacf13b
more updates from paging PR branch
annelo-msft Jul 16, 2024
408bfef
add operation rehydration methods and tests
annelo-msft Jul 16, 2024
9a920f8
nits: backup before enumerable experiment
annelo-msft Jul 16, 2024
5373a8c
experiment with adding GetUpdates API to polling LRO type to enable e…
annelo-msft Jul 16, 2024
ea73b60
Merge remote-tracking branch 'upstream/main' into oai-streamlroinherits
annelo-msft Jul 17, 2024
8d9eadb
name RunId to Id; add GetRun methods back to client
annelo-msft Jul 17, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions OpenAI.sln
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenAI", "src\OpenAI.csproj
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenAI.Examples", "examples\OpenAI.Examples.csproj", "{1F1CD1D4-9932-4B73-99D8-C252A67D4B46}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenAI.Tests", "tests\OpenAI.Tests.csproj", "{6F156401-2544-41D7-B204-3148C51C1D09}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenAI.Tests", "tests\OpenAI.Tests.csproj", "{6F156401-2544-41D7-B204-3148C51C1D09}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.ClientModel", "..\azure-sdk-for-net\sdk\core\System.ClientModel\src\System.ClientModel.csproj", "{8CDFA283-1045-45D3-B86A-56DE301B331B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand All @@ -26,11 +28,15 @@ Global
{6F156401-2544-41D7-B204-3148C51C1D09}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6F156401-2544-41D7-B204-3148C51C1D09}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6F156401-2544-41D7-B204-3148C51C1D09}.Release|Any CPU.Build.0 = Release|Any CPU
{8CDFA283-1045-45D3-B86A-56DE301B331B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8CDFA283-1045-45D3-B86A-56DE301B331B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8CDFA283-1045-45D3-B86A-56DE301B331B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8CDFA283-1045-45D3-B86A-56DE301B331B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A97F4B90-2591-4689-B1F8-5F21FE6D6CAE}
EndGlobalSection
EndGlobal
EndGlobal
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,11 @@ When you request a chat completion, the default behavior is for the server to ge
The client library offers a convenient approach to working with streaming chat completions. If you wanted to re-write the example from the previous section using streaming, rather than calling the `ChatClient`'s `CompleteChat` method, you would call its `CompleteChatStreaming` method instead:

```csharp
ResultCollection<StreamingChatCompletionUpdate> updates
CollectionResult<StreamingChatCompletionUpdate> updates
= client.CompleteChatStreaming("Say 'this is a test.'");
```

Notice that the returned value is a `ResultCollection<StreamingChatCompletionUpdate>` instance, which can be enumerated to process the streaming response chunks as they arrive:
Notice that the returned value is a `CollectionResult<StreamingChatCompletionUpdate>` instance, which can be enumerated to process the streaming response chunks as they arrive:

```csharp
Console.WriteLine($"[ASSISTANT]:");
Expand All @@ -132,10 +132,10 @@ foreach (StreamingChatCompletionUpdate update in updates)
}
```

Alternatively, you can do this asynchronously by calling the `CompleteChatStreamingAsync` method to get an `AsyncResultCollection<StreamingChatCompletionUpdate>` and enumerate it using `await foreach`:
Alternatively, you can do this asynchronously by calling the `CompleteChatStreamingAsync` method to get an `AsyncCollectionResult<StreamingChatCompletionUpdate>` and enumerate it using `await foreach`:

```csharp
AsyncResultCollection<StreamingChatCompletionUpdate> updates
AsyncCollectionResult<StreamingChatCompletionUpdate> updates
= client.CompleteChatStreamingAsync("Say 'this is a test.'");

Console.WriteLine($"[ASSISTANT]:");
Expand Down Expand Up @@ -528,7 +528,7 @@ Finally, you can use the `AssistantClient`'s `GetMessages` method to retrieve th
For illustrative purposes, you could print the messages to the console and also save any images produced by the assistant to local storage:

```csharp
PageableCollection<ThreadMessage> messages = assistantClient.GetMessages(threadRun.ThreadId, ListOrder.OldestFirst);
PageCollection<ThreadMessage> messages = assistantClient.GetMessages(threadRun.ThreadId, ListOrder.OldestFirst);

foreach (ThreadMessage message in messages)
{
Expand Down Expand Up @@ -640,10 +640,10 @@ AssistantThread thread = assistantClient.CreateThread(new ThreadCreationOptions(
});
```

With the assistant and thread prepared, use the `CreateRunStreaming` method to get an enumerable `ResultCollection<StreamingUpdate>`. You can then iterate over this collection with `foreach`. For async calling patterns, use `CreateRunStreamingAsync` and iterate over the `AsyncResultCollection<StreamingUpdate>` with `await foreach`, instead. Note that streaming variants also exist for `CreateThreadAndRunStreaming` and `SubmitToolOutputsToRunStreaming`.
With the assistant and thread prepared, use the `CreateRunStreaming` method to get an enumerable `CollectionResult<StreamingUpdate>`. You can then iterate over this collection with `foreach`. For async calling patterns, use `CreateRunStreamingAsync` and iterate over the `AsyncCollectionResult<StreamingUpdate>` with `await foreach`, instead. Note that streaming variants also exist for `CreateThreadAndRunStreaming` and `SubmitToolOutputsToRunStreaming`.

```csharp
ResultCollection<StreamingUpdate> streamingUpdates = assistantClient.CreateRunStreaming(
CollectionResult<StreamingUpdate> streamingUpdates = assistantClient.CreateRunStreaming(
thread,
assistant,
new RunCreationOptions()
Expand Down
265 changes: 130 additions & 135 deletions examples/Assistants/Example01_RetrievalAugmentedGeneration.cs
Original file line number Diff line number Diff line change
@@ -1,149 +1,144 @@
using NUnit.Framework;
using OpenAI.Assistants;
using OpenAI.Files;
using System;
using System.ClientModel;
using System.Collections.Generic;
using System.IO;
using System.Threading;
//using NUnit.Framework;
//using OpenAI.Assistants;
//using OpenAI.Files;
//using System;
//using System.ClientModel;
//using System.Collections.Generic;
//using System.IO;
//using System.Threading;

namespace OpenAI.Examples;
//namespace OpenAI.Examples;

public partial class AssistantExamples
{
[Test]
public void Example01_RetrievalAugmentedGeneration()
{
// Assistants is a beta API and subject to change; acknowledge its experimental status by suppressing the matching warning.
#pragma warning disable OPENAI001
OpenAIClient openAIClient = new(Environment.GetEnvironmentVariable("OPENAI_API_KEY"));
FileClient fileClient = openAIClient.GetFileClient();
AssistantClient assistantClient = openAIClient.GetAssistantClient();
//public partial class AssistantExamples
//{
// [Test]
// public void Example01_RetrievalAugmentedGeneration()
// {
// // Assistants is a beta API and subject to change; acknowledge its experimental status by suppressing the matching warning.
//#pragma warning disable OPENAI001
// OpenAIClient openAIClient = new(Environment.GetEnvironmentVariable("OPENAI_API_KEY"));
// FileClient fileClient = openAIClient.GetFileClient();
// AssistantClient assistantClient = openAIClient.GetAssistantClient();

// First, let's contrive a document we'll use retrieval with and upload it.
using Stream document = BinaryData.FromString("""
{
"description": "This document contains the sale history data for Contoso products.",
"sales": [
{
"month": "January",
"by_product": {
"113043": 15,
"113045": 12,
"113049": 2
}
},
{
"month": "February",
"by_product": {
"113045": 22
}
},
{
"month": "March",
"by_product": {
"113045": 16,
"113055": 5
}
}
]
}
""").ToStream();
// // First, let's contrive a document we'll use retrieval with and upload it.
// using Stream document = BinaryData.FromString("""
// {
// "description": "This document contains the sale history data for Contoso products.",
// "sales": [
// {
// "month": "January",
// "by_product": {
// "113043": 15,
// "113045": 12,
// "113049": 2
// }
// },
// {
// "month": "February",
// "by_product": {
// "113045": 22
// }
// },
// {
// "month": "March",
// "by_product": {
// "113045": 16,
// "113055": 5
// }
// }
// ]
// }
// """).ToStream();

OpenAIFileInfo salesFile = fileClient.UploadFile(
document,
"monthly_sales.json",
FileUploadPurpose.Assistants);
// OpenAIFileInfo salesFile = fileClient.UploadFile(
// document,
// "monthly_sales.json",
// FileUploadPurpose.Assistants);

// Now, we'll create a client intended to help with that data
AssistantCreationOptions assistantOptions = new()
{
Name = "Example: Contoso sales RAG",
Instructions =
"You are an assistant that looks up sales data and helps visualize the information based"
+ " on user queries. When asked to generate a graph, chart, or other visualization, use"
+ " the code interpreter tool to do so.",
Tools =
{
new FileSearchToolDefinition(),
new CodeInterpreterToolDefinition(),
},
ToolResources = new()
{
FileSearch = new()
{
NewVectorStores =
{
new VectorStoreCreationHelper([salesFile.Id]),
}
}
},
};
// // Now, we'll create a client intended to help with that data
// AssistantCreationOptions assistantOptions = new()
// {
// Name = "Example: Contoso sales RAG",
// Instructions =
// "You are an assistant that looks up sales data and helps visualize the information based"
// + " on user queries. When asked to generate a graph, chart, or other visualization, use"
// + " the code interpreter tool to do so.",
// Tools =
// {
// new FileSearchToolDefinition(),
// new CodeInterpreterToolDefinition(),
// },
// ToolResources = new()
// {
// FileSearch = new()
// {
// NewVectorStores =
// {
// new VectorStoreCreationHelper([salesFile.Id]),
// }
// }
// },
// };

Assistant assistant = assistantClient.CreateAssistant("gpt-4o", assistantOptions);
// Assistant assistant = assistantClient.CreateAssistant("gpt-4o", assistantOptions);

// Now we'll create a thread with a user query about the data already associated with the assistant, then run it
ThreadCreationOptions threadOptions = new()
{
InitialMessages = { "How well did product 113045 sell in February? Graph its trend over time." }
};
// // Now we'll create a thread with a user query about the data already associated with the assistant, then run it
// ThreadCreationOptions threadOptions = new()
// {
// InitialMessages = { "How well did product 113045 sell in February? Graph its trend over time." }
// };

ThreadRun threadRun = assistantClient.CreateThreadAndRun(assistant.Id, threadOptions);
// // Passing ReturnWhen.Completed means CreateThreadAndRun will return control after the run is complete.
// ThreadRunOperation runOperation = assistantClient.CreateThreadAndRun(ReturnWhen.Completed, assistant.Id, threadOptions);

// Check back to see when the run is done
do
{
Thread.Sleep(TimeSpan.FromSeconds(1));
threadRun = assistantClient.GetRun(threadRun.ThreadId, threadRun.Id);
} while (!threadRun.Status.IsTerminal);
// // Finally, we'll print out the full history for the thread that includes the augmented generation
// PageCollection<ThreadMessage> messagePages
// = assistantClient.GetMessages(runOperation.ThreadId, new MessageCollectionOptions() { Order = ListOrder.OldestFirst });
// IEnumerable<ThreadMessage> messages = messagePages.GetAllValues();

// Finally, we'll print out the full history for the thread that includes the augmented generation
PageableCollection<ThreadMessage> messages
= assistantClient.GetMessages(threadRun.ThreadId, ListOrder.OldestFirst);
// foreach (ThreadMessage message in messages)
// {
// Console.Write($"[{message.Role.ToString().ToUpper()}]: ");
// foreach (MessageContent contentItem in message.Content)
// {
// if (!string.IsNullOrEmpty(contentItem.Text))
// {
// Console.WriteLine($"{contentItem.Text}");

foreach (ThreadMessage message in messages)
{
Console.Write($"[{message.Role.ToString().ToUpper()}]: ");
foreach (MessageContent contentItem in message.Content)
{
if (!string.IsNullOrEmpty(contentItem.Text))
{
Console.WriteLine($"{contentItem.Text}");
// if (contentItem.TextAnnotations.Count > 0)
// {
// Console.WriteLine();
// }

if (contentItem.TextAnnotations.Count > 0)
{
Console.WriteLine();
}
// // Include annotations, if any.
// foreach (TextAnnotation annotation in contentItem.TextAnnotations)
// {
// if (!string.IsNullOrEmpty(annotation.InputFileId))
// {
// Console.WriteLine($"* File citation, file ID: {annotation.InputFileId}");
// }
// if (!string.IsNullOrEmpty(annotation.OutputFileId))
// {
// Console.WriteLine($"* File output, new file ID: {annotation.OutputFileId}");
// }
// }
// }
// if (!string.IsNullOrEmpty(contentItem.ImageFileId))
// {
// OpenAIFileInfo imageInfo = fileClient.GetFile(contentItem.ImageFileId);
// BinaryData imageBytes = fileClient.DownloadFile(contentItem.ImageFileId);
// using FileStream stream = File.OpenWrite($"{imageInfo.Filename}.png");
// imageBytes.ToStream().CopyTo(stream);

// Include annotations, if any.
foreach (TextAnnotation annotation in contentItem.TextAnnotations)
{
if (!string.IsNullOrEmpty(annotation.InputFileId))
{
Console.WriteLine($"* File citation, file ID: {annotation.InputFileId}");
}
if (!string.IsNullOrEmpty(annotation.OutputFileId))
{
Console.WriteLine($"* File output, new file ID: {annotation.OutputFileId}");
}
}
}
if (!string.IsNullOrEmpty(contentItem.ImageFileId))
{
OpenAIFileInfo imageInfo = fileClient.GetFile(contentItem.ImageFileId);
BinaryData imageBytes = fileClient.DownloadFile(contentItem.ImageFileId);
using FileStream stream = File.OpenWrite($"{imageInfo.Filename}.png");
imageBytes.ToStream().CopyTo(stream);
// Console.WriteLine($"<image: {imageInfo.Filename}.png>");
// }
// }
// Console.WriteLine();
// }

Console.WriteLine($"<image: {imageInfo.Filename}.png>");
}
}
Console.WriteLine();
}

// Optionally, delete any persistent resources you no longer need.
_ = assistantClient.DeleteThread(threadRun.ThreadId);
_ = assistantClient.DeleteAssistant(assistant);
_ = fileClient.DeleteFile(salesFile);
}
}
// // Optionally, delete any persistent resources you no longer need.
// _ = assistantClient.DeleteThread(runOperation.ThreadId);
// _ = assistantClient.DeleteAssistant(assistant);
// _ = fileClient.DeleteFile(salesFile);
// }
//}
Loading