Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
42 changes: 41 additions & 1 deletion src/Custom/Assistants/MessageCreationAttachment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,47 @@ public partial class MessageCreationAttachment
public IReadOnlyList<ToolDefinition> Tools { get; }

private void SerializeTools(Utf8JsonWriter writer, ModelReaderWriterOptions options)
=> writer.WriteObjectValue(Tools, options);
{
if (Tools is null)
{
writer.WriteNullValue();
return;
}

writer.WriteStartArray();
foreach (ToolDefinition tool in Tools)
{
using var ms = new System.IO.MemoryStream();
using (var tempWriter = new Utf8JsonWriter(ms))
{
tempWriter.WriteObjectValue(tool, options);
tempWriter.Flush();
}

using (JsonDocument doc = JsonDocument.Parse(ms.ToArray()))
{
JsonElement root = doc.RootElement;
if (root.ValueKind == JsonValueKind.Object)
{
writer.WriteStartObject();
foreach (var prop in root.EnumerateObject())
{
if (prop.NameEquals("file_search"u8))
{
continue;
}
prop.WriteTo(writer);
}
writer.WriteEndObject();
}
else
{
root.WriteTo(writer);
}
}
}
writer.WriteEndArray();
}

private static void DeserializeTools(JsonProperty property, ref IReadOnlyList<ToolDefinition> tools)
{
Expand Down
59 changes: 51 additions & 8 deletions tests/Assistants/AssistantsTests.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
using Microsoft.ClientModel.TestFramework;
using NUnit.Framework;
using NUnit.Framework.Internal;
using OpenAI.Assistants;
using OpenAI.Files;
using OpenAI.Tests.Utility;
using OpenAI.VectorStores;
using System;
using System.ClientModel;
using System.ClientModel.Primitives;
Expand All @@ -14,6 +7,12 @@
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.ClientModel.TestFramework;
using NUnit.Framework;
using OpenAI.Assistants;
using OpenAI.Files;
using OpenAI.Tests.Utility;
using OpenAI.VectorStores;
using static OpenAI.Tests.TestHelpers;

namespace OpenAI.Tests.Assistants;
Expand All @@ -30,7 +29,6 @@ public class AssistantsTests : OpenAIRecordedTestBase
private readonly List<string> _vectorStoreIdsToDelete = [];

private static readonly DateTimeOffset s_2024 = new(2024, 1, 1, 0, 0, 0, TimeSpan.Zero);
private static readonly string s_testAssistantName = $".NET SDK Test Assistant - Please Delete Me";
private static readonly string s_cleanupMetadataKey = $"test_metadata_cleanup_eligible";

private AssistantClient GetTestClient() => GetProxiedOpenAIClient<AssistantClient>(TestScenario.Assistants);
Expand Down Expand Up @@ -847,6 +845,51 @@ This file describes the favorite foods of several people.
});
}

[RecordedTest]
[LiveOnly]
public async Task FileOnMessageWorks()
{
// First, we need to upload a simple test file.
OpenAIFileClient fileClient = GetTestClient<OpenAIFileClient>(TestScenario.Files);
OpenAIFile testFile = await fileClient.UploadFileAsync(
BinaryData.FromString("""
This file describes the favorite foods of several people.

Summanus Ferdinand: tacos
Tekakwitha Effie: pizza
Filip Carola: cake
""").ToStream(),
"favorite_foods.txt",
FileUploadPurpose.Assistants);
Validate(testFile);

AssistantClient client = GetTestClient();

AssistantThread thread = await client.CreateThreadAsync();
Validate(thread);

Assistant assistant = await client.CreateAssistantAsync("gpt-4o-mini");
Validate(assistant);

ThreadMessage message = await client.CreateMessageAsync(
thread.Id,
MessageRole.User,
new[] {
MessageContent.FromText("What is this file?"),
},
new MessageCreationOptions()
{
Attachments = [
new MessageCreationAttachment(testFile.Id, new List<ToolDefinition>() { ToolDefinition.CreateFileSearch() }),
new MessageCreationAttachment(testFile.Id, new List<ToolDefinition>() { ToolDefinition.CreateCodeInterpreter() })
]
}
);
Validate(message);

var result = client.CreateRunStreamingAsync(thread.Id, assistant.Id);
}

