Files
wehub-resource-sync db620d33df
dotnet-build-and-test / dotnet-test-functions (push) Has been cancelled
dotnet-build-and-test / paths-filter (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Debug, windows-latest, net9.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net8.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-foundry-hosted-it (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test-check (push) Has been cancelled
dotnet-build-and-test / Integration Test Report (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:39:25 +08:00

120 lines
5.1 KiB
C#

// Copyright (c) Microsoft. All rights reserved.
// Hosted Local CodeAct sample. Wires Microsoft.Agents.AI.LocalCodeAct into a
// Foundry hosted agent. The model only sees a single `execute_code` tool;
// `compute` and `fetch_data` are registered as sandbox-only host tools that
// generated Python reaches via `await call_tool(...)`. This mirrors the Python
// `foundry_hosted_agent.py` sample for the local-codeact package.
//
// SECURITY: LocalCodeAct executes LLM-generated Python in the agent process.
// Only deploy this sample to an externally sandboxed environment such as a
// Foundry hosted-agent container.
using System.ComponentModel;
using Azure.AI.Projects;
using Azure.Core;
using Azure.Identity;
using DotNetEnv;
using Hosted_Shared_Contributor_Setup;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Foundry.Hosting;
using Microsoft.Agents.AI.LocalCodeAct;
using Microsoft.Extensions.AI;
// Load .env file if present (for local development)
Env.TraversePath().Load();
string endpoint = Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT")
?? throw new InvalidOperationException("FOUNDRY_PROJECT_ENDPOINT is not set.");
string deploymentName = Environment.GetEnvironmentVariable("FOUNDRY_MODEL") ?? "gpt-4o";
string pythonExecutable = Environment.GetEnvironmentVariable("LOCAL_CODEACT_PYTHON")
?? (OperatingSystem.IsWindows() ? "python.exe" : "python3");
TokenCredential credential = new ChainedTokenCredential(
new DevTemporaryTokenCredential(),
new DefaultAzureCredential());
// ── Sandbox-only tools (model never sees these directly) ─────────────────────
[Description("Perform a math operation: add, subtract, multiply, or divide.")]
static double Compute(
[Description("Operation: add, subtract, multiply, or divide.")] string operation,
[Description("First numeric operand.")] double a,
[Description("Second numeric operand.")] double b) => operation switch
{
"add" => a + b,
"subtract" => a - b,
"multiply" => a * b,
"divide" => b == 0 ? double.PositiveInfinity : a / b,
_ => throw new ArgumentException($"Unknown operation '{operation}'.", nameof(operation)),
};
[Description("Fetch records from a named simulated table (users or products).")]
static IReadOnlyList<IReadOnlyDictionary<string, object>> FetchData(
[Description("Name of the simulated table to query.")] string table)
{
Dictionary<string, IReadOnlyList<IReadOnlyDictionary<string, object>>> data = new()
{
["users"] =
[
new Dictionary<string, object> { ["id"] = 1, ["name"] = "Alice", ["role"] = "admin" },
new Dictionary<string, object> { ["id"] = 2, ["name"] = "Bob", ["role"] = "user" },
new Dictionary<string, object> { ["id"] = 3, ["name"] = "Charlie", ["role"] = "admin" },
],
["products"] =
[
new Dictionary<string, object> { ["id"] = 101, ["name"] = "Widget", ["price"] = 9.99 },
new Dictionary<string, object> { ["id"] = 102, ["name"] = "Gadget", ["price"] = 19.99 },
],
};
return data.TryGetValue(table, out var rows) ? rows : [];
}
// ── LocalCodeAct provider with sandbox-only host tools ───────────────────────
var codeActOptions = new LocalCodeActProviderOptions
{
Tools =
[
AIFunctionFactory.Create(Compute, name: "compute"),
AIFunctionFactory.Create(FetchData, name: "fetch_data"),
],
ExecutionLimits = new ProcessExecutionLimits { TimeoutSeconds = 5 },
};
var codeAct = new LocalCodeActProvider(pythonExecutable, codeActOptions);
// ── Build the hosted agent ───────────────────────────────────────────────────
AIAgent agent = new AIProjectClient(new Uri(endpoint), credential)
.AsAIAgent(new ChatClientAgentOptions
{
Name = Environment.GetEnvironmentVariable("AGENT_NAME") ?? "hosted-local-codeact",
Description = "Hosted CodeAct agent with sandbox-only compute and fetch_data tools.",
ChatOptions = new ChatOptions
{
ModelId = deploymentName,
Instructions =
"""
You are a helpful assistant. Keep your answers brief. Prefer orchestrating your work
in a single `execute_code` block using `await call_tool(...)` over issuing many
direct tool calls. The sandbox exposes `compute` and `fetch_data` via `call_tool`.
""",
},
AIContextProviders = [codeAct],
});
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddFoundryResponses(agent);
var app = builder.Build();
app.MapFoundryResponses();
// Contributor-only: in Development, also map the per-agent OpenAI route shape that live Foundry uses
// so a local REPL client can target this server via AIProjectClient.AsAIAgent(Uri agentEndpoint).
// Do not use this in production. Hosted Foundry agents only support the agent-endpoint path.
app.MapDevTemporaryLocalAgentEndpoint();
app.Run();