Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/CodexSdk.MAF/CodexAIAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ protected override async Task<AgentResponse> RunCoreAsync(

try
{
await foreach (var threadEvent in thread.RunStreamedAsync(inputLease.Input, cancellationToken: cancellationToken))
await foreach (var threadEvent in thread.RunStreamedAsync(inputLease.Input, _options.TurnOptions, cancellationToken))
{
if (threadEvent is ThreadStartedEvent started)
{
Expand Down Expand Up @@ -179,7 +179,7 @@ protected override async IAsyncEnumerable<AgentResponseUpdate> RunCoreStreamingA

try
{
await foreach (var threadEvent in thread.RunStreamedAsync(inputLease.Input, cancellationToken: cancellationToken))
await foreach (var threadEvent in thread.RunStreamedAsync(inputLease.Input, _options.TurnOptions, cancellationToken))
{
if (threadEvent is ThreadStartedEvent started)
{
Expand Down
6 changes: 6 additions & 0 deletions src/CodexSdk.MAF/CodexAIAgentOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ public sealed record CodexAIAgentOptions
SkipGitRepoCheck = true,
};

/// <summary>
/// Per-turn options forwarded to <see cref="Thread.RunStreamedAsync"/> on both the buffered and
/// streaming paths. Use <see cref="TurnOptions.OutputSchema"/> to require structured output.
/// </summary>
public TurnOptions? TurnOptions { get; init; }

public Guid? ThreadId { get; init; }

public bool IsResume { get; init; }
Expand Down
79 changes: 79 additions & 0 deletions tests/CodexSdk.MAF.Tests/CodexAIAgentTurnOptionsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using CodexSdk.TestSupport;
using OpenAI.CodexSdk;
using OpenAI.CodexSdk.MAF;
using Xunit;

namespace CodexSdk.MAF.Tests;

public sealed class CodexAIAgentTurnOptionsTests
{
[Fact]
public async Task RunStreamingAsync_WithOutputSchema_PassesOutputSchemaFlagToCli()
{
// Arrange
using var cli = new FakeCodexCli();
var agent = new CodexAIAgent(
new CodexAIAgentOptions
{
CodexOptions = cli.CreateOptions(),
TurnOptions = new TurnOptions
{
OutputSchema = new Dictionary<string, object?> { ["type"] = "object" },
},
}
);
var session = await agent.CreateSessionAsync(TestContext.Current.CancellationToken);

// Act
await foreach (
var _ in agent.RunStreamingAsync("hi", session, cancellationToken: TestContext.Current.CancellationToken)
) { }

// Assert
var args = Assert.Single(cli.ReadArguments());
Assert.Contains("--output-schema", args);
}

[Fact]
public async Task RunAsync_WithOutputSchema_PassesOutputSchemaFlagToCli()
{
// Arrange
using var cli = new FakeCodexCli();
var agent = new CodexAIAgent(
new CodexAIAgentOptions
{
CodexOptions = cli.CreateOptions(),
TurnOptions = new TurnOptions
{
OutputSchema = new Dictionary<string, object?> { ["type"] = "object" },
},
}
);
var session = await agent.CreateSessionAsync(TestContext.Current.CancellationToken);

// Act
await agent.RunAsync("hi", session, cancellationToken: TestContext.Current.CancellationToken);

// Assert
var args = Assert.Single(cli.ReadArguments());
Assert.Contains("--output-schema", args);
}

[Fact]
public async Task RunStreamingAsync_WithoutTurnOptions_OmitsOutputSchemaFlag()
{
// Arrange
using var cli = new FakeCodexCli();
var agent = new CodexAIAgent(new CodexAIAgentOptions { CodexOptions = cli.CreateOptions() });
var session = await agent.CreateSessionAsync(TestContext.Current.CancellationToken);

// Act
await foreach (
var _ in agent.RunStreamingAsync("hi", session, cancellationToken: TestContext.Current.CancellationToken)
) { }

// Assert
var args = Assert.Single(cli.ReadArguments());
Assert.DoesNotContain("--output-schema", args);
}
}
Loading