Skip to content
6 changes: 3 additions & 3 deletions dotnet/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
<PackageVersion Include="Aspire.Microsoft.Azure.Cosmos" Version="$(AspireAppHostSdkVersion)" />
<PackageVersion Include="CommunityToolkit.Aspire.OllamaSharp" Version="13.0.0" />
<!-- Azure.* -->
<PackageVersion Include="Azure.AI.Projects" Version="1.2.0-beta.5" />
<PackageVersion Include="Azure.AI.Projects.OpenAI" Version="1.0.0-beta.5" />
<PackageVersion Include="Azure.AI.Projects" Version="2.0.0-beta.1" />
<PackageVersion Include="Azure.AI.Projects.OpenAI" Version="2.0.0-beta.1" />
<PackageVersion Include="Azure.AI.Agents.Persistent" Version="1.2.0-beta.8" />
<PackageVersion Include="Azure.AI.OpenAI" Version="2.8.0-beta.1" />
<PackageVersion Include="Azure.Identity" Version="1.17.1" />
Expand All @@ -35,7 +35,7 @@
<!-- System.* -->
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="10.0.3" />
<PackageVersion Include="Microsoft.Bcl.HashCode" Version="6.0.0" />
<PackageVersion Include="System.ClientModel" Version="1.8.1" />
<PackageVersion Include="System.ClientModel" Version="1.9.0" />
<PackageVersion Include="System.CodeDom" Version="10.0.0" />
<PackageVersion Include="System.Collections.Immutable" Version="10.0.1" />
<PackageVersion Include="System.CommandLine" Version="2.0.0-rc.2.25502.107" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@

// Submit the red team run to the service
Console.WriteLine("Submitting red team run...");
RedTeam redTeamRun = await aiProjectClient.RedTeams.CreateAsync(redTeamConfig);
RedTeam redTeamRun = await aiProjectClient.RedTeams.CreateAsync(redTeamConfig, options: null);

Console.WriteLine($"Red team run created: {redTeamRun.Name}");
Console.WriteLine($"Status: {redTeamRun.Status}");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Use the memory search tool to recall relevant information from previous interact
AIProjectClient aiProjectClient = new(new Uri(endpoint), new AzureCliCredential());

