Files
agent-framework/python/samples
T
Eduard van Valkenburg 4609535e22 Python: feat: add agent-framework-monty (Monty-backed CodeAct provider) (#5915)
* Python: feat: add agent-framework-monty (Monty-backed CodeAct)

New alpha package that wraps pydantic-monty (a Rust-based Python
interpreter) behind the same CodeAct API surface as
agent-framework-hyperlight, so users can swap providers with minimal
code change.

Public API (agent_framework_monty):
- MontyCodeActProvider — ContextProvider that injects a run-scoped
  execute_code tool plus dynamic CodeAct instructions.
- MontyExecuteCodeTool — standalone FunctionTool for mixed-tool agents
  or manual static wiring.
- FileMount / FileMountInput / MountMode — public types mirroring the
  Hyperlight names, with Monty's mode (read-only/read-write/overlay)
  and write_bytes_limit on FileMount.

Constructor kwargs (both classes) mirror Hyperlight where possible:
tools, approval_mode, workspace_root, file_mounts; plus a Monty-only
resource_limits forwarding ResourceLimits to Monty.start().

Filesystem flow:
- workspace_root auto-mounts at /input (read-write), matching Hyperlight.
- file_mounts accepts string shorthand, (host, mount) tuple, or
  FileMount with mode + write cap.
- Files written under read-write mounts are scanned post-execution and
  returned as Content.from_data items (mirrors Hyperlight /output).
- overlay mounts buffer writes in-memory; read-only mounts reject writes.

Internals:
- _monty_bridge.InlineCodeBridge ports the inline (non-durable) bridge
  from anthonychu/maf-codeact-monty-python; handles FunctionSnapshot /
  FutureSnapshot pause/resume, dispatches direct typed calls + the
  call_tool fallback, forwards mount/limits to Monty.start(...).
- generate_type_stubs emits per-tool stubs so Monty's `ty` type-checker
  rejects bad calls before any host tool runs.

Alpha-policy compliance (per python-package-management skill):
- Added agent-framework-monty = { workspace = true } to root
  pyproject.toml.
- Added row to python/PACKAGE_STATUS.md.
- Added monty entry under Experimental in python/AGENTS.md.
- NOT added to core[all]; NO agent_framework.monty lazy shim (deferred
  to beta promotion).

Samples (three sets, import from agent_framework_monty directly):
- samples/02-agents/context_providers/code_act/monty_code_act.py
  (provider pattern) + updated local README.
- samples/02-agents/tools/monty_code_interpreter/ (standalone +
  manual-wiring + README).
- samples/04-hosting/foundry-hosted-agents/responses/11_monty_codeact/
  (full hosted-agent layout with uv-based pyproject.toml + Dockerfile,
  Azure Monitor wiring via APPLICATIONINSIGHTS_CONNECTION_STRING +
  enable_instrumentation, ENABLE_INSTRUMENTATION and
  ENABLE_SENSITIVE_DATA env vars). The alpha wheel is vendored into
  ./wheels/ (gitignored) via vendor-wheel.sh; new row added to the
  parent Responses-API README.

Tests:
- 28 hermetic unit tests (stubbed pydantic_monty).
- 18 integration tests marked @pytest.mark.integration, auto-skipped
  when pydantic_monty is unimportable; exercise the real Monty
  runtime: print round-trip, last-expression value, direct typed
  tool dispatch, call_tool fallback, async tool, asyncio.gather
  parallelism, ty type-check rejection, OS blocked by default,
  workspace_root read+write capture, read-only / overlay mount
  semantics, resource_limits.max_duration_secs abort, approval
  gating end-to-end, full Agent run with a scripted chat client.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: fix: monty FileMount test compares against the normalized POSIX path

The shorthand string mount goes through _normalize_mount_path, which
rewrites Windows drive letters like 'C:\\Users\\...' into
'/C:/Users/...' (POSIX-style). The Windows CI runners surfaced this
because tmp_path resolves to a backslashed Windows path; the test was
comparing against the raw str(host_a) instead of the normalized form.

Compare against _normalize_mount_path(str(host_a)) so the assertion is
platform-independent.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: fix: address PR #5915 review feedback

- _execute_code_tool docstring: clarify that the Monty backend supports
  scoped filesystem access via workspace_root / file_mounts (blocked by
  default).
- _to_monty_mount: import pydantic_monty lazily through load_monty so
  missing-dependency errors surface as the same actionable RuntimeError
  the rest of the package raises (not a bare ImportError at module load).
  Renamed _load_monty -> load_monty for the same reason.
- _python_type_repr: emit None for type(None) instead of Any, and
  normalize both typing.Union[...] and PEP-604 X | Y to PEP-604 syntax
  so Optional[X] / Union[..., None] / -> None signatures round-trip
  correctly through ty validation. Added a regression test.
- _PrintCollector: track a running character count instead of
  recomputing sum(len(c) for c in self.chunks) per callback. Eliminates
  the O(n^2) cost on print-heavy code.
- Instructions: mention that the value of the final expression is also
  returned alongside captured stdout (matches actual behavior).
- 11_monty_codeact Dockerfile: pin ghcr.io/astral-sh/uv to 0.11.6
  instead of :latest for reproducible builds.
- 11_monty_codeact README: replace the bare "see parent README" pointer
  with sample-specific steps (./vendor-wheel.sh + uv sync + uv run),
  since the sample uses pyproject.toml + a vendored wheel rather than
  requirements.txt.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: sample: 11_monty_codeact installs agent-framework-monty from PyPI

Drop the vendored-wheel scaffolding now that agent-framework-monty is on
PyPI as an alpha (1.0.0a*) release:

- pyproject.toml: remove [tool.uv.sources] override; keep [tool.uv]
  prerelease = "allow" so uv pulls the alpha automatically.
- Dockerfile: drop the COPY wheels/ step.
- README: drop the ./vendor-wheel.sh setup step and the
  not-yet-on-PyPI warning.
- Delete vendor-wheel.sh and the gitignored wheels/ directory.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: fix(monty): harden post-execution file capture against symlink escape

Same class of issue as the MSRC-reported Hyperlight finding: the
post-execution capture walked workspace_root with Path.rglob() +
is_file() + read_bytes() - all of which follow symlinks. An attacker
who controls the workspace (cloned repo, extracted archive, shared
workspace) could pre-place `workspace/leak.txt -> /etc/passwd` or
`workspace/outside_dir -> /etc/` and have host files surface as
captured Content items.

Monty's mount layer already rejects symlink reads from inside the
sandbox across all three modes (verified empirically), so the runtime
path was safe. This commit closes the post-execution scan path.

Changes:
- New `_iter_real_files(root)` walker that uses iterdir() +
  is_symlink() to skip symlinks at every directory level and yields
  only real files. Replaces the previous `host_root.rglob("*")` calls
  in both `_snapshot_writable_mounts` and `_capture_written_files`.
- Use `Path.lstat()` instead of `Path.stat()` so size/mtime can never
  be taken from a symlink target.
- Three new integration tests reproducing the MSRC attack shape
  against the workspace_root flow: symlink-to-file outside workspace,
  symlink-to-directory outside workspace, and a guard ensuring
  legitimate sandbox writes are still captured when symlinks are
  present.

Per user request, hyperlight is untouched in this commit (separate fix).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: fix(monty): skip symlink regression tests when unsupported

Apply the same Windows-CI safety guard as the hyperlight fix in PR #5919:
the three symlink integration tests create symlinks via Path.symlink_to(),
which fails with OSError / NotImplementedError on unprivileged Windows
runners. Add a local _symlinks_supported helper (mirroring the one in
packages/core/tests/core/test_skills.py) and pytest.skip when symlinks
aren't available, so the tests no longer fail for environment reasons.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: fix(monty): address PR #5915 follow-up review feedback

- _invoke_tool: drop the inspect.iscoroutinefunction(...) branch and
  always `await self.tool_map[name](**kwargs)`. Every entry in
  tool_map is `partial(FunctionTool.invoke, skip_parsing=True)` and
  FunctionTool.invoke is `async def`, so the branching was dead code -
  and on Python versions affected by cpython#98590,
  iscoroutinefunction(partial(bound_async_method, ...)) returns False,
  causing the bridge to take the asyncio.to_thread path, return an
  unawaited coroutine, and surface it as a JSON-serialization failure
  for every tool call. Added a regression test
  test_invoke_tool_awaits_partial_wrapped_async_method.

- generate_type_stubs: skip tools whose name is not a valid Python
  identifier or is a Python keyword. FunctionTool.name has no upstream
  validation, so a name like "weird-name" produced a syntax error in
  the stubs and a name like "broken\n    pass\nasync def injected"
  would inject arbitrary stub source. Non-identifier names stay
  reachable via `call_tool("weird-name", ...)` at runtime; they just
  don't get type-checked stubs. Added regression test
  test_generate_type_stubs_skips_non_identifier_tool_names.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
4609535e22 · 2026-05-20 00:35:23 +00:00
History
..
2025-07-28 07:33:42 +00:00

Python Samples

This directory contains samples demonstrating the capabilities of Microsoft Agent Framework for Python.

Structure

Folder Description
01-get-started/ Progressive tutorial: hello agent → hosting
02-agents/ Deep-dive by concept: tools, middleware, providers, orchestrations
03-workflows/ Workflow patterns: sequential, concurrent, state, declarative, explicit output designation
04-hosting/ Deployment: Azure Functions, Durable Tasks, A2A
05-end-to-end/ Full applications, evaluation, demos

Getting Started

Start with 01-get-started/ and work through the numbered files:

  1. 01_hello_agent.py — Create and run your first agent
  2. 02_add_tools.py — Add function tools with @tool
  3. 03_multi_turn.py — Multi-turn conversations with AgentSession
  4. 04_memory.py — Agent memory with ContextProvider
  5. 05_functional_workflow_with_agents.py — Call agents inside a functional workflow
  6. 06_functional_workflow_basics.py — Write a workflow as a plain async function
  7. 07_first_graph_workflow.py — Build a workflow with executors and edges
  8. 08_host_your_agent.py — Host your agent via Azure Functions

Prerequisites

pip install agent-framework

Environment Variables

Samples call load_dotenv() to automatically load environment variables from a .env file in the python/ directory. This is a convenience for local development and testing.

For local development, set up your environment using any of these methods:

Option 1: Using a .env file (recommended for local development):

  1. Copy .env.example to .env in the python/ directory:
    cp .env.example .env
    
  2. Edit .env and set your values (API keys, endpoints, etc.)

Option 2: Export environment variables directly:

export FOUNDRY_PROJECT_ENDPOINT="your-foundry-project-endpoint"
export FOUNDRY_MODEL="gpt-4o"

Option 3: Using env_file_path parameter (for per-client configuration):

All client classes (e.g., OpenAIChatClient, OpenAIChatCompletionClient) support an env_file_path parameter to load environment variables from a specific file:

from agent_framework.openai import OpenAIChatClient

# Load from a custom .env file
client = OpenAIChatClient(env_file_path="path/to/custom.env")

This allows different clients to use different configuration files if needed.

For the generic OpenAI clients (OpenAIChatClient and OpenAIChatCompletionClient), routing precedence is:

  1. Explicit Azure inputs such as credential, azure_endpoint, or api_version
  2. OPENAI_API_KEY / explicit OpenAI API-key parameters
  3. Azure environment fallback such as AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_API_KEY

If you keep both OpenAI and Azure variables in your shell, the generic clients stay on OpenAI until you pass an explicit Azure input.

For the getting-started samples, you'll need at minimum:

FOUNDRY_PROJECT_ENDPOINT="your-foundry-project-endpoint"
FOUNDRY_MODEL="gpt-4o"

Consolidated sample env inventory

This is the single source of truth for package-level environment variables read by packages included by agent-framework-core[all]. It intentionally excludes variables that are only read by standalone samples, package sample folders, or tests. When package code adds, removes, or renames an environment variable, update this table in the same change.

Example values below are illustrative. For entries not backed by a single public class, the class column names the closest public surface, helper, or package-level initialization point that reads the variable.

package class env var example value
agent-framework-anthropic AnthropicClient ANTHROPIC_API_KEY sk-ant-api03-...
agent-framework-anthropic AnthropicClient ANTHROPIC_CHAT_MODEL claude-sonnet-4-5-20250929
agent-framework-foundry FoundryEmbeddingClient FOUNDRY_MODELS_ENDPOINT https://my-endpoint.inference.ai.azure.com
agent-framework-foundry FoundryEmbeddingClient FOUNDRY_MODELS_API_KEY env-key
agent-framework-foundry FoundryEmbeddingClient FOUNDRY_EMBEDDING_MODEL text-embedding-3-small
agent-framework-foundry FoundryEmbeddingClient FOUNDRY_IMAGE_EMBEDDING_MODEL Cohere-embed-v3-english
agent-framework-azure-ai-search AzureAISearchContextProvider AZURE_SEARCH_ENDPOINT https://my-search.search.windows.net
agent-framework-azure-ai-search AzureAISearchContextProvider AZURE_SEARCH_API_KEY search-key
agent-framework-azure-ai-search AzureAISearchContextProvider AZURE_SEARCH_INDEX_NAME hotels-index
agent-framework-azure-ai-search AzureAISearchContextProvider AZURE_SEARCH_KNOWLEDGE_BASE_NAME hotels-kb
agent-framework-azure-cosmos CosmosHistoryProvider AZURE_COSMOS_ENDPOINT https://my-cosmos.documents.azure.com:443/
agent-framework-azure-cosmos CosmosHistoryProvider AZURE_COSMOS_DATABASE_NAME agent-history
agent-framework-azure-cosmos CosmosHistoryProvider AZURE_COSMOS_CONTAINER_NAME messages
agent-framework-azure-cosmos CosmosHistoryProvider AZURE_COSMOS_KEY C2F...==
agent-framework-bedrock BedrockChatClient BEDROCK_REGION us-east-1
agent-framework-bedrock BedrockChatClient BEDROCK_CHAT_MODEL anthropic.claude-3-5-sonnet-20241022-v2:0
agent-framework-bedrock BedrockEmbeddingClient BEDROCK_REGION us-east-1
agent-framework-bedrock BedrockEmbeddingClient BEDROCK_EMBEDDING_MODEL amazon.titan-embed-text-v2:0
agent-framework-bedrock BedrockChatClient / BedrockEmbeddingClient AWS_ACCESS_KEY_ID AKIAIOSFODNN7EXAMPLE
agent-framework-bedrock BedrockChatClient / BedrockEmbeddingClient AWS_SECRET_ACCESS_KEY wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
agent-framework-bedrock BedrockChatClient / BedrockEmbeddingClient AWS_SESSION_TOKEN IQoJb3JpZ2luX2VjEO7//////////wEaCXVzLXdlc3QtMiJHMEUCIQD...
agent-framework-copilotstudio CopilotStudioAgent COPILOTSTUDIOAGENT__ENVIRONMENTID 00000000-0000-0000-0000-000000000000
agent-framework-copilotstudio CopilotStudioAgent COPILOTSTUDIOAGENT__SCHEMANAME cr123_agentname
agent-framework-copilotstudio CopilotStudioAgent COPILOTSTUDIOAGENT__TENANTID 11111111-1111-1111-1111-111111111111
agent-framework-copilotstudio CopilotStudioAgent COPILOTSTUDIOAGENT__AGENTAPPID 22222222-2222-2222-2222-222222222222
agent-framework-core enable_instrumentation() ENABLE_INSTRUMENTATION true
agent-framework-core enable_instrumentation() ENABLE_SENSITIVE_DATA false
agent-framework-core enable_instrumentation() ENABLE_CONSOLE_EXPORTERS true
agent-framework-core enable_instrumentation() OTEL_EXPORTER_OTLP_ENDPOINT http://localhost:4317
agent-framework-core enable_instrumentation() OTEL_EXPORTER_OTLP_TRACES_ENDPOINT http://localhost:4318/v1/traces
agent-framework-core enable_instrumentation() OTEL_EXPORTER_OTLP_METRICS_ENDPOINT http://localhost:4318/v1/metrics
agent-framework-core enable_instrumentation() OTEL_EXPORTER_OTLP_LOGS_ENDPOINT http://localhost:4318/v1/logs
agent-framework-core enable_instrumentation() OTEL_EXPORTER_OTLP_PROTOCOL grpc
agent-framework-core enable_instrumentation() OTEL_EXPORTER_OTLP_HEADERS api-key=demo
agent-framework-core enable_instrumentation() OTEL_EXPORTER_OTLP_TRACES_HEADERS api-key=trace-demo
agent-framework-core enable_instrumentation() OTEL_EXPORTER_OTLP_METRICS_HEADERS api-key=metric-demo
agent-framework-core enable_instrumentation() OTEL_EXPORTER_OTLP_LOGS_HEADERS api-key=log-demo
agent-framework-core enable_instrumentation() OTEL_SERVICE_NAME sample-agent
agent-framework-core enable_instrumentation() OTEL_SERVICE_VERSION 1.0.0
agent-framework-core enable_instrumentation() OTEL_RESOURCE_ATTRIBUTES deployment.environment=dev,service.namespace=agent-framework
agent-framework-devui DevUI server DEVUI_AUTH_TOKEN my-devui-token
agent-framework-foundry FoundryChatClient FOUNDRY_PROJECT_ENDPOINT https://my-project.services.ai.azure.com/api/projects/my-project
agent-framework-foundry FoundryChatClient FOUNDRY_MODEL gpt-4o
agent-framework-foundry FoundryAgent FOUNDRY_AGENT_NAME travel-planner
agent-framework-foundry FoundryAgent FOUNDRY_AGENT_VERSION v1
agent-framework-github-copilot GitHubCopilotAgent GITHUB_COPILOT_CLI_PATH copilot
agent-framework-github-copilot GitHubCopilotAgent GITHUB_COPILOT_MODEL gpt-5
agent-framework-github-copilot GitHubCopilotAgent GITHUB_COPILOT_TIMEOUT 60
agent-framework-github-copilot GitHubCopilotAgent GITHUB_COPILOT_LOG_LEVEL info
agent-framework-mem0 agent_framework_mem0 package import MEM0_TELEMETRY false
agent-framework-ollama OllamaChatClient OLLAMA_HOST http://localhost:11434
agent-framework-ollama OllamaChatClient OLLAMA_MODEL llama3.1:8b
agent-framework-openai OpenAIChatClient / OpenAIChatCompletionClient / OpenAIEmbeddingClient OPENAI_API_KEY sk-proj-...
agent-framework-openai OpenAIChatClient / OpenAIChatCompletionClient / OpenAIEmbeddingClient OPENAI_MODEL gpt-4o-mini
agent-framework-openai OpenAIChatClient OPENAI_CHAT_MODEL gpt-4.1-mini
agent-framework-openai OpenAIChatCompletionClient OPENAI_CHAT_COMPLETION_MODEL gpt-4o
agent-framework-openai OpenAIEmbeddingClient OPENAI_EMBEDDING_MODEL text-embedding-3-small
agent-framework-openai OpenAIChatClient / OpenAIChatCompletionClient / OpenAIEmbeddingClient OPENAI_BASE_URL https://api.openai.com/v1/
agent-framework-openai OpenAIChatClient / OpenAIChatCompletionClient / OpenAIEmbeddingClient OPENAI_ORG_ID org_123456789
agent-framework-openai OpenAIChatClient / OpenAIChatCompletionClient / OpenAIEmbeddingClient AZURE_OPENAI_ENDPOINT https://my-resource.openai.azure.com/
agent-framework-openai OpenAIChatClient / OpenAIChatCompletionClient / OpenAIEmbeddingClient AZURE_OPENAI_API_KEY sk-azure-...
agent-framework-openai OpenAIChatClient / OpenAIChatCompletionClient / OpenAIEmbeddingClient AZURE_OPENAI_API_VERSION 2024-10-21
agent-framework-openai OpenAIChatClient / OpenAIChatCompletionClient / OpenAIEmbeddingClient AZURE_OPENAI_BASE_URL https://my-resource.openai.azure.com/openai/v1/
agent-framework-openai OpenAIChatClient / OpenAIChatCompletionClient / OpenAIEmbeddingClient AZURE_OPENAI_MODEL gpt-4o
agent-framework-openai OpenAIChatClient AZURE_OPENAI_CHAT_MODEL gpt-4.1
agent-framework-openai OpenAIChatCompletionClient AZURE_OPENAI_CHAT_COMPLETION_MODEL gpt-4o-mini
agent-framework-openai OpenAIEmbeddingClient AZURE_OPENAI_EMBEDDING_MODEL text-embedding-3-large
agent-framework-openai OpenAIChatClient / OpenAIChatCompletionClient / OpenAIEmbeddingClient AZURE_OPENAI_RESOURCE_URL https://cognitiveservices.azure.com/

agent-framework-openai supports the Azure OpenAI client-specific deployment aliases listed above; keep packages/openai/README.md as the authoritative reference for the exact fallback order and package-specific behavior.

Note for production: In production environments, set environment variables through your deployment platform (e.g., Azure App Settings, Kubernetes ConfigMaps/Secrets) rather than using .env files. The load_dotenv() call in samples will have no effect when a .env file is not present, allowing environment variables to be loaded from the system.

For Azure authentication, run az login before running samples.

Note on XML tags

Some sample files include XML-style snippet tags (for example <snippet_name> and </snippet_name>). These are used by our documentation tooling and can be ignored or removed when you use the samples outside this repository.

Additional Resources