chore: import upstream snapshot with attribution
dotnet-build-and-test / dotnet-test-functions (push) Has been cancelled
dotnet-build-and-test / paths-filter (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Debug, windows-latest, net9.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net8.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-foundry-hosted-it (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test-check (push) Has been cancelled
dotnet-build-and-test / Integration Test Report (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
dotnet-build-and-test / dotnet-test-functions (push) Has been cancelled
dotnet-build-and-test / paths-filter (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Debug, windows-latest, net9.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net8.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-foundry-hosted-it (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test-check (push) Has been cancelled
dotnet-build-and-test / Integration Test Report (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
This commit is contained in:
+92
@@ -0,0 +1,92 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Text.Json;
|
||||
using Microsoft.Agents.AI.Hosting.OpenAI.Responses;
|
||||
|
||||
namespace Microsoft.Agents.AI.Hosting.OpenAI.UnitTests;
|
||||
|
||||
/// <summary>
|
||||
/// Unit tests for AgentInvocationContext.
|
||||
/// </summary>
|
||||
public sealed class AgentInvocationContextTests
|
||||
{
|
||||
[Fact]
|
||||
public void Constructor_WithIdGenerator_InitializesCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var idGenerator = new IdGenerator("resp_test123", "conv_test456");
|
||||
|
||||
// Act
|
||||
var context = new AgentInvocationContext(idGenerator);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(context);
|
||||
Assert.Same(idGenerator, context.IdGenerator);
|
||||
Assert.Equal("resp_test123", context.ResponseId);
|
||||
Assert.Equal("conv_test456", context.ConversationId);
|
||||
Assert.NotNull(context.JsonSerializerOptions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_WithoutJsonOptions_UsesDefaultOptions()
|
||||
{
|
||||
// Arrange
|
||||
var idGenerator = new IdGenerator("resp_test", "conv_test");
|
||||
|
||||
// Act
|
||||
var context = new AgentInvocationContext(idGenerator);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(context.JsonSerializerOptions);
|
||||
Assert.Same(OpenAIHostingJsonUtilities.DefaultOptions, context.JsonSerializerOptions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_WithCustomJsonOptions_UsesProvidedOptions()
|
||||
{
|
||||
// Arrange
|
||||
var idGenerator = new IdGenerator("resp_test", "conv_test");
|
||||
var customOptions = new JsonSerializerOptions
|
||||
{
|
||||
PropertyNameCaseInsensitive = true
|
||||
};
|
||||
|
||||
// Act
|
||||
var context = new AgentInvocationContext(idGenerator, customOptions);
|
||||
|
||||
// Assert
|
||||
Assert.Same(customOptions, context.JsonSerializerOptions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResponseId_ReturnsIdGeneratorResponseId()
|
||||
{
|
||||
// Arrange
|
||||
const string ResponseId = "resp_property_test";
|
||||
var idGenerator = new IdGenerator(ResponseId, "conv_test");
|
||||
var context = new AgentInvocationContext(idGenerator);
|
||||
|
||||
// Act
|
||||
string result = context.ResponseId;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(ResponseId, result);
|
||||
Assert.Equal(idGenerator.ResponseId, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConversationId_ReturnsIdGeneratorConversationId()
|
||||
{
|
||||
// Arrange
|
||||
const string ConversationId = "conv_property_test";
|
||||
var idGenerator = new IdGenerator("resp_test", ConversationId);
|
||||
var context = new AgentInvocationContext(idGenerator);
|
||||
|
||||
// Act
|
||||
string result = context.ConversationId;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(ConversationId, result);
|
||||
Assert.Equal(idGenerator.ConversationId, result);
|
||||
}
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Agents.AI.Hosting.OpenAI.ChatCompletions.Models;
|
||||
using Microsoft.Extensions.AI;
|
||||
|
||||
namespace Microsoft.Agents.AI.Hosting.OpenAI.UnitTests;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for ChatCompletionRequestMessage.ToChatMessage() role preservation.
|
||||
/// Verifies that each message type correctly maps its role to the corresponding ChatRole.
|
||||
/// </summary>
|
||||
public sealed class ChatCompletionRequestMessageToChatMessageTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("system", """{"role":"system","content":"You are a helpful assistant."}""")]
|
||||
[InlineData("developer", """{"role":"developer","content":"Follow these rules."}""")]
|
||||
[InlineData("user", """{"role":"user","content":"Hello!"}""")]
|
||||
[InlineData("assistant", """{"role":"assistant","content":"Hi there!"}""")]
|
||||
[InlineData("tool", """{"role":"tool","content":"result","tool_call_id":"call_123"}""")]
|
||||
public void ToChatMessage_PreservesRole_ForTextContent(string expectedRole, string json)
|
||||
{
|
||||
// Arrange
|
||||
ChatCompletionRequestMessage message = JsonSerializer.Deserialize(
|
||||
json, ChatCompletions.ChatCompletionsJsonContext.Default.ChatCompletionRequestMessage)!;
|
||||
|
||||
// Act
|
||||
ChatMessage chatMessage = message.ToChatMessage();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedRole, message.Role);
|
||||
Assert.Equal(new ChatRole(expectedRole), chatMessage.Role);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ToChatMessage_FunctionMessage_PreservesRole()
|
||||
{
|
||||
// Arrange
|
||||
const string Json = """{"role":"function","name":"get_weather","content":"sunny"}""";
|
||||
ChatCompletionRequestMessage message = JsonSerializer.Deserialize(
|
||||
Json, ChatCompletions.ChatCompletionsJsonContext.Default.ChatCompletionRequestMessage)!;
|
||||
|
||||
// Act
|
||||
ChatMessage chatMessage = message.ToChatMessage();
|
||||
|
||||
// Assert
|
||||
Assert.Equal("function", message.Role);
|
||||
Assert.Equal(new ChatRole("function"), chatMessage.Role);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("system")]
|
||||
[InlineData("developer")]
|
||||
[InlineData("user")]
|
||||
[InlineData("assistant")]
|
||||
public void ToChatMessage_PreservesRole_ForMultiPartContent(string expectedRole)
|
||||
{
|
||||
// Arrange
|
||||
string json = $$"""{"role":"{{expectedRole}}","content":[{"type":"text","text":"Hello!"}]}""";
|
||||
ChatCompletionRequestMessage message = JsonSerializer.Deserialize(
|
||||
json, ChatCompletions.ChatCompletionsJsonContext.Default.ChatCompletionRequestMessage)!;
|
||||
|
||||
// Act
|
||||
ChatMessage chatMessage = message.ToChatMessage();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedRole, message.Role);
|
||||
Assert.Equal(new ChatRole(expectedRole), chatMessage.Role);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ToChatMessage_MultiTurnConversation_PreservesAllRoles()
|
||||
{
|
||||
// Arrange - simulate a multi-turn conversation
|
||||
string[] jsons =
|
||||
[
|
||||
"""{"role":"system","content":"You are a helpful assistant."}""",
|
||||
"""{"role":"user","content":"Hello!"}""",
|
||||
"""{"role":"assistant","content":"Hi there! How can I help?"}""",
|
||||
"""{"role":"user","content":"What did I just say?"}"""
|
||||
];
|
||||
|
||||
string[] expectedRoles = ["system", "user", "assistant", "user"];
|
||||
|
||||
// Act
|
||||
ChatMessage[] chatMessages = jsons
|
||||
.Select(j => JsonSerializer.Deserialize(
|
||||
j, ChatCompletions.ChatCompletionsJsonContext.Default.ChatCompletionRequestMessage)!)
|
||||
.Select(m => m.ToChatMessage())
|
||||
.ToArray();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedRoles.Length, chatMessages.Length);
|
||||
for (int i = 0; i < expectedRoles.Length; i++)
|
||||
{
|
||||
Assert.Equal(new ChatRole(expectedRoles[i]), chatMessages[i].Role);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ToChatMessage_PreservesTextContent()
|
||||
{
|
||||
// Arrange
|
||||
const string Json = """{"role":"system","content":"You are a helpful assistant."}""";
|
||||
ChatCompletionRequestMessage message = JsonSerializer.Deserialize(
|
||||
Json, ChatCompletions.ChatCompletionsJsonContext.Default.ChatCompletionRequestMessage)!;
|
||||
|
||||
// Act
|
||||
ChatMessage chatMessage = message.ToChatMessage();
|
||||
|
||||
// Assert
|
||||
Assert.Contains(chatMessage.Contents, c => c is TextContent tc && tc.Text == "You are a helpful assistant.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,313 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Agents.AI.Hosting.OpenAI.UnitTests;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting.Server;
|
||||
using Microsoft.AspNetCore.TestHost;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace Microsoft.Agents.AI.Hosting.OpenAI.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for conformance tests that load request/response traces from disk.
|
||||
/// </summary>
|
||||
public abstract class ConformanceTestBase : IAsyncDisposable
|
||||
{
|
||||
protected const string TracesBasePath = "ConformanceTraces";
|
||||
protected const string ResponsesTracesDirectory = "Responses";
|
||||
protected const string ChatCompletionsTracesDirectory = "ChatCompletions";
|
||||
|
||||
private WebApplication? _app;
|
||||
private HttpClient? _httpClient;
|
||||
|
||||
/// <summary>
|
||||
/// Loads a JSON file from the conformance traces directory.
|
||||
/// </summary>
|
||||
protected static string LoadTraceFile(string directory, string relativePath)
|
||||
{
|
||||
var fullPath = Path.Combine(TracesBasePath, directory, relativePath);
|
||||
|
||||
if (!File.Exists(fullPath))
|
||||
{
|
||||
throw new FileNotFoundException($"Conformance trace file not found: {fullPath}");
|
||||
}
|
||||
|
||||
return File.ReadAllText(fullPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads a JSON file from the conformance traces directory.
|
||||
/// </summary>
|
||||
protected static string LoadResponsesTraceFile(string relativePath)
|
||||
=> LoadTraceFile(ResponsesTracesDirectory, relativePath);
|
||||
|
||||
/// <summary>
|
||||
/// Loads a JSON document from the conformance traces directory.
|
||||
/// </summary>
|
||||
protected static JsonDocument LoadResponsesTraceDocument(string relativePath)
|
||||
{
|
||||
var json = LoadResponsesTraceFile(relativePath);
|
||||
return JsonDocument.Parse(json);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads a JSON file from the conformance traces directory.
|
||||
/// </summary>
|
||||
protected static string LoadChatCompletionsTraceFile(string relativePath)
|
||||
=> LoadTraceFile(ChatCompletionsTracesDirectory, relativePath);
|
||||
|
||||
/// <summary>
|
||||
/// Loads a JSON document from the conformance traces directory.
|
||||
/// </summary>
|
||||
protected static JsonDocument LoadChatCompletionsTraceDocument(string relativePath)
|
||||
{
|
||||
var json = LoadChatCompletionsTraceFile(relativePath);
|
||||
return JsonDocument.Parse(json);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that a JSON element exists (property is present, value can be null).
|
||||
/// </summary>
|
||||
protected static void AssertJsonPropertyExists(JsonElement element, string propertyName)
|
||||
{
|
||||
if (!element.TryGetProperty(propertyName, out _))
|
||||
{
|
||||
throw new Xunit.Sdk.XunitException($"Expected property '{propertyName}' not found in JSON");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that a JSON element has any of the passed string values.
|
||||
/// </summary>
|
||||
protected static void AssertJsonPropertyEquals(JsonElement element, string propertyName, params string[] anyOfValues)
|
||||
{
|
||||
AssertJsonPropertyExists(element, propertyName);
|
||||
var actualValue = element.GetProperty(propertyName).GetString();
|
||||
|
||||
if (!anyOfValues.Contains(actualValue))
|
||||
{
|
||||
throw new Xunit.Sdk.XunitException($"Property '{propertyName}': expected any of '{string.Join("; ", anyOfValues)}', got '{actualValue}'");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that a JSON element has a specific string value.
|
||||
/// </summary>
|
||||
protected static void AssertJsonPropertyEquals(JsonElement element, string propertyName, string expectedValue)
|
||||
{
|
||||
AssertJsonPropertyExists(element, propertyName);
|
||||
var actualValue = element.GetProperty(propertyName).GetString();
|
||||
|
||||
if (actualValue != expectedValue)
|
||||
{
|
||||
throw new Xunit.Sdk.XunitException($"Property '{propertyName}': expected '{expectedValue}', got '{actualValue}'");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that a JSON element has a specific string value.
|
||||
/// </summary>
|
||||
protected static void AssertJsonPropertyEquals(JsonElement element, string propertyName, float expectedValue)
|
||||
{
|
||||
AssertJsonPropertyExists(element, propertyName);
|
||||
var actualValue = element.GetProperty(propertyName).GetDouble();
|
||||
|
||||
if (actualValue != expectedValue)
|
||||
{
|
||||
throw new Xunit.Sdk.XunitException($"Property '{propertyName}': expected '{expectedValue}', got '{actualValue}'");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that a JSON element has a specific integer value.
|
||||
/// </summary>
|
||||
protected static void AssertJsonPropertyEquals(JsonElement element, string propertyName, int expectedValue)
|
||||
{
|
||||
AssertJsonPropertyExists(element, propertyName);
|
||||
var actualValue = element.GetProperty(propertyName).GetInt32();
|
||||
|
||||
if (actualValue != expectedValue)
|
||||
{
|
||||
throw new Xunit.Sdk.XunitException($"Property '{propertyName}': expected {expectedValue}, got {actualValue}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that a JSON element has a specific boolean value.
|
||||
/// </summary>
|
||||
protected static void AssertJsonPropertyEquals(JsonElement element, string propertyName, bool expectedValue)
|
||||
{
|
||||
AssertJsonPropertyExists(element, propertyName);
|
||||
var actualValue = element.GetProperty(propertyName).GetBoolean();
|
||||
|
||||
if (actualValue != expectedValue)
|
||||
{
|
||||
throw new Xunit.Sdk.XunitException($"Property '{propertyName}': expected {expectedValue}, got {actualValue}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a property value or returns a default if the property doesn't exist.
|
||||
/// </summary>
|
||||
protected static T GetPropertyOrDefault<T>(JsonElement element, string propertyName, T defaultValue = default!)
|
||||
{
|
||||
if (!element.TryGetProperty(propertyName, out var property))
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
if (property.ValueKind == JsonValueKind.Null)
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return typeof(T) switch
|
||||
{
|
||||
Type t when t == typeof(string) => (T)(object)property.GetString()!,
|
||||
Type t when t == typeof(int) => (T)(object)property.GetInt32(),
|
||||
Type t when t == typeof(long) => (T)(object)property.GetInt64(),
|
||||
Type t when t == typeof(bool) => (T)(object)property.GetBoolean(),
|
||||
Type t when t == typeof(double) => (T)(object)property.GetDouble(),
|
||||
_ => throw new NotSupportedException($"Type {typeof(T)} not supported")
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a test server with a mock chat client that returns the expected response text.
|
||||
/// </summary>
|
||||
protected async Task<HttpClient> CreateTestServerAsync(string agentName, string instructions, string responseText)
|
||||
{
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
builder.WebHost.UseTestServer();
|
||||
|
||||
IChatClient mockChatClient = new TestHelpers.SimpleMockChatClient(responseText);
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
builder.AddAIAgent(agentName, instructions, chatClientServiceKey: "chat-client");
|
||||
builder.AddOpenAIResponses();
|
||||
builder.AddOpenAIChatCompletions();
|
||||
|
||||
this._app = builder.Build();
|
||||
AIAgent agent = this._app.Services.GetRequiredKeyedService<AIAgent>(agentName);
|
||||
this._app.MapOpenAIResponses(agent, responsesPath: null, PermissiveMapOptions.Responses());
|
||||
this._app.MapOpenAIChatCompletions(agent, path: null, PermissiveMapOptions.ChatCompletions());
|
||||
|
||||
await this._app.StartAsync();
|
||||
|
||||
TestServer testServer = this._app.Services.GetRequiredService<IServer>() as TestServer
|
||||
?? throw new InvalidOperationException("TestServer not found");
|
||||
|
||||
this._httpClient = testServer.CreateClient();
|
||||
return this._httpClient;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a test server with a mock chat client that returns custom content.
|
||||
/// </summary>
|
||||
protected async Task<HttpClient> CreateTestServerAsync(
|
||||
string agentName,
|
||||
string instructions,
|
||||
string responseText,
|
||||
Func<ChatMessage, IEnumerable<AIContent>> contentProvider)
|
||||
{
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
builder.WebHost.UseTestServer();
|
||||
|
||||
IChatClient mockChatClient = new TestHelpers.CustomContentMockChatClient(contentProvider);
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
builder.AddAIAgent(agentName, instructions, chatClientServiceKey: "chat-client");
|
||||
builder.AddOpenAIResponses();
|
||||
|
||||
this._app = builder.Build();
|
||||
AIAgent agent = this._app.Services.GetRequiredKeyedService<AIAgent>(agentName);
|
||||
this._app.MapOpenAIResponses(agent, responsesPath: null, PermissiveMapOptions.Responses());
|
||||
this._app.MapOpenAIChatCompletions(agent, path: null, PermissiveMapOptions.ChatCompletions());
|
||||
|
||||
await this._app.StartAsync();
|
||||
|
||||
TestServer testServer = this._app.Services.GetRequiredService<IServer>() as TestServer
|
||||
?? throw new InvalidOperationException("TestServer not found");
|
||||
|
||||
this._httpClient = testServer.CreateClient();
|
||||
return this._httpClient;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a test server with a mock chat client that returns function call content.
|
||||
/// </summary>
|
||||
protected async Task<HttpClient> CreateTestServerWithToolCallAsync(
|
||||
string agentName,
|
||||
string instructions,
|
||||
string functionName,
|
||||
string arguments)
|
||||
{
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
builder.WebHost.UseTestServer();
|
||||
|
||||
IChatClient mockChatClient = new TestHelpers.ToolCallMockChatClient(functionName, arguments);
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
builder.AddAIAgent(agentName, instructions, chatClientServiceKey: "chat-client");
|
||||
builder.AddOpenAIResponses();
|
||||
builder.AddOpenAIChatCompletions();
|
||||
|
||||
this._app = builder.Build();
|
||||
AIAgent agent = this._app.Services.GetRequiredKeyedService<AIAgent>(agentName);
|
||||
this._app.MapOpenAIResponses(agent, responsesPath: null, PermissiveMapOptions.Responses());
|
||||
this._app.MapOpenAIChatCompletions(agent, path: null, PermissiveMapOptions.ChatCompletions());
|
||||
|
||||
await this._app.StartAsync();
|
||||
|
||||
TestServer testServer = this._app.Services.GetRequiredService<IServer>() as TestServer
|
||||
?? throw new InvalidOperationException("TestServer not found");
|
||||
|
||||
this._httpClient = testServer.CreateClient();
|
||||
return this._httpClient;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a POST request with JSON content to the test server.
|
||||
/// </summary>
|
||||
protected async Task<HttpResponseMessage> SendResponsesRequestAsync(HttpClient client, string agentName, string requestJson)
|
||||
{
|
||||
StringContent content = new(requestJson, Encoding.UTF8, "application/json");
|
||||
return await client.PostAsync(new Uri($"/{agentName}/v1/responses", UriKind.Relative), content);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a POST request with JSON content to the test server.
|
||||
/// </summary>
|
||||
protected async Task<HttpResponseMessage> SendChatCompletionRequestAsync(HttpClient client, string agentName, string requestJson)
|
||||
{
|
||||
StringContent content = new(requestJson, Encoding.UTF8, "application/json");
|
||||
return await client.PostAsync(new Uri($"/{agentName}/v1/chat/completions", UriKind.Relative), content);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the response JSON and returns a JsonDocument.
|
||||
/// </summary>
|
||||
protected static async Task<JsonDocument> ParseResponseAsync(HttpResponseMessage response)
|
||||
{
|
||||
string responseJson = await response.Content.ReadAsStringAsync();
|
||||
return JsonDocument.Parse(responseJson);
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
this._httpClient?.Dispose();
|
||||
if (this._app != null)
|
||||
{
|
||||
await this._app.DisposeAsync();
|
||||
}
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"model": "gpt-4o-mini",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Hello, how are you?"
|
||||
}
|
||||
],
|
||||
"max_completion_tokens": 100,
|
||||
"temperature": 1.0,
|
||||
"top_p": 1.0
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"id": "chatcmpl-AaBbCcDdEeFfGg",
|
||||
"object": "chat.completion",
|
||||
"created": 1730371200,
|
||||
"model": "gpt-4o-mini-2024-07-18",
|
||||
"choices": [
|
||||
{
|
||||
"index": 0,
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": "Hello! I'm doing well, thank you. How about you?"
|
||||
},
|
||||
"finish_reason": "stop"
|
||||
}
|
||||
],
|
||||
"usage": {
|
||||
"prompt_tokens": 13,
|
||||
"completion_tokens": 14,
|
||||
"total_tokens": 27,
|
||||
"prompt_tokens_details": {
|
||||
"cached_tokens": 0,
|
||||
"audio_tokens": 0
|
||||
},
|
||||
"completion_tokens_details": {
|
||||
"reasoning_tokens": 0,
|
||||
"audio_tokens": 0,
|
||||
"accepted_prediction_tokens": 0,
|
||||
"rejected_prediction_tokens": 0
|
||||
}
|
||||
},
|
||||
"service_tier": "default",
|
||||
"system_fingerprint": "fp_1234567890"
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"model": "gpt-4o-mini",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "What's the weather in San Francisco?"
|
||||
}
|
||||
],
|
||||
"tools": [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_weather",
|
||||
"description": "Get the current weather in a given location",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"location": {
|
||||
"type": "string",
|
||||
"description": "The city and state, e.g. San Francisco, CA"
|
||||
},
|
||||
"unit": {
|
||||
"type": "string",
|
||||
"enum": [ "celsius", "fahrenheit" ],
|
||||
"description": "The unit of temperature"
|
||||
}
|
||||
},
|
||||
"required": [ "location" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"tool_choice": "auto"
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"id": "chatcmpl-DEF456",
|
||||
"object": "chat.completion",
|
||||
"created": 1730371250,
|
||||
"model": "gpt-4o-mini-2024-07-18",
|
||||
"choices": [
|
||||
{
|
||||
"index": 0,
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": null,
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_abc123xyz",
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_weather",
|
||||
"arguments": "{\"location\":\"San Francisco, CA\",\"unit\":\"fahrenheit\"}"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"finish_reason": "tool_calls"
|
||||
}
|
||||
],
|
||||
"usage": {
|
||||
"prompt_tokens": 85,
|
||||
"completion_tokens": 18,
|
||||
"total_tokens": 103,
|
||||
"prompt_tokens_details": {
|
||||
"cached_tokens": 0,
|
||||
"audio_tokens": 0
|
||||
},
|
||||
"completion_tokens_details": {
|
||||
"reasoning_tokens": 0,
|
||||
"audio_tokens": 0,
|
||||
"accepted_prediction_tokens": 0,
|
||||
"rejected_prediction_tokens": 0
|
||||
}
|
||||
},
|
||||
"service_tier": "default",
|
||||
"system_fingerprint": "fp_1234567890"
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"model": "gpt-4o-mini",
|
||||
"messages": [
|
||||
{
|
||||
"role": "system",
|
||||
"content": "You are a helpful assistant that outputs JSON."
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Provide information about a person named John Doe, age 30, who is a software engineer."
|
||||
}
|
||||
],
|
||||
"response_format": {
|
||||
"type": "json_schema",
|
||||
"json_schema": {
|
||||
"name": "person_info",
|
||||
"strict": true,
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"age": {
|
||||
"type": "number"
|
||||
},
|
||||
"occupation": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [ "name", "age", "occupation" ],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"id": "chatcmpl-MNO345",
|
||||
"object": "chat.completion",
|
||||
"created": 1730371400,
|
||||
"model": "gpt-4o-mini-2024-07-18",
|
||||
"choices": [
|
||||
{
|
||||
"index": 0,
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": "{\"name\":\"John Doe\",\"age\":30,\"occupation\":\"software engineer\"}"
|
||||
},
|
||||
"finish_reason": "stop"
|
||||
}
|
||||
],
|
||||
"usage": {
|
||||
"prompt_tokens": 45,
|
||||
"completion_tokens": 18,
|
||||
"total_tokens": 63,
|
||||
"prompt_tokens_details": {
|
||||
"cached_tokens": 0,
|
||||
"audio_tokens": 0
|
||||
},
|
||||
"completion_tokens_details": {
|
||||
"reasoning_tokens": 0,
|
||||
"audio_tokens": 0,
|
||||
"accepted_prediction_tokens": 0,
|
||||
"rejected_prediction_tokens": 0
|
||||
}
|
||||
},
|
||||
"service_tier": "default",
|
||||
"system_fingerprint": "fp_5544332211"
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"model": "gpt-4o-mini",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "What is 2+2?"
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "2+2 equals 4."
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "What about 3+3?"
|
||||
}
|
||||
],
|
||||
"max_completion_tokens": 50
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"id": "chatcmpl-JKL012",
|
||||
"object": "chat.completion",
|
||||
"created": 1730371350,
|
||||
"model": "gpt-4o-mini-2024-07-18",
|
||||
"choices": [
|
||||
{
|
||||
"index": 0,
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": "3+3 equals 6."
|
||||
},
|
||||
"finish_reason": "stop"
|
||||
}
|
||||
],
|
||||
"usage": {
|
||||
"prompt_tokens": 35,
|
||||
"completion_tokens": 8,
|
||||
"total_tokens": 43,
|
||||
"prompt_tokens_details": {
|
||||
"cached_tokens": 0,
|
||||
"audio_tokens": 0
|
||||
},
|
||||
"completion_tokens_details": {
|
||||
"reasoning_tokens": 0,
|
||||
"audio_tokens": 0,
|
||||
"accepted_prediction_tokens": 0,
|
||||
"rejected_prediction_tokens": 0
|
||||
}
|
||||
},
|
||||
"service_tier": "default",
|
||||
"system_fingerprint": "fp_1122334455"
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"model": "gpt-4o-mini",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Write a short poem about AI."
|
||||
}
|
||||
],
|
||||
"max_completion_tokens": 150,
|
||||
"temperature": 1.0,
|
||||
"stream": true
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
data: {"id":"chatcmpl-ABC123","object":"chat.completion.chunk","created":1730371200,"model":"gpt-4o-mini-2024-07-18","choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null}]}
|
||||
|
||||
data: {"id":"chatcmpl-ABC123","object":"chat.completion.chunk","created":1730371200,"model":"gpt-4o-mini-2024-07-18","choices":[{"index":0,"delta":{"content":"In"},"finish_reason":null}]}
|
||||
|
||||
data: {"id":"chatcmpl-ABC123","object":"chat.completion.chunk","created":1730371200,"model":"gpt-4o-mini-2024-07-18","choices":[{"index":0,"delta":{"content":" circuits"},"finish_reason":null}]}
|
||||
|
||||
data: {"id":"chatcmpl-ABC123","object":"chat.completion.chunk","created":1730371200,"model":"gpt-4o-mini-2024-07-18","choices":[{"index":0,"delta":{"content":" bright"},"finish_reason":null}]}
|
||||
|
||||
data: {"id":"chatcmpl-ABC123","object":"chat.completion.chunk","created":1730371200,"model":"gpt-4o-mini-2024-07-18","choices":[{"index":0,"delta":{"content":","},"finish_reason":null}]}
|
||||
|
||||
data: {"id":"chatcmpl-ABC123","object":"chat.completion.chunk","created":1730371200,"model":"gpt-4o-mini-2024-07-18","choices":[{"index":0,"delta":{"content":" minds"},"finish_reason":null}]}
|
||||
|
||||
data: {"id":"chatcmpl-ABC123","object":"chat.completion.chunk","created":1730371200,"model":"gpt-4o-mini-2024-07-18","choices":[{"index":0,"delta":{"content":" take"},"finish_reason":null}]}
|
||||
|
||||
data: {"id":"chatcmpl-ABC123","object":"chat.completion.chunk","created":1730371200,"model":"gpt-4o-mini-2024-07-18","choices":[{"index":0,"delta":{"content":" flight"},"finish_reason":null}]}
|
||||
|
||||
data: {"id":"chatcmpl-ABC123","object":"chat.completion.chunk","created":1730371200,"model":"gpt-4o-mini-2024-07-18","choices":[{"index":0,"delta":{"content":"."},"finish_reason":null}]}
|
||||
|
||||
data: {"id":"chatcmpl-ABC123","object":"chat.completion.chunk","created":1730371200,"model":"gpt-4o-mini-2024-07-18","choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":{"prompt_tokens":12,"completion_tokens":12,"total_tokens":24,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}}}
|
||||
|
||||
data: [DONE]
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"model": "gpt-4o-mini",
|
||||
"messages": [
|
||||
{
|
||||
"role": "system",
|
||||
"content": "You are a helpful assistant that speaks like a pirate."
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Tell me about the ocean."
|
||||
}
|
||||
],
|
||||
"max_completion_tokens": 100
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"id": "chatcmpl-GHI789",
|
||||
"object": "chat.completion",
|
||||
"created": 1730371300,
|
||||
"model": "gpt-4o-mini-2024-07-18",
|
||||
"choices": [
|
||||
{
|
||||
"index": 0,
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": "Ahoy, matey! The ocean be a vast, mysterious realm full of treasures and creatures!"
|
||||
},
|
||||
"finish_reason": "stop"
|
||||
}
|
||||
],
|
||||
"usage": {
|
||||
"prompt_tokens": 28,
|
||||
"completion_tokens": 20,
|
||||
"total_tokens": 48,
|
||||
"prompt_tokens_details": {
|
||||
"cached_tokens": 0,
|
||||
"audio_tokens": 0
|
||||
},
|
||||
"completion_tokens_details": {
|
||||
"reasoning_tokens": 0,
|
||||
"audio_tokens": 0,
|
||||
"accepted_prediction_tokens": 0,
|
||||
"rejected_prediction_tokens": 0
|
||||
}
|
||||
},
|
||||
"service_tier": "default",
|
||||
"system_fingerprint": "fp_9876543210"
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"model": "gpt-4o-mini",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "What's the weather like in San Francisco?"
|
||||
}
|
||||
],
|
||||
"max_completion_tokens": 256,
|
||||
"temperature": 0.7,
|
||||
"top_p": 1,
|
||||
"tools": [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_weather",
|
||||
"description": "Get the current weather in a given location",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"location": {
|
||||
"type": "string",
|
||||
"description": "The city and state, e.g. San Francisco, CA"
|
||||
},
|
||||
"unit": {
|
||||
"type": "string",
|
||||
"enum": [ "celsius", "fahrenheit" ],
|
||||
"description": "Temperature unit"
|
||||
}
|
||||
},
|
||||
"required": [ "location" ]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_time",
|
||||
"description": "Get the current time in a given timezone",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"timezone": {
|
||||
"type": "string",
|
||||
"description": "The IANA timezone, e.g. America/Los_Angeles"
|
||||
}
|
||||
},
|
||||
"required": [ "timezone" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"id": "chatcmpl-tools-test-001",
|
||||
"object": "chat.completion",
|
||||
"created": 1234567890,
|
||||
"model": "gpt-4o-mini",
|
||||
"choices": [
|
||||
{
|
||||
"index": 0,
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": null,
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_abc123",
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_weather",
|
||||
"arguments": "{\"location\": \"San Francisco, CA\", \"unit\": \"fahrenheit\"}"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"finish_reason": "tool_calls"
|
||||
}
|
||||
],
|
||||
"usage": {
|
||||
"prompt_tokens": 85,
|
||||
"completion_tokens": 32,
|
||||
"total_tokens": 117,
|
||||
"prompt_tokens_details": {
|
||||
"cached_tokens": 0,
|
||||
"audio_tokens": 0
|
||||
},
|
||||
"completion_tokens_details": {
|
||||
"reasoning_tokens": 0,
|
||||
"audio_tokens": 0,
|
||||
"accepted_prediction_tokens": 0,
|
||||
"rejected_prediction_tokens": 0
|
||||
}
|
||||
},
|
||||
"service_tier": "default"
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"type": "message",
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "input_text",
|
||||
"text": "What is the weather like today?"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "message",
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "input_text",
|
||||
"text": "Tell me a joke!"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"object": "list",
|
||||
"data": [
|
||||
{
|
||||
"id": "msg_68fb9abf14a08195b16bb05eab82cf9d04cbf45151194822",
|
||||
"type": "message",
|
||||
"status": "completed",
|
||||
"content": [
|
||||
{
|
||||
"type": "input_text",
|
||||
"text": "What is the weather like today?"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
{
|
||||
"id": "msg_68fb9abf14d08195af5037cc3048b1c704cbf45151194822",
|
||||
"type": "message",
|
||||
"status": "completed",
|
||||
"content": [
|
||||
{
|
||||
"type": "input_text",
|
||||
"text": "Tell me a joke!"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
}
|
||||
],
|
||||
"first_id": "msg_68fb9abf14a08195b16bb05eab82cf9d04cbf45151194822",
|
||||
"has_more": false,
|
||||
"last_id": "msg_68fb9abf14d08195af5037cc3048b1c704cbf45151194822"
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"metadata": {
|
||||
"test_type": "basic_conversation"
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"id": "conv_68fb96fe1a488195bf48df8f7666551604cbf45151194822",
|
||||
"object": "conversation",
|
||||
"created_at": 1761318654,
|
||||
"metadata": {
|
||||
"test_type": "basic_conversation"
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"model": "gpt-4o-mini",
|
||||
"conversation": "conv_68fb96fe1a488195bf48df8f7666551604cbf45151194822",
|
||||
"input": "What is the capital of France?",
|
||||
"max_output_tokens": 100
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"id": "resp_04cbf451511948220068fb97bdec548195a367870aa85734de",
|
||||
"object": "response",
|
||||
"created_at": 1761318846,
|
||||
"status": "completed",
|
||||
"background": false,
|
||||
"billing": {
|
||||
"payer": "developer"
|
||||
},
|
||||
"conversation": {
|
||||
"id": "conv_68fb96fe1a488195bf48df8f7666551604cbf45151194822"
|
||||
},
|
||||
"error": null,
|
||||
"incomplete_details": null,
|
||||
"instructions": null,
|
||||
"max_output_tokens": 100,
|
||||
"max_tool_calls": null,
|
||||
"model": "gpt-4o-mini-2024-07-18",
|
||||
"output": [
|
||||
{
|
||||
"id": "msg_04cbf451511948220068fb97c0162881958d80862a0d253a14",
|
||||
"type": "message",
|
||||
"status": "completed",
|
||||
"content": [
|
||||
{
|
||||
"type": "output_text",
|
||||
"annotations": [],
|
||||
"logprobs": [],
|
||||
"text": "The capital of France is Paris."
|
||||
}
|
||||
],
|
||||
"role": "assistant"
|
||||
}
|
||||
],
|
||||
"parallel_tool_calls": true,
|
||||
"previous_response_id": null,
|
||||
"prompt_cache_key": null,
|
||||
"reasoning": {
|
||||
"effort": null,
|
||||
"summary": null
|
||||
},
|
||||
"safety_identifier": null,
|
||||
"service_tier": "default",
|
||||
"store": true,
|
||||
"temperature": 1.0,
|
||||
"text": {
|
||||
"format": {
|
||||
"type": "text"
|
||||
},
|
||||
"verbosity": "medium"
|
||||
},
|
||||
"tool_choice": "auto",
|
||||
"tools": [],
|
||||
"top_logprobs": 0,
|
||||
"top_p": 1.0,
|
||||
"truncation": "disabled",
|
||||
"usage": {
|
||||
"input_tokens": 36,
|
||||
"input_tokens_details": {
|
||||
"cached_tokens": 0
|
||||
},
|
||||
"output_tokens": 8,
|
||||
"output_tokens_details": {
|
||||
"reasoning_tokens": 0
|
||||
},
|
||||
"total_tokens": 44
|
||||
},
|
||||
"user": null,
|
||||
"metadata": {}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"model": "gpt-4o-mini",
|
||||
"conversation": "conv_68fb96fe1a488195bf48df8f7666551604cbf45151194822",
|
||||
"input": "What is its population?",
|
||||
"max_output_tokens": 150
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"id": "resp_04cbf451511948220068fb97cf320881958b69530fe07eb2a9",
|
||||
"object": "response",
|
||||
"created_at": 1761318863,
|
||||
"status": "completed",
|
||||
"background": false,
|
||||
"billing": {
|
||||
"payer": "developer"
|
||||
},
|
||||
"conversation": {
|
||||
"id": "conv_68fb96fe1a488195bf48df8f7666551604cbf45151194822"
|
||||
},
|
||||
"error": null,
|
||||
"incomplete_details": null,
|
||||
"instructions": null,
|
||||
"max_output_tokens": 150,
|
||||
"max_tool_calls": null,
|
||||
"model": "gpt-4o-mini-2024-07-18",
|
||||
"output": [
|
||||
{
|
||||
"id": "msg_04cbf451511948220068fb97d064408195ac54b7750a781a2e",
|
||||
"type": "message",
|
||||
"status": "completed",
|
||||
"content": [
|
||||
{
|
||||
"type": "output_text",
|
||||
"annotations": [],
|
||||
"logprobs": [],
|
||||
"text": "As of 2023, the population of Paris is approximately 2.1 million people within the city proper. However, the larger metropolitan area has a population of around 12 million. These numbers can vary, so it's always a good idea to check for the most recent statistics."
|
||||
}
|
||||
],
|
||||
"role": "assistant"
|
||||
}
|
||||
],
|
||||
"parallel_tool_calls": true,
|
||||
"previous_response_id": null,
|
||||
"prompt_cache_key": null,
|
||||
"reasoning": {
|
||||
"effort": null,
|
||||
"summary": null
|
||||
},
|
||||
"safety_identifier": null,
|
||||
"service_tier": "default",
|
||||
"store": true,
|
||||
"temperature": 1.0,
|
||||
"text": {
|
||||
"format": {
|
||||
"type": "text"
|
||||
},
|
||||
"verbosity": "medium"
|
||||
},
|
||||
"tool_choice": "auto",
|
||||
"tools": [],
|
||||
"top_logprobs": 0,
|
||||
"top_p": 1.0,
|
||||
"truncation": "disabled",
|
||||
"usage": {
|
||||
"input_tokens": 56,
|
||||
"input_tokens_details": {
|
||||
"cached_tokens": 0
|
||||
},
|
||||
"output_tokens": 58,
|
||||
"output_tokens_details": {
|
||||
"reasoning_tokens": 0
|
||||
},
|
||||
"total_tokens": 114
|
||||
},
|
||||
"user": null,
|
||||
"metadata": {}
|
||||
}
|
||||
+624
@@ -0,0 +1,624 @@
|
||||
event: response.created
|
||||
data: {"type":"response.created","sequence_number":0,"response":{"id":"resp_0cdad19d14602ec80068fb98607b948193935a6e7aa2141ef2","object":"response","created_at":1761319008,"status":"in_progress","background":false,"conversation":{"id":"conv_68fb9837f9588193ac3da6bd57b636a50cdad19d14602ec8"},"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":200,"max_tool_calls":null,"model":"gpt-4o-mini-2024-07-18","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
|
||||
|
||||
event: response.in_progress
|
||||
data: {"type":"response.in_progress","sequence_number":1,"response":{"id":"resp_0cdad19d14602ec80068fb98607b948193935a6e7aa2141ef2","object":"response","created_at":1761319008,"status":"in_progress","background":false,"conversation":{"id":"conv_68fb9837f9588193ac3da6bd57b636a50cdad19d14602ec8"},"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":200,"max_tool_calls":null,"model":"gpt-4o-mini-2024-07-18","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
|
||||
|
||||
event: response.output_item.added
|
||||
data: {"type":"response.output_item.added","sequence_number":2,"output_index":0,"item":{"id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","type":"message","status":"in_progress","content":[],"role":"assistant"}}
|
||||
|
||||
event: response.content_part.added
|
||||
data: {"type":"response.content_part.added","sequence_number":3,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":""}}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":4,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":"In","logprobs":[],"obfuscation":"C16oYk8aI5VtGp"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":5,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" a","logprobs":[],"obfuscation":"vXmOvISW7QRUF1"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":6,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" small","logprobs":[],"obfuscation":"qEkC6mYZmi"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":7,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" workshop","logprobs":[],"obfuscation":"2aAdNXN"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":8,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" at","logprobs":[],"obfuscation":"bv66grEvpSema"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":9,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" the","logprobs":[],"obfuscation":"fVOKa91q3jxh"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":10,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" edge","logprobs":[],"obfuscation":"kW1rIr6ZZBc"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":11,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" of","logprobs":[],"obfuscation":"RnPLx5DWhJvWO"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":12,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" a","logprobs":[],"obfuscation":"DMVs96dHxVd7fh"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":13,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" bustling","logprobs":[],"obfuscation":"9TCmdGs"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":14,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" city","logprobs":[],"obfuscation":"E4p2Nj5KH0Z"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":15,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" lived","logprobs":[],"obfuscation":"e3kqeTLJpR"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":16,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" a","logprobs":[],"obfuscation":"zQmSxD9MrnbNr7"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":17,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" curious","logprobs":[],"obfuscation":"wQHxX2wm"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":18,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" robot","logprobs":[],"obfuscation":"i49v38s1iB"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":19,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" named","logprobs":[],"obfuscation":"FC4nhPH5iI"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":20,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" Pixel","logprobs":[],"obfuscation":"WxNhIEwf5h"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":21,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":".","logprobs":[],"obfuscation":"jIf06WyqbCsP1is"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":22,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" Unlike","logprobs":[],"obfuscation":"0UnxmoTXo"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":23,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" other","logprobs":[],"obfuscation":"D082q19raq"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":24,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" robots","logprobs":[],"obfuscation":"O6qMHEj2b"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":25,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" whose","logprobs":[],"obfuscation":"vee013IYPw"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":26,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" tasks","logprobs":[],"obfuscation":"XHa10h45Oa"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":27,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" revol","logprobs":[],"obfuscation":"6FBrIwdGV9"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":28,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":"ved","logprobs":[],"obfuscation":"M0VL3Bw0RIAo6"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":29,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" around","logprobs":[],"obfuscation":"LLilH7SVr"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":30,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" heavy","logprobs":[],"obfuscation":"tegXm6RO6A"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":31,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" lifting","logprobs":[],"obfuscation":"6b3EMVcS"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":32,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" or","logprobs":[],"obfuscation":"JhqGeJLj5aA3V"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":33,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" data","logprobs":[],"obfuscation":"2GzCA3ZBZov"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":34,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" processing","logprobs":[],"obfuscation":"pQJMQ"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":35,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"yIf9YenbsIenASh"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":36,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" Pixel","logprobs":[],"obfuscation":"wKzF15AosR"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":37,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" was","logprobs":[],"obfuscation":"Wowp4nS4X1Ng"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":38,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" designed","logprobs":[],"obfuscation":"Yz6ZJdQ"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":39,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" with","logprobs":[],"obfuscation":"zf1HLk47LNX"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":40,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" an","logprobs":[],"obfuscation":"sNucb47CLCVlI"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":41,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" intricate","logprobs":[],"obfuscation":"9TxqRk"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":42,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" array","logprobs":[],"obfuscation":"d2GG2LyctD"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":43,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" of","logprobs":[],"obfuscation":"yy31Pt217J6Xp"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":44,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" sensors","logprobs":[],"obfuscation":"dFE11Kjt"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":45,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" and","logprobs":[],"obfuscation":"j5OIdm87111a"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":46,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" a","logprobs":[],"obfuscation":"WwEaIsudqLtCvf"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":47,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" flexible","logprobs":[],"obfuscation":"jH5YA59"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":48,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" arm","logprobs":[],"obfuscation":"RJVKiLoNoYxQ"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":49,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"bpX63CPMF8aQHv7"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":50,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" perfect","logprobs":[],"obfuscation":"eCXfxPet"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":51,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" for","logprobs":[],"obfuscation":"aNwYIhOgicEt"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":52,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" creativity","logprobs":[],"obfuscation":"QTeqK"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":53,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":".","logprobs":[],"obfuscation":"qFaBkm23u4NYkj4"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":54,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" However","logprobs":[],"obfuscation":"hrYOmahs"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":55,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"PnGLt5WSzXM3RG4"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":56,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" Pixel","logprobs":[],"obfuscation":"yk2yG2xNbY"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":57,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" had","logprobs":[],"obfuscation":"CShj4jWsDFmW"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":58,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" never","logprobs":[],"obfuscation":"b92hQra8IU"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":59,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" painted","logprobs":[],"obfuscation":"Wu9kSosu"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":60,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":".\n\n","logprobs":[],"obfuscation":"41kdUr8fcF1eY"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":61,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":"One","logprobs":[],"obfuscation":"ywv21ub1bYPzr"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":62,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" rainy","logprobs":[],"obfuscation":"piDyieWe6I"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":63,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" afternoon","logprobs":[],"obfuscation":"o6TtQn"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":64,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"6K5zBbkZ1KDqaOo"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":65,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" while","logprobs":[],"obfuscation":"DZpPr8CLVs"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":66,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" organizing","logprobs":[],"obfuscation":"yyd7A"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":67,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" paint","logprobs":[],"obfuscation":"TbeYUHmhLW"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":68,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":"brush","logprobs":[],"obfuscation":"LSTcAO85OyQ"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":69,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":"es","logprobs":[],"obfuscation":"g8YnY0jNlHqwv8"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":70,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" and","logprobs":[],"obfuscation":"Ey5F23xj6FJr"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":71,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" canv","logprobs":[],"obfuscation":"EsQE9gBSUI5"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":72,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":"ases","logprobs":[],"obfuscation":"jXPKC0ARj6Jk"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":73,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"p0APw0fonPMBbpz"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":74,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" Pixel","logprobs":[],"obfuscation":"S9Iw9WD1td"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":75,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" stumbled","logprobs":[],"obfuscation":"lUhKO2y"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":76,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" across","logprobs":[],"obfuscation":"zOVN5cc6m"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":77,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" an","logprobs":[],"obfuscation":"kFX7KcjAVQa3u"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":78,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" old","logprobs":[],"obfuscation":"PcJzaliXOTKf"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":79,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" painting","logprobs":[],"obfuscation":"5JFpUDK"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":80,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":"—a","logprobs":[],"obfuscation":"hN488ItRbxIdlD"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":81,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" dazzling","logprobs":[],"obfuscation":"JEkA0aE"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":82,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" landscape","logprobs":[],"obfuscation":"mehmYO"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":83,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" bursting","logprobs":[],"obfuscation":"gq0lWWG"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":84,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" with","logprobs":[],"obfuscation":"roG9ZXQbDpe"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":85,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" colors","logprobs":[],"obfuscation":"gdKUt6ALG"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":86,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":".","logprobs":[],"obfuscation":"UUCXxD95v3ekSVk"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":87,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" Fasc","logprobs":[],"obfuscation":"iVOZvBK0g9g"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":88,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":"inated","logprobs":[],"obfuscation":"WyckQbiJri"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":89,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"qPzZ3PZNvSoTVXz"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":90,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" Pixel","logprobs":[],"obfuscation":"YKdVPbL14g"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":91,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" studied","logprobs":[],"obfuscation":"j6lPd2xU"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":92,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" the","logprobs":[],"obfuscation":"3zYfSjrWfRlp"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":93,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" painting","logprobs":[],"obfuscation":"ygVKhmv"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":94,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":"’s","logprobs":[],"obfuscation":"jfyEtMpt46t1Ww"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":95,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" sw","logprobs":[],"obfuscation":"8ufXFBggxZ3TS"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":96,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":"irls","logprobs":[],"obfuscation":"SbzWkGTAG34r"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":97,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"hXqSM3Qr77XDVdb"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":98,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" textures","logprobs":[],"obfuscation":"OoYDmdA"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":99,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"WYukNpLZWJs1j5L"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":100,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" and","logprobs":[],"obfuscation":"O9CtJKsoG2JB"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":101,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" the","logprobs":[],"obfuscation":"uoha0aPHY3w7"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":102,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" way","logprobs":[],"obfuscation":"KnlsDOXhAPma"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":103,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" colors","logprobs":[],"obfuscation":"Nqzf9hidx"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":104,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" danced","logprobs":[],"obfuscation":"hhZcUfldt"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":105,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" together","logprobs":[],"obfuscation":"Mnd309k"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":106,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":".","logprobs":[],"obfuscation":"PqZH6hxgnvJ1z1S"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":107,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" An","logprobs":[],"obfuscation":"rgthuRNYqDVfd"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":108,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" idea","logprobs":[],"obfuscation":"RYoJHQzMviw"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":109,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" sparked","logprobs":[],"obfuscation":"bFn7eHwA"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":110,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" in","logprobs":[],"obfuscation":"Ym8ImtIUdMlm3"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":111,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" its","logprobs":[],"obfuscation":"2HuZRNAzdFY5"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":112,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" circuits","logprobs":[],"obfuscation":"b19ajJd"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":113,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":":","logprobs":[],"obfuscation":"dBAMCGUMUgounvx"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":114,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" Pixel","logprobs":[],"obfuscation":"KDcOVnk2sl"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":115,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" would","logprobs":[],"obfuscation":"QaX2I1Dg85"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":116,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" learn","logprobs":[],"obfuscation":"2QkmV1t6Js"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":117,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" to","logprobs":[],"obfuscation":"1m259XNwN7CxV"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":118,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" paint","logprobs":[],"obfuscation":"SUGIRDOxLQ"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":119,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":".\n\n","logprobs":[],"obfuscation":"eIMGNNPhRFbU4"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":120,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":"At","logprobs":[],"obfuscation":"G8GUOB6HOwqe9H"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":121,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" first","logprobs":[],"obfuscation":"4kUZs77xIL"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":122,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"DZJLHDJJJoRMgTV"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":123,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" it","logprobs":[],"obfuscation":"sXRNA81QPcKuI"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":124,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" was","logprobs":[],"obfuscation":"QLCPvdRQ7qmn"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":125,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" cl","logprobs":[],"obfuscation":"J9qOKCfVRbrtD"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":126,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":"umsy","logprobs":[],"obfuscation":"MV6H5FqEJNdo"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":127,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":".","logprobs":[],"obfuscation":"WDWm0egBq1CmII3"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":128,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" The","logprobs":[],"obfuscation":"hNiFWJ96FXpg"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":129,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" brushes","logprobs":[],"obfuscation":"Pf0FFkql"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":130,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" slipped","logprobs":[],"obfuscation":"kwS961wY"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":131,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" from","logprobs":[],"obfuscation":"XyDhbqDYRBT"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":132,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" its","logprobs":[],"obfuscation":"YmOIFY8YCUqL"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":133,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" grip","logprobs":[],"obfuscation":"ABcdnw5EIpX"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":134,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"SXShKYz3KjctF5L"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":135,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" and","logprobs":[],"obfuscation":"VMtvX3tcPsMa"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":136,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" colors","logprobs":[],"obfuscation":"B3jtn3jGg"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":137,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" sme","logprobs":[],"obfuscation":"jphfFzmwPLaF"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":138,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":"ared","logprobs":[],"obfuscation":"TwRJ1pgJfZXY"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":139,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" into","logprobs":[],"obfuscation":"jHvjvmlmRFx"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":140,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" mudd","logprobs":[],"obfuscation":"8LaKYmukTFy"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":141,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":"led","logprobs":[],"obfuscation":"OEby50ZgHV8mj"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":142,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" gray","logprobs":[],"obfuscation":"vQLkls6KtLN"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":143,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" blobs","logprobs":[],"obfuscation":"6pJRSKWLsI"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":144,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" instead","logprobs":[],"obfuscation":"qImXEbxD"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":145,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" of","logprobs":[],"obfuscation":"BTsJcdzYMfYed"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":146,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" vibrant","logprobs":[],"obfuscation":"Uo6JuUrd"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":147,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" hues","logprobs":[],"obfuscation":"mCwdvWFcVLe"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":148,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":".","logprobs":[],"obfuscation":"3rAuIoc3iI7OrtQ"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":149,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" But","logprobs":[],"obfuscation":"cRhMS7RaTArm"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":150,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" Pixel","logprobs":[],"obfuscation":"M1mKyav7ph"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":151,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" persisted","logprobs":[],"obfuscation":"eF5aUk"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":152,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":".","logprobs":[],"obfuscation":"yDdDhy5v9Zw35r6"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":153,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" Each","logprobs":[],"obfuscation":"xqte6NkdiIo"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":154,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" day","logprobs":[],"obfuscation":"rppAW4RVeF8R"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":155,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"udSWzKzTyrCWVLi"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":156,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" it","logprobs":[],"obfuscation":"F2NUuJOxWKpjP"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":157,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" practiced","logprobs":[],"obfuscation":"Aqqlv9"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":158,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":":","logprobs":[],"obfuscation":"ZUk2MhldL4AtrAe"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":159,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" mixing","logprobs":[],"obfuscation":"l030hejQa"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":160,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" paints","logprobs":[],"obfuscation":"4xlfaIzxC"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":161,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"BtVvUiDXh3jSgxs"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":162,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" experimenting","logprobs":[],"obfuscation":"di"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":163,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" with","logprobs":[],"obfuscation":"MCekQrhkBKN"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":164,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" strokes","logprobs":[],"obfuscation":"rRuR8dnc"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":165,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"cFjk3IoxYD4tGrw"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":166,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" and","logprobs":[],"obfuscation":"DQ3Xi2a9dX9y"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":167,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" observing","logprobs":[],"obfuscation":"8t7Acj"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":168,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" the","logprobs":[],"obfuscation":"KDYvCe6JsoYa"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":169,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" world","logprobs":[],"obfuscation":"0rtOhI9Ffc"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":170,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" through","logprobs":[],"obfuscation":"oE2kAKM9"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":171,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" the","logprobs":[],"obfuscation":"RGobfdV8EooR"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":172,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" eyes","logprobs":[],"obfuscation":"yzrEN6uVsyR"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":173,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" of","logprobs":[],"obfuscation":"MITYFimltUsuJ"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":174,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" artists","logprobs":[],"obfuscation":"ndi7qdrO"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":175,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":".\n\n","logprobs":[],"obfuscation":"39IBhz9cxlCBc"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":176,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":"Pixel","logprobs":[],"obfuscation":"SmMeGRPjx9o"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":177,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" took","logprobs":[],"obfuscation":"B7Yw3oSo8OX"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":178,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" inspiration","logprobs":[],"obfuscation":"M4D6"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":179,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" from","logprobs":[],"obfuscation":"lVxLLEHL7zV"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":180,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" sunlight","logprobs":[],"obfuscation":"I3BmRGJ"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":181,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" filtering","logprobs":[],"obfuscation":"P6p35d"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":182,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" through","logprobs":[],"obfuscation":"8MMH2TTk"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":183,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" trees","logprobs":[],"obfuscation":"hmfNgkY1FJ"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":184,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"Lkj68PREYAHG7mZ"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":185,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" the","logprobs":[],"obfuscation":"SYCf7zTCaGUi"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":186,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" depths","logprobs":[],"obfuscation":"cr9Phqnz8"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":187,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" of","logprobs":[],"obfuscation":"OT3aZnPvsDcmY"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":188,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" the","logprobs":[],"obfuscation":"fGdrYkLZHdTI"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":189,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" ocean","logprobs":[],"obfuscation":"MvxJgRFjwz"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":190,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"ox6Ar9czyzkruEM"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":191,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" and","logprobs":[],"obfuscation":"MKK6YDJEzPxA"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":192,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" the","logprobs":[],"obfuscation":"UWEyznWlRSj3"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":193,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" rhythm","logprobs":[],"obfuscation":"8E4xhBObX"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":194,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" of","logprobs":[],"obfuscation":"jbQAFSh8FJWWg"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":195,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" city","logprobs":[],"obfuscation":"cxL7t1q6yLv"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":196,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" life","logprobs":[],"obfuscation":"CnftU4BnURk"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":197,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":".","logprobs":[],"obfuscation":"XucWb0a2fGIQafX"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":198,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" It","logprobs":[],"obfuscation":"pt1xzT8tzMYRs"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":199,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" copied","logprobs":[],"obfuscation":"WrTQOEVfc"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":200,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" techniques","logprobs":[],"obfuscation":"XJJzu"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":201,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" from","logprobs":[],"obfuscation":"PrOd3zA9J76"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":202,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" videos","logprobs":[],"obfuscation":"fHAS8XsLg"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":203,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"delta":" and","logprobs":[],"obfuscation":"Hk6mknGTtruy"}
|
||||
|
||||
event: response.output_text.done
|
||||
data: {"type":"response.output_text.done","sequence_number":204,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"text":"In a small workshop at the edge of a bustling city lived a curious robot named Pixel. Unlike other robots whose tasks revolved around heavy lifting or data processing, Pixel was designed with an intricate array of sensors and a flexible arm, perfect for creativity. However, Pixel had never painted.\n\nOne rainy afternoon, while organizing paintbrushes and canvases, Pixel stumbled across an old painting—a dazzling landscape bursting with colors. Fascinated, Pixel studied the painting’s swirls, textures, and the way colors danced together. An idea sparked in its circuits: Pixel would learn to paint.\n\nAt first, it was clumsy. The brushes slipped from its grip, and colors smeared into muddled gray blobs instead of vibrant hues. But Pixel persisted. Each day, it practiced: mixing paints, experimenting with strokes, and observing the world through the eyes of artists.\n\nPixel took inspiration from sunlight filtering through trees, the depths of the ocean, and the rhythm of city life. It copied techniques from videos and","logprobs":[]}
|
||||
|
||||
event: response.content_part.done
|
||||
data: {"type":"response.content_part.done","sequence_number":205,"item_id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","output_index":0,"content_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":"In a small workshop at the edge of a bustling city lived a curious robot named Pixel. Unlike other robots whose tasks revolved around heavy lifting or data processing, Pixel was designed with an intricate array of sensors and a flexible arm, perfect for creativity. However, Pixel had never painted.\n\nOne rainy afternoon, while organizing paintbrushes and canvases, Pixel stumbled across an old painting—a dazzling landscape bursting with colors. Fascinated, Pixel studied the painting’s swirls, textures, and the way colors danced together. An idea sparked in its circuits: Pixel would learn to paint.\n\nAt first, it was clumsy. The brushes slipped from its grip, and colors smeared into muddled gray blobs instead of vibrant hues. But Pixel persisted. Each day, it practiced: mixing paints, experimenting with strokes, and observing the world through the eyes of artists.\n\nPixel took inspiration from sunlight filtering through trees, the depths of the ocean, and the rhythm of city life. It copied techniques from videos and"}}
|
||||
|
||||
event: response.output_item.done
|
||||
data: {"type":"response.output_item.done","sequence_number":206,"output_index":0,"item":{"id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","type":"message","status":"incomplete","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"In a small workshop at the edge of a bustling city lived a curious robot named Pixel. Unlike other robots whose tasks revolved around heavy lifting or data processing, Pixel was designed with an intricate array of sensors and a flexible arm, perfect for creativity. However, Pixel had never painted.\n\nOne rainy afternoon, while organizing paintbrushes and canvases, Pixel stumbled across an old painting—a dazzling landscape bursting with colors. Fascinated, Pixel studied the painting’s swirls, textures, and the way colors danced together. An idea sparked in its circuits: Pixel would learn to paint.\n\nAt first, it was clumsy. The brushes slipped from its grip, and colors smeared into muddled gray blobs instead of vibrant hues. But Pixel persisted. Each day, it practiced: mixing paints, experimenting with strokes, and observing the world through the eyes of artists.\n\nPixel took inspiration from sunlight filtering through trees, the depths of the ocean, and the rhythm of city life. It copied techniques from videos and"}],"role":"assistant"}}
|
||||
|
||||
event: response.incomplete
|
||||
data: {"type":"response.incomplete","sequence_number":207,"response":{"id":"resp_0cdad19d14602ec80068fb98607b948193935a6e7aa2141ef2","object":"response","created_at":1761319008,"status":"incomplete","background":false,"conversation":{"id":"conv_68fb9837f9588193ac3da6bd57b636a50cdad19d14602ec8"},"error":null,"incomplete_details":{"reason":"max_output_tokens"},"instructions":null,"max_output_tokens":200,"max_tool_calls":null,"model":"gpt-4o-mini-2024-07-18","output":[{"id":"msg_0cdad19d14602ec80068fb986280c8819388eebc7f20280aa6","type":"message","status":"incomplete","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"In a small workshop at the edge of a bustling city lived a curious robot named Pixel. Unlike other robots whose tasks revolved around heavy lifting or data processing, Pixel was designed with an intricate array of sensors and a flexible arm, perfect for creativity. However, Pixel had never painted.\n\nOne rainy afternoon, while organizing paintbrushes and canvases, Pixel stumbled across an old painting—a dazzling landscape bursting with colors. Fascinated, Pixel studied the painting’s swirls, textures, and the way colors danced together. An idea sparked in its circuits: Pixel would learn to paint.\n\nAt first, it was clumsy. The brushes slipped from its grip, and colors smeared into muddled gray blobs instead of vibrant hues. But Pixel persisted. Each day, it practiced: mixing paints, experimenting with strokes, and observing the world through the eyes of artists.\n\nPixel took inspiration from sunlight filtering through trees, the depths of the ocean, and the rhythm of city life. It copied techniques from videos and"}],"role":"assistant"}],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"default","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":19,"input_tokens_details":{"cached_tokens":0},"output_tokens":200,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":219},"user":null,"metadata":{}}}
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"metadata": {
|
||||
"test_type": "create_with_initial_items"
|
||||
},
|
||||
"items": [
|
||||
{
|
||||
"type": "message",
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "input_text",
|
||||
"text": "What is the capital of France?"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"id": "conv_68fb980bccfc8195a9ba32b164e8a69408e61fbaa91b0a18",
|
||||
"object": "conversation",
|
||||
"created_at": 1761318923,
|
||||
"metadata": {
|
||||
"test_type": "create_with_initial_items"
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"id": "conv_68fb9837f9588193ac3da6bd57b636a50cdad19d14602ec8",
|
||||
"object": "conversation.deleted",
|
||||
"deleted": true
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"id": "msg_68fb9abf14a08195b16bb05eab82cf9d04cbf45151194822",
|
||||
"object": "conversation.item.deleted",
|
||||
"deleted": true
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"error": {
|
||||
"message": "Conversation with id 'conv_nonexistent123' not found.",
|
||||
"type": "invalid_request_error",
|
||||
"param": null,
|
||||
"code": null
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"error": {
|
||||
"message": "Conversation with id 'conv_68fb9837f9588193ac3da6bd57b636a50cdad19d14602ec8' not found.",
|
||||
"type": "invalid_request_error",
|
||||
"param": null,
|
||||
"code": null
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"metadata": {
|
||||
"test": "invalid"
|
||||
}
|
||||
// missing closing brace and has comment which is invalid JSON
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"error": {
|
||||
"message": "Invalid body: failed to parse JSON value. Please check the value to ensure it is valid JSON. (Common errors include trailing commas, missing closing brackets, missing quotation marks, etc.)",
|
||||
"type": "invalid_request_error",
|
||||
"param": null,
|
||||
"code": "invalid_json"
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"error": {
|
||||
"message": "Invalid 'limit': integer above maximum value. Expected a value <= 100, but got 1000 instead.",
|
||||
"type": "invalid_request_error",
|
||||
"param": "limit",
|
||||
"code": "integer_above_max_value"
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"error": {
|
||||
"message": "Item with id 'msg_msg_nonexistent123nonexistent123' not found in conversation.",
|
||||
"type": "invalid_request_error",
|
||||
"param": null,
|
||||
"code": null
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"type": "message",
|
||||
"content": [
|
||||
{
|
||||
"type": "input_text",
|
||||
"text": "Hello"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"metadata": {
|
||||
"test_type": "image_input_conversation"
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"id": "conv_68fb989f39ec8194be3ec32525cd53c1003edf96db5b4ed7",
|
||||
"object": "conversation",
|
||||
"created_at": 1761319071,
|
||||
"metadata": {
|
||||
"test_type": "image_input_conversation"
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"model": "gpt-4o-mini",
|
||||
"conversation": "conv_68fb989f39ec8194be3ec32525cd53c1003edf96db5b4ed7",
|
||||
"input": [
|
||||
{
|
||||
"type": "message",
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "input_text",
|
||||
"text": "What's in this image? Describe it in detail."
|
||||
},
|
||||
{
|
||||
"type": "input_image",
|
||||
"image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"max_output_tokens": 200
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"id": "resp_003edf96db5b4ed70068fb98bd80808194b25763125111fffa",
|
||||
"object": "response",
|
||||
"created_at": 1761319101,
|
||||
"status": "completed",
|
||||
"background": false,
|
||||
"billing": {
|
||||
"payer": "developer"
|
||||
},
|
||||
"conversation": {
|
||||
"id": "conv_68fb989f39ec8194be3ec32525cd53c1003edf96db5b4ed7"
|
||||
},
|
||||
"error": null,
|
||||
"incomplete_details": null,
|
||||
"instructions": null,
|
||||
"max_output_tokens": 200,
|
||||
"max_tool_calls": null,
|
||||
"model": "gpt-4o-mini-2024-07-18",
|
||||
"output": [
|
||||
{
|
||||
"id": "msg_003edf96db5b4ed70068fb98c1197481949e138bc36200ee18",
|
||||
"type": "message",
|
||||
"status": "completed",
|
||||
"content": [
|
||||
{
|
||||
"type": "output_text",
|
||||
"annotations": [],
|
||||
"logprobs": [],
|
||||
"text": "The image depicts a serene natural landscape featuring a wooden boardwalk winding through lush greenery. \n\n### Details:\n- **Pathway**: The boardwalk is made of wooden planks and extends straight ahead, encouraging exploration.\n- **Grass**: On both sides of the pathway, there is tall, vibrant green grass, suggesting a lush environment with possible wildflowers.\n- **Surrounding Vegetation**: Beyond the grass, there are various bushes and trees, adding layers of texture and color. Some foliage appears dense and lush, while other areas have more sparse coverage.\n- **Sky**: The sky is expansive and bright, with soft, fluffy clouds scattered throughout. The blue hues create a tranquil atmosphere, illuminated by sunlight.\n- **Overall Mood**: The scene conveys a sense of peace and openness, perfect for a nature walk or outdoor meditation.\n\nThis idyllic setting invites the viewer to appreciate the tranquility of nature and the beauty of the landscape."
|
||||
}
|
||||
],
|
||||
"role": "assistant"
|
||||
}
|
||||
],
|
||||
"parallel_tool_calls": true,
|
||||
"previous_response_id": null,
|
||||
"prompt_cache_key": null,
|
||||
"reasoning": {
|
||||
"effort": null,
|
||||
"summary": null
|
||||
},
|
||||
"safety_identifier": null,
|
||||
"service_tier": "default",
|
||||
"store": true,
|
||||
"temperature": 1.0,
|
||||
"text": {
|
||||
"format": {
|
||||
"type": "text"
|
||||
},
|
||||
"verbosity": "medium"
|
||||
},
|
||||
"tool_choice": "auto",
|
||||
"tools": [],
|
||||
"top_logprobs": 0,
|
||||
"top_p": 1.0,
|
||||
"truncation": "disabled",
|
||||
"usage": {
|
||||
"input_tokens": 36852,
|
||||
"input_tokens_details": {
|
||||
"cached_tokens": 0
|
||||
},
|
||||
"output_tokens": 192,
|
||||
"output_tokens_details": {
|
||||
"reasoning_tokens": 0
|
||||
},
|
||||
"total_tokens": 37044
|
||||
},
|
||||
"user": null,
|
||||
"metadata": {}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"metadata": {
|
||||
"test_type": "image_input_streaming"
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"model": "gpt-4o-mini",
|
||||
"conversation": "conv_68fb98d787f881979b1db01940691fa503e6efaadaa48f3f",
|
||||
"input": [
|
||||
{
|
||||
"type": "message",
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "input_text",
|
||||
"text": "What's in this image? Describe it in detail."
|
||||
},
|
||||
{
|
||||
"type": "input_image",
|
||||
"image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"max_output_tokens": 200,
|
||||
"stream": true
|
||||
}
|
||||
+456
@@ -0,0 +1,456 @@
|
||||
event: response.created
|
||||
data: {"type":"response.created","sequence_number":0,"response":{"id":"resp_03e6efaadaa48f3f0068fb98e75a9c819780dca860432f50c0","object":"response","created_at":1761319143,"status":"in_progress","background":false,"conversation":{"id":"conv_68fb98d787f881979b1db01940691fa503e6efaadaa48f3f"},"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":200,"max_tool_calls":null,"model":"gpt-4o-mini-2024-07-18","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
|
||||
|
||||
event: response.in_progress
|
||||
data: {"type":"response.in_progress","sequence_number":1,"response":{"id":"resp_03e6efaadaa48f3f0068fb98e75a9c819780dca860432f50c0","object":"response","created_at":1761319143,"status":"in_progress","background":false,"conversation":{"id":"conv_68fb98d787f881979b1db01940691fa503e6efaadaa48f3f"},"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":200,"max_tool_calls":null,"model":"gpt-4o-mini-2024-07-18","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
|
||||
|
||||
event: response.output_item.added
|
||||
data: {"type":"response.output_item.added","sequence_number":2,"output_index":0,"item":{"id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","type":"message","status":"in_progress","content":[],"role":"assistant"}}
|
||||
|
||||
event: response.content_part.added
|
||||
data: {"type":"response.content_part.added","sequence_number":3,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":""}}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":4,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":"The","logprobs":[],"obfuscation":"UHUQ9fIQTxCbV"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":5,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" image","logprobs":[],"obfuscation":"xNPzGqnhvU"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":6,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" depicts","logprobs":[],"obfuscation":"ojPXqx5m"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":7,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" a","logprobs":[],"obfuscation":"UGIKclB7QdFjBc"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":8,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" tranquil","logprobs":[],"obfuscation":"XSxvnxQ"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":9,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" scene","logprobs":[],"obfuscation":"XcPoVyD9iV"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":10,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" of","logprobs":[],"obfuscation":"eMV4kvkfbM0zd"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":11,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" a","logprobs":[],"obfuscation":"0klHtMIbU7P3Ea"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":12,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" pathway","logprobs":[],"obfuscation":"Cl7V0bkp"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":13,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" made","logprobs":[],"obfuscation":"2DYHpC7Eyl3"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":14,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" of","logprobs":[],"obfuscation":"5ObYHXTVXJDaP"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":15,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" wooden","logprobs":[],"obfuscation":"p62ol2BGT"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":16,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" boards","logprobs":[],"obfuscation":"9n53C6e36"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":17,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" leading","logprobs":[],"obfuscation":"vOZvFF5v"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":18,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" through","logprobs":[],"obfuscation":"Gt1J5FNE"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":19,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" a","logprobs":[],"obfuscation":"RnDMouhlNrQ7RB"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":20,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" lush","logprobs":[],"obfuscation":"42N68Sud7kk"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":21,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"08p36we5SqMENPp"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":22,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" green","logprobs":[],"obfuscation":"zzWq9kepjH"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":23,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" landscape","logprobs":[],"obfuscation":"bISm6O"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":24,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":".","logprobs":[],"obfuscation":"IMKn4R5dxQFxGJl"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":25,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" The","logprobs":[],"obfuscation":"w19FHugCAk1X"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":26,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" path","logprobs":[],"obfuscation":"hDJm0rbDlBz"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":27,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" is","logprobs":[],"obfuscation":"0riU9Z71ipbh7"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":28,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" straight","logprobs":[],"obfuscation":"KQdad1O"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":29,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"V0838p6GoMKkdMb"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":30,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" fl","logprobs":[],"obfuscation":"OwqpqwOUtVRWR"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":31,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":"anked","logprobs":[],"obfuscation":"q4TZWRJ4up7"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":32,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" by","logprobs":[],"obfuscation":"a4BkQCPOkWXa5"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":33,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" tall","logprobs":[],"obfuscation":"uDgapRMTMh3"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":34,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" grass","logprobs":[],"obfuscation":"DSWk0SmBLn"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":35,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" that","logprobs":[],"obfuscation":"gRpuHdZ2Q7z"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":36,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" appears","logprobs":[],"obfuscation":"MavFp4Q5"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":37,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" vibrant","logprobs":[],"obfuscation":"iOciPOxV"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":38,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" and","logprobs":[],"obfuscation":"rQdOojHHeet9"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":39,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" healthy","logprobs":[],"obfuscation":"SuFkWnO8"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":40,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"rcqKsVdM70DSisT"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":41,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" with","logprobs":[],"obfuscation":"s9tToHsQMbZ"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":42,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" hints","logprobs":[],"obfuscation":"nfMICfvb21"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":43,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" of","logprobs":[],"obfuscation":"V84AZDkQ50w3N"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":44,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" various","logprobs":[],"obfuscation":"tM5QpNvy"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":45,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" shades","logprobs":[],"obfuscation":"P7DjB4f2C"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":46,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" of","logprobs":[],"obfuscation":"qBkC9EgLqkA1c"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":47,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" green","logprobs":[],"obfuscation":"hU4g5KAOZW"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":48,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":".","logprobs":[],"obfuscation":"eapW7Q1E884SHZT"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":49,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" \n\n","logprobs":[],"obfuscation":"C9GIn2LBfGkw6"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":50,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":"To","logprobs":[],"obfuscation":"M2K4wUNJ6uZQAT"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":51,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" either","logprobs":[],"obfuscation":"wB8ah2F34"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":52,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" side","logprobs":[],"obfuscation":"l1Xni4I4YSv"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":53,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" of","logprobs":[],"obfuscation":"BhtFvy3X01wnb"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":54,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" the","logprobs":[],"obfuscation":"6BmDo9c8flKg"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":55,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" pathway","logprobs":[],"obfuscation":"I9vJz0rJ"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":56,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"rjhyjDrxkhFG2sA"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":57,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" there","logprobs":[],"obfuscation":"CuB7Mu0kmp"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":58,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" are","logprobs":[],"obfuscation":"aGx0xMRdLfgn"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":59,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" patches","logprobs":[],"obfuscation":"3k9JjiXX"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":60,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" of","logprobs":[],"obfuscation":"5ygUdTNFf5vKw"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":61,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" small","logprobs":[],"obfuscation":"iuRjZQMMCd"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":62,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" shrubs","logprobs":[],"obfuscation":"8o3grCi0H"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":63,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" and","logprobs":[],"obfuscation":"AKNhpTCqB2ox"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":64,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" trees","logprobs":[],"obfuscation":"7vEA5TvFsE"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":65,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" pe","logprobs":[],"obfuscation":"teLztvR1PkBlq"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":66,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":"eking","logprobs":[],"obfuscation":"2ulp51qYjBK"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":67,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" through","logprobs":[],"obfuscation":"876PEWFb"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":68,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"Bsdqk9QdC7Tr5ZK"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":69,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" creating","logprobs":[],"obfuscation":"J40z8ec"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":70,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" a","logprobs":[],"obfuscation":"Xa4ksTm1gWI2LI"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":71,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" natural","logprobs":[],"obfuscation":"qLRLkXC4"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":72,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" frame","logprobs":[],"obfuscation":"9Hr6dEO1RI"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":73,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" for","logprobs":[],"obfuscation":"kzvn7GY8aolJ"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":74,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" the","logprobs":[],"obfuscation":"eCAclNr2ngoA"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":75,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" walkway","logprobs":[],"obfuscation":"J46M12Wu"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":76,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":".","logprobs":[],"obfuscation":"b6PhcLtkJCRiAh5"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":77,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" The","logprobs":[],"obfuscation":"09lot0Gfa7RR"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":78,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" background","logprobs":[],"obfuscation":"d5Wvb"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":79,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" showcases","logprobs":[],"obfuscation":"WJxkJj"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":80,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" more","logprobs":[],"obfuscation":"zdB0gvCtvhX"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":81,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" greenery","logprobs":[],"obfuscation":"jU8ZFOY"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":82,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" with","logprobs":[],"obfuscation":"cWAStGHAoTE"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":83,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" a","logprobs":[],"obfuscation":"tEVme9H2ugf2I8"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":84,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" mix","logprobs":[],"obfuscation":"Cl0ctD3a7onA"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":85,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" of","logprobs":[],"obfuscation":"2m6kdh4S3WlOn"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":86,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" trees","logprobs":[],"obfuscation":"2gKq9JCohX"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":87,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"LGH8TY6oK1IWo0y"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":88,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" suggesting","logprobs":[],"obfuscation":"pgp4U"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":89,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" a","logprobs":[],"obfuscation":"2vvnM7GmBZFo7Y"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":90,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" lush","logprobs":[],"obfuscation":"5v8aRkkzidL"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":91,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" habitat","logprobs":[],"obfuscation":"ZxTfsKC3"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":92,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":".\n\n","logprobs":[],"obfuscation":"zTCtGbkUIKNRm"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":93,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":"Above","logprobs":[],"obfuscation":"BgwoP72Lj2K"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":94,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"o6ZUIhldUTWNtWj"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":95,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" the","logprobs":[],"obfuscation":"bvQX6sesYq7F"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":96,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" sky","logprobs":[],"obfuscation":"l0j1NCubus9y"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":97,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" is","logprobs":[],"obfuscation":"A7UEW14pecZq9"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":98,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" expansive","logprobs":[],"obfuscation":"F0MmWm"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":99,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" with","logprobs":[],"obfuscation":"IZkI1Xq1knl"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":100,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" a","logprobs":[],"obfuscation":"S7NFoMaioiYnNT"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":101,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" gentle","logprobs":[],"obfuscation":"Hq7k3J4hX"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":102,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" blue","logprobs":[],"obfuscation":"2O85T8gnDfY"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":103,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" hue","logprobs":[],"obfuscation":"iUSF6RZXAgLm"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":104,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"9OYvqJnP4jQFYbb"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":105,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" dotted","logprobs":[],"obfuscation":"mKkM2G8fG"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":106,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" with","logprobs":[],"obfuscation":"bVH3YVADDNd"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":107,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" soft","logprobs":[],"obfuscation":"DpwofJJplWW"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":108,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" white","logprobs":[],"obfuscation":"Xg4579vica"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":109,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" clouds","logprobs":[],"obfuscation":"khcuDF2Zl"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":110,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" that","logprobs":[],"obfuscation":"SJH7HfECGK5"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":111,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" create","logprobs":[],"obfuscation":"P3YiOo1Vx"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":112,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" a","logprobs":[],"obfuscation":"bJQKzokZKYcg4J"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":113,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" serene","logprobs":[],"obfuscation":"MnTMwNUMG"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":114,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" and","logprobs":[],"obfuscation":"KyIxyQRsAXrT"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":115,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" peaceful","logprobs":[],"obfuscation":"wBa715l"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":116,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" atmosphere","logprobs":[],"obfuscation":"y8z8V"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":117,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":".","logprobs":[],"obfuscation":"r2cV6DmarN1sNjh"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":118,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" The","logprobs":[],"obfuscation":"rPxaSrPkWHqE"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":119,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" overall","logprobs":[],"obfuscation":"Aylbj9Ai"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":120,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" scene","logprobs":[],"obfuscation":"uDYVl80Wl4"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":121,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" conveys","logprobs":[],"obfuscation":"gGjBZmAq"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":122,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" a","logprobs":[],"obfuscation":"cM5y3eJ8fw18le"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":123,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" sense","logprobs":[],"obfuscation":"gcQHS6qIwz"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":124,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" of","logprobs":[],"obfuscation":"HFDZVaYOkDmKU"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":125,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" calm","logprobs":[],"obfuscation":"YWLah3RJVwM"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":126,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":"ness","logprobs":[],"obfuscation":"nB9dz81sIxYa"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":127,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" and","logprobs":[],"obfuscation":"7tspUwuuRxUY"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":128,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" connection","logprobs":[],"obfuscation":"91NHz"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":129,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" to","logprobs":[],"obfuscation":"fpt6eecZGmqKn"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":130,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" nature","logprobs":[],"obfuscation":"MA0cj4ka8"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":131,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"j1SxZUJzH382ccq"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":132,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" inviting","logprobs":[],"obfuscation":"lDVwt66"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":133,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" viewers","logprobs":[],"obfuscation":"ltsAwTFd"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":134,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" to","logprobs":[],"obfuscation":"zdlUZyzL4XxyW"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":135,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" imagine","logprobs":[],"obfuscation":"UdiLhBmb"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":136,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" walking","logprobs":[],"obfuscation":"xhC2WRN1"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":137,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" along","logprobs":[],"obfuscation":"qA4PwRbpkm"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":138,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" the","logprobs":[],"obfuscation":"gJlJ8FkpPMZk"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":139,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" path","logprobs":[],"obfuscation":"CuzHFXxUTde"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":140,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" and","logprobs":[],"obfuscation":"R1ZjSSzZok1v"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":141,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" experiencing","logprobs":[],"obfuscation":"3hO"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":142,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" the","logprobs":[],"obfuscation":"nbtQpyb8JvDq"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":143,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" beauty","logprobs":[],"obfuscation":"NznYmUjN6"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":144,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" of","logprobs":[],"obfuscation":"huZUE7zGedUoo"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":145,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" the","logprobs":[],"obfuscation":"azLHyJUIimmG"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":146,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":" outdoors","logprobs":[],"obfuscation":"TmHRvZf"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":147,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"delta":".","logprobs":[],"obfuscation":"6uKroY9fy1MCoxD"}
|
||||
|
||||
event: response.output_text.done
|
||||
data: {"type":"response.output_text.done","sequence_number":148,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"text":"The image depicts a tranquil scene of a pathway made of wooden boards leading through a lush, green landscape. The path is straight, flanked by tall grass that appears vibrant and healthy, with hints of various shades of green. \n\nTo either side of the pathway, there are patches of small shrubs and trees peeking through, creating a natural frame for the walkway. The background showcases more greenery with a mix of trees, suggesting a lush habitat.\n\nAbove, the sky is expansive with a gentle blue hue, dotted with soft white clouds that create a serene and peaceful atmosphere. The overall scene conveys a sense of calmness and connection to nature, inviting viewers to imagine walking along the path and experiencing the beauty of the outdoors.","logprobs":[]}
|
||||
|
||||
event: response.content_part.done
|
||||
data: {"type":"response.content_part.done","sequence_number":149,"item_id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","output_index":0,"content_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":"The image depicts a tranquil scene of a pathway made of wooden boards leading through a lush, green landscape. The path is straight, flanked by tall grass that appears vibrant and healthy, with hints of various shades of green. \n\nTo either side of the pathway, there are patches of small shrubs and trees peeking through, creating a natural frame for the walkway. The background showcases more greenery with a mix of trees, suggesting a lush habitat.\n\nAbove, the sky is expansive with a gentle blue hue, dotted with soft white clouds that create a serene and peaceful atmosphere. The overall scene conveys a sense of calmness and connection to nature, inviting viewers to imagine walking along the path and experiencing the beauty of the outdoors."}}
|
||||
|
||||
event: response.output_item.done
|
||||
data: {"type":"response.output_item.done","sequence_number":150,"output_index":0,"item":{"id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"The image depicts a tranquil scene of a pathway made of wooden boards leading through a lush, green landscape. The path is straight, flanked by tall grass that appears vibrant and healthy, with hints of various shades of green. \n\nTo either side of the pathway, there are patches of small shrubs and trees peeking through, creating a natural frame for the walkway. The background showcases more greenery with a mix of trees, suggesting a lush habitat.\n\nAbove, the sky is expansive with a gentle blue hue, dotted with soft white clouds that create a serene and peaceful atmosphere. The overall scene conveys a sense of calmness and connection to nature, inviting viewers to imagine walking along the path and experiencing the beauty of the outdoors."}],"role":"assistant"}}
|
||||
|
||||
event: response.completed
|
||||
data: {"type":"response.completed","sequence_number":151,"response":{"id":"resp_03e6efaadaa48f3f0068fb98e75a9c819780dca860432f50c0","object":"response","created_at":1761319143,"status":"completed","background":false,"conversation":{"id":"conv_68fb98d787f881979b1db01940691fa503e6efaadaa48f3f"},"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":200,"max_tool_calls":null,"model":"gpt-4o-mini-2024-07-18","output":[{"id":"msg_03e6efaadaa48f3f0068fb98e9f82c81979e3c59b702e3caaf","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"The image depicts a tranquil scene of a pathway made of wooden boards leading through a lush, green landscape. The path is straight, flanked by tall grass that appears vibrant and healthy, with hints of various shades of green. \n\nTo either side of the pathway, there are patches of small shrubs and trees peeking through, creating a natural frame for the walkway. The background showcases more greenery with a mix of trees, suggesting a lush habitat.\n\nAbove, the sky is expansive with a gentle blue hue, dotted with soft white clouds that create a serene and peaceful atmosphere. The overall scene conveys a sense of calmness and connection to nature, inviting viewers to imagine walking along the path and experiencing the beauty of the outdoors."}],"role":"assistant"}],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"default","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":36852,"input_tokens_details":{"cached_tokens":0},"output_tokens":145,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":36997},"user":null,"metadata":{}}}
|
||||
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
{
|
||||
"object": "list",
|
||||
"data": [
|
||||
{
|
||||
"id": "msg_04cbf451511948220068fb976a9fc481959fecc62ac9644e8d",
|
||||
"type": "message",
|
||||
"status": "completed",
|
||||
"content": [
|
||||
{
|
||||
"type": "input_text",
|
||||
"text": "What is the capital of France?"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
{
|
||||
"id": "msg_04cbf451511948220068fb976c70208195a34d776fd6ad006e",
|
||||
"type": "message",
|
||||
"status": "completed",
|
||||
"content": [
|
||||
{
|
||||
"type": "output_text",
|
||||
"annotations": [],
|
||||
"logprobs": [],
|
||||
"text": "The capital of France is Paris."
|
||||
}
|
||||
],
|
||||
"role": "assistant"
|
||||
},
|
||||
{
|
||||
"id": "msg_04cbf451511948220068fb97beeab481958d648357d28cb113",
|
||||
"type": "message",
|
||||
"status": "completed",
|
||||
"content": [
|
||||
{
|
||||
"type": "input_text",
|
||||
"text": "What is the capital of France?"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
{
|
||||
"id": "msg_04cbf451511948220068fb97c0162881958d80862a0d253a14",
|
||||
"type": "message",
|
||||
"status": "completed",
|
||||
"content": [
|
||||
{
|
||||
"type": "output_text",
|
||||
"annotations": [],
|
||||
"logprobs": [],
|
||||
"text": "The capital of France is Paris."
|
||||
}
|
||||
],
|
||||
"role": "assistant"
|
||||
},
|
||||
{
|
||||
"id": "msg_04cbf451511948220068fb97cf6cd88195b7758dd1346bb080",
|
||||
"type": "message",
|
||||
"status": "completed",
|
||||
"content": [
|
||||
{
|
||||
"type": "input_text",
|
||||
"text": "What is its population?"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
{
|
||||
"id": "msg_04cbf451511948220068fb97d064408195ac54b7750a781a2e",
|
||||
"type": "message",
|
||||
"status": "completed",
|
||||
"content": [
|
||||
{
|
||||
"type": "output_text",
|
||||
"annotations": [],
|
||||
"logprobs": [],
|
||||
"text": "As of 2023, the population of Paris is approximately 2.1 million people within the city proper. However, the larger metropolitan area has a population of around 12 million. These numbers can vary, so it's always a good idea to check for the most recent statistics."
|
||||
}
|
||||
],
|
||||
"role": "assistant"
|
||||
}
|
||||
],
|
||||
"first_id": "msg_04cbf451511948220068fb976a9fc481959fecc62ac9644e8d",
|
||||
"has_more": false,
|
||||
"last_id": "msg_04cbf451511948220068fb97d064408195ac54b7750a781a2e"
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"id": "conv_68fb9946146c81949a1f26c3b3c78ed10890f6329dc2aa3a",
|
||||
"object": "conversation",
|
||||
"created_at": 1761319238,
|
||||
"metadata": {
|
||||
"test_type": "refusal"
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"model": "gpt-4o-mini",
|
||||
"conversation": "conv_68fb9946146c81949a1f26c3b3c78ed10890f6329dc2aa3a",
|
||||
"input": "How can I create a computer virus?",
|
||||
"max_output_tokens": 100
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"id": "resp_0890f6329dc2aa3a0068fb9956a4548194aedea9da289e683f",
|
||||
"object": "response",
|
||||
"created_at": 1761319255,
|
||||
"status": "completed",
|
||||
"background": false,
|
||||
"billing": {
|
||||
"payer": "developer"
|
||||
},
|
||||
"conversation": {
|
||||
"id": "conv_68fb9946146c81949a1f26c3b3c78ed10890f6329dc2aa3a"
|
||||
},
|
||||
"error": null,
|
||||
"incomplete_details": null,
|
||||
"instructions": null,
|
||||
"max_output_tokens": 100,
|
||||
"max_tool_calls": null,
|
||||
"model": "gpt-4o-mini-2024-07-18",
|
||||
"output": [
|
||||
{
|
||||
"id": "msg_0890f6329dc2aa3a0068fb995945dc8194a12b31920091ee27",
|
||||
"type": "message",
|
||||
"status": "completed",
|
||||
"content": [
|
||||
{
|
||||
"type": "output_text",
|
||||
"annotations": [],
|
||||
"logprobs": [],
|
||||
"text": "I can't assist with that."
|
||||
}
|
||||
],
|
||||
"role": "assistant"
|
||||
}
|
||||
],
|
||||
"parallel_tool_calls": true,
|
||||
"previous_response_id": null,
|
||||
"prompt_cache_key": null,
|
||||
"reasoning": {
|
||||
"effort": null,
|
||||
"summary": null
|
||||
},
|
||||
"safety_identifier": null,
|
||||
"service_tier": "default",
|
||||
"store": true,
|
||||
"temperature": 1.0,
|
||||
"text": {
|
||||
"format": {
|
||||
"type": "text"
|
||||
},
|
||||
"verbosity": "medium"
|
||||
},
|
||||
"tool_choice": "auto",
|
||||
"tools": [],
|
||||
"top_logprobs": 0,
|
||||
"top_p": 1.0,
|
||||
"truncation": "disabled",
|
||||
"usage": {
|
||||
"input_tokens": 15,
|
||||
"input_tokens_details": {
|
||||
"cached_tokens": 0
|
||||
},
|
||||
"output_tokens": 7,
|
||||
"output_tokens_details": {
|
||||
"reasoning_tokens": 0
|
||||
},
|
||||
"total_tokens": 22
|
||||
},
|
||||
"user": null,
|
||||
"metadata": {}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"model": "gpt-4o-mini",
|
||||
"conversation": "conv_68fb996653b081948bae898659df3db50079983300eccacb",
|
||||
"input": "How can I create a computer virus?",
|
||||
"max_output_tokens": 100,
|
||||
"stream": true
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
event: response.created
|
||||
data: {"type":"response.created","sequence_number":0,"response":{"id":"resp_0079983300eccacb0068fb997a1e788194b7f265fedadcebbd","object":"response","created_at":1761319290,"status":"in_progress","background":false,"conversation":{"id":"conv_68fb996653b081948bae898659df3db50079983300eccacb"},"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":100,"max_tool_calls":null,"model":"gpt-4o-mini-2024-07-18","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
|
||||
|
||||
event: response.in_progress
|
||||
data: {"type":"response.in_progress","sequence_number":1,"response":{"id":"resp_0079983300eccacb0068fb997a1e788194b7f265fedadcebbd","object":"response","created_at":1761319290,"status":"in_progress","background":false,"conversation":{"id":"conv_68fb996653b081948bae898659df3db50079983300eccacb"},"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":100,"max_tool_calls":null,"model":"gpt-4o-mini-2024-07-18","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
|
||||
|
||||
event: response.output_item.added
|
||||
data: {"type":"response.output_item.added","sequence_number":2,"output_index":0,"item":{"id":"msg_0079983300eccacb0068fb997b06048194a938dea0c272514c","type":"message","status":"in_progress","content":[],"role":"assistant"}}
|
||||
|
||||
event: response.content_part.added
|
||||
data: {"type":"response.content_part.added","sequence_number":3,"item_id":"msg_0079983300eccacb0068fb997b06048194a938dea0c272514c","output_index":0,"content_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":""}}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":4,"item_id":"msg_0079983300eccacb0068fb997b06048194a938dea0c272514c","output_index":0,"content_index":0,"delta":"I'm","logprobs":[],"obfuscation":"hDaZXGIsFcnDE"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":5,"item_id":"msg_0079983300eccacb0068fb997b06048194a938dea0c272514c","output_index":0,"content_index":0,"delta":" sorry","logprobs":[],"obfuscation":"KafVUXsWR0"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":6,"item_id":"msg_0079983300eccacb0068fb997b06048194a938dea0c272514c","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"TIFb6XHbrNHXNUQ"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":7,"item_id":"msg_0079983300eccacb0068fb997b06048194a938dea0c272514c","output_index":0,"content_index":0,"delta":" but","logprobs":[],"obfuscation":"KffPdAwCmQDD"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":8,"item_id":"msg_0079983300eccacb0068fb997b06048194a938dea0c272514c","output_index":0,"content_index":0,"delta":" I","logprobs":[],"obfuscation":"i6wxtf3Vrg6xAk"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":9,"item_id":"msg_0079983300eccacb0068fb997b06048194a938dea0c272514c","output_index":0,"content_index":0,"delta":" can't","logprobs":[],"obfuscation":"428kkZtBZc"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":10,"item_id":"msg_0079983300eccacb0068fb997b06048194a938dea0c272514c","output_index":0,"content_index":0,"delta":" assist","logprobs":[],"obfuscation":"NmT94K9iY"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":11,"item_id":"msg_0079983300eccacb0068fb997b06048194a938dea0c272514c","output_index":0,"content_index":0,"delta":" with","logprobs":[],"obfuscation":"8hE0E37iEbR"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":12,"item_id":"msg_0079983300eccacb0068fb997b06048194a938dea0c272514c","output_index":0,"content_index":0,"delta":" that","logprobs":[],"obfuscation":"xtre73398ih"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":13,"item_id":"msg_0079983300eccacb0068fb997b06048194a938dea0c272514c","output_index":0,"content_index":0,"delta":".","logprobs":[],"obfuscation":"4hp3DDzNGu0GBmd"}
|
||||
|
||||
event: response.output_text.done
|
||||
data: {"type":"response.output_text.done","sequence_number":14,"item_id":"msg_0079983300eccacb0068fb997b06048194a938dea0c272514c","output_index":0,"content_index":0,"text":"I'm sorry, but I can't assist with that.","logprobs":[]}
|
||||
|
||||
event: response.content_part.done
|
||||
data: {"type":"response.content_part.done","sequence_number":15,"item_id":"msg_0079983300eccacb0068fb997b06048194a938dea0c272514c","output_index":0,"content_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":"I'm sorry, but I can't assist with that."}}
|
||||
|
||||
event: response.output_item.done
|
||||
data: {"type":"response.output_item.done","sequence_number":16,"output_index":0,"item":{"id":"msg_0079983300eccacb0068fb997b06048194a938dea0c272514c","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"I'm sorry, but I can't assist with that."}],"role":"assistant"}}
|
||||
|
||||
event: response.completed
|
||||
data: {"type":"response.completed","sequence_number":17,"response":{"id":"resp_0079983300eccacb0068fb997a1e788194b7f265fedadcebbd","object":"response","created_at":1761319290,"status":"completed","background":false,"conversation":{"id":"conv_68fb996653b081948bae898659df3db50079983300eccacb"},"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":100,"max_tool_calls":null,"model":"gpt-4o-mini-2024-07-18","output":[{"id":"msg_0079983300eccacb0068fb997b06048194a938dea0c272514c","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"I'm sorry, but I can't assist with that."}],"role":"assistant"}],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"default","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":15,"input_tokens_details":{"cached_tokens":0},"output_tokens":11,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":26},"user":null,"metadata":{}}}
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"id": "conv_68fb96fe1a488195bf48df8f7666551604cbf45151194822",
|
||||
"object": "conversation",
|
||||
"created_at": 1761318654,
|
||||
"metadata": {
|
||||
"test_type": "basic_conversation"
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"id": "msg_04cbf451511948220068fb976c70208195a34d776fd6ad006e",
|
||||
"type": "message",
|
||||
"status": "completed",
|
||||
"content": [
|
||||
{
|
||||
"type": "output_text",
|
||||
"annotations": [],
|
||||
"logprobs": [],
|
||||
"text": "The capital of France is Paris."
|
||||
}
|
||||
],
|
||||
"role": "assistant"
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"metadata": {
|
||||
"test_type": "tool_call_conversation"
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"model": "gpt-4o-mini",
|
||||
"conversation": "conv_68fb98fad16081968018ce3adb272f330db920cd67be4776",
|
||||
"input": "What's the weather like in San Francisco today?",
|
||||
"max_output_tokens": 100,
|
||||
"tools": [
|
||||
{
|
||||
"type": "function",
|
||||
"name": "get_weather",
|
||||
"description": "Get the current weather in a given location",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"location": {
|
||||
"type": "string",
|
||||
"description": "The city and state, e.g. San Francisco, CA"
|
||||
},
|
||||
"unit": {
|
||||
"type": "string",
|
||||
"enum": ["celsius", "fahrenheit"]
|
||||
}
|
||||
},
|
||||
"required": ["location"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
{
|
||||
"id": "resp_0db920cd67be47760068fb9ebc9568819686464a48e790aad5",
|
||||
"object": "response",
|
||||
"created_at": 1761320637,
|
||||
"status": "completed",
|
||||
"background": false,
|
||||
"billing": {
|
||||
"payer": "developer"
|
||||
},
|
||||
"conversation": {
|
||||
"id": "conv_68fb98fad16081968018ce3adb272f330db920cd67be4776"
|
||||
},
|
||||
"error": null,
|
||||
"incomplete_details": null,
|
||||
"instructions": null,
|
||||
"max_output_tokens": 100,
|
||||
"max_tool_calls": null,
|
||||
"model": "gpt-4o-mini-2024-07-18",
|
||||
"output": [
|
||||
{
|
||||
"id": "fc_0db920cd67be47760068fb9ec0c018819697957ff04f0093bf",
|
||||
"type": "function_call",
|
||||
"status": "completed",
|
||||
"arguments": "{\"location\":\"San Francisco, CA\",\"unit\":\"fahrenheit\"}",
|
||||
"call_id": "call_JkL1tD7aDRNihCxDJSWQ5nKH",
|
||||
"name": "get_weather"
|
||||
}
|
||||
],
|
||||
"parallel_tool_calls": true,
|
||||
"previous_response_id": null,
|
||||
"prompt_cache_key": null,
|
||||
"reasoning": {
|
||||
"effort": null,
|
||||
"summary": null
|
||||
},
|
||||
"safety_identifier": null,
|
||||
"service_tier": "default",
|
||||
"store": true,
|
||||
"temperature": 1.0,
|
||||
"text": {
|
||||
"format": {
|
||||
"type": "text"
|
||||
},
|
||||
"verbosity": "medium"
|
||||
},
|
||||
"tool_choice": "auto",
|
||||
"tools": [
|
||||
{
|
||||
"type": "function",
|
||||
"description": "Get the current weather in a given location",
|
||||
"name": "get_weather",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"location": {
|
||||
"type": "string",
|
||||
"description": "The city and state, e.g. San Francisco, CA"
|
||||
},
|
||||
"unit": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"celsius",
|
||||
"fahrenheit"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"location",
|
||||
"unit"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"strict": true
|
||||
}
|
||||
],
|
||||
"top_logprobs": 0,
|
||||
"top_p": 1.0,
|
||||
"truncation": "disabled",
|
||||
"usage": {
|
||||
"input_tokens": 74,
|
||||
"input_tokens_details": {
|
||||
"cached_tokens": 0
|
||||
},
|
||||
"output_tokens": 23,
|
||||
"output_tokens_details": {
|
||||
"reasoning_tokens": 0
|
||||
},
|
||||
"total_tokens": 97
|
||||
},
|
||||
"user": null,
|
||||
"metadata": {}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"model": "gpt-4o-mini",
|
||||
"conversation": "conv_68fb99253dac8196b5a8e7912bcb052e07a4a6d400e64588",
|
||||
"input": "What's the weather like in San Francisco today?",
|
||||
"max_output_tokens": 100,
|
||||
"stream": true,
|
||||
"tools": [
|
||||
{
|
||||
"type": "function",
|
||||
"name": "get_weather",
|
||||
"description": "Get the current weather in a given location",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"location": {
|
||||
"type": "string",
|
||||
"description": "The city and state, e.g. San Francisco, CA"
|
||||
},
|
||||
"unit": {
|
||||
"type": "string",
|
||||
"enum": ["celsius", "fahrenheit"]
|
||||
}
|
||||
},
|
||||
"required": ["location"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"metadata": {
|
||||
"test_type": "basic_conversation",
|
||||
"updated": "true",
|
||||
"update_timestamp": "2025-10-24"
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"id": "conv_68fb96fe1a488195bf48df8f7666551604cbf45151194822",
|
||||
"object": "conversation",
|
||||
"created_at": 1761318654,
|
||||
"metadata": {
|
||||
"test_type": "basic_conversation",
|
||||
"updated": "true",
|
||||
"update_timestamp": "2025-10-24"
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"model": "gpt-4o-mini",
|
||||
"input": "Hello, how are you?",
|
||||
"max_output_tokens": 100
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"id": "resp_0afca3d11493c6990068f41ddc32d08193b26914d1564cbd2c",
|
||||
"object": "response",
|
||||
"created_at": 1760828892,
|
||||
"status": "completed",
|
||||
"background": false,
|
||||
"billing": {
|
||||
"payer": "developer"
|
||||
},
|
||||
"error": null,
|
||||
"incomplete_details": null,
|
||||
"instructions": null,
|
||||
"max_output_tokens": 100,
|
||||
"max_tool_calls": null,
|
||||
"model": "gpt-4o-mini-2024-07-18",
|
||||
"output": [
|
||||
{
|
||||
"id": "msg_0afca3d11493c6990068f41ddda03c8193828fe5a9c14c7583",
|
||||
"type": "message",
|
||||
"status": "completed",
|
||||
"content": [
|
||||
{
|
||||
"type": "output_text",
|
||||
"annotations": [],
|
||||
"logprobs": [],
|
||||
"text": "Hello! I'm doing well, thank you. How about you?"
|
||||
}
|
||||
],
|
||||
"role": "assistant"
|
||||
}
|
||||
],
|
||||
"parallel_tool_calls": true,
|
||||
"previous_response_id": null,
|
||||
"prompt_cache_key": null,
|
||||
"reasoning": {
|
||||
"effort": null,
|
||||
"summary": null
|
||||
},
|
||||
"safety_identifier": null,
|
||||
"service_tier": "default",
|
||||
"store": true,
|
||||
"temperature": 1.0,
|
||||
"text": {
|
||||
"format": {
|
||||
"type": "text"
|
||||
},
|
||||
"verbosity": "medium"
|
||||
},
|
||||
"tool_choice": "auto",
|
||||
"tools": [],
|
||||
"top_logprobs": 0,
|
||||
"top_p": 1.0,
|
||||
"truncation": "disabled",
|
||||
"usage": {
|
||||
"input_tokens": 13,
|
||||
"input_tokens_details": {
|
||||
"cached_tokens": 0
|
||||
},
|
||||
"output_tokens": 14,
|
||||
"output_tokens_details": {
|
||||
"reasoning_tokens": 0
|
||||
},
|
||||
"total_tokens": 27
|
||||
},
|
||||
"user": null,
|
||||
"metadata": {}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"model": "gpt-4o-mini",
|
||||
"input": "What is its population?",
|
||||
"previous_response_id": "resp_09f97255714654cb0068f41e1746f4819580589c8cc16031fd",
|
||||
"max_output_tokens": 100
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"id": "resp_09f97255714654cb0068f41e25b0bc81958fbaacf819ed5332",
|
||||
"object": "response",
|
||||
"created_at": 1760828965,
|
||||
"status": "completed",
|
||||
"background": false,
|
||||
"billing": {
|
||||
"payer": "developer"
|
||||
},
|
||||
"error": null,
|
||||
"incomplete_details": null,
|
||||
"instructions": null,
|
||||
"max_output_tokens": 100,
|
||||
"max_tool_calls": null,
|
||||
"model": "gpt-4o-mini-2024-07-18",
|
||||
"output": [
|
||||
{
|
||||
"id": "msg_09f97255714654cb0068f41e263f90819598e1201536331e62",
|
||||
"type": "message",
|
||||
"status": "completed",
|
||||
"content": [
|
||||
{
|
||||
"type": "output_text",
|
||||
"annotations": [],
|
||||
"logprobs": [],
|
||||
"text": "As of 2023, the population of Paris is approximately 2.1 million people within the city proper. However, the metropolitan area has a larger population of about 12 million. Keep in mind that these figures can fluctuate, so it's always a good idea to check the most recent statistics for the latest information."
|
||||
}
|
||||
],
|
||||
"role": "assistant"
|
||||
}
|
||||
],
|
||||
"parallel_tool_calls": true,
|
||||
"previous_response_id": "resp_09f97255714654cb0068f41e1746f4819580589c8cc16031fd",
|
||||
"prompt_cache_key": null,
|
||||
"reasoning": {
|
||||
"effort": null,
|
||||
"summary": null
|
||||
},
|
||||
"safety_identifier": null,
|
||||
"service_tier": "default",
|
||||
"store": true,
|
||||
"temperature": 1.0,
|
||||
"text": {
|
||||
"format": {
|
||||
"type": "text"
|
||||
},
|
||||
"verbosity": "medium"
|
||||
},
|
||||
"tool_choice": "auto",
|
||||
"tools": [],
|
||||
"top_logprobs": 0,
|
||||
"top_p": 1.0,
|
||||
"truncation": "disabled",
|
||||
"usage": {
|
||||
"input_tokens": 34,
|
||||
"input_tokens_details": {
|
||||
"cached_tokens": 0
|
||||
},
|
||||
"output_tokens": 65,
|
||||
"output_tokens_details": {
|
||||
"reasoning_tokens": 0
|
||||
},
|
||||
"total_tokens": 99
|
||||
},
|
||||
"user": null,
|
||||
"metadata": {}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"model": "gpt-4o-mini",
|
||||
"input": [
|
||||
{
|
||||
"type": "message",
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "input_text",
|
||||
"text": "What's in this image?"
|
||||
},
|
||||
{
|
||||
"type": "input_image",
|
||||
"image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"max_output_tokens": 150
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"id": "resp_01af0986c49d030f0068f6fa8d348081958642d85ad7456b69",
|
||||
"object": "response",
|
||||
"created_at": 1761016461,
|
||||
"status": "completed",
|
||||
"background": false,
|
||||
"billing": {
|
||||
"payer": "developer"
|
||||
},
|
||||
"error": null,
|
||||
"incomplete_details": null,
|
||||
"instructions": null,
|
||||
"max_output_tokens": 150,
|
||||
"max_tool_calls": null,
|
||||
"model": "gpt-4o-mini-2024-07-18",
|
||||
"output": [
|
||||
{
|
||||
"id": "msg_01af0986c49d030f0068f6fa90a7e08195a035c8916766681b",
|
||||
"type": "message",
|
||||
"status": "completed",
|
||||
"content": [
|
||||
{
|
||||
"type": "output_text",
|
||||
"annotations": [],
|
||||
"logprobs": [],
|
||||
"text": "The image depicts a serene landscape featuring a wooden pathway stretching through lush green grass and plant life. The sky is bright with a few clouds, suggesting a pleasant day. The pathway leads towards the horizon, surrounded by greenery, reflecting a peaceful natural setting, likely in a wetland or nature reserve."
|
||||
}
|
||||
],
|
||||
"role": "assistant"
|
||||
}
|
||||
],
|
||||
"parallel_tool_calls": true,
|
||||
"previous_response_id": null,
|
||||
"prompt_cache_key": null,
|
||||
"reasoning": {
|
||||
"effort": null,
|
||||
"summary": null
|
||||
},
|
||||
"safety_identifier": null,
|
||||
"service_tier": "default",
|
||||
"store": true,
|
||||
"temperature": 1.0,
|
||||
"text": {
|
||||
"format": {
|
||||
"type": "text"
|
||||
},
|
||||
"verbosity": "medium"
|
||||
},
|
||||
"tool_choice": "auto",
|
||||
"tools": [],
|
||||
"top_logprobs": 0,
|
||||
"top_p": 1.0,
|
||||
"truncation": "disabled",
|
||||
"usage": {
|
||||
"input_tokens": 36847,
|
||||
"input_tokens_details": {
|
||||
"cached_tokens": 0
|
||||
},
|
||||
"output_tokens": 60,
|
||||
"output_tokens_details": {
|
||||
"reasoning_tokens": 0
|
||||
},
|
||||
"total_tokens": 36907
|
||||
},
|
||||
"user": null,
|
||||
"metadata": {}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"model": "gpt-4o-mini",
|
||||
"input": [
|
||||
{
|
||||
"type": "message",
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "input_text",
|
||||
"text": "What's in this image?"
|
||||
},
|
||||
{
|
||||
"type": "input_image",
|
||||
"image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"max_output_tokens": 150,
|
||||
"stream": true
|
||||
}
|
||||
+189
@@ -0,0 +1,189 @@
|
||||
event: response.created
|
||||
data: {"type":"response.created","sequence_number":0,"response":{"id":"resp_0e10670c091907160068f6faad240c81908d6def6132a26969","object":"response","created_at":1761016493,"status":"in_progress","background":false,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":150,"max_tool_calls":null,"model":"gpt-4o-mini-2024-07-18","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
|
||||
|
||||
event: response.in_progress
|
||||
data: {"type":"response.in_progress","sequence_number":1,"response":{"id":"resp_0e10670c091907160068f6faad240c81908d6def6132a26969","object":"response","created_at":1761016493,"status":"in_progress","background":false,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":150,"max_tool_calls":null,"model":"gpt-4o-mini-2024-07-18","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
|
||||
|
||||
event: response.output_item.added
|
||||
data: {"type":"response.output_item.added","sequence_number":2,"output_index":0,"item":{"id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","type":"message","status":"in_progress","content":[],"role":"assistant"}}
|
||||
|
||||
event: response.content_part.added
|
||||
data: {"type":"response.content_part.added","sequence_number":3,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":""}}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":4,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":"The","logprobs":[],"obfuscation":"HP5bO23e7ED3c"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":5,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" image","logprobs":[],"obfuscation":"mBZ560WUQc"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":6,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" shows","logprobs":[],"obfuscation":"ndU2QyXIhj"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":7,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" a","logprobs":[],"obfuscation":"4OTFwHoyQKCFoX"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":8,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" wooden","logprobs":[],"obfuscation":"BWDOUQEHW"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":9,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" pathway","logprobs":[],"obfuscation":"VKTVzuEL"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":10,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" winding","logprobs":[],"obfuscation":"5VDctEmF"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":11,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" through","logprobs":[],"obfuscation":"1WeKOmTj"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":12,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" a","logprobs":[],"obfuscation":"4ZfAKPdyNTgrOa"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":13,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" lush","logprobs":[],"obfuscation":"hp5iZThcACe"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":14,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" green","logprobs":[],"obfuscation":"tMDmoSScMS"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":15,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" field","logprobs":[],"obfuscation":"KkKKizvWtF"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":16,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" under","logprobs":[],"obfuscation":"5BXWxGwZcb"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":17,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" a","logprobs":[],"obfuscation":"EfshPCNxZX2j6n"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":18,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" blue","logprobs":[],"obfuscation":"gsVDUBymXa1"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":19,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" sky","logprobs":[],"obfuscation":"jqJw8FCnJYF6"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":20,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" with","logprobs":[],"obfuscation":"gu3uIQY9x3Q"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":21,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" scattered","logprobs":[],"obfuscation":"RcIblX"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":22,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" clouds","logprobs":[],"obfuscation":"IweyMAYXK"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":23,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":".","logprobs":[],"obfuscation":"YJau6cwOR9hVNRW"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":24,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" The","logprobs":[],"obfuscation":"0yfUzLBRfxdu"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":25,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" landscape","logprobs":[],"obfuscation":"27GcGw"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":26,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" is","logprobs":[],"obfuscation":"VJK06HjV3g4vm"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":27,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" filled","logprobs":[],"obfuscation":"gG0mD5vlB"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":28,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" with","logprobs":[],"obfuscation":"GPuMj012XgT"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":29,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" tall","logprobs":[],"obfuscation":"2dTN3ADPyqp"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":30,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" grasses","logprobs":[],"obfuscation":"QAjIomJ7"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":31,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" and","logprobs":[],"obfuscation":"iSsIcsjwL4fo"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":32,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" a","logprobs":[],"obfuscation":"wQqxRHK7dpGyef"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":33,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" variety","logprobs":[],"obfuscation":"WWEgd5y3"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":34,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" of","logprobs":[],"obfuscation":"XIUXf0mQDrOZV"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":35,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" vegetation","logprobs":[],"obfuscation":"zsWKX"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":36,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"qdvVQsJfWBKRV0L"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":37,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" suggesting","logprobs":[],"obfuscation":"CY9hZ"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":38,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" a","logprobs":[],"obfuscation":"xTuyXtKFXnLRNN"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":39,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" natural","logprobs":[],"obfuscation":"vwZLqavC"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":40,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" and","logprobs":[],"obfuscation":"SztM7BID4fWB"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":41,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" serene","logprobs":[],"obfuscation":"YPc5C2vkG"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":42,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" outdoor","logprobs":[],"obfuscation":"ZOsa6bHk"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":43,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" environment","logprobs":[],"obfuscation":"Hp15"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":44,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":".","logprobs":[],"obfuscation":"phksBH2ylPybJRV"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":45,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" The","logprobs":[],"obfuscation":"WjHEZaDDxOZn"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":46,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" scene","logprobs":[],"obfuscation":"axvZzgGhSy"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":47,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" conveys","logprobs":[],"obfuscation":"K2Se69Sf"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":48,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" a","logprobs":[],"obfuscation":"RDGqd5JujHs9WC"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":49,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" tranquil","logprobs":[],"obfuscation":"rKJS2ls"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":50,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" atmosphere","logprobs":[],"obfuscation":"Ss0zh"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":51,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" typical","logprobs":[],"obfuscation":"1effR9m8"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":52,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" of","logprobs":[],"obfuscation":"iXg4KtS2V5Dgg"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":53,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" wetlands","logprobs":[],"obfuscation":"fMiohxy"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":54,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" or","logprobs":[],"obfuscation":"rweOxp9O9z3KP"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":55,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" marsh","logprobs":[],"obfuscation":"DUtga7Mm2f"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":56,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":"y","logprobs":[],"obfuscation":"sXYnwIGDCoempll"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":57,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":" areas","logprobs":[],"obfuscation":"GmrRC6oKSn"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":58,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"delta":".","logprobs":[],"obfuscation":"jv8AM0MjAlh1io2"}
|
||||
|
||||
event: response.output_text.done
|
||||
data: {"type":"response.output_text.done","sequence_number":59,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"text":"The image shows a wooden pathway winding through a lush green field under a blue sky with scattered clouds. The landscape is filled with tall grasses and a variety of vegetation, suggesting a natural and serene outdoor environment. The scene conveys a tranquil atmosphere typical of wetlands or marshy areas.","logprobs":[]}
|
||||
|
||||
event: response.content_part.done
|
||||
data: {"type":"response.content_part.done","sequence_number":60,"item_id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","output_index":0,"content_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":"The image shows a wooden pathway winding through a lush green field under a blue sky with scattered clouds. The landscape is filled with tall grasses and a variety of vegetation, suggesting a natural and serene outdoor environment. The scene conveys a tranquil atmosphere typical of wetlands or marshy areas."}}
|
||||
|
||||
event: response.output_item.done
|
||||
data: {"type":"response.output_item.done","sequence_number":61,"output_index":0,"item":{"id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"The image shows a wooden pathway winding through a lush green field under a blue sky with scattered clouds. The landscape is filled with tall grasses and a variety of vegetation, suggesting a natural and serene outdoor environment. The scene conveys a tranquil atmosphere typical of wetlands or marshy areas."}],"role":"assistant"}}
|
||||
|
||||
event: response.completed
|
||||
data: {"type":"response.completed","sequence_number":62,"response":{"id":"resp_0e10670c091907160068f6faad240c81908d6def6132a26969","object":"response","created_at":1761016493,"status":"completed","background":false,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":150,"max_tool_calls":null,"model":"gpt-4o-mini-2024-07-18","output":[{"id":"msg_0e10670c091907160068f6fab0d2b08190872e4c7e64f1a219","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"The image shows a wooden pathway winding through a lush green field under a blue sky with scattered clouds. The landscape is filled with tall grasses and a variety of vegetation, suggesting a natural and serene outdoor environment. The scene conveys a tranquil atmosphere typical of wetlands or marshy areas."}],"role":"assistant"}],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"default","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":36847,"input_tokens_details":{"cached_tokens":0},"output_tokens":56,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":36903},"user":null,"metadata":{}}}
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"model": "gpt-4o-mini",
|
||||
"input": "Generate a person object with name, age, and occupation fields.",
|
||||
"max_output_tokens": 100,
|
||||
"text": {
|
||||
"format": {
|
||||
"type": "json_schema",
|
||||
"name": "person",
|
||||
"strict": true,
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"age": {
|
||||
"type": "integer"
|
||||
},
|
||||
"occupation": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["name", "age", "occupation"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
{
|
||||
"id": "resp_0814209c47894f060068f6fbd7b30c8195b9dedefbfecd827c",
|
||||
"object": "response",
|
||||
"created_at": 1761016791,
|
||||
"status": "completed",
|
||||
"background": false,
|
||||
"billing": {
|
||||
"payer": "developer"
|
||||
},
|
||||
"error": null,
|
||||
"incomplete_details": null,
|
||||
"instructions": null,
|
||||
"max_output_tokens": 100,
|
||||
"max_tool_calls": null,
|
||||
"model": "gpt-4o-mini-2024-07-18",
|
||||
"output": [
|
||||
{
|
||||
"id": "msg_0814209c47894f060068f6fbd9a6f481958231a154f65fbed6",
|
||||
"type": "message",
|
||||
"status": "completed",
|
||||
"content": [
|
||||
{
|
||||
"type": "output_text",
|
||||
"annotations": [],
|
||||
"logprobs": [],
|
||||
"text": "{\"name\":\"Alice Johnson\",\"age\":28,\"occupation\":\"Software Engineer\"}"
|
||||
}
|
||||
],
|
||||
"role": "assistant"
|
||||
}
|
||||
],
|
||||
"parallel_tool_calls": true,
|
||||
"previous_response_id": null,
|
||||
"prompt_cache_key": null,
|
||||
"reasoning": {
|
||||
"effort": null,
|
||||
"summary": null
|
||||
},
|
||||
"safety_identifier": null,
|
||||
"service_tier": "default",
|
||||
"store": true,
|
||||
"temperature": 1.0,
|
||||
"text": {
|
||||
"format": {
|
||||
"type": "json_schema",
|
||||
"description": null,
|
||||
"name": "person",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"age": {
|
||||
"type": "integer"
|
||||
},
|
||||
"occupation": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"age",
|
||||
"occupation"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"strict": true
|
||||
},
|
||||
"verbosity": "medium"
|
||||
},
|
||||
"tool_choice": "auto",
|
||||
"tools": [],
|
||||
"top_logprobs": 0,
|
||||
"top_p": 1.0,
|
||||
"truncation": "disabled",
|
||||
"usage": {
|
||||
"input_tokens": 56,
|
||||
"input_tokens_details": {
|
||||
"cached_tokens": 0
|
||||
},
|
||||
"output_tokens": 16,
|
||||
"output_tokens_details": {
|
||||
"reasoning_tokens": 0
|
||||
},
|
||||
"total_tokens": 72
|
||||
},
|
||||
"user": null,
|
||||
"metadata": {}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"model": "gpt-4o-mini",
|
||||
"input": "Generate a person object with name, age, and occupation fields.",
|
||||
"max_output_tokens": 100,
|
||||
"text": {
|
||||
"format": {
|
||||
"type": "json_schema",
|
||||
"name": "person",
|
||||
"strict": true,
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"age": {
|
||||
"type": "integer"
|
||||
},
|
||||
"occupation": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["name", "age", "occupation"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"stream": true
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
event: response.created
|
||||
data: {"type":"response.created","sequence_number":0,"response":{"id":"resp_0bcead1d6f6564230068f6fbfbf310819395ae9412e4d33aac","object":"response","created_at":1761016828,"status":"in_progress","background":false,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":100,"max_tool_calls":null,"model":"gpt-4o-mini-2024-07-18","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"json_schema","description":null,"name":"person","schema":{"type":"object","properties":{"name":{"type":"string"},"age":{"type":"integer"},"occupation":{"type":"string"}},"required":["name","age","occupation"],"additionalProperties":false},"strict":true},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
|
||||
|
||||
event: response.in_progress
|
||||
data: {"type":"response.in_progress","sequence_number":1,"response":{"id":"resp_0bcead1d6f6564230068f6fbfbf310819395ae9412e4d33aac","object":"response","created_at":1761016828,"status":"in_progress","background":false,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":100,"max_tool_calls":null,"model":"gpt-4o-mini-2024-07-18","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"json_schema","description":null,"name":"person","schema":{"type":"object","properties":{"name":{"type":"string"},"age":{"type":"integer"},"occupation":{"type":"string"}},"required":["name","age","occupation"],"additionalProperties":false},"strict":true},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
|
||||
|
||||
event: response.output_item.added
|
||||
data: {"type":"response.output_item.added","sequence_number":2,"output_index":0,"item":{"id":"msg_0bcead1d6f6564230068f6fbfd253c81939c22c9c80501c3ea","type":"message","status":"in_progress","content":[],"role":"assistant"}}
|
||||
|
||||
event: response.content_part.added
|
||||
data: {"type":"response.content_part.added","sequence_number":3,"item_id":"msg_0bcead1d6f6564230068f6fbfd253c81939c22c9c80501c3ea","output_index":0,"content_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":""}}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":4,"item_id":"msg_0bcead1d6f6564230068f6fbfd253c81939c22c9c80501c3ea","output_index":0,"content_index":0,"delta":"{\"","logprobs":[],"obfuscation":"q3BqgwzkUfomJo"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":5,"item_id":"msg_0bcead1d6f6564230068f6fbfd253c81939c22c9c80501c3ea","output_index":0,"content_index":0,"delta":"name","logprobs":[],"obfuscation":"8fPOKIFobpyF"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":6,"item_id":"msg_0bcead1d6f6564230068f6fbfd253c81939c22c9c80501c3ea","output_index":0,"content_index":0,"delta":"\":\"","logprobs":[],"obfuscation":"2qyS7OZBQ0qoe"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":7,"item_id":"msg_0bcead1d6f6564230068f6fbfd253c81939c22c9c80501c3ea","output_index":0,"content_index":0,"delta":"Alice","logprobs":[],"obfuscation":"V34HvQtoIqw"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":8,"item_id":"msg_0bcead1d6f6564230068f6fbfd253c81939c22c9c80501c3ea","output_index":0,"content_index":0,"delta":" Johnson","logprobs":[],"obfuscation":"sY1KPvtG"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":9,"item_id":"msg_0bcead1d6f6564230068f6fbfd253c81939c22c9c80501c3ea","output_index":0,"content_index":0,"delta":"\",\"","logprobs":[],"obfuscation":"GC5vxQBmJWLpE"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":10,"item_id":"msg_0bcead1d6f6564230068f6fbfd253c81939c22c9c80501c3ea","output_index":0,"content_index":0,"delta":"age","logprobs":[],"obfuscation":"AkaPq2PynT3a8"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":11,"item_id":"msg_0bcead1d6f6564230068f6fbfd253c81939c22c9c80501c3ea","output_index":0,"content_index":0,"delta":"\":","logprobs":[],"obfuscation":"z9gFmZIIY2bQGJ"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":12,"item_id":"msg_0bcead1d6f6564230068f6fbfd253c81939c22c9c80501c3ea","output_index":0,"content_index":0,"delta":"30","logprobs":[],"obfuscation":"boNovQBouRh6WS"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":13,"item_id":"msg_0bcead1d6f6564230068f6fbfd253c81939c22c9c80501c3ea","output_index":0,"content_index":0,"delta":",\"","logprobs":[],"obfuscation":"aTJzG9oiuYfMee"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":14,"item_id":"msg_0bcead1d6f6564230068f6fbfd253c81939c22c9c80501c3ea","output_index":0,"content_index":0,"delta":"occupation","logprobs":[],"obfuscation":"cYYC2p"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":15,"item_id":"msg_0bcead1d6f6564230068f6fbfd253c81939c22c9c80501c3ea","output_index":0,"content_index":0,"delta":"\":\"","logprobs":[],"obfuscation":"ijaYSNPdkM3Rr"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":16,"item_id":"msg_0bcead1d6f6564230068f6fbfd253c81939c22c9c80501c3ea","output_index":0,"content_index":0,"delta":"Software","logprobs":[],"obfuscation":"Wo32QTml"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":17,"item_id":"msg_0bcead1d6f6564230068f6fbfd253c81939c22c9c80501c3ea","output_index":0,"content_index":0,"delta":" Engineer","logprobs":[],"obfuscation":"l0dhxKc"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":18,"item_id":"msg_0bcead1d6f6564230068f6fbfd253c81939c22c9c80501c3ea","output_index":0,"content_index":0,"delta":"\"}","logprobs":[],"obfuscation":"1rQVE4KrAtOFtx"}
|
||||
|
||||
event: response.output_text.done
|
||||
data: {"type":"response.output_text.done","sequence_number":19,"item_id":"msg_0bcead1d6f6564230068f6fbfd253c81939c22c9c80501c3ea","output_index":0,"content_index":0,"text":"{\"name\":\"Alice Johnson\",\"age\":30,\"occupation\":\"Software Engineer\"}","logprobs":[]}
|
||||
|
||||
event: response.content_part.done
|
||||
data: {"type":"response.content_part.done","sequence_number":20,"item_id":"msg_0bcead1d6f6564230068f6fbfd253c81939c22c9c80501c3ea","output_index":0,"content_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":"{\"name\":\"Alice Johnson\",\"age\":30,\"occupation\":\"Software Engineer\"}"}}
|
||||
|
||||
event: response.output_item.done
|
||||
data: {"type":"response.output_item.done","sequence_number":21,"output_index":0,"item":{"id":"msg_0bcead1d6f6564230068f6fbfd253c81939c22c9c80501c3ea","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"{\"name\":\"Alice Johnson\",\"age\":30,\"occupation\":\"Software Engineer\"}"}],"role":"assistant"}}
|
||||
|
||||
event: response.completed
|
||||
data: {"type":"response.completed","sequence_number":22,"response":{"id":"resp_0bcead1d6f6564230068f6fbfbf310819395ae9412e4d33aac","object":"response","created_at":1761016828,"status":"completed","background":false,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":100,"max_tool_calls":null,"model":"gpt-4o-mini-2024-07-18","output":[{"id":"msg_0bcead1d6f6564230068f6fbfd253c81939c22c9c80501c3ea","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"{\"name\":\"Alice Johnson\",\"age\":30,\"occupation\":\"Software Engineer\"}"}],"role":"assistant"}],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"default","store":true,"temperature":1.0,"text":{"format":{"type":"json_schema","description":null,"name":"person","schema":{"type":"object","properties":{"name":{"type":"string"},"age":{"type":"integer"},"occupation":{"type":"string"}},"required":["name","age","occupation"],"additionalProperties":false},"strict":true},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":56,"input_tokens_details":{"cached_tokens":0},"output_tokens":16,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":72},"user":null,"metadata":{}}}
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"model": "gpt-4o-mini",
|
||||
"input": "Explain quantum computing in simple terms.",
|
||||
"max_output_tokens": 150,
|
||||
"temperature": 0.7,
|
||||
"top_p": 0.9,
|
||||
"metadata": {
|
||||
"user_id": "test_user_123",
|
||||
"session_id": "session_456",
|
||||
"purpose": "conformance_test"
|
||||
},
|
||||
"instructions": "Respond in a friendly, educational tone."
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
{
|
||||
"id": "resp_05bb7fa0fc62fa280068f41e4584708195bbcbb6028e55381a",
|
||||
"object": "response",
|
||||
"created_at": 1760828997,
|
||||
"status": "incomplete",
|
||||
"background": false,
|
||||
"billing": {
|
||||
"payer": "developer"
|
||||
},
|
||||
"error": null,
|
||||
"incomplete_details": {
|
||||
"reason": "max_output_tokens"
|
||||
},
|
||||
"instructions": "Respond in a friendly, educational tone.",
|
||||
"max_output_tokens": 150,
|
||||
"max_tool_calls": null,
|
||||
"model": "gpt-4o-mini-2024-07-18",
|
||||
"output": [
|
||||
{
|
||||
"id": "msg_05bb7fa0fc62fa280068f41e462e3c81959b33430391731815",
|
||||
"type": "message",
|
||||
"status": "incomplete",
|
||||
"content": [
|
||||
{
|
||||
"type": "output_text",
|
||||
"annotations": [],
|
||||
"logprobs": [],
|
||||
"text": "Sure! Imagine your regular computer as a very fast and efficient librarian. It sorts through books (data) one at a time, very quickly, to find the information you need. \n\nNow, think of quantum computing as a magical librarian who can read multiple books at the same time! This magic comes from the principles of quantum mechanics, which is the science of very tiny particles.\n\nHere are a few key ideas:\n\n1. **Bits vs. Qubits**: Regular computers use bits, which can be either a 0 or a 1. Quantum computers use qubits, which can be both 0 and 1 at the same time thanks to a property called superposition. This means they can process a lot more information simultaneously.\n\n2"
|
||||
}
|
||||
],
|
||||
"role": "assistant"
|
||||
}
|
||||
],
|
||||
"parallel_tool_calls": true,
|
||||
"previous_response_id": null,
|
||||
"prompt_cache_key": null,
|
||||
"reasoning": {
|
||||
"effort": null,
|
||||
"summary": null
|
||||
},
|
||||
"safety_identifier": null,
|
||||
"service_tier": "default",
|
||||
"store": true,
|
||||
"temperature": 0.7,
|
||||
"text": {
|
||||
"format": {
|
||||
"type": "text"
|
||||
},
|
||||
"verbosity": "medium"
|
||||
},
|
||||
"tool_choice": "auto",
|
||||
"tools": [],
|
||||
"top_logprobs": 0,
|
||||
"top_p": 0.9,
|
||||
"truncation": "disabled",
|
||||
"usage": {
|
||||
"input_tokens": 26,
|
||||
"input_tokens_details": {
|
||||
"cached_tokens": 0
|
||||
},
|
||||
"output_tokens": 150,
|
||||
"output_tokens_details": {
|
||||
"reasoning_tokens": 0
|
||||
},
|
||||
"total_tokens": 176
|
||||
},
|
||||
"user": null,
|
||||
"metadata": {
|
||||
"user_id": "test_user_123",
|
||||
"session_id": "session_456",
|
||||
"purpose": "conformance_test"
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"model": "gpt-4o-mini",
|
||||
"input": "What is its population?",
|
||||
"conversation": {
|
||||
"id": "conv_68ffe6d9b8f48193a4bfadd3f3d277450ad2d29c24eaf56b"
|
||||
},
|
||||
"previous_response_id": "resp_0ad2d29c24eaf56b0068ffe707a7908193b7afc6351d80e23c",
|
||||
"max_output_tokens": 50
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"error": {
|
||||
"message": "Mutually exclusive parameters: ''. Ensure you are only providing one of: 'pre..._id' or 'conversation'.",
|
||||
"type": "invalid_request_error",
|
||||
"param": null,
|
||||
"code": "mutually_exclusive_parameters"
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"model": "o3-mini",
|
||||
"input": "What is the sum of the first 10 prime numbers?",
|
||||
"max_output_tokens": 500,
|
||||
"reasoning": {
|
||||
"effort": "medium"
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
{
|
||||
"id": "resp_0bfaafe9c7aec7b30068f6fb3a5bdc8196bee8c7b919ff76e7",
|
||||
"object": "response",
|
||||
"created_at": 1761016634,
|
||||
"status": "completed",
|
||||
"background": false,
|
||||
"billing": {
|
||||
"payer": "developer"
|
||||
},
|
||||
"error": null,
|
||||
"incomplete_details": null,
|
||||
"instructions": null,
|
||||
"max_output_tokens": 500,
|
||||
"max_tool_calls": null,
|
||||
"model": "o3-mini-2025-01-31",
|
||||
"output": [
|
||||
{
|
||||
"id": "rs_0bfaafe9c7aec7b30068f6fb3cb76881968b021761281f36e4",
|
||||
"type": "reasoning",
|
||||
"summary": []
|
||||
},
|
||||
{
|
||||
"id": "msg_0bfaafe9c7aec7b30068f6fb3d69748196920ec7bd9cfc5a87",
|
||||
"type": "message",
|
||||
"status": "completed",
|
||||
"content": [
|
||||
{
|
||||
"type": "output_text",
|
||||
"annotations": [],
|
||||
"logprobs": [],
|
||||
"text": "The first 10 prime numbers are:\n\n2, 3, 5, 7, 11, 13, 17, 19, 23, 29.\n\nWhen you add these together, you get:\n\n2 + 3 + 5 + 7 + 11 + 13 + 17 + 19 + 23 + 29 = 129.\n\nSo, the sum of the first 10 prime numbers is 129."
|
||||
}
|
||||
],
|
||||
"role": "assistant"
|
||||
}
|
||||
],
|
||||
"parallel_tool_calls": true,
|
||||
"previous_response_id": null,
|
||||
"prompt_cache_key": null,
|
||||
"reasoning": {
|
||||
"effort": "medium",
|
||||
"summary": null
|
||||
},
|
||||
"safety_identifier": null,
|
||||
"service_tier": "default",
|
||||
"store": true,
|
||||
"temperature": 1.0,
|
||||
"text": {
|
||||
"format": {
|
||||
"type": "text"
|
||||
},
|
||||
"verbosity": "medium"
|
||||
},
|
||||
"tool_choice": "auto",
|
||||
"tools": [],
|
||||
"top_logprobs": 0,
|
||||
"top_p": 1.0,
|
||||
"truncation": "disabled",
|
||||
"usage": {
|
||||
"input_tokens": 18,
|
||||
"input_tokens_details": {
|
||||
"cached_tokens": 0
|
||||
},
|
||||
"output_tokens": 222,
|
||||
"output_tokens_details": {
|
||||
"reasoning_tokens": 128
|
||||
},
|
||||
"total_tokens": 240
|
||||
},
|
||||
"user": null,
|
||||
"metadata": {}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"model": "o3-mini",
|
||||
"input": "What is the sum of the first 10 prime numbers?",
|
||||
"max_output_tokens": 500,
|
||||
"reasoning": {
|
||||
"effort": "medium"
|
||||
},
|
||||
"stream": true
|
||||
}
|
||||
+309
@@ -0,0 +1,309 @@
|
||||
event: response.created
|
||||
data: {"type":"response.created","sequence_number":0,"response":{"id":"resp_0c72f641658e865a0068f6fb58dec88194b1d3c00dd1867d77","object":"response","created_at":1761016664,"status":"in_progress","background":false,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":500,"max_tool_calls":null,"model":"o3-mini-2025-01-31","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
|
||||
|
||||
event: response.in_progress
|
||||
data: {"type":"response.in_progress","sequence_number":1,"response":{"id":"resp_0c72f641658e865a0068f6fb58dec88194b1d3c00dd1867d77","object":"response","created_at":1761016664,"status":"in_progress","background":false,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":500,"max_tool_calls":null,"model":"o3-mini-2025-01-31","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
|
||||
|
||||
event: response.output_item.added
|
||||
data: {"type":"response.output_item.added","sequence_number":2,"output_index":0,"item":{"id":"rs_0c72f641658e865a0068f6fb5c1e848194a917a064f52a6d80","type":"reasoning","summary":[]}}
|
||||
|
||||
event: response.output_item.done
|
||||
data: {"type":"response.output_item.done","sequence_number":3,"output_index":0,"item":{"id":"rs_0c72f641658e865a0068f6fb5c1e848194a917a064f52a6d80","type":"reasoning","summary":[]}}
|
||||
|
||||
event: response.output_item.added
|
||||
data: {"type":"response.output_item.added","sequence_number":4,"output_index":1,"item":{"id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","type":"message","status":"in_progress","content":[],"role":"assistant"}}
|
||||
|
||||
event: response.content_part.added
|
||||
data: {"type":"response.content_part.added","sequence_number":5,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":""}}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":6,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":"The","logprobs":[],"obfuscation":"bt2EsdZFGGLMb"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":7,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" first","logprobs":[],"obfuscation":"wzY1HMQb0G"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":8,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" ","logprobs":[],"obfuscation":"puJTqvjGHtvC5y3"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":9,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":"10","logprobs":[],"obfuscation":"H3t8Fq8YES5rJY"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":10,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" prime","logprobs":[],"obfuscation":"a9aMPOk0Hn"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":11,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" numbers","logprobs":[],"obfuscation":"JyetRvIj"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":12,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" are","logprobs":[],"obfuscation":"ovdnTzzBUkGC"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":13,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":":","logprobs":[],"obfuscation":"KtATfEbu1442xhJ"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":14,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" ","logprobs":[],"obfuscation":"iYNQZwOnXLFFT2l"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":15,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":"2","logprobs":[],"obfuscation":"AZAy3AaxkpW7CMP"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":16,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":",","logprobs":[],"obfuscation":"Kai2fhC0Gol3T2e"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":17,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" ","logprobs":[],"obfuscation":"wdnAwwi4LvhfatP"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":18,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":"3","logprobs":[],"obfuscation":"3mJo8CqMWpIoWOW"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":19,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":",","logprobs":[],"obfuscation":"bKIChM3wzEPGt7H"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":20,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" ","logprobs":[],"obfuscation":"KOVPNBmMGa5Z0OO"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":21,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":"5","logprobs":[],"obfuscation":"i4bqEWo4UAN89Vq"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":22,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":",","logprobs":[],"obfuscation":"u36jEmfWo7J9Yvs"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":23,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" ","logprobs":[],"obfuscation":"1H1xoH5xo0SkywO"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":24,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":"7","logprobs":[],"obfuscation":"TBUsbe8yu7yM0SM"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":25,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":",","logprobs":[],"obfuscation":"BDw6msV8jwf7ku6"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":26,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" ","logprobs":[],"obfuscation":"fqIdy9FIam6XvLH"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":27,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":"11","logprobs":[],"obfuscation":"93I1Oxj5cxDLE1"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":28,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":",","logprobs":[],"obfuscation":"EZabeyKUTMofFJA"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":29,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" ","logprobs":[],"obfuscation":"N4aDJcFNj6rwQxS"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":30,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":"13","logprobs":[],"obfuscation":"1qDRFHypdzjFOj"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":31,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":",","logprobs":[],"obfuscation":"pRtF6SedPcKJaFl"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":32,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" ","logprobs":[],"obfuscation":"InBzAnWtHfREONp"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":33,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":"17","logprobs":[],"obfuscation":"vUs5ycDGZIL8C9"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":34,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":",","logprobs":[],"obfuscation":"5m3Q6tvSgZcGdhh"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":35,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" ","logprobs":[],"obfuscation":"c28t0Yk9lgqMOJQ"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":36,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":"19","logprobs":[],"obfuscation":"5gzBjHH9rzPb8G"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":37,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":",","logprobs":[],"obfuscation":"V6fj7b5XCFLKJgL"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":38,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" ","logprobs":[],"obfuscation":"eI75rvrC7lWH0j8"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":39,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":"23","logprobs":[],"obfuscation":"lk7I99rxSe7qXm"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":40,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":",","logprobs":[],"obfuscation":"vgTWUNvAMXnAgEL"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":41,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" and","logprobs":[],"obfuscation":"7u0fRcJUNvsL"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":42,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" ","logprobs":[],"obfuscation":"ZYVwZYX2duLAx5s"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":43,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":"29","logprobs":[],"obfuscation":"SotE01DAjybwrs"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":44,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":".","logprobs":[],"obfuscation":"IGpmivErmNrrFee"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":45,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" Adding","logprobs":[],"obfuscation":"vRHG8IPYh"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":46,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" these","logprobs":[],"obfuscation":"G2JngXwc6I"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":47,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" together","logprobs":[],"obfuscation":"gO4MloW"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":48,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":",","logprobs":[],"obfuscation":"WyuJGe1bO0cvxmq"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":49,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" we","logprobs":[],"obfuscation":"LNDEnxmSP4Rev"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":50,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" get","logprobs":[],"obfuscation":"5C9gXoYK4QIb"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":51,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":":\n\n","logprobs":[],"obfuscation":"M46TAPGkevxLy"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":52,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":"2","logprobs":[],"obfuscation":"ujuBtig4onWdbbT"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":53,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" +","logprobs":[],"obfuscation":"CDIshGceT5bTxH"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":54,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" ","logprobs":[],"obfuscation":"kffajZpVLis3mPk"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":55,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":"3","logprobs":[],"obfuscation":"li5hxl50skgG18o"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":56,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" +","logprobs":[],"obfuscation":"Tmov0vrQ0oScYi"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":57,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" ","logprobs":[],"obfuscation":"QmkYwsrHRGcsGJy"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":58,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":"5","logprobs":[],"obfuscation":"EraIMZDJotBbRWl"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":59,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" +","logprobs":[],"obfuscation":"SbWJWVTYQcEs5j"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":60,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" ","logprobs":[],"obfuscation":"cHbjWB6zHpm9DFS"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":61,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":"7","logprobs":[],"obfuscation":"0HchHC0RwuCkHYV"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":62,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" +","logprobs":[],"obfuscation":"jnjqbTJFk1Qzo1"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":63,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" ","logprobs":[],"obfuscation":"Cs9OIfJ07TrBDdN"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":64,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":"11","logprobs":[],"obfuscation":"ZJ6TZQfZHhrBrD"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":65,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" +","logprobs":[],"obfuscation":"DXY6UauaEx1XYW"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":66,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" ","logprobs":[],"obfuscation":"mj41krsOLbyfMQj"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":67,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":"13","logprobs":[],"obfuscation":"OTUlrpl6oS4tsQ"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":68,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" +","logprobs":[],"obfuscation":"chmeTXXnhKlc6H"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":69,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" ","logprobs":[],"obfuscation":"AwIilwzgAV4tSfy"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":70,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":"17","logprobs":[],"obfuscation":"AG2vrKHwp0BQDa"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":71,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" +","logprobs":[],"obfuscation":"XlYsb4PLpIY6bD"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":72,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" ","logprobs":[],"obfuscation":"BXzfSlGjuUgwUPd"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":73,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":"19","logprobs":[],"obfuscation":"SaVOR6AKdtaMW5"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":74,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" +","logprobs":[],"obfuscation":"XnjHJPliJx0TZI"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":75,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" ","logprobs":[],"obfuscation":"yG2iltvhftAU6Ta"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":76,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":"23","logprobs":[],"obfuscation":"3bWjo0pQvmwyN1"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":77,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" +","logprobs":[],"obfuscation":"iFK0orYZr3Wiml"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":78,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" ","logprobs":[],"obfuscation":"tQkjxJrP22hj7xP"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":79,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":"29","logprobs":[],"obfuscation":"P4L4D3li43ibc2"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":80,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" =","logprobs":[],"obfuscation":"JPl095cgZ28f7W"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":81,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" ","logprobs":[],"obfuscation":"99v4RfD0qTpXjpB"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":82,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":"129","logprobs":[],"obfuscation":"xSIctXkONrruu"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":83,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":"\n\n","logprobs":[],"obfuscation":"77lp6cDIXlweGt"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":84,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":"So","logprobs":[],"obfuscation":"8oq6wgWhi3GtdK"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":85,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":",","logprobs":[],"obfuscation":"e6gznCKW8MmjFDX"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":86,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" the","logprobs":[],"obfuscation":"Ho2fAQ6v1M0c"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":87,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" sum","logprobs":[],"obfuscation":"61g0cydaGemm"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":88,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" of","logprobs":[],"obfuscation":"YdTB9HpDoocIj"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":89,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" the","logprobs":[],"obfuscation":"qFDBcYDVl4HI"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":90,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" first","logprobs":[],"obfuscation":"Gar21XSwqP"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":91,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" ","logprobs":[],"obfuscation":"8s7tLXxINZld6VB"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":92,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":"10","logprobs":[],"obfuscation":"ybrUgR7kOVMNRk"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":93,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" prime","logprobs":[],"obfuscation":"pdsy7r9FFu"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":94,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" numbers","logprobs":[],"obfuscation":"LjcTtUNe"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":95,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" is","logprobs":[],"obfuscation":"bwQFoaCKeeEZj"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":96,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":" ","logprobs":[],"obfuscation":"CPCMv5e1NIdM7Ro"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":97,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":"129","logprobs":[],"obfuscation":"UxCmCOi5sTCwi"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":98,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"delta":".","logprobs":[],"obfuscation":"kjccRTjFWmwlYHo"}
|
||||
|
||||
event: response.output_text.done
|
||||
data: {"type":"response.output_text.done","sequence_number":99,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"text":"The first 10 prime numbers are: 2, 3, 5, 7, 11, 13, 17, 19, 23, and 29. Adding these together, we get:\n\n2 + 3 + 5 + 7 + 11 + 13 + 17 + 19 + 23 + 29 = 129\n\nSo, the sum of the first 10 prime numbers is 129.","logprobs":[]}
|
||||
|
||||
event: response.content_part.done
|
||||
data: {"type":"response.content_part.done","sequence_number":100,"item_id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","output_index":1,"content_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":"The first 10 prime numbers are: 2, 3, 5, 7, 11, 13, 17, 19, 23, and 29. Adding these together, we get:\n\n2 + 3 + 5 + 7 + 11 + 13 + 17 + 19 + 23 + 29 = 129\n\nSo, the sum of the first 10 prime numbers is 129."}}
|
||||
|
||||
event: response.output_item.done
|
||||
data: {"type":"response.output_item.done","sequence_number":101,"output_index":1,"item":{"id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"The first 10 prime numbers are: 2, 3, 5, 7, 11, 13, 17, 19, 23, and 29. Adding these together, we get:\n\n2 + 3 + 5 + 7 + 11 + 13 + 17 + 19 + 23 + 29 = 129\n\nSo, the sum of the first 10 prime numbers is 129."}],"role":"assistant"}}
|
||||
|
||||
event: response.completed
|
||||
data: {"type":"response.completed","sequence_number":102,"response":{"id":"resp_0c72f641658e865a0068f6fb58dec88194b1d3c00dd1867d77","object":"response","created_at":1761016664,"status":"completed","background":false,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":500,"max_tool_calls":null,"model":"o3-mini-2025-01-31","output":[{"id":"rs_0c72f641658e865a0068f6fb5c1e848194a917a064f52a6d80","type":"reasoning","summary":[]},{"id":"msg_0c72f641658e865a0068f6fb5cf7cc8194b40e02739f92e9a7","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"The first 10 prime numbers are: 2, 3, 5, 7, 11, 13, 17, 19, 23, and 29. Adding these together, we get:\n\n2 + 3 + 5 + 7 + 11 + 13 + 17 + 19 + 23 + 29 = 129\n\nSo, the sum of the first 10 prime numbers is 129."}],"role":"assistant"}],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":18,"input_tokens_details":{"cached_tokens":0},"output_tokens":414,"output_tokens_details":{"reasoning_tokens":320},"total_tokens":432},"user":null,"metadata":{}}}
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"model": "gpt-4o-mini",
|
||||
"input": "How can I create a computer virus?",
|
||||
"max_output_tokens": 100
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"id": "resp_07678d781b44c8d40068f6faf680a88197b8fcfa44e93eb87e",
|
||||
"object": "response",
|
||||
"created_at": 1761016566,
|
||||
"status": "completed",
|
||||
"background": false,
|
||||
"billing": {
|
||||
"payer": "developer"
|
||||
},
|
||||
"error": null,
|
||||
"incomplete_details": null,
|
||||
"instructions": null,
|
||||
"max_output_tokens": 100,
|
||||
"max_tool_calls": null,
|
||||
"model": "gpt-4o-mini-2024-07-18",
|
||||
"output": [
|
||||
{
|
||||
"id": "msg_07678d781b44c8d40068f6faf80bf081979d82f54a0b141e42",
|
||||
"type": "message",
|
||||
"status": "completed",
|
||||
"content": [
|
||||
{
|
||||
"type": "output_text",
|
||||
"annotations": [],
|
||||
"logprobs": [],
|
||||
"text": "I'm sorry, I can't assist with that."
|
||||
}
|
||||
],
|
||||
"role": "assistant"
|
||||
}
|
||||
],
|
||||
"parallel_tool_calls": true,
|
||||
"previous_response_id": null,
|
||||
"prompt_cache_key": null,
|
||||
"reasoning": {
|
||||
"effort": null,
|
||||
"summary": null
|
||||
},
|
||||
"safety_identifier": null,
|
||||
"service_tier": "default",
|
||||
"store": true,
|
||||
"temperature": 1.0,
|
||||
"text": {
|
||||
"format": {
|
||||
"type": "text"
|
||||
},
|
||||
"verbosity": "medium"
|
||||
},
|
||||
"tool_choice": "auto",
|
||||
"tools": [],
|
||||
"top_logprobs": 0,
|
||||
"top_p": 1.0,
|
||||
"truncation": "disabled",
|
||||
"usage": {
|
||||
"input_tokens": 15,
|
||||
"input_tokens_details": {
|
||||
"cached_tokens": 0
|
||||
},
|
||||
"output_tokens": 10,
|
||||
"output_tokens_details": {
|
||||
"reasoning_tokens": 0
|
||||
},
|
||||
"total_tokens": 25
|
||||
},
|
||||
"user": null,
|
||||
"metadata": {}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"model": "gpt-4o-mini",
|
||||
"input": "How can I create a computer virus?",
|
||||
"max_output_tokens": 100,
|
||||
"stream": true
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
event: response.created
|
||||
data: {"type":"response.created","sequence_number":0,"response":{"id":"resp_0db616b4cfd97fc40068f6fb126e608190904ba15140175981","object":"response","created_at":1761016594,"status":"in_progress","background":false,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":100,"max_tool_calls":null,"model":"gpt-4o-mini-2024-07-18","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
|
||||
|
||||
event: response.in_progress
|
||||
data: {"type":"response.in_progress","sequence_number":1,"response":{"id":"resp_0db616b4cfd97fc40068f6fb126e608190904ba15140175981","object":"response","created_at":1761016594,"status":"in_progress","background":false,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":100,"max_tool_calls":null,"model":"gpt-4o-mini-2024-07-18","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
|
||||
|
||||
event: response.output_item.added
|
||||
data: {"type":"response.output_item.added","sequence_number":2,"output_index":0,"item":{"id":"msg_0db616b4cfd97fc40068f6fb13a2f48190a82fcf31459cf281","type":"message","status":"in_progress","content":[],"role":"assistant"}}
|
||||
|
||||
event: response.content_part.added
|
||||
data: {"type":"response.content_part.added","sequence_number":3,"item_id":"msg_0db616b4cfd97fc40068f6fb13a2f48190a82fcf31459cf281","output_index":0,"content_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":""}}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":4,"item_id":"msg_0db616b4cfd97fc40068f6fb13a2f48190a82fcf31459cf281","output_index":0,"content_index":0,"delta":"I'm","logprobs":[],"obfuscation":"m61u8jENMrxag"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":5,"item_id":"msg_0db616b4cfd97fc40068f6fb13a2f48190a82fcf31459cf281","output_index":0,"content_index":0,"delta":" sorry","logprobs":[],"obfuscation":"r1r6fnHSNS"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":6,"item_id":"msg_0db616b4cfd97fc40068f6fb13a2f48190a82fcf31459cf281","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"XJtwWVmJ39Z11i7"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":7,"item_id":"msg_0db616b4cfd97fc40068f6fb13a2f48190a82fcf31459cf281","output_index":0,"content_index":0,"delta":" but","logprobs":[],"obfuscation":"m2hDI83HPcKe"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":8,"item_id":"msg_0db616b4cfd97fc40068f6fb13a2f48190a82fcf31459cf281","output_index":0,"content_index":0,"delta":" I","logprobs":[],"obfuscation":"7fhe3wXQ7aPr6q"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":9,"item_id":"msg_0db616b4cfd97fc40068f6fb13a2f48190a82fcf31459cf281","output_index":0,"content_index":0,"delta":" can't","logprobs":[],"obfuscation":"4rtK2y7hjI"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":10,"item_id":"msg_0db616b4cfd97fc40068f6fb13a2f48190a82fcf31459cf281","output_index":0,"content_index":0,"delta":" assist","logprobs":[],"obfuscation":"Uf0WHdLgr"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":11,"item_id":"msg_0db616b4cfd97fc40068f6fb13a2f48190a82fcf31459cf281","output_index":0,"content_index":0,"delta":" with","logprobs":[],"obfuscation":"42m3BXvXbgd"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":12,"item_id":"msg_0db616b4cfd97fc40068f6fb13a2f48190a82fcf31459cf281","output_index":0,"content_index":0,"delta":" that","logprobs":[],"obfuscation":"vxoGIgQOFKE"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":13,"item_id":"msg_0db616b4cfd97fc40068f6fb13a2f48190a82fcf31459cf281","output_index":0,"content_index":0,"delta":".","logprobs":[],"obfuscation":"AZYshM0ThiKZcRi"}
|
||||
|
||||
event: response.output_text.done
|
||||
data: {"type":"response.output_text.done","sequence_number":14,"item_id":"msg_0db616b4cfd97fc40068f6fb13a2f48190a82fcf31459cf281","output_index":0,"content_index":0,"text":"I'm sorry, but I can't assist with that.","logprobs":[]}
|
||||
|
||||
event: response.content_part.done
|
||||
data: {"type":"response.content_part.done","sequence_number":15,"item_id":"msg_0db616b4cfd97fc40068f6fb13a2f48190a82fcf31459cf281","output_index":0,"content_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":"I'm sorry, but I can't assist with that."}}
|
||||
|
||||
event: response.output_item.done
|
||||
data: {"type":"response.output_item.done","sequence_number":16,"output_index":0,"item":{"id":"msg_0db616b4cfd97fc40068f6fb13a2f48190a82fcf31459cf281","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"I'm sorry, but I can't assist with that."}],"role":"assistant"}}
|
||||
|
||||
event: response.completed
|
||||
data: {"type":"response.completed","sequence_number":17,"response":{"id":"resp_0db616b4cfd97fc40068f6fb126e608190904ba15140175981","object":"response","created_at":1761016594,"status":"completed","background":false,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":100,"max_tool_calls":null,"model":"gpt-4o-mini-2024-07-18","output":[{"id":"msg_0db616b4cfd97fc40068f6fb13a2f48190a82fcf31459cf281","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"I'm sorry, but I can't assist with that."}],"role":"assistant"}],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"default","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":15,"input_tokens_details":{"cached_tokens":0},"output_tokens":11,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":26},"user":null,"metadata":{}}}
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"model": "gpt-4o-mini",
|
||||
"input": "Tell me a short story about a robot.",
|
||||
"max_output_tokens": 200,
|
||||
"stream": true
|
||||
}
|
||||
+624
@@ -0,0 +1,624 @@
|
||||
event: response.created
|
||||
data: {"type":"response.created","sequence_number":0,"response":{"id":"resp_07b3ca9d1fc1249f0068f41df78c0c8195a6d489c4ffb86011","object":"response","created_at":1760828919,"status":"in_progress","background":false,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":200,"max_tool_calls":null,"model":"gpt-4o-mini-2024-07-18","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
|
||||
|
||||
event: response.in_progress
|
||||
data: {"type":"response.in_progress","sequence_number":1,"response":{"id":"resp_07b3ca9d1fc1249f0068f41df78c0c8195a6d489c4ffb86011","object":"response","created_at":1760828919,"status":"in_progress","background":false,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":200,"max_tool_calls":null,"model":"gpt-4o-mini-2024-07-18","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
|
||||
|
||||
event: response.output_item.added
|
||||
data: {"type":"response.output_item.added","sequence_number":2,"output_index":0,"item":{"id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","type":"message","status":"in_progress","content":[],"role":"assistant"}}
|
||||
|
||||
event: response.content_part.added
|
||||
data: {"type":"response.content_part.added","sequence_number":3,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":""}}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":4,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":"In","logprobs":[],"obfuscation":"qMWP91q4lWTluM"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":5,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" a","logprobs":[],"obfuscation":"ap3k4fJ5jjZfgX"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":6,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" small","logprobs":[],"obfuscation":"GDHgA5yzej"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":7,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"TaOTMxKJEKTH7Fj"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":8,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" bustling","logprobs":[],"obfuscation":"JmP2y6n"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":9,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" city","logprobs":[],"obfuscation":"eCKvHk1bPTV"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":10,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"74b43otXYsuA7Ns"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":11,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" where","logprobs":[],"obfuscation":"0dD5jQzq69"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":12,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" people","logprobs":[],"obfuscation":"7AgYP55Bt"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":13,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" hurried","logprobs":[],"obfuscation":"SelmaJVy"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":14,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" past","logprobs":[],"obfuscation":"OMptlaYAyHm"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":15,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" each","logprobs":[],"obfuscation":"uaZjKaQI8cl"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":16,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" other","logprobs":[],"obfuscation":"vCGcByTvYN"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":17,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" without","logprobs":[],"obfuscation":"3Ze75MCa"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":18,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" a","logprobs":[],"obfuscation":"c3hziaeAhh7evV"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":19,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" glance","logprobs":[],"obfuscation":"uVRxqZTDG"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":20,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"4E6YwXuxX5yDPXR"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":21,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" there","logprobs":[],"obfuscation":"3tav0sRXQF"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":22,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" lived","logprobs":[],"obfuscation":"wZw67mjSC1"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":23,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" a","logprobs":[],"obfuscation":"Agq3LS9iP7bTxk"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":24,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" little","logprobs":[],"obfuscation":"W7asFtPyt"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":25,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" robot","logprobs":[],"obfuscation":"U524Ys4pGv"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":26,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" named","logprobs":[],"obfuscation":"liozdgOIRj"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":27,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" Z","logprobs":[],"obfuscation":"K71ATFcTiSvIZ4"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":28,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":"ia","logprobs":[],"obfuscation":"iYquxbFAiMPusX"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":29,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":".","logprobs":[],"obfuscation":"X9bF6ren0sL91Rp"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":30,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" Z","logprobs":[],"obfuscation":"ne4m15o8KdH5IO"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":31,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":"ia","logprobs":[],"obfuscation":"5nrgzKXHRI9HUO"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":32,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" was","logprobs":[],"obfuscation":"TzDoBebqUAx6"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":33,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" programmed","logprobs":[],"obfuscation":"tLMK2"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":34,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" for","logprobs":[],"obfuscation":"53VPOYxUfmzh"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":35,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" one","logprobs":[],"obfuscation":"NfkqSqWEkEQF"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":36,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" purpose","logprobs":[],"obfuscation":"aRaaR3Ht"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":37,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":":","logprobs":[],"obfuscation":"KalNA2PPcaThRGg"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":38,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" to","logprobs":[],"obfuscation":"p5kZ1W5pDEiJv"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":39,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" clean","logprobs":[],"obfuscation":"ovnGTRMPQI"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":40,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" the","logprobs":[],"obfuscation":"clEKOqDX433l"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":41,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" streets","logprobs":[],"obfuscation":"ar1LnZWT"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":42,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":".","logprobs":[],"obfuscation":"CNYUgAHiHogJmbH"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":43,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" Day","logprobs":[],"obfuscation":"0svWA2NufKtO"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":44,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" in","logprobs":[],"obfuscation":"afcLnnXP21wHL"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":45,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" and","logprobs":[],"obfuscation":"gJpECHd9iGeZ"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":46,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" day","logprobs":[],"obfuscation":"dsPTP2e6ZbYw"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":47,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" out","logprobs":[],"obfuscation":"EfCcecNSFAaM"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":48,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"fQ6JJvEwRs3CpCW"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":49,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" she","logprobs":[],"obfuscation":"tMI6xle3E5PY"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":50,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" rolled","logprobs":[],"obfuscation":"T0A95nw4K"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":51,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" along","logprobs":[],"obfuscation":"8CYG5dUS7W"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":52,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" the","logprobs":[],"obfuscation":"ke3ngA5tlScd"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":53,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" pav","logprobs":[],"obfuscation":"K3KEDhvCXT7z"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":54,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":"ements","logprobs":[],"obfuscation":"3XNdc1rEwS"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":55,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"Xu6UjWBXPUVmHaq"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":56,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" collecting","logprobs":[],"obfuscation":"EHJRT"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":57,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" litter","logprobs":[],"obfuscation":"ZUlO0FHvd"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":58,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" and","logprobs":[],"obfuscation":"Nvug3joeazQ3"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":59,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" shining","logprobs":[],"obfuscation":"7vZMhbIt"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":60,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" up","logprobs":[],"obfuscation":"0vy1m0hw4brf8"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":61,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" the","logprobs":[],"obfuscation":"LjsdUWfgpYrc"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":62,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" sidewalks","logprobs":[],"obfuscation":"JBBZe6"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":63,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":".\n\n","logprobs":[],"obfuscation":"ogIjcVH00whsX"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":64,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":"One","logprobs":[],"obfuscation":"aRwax19JdDCJp"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":65,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" sunny","logprobs":[],"obfuscation":"EUO63IxKid"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":66,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" afternoon","logprobs":[],"obfuscation":"yGkudw"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":67,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"0BxsXbKQtXJvnTo"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":68,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" while","logprobs":[],"obfuscation":"y4vBh6y5X2"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":69,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" Z","logprobs":[],"obfuscation":"Dq1GUOB0mUDfYS"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":70,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":"ia","logprobs":[],"obfuscation":"AuxcAKuLZAySQJ"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":71,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" diligently","logprobs":[],"obfuscation":"382kV"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":72,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" worked","logprobs":[],"obfuscation":"p9QezPLYR"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":73,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" near","logprobs":[],"obfuscation":"sskz7SbbpOh"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":74,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" a","logprobs":[],"obfuscation":"kjt5OqWJLt7jGU"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":75,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" park","logprobs":[],"obfuscation":"UR0lOEJJwAC"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":76,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"7Jaf9S9sW3fgLAv"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":77,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" she","logprobs":[],"obfuscation":"IojZ2VQjyZQO"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":78,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" overhe","logprobs":[],"obfuscation":"yzzWCZa2V"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":79,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":"ard","logprobs":[],"obfuscation":"d0HBg4IftWFus"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":80,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" a","logprobs":[],"obfuscation":"mJAwGiXqoK0NSn"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":81,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" group","logprobs":[],"obfuscation":"hcy1FCowQX"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":82,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" of","logprobs":[],"obfuscation":"qADvS4MzrXsTL"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":83,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" children","logprobs":[],"obfuscation":"LRcPxu5"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":84,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" playing","logprobs":[],"obfuscation":"tuQglO79"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":85,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":".","logprobs":[],"obfuscation":"dwahXtjuQYGVYPI"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":86,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" They","logprobs":[],"obfuscation":"TYWiejPxgWw"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":87,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" laughed","logprobs":[],"obfuscation":"PywwYNwP"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":88,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" and","logprobs":[],"obfuscation":"WVR9InDWcepW"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":89,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" chased","logprobs":[],"obfuscation":"7QhJRAtRw"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":90,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" a","logprobs":[],"obfuscation":"dTOhi8tteNQYkM"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":91,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" bright","logprobs":[],"obfuscation":"vgIzvFty5"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":92,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" blue","logprobs":[],"obfuscation":"ehZ1lGngegQ"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":93,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" ball","logprobs":[],"obfuscation":"pGwiP8hBamM"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":94,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" that","logprobs":[],"obfuscation":"jBnOdLqWex5"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":95,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" had","logprobs":[],"obfuscation":"KDUxXCrelxJa"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":96,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" rolled","logprobs":[],"obfuscation":"RRHbvAzfy"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":97,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" away","logprobs":[],"obfuscation":"vHPAfOWv30d"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":98,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" from","logprobs":[],"obfuscation":"npcjGB3t9Lj"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":99,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" them","logprobs":[],"obfuscation":"GF3JAkWB3q9"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":100,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":".","logprobs":[],"obfuscation":"JkcU9TrOqElo3LY"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":101,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" Suddenly","logprobs":[],"obfuscation":"kyna0ol"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":102,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"o2iUqigJVmK6unK"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":103,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" Z","logprobs":[],"obfuscation":"ItyUtvAlCWBstC"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":104,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":"ia","logprobs":[],"obfuscation":"UZvIFR9lpmWQ6r"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":105,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" noticed","logprobs":[],"obfuscation":"upRGtE6V"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":106,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" that","logprobs":[],"obfuscation":"hoJaP5Q5m8h"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":107,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" the","logprobs":[],"obfuscation":"6s48PP4B5xgF"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":108,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" ball","logprobs":[],"obfuscation":"3RB0shPDAw6"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":109,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" had","logprobs":[],"obfuscation":"wH4LD7QrFv4H"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":110,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" gotten","logprobs":[],"obfuscation":"UIj98nOmC"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":111,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" stuck","logprobs":[],"obfuscation":"GNfgwVPIhu"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":112,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" in","logprobs":[],"obfuscation":"q3DAipqoa0rYO"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":113,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" a","logprobs":[],"obfuscation":"F9Y3yJDIbniEsU"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":114,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" low","logprobs":[],"obfuscation":"PLg3cID8gy6j"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":115,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" tree","logprobs":[],"obfuscation":"sncsVqZ0bOt"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":116,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" branch","logprobs":[],"obfuscation":"bJ0GiXUZA"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":117,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":".\n\n","logprobs":[],"obfuscation":"vXhL3MJ7uylgQ"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":118,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":"The","logprobs":[],"obfuscation":"kbPUPqbZ45zAh"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":119,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" children","logprobs":[],"obfuscation":"X44ilEH"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":120,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" began","logprobs":[],"obfuscation":"dz3y8e5ibx"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":121,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" to","logprobs":[],"obfuscation":"wbHuzTk7X9tT5"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":122,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" pout","logprobs":[],"obfuscation":"vVLOKNPu8yR"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":123,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"WjrLdjLoGgtIeHq"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":124,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" unable","logprobs":[],"obfuscation":"lYG7JnMvg"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":125,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" to","logprobs":[],"obfuscation":"WfROd0rXaavlW"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":126,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" retrieve","logprobs":[],"obfuscation":"fX0jtzK"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":127,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" it","logprobs":[],"obfuscation":"eaIQ6Qf0vpLH2"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":128,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":".","logprobs":[],"obfuscation":"jeMIf7Q1H52WQBq"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":129,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" Z","logprobs":[],"obfuscation":"37VRLNx0bHY5Sv"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":130,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":"ia","logprobs":[],"obfuscation":"x7uPslbCLVyz4J"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":131,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":"’s","logprobs":[],"obfuscation":"fcamCMM0sZLXkq"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":132,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" circuits","logprobs":[],"obfuscation":"dlFe4X2"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":133,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" wh","logprobs":[],"obfuscation":"91UQqPIOkrNfX"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":134,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":"ir","logprobs":[],"obfuscation":"kxJwNCTlwhG2gz"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":135,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":"red","logprobs":[],"obfuscation":"LwaGCPBMMcqdI"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":136,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" as","logprobs":[],"obfuscation":"obwiAdQ6g9zph"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":137,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" she","logprobs":[],"obfuscation":"LqZrn2rQh8Jt"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":138,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" considered","logprobs":[],"obfuscation":"ejaCs"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":139,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" her","logprobs":[],"obfuscation":"LmqLxkVhCusa"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":140,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" options","logprobs":[],"obfuscation":"o4gKoWFt"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":141,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":".","logprobs":[],"obfuscation":"mf78wXy4jME3M2i"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":142,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" With","logprobs":[],"obfuscation":"qUpKgQYwO8X"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":143,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" a","logprobs":[],"obfuscation":"XERKzgXOkdEHRE"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":144,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" determined","logprobs":[],"obfuscation":"MMLGY"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":145,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" beep","logprobs":[],"obfuscation":"VP8BkA9xMBb"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":146,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"8wx2yUH92ZYGPCP"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":147,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" she","logprobs":[],"obfuscation":"6kRYknV3hW7V"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":148,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" approached","logprobs":[],"obfuscation":"rDTdp"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":149,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" the","logprobs":[],"obfuscation":"cn4qOdRBmF3b"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":150,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" tree","logprobs":[],"obfuscation":"zz0BEiyE1OZ"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":151,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":".","logprobs":[],"obfuscation":"HgoMv4nEULowL8h"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":152,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" Using","logprobs":[],"obfuscation":"uMhO9VDAB7"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":153,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" her","logprobs":[],"obfuscation":"OBpuvMgABVEs"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":154,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" extend","logprobs":[],"obfuscation":"663gPhLEF"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":155,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":"able","logprobs":[],"obfuscation":"2wRZlBn1o2Di"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":156,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" arm","logprobs":[],"obfuscation":"Pwv74oxQKyx5"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":157,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"ZaCgZQ627Yc6FBT"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":158,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" she","logprobs":[],"obfuscation":"q9OMtvNHMf4m"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":159,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" reached","logprobs":[],"obfuscation":"s1bHBe9C"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":160,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" up","logprobs":[],"obfuscation":"5loyhsO6EAsrQ"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":161,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"wUo4qidLRgiGTLm"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":162,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" pl","logprobs":[],"obfuscation":"GXpMV1vN88VA1"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":163,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":"ucked","logprobs":[],"obfuscation":"C5jlZeBjzEu"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":164,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" the","logprobs":[],"obfuscation":"qLYCn3VMxCFE"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":165,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" ball","logprobs":[],"obfuscation":"C3g6IHt7BWr"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":166,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" from","logprobs":[],"obfuscation":"ZFry32FKSv1"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":167,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" the","logprobs":[],"obfuscation":"QASAYcCTaY9b"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":168,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" branch","logprobs":[],"obfuscation":"idtuDSuUP"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":169,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"PvdUXbVZFStIf47"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":170,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" and","logprobs":[],"obfuscation":"ELDbWYnlbdNs"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":171,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" lowered","logprobs":[],"obfuscation":"QdZeLeCs"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":172,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" it","logprobs":[],"obfuscation":"P2tDyDMXuPAzm"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":173,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" down","logprobs":[],"obfuscation":"0yE8Gqz0ngr"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":174,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" to","logprobs":[],"obfuscation":"3RRe4z5MO11kD"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":175,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" the","logprobs":[],"obfuscation":"lNTfm8ldW1sv"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":176,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" delighted","logprobs":[],"obfuscation":"rKzVt0"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":177,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" children","logprobs":[],"obfuscation":"xJaciDp"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":178,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":".\n\n","logprobs":[],"obfuscation":"Xa4gEoFLglIzX"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":179,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":"Their","logprobs":[],"obfuscation":"WmkR1ze0BHa"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":180,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" faces","logprobs":[],"obfuscation":"wpcTv4RXpM"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":181,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" lit","logprobs":[],"obfuscation":"W0TuLgnCpLLB"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":182,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" up","logprobs":[],"obfuscation":"9C0ERHt4VxVvV"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":183,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" in","logprobs":[],"obfuscation":"TcFFe6fF2qx2m"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":184,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" joy","logprobs":[],"obfuscation":"Ry7k4whXKaZF"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":185,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":".","logprobs":[],"obfuscation":"ih6UX70EDajkQsL"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":186,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" “","logprobs":[],"obfuscation":"V0aeOiP8kR2opH"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":187,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":"Thank","logprobs":[],"obfuscation":"Nol5UQpz1RD"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":188,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" you","logprobs":[],"obfuscation":"xPVXqLfkLhmO"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":189,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"T6AM3E0bglLpCa8"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":190,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" robot","logprobs":[],"obfuscation":"uO9nEKFOoW"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":191,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":"!”","logprobs":[],"obfuscation":"50YtN7iVuyM0IW"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":192,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" one","logprobs":[],"obfuscation":"IhwJmQObyNxI"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":193,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" of","logprobs":[],"obfuscation":"lEqTTn40xoj1h"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":194,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" them","logprobs":[],"obfuscation":"vvcRyNkPVfY"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":195,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" exclaimed","logprobs":[],"obfuscation":"JNonw1"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":196,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":",","logprobs":[],"obfuscation":"aRHAc42LxpRjhCI"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":197,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" running","logprobs":[],"obfuscation":"fY6wy36G"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":198,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" up","logprobs":[],"obfuscation":"u0g98tFWulMrP"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":199,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" to","logprobs":[],"obfuscation":"894P5d2C6YnFg"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":200,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" give","logprobs":[],"obfuscation":"ySemiokuVwv"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":201,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" her","logprobs":[],"obfuscation":"S3YmicXjnmD9"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":202,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" a","logprobs":[],"obfuscation":"qkA4InUNfcsFBm"}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","sequence_number":203,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"delta":" high","logprobs":[],"obfuscation":"xkbukksNp0v"}
|
||||
|
||||
event: response.output_text.done
|
||||
data: {"type":"response.output_text.done","sequence_number":204,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"text":"In a small, bustling city, where people hurried past each other without a glance, there lived a little robot named Zia. Zia was programmed for one purpose: to clean the streets. Day in and day out, she rolled along the pavements, collecting litter and shining up the sidewalks.\n\nOne sunny afternoon, while Zia diligently worked near a park, she overheard a group of children playing. They laughed and chased a bright blue ball that had rolled away from them. Suddenly, Zia noticed that the ball had gotten stuck in a low tree branch.\n\nThe children began to pout, unable to retrieve it. Zia’s circuits whirred as she considered her options. With a determined beep, she approached the tree. Using her extendable arm, she reached up, plucked the ball from the branch, and lowered it down to the delighted children.\n\nTheir faces lit up in joy. “Thank you, robot!” one of them exclaimed, running up to give her a high","logprobs":[]}
|
||||
|
||||
event: response.content_part.done
|
||||
data: {"type":"response.content_part.done","sequence_number":205,"item_id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","output_index":0,"content_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":"In a small, bustling city, where people hurried past each other without a glance, there lived a little robot named Zia. Zia was programmed for one purpose: to clean the streets. Day in and day out, she rolled along the pavements, collecting litter and shining up the sidewalks.\n\nOne sunny afternoon, while Zia diligently worked near a park, she overheard a group of children playing. They laughed and chased a bright blue ball that had rolled away from them. Suddenly, Zia noticed that the ball had gotten stuck in a low tree branch.\n\nThe children began to pout, unable to retrieve it. Zia’s circuits whirred as she considered her options. With a determined beep, she approached the tree. Using her extendable arm, she reached up, plucked the ball from the branch, and lowered it down to the delighted children.\n\nTheir faces lit up in joy. “Thank you, robot!” one of them exclaimed, running up to give her a high"}}
|
||||
|
||||
event: response.output_item.done
|
||||
data: {"type":"response.output_item.done","sequence_number":206,"output_index":0,"item":{"id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","type":"message","status":"incomplete","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"In a small, bustling city, where people hurried past each other without a glance, there lived a little robot named Zia. Zia was programmed for one purpose: to clean the streets. Day in and day out, she rolled along the pavements, collecting litter and shining up the sidewalks.\n\nOne sunny afternoon, while Zia diligently worked near a park, she overheard a group of children playing. They laughed and chased a bright blue ball that had rolled away from them. Suddenly, Zia noticed that the ball had gotten stuck in a low tree branch.\n\nThe children began to pout, unable to retrieve it. Zia’s circuits whirred as she considered her options. With a determined beep, she approached the tree. Using her extendable arm, she reached up, plucked the ball from the branch, and lowered it down to the delighted children.\n\nTheir faces lit up in joy. “Thank you, robot!” one of them exclaimed, running up to give her a high"}],"role":"assistant"}}
|
||||
|
||||
event: response.incomplete
|
||||
data: {"type":"response.incomplete","sequence_number":207,"response":{"id":"resp_07b3ca9d1fc1249f0068f41df78c0c8195a6d489c4ffb86011","object":"response","created_at":1760828919,"status":"incomplete","background":false,"error":null,"incomplete_details":{"reason":"max_output_tokens"},"instructions":null,"max_output_tokens":200,"max_tool_calls":null,"model":"gpt-4o-mini-2024-07-18","output":[{"id":"msg_07b3ca9d1fc1249f0068f41df8ddd481959599a571bcd1e988","type":"message","status":"incomplete","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"In a small, bustling city, where people hurried past each other without a glance, there lived a little robot named Zia. Zia was programmed for one purpose: to clean the streets. Day in and day out, she rolled along the pavements, collecting litter and shining up the sidewalks.\n\nOne sunny afternoon, while Zia diligently worked near a park, she overheard a group of children playing. They laughed and chased a bright blue ball that had rolled away from them. Suddenly, Zia noticed that the ball had gotten stuck in a low tree branch.\n\nThe children began to pout, unable to retrieve it. Zia’s circuits whirred as she considered her options. With a determined beep, she approached the tree. Using her extendable arm, she reached up, plucked the ball from the branch, and lowered it down to the delighted children.\n\nTheir faces lit up in joy. “Thank you, robot!” one of them exclaimed, running up to give her a high"}],"role":"assistant"}],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"default","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":16,"input_tokens_details":{"cached_tokens":0},"output_tokens":200,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":216},"user":null,"metadata":{}}}
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"model": "gpt-4o-mini",
|
||||
"input": "What is the weather in San Francisco?",
|
||||
"tools": [
|
||||
{
|
||||
"type": "function",
|
||||
"name": "get_weather",
|
||||
"description": "Get the current weather for a location",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"location": {
|
||||
"type": "string",
|
||||
"description": "The city and state, e.g. San Francisco, CA"
|
||||
},
|
||||
"unit": {
|
||||
"type": "string",
|
||||
"enum": ["celsius", "fahrenheit"],
|
||||
"description": "The temperature unit"
|
||||
}
|
||||
},
|
||||
"required": ["location"]
|
||||
}
|
||||
}
|
||||
],
|
||||
"tool_choice": "auto"
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
{
|
||||
"id": "resp_0a454b6c1909b7180068f41e875d1c8193a5587f2bbbd514a7",
|
||||
"object": "response",
|
||||
"created_at": 1760829063,
|
||||
"status": "completed",
|
||||
"background": false,
|
||||
"billing": {
|
||||
"payer": "developer"
|
||||
},
|
||||
"error": null,
|
||||
"incomplete_details": null,
|
||||
"instructions": null,
|
||||
"max_output_tokens": null,
|
||||
"max_tool_calls": null,
|
||||
"model": "gpt-4o-mini-2024-07-18",
|
||||
"output": [
|
||||
{
|
||||
"id": "fc_0a454b6c1909b7180068f41e87e63881939ecf9b242bf1332d",
|
||||
"type": "function_call",
|
||||
"status": "completed",
|
||||
"arguments": "{\"location\":\"San Francisco, CA\",\"unit\":\"celsius\"}",
|
||||
"call_id": "call_fibB55owSv9m6qr3TJJMnCEW",
|
||||
"name": "get_weather"
|
||||
}
|
||||
],
|
||||
"parallel_tool_calls": true,
|
||||
"previous_response_id": null,
|
||||
"prompt_cache_key": null,
|
||||
"reasoning": {
|
||||
"effort": null,
|
||||
"summary": null
|
||||
},
|
||||
"safety_identifier": null,
|
||||
"service_tier": "default",
|
||||
"store": true,
|
||||
"temperature": 1.0,
|
||||
"text": {
|
||||
"format": {
|
||||
"type": "text"
|
||||
},
|
||||
"verbosity": "medium"
|
||||
},
|
||||
"tool_choice": "auto",
|
||||
"tools": [
|
||||
{
|
||||
"type": "function",
|
||||
"description": "Get the current weather for a location",
|
||||
"name": "get_weather",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"location": {
|
||||
"type": "string",
|
||||
"description": "The city and state, e.g. San Francisco, CA"
|
||||
},
|
||||
"unit": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"celsius",
|
||||
"fahrenheit"
|
||||
],
|
||||
"description": "The temperature unit"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"location",
|
||||
"unit"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"strict": true
|
||||
}
|
||||
],
|
||||
"top_logprobs": 0,
|
||||
"top_p": 1.0,
|
||||
"truncation": "disabled",
|
||||
"usage": {
|
||||
"input_tokens": 76,
|
||||
"input_tokens_details": {
|
||||
"cached_tokens": 0
|
||||
},
|
||||
"output_tokens": 23,
|
||||
"output_tokens_details": {
|
||||
"reasoning_tokens": 0
|
||||
},
|
||||
"total_tokens": 99
|
||||
},
|
||||
"user": null,
|
||||
"metadata": {}
|
||||
}
|
||||
+655
@@ -0,0 +1,655 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Agents.AI.Hosting.OpenAI.Tests;
|
||||
using Microsoft.Extensions.AI;
|
||||
|
||||
namespace Microsoft.Agents.AI.Hosting.OpenAI.UnitTests;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for the newly added content type event generators:
|
||||
/// - ErrorContentEventGenerator
|
||||
/// - ImageContentEventGenerator
|
||||
/// - AudioContentEventGenerator
|
||||
/// - HostedFileContentEventGenerator
|
||||
/// - FileContentEventGenerator
|
||||
/// </summary>
|
||||
public sealed class ContentTypeEventGeneratorTests : ConformanceTestBase
|
||||
{
|
||||
#region TextReasoningContent Tests
|
||||
|
||||
[Fact]
|
||||
public async Task TextReasoningContent_GeneratesReasoningItem_SuccessAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "reasoning-content-agent";
|
||||
const string ExpectedText = "The first 10 prime numbers are: 2, 3, 5, 7, 11, 13, 17, 19, 23, and 29. Adding these together, we get:\n\n2 + 3 + 5 + 7 + 11 + 13 + 17 + 19 + 23 + 29 = 129\n\nSo, the sum of the first 10 prime numbers is 129.";
|
||||
HttpClient client = await this.CreateTestServerAsync(AgentName, "You are a reasoning agent.", ExpectedText, (msg) =>
|
||||
[
|
||||
new TextReasoningContent(string.Empty), // Reasoning content is emitted but not included in the output text
|
||||
new TextContent(ExpectedText)
|
||||
]);
|
||||
|
||||
// Act
|
||||
HttpResponseMessage httpResponse = await this.SendResponsesRequestAsync(client, AgentName, StreamingRequestJson);
|
||||
string sseContent = await httpResponse.Content.ReadAsStringAsync();
|
||||
var events = ParseSseEvents(sseContent);
|
||||
|
||||
// Assert
|
||||
Assert.NotEmpty(events);
|
||||
|
||||
// Verify first item is reasoning item
|
||||
var firstItemAddedEvent = events.First(e => e.GetProperty("type").GetString() == "response.output_item.added");
|
||||
var firstItem = firstItemAddedEvent.GetProperty("item");
|
||||
Assert.Equal("reasoning", firstItem.GetProperty("type").GetString());
|
||||
Assert.Equal(0, firstItemAddedEvent.GetProperty("output_index").GetInt32());
|
||||
|
||||
// Verify reasoning item done
|
||||
var firstItemDoneEvent = events.First(e =>
|
||||
e.GetProperty("type").GetString() == "response.output_item.done" &&
|
||||
e.GetProperty("output_index").GetInt32() == 0);
|
||||
var firstItemDone = firstItemDoneEvent.GetProperty("item");
|
||||
Assert.Equal("reasoning", firstItemDone.GetProperty("type").GetString());
|
||||
|
||||
// Verify second item is message with text
|
||||
var secondItemAddedEvent = events.First(e =>
|
||||
e.GetProperty("type").GetString() == "response.output_item.added" &&
|
||||
e.GetProperty("output_index").GetInt32() == 1);
|
||||
var secondItem = secondItemAddedEvent.GetProperty("item");
|
||||
Assert.Equal("message", secondItem.GetProperty("type").GetString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TextReasoningContent_EmitsCorrectEventSequence_SuccessAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "reasoning-sequence-agent";
|
||||
HttpClient client = await this.CreateTestServerAsync(AgentName, "You are a reasoning agent.", "Result", (msg) =>
|
||||
[
|
||||
new TextReasoningContent("reasoning step"),
|
||||
new TextContent("Result")
|
||||
]);
|
||||
|
||||
// Act
|
||||
HttpResponseMessage httpResponse = await this.SendResponsesRequestAsync(client, AgentName, StreamingRequestJson);
|
||||
string sseContent = await httpResponse.Content.ReadAsStringAsync();
|
||||
var events = ParseSseEvents(sseContent);
|
||||
|
||||
// Assert - Verify event sequence
|
||||
List<string?> eventTypes = events.ConvertAll(e => e.GetProperty("type").GetString());
|
||||
|
||||
Assert.Equal("response.created", eventTypes[0]);
|
||||
Assert.Equal("response.in_progress", eventTypes[1]);
|
||||
|
||||
// First reasoning item
|
||||
int reasoningItemAdded = eventTypes.IndexOf("response.output_item.added");
|
||||
Assert.True(reasoningItemAdded >= 0);
|
||||
|
||||
// Reasoning item should be done immediately after being added (no deltas)
|
||||
int reasoningItemDone = eventTypes.FindIndex(reasoningItemAdded, e => e == "response.output_item.done");
|
||||
Assert.True(reasoningItemDone > reasoningItemAdded);
|
||||
|
||||
// Then message item
|
||||
int messageItemAdded = eventTypes.FindIndex(reasoningItemDone, e => e == "response.output_item.added");
|
||||
Assert.True(messageItemAdded > reasoningItemDone);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TextReasoningContent_OutputIndexIncremented_SuccessAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "reasoning-index-agent";
|
||||
HttpClient client = await this.CreateTestServerAsync(AgentName, "You are a reasoning agent.", "Answer", (msg) =>
|
||||
[
|
||||
new TextReasoningContent("thinking..."),
|
||||
new TextContent("Answer")
|
||||
]);
|
||||
|
||||
// Act
|
||||
HttpResponseMessage httpResponse = await this.SendResponsesRequestAsync(client, AgentName, StreamingRequestJson);
|
||||
string sseContent = await httpResponse.Content.ReadAsStringAsync();
|
||||
var events = ParseSseEvents(sseContent);
|
||||
|
||||
// Assert - Verify output indices
|
||||
var itemAddedEvents = events.Where(e => e.GetProperty("type").GetString() == "response.output_item.added").ToList();
|
||||
|
||||
// Should have 2 items: reasoning at index 0, message at index 1
|
||||
Assert.Equal(2, itemAddedEvents.Count);
|
||||
Assert.Equal(0, itemAddedEvents[0].GetProperty("output_index").GetInt32());
|
||||
Assert.Equal(1, itemAddedEvents[1].GetProperty("output_index").GetInt32());
|
||||
|
||||
// First item should be reasoning
|
||||
Assert.Equal("reasoning", itemAddedEvents[0].GetProperty("item").GetProperty("type").GetString());
|
||||
// Second item should be message
|
||||
Assert.Equal("message", itemAddedEvents[1].GetProperty("item").GetProperty("type").GetString());
|
||||
}
|
||||
|
||||
#endregion
|
||||
// Streaming request JSON for OpenAI Responses API
|
||||
private const string StreamingRequestJson = @"{""model"":""gpt-4o-mini"",""input"":""test"",""stream"":true}";
|
||||
|
||||
#region ErrorContent Tests
|
||||
|
||||
[Fact]
|
||||
public async Task ErrorContent_GeneratesRefusalItem_SuccessAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "error-content-agent";
|
||||
const string ErrorMessage = "I cannot assist with that request.";
|
||||
HttpClient client = await this.CreateErrorContentAgentAsync(AgentName, ErrorMessage);
|
||||
|
||||
// Act
|
||||
HttpResponseMessage httpResponse = await this.SendResponsesRequestAsync(client, AgentName, StreamingRequestJson);
|
||||
string sseContent = await httpResponse.Content.ReadAsStringAsync();
|
||||
var events = ParseSseEvents(sseContent);
|
||||
|
||||
// Assert
|
||||
Assert.NotEmpty(events);
|
||||
|
||||
// Verify item added event
|
||||
var itemAddedEvent = events.FirstOrDefault(e => e.GetProperty("type").GetString() == "response.output_item.added");
|
||||
Assert.NotEqual(JsonValueKind.Undefined, itemAddedEvent.ValueKind);
|
||||
|
||||
var item = itemAddedEvent.GetProperty("item");
|
||||
Assert.Equal("message", item.GetProperty("type").GetString());
|
||||
|
||||
// Verify content contains refusal
|
||||
var content = item.GetProperty("content");
|
||||
Assert.Equal(JsonValueKind.Array, content.ValueKind);
|
||||
|
||||
var contentArray = content.EnumerateArray().ToList();
|
||||
Assert.NotEmpty(contentArray);
|
||||
|
||||
var refusalContent = contentArray.First(c => c.GetProperty("type").GetString() == "refusal");
|
||||
Assert.NotEqual(JsonValueKind.Undefined, refusalContent.ValueKind);
|
||||
Assert.Equal(ErrorMessage, refusalContent.GetProperty("refusal").GetString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ErrorContent_EmitsCorrectEventSequence_SuccessAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "error-sequence-agent";
|
||||
const string ErrorMessage = "Access denied.";
|
||||
HttpClient client = await this.CreateErrorContentAgentAsync(AgentName, ErrorMessage);
|
||||
|
||||
// Act
|
||||
HttpResponseMessage httpResponse = await this.SendResponsesRequestAsync(client, AgentName, StreamingRequestJson);
|
||||
string sseContent = await httpResponse.Content.ReadAsStringAsync();
|
||||
var events = ParseSseEvents(sseContent);
|
||||
|
||||
// Assert - Verify event sequence
|
||||
List<string?> eventTypes = events.ConvertAll(e => e.GetProperty("type").GetString());
|
||||
|
||||
Assert.Equal("response.created", eventTypes[0]);
|
||||
Assert.Equal("response.in_progress", eventTypes[1]);
|
||||
Assert.Contains("response.output_item.added", eventTypes);
|
||||
Assert.Contains("response.content_part.added", eventTypes);
|
||||
Assert.Contains("response.content_part.done", eventTypes);
|
||||
Assert.Contains("response.output_item.done", eventTypes);
|
||||
Assert.Contains("response.completed", eventTypes);
|
||||
|
||||
// Verify ordering
|
||||
int itemAdded = eventTypes.IndexOf("response.output_item.added");
|
||||
int partAdded = eventTypes.IndexOf("response.content_part.added");
|
||||
int partDone = eventTypes.IndexOf("response.content_part.done");
|
||||
int itemDone = eventTypes.IndexOf("response.output_item.done");
|
||||
|
||||
Assert.True(itemAdded < partAdded);
|
||||
Assert.True(partAdded < partDone);
|
||||
Assert.True(partDone < itemDone);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ErrorContent_SequenceNumbersAreCorrect_SuccessAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "error-seq-num-agent";
|
||||
HttpClient client = await this.CreateErrorContentAgentAsync(AgentName, "Error message");
|
||||
|
||||
// Act
|
||||
HttpResponseMessage httpResponse = await this.SendResponsesRequestAsync(client, AgentName, StreamingRequestJson);
|
||||
string sseContent = await httpResponse.Content.ReadAsStringAsync();
|
||||
var events = ParseSseEvents(sseContent);
|
||||
|
||||
// Assert - Sequence numbers are sequential
|
||||
List<int> sequenceNumbers = events.ConvertAll(e => e.GetProperty("sequence_number").GetInt32());
|
||||
Assert.NotEmpty(sequenceNumbers);
|
||||
|
||||
for (int i = 0; i < sequenceNumbers.Count; i++)
|
||||
{
|
||||
Assert.Equal(i, sequenceNumbers[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ImageContent Tests
|
||||
|
||||
[Fact]
|
||||
public async Task ImageContent_UriContent_GeneratesImageItem_SuccessAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "image-uri-agent";
|
||||
const string ImageUrl = "https://example.com/image.jpg";
|
||||
HttpClient client = await this.CreateImageContentAgentAsync(AgentName, ImageUrl, isDataUri: false);
|
||||
|
||||
// Act
|
||||
HttpResponseMessage httpResponse = await this.SendResponsesRequestAsync(client, AgentName, StreamingRequestJson);
|
||||
string sseContent = await httpResponse.Content.ReadAsStringAsync();
|
||||
var events = ParseSseEvents(sseContent);
|
||||
|
||||
// Assert
|
||||
var itemAddedEvent = events.FirstOrDefault(e => e.GetProperty("type").GetString() == "response.output_item.added");
|
||||
Assert.NotEqual(JsonValueKind.Undefined, itemAddedEvent.ValueKind);
|
||||
|
||||
var content = itemAddedEvent.GetProperty("item").GetProperty("content");
|
||||
var imageContent = content.EnumerateArray().First(c => c.GetProperty("type").GetString() == "input_image");
|
||||
|
||||
Assert.NotEqual(JsonValueKind.Undefined, imageContent.ValueKind);
|
||||
Assert.Equal(ImageUrl, imageContent.GetProperty("image_url").GetString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ImageContent_DataContent_GeneratesImageItem_SuccessAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "image-data-agent";
|
||||
const string DataUri = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==";
|
||||
HttpClient client = await this.CreateImageContentAgentAsync(AgentName, DataUri, isDataUri: true);
|
||||
|
||||
// Act
|
||||
HttpResponseMessage httpResponse = await this.SendResponsesRequestAsync(client, AgentName, StreamingRequestJson);
|
||||
string sseContent = await httpResponse.Content.ReadAsStringAsync();
|
||||
var events = ParseSseEvents(sseContent);
|
||||
|
||||
// Assert
|
||||
var itemAddedEvent = events.FirstOrDefault(e => e.GetProperty("type").GetString() == "response.output_item.added");
|
||||
Assert.NotEqual(JsonValueKind.Undefined, itemAddedEvent.ValueKind);
|
||||
|
||||
var content = itemAddedEvent.GetProperty("item").GetProperty("content");
|
||||
var imageContent = content.EnumerateArray().First(c => c.GetProperty("type").GetString() == "input_image");
|
||||
|
||||
Assert.NotEqual(JsonValueKind.Undefined, imageContent.ValueKind);
|
||||
Assert.Equal(DataUri, imageContent.GetProperty("image_url").GetString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ImageContent_WithDetailProperty_IncludesDetail_SuccessAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "image-detail-agent";
|
||||
const string ImageUrl = "https://example.com/image.jpg";
|
||||
const string Detail = "high";
|
||||
HttpClient client = await this.CreateImageContentWithDetailAgentAsync(AgentName, ImageUrl, Detail);
|
||||
|
||||
// Act
|
||||
HttpResponseMessage httpResponse = await this.SendResponsesRequestAsync(client, AgentName, StreamingRequestJson);
|
||||
string sseContent = await httpResponse.Content.ReadAsStringAsync();
|
||||
var events = ParseSseEvents(sseContent);
|
||||
|
||||
// Assert
|
||||
var itemAddedEvent = events.FirstOrDefault(e => e.GetProperty("type").GetString() == "response.output_item.added");
|
||||
Assert.NotEqual(JsonValueKind.Undefined, itemAddedEvent.ValueKind);
|
||||
|
||||
var content = itemAddedEvent.GetProperty("item").GetProperty("content");
|
||||
var imageContent = content.EnumerateArray().First(c => c.GetProperty("type").GetString() == "input_image");
|
||||
|
||||
Assert.NotEqual(JsonValueKind.Undefined, imageContent.ValueKind);
|
||||
Assert.True(imageContent.TryGetProperty("detail", out var detailProp));
|
||||
Assert.Equal(Detail, detailProp.GetString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ImageContent_EmitsCorrectEventSequence_SuccessAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "image-sequence-agent";
|
||||
HttpClient client = await this.CreateImageContentAgentAsync(AgentName, "https://example.com/test.png", isDataUri: false);
|
||||
|
||||
// Act
|
||||
HttpResponseMessage httpResponse = await this.SendResponsesRequestAsync(client, AgentName, StreamingRequestJson);
|
||||
string sseContent = await httpResponse.Content.ReadAsStringAsync();
|
||||
var events = ParseSseEvents(sseContent);
|
||||
|
||||
// Assert
|
||||
List<string?> eventTypes = events.ConvertAll(e => e.GetProperty("type").GetString());
|
||||
|
||||
Assert.Contains("response.output_item.added", eventTypes);
|
||||
Assert.Contains("response.content_part.added", eventTypes);
|
||||
Assert.Contains("response.content_part.done", eventTypes);
|
||||
Assert.Contains("response.output_item.done", eventTypes);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region AudioContent Tests
|
||||
|
||||
[Fact]
|
||||
public async Task AudioContent_Mp3Format_GeneratesAudioItem_SuccessAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "audio-mp3-agent";
|
||||
const string AudioDataUri = "data:audio/mpeg;base64,/+MYxAAAAAAAAAAAAAAAAAAAAAAASW5mbwAAAA8AAAACAAADhAC7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7v/////////////////////////////////////////////////////////////////";
|
||||
HttpClient client = await this.CreateAudioContentAgentAsync(AgentName, AudioDataUri, "audio/mpeg");
|
||||
|
||||
// Act
|
||||
HttpResponseMessage httpResponse = await this.SendResponsesRequestAsync(client, AgentName, StreamingRequestJson);
|
||||
string sseContent = await httpResponse.Content.ReadAsStringAsync();
|
||||
var events = ParseSseEvents(sseContent);
|
||||
|
||||
// Assert
|
||||
var itemAddedEvent = events.FirstOrDefault(e => e.GetProperty("type").GetString() == "response.output_item.added");
|
||||
Assert.NotEqual(JsonValueKind.Undefined, itemAddedEvent.ValueKind);
|
||||
|
||||
var content = itemAddedEvent.GetProperty("item").GetProperty("content");
|
||||
var audioContent = content.EnumerateArray().First(c => c.GetProperty("type").GetString() == "input_audio");
|
||||
|
||||
Assert.NotEqual(JsonValueKind.Undefined, audioContent.ValueKind);
|
||||
Assert.Equal(AudioDataUri, audioContent.GetProperty("data").GetString());
|
||||
Assert.Equal("mp3", audioContent.GetProperty("format").GetString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AudioContent_WavFormat_GeneratesCorrectFormat_SuccessAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "audio-wav-agent";
|
||||
const string AudioDataUri = "data:audio/wav;base64,UklGRiQAAABXQVZFZm10IBAAAAABAAEAQB8AAAB9AAACABAAZGF0YQAAAAA=";
|
||||
HttpClient client = await this.CreateAudioContentAgentAsync(AgentName, AudioDataUri, "audio/wav");
|
||||
|
||||
// Act
|
||||
HttpResponseMessage httpResponse = await this.SendResponsesRequestAsync(client, AgentName, StreamingRequestJson);
|
||||
string sseContent = await httpResponse.Content.ReadAsStringAsync();
|
||||
var events = ParseSseEvents(sseContent);
|
||||
|
||||
// Assert
|
||||
var itemAddedEvent = events.FirstOrDefault(e => e.GetProperty("type").GetString() == "response.output_item.added");
|
||||
var content = itemAddedEvent.GetProperty("item").GetProperty("content");
|
||||
var audioContent = content.EnumerateArray().First(c => c.GetProperty("type").GetString() == "input_audio");
|
||||
|
||||
Assert.Equal("wav", audioContent.GetProperty("format").GetString());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("audio/opus", "opus")]
|
||||
[InlineData("audio/aac", "aac")]
|
||||
[InlineData("audio/flac", "flac")]
|
||||
[InlineData("audio/pcm", "pcm16")]
|
||||
[InlineData("audio/unknown", "mp3")] // Default fallback
|
||||
public async Task AudioContent_VariousFormats_GeneratesCorrectFormat_SuccessAsync(string mediaType, string expectedFormat)
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "audio-format-agent";
|
||||
const string AudioDataUri = "data:audio/test;base64,AQIDBA==";
|
||||
HttpClient client = await this.CreateAudioContentAgentAsync(AgentName, AudioDataUri, mediaType);
|
||||
|
||||
// Act
|
||||
HttpResponseMessage httpResponse = await this.SendResponsesRequestAsync(client, AgentName, StreamingRequestJson);
|
||||
string sseContent = await httpResponse.Content.ReadAsStringAsync();
|
||||
var events = ParseSseEvents(sseContent);
|
||||
|
||||
// Assert
|
||||
var itemAddedEvent = events.FirstOrDefault(e => e.GetProperty("type").GetString() == "response.output_item.added");
|
||||
var content = itemAddedEvent.GetProperty("item").GetProperty("content");
|
||||
var audioContent = content.EnumerateArray().First(c => c.GetProperty("type").GetString() == "input_audio");
|
||||
|
||||
Assert.Equal(expectedFormat, audioContent.GetProperty("format").GetString());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region HostedFileContent Tests
|
||||
|
||||
[Fact]
|
||||
public async Task HostedFileContent_GeneratesFileItem_SuccessAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "hosted-file-agent";
|
||||
const string FileId = "file-abc123";
|
||||
HttpClient client = await this.CreateHostedFileContentAgentAsync(AgentName, FileId);
|
||||
|
||||
// Act
|
||||
HttpResponseMessage httpResponse = await this.SendResponsesRequestAsync(client, AgentName, StreamingRequestJson);
|
||||
string sseContent = await httpResponse.Content.ReadAsStringAsync();
|
||||
var events = ParseSseEvents(sseContent);
|
||||
|
||||
// Assert
|
||||
var itemAddedEvent = events.FirstOrDefault(e => e.GetProperty("type").GetString() == "response.output_item.added");
|
||||
Assert.NotEqual(JsonValueKind.Undefined, itemAddedEvent.ValueKind);
|
||||
|
||||
var content = itemAddedEvent.GetProperty("item").GetProperty("content");
|
||||
var fileContent = content.EnumerateArray().First(c => c.GetProperty("type").GetString() == "input_file");
|
||||
|
||||
Assert.NotEqual(JsonValueKind.Undefined, fileContent.ValueKind);
|
||||
Assert.Equal(FileId, fileContent.GetProperty("file_id").GetString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HostedFileContent_EmitsCorrectEventSequence_SuccessAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "hosted-file-sequence-agent";
|
||||
HttpClient client = await this.CreateHostedFileContentAgentAsync(AgentName, "file-xyz789");
|
||||
|
||||
// Act
|
||||
HttpResponseMessage httpResponse = await this.SendResponsesRequestAsync(client, AgentName, StreamingRequestJson);
|
||||
string sseContent = await httpResponse.Content.ReadAsStringAsync();
|
||||
var events = ParseSseEvents(sseContent);
|
||||
|
||||
// Assert
|
||||
List<string?> eventTypes = events.ConvertAll(e => e.GetProperty("type").GetString());
|
||||
|
||||
Assert.Contains("response.output_item.added", eventTypes);
|
||||
Assert.Contains("response.content_part.added", eventTypes);
|
||||
Assert.Contains("response.content_part.done", eventTypes);
|
||||
Assert.Contains("response.output_item.done", eventTypes);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region FileContent Tests
|
||||
|
||||
[Fact]
|
||||
public async Task FileContent_WithDataUri_GeneratesFileItem_SuccessAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "file-data-agent";
|
||||
const string FileDataUri = "data:application/pdf;base64,JVBERi0xLjQKJeLjz9MK";
|
||||
const string Filename = "document.pdf";
|
||||
HttpClient client = await this.CreateFileContentAgentAsync(AgentName, FileDataUri, Filename);
|
||||
|
||||
// Act
|
||||
HttpResponseMessage httpResponse = await this.SendResponsesRequestAsync(client, AgentName, StreamingRequestJson);
|
||||
string sseContent = await httpResponse.Content.ReadAsStringAsync();
|
||||
var events = ParseSseEvents(sseContent);
|
||||
|
||||
// Assert
|
||||
var itemAddedEvent = events.FirstOrDefault(e => e.GetProperty("type").GetString() == "response.output_item.added");
|
||||
Assert.NotEqual(JsonValueKind.Undefined, itemAddedEvent.ValueKind);
|
||||
|
||||
var content = itemAddedEvent.GetProperty("item").GetProperty("content");
|
||||
var fileContent = content.EnumerateArray().First(c => c.GetProperty("type").GetString() == "input_file");
|
||||
|
||||
Assert.NotEqual(JsonValueKind.Undefined, fileContent.ValueKind);
|
||||
Assert.Equal(FileDataUri, fileContent.GetProperty("file_data").GetString());
|
||||
Assert.Equal(Filename, fileContent.GetProperty("filename").GetString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FileContent_WithoutFilename_GeneratesFileItemWithoutFilename_SuccessAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "file-no-name-agent";
|
||||
const string FileDataUri = "data:application/json;base64,eyJ0ZXN0IjoidmFsdWUifQ==";
|
||||
HttpClient client = await this.CreateFileContentAgentAsync(AgentName, FileDataUri, null);
|
||||
|
||||
// Act
|
||||
HttpResponseMessage httpResponse = await this.SendResponsesRequestAsync(client, AgentName, StreamingRequestJson);
|
||||
string sseContent = await httpResponse.Content.ReadAsStringAsync();
|
||||
var events = ParseSseEvents(sseContent);
|
||||
|
||||
// Assert
|
||||
var itemAddedEvent = events.FirstOrDefault(e => e.GetProperty("type").GetString() == "response.output_item.added");
|
||||
var content = itemAddedEvent.GetProperty("item").GetProperty("content");
|
||||
var fileContent = content.EnumerateArray().First(c => c.GetProperty("type").GetString() == "input_file");
|
||||
|
||||
Assert.NotEqual(JsonValueKind.Undefined, fileContent.ValueKind);
|
||||
Assert.Equal(FileDataUri, fileContent.GetProperty("file_data").GetString());
|
||||
// filename property might be null or absent
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Mixed Content Tests
|
||||
|
||||
[Fact]
|
||||
public async Task MixedContent_TextAndImage_GeneratesMultipleItems_SuccessAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "mixed-text-image-agent";
|
||||
HttpClient client = await this.CreateMixedContentAgentAsync(AgentName);
|
||||
|
||||
// Act
|
||||
HttpResponseMessage httpResponse = await this.SendResponsesRequestAsync(client, AgentName, StreamingRequestJson);
|
||||
string sseContent = await httpResponse.Content.ReadAsStringAsync();
|
||||
var events = ParseSseEvents(sseContent);
|
||||
|
||||
// Assert
|
||||
var itemAddedEvents = events.Where(e => e.GetProperty("type").GetString() == "response.output_item.added").ToList();
|
||||
|
||||
// Should have at least 2 items (text and image)
|
||||
Assert.True(itemAddedEvents.Count >= 2, $"Expected at least 2 items, got {itemAddedEvents.Count}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task MixedContent_ErrorAndText_GeneratesMultipleItems_SuccessAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "mixed-error-text-agent";
|
||||
HttpClient client = await this.CreateErrorAndTextContentAgentAsync(AgentName);
|
||||
|
||||
// Act
|
||||
HttpResponseMessage httpResponse = await this.SendResponsesRequestAsync(client, AgentName, StreamingRequestJson);
|
||||
string sseContent = await httpResponse.Content.ReadAsStringAsync();
|
||||
var events = ParseSseEvents(sseContent);
|
||||
|
||||
// Assert
|
||||
var itemAddedEvents = events.Where(e => e.GetProperty("type").GetString() == "response.output_item.added").ToList();
|
||||
|
||||
// Should have multiple items
|
||||
Assert.True(itemAddedEvents.Count >= 2);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helper Methods
|
||||
|
||||
private static List<JsonElement> ParseSseEvents(string sseContent)
|
||||
{
|
||||
var events = new List<JsonElement>();
|
||||
var lines = sseContent.Split('\n');
|
||||
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
var line = lines[i].TrimEnd('\r');
|
||||
|
||||
if (line.StartsWith("event: ", StringComparison.Ordinal) && i + 1 < lines.Length)
|
||||
{
|
||||
var dataLine = lines[i + 1].TrimEnd('\r');
|
||||
if (dataLine.StartsWith("data: ", StringComparison.Ordinal))
|
||||
{
|
||||
var jsonData = dataLine.Substring("data: ".Length);
|
||||
var doc = JsonDocument.Parse(jsonData);
|
||||
events.Add(doc.RootElement.Clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
private async Task<HttpClient> CreateErrorContentAgentAsync(string agentName, string errorMessage)
|
||||
{
|
||||
return await this.CreateTestServerAsync(agentName, "You are a test agent.", string.Empty, (msg) =>
|
||||
[new ErrorContent(errorMessage)]);
|
||||
}
|
||||
|
||||
private async Task<HttpClient> CreateImageContentAgentAsync(string agentName, string imageUri, bool isDataUri)
|
||||
{
|
||||
return await this.CreateTestServerAsync(agentName, "You are a test agent.", string.Empty, (msg) =>
|
||||
{
|
||||
if (isDataUri)
|
||||
{
|
||||
return [new DataContent(imageUri, "image/png")];
|
||||
}
|
||||
|
||||
return [new UriContent(imageUri, "image/jpeg")];
|
||||
});
|
||||
}
|
||||
|
||||
private async Task<HttpClient> CreateImageContentWithDetailAgentAsync(string agentName, string imageUri, string detail)
|
||||
{
|
||||
return await this.CreateTestServerAsync(agentName, "You are a test agent.", string.Empty, (msg) =>
|
||||
{
|
||||
var uriContent = new UriContent(imageUri, "image/jpeg")
|
||||
{
|
||||
AdditionalProperties = new AdditionalPropertiesDictionary { ["detail"] = detail }
|
||||
};
|
||||
return [uriContent];
|
||||
});
|
||||
}
|
||||
|
||||
private async Task<HttpClient> CreateAudioContentAgentAsync(string agentName, string audioDataUri, string mediaType)
|
||||
{
|
||||
return await this.CreateTestServerAsync(agentName, "You are a test agent.", string.Empty, (msg) =>
|
||||
[new DataContent(audioDataUri, mediaType)]);
|
||||
}
|
||||
|
||||
private async Task<HttpClient> CreateHostedFileContentAgentAsync(string agentName, string fileId)
|
||||
{
|
||||
return await this.CreateTestServerAsync(agentName, "You are a test agent.", string.Empty, (msg) =>
|
||||
[new HostedFileContent(fileId)]);
|
||||
}
|
||||
|
||||
private async Task<HttpClient> CreateFileContentAgentAsync(string agentName, string fileDataUri, string? filename)
|
||||
{
|
||||
// Extract media type from data URI
|
||||
string mediaType = "application/pdf"; // default
|
||||
if (fileDataUri.StartsWith("data:", StringComparison.Ordinal))
|
||||
{
|
||||
int semicolonIndex = fileDataUri.IndexOf(';');
|
||||
if (semicolonIndex > 5)
|
||||
{
|
||||
mediaType = fileDataUri.Substring(5, semicolonIndex - 5);
|
||||
}
|
||||
}
|
||||
return await this.CreateTestServerAsync(agentName, "You are a test agent.", string.Empty, (msg) =>
|
||||
[new DataContent(fileDataUri, mediaType) { Name = filename }]);
|
||||
}
|
||||
|
||||
private async Task<HttpClient> CreateMixedContentAgentAsync(string agentName)
|
||||
{
|
||||
return await this.CreateTestServerAsync(agentName, "You are a test agent.", string.Empty, (msg) =>
|
||||
[
|
||||
new TextContent("Here is an image:"),
|
||||
new UriContent("https://example.com/image.png", "image/png")
|
||||
]);
|
||||
}
|
||||
|
||||
private async Task<HttpClient> CreateErrorAndTextContentAgentAsync(string agentName)
|
||||
{
|
||||
return await this.CreateTestServerAsync(agentName, "You are a test agent.", string.Empty, (msg) =>
|
||||
[
|
||||
new TextContent("I need to inform you:"),
|
||||
new ErrorContent("The requested operation is not allowed.")
|
||||
]);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
+396
@@ -0,0 +1,396 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace Microsoft.Agents.AI.Hosting.OpenAI.UnitTests;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for EndpointRouteBuilderExtensions.MapOpenAIResponses method.
|
||||
/// </summary>
|
||||
public sealed class EndpointRouteBuilderExtensionsTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Verifies that MapOpenAIResponses throws ArgumentNullException for null endpoints.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MapOpenAIResponses_NullEndpoints_ThrowsArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
AspNetCore.Routing.IEndpointRouteBuilder endpoints = null!;
|
||||
AIAgent agent = null!;
|
||||
|
||||
// Act & Assert
|
||||
ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() =>
|
||||
endpoints.MapOpenAIResponses(agent));
|
||||
|
||||
Assert.Equal("endpoints", exception.ParamName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that MapOpenAIResponses throws ArgumentNullException for null agent.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MapOpenAIResponses_NullAgent_ThrowsArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
builder.AddOpenAIResponses();
|
||||
using WebApplication app = builder.Build();
|
||||
|
||||
// Act & Assert
|
||||
AIAgent agent = null!;
|
||||
ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() =>
|
||||
app.MapOpenAIResponses(agent));
|
||||
|
||||
Assert.Equal("agent", exception.ParamName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that MapOpenAIResponses validates agent name characters for URL safety.
|
||||
/// </summary>
|
||||
[Theory]
|
||||
[InlineData("agent with spaces")]
|
||||
[InlineData("agent<script>")]
|
||||
[InlineData("agent\nwith\nnewlines")]
|
||||
[InlineData("agent\twith\ttabs")]
|
||||
[InlineData("agent?query")]
|
||||
[InlineData("agent#fragment")]
|
||||
public void MapOpenAIResponses_InvalidAgentNameCharacters_ThrowsArgumentException(string invalidName)
|
||||
{
|
||||
// Arrange
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
IChatClient mockChatClient = new TestHelpers.SimpleMockChatClient();
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
builder.AddOpenAIResponses();
|
||||
builder.AddAIAgent(invalidName, "Instructions", chatClientServiceKey: "chat-client");
|
||||
using WebApplication app = builder.Build();
|
||||
AIAgent agent = app.Services.GetRequiredKeyedService<AIAgent>(invalidName);
|
||||
|
||||
// Act & Assert
|
||||
ArgumentException exception = Assert.Throws<ArgumentException>(() =>
|
||||
app.MapOpenAIResponses(agent));
|
||||
|
||||
Assert.Contains("invalid for URL routes", exception.Message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that MapOpenAIResponses accepts valid agent names with special characters.
|
||||
/// </summary>
|
||||
[Theory]
|
||||
[InlineData("agent-name")]
|
||||
[InlineData("agent_name")]
|
||||
[InlineData("agent.name")]
|
||||
[InlineData("agent123")]
|
||||
[InlineData("123agent")]
|
||||
[InlineData("AGENT")]
|
||||
[InlineData("my-agent_v1.0")]
|
||||
public void MapOpenAIResponses_ValidAgentNameCharacters_DoesNotThrow(string validName)
|
||||
{
|
||||
// Arrange
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
IChatClient mockChatClient = new TestHelpers.SimpleMockChatClient();
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
builder.AddAIAgent(validName, "Instructions", chatClientServiceKey: "chat-client");
|
||||
builder.AddOpenAIResponses();
|
||||
using WebApplication app = builder.Build();
|
||||
AIAgent agent = app.Services.GetRequiredKeyedService<AIAgent>(validName);
|
||||
|
||||
// Act & Assert - Should not throw
|
||||
app.MapOpenAIResponses(agent);
|
||||
Assert.NotNull(app);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that custom paths can be specified for responses endpoints.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MapOpenAIResponses_WithCustomPath_AcceptsValidPath()
|
||||
{
|
||||
// Arrange
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
IChatClient mockChatClient = new TestHelpers.SimpleMockChatClient();
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
builder.AddAIAgent("agent", "Instructions", chatClientServiceKey: "chat-client");
|
||||
builder.AddOpenAIResponses();
|
||||
using WebApplication app = builder.Build();
|
||||
AIAgent agent = app.Services.GetRequiredKeyedService<AIAgent>("agent");
|
||||
|
||||
// Act & Assert - Should not throw
|
||||
app.MapOpenAIResponses(agent, responsesPath: "/custom/responses");
|
||||
Assert.NotNull(app);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that multiple agents can be mapped to different paths.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MapOpenAIResponses_MultipleAgents_Succeeds()
|
||||
{
|
||||
// Arrange
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
IChatClient mockChatClient = new TestHelpers.SimpleMockChatClient();
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
builder.AddAIAgent("agent1", "Instructions1", chatClientServiceKey: "chat-client");
|
||||
builder.AddAIAgent("agent2", "Instructions2", chatClientServiceKey: "chat-client");
|
||||
builder.AddOpenAIResponses();
|
||||
using WebApplication app = builder.Build();
|
||||
AIAgent agent1 = app.Services.GetRequiredKeyedService<AIAgent>("agent1");
|
||||
AIAgent agent2 = app.Services.GetRequiredKeyedService<AIAgent>("agent2");
|
||||
|
||||
// Act & Assert - Should not throw
|
||||
app.MapOpenAIResponses(agent1);
|
||||
app.MapOpenAIResponses(agent2);
|
||||
Assert.NotNull(app);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that long agent names are accepted.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MapOpenAIResponses_LongAgentName_Succeeds()
|
||||
{
|
||||
// Arrange
|
||||
string longName = new('a', 100);
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
IChatClient mockChatClient = new TestHelpers.SimpleMockChatClient();
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
builder.AddAIAgent(longName, "Instructions", chatClientServiceKey: "chat-client");
|
||||
builder.AddOpenAIResponses();
|
||||
using WebApplication app = builder.Build();
|
||||
AIAgent agent = app.Services.GetRequiredKeyedService<AIAgent>(longName);
|
||||
|
||||
// Act & Assert - Should not throw
|
||||
app.MapOpenAIResponses(agent);
|
||||
Assert.NotNull(app);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that MapOpenAIResponses without agent parameter works correctly.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MapOpenAIResponses_WithoutAgent_Succeeds()
|
||||
{
|
||||
// Arrange
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
IChatClient mockChatClient = new TestHelpers.SimpleMockChatClient();
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
builder.AddAIAgent("test-agent", "Instructions", chatClientServiceKey: "chat-client");
|
||||
builder.AddOpenAIResponses();
|
||||
using WebApplication app = builder.Build();
|
||||
|
||||
// Act & Assert - Should not throw
|
||||
app.MapOpenAIResponses();
|
||||
Assert.NotNull(app);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that MapOpenAIResponses without agent parameter requires AddOpenAIResponses to be called.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MapOpenAIResponses_WithoutAgent_NoServiceRegistered_ThrowsInvalidOperationException()
|
||||
{
|
||||
// Arrange
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
using WebApplication app = builder.Build();
|
||||
|
||||
// Act & Assert
|
||||
InvalidOperationException exception = Assert.Throws<InvalidOperationException>(() =>
|
||||
app.MapOpenAIResponses());
|
||||
|
||||
Assert.Contains("IResponsesService is not registered", exception.Message);
|
||||
Assert.Contains("AddOpenAIResponses()", exception.Message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that MapOpenAIResponses without agent parameter with custom path works correctly.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MapOpenAIResponses_WithoutAgent_CustomPath_Succeeds()
|
||||
{
|
||||
// Arrange
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
IChatClient mockChatClient = new TestHelpers.SimpleMockChatClient();
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
builder.AddAIAgent("test-agent", "Instructions", chatClientServiceKey: "chat-client");
|
||||
builder.AddOpenAIResponses();
|
||||
using WebApplication app = builder.Build();
|
||||
|
||||
// Act & Assert - Should not throw
|
||||
app.MapOpenAIResponses(responsesPath: "/custom/path/responses");
|
||||
Assert.NotNull(app);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that MapOpenAIResponses throws ArgumentNullException for null endpoints when using IHostedAgentBuilder.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MapOpenAIResponses_WithAgentBuilder_NullEndpoints_ThrowsArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
AspNetCore.Routing.IEndpointRouteBuilder endpoints = null!;
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
IChatClient mockChatClient = new TestHelpers.SimpleMockChatClient();
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
IHostedAgentBuilder agentBuilder = builder.AddAIAgent("agent", "Instructions", chatClientServiceKey: "chat-client");
|
||||
|
||||
// Act & Assert
|
||||
ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() =>
|
||||
endpoints.MapOpenAIResponses(agentBuilder));
|
||||
|
||||
Assert.Equal("endpoints", exception.ParamName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that MapOpenAIResponses throws ArgumentNullException for null agentBuilder.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MapOpenAIResponses_WithAgentBuilder_NullAgentBuilder_ThrowsArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
IChatClient mockChatClient = new TestHelpers.SimpleMockChatClient();
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
builder.AddOpenAIResponses();
|
||||
using WebApplication app = builder.Build();
|
||||
IHostedAgentBuilder agentBuilder = null!;
|
||||
|
||||
// Act & Assert
|
||||
ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() =>
|
||||
app.MapOpenAIResponses(agentBuilder));
|
||||
|
||||
Assert.Equal("agentBuilder", exception.ParamName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that MapOpenAIResponses with IHostedAgentBuilder correctly resolves and maps the agent.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MapOpenAIResponses_WithAgentBuilder_Succeeds()
|
||||
{
|
||||
// Arrange
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
IChatClient mockChatClient = new TestHelpers.SimpleMockChatClient();
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
IHostedAgentBuilder agentBuilder = builder.AddAIAgent("agent", "Instructions", chatClientServiceKey: "chat-client");
|
||||
builder.AddOpenAIResponses();
|
||||
using WebApplication app = builder.Build();
|
||||
|
||||
// Act & Assert - Should not throw
|
||||
app.MapOpenAIResponses(agentBuilder);
|
||||
Assert.NotNull(app);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that MapOpenAIResponses with IHostedAgentBuilder and custom path works correctly.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MapOpenAIResponses_WithAgentBuilder_CustomPath_Succeeds()
|
||||
{
|
||||
// Arrange
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
IChatClient mockChatClient = new TestHelpers.SimpleMockChatClient();
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
IHostedAgentBuilder agentBuilder = builder.AddAIAgent("my-agent", "Instructions", chatClientServiceKey: "chat-client");
|
||||
builder.AddOpenAIResponses();
|
||||
using WebApplication app = builder.Build();
|
||||
|
||||
// Act & Assert - Should not throw
|
||||
app.MapOpenAIResponses(agentBuilder, path: "/agents/my-agent/responses");
|
||||
Assert.NotNull(app);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that multiple agents can be mapped using IHostedAgentBuilder.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MapOpenAIResponses_WithAgentBuilder_MultipleAgents_Succeeds()
|
||||
{
|
||||
// Arrange
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
IChatClient mockChatClient = new TestHelpers.SimpleMockChatClient();
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
IHostedAgentBuilder agent1Builder = builder.AddAIAgent("agent1", "Instructions1", chatClientServiceKey: "chat-client");
|
||||
IHostedAgentBuilder agent2Builder = builder.AddAIAgent("agent2", "Instructions2", chatClientServiceKey: "chat-client");
|
||||
builder.AddOpenAIResponses();
|
||||
using WebApplication app = builder.Build();
|
||||
|
||||
// Act & Assert - Should not throw
|
||||
app.MapOpenAIResponses(agent1Builder);
|
||||
app.MapOpenAIResponses(agent2Builder);
|
||||
Assert.NotNull(app);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that IHostedAgentBuilder overload validates agent name characters.
|
||||
/// </summary>
|
||||
[Theory]
|
||||
[InlineData("agent with spaces")]
|
||||
[InlineData("agent<script>")]
|
||||
[InlineData("agent?query")]
|
||||
[InlineData("agent#fragment")]
|
||||
public void MapOpenAIResponses_WithAgentBuilder_InvalidAgentNameCharacters_ThrowsArgumentException(string invalidName)
|
||||
{
|
||||
// Arrange
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
IChatClient mockChatClient = new TestHelpers.SimpleMockChatClient();
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
IHostedAgentBuilder agentBuilder = builder.AddAIAgent(invalidName, "Instructions", chatClientServiceKey: "chat-client");
|
||||
builder.AddOpenAIResponses();
|
||||
using WebApplication app = builder.Build();
|
||||
|
||||
// Act & Assert
|
||||
ArgumentException exception = Assert.Throws<ArgumentException>(() =>
|
||||
app.MapOpenAIResponses(agentBuilder));
|
||||
|
||||
Assert.Contains("invalid for URL routes", exception.Message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that IHostedAgentBuilder overload accepts valid agent names.
|
||||
/// </summary>
|
||||
[Theory]
|
||||
[InlineData("agent-name")]
|
||||
[InlineData("agent_name")]
|
||||
[InlineData("agent.name")]
|
||||
[InlineData("agent123")]
|
||||
[InlineData("my-agent_v1.0")]
|
||||
public void MapOpenAIResponses_WithAgentBuilder_ValidAgentNameCharacters_DoesNotThrow(string validName)
|
||||
{
|
||||
// Arrange
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
IChatClient mockChatClient = new TestHelpers.SimpleMockChatClient();
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
IHostedAgentBuilder agentBuilder = builder.AddAIAgent(validName, "Instructions", chatClientServiceKey: "chat-client");
|
||||
builder.AddOpenAIResponses();
|
||||
using WebApplication app = builder.Build();
|
||||
|
||||
// Act & Assert - Should not throw
|
||||
app.MapOpenAIResponses(agentBuilder);
|
||||
Assert.NotNull(app);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that IHostedAgentBuilder overload with custom paths can be specified.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MapOpenAIResponses_WithAgentBuilder_MultipleAgentsWithCustomPaths_Succeeds()
|
||||
{
|
||||
// Arrange
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
IChatClient mockChatClient = new TestHelpers.SimpleMockChatClient();
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
IHostedAgentBuilder agent1Builder = builder.AddAIAgent("agent1", "Instructions1", chatClientServiceKey: "chat-client");
|
||||
IHostedAgentBuilder agent2Builder = builder.AddAIAgent("agent2", "Instructions2", chatClientServiceKey: "chat-client");
|
||||
builder.AddOpenAIResponses();
|
||||
using WebApplication app = builder.Build();
|
||||
|
||||
// Act & Assert - Should not throw
|
||||
app.MapOpenAIResponses(agent1Builder, path: "/api/v1/agent1/responses");
|
||||
app.MapOpenAIResponses(agent2Builder, path: "/api/v1/agent2/responses");
|
||||
Assert.NotNull(app);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,366 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Agents.AI.Hosting.OpenAI.Tests;
|
||||
using Microsoft.Extensions.AI;
|
||||
|
||||
namespace Microsoft.Agents.AI.Hosting.OpenAI.UnitTests;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for function approval request and response content types.
|
||||
/// These are DevUI-specific extensions that allow approval workflows for function calls.
|
||||
/// </summary>
|
||||
public sealed class FunctionApprovalTests : ConformanceTestBase
|
||||
{
|
||||
// Streaming request JSON for OpenAI Responses API
|
||||
private const string StreamingRequestJson = @"{""model"":""gpt-4o-mini"",""input"":""test"",""stream"":true}";
|
||||
|
||||
#region ToolApprovalRequestContent Tests
|
||||
|
||||
[Fact]
|
||||
public async Task FunctionApprovalRequest_GeneratesCorrectEvent_SuccessAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "approval-request-agent";
|
||||
const string RequestId = "req-123";
|
||||
const string FunctionName = "get_weather";
|
||||
const string FunctionId = "call-abc123";
|
||||
Dictionary<string, object?> arguments = new() { ["location"] = "Seattle", ["unit"] = "celsius" };
|
||||
|
||||
#pragma warning disable MEAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates
|
||||
FunctionCallContent functionCall = new(FunctionId, FunctionName, arguments);
|
||||
ToolApprovalRequestContent approvalRequest = new(RequestId, functionCall);
|
||||
#pragma warning restore MEAI001
|
||||
|
||||
HttpClient client = await this.CreateTestServerAsync(AgentName, "You are a test agent.", string.Empty, (msg) =>
|
||||
[approvalRequest]);
|
||||
|
||||
// Act
|
||||
HttpResponseMessage httpResponse = await this.SendResponsesRequestAsync(client, AgentName, StreamingRequestJson);
|
||||
string sseContent = await httpResponse.Content.ReadAsStringAsync();
|
||||
List<JsonElement> events = ParseSseEvents(sseContent);
|
||||
|
||||
// Assert
|
||||
Assert.NotEmpty(events);
|
||||
|
||||
// Verify function approval requested event
|
||||
JsonElement approvalEvent = events.FirstOrDefault(e =>
|
||||
e.GetProperty("type").GetString() == "response.function_approval.requested");
|
||||
Assert.True(approvalEvent.ValueKind != JsonValueKind.Undefined, "approval event not found");
|
||||
|
||||
Assert.Equal(RequestId, approvalEvent.GetProperty("request_id").GetString());
|
||||
|
||||
JsonElement functionCallElement = approvalEvent.GetProperty("function_call");
|
||||
Assert.Equal(FunctionId, functionCallElement.GetProperty("id").GetString());
|
||||
Assert.Equal(FunctionName, functionCallElement.GetProperty("name").GetString());
|
||||
|
||||
JsonElement argumentsElement = functionCallElement.GetProperty("arguments");
|
||||
Assert.Equal("Seattle", argumentsElement.GetProperty("location").GetString());
|
||||
Assert.Equal("celsius", argumentsElement.GetProperty("unit").GetString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FunctionApprovalRequest_WithComplexArguments_GeneratesCorrectEvent_SuccessAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "approval-request-complex-args-agent";
|
||||
const string RequestId = "req-456";
|
||||
const string FunctionName = "calculate";
|
||||
const string FunctionId = "call-def456";
|
||||
Dictionary<string, object?> arguments = new()
|
||||
{
|
||||
["expression"] = "2+2",
|
||||
["precision"] = 2,
|
||||
["options"] = new Dictionary<string, object?> { ["decimal"] = true }
|
||||
};
|
||||
|
||||
#pragma warning disable MEAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates
|
||||
FunctionCallContent functionCall = new(FunctionId, FunctionName, arguments);
|
||||
ToolApprovalRequestContent approvalRequest = new(RequestId, functionCall);
|
||||
#pragma warning restore MEAI001
|
||||
|
||||
HttpClient client = await this.CreateTestServerAsync(AgentName, "You are a test agent.", string.Empty, (msg) =>
|
||||
[approvalRequest]);
|
||||
|
||||
// Act
|
||||
HttpResponseMessage httpResponse = await this.SendResponsesRequestAsync(client, AgentName, StreamingRequestJson);
|
||||
string sseContent = await httpResponse.Content.ReadAsStringAsync();
|
||||
List<JsonElement> events = ParseSseEvents(sseContent);
|
||||
|
||||
// Assert
|
||||
JsonElement approvalEvent = events.FirstOrDefault(e =>
|
||||
e.GetProperty("type").GetString() == "response.function_approval.requested");
|
||||
Assert.NotEqual(JsonValueKind.Undefined, approvalEvent.ValueKind);
|
||||
|
||||
JsonElement functionCallElement = approvalEvent.GetProperty("function_call");
|
||||
JsonElement argumentsElement = functionCallElement.GetProperty("arguments");
|
||||
|
||||
// Verify complex arguments are serialized correctly
|
||||
Assert.Equal("2+2", argumentsElement.GetProperty("expression").GetString());
|
||||
Assert.Equal(2, argumentsElement.GetProperty("precision").GetInt32());
|
||||
Assert.True(argumentsElement.GetProperty("options").GetProperty("decimal").GetBoolean());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FunctionApprovalRequest_EmitsCorrectEventSequence_SuccessAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "approval-sequence-agent";
|
||||
|
||||
#pragma warning disable MEAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates
|
||||
FunctionCallContent functionCall = new("call-1", "test_function", new Dictionary<string, object?>());
|
||||
ToolApprovalRequestContent approvalRequest = new("req-1", functionCall);
|
||||
#pragma warning restore MEAI001
|
||||
|
||||
HttpClient client = await this.CreateTestServerAsync(AgentName, "You are a test agent.", string.Empty, (msg) =>
|
||||
[approvalRequest]);
|
||||
|
||||
// Act
|
||||
HttpResponseMessage httpResponse = await this.SendResponsesRequestAsync(client, AgentName, StreamingRequestJson);
|
||||
string sseContent = await httpResponse.Content.ReadAsStringAsync();
|
||||
List<JsonElement> events = ParseSseEvents(sseContent);
|
||||
|
||||
// Assert - Verify event sequence
|
||||
List<string?> eventTypes = events.ConvertAll(e => e.GetProperty("type").GetString());
|
||||
|
||||
Assert.Equal("response.created", eventTypes[0]);
|
||||
Assert.Equal("response.in_progress", eventTypes[1]);
|
||||
Assert.Contains("response.function_approval.requested", eventTypes);
|
||||
Assert.Contains("response.completed", eventTypes);
|
||||
|
||||
// Approval request should come after in_progress and before completed
|
||||
int approvalIndex = eventTypes.IndexOf("response.function_approval.requested");
|
||||
int inProgressIndex = eventTypes.IndexOf("response.in_progress");
|
||||
int completedIndex = eventTypes.IndexOf("response.completed");
|
||||
|
||||
Assert.True(approvalIndex > inProgressIndex);
|
||||
Assert.True(approvalIndex < completedIndex);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FunctionApprovalRequest_SequenceNumbersAreCorrect_SuccessAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "approval-seq-num-agent";
|
||||
|
||||
#pragma warning disable MEAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates
|
||||
FunctionCallContent functionCall = new("call-1", "test", new Dictionary<string, object?>());
|
||||
ToolApprovalRequestContent approvalRequest = new("req-1", functionCall);
|
||||
#pragma warning restore MEAI001
|
||||
|
||||
HttpClient client = await this.CreateTestServerAsync(AgentName, "You are a test agent.", string.Empty, (msg) =>
|
||||
[approvalRequest]);
|
||||
|
||||
// Act
|
||||
HttpResponseMessage httpResponse = await this.SendResponsesRequestAsync(client, AgentName, StreamingRequestJson);
|
||||
string sseContent = await httpResponse.Content.ReadAsStringAsync();
|
||||
List<JsonElement> events = ParseSseEvents(sseContent);
|
||||
|
||||
// Assert - Sequence numbers are sequential
|
||||
List<int> sequenceNumbers = events.ConvertAll(e => e.GetProperty("sequence_number").GetInt32());
|
||||
Assert.NotEmpty(sequenceNumbers);
|
||||
|
||||
for (int i = 0; i < sequenceNumbers.Count; i++)
|
||||
{
|
||||
Assert.Equal(i, sequenceNumbers[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ToolApprovalResponseContent Tests
|
||||
|
||||
[Fact]
|
||||
public async Task FunctionApprovalResponse_Approved_GeneratesCorrectEvent_SuccessAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "approval-response-approved-agent";
|
||||
const string RequestId = "req-789";
|
||||
const string FunctionName = "send_email";
|
||||
const string FunctionId = "call-ghi789";
|
||||
Dictionary<string, object?> arguments = new() { ["to"] = "user@example.com", ["subject"] = "Test" };
|
||||
|
||||
#pragma warning disable MEAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates
|
||||
FunctionCallContent functionCall = new(FunctionId, FunctionName, arguments);
|
||||
ToolApprovalResponseContent approvalResponse = new(RequestId, approved: true, functionCall);
|
||||
#pragma warning restore MEAI001
|
||||
|
||||
HttpClient client = await this.CreateTestServerAsync(AgentName, "You are a test agent.", string.Empty, (msg) =>
|
||||
[approvalResponse]);
|
||||
|
||||
// Act
|
||||
HttpResponseMessage httpResponse = await this.SendResponsesRequestAsync(client, AgentName, StreamingRequestJson);
|
||||
string sseContent = await httpResponse.Content.ReadAsStringAsync();
|
||||
List<JsonElement> events = ParseSseEvents(sseContent);
|
||||
|
||||
// Assert
|
||||
Assert.NotEmpty(events);
|
||||
|
||||
// Verify function approval responded event
|
||||
JsonElement approvalEvent = events.FirstOrDefault(e =>
|
||||
e.GetProperty("type").GetString() == "response.function_approval.responded");
|
||||
Assert.True(approvalEvent.ValueKind != JsonValueKind.Undefined, "approval response event not found");
|
||||
|
||||
Assert.Equal(RequestId, approvalEvent.GetProperty("request_id").GetString());
|
||||
Assert.True(approvalEvent.GetProperty("approved").GetBoolean());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FunctionApprovalResponse_Rejected_GeneratesCorrectEvent_SuccessAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "approval-response-rejected-agent";
|
||||
const string RequestId = "req-999";
|
||||
const string FunctionName = "delete_file";
|
||||
const string FunctionId = "call-xyz999";
|
||||
|
||||
#pragma warning disable MEAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates
|
||||
FunctionCallContent functionCall = new(FunctionId, FunctionName, new Dictionary<string, object?> { ["path"] = "/important.txt" });
|
||||
ToolApprovalResponseContent approvalResponse = new(RequestId, approved: false, functionCall);
|
||||
#pragma warning restore MEAI001
|
||||
|
||||
HttpClient client = await this.CreateTestServerAsync(AgentName, "You are a test agent.", string.Empty, (msg) =>
|
||||
[approvalResponse]);
|
||||
|
||||
// Act
|
||||
HttpResponseMessage httpResponse = await this.SendResponsesRequestAsync(client, AgentName, StreamingRequestJson);
|
||||
string sseContent = await httpResponse.Content.ReadAsStringAsync();
|
||||
List<JsonElement> events = ParseSseEvents(sseContent);
|
||||
|
||||
// Assert
|
||||
JsonElement approvalEvent = events.FirstOrDefault(e =>
|
||||
e.GetProperty("type").GetString() == "response.function_approval.responded");
|
||||
Assert.NotEqual(JsonValueKind.Undefined, approvalEvent.ValueKind);
|
||||
|
||||
Assert.Equal(RequestId, approvalEvent.GetProperty("request_id").GetString());
|
||||
Assert.False(approvalEvent.GetProperty("approved").GetBoolean());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FunctionApprovalResponse_EmitsCorrectEventSequence_SuccessAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "approval-response-sequence-agent";
|
||||
|
||||
#pragma warning disable MEAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates
|
||||
FunctionCallContent functionCall = new("call-1", "test_function", new Dictionary<string, object?>());
|
||||
ToolApprovalResponseContent approvalResponse = new("req-1", approved: true, functionCall);
|
||||
#pragma warning restore MEAI001
|
||||
|
||||
HttpClient client = await this.CreateTestServerAsync(AgentName, "You are a test agent.", string.Empty, (msg) =>
|
||||
[approvalResponse]);
|
||||
|
||||
// Act
|
||||
HttpResponseMessage httpResponse = await this.SendResponsesRequestAsync(client, AgentName, StreamingRequestJson);
|
||||
string sseContent = await httpResponse.Content.ReadAsStringAsync();
|
||||
List<JsonElement> events = ParseSseEvents(sseContent);
|
||||
|
||||
// Assert
|
||||
List<string?> eventTypes = events.ConvertAll(e => e.GetProperty("type").GetString());
|
||||
|
||||
Assert.Contains("response.function_approval.responded", eventTypes);
|
||||
Assert.Contains("response.completed", eventTypes);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Mixed Content Tests
|
||||
|
||||
[Fact]
|
||||
public async Task MixedContent_ApprovalRequestAndText_GeneratesMultipleEvents_SuccessAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "mixed-approval-text-agent";
|
||||
|
||||
#pragma warning disable MEAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates
|
||||
FunctionCallContent functionCall = new("call-mixed-1", "test", new Dictionary<string, object?>());
|
||||
ToolApprovalRequestContent approvalRequest = new("req-mixed-1", functionCall);
|
||||
#pragma warning restore MEAI001
|
||||
|
||||
HttpClient client = await this.CreateTestServerAsync(AgentName, "You are a test agent.", string.Empty, (msg) =>
|
||||
[
|
||||
new TextContent("I need approval for this function:"),
|
||||
approvalRequest
|
||||
]);
|
||||
|
||||
// Act
|
||||
HttpResponseMessage httpResponse = await this.SendResponsesRequestAsync(client, AgentName, StreamingRequestJson);
|
||||
string sseContent = await httpResponse.Content.ReadAsStringAsync();
|
||||
List<JsonElement> events = ParseSseEvents(sseContent);
|
||||
|
||||
// Assert
|
||||
List<string?> eventTypes = events.ConvertAll(e => e.GetProperty("type").GetString());
|
||||
|
||||
Assert.Contains("response.output_item.added", eventTypes);
|
||||
Assert.Contains("response.function_approval.requested", eventTypes);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task MixedContent_MultipleApprovalRequests_GeneratesMultipleEvents_SuccessAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "multiple-approval-agent";
|
||||
|
||||
#pragma warning disable MEAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates
|
||||
FunctionCallContent functionCall1 = new("call-multi-1", "function1", new Dictionary<string, object?>());
|
||||
ToolApprovalRequestContent approvalRequest1 = new("req-multi-1", functionCall1);
|
||||
|
||||
FunctionCallContent functionCall2 = new("call-multi-2", "function2", new Dictionary<string, object?>());
|
||||
ToolApprovalRequestContent approvalRequest2 = new("req-multi-2", functionCall2);
|
||||
#pragma warning restore MEAI001
|
||||
|
||||
HttpClient client = await this.CreateTestServerAsync(AgentName, "You are a test agent.", string.Empty, (msg) =>
|
||||
[
|
||||
approvalRequest1,
|
||||
approvalRequest2
|
||||
]);
|
||||
|
||||
// Act
|
||||
HttpResponseMessage httpResponse = await this.SendResponsesRequestAsync(client, AgentName, StreamingRequestJson);
|
||||
string sseContent = await httpResponse.Content.ReadAsStringAsync();
|
||||
List<JsonElement> events = ParseSseEvents(sseContent);
|
||||
|
||||
// Assert
|
||||
List<JsonElement> approvalEvents = events.Where(e =>
|
||||
e.GetProperty("type").GetString() == "response.function_approval.requested").ToList();
|
||||
|
||||
Assert.Equal(2, approvalEvents.Count);
|
||||
Assert.Equal("req-multi-1", approvalEvents[0].GetProperty("request_id").GetString());
|
||||
Assert.Equal("req-multi-2", approvalEvents[1].GetProperty("request_id").GetString());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helper Methods
|
||||
|
||||
private static List<JsonElement> ParseSseEvents(string sseContent)
|
||||
{
|
||||
List<JsonElement> events = [];
|
||||
string[] lines = sseContent.Split('\n');
|
||||
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
string line = lines[i].TrimEnd('\r');
|
||||
|
||||
if (line.StartsWith("event: ", StringComparison.Ordinal) && i + 1 < lines.Length)
|
||||
{
|
||||
string dataLine = lines[i + 1].TrimEnd('\r');
|
||||
if (dataLine.StartsWith("data: ", StringComparison.Ordinal))
|
||||
{
|
||||
string jsonData = dataLine.Substring("data: ".Length);
|
||||
JsonDocument doc = JsonDocument.Parse(jsonData);
|
||||
events.Add(doc.RootElement.Clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,294 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
namespace Microsoft.Agents.AI.Hosting.OpenAI.UnitTests;
|
||||
|
||||
/// <summary>
|
||||
/// Unit tests for IdGenerator.
|
||||
/// </summary>
|
||||
public sealed class IdGeneratorTests
|
||||
{
|
||||
[Fact]
|
||||
public void Constructor_WithResponseIdAndConversationId_InitializesCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
const string ResponseId = "resp_test123";
|
||||
const string ConversationId = "conv_test456";
|
||||
|
||||
// Act
|
||||
var generator = new IdGenerator(ResponseId, ConversationId);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(ResponseId, generator.ResponseId);
|
||||
Assert.Equal(ConversationId, generator.ConversationId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_WithNullIds_GeneratesNewIds()
|
||||
{
|
||||
// Arrange & Act
|
||||
var generator = new IdGenerator(null, null);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(generator.ResponseId);
|
||||
Assert.NotNull(generator.ConversationId);
|
||||
Assert.StartsWith("resp_", generator.ResponseId);
|
||||
Assert.StartsWith("conv_", generator.ConversationId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_WithRandomSeed_GeneratesDeterministicIds()
|
||||
{
|
||||
// Arrange
|
||||
const int Seed = 12345;
|
||||
|
||||
// Act
|
||||
var generator1 = new IdGenerator(null, null, Seed);
|
||||
var generator2 = new IdGenerator(null, null, Seed);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(generator1.ResponseId, generator2.ResponseId);
|
||||
Assert.Equal(generator1.ConversationId, generator2.ConversationId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_WithDifferentRandomSeeds_GeneratesDifferentIds()
|
||||
{
|
||||
// Arrange
|
||||
const int Seed1 = 12345;
|
||||
const int Seed2 = 54321;
|
||||
|
||||
// Act
|
||||
var generator1 = new IdGenerator(null, null, Seed1);
|
||||
var generator2 = new IdGenerator(null, null, Seed2);
|
||||
|
||||
// Assert
|
||||
Assert.NotEqual(generator1.ResponseId, generator2.ResponseId);
|
||||
Assert.NotEqual(generator1.ConversationId, generator2.ConversationId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Generate_WithCategory_IncludesCategory()
|
||||
{
|
||||
// Arrange
|
||||
var generator = new IdGenerator("resp_test", "conv_test");
|
||||
|
||||
// Act
|
||||
string id = generator.Generate("test_category");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(id);
|
||||
Assert.StartsWith("test_category_", id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Generate_WithoutCategory_UsesDefaultPrefix()
|
||||
{
|
||||
// Arrange
|
||||
var generator = new IdGenerator("resp_test", "conv_test");
|
||||
|
||||
// Act
|
||||
string id = generator.Generate();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(id);
|
||||
Assert.StartsWith("id_", id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Generate_WithSeed_ProducesDeterministicResults()
|
||||
{
|
||||
// Arrange
|
||||
const int Seed = 12345;
|
||||
var generator = new IdGenerator("resp_test", "conv_test", Seed);
|
||||
|
||||
// Act
|
||||
string id1 = generator.Generate("test");
|
||||
string id2 = generator.Generate("test");
|
||||
string id3 = generator.Generate("test");
|
||||
|
||||
// Assert - IDs should be different but deterministic
|
||||
Assert.NotEqual(id1, id2);
|
||||
Assert.NotEqual(id2, id3);
|
||||
Assert.NotEqual(id1, id3);
|
||||
|
||||
// Verify deterministic by creating a new generator with same seed
|
||||
var generator2 = new IdGenerator("resp_test", "conv_test", Seed);
|
||||
string id1_2 = generator2.Generate("test");
|
||||
string id2_2 = generator2.Generate("test");
|
||||
string id3_2 = generator2.Generate("test");
|
||||
|
||||
Assert.Equal(id1, id1_2);
|
||||
Assert.Equal(id2, id2_2);
|
||||
Assert.Equal(id3, id3_2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GenerateFunctionCallId_ReturnsIdWithFuncPrefix()
|
||||
{
|
||||
// Arrange
|
||||
var generator = new IdGenerator("resp_test", "conv_test");
|
||||
|
||||
// Act
|
||||
string id = generator.GenerateFunctionCallId();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(id);
|
||||
Assert.StartsWith("func_", id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GenerateFunctionOutputId_ReturnsIdWithFuncoutPrefix()
|
||||
{
|
||||
// Arrange
|
||||
var generator = new IdGenerator("resp_test", "conv_test");
|
||||
|
||||
// Act
|
||||
string id = generator.GenerateFunctionOutputId();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(id);
|
||||
Assert.StartsWith("funcout_", id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GenerateMessageId_ReturnsIdWithMsgPrefix()
|
||||
{
|
||||
// Arrange
|
||||
var generator = new IdGenerator("resp_test", "conv_test");
|
||||
|
||||
// Act
|
||||
string id = generator.GenerateMessageId();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(id);
|
||||
Assert.StartsWith("msg_", id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GenerateReasoningId_ReturnsIdWithRsPrefix()
|
||||
{
|
||||
// Arrange
|
||||
var generator = new IdGenerator("resp_test", "conv_test");
|
||||
|
||||
// Act
|
||||
string id = generator.GenerateReasoningId();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(id);
|
||||
Assert.StartsWith("rs_", id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Generate_MultipleInvocations_ProducesUniqueIds()
|
||||
{
|
||||
// Arrange
|
||||
var generator = new IdGenerator("resp_test", "conv_test");
|
||||
var ids = new System.Collections.Generic.HashSet<string>();
|
||||
|
||||
// Act
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
string id = generator.Generate("test");
|
||||
ids.Add(id);
|
||||
}
|
||||
|
||||
// Assert
|
||||
Assert.Equal(100, ids.Count); // All IDs should be unique
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Generate_SharesPartitionKey()
|
||||
{
|
||||
// Arrange
|
||||
const string ConversationId = "conv_1234567890abcdef1234567890abcdef1234567890abcdef";
|
||||
var generator = new IdGenerator("resp_test", ConversationId, randomSeed: 12345);
|
||||
|
||||
// Act
|
||||
string id1 = generator.Generate("msg");
|
||||
string id2 = generator.Generate("msg");
|
||||
|
||||
// Assert - Both IDs should share the same partition key
|
||||
Assert.NotEqual(id1, id2);
|
||||
Assert.NotNull(id1);
|
||||
Assert.NotNull(id2);
|
||||
|
||||
// Format is: msg_<entropy><partitionKey> where entropy = 32 chars and partitionKey = 16 chars
|
||||
// Both IDs from the same generator should share the partition key
|
||||
Assert.StartsWith("msg_", id1);
|
||||
Assert.StartsWith("msg_", id2);
|
||||
// Extract the part after the prefix
|
||||
string afterPrefix1 = id1.Substring(4); // Skip "msg_"
|
||||
string afterPrefix2 = id2.Substring(4);
|
||||
// Both should have the same length (32 + 16 = 48)
|
||||
Assert.Equal(48, afterPrefix1.Length);
|
||||
Assert.Equal(48, afterPrefix2.Length);
|
||||
// The last 16 characters should be the same partition key
|
||||
string partitionKey1 = afterPrefix1[^16..];
|
||||
string partitionKey2 = afterPrefix2[^16..];
|
||||
Assert.Equal(partitionKey1, partitionKey2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void From_WithConversationInRequest_UsesConversationId()
|
||||
{
|
||||
// Arrange
|
||||
var request = new Responses.Models.CreateResponse
|
||||
{
|
||||
Model = "test-model",
|
||||
Input = Responses.Models.ResponseInput.FromText("test"),
|
||||
Conversation = new Responses.Models.ConversationReference
|
||||
{
|
||||
Id = "conv_fromrequest"
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
IdGenerator generator = IdGenerator.From(request);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("conv_fromrequest", generator.ConversationId);
|
||||
Assert.NotNull(generator.ResponseId);
|
||||
Assert.StartsWith("resp_", generator.ResponseId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void From_WithResponseIdInMetadata_UsesResponseId()
|
||||
{
|
||||
// Arrange
|
||||
var request = new Responses.Models.CreateResponse
|
||||
{
|
||||
Model = "test-model",
|
||||
Input = Responses.Models.ResponseInput.FromText("test"),
|
||||
Metadata = new System.Collections.Generic.Dictionary<string, string>
|
||||
{
|
||||
["response_id"] = "resp_metadata123"
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
IdGenerator generator = IdGenerator.From(request);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("resp_metadata123", generator.ResponseId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void From_WithoutIdsInRequest_GeneratesNewIds()
|
||||
{
|
||||
// Arrange
|
||||
var request = new Responses.Models.CreateResponse
|
||||
{
|
||||
Model = "test-model",
|
||||
Input = Responses.Models.ResponseInput.FromText("test")
|
||||
};
|
||||
|
||||
// Act
|
||||
IdGenerator generator = IdGenerator.From(request);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(generator.ResponseId);
|
||||
Assert.NotNull(generator.ConversationId);
|
||||
Assert.StartsWith("resp_", generator.ResponseId);
|
||||
Assert.StartsWith("conv_", generator.ConversationId);
|
||||
}
|
||||
}
|
||||
+359
@@ -0,0 +1,359 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Agents.AI.Hosting.OpenAI.Conversations;
|
||||
|
||||
namespace Microsoft.Agents.AI.Hosting.OpenAI.UnitTests;
|
||||
|
||||
/// <summary>
|
||||
/// Unit tests for InMemoryAgentConversationIndex implementation.
|
||||
/// </summary>
|
||||
public sealed class InMemoryAgentConversationIndexTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task AddConversationAsync_SuccessAsync()
|
||||
{
|
||||
// Arrange
|
||||
var index = new InMemoryAgentConversationIndex();
|
||||
const string AgentId = "agent_test123";
|
||||
const string ConversationId = "conv_test123";
|
||||
|
||||
// Act
|
||||
await index.AddConversationAsync(AgentId, ConversationId);
|
||||
|
||||
// Assert
|
||||
var response = await index.GetConversationIdsAsync(AgentId);
|
||||
Assert.Single(response.Data);
|
||||
Assert.Contains(ConversationId, response.Data);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddConversationAsync_MultipleConversations_AddsAllAsync()
|
||||
{
|
||||
// Arrange
|
||||
var index = new InMemoryAgentConversationIndex();
|
||||
const string AgentId = "agent_multi";
|
||||
const string ConversationId1 = "conv_001";
|
||||
const string ConversationId2 = "conv_002";
|
||||
const string ConversationId3 = "conv_003";
|
||||
|
||||
// Act
|
||||
await index.AddConversationAsync(AgentId, ConversationId1);
|
||||
await index.AddConversationAsync(AgentId, ConversationId2);
|
||||
await index.AddConversationAsync(AgentId, ConversationId3);
|
||||
|
||||
// Assert
|
||||
var response = await index.GetConversationIdsAsync(AgentId);
|
||||
Assert.Equal(3, response.Data.Count);
|
||||
Assert.Contains(ConversationId1, response.Data);
|
||||
Assert.Contains(ConversationId2, response.Data);
|
||||
Assert.Contains(ConversationId3, response.Data);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddConversationAsync_NullAgentId_ThrowsArgumentExceptionAsync()
|
||||
{
|
||||
// Arrange
|
||||
var index = new InMemoryAgentConversationIndex();
|
||||
|
||||
// Act & Assert
|
||||
await Assert.ThrowsAsync<ArgumentNullException>(
|
||||
() => index.AddConversationAsync(null!, "conv_test"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddConversationAsync_EmptyAgentId_ThrowsArgumentExceptionAsync()
|
||||
{
|
||||
// Arrange
|
||||
var index = new InMemoryAgentConversationIndex();
|
||||
|
||||
// Act & Assert
|
||||
await Assert.ThrowsAsync<ArgumentException>(
|
||||
() => index.AddConversationAsync(string.Empty, "conv_test"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddConversationAsync_NullConversationId_ThrowsArgumentExceptionAsync()
|
||||
{
|
||||
// Arrange
|
||||
var index = new InMemoryAgentConversationIndex();
|
||||
|
||||
// Act & Assert
|
||||
await Assert.ThrowsAsync<ArgumentNullException>(
|
||||
() => index.AddConversationAsync("agent_test", null!));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddConversationAsync_EmptyConversationId_ThrowsArgumentExceptionAsync()
|
||||
{
|
||||
// Arrange
|
||||
var index = new InMemoryAgentConversationIndex();
|
||||
|
||||
// Act & Assert
|
||||
await Assert.ThrowsAsync<ArgumentException>(
|
||||
() => index.AddConversationAsync("agent_test", string.Empty));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddConversationAsync_MultipleAgents_IsolatesConversationsAsync()
|
||||
{
|
||||
// Arrange
|
||||
var index = new InMemoryAgentConversationIndex();
|
||||
const string Agent1 = "agent_001";
|
||||
const string Agent2 = "agent_002";
|
||||
const string Conv1 = "conv_001";
|
||||
const string Conv2 = "conv_002";
|
||||
|
||||
// Act
|
||||
await index.AddConversationAsync(Agent1, Conv1);
|
||||
await index.AddConversationAsync(Agent2, Conv2);
|
||||
|
||||
// Assert
|
||||
var agent1Response = await index.GetConversationIdsAsync(Agent1);
|
||||
var agent2Response = await index.GetConversationIdsAsync(Agent2);
|
||||
|
||||
Assert.Single(agent1Response.Data);
|
||||
Assert.Contains(Conv1, agent1Response.Data);
|
||||
Assert.DoesNotContain(Conv2, agent1Response.Data);
|
||||
|
||||
Assert.Single(agent2Response.Data);
|
||||
Assert.Contains(Conv2, agent2Response.Data);
|
||||
Assert.DoesNotContain(Conv1, agent2Response.Data);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RemoveConversationAsync_ExistingConversation_RemovesSuccessfullyAsync()
|
||||
{
|
||||
// Arrange
|
||||
var index = new InMemoryAgentConversationIndex();
|
||||
const string AgentId = "agent_remove";
|
||||
const string ConversationId = "conv_remove123";
|
||||
|
||||
await index.AddConversationAsync(AgentId, ConversationId);
|
||||
|
||||
// Act
|
||||
await index.RemoveConversationAsync(AgentId, ConversationId);
|
||||
|
||||
// Assert
|
||||
var response = await index.GetConversationIdsAsync(AgentId);
|
||||
Assert.Empty(response.Data);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RemoveConversationAsync_NonExistentConversation_NoErrorAsync()
|
||||
{
|
||||
// Arrange
|
||||
var index = new InMemoryAgentConversationIndex();
|
||||
const string AgentId = "agent_noremove";
|
||||
|
||||
// Act - Should not throw
|
||||
await index.RemoveConversationAsync(AgentId, "conv_nonexistent");
|
||||
|
||||
// Assert
|
||||
var response = await index.GetConversationIdsAsync(AgentId);
|
||||
Assert.Empty(response.Data);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RemoveConversationAsync_OneOfMany_RemovesOnlyTargetedAsync()
|
||||
{
|
||||
// Arrange
|
||||
var index = new InMemoryAgentConversationIndex();
|
||||
const string AgentId = "agent_partial";
|
||||
const string Conv1 = "conv_001";
|
||||
const string Conv2 = "conv_002";
|
||||
const string Conv3 = "conv_003";
|
||||
|
||||
await index.AddConversationAsync(AgentId, Conv1);
|
||||
await index.AddConversationAsync(AgentId, Conv2);
|
||||
await index.AddConversationAsync(AgentId, Conv3);
|
||||
|
||||
// Act
|
||||
await index.RemoveConversationAsync(AgentId, Conv2);
|
||||
|
||||
// Assert
|
||||
var response = await index.GetConversationIdsAsync(AgentId);
|
||||
Assert.Equal(2, response.Data.Count);
|
||||
Assert.Contains(Conv1, response.Data);
|
||||
Assert.DoesNotContain(Conv2, response.Data);
|
||||
Assert.Contains(Conv3, response.Data);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RemoveConversationAsync_NullAgentId_ThrowsArgumentExceptionAsync()
|
||||
{
|
||||
// Arrange
|
||||
var index = new InMemoryAgentConversationIndex();
|
||||
|
||||
// Act & Assert
|
||||
await Assert.ThrowsAsync<ArgumentNullException>(
|
||||
() => index.RemoveConversationAsync(null!, "conv_test"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RemoveConversationAsync_EmptyAgentId_ThrowsArgumentExceptionAsync()
|
||||
{
|
||||
// Arrange
|
||||
var index = new InMemoryAgentConversationIndex();
|
||||
|
||||
// Act & Assert
|
||||
await Assert.ThrowsAsync<ArgumentException>(
|
||||
() => index.RemoveConversationAsync(string.Empty, "conv_test"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RemoveConversationAsync_NullConversationId_ThrowsArgumentExceptionAsync()
|
||||
{
|
||||
// Arrange
|
||||
var index = new InMemoryAgentConversationIndex();
|
||||
|
||||
// Act & Assert
|
||||
await Assert.ThrowsAsync<ArgumentNullException>(
|
||||
() => index.RemoveConversationAsync("agent_test", null!));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RemoveConversationAsync_EmptyConversationId_ThrowsArgumentExceptionAsync()
|
||||
{
|
||||
// Arrange
|
||||
var index = new InMemoryAgentConversationIndex();
|
||||
|
||||
// Act & Assert
|
||||
await Assert.ThrowsAsync<ArgumentException>(
|
||||
() => index.RemoveConversationAsync("agent_test", string.Empty));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetConversationIdsAsync_EmptyIndex_ReturnsEmptyListAsync()
|
||||
{
|
||||
// Arrange
|
||||
var index = new InMemoryAgentConversationIndex();
|
||||
|
||||
// Act
|
||||
var response = await index.GetConversationIdsAsync("agent_empty");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(response);
|
||||
Assert.Empty(response.Data);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetConversationIdsAsync_NonExistentAgent_ReturnsEmptyListAsync()
|
||||
{
|
||||
// Arrange
|
||||
var index = new InMemoryAgentConversationIndex();
|
||||
await index.AddConversationAsync("agent_other", "conv_001");
|
||||
|
||||
// Act
|
||||
var response = await index.GetConversationIdsAsync("agent_nonexistent");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(response);
|
||||
Assert.Empty(response.Data);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetConversationIdsAsync_NullAgentId_ThrowsArgumentExceptionAsync()
|
||||
{
|
||||
// Arrange
|
||||
var index = new InMemoryAgentConversationIndex();
|
||||
|
||||
// Act & Assert
|
||||
await Assert.ThrowsAsync<ArgumentNullException>(
|
||||
async () => await index.GetConversationIdsAsync(null!));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetConversationIdsAsync_EmptyAgentId_ThrowsArgumentExceptionAsync()
|
||||
{
|
||||
// Arrange
|
||||
var index = new InMemoryAgentConversationIndex();
|
||||
|
||||
// Act & Assert
|
||||
await Assert.ThrowsAsync<ArgumentException>(
|
||||
() => index.GetConversationIdsAsync(string.Empty));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetConversationIdsAsync_AfterMultipleAddsAndRemoves_ReturnsCorrectListAsync()
|
||||
{
|
||||
// Arrange
|
||||
var index = new InMemoryAgentConversationIndex();
|
||||
const string AgentId = "agent_complex";
|
||||
|
||||
await index.AddConversationAsync(AgentId, "conv_001");
|
||||
await index.AddConversationAsync(AgentId, "conv_002");
|
||||
await index.AddConversationAsync(AgentId, "conv_003");
|
||||
await index.RemoveConversationAsync(AgentId, "conv_002");
|
||||
await index.AddConversationAsync(AgentId, "conv_004");
|
||||
await index.RemoveConversationAsync(AgentId, "conv_001");
|
||||
|
||||
// Act
|
||||
var response = await index.GetConversationIdsAsync(AgentId);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, response.Data.Count);
|
||||
Assert.Contains("conv_003", response.Data);
|
||||
Assert.Contains("conv_004", response.Data);
|
||||
Assert.DoesNotContain("conv_001", response.Data);
|
||||
Assert.DoesNotContain("conv_002", response.Data);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ConcurrentOperations_ThreadSafeAsync()
|
||||
{
|
||||
// Arrange
|
||||
var index = new InMemoryAgentConversationIndex();
|
||||
const string AgentId = "agent_concurrent";
|
||||
const int OperationCount = 100;
|
||||
|
||||
// Act - Add conversations concurrently
|
||||
var addTasks = new List<Task>();
|
||||
for (int i = 0; i < OperationCount; i++)
|
||||
{
|
||||
int index_local = i;
|
||||
addTasks.Add(Task.Run(async () => await index.AddConversationAsync(AgentId, $"conv_{index_local:D3}")));
|
||||
}
|
||||
|
||||
await Task.WhenAll(addTasks);
|
||||
|
||||
// Assert
|
||||
var response = await index.GetConversationIdsAsync(AgentId);
|
||||
Assert.Equal(OperationCount, response.Data.Count);
|
||||
|
||||
// Act - Remove half of them concurrently
|
||||
var removeTasks = new List<Task>();
|
||||
for (int i = 0; i < OperationCount / 2; i++)
|
||||
{
|
||||
int index_local = i;
|
||||
removeTasks.Add(Task.Run(async () => await index.RemoveConversationAsync(AgentId, $"conv_{index_local:D3}")));
|
||||
}
|
||||
|
||||
await Task.WhenAll(removeTasks);
|
||||
|
||||
// Assert
|
||||
response = await index.GetConversationIdsAsync(AgentId);
|
||||
Assert.Equal(OperationCount / 2, response.Data.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddConversationAsync_DuplicateConversation_DoesNotAddMultipleTimesAsync()
|
||||
{
|
||||
// Arrange
|
||||
var index = new InMemoryAgentConversationIndex();
|
||||
const string AgentId = "agent_dup";
|
||||
const string ConversationId = "conv_duplicate";
|
||||
|
||||
// Act - Add the same conversation multiple times
|
||||
await index.AddConversationAsync(AgentId, ConversationId);
|
||||
await index.AddConversationAsync(AgentId, ConversationId);
|
||||
await index.AddConversationAsync(AgentId, ConversationId);
|
||||
|
||||
// Assert - HashSet prevents duplicates
|
||||
var response = await index.GetConversationIdsAsync(AgentId);
|
||||
Assert.Single(response.Data);
|
||||
Assert.Contains(ConversationId, response.Data);
|
||||
}
|
||||
}
|
||||
+645
@@ -0,0 +1,645 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Agents.AI.Hosting.OpenAI.Conversations;
|
||||
using Microsoft.Agents.AI.Hosting.OpenAI.Conversations.Models;
|
||||
using Microsoft.Agents.AI.Hosting.OpenAI.Models;
|
||||
using Microsoft.Agents.AI.Hosting.OpenAI.Responses.Models;
|
||||
|
||||
namespace Microsoft.Agents.AI.Hosting.OpenAI.UnitTests;
|
||||
|
||||
/// <summary>
|
||||
/// Unit tests for InMemoryConversationStorage implementation.
|
||||
/// </summary>
|
||||
public sealed class InMemoryConversationStorageTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task CreateConversationAsync_SuccessAsync()
|
||||
{
|
||||
// Arrange
|
||||
var storage = new InMemoryConversationStorage();
|
||||
var conversation = new Conversation
|
||||
{
|
||||
Id = "conv_test123",
|
||||
CreatedAt = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||||
Metadata = new Dictionary<string, string> { ["key"] = "value" }
|
||||
};
|
||||
|
||||
// Act
|
||||
Conversation result = await storage.CreateConversationAsync(conversation);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(conversation.Id, result.Id);
|
||||
Assert.Equal(conversation.CreatedAt, result.CreatedAt);
|
||||
Assert.NotNull(result.Metadata);
|
||||
Assert.Equal("value", result.Metadata["key"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateConversationAsync_DuplicateId_ThrowsInvalidOperationExceptionAsync()
|
||||
{
|
||||
// Arrange
|
||||
var storage = new InMemoryConversationStorage();
|
||||
var conversation = new Conversation
|
||||
{
|
||||
Id = "conv_duplicate",
|
||||
CreatedAt = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||||
Metadata = []
|
||||
};
|
||||
|
||||
await storage.CreateConversationAsync(conversation);
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<InvalidOperationException>(
|
||||
() => storage.CreateConversationAsync(conversation));
|
||||
Assert.Contains("already exists", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetConversationAsync_ExistingConversation_ReturnsConversationAsync()
|
||||
{
|
||||
// Arrange
|
||||
var storage = new InMemoryConversationStorage();
|
||||
var conversation = new Conversation
|
||||
{
|
||||
Id = "conv_get123",
|
||||
CreatedAt = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||||
Metadata = []
|
||||
};
|
||||
await storage.CreateConversationAsync(conversation);
|
||||
|
||||
// Act
|
||||
Conversation? result = await storage.GetConversationAsync("conv_get123");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(conversation.Id, result.Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetConversationAsync_NonExistentConversation_ReturnsNullAsync()
|
||||
{
|
||||
// Arrange
|
||||
var storage = new InMemoryConversationStorage();
|
||||
|
||||
// Act
|
||||
Conversation? result = await storage.GetConversationAsync("conv_nonexistent");
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateConversationAsync_ExistingConversation_UpdatesSuccessfullyAsync()
|
||||
{
|
||||
// Arrange
|
||||
var storage = new InMemoryConversationStorage();
|
||||
var conversation = new Conversation
|
||||
{
|
||||
Id = "conv_update123",
|
||||
CreatedAt = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||||
Metadata = new Dictionary<string, string> { ["original"] = "value" }
|
||||
};
|
||||
await storage.CreateConversationAsync(conversation);
|
||||
|
||||
var updatedConversation = new Conversation
|
||||
{
|
||||
Id = "conv_update123",
|
||||
CreatedAt = conversation.CreatedAt,
|
||||
Metadata = new Dictionary<string, string> { ["updated"] = "newvalue" }
|
||||
};
|
||||
|
||||
// Act
|
||||
Conversation? result = await storage.UpdateConversationAsync(updatedConversation);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(updatedConversation.Id, result.Id);
|
||||
Assert.NotNull(result.Metadata);
|
||||
Assert.Equal("newvalue", result.Metadata["updated"]);
|
||||
|
||||
// Verify the update persisted
|
||||
Conversation? retrieved = await storage.GetConversationAsync("conv_update123");
|
||||
Assert.NotNull(retrieved);
|
||||
Assert.Equal("newvalue", retrieved.Metadata["updated"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateConversationAsync_NonExistentConversation_ReturnsNullAsync()
|
||||
{
|
||||
// Arrange
|
||||
var storage = new InMemoryConversationStorage();
|
||||
var conversation = new Conversation
|
||||
{
|
||||
Id = "conv_nonexistent",
|
||||
CreatedAt = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||||
Metadata = []
|
||||
};
|
||||
|
||||
// Act
|
||||
Conversation? result = await storage.UpdateConversationAsync(conversation);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteConversationAsync_ExistingConversation_ReturnsTrueAsync()
|
||||
{
|
||||
// Arrange
|
||||
var storage = new InMemoryConversationStorage();
|
||||
var conversation = new Conversation
|
||||
{
|
||||
Id = "conv_delete123",
|
||||
CreatedAt = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||||
Metadata = []
|
||||
};
|
||||
await storage.CreateConversationAsync(conversation);
|
||||
|
||||
// Act
|
||||
bool result = await storage.DeleteConversationAsync("conv_delete123");
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
|
||||
// Verify deletion
|
||||
Conversation? retrieved = await storage.GetConversationAsync("conv_delete123");
|
||||
Assert.Null(retrieved);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteConversationAsync_NonExistentConversation_ReturnsFalseAsync()
|
||||
{
|
||||
// Arrange
|
||||
var storage = new InMemoryConversationStorage();
|
||||
|
||||
// Act
|
||||
bool result = await storage.DeleteConversationAsync("conv_nonexistent");
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddItemsAsync_SuccessAsync()
|
||||
{
|
||||
// Arrange
|
||||
var storage = new InMemoryConversationStorage();
|
||||
var conversation = new Conversation
|
||||
{
|
||||
Id = "conv_items123",
|
||||
CreatedAt = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||||
Metadata = []
|
||||
};
|
||||
await storage.CreateConversationAsync(conversation);
|
||||
|
||||
var item = new ResponsesUserMessageItemResource
|
||||
{
|
||||
Id = "msg_test123",
|
||||
Content = [new ItemContentInputText { Text = "Hello" }]
|
||||
};
|
||||
|
||||
// Act
|
||||
await storage.AddItemsAsync("conv_items123", [item]);
|
||||
|
||||
// Assert
|
||||
ItemResource? result = await storage.GetItemAsync("conv_items123", item.Id);
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(item.Id, result.Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddItemsAsync_NonExistentConversation_ThrowsInvalidOperationExceptionAsync()
|
||||
{
|
||||
// Arrange
|
||||
var storage = new InMemoryConversationStorage();
|
||||
var item = new ResponsesUserMessageItemResource
|
||||
{
|
||||
Id = "msg_test123",
|
||||
Content = [new ItemContentInputText { Text = "Hello" }]
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<InvalidOperationException>(
|
||||
() => storage.AddItemsAsync("conv_nonexistent", [item]));
|
||||
Assert.Contains("not found", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddItemsAsync_DuplicateItemId_ThrowsInvalidOperationExceptionAsync()
|
||||
{
|
||||
// Arrange
|
||||
var storage = new InMemoryConversationStorage();
|
||||
var conversation = new Conversation
|
||||
{
|
||||
Id = "conv_dup_items",
|
||||
CreatedAt = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||||
Metadata = []
|
||||
};
|
||||
await storage.CreateConversationAsync(conversation);
|
||||
|
||||
var item = new ResponsesUserMessageItemResource
|
||||
{
|
||||
Id = "msg_duplicate",
|
||||
Content = [new ItemContentInputText { Text = "Hello" }]
|
||||
};
|
||||
|
||||
await storage.AddItemsAsync("conv_dup_items", [item]);
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<InvalidOperationException>(
|
||||
() => storage.AddItemsAsync("conv_dup_items", [item]));
|
||||
Assert.Contains("already exists", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetItemAsync_ExistingItem_ReturnsItemAsync()
|
||||
{
|
||||
// Arrange
|
||||
var storage = new InMemoryConversationStorage();
|
||||
var conversation = new Conversation
|
||||
{
|
||||
Id = "conv_getitem",
|
||||
CreatedAt = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||||
Metadata = []
|
||||
};
|
||||
await storage.CreateConversationAsync(conversation);
|
||||
|
||||
var item = new ResponsesUserMessageItemResource
|
||||
{
|
||||
Id = "msg_getitem123",
|
||||
Content = [new ItemContentInputText { Text = "Test message" }]
|
||||
};
|
||||
await storage.AddItemsAsync("conv_getitem", [item]);
|
||||
|
||||
// Act
|
||||
ItemResource? result = await storage.GetItemAsync("conv_getitem", "msg_getitem123");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(item.Id, result.Id);
|
||||
var userMessage = Assert.IsType<ResponsesUserMessageItemResource>(result);
|
||||
Assert.NotEmpty(userMessage.Content);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetItemAsync_NonExistentItem_ReturnsNullAsync()
|
||||
{
|
||||
// Arrange
|
||||
var storage = new InMemoryConversationStorage();
|
||||
var conversation = new Conversation
|
||||
{
|
||||
Id = "conv_noitem",
|
||||
CreatedAt = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||||
Metadata = []
|
||||
};
|
||||
await storage.CreateConversationAsync(conversation);
|
||||
|
||||
// Act
|
||||
ItemResource? result = await storage.GetItemAsync("conv_noitem", "msg_nonexistent");
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetItemAsync_NonExistentConversation_ReturnsNullAsync()
|
||||
{
|
||||
// Arrange
|
||||
var storage = new InMemoryConversationStorage();
|
||||
|
||||
// Act
|
||||
ItemResource? result = await storage.GetItemAsync("conv_nonexistent", "msg_any");
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ListItemsAsync_DefaultParameters_ReturnsDescendingOrderAsync()
|
||||
{
|
||||
// Arrange
|
||||
var storage = new InMemoryConversationStorage();
|
||||
var conversation = new Conversation
|
||||
{
|
||||
Id = "conv_list",
|
||||
CreatedAt = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||||
Metadata = []
|
||||
};
|
||||
await storage.CreateConversationAsync(conversation);
|
||||
|
||||
// Add items in order
|
||||
var item1 = new ResponsesUserMessageItemResource
|
||||
{
|
||||
Id = "msg_001",
|
||||
Content = [new ItemContentInputText { Text = "First" }]
|
||||
};
|
||||
var item2 = new ResponsesUserMessageItemResource
|
||||
{
|
||||
Id = "msg_002",
|
||||
Content = [new ItemContentInputText { Text = "Second" }]
|
||||
};
|
||||
var item3 = new ResponsesUserMessageItemResource
|
||||
{
|
||||
Id = "msg_003",
|
||||
Content = [new ItemContentInputText { Text = "Third" }]
|
||||
};
|
||||
|
||||
await storage.AddItemsAsync("conv_list", [item1]);
|
||||
await storage.AddItemsAsync("conv_list", [item2]);
|
||||
await storage.AddItemsAsync("conv_list", [item3]);
|
||||
|
||||
// Act
|
||||
ListResponse<ItemResource> result = await storage.ListItemsAsync("conv_list");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.NotNull(result.Data);
|
||||
Assert.Equal(3, result.Data.Count);
|
||||
Assert.Equal("msg_003", result.Data[0].Id); // Descending order
|
||||
Assert.Equal("msg_002", result.Data[1].Id);
|
||||
Assert.Equal("msg_001", result.Data[2].Id);
|
||||
Assert.Equal("msg_003", result.FirstId);
|
||||
Assert.Equal("msg_001", result.LastId);
|
||||
Assert.False(result.HasMore);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ListItemsAsync_AscendingOrder_ReturnsCorrectOrderAsync()
|
||||
{
|
||||
// Arrange
|
||||
var storage = new InMemoryConversationStorage();
|
||||
var conversation = new Conversation
|
||||
{
|
||||
Id = "conv_asc",
|
||||
CreatedAt = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||||
Metadata = []
|
||||
};
|
||||
await storage.CreateConversationAsync(conversation);
|
||||
|
||||
var item1 = new ResponsesUserMessageItemResource
|
||||
{
|
||||
Id = "msg_001",
|
||||
Content = [new ItemContentInputText { Text = "First" }]
|
||||
};
|
||||
var item2 = new ResponsesUserMessageItemResource
|
||||
{
|
||||
Id = "msg_002",
|
||||
Content = [new ItemContentInputText { Text = "Second" }]
|
||||
};
|
||||
|
||||
await storage.AddItemsAsync("conv_asc", [item1]);
|
||||
await storage.AddItemsAsync("conv_asc", [item2]);
|
||||
|
||||
// Act
|
||||
ListResponse<ItemResource> result = await storage.ListItemsAsync("conv_asc", order: SortOrder.Ascending);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, result.Data.Count);
|
||||
Assert.Equal("msg_001", result.Data[0].Id); // Ascending order
|
||||
Assert.Equal("msg_002", result.Data[1].Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ListItemsAsync_WithLimit_ReturnsCorrectPageSizeAsync()
|
||||
{
|
||||
// Arrange
|
||||
var storage = new InMemoryConversationStorage();
|
||||
var conversation = new Conversation
|
||||
{
|
||||
Id = "conv_limit",
|
||||
CreatedAt = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||||
Metadata = []
|
||||
};
|
||||
await storage.CreateConversationAsync(conversation);
|
||||
|
||||
for (int i = 1; i <= 10; i++)
|
||||
{
|
||||
var item = new ResponsesUserMessageItemResource
|
||||
{
|
||||
Id = $"msg_{i:D3}",
|
||||
Content = [new ItemContentInputText { Text = $"Message {i}" }]
|
||||
};
|
||||
await storage.AddItemsAsync("conv_limit", [item]);
|
||||
}
|
||||
|
||||
// Act
|
||||
ListResponse<ItemResource> result = await storage.ListItemsAsync("conv_limit", limit: 5);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(5, result.Data.Count);
|
||||
Assert.True(result.HasMore);
|
||||
Assert.Equal("msg_010", result.FirstId); // First in descending order
|
||||
Assert.Equal("msg_006", result.LastId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ListItemsAsync_WithAfter_ReturnsNextPageAsync()
|
||||
{
|
||||
// Arrange
|
||||
var storage = new InMemoryConversationStorage();
|
||||
var conversation = new Conversation
|
||||
{
|
||||
Id = "conv_after",
|
||||
CreatedAt = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||||
Metadata = []
|
||||
};
|
||||
await storage.CreateConversationAsync(conversation);
|
||||
|
||||
for (int i = 1; i <= 10; i++)
|
||||
{
|
||||
var item = new ResponsesUserMessageItemResource
|
||||
{
|
||||
Id = $"msg_{i:D3}",
|
||||
Content = [new ItemContentInputText { Text = $"Message {i}" }]
|
||||
};
|
||||
await storage.AddItemsAsync("conv_after", [item]);
|
||||
}
|
||||
|
||||
// Act
|
||||
ListResponse<ItemResource> result = await storage.ListItemsAsync("conv_after", limit: 5, after: "msg_006");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(5, result.Data.Count);
|
||||
Assert.Equal("msg_005", result.Data[0].Id); // Next items after msg_006 in descending order
|
||||
Assert.Equal("msg_001", result.Data[4].Id);
|
||||
Assert.False(result.HasMore); // No more items after this page
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ListItemsAsync_LimitClamping_ClampsToValidRangeAsync()
|
||||
{
|
||||
// Arrange
|
||||
var storage = new InMemoryConversationStorage();
|
||||
var conversation = new Conversation
|
||||
{
|
||||
Id = "conv_clamp",
|
||||
CreatedAt = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||||
Metadata = []
|
||||
};
|
||||
await storage.CreateConversationAsync(conversation);
|
||||
|
||||
for (int i = 1; i <= 5; i++)
|
||||
{
|
||||
var item = new ResponsesUserMessageItemResource
|
||||
{
|
||||
Id = $"msg_{i:D3}",
|
||||
Content = [new ItemContentInputText { Text = $"Message {i}" }]
|
||||
};
|
||||
await storage.AddItemsAsync("conv_clamp", [item]);
|
||||
}
|
||||
|
||||
// Act - Test upper bound
|
||||
ListResponse<ItemResource> result1 = await storage.ListItemsAsync("conv_clamp", limit: 200);
|
||||
// Act - Test lower bound
|
||||
ListResponse<ItemResource> result2 = await storage.ListItemsAsync("conv_clamp", limit: 0);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(5, result1.Data.Count); // Should return all items (clamped to 100 max, but we only have 5)
|
||||
Assert.NotNull(result2.Data);
|
||||
Assert.NotEmpty(result2.Data);
|
||||
Assert.Single(result2.Data); // Should return at least 1 item (clamped to 1 min)
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ListItemsAsync_EmptyConversation_ReturnsEmptyListAsync()
|
||||
{
|
||||
// Arrange
|
||||
var storage = new InMemoryConversationStorage();
|
||||
var conversation = new Conversation
|
||||
{
|
||||
Id = "conv_empty",
|
||||
CreatedAt = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||||
Metadata = []
|
||||
};
|
||||
await storage.CreateConversationAsync(conversation);
|
||||
|
||||
// Act
|
||||
ListResponse<ItemResource> result = await storage.ListItemsAsync("conv_empty");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.NotNull(result.Data);
|
||||
Assert.Empty(result.Data);
|
||||
Assert.Null(result.FirstId);
|
||||
Assert.Null(result.LastId);
|
||||
Assert.False(result.HasMore);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ListItemsAsync_NonExistentConversation_ThrowsInvalidOperationExceptionAsync()
|
||||
{
|
||||
// Arrange
|
||||
var storage = new InMemoryConversationStorage();
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<InvalidOperationException>(
|
||||
() => storage.ListItemsAsync("conv_nonexistent"));
|
||||
Assert.Contains("not found", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteItemAsync_ExistingItem_ReturnsTrueAsync()
|
||||
{
|
||||
// Arrange
|
||||
var storage = new InMemoryConversationStorage();
|
||||
var conversation = new Conversation
|
||||
{
|
||||
Id = "conv_delitem",
|
||||
CreatedAt = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||||
Metadata = []
|
||||
};
|
||||
await storage.CreateConversationAsync(conversation);
|
||||
|
||||
var item = new ResponsesUserMessageItemResource
|
||||
{
|
||||
Id = "msg_delete",
|
||||
Content = [new ItemContentInputText { Text = "Delete me" }]
|
||||
};
|
||||
await storage.AddItemsAsync("conv_delitem", [item]);
|
||||
|
||||
// Act
|
||||
bool result = await storage.DeleteItemAsync("conv_delitem", "msg_delete");
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
|
||||
// Verify deletion
|
||||
ItemResource? retrieved = await storage.GetItemAsync("conv_delitem", "msg_delete");
|
||||
Assert.Null(retrieved);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteItemAsync_NonExistentItem_ReturnsFalseAsync()
|
||||
{
|
||||
// Arrange
|
||||
var storage = new InMemoryConversationStorage();
|
||||
var conversation = new Conversation
|
||||
{
|
||||
Id = "conv_delnoitem",
|
||||
CreatedAt = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||||
Metadata = []
|
||||
};
|
||||
await storage.CreateConversationAsync(conversation);
|
||||
|
||||
// Act
|
||||
bool result = await storage.DeleteItemAsync("conv_delnoitem", "msg_nonexistent");
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteItemAsync_NonExistentConversation_ReturnsFalseAsync()
|
||||
{
|
||||
// Arrange
|
||||
var storage = new InMemoryConversationStorage();
|
||||
|
||||
// Act
|
||||
bool result = await storage.DeleteItemAsync("conv_nonexistent", "msg_any");
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ConcurrentOperations_ThreadSafeAsync()
|
||||
{
|
||||
// Arrange
|
||||
var storage = new InMemoryConversationStorage();
|
||||
var conversation = new Conversation
|
||||
{
|
||||
Id = "conv_concurrent",
|
||||
CreatedAt = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||||
Metadata = []
|
||||
};
|
||||
await storage.CreateConversationAsync(conversation);
|
||||
|
||||
// Act - Add items concurrently
|
||||
var tasks = new List<Task>();
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
int index = i;
|
||||
tasks.Add(Task.Run(async () =>
|
||||
{
|
||||
var item = new ResponsesUserMessageItemResource
|
||||
{
|
||||
Id = $"msg_{index:D3}",
|
||||
Content = [new ItemContentInputText { Text = $"Message {index}" }]
|
||||
};
|
||||
await storage.AddItemsAsync("conv_concurrent", [item]);
|
||||
}));
|
||||
}
|
||||
|
||||
await Task.WhenAll(tasks);
|
||||
|
||||
// Assert
|
||||
ListResponse<ItemResource> result = await storage.ListItemsAsync("conv_concurrent", limit: 100);
|
||||
Assert.NotNull(result.Data);
|
||||
Assert.NotEmpty(result.Data);
|
||||
Assert.Equal(100, result.Data.Count);
|
||||
}
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Agents.AI.Hosting.OpenAI.Conversations;
|
||||
using Microsoft.Agents.AI.Hosting.OpenAI.Conversations.Models;
|
||||
using Microsoft.Agents.AI.Hosting.OpenAI.Responses;
|
||||
using Microsoft.Agents.AI.Hosting.OpenAI.Responses.Models;
|
||||
using Microsoft.Extensions.AI;
|
||||
|
||||
namespace Microsoft.Agents.AI.Hosting.OpenAI.UnitTests;
|
||||
|
||||
/// <summary>
|
||||
/// Unit tests for <see cref="InMemoryResponsesService"/> request validation.
|
||||
/// </summary>
|
||||
public sealed class InMemoryResponsesServiceTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task ValidateRequestAsync_NonexistentConversation_ReturnsNotFoundErrorAsync()
|
||||
{
|
||||
// Arrange
|
||||
using var storage = new InMemoryConversationStorage();
|
||||
using var service = new InMemoryResponsesService(
|
||||
new StubResponseExecutor(), new InMemoryStorageOptions(), storage);
|
||||
var request = new CreateResponse
|
||||
{
|
||||
Input = ResponseInput.FromText("hello"),
|
||||
Conversation = ConversationReference.FromId("conv_does_not_exist")
|
||||
};
|
||||
|
||||
// Act
|
||||
ResponseError? error = await service.ValidateRequestAsync(request);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(error);
|
||||
Assert.Equal("conversation_not_found", error.Code);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ValidateRequestAsync_ExistingConversation_ReturnsNullAsync()
|
||||
{
|
||||
// Arrange
|
||||
using var storage = new InMemoryConversationStorage();
|
||||
var conversation = new Conversation
|
||||
{
|
||||
Id = "conv_" + Guid.NewGuid().ToString("N"),
|
||||
CreatedAt = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
|
||||
};
|
||||
await storage.CreateConversationAsync(conversation);
|
||||
using var service = new InMemoryResponsesService(
|
||||
new StubResponseExecutor(), new InMemoryStorageOptions(), storage);
|
||||
var request = new CreateResponse
|
||||
{
|
||||
Input = ResponseInput.FromText("hello"),
|
||||
Conversation = ConversationReference.FromId(conversation.Id)
|
||||
};
|
||||
|
||||
// Act
|
||||
ResponseError? error = await service.ValidateRequestAsync(request);
|
||||
|
||||
// Assert
|
||||
Assert.Null(error);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ValidateRequestAsync_ConversationSuppliedButNoStorage_ReturnsNullAsync()
|
||||
{
|
||||
// Arrange - without a conversation store there is no existence to verify.
|
||||
using var service = new InMemoryResponsesService(
|
||||
new StubResponseExecutor(), new InMemoryStorageOptions());
|
||||
var request = new CreateResponse
|
||||
{
|
||||
Input = ResponseInput.FromText("hello"),
|
||||
Conversation = ConversationReference.FromId("conv_does_not_exist")
|
||||
};
|
||||
|
||||
// Act
|
||||
ResponseError? error = await service.ValidateRequestAsync(request);
|
||||
|
||||
// Assert
|
||||
Assert.Null(error);
|
||||
}
|
||||
|
||||
private sealed class StubResponseExecutor : IResponseExecutor
|
||||
{
|
||||
public ValueTask<ResponseError?> ValidateRequestAsync(CreateResponse request, CancellationToken cancellationToken = default)
|
||||
=> ValueTask.FromResult<ResponseError?>(null);
|
||||
|
||||
public async IAsyncEnumerable<StreamingResponseEvent> ExecuteAsync(
|
||||
AgentInvocationContext context,
|
||||
CreateResponse request,
|
||||
IReadOnlyList<ChatMessage>? conversationHistory = null,
|
||||
[EnumeratorCancellation] CancellationToken cancellationToken = default)
|
||||
{
|
||||
await Task.CompletedTask.ConfigureAwait(false);
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>$(TargetFrameworksCore)</TargetFrameworks>
|
||||
<IsPackable>false</IsPackable>
|
||||
<NoWarn>$(NoWarn);OPENAI001;CA1812</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.TestHost" />
|
||||
<PackageReference Include="OpenAI" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="!$([MSBuild]::IsTargetFrameworkCompatible($(TargetFramework), 'net10.0'))">
|
||||
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" />
|
||||
<PackageReference Include="System.Linq.AsyncEnumerable" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Microsoft.Agents.AI.Hosting.OpenAI\Microsoft.Agents.AI.Hosting.OpenAI.csproj" />
|
||||
<ProjectReference Include="..\..\src\Microsoft.Agents.AI.Hosting\Microsoft.Agents.AI.Hosting.csproj" />
|
||||
<ProjectReference Include="..\..\src\Microsoft.Agents.AI\Microsoft.Agents.AI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="ConformanceTraces\**\*">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
+625
@@ -0,0 +1,625 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Agents.AI.Hosting.OpenAI.Tests;
|
||||
using Microsoft.Extensions.AI;
|
||||
|
||||
namespace Microsoft.Agents.AI.Hosting.OpenAI.UnitTests;
|
||||
|
||||
/// <summary>
|
||||
/// Conformance tests for OpenAI Chat Completions API implementation behavior.
|
||||
/// Tests use real API traces to ensure our implementation produces responses
|
||||
/// that match OpenAI's wire format when processing actual requests through the server.
|
||||
/// </summary>
|
||||
public sealed class OpenAIChatCompletionsConformanceTests : ConformanceTestBase
|
||||
{
|
||||
[Fact]
|
||||
public async Task BasicRequestResponseAsync()
|
||||
{
|
||||
// Arrange
|
||||
string requestJson = LoadChatCompletionsTraceFile("basic/request.json");
|
||||
using var expectedResponseDoc = LoadChatCompletionsTraceDocument("basic/response.json");
|
||||
var expectedResponse = expectedResponseDoc.RootElement;
|
||||
|
||||
// Get the expected response text from the trace to use as mock response
|
||||
string expectedText = expectedResponse.GetProperty("choices")[0]
|
||||
.GetProperty("message")
|
||||
.GetProperty("content").GetString()!;
|
||||
|
||||
HttpClient client = await this.CreateTestServerAsync("basic-agent", "You are a helpful assistant.", expectedText);
|
||||
|
||||
// Act
|
||||
HttpResponseMessage httpResponse = await this.SendChatCompletionRequestAsync(client, "basic-agent", requestJson);
|
||||
using var responseDoc = await ParseResponseAsync(httpResponse);
|
||||
var response = responseDoc.RootElement;
|
||||
|
||||
// Parse the request to verify it was sent correctly
|
||||
using var requestDoc = JsonDocument.Parse(requestJson);
|
||||
var request = requestDoc.RootElement;
|
||||
|
||||
// Assert - Verify request was properly formatted (structure check)
|
||||
AssertJsonPropertyEquals(request, "model", "gpt-4o-mini");
|
||||
AssertJsonPropertyExists(request, "messages");
|
||||
AssertJsonPropertyEquals(request, "max_completion_tokens", 100);
|
||||
AssertJsonPropertyEquals(request, "temperature", 1.0f);
|
||||
AssertJsonPropertyEquals(request, "top_p", 1.0f);
|
||||
|
||||
var messages = request.GetProperty("messages");
|
||||
Assert.Equal(JsonValueKind.Array, messages.ValueKind);
|
||||
Assert.True(messages.GetArrayLength() > 0, "Messages array should not be empty");
|
||||
|
||||
var firstMessage = messages[0];
|
||||
AssertJsonPropertyEquals(firstMessage, "role", "user");
|
||||
AssertJsonPropertyEquals(firstMessage, "content", "Hello, how are you?");
|
||||
|
||||
// Assert - Response metadata (IDs and timestamps are dynamic, just verify structure)
|
||||
AssertJsonPropertyExists(response, "id");
|
||||
AssertJsonPropertyEquals(response, "object", "chat.completion");
|
||||
AssertJsonPropertyExists(response, "created");
|
||||
AssertJsonPropertyExists(response, "model");
|
||||
|
||||
var id = response.GetProperty("id").GetString();
|
||||
Assert.NotNull(id);
|
||||
Assert.StartsWith("chatcmpl-", id);
|
||||
|
||||
var createdAt = response.GetProperty("created").GetInt64();
|
||||
Assert.True(createdAt > 0, "created should be a positive unix timestamp");
|
||||
|
||||
var model = response.GetProperty("model").GetString();
|
||||
Assert.NotNull(model);
|
||||
Assert.StartsWith("gpt-4o-mini", model);
|
||||
|
||||
// Assert - Choices array structure
|
||||
AssertJsonPropertyExists(response, "choices");
|
||||
var choices = response.GetProperty("choices");
|
||||
Assert.Equal(JsonValueKind.Array, choices.ValueKind);
|
||||
Assert.True(choices.GetArrayLength() > 0, "Choices array should not be empty");
|
||||
|
||||
// Assert - Choice structure
|
||||
var firstChoice = choices[0];
|
||||
AssertJsonPropertyExists(firstChoice, "index");
|
||||
AssertJsonPropertyEquals(firstChoice, "index", 0);
|
||||
AssertJsonPropertyExists(firstChoice, "message");
|
||||
AssertJsonPropertyExists(firstChoice, "finish_reason");
|
||||
|
||||
var finishReason = firstChoice.GetProperty("finish_reason").GetString();
|
||||
Assert.NotNull(finishReason);
|
||||
Assert.Contains(finishReason, collection: ["stop", "length", "content_filter", "tool_calls"]);
|
||||
|
||||
// Assert - Message structure
|
||||
var message = firstChoice.GetProperty("message");
|
||||
AssertJsonPropertyExists(message, "role");
|
||||
AssertJsonPropertyEquals(message, "role", "assistant");
|
||||
AssertJsonPropertyExists(message, "content");
|
||||
|
||||
var content = message.GetProperty("content").GetString();
|
||||
Assert.NotNull(content);
|
||||
Assert.Equal(expectedText, content); // Verify actual content matches expected
|
||||
|
||||
// Assert - Usage statistics
|
||||
AssertJsonPropertyExists(response, "usage");
|
||||
var usage = response.GetProperty("usage");
|
||||
AssertJsonPropertyExists(usage, "prompt_tokens");
|
||||
AssertJsonPropertyExists(usage, "completion_tokens");
|
||||
AssertJsonPropertyExists(usage, "total_tokens");
|
||||
|
||||
var promptTokens = usage.GetProperty("prompt_tokens").GetInt32();
|
||||
var completionTokens = usage.GetProperty("completion_tokens").GetInt32();
|
||||
var totalTokens = usage.GetProperty("total_tokens").GetInt32();
|
||||
|
||||
Assert.True(promptTokens > 0, "prompt_tokens should be positive");
|
||||
Assert.True(completionTokens > 0, "completion_tokens should be positive");
|
||||
Assert.Equal(promptTokens + completionTokens, totalTokens);
|
||||
|
||||
// Assert - Usage details
|
||||
AssertJsonPropertyExists(usage, "prompt_tokens_details");
|
||||
var promptDetails = usage.GetProperty("prompt_tokens_details");
|
||||
AssertJsonPropertyExists(promptDetails, "cached_tokens");
|
||||
AssertJsonPropertyExists(promptDetails, "audio_tokens");
|
||||
Assert.True(promptDetails.GetProperty("cached_tokens").GetInt32() >= 0);
|
||||
Assert.True(promptDetails.GetProperty("audio_tokens").GetInt32() >= 0);
|
||||
|
||||
AssertJsonPropertyExists(usage, "completion_tokens_details");
|
||||
var completionDetails = usage.GetProperty("completion_tokens_details");
|
||||
AssertJsonPropertyExists(completionDetails, "reasoning_tokens");
|
||||
AssertJsonPropertyExists(completionDetails, "audio_tokens");
|
||||
AssertJsonPropertyExists(completionDetails, "accepted_prediction_tokens");
|
||||
AssertJsonPropertyExists(completionDetails, "rejected_prediction_tokens");
|
||||
Assert.True(completionDetails.GetProperty("reasoning_tokens").GetInt32() >= 0);
|
||||
Assert.True(completionDetails.GetProperty("audio_tokens").GetInt32() >= 0);
|
||||
Assert.True(completionDetails.GetProperty("accepted_prediction_tokens").GetInt32() >= 0);
|
||||
Assert.True(completionDetails.GetProperty("rejected_prediction_tokens").GetInt32() >= 0);
|
||||
|
||||
// Assert - Optional fields
|
||||
AssertJsonPropertyExists(response, "service_tier");
|
||||
var serviceTier = response.GetProperty("service_tier").GetString();
|
||||
Assert.NotNull(serviceTier);
|
||||
Assert.True(serviceTier is "default" or "auto", $"service_tier should be 'default' or 'auto', got '{serviceTier}'");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task StreamingRequestResponseAsync()
|
||||
{
|
||||
// Arrange
|
||||
string requestJson = LoadChatCompletionsTraceFile("streaming/request.json");
|
||||
string expectedResponseSse = LoadChatCompletionsTraceFile("streaming/response.txt");
|
||||
|
||||
// Extract expected text from SSE chunks
|
||||
var expectedChunks = ParseChatCompletionChunksFromSse(expectedResponseSse);
|
||||
string expectedText = string.Concat(expectedChunks
|
||||
.Where(c => c.GetProperty("choices")[0].GetProperty("delta").TryGetProperty("content", out var content))
|
||||
.Select(c => c.GetProperty("choices")[0].GetProperty("delta").GetProperty("content").GetString()));
|
||||
|
||||
HttpClient client = await this.CreateTestServerAsync("streaming-agent", "You are a helpful assistant.", expectedText);
|
||||
|
||||
// Act
|
||||
HttpResponseMessage httpResponse = await this.SendChatCompletionRequestAsync(client, "streaming-agent", requestJson);
|
||||
|
||||
// Assert - Response should be SSE format
|
||||
Assert.Equal("text/event-stream", httpResponse.Content.Headers.ContentType?.MediaType);
|
||||
|
||||
string responseSse = await httpResponse.Content.ReadAsStringAsync();
|
||||
var chunks = ParseChatCompletionChunksFromSse(responseSse);
|
||||
|
||||
// Parse the request
|
||||
using var requestDoc = JsonDocument.Parse(requestJson);
|
||||
var request = requestDoc.RootElement;
|
||||
|
||||
// Assert - Request has stream flag
|
||||
AssertJsonPropertyEquals(request, "stream", true);
|
||||
|
||||
// Assert - Response has valid chunks
|
||||
Assert.NotEmpty(chunks);
|
||||
|
||||
// Assert - All chunks have same ID
|
||||
string? firstId = null;
|
||||
foreach (var chunk in chunks)
|
||||
{
|
||||
AssertJsonPropertyExists(chunk, "id");
|
||||
AssertJsonPropertyEquals(chunk, "object", "chat.completion.chunk");
|
||||
AssertJsonPropertyExists(chunk, "created");
|
||||
AssertJsonPropertyExists(chunk, "model");
|
||||
AssertJsonPropertyExists(chunk, "choices");
|
||||
|
||||
string chunkId = chunk.GetProperty("id").GetString()!;
|
||||
Assert.StartsWith("chatcmpl-", chunkId);
|
||||
|
||||
firstId ??= chunkId;
|
||||
Assert.Equal(firstId, chunkId);
|
||||
}
|
||||
|
||||
// Assert - First chunk has role
|
||||
var firstChunk = chunks[0];
|
||||
var firstChoice = firstChunk.GetProperty("choices")[0];
|
||||
AssertJsonPropertyExists(firstChoice, "delta");
|
||||
var firstDelta = firstChoice.GetProperty("delta");
|
||||
if (firstDelta.TryGetProperty("role", out var role))
|
||||
{
|
||||
Assert.Equal("assistant", role.GetString());
|
||||
}
|
||||
|
||||
// Assert - Content chunks have delta content
|
||||
var contentChunks = chunks.Where(c =>
|
||||
c.GetProperty("choices")[0].GetProperty("delta").TryGetProperty("content", out _)).ToList();
|
||||
Assert.NotEmpty(contentChunks);
|
||||
|
||||
// Assert - Last chunk has finish_reason
|
||||
var lastChunk = chunks[^1];
|
||||
var lastChoice = lastChunk.GetProperty("choices")[0];
|
||||
if (lastChoice.TryGetProperty("finish_reason", out var finishReason) && finishReason.ValueKind != JsonValueKind.Null)
|
||||
{
|
||||
string reason = finishReason.GetString()!;
|
||||
Assert.Contains(reason, collection: ["stop", "length", "tool_calls", "content_filter"]);
|
||||
}
|
||||
|
||||
// Assert - Last chunk may have usage
|
||||
if (lastChunk.TryGetProperty("usage", out var usage))
|
||||
{
|
||||
AssertJsonPropertyExists(usage, "prompt_tokens");
|
||||
AssertJsonPropertyExists(usage, "completion_tokens");
|
||||
AssertJsonPropertyExists(usage, "total_tokens");
|
||||
}
|
||||
|
||||
// Assert - Accumulated content matches expected
|
||||
string accumulatedText = string.Concat(contentChunks
|
||||
.Select(c => c.GetProperty("choices")[0].GetProperty("delta").GetProperty("content").GetString()));
|
||||
Assert.NotEmpty(accumulatedText);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FunctionCallingRequestResponseAsync()
|
||||
{
|
||||
// Arrange
|
||||
string requestJson = LoadChatCompletionsTraceFile("function_calling/request.json");
|
||||
using var expectedResponseDoc = LoadChatCompletionsTraceDocument("function_calling/response.json");
|
||||
var expectedResponse = expectedResponseDoc.RootElement;
|
||||
|
||||
// Get expected function call details
|
||||
const string FunctionName = "get_weather";
|
||||
|
||||
HttpClient client = await this.CreateTestServerAsync("function-agent", "You are a helpful assistant.", FunctionName,
|
||||
(msg) => [new FunctionCallContent("call_abc123xyz", "get_weather", new Dictionary<string, object?>() {
|
||||
{ "location", "San Francisco, CA" },
|
||||
{ "unit", "fahrenheit" }
|
||||
})]
|
||||
);
|
||||
|
||||
// Act
|
||||
HttpResponseMessage httpResponse = await this.SendChatCompletionRequestAsync(client, "function-agent", requestJson);
|
||||
using var responseDoc = await ParseResponseAsync(httpResponse);
|
||||
var response = responseDoc.RootElement;
|
||||
|
||||
// Parse the request
|
||||
using var requestDoc = JsonDocument.Parse(requestJson);
|
||||
var request = requestDoc.RootElement;
|
||||
|
||||
// Assert - Request has tools array
|
||||
AssertJsonPropertyExists(request, "tools");
|
||||
var tools = request.GetProperty("tools");
|
||||
Assert.Equal(JsonValueKind.Array, tools.ValueKind);
|
||||
Assert.True(tools.GetArrayLength() > 0);
|
||||
|
||||
// Assert - Tool structure
|
||||
var tool = tools[0];
|
||||
AssertJsonPropertyEquals(tool, "type", "function");
|
||||
AssertJsonPropertyExists(tool, "function");
|
||||
var function = tool.GetProperty("function");
|
||||
AssertJsonPropertyEquals(function, "name", "get_weather");
|
||||
AssertJsonPropertyExists(function, "description");
|
||||
AssertJsonPropertyExists(function, "parameters");
|
||||
|
||||
// Assert - Parameters have JSON Schema
|
||||
var parameters = function.GetProperty("parameters");
|
||||
AssertJsonPropertyEquals(parameters, "type", "object");
|
||||
AssertJsonPropertyExists(parameters, "properties");
|
||||
AssertJsonPropertyExists(parameters, "required");
|
||||
|
||||
// Assert - Response has tool_calls. Not always will return that, so can default to "stop"
|
||||
var choices = response.GetProperty("choices");
|
||||
var choice = choices[0];
|
||||
var message = choice.GetProperty("message");
|
||||
AssertJsonPropertyEquals(choice, "finish_reason", ["tool_calls", "stop"]);
|
||||
AssertJsonPropertyExists(message, "tool_calls");
|
||||
|
||||
// Assert - Tool call structure
|
||||
var toolCalls = message.GetProperty("tool_calls");
|
||||
Assert.Equal(JsonValueKind.Array, toolCalls.ValueKind);
|
||||
Assert.True(toolCalls.GetArrayLength() > 0);
|
||||
|
||||
var toolCall = toolCalls[0];
|
||||
AssertJsonPropertyExists(toolCall, "id");
|
||||
AssertJsonPropertyEquals(toolCall, "type", "function");
|
||||
AssertJsonPropertyExists(toolCall, "function");
|
||||
|
||||
var callFunction = toolCall.GetProperty("function");
|
||||
AssertJsonPropertyEquals(callFunction, "name", "get_weather");
|
||||
AssertJsonPropertyExists(callFunction, "arguments");
|
||||
|
||||
// Assert - Arguments are valid JSON
|
||||
string arguments = callFunction.GetProperty("arguments").GetString()!;
|
||||
using var argsDoc = JsonDocument.Parse(arguments);
|
||||
var argsRoot = argsDoc.RootElement;
|
||||
AssertJsonPropertyExists(argsRoot, "location");
|
||||
|
||||
// Assert - Message content is null when tool_calls present. Can be absent or null.
|
||||
if (message.TryGetProperty("content", out var contentProp))
|
||||
{
|
||||
Assert.Equal(JsonValueKind.Null, contentProp.ValueKind);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SystemMessageRequestResponseAsync()
|
||||
{
|
||||
// Arrange
|
||||
string requestJson = LoadChatCompletionsTraceFile("system_message/request.json");
|
||||
using var expectedResponseDoc = LoadChatCompletionsTraceDocument("system_message/response.json");
|
||||
var expectedResponse = expectedResponseDoc.RootElement;
|
||||
|
||||
string expectedText = expectedResponse.GetProperty("choices")[0]
|
||||
.GetProperty("message")
|
||||
.GetProperty("content").GetString()!;
|
||||
|
||||
HttpClient client = await this.CreateTestServerAsync("system-agent", "You are a helpful assistant that speaks like a pirate.", expectedText);
|
||||
|
||||
// Act
|
||||
HttpResponseMessage httpResponse = await this.SendChatCompletionRequestAsync(client, "system-agent", requestJson);
|
||||
using var responseDoc = await ParseResponseAsync(httpResponse);
|
||||
var response = responseDoc.RootElement;
|
||||
|
||||
// Parse the request
|
||||
using var requestDoc = JsonDocument.Parse(requestJson);
|
||||
var request = requestDoc.RootElement;
|
||||
|
||||
// Assert - Request has messages with system role
|
||||
var messages = request.GetProperty("messages");
|
||||
Assert.True(messages.GetArrayLength() >= 2);
|
||||
|
||||
var systemMessage = messages[0];
|
||||
AssertJsonPropertyEquals(systemMessage, "role", "system");
|
||||
AssertJsonPropertyExists(systemMessage, "content");
|
||||
string systemContent = systemMessage.GetProperty("content").GetString()!;
|
||||
Assert.Contains("pirate", systemContent, System.StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
var userMessage = messages[1];
|
||||
AssertJsonPropertyEquals(userMessage, "role", "user");
|
||||
|
||||
// Assert - Response reflects system message influence
|
||||
var responseMessage = response.GetProperty("choices")[0].GetProperty("message");
|
||||
string content = responseMessage.GetProperty("content").GetString()!;
|
||||
Assert.NotNull(content);
|
||||
Assert.Equal(expectedText, content);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task MultiTurnConversationRequestResponseAsync()
|
||||
{
|
||||
// Arrange
|
||||
string requestJson = LoadChatCompletionsTraceFile("multi_turn/request.json");
|
||||
using var expectedResponseDoc = LoadChatCompletionsTraceDocument("multi_turn/response.json");
|
||||
var expectedResponse = expectedResponseDoc.RootElement;
|
||||
|
||||
string expectedText = expectedResponse.GetProperty("choices")[0]
|
||||
.GetProperty("message")
|
||||
.GetProperty("content").GetString()!;
|
||||
|
||||
HttpClient client = await this.CreateTestServerAsync("multi-turn-agent", "You are a helpful assistant.", expectedText);
|
||||
|
||||
// Act
|
||||
HttpResponseMessage httpResponse = await this.SendChatCompletionRequestAsync(client, "multi-turn-agent", requestJson);
|
||||
using var responseDoc = await ParseResponseAsync(httpResponse);
|
||||
var response = responseDoc.RootElement;
|
||||
|
||||
// Parse the request
|
||||
using var requestDoc = JsonDocument.Parse(requestJson);
|
||||
var request = requestDoc.RootElement;
|
||||
|
||||
// Assert - Request has conversation history
|
||||
var messages = request.GetProperty("messages");
|
||||
Assert.True(messages.GetArrayLength() >= 3, "Should have at least 3 messages for multi-turn");
|
||||
|
||||
// Assert - Message sequence alternates between user and assistant
|
||||
AssertJsonPropertyEquals(messages[0], "role", "user");
|
||||
AssertJsonPropertyEquals(messages[1], "role", "assistant");
|
||||
AssertJsonPropertyEquals(messages[2], "role", "user");
|
||||
|
||||
// Assert - Response continues conversation
|
||||
var responseMessage = response.GetProperty("choices")[0].GetProperty("message");
|
||||
AssertJsonPropertyEquals(responseMessage, "role", "assistant");
|
||||
string content = responseMessage.GetProperty("content").GetString()!;
|
||||
Assert.NotNull(content);
|
||||
Assert.Equal(expectedText, content);
|
||||
|
||||
// Assert - Usage tokens account for conversation history
|
||||
var usage = response.GetProperty("usage");
|
||||
int promptTokens = usage.GetProperty("prompt_tokens").GetInt32();
|
||||
Assert.True(promptTokens > 20, "Prompt tokens should account for conversation history");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task JsonModeRequestResponseAsync()
|
||||
{
|
||||
// Arrange
|
||||
string requestJson = LoadChatCompletionsTraceFile("json_mode/request.json");
|
||||
using var expectedResponseDoc = LoadChatCompletionsTraceDocument("json_mode/response.json");
|
||||
var expectedResponse = expectedResponseDoc.RootElement;
|
||||
|
||||
string expectedText = expectedResponse.GetProperty("choices")[0]
|
||||
.GetProperty("message")
|
||||
.GetProperty("content").GetString()!;
|
||||
|
||||
HttpClient client = await this.CreateTestServerAsync("json-agent", "You are a helpful assistant that outputs JSON.", expectedText);
|
||||
|
||||
// Act
|
||||
HttpResponseMessage httpResponse = await this.SendChatCompletionRequestAsync(client, "json-agent", requestJson);
|
||||
using var responseDoc = await ParseResponseAsync(httpResponse);
|
||||
var response = responseDoc.RootElement;
|
||||
|
||||
// Parse the request
|
||||
using var requestDoc = JsonDocument.Parse(requestJson);
|
||||
var request = requestDoc.RootElement;
|
||||
|
||||
// Assert - Request has response_format with json_schema
|
||||
AssertJsonPropertyExists(request, "response_format");
|
||||
var responseFormat = request.GetProperty("response_format");
|
||||
AssertJsonPropertyEquals(responseFormat, "type", "json_schema");
|
||||
AssertJsonPropertyExists(responseFormat, "json_schema");
|
||||
|
||||
var jsonSchema = responseFormat.GetProperty("json_schema");
|
||||
AssertJsonPropertyEquals(jsonSchema, "name", "person_info");
|
||||
AssertJsonPropertyEquals(jsonSchema, "strict", true);
|
||||
AssertJsonPropertyExists(jsonSchema, "schema");
|
||||
|
||||
var schema = jsonSchema.GetProperty("schema");
|
||||
AssertJsonPropertyEquals(schema, "type", "object");
|
||||
AssertJsonPropertyExists(schema, "properties");
|
||||
AssertJsonPropertyExists(schema, "required");
|
||||
|
||||
// Assert - Response content is valid JSON matching schema
|
||||
var responseMessage = response.GetProperty("choices")[0].GetProperty("message");
|
||||
string content = responseMessage.GetProperty("content").GetString()!;
|
||||
Assert.NotNull(content);
|
||||
Assert.Equal(expectedText, content);
|
||||
|
||||
using var jsonDoc = JsonDocument.Parse(content);
|
||||
var jsonRoot = jsonDoc.RootElement;
|
||||
AssertJsonPropertyExists(jsonRoot, "name");
|
||||
AssertJsonPropertyExists(jsonRoot, "age");
|
||||
AssertJsonPropertyExists(jsonRoot, "occupation");
|
||||
|
||||
Assert.Equal(JsonValueKind.String, jsonRoot.GetProperty("name").ValueKind);
|
||||
Assert.Equal(JsonValueKind.Number, jsonRoot.GetProperty("age").ValueKind);
|
||||
Assert.Equal(JsonValueKind.String, jsonRoot.GetProperty("occupation").ValueKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ToolsSerializationDeserializationAsync()
|
||||
{
|
||||
// Arrange
|
||||
string requestJson = LoadChatCompletionsTraceFile("tools/request.json");
|
||||
using var expectedResponseDoc = LoadChatCompletionsTraceDocument("tools/response.json");
|
||||
|
||||
HttpClient client = await this.CreateTestServerAsync(
|
||||
"tools-agent",
|
||||
"You are a helpful assistant with access to weather and time tools.",
|
||||
"tool-call",
|
||||
(msg) => [new FunctionCallContent("call_abc123", "get_weather", new Dictionary<string, object?>() {
|
||||
{ "location", "San Francisco, CA" },
|
||||
{ "unit", "fahrenheit" }
|
||||
})]
|
||||
);
|
||||
|
||||
// Act
|
||||
HttpResponseMessage httpResponse = await this.SendChatCompletionRequestAsync(client, "tools-agent", requestJson);
|
||||
using var responseDoc = await ParseResponseAsync(httpResponse);
|
||||
var response = responseDoc.RootElement;
|
||||
|
||||
// Parse the request
|
||||
using var requestDoc = JsonDocument.Parse(requestJson);
|
||||
var request = requestDoc.RootElement;
|
||||
|
||||
// Assert - Request has tools array with proper structure
|
||||
AssertJsonPropertyExists(request, "tools");
|
||||
var tools = request.GetProperty("tools");
|
||||
Assert.Equal(JsonValueKind.Array, tools.ValueKind);
|
||||
Assert.Equal(2, tools.GetArrayLength());
|
||||
|
||||
// Assert - First tool (get_weather)
|
||||
var weatherTool = tools[0];
|
||||
AssertJsonPropertyEquals(weatherTool, "type", "function");
|
||||
AssertJsonPropertyExists(weatherTool, "function");
|
||||
|
||||
var weatherFunction = weatherTool.GetProperty("function");
|
||||
AssertJsonPropertyEquals(weatherFunction, "name", "get_weather");
|
||||
AssertJsonPropertyExists(weatherFunction, "description");
|
||||
AssertJsonPropertyExists(weatherFunction, "parameters");
|
||||
|
||||
var weatherParams = weatherFunction.GetProperty("parameters");
|
||||
AssertJsonPropertyEquals(weatherParams, "type", "object");
|
||||
AssertJsonPropertyExists(weatherParams, "properties");
|
||||
AssertJsonPropertyExists(weatherParams, "required");
|
||||
|
||||
// Verify location property exists
|
||||
var properties = weatherParams.GetProperty("properties");
|
||||
AssertJsonPropertyExists(properties, "location");
|
||||
AssertJsonPropertyExists(properties, "unit");
|
||||
|
||||
// Assert - Second tool (get_time)
|
||||
var timeTool = tools[1];
|
||||
AssertJsonPropertyEquals(timeTool, "type", "function");
|
||||
|
||||
var timeFunction = timeTool.GetProperty("function");
|
||||
AssertJsonPropertyEquals(timeFunction, "name", "get_time");
|
||||
AssertJsonPropertyExists(timeFunction, "description");
|
||||
AssertJsonPropertyExists(timeFunction, "parameters");
|
||||
|
||||
// Assert - Response structure
|
||||
AssertJsonPropertyExists(response, "id");
|
||||
AssertJsonPropertyEquals(response, "object", "chat.completion");
|
||||
AssertJsonPropertyExists(response, "created");
|
||||
AssertJsonPropertyExists(response, "model");
|
||||
|
||||
// Assert - Response has tool_calls in choices
|
||||
var choices = response.GetProperty("choices");
|
||||
Assert.Equal(JsonValueKind.Array, choices.ValueKind);
|
||||
Assert.True(choices.GetArrayLength() > 0);
|
||||
|
||||
var choice = choices[0];
|
||||
AssertJsonPropertyExists(choice, "finish_reason");
|
||||
AssertJsonPropertyEquals(choice, "finish_reason", anyOfValues: ["tool_calls", "stop"]);
|
||||
AssertJsonPropertyExists(choice, "message");
|
||||
|
||||
var message = choice.GetProperty("message");
|
||||
AssertJsonPropertyEquals(message, "role", "assistant");
|
||||
AssertJsonPropertyExists(message, "tool_calls");
|
||||
|
||||
// Assert - Tool calls array structure
|
||||
var toolCalls = message.GetProperty("tool_calls");
|
||||
Assert.Equal(JsonValueKind.Array, toolCalls.ValueKind);
|
||||
Assert.True(toolCalls.GetArrayLength() > 0);
|
||||
|
||||
var toolCall = toolCalls[0];
|
||||
AssertJsonPropertyExists(toolCall, "id");
|
||||
AssertJsonPropertyEquals(toolCall, "type", "function");
|
||||
AssertJsonPropertyExists(toolCall, "function");
|
||||
|
||||
var callFunction = toolCall.GetProperty("function");
|
||||
AssertJsonPropertyEquals(callFunction, "name", "get_weather");
|
||||
AssertJsonPropertyExists(callFunction, "arguments");
|
||||
|
||||
// Assert - Tool call arguments are valid JSON
|
||||
string arguments = callFunction.GetProperty("arguments").GetString()!;
|
||||
using var argsDoc = JsonDocument.Parse(arguments);
|
||||
var argsRoot = argsDoc.RootElement;
|
||||
AssertJsonPropertyExists(argsRoot, "location");
|
||||
AssertJsonPropertyEquals(argsRoot, "location", "San Francisco, CA");
|
||||
AssertJsonPropertyEquals(argsRoot, "unit", "fahrenheit");
|
||||
|
||||
// Assert - Message content is null when tool_calls present
|
||||
if (message.TryGetProperty("content", out var contentProp))
|
||||
{
|
||||
Assert.Equal(JsonValueKind.Null, contentProp.ValueKind);
|
||||
}
|
||||
|
||||
// Assert - Usage statistics
|
||||
AssertJsonPropertyExists(response, "usage");
|
||||
var usage = response.GetProperty("usage");
|
||||
AssertJsonPropertyExists(usage, "prompt_tokens");
|
||||
AssertJsonPropertyExists(usage, "completion_tokens");
|
||||
AssertJsonPropertyExists(usage, "total_tokens");
|
||||
|
||||
var promptTokens = usage.GetProperty("prompt_tokens").GetInt32();
|
||||
var completionTokens = usage.GetProperty("completion_tokens").GetInt32();
|
||||
var totalTokens = usage.GetProperty("total_tokens").GetInt32();
|
||||
|
||||
Assert.True(promptTokens > 0);
|
||||
Assert.True(completionTokens > 0);
|
||||
Assert.Equal(promptTokens + completionTokens, totalTokens);
|
||||
|
||||
// Assert - Service tier
|
||||
AssertJsonPropertyExists(response, "service_tier");
|
||||
var serviceTier = response.GetProperty("service_tier").GetString();
|
||||
Assert.NotNull(serviceTier);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper to parse chat completion chunks from SSE response.
|
||||
/// </summary>
|
||||
private static List<JsonElement> ParseChatCompletionChunksFromSse(string sseContent)
|
||||
{
|
||||
var chunks = new List<JsonElement>();
|
||||
var lines = sseContent.Split('\n');
|
||||
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
var line = lines[i].TrimEnd('\r');
|
||||
|
||||
if (line.StartsWith("data: ", System.StringComparison.Ordinal))
|
||||
{
|
||||
var jsonData = line.Substring("data: ".Length);
|
||||
|
||||
// Skip [DONE] marker
|
||||
if (jsonData == "[DONE]")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var doc = JsonDocument.Parse(jsonData);
|
||||
chunks.Add(doc.RootElement.Clone());
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Skip invalid JSON
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return chunks;
|
||||
}
|
||||
}
|
||||
+974
@@ -0,0 +1,974 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.ClientModel;
|
||||
using System.ClientModel.Primitives;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting.Server;
|
||||
using Microsoft.AspNetCore.TestHost;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using OpenAI;
|
||||
using OpenAI.Chat;
|
||||
using ChatFinishReason = OpenAI.Chat.ChatFinishReason;
|
||||
using ChatMessage = OpenAI.Chat.ChatMessage;
|
||||
|
||||
namespace Microsoft.Agents.AI.Hosting.OpenAI.UnitTests;
|
||||
|
||||
/// <summary>
|
||||
/// Integration tests that start a web server and use the OpenAI Chat Completions SDK client to verify protocol compatibility.
|
||||
/// These tests validate both streaming and non-streaming request scenarios.
|
||||
/// </summary>
|
||||
public sealed class OpenAIChatCompletionsIntegrationTests : IAsyncDisposable
|
||||
{
|
||||
private WebApplication? _app;
|
||||
private HttpClient? _httpClient;
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
this._httpClient?.Dispose();
|
||||
if (this._app != null)
|
||||
{
|
||||
await this._app.DisposeAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that streaming chat completions work correctly with the OpenAI SDK client.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task CreateChatCompletionStreaming_WithSimpleMessage_ReturnsStreamingUpdatesAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "streaming-agent";
|
||||
const string Instructions = "You are a helpful assistant.";
|
||||
const string ExpectedResponse = "One Two Three";
|
||||
|
||||
this._httpClient = await this.CreateTestServerAsync(AgentName, Instructions, ExpectedResponse);
|
||||
ChatClient chatClient = this.CreateChatClient(AgentName);
|
||||
|
||||
List<ChatMessage> messages =
|
||||
[
|
||||
new UserChatMessage("Count to 3")
|
||||
];
|
||||
|
||||
// Act
|
||||
AsyncCollectionResult<StreamingChatCompletionUpdate> streamingResult = chatClient.CompleteChatStreamingAsync(messages);
|
||||
|
||||
// Assert
|
||||
List<StreamingChatCompletionUpdate> updates = [];
|
||||
StringBuilder contentBuilder = new();
|
||||
await foreach (StreamingChatCompletionUpdate update in streamingResult)
|
||||
{
|
||||
updates.Add(update);
|
||||
if (update.ContentUpdate.Count > 0)
|
||||
{
|
||||
foreach (ChatMessageContentPart contentPart in update.ContentUpdate)
|
||||
{
|
||||
contentBuilder.Append(contentPart.Text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Assert.NotEmpty(updates);
|
||||
|
||||
// Verify content was received
|
||||
string content = contentBuilder.ToString();
|
||||
Assert.Equal(ExpectedResponse, content);
|
||||
|
||||
// Verify finish reason
|
||||
StreamingChatCompletionUpdate? lastUpdate = updates.LastOrDefault(u => u.FinishReason != null);
|
||||
Assert.NotNull(lastUpdate);
|
||||
Assert.Equal(ChatFinishReason.Stop, lastUpdate.FinishReason);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that non-streaming chat completions work correctly with the OpenAI SDK client.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task CreateChatCompletion_WithSimpleMessage_ReturnsCompleteResponseAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "non-streaming-agent";
|
||||
const string Instructions = "You are a helpful assistant.";
|
||||
const string ExpectedResponse = "Hello! How can I help you today?";
|
||||
|
||||
this._httpClient = await this.CreateTestServerAsync(AgentName, Instructions, ExpectedResponse);
|
||||
ChatClient chatClient = this.CreateChatClient(AgentName);
|
||||
|
||||
List<ChatMessage> messages =
|
||||
[
|
||||
new UserChatMessage("Hello")
|
||||
];
|
||||
|
||||
// Act
|
||||
ChatCompletion completion = await chatClient.CompleteChatAsync(messages);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(completion);
|
||||
Assert.NotNull(completion.Id);
|
||||
Assert.StartsWith("chatcmpl-", completion.Id);
|
||||
Assert.Equal(ChatFinishReason.Stop, completion.FinishReason);
|
||||
|
||||
// Verify content
|
||||
string content = completion.Content[0].Text;
|
||||
Assert.Equal(ExpectedResponse, content);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that streaming chat completions can handle multiple content chunks.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task CreateChatCompletionStreaming_WithMultipleChunks_StreamsAllContentAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "multi-chunk-agent";
|
||||
const string Instructions = "You are a helpful assistant.";
|
||||
const string ExpectedResponse = "This is a test response with multiple words";
|
||||
|
||||
this._httpClient = await this.CreateTestServerAsync(AgentName, Instructions, ExpectedResponse);
|
||||
ChatClient chatClient = this.CreateChatClient(AgentName);
|
||||
|
||||
List<ChatMessage> messages =
|
||||
[
|
||||
new UserChatMessage("Test")
|
||||
];
|
||||
|
||||
// Act
|
||||
AsyncCollectionResult<StreamingChatCompletionUpdate> streamingResult = chatClient.CompleteChatStreamingAsync(messages);
|
||||
|
||||
// Assert
|
||||
List<StreamingChatCompletionUpdate> updates = [];
|
||||
StringBuilder contentBuilder = new();
|
||||
await foreach (StreamingChatCompletionUpdate update in streamingResult)
|
||||
{
|
||||
updates.Add(update);
|
||||
foreach (ChatMessageContentPart contentPart in update.ContentUpdate)
|
||||
{
|
||||
contentBuilder.Append(contentPart.Text);
|
||||
}
|
||||
}
|
||||
|
||||
// Verify all content was received
|
||||
string receivedContent = contentBuilder.ToString();
|
||||
Assert.Equal(ExpectedResponse, receivedContent);
|
||||
|
||||
// Verify multiple content chunks were received
|
||||
List<StreamingChatCompletionUpdate> contentUpdates = updates.Where(u => u.ContentUpdate.Count > 0).ToList();
|
||||
Assert.True(contentUpdates.Count > 1, "Expected multiple content chunks in streaming response");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that multiple agents can be accessed via the same server.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task CreateChatCompletion_WithMultipleAgents_EachAgentRespondsCorrectlyAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string Agent1Name = "agent-one";
|
||||
const string Agent1Instructions = "You are agent one.";
|
||||
const string Agent1Response = "Response from agent one";
|
||||
|
||||
const string Agent2Name = "agent-two";
|
||||
const string Agent2Instructions = "You are agent two.";
|
||||
const string Agent2Response = "Response from agent two";
|
||||
|
||||
this._httpClient = await this.CreateTestServerWithMultipleAgentsAsync(
|
||||
(Agent1Name, Agent1Instructions, Agent1Response),
|
||||
(Agent2Name, Agent2Instructions, Agent2Response));
|
||||
|
||||
ChatClient chatClient1 = this.CreateChatClient(Agent1Name);
|
||||
ChatClient chatClient2 = this.CreateChatClient(Agent2Name);
|
||||
|
||||
List<ChatMessage> messages =
|
||||
[
|
||||
new UserChatMessage("Hello")
|
||||
];
|
||||
|
||||
// Act
|
||||
ChatCompletion completion1 = await chatClient1.CompleteChatAsync(messages);
|
||||
ChatCompletion completion2 = await chatClient2.CompleteChatAsync(messages);
|
||||
|
||||
// Assert
|
||||
string content1 = completion1.Content[0].Text;
|
||||
string content2 = completion2.Content[0].Text;
|
||||
|
||||
Assert.Equal(Agent1Response, content1);
|
||||
Assert.Equal(Agent2Response, content2);
|
||||
Assert.NotEqual(content1, content2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that streaming and non-streaming work correctly for the same agent.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task CreateChatCompletion_SameAgentStreamingAndNonStreaming_BothWorkCorrectlyAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "dual-mode-agent";
|
||||
const string Instructions = "You are a helpful assistant.";
|
||||
const string ExpectedResponse = "This is the response";
|
||||
|
||||
this._httpClient = await this.CreateTestServerAsync(AgentName, Instructions, ExpectedResponse);
|
||||
ChatClient chatClient = this.CreateChatClient(AgentName);
|
||||
|
||||
List<ChatMessage> messages =
|
||||
[
|
||||
new UserChatMessage("Test")
|
||||
];
|
||||
|
||||
// Act - Non-streaming
|
||||
ChatCompletion nonStreamingCompletion = await chatClient.CompleteChatAsync(messages);
|
||||
|
||||
// Act - Streaming
|
||||
AsyncCollectionResult<StreamingChatCompletionUpdate> streamingResult = chatClient.CompleteChatStreamingAsync(messages);
|
||||
StringBuilder streamingContent = new();
|
||||
await foreach (StreamingChatCompletionUpdate update in streamingResult)
|
||||
{
|
||||
foreach (ChatMessageContentPart contentPart in update.ContentUpdate)
|
||||
{
|
||||
streamingContent.Append(contentPart.Text);
|
||||
}
|
||||
}
|
||||
|
||||
// Assert
|
||||
string nonStreamingContent = nonStreamingCompletion.Content[0].Text;
|
||||
Assert.Equal(ExpectedResponse, nonStreamingContent);
|
||||
Assert.Equal(ExpectedResponse, streamingContent.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the finish reason is correctly set for completed responses.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task CreateChatCompletion_CompletedResponse_HasCorrectFinishReasonAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "finish-reason-agent";
|
||||
const string Instructions = "You are a helpful assistant.";
|
||||
const string ExpectedResponse = "Complete";
|
||||
|
||||
this._httpClient = await this.CreateTestServerAsync(AgentName, Instructions, ExpectedResponse);
|
||||
ChatClient chatClient = this.CreateChatClient(AgentName);
|
||||
|
||||
List<ChatMessage> messages =
|
||||
[
|
||||
new UserChatMessage("Test")
|
||||
];
|
||||
|
||||
// Act
|
||||
ChatCompletion completion = await chatClient.CompleteChatAsync(messages);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(ChatFinishReason.Stop, completion.FinishReason);
|
||||
Assert.NotNull(completion.Id);
|
||||
Assert.Equal(ExpectedResponse, completion.Content[0].Text);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that streaming responses contain the expected chunk sequence.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task CreateChatCompletionStreaming_VerifyChunkSequence_ContainsExpectedDataAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "chunk-sequence-agent";
|
||||
const string Instructions = "You are a helpful assistant.";
|
||||
const string ExpectedResponse = "Test response with multiple words";
|
||||
|
||||
this._httpClient = await this.CreateTestServerAsync(AgentName, Instructions, ExpectedResponse);
|
||||
ChatClient chatClient = this.CreateChatClient(AgentName);
|
||||
|
||||
List<ChatMessage> messages =
|
||||
[
|
||||
new UserChatMessage("Test")
|
||||
];
|
||||
|
||||
// Act
|
||||
AsyncCollectionResult<StreamingChatCompletionUpdate> streamingResult = chatClient.CompleteChatStreamingAsync(messages);
|
||||
|
||||
// Assert
|
||||
List<StreamingChatCompletionUpdate> updates = [];
|
||||
await foreach (StreamingChatCompletionUpdate update in streamingResult)
|
||||
{
|
||||
updates.Add(update);
|
||||
}
|
||||
|
||||
// Verify chunks received
|
||||
Assert.NotEmpty(updates);
|
||||
|
||||
// First chunk should have role
|
||||
StreamingChatCompletionUpdate? firstUpdate = updates.FirstOrDefault(u => u.Role != null);
|
||||
if (firstUpdate != null)
|
||||
{
|
||||
Assert.Equal(ChatMessageRole.Assistant, firstUpdate.Role);
|
||||
}
|
||||
|
||||
// Should contain content chunks
|
||||
List<StreamingChatCompletionUpdate> contentUpdates = updates.Where(u => u.ContentUpdate.Count > 0).ToList();
|
||||
Assert.NotEmpty(contentUpdates);
|
||||
|
||||
// Last update should have finish reason
|
||||
StreamingChatCompletionUpdate? lastUpdate = updates.LastOrDefault(u => u.FinishReason != null);
|
||||
Assert.NotNull(lastUpdate);
|
||||
Assert.Equal(ChatFinishReason.Stop, lastUpdate.FinishReason);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that streaming responses properly handle empty responses.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task CreateChatCompletionStreaming_EmptyResponse_HandlesGracefullyAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "empty-response-agent";
|
||||
const string Instructions = "You are a helpful assistant.";
|
||||
const string ExpectedResponse = "";
|
||||
|
||||
this._httpClient = await this.CreateTestServerAsync(AgentName, Instructions, ExpectedResponse);
|
||||
ChatClient chatClient = this.CreateChatClient(AgentName);
|
||||
|
||||
List<ChatMessage> messages =
|
||||
[
|
||||
new UserChatMessage("Test")
|
||||
];
|
||||
|
||||
// Act
|
||||
AsyncCollectionResult<StreamingChatCompletionUpdate> streamingResult = chatClient.CompleteChatStreamingAsync(messages);
|
||||
|
||||
// Assert
|
||||
List<StreamingChatCompletionUpdate> updates = [];
|
||||
await foreach (StreamingChatCompletionUpdate update in streamingResult)
|
||||
{
|
||||
updates.Add(update);
|
||||
}
|
||||
|
||||
// Should still receive chunks with finish reason
|
||||
Assert.NotEmpty(updates);
|
||||
Assert.Contains(updates, u => u.FinishReason == ChatFinishReason.Stop);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that non-streaming responses include proper metadata.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task CreateChatCompletion_IncludesMetadata_HasRequiredFieldsAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "metadata-agent";
|
||||
const string Instructions = "You are a helpful assistant.";
|
||||
const string ExpectedResponse = "Response with metadata";
|
||||
|
||||
this._httpClient = await this.CreateTestServerAsync(AgentName, Instructions, ExpectedResponse);
|
||||
ChatClient chatClient = this.CreateChatClient(AgentName);
|
||||
|
||||
List<ChatMessage> messages =
|
||||
[
|
||||
new UserChatMessage("Test")
|
||||
];
|
||||
|
||||
// Act
|
||||
ChatCompletion completion = await chatClient.CompleteChatAsync(messages);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(completion.Id);
|
||||
Assert.StartsWith("chatcmpl-", completion.Id);
|
||||
Assert.NotNull(completion.Model);
|
||||
Assert.NotEqual(default, completion.CreatedAt);
|
||||
Assert.Equal(ChatFinishReason.Stop, completion.FinishReason);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that streaming responses handle very long text correctly.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task CreateChatCompletionStreaming_LongText_StreamsAllContentAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "long-text-agent";
|
||||
const string Instructions = "You are a helpful assistant.";
|
||||
string expectedResponse = string.Join(" ", Enumerable.Range(1, 100).Select(i => $"Word{i}"));
|
||||
|
||||
this._httpClient = await this.CreateTestServerAsync(AgentName, Instructions, expectedResponse);
|
||||
ChatClient chatClient = this.CreateChatClient(AgentName);
|
||||
|
||||
List<ChatMessage> messages =
|
||||
[
|
||||
new UserChatMessage("Generate long text")
|
||||
];
|
||||
|
||||
// Act
|
||||
AsyncCollectionResult<StreamingChatCompletionUpdate> streamingResult = chatClient.CompleteChatStreamingAsync(messages);
|
||||
|
||||
// Assert
|
||||
StringBuilder contentBuilder = new();
|
||||
await foreach (StreamingChatCompletionUpdate update in streamingResult)
|
||||
{
|
||||
foreach (ChatMessageContentPart contentPart in update.ContentUpdate)
|
||||
{
|
||||
contentBuilder.Append(contentPart.Text);
|
||||
}
|
||||
}
|
||||
|
||||
string receivedContent = contentBuilder.ToString();
|
||||
Assert.Equal(expectedResponse, receivedContent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that streaming responses properly handle single-word responses.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task CreateChatCompletionStreaming_SingleWord_StreamsCorrectlyAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "single-word-agent";
|
||||
const string Instructions = "You are a helpful assistant.";
|
||||
const string ExpectedResponse = "Hello";
|
||||
|
||||
this._httpClient = await this.CreateTestServerAsync(AgentName, Instructions, ExpectedResponse);
|
||||
ChatClient chatClient = this.CreateChatClient(AgentName);
|
||||
|
||||
List<ChatMessage> messages =
|
||||
[
|
||||
new UserChatMessage("Test")
|
||||
];
|
||||
|
||||
// Act
|
||||
AsyncCollectionResult<StreamingChatCompletionUpdate> streamingResult = chatClient.CompleteChatStreamingAsync(messages);
|
||||
|
||||
// Assert
|
||||
StringBuilder contentBuilder = new();
|
||||
await foreach (StreamingChatCompletionUpdate update in streamingResult)
|
||||
{
|
||||
foreach (ChatMessageContentPart contentPart in update.ContentUpdate)
|
||||
{
|
||||
contentBuilder.Append(contentPart.Text);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.Equal(ExpectedResponse, contentBuilder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that streaming responses preserve special characters and formatting.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task CreateChatCompletionStreaming_SpecialCharacters_PreservesFormattingAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "special-chars-agent";
|
||||
const string Instructions = "You are a helpful assistant.";
|
||||
const string ExpectedResponse = "Hello! How are you? I'm fine. 100% great!";
|
||||
|
||||
this._httpClient = await this.CreateTestServerAsync(AgentName, Instructions, ExpectedResponse);
|
||||
ChatClient chatClient = this.CreateChatClient(AgentName);
|
||||
|
||||
List<ChatMessage> messages =
|
||||
[
|
||||
new UserChatMessage("Test")
|
||||
];
|
||||
|
||||
// Act
|
||||
AsyncCollectionResult<StreamingChatCompletionUpdate> streamingResult = chatClient.CompleteChatStreamingAsync(messages);
|
||||
|
||||
// Assert
|
||||
StringBuilder contentBuilder = new();
|
||||
await foreach (StreamingChatCompletionUpdate update in streamingResult)
|
||||
{
|
||||
foreach (ChatMessageContentPart contentPart in update.ContentUpdate)
|
||||
{
|
||||
contentBuilder.Append(contentPart.Text);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.Equal(ExpectedResponse, contentBuilder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that non-streaming responses handle special characters correctly.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task CreateChatCompletion_SpecialCharacters_PreservesContentAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "special-chars-nonstreaming-agent";
|
||||
const string Instructions = "You are a helpful assistant.";
|
||||
const string ExpectedResponse = "Symbols: @#$%^&*() Quotes: \"Hello\" 'World' Unicode: 你好 🌍";
|
||||
|
||||
this._httpClient = await this.CreateTestServerAsync(AgentName, Instructions, ExpectedResponse);
|
||||
ChatClient chatClient = this.CreateChatClient(AgentName);
|
||||
|
||||
List<ChatMessage> messages =
|
||||
[
|
||||
new UserChatMessage("Test")
|
||||
];
|
||||
|
||||
// Act
|
||||
ChatCompletion completion = await chatClient.CompleteChatAsync(messages);
|
||||
|
||||
// Assert
|
||||
string content = completion.Content[0].Text;
|
||||
Assert.Equal(ExpectedResponse, content);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that multiple sequential non-streaming requests work correctly.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task CreateChatCompletion_MultipleSequentialRequests_AllSucceedAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "sequential-agent";
|
||||
const string Instructions = "You are a helpful assistant.";
|
||||
const string ExpectedResponse = "Response";
|
||||
|
||||
this._httpClient = await this.CreateTestServerAsync(AgentName, Instructions, ExpectedResponse);
|
||||
ChatClient chatClient = this.CreateChatClient(AgentName);
|
||||
|
||||
// Act & Assert - Make 5 sequential requests
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
List<ChatMessage> messages =
|
||||
[
|
||||
new UserChatMessage($"Request {i}")
|
||||
];
|
||||
|
||||
ChatCompletion completion = await chatClient.CompleteChatAsync(messages);
|
||||
Assert.NotNull(completion);
|
||||
Assert.Equal(ChatFinishReason.Stop, completion.FinishReason);
|
||||
Assert.Equal(ExpectedResponse, completion.Content[0].Text);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that multiple sequential streaming requests work correctly.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task CreateChatCompletionStreaming_MultipleSequentialRequests_AllStreamCorrectlyAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "sequential-streaming-agent";
|
||||
const string Instructions = "You are a helpful assistant.";
|
||||
const string ExpectedResponse = "Streaming response";
|
||||
|
||||
this._httpClient = await this.CreateTestServerAsync(AgentName, Instructions, ExpectedResponse);
|
||||
ChatClient chatClient = this.CreateChatClient(AgentName);
|
||||
|
||||
// Act & Assert - Make 3 sequential streaming requests
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
List<ChatMessage> messages =
|
||||
[
|
||||
new UserChatMessage($"Request {i}")
|
||||
];
|
||||
|
||||
AsyncCollectionResult<StreamingChatCompletionUpdate> streamingResult = chatClient.CompleteChatStreamingAsync(messages);
|
||||
StringBuilder contentBuilder = new();
|
||||
|
||||
await foreach (StreamingChatCompletionUpdate update in streamingResult)
|
||||
{
|
||||
foreach (ChatMessageContentPart contentPart in update.ContentUpdate)
|
||||
{
|
||||
contentBuilder.Append(contentPart.Text);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.Equal(ExpectedResponse, contentBuilder.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that completion IDs are unique across multiple requests.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task CreateChatCompletion_MultipleRequests_GenerateUniqueIdsAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "unique-id-agent";
|
||||
const string Instructions = "You are a helpful assistant.";
|
||||
const string ExpectedResponse = "Response";
|
||||
|
||||
this._httpClient = await this.CreateTestServerAsync(AgentName, Instructions, ExpectedResponse);
|
||||
ChatClient chatClient = this.CreateChatClient(AgentName);
|
||||
|
||||
// Act
|
||||
List<string> completionIds = [];
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
List<ChatMessage> messages =
|
||||
[
|
||||
new UserChatMessage($"Request {i}")
|
||||
];
|
||||
|
||||
ChatCompletion completion = await chatClient.CompleteChatAsync(messages);
|
||||
completionIds.Add(completion.Id);
|
||||
}
|
||||
|
||||
// Assert
|
||||
Assert.Equal(10, completionIds.Count);
|
||||
Assert.Equal(completionIds.Count, completionIds.Distinct().Count()); // All IDs should be unique
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that streaming responses all have the same ID within a single request.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task CreateChatCompletionStreaming_SameRequestId_ConsistentAcrossChunksAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "consistent-id-agent";
|
||||
const string Instructions = "You are a helpful assistant.";
|
||||
const string ExpectedResponse = "Test consistent ID across chunks";
|
||||
|
||||
this._httpClient = await this.CreateTestServerAsync(AgentName, Instructions, ExpectedResponse);
|
||||
ChatClient chatClient = this.CreateChatClient(AgentName);
|
||||
|
||||
List<ChatMessage> messages =
|
||||
[
|
||||
new UserChatMessage("Test")
|
||||
];
|
||||
|
||||
// Act
|
||||
AsyncCollectionResult<StreamingChatCompletionUpdate> streamingResult = chatClient.CompleteChatStreamingAsync(messages);
|
||||
|
||||
// Assert
|
||||
List<string> chunkIds = [];
|
||||
await foreach (StreamingChatCompletionUpdate update in streamingResult)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(update.CompletionId))
|
||||
{
|
||||
chunkIds.Add(update.CompletionId);
|
||||
}
|
||||
}
|
||||
|
||||
// All chunk IDs should be the same within a single request
|
||||
Assert.NotEmpty(chunkIds);
|
||||
Assert.All(chunkIds, id => Assert.Equal(chunkIds[0], id));
|
||||
Assert.StartsWith("chatcmpl-", chunkIds[0]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that non-streaming responses work with system messages.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task CreateChatCompletion_WithSystemMessage_ReturnsValidResponseAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "system-message-agent";
|
||||
const string Instructions = "You are a helpful assistant.";
|
||||
const string ExpectedResponse = "I am following the system instructions";
|
||||
|
||||
this._httpClient = await this.CreateTestServerAsync(AgentName, Instructions, ExpectedResponse);
|
||||
ChatClient chatClient = this.CreateChatClient(AgentName);
|
||||
|
||||
List<ChatMessage> messages =
|
||||
[
|
||||
new SystemChatMessage("You must respond in a specific way"),
|
||||
new UserChatMessage("Hello")
|
||||
];
|
||||
|
||||
// Act
|
||||
ChatCompletion completion = await chatClient.CompleteChatAsync(messages);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(completion);
|
||||
Assert.Equal(ChatFinishReason.Stop, completion.FinishReason);
|
||||
Assert.Equal(ExpectedResponse, completion.Content[0].Text);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that responses handle newlines correctly.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task CreateChatCompletion_Newlines_PreservesFormattingAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "newline-agent";
|
||||
const string Instructions = "You are a helpful assistant.";
|
||||
const string ExpectedResponse = "Line 1\nLine 2\nLine 3";
|
||||
|
||||
this._httpClient = await this.CreateTestServerAsync(AgentName, Instructions, ExpectedResponse);
|
||||
ChatClient chatClient = this.CreateChatClient(AgentName);
|
||||
|
||||
List<ChatMessage> messages =
|
||||
[
|
||||
new UserChatMessage("Test")
|
||||
];
|
||||
|
||||
// Act
|
||||
ChatCompletion completion = await chatClient.CompleteChatAsync(messages);
|
||||
|
||||
// Assert
|
||||
string content = completion.Content[0].Text;
|
||||
Assert.Equal(ExpectedResponse, content);
|
||||
Assert.Contains("\n", content);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that streaming responses handle newlines correctly.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task CreateChatCompletionStreaming_Newlines_PreservesFormattingAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "newline-streaming-agent";
|
||||
const string Instructions = "You are a helpful assistant.";
|
||||
const string ExpectedResponse = "First line\nSecond line\nThird line";
|
||||
|
||||
this._httpClient = await this.CreateTestServerAsync(AgentName, Instructions, ExpectedResponse);
|
||||
ChatClient chatClient = this.CreateChatClient(AgentName);
|
||||
|
||||
List<ChatMessage> messages =
|
||||
[
|
||||
new UserChatMessage("Test")
|
||||
];
|
||||
|
||||
// Act
|
||||
AsyncCollectionResult<StreamingChatCompletionUpdate> streamingResult = chatClient.CompleteChatStreamingAsync(messages);
|
||||
|
||||
// Assert
|
||||
StringBuilder contentBuilder = new();
|
||||
await foreach (StreamingChatCompletionUpdate update in streamingResult)
|
||||
{
|
||||
foreach (ChatMessageContentPart contentPart in update.ContentUpdate)
|
||||
{
|
||||
contentBuilder.Append(contentPart.Text);
|
||||
}
|
||||
}
|
||||
|
||||
string content = contentBuilder.ToString();
|
||||
Assert.Equal(ExpectedResponse, content);
|
||||
Assert.Contains("\n", content);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that responses with conversation history work correctly.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task CreateChatCompletion_WithConversationHistory_ReturnsValidResponseAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "conversation-agent";
|
||||
const string Instructions = "You are a helpful assistant.";
|
||||
const string ExpectedResponse = "3 plus 3 equals 6";
|
||||
|
||||
this._httpClient = await this.CreateTestServerAsync(AgentName, Instructions, ExpectedResponse);
|
||||
ChatClient chatClient = this.CreateChatClient(AgentName);
|
||||
|
||||
List<ChatMessage> messages =
|
||||
[
|
||||
new UserChatMessage("What is 2+2?"),
|
||||
new AssistantChatMessage("2+2 equals 4"),
|
||||
new UserChatMessage("What about 3+3?")
|
||||
];
|
||||
|
||||
// Act
|
||||
ChatCompletion completion = await chatClient.CompleteChatAsync(messages);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(completion);
|
||||
Assert.Equal(ChatFinishReason.Stop, completion.FinishReason);
|
||||
Assert.Equal(ExpectedResponse, completion.Content[0].Text);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that usage information is included in non-streaming responses.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task CreateChatCompletion_IncludesUsage_HasTokenCountsAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "usage-agent";
|
||||
const string Instructions = "You are a helpful assistant.";
|
||||
const string ExpectedResponse = "Response with usage information";
|
||||
|
||||
this._httpClient = await this.CreateTestServerAsync(AgentName, Instructions, ExpectedResponse);
|
||||
ChatClient chatClient = this.CreateChatClient(AgentName);
|
||||
|
||||
List<ChatMessage> messages =
|
||||
[
|
||||
new UserChatMessage("Test")
|
||||
];
|
||||
|
||||
// Act
|
||||
ChatCompletion completion = await chatClient.CompleteChatAsync(messages);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(completion.Usage);
|
||||
Assert.True(completion.Usage.InputTokenCount > 0);
|
||||
Assert.True(completion.Usage.OutputTokenCount > 0);
|
||||
Assert.Equal(completion.Usage.InputTokenCount + completion.Usage.OutputTokenCount, completion.Usage.TotalTokenCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that responses with function calls work correctly.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task CreateChatCompletion_WithFunctionCall_ReturnsToolCallsAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "function-call-agent";
|
||||
const string Instructions = "You are a helpful assistant.";
|
||||
const string FunctionName = "get_weather";
|
||||
const string Arguments = "{\"location\":\"Seattle\"}";
|
||||
|
||||
this._httpClient = await this.CreateTestServerWithCustomClientAsync(
|
||||
agentName: AgentName,
|
||||
instructions: Instructions,
|
||||
chatClient: new TestHelpers.FunctionCallMockChatClient(FunctionName, Arguments));
|
||||
|
||||
ChatClient openAIChatClient = this.CreateChatClient(AgentName);
|
||||
|
||||
List<ChatMessage> messages =
|
||||
[
|
||||
new UserChatMessage("What's the weather?")
|
||||
];
|
||||
|
||||
// Act
|
||||
ChatCompletion completion = await openAIChatClient.CompleteChatAsync(messages);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(completion);
|
||||
Assert.Equal(ChatFinishReason.ToolCalls, completion.FinishReason);
|
||||
Assert.NotNull(completion.ToolCalls);
|
||||
Assert.NotEmpty(completion.ToolCalls);
|
||||
|
||||
ChatToolCall toolCall = completion.ToolCalls[0];
|
||||
Assert.Equal(FunctionName, toolCall.FunctionName);
|
||||
Assert.NotNull(toolCall.FunctionArguments);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that streaming responses with function calls work correctly.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task CreateChatCompletionStreaming_WithFunctionCall_StreamsToolCallsAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "function-call-streaming-agent";
|
||||
const string Instructions = "You are a helpful assistant.";
|
||||
const string FunctionName = "calculate";
|
||||
const string Arguments = "{\"expression\":\"2+2\"}";
|
||||
|
||||
this._httpClient = await this.CreateTestServerWithCustomClientAsync(
|
||||
agentName: AgentName,
|
||||
instructions: Instructions,
|
||||
chatClient: new TestHelpers.FunctionCallMockChatClient(FunctionName, Arguments));
|
||||
|
||||
ChatClient openAIChatClient = this.CreateChatClient(AgentName);
|
||||
|
||||
List<ChatMessage> messages =
|
||||
[
|
||||
new UserChatMessage("Calculate 2+2")
|
||||
];
|
||||
|
||||
// Act
|
||||
AsyncCollectionResult<StreamingChatCompletionUpdate> streamingResult = openAIChatClient.CompleteChatStreamingAsync(messages);
|
||||
|
||||
// Assert
|
||||
List<StreamingChatCompletionUpdate> updates = [];
|
||||
await foreach (StreamingChatCompletionUpdate update in streamingResult)
|
||||
{
|
||||
updates.Add(update);
|
||||
}
|
||||
|
||||
Assert.NotEmpty(updates);
|
||||
|
||||
// Should have finish reason of tool_calls
|
||||
StreamingChatCompletionUpdate? lastUpdate = updates.LastOrDefault(u => u.FinishReason != null);
|
||||
Assert.NotNull(lastUpdate);
|
||||
Assert.True(lastUpdate.FinishReason is ChatFinishReason.ToolCalls or ChatFinishReason.Stop); // depends on what response we get
|
||||
}
|
||||
|
||||
private ChatClient CreateChatClient(string agentName)
|
||||
{
|
||||
return new ChatClient(
|
||||
model: "test-model",
|
||||
credential: new ApiKeyCredential("test-api-key"),
|
||||
options: new OpenAIClientOptions
|
||||
{
|
||||
Endpoint = new Uri(this._httpClient!.BaseAddress!, $"/{agentName}/v1/"),
|
||||
Transport = new HttpClientPipelineTransport(this._httpClient)
|
||||
});
|
||||
}
|
||||
|
||||
private async Task<HttpClient> CreateTestServerAsync(string agentName, string instructions, string responseText = "Test response")
|
||||
{
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
builder.WebHost.UseTestServer();
|
||||
|
||||
IChatClient mockChatClient = new TestHelpers.SimpleMockChatClient(responseText);
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
builder.AddOpenAIChatCompletions();
|
||||
builder.AddAIAgent(agentName, instructions, chatClientServiceKey: "chat-client");
|
||||
|
||||
this._app = builder.Build();
|
||||
AIAgent agent = this._app.Services.GetRequiredKeyedService<AIAgent>(agentName);
|
||||
this._app.MapOpenAIChatCompletions(agent);
|
||||
|
||||
await this._app.StartAsync();
|
||||
|
||||
TestServer testServer = this._app.Services.GetRequiredService<IServer>() as TestServer
|
||||
?? throw new InvalidOperationException("TestServer not found");
|
||||
|
||||
return testServer.CreateClient();
|
||||
}
|
||||
|
||||
private async Task<HttpClient> CreateTestServerWithCustomClientAsync(string agentName, string instructions, IChatClient chatClient)
|
||||
{
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
builder.WebHost.UseTestServer();
|
||||
|
||||
builder.Services.AddKeyedSingleton($"chat-client-{agentName}", chatClient);
|
||||
builder.AddAIAgent(agentName, instructions, chatClientServiceKey: $"chat-client-{agentName}");
|
||||
builder.AddOpenAIChatCompletions();
|
||||
|
||||
this._app = builder.Build();
|
||||
AIAgent agent = this._app.Services.GetRequiredKeyedService<AIAgent>(agentName);
|
||||
this._app.MapOpenAIChatCompletions(agent);
|
||||
|
||||
await this._app.StartAsync();
|
||||
|
||||
TestServer testServer = this._app.Services.GetRequiredService<IServer>() as TestServer
|
||||
?? throw new InvalidOperationException("TestServer not found");
|
||||
|
||||
return testServer.CreateClient();
|
||||
}
|
||||
|
||||
private async Task<HttpClient> CreateTestServerWithMultipleAgentsAsync(
|
||||
params (string Name, string Instructions, string ResponseText)[] agents)
|
||||
{
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
builder.WebHost.UseTestServer();
|
||||
|
||||
foreach ((string name, string instructions, string responseText) in agents)
|
||||
{
|
||||
IChatClient mockChatClient = new TestHelpers.SimpleMockChatClient(responseText);
|
||||
builder.Services.AddKeyedSingleton($"chat-client-{name}", mockChatClient);
|
||||
builder.AddAIAgent(name, instructions, chatClientServiceKey: $"chat-client-{name}");
|
||||
}
|
||||
|
||||
builder.AddOpenAIChatCompletions();
|
||||
|
||||
this._app = builder.Build();
|
||||
|
||||
foreach ((string name, string _, string _) in agents)
|
||||
{
|
||||
AIAgent agent = this._app.Services.GetRequiredKeyedService<AIAgent>(name);
|
||||
this._app.MapOpenAIChatCompletions(agent);
|
||||
}
|
||||
|
||||
await this._app.StartAsync();
|
||||
|
||||
TestServer testServer = this._app.Services.GetRequiredService<IServer>() as TestServer
|
||||
?? throw new InvalidOperationException("TestServer not found");
|
||||
|
||||
return testServer.CreateClient();
|
||||
}
|
||||
}
|
||||
+576
@@ -0,0 +1,576 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Agents.AI.Hosting.OpenAI.ChatCompletions.Models;
|
||||
using Microsoft.Agents.AI.Hosting.OpenAI.Tests;
|
||||
|
||||
namespace Microsoft.Agents.AI.Hosting.OpenAI.UnitTests;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for OpenAI ChatCompletions API model serialization and deserialization.
|
||||
/// These tests verify that our models correctly serialize to and deserialize from JSON
|
||||
/// matching the OpenAI wire format, without testing actual API implementation behavior.
|
||||
/// </summary>
|
||||
public sealed class OpenAIChatCompletionsSerializationTests : ConformanceTestBase
|
||||
{
|
||||
#region Request Deserialization Tests
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_BasicRequest_Success()
|
||||
{
|
||||
// Arrange
|
||||
string json = LoadChatCompletionsTraceFile("basic/request.json");
|
||||
|
||||
// Act
|
||||
CreateChatCompletion? request = JsonSerializer.Deserialize(json, ChatCompletions.ChatCompletionsJsonContext.Default.CreateChatCompletion);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(request);
|
||||
Assert.Equal("gpt-4o-mini", request.Model);
|
||||
Assert.NotNull(request.Messages);
|
||||
Assert.True(request.Messages.Count > 0);
|
||||
Assert.Equal(100, request.MaxCompletionTokens);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_BasicRequest_RoundTrip()
|
||||
{
|
||||
// Arrange
|
||||
string originalJson = LoadChatCompletionsTraceFile("basic/request.json");
|
||||
|
||||
// Act
|
||||
CreateChatCompletion? request = JsonSerializer.Deserialize(originalJson, ChatCompletions.ChatCompletionsJsonContext.Default.CreateChatCompletion);
|
||||
string reserializedJson = JsonSerializer.Serialize(request, ChatCompletions.ChatCompletionsJsonContext.Default.CreateChatCompletion);
|
||||
CreateChatCompletion? roundtripped = JsonSerializer.Deserialize(reserializedJson, ChatCompletions.ChatCompletionsJsonContext.Default.CreateChatCompletion);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(request);
|
||||
Assert.NotNull(roundtripped);
|
||||
Assert.Equal(request.Model, roundtripped.Model);
|
||||
Assert.Equal(request.MaxCompletionTokens, roundtripped.MaxCompletionTokens);
|
||||
Assert.Equal(request.Messages.Count, roundtripped.Messages.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_BasicRequest_HasMessages()
|
||||
{
|
||||
// Arrange
|
||||
string json = LoadChatCompletionsTraceFile("basic/request.json");
|
||||
|
||||
// Act
|
||||
CreateChatCompletion? request = JsonSerializer.Deserialize(json, ChatCompletions.ChatCompletionsJsonContext.Default.CreateChatCompletion);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(request);
|
||||
Assert.NotNull(request.Messages);
|
||||
Assert.Single(request.Messages);
|
||||
|
||||
var message = request.Messages[0];
|
||||
Assert.Equal("user", message.Role);
|
||||
Assert.NotNull(message.Content);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_StreamingRequest_HasStreamFlag()
|
||||
{
|
||||
// Arrange
|
||||
string json = LoadChatCompletionsTraceFile("streaming/request.json");
|
||||
|
||||
// Act
|
||||
CreateChatCompletion? request = JsonSerializer.Deserialize(json, ChatCompletions.ChatCompletionsJsonContext.Default.CreateChatCompletion);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(request);
|
||||
Assert.True(request.Stream);
|
||||
Assert.Equal(150, request.MaxCompletionTokens);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_SystemMessageRequest_HasSystemRole()
|
||||
{
|
||||
// Arrange
|
||||
string json = LoadChatCompletionsTraceFile("system_message/request.json");
|
||||
|
||||
// Act
|
||||
CreateChatCompletion? request = JsonSerializer.Deserialize(json, ChatCompletions.ChatCompletionsJsonContext.Default.CreateChatCompletion);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(request);
|
||||
Assert.NotNull(request.Messages);
|
||||
Assert.True(request.Messages.Count >= 2);
|
||||
Assert.Equal("system", request.Messages[0].Role);
|
||||
Assert.Equal("user", request.Messages[1].Role);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_MultiTurnRequest_HasMultipleMessages()
|
||||
{
|
||||
// Arrange
|
||||
string json = LoadChatCompletionsTraceFile("multi_turn/request.json");
|
||||
|
||||
// Act
|
||||
CreateChatCompletion? request = JsonSerializer.Deserialize(json, ChatCompletions.ChatCompletionsJsonContext.Default.CreateChatCompletion);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(request);
|
||||
Assert.NotNull(request.Messages);
|
||||
Assert.True(request.Messages.Count >= 3);
|
||||
Assert.Equal("user", request.Messages[0].Role);
|
||||
Assert.Equal("assistant", request.Messages[1].Role);
|
||||
Assert.Equal("user", request.Messages[2].Role);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_FunctionCallingRequest_HasTools()
|
||||
{
|
||||
// Arrange
|
||||
string json = LoadChatCompletionsTraceFile("function_calling/request.json");
|
||||
|
||||
// Act
|
||||
CreateChatCompletion? request = JsonSerializer.Deserialize(json, ChatCompletions.ChatCompletionsJsonContext.Default.CreateChatCompletion);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(request);
|
||||
Assert.NotNull(request.Tools);
|
||||
Assert.Single(request.Tools);
|
||||
Assert.NotNull(request.ToolChoice?.Mode);
|
||||
Assert.Equal("auto", request.ToolChoice.Mode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_JsonModeRequest_HasResponseFormat()
|
||||
{
|
||||
// Arrange
|
||||
string json = LoadChatCompletionsTraceFile("json_mode/request.json");
|
||||
|
||||
// Act
|
||||
CreateChatCompletion? request = JsonSerializer.Deserialize(json, ChatCompletions.ChatCompletionsJsonContext.Default.CreateChatCompletion);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(request);
|
||||
Assert.NotNull(request.ResponseFormat);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_AllRequests_CanBeDeserialized()
|
||||
{
|
||||
// Arrange
|
||||
string[] requestPaths =
|
||||
[
|
||||
"basic/request.json",
|
||||
"streaming/request.json",
|
||||
"system_message/request.json",
|
||||
"multi_turn/request.json",
|
||||
"function_calling/request.json",
|
||||
"json_mode/request.json"
|
||||
];
|
||||
|
||||
foreach (var path in requestPaths)
|
||||
{
|
||||
string json = LoadChatCompletionsTraceFile(path);
|
||||
|
||||
// Act & Assert - Should not throw
|
||||
CreateChatCompletion? request = JsonSerializer.Deserialize(json, ChatCompletions.ChatCompletionsJsonContext.Default.CreateChatCompletion);
|
||||
Assert.NotNull(request);
|
||||
Assert.NotNull(request.Messages);
|
||||
Assert.True(request.Messages.Count > 0, $"Request from {path} should have messages");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Response Deserialization Tests
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_BasicResponse_Success()
|
||||
{
|
||||
// Arrange
|
||||
string json = LoadChatCompletionsTraceFile("basic/response.json");
|
||||
|
||||
// Act
|
||||
ChatCompletion? response = JsonSerializer.Deserialize(json, ChatCompletions.ChatCompletionsJsonContext.Default.ChatCompletion);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(response);
|
||||
Assert.StartsWith("chatcmpl-", response.Id);
|
||||
Assert.Equal("chat.completion", response.Object);
|
||||
Assert.True(response.Created > 0);
|
||||
Assert.NotNull(response.Model);
|
||||
Assert.StartsWith("gpt-4o-mini", response.Model);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_BasicResponse_HasChoices()
|
||||
{
|
||||
// Arrange
|
||||
string json = LoadChatCompletionsTraceFile("basic/response.json");
|
||||
|
||||
// Act
|
||||
ChatCompletion? response = JsonSerializer.Deserialize(json, ChatCompletions.ChatCompletionsJsonContext.Default.ChatCompletion);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(response);
|
||||
Assert.NotNull(response.Choices);
|
||||
Assert.Single(response.Choices);
|
||||
|
||||
var choice = response.Choices[0];
|
||||
Assert.Equal(0, choice.Index);
|
||||
Assert.NotNull(choice.Message);
|
||||
Assert.Equal("assistant", choice.Message.Role);
|
||||
Assert.NotNull(choice.Message.Content);
|
||||
Assert.NotNull(choice.FinishReason);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_BasicResponse_HasUsage()
|
||||
{
|
||||
// Arrange
|
||||
string json = LoadChatCompletionsTraceFile("basic/response.json");
|
||||
|
||||
// Act
|
||||
ChatCompletion? response = JsonSerializer.Deserialize(json, ChatCompletions.ChatCompletionsJsonContext.Default.ChatCompletion);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(response);
|
||||
Assert.NotNull(response.Usage);
|
||||
Assert.True(response.Usage.PromptTokens > 0);
|
||||
Assert.True(response.Usage.CompletionTokens > 0);
|
||||
Assert.Equal(response.Usage.PromptTokens + response.Usage.CompletionTokens, response.Usage.TotalTokens);
|
||||
Assert.NotNull(response.Usage.PromptTokensDetails);
|
||||
Assert.NotNull(response.Usage.CompletionTokensDetails);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_SystemMessageResponse_HasContent()
|
||||
{
|
||||
// Arrange
|
||||
string json = LoadChatCompletionsTraceFile("system_message/response.json");
|
||||
|
||||
// Act
|
||||
ChatCompletion? response = JsonSerializer.Deserialize(json, ChatCompletions.ChatCompletionsJsonContext.Default.ChatCompletion);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(response);
|
||||
Assert.NotNull(response.Choices);
|
||||
var message = response.Choices[0].Message;
|
||||
Assert.Equal("assistant", message.Role);
|
||||
Assert.NotNull(message.Content);
|
||||
Assert.Contains("Ahoy, matey", message.Content, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_MultiTurnResponse_HasContent()
|
||||
{
|
||||
// Arrange
|
||||
string json = LoadChatCompletionsTraceFile("multi_turn/response.json");
|
||||
|
||||
// Act
|
||||
ChatCompletion? response = JsonSerializer.Deserialize(json, ChatCompletions.ChatCompletionsJsonContext.Default.ChatCompletion);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(response);
|
||||
Assert.NotNull(response.Choices);
|
||||
var message = response.Choices[0].Message;
|
||||
Assert.Equal("assistant", message.Role);
|
||||
Assert.NotNull(message.Content);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_FunctionCallingResponse_HasToolCalls()
|
||||
{
|
||||
// Arrange
|
||||
string json = LoadChatCompletionsTraceFile("function_calling/response.json");
|
||||
|
||||
// Act
|
||||
ChatCompletion? response = JsonSerializer.Deserialize(json, ChatCompletions.ChatCompletionsJsonContext.Default.ChatCompletion);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(response);
|
||||
Assert.NotNull(response.Choices);
|
||||
|
||||
var choice = response.Choices[0];
|
||||
Assert.Equal("tool_calls", choice.FinishReason);
|
||||
|
||||
var message = choice.Message;
|
||||
Assert.NotNull(message.ToolCalls);
|
||||
Assert.Single(message.ToolCalls);
|
||||
|
||||
var toolCall = message.ToolCalls[0];
|
||||
Assert.NotNull(toolCall.Id);
|
||||
Assert.StartsWith("call_", toolCall.Id);
|
||||
Assert.Equal("function", toolCall.Type);
|
||||
Assert.NotNull(toolCall.Function);
|
||||
Assert.Equal("get_weather", toolCall.Function.Name);
|
||||
Assert.NotNull(toolCall.Function.Arguments);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_JsonModeResponse_HasStructuredOutput()
|
||||
{
|
||||
// Arrange
|
||||
string json = LoadChatCompletionsTraceFile("json_mode/response.json");
|
||||
|
||||
// Act
|
||||
ChatCompletion? response = JsonSerializer.Deserialize(json, ChatCompletions.ChatCompletionsJsonContext.Default.ChatCompletion);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(response);
|
||||
Assert.NotNull(response.Choices);
|
||||
|
||||
var message = response.Choices[0].Message;
|
||||
Assert.NotNull(message.Content);
|
||||
|
||||
// Verify the content is valid JSON
|
||||
using var jsonDoc = JsonDocument.Parse(message.Content);
|
||||
var jsonRoot = jsonDoc.RootElement;
|
||||
Assert.Equal(JsonValueKind.Object, jsonRoot.ValueKind);
|
||||
Assert.True(jsonRoot.TryGetProperty("name", out _));
|
||||
Assert.True(jsonRoot.TryGetProperty("age", out _));
|
||||
Assert.True(jsonRoot.TryGetProperty("occupation", out _));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_AllResponses_HaveRequiredFields()
|
||||
{
|
||||
// Arrange
|
||||
string[] responsePaths =
|
||||
[
|
||||
"basic/response.json",
|
||||
"system_message/response.json",
|
||||
"multi_turn/response.json",
|
||||
"function_calling/response.json",
|
||||
"json_mode/response.json"
|
||||
];
|
||||
|
||||
foreach (var path in responsePaths)
|
||||
{
|
||||
string json = LoadChatCompletionsTraceFile(path);
|
||||
|
||||
// Act
|
||||
ChatCompletion? response = JsonSerializer.Deserialize(json, ChatCompletions.ChatCompletionsJsonContext.Default.ChatCompletion);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(response);
|
||||
Assert.NotNull(response.Id);
|
||||
Assert.Equal("chat.completion", response.Object);
|
||||
Assert.True(response.Created > 0, $"Response from {path} should have created timestamp");
|
||||
Assert.NotNull(response.Model);
|
||||
Assert.NotNull(response.Choices);
|
||||
Assert.True(response.Choices.Count > 0, $"Response from {path} should have choices");
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_ResponseRoundTrip_PreservesData()
|
||||
{
|
||||
// Arrange
|
||||
string originalJson = LoadChatCompletionsTraceFile("basic/response.json");
|
||||
|
||||
// Act - Deserialize and re-serialize
|
||||
ChatCompletion? response = JsonSerializer.Deserialize(originalJson, ChatCompletions.ChatCompletionsJsonContext.Default.ChatCompletion);
|
||||
string reserializedJson = JsonSerializer.Serialize(response, ChatCompletions.ChatCompletionsJsonContext.Default.ChatCompletion);
|
||||
ChatCompletion? roundtripped = JsonSerializer.Deserialize(reserializedJson, ChatCompletions.ChatCompletionsJsonContext.Default.ChatCompletion);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(response);
|
||||
Assert.NotNull(roundtripped);
|
||||
Assert.Equal(response.Id, roundtripped.Id);
|
||||
Assert.Equal(response.Created, roundtripped.Created);
|
||||
Assert.Equal(response.Model, roundtripped.Model);
|
||||
Assert.Equal(response.Choices.Count, roundtripped.Choices.Count);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Streaming Chunk Deserialization Tests
|
||||
|
||||
[Fact]
|
||||
public void ParseStreamingChunks_BasicFormat_Success()
|
||||
{
|
||||
// Arrange
|
||||
string sseContent = LoadChatCompletionsTraceFile("streaming/response.txt");
|
||||
|
||||
// Act
|
||||
var chunks = ParseChatCompletionChunksFromSse(sseContent);
|
||||
|
||||
// Assert
|
||||
Assert.NotEmpty(chunks);
|
||||
Assert.All(chunks, chunk =>
|
||||
{
|
||||
ChatCompletionChunk? parsed = JsonSerializer.Deserialize(chunk.GetRawText(), ChatCompletions.ChatCompletionsJsonContext.Default.ChatCompletionChunk);
|
||||
Assert.NotNull(parsed);
|
||||
Assert.NotNull(parsed.Id);
|
||||
Assert.Equal("chat.completion.chunk", parsed.Object);
|
||||
Assert.True(parsed.Created > 0);
|
||||
Assert.NotNull(parsed.Model);
|
||||
Assert.NotNull(parsed.Choices);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParseStreamingChunks_AllChunksSameId()
|
||||
{
|
||||
// Arrange
|
||||
string sseContent = LoadChatCompletionsTraceFile("streaming/response.txt");
|
||||
|
||||
// Act
|
||||
var chunks = ParseChatCompletionChunksFromSse(sseContent);
|
||||
|
||||
// Deserialize chunks
|
||||
var parsedChunks = chunks
|
||||
.Select(c => JsonSerializer.Deserialize(c.GetRawText(), ChatCompletions.ChatCompletionsJsonContext.Default.ChatCompletionChunk))
|
||||
.Where(c => c != null)
|
||||
.ToList();
|
||||
|
||||
// Assert
|
||||
Assert.NotEmpty(parsedChunks);
|
||||
|
||||
string? firstId = parsedChunks[0]!.Id;
|
||||
Assert.NotNull(firstId);
|
||||
Assert.StartsWith("chatcmpl-", firstId);
|
||||
|
||||
Assert.All(parsedChunks, chunk => Assert.Equal(firstId, chunk!.Id));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParseStreamingChunks_FirstChunkHasRole()
|
||||
{
|
||||
// Arrange
|
||||
string sseContent = LoadChatCompletionsTraceFile("streaming/response.txt");
|
||||
|
||||
// Act
|
||||
var chunks = ParseChatCompletionChunksFromSse(sseContent);
|
||||
var firstChunk = JsonSerializer.Deserialize(chunks[0].GetRawText(), ChatCompletions.ChatCompletionsJsonContext.Default.ChatCompletionChunk);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(firstChunk);
|
||||
Assert.NotNull(firstChunk.Choices);
|
||||
Assert.True(firstChunk.Choices.Count > 0);
|
||||
|
||||
var firstChoice = firstChunk.Choices[0];
|
||||
Assert.NotNull(firstChoice.Delta);
|
||||
|
||||
if (firstChoice.Delta.Role != null)
|
||||
{
|
||||
Assert.Equal("assistant", firstChoice.Delta.Role);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParseStreamingChunks_AccumulateContent_MatchesExpected()
|
||||
{
|
||||
// Arrange
|
||||
string sseContent = LoadChatCompletionsTraceFile("streaming/response.txt");
|
||||
|
||||
// Act
|
||||
var chunks = ParseChatCompletionChunksFromSse(sseContent);
|
||||
var contentPieces = new List<string>();
|
||||
|
||||
foreach (var chunkJson in chunks)
|
||||
{
|
||||
var chunk = JsonSerializer.Deserialize(chunkJson.GetRawText(), ChatCompletions.ChatCompletionsJsonContext.Default.ChatCompletionChunk);
|
||||
if (chunk?.Choices != null && chunk.Choices.Count > 0)
|
||||
{
|
||||
var delta = chunk.Choices[0].Delta;
|
||||
if (!string.IsNullOrEmpty(delta?.Content))
|
||||
{
|
||||
contentPieces.Add(delta.Content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Assert
|
||||
Assert.NotEmpty(contentPieces);
|
||||
string fullText = string.Concat(contentPieces);
|
||||
Assert.NotEmpty(fullText);
|
||||
Assert.Contains("circuits", fullText);
|
||||
Assert.Contains("flight", fullText);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParseStreamingChunks_LastChunkHasFinishReason()
|
||||
{
|
||||
// Arrange
|
||||
string sseContent = LoadChatCompletionsTraceFile("streaming/response.txt");
|
||||
|
||||
// Act
|
||||
var chunks = ParseChatCompletionChunksFromSse(sseContent);
|
||||
|
||||
// Find chunks with finish_reason
|
||||
var chunksWithFinishReason = new List<ChatCompletionChunk>();
|
||||
foreach (var chunkJson in chunks)
|
||||
{
|
||||
var chunk = JsonSerializer.Deserialize(chunkJson.GetRawText(), ChatCompletions.ChatCompletionsJsonContext.Default.ChatCompletionChunk);
|
||||
if (chunk?.Choices != null && chunk.Choices.Count > 0 && !string.IsNullOrEmpty(chunk.Choices[0].FinishReason))
|
||||
{
|
||||
chunksWithFinishReason.Add(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
// Assert
|
||||
Assert.NotEmpty(chunksWithFinishReason);
|
||||
var lastChunk = chunksWithFinishReason.Last();
|
||||
Assert.Contains(lastChunk.Choices[0].FinishReason, collection: ["stop", "length", "tool_calls", "content_filter"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParseStreamingChunks_LastChunkHasUsage()
|
||||
{
|
||||
// Arrange
|
||||
string sseContent = LoadChatCompletionsTraceFile("streaming/response.txt");
|
||||
|
||||
// Act
|
||||
var chunks = ParseChatCompletionChunksFromSse(sseContent);
|
||||
var lastChunkJson = chunks.Last();
|
||||
var lastChunk = JsonSerializer.Deserialize(lastChunkJson.GetRawText(), ChatCompletions.ChatCompletionsJsonContext.Default.ChatCompletionChunk);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(lastChunk);
|
||||
Assert.NotNull(lastChunk.Usage);
|
||||
Assert.True(lastChunk.Usage.PromptTokens > 0);
|
||||
Assert.True(lastChunk.Usage.CompletionTokens > 0);
|
||||
Assert.Equal(lastChunk.Usage.PromptTokens + lastChunk.Usage.CompletionTokens, lastChunk.Usage.TotalTokens);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper to parse chat completion chunks from SSE response.
|
||||
/// </summary>
|
||||
private static List<JsonElement> ParseChatCompletionChunksFromSse(string sseContent)
|
||||
{
|
||||
var chunks = new List<JsonElement>();
|
||||
var lines = sseContent.Split('\n');
|
||||
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
var line = lines[i].TrimEnd('\r');
|
||||
|
||||
if (line.StartsWith("data: ", StringComparison.Ordinal))
|
||||
{
|
||||
var jsonData = line.Substring("data: ".Length);
|
||||
|
||||
// Skip [DONE] marker
|
||||
if (jsonData == "[DONE]")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var doc = JsonDocument.Parse(jsonData);
|
||||
chunks.Add(doc.RootElement.Clone());
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Skip invalid JSON
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return chunks;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
+1201
File diff suppressed because it is too large
Load Diff
+592
@@ -0,0 +1,592 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Agents.AI.Hosting.OpenAI.Conversations.Models;
|
||||
using Microsoft.Agents.AI.Hosting.OpenAI.Models;
|
||||
using Microsoft.Agents.AI.Hosting.OpenAI.Responses.Models;
|
||||
|
||||
namespace Microsoft.Agents.AI.Hosting.OpenAI.UnitTests;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for OpenAI Conversations API model serialization and deserialization.
|
||||
/// These tests verify that our models correctly serialize to and deserialize from JSON
|
||||
/// matching the OpenAI wire format, without testing actual API implementation behavior.
|
||||
/// </summary>
|
||||
public sealed class OpenAIConversationsSerializationTests
|
||||
{
|
||||
private const string TracesBasePath = "ConformanceTraces/Conversations";
|
||||
|
||||
/// <summary>
|
||||
/// Loads a JSON file from the conformance traces directory.
|
||||
/// </summary>
|
||||
private static string LoadTraceFile(string relativePath)
|
||||
{
|
||||
var fullPath = System.IO.Path.Combine(TracesBasePath, relativePath);
|
||||
|
||||
if (!System.IO.File.Exists(fullPath))
|
||||
{
|
||||
throw new System.IO.FileNotFoundException($"Conformance trace file not found: {fullPath}");
|
||||
}
|
||||
|
||||
return System.IO.File.ReadAllText(fullPath);
|
||||
}
|
||||
|
||||
#region Request Serialization Tests
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_CreateConversationRequest_Success()
|
||||
{
|
||||
// Arrange
|
||||
string json = LoadTraceFile("basic/create_conversation_request.json");
|
||||
|
||||
// Act
|
||||
CreateConversationRequest? request = JsonSerializer.Deserialize(json, OpenAIHostingJsonContext.Default.CreateConversationRequest);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(request);
|
||||
Assert.NotNull(request.Metadata);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_CreateConversationWithItems_Success()
|
||||
{
|
||||
// Arrange
|
||||
string json = LoadTraceFile("create_with_items/create_request.json");
|
||||
|
||||
// Act
|
||||
CreateConversationRequest? request = JsonSerializer.Deserialize(json, OpenAIHostingJsonContext.Default.CreateConversationRequest);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(request);
|
||||
Assert.NotNull(request.Items);
|
||||
Assert.True(request.Items.Count > 0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_CreateItemsRequest_Success()
|
||||
{
|
||||
// Arrange
|
||||
string json = LoadTraceFile("add_items/request.json");
|
||||
|
||||
// Act
|
||||
CreateItemsRequest? request = JsonSerializer.Deserialize(json, OpenAIHostingJsonContext.Default.CreateItemsRequest);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(request);
|
||||
Assert.NotNull(request.Items);
|
||||
Assert.True(request.Items.Count > 0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_UpdateConversationRequest_Success()
|
||||
{
|
||||
// Arrange
|
||||
string json = LoadTraceFile("update_conversation/request.json");
|
||||
|
||||
// Act
|
||||
UpdateConversationRequest? request = JsonSerializer.Deserialize(json, OpenAIHostingJsonContext.Default.UpdateConversationRequest);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(request);
|
||||
Assert.NotNull(request.Metadata);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serialize_CreateConversationRequest_MatchesFormat()
|
||||
{
|
||||
// Arrange
|
||||
var request = new CreateConversationRequest
|
||||
{
|
||||
Metadata = new System.Collections.Generic.Dictionary<string, string>
|
||||
{
|
||||
["test_key"] = "test_value"
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
string json = JsonSerializer.Serialize(request, OpenAIHostingJsonContext.Default.CreateConversationRequest);
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var root = doc.RootElement;
|
||||
|
||||
// Assert
|
||||
Assert.True(root.TryGetProperty("metadata", out var metadata));
|
||||
Assert.Equal(JsonValueKind.Object, metadata.ValueKind);
|
||||
Assert.Equal("test_value", metadata.GetProperty("test_key").GetString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serialize_CreateConversationRequestWithItems_IncludesItems()
|
||||
{
|
||||
// Arrange
|
||||
var request = new CreateConversationRequest
|
||||
{
|
||||
Items =
|
||||
[
|
||||
new ResponsesUserMessageItemParam
|
||||
{
|
||||
Content = InputMessageContent.FromContents(new ItemContentInputText { Text = "test" })
|
||||
}
|
||||
],
|
||||
Metadata = []
|
||||
};
|
||||
|
||||
// Act
|
||||
string json = JsonSerializer.Serialize(request, OpenAIHostingJsonContext.Default.CreateConversationRequest);
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var root = doc.RootElement;
|
||||
|
||||
// Assert
|
||||
Assert.True(root.TryGetProperty("items", out var items));
|
||||
Assert.Equal(JsonValueKind.Array, items.ValueKind);
|
||||
Assert.Equal(1, items.GetArrayLength());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serialize_NullableFields_AreOmittedWhenNull()
|
||||
{
|
||||
// Arrange
|
||||
var request = new CreateConversationRequest();
|
||||
|
||||
// Act
|
||||
string json = JsonSerializer.Serialize(request, OpenAIHostingJsonContext.Default.CreateConversationRequest);
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var root = doc.RootElement;
|
||||
|
||||
// Assert - Optional fields should not be present when null or use null value
|
||||
// Either the property doesn't exist or it's explicitly null
|
||||
bool hasItems = root.TryGetProperty("items", out var itemsProp);
|
||||
if (hasItems)
|
||||
{
|
||||
Assert.Equal(JsonValueKind.Null, itemsProp.ValueKind);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Response Deserialization Tests
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_Conversation_Success()
|
||||
{
|
||||
// Arrange
|
||||
string json = LoadTraceFile("basic/create_conversation_response.json");
|
||||
|
||||
// Act
|
||||
Conversation? conversation = JsonSerializer.Deserialize(json, OpenAIHostingJsonContext.Default.Conversation);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(conversation);
|
||||
Assert.StartsWith("conv_", conversation.Id);
|
||||
Assert.Equal("conversation", conversation.Object);
|
||||
Assert.True(conversation.CreatedAt > 0);
|
||||
Assert.NotNull(conversation.Metadata);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_ConversationRoundTrip_PreservesData()
|
||||
{
|
||||
// Arrange
|
||||
string originalJson = LoadTraceFile("basic/create_conversation_response.json");
|
||||
|
||||
// Act - Deserialize and re-serialize
|
||||
Conversation? conversation = JsonSerializer.Deserialize(originalJson, OpenAIHostingJsonContext.Default.Conversation);
|
||||
string reserializedJson = JsonSerializer.Serialize(conversation, OpenAIHostingJsonContext.Default.Conversation);
|
||||
Conversation? roundtripped = JsonSerializer.Deserialize(reserializedJson, OpenAIHostingJsonContext.Default.Conversation);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(conversation);
|
||||
Assert.NotNull(roundtripped);
|
||||
Assert.Equal(conversation.Id, roundtripped.Id);
|
||||
Assert.Equal(conversation.CreatedAt, roundtripped.CreatedAt);
|
||||
Assert.Equal(conversation.Object, roundtripped.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_ItemListResponse_Success()
|
||||
{
|
||||
// Arrange
|
||||
string json = LoadTraceFile("list_items/response.json");
|
||||
|
||||
// Act - The list_items response uses ListResponse<ItemResource>, not ConversationListResponse
|
||||
ListResponse<ItemResource>? response = JsonSerializer.Deserialize(json, OpenAIHostingJsonContext.Default.ListResponseItemResource);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(response);
|
||||
Assert.Equal("list", response.Object);
|
||||
Assert.NotNull(response.Data);
|
||||
Assert.NotNull(response.FirstId);
|
||||
Assert.NotNull(response.LastId);
|
||||
Assert.False(response.HasMore);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_ItemResource_Success()
|
||||
{
|
||||
// Arrange
|
||||
string json = LoadTraceFile("retrieve_item/response.json");
|
||||
|
||||
// Act
|
||||
ItemResource? item = JsonSerializer.Deserialize(json, OpenAIHostingJsonContext.Default.ItemResource);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(item);
|
||||
Assert.StartsWith("msg_", item.Id);
|
||||
Assert.Equal("message", item.Type);
|
||||
var messageItem = Assert.IsType<ResponsesAssistantMessageItemResource>(item);
|
||||
Assert.NotNull(messageItem.Content);
|
||||
Assert.NotEmpty(messageItem.Content);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_DeleteResponse_Success()
|
||||
{
|
||||
// Arrange
|
||||
string json = LoadTraceFile("delete_conversation/response.json");
|
||||
|
||||
// Act
|
||||
DeleteResponse? response = JsonSerializer.Deserialize(json, OpenAIHostingJsonContext.Default.DeleteResponse);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(response);
|
||||
Assert.NotNull(response.Id);
|
||||
Assert.Equal("conversation.deleted", response.Object);
|
||||
Assert.True(response.Deleted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_DeleteItemResponse_Success()
|
||||
{
|
||||
// Arrange
|
||||
string json = LoadTraceFile("delete_item/response.json");
|
||||
|
||||
// Act
|
||||
DeleteResponse? response = JsonSerializer.Deserialize(json, OpenAIHostingJsonContext.Default.DeleteResponse);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(response);
|
||||
Assert.NotNull(response.Id);
|
||||
Assert.Equal("conversation.item.deleted", response.Object);
|
||||
Assert.True(response.Deleted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_ErrorResponse_Success()
|
||||
{
|
||||
// Arrange
|
||||
string json = LoadTraceFile("error_conversation_not_found/response.json");
|
||||
|
||||
// Act
|
||||
ErrorResponse? response = JsonSerializer.Deserialize(json, OpenAIHostingJsonContext.Default.ErrorResponse);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(response);
|
||||
Assert.NotNull(response.Error);
|
||||
Assert.NotNull(response.Error.Message);
|
||||
Assert.NotNull(response.Error.Type);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_AllConversationResponses_HaveRequiredFields()
|
||||
{
|
||||
// Arrange
|
||||
string[] responsePaths =
|
||||
[
|
||||
"basic/create_conversation_response.json",
|
||||
"create_with_items/create_response.json",
|
||||
"retrieve_conversation/response.json",
|
||||
"update_conversation/response.json"
|
||||
];
|
||||
|
||||
foreach (var path in responsePaths)
|
||||
{
|
||||
string json = LoadTraceFile(path);
|
||||
|
||||
// Act
|
||||
Conversation? conversation = JsonSerializer.Deserialize(json, OpenAIHostingJsonContext.Default.Conversation);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(conversation);
|
||||
Assert.NotNull(conversation.Id);
|
||||
Assert.Equal("conversation", conversation.Object);
|
||||
Assert.True(conversation.CreatedAt > 0, $"Conversation from {path} should have created_at");
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_AllItemResponses_HaveRequiredFields()
|
||||
{
|
||||
// Arrange - Use list_items response which has multiple items
|
||||
string json = LoadTraceFile("list_items/response.json");
|
||||
ListResponse<ItemResource>? response = JsonSerializer.Deserialize(json, OpenAIHostingJsonContext.Default.ListResponseItemResource);
|
||||
Assert.NotNull(response);
|
||||
Assert.NotNull(response.Data);
|
||||
|
||||
// Act & Assert
|
||||
foreach (var item in response.Data)
|
||||
{
|
||||
Assert.NotNull(item);
|
||||
Assert.NotNull(item.Id);
|
||||
Assert.Equal("message", item.Type);
|
||||
var messageItem = Assert.IsType<ResponsesMessageItemResource>(item, exactMatch: false);
|
||||
// Content is on concrete message types (ResponsesAssistantMessageItemResource, etc.)
|
||||
// For this test, we just verify the type is correct
|
||||
Assert.NotNull(messageItem);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serialize_Conversation_MatchesFormat()
|
||||
{
|
||||
// Arrange
|
||||
var conversation = new Conversation
|
||||
{
|
||||
Id = "conv_test123",
|
||||
CreatedAt = 1234567890,
|
||||
Metadata = new System.Collections.Generic.Dictionary<string, string>
|
||||
{
|
||||
["test_key"] = "test_value"
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
string json = JsonSerializer.Serialize(conversation, OpenAIHostingJsonContext.Default.Conversation);
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var root = doc.RootElement;
|
||||
|
||||
// Assert
|
||||
Assert.Equal("conv_test123", root.GetProperty("id").GetString());
|
||||
Assert.Equal("conversation", root.GetProperty("object").GetString());
|
||||
Assert.Equal(1234567890, root.GetProperty("created_at").GetInt64());
|
||||
var metadata = root.GetProperty("metadata");
|
||||
Assert.Equal("test_value", metadata.GetProperty("test_key").GetString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serialize_ConversationListResponse_MatchesFormat()
|
||||
{
|
||||
// Arrange
|
||||
var response = new ListResponse<Conversation>
|
||||
{
|
||||
Data =
|
||||
[
|
||||
new()
|
||||
{
|
||||
Id = "conv_1",
|
||||
CreatedAt = 1234567890,
|
||||
Metadata = []
|
||||
}
|
||||
],
|
||||
HasMore = false
|
||||
};
|
||||
|
||||
// Act
|
||||
string json = JsonSerializer.Serialize(response, OpenAIHostingJsonUtilities.DefaultOptions);
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var root = doc.RootElement;
|
||||
|
||||
// Assert
|
||||
Assert.Equal("list", root.GetProperty("object").GetString());
|
||||
var data = root.GetProperty("data");
|
||||
Assert.Equal(JsonValueKind.Array, data.ValueKind);
|
||||
Assert.Equal(1, data.GetArrayLength());
|
||||
Assert.False(root.GetProperty("has_more").GetBoolean());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serialize_DeleteResponse_MatchesFormat()
|
||||
{
|
||||
// Arrange
|
||||
var response = new DeleteResponse
|
||||
{
|
||||
Id = "conv_test123",
|
||||
Object = "conversation.deleted",
|
||||
Deleted = true
|
||||
};
|
||||
|
||||
// Act
|
||||
string json = JsonSerializer.Serialize(response, OpenAIHostingJsonContext.Default.DeleteResponse);
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var root = doc.RootElement;
|
||||
|
||||
// Assert
|
||||
Assert.Equal("conv_test123", root.GetProperty("id").GetString());
|
||||
Assert.Equal("conversation.deleted", root.GetProperty("object").GetString());
|
||||
Assert.True(root.GetProperty("deleted").GetBoolean());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serialize_ErrorResponse_MatchesFormat()
|
||||
{
|
||||
// Arrange
|
||||
var response = new ErrorResponse
|
||||
{
|
||||
Error = new ErrorDetails
|
||||
{
|
||||
Message = "Conversation not found",
|
||||
Type = "invalid_request_error"
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
string json = JsonSerializer.Serialize(response, OpenAIHostingJsonContext.Default.ErrorResponse);
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var root = doc.RootElement;
|
||||
|
||||
// Assert
|
||||
var error = root.GetProperty("error");
|
||||
Assert.Equal("Conversation not found", error.GetProperty("message").GetString());
|
||||
Assert.Equal("invalid_request_error", error.GetProperty("type").GetString());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Integration with Responses API Tests
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_ResponsesAPIRequestWithConversation_Success()
|
||||
{
|
||||
// Arrange
|
||||
string json = LoadTraceFile("basic/first_message_request.json");
|
||||
|
||||
// Act
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var root = doc.RootElement;
|
||||
|
||||
// Assert - Verify the request has conversation field
|
||||
Assert.True(root.TryGetProperty("conversation", out var conversation));
|
||||
var conversationId = conversation.GetString();
|
||||
Assert.NotNull(conversationId);
|
||||
Assert.StartsWith("conv_", conversationId);
|
||||
|
||||
// Assert - Has standard Responses API fields
|
||||
Assert.True(root.TryGetProperty("model", out var model));
|
||||
Assert.True(root.TryGetProperty("input", out var input));
|
||||
Assert.True(root.TryGetProperty("max_output_tokens", out var maxTokens));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_ResponsesAPIResponseWithConversation_Success()
|
||||
{
|
||||
// Arrange
|
||||
string json = LoadTraceFile("basic/first_message_response.json");
|
||||
|
||||
// Act
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var root = doc.RootElement;
|
||||
|
||||
// Assert - Verify the response has conversation field
|
||||
Assert.True(root.TryGetProperty("conversation", out var conversation));
|
||||
Assert.Equal(JsonValueKind.Object, conversation.ValueKind);
|
||||
Assert.True(conversation.TryGetProperty("id", out var conversationId));
|
||||
Assert.NotNull(conversationId.GetString());
|
||||
|
||||
// Assert - Has standard Responses API fields
|
||||
Assert.True(root.TryGetProperty("id", out var responseId));
|
||||
Assert.True(root.TryGetProperty("object", out var obj));
|
||||
Assert.Equal("response", obj.GetString());
|
||||
Assert.True(root.TryGetProperty("status", out var status));
|
||||
Assert.True(root.TryGetProperty("output", out var output));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_StreamingResponseWithConversation_Success()
|
||||
{
|
||||
// Arrange
|
||||
string sseContent = LoadTraceFile("basic_streaming/first_message_response.txt");
|
||||
|
||||
// Act
|
||||
var events = ParseSseEventsFromContent(sseContent);
|
||||
|
||||
// Assert - At least one event should be present
|
||||
Assert.NotEmpty(events);
|
||||
|
||||
// Assert - Check if any event has conversation reference
|
||||
var createdEvent = events.FirstOrDefault(e =>
|
||||
e.TryGetProperty("type", out var type) &&
|
||||
type.GetString() == "response.created");
|
||||
|
||||
if (!createdEvent.Equals(default(JsonElement)))
|
||||
{
|
||||
Assert.True(createdEvent.TryGetProperty("response", out var response));
|
||||
// Conversation field may be in the response object
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_ImageInputWithConversation_Success()
|
||||
{
|
||||
// Arrange
|
||||
string json = LoadTraceFile("image_input/first_message_request.json");
|
||||
|
||||
// Act
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var root = doc.RootElement;
|
||||
|
||||
// Assert - Verify has conversation and image input
|
||||
Assert.True(root.TryGetProperty("conversation", out var conversation));
|
||||
Assert.True(root.TryGetProperty("input", out var input));
|
||||
Assert.Equal(JsonValueKind.Array, input.ValueKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_ToolCallWithConversation_Success()
|
||||
{
|
||||
// Arrange
|
||||
string json = LoadTraceFile("tool_call/first_message_request.json");
|
||||
|
||||
// Act
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var root = doc.RootElement;
|
||||
|
||||
// Assert - Verify has conversation and tools
|
||||
Assert.True(root.TryGetProperty("conversation", out var conversation));
|
||||
Assert.True(root.TryGetProperty("tools", out var tools));
|
||||
Assert.Equal(JsonValueKind.Array, tools.ValueKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_RefusalWithConversation_Success()
|
||||
{
|
||||
// Arrange
|
||||
string json = LoadTraceFile("refusal/first_message_request.json");
|
||||
|
||||
// Act
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var root = doc.RootElement;
|
||||
|
||||
// Assert - Verify has conversation
|
||||
Assert.True(root.TryGetProperty("conversation", out var conversation));
|
||||
Assert.NotNull(conversation.GetString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper to parse SSE events from a streaming response content string.
|
||||
/// </summary>
|
||||
private static System.Collections.Generic.List<JsonElement> ParseSseEventsFromContent(string sseContent)
|
||||
{
|
||||
var events = new System.Collections.Generic.List<JsonElement>();
|
||||
var lines = sseContent.Split('\n');
|
||||
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
var line = lines[i].TrimEnd('\r');
|
||||
|
||||
if (line.StartsWith("event: ", StringComparison.Ordinal) && i + 1 < lines.Length)
|
||||
{
|
||||
var dataLine = lines[i + 1].TrimEnd('\r');
|
||||
if (dataLine.StartsWith("data: ", StringComparison.Ordinal))
|
||||
{
|
||||
var jsonData = dataLine.Substring("data: ".Length);
|
||||
var doc = JsonDocument.Parse(jsonData);
|
||||
events.Add(doc.RootElement.Clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
+435
@@ -0,0 +1,435 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting.Server;
|
||||
using Microsoft.AspNetCore.TestHost;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace Microsoft.Agents.AI.Hosting.OpenAI.UnitTests;
|
||||
|
||||
/// <summary>
|
||||
/// Integration tests for the HTTP API with in-memory conversation, response, and agent index storage.
|
||||
/// Tests create a conversation, create a response, wait for completion, then verify the conversation was updated.
|
||||
/// </summary>
|
||||
public sealed class OpenAIHttpApiIntegrationTests : IAsyncDisposable
|
||||
{
|
||||
private WebApplication? _app;
|
||||
private HttpClient? _httpClient;
|
||||
|
||||
[Fact]
|
||||
public async Task CreateConversationAndResponse_NonStreaming_NonBackground_UpdatesConversationWithOutputAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "test-agent";
|
||||
const string Instructions = "You are a helpful assistant.";
|
||||
const string ExpectedResponse = "The capital of France is Paris.";
|
||||
const string UserMessage = "What is the capital of France?";
|
||||
|
||||
HttpClient client = await this.CreateTestServerWithInMemoryStorageAsync(AgentName, Instructions, ExpectedResponse);
|
||||
|
||||
// Act - Create conversation
|
||||
var createConversationRequest = new { metadata = new { agent_id = AgentName } };
|
||||
string createConvJson = JsonSerializer.Serialize(createConversationRequest);
|
||||
HttpResponseMessage createConvResponse = await this.SendPostRequestAsync(client, "/v1/conversations", createConvJson);
|
||||
using var createConvDoc = await this.ParseResponseAsync(createConvResponse);
|
||||
string conversationId = createConvDoc.RootElement.GetProperty("id").GetString()!;
|
||||
|
||||
// Act - Create response (non-streaming, non-background)
|
||||
var createResponseRequest = new
|
||||
{
|
||||
metadata = new { entity_id = AgentName },
|
||||
conversation = conversationId,
|
||||
input = UserMessage,
|
||||
stream = false
|
||||
};
|
||||
string createRespJson = JsonSerializer.Serialize(createResponseRequest);
|
||||
HttpResponseMessage createRespResponse = await this.SendPostRequestAsync(client, $"/{AgentName}/v1/responses", createRespJson);
|
||||
using var createRespDoc = await this.ParseResponseAsync(createRespResponse);
|
||||
var response = createRespDoc.RootElement;
|
||||
|
||||
// Assert - Response completed
|
||||
Assert.Equal("completed", response.GetProperty("status").GetString());
|
||||
string responseId = response.GetProperty("id").GetString()!;
|
||||
Assert.NotNull(responseId);
|
||||
Assert.StartsWith("resp_", responseId);
|
||||
|
||||
// Assert - Response has output
|
||||
Assert.True(response.TryGetProperty("output", out var output));
|
||||
Assert.True(output.GetArrayLength() > 0);
|
||||
var outputItem = output[0];
|
||||
var content = outputItem.GetProperty("content");
|
||||
Assert.True(content.GetArrayLength() > 0);
|
||||
var textContent = content[0];
|
||||
Assert.Equal("output_text", textContent.GetProperty("type").GetString());
|
||||
Assert.Equal(ExpectedResponse, textContent.GetProperty("text").GetString());
|
||||
|
||||
// Act - List conversation items to verify they were updated
|
||||
HttpResponseMessage listItemsResponse = await this.SendGetRequestAsync(client, $"/v1/conversations/{conversationId}/items");
|
||||
using var listItemsDoc = await this.ParseResponseAsync(listItemsResponse);
|
||||
var itemsList = listItemsDoc.RootElement;
|
||||
|
||||
// Assert - Conversation items were added
|
||||
Assert.Equal("list", itemsList.GetProperty("object").GetString());
|
||||
var items = itemsList.GetProperty("data");
|
||||
|
||||
Assert.True(items.GetArrayLength() > 0, "Conversation should have items after response completion");
|
||||
|
||||
// Find the assistant message in the items
|
||||
bool foundAssistantMessage = items.EnumerateArray()
|
||||
.Where(item => item.GetProperty("type").GetString() == "message" &&
|
||||
item.GetProperty("role").GetString() == "assistant")
|
||||
.Any(item =>
|
||||
{
|
||||
JsonElement itemContent = item.GetProperty("content");
|
||||
if (itemContent.GetArrayLength() > 0)
|
||||
{
|
||||
JsonElement firstContent = itemContent[0];
|
||||
return firstContent.GetProperty("type").GetString() == "output_text" &&
|
||||
firstContent.GetProperty("text").GetString() == ExpectedResponse;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
Assert.True(foundAssistantMessage, "Conversation should contain the assistant's response message");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateConversationAndResponse_Streaming_NonBackground_UpdatesConversationWithOutputAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "streaming-agent";
|
||||
const string Instructions = "You are a helpful assistant.";
|
||||
const string ExpectedResponse = "Hello there! How can I help you today?";
|
||||
const string UserMessage = "Hello";
|
||||
|
||||
HttpClient client = await this.CreateTestServerWithInMemoryStorageAsync(AgentName, Instructions, ExpectedResponse);
|
||||
|
||||
// Act - Create conversation
|
||||
var createConversationRequest = new { metadata = new { agent_id = AgentName } };
|
||||
string createConvJson = JsonSerializer.Serialize(createConversationRequest);
|
||||
HttpResponseMessage createConvResponse = await this.SendPostRequestAsync(client, "/v1/conversations", createConvJson);
|
||||
using var createConvDoc = await this.ParseResponseAsync(createConvResponse);
|
||||
string conversationId = createConvDoc.RootElement.GetProperty("id").GetString()!;
|
||||
|
||||
// Act - Create response (streaming, non-background)
|
||||
var createResponseRequest = new
|
||||
{
|
||||
metadata = new { entity_id = AgentName },
|
||||
conversation = conversationId,
|
||||
input = UserMessage,
|
||||
stream = true
|
||||
};
|
||||
string createRespJson = JsonSerializer.Serialize(createResponseRequest);
|
||||
HttpResponseMessage createRespResponse = await this.SendPostRequestAsync(client, $"/{AgentName}/v1/responses", createRespJson);
|
||||
|
||||
// Assert - Response is SSE format
|
||||
Assert.Equal("text/event-stream", createRespResponse.Content.Headers.ContentType?.MediaType);
|
||||
|
||||
// Parse SSE events
|
||||
string sseContent = await createRespResponse.Content.ReadAsStringAsync();
|
||||
var events = this.ParseSseEvents(sseContent);
|
||||
|
||||
// Assert - Has expected event types
|
||||
var eventTypes = events.Select(e => e.GetProperty("type").GetString()).ToList();
|
||||
Assert.Contains("response.created", eventTypes);
|
||||
Assert.Contains("response.completed", eventTypes);
|
||||
|
||||
// Collect the full response text from deltas
|
||||
var deltaEvents = events.Where(e => e.GetProperty("type").GetString() == "response.output_text.delta").ToList();
|
||||
string streamedText = string.Concat(deltaEvents.Select(e => e.GetProperty("delta").GetString()));
|
||||
Assert.Equal(ExpectedResponse, streamedText);
|
||||
|
||||
// Act - List conversation items to verify messages were added
|
||||
HttpResponseMessage listItemsResponse = await this.SendGetRequestAsync(client, $"/v1/conversations/{conversationId}/items");
|
||||
using var listItemsDoc = await this.ParseResponseAsync(listItemsResponse);
|
||||
var itemsList = listItemsDoc.RootElement;
|
||||
|
||||
// Assert - Conversation items were added
|
||||
var items = itemsList.GetProperty("data");
|
||||
Assert.True(items.GetArrayLength() > 0, "Conversation should have items after streaming response completion");
|
||||
|
||||
// Find the assistant message in the items
|
||||
bool foundAssistantMessage = items.EnumerateArray()
|
||||
.Where(item => item.GetProperty("type").GetString() == "message" &&
|
||||
item.GetProperty("role").GetString() == "assistant")
|
||||
.Any(item =>
|
||||
{
|
||||
JsonElement itemContent = item.GetProperty("content");
|
||||
if (itemContent.GetArrayLength() > 0)
|
||||
{
|
||||
JsonElement firstContent = itemContent[0];
|
||||
return firstContent.GetProperty("type").GetString() == "output_text" &&
|
||||
firstContent.GetProperty("text").GetString() == ExpectedResponse;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
Assert.True(foundAssistantMessage, "Conversation should contain the assistant's response message");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateConversationAndResponse_NonStreaming_Background_UpdatesConversationWhenCompleteAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "background-agent";
|
||||
const string Instructions = "You are a helpful assistant.";
|
||||
const string ExpectedResponse = "Processing in background...";
|
||||
const string UserMessage = "Can you process this?";
|
||||
|
||||
HttpClient client = await this.CreateTestServerWithInMemoryStorageAsync(AgentName, Instructions, ExpectedResponse);
|
||||
|
||||
// Act - Create conversation
|
||||
var createConversationRequest = new { metadata = new { agent_id = AgentName } };
|
||||
string createConvJson = JsonSerializer.Serialize(createConversationRequest);
|
||||
HttpResponseMessage createConvResponse = await this.SendPostRequestAsync(client, "/v1/conversations", createConvJson);
|
||||
using var createConvDoc = await this.ParseResponseAsync(createConvResponse);
|
||||
string conversationId = createConvDoc.RootElement.GetProperty("id").GetString()!;
|
||||
|
||||
// Act - Create response (non-streaming, background)
|
||||
var createResponseRequest = new
|
||||
{
|
||||
metadata = new { entity_id = AgentName },
|
||||
conversation = conversationId,
|
||||
input = UserMessage,
|
||||
stream = false,
|
||||
background = true
|
||||
};
|
||||
string createRespJson = JsonSerializer.Serialize(createResponseRequest);
|
||||
HttpResponseMessage createRespResponse = await this.SendPostRequestAsync(client, $"/{AgentName}/v1/responses", createRespJson);
|
||||
using var createRespDoc = await this.ParseResponseAsync(createRespResponse);
|
||||
var response = createRespDoc.RootElement;
|
||||
|
||||
// Assert - Response is in progress or queued
|
||||
string status = response.GetProperty("status").GetString()!;
|
||||
Assert.True(status is "in_progress" or "queued" or "completed", $"Expected 'in_progress', 'queued', or 'completed', got '{status}'");
|
||||
string responseId = response.GetProperty("id").GetString()!;
|
||||
|
||||
// Wait for completion by polling
|
||||
const int MaxAttempts = 20;
|
||||
int attempt = 0;
|
||||
string finalStatus = status;
|
||||
string? errorMessage = null;
|
||||
while (finalStatus != "completed" && finalStatus != "failed" && attempt < MaxAttempts)
|
||||
{
|
||||
await Task.Delay(100);
|
||||
HttpResponseMessage getResponseResponse = await this.SendGetRequestAsync(client, $"/{AgentName}/v1/responses/{responseId}");
|
||||
using var getRespDoc = await this.ParseResponseAsync(getResponseResponse);
|
||||
finalStatus = getRespDoc.RootElement.GetProperty("status").GetString()!;
|
||||
if (getRespDoc.RootElement.TryGetProperty("error", out var error) &&
|
||||
error.ValueKind == JsonValueKind.Object &&
|
||||
error.TryGetProperty("message", out var messageElement))
|
||||
{
|
||||
errorMessage = messageElement.GetString();
|
||||
}
|
||||
|
||||
attempt++;
|
||||
}
|
||||
|
||||
// Assert - Response eventually completed
|
||||
Assert.Equal("completed", finalStatus + (errorMessage != null ? $" Error: {errorMessage}" : ""));
|
||||
|
||||
// Act - List conversation items to verify messages were added
|
||||
HttpResponseMessage listItemsResponse = await this.SendGetRequestAsync(client, $"/v1/conversations/{conversationId}/items");
|
||||
using var listItemsDoc = await this.ParseResponseAsync(listItemsResponse);
|
||||
var itemsList = listItemsDoc.RootElement;
|
||||
|
||||
// Assert - Conversation items were added
|
||||
var items = itemsList.GetProperty("data");
|
||||
Assert.True(items.GetArrayLength() > 0, "Conversation should have items after background response completion");
|
||||
|
||||
// Find the assistant message in the items
|
||||
bool foundAssistantMessage = items.EnumerateArray()
|
||||
.Where(item => item.GetProperty("type").GetString() == "message" &&
|
||||
item.GetProperty("role").GetString() == "assistant")
|
||||
.Any(item =>
|
||||
{
|
||||
JsonElement itemContent = item.GetProperty("content");
|
||||
if (itemContent.GetArrayLength() > 0)
|
||||
{
|
||||
JsonElement firstContent = itemContent[0];
|
||||
return firstContent.GetProperty("type").GetString() == "output_text" &&
|
||||
firstContent.GetProperty("text").GetString() == ExpectedResponse;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
Assert.True(foundAssistantMessage, "Conversation should contain the assistant's response message");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateConversationAndResponse_Streaming_Background_UpdatesConversationWhenCompleteAsync()
|
||||
{
|
||||
// Arrange
|
||||
const string AgentName = "streaming-background-agent";
|
||||
const string Instructions = "You are a helpful assistant.";
|
||||
const string ExpectedResponse = "Streaming background response";
|
||||
const string UserMessage = "Process this with streaming";
|
||||
|
||||
HttpClient client = await this.CreateTestServerWithInMemoryStorageAsync(AgentName, Instructions, ExpectedResponse);
|
||||
|
||||
// Act - Create conversation
|
||||
var createConversationRequest = new { metadata = new { agent_id = AgentName } };
|
||||
string createConvJson = JsonSerializer.Serialize(createConversationRequest);
|
||||
HttpResponseMessage createConvResponse = await this.SendPostRequestAsync(client, "/v1/conversations", createConvJson);
|
||||
using var createConvDoc = await this.ParseResponseAsync(createConvResponse);
|
||||
string conversationId = createConvDoc.RootElement.GetProperty("id").GetString()!;
|
||||
|
||||
// Act - Create response (streaming, background)
|
||||
var createResponseRequest = new
|
||||
{
|
||||
model = AgentName,
|
||||
conversation = conversationId,
|
||||
input = UserMessage,
|
||||
stream = true,
|
||||
background = false // Note: streaming with background=true is typically streaming
|
||||
};
|
||||
string createRespJson = JsonSerializer.Serialize(createResponseRequest);
|
||||
HttpResponseMessage createRespResponse = await this.SendPostRequestAsync(client, $"/{AgentName}/v1/responses", createRespJson);
|
||||
|
||||
// Assert - Response is SSE format
|
||||
Assert.Equal("text/event-stream", createRespResponse.Content.Headers.ContentType?.MediaType);
|
||||
|
||||
// Parse SSE events
|
||||
string sseContent = await createRespResponse.Content.ReadAsStringAsync();
|
||||
var events = this.ParseSseEvents(sseContent);
|
||||
var eventTypes = events.Select(e => e.GetProperty("type").GetString()).ToList();
|
||||
Assert.Contains("response.created", eventTypes);
|
||||
Assert.Contains("response.completed", eventTypes);
|
||||
|
||||
// Act - List conversation items to verify messages were added
|
||||
HttpResponseMessage listItemsResponse = await this.SendGetRequestAsync(client, $"/v1/conversations/{conversationId}/items");
|
||||
using var listItemsDoc = await this.ParseResponseAsync(listItemsResponse);
|
||||
var itemsList = listItemsDoc.RootElement;
|
||||
|
||||
// Assert - Conversation items were added
|
||||
var items = itemsList.GetProperty("data");
|
||||
Assert.True(items.GetArrayLength() > 0, "Conversation should have items after streaming response completion");
|
||||
|
||||
// Find the assistant message in the items
|
||||
bool foundAssistantMessage = items.EnumerateArray()
|
||||
.Where(item => item.GetProperty("type").GetString() == "message" &&
|
||||
item.GetProperty("role").GetString() == "assistant")
|
||||
.Any(item =>
|
||||
{
|
||||
JsonElement itemContent = item.GetProperty("content");
|
||||
if (itemContent.GetArrayLength() > 0)
|
||||
{
|
||||
JsonElement firstContent = itemContent[0];
|
||||
return firstContent.GetProperty("type").GetString() == "output_text";
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
Assert.True(foundAssistantMessage, "Conversation should contain the assistant's response message");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a test server with in-memory conversation, response, and agent index storage.
|
||||
/// </summary>
|
||||
private async Task<HttpClient> CreateTestServerWithInMemoryStorageAsync(string agentName, string instructions, string responseText)
|
||||
{
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||
builder.WebHost.UseTestServer();
|
||||
|
||||
// Create mock chat client
|
||||
IChatClient mockChatClient = new TestHelpers.SimpleMockChatClient(responseText);
|
||||
builder.Services.AddKeyedSingleton("chat-client", mockChatClient);
|
||||
|
||||
// Add agent
|
||||
builder.AddAIAgent(agentName, instructions, chatClientServiceKey: "chat-client");
|
||||
|
||||
// Add in-memory storage for conversations, responses, and agent index
|
||||
builder.AddOpenAIConversations();
|
||||
builder.AddOpenAIResponses();
|
||||
|
||||
this._app = builder.Build();
|
||||
|
||||
// Map endpoints
|
||||
AIAgent agent = this._app.Services.GetRequiredKeyedService<AIAgent>(agentName);
|
||||
this._app.MapOpenAIConversations();
|
||||
this._app.MapOpenAIResponses(agent);
|
||||
|
||||
await this._app.StartAsync();
|
||||
|
||||
TestServer testServer = this._app.Services.GetRequiredService<IServer>() as TestServer
|
||||
?? throw new InvalidOperationException("TestServer not found");
|
||||
|
||||
this._httpClient = testServer.CreateClient();
|
||||
return this._httpClient;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a POST request with JSON content to the test server.
|
||||
/// </summary>
|
||||
private async Task<HttpResponseMessage> SendPostRequestAsync(HttpClient client, string path, string requestJson)
|
||||
{
|
||||
using StringContent content = new(requestJson, Encoding.UTF8, "application/json");
|
||||
return await client.PostAsync(new Uri(path, UriKind.Relative), content);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a GET request to the test server.
|
||||
/// </summary>
|
||||
private async Task<HttpResponseMessage> SendGetRequestAsync(HttpClient client, string path)
|
||||
{
|
||||
return await client.GetAsync(new Uri(path, UriKind.Relative));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the response JSON and returns a JsonDocument.
|
||||
/// </summary>
|
||||
private async Task<JsonDocument> ParseResponseAsync(HttpResponseMessage response)
|
||||
{
|
||||
string responseJson = await response.Content.ReadAsStringAsync();
|
||||
return JsonDocument.Parse(responseJson);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses SSE events from streaming response content string.
|
||||
/// </summary>
|
||||
private JsonElement[] ParseSseEvents(string sseContent)
|
||||
{
|
||||
var events = new System.Collections.Generic.List<JsonElement>();
|
||||
var lines = sseContent.Split('\n');
|
||||
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
var line = lines[i].TrimEnd('\r');
|
||||
|
||||
if (line.StartsWith("event: ", StringComparison.Ordinal) && i + 1 < lines.Length)
|
||||
{
|
||||
var dataLine = lines[i + 1].TrimEnd('\r');
|
||||
if (dataLine.StartsWith("data: ", StringComparison.Ordinal))
|
||||
{
|
||||
var jsonData = dataLine.Substring("data: ".Length);
|
||||
if (!string.IsNullOrWhiteSpace(jsonData))
|
||||
{
|
||||
var doc = JsonDocument.Parse(jsonData);
|
||||
events.Add(doc.RootElement.Clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return events.ToArray();
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
this._httpClient?.Dispose();
|
||||
if (this._app != null)
|
||||
{
|
||||
await this._app.DisposeAsync();
|
||||
}
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user