// Create the Memory Search tool configuration
MemorySearchTool memorySearchTool = new(memoryStoreName, userScope)
MemorySearchPreviewTool memorySearchTool = new(memoryStoreName, userScope)
{
// Optional: Configure how quickly new memories are indexed (in seconds)
UpdateDelay = 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,9 @@ private static async Task<AgentVersion> CreateWorkflowAsync(AIProjectClient agen
{
string workflowYaml = File.ReadAllText("MathChat.yaml");

#pragma warning disable AAIP001 // WorkflowAgentDefinition is experimental
WorkflowAgentDefinition workflowAgentDefinition = WorkflowAgentDefinition.FromYaml(workflowYaml);
#pragma warning restore AAIP001

return
await agentClient.CreateAgentAsync(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public static partial class AzureAIProjectChatClientExtensions
/// <exception cref="InvalidOperationException">The agent with the specified name was not found.</exception>
/// <remarks>
/// When instantiating a <see cref="ChatClientAgent"/> by using an <see cref="AgentReference"/>, minimal information will be available about the agent in the instance level, and any logic that relies
/// on <see cref="AIAgent.GetService(Type, object?)"/> to retrieve information about the agent like <see cref="AgentVersion" /> will receive <see langword="null"/> as the result.
/// on <see cref="AIAgent.GetService{TService}(object?)"/> to retrieve information about the agent like <see cref="AgentVersion" /> will receive <see langword="null"/> as the result.
/// </remarks>
public static ChatClientAgent AsAIAgent(
this AIProjectClient aiProjectClient,
Expand Down Expand Up @@ -355,28 +355,27 @@ public static Task<ChatClientAgent> CreateAIAgentAsync(
private static readonly ModelReaderWriterOptions s_modelWriterOptionsWire = new("W");

/// <summary>
/// Asynchronously retrieves an agent record by name using the Protocol method with user-agent header.
/// Asynchronously retrieves an agent record by name using the protocol method to inject user-agent headers.
/// </summary>
private static async Task<AgentRecord> GetAgentRecordByNameAsync(AIProjectClient aiProjectClient, string agentName, CancellationToken cancellationToken)
{
ClientResult protocolResponse = await aiProjectClient.Agents.GetAgentAsync(agentName, cancellationToken.ToRequestOptions(false)).ConfigureAwait(false);
var rawResponse = protocolResponse.GetRawResponse();
AgentRecord? result = ModelReaderWriter.Read<AgentRecord>(rawResponse.Content, s_modelWriterOptionsWire, AzureAIProjectsOpenAIContext.Default);
return ClientResult.FromOptionalValue(result, rawResponse).Value!
?? throw new InvalidOperationException($"Agent with name '{agentName}' not found.");
return result ?? throw new InvalidOperationException($"Agent with name '{agentName}' not found.");
}

/// <summary>
/// Asynchronously creates an agent version using the Protocol method with user-agent header.
/// Asynchronously creates an agent version using the protocol method to inject user-agent headers.
/// </summary>
private static async Task<AgentVersion> CreateAgentVersionWithProtocolAsync(AIProjectClient aiProjectClient, string agentName, AgentVersionCreationOptions creationOptions, CancellationToken cancellationToken)
{
using BinaryContent protocolRequest = BinaryContent.Create(ModelReaderWriter.Write(creationOptions, ModelReaderWriterOptions.Json, AzureAIProjectsContext.Default));
ClientResult protocolResponse = await aiProjectClient.Agents.CreateAgentVersionAsync(agentName, protocolRequest, cancellationToken.ToRequestOptions(false)).ConfigureAwait(false);

BinaryData serializedOptions = ModelReaderWriter.Write(creationOptions, s_modelWriterOptionsWire, AzureAIProjectsContext.Default);
BinaryContent content = BinaryContent.Create(serializedOptions);
ClientResult protocolResponse = await aiProjectClient.Agents.CreateAgentVersionAsync(agentName, content, foundryFeatures: null, cancellationToken.ToRequestOptions(false)).ConfigureAwait(false);
var rawResponse = protocolResponse.GetRawResponse();
AgentVersion? result = ModelReaderWriter.Read<AgentVersion>(rawResponse.Content, s_modelWriterOptionsWire, AzureAIProjectsOpenAIContext.Default);
return ClientResult.FromValue(result, rawResponse).Value!;
return result ?? throw new InvalidOperationException($"Failed to create agent version for agent '{agentName}'.");
}

private static async Task<ChatClientAgent> CreateAIAgentAsync(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,18 @@ private async Task RunLoopAsync(CancellationToken cancellationToken)

while (!linkedSource.Token.IsCancellationRequested)
{
// Guard against spurious wake-ups from the input waiter timeout.
// Without this check, a timeout wake-up (no actual input) would create
// a new workflow_invoke Activity even though there is no work to process.
if (!this._stepRunner.HasUnprocessedMessages)
{
await this._inputWaiter.WaitForInputAsync(TimeSpan.FromSeconds(1), linkedSource.Token).ConfigureAwait(false);
continue;
}

// When signaled with actual input, resume running
this._runStatus = RunStatus.Running;

// Start a new run-stage activity for this input→processing→halt cycle
runActivity = this._stepRunner.TelemetryContext.StartWorkflowRunActivity();
runActivity?.SetTag(Tags.WorkflowId, this._stepRunner.StartExecutorId)
Expand Down Expand Up @@ -117,9 +129,6 @@ private async Task RunLoopAsync(CancellationToken cancellationToken)
// Wait for next input from the consumer
// Works for both Idle (no work) and PendingRequests (waiting for responses)
await this._inputWaiter.WaitForInputAsync(TimeSpan.FromSeconds(1), linkedSource.Token).ConfigureAwait(false);

// When signaled, resume running
this._runStatus = RunStatus.Running;
}
}
catch (OperationCanceledException)
Expand Down Expand Up @@ -197,8 +206,12 @@ public async IAsyncEnumerable<WorkflowEvent> TakeEventStreamAsync(
bool blockOnPendingRequest,
[EnumeratorCancellation] CancellationToken cancellationToken = default)
{
// Get the current epoch - we'll only respond to completion signals from this epoch or later
int myEpoch = Volatile.Read(ref this._completionEpoch) + 1;
// Get the current epoch - we'll only respond to completion signals from this epoch or later.
// Note: We read the current value (not +1) because the HasUnprocessedMessages guard in the
// run loop prevents spurious completion signals, so there are no stale signals to filter.
// Using +1 would race with the run loop's Interlocked.Increment, causing the consumer to
// skip the valid signal when the run loop finishes before TakeEventStreamAsync starts.
int myEpoch = Volatile.Read(ref this._completionEpoch);

// Use custom async enumerable to avoid exceptions on cancellation.
NonThrowingChannelReaderAsyncEnumerable<WorkflowEvent> eventStream = new(this._eventChannel.Reader);
Expand Down
Loading
Loading