mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
348ac764e6 | ||
|
|
e8243b7d11 | ||
|
|
4b0f724e62 | ||
|
+20 |
edb367a2b9 | ||
|
|
4bffe1ebc8 |
@@ -2,6 +2,7 @@
|
||||
|
||||
// This sample demonstrates basic usage of the DevUI in an ASP.NET Core application with AI agents.
|
||||
|
||||
using System.ComponentModel;
|
||||
using Azure.AI.OpenAI;
|
||||
using Azure.Identity;
|
||||
using Microsoft.Agents.AI;
|
||||
@@ -18,10 +19,11 @@ namespace DevUI_Step01_BasicUsage;
|
||||
/// <remarks>
|
||||
/// This sample shows how to:
|
||||
/// 1. Set up Azure OpenAI as the chat client
|
||||
/// 2. Register agents and workflows using the hosting packages
|
||||
/// 3. Map the DevUI endpoint which automatically configures the middleware
|
||||
/// 4. Map the dynamic OpenAI Responses API for Python DevUI compatibility
|
||||
/// 5. Access the DevUI in a web browser
|
||||
/// 2. Create function tools for agents to use
|
||||
/// 3. Register agents and workflows using the hosting packages with tools
|
||||
/// 4. Map the DevUI endpoint which automatically configures the middleware
|
||||
/// 5. Map the dynamic OpenAI Responses API for Python DevUI compatibility
|
||||
/// 6. Access the DevUI in a web browser
|
||||
///
|
||||
/// The DevUI provides an interactive web interface for testing and debugging AI agents.
|
||||
/// DevUI assets are served from embedded resources within the assembly.
|
||||
@@ -50,10 +52,30 @@ internal static class Program
|
||||
|
||||
builder.Services.AddChatClient(chatClient);
|
||||
|
||||
// Register sample agents
|
||||
builder.AddAIAgent("assistant", "You are a helpful assistant. Answer questions concisely and accurately.");
|
||||
// Define some example tools
|
||||
[Description("Get the weather for a given location.")]
|
||||
static string GetWeather([Description("The location to get the weather for.")] string location)
|
||||
=> $"The weather in {location} is cloudy with a high of 15°C.";
|
||||
|
||||
[Description("Calculate the sum of two numbers.")]
|
||||
static double Add([Description("The first number.")] double a, [Description("The second number.")] double b)
|
||||
=> a + b;
|
||||
|
||||
[Description("Get the current time.")]
|
||||
static string GetCurrentTime()
|
||||
=> DateTime.Now.ToString("HH:mm:ss");
|
||||
|
||||
// Register sample agents with tools
|
||||
builder.AddAIAgent("assistant", "You are a helpful assistant. Answer questions concisely and accurately.")
|
||||
.WithAITools(
|
||||
AIFunctionFactory.Create(GetWeather, name: "get_weather"),
|
||||
AIFunctionFactory.Create(GetCurrentTime, name: "get_current_time")
|
||||
);
|
||||
|
||||
builder.AddAIAgent("poet", "You are a creative poet. Respond to all requests with beautiful poetry.");
|
||||
builder.AddAIAgent("coder", "You are an expert programmer. Help users with coding questions and provide code examples.");
|
||||
|
||||
builder.AddAIAgent("coder", "You are an expert programmer. Help users with coding questions and provide code examples.")
|
||||
.WithAITool(AIFunctionFactory.Create(Add, name: "add"));
|
||||
|
||||
// Register sample workflows
|
||||
var assistantBuilder = builder.AddAIAgent("workflow-assistant", "You are a helpful assistant in a workflow.");
|
||||
|
||||
@@ -18,9 +18,13 @@ namespace Microsoft.Agents.AI.DevUI.Entities;
|
||||
[JsonSerializable(typeof(MetaResponse))]
|
||||
[JsonSerializable(typeof(EnvVarRequirement))]
|
||||
[JsonSerializable(typeof(List<EntityInfo>))]
|
||||
[JsonSerializable(typeof(List<JsonElement>))]
|
||||
[JsonSerializable(typeof(List<Dictionary<string, JsonElement>>))]
|
||||
[JsonSerializable(typeof(List<Dictionary<string, string>>))]
|
||||
[JsonSerializable(typeof(Dictionary<string, JsonElement>))]
|
||||
[JsonSerializable(typeof(Dictionary<string, bool>))]
|
||||
[JsonSerializable(typeof(Dictionary<string, Dictionary<string, string>>))]
|
||||
[JsonSerializable(typeof(Dictionary<string, string>))]
|
||||
[JsonSerializable(typeof(JsonElement))]
|
||||
[JsonSerializable(typeof(string))]
|
||||
[JsonSerializable(typeof(int))]
|
||||
[ExcludeFromCodeCoverage]
|
||||
internal sealed partial class EntitiesJsonContext : JsonSerializerContext;
|
||||
|
||||
@@ -36,16 +36,16 @@ internal sealed record EntityInfo(
|
||||
string Name,
|
||||
|
||||
[property: JsonPropertyName("description")]
|
||||
string? Description = null,
|
||||
string? Description,
|
||||
|
||||
[property: JsonPropertyName("framework")]
|
||||
string Framework = "dotnet",
|
||||
string Framework,
|
||||
|
||||
[property: JsonPropertyName("tools")]
|
||||
List<string>? Tools = null,
|
||||
List<string> Tools,
|
||||
|
||||
[property: JsonPropertyName("metadata")]
|
||||
Dictionary<string, JsonElement>? Metadata = null
|
||||
Dictionary<string, JsonElement> Metadata
|
||||
)
|
||||
{
|
||||
[JsonPropertyName("source")]
|
||||
@@ -54,6 +54,32 @@ internal sealed record EntityInfo(
|
||||
[JsonPropertyName("original_url")]
|
||||
public string? OriginalUrl { get; init; }
|
||||
|
||||
// Deployment support
|
||||
[JsonPropertyName("deployment_supported")]
|
||||
public bool DeploymentSupported { get; init; }
|
||||
|
||||
[JsonPropertyName("deployment_reason")]
|
||||
public string? DeploymentReason { get; init; }
|
||||
|
||||
// Agent-specific fields
|
||||
[JsonPropertyName("instructions")]
|
||||
public string? Instructions { get; init; }
|
||||
|
||||
[JsonPropertyName("model_id")]
|
||||
public string? ModelId { get; init; }
|
||||
|
||||
[JsonPropertyName("chat_client_type")]
|
||||
public string? ChatClientType { get; init; }
|
||||
|
||||
[JsonPropertyName("context_providers")]
|
||||
public List<string>? ContextProviders { get; init; }
|
||||
|
||||
[JsonPropertyName("middleware")]
|
||||
public List<string>? Middleware { get; init; }
|
||||
|
||||
[JsonPropertyName("module_path")]
|
||||
public string? ModulePath { get; init; }
|
||||
|
||||
// Workflow-specific fields
|
||||
[JsonPropertyName("required_env_vars")]
|
||||
public List<EnvVarRequirement>? RequiredEnvVars { get; init; }
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization.Metadata;
|
||||
using Microsoft.Agents.AI.Workflows;
|
||||
using Microsoft.Agents.AI.Workflows.Checkpointing;
|
||||
|
||||
@@ -17,31 +19,37 @@ internal static class WorkflowSerializationExtensions
|
||||
/// Converts a workflow to a dictionary representation compatible with DevUI frontend.
|
||||
/// This matches the Python workflow.to_dict() format expected by the UI.
|
||||
/// </summary>
|
||||
public static Dictionary<string, object> ToDevUIDict(this Workflow workflow)
|
||||
/// <param name="workflow">The workflow to convert.</param>
|
||||
/// <returns>A dictionary with string keys and JsonElement values containing the workflow data.</returns>
|
||||
public static Dictionary<string, JsonElement> ToDevUIDict(this Workflow workflow)
|
||||
{
|
||||
var result = new Dictionary<string, object>
|
||||
var result = new Dictionary<string, JsonElement>
|
||||
{
|
||||
["id"] = workflow.Name ?? Guid.NewGuid().ToString(),
|
||||
["start_executor_id"] = workflow.StartExecutorId,
|
||||
["max_iterations"] = MaxIterationsDefault
|
||||
["id"] = Serialize(workflow.Name ?? Guid.NewGuid().ToString(), EntitiesJsonContext.Default.String),
|
||||
["start_executor_id"] = Serialize(workflow.StartExecutorId, EntitiesJsonContext.Default.String),
|
||||
["max_iterations"] = Serialize(MaxIterationsDefault, EntitiesJsonContext.Default.Int32)
|
||||
};
|
||||
|
||||
// Add optional fields
|
||||
if (!string.IsNullOrEmpty(workflow.Name))
|
||||
{
|
||||
result["name"] = workflow.Name;
|
||||
result["name"] = Serialize(workflow.Name, EntitiesJsonContext.Default.String);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(workflow.Description))
|
||||
{
|
||||
result["description"] = workflow.Description;
|
||||
result["description"] = Serialize(workflow.Description, EntitiesJsonContext.Default.String);
|
||||
}
|
||||
|
||||
// Convert executors to Python-compatible format
|
||||
result["executors"] = ConvertExecutorsToDict(workflow);
|
||||
result["executors"] = Serialize(
|
||||
ConvertExecutorsToDict(workflow),
|
||||
EntitiesJsonContext.Default.DictionaryStringDictionaryStringString);
|
||||
|
||||
// Convert edges to edge_groups format
|
||||
result["edge_groups"] = ConvertEdgesToEdgeGroups(workflow);
|
||||
result["edge_groups"] = Serialize(
|
||||
ConvertEdgesToEdgeGroups(workflow),
|
||||
EntitiesJsonContext.Default.ListDictionaryStringJsonElement);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -49,9 +57,9 @@ internal static class WorkflowSerializationExtensions
|
||||
/// <summary>
|
||||
/// Converts workflow executors to a dictionary format compatible with Python
|
||||
/// </summary>
|
||||
private static Dictionary<string, object> ConvertExecutorsToDict(Workflow workflow)
|
||||
private static Dictionary<string, Dictionary<string, string>> ConvertExecutorsToDict(Workflow workflow)
|
||||
{
|
||||
var executors = new Dictionary<string, object>();
|
||||
var executors = new Dictionary<string, Dictionary<string, string>>();
|
||||
|
||||
// Extract executor IDs from edges and start executor
|
||||
// (Registrations is internal, so we infer executors from the graph structure)
|
||||
@@ -73,7 +81,7 @@ internal static class WorkflowSerializationExtensions
|
||||
// Create executor entries (we can't access internal Registrations for type info)
|
||||
foreach (var executorId in executorIds)
|
||||
{
|
||||
executors[executorId] = new Dictionary<string, object>
|
||||
executors[executorId] = new Dictionary<string, string>
|
||||
{
|
||||
["id"] = executorId,
|
||||
["type"] = "Executor"
|
||||
@@ -86,9 +94,9 @@ internal static class WorkflowSerializationExtensions
|
||||
/// <summary>
|
||||
/// Converts workflow edges to edge_groups format expected by the UI
|
||||
/// </summary>
|
||||
private static List<object> ConvertEdgesToEdgeGroups(Workflow workflow)
|
||||
private static List<Dictionary<string, JsonElement>> ConvertEdgesToEdgeGroups(Workflow workflow)
|
||||
{
|
||||
var edgeGroups = new List<object>();
|
||||
var edgeGroups = new List<Dictionary<string, JsonElement>>();
|
||||
var edgeGroupId = 0;
|
||||
|
||||
// Get edges using the public ReflectEdges method
|
||||
@@ -101,13 +109,13 @@ internal static class WorkflowSerializationExtensions
|
||||
if (edgeInfo is DirectEdgeInfo directEdge)
|
||||
{
|
||||
// Single edge group for direct edges
|
||||
var edges = new List<object>();
|
||||
var edges = new List<Dictionary<string, string>>();
|
||||
|
||||
foreach (var source in directEdge.Connection.SourceIds)
|
||||
{
|
||||
foreach (var sink in directEdge.Connection.SinkIds)
|
||||
{
|
||||
var edge = new Dictionary<string, object>
|
||||
var edge = new Dictionary<string, string>
|
||||
{
|
||||
["source_id"] = source,
|
||||
["target_id"] = sink
|
||||
@@ -123,23 +131,25 @@ internal static class WorkflowSerializationExtensions
|
||||
}
|
||||
}
|
||||
|
||||
edgeGroups.Add(new Dictionary<string, object>
|
||||
var edgeGroup = new Dictionary<string, JsonElement>
|
||||
{
|
||||
["id"] = $"edge_group_{edgeGroupId++}",
|
||||
["type"] = "SingleEdgeGroup",
|
||||
["edges"] = edges
|
||||
});
|
||||
["id"] = Serialize($"edge_group_{edgeGroupId++}", EntitiesJsonContext.Default.String),
|
||||
["type"] = Serialize("SingleEdgeGroup", EntitiesJsonContext.Default.String),
|
||||
["edges"] = Serialize(edges, EntitiesJsonContext.Default.ListDictionaryStringString)
|
||||
};
|
||||
|
||||
edgeGroups.Add(edgeGroup);
|
||||
}
|
||||
else if (edgeInfo is FanOutEdgeInfo fanOutEdge)
|
||||
{
|
||||
// FanOut edge group
|
||||
var edges = new List<object>();
|
||||
var edges = new List<Dictionary<string, string>>();
|
||||
|
||||
foreach (var source in fanOutEdge.Connection.SourceIds)
|
||||
{
|
||||
foreach (var sink in fanOutEdge.Connection.SinkIds)
|
||||
{
|
||||
edges.Add(new Dictionary<string, object>
|
||||
edges.Add(new Dictionary<string, string>
|
||||
{
|
||||
["source_id"] = source,
|
||||
["target_id"] = sink
|
||||
@@ -147,16 +157,16 @@ internal static class WorkflowSerializationExtensions
|
||||
}
|
||||
}
|
||||
|
||||
var fanOutGroup = new Dictionary<string, object>
|
||||
var fanOutGroup = new Dictionary<string, JsonElement>
|
||||
{
|
||||
["id"] = $"edge_group_{edgeGroupId++}",
|
||||
["type"] = "FanOutEdgeGroup",
|
||||
["edges"] = edges
|
||||
["id"] = Serialize($"edge_group_{edgeGroupId++}", EntitiesJsonContext.Default.String),
|
||||
["type"] = Serialize("FanOutEdgeGroup", EntitiesJsonContext.Default.String),
|
||||
["edges"] = Serialize(edges, EntitiesJsonContext.Default.ListDictionaryStringString)
|
||||
};
|
||||
|
||||
if (fanOutEdge.HasAssigner)
|
||||
{
|
||||
fanOutGroup["selection_func_name"] = "selector";
|
||||
fanOutGroup["selection_func_name"] = Serialize("selector", EntitiesJsonContext.Default.String);
|
||||
}
|
||||
|
||||
edgeGroups.Add(fanOutGroup);
|
||||
@@ -164,13 +174,13 @@ internal static class WorkflowSerializationExtensions
|
||||
else if (edgeInfo is FanInEdgeInfo fanInEdge)
|
||||
{
|
||||
// FanIn edge group
|
||||
var edges = new List<object>();
|
||||
var edges = new List<Dictionary<string, string>>();
|
||||
|
||||
foreach (var source in fanInEdge.Connection.SourceIds)
|
||||
{
|
||||
foreach (var sink in fanInEdge.Connection.SinkIds)
|
||||
{
|
||||
edges.Add(new Dictionary<string, object>
|
||||
edges.Add(new Dictionary<string, string>
|
||||
{
|
||||
["source_id"] = source,
|
||||
["target_id"] = sink
|
||||
@@ -178,16 +188,20 @@ internal static class WorkflowSerializationExtensions
|
||||
}
|
||||
}
|
||||
|
||||
edgeGroups.Add(new Dictionary<string, object>
|
||||
var edgeGroup = new Dictionary<string, JsonElement>
|
||||
{
|
||||
["id"] = $"edge_group_{edgeGroupId++}",
|
||||
["type"] = "FanInEdgeGroup",
|
||||
["edges"] = edges
|
||||
});
|
||||
["id"] = Serialize($"edge_group_{edgeGroupId++}", EntitiesJsonContext.Default.String),
|
||||
["type"] = Serialize("FanInEdgeGroup", EntitiesJsonContext.Default.String),
|
||||
["edges"] = Serialize(edges, EntitiesJsonContext.Default.ListDictionaryStringString)
|
||||
};
|
||||
|
||||
edgeGroups.Add(edgeGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return edgeGroups;
|
||||
}
|
||||
|
||||
private static JsonElement Serialize<T>(T value, JsonTypeInfo<T> typeInfo) => JsonSerializer.SerializeToElement(value, typeInfo);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Text.Json;
|
||||
using Microsoft.Agents.AI.DevUI.Entities;
|
||||
using Microsoft.Agents.AI.Hosting;
|
||||
using Microsoft.Agents.AI.Workflows;
|
||||
using Microsoft.Extensions.AI;
|
||||
|
||||
namespace Microsoft.Agents.AI.DevUI;
|
||||
|
||||
@@ -56,21 +57,21 @@ internal static class EntitiesApiExtensions
|
||||
{
|
||||
try
|
||||
{
|
||||
var entities = new List<EntityInfo>();
|
||||
var entities = new Dictionary<string, EntityInfo>();
|
||||
|
||||
// Discover agents
|
||||
await foreach (var agentInfo in DiscoverAgentsAsync(agentCatalog, entityIdFilter: null, cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
entities.Add(agentInfo);
|
||||
entities[agentInfo.Id] = agentInfo;
|
||||
}
|
||||
|
||||
// Discover workflows
|
||||
await foreach (var workflowInfo in DiscoverWorkflowsAsync(workflowCatalog, entityIdFilter: null, cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
entities.Add(workflowInfo);
|
||||
entities[workflowInfo.Id] = workflowInfo;
|
||||
}
|
||||
|
||||
return Results.Json(new DiscoveryResponse([.. entities]), EntitiesJsonContext.Default.DiscoveryResponse);
|
||||
return Results.Json(new DiscoveryResponse([.. entities.Values.OrderBy(e => e.Id)]), EntitiesJsonContext.Default.DiscoveryResponse);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -90,14 +91,6 @@ internal static class EntitiesApiExtensions
|
||||
{
|
||||
try
|
||||
{
|
||||
if (type is null || string.Equals(type, "agent", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
await foreach (var agentInfo in DiscoverAgentsAsync(agentCatalog, entityId, cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
return Results.Json(agentInfo, EntitiesJsonContext.Default.EntityInfo);
|
||||
}
|
||||
}
|
||||
|
||||
if (type is null || string.Equals(type, "workflow", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
await foreach (var workflowInfo in DiscoverWorkflowsAsync(workflowCatalog, entityId, cancellationToken).ConfigureAwait(false))
|
||||
@@ -106,6 +99,14 @@ internal static class EntitiesApiExtensions
|
||||
}
|
||||
}
|
||||
|
||||
if (type is null || string.Equals(type, "agent", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
await foreach (var agentInfo in DiscoverAgentsAsync(agentCatalog, entityId, cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
return Results.Json(agentInfo, EntitiesJsonContext.Default.EntityInfo);
|
||||
}
|
||||
}
|
||||
|
||||
return Results.NotFound(new { error = new { message = $"Entity '{entityId}' not found.", type = "invalid_request_error" } });
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -180,17 +181,82 @@ internal static class EntitiesApiExtensions
|
||||
private static EntityInfo CreateAgentEntityInfo(AIAgent agent)
|
||||
{
|
||||
var entityId = agent.Name ?? agent.Id;
|
||||
|
||||
// Extract tools and other metadata using GetService
|
||||
List<string> tools = [];
|
||||
var metadata = new Dictionary<string, JsonElement>();
|
||||
|
||||
// Try to get ChatOptions from the agent which may contain tools
|
||||
if (agent.GetService<ChatOptions>() is { Tools: { Count: > 0 } agentTools })
|
||||
{
|
||||
tools = agentTools
|
||||
.Where(tool => !string.IsNullOrWhiteSpace(tool.Name))
|
||||
.Select(tool => tool.Name!)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
}
|
||||
|
||||
// Extract agent-specific fields (top-level properties for compatibility with Python)
|
||||
string? instructions = null;
|
||||
string? modelId = null;
|
||||
string? chatClientType = null;
|
||||
|
||||
// Get instructions from ChatClientAgent
|
||||
if (agent is ChatClientAgent chatAgent && !string.IsNullOrWhiteSpace(chatAgent.Instructions))
|
||||
{
|
||||
instructions = chatAgent.Instructions;
|
||||
}
|
||||
|
||||
// Get IChatClient to extract metadata
|
||||
IChatClient? chatClient = agent.GetService<IChatClient>();
|
||||
if (chatClient != null)
|
||||
{
|
||||
// Get chat client type
|
||||
chatClientType = chatClient.GetType().Name;
|
||||
|
||||
// Get model ID from ChatClientMetadata
|
||||
if (chatClient.GetService<ChatClientMetadata>() is { } chatClientMetadata)
|
||||
{
|
||||
modelId = chatClientMetadata.DefaultModelId;
|
||||
|
||||
// Add additional metadata for compatibility
|
||||
if (!string.IsNullOrWhiteSpace(chatClientMetadata.ProviderName))
|
||||
{
|
||||
metadata["chat_client_provider"] = JsonSerializer.SerializeToElement(chatClientMetadata.ProviderName, EntitiesJsonContext.Default.String);
|
||||
}
|
||||
|
||||
if (chatClientMetadata.ProviderUri is not null)
|
||||
{
|
||||
metadata["provider_uri"] = JsonSerializer.SerializeToElement(chatClientMetadata.ProviderUri.ToString(), EntitiesJsonContext.Default.String);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add provider name from AIAgentMetadata if available
|
||||
if (agent.GetService<AIAgentMetadata>() is { } agentMetadata && !string.IsNullOrWhiteSpace(agentMetadata.ProviderName))
|
||||
{
|
||||
metadata["provider_name"] = JsonSerializer.SerializeToElement(agentMetadata.ProviderName, EntitiesJsonContext.Default.String);
|
||||
}
|
||||
|
||||
// Add agent type information to metadata (in addition to chat_client_type)
|
||||
var agentTypeName = agent.GetType().Name;
|
||||
metadata["agent_type"] = JsonSerializer.SerializeToElement(agentTypeName, EntitiesJsonContext.Default.String);
|
||||
|
||||
return new EntityInfo(
|
||||
Id: entityId,
|
||||
Type: "agent",
|
||||
Name: entityId,
|
||||
Name: agent.DisplayName,
|
||||
Description: agent.Description,
|
||||
Framework: "agent-framework",
|
||||
Tools: null,
|
||||
Metadata: []
|
||||
Framework: "agent_framework",
|
||||
Tools: tools,
|
||||
Metadata: metadata
|
||||
)
|
||||
{
|
||||
Source = "in_memory"
|
||||
Source = "in_memory",
|
||||
Instructions = instructions,
|
||||
ModelId = modelId,
|
||||
ChatClientType = chatClientType,
|
||||
Executors = [], // Agents have empty executors list (workflows use this field)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -212,7 +278,7 @@ internal static class EntitiesApiExtensions
|
||||
}
|
||||
|
||||
// Create a default input schema (string type)
|
||||
var defaultInputSchema = new Dictionary<string, object>
|
||||
var defaultInputSchema = new Dictionary<string, string>
|
||||
{
|
||||
["type"] = "string"
|
||||
};
|
||||
@@ -223,14 +289,17 @@ internal static class EntitiesApiExtensions
|
||||
Type: "workflow",
|
||||
Name: workflowId,
|
||||
Description: workflow.Description,
|
||||
Framework: "agent-framework",
|
||||
Tools: [.. executorIds],
|
||||
Framework: "agent_framework",
|
||||
Tools: [],
|
||||
Metadata: []
|
||||
)
|
||||
{
|
||||
Source = "in_memory",
|
||||
WorkflowDump = JsonSerializer.SerializeToElement(workflow.ToDevUIDict()),
|
||||
InputSchema = JsonSerializer.SerializeToElement(defaultInputSchema),
|
||||
Executors = [.. executorIds], // Workflows use Executors instead of Tools
|
||||
WorkflowDump = JsonSerializer.SerializeToElement(
|
||||
workflow.ToDevUIDict(),
|
||||
EntitiesJsonContext.Default.DictionaryStringJsonElement),
|
||||
InputSchema = JsonSerializer.SerializeToElement(defaultInputSchema, EntitiesJsonContext.Default.DictionaryStringString),
|
||||
InputTypeName = "string",
|
||||
StartExecutorId = workflow.StartExecutorId
|
||||
};
|
||||
|
||||
@@ -281,6 +281,8 @@ public sealed partial class ChatClientAgent : AIAgent
|
||||
base.GetService(serviceType, serviceKey) ??
|
||||
(serviceType == typeof(AIAgentMetadata) ? this._agentMetadata
|
||||
: serviceType == typeof(IChatClient) ? this.ChatClient
|
||||
: serviceType == typeof(ChatOptions) ? this._agentOptions?.ChatOptions
|
||||
: serviceType == typeof(ChatClientAgentOptions) ? this._agentOptions
|
||||
: this.ChatClient.GetService(serviceType, serviceKey));
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
||||
@@ -68,7 +68,7 @@ public class OpenAIResponseFixture(bool store) : IChatClientAgentFixture
|
||||
string name = "HelpfulAssistant",
|
||||
string instructions = "You are a helpful assistant.",
|
||||
IList<AITool>? aiTools = null) =>
|
||||
new ChatClientAgent(
|
||||
new(
|
||||
this._openAIResponseClient.AsIChatClient(),
|
||||
options: new()
|
||||
{
|
||||
|
||||
+17
-1
@@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [1.0.0b251112] - 2025-11-12
|
||||
|
||||
### Added
|
||||
|
||||
- **agent-framework-azure-ai**: Azure AI client based on new `azure-ai-projects` package ([#1910](https://github.com/microsoft/agent-framework/pull/1910))
|
||||
- **agent-framework-anthropic**: Add convenience method on data content ([#2083](https://github.com/microsoft/agent-framework/pull/2083))
|
||||
|
||||
### Changed
|
||||
|
||||
- **agent-framework-core**: Update OpenAI samples to use agents ([#2012](https://github.com/microsoft/agent-framework/pull/2012))
|
||||
|
||||
### Fixed
|
||||
|
||||
- **agent-framework-anthropic**: Fixed image handling in Anthropic client ([#2083](https://github.com/microsoft/agent-framework/pull/2083))
|
||||
|
||||
## [1.0.0b251111] - 2025-11-11
|
||||
|
||||
### Added
|
||||
@@ -204,7 +219,8 @@ 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.0b251111...HEAD
|
||||
[Unreleased]: https://github.com/microsoft/agent-framework/compare/python-1.0.0b251112...HEAD
|
||||
[1.0.0b251112]: https://github.com/microsoft/agent-framework/compare/python-1.0.0b251111...python-1.0.0b251112
|
||||
[1.0.0b251111]: https://github.com/microsoft/agent-framework/compare/python-1.0.0b251108...python-1.0.0b251111
|
||||
[1.0.0b251108]: https://github.com/microsoft/agent-framework/compare/python-1.0.0b251106.post1...python-1.0.0b251108
|
||||
[1.0.0b251106.post1]: https://github.com/microsoft/agent-framework/compare/python-1.0.0b251106...python-1.0.0b251106.post1
|
||||
|
||||
@@ -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.0b251111"
|
||||
version = "1.0.0b251112"
|
||||
license-files = ["LICENSE"]
|
||||
urls.homepage = "https://aka.ms/agent-framework"
|
||||
urls.source = "https://github.com/microsoft/agent-framework/tree/main/python"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "agent-framework-ag-ui"
|
||||
version = "1.0.0b251111"
|
||||
version = "1.0.0b251112"
|
||||
description = "AG-UI protocol integration for Agent Framework"
|
||||
readme = "README.md"
|
||||
license-files = ["LICENSE"]
|
||||
|
||||
@@ -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.0b251111"
|
||||
version = "1.0.0b251112"
|
||||
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.0b251111"
|
||||
version = "1.0.0b251112"
|
||||
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 = "OpenAI ChatKit integration for Microsoft Agent Framework."
|
||||
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
version = "1.0.0b251111"
|
||||
version = "1.0.0b251112"
|
||||
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.0b251111"
|
||||
version = "1.0.0b251112"
|
||||
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.0b251111"
|
||||
version = "1.0.0b251112"
|
||||
license-files = ["LICENSE"]
|
||||
urls.homepage = "https://aka.ms/agent-framework"
|
||||
urls.source = "https://github.com/microsoft/agent-framework/tree/main/python"
|
||||
|
||||
File diff suppressed because one or more lines are too long
+13
-13
@@ -94,14 +94,14 @@ export function AgentDetailsModal({
|
||||
{/* Grid Layout for Metadata */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
||||
{/* Model & Client */}
|
||||
{(agent.model || agent.chat_client_type) && (
|
||||
{(agent.model_id || agent.chat_client_type) && (
|
||||
<DetailCard
|
||||
title="Model & Client"
|
||||
icon={<Bot className="h-4 w-4 text-muted-foreground" />}
|
||||
>
|
||||
<div className="space-y-1">
|
||||
{agent.model && (
|
||||
<div className="font-mono text-foreground">{agent.model}</div>
|
||||
{agent.model_id && (
|
||||
<div className="font-mono text-foreground">{agent.model_id}</div>
|
||||
)}
|
||||
{agent.chat_client_type && (
|
||||
<div className="text-xs">({agent.chat_client_type})</div>
|
||||
@@ -136,7 +136,9 @@ export function AgentDetailsModal({
|
||||
>
|
||||
<div
|
||||
className={
|
||||
agent.has_env ? "text-orange-600 dark:text-orange-400" : "text-green-600 dark:text-green-400"
|
||||
agent.has_env
|
||||
? "text-orange-600 dark:text-orange-400"
|
||||
: "text-green-600 dark:text-green-400"
|
||||
}
|
||||
>
|
||||
{agent.has_env
|
||||
@@ -162,11 +164,11 @@ export function AgentDetailsModal({
|
||||
{/* Tools and Middleware Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{/* Tools */}
|
||||
<DetailCard
|
||||
title={`Tools (${agent.tools.length})`}
|
||||
icon={<Package className="h-4 w-4 text-muted-foreground" />}
|
||||
>
|
||||
{agent.tools.length > 0 ? (
|
||||
{agent.tools && agent.tools.length > 0 && (
|
||||
<DetailCard
|
||||
title={`Tools (${agent.tools.length})`}
|
||||
icon={<Package className="h-4 w-4 text-muted-foreground" />}
|
||||
>
|
||||
<ul className="space-y-1">
|
||||
{agent.tools.map((tool, index) => (
|
||||
<li key={index} className="font-mono text-xs text-foreground">
|
||||
@@ -174,10 +176,8 @@ export function AgentDetailsModal({
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<div className="text-muted-foreground">No tools configured</div>
|
||||
)}
|
||||
</DetailCard>
|
||||
</DetailCard>
|
||||
)}
|
||||
|
||||
{/* Middleware */}
|
||||
{agent.middleware && agent.middleware.length > 0 && (
|
||||
|
||||
+4
-3
@@ -64,8 +64,8 @@ export function WorkflowDetailsModal({
|
||||
workflow.source === "directory"
|
||||
? "Local"
|
||||
: workflow.source === "in_memory"
|
||||
? "In-Memory"
|
||||
: "Gallery";
|
||||
? "In-Memory"
|
||||
: "Gallery";
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
@@ -151,7 +151,8 @@ export function WorkflowDetailsModal({
|
||||
{workflow.executors.map((executor, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="font-mono text-xs text-foreground bg-muted px-2 py-1 rounded"
|
||||
className="font-mono text-xs text-foreground bg-muted px-2 py-1 rounded truncate"
|
||||
title={executor}
|
||||
>
|
||||
{executor}
|
||||
</div>
|
||||
|
||||
@@ -33,12 +33,13 @@ interface BackendEntityInfo {
|
||||
tools?: (string | Record<string, unknown>)[];
|
||||
metadata: Record<string, unknown>;
|
||||
source?: string;
|
||||
required_env_vars?: import("@/types").EnvVarRequirement[];
|
||||
// Deployment support
|
||||
deployment_supported?: boolean;
|
||||
deployment_reason?: string;
|
||||
// Agent-specific fields (present when type === "agent")
|
||||
instructions?: string;
|
||||
model?: string;
|
||||
model_id?: string;
|
||||
chat_client_type?: string;
|
||||
context_providers?: string[];
|
||||
middleware?: string[];
|
||||
@@ -205,41 +206,51 @@ class ApiClient {
|
||||
tools: (entity.tools || []).map((tool) =>
|
||||
typeof tool === "string" ? tool : JSON.stringify(tool)
|
||||
),
|
||||
has_env: false, // Default value
|
||||
has_env: !!(entity.required_env_vars && entity.required_env_vars.length > 0),
|
||||
module_path:
|
||||
typeof entity.metadata?.module_path === "string"
|
||||
? entity.metadata.module_path
|
||||
: undefined,
|
||||
required_env_vars: entity.required_env_vars,
|
||||
metadata: entity.metadata, // Preserve metadata including lazy_loaded flag
|
||||
// Deployment support
|
||||
deployment_supported: entity.deployment_supported,
|
||||
deployment_reason: entity.deployment_reason,
|
||||
// Agent-specific fields
|
||||
instructions: entity.instructions,
|
||||
model: entity.model,
|
||||
model_id: entity.model_id,
|
||||
chat_client_type: entity.chat_client_type,
|
||||
context_providers: entity.context_providers,
|
||||
middleware: entity.middleware,
|
||||
};
|
||||
} else {
|
||||
// Workflow
|
||||
const firstTool = entity.tools?.[0];
|
||||
const startExecutorId = typeof firstTool === "string" ? firstTool : "";
|
||||
|
||||
// Workflow - prefer executors field, fall back to tools for backward compatibility
|
||||
const executorList = entity.executors || entity.tools || [];
|
||||
|
||||
// Determine start_executor_id: use entity value, or first executor if it's a string
|
||||
let startExecutorId = entity.start_executor_id || "";
|
||||
if (!startExecutorId && executorList.length > 0) {
|
||||
const firstExecutor = executorList[0];
|
||||
if (typeof firstExecutor === "string") {
|
||||
startExecutorId = firstExecutor;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
id: entity.id,
|
||||
name: entity.name,
|
||||
description: entity.description,
|
||||
type: "workflow" as const,
|
||||
source: (entity.source as AgentSource) || "directory",
|
||||
executors: (entity.tools || []).map((tool) =>
|
||||
typeof tool === "string" ? tool : JSON.stringify(tool)
|
||||
executors: executorList.map((executor) =>
|
||||
typeof executor === "string" ? executor : JSON.stringify(executor)
|
||||
),
|
||||
has_env: false,
|
||||
has_env: !!(entity.required_env_vars && entity.required_env_vars.length > 0),
|
||||
module_path:
|
||||
typeof entity.metadata?.module_path === "string"
|
||||
? entity.metadata.module_path
|
||||
: undefined,
|
||||
required_env_vars: entity.required_env_vars,
|
||||
metadata: entity.metadata, // Preserve metadata including lazy_loaded flag
|
||||
// Deployment support
|
||||
deployment_supported: entity.deployment_supported,
|
||||
@@ -250,6 +261,7 @@ class ApiClient {
|
||||
}, // Default schema
|
||||
input_type_name: entity.input_type_name || "Input",
|
||||
start_executor_id: startExecutorId,
|
||||
tools: [],
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -37,7 +37,7 @@ export interface AgentInfo {
|
||||
deployment_reason?: string;
|
||||
// Agent-specific fields
|
||||
instructions?: string;
|
||||
model?: string;
|
||||
model_id?: string;
|
||||
chat_client_type?: string;
|
||||
context_providers?: string[];
|
||||
middleware?: string[];
|
||||
|
||||
@@ -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.0b251111"
|
||||
version = "1.0.0b251112"
|
||||
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.0b251111"
|
||||
version = "1.0.0b251112"
|
||||
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.0b251111"
|
||||
version = "1.0.0b251112"
|
||||
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.0b251111"
|
||||
version = "1.0.0b251112"
|
||||
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.0b251111"
|
||||
version = "1.0.0b251112"
|
||||
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.0b251111"
|
||||
version = "1.0.0b251112"
|
||||
license-files = ["LICENSE"]
|
||||
urls.homepage = "https://aka.ms/agent-framework"
|
||||
urls.source = "https://github.com/microsoft/agent-framework/tree/main/python"
|
||||
|
||||
@@ -17,6 +17,8 @@ This folder contains examples demonstrating different ways to create and use age
|
||||
| [`azure_ai_with_hosted_mcp.py`](azure_ai_with_hosted_mcp.py) | Shows how to integrate hosted Model Context Protocol (MCP) tools with Azure AI Agent. |
|
||||
| [`azure_ai_with_response_format.py`](azure_ai_with_response_format.py) | Shows how to use structured outputs (response format) with Azure AI agents using Pydantic models to enforce specific response schemas. |
|
||||
| [`azure_ai_with_thread.py`](azure_ai_with_thread.py) | Demonstrates thread management with Azure AI agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. |
|
||||
| [`azure_ai_with_image_generation.py`](azure_ai_with_image_generation.py) | Shows how to use the `ImageGenTool` with Azure AI agents to generate images based on text prompts. |
|
||||
| [`azure_ai_with_web_search.py`](azure_ai_with_web_search.py) | Shows how to use the `HostedWebSearchTool` with Azure AI agents to perform web searches and retrieve up-to-date information from the internet. |
|
||||
|
||||
## Environment Variables
|
||||
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
from pathlib import Path
|
||||
|
||||
import aiofiles
|
||||
from agent_framework import DataContent
|
||||
from agent_framework.azure import AzureAIClient
|
||||
from azure.ai.projects.models import ImageGenTool
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
|
||||
"""
|
||||
Azure AI Agent With Image Generation
|
||||
|
||||
This sample demonstrates basic usage of AzureAIClient to create an agent
|
||||
that can generate images based on user requirements.
|
||||
|
||||
Pre-requisites:
|
||||
- Make sure to set up the AZURE_AI_PROJECT_ENDPOINT and AZURE_AI_MODEL_DEPLOYMENT_NAME
|
||||
environment variables before running this sample.
|
||||
"""
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
# Since no Agent ID is provided, the agent will be automatically created.
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
async with (
|
||||
AzureCliCredential() as credential,
|
||||
AzureAIClient(async_credential=credential).create_agent(
|
||||
name="ImageGenAgent",
|
||||
instructions="Generate images based on user requirements.",
|
||||
tools=[ImageGenTool(quality="low", size="1024x1024")],
|
||||
) as agent,
|
||||
):
|
||||
query = "Generate an image of Microsoft logo."
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(
|
||||
query,
|
||||
# These additional options are required for image generation
|
||||
additional_chat_options={
|
||||
"extra_headers": {"x-ms-oai-image-generation-deployment": "gpt-image-1"},
|
||||
},
|
||||
)
|
||||
print(f"Agent: {result}\n")
|
||||
|
||||
# Save the image to a file
|
||||
print("Downloading generated image...")
|
||||
image_data = [
|
||||
content
|
||||
for content in result.messages[0].contents
|
||||
if isinstance(content, DataContent) and content.media_type == "image/png"
|
||||
]
|
||||
if image_data and image_data[0]:
|
||||
# Save to the same directory as this script
|
||||
filename = "microsoft.png"
|
||||
current_dir = Path(__file__).parent.resolve()
|
||||
file_path = current_dir / filename
|
||||
async with aiofiles.open(file_path, "wb") as f:
|
||||
await f.write(image_data[0].get_data_bytes())
|
||||
|
||||
print(f"Image downloaded and saved to: {file_path}")
|
||||
else:
|
||||
print("No image data found in the agent response.")
|
||||
|
||||
"""
|
||||
Sample output:
|
||||
User: Generate an image of Microsoft logo.
|
||||
Agent: Here is the Microsoft logo image featuring its iconic four quadrants.
|
||||
|
||||
Downloading generated image...
|
||||
Image downloaded and saved to: .../microsoft.png
|
||||
"""
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,48 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
|
||||
from agent_framework import HostedWebSearchTool
|
||||
from agent_framework.azure import AzureAIClient
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
|
||||
"""
|
||||
Azure AI Agent With Web Search
|
||||
|
||||
This sample demonstrates basic usage of AzureAIClient to create an agent
|
||||
that can perform web searches using the HostedWebSearchTool.
|
||||
|
||||
Pre-requisites:
|
||||
- Make sure to set up the AZURE_AI_PROJECT_ENDPOINT and AZURE_AI_MODEL_DEPLOYMENT_NAME
|
||||
environment variables before running this sample.
|
||||
"""
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
# Since no Agent ID is provided, the agent will be automatically created.
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option.
|
||||
async with (
|
||||
AzureCliCredential() as credential,
|
||||
AzureAIClient(async_credential=credential).create_agent(
|
||||
name="WebsearchAgent",
|
||||
instructions="You are a helpful assistant that can search the web",
|
||||
tools=[HostedWebSearchTool()],
|
||||
) as agent,
|
||||
):
|
||||
query = "What's the weather today in Seattle?"
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(query)
|
||||
print(f"Agent: {result}\n")
|
||||
|
||||
"""
|
||||
Sample output:
|
||||
User: What's the weather today in Seattle?
|
||||
Agent: Here is the updated weather forecast for Seattle: The current temperature is approximately 57°F,
|
||||
mostly cloudy conditions, with light winds and a chance of rain later tonight. Check out more details
|
||||
at the [National Weather Service](https://forecast.weather.gov/zipcity.php?inputstring=Seattle%2CWA).
|
||||
"""
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
+4
-6
@@ -1,7 +1,7 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import os
|
||||
import asyncio
|
||||
import os
|
||||
|
||||
from agent_framework import ChatAgent, MCPStreamableHTTPTool
|
||||
from agent_framework.azure import AzureOpenAIResponsesClient
|
||||
@@ -18,13 +18,14 @@ servers.
|
||||
# --- Below code uses Microsoft Learn MCP server over Streamable HTTP ---
|
||||
# --- Users can set these environment variables, or just edit the values below to their desired local MCP server
|
||||
MCP_NAME = os.environ.get("MCP_NAME", "Microsoft Learn MCP") # example name
|
||||
MCP_URL = os.environ.get("MCP_URL", "https://learn.microsoft.com/api/mcp") # example endpoint
|
||||
MCP_URL = os.environ.get("MCP_URL", "https://learn.microsoft.com/api/mcp") # example endpoint
|
||||
|
||||
# Environment variables for Azure OpenAI Responses authentication
|
||||
# AZURE_OPENAI_ENDPOINT="<your-azure openai-endpoint>"
|
||||
# AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME="<your-deployment-name>"
|
||||
# AZURE_OPENAI_API_VERSION="<your-api-version>" # e.g. "2025-03-01-preview"
|
||||
|
||||
|
||||
async def main():
|
||||
"""Example showing local MCP tools for a Azure OpenAI Responses Agent."""
|
||||
# AuthN: use Azure CLI
|
||||
@@ -38,16 +39,13 @@ async def main():
|
||||
|
||||
agent: ChatAgent = responses_client.create_agent(
|
||||
name="DocsAgent",
|
||||
instructions=(
|
||||
"You are a helpful assistant that can help with Microsoft documentation questions."
|
||||
),
|
||||
instructions=("You are a helpful assistant that can help with Microsoft documentation questions."),
|
||||
)
|
||||
|
||||
# Connect to the MCP server (Streamable HTTP)
|
||||
async with MCPStreamableHTTPTool(
|
||||
name=MCP_NAME,
|
||||
url=MCP_URL,
|
||||
|
||||
) as mcp_tool:
|
||||
# First query — expect the agent to use the MCP tool if it helps
|
||||
q1 = "How to create an Azure storage account using az cli?"
|
||||
|
||||
+13
-15
@@ -2,7 +2,7 @@
|
||||
|
||||
import asyncio
|
||||
|
||||
from agent_framework import HostedWebSearchTool
|
||||
from agent_framework import ChatAgent, HostedWebSearchTool
|
||||
from agent_framework.openai import OpenAIChatClient
|
||||
|
||||
"""
|
||||
@@ -14,34 +14,32 @@ for real-time information retrieval and current data access.
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
client = OpenAIChatClient(model_id="gpt-4o-search-preview")
|
||||
|
||||
message = "What is the current weather? Do not ask for my current location."
|
||||
# Test that the client will use the web search tool with location
|
||||
# Test that the agent will use the web search tool with location
|
||||
additional_properties = {
|
||||
"user_location": {
|
||||
"country": "US",
|
||||
"city": "Seattle",
|
||||
}
|
||||
}
|
||||
|
||||
agent = ChatAgent(
|
||||
chat_client=OpenAIChatClient(model_id="gpt-4o-search-preview"),
|
||||
instructions="You are a helpful assistant that can search the web for current information.",
|
||||
tools=[HostedWebSearchTool(additional_properties=additional_properties)],
|
||||
)
|
||||
|
||||
message = "What is the current weather? Do not ask for my current location."
|
||||
stream = False
|
||||
print(f"User: {message}")
|
||||
|
||||
if stream:
|
||||
print("Assistant: ", end="")
|
||||
async for chunk in client.get_streaming_response(
|
||||
message,
|
||||
tools=[HostedWebSearchTool(additional_properties=additional_properties)],
|
||||
tool_choice="auto",
|
||||
):
|
||||
async for chunk in agent.run_stream(message):
|
||||
if chunk.text:
|
||||
print(chunk.text, end="")
|
||||
print("")
|
||||
else:
|
||||
response = await client.get_response(
|
||||
message,
|
||||
tools=[HostedWebSearchTool(additional_properties=additional_properties)],
|
||||
tool_choice="auto",
|
||||
)
|
||||
response = await agent.run(message)
|
||||
print(f"Assistant: {response}")
|
||||
|
||||
|
||||
|
||||
+10
-11
@@ -2,7 +2,7 @@
|
||||
|
||||
import asyncio
|
||||
|
||||
from agent_framework import HostedFileSearchTool, HostedVectorStoreContent
|
||||
from agent_framework import ChatAgent, HostedFileSearchTool, HostedVectorStoreContent
|
||||
from agent_framework.openai import OpenAIResponsesClient
|
||||
|
||||
"""
|
||||
@@ -46,22 +46,21 @@ async def main() -> None:
|
||||
stream = False
|
||||
print(f"User: {message}")
|
||||
file_id, vector_store = await create_vector_store(client)
|
||||
|
||||
agent = ChatAgent(
|
||||
chat_client=client,
|
||||
instructions="You are a helpful assistant that can search through files to find information.",
|
||||
tools=[HostedFileSearchTool(inputs=vector_store)],
|
||||
)
|
||||
|
||||
if stream:
|
||||
print("Assistant: ", end="")
|
||||
async for chunk in client.get_streaming_response(
|
||||
message,
|
||||
tools=[HostedFileSearchTool(inputs=vector_store)],
|
||||
tool_choice="auto",
|
||||
):
|
||||
async for chunk in agent.run_stream(message):
|
||||
if chunk.text:
|
||||
print(chunk.text, end="")
|
||||
print("")
|
||||
else:
|
||||
response = await client.get_response(
|
||||
message,
|
||||
tools=[HostedFileSearchTool(inputs=vector_store)],
|
||||
tool_choice="auto",
|
||||
)
|
||||
response = await agent.run(message)
|
||||
print(f"Assistant: {response}")
|
||||
await delete_vector_store(client, file_id, vector_store.vector_store_id)
|
||||
|
||||
|
||||
+13
-15
@@ -2,7 +2,7 @@
|
||||
|
||||
import asyncio
|
||||
|
||||
from agent_framework import HostedWebSearchTool
|
||||
from agent_framework import ChatAgent, HostedWebSearchTool
|
||||
from agent_framework.openai import OpenAIResponsesClient
|
||||
|
||||
"""
|
||||
@@ -14,34 +14,32 @@ for direct real-time information retrieval and current data access.
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
client = OpenAIResponsesClient()
|
||||
|
||||
message = "What is the current weather? Do not ask for my current location."
|
||||
# Test that the client will use the web search tool with location
|
||||
# Test that the agent will use the web search tool with location
|
||||
additional_properties = {
|
||||
"user_location": {
|
||||
"country": "US",
|
||||
"city": "Seattle",
|
||||
}
|
||||
}
|
||||
|
||||
agent = ChatAgent(
|
||||
chat_client=OpenAIResponsesClient(),
|
||||
instructions="You are a helpful assistant that can search the web for current information.",
|
||||
tools=[HostedWebSearchTool(additional_properties=additional_properties)],
|
||||
)
|
||||
|
||||
message = "What is the current weather? Do not ask for my current location."
|
||||
stream = False
|
||||
print(f"User: {message}")
|
||||
|
||||
if stream:
|
||||
print("Assistant: ", end="")
|
||||
async for chunk in client.get_streaming_response(
|
||||
message,
|
||||
tools=[HostedWebSearchTool(additional_properties=additional_properties)],
|
||||
tool_choice="auto",
|
||||
):
|
||||
async for chunk in agent.run_stream(message):
|
||||
if chunk.text:
|
||||
print(chunk.text, end="")
|
||||
print("")
|
||||
else:
|
||||
response = await client.get_response(
|
||||
message,
|
||||
tools=[HostedWebSearchTool(additional_properties=additional_properties)],
|
||||
tool_choice="auto",
|
||||
)
|
||||
response = await agent.run(message)
|
||||
print(f"Assistant: {response}")
|
||||
|
||||
|
||||
|
||||
Generated
+13
-13
@@ -81,7 +81,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "agent-framework"
|
||||
version = "1.0.0b251111"
|
||||
version = "1.0.0b251112"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "agent-framework-a2a", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
@@ -172,7 +172,7 @@ docs = [
|
||||
|
||||
[[package]]
|
||||
name = "agent-framework-a2a"
|
||||
version = "1.0.0b251111"
|
||||
version = "1.0.0b251112"
|
||||
source = { editable = "packages/a2a" }
|
||||
dependencies = [
|
||||
{ name = "a2a-sdk", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
@@ -187,7 +187,7 @@ requires-dist = [
|
||||
|
||||
[[package]]
|
||||
name = "agent-framework-ag-ui"
|
||||
version = "1.0.0b251111"
|
||||
version = "1.0.0b251112"
|
||||
source = { editable = "packages/ag-ui" }
|
||||
dependencies = [
|
||||
{ name = "ag-ui-protocol", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
@@ -217,7 +217,7 @@ provides-extras = ["dev"]
|
||||
|
||||
[[package]]
|
||||
name = "agent-framework-anthropic"
|
||||
version = "1.0.0b251111"
|
||||
version = "1.0.0b251112"
|
||||
source = { editable = "packages/anthropic" }
|
||||
dependencies = [
|
||||
{ name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
@@ -232,7 +232,7 @@ requires-dist = [
|
||||
|
||||
[[package]]
|
||||
name = "agent-framework-azure-ai"
|
||||
version = "1.0.0b251111"
|
||||
version = "1.0.0b251112"
|
||||
source = { editable = "packages/azure-ai" }
|
||||
dependencies = [
|
||||
{ name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
@@ -251,7 +251,7 @@ requires-dist = [
|
||||
|
||||
[[package]]
|
||||
name = "agent-framework-chatkit"
|
||||
version = "1.0.0b251111"
|
||||
version = "1.0.0b251112"
|
||||
source = { editable = "packages/chatkit" }
|
||||
dependencies = [
|
||||
{ name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
@@ -266,7 +266,7 @@ requires-dist = [
|
||||
|
||||
[[package]]
|
||||
name = "agent-framework-copilotstudio"
|
||||
version = "1.0.0b251111"
|
||||
version = "1.0.0b251112"
|
||||
source = { editable = "packages/copilotstudio" }
|
||||
dependencies = [
|
||||
{ name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
@@ -281,7 +281,7 @@ requires-dist = [
|
||||
|
||||
[[package]]
|
||||
name = "agent-framework-core"
|
||||
version = "1.0.0b251111"
|
||||
version = "1.0.0b251112"
|
||||
source = { editable = "packages/core" }
|
||||
dependencies = [
|
||||
{ name = "azure-identity", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
@@ -337,7 +337,7 @@ provides-extras = ["all"]
|
||||
|
||||
[[package]]
|
||||
name = "agent-framework-devui"
|
||||
version = "1.0.0b251111"
|
||||
version = "1.0.0b251112"
|
||||
source = { editable = "packages/devui" }
|
||||
dependencies = [
|
||||
{ name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
@@ -371,7 +371,7 @@ provides-extras = ["dev", "all"]
|
||||
|
||||
[[package]]
|
||||
name = "agent-framework-lab"
|
||||
version = "1.0.0b251111"
|
||||
version = "1.0.0b251112"
|
||||
source = { editable = "packages/lab" }
|
||||
dependencies = [
|
||||
{ name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
@@ -462,7 +462,7 @@ dev = [
|
||||
|
||||
[[package]]
|
||||
name = "agent-framework-mem0"
|
||||
version = "1.0.0b251111"
|
||||
version = "1.0.0b251112"
|
||||
source = { editable = "packages/mem0" }
|
||||
dependencies = [
|
||||
{ name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
@@ -477,7 +477,7 @@ requires-dist = [
|
||||
|
||||
[[package]]
|
||||
name = "agent-framework-purview"
|
||||
version = "1.0.0b251111"
|
||||
version = "1.0.0b251112"
|
||||
source = { editable = "packages/purview" }
|
||||
dependencies = [
|
||||
{ name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
@@ -494,7 +494,7 @@ requires-dist = [
|
||||
|
||||
[[package]]
|
||||
name = "agent-framework-redis"
|
||||
version = "1.0.0b251111"
|
||||
version = "1.0.0b251112"
|
||||
source = { editable = "packages/redis" }
|
||||
dependencies = [
|
||||
{ name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
|
||||
Reference in New Issue
Block a user