Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
fda73b7
feat(foundry): add experimental to_prompt_agent converter
eavanvalkenburg May 19, 2026
6e0c047
chore(samples): remove snippet tags from portable agent sample
eavanvalkenburg May 19, 2026
cdca033
chore(samples): inline FoundryChatClient and enable prompt-agent publish
eavanvalkenburg May 19, 2026
01c1634
chore(samples): drop async credential context manager
eavanvalkenburg May 19, 2026
2a14165
docs(foundry): trim README to_prompt_agent example to publish-only flow
eavanvalkenburg May 19, 2026
55e79de
docs(foundry): note FoundryAgent runs @tool callables for deployed pr…
eavanvalkenburg May 19, 2026
4551081
fix(foundry): address review comments on to_prompt_agent converter
eavanvalkenburg May 19, 2026
c9edd1c
fix(foundry): match Agent.__init__ model resolution in to_prompt_agent
eavanvalkenburg May 19, 2026
e5e6438
feat(foundry): add deploy_as_prompt_agent helper + samples
eavanvalkenburg May 20, 2026
881b421
fix(foundry): restore missing-model ValueError in to_prompt_agent
eavanvalkenburg May 20, 2026
c628c2a
refactor(foundry): rename deploy_as_prompt_agent -> create_prompt_agent
eavanvalkenburg May 21, 2026
7c3f41a
refactor(foundry): drop create_prompt_agent, enrich to_prompt_agent p…
eavanvalkenburg May 26, 2026
5e46fd7
refactor(foundry): single source of truth for prompt-agent options
eavanvalkenburg May 27, 2026
31216de
docs(foundry): consolidate prompt-agent sample
eavanvalkenburg May 27, 2026
7bbd8a9
docs(foundry): run local Agent + deployed agent in same sample
eavanvalkenburg May 27, 2026
ec9cf26
test(foundry): cover conflicting response_format + text.format in to_…
eavanvalkenburg May 27, 2026
dc5a740
refactor(foundry): move _prepare_prompt_agent_options into _to_prompt…
eavanvalkenburg May 27, 2026
4dc670a
docs(python): codify GA terminology + post-run docs review
eavanvalkenburg May 27, 2026
d494ac1
address PR review: strict=True default, Tool._deserialize dispatch, s…
eavanvalkenburg May 27, 2026
f26afd4
fix(ci): correct linkspector ignorePattern typo (./pulls -> ./pull)
eavanvalkenburg May 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/.linkspector.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ ignorePatterns:
- pattern: "./blob"
- pattern: "./issues"
- pattern: "./discussions"
- pattern: "./pulls"
- pattern: "./pull"
- pattern: "https:\/\/platform.openai.com"
- pattern: "http:\/\/localhost"
- pattern: "http:\/\/127.0.0.1"
Expand Down
15 changes: 15 additions & 0 deletions python/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,21 @@ When making changes to a package, check if the following need updates:
- The package's `AGENTS.md` file (adding/removing/renaming public APIs, architecture changes, import path changes)
- The agent skills in `.github/skills/` if conventions, commands, or workflows change

At the end of every run, re-read `AGENTS.md` and the relevant skill files and
update any guidance that the conversation revealed to be out of date,
incomplete, or misleading (renamed files, changed commands, new conventions
the user confirmed, etc.). **Before adding a new principle or rule, ask the
user whether they want it captured as a durable principle** — do not invent
team norms from a single conversation without explicit confirmation.

## Terminology

- **Avoid "GA" for Agent Framework code.** Reserve *GA* for hosted services
(e.g. "the Foundry service is GA"). For Agent Framework packages, features,
and APIs use **"released"** or **"stable"** depending on context — these
match the feature-lifecycle stages documented in the
`python-feature-lifecycle` skill.

## Pull Request Description Guidance

When preparing a PR description:
Expand Down
1 change: 1 addition & 0 deletions python/packages/core/agent_framework/_feature_stage.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ class ExperimentalFeature(str, Enum):
FUNCTIONAL_WORKFLOWS = "FUNCTIONAL_WORKFLOWS"
HARNESS = "HARNESS"
SKILLS = "SKILLS"
TO_PROMPT_AGENT = "TO_PROMPT_AGENT"


class ReleaseCandidateFeature(str, Enum):
Expand Down
1 change: 1 addition & 0 deletions python/packages/core/agent_framework/foundry/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"RawFoundryEmbeddingClient": ("agent_framework_foundry", "agent-framework-foundry"),
"evaluate_foundry_target": ("agent_framework_foundry", "agent-framework-foundry"),
"evaluate_traces": ("agent_framework_foundry", "agent-framework-foundry"),
"to_prompt_agent": ("agent_framework_foundry", "agent-framework-foundry"),
}


