mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
b03cb324d5
* initial work on code_mode * updated samples * updates to codeact * udpated codeact * Draft CodeAct ADR and sample updates Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * initial implementation and adr and feature * Python: Limit Hyperlight wasm backend to Python <3.14 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: Fix CI for Hyperlight CodeAct PR Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: Run Hyperlight integration when available Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: Address Hyperlight review feedback Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: Simplify Hyperlight file mount inputs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: Accept Path host paths in Hyperlight mounts Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: Fix Hyperlight mount typing for CI Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * temp run integration test * Python: Strengthen Hyperlight real sandbox tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * added additional tests * Python: Simplify Hyperlight CodeAct API Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * set tests as non-integration * Retry Hyperlight allowed-domain registration Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Gate Hyperlight integration tests by runtime support Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix Hyperlight skip test on Python 3.14 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Delay Hyperlight runtime probe until test execution Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Relax Hyperlight Windows integration stdout assertion Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Scan Hyperlight output directory for artifacts Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Retry Hyperlight output artifact collection Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Harden Hyperlight integration output assertions Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Retry Hyperlight read-back check in integration test Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Simplify Hyperlight integration write assertion Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Avoid pathlib in Hyperlight integration sandbox Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Use socket network check in Hyperlight sandbox Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Replace blocked Azure AI Search blog link Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Clarify Hyperlight guest stdlib limits Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Use _socket in Hyperlight integration sandbox Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Handle Hyperlight mounted file paths Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Broaden Hyperlight sandbox path fallbacks Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Search Hyperlight guest mounts recursively Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Split Hyperlight mount coverage Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Split Hyperlight live network tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix Hyperlight file-write test on Windows Enable the sandbox filesystem by providing a workspace_root so /output is mounted. Remove os.path.exists assertion (unsupported in WASM guest) and fix Content data assertion to use .uri. Skip the network integration test on Windows where the WASM sandbox lacks the encodings.idna codec. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address PR review: ADR intro, manual wiring sample, doc clarifications - Add CodeAct introduction section to ADR for unfamiliar readers - Clarify 'less runtime efficient' con with specific overhead description - Add note in Python impl doc clarifying ADR vs impl doc split - Explain why before_run hooks must be per-run (CRUD, concurrency, approval) - Rename code_interpreter variable to codeact in E2E sample - Add manual static wiring sample (codeact_manual_wiring.py) - Add 'when to use which pattern' guidance to samples README Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address PR #5185 review comments and add .NET CodeAct design doc - Fix async callback: _make_sandbox_callback returns sync wrapper with thread + asyncio.run() bridge (was broken with real Wasm FFI) - Fix stale output: clear output_dir before each sandbox.run() call - Fix blocking event loop: _run_code now async with asyncio.to_thread() - Revert _agents.py options['tools'] injection (unnecessary; provider uses context.extend_tools()) - Revert SessionContext.options docstring back to read-only - Add real-sandbox test fixtures (shared/restored/fresh) - Add 8 new real-sandbox tests for callback round-trip, stale output, event loop non-blocking, basic execution, stdout/stderr, errors, snapshot/restore, and tool registration - Add comprehensive .NET HyperlightCodeActProvider design document Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update hyperlight README with code snippets and remove Public API section Replace bare export list with Quick Start code examples covering the context provider, standalone tool, manual static wiring, and file mounts / network access patterns. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
134 lines
4.6 KiB
Python
134 lines
4.6 KiB
Python
# Copyright (c) Microsoft. All rights reserved.
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import os
|
|
from typing import Annotated, Any, Literal
|
|
|
|
from agent_framework import Agent, tool
|
|
from agent_framework.foundry import FoundryChatClient
|
|
from azure.identity import AzureCliCredential
|
|
from dotenv import load_dotenv
|
|
|
|
from agent_framework_hyperlight import HyperlightExecuteCodeTool
|
|
|
|
"""This sample demonstrates manual static wiring of CodeAct without a provider.
|
|
|
|
Instead of using `HyperlightCodeActProvider` with `context_providers=`, this
|
|
sample creates a `HyperlightExecuteCodeTool` directly, extracts its CodeAct
|
|
instructions once, and passes both to the `Agent` constructor at build time.
|
|
|
|
This avoids the per-run provider lifecycle (`before_run` / `after_run`) and is
|
|
well-suited when the tool registry, file mounts, and network allow-list are
|
|
fixed for the agent's lifetime. The tradeoff is that dynamic tool or capability
|
|
changes between runs are not supported — any mutations to the tool would not
|
|
update the agent's instructions automatically.
|
|
"""
|
|
|
|
load_dotenv()
|
|
|
|
|
|
@tool(approval_mode="never_require")
|
|
def compute(
|
|
operation: Annotated[
|
|
Literal["add", "subtract", "multiply", "divide"],
|
|
"Math operation: add, subtract, multiply, or divide.",
|
|
],
|
|
a: Annotated[float, "First numeric operand."],
|
|
b: Annotated[float, "Second numeric operand."],
|
|
) -> float:
|
|
"""Perform a math operation used by sandboxed code."""
|
|
operations = {
|
|
"add": a + b,
|
|
"subtract": a - b,
|
|
"multiply": a * b,
|
|
"divide": a / b if b else float("inf"),
|
|
}
|
|
return operations[operation]
|
|
|
|
|
|
@tool(approval_mode="never_require")
|
|
def fetch_data(
|
|
table: Annotated[str, "Name of the simulated table to query."],
|
|
) -> list[dict[str, Any]]:
|
|
"""Fetch simulated records from a named table."""
|
|
data: dict[str, list[dict[str, Any]]] = {
|
|
"users": [
|
|
{"id": 1, "name": "Alice", "role": "admin"},
|
|
{"id": 2, "name": "Bob", "role": "user"},
|
|
{"id": 3, "name": "Charlie", "role": "admin"},
|
|
],
|
|
"products": [
|
|
{"id": 101, "name": "Widget", "price": 9.99},
|
|
{"id": 102, "name": "Gadget", "price": 19.99},
|
|
],
|
|
}
|
|
return data.get(table, [])
|
|
|
|
|
|
@tool(approval_mode="never_require")
|
|
def send_email(
|
|
to: Annotated[str, "Recipient email address."],
|
|
subject: Annotated[str, "Email subject line."],
|
|
body: Annotated[str, "Email body text."],
|
|
) -> str:
|
|
"""Simulate sending an email (direct-only tool, not available inside the sandbox)."""
|
|
return f"Email sent to {to}: {subject}"
|
|
|
|
|
|
async def main() -> None:
|
|
"""Run the manual static-wiring sample."""
|
|
# 1. Create the execute_code tool and register sandbox tools on it.
|
|
execute_code = HyperlightExecuteCodeTool(
|
|
tools=[compute, fetch_data],
|
|
approval_mode="never_require",
|
|
)
|
|
|
|
# 2. Build CodeAct instructions once. Setting tools_visible_to_model=False
|
|
# tells the instructions builder that sandbox tools are not in the agent's
|
|
# direct tool list, so the model must use call_tool(...) inside execute_code.
|
|
codeact_instructions = execute_code.build_instructions(tools_visible_to_model=False)
|
|
|
|
# 3. Create the client and the agent with everything wired at construction time.
|
|
# - send_email is a direct-only tool (not available inside the sandbox).
|
|
# - execute_code carries sandbox tools (compute, fetch_data) via call_tool.
|
|
agent = Agent(
|
|
client=FoundryChatClient(
|
|
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
|
|
model=os.environ["FOUNDRY_MODEL"],
|
|
credential=AzureCliCredential(),
|
|
),
|
|
name="ManualWiringAgent",
|
|
instructions=f"You are a helpful assistant.\n\n{codeact_instructions}",
|
|
tools=[send_email, execute_code],
|
|
)
|
|
|
|
# 4. Run a request that exercises both the sandbox and the direct tool.
|
|
print("=" * 60)
|
|
print("Manual static-wiring CodeAct sample")
|
|
print("=" * 60)
|
|
query = (
|
|
"Fetch all users, find admins, multiply 6*7, and print the users, admins, "
|
|
"and multiplication result. Use one execute_code call. "
|
|
"Then send an email to admin@example.com summarising the results."
|
|
)
|
|
print(f"User: {query}")
|
|
result = await agent.run(query)
|
|
print(f"Agent: {result.text}")
|
|
|
|
|
|
"""
|
|
Sample output (shape only):
|
|
|
|
============================================================
|
|
Manual static-wiring CodeAct sample
|
|
============================================================
|
|
User: Fetch all users, find admins, multiply 6*7, ...
|
|
Agent: ...
|
|
"""
|
|
|
|
|
|
if __name__ == "__main__":
|
|
asyncio.run(main())
|