Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ async def contact_mothership(query: str) -> str:

async def main() -> None:
model = ai.get_model("anthropic/claude-sonnet-4")
agent = ai.agent(tools=[contact_mothership])
agent = ai.Agent(tools=[contact_mothership])

messages = [
ai.system_message(
Expand Down
2 changes: 1 addition & 1 deletion docs/ai-python/content/docs/basics/agents.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ async def contact_mothership(query: str) -> str:

async def main() -> None:
model = ai.get_model("anthropic/claude-sonnet-4")
agent = ai.agent(tools=[contact_mothership])
agent = ai.Agent(tools=[contact_mothership])
messages = [
ai.system_message(
"Use the contact_mothership tool when asked about the future."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ mothership_model = ai.get_model("anthropic/claude-sonnet-4")
@ai.tool
async def ask_mothership(topic: str) -> ai.SubAgentTool:
"""Ask a specialist agent for mothership guidance."""
sub_agent = ai.agent()
sub_agent = ai.Agent()
sub_messages = [
ai.system_message("Answer as the mothership operations desk."),
ai.user_message(topic),
Expand Down
2 changes: 1 addition & 1 deletion docs/ai-python/content/docs/basics/tools.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ tools = await ai.mcp.get_http_tools(
headers={"Authorization": "Bearer your_access_token_here"},
)

agent = ai.agent(tools=tools)
agent = ai.Agent(tools=tools)
```

Use `ai.mcp.get_stdio_tools` for subprocess-based MCP servers.
10 changes: 4 additions & 6 deletions docs/ai-python/content/docs/reference/ai/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -236,21 +236,19 @@ Part builder exports:

## Tools and Agents

### agent
### Agent

`agent` creates an `Agent`.
`Agent` runs the default agent loop.

```python
agent = ai.agent(tools=[contact_mothership])
agent = ai.Agent(tools=[contact_mothership])
```

Arguments:

- `tools`: Optional `AgentTool` values from `tool` and schema-only `Tool`
declarations for provider-executed tools.

Returns `Agent`.

### AgentTool

`AgentTool` binds a model-facing `Tool` declaration to an executable Python
Expand All @@ -264,7 +262,7 @@ tool.validator
tool.require_approval
```

Pass `AgentTool` values to `agent(tools=[...])`.
Pass `AgentTool` values to `Agent(tools=[...])`.

### Context

Expand Down
4 changes: 2 additions & 2 deletions docs/ai-python/content/docs/reference/mcp.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ uv add "ai[mcp]"
## get_stdio_tools

`get_stdio_tools` starts an MCP server subprocess and returns `AgentTool`
values that can be passed to `ai.agent`.
values that can be passed to `ai.Agent`.

```python
tools = await ai.mcp.get_stdio_tools(
Expand All @@ -35,7 +35,7 @@ Arguments:
## get_http_tools

`get_http_tools` connects to an MCP HTTP server and returns `AgentTool` values
that can be passed to `ai.agent`.
that can be passed to `ai.Agent`.

```python
tools = await ai.mcp.get_http_tools("https://example.com/mcp")
Expand Down
2 changes: 1 addition & 1 deletion skills/ai-python-basics/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ async def get_weather(city: str) -> str:

async def main() -> None:
model = ai.get_model("anthropic/claude-sonnet-4")
agent = ai.agent(tools=[get_weather])
agent = ai.Agent(tools=[get_weather])
messages = [
ai.system_message("Use tools when useful."),
ai.user_message("What is the weather in San Francisco?"),
Expand Down
2 changes: 1 addition & 1 deletion skills/ai-python-streaming-tools/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ Use `ai.SubAgentTool` when a tool runs another agent and streams its events.
```python
@ai.tool
async def research(topic: str) -> ai.SubAgentTool:
researcher = ai.agent()
researcher = ai.Agent()

messages = [
ai.system_message("Research briefly."),
Expand Down
2 changes: 1 addition & 1 deletion skills/ai-python-subagents/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ agent.
```python
@ai.tool
async def research(topic: str) -> ai.SubAgentTool:
child = ai.agent(tools=[lookup])
child = ai.Agent(tools=[lookup])
messages = [
ai.system_message("Research briefly."),
ai.user_message(topic),
Expand Down
2 changes: 0 additions & 2 deletions src/ai/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
ToolCall,
ToolRunner,
abort_pending_hook,
agent,
cancel_hook,
hook,
mcp,
Expand Down Expand Up @@ -176,7 +175,6 @@
"UnsupportedProviderError",
"VideoParams",
"abort_pending_hook",
"agent",
"assistant_message",
"cancel_hook",
"content_output",
Expand Down
10 changes: 0 additions & 10 deletions src/ai/_types.py

This file was deleted.

2 changes: 0 additions & 2 deletions src/ai/agents/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
ToolCall,
ToolCallCallable,
ToolRunner,
agent,
pending_tool_result,
tool,
tool_result,
Expand Down Expand Up @@ -53,7 +52,6 @@
"ToolCallCallable",
"ToolRunner",
"abort_pending_hook",
"agent",
"cancel_hook",
"hook",
"mcp",
Expand Down
10 changes: 1 addition & 9 deletions src/ai/agents/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ async def fetch(url: str) -> StreamingStatusTool[str]:

@ai.tool
async def research(topic: str) -> SubAgentTool:
sub = ai.agent(tools=[...])
sub = ai.Agent(tools=[...])
async with sub.run(model, messages) as stream:
async for event in stream:
yield event
Expand Down Expand Up @@ -1394,11 +1394,3 @@ async def _stream() -> AsyncGenerator[events_.AgentEvent]:
middleware_.deactivate(mw_token)

yield AgentStream(_stream(), context)


def agent(
*,
tools: Sequence[AgentTool | Tool] | None = None,
) -> Agent:
"""Create an Agent."""
return Agent(tools=tools)
3 changes: 1 addition & 2 deletions src/ai/types/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
from . import events, media, messages, proto, tools, usage
from . import events, media, messages, tools, usage

__all__ = [
"events",
"media",
"messages",
"proto",
"tools",
"usage",
]
3 changes: 0 additions & 3 deletions src/ai/types/proto.py

This file was deleted.

2 changes: 1 addition & 1 deletion tests/agents/mcp/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ async def fake_fn(**kwargs: str) -> str:
# Replace the executor (which would try to connect) with our fake.
native = dataclasses.replace(native, fn=fake_fn)

my_agent = ai.agent(tools=[native])
my_agent = ai.Agent(tools=[native])

call1 = [
tool_call_msg(
Expand Down
2 changes: 1 addition & 1 deletion tests/agents/test_aggregate_marker.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ async def alias_progress_tool(query: str) -> ai.StreamingStatusTool[str]:

async def test_alias_declared_tool_runs_end_to_end() -> None:
"""An alias-declared streaming tool behaves like the kwarg form."""
my_agent = ai.agent(tools=[alias_progress_tool])
my_agent = ai.Agent(tools=[alias_progress_tool])

call = [
tool_call_msg(
Expand Down
6 changes: 3 additions & 3 deletions tests/agents/test_generator_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ async def progress_tool(query: str) -> AsyncGenerator[str]:
async def test_generator_tool_streams_and_returns_result() -> None:
"""Generator tool yields streaming events visible to consumer;
final text becomes the tool result fed back to the LLM."""
my_agent = ai.agent(tools=[progress_tool])
my_agent = ai.Agent(tools=[progress_tool])

# Turn 1: LLM calls progress_tool
# Turn 2: LLM produces final text after seeing the tool result
Expand Down Expand Up @@ -119,7 +119,7 @@ async def inner_fact(topic: str) -> str:
@ai.tool(aggregator=ai.agents.MessageAggregator)
async def research_tool(topic: str) -> AsyncGenerator[agent_events_.AgentEvent]:
"""Nested agent that researches a topic."""
inner = ai.agent(tools=[inner_fact])
inner = ai.Agent(tools=[inner_fact])

msgs = [
ai.system_message("Be concise."),
Expand All @@ -138,7 +138,7 @@ async def test_yield_from_nested_agent() -> None:
queue and discards bare Messages, so the parent agent's
context.messages stays clean.
"""
outer = ai.agent(tools=[research_tool])
outer = ai.Agent(tools=[research_tool])

# Inner agent: text-only reply (no tools, to keep it simple)
inner_reply = [text_msg("Mars has two moons.", id="inner-msg")]
Expand Down
14 changes: 7 additions & 7 deletions tests/agents/test_run_blocked.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def _multi_call_msg(*tc: tuple[str, str]) -> messages_.Message:

async def test_gated_tool_block_cycle() -> None:
"""Park on approval -> RunBlocked; the resolved HookEvent unblocks."""
my_agent = ai.agent(tools=[gated])
my_agent = ai.Agent(tools=[gated])
mock_llm(
[
[tool_call_msg(name="gated", args='{"x": 1}')],
Expand Down Expand Up @@ -118,7 +118,7 @@ async def slow(x: int) -> str:
await release.wait()
return "slow done"

my_agent = ai.agent(tools=[gated, slow])
my_agent = ai.Agent(tools=[gated, slow])
mock_llm(
[
[_multi_call_msg(("tc-1", "gated"), ("tc-2", "slow"))],
Expand Down Expand Up @@ -154,7 +154,7 @@ async def slow(x: int) -> str:
async def test_parallel_gated_tools() -> None:
"""Blocking needs *every* in-flight tool parked, and the signal
re-fires when the run parks again on the remaining hook."""
my_agent = ai.agent(tools=[gated])
my_agent = ai.Agent(tools=[gated])
mock_llm(
[
[_multi_call_msg(("tc-1", "gated"), ("tc-2", "gated"))],
Expand Down Expand Up @@ -203,7 +203,7 @@ async def loop(

async def test_abort_leaves_blocked() -> None:
"""Serverless abort: the run ends still blocked, hooks still pending."""
my_agent = ai.agent(tools=[gated])
my_agent = ai.Agent(tools=[gated])
mock_llm([[tool_call_msg(name="gated", args='{"x": 1}')]])

blocked_events: list[events_.RunBlocked] = []
Expand Down Expand Up @@ -231,7 +231,7 @@ async def plain(x: int) -> str:
"""A plain tool."""
return "plain done"

my_agent = ai.agent(tools=[plain])
my_agent = ai.Agent(tools=[plain])
mock_llm(
[
[tool_call_msg(name="plain", args='{"x": 1}')],
Expand Down Expand Up @@ -260,7 +260,7 @@ async def test_block_signal_waits_for_stream_end() -> None:
messages_.TextPart(text="still streaming..."),
],
)
my_agent = ai.agent(tools=[gated])
my_agent = ai.Agent(tools=[gated])
mock_llm([[msg], [text_msg("done", id="msg-2")]])

delivered: list[events_.AgentEvent] = []
Expand Down Expand Up @@ -291,7 +291,7 @@ async def self_gated(x: int) -> str:
await ai.hook("self_gate", payload=Confirmation)
return "ran"

my_agent = ai.agent(tools=[self_gated])
my_agent = ai.Agent(tools=[self_gated])
mock_llm(
[
[tool_call_msg(name="self_gated", args='{"x": 1}')],
Expand Down
8 changes: 4 additions & 4 deletions tests/agents/test_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ async def concat(a: str, b: str) -> str:

async def test_agent_text_only() -> None:
"""Agent default loop with no tool calls returns after one LLM call."""
my_agent = ai.agent(tools=[double])
my_agent = ai.Agent(tools=[double])

llm = mock_llm([[text_msg("Hello!")]])
async with my_agent.run(MOCK_MODEL, [ai.user_message("Hi")]) as stream:
Expand All @@ -47,7 +47,7 @@ async def test_agent_text_only() -> None:

async def test_agent_tool_then_text() -> None:
"""Agent default loop calls tool, feeds result back, gets final text."""
my_agent = ai.agent(tools=[double])
my_agent = ai.Agent(tools=[double])

call1 = [tool_call_msg(tc_id="tc-1", name="double", args='{"x": 5}')]
call2 = [text_msg("The answer is 10.")]
Expand All @@ -69,7 +69,7 @@ async def test_agent_tool_then_text() -> None:

async def test_agent_parallel_tools() -> None:
"""LLM returns two tool calls in one message; both execute."""
my_agent = ai.agent(tools=[double])
my_agent = ai.Agent(tools=[double])

two_tools = messages.Message(
id="msg-1",
Expand Down Expand Up @@ -104,7 +104,7 @@ async def test_agent_parallel_tools() -> None:

async def test_agent_multi_turn() -> None:
"""LLM calls a tool, then calls another tool, then returns text."""
my_agent = ai.agent(tools=[double, concat])
my_agent = ai.Agent(tools=[double, concat])

turn1 = [
tool_call_msg(
Expand Down
6 changes: 3 additions & 3 deletions tests/agents/test_validation_error_approval.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ async def edit(path: str, edits: list[TextEdit]) -> str:
async def test_invalid_args_with_approval_returns_error_result() -> None:
"""Invalid tool args on an approval-gated tool should produce an error
tool result without firing the approval hook."""
my_agent = ai.agent(tools=[edit])
my_agent = ai.Agent(tools=[edit])

# Model sends edits as a dict instead of a list — this will fail validation
bad_call = messages_.Message(
Expand Down Expand Up @@ -86,7 +86,7 @@ async def test_invalid_args_with_approval_returns_error_result() -> None:
async def test_invalid_args_skips_approval_hook() -> None:
"""Invalid args should produce a validation error result without
ever prompting for approval."""
my_agent = ai.agent(tools=[edit])
my_agent = ai.Agent(tools=[edit])

bad_call = messages_.Message(
id="msg-1",
Expand Down Expand Up @@ -132,7 +132,7 @@ async def test_invalid_args_skips_approval_hook() -> None:

async def test_completely_invalid_json_with_approval() -> None:
"""Completely unparseable tool_args should also be handled gracefully."""
my_agent = ai.agent(tools=[edit])
my_agent = ai.Agent(tools=[edit])

bad_call = messages_.Message(
id="msg-1",
Expand Down
Loading
Loading