Expand Down
2 changes: 2 additions & 0 deletions python/packages/core/agent_framework/foundry/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ from agent_framework_foundry import (
RawFoundryEmbeddingClient,
evaluate_foundry_target,
evaluate_traces,
to_prompt_agent,
)
from agent_framework_foundry_local import (
FoundryLocalChatOptions,
Expand Down Expand Up @@ -58,4 +59,5 @@ __all__ = [
"RawFoundryEmbeddingClient",
"evaluate_foundry_target",
"evaluate_traces",
"to_prompt_agent",
]
126 changes: 126 additions & 0 deletions python/packages/foundry/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,129 @@ Generally available factories: `get_code_interpreter_tool`,
| `get_browser_automation_tool(connection_id)` | `BrowserAutomationPreviewTool` |
| `get_bing_custom_search_tool(connection_id, instance_name, ...)` | `BingCustomSearchPreviewTool` |
| `get_a2a_tool(base_url=..., project_connection_id=..., ...)` | `A2APreviewTool` |
## Publishing an agent as a Foundry prompt agent

> **Experimental — `ExperimentalFeature.TO_PROMPT_AGENT`.** `to_prompt_agent`
> is a preview API and may change before reaching GA. The warning fires the
> first time the `TO_PROMPT_AGENT` feature is exercised in a process and is
> then deduplicated.

`to_prompt_agent(agent)` converts an `Agent` whose chat client is a
`FoundryChatClient` into a Foundry `PromptAgentDefinition` that can be
published with `AIProjectClient.agents.create_version(...)`. The model is read
from `default_options["model"]` first and falls back to the bound
`FoundryChatClient.model` (matching `Agent.__init__`'s resolution order), so
the same agent definition you run locally can be published as a hosted prompt
agent without restating the model deployment name.

Every generation parameter that has an Agent Framework equivalent is sourced
from `agent.default_options` and translated into the matching Foundry shape by
`_prepare_prompt_agent_options` (a module-private helper in
`agent_framework_foundry._to_prompt_agent` that reuses the chat client's own
request-path helpers):

| `default_options` key | `PromptAgentDefinition` field |
|---|---|
| `temperature` | `temperature` |
| `top_p` | `top_p` |
| `tool_choice` (dropped when no tools) | `tool_choice` (`str` / `ToolChoiceFunction` / `ToolChoiceAllowed`) |
| `reasoning` (dict or `Reasoning`) | `reasoning` |
| `response_format` (dict or `BaseModel`) | `text.format` |
| `verbosity` | `text.verbosity` |
| `text` | merged into `text` |

This keeps the `Agent` as the single source of truth for everything it can
already express. Only Foundry-specific fields with no Agent Framework
equivalent are accepted as keyword arguments on `to_prompt_agent`:

- `structured_inputs` — `dict[str, StructuredInputDefinition]`
- `rai_config` — `RaiConfig`

```python
import asyncio
import os

from agent_framework import Agent
from agent_framework.foundry import FoundryChatClient, to_prompt_agent
from azure.ai.projects.aio import AIProjectClient
from azure.identity.aio import AzureCliCredential


async def main() -> None:
credential = AzureCliCredential()
project_endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"]

agent = Agent(
client=FoundryChatClient(
project_endpoint=project_endpoint,
model="gpt-4o",
credential=credential,
),
name="travel-agent",
description="Helps Contoso employees book travel.",
instructions="You are a helpful travel assistant.",
tools=[
FoundryChatClient.get_web_search_tool(),
FoundryChatClient.get_code_interpreter_tool(),
],
# Generation parameters set on the Agent flow through automatically.
default_options={
"temperature": 0.3,
"top_p": 0.95,
"reasoning": {"effort": "medium"},
},
)

definition = to_prompt_agent(agent)

project_client = AIProjectClient(endpoint=project_endpoint, credential=credential)
created = await project_client.agents.create_version(
agent_name=agent.name,
definition=definition,
description=agent.description,
)
print(f"Published {created.name} v{created.version}")


asyncio.run(main())
```

Behaviour:

- `agent.client` must be a `FoundryChatClient` (or subclass) — otherwise the
converter raises `TypeError`.
- The bound client must have a `model` set — otherwise the converter raises
`ValueError`.
- Foundry SDK tool instances returned by `FoundryChatClient.get_*_tool()` are
passed through unchanged.
- AF `FunctionTool` instances (and `@tool`-decorated callables) are emitted as
Foundry `FunctionTool` **declarations** — the prompt agent receives the
schema only, not the Python implementation. To execute the function when
invoking the deployed prompt agent, connect with `FoundryAgent` and pass the
same callable via `tools=`:

```python
from agent_framework.foundry import FoundryAgent

deployed = FoundryAgent(
project_endpoint=project_endpoint,
agent_name="travel-agent",
credential=credential,
tools=[book_hotel], # same @tool-decorated callable used at publish time
)
result = await deployed.run("Book me a hotel in Seattle for 3 nights.")
```

`FoundryAgent` runs the function locally when the prompt agent calls it, so
the declaration on the server and the implementation on the client stay in
sync via the shared `@tool` definition.
- Local Agent Framework MCP tools cannot be published as prompt-agent tools —
the converter raises `ValueError` and points at
`FoundryChatClient.get_mcp_tool(...)` for hosted MCP servers.

See the runnable example under `samples/02-agents/providers/foundry/`:

- [`foundry_prompt_agents.py`](../../samples/02-agents/providers/foundry/foundry_prompt_agents.py)
— publish with `to_prompt_agent`, then connect back with `FoundryAgent` and
execute the same local `@tool` callable that the deployed prompt agent
invokes by name.
2 changes: 2 additions & 0 deletions python/packages/foundry/agent_framework_foundry/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
evaluate_traces,
)
from ._memory_provider import FoundryMemoryProvider
from ._to_prompt_agent import to_prompt_agent

try:
__version__ = importlib.metadata.version(__name__)
Expand All @@ -39,4 +40,5 @@
"__version__",
"evaluate_foundry_target",
"evaluate_traces",
"to_prompt_agent",
]
Loading
Loading