mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ebbed6fd6 | ||
|
|
900135b092 | ||
|
|
9bbfab6c1e | ||
|
|
1538c631c8 | ||
|
|
448785b6b3 | ||
|
|
58af9ecb89 | ||
|
|
027d1e04a7 | ||
|
|
3f7b0d1085 | ||
|
|
8baacc4fe3 | ||
|
|
2663467914 | ||
|
|
4de5f1b421 | ||
|
|
6c969418ac |
@@ -76,6 +76,15 @@ jobs:
|
||||
dotnet
|
||||
workflow-samples
|
||||
|
||||
- name: Start Azurite
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
docker run -d \
|
||||
-p 10000:10000 \
|
||||
-p 10001:10001 \
|
||||
-p 10002:10002 \
|
||||
mcr.microsoft.com/azure-storage/azurite:latest
|
||||
|
||||
- name: Setup dotnet
|
||||
uses: actions/setup-dotnet@v5.0.0
|
||||
with:
|
||||
@@ -239,4 +248,4 @@ jobs:
|
||||
if: contains(join(needs.*.result, ','), 'cancelled')
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: core.setFailed('Integration Tests Cancelled!')
|
||||
script: core.setFailed('Integration Tests Cancelled!')
|
||||
@@ -21,6 +21,7 @@
|
||||
<PackageVersion Include="Azure.AI.OpenAI" Version="2.5.0-beta.1" />
|
||||
<PackageVersion Include="Azure.Identity" Version="1.17.0" />
|
||||
<PackageVersion Include="Azure.Monitor.OpenTelemetry.Exporter" Version="1.4.0" />
|
||||
<PackageVersion Include="Azure.Storage.Blobs" Version="12.26.0" />
|
||||
<!-- System.* -->
|
||||
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.10" />
|
||||
<PackageVersion Include="Microsoft.Bcl.HashCode" Version="6.0.0" />
|
||||
@@ -105,6 +106,7 @@
|
||||
<PackageVersion Include="xunit" Version="2.9.3" />
|
||||
<PackageVersion Include="xunit.abstractions" Version="2.0.3" />
|
||||
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.3" />
|
||||
<PackageVersion Include="Xunit.SkippableFact" Version="1.5.23" />
|
||||
<PackageVersion Include="xretry" Version="1.9.0" />
|
||||
<PackageVersion Include="coverlet.collector" Version="6.0.4" />
|
||||
<!-- Symbols -->
|
||||
|
||||
@@ -133,7 +133,7 @@
|
||||
<Project Path="samples/GettingStarted/Workflows/Observability/WorkflowAsAnAgent/WorkflowAsAnAgentObservability.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Samples/GettingStarted/Workflows/Visualization/">
|
||||
<Project Path="samples/GettingStarted/Workflows/Visualization/Visualization.csproj" Id="99bf0bc6-2440-428e-b3e7-d880e4b7a5fd" />
|
||||
<Project Path="samples/GettingStarted/Workflows/Visualization/Visualization.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Samples/GettingStarted/Workflows/_Foundational/">
|
||||
<Project Path="samples/GettingStarted/Workflows/_Foundational/01_ExecutorsAndEdges/01_ExecutorsAndEdges.csproj" />
|
||||
@@ -275,6 +275,7 @@
|
||||
<Project Path="src/Microsoft.Agents.AI.CopilotStudio/Microsoft.Agents.AI.CopilotStudio.csproj" />
|
||||
<Project Path="src/Microsoft.Agents.AI.Hosting.A2A.AspNetCore/Microsoft.Agents.AI.Hosting.A2A.AspNetCore.csproj" />
|
||||
<Project Path="src/Microsoft.Agents.AI.Hosting.A2A/Microsoft.Agents.AI.Hosting.A2A.csproj" />
|
||||
<Project Path="src/Microsoft.Agents.AI.Hosting.AzureStorage/Microsoft.Agents.AI.Hosting.AzureStorage.csproj" />
|
||||
<Project Path="src/Microsoft.Agents.AI.Hosting.OpenAI/Microsoft.Agents.AI.Hosting.OpenAI.csproj" />
|
||||
<Project Path="src/Microsoft.Agents.AI.Hosting/Microsoft.Agents.AI.Hosting.csproj" />
|
||||
<Project Path="src/Microsoft.Agents.AI.Mem0/Microsoft.Agents.AI.Mem0.csproj" />
|
||||
@@ -298,8 +299,9 @@
|
||||
<Project Path="tests/Microsoft.Agents.AI.A2A.UnitTests/Microsoft.Agents.AI.A2A.UnitTests.csproj" />
|
||||
<Project Path="tests/Microsoft.Agents.AI.Abstractions.UnitTests/Microsoft.Agents.AI.Abstractions.UnitTests.csproj" />
|
||||
<Project Path="tests/Microsoft.Agents.AI.AzureAI.UnitTests/Microsoft.Agents.AI.AzureAI.UnitTests.csproj" />
|
||||
<Project Path="tests/Microsoft.Agents.AI.Hosting.A2A.Tests/Microsoft.Agents.AI.Hosting.A2A.Tests.csproj" Id="2a1c544d-237d-4436-8732-ba0c447ac06b" />
|
||||
<Project Path="tests/Microsoft.Agents.AI.Hosting.A2A.Tests/Microsoft.Agents.AI.Hosting.A2A.Tests.csproj" />
|
||||
<Project Path="tests/Microsoft.Agents.AI.Hosting.OpenAI.UnitTests/Microsoft.Agents.AI.Hosting.OpenAI.UnitTests.csproj" />
|
||||
<Project Path="tests/Microsoft.Agents.AI.Hosting.AzureStorage.UnitTests/Microsoft.Agents.AI.Hosting.AzureStorage.UnitTests.csproj" />
|
||||
<Project Path="tests/Microsoft.Agents.AI.Hosting.UnitTests/Microsoft.Agents.AI.Hosting.UnitTests.csproj" />
|
||||
<Project Path="tests/Microsoft.Agents.AI.Mem0.UnitTests/Microsoft.Agents.AI.Mem0.UnitTests.csproj" />
|
||||
<Project Path="tests/Microsoft.Agents.AI.OpenAI.UnitTests/Microsoft.Agents.AI.OpenAI.UnitTests.csproj" />
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
<PropertyGroup>
|
||||
<!-- Central version prefix - applies to all nuget packages. -->
|
||||
<VersionPrefix>1.0.0</VersionPrefix>
|
||||
<PackageVersion Condition="'$(VersionSuffix)' != ''">$(VersionPrefix)-$(VersionSuffix).251104.1</PackageVersion>
|
||||
<PackageVersion Condition="'$(VersionSuffix)' == ''">$(VersionPrefix)-preview.251104.1</PackageVersion>
|
||||
<GitTag>1.0.0-preview.251104.1</GitTag>
|
||||
<PackageVersion Condition="'$(VersionSuffix)' != ''">$(VersionPrefix)-$(VersionSuffix).251028.1</PackageVersion>
|
||||
<PackageVersion Condition="'$(VersionSuffix)' == ''">$(VersionPrefix)-preview.251028.1</PackageVersion>
|
||||
<GitTag>1.0.0-preview.251028.1</GitTag>
|
||||
|
||||
<Configurations>Debug;Release;Publish</Configurations>
|
||||
<IsPackable>true</IsPackable>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\src\Microsoft.Agents.AI.Hosting.AzureStorage\Microsoft.Agents.AI.Hosting.AzureStorage.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\Microsoft.Agents.AI.Workflows\Microsoft.Agents.AI.Workflows.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\Microsoft.Agents.AI.Abstractions\Microsoft.Agents.AI.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\Microsoft.Agents.AI.Hosting\Microsoft.Agents.AI.Hosting.csproj" />
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
using A2A.AspNetCore;
|
||||
using AgentWebChat.AgentHost;
|
||||
using AgentWebChat.AgentHost.Utilities;
|
||||
using Azure.Storage.Blobs;
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Agents.AI.Hosting;
|
||||
using Microsoft.Agents.AI.Workflows;
|
||||
@@ -27,6 +28,13 @@ builder.AddAIAgent(
|
||||
chatClientServiceKey: "chat-model")
|
||||
.WithInMemoryThreadStore();
|
||||
|
||||
builder.AddAIAgent(
|
||||
"gambler",
|
||||
instructions: "You are a gambler. Talk like a gambler.",
|
||||
description: "An agent which gambles",
|
||||
chatClientServiceKey: "chat-model")
|
||||
.WithAzureBlobThreadStore(sp => new BlobContainerClient(connectionString: "UseDevelopmentStorage=true", "agent-threads"));
|
||||
|
||||
builder.AddAIAgent("knights-and-knaves", (sp, key) =>
|
||||
{
|
||||
var chatClient = sp.GetRequiredKeyedService<IChatClient>("chat-model");
|
||||
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Azure;
|
||||
using Azure.Storage.Blobs;
|
||||
using Azure.Storage.Blobs.Models;
|
||||
using Microsoft.Shared.Diagnostics;
|
||||
|
||||
namespace Microsoft.Agents.AI.Hosting.AzureStorage.Blob;
|
||||
|
||||
internal sealed class AzureBlobAgentThreadStore : AgentThreadStore
|
||||
{
|
||||
private static readonly BlobOpenWriteOptions s_uploadJsonOptions = new()
|
||||
{
|
||||
HttpHeaders = new()
|
||||
{
|
||||
ContentType = "application/json"
|
||||
}
|
||||
};
|
||||
|
||||
private readonly BlobContainerClient _containerClient;
|
||||
private readonly AzureBlobAgentThreadStoreOptions _options;
|
||||
private bool _containerInitialized;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AzureBlobAgentThreadStore"/> class using a <see cref="BlobContainerClient"/>.
|
||||
/// </summary>
|
||||
/// <param name="containerClient">The blob container client to use for storage operations.</param>
|
||||
/// <param name="options">Optional configuration options. If <see langword="null"/>, default options will be used.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="containerClient"/> is <see langword="null"/>.</exception>
|
||||
public AzureBlobAgentThreadStore(BlobContainerClient containerClient, AzureBlobAgentThreadStoreOptions? options = null)
|
||||
{
|
||||
this._containerClient = containerClient ?? throw new ArgumentNullException(nameof(containerClient));
|
||||
this._options = options ?? new AzureBlobAgentThreadStoreOptions();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async ValueTask SaveThreadAsync(
|
||||
AIAgent agent,
|
||||
string conversationId,
|
||||
AgentThread thread,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
Throw.IfNull(agent);
|
||||
Throw.IfNull(conversationId);
|
||||
Throw.IfNull(thread);
|
||||
|
||||
await this.EnsureContainerExistsAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var blobName = this.GetBlobName(agent.Id, conversationId);
|
||||
var blobClient = this._containerClient.GetBlobClient(blobName);
|
||||
|
||||
JsonElement serializedThread = thread.Serialize();
|
||||
using Stream stream = await blobClient.OpenWriteAsync(overwrite: true, s_uploadJsonOptions, cancellationToken).ConfigureAwait(false);
|
||||
using Utf8JsonWriter writer = new(stream);
|
||||
|
||||
serializedThread.WriteTo(writer);
|
||||
await writer.FlushAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async ValueTask<AgentThread> GetThreadAsync(
|
||||
AIAgent agent,
|
||||
string conversationId,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
Throw.IfNull(agent);
|
||||
Throw.IfNull(conversationId);
|
||||
|
||||
await this.EnsureContainerExistsAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var blobName = this.GetBlobName(agent.Id, conversationId);
|
||||
var blobClient = this._containerClient.GetBlobClient(blobName);
|
||||
|
||||
try
|
||||
{
|
||||
var stream = await blobClient.OpenReadAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
var jsonDoc = await JsonDocument.ParseAsync(stream, cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
var serializedThread = jsonDoc.RootElement;
|
||||
|
||||
return agent.DeserializeThread(serializedThread);
|
||||
}
|
||||
catch (RequestFailedException ex) when (ex.Status == 404)
|
||||
{
|
||||
// Blob doesn't exist, return a new thread
|
||||
return agent.GetNewThread();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the blob container exists, creating it if necessary.
|
||||
/// </summary>
|
||||
private async Task EnsureContainerExistsAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (this._containerInitialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._containerInitialized)
|
||||
{
|
||||
if (this._options.CreateContainerIfNotExists)
|
||||
{
|
||||
await this._containerClient.CreateIfNotExistsAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
this._containerInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates the blob name for a given agent and conversation.
|
||||
/// </summary>
|
||||
// internal for testing
|
||||
internal string GetBlobName(string agentId, string conversationId)
|
||||
{
|
||||
string sanitizedAgentId = this.SanitizeBlobNameSegment(agentId);
|
||||
string sanitizedConversationId = this.SanitizeBlobNameSegment(conversationId);
|
||||
string baseName = $"{sanitizedAgentId}/{sanitizedConversationId}.json";
|
||||
|
||||
return string.IsNullOrEmpty(this._options.BlobNamePrefix)
|
||||
? baseName
|
||||
: $"{this._options.BlobNamePrefix.TrimEnd('/')}/{baseName}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sanitizes a string to be safe for use in blob names.
|
||||
/// </summary>
|
||||
private string SanitizeBlobNameSegment(string input)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(input))
|
||||
{
|
||||
return "default";
|
||||
}
|
||||
|
||||
// Replace invalid characters with underscore
|
||||
StringBuilder builder = new(input.Length);
|
||||
foreach (char c in input)
|
||||
{
|
||||
if (char.IsLetterOrDigit(c) || c == '-' || c == '_' || c == '.')
|
||||
{
|
||||
builder.Append(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Append('_');
|
||||
}
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
namespace Microsoft.Agents.AI.Hosting.AzureStorage.Blob;
|
||||
|
||||
/// <summary>
|
||||
/// Configuration options for <see cref="AzureBlobAgentThreadStore"/>.
|
||||
/// </summary>
|
||||
public sealed class AzureBlobAgentThreadStoreOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to automatically create the container if it doesn't exist.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Defaults to <see langword="true"/>.
|
||||
/// </remarks>
|
||||
public bool CreateContainerIfNotExists { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the blob name prefix to use for organizing threads.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This can be used to namespace threads within a container.
|
||||
/// For example, setting this to "prod/" will store all blobs under a "prod/" prefix.
|
||||
/// </remarks>
|
||||
public string? BlobNamePrefix { get; set; }
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using Azure.Storage.Blobs;
|
||||
using Microsoft.Agents.AI.Hosting.AzureStorage.Blob;
|
||||
|
||||
namespace Microsoft.Agents.AI.Hosting;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for configuring <see cref="AIAgent"/>.
|
||||
/// </summary>
|
||||
public static partial class HostedAgentBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Configures the host agent builder to use an Azure Blob thread store for agent thread management.
|
||||
/// </summary>
|
||||
/// <param name="builder">The host agent builder to configure with the Azure blob thread store.</param>
|
||||
/// <param name="containerClient">The blob container client to use for storage operations.</param>
|
||||
/// <param name="options">Optional configuration options for the blob thread store.</param>
|
||||
/// <returns>The same <paramref name="builder"/> instance, configured to use Azure blob thread store.</returns>
|
||||
public static IHostedAgentBuilder WithAzureBlobThreadStore(this IHostedAgentBuilder builder, BlobContainerClient containerClient, AzureBlobAgentThreadStoreOptions? options = null)
|
||||
=> WithAzureBlobThreadStore(builder, sp => containerClient, options);
|
||||
|
||||
/// <summary>
|
||||
/// Configures the agent builder to use Azure Blob Storage as the thread store for agent state persistence.
|
||||
/// </summary>
|
||||
/// <param name="builder">The agent builder to configure with Azure Blob thread store support.</param>
|
||||
/// <param name="createBlobContainer">A factory function that provides a configured BlobContainerClient instance for accessing the Azure Blob
|
||||
/// container used to store thread data.</param>
|
||||
/// <param name="options">Optional settings for customizing the Azure Blob thread store behavior. If null, default options are used.</param>
|
||||
/// <returns>The same agent builder instance, configured to use Azure Blob Storage for thread persistence.</returns>
|
||||
public static IHostedAgentBuilder WithAzureBlobThreadStore(
|
||||
this IHostedAgentBuilder builder,
|
||||
Func<IServiceProvider, BlobContainerClient> createBlobContainer,
|
||||
AzureBlobAgentThreadStoreOptions? options = null)
|
||||
=> builder.WithThreadStore((sp, key) =>
|
||||
{
|
||||
var blobContainer = createBlobContainer(sp);
|
||||
return new AzureBlobAgentThreadStore(blobContainer, options);
|
||||
});
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>$(ProjectsCoreTargetFrameworks)</TargetFrameworks>
|
||||
<TargetFrameworks Condition="'$(Configuration)' == 'Debug'">$(ProjectsDebugCoreTargetFrameworks)</TargetFrameworks>
|
||||
<VersionSuffix>preview</VersionSuffix>
|
||||
<InjectSharedThrow>true</InjectSharedThrow>
|
||||
|
||||
<!-- NuGet Package Settings -->
|
||||
<Title>Microsoft Agent Framework AzureStorage</Title>
|
||||
<Description>Provides AzureStorage integration with Microsoft Agent Framework.</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(RepoRoot)/dotnet/nuget/nuget-package.props" />
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Azure.Storage.Blobs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Microsoft.Agents.AI.Hosting\Microsoft.Agents.AI.Hosting.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<InternalsVisibleTo Include="Microsoft.Agents.AI.Hosting.AzureStorage.UnitTests" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<InternalsVisibleTo Include="Microsoft.Agents.AI.UnitTests" />
|
||||
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" />
|
||||
<InternalsVisibleTo Include="DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Azure.Storage.Blobs;
|
||||
using Microsoft.Agents.AI.Hosting.AzureStorage.Blob;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Microsoft.Agents.AI.Hosting.AzureStorage.UnitTests;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for <see cref="AzureBlobAgentThreadStore"/>.
|
||||
/// </summary>
|
||||
public sealed class AzureBlobAgentThreadStoreTests(ITestOutputHelper output) : IAsyncLifetime
|
||||
{
|
||||
private const string AzuriteConnectionString = "UseDevelopmentStorage=true";
|
||||
private const string TestContainerName = "agent-threads-test";
|
||||
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
|
||||
private BlobServiceClient _blobServiceClient;
|
||||
private BlobContainerClient _containerClient;
|
||||
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
await AzureStorageEmulatorAvailabilityHelper.SkipIfNotAvailableAsync();
|
||||
|
||||
this._blobServiceClient = new BlobServiceClient(AzuriteConnectionString);
|
||||
this._containerClient = this._blobServiceClient.GetBlobContainerClient(TestContainerName);
|
||||
|
||||
// Clean up any existing test container
|
||||
await this._containerClient.DeleteIfExistsAsync();
|
||||
}
|
||||
|
||||
public async Task DisposeAsync()
|
||||
{
|
||||
if (this._containerClient is not null)
|
||||
{
|
||||
await this._containerClient.DeleteIfExistsAsync();
|
||||
}
|
||||
}
|
||||
|
||||
[SkippableFact]
|
||||
public async Task AIHostAgent_SavesAndRetrievesThread_UsingAzureBlobStoreAsync()
|
||||
{
|
||||
AzureBlobAgentThreadStore threadStore = new(this._containerClient!);
|
||||
var testRunner = TestRunner.Initialize(output, threadStore);
|
||||
var blobName = GetBlobContainerName(threadStore, testRunner);
|
||||
|
||||
var runResult = await testRunner.RunAgentAsync("hello agent");
|
||||
Assert.Single(runResult.ResponseMessages);
|
||||
Assert.Equal(2, runResult.ThreadMessages.Count);
|
||||
await this.AssertBlobHasTextAsync(blobName, runResult.ThreadMessages);
|
||||
|
||||
var runResult2 = await testRunner.RunAgentAsync("hello again");
|
||||
Assert.Single(runResult2.ResponseMessages);
|
||||
Assert.Equal(4, runResult2.ThreadMessages.Count);
|
||||
await this.AssertBlobHasTextAsync(blobName, runResult2.ThreadMessages);
|
||||
}
|
||||
|
||||
private Task AssertBlobHasTextAsync(string blobName, IList<ChatMessage> chatMessages)
|
||||
{
|
||||
var texts = chatMessages.SelectMany(x => x.Contents).OfType<TextContent>().Select(x => x.Text).ToArray();
|
||||
return this.AssertBlobHasTextAsync(blobName, texts);
|
||||
}
|
||||
|
||||
private async Task AssertBlobHasTextAsync(string blobName, params string[] expectedTexts)
|
||||
{
|
||||
var blobClient = this._containerClient.GetBlobClient(blobName);
|
||||
var exists = await blobClient.ExistsAsync();
|
||||
Assert.True(exists, $"Blob '{blobName}' should exist.");
|
||||
|
||||
var downloadResponse = await blobClient.DownloadContentAsync();
|
||||
var blobJson = downloadResponse.Value.Content.ToString();
|
||||
output.WriteLine($"Actual blob json: {blobJson}");
|
||||
|
||||
foreach (var text in expectedTexts)
|
||||
{
|
||||
Assert.Contains(text, blobJson);
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetBlobContainerName(AzureBlobAgentThreadStore threadStore, TestRunner testRunner)
|
||||
=> threadStore.GetBlobName(testRunner.HostAgent.Id, testRunner.ConversationId);
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Azure;
|
||||
using Azure.Storage.Blobs;
|
||||
using Skip = Xunit.Skip;
|
||||
|
||||
namespace Microsoft.Agents.AI.Hosting.AzureStorage.UnitTests;
|
||||
|
||||
/// <summary>
|
||||
/// Helper class to check if Azurite (Azure Storage Emulator) is available and running.
|
||||
/// </summary>
|
||||
internal static class AzureStorageEmulatorAvailabilityHelper
|
||||
{
|
||||
private const string AzuriteConnectionString = "UseDevelopmentStorage=true";
|
||||
|
||||
/// <summary>
|
||||
/// Checks if Azurite is running and accessible.
|
||||
/// </summary>
|
||||
/// <returns><see langword="true"/> if Azurite is available; otherwise, <see langword="false"/>.</returns>
|
||||
public static async Task<bool> IsAvailableAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
BlobServiceClient serviceClient = new(AzuriteConnectionString);
|
||||
|
||||
// Try to get service properties to verify connection
|
||||
await serviceClient.GetPropertiesAsync(cancellationToken);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (RequestFailedException)
|
||||
{
|
||||
// Azurite is not running or not accessible
|
||||
return false;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Any other exception means Azurite is not available
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task SkipIfNotAvailableAsync()
|
||||
{
|
||||
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3));
|
||||
bool isAvailable = await IsAvailableAsync(cts.Token);
|
||||
Skip.IfNot(isAvailable, "Azurite / Azure Storage Emulator is not running. Start Azurite to run these tests.");
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>$(ProjectsCoreTargetFrameworks)</TargetFrameworks>
|
||||
<TargetFrameworks Condition="'$(Configuration)' == 'Debug'">$(ProjectsDebugCoreTargetFrameworks)</TargetFrameworks>
|
||||
<InjectSharedIntegrationTestCode>True</InjectSharedIntegrationTestCode>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Xunit.SkippableFact" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Microsoft.Agents.AI.Hosting.AzureStorage\Microsoft.Agents.AI.Hosting.AzureStorage.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.AI;
|
||||
|
||||
namespace Microsoft.Agents.AI.Hosting.AzureStorage.UnitTests.Mock;
|
||||
|
||||
internal sealed class MockChatClient : IChatClient
|
||||
{
|
||||
private int _responsesCounter = 1;
|
||||
|
||||
public ChatClientMetadata? Metadata { get; }
|
||||
|
||||
public async Task<ChatResponse> GetResponseAsync(
|
||||
IEnumerable<ChatMessage> messages,
|
||||
ChatOptions? options = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
await Task.Yield();
|
||||
|
||||
ChatMessage responseMessage = new(ChatRole.Assistant, $"Response #{this._responsesCounter++}");
|
||||
return new ChatResponse([responseMessage]);
|
||||
}
|
||||
|
||||
public async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(
|
||||
IEnumerable<ChatMessage> messages,
|
||||
ChatOptions? options = null,
|
||||
[EnumeratorCancellation] CancellationToken cancellationToken = default)
|
||||
{
|
||||
await Task.Yield();
|
||||
|
||||
var responseText = $"Response #{this._responsesCounter++}";
|
||||
|
||||
yield return new ChatResponseUpdate(ChatRole.Assistant, responseText);
|
||||
}
|
||||
|
||||
public object? GetService(Type serviceType, object? serviceKey = null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public TService? GetService<TService>(object? serviceKey = null)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Agents.AI.Hosting.AzureStorage.UnitTests.Mock;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Xunit.Abstractions;
|
||||
using ThreadStore = Microsoft.Agents.AI.Hosting.AgentThreadStore;
|
||||
|
||||
namespace Microsoft.Agents.AI.Hosting.AzureStorage.UnitTests;
|
||||
|
||||
internal sealed class TestRunner
|
||||
{
|
||||
private int _requestCounter = 1;
|
||||
|
||||
private readonly ITestOutputHelper _testOutputHelper;
|
||||
public AIHostAgent HostAgent { get; }
|
||||
public string ConversationId { get; }
|
||||
|
||||
private TestRunner(ITestOutputHelper testOutputHelper, AIHostAgent hostAgent, string conversationId)
|
||||
{
|
||||
this._testOutputHelper = testOutputHelper;
|
||||
this.HostAgent = hostAgent;
|
||||
this.ConversationId = conversationId;
|
||||
}
|
||||
|
||||
public static TestRunner Initialize(
|
||||
ITestOutputHelper testOutputHelper,
|
||||
ThreadStore threadStore,
|
||||
IChatClient? chatClient = null)
|
||||
{
|
||||
chatClient ??= new MockChatClient();
|
||||
|
||||
var chatClientAgent = new ChatClientAgent(chatClient);
|
||||
var hostAgent = new AIHostAgent(chatClientAgent, threadStore);
|
||||
|
||||
var conversationId = NewConversationId();
|
||||
|
||||
return new(testOutputHelper, hostAgent, conversationId);
|
||||
}
|
||||
|
||||
public Task<HostAgentRunResult> RunAgentAsync(string userMessage)
|
||||
=> this.RunAgentAsync(new ChatMessage(ChatRole.User, userMessage));
|
||||
|
||||
public async Task<HostAgentRunResult> RunAgentAsync(ChatMessage userMessage)
|
||||
{
|
||||
if (userMessage.Contents.FirstOrDefault() is TextContent text)
|
||||
{
|
||||
text.Text = $"Request #{this._requestCounter++}: {text.Text}";
|
||||
}
|
||||
|
||||
AgentThread thread = await this.HostAgent.GetOrCreateThreadAsync(this.ConversationId);
|
||||
var response = await this.HostAgent.RunAsync(thread: thread, messages: [userMessage]);
|
||||
|
||||
await this.HostAgent.SaveThreadAsync(this.ConversationId, thread);
|
||||
this._testOutputHelper.WriteLine($"Saved thread {this.ConversationId}");
|
||||
|
||||
var chatClientAgentThread = thread as ChatClientAgentThread;
|
||||
Assert.NotNull(chatClientAgentThread);
|
||||
Assert.NotNull(chatClientAgentThread.MessageStore);
|
||||
|
||||
var threadMessages = (await chatClientAgentThread.MessageStore.GetMessagesAsync()).ToList();
|
||||
|
||||
return new()
|
||||
{
|
||||
Response = response,
|
||||
Thread = chatClientAgentThread,
|
||||
ThreadMessages = threadMessages
|
||||
};
|
||||
}
|
||||
|
||||
private static string NewConversationId() => Guid.NewGuid().ToString();
|
||||
}
|
||||
|
||||
internal struct HostAgentRunResult
|
||||
{
|
||||
public AgentRunResponse Response { get; init; }
|
||||
public IList<ChatMessage> ResponseMessages => this.Response.Messages;
|
||||
|
||||
public ChatClientAgentThread Thread { get; init; }
|
||||
public IList<ChatMessage> ThreadMessages { get; init; }
|
||||
}
|
||||
+1
-25
@@ -7,29 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [1.0.0b251104] - 2025-11-04
|
||||
|
||||
### Added
|
||||
|
||||
- Introducing the Anthropic Client ([#1819](https://github.com/microsoft/agent-framework/pull/1819))
|
||||
|
||||
### Changed
|
||||
|
||||
- [BREAKING] Consolidate workflow run APIs ([#1723](https://github.com/microsoft/agent-framework/pull/1723))
|
||||
- [BREAKING] Remove request_type param from ctx.request_info() ([#1824](https://github.com/microsoft/agent-framework/pull/1824))
|
||||
- [BREAKING] Cleanup of dependencies ([#1803](https://github.com/microsoft/agent-framework/pull/1803))
|
||||
- [BREAKING] Replace `RequestInfoExecutor` with `request_info` API and `@response_handler` ([#1466](https://github.com/microsoft/agent-framework/pull/1466))
|
||||
- Azure AI Search Support Update + Refactored Samples & Unit Tests ([#1683](https://github.com/microsoft/agent-framework/pull/1683))
|
||||
- Lab: Updates to GAIA module ([#1763](https://github.com/microsoft/agent-framework/pull/1763))
|
||||
|
||||
### Fixed
|
||||
|
||||
- Azure AI `top_p` and `temperature` parameters fix ([#1839](https://github.com/microsoft/agent-framework/pull/1839))
|
||||
- Ensure agent thread is part of checkpoint ([#1756](https://github.com/microsoft/agent-framework/pull/1756))
|
||||
- Fix middleware and cleanup confusing function ([#1865](https://github.com/microsoft/agent-framework/pull/1865))
|
||||
- Fix type compatibility check ([#1753](https://github.com/microsoft/agent-framework/pull/1753))
|
||||
- Fix mcp tool cloning for handoff pattern ([#1883](https://github.com/microsoft/agent-framework/pull/1883))
|
||||
|
||||
## [1.0.0b251028] - 2025-10-28
|
||||
|
||||
### Added
|
||||
@@ -147,8 +124,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
For more information, see the [announcement blog post](https://devblogs.microsoft.com/foundry/introducing-microsoft-agent-framework-the-open-source-engine-for-agentic-ai-apps/).
|
||||
|
||||
[Unreleased]: https://github.com/microsoft/agent-framework/compare/python-1.0.0b251104...HEAD
|
||||
[1.0.0b251104]: https://github.com/microsoft/agent-framework/compare/python-1.0.0b251028...python-1.0.0b251104
|
||||
[Unreleased]: https://github.com/microsoft/agent-framework/compare/python-1.0.0b251028...HEAD
|
||||
[1.0.0b251028]: https://github.com/microsoft/agent-framework/compare/python-1.0.0b251016...python-1.0.0b251028
|
||||
[1.0.0b251016]: https://github.com/microsoft/agent-framework/compare/python-1.0.0b251007...python-1.0.0b251016
|
||||
[1.0.0b251007]: https://github.com/microsoft/agent-framework/compare/python-1.0.0b251001...python-1.0.0b251007
|
||||
|
||||
@@ -4,7 +4,7 @@ description = "A2A integration for Microsoft Agent Framework."
|
||||
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
version = "1.0.0b251104"
|
||||
version = "1.0.0b251028"
|
||||
license-files = ["LICENSE"]
|
||||
urls.homepage = "https://aka.ms/agent-framework"
|
||||
urls.source = "https://github.com/microsoft/agent-framework/tree/main/python"
|
||||
|
||||
@@ -4,7 +4,7 @@ description = "Anthropic integration for Microsoft Agent Framework."
|
||||
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
version = "1.0.0b251104"
|
||||
version = "1.0.0b251028"
|
||||
license-files = ["LICENSE"]
|
||||
urls.homepage = "https://aka.ms/agent-framework"
|
||||
urls.source = "https://github.com/microsoft/agent-framework/tree/main/python"
|
||||
|
||||
@@ -4,7 +4,7 @@ description = "Azure AI Foundry integration for Microsoft Agent Framework."
|
||||
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
version = "1.0.0b251104"
|
||||
version = "1.0.0b251028"
|
||||
license-files = ["LICENSE"]
|
||||
urls.homepage = "https://aka.ms/agent-framework"
|
||||
urls.source = "https://github.com/microsoft/agent-framework/tree/main/python"
|
||||
|
||||
@@ -4,7 +4,7 @@ description = "Copilot Studio integration for Microsoft Agent Framework."
|
||||
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
version = "1.0.0b251104"
|
||||
version = "1.0.0b251028"
|
||||
license-files = ["LICENSE"]
|
||||
urls.homepage = "https://aka.ms/agent-framework"
|
||||
urls.source = "https://github.com/microsoft/agent-framework/tree/main/python"
|
||||
|
||||
@@ -80,14 +80,6 @@ def _clone_chat_agent(agent: ChatAgent) -> ChatAgent:
|
||||
options = agent.chat_options
|
||||
middleware = list(agent.middleware or [])
|
||||
|
||||
# Reconstruct the original tools list by combining regular tools with MCP tools.
|
||||
# ChatAgent.__init__ separates MCP tools into _local_mcp_tools during initialization,
|
||||
# so we need to recombine them here to pass the complete tools list to the constructor.
|
||||
# This makes sure MCP tools are preserved when cloning agents for handoff workflows.
|
||||
all_tools = list(options.tools) if options.tools else []
|
||||
if agent._local_mcp_tools:
|
||||
all_tools.extend(agent._local_mcp_tools)
|
||||
|
||||
return ChatAgent(
|
||||
chat_client=agent.chat_client,
|
||||
instructions=options.instructions,
|
||||
@@ -109,7 +101,7 @@ def _clone_chat_agent(agent: ChatAgent) -> ChatAgent:
|
||||
store=options.store,
|
||||
temperature=options.temperature,
|
||||
tool_choice=options.tool_choice, # type: ignore[arg-type]
|
||||
tools=all_tools if all_tools else None,
|
||||
tools=list(options.tools) if options.tools else None,
|
||||
top_p=options.top_p,
|
||||
user=options.user,
|
||||
additional_chat_options=dict(options.additional_properties),
|
||||
|
||||
@@ -4,7 +4,7 @@ description = "Microsoft Agent Framework for building AI Agents with Python. Thi
|
||||
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
version = "1.0.0b251104"
|
||||
version = "1.0.0b251028"
|
||||
license-files = ["LICENSE"]
|
||||
urls.homepage = "https://aka.ms/agent-framework"
|
||||
urls.source = "https://github.com/microsoft/agent-framework/tree/main/python"
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
from collections.abc import AsyncIterable, AsyncIterator
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, cast
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -11,7 +10,6 @@ from agent_framework import (
|
||||
AgentRunResponse,
|
||||
AgentRunResponseUpdate,
|
||||
BaseAgent,
|
||||
ChatAgent,
|
||||
ChatMessage,
|
||||
FunctionCallContent,
|
||||
HandoffBuilder,
|
||||
@@ -22,8 +20,6 @@ from agent_framework import (
|
||||
WorkflowEvent,
|
||||
WorkflowOutputEvent,
|
||||
)
|
||||
from agent_framework._mcp import MCPTool
|
||||
from agent_framework._workflows._handoff import _clone_chat_agent
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -372,32 +368,3 @@ async def test_handoff_async_termination_condition() -> None:
|
||||
user_messages = [msg for msg in final_conv_list if msg.role == Role.USER]
|
||||
assert len(user_messages) == 2
|
||||
assert termination_call_count > 0
|
||||
|
||||
|
||||
async def test_clone_chat_agent_preserves_mcp_tools() -> None:
|
||||
"""Test that _clone_chat_agent preserves MCP tools when cloning an agent."""
|
||||
mock_chat_client = MagicMock()
|
||||
|
||||
mock_mcp_tool = MagicMock(spec=MCPTool)
|
||||
mock_mcp_tool.name = "test_mcp_tool"
|
||||
|
||||
def sample_function() -> str:
|
||||
return "test"
|
||||
|
||||
original_agent = ChatAgent(
|
||||
chat_client=mock_chat_client,
|
||||
name="TestAgent",
|
||||
instructions="Test instructions",
|
||||
tools=[mock_mcp_tool, sample_function],
|
||||
)
|
||||
|
||||
assert hasattr(original_agent, "_local_mcp_tools")
|
||||
assert len(original_agent._local_mcp_tools) == 1
|
||||
assert original_agent._local_mcp_tools[0] == mock_mcp_tool
|
||||
|
||||
cloned_agent = _clone_chat_agent(original_agent)
|
||||
|
||||
assert hasattr(cloned_agent, "_local_mcp_tools")
|
||||
assert len(cloned_agent._local_mcp_tools) == 1
|
||||
assert cloned_agent._local_mcp_tools[0] == mock_mcp_tool
|
||||
assert len(cloned_agent.chat_options.tools) == 1
|
||||
|
||||
@@ -4,7 +4,7 @@ description = "Debug UI for Microsoft Agent Framework with OpenAI-compatible API
|
||||
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
version = "1.0.0b251104"
|
||||
version = "1.0.0b251028"
|
||||
license-files = ["LICENSE"]
|
||||
urls.homepage = "https://github.com/microsoft/agent-framework"
|
||||
urls.source = "https://github.com/microsoft/agent-framework/tree/main/python"
|
||||
|
||||
@@ -4,7 +4,7 @@ description = "Experimental modules for Microsoft Agent Framework"
|
||||
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
version = "1.0.0b251104"
|
||||
version = "1.0.0b251028"
|
||||
license-files = ["LICENSE"]
|
||||
urls.homepage = "https://aka.ms/agent-framework"
|
||||
urls.source = "https://github.com/microsoft/agent-framework/tree/main/python"
|
||||
|
||||
@@ -4,7 +4,7 @@ description = "Mem0 integration for Microsoft Agent Framework."
|
||||
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
version = "1.0.0b251104"
|
||||
version = "1.0.0b251028"
|
||||
license-files = ["LICENSE"]
|
||||
urls.homepage = "https://aka.ms/agent-framework"
|
||||
urls.source = "https://github.com/microsoft/agent-framework/tree/main/python"
|
||||
|
||||
@@ -4,7 +4,7 @@ description = "Microsoft Purview (Graph dataSecurityAndGovernance) integration f
|
||||
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
version = "1.0.0b251104"
|
||||
version = "1.0.0b251028"
|
||||
license-files = ["LICENSE"]
|
||||
urls.homepage = "https://github.com/microsoft/agent-framework"
|
||||
urls.source = "https://github.com/microsoft/agent-framework/tree/main/python"
|
||||
|
||||
@@ -4,7 +4,7 @@ description = "Redis integration for Microsoft Agent Framework."
|
||||
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
version = "1.0.0b251104"
|
||||
version = "1.0.0b251028"
|
||||
license-files = ["LICENSE"]
|
||||
urls.homepage = "https://aka.ms/agent-framework"
|
||||
urls.source = "https://github.com/microsoft/agent-framework/tree/main/python"
|
||||
|
||||
@@ -4,7 +4,7 @@ description = "Microsoft Agent Framework for building AI Agents with Python. Thi
|
||||
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
version = "1.0.0b251104"
|
||||
version = "1.0.0b251028"
|
||||
license-files = ["LICENSE"]
|
||||
urls.homepage = "https://aka.ms/agent-framework"
|
||||
urls.source = "https://github.com/microsoft/agent-framework/tree/main/python"
|
||||
|
||||
Generated
+11
-21
@@ -73,7 +73,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "agent-framework"
|
||||
version = "1.0.0b251104"
|
||||
version = "1.0.0b251028"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "agent-framework-a2a", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
@@ -160,7 +160,7 @@ docs = [
|
||||
|
||||
[[package]]
|
||||
name = "agent-framework-a2a"
|
||||
version = "1.0.0b251104"
|
||||
version = "1.0.0b251028"
|
||||
source = { editable = "packages/a2a" }
|
||||
dependencies = [
|
||||
{ name = "a2a-sdk", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
@@ -175,7 +175,7 @@ requires-dist = [
|
||||
|
||||
[[package]]
|
||||
name = "agent-framework-anthropic"
|
||||
version = "1.0.0b251104"
|
||||
version = "1.0.0b251028"
|
||||
source = { editable = "packages/anthropic" }
|
||||
dependencies = [
|
||||
{ name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
@@ -190,7 +190,7 @@ requires-dist = [
|
||||
|
||||
[[package]]
|
||||
name = "agent-framework-azure-ai"
|
||||
version = "1.0.0b251104"
|
||||
version = "1.0.0b251028"
|
||||
source = { editable = "packages/azure-ai" }
|
||||
dependencies = [
|
||||
{ name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
@@ -207,7 +207,7 @@ requires-dist = [
|
||||
|
||||
[[package]]
|
||||
name = "agent-framework-copilotstudio"
|
||||
version = "1.0.0b251104"
|
||||
version = "1.0.0b251028"
|
||||
source = { editable = "packages/copilotstudio" }
|
||||
dependencies = [
|
||||
{ name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
@@ -222,7 +222,7 @@ requires-dist = [
|
||||
|
||||
[[package]]
|
||||
name = "agent-framework-core"
|
||||
version = "1.0.0b251104"
|
||||
version = "1.0.0b251028"
|
||||
source = { editable = "packages/core" }
|
||||
dependencies = [
|
||||
{ name = "azure-identity", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
@@ -274,7 +274,7 @@ provides-extras = ["all"]
|
||||
|
||||
[[package]]
|
||||
name = "agent-framework-devui"
|
||||
version = "1.0.0b251104"
|
||||
version = "1.0.0b251028"
|
||||
source = { editable = "packages/devui" }
|
||||
dependencies = [
|
||||
{ name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
@@ -308,7 +308,7 @@ provides-extras = ["dev", "all"]
|
||||
|
||||
[[package]]
|
||||
name = "agent-framework-lab"
|
||||
version = "1.0.0b251104"
|
||||
version = "1.0.0b251028"
|
||||
source = { editable = "packages/lab" }
|
||||
dependencies = [
|
||||
{ name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
@@ -399,7 +399,7 @@ dev = [
|
||||
|
||||
[[package]]
|
||||
name = "agent-framework-mem0"
|
||||
version = "1.0.0b251104"
|
||||
version = "1.0.0b251028"
|
||||
source = { editable = "packages/mem0" }
|
||||
dependencies = [
|
||||
{ name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
@@ -414,7 +414,7 @@ requires-dist = [
|
||||
|
||||
[[package]]
|
||||
name = "agent-framework-purview"
|
||||
version = "1.0.0b251104"
|
||||
version = "1.0.0b251028"
|
||||
source = { editable = "packages/purview" }
|
||||
dependencies = [
|
||||
{ name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
@@ -431,7 +431,7 @@ requires-dist = [
|
||||
|
||||
[[package]]
|
||||
name = "agent-framework-redis"
|
||||
version = "1.0.0b251104"
|
||||
version = "1.0.0b251028"
|
||||
source = { editable = "packages/redis" }
|
||||
dependencies = [
|
||||
{ name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
@@ -2039,8 +2039,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7f/91/ae2eb6b7979e2f9b035a9f612cf70f1bf54aad4e1d125129bef1eae96f19/greenlet-3.2.4-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c2ca18a03a8cfb5b25bc1cbe20f3d9a4c80d8c3b13ba3df49ac3961af0b1018d", size = 584358, upload-time = "2025-08-07T13:18:23.708Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f7/85/433de0c9c0252b22b16d413c9407e6cb3b41df7389afc366ca204dbc1393/greenlet-3.2.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9fe0a28a7b952a21e2c062cd5756d34354117796c6d9215a87f55e38d15402c5", size = 1113550, upload-time = "2025-08-07T13:42:37.467Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a1/8d/88f3ebd2bc96bf7747093696f4335a0a8a4c5acfcf1b757717c0d2474ba3/greenlet-3.2.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8854167e06950ca75b898b104b63cc646573aa5fef1353d4508ecdd1ee76254f", size = 1137126, upload-time = "2025-08-07T13:18:20.239Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/29/74242b7d72385e29bcc5563fba67dad94943d7cd03552bac320d597f29b2/greenlet-3.2.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f47617f698838ba98f4ff4189aef02e7343952df3a615f847bb575c3feb177a7", size = 1544904, upload-time = "2025-11-04T12:42:04.763Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/e2/1572b8eeab0f77df5f6729d6ab6b141e4a84ee8eb9bc8c1e7918f94eda6d/greenlet-3.2.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af41be48a4f60429d5cad9d22175217805098a9ef7c40bfef44f7669fb9d74d8", size = 1611228, upload-time = "2025-11-04T12:42:08.423Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/6f/b60b0291d9623c496638c582297ead61f43c4b72eef5e9c926ef4565ec13/greenlet-3.2.4-cp310-cp310-win_amd64.whl", hash = "sha256:73f49b5368b5359d04e18d15828eecc1806033db5233397748f4ca813ff1056c", size = 298654, upload-time = "2025-08-07T13:50:00.469Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a4/de/f28ced0a67749cac23fecb02b694f6473f47686dff6afaa211d186e2ef9c/greenlet-3.2.4-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:96378df1de302bc38e99c3a9aa311967b7dc80ced1dcc6f171e99842987882a2", size = 272305, upload-time = "2025-08-07T13:15:41.288Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/09/16/2c3792cba130000bf2a31c5272999113f4764fd9d874fb257ff588ac779a/greenlet-3.2.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1ee8fae0519a337f2329cb78bd7a8e128ec0f881073d43f023c7b8d4831d5246", size = 632472, upload-time = "2025-08-07T13:42:55.044Z" },
|
||||
@@ -2050,8 +2048,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1f/8e/abdd3f14d735b2929290a018ecf133c901be4874b858dd1c604b9319f064/greenlet-3.2.4-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2523e5246274f54fdadbce8494458a2ebdcdbc7b802318466ac5606d3cded1f8", size = 587684, upload-time = "2025-08-07T13:18:25.164Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/65/deb2a69c3e5996439b0176f6651e0052542bb6c8f8ec2e3fba97c9768805/greenlet-3.2.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1987de92fec508535687fb807a5cea1560f6196285a4cde35c100b8cd632cc52", size = 1116647, upload-time = "2025-08-07T13:42:38.655Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3f/cc/b07000438a29ac5cfb2194bfc128151d52f333cee74dd7dfe3fb733fc16c/greenlet-3.2.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:55e9c5affaa6775e2c6b67659f3a71684de4c549b3dd9afca3bc773533d284fa", size = 1142073, upload-time = "2025-08-07T13:18:21.737Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/24/28a5b2fa42d12b3d7e5614145f0bd89714c34c08be6aabe39c14dd52db34/greenlet-3.2.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c9c6de1940a7d828635fbd254d69db79e54619f165ee7ce32fda763a9cb6a58c", size = 1548385, upload-time = "2025-11-04T12:42:11.067Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/05/03f2f0bdd0b0ff9a4f7b99333d57b53a7709c27723ec8123056b084e69cd/greenlet-3.2.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:03c5136e7be905045160b1b9fdca93dd6727b180feeafda6818e6496434ed8c5", size = 1613329, upload-time = "2025-11-04T12:42:12.928Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d8/0f/30aef242fcab550b0b3520b8e3561156857c94288f0332a79928c31a52cf/greenlet-3.2.4-cp311-cp311-win_amd64.whl", hash = "sha256:9c40adce87eaa9ddb593ccb0fa6a07caf34015a29bf8d344811665b573138db9", size = 299100, upload-time = "2025-08-07T13:44:12.287Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/69/9b804adb5fd0671f367781560eb5eb586c4d495277c93bde4307b9e28068/greenlet-3.2.4-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3b67ca49f54cede0186854a008109d6ee71f66bd57bb36abd6d0a0267b540cdd", size = 274079, upload-time = "2025-08-07T13:15:45.033Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/46/e9/d2a80c99f19a153eff70bc451ab78615583b8dac0754cfb942223d2c1a0d/greenlet-3.2.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ddf9164e7a5b08e9d22511526865780a576f19ddd00d62f8a665949327fde8bb", size = 640997, upload-time = "2025-08-07T13:42:56.234Z" },
|
||||
@@ -2061,8 +2057,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/19/0d/6660d55f7373b2ff8152401a83e02084956da23ae58cddbfb0b330978fe9/greenlet-3.2.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b3812d8d0c9579967815af437d96623f45c0f2ae5f04e366de62a12d83a8fb0", size = 607586, upload-time = "2025-08-07T13:18:28.544Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8e/1a/c953fdedd22d81ee4629afbb38d2f9d71e37d23caace44775a3a969147d4/greenlet-3.2.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:abbf57b5a870d30c4675928c37278493044d7c14378350b3aa5d484fa65575f0", size = 1123281, upload-time = "2025-08-07T13:42:39.858Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3f/c7/12381b18e21aef2c6bd3a636da1088b888b97b7a0362fac2e4de92405f97/greenlet-3.2.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:20fb936b4652b6e307b8f347665e2c615540d4b42b3b4c8a321d8286da7e520f", size = 1151142, upload-time = "2025-08-07T13:18:22.981Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/27/45/80935968b53cfd3f33cf99ea5f08227f2646e044568c9b1555b58ffd61c2/greenlet-3.2.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ee7a6ec486883397d70eec05059353b8e83eca9168b9f3f9a361971e77e0bcd0", size = 1564846, upload-time = "2025-11-04T12:42:15.191Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/69/02/b7c30e5e04752cb4db6202a3858b149c0710e5453b71a3b2aec5d78a1aab/greenlet-3.2.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:326d234cbf337c9c3def0676412eb7040a35a768efc92504b947b3e9cfc7543d", size = 1633814, upload-time = "2025-11-04T12:42:17.175Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/08/b0814846b79399e585f974bbeebf5580fbe59e258ea7be64d9dfb253c84f/greenlet-3.2.4-cp312-cp312-win_amd64.whl", hash = "sha256:a7d4e128405eea3814a12cc2605e0e6aedb4035bf32697f72deca74de4105e02", size = 299899, upload-time = "2025-08-07T13:38:53.448Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/49/e8/58c7f85958bda41dafea50497cbd59738c5c43dbbea5ee83d651234398f4/greenlet-3.2.4-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:1a921e542453fe531144e91e1feedf12e07351b1cf6c9e8a3325ea600a715a31", size = 272814, upload-time = "2025-08-07T13:15:50.011Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/62/dd/b9f59862e9e257a16e4e610480cfffd29e3fae018a68c2332090b53aac3d/greenlet-3.2.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd3c8e693bff0fff6ba55f140bf390fa92c994083f838fece0f63be121334945", size = 641073, upload-time = "2025-08-07T13:42:57.23Z" },
|
||||
@@ -2072,8 +2066,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ee/43/3cecdc0349359e1a527cbf2e3e28e5f8f06d3343aaf82ca13437a9aa290f/greenlet-3.2.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23768528f2911bcd7e475210822ffb5254ed10d71f4028387e5a99b4c6699671", size = 610497, upload-time = "2025-08-07T13:18:31.636Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/19/06b6cf5d604e2c382a6f31cafafd6f33d5dea706f4db7bdab184bad2b21d/greenlet-3.2.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:00fadb3fedccc447f517ee0d3fd8fe49eae949e1cd0f6a611818f4f6fb7dc83b", size = 1121662, upload-time = "2025-08-07T13:42:41.117Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a2/15/0d5e4e1a66fab130d98168fe984c509249c833c1a3c16806b90f253ce7b9/greenlet-3.2.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d25c5091190f2dc0eaa3f950252122edbbadbb682aa7b1ef2f8af0f8c0afefae", size = 1149210, upload-time = "2025-08-07T13:18:24.072Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1c/53/f9c440463b3057485b8594d7a638bed53ba531165ef0ca0e6c364b5cc807/greenlet-3.2.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e343822feb58ac4d0a1211bd9399de2b3a04963ddeec21530fc426cc121f19b", size = 1564759, upload-time = "2025-11-04T12:42:19.395Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/47/e4/3bb4240abdd0a8d23f4f88adec746a3099f0d86bfedb623f063b2e3b4df0/greenlet-3.2.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca7f6f1f2649b89ce02f6f229d7c19f680a6238af656f61e0115b24857917929", size = 1634288, upload-time = "2025-11-04T12:42:21.174Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0b/55/2321e43595e6801e105fcfdee02b34c0f996eb71e6ddffca6b10b7e1d771/greenlet-3.2.4-cp313-cp313-win_amd64.whl", hash = "sha256:554b03b6e73aaabec3745364d6239e9e012d64c68ccd0b8430c64ccc14939a8b", size = 299685, upload-time = "2025-08-07T13:24:38.824Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/5c/85273fd7cc388285632b0498dbbab97596e04b154933dfe0f3e68156c68c/greenlet-3.2.4-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:49a30d5fda2507ae77be16479bdb62a660fa51b1eb4928b524975b3bde77b3c0", size = 273586, upload-time = "2025-08-07T13:16:08.004Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/75/10aeeaa3da9332c2e761e4c50d4c3556c21113ee3f0afa2cf5769946f7a3/greenlet-3.2.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:299fd615cd8fc86267b47597123e3f43ad79c9d8a22bebdce535e53550763e2f", size = 686346, upload-time = "2025-08-07T13:42:59.944Z" },
|
||||
@@ -2081,8 +2073,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/dc/8b/29aae55436521f1d6f8ff4e12fb676f3400de7fcf27fccd1d4d17fd8fecd/greenlet-3.2.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b4a1870c51720687af7fa3e7cda6d08d801dae660f75a76f3845b642b4da6ee1", size = 694659, upload-time = "2025-08-07T13:53:17.759Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/2e/ea25914b1ebfde93b6fc4ff46d6864564fba59024e928bdc7de475affc25/greenlet-3.2.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:061dc4cf2c34852b052a8620d40f36324554bc192be474b9e9770e8c042fd735", size = 695355, upload-time = "2025-08-07T13:18:34.517Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/72/60/fc56c62046ec17f6b0d3060564562c64c862948c9d4bc8aa807cf5bd74f4/greenlet-3.2.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44358b9bf66c8576a9f57a590d5f5d6e72fa4228b763d0e43fee6d3b06d3a337", size = 657512, upload-time = "2025-08-07T13:18:33.969Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/6e/74407aed965a4ab6ddd93a7ded3180b730d281c77b765788419484cdfeef/greenlet-3.2.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2917bdf657f5859fbf3386b12d68ede4cf1f04c90c3a6bc1f013dd68a22e2269", size = 1612508, upload-time = "2025-11-04T12:42:23.427Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/da/343cd760ab2f92bac1845ca07ee3faea9fe52bee65f7bcb19f16ad7de08b/greenlet-3.2.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:015d48959d4add5d6c9f6c5210ee3803a830dce46356e3bc326d6776bde54681", size = 1680760, upload-time = "2025-11-04T12:42:25.341Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e3/a5/6ddab2b4c112be95601c13428db1d8b6608a8b6039816f2ba09c346c08fc/greenlet-3.2.4-cp314-cp314-win_amd64.whl", hash = "sha256:e37ab26028f12dbb0ff65f29a8d3d44a765c61e729647bf2ddfbbed621726f01", size = 303425, upload-time = "2025-08-07T13:32:27.59Z" },
|
||||
]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user