[RecordedTest]
public async Task FileSearchStreamingWorks()
{
Expand Down
192 changes: 192 additions & 0 deletions tests/SessionRecords/AssistantsTests/FileOnMessageWorksAsync.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
{
"Entries": [
{
"RequestUri": "https://api.openai.com/v1/threads",
"RequestMethod": "POST",
"RequestHeaders": {
"Accept": "application/json",
"Authorization": "Sanitized",
"Content-Length": "0",
"OpenAI-Beta": "assistants=v2",
"User-Agent": "OpenAI/2.5.0 (.NET 9.0.10; Darwin 25.0.0 Darwin Kernel Version 25.0.0: Wed Sep 17 21:42:08 PDT 2025; root:xnu-12377.1.9~141/RELEASE_ARM64_T8132)"
},
"RequestBody": null,
"StatusCode": 200,
"ResponseHeaders": {
"Alt-Svc": "h3=\":443\"",
"cf-cache-status": "DYNAMIC",
"CF-RAY": "99c6d22e3c896b33-DFW",
"Connection": "keep-alive",
"Content-Length": "137",
"Content-Type": "application/json",
"Date": "Sanitized",
"openai-organization": "Sanitized",
"openai-processing-ms": "Sanitized",
"openai-project": "Sanitized",
"openai-version": "2020-10-01",
"Server": "cloudflare",
"Set-Cookie": [
"Sanitized",
"Sanitized"
],
"Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload",
"X-Content-Type-Options": "nosniff",
"x-envoy-upstream-service-time": "167",
"x-openai-proxy-wasm": "v0.1",
"X-Request-ID": "Sanitized"
},
"ResponseBody": {
"id": "thread_gAAkyuIXs5PLiQoe8sIEDTXV",
"object": "thread",
"created_at": 1762791037,
"metadata": {},
"tool_resources": {}
}
},
{
"RequestUri": "https://api.openai.com/v1/assistants",
"RequestMethod": "POST",
"RequestHeaders": {
"Accept": "application/json",
"Authorization": "Sanitized",
"Content-Length": "23",
"Content-Type": "application/json",
"OpenAI-Beta": "assistants=v2",
"User-Agent": "OpenAI/2.5.0 (.NET 9.0.10; Darwin 25.0.0 Darwin Kernel Version 25.0.0: Wed Sep 17 21:42:08 PDT 2025; root:xnu-12377.1.9~141/RELEASE_ARM64_T8132)"
},
"RequestBody": {
"model": "gpt-4o-mini"
},
"StatusCode": 200,
"ResponseHeaders": {
"Alt-Svc": "h3=\":443\"",
"cf-cache-status": "DYNAMIC",
"CF-RAY": "99c6d22fae616b33-DFW",
"Connection": "keep-alive",
"Content-Length": "337",
"Content-Type": "application/json",
"Date": "Sanitized",
"openai-organization": "Sanitized",
"openai-processing-ms": "Sanitized",
"openai-project": "Sanitized",
"openai-version": "2020-10-01",
"Server": "cloudflare",
"Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload",
"X-Content-Type-Options": "nosniff",
"x-envoy-upstream-service-time": "664",
"x-openai-proxy-wasm": "v0.1",
"X-Request-ID": "Sanitized"
},
"ResponseBody": {
"id": "asst_3Gz6lp6RnOg4hCaB8QzgaTAE",
"object": "assistant",
"created_at": 1762791037,
"name": null,
"description": null,
"model": "gpt-4o-mini",
"instructions": null,
"tools": [],
"top_p": 1.0,
"temperature": 1.0,
"reasoning_effort": null,
"tool_resources": {},
"metadata": {},
"response_format": "auto"
}
},
{
"RequestUri": "https://api.openai.com/v1/threads/thread_gAAkyuIXs5PLiQoe8sIEDTXV/messages",
"RequestMethod": "POST",
"RequestHeaders": {
"Accept": "application/json",
"Authorization": "Sanitized",
"Content-Length": "217",
"Content-Type": "application/json",
"OpenAI-Beta": "assistants=v2",
"User-Agent": "OpenAI/2.5.0 (.NET 9.0.10; Darwin 25.0.0 Darwin Kernel Version 25.0.0: Wed Sep 17 21:42:08 PDT 2025; root:xnu-12377.1.9~141/RELEASE_ARM64_T8132)"
},
"RequestBody": {
"role": "user",
"content": "What is this file?",
"attachments": [
{
"file_id": "file-LEY8LWxuCKn5TNcydztuW1",
"tools": [
{
"type": "file_search"
}
]
},
{
"file_id": "file-LEY8LWxuCKn5TNcydztuW1",
"tools": [
{
"type": "code_interpreter"
}
]
}
]
},
"StatusCode": 200,
"ResponseHeaders": {
"Alt-Svc": "h3=\":443\"",
"cf-cache-status": "DYNAMIC",
"CF-RAY": "99c6d2347bdd6b33-DFW",
"Connection": "keep-alive",
"Content-Length": "675",
"Content-Type": "application/json",
"Date": "Sanitized",
"openai-organization": "Sanitized",
"openai-processing-ms": "Sanitized",
"openai-project": "Sanitized",
"openai-version": "2020-10-01",
"Server": "cloudflare",
"Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload",
"X-Content-Type-Options": "nosniff",
"x-envoy-upstream-service-time": "2353",
"x-openai-proxy-wasm": "v0.1",
"X-Request-ID": "Sanitized"
},
"ResponseBody": {
"id": "msg_MZwW4GbZHxjqCfOHGFFuybon",
"object": "thread.message",
"created_at": 1762791039,
"assistant_id": null,
"thread_id": "thread_gAAkyuIXs5PLiQoe8sIEDTXV",
"run_id": null,
"role": "user",
"content": [
{
"type": "text",
"text": {
"value": "What is this file?",
"annotations": []
}
}
],
"attachments": [
{
"file_id": "file-LEY8LWxuCKn5TNcydztuW1",
"tools": [
{
"type": "file_search"
}
]
},
{
"file_id": "file-LEY8LWxuCKn5TNcydztuW1",
"tools": [
{
"type": "code_interpreter"
}
]
}
],
"metadata": {}
}
}
],
"Variables": {
"OPEN-API-KEY": "api-key"
}
}
Loading
Loading