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
17 changes: 12 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ async def contact_mothership(query: str) -> str:


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

messages = [
Expand Down Expand Up @@ -155,10 +155,17 @@ ai.resolve_hook("approve_send_email", {"granted": True, "reason": "approved"})

## Examples

Focused samples live in `examples/`.
Focused samples live in category directories under `examples/`.

- `examples/agents/` - agent loops, tools, hooks, and MCP
- `examples/media/` - image, video, and multimodal input/output
- `examples/models/` - streaming, structured output, and provider examples
- `examples/apps/` - end-to-end demos

End-to-end demos:

- `examples/fastapi-vite/` - FastAPI + React chat with tool approval
- `examples/multiagent-textual/` - parallel agents with terminal hook resolution
- `examples/temporal-direct/` - durable agent with a custom loop
- `examples/apps/web_agent/` - FastAPI + React chat with tool approval
- `examples/apps/coding_agent/` - coding agent
- `examples/apps/durable_agent_temporal/` - durable agent with Temporal
- `examples/apps/durable_agent_workflows/` - durable agent with Workflows
- `examples/apps/slack_agent/` - Slack agent
21 changes: 11 additions & 10 deletions docs/ai-python/content/docs/basics/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,17 @@ needs those control points.

## Example map

Focused samples live in `examples/`:
Focused samples live in category directories under `examples/`:

- `stream.py` streams text from a model.
- `tools_schema.py` passes a schema-only tool to a model.
- `agent_simple.py` runs the default agent loop.
- `agent_custom_loop.py` overrides `Agent.loop`.
- `streaming_tool.py` streams partial output from a tool.
- `agent_nested.py` runs a subagent as a tool.
- `examples/models/stream.py` streams text from a model.
- `examples/agents/basic.py` runs the default agent loop.
- `examples/agents/custom_loop.py` overrides `Agent.loop`.
- `examples/agents/streaming_tool.py` streams partial output from a tool.
- `examples/agents/subagent.py` runs a subagent as a tool.

End-to-end demos live in
[`examples/fastapi-vite`](https://github.com/vercel-labs/ai-python/tree/main/examples/fastapi-vite),
[`examples/multiagent-textual`](https://github.com/vercel-labs/ai-python/tree/main/examples/multiagent-textual),
and [`examples/temporal-direct`](https://github.com/vercel-labs/ai-python/tree/main/examples/temporal-direct).
[`examples/apps/web_agent`](https://github.com/vercel-labs/ai-python/tree/main/examples/apps/web_agent),
[`examples/apps/coding_agent`](https://github.com/vercel-labs/ai-python/tree/main/examples/apps/coding_agent),
[`examples/apps/durable_agent_temporal`](https://github.com/vercel-labs/ai-python/tree/main/examples/apps/durable_agent_temporal),
[`examples/apps/durable_agent_workflows`](https://github.com/vercel-labs/ai-python/tree/main/examples/apps/durable_agent_workflows),
and [`examples/apps/slack_agent`](https://github.com/vercel-labs/ai-python/tree/main/examples/apps/slack_agent).
17 changes: 11 additions & 6 deletions docs/ai-python/content/docs/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,17 @@ After iteration, `s.message`, `s.text`, `s.tool_calls`, `s.output`, and
- **Core concepts**: Learn the primitives that shape the SDK.
- **Streaming**: Stream model responses and inspect events.
- **Agents**: Add tools, customize the loop, and handle approvals.
- **Samples**: Focused, single-file examples live in
- **Samples**: Focused, single-file examples live in the `agents`, `media`,
and `models` directories under
[`examples/`](https://github.com/vercel-labs/ai-python/tree/main/examples).
- **End-to-end demos**:
[`fastapi-vite`](https://github.com/vercel-labs/ai-python/tree/main/examples/fastapi-vite)
[`web_agent`](https://github.com/vercel-labs/ai-python/tree/main/examples/apps/web_agent)
(web chat with human-in-the-loop approval),
[`multiagent-textual`](https://github.com/vercel-labs/ai-python/tree/main/examples/multiagent-textual)
(parallel agents with a terminal UI),
[`temporal-direct`](https://github.com/vercel-labs/ai-python/tree/main/examples/temporal-direct)
(durable agent runs).
[`coding_agent`](https://github.com/vercel-labs/ai-python/tree/main/examples/apps/coding_agent)
(coding agent),
[`durable_agent_temporal`](https://github.com/vercel-labs/ai-python/tree/main/examples/apps/durable_agent_temporal)
(durable agent runs with Temporal),
[`durable_agent_workflows`](https://github.com/vercel-labs/ai-python/tree/main/examples/apps/durable_agent_workflows)
(durable agent runs with Workflows), and
[`slack_agent`](https://github.com/vercel-labs/ai-python/tree/main/examples/apps/slack_agent)
(Slack agent).
36 changes: 26 additions & 10 deletions examples/.test_scripts/check-examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,44 @@

REPO = Path(__file__).resolve().parent.parent.parent
MYPY_VERSION = "mypy>=1.11"
_EXAMPLES_DIR = REPO / "examples"

_SAMPLE_FILES = sorted(p.name for p in (REPO / "examples").glob("*.py"))
_SAMPLE_FILES = sorted(
str(p.relative_to(_EXAMPLES_DIR))
for p in _EXAMPLES_DIR.rglob("*.py")
if ".test_scripts" not in p.parts
and p.relative_to(_EXAMPLES_DIR).parts[:1] != ("apps",)
)

# Each entry: (display name, directory to check, extra --with deps, targets)
EXAMPLES: list[tuple[str, Path, list[str], list[str]]] = [
("samples", REPO / "examples", [], _SAMPLE_FILES),
("samples", _EXAMPLES_DIR, [], _SAMPLE_FILES),
(
"fastapi-vite/backend",
REPO / "examples" / "fastapi-vite" / "backend",
"web_agent/backend",
_EXAMPLES_DIR / "apps" / "web_agent" / "backend",
["fastapi"],
["."],
),
(
"multiagent-textual",
REPO / "examples" / "multiagent-textual",
["fastapi", "textual", "websockets"],
"durable_agent_temporal",
_EXAMPLES_DIR / "apps" / "durable_agent_temporal",
["temporalio"],
["."],
),
(
"temporal-direct",
REPO / "examples" / "temporal-direct",
["temporalio"],
"durable_agent_workflows/backend",
_EXAMPLES_DIR / "apps" / "durable_agent_workflows" / "backend",
[
"fastapi",
"vercel @ git+https://github.com/vercel/vercel-py@47d881242e35b18dcbc7d58b7018e1188894f332",
"vercel-workers>=0.0.22",
],
["."],
),
(
"slack_agent",
_EXAMPLES_DIR / "apps" / "slack_agent",
["slack-bolt>=1.29", "aiohttp"],
["."],
),
]
Expand Down
102 changes: 39 additions & 63 deletions examples/.test_scripts/run-examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
uv run examples/.test_scripts/run-examples.py --e2e # also e2e test scripts
uv run examples/.test_scripts/run-examples.py --all # everything
uv run examples/.test_scripts/run-examples.py --parallel # in parallel
uv run examples/.test_scripts/run-examples.py stream.py tools_schema.py
uv run examples/.test_scripts/run-examples.py models/stream.py
# run selected example files
uv run examples/.test_scripts/run-examples.py --model MODEL
# patch ai.get_model() to use the given model for every sample
Expand Down Expand Up @@ -40,87 +40,58 @@ class Sample:


TEXT_SAMPLES = [
Sample("stream.py"),
Sample("stream_all.py"),
Sample("tools_schema.py"),
Sample("agent_simple.py"),
Sample("agent_custom_loop.py"),
Sample("agent_nested.py"),
Sample("streaming_tool.py"),
Sample("openai_chat_completions.py"),
Sample("explicit_client.py"),
Sample("multimodal_input.py"),
Sample("check_connection.py"),
Sample("agent_hooks.py", stdin="y\n"),
Sample("agent_hooks_inline.py", stdin="y\n"),
Sample("agent_hooks_serverless.py"),
Sample("mcp_tools.py"),
Sample("builtin_web_search.py"),
Sample("models/stream.py"),
Sample("models/gateway/stream.py"),
Sample("models/anthropic/stream.py"),
Sample("models/openai/stream.py"),
Sample("agents/basic.py"),
Sample("agents/custom_loop.py"),
Sample("agents/subagent.py"),
Sample("agents/streaming_tool.py"),
Sample("models/openai/openai_chat_completions.py"),
Sample("models/openai/explicit_client.py"),
Sample("media/multimodal_input.py"),
Sample("models/check_connection.py"),
Sample("agents/tool_approval.py"),
Sample("agents/custom_hook.py"),
Sample("agents/mcp_tools.py"),
Sample("models/anthropic/builtin_web_search.py"),
Sample("models/structured_output.py"),
]

IMAGE_SAMPLES = [
Sample("image_generation.py"),
Sample("image_edit.py"),
Sample("inline_image.py"),
Sample("media/image_generation.py"),
Sample("media/image_edit.py"),
Sample("media/inline_image.py"),
]

VIDEO_SAMPLES = [
Sample("video_generation.py"),
Sample("media/video_generation.py"),
]

BROKEN_SAMPLES = [
Sample("structured_output.py"),
]
BROKEN_SAMPLES: list[Sample] = []

# E2E tests pick non-default ports so they don't collide with a running
# dev server on 8000/5173. Each test gets its own ports so that --parallel
# doesn't make them collide with each other either.
_MULTIAGENT_SERVER_PORT = "18000"
_FASTAPI_BACKEND_PORT = "18001"
_FASTAPI_FRONTEND_PORT = "15173"
_WEB_AGENT_BACKEND_PORT = "18001"
_WEB_AGENT_FRONTEND_PORT = "15173"

E2E_TESTS = [
Sample(
"multiagent-textual/test-e2e.py",
cmd=[
"uv",
"run",
"--frozen",
"--with-editable",
str(REPO),
"python",
str(REPO / "examples" / "multiagent-textual" / "test-e2e.py"),
],
extra_env={"SERVER_PORT": _MULTIAGENT_SERVER_PORT},
timeout=300.0,
),
Sample(
"fastapi-vite/e2e-test/run.sh",
"apps/web_agent/e2e-test/run.sh",
cmd=[
"bash",
str(REPO / "examples" / "fastapi-vite" / "e2e-test" / "run.sh"),
str(
REPO / "examples" / "apps" / "web_agent" / "e2e-test" / "run.sh"
),
],
extra_env={
"BACKEND_PORT": _FASTAPI_BACKEND_PORT,
"FRONTEND_PORT": _FASTAPI_FRONTEND_PORT,
"BACKEND_PORT": _WEB_AGENT_BACKEND_PORT,
"FRONTEND_PORT": _WEB_AGENT_FRONTEND_PORT,
},
timeout=300.0,
),
Sample(
"temporal-direct/test_durability.py",
cmd=[
"uv",
"run",
"--frozen",
"--directory",
str(REPO / "examples" / "temporal-direct"),
"--with-editable",
str(REPO),
"python",
"test_durability.py",
],
timeout=300.0,
),
]

KNOWN_SAMPLES = [
Expand Down Expand Up @@ -163,9 +134,13 @@ def _select_sample(
sample = known_samples.get(_path_key(Path(name).resolve()))
if sample is not None:
return sample
path = Path(name)
if not path.is_absolute() and len(path.parts) == 1:
matches = [s for s in KNOWN_SAMPLES if Path(s.name).name == name]
if len(matches) == 1:
return matches[0]
if _sample_path(name).is_file():
return Sample(name)
path = Path(name)
if not path.is_absolute() and path.parts[:1] != ("examples",):
example_path = REPO / "examples" / path
if example_path.is_file():
Expand Down Expand Up @@ -278,7 +253,7 @@ def main() -> None:
"--model",
help=(
"run each sample through run-with-patched-model.py with this "
"model id (e.g. 'gateway:openai/gpt-5.4-mini'); ignored for "
"model id (e.g. 'openai/gpt-5.4-mini'); ignored for "
"samples with a custom cmd"
),
)
Expand All @@ -295,7 +270,8 @@ def main() -> None:
nargs="*",
metavar="example",
help=(
"example file(s) to run, e.g. stream.py or " "examples/stream.py"
"example file(s) to run, e.g. models/stream.py or "
"examples/models/stream.py"
),
)
args = parser.parse_args()
Expand Down
6 changes: 3 additions & 3 deletions examples/.test_scripts/run-with-patched-model.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@

Example:
uv run examples/.test_scripts/run-with-patched-model.py \\
gateway:openai/gpt-5.4-mini \\
examples/stream.py
openai/gpt-5.4-mini \\
examples/models/stream.py

"""

Expand Down Expand Up @@ -62,7 +62,7 @@ def _protocol_factory(
def _parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--model", help="model id, e.g. 'gateway:anthropic/claude-sonnet-4.6'"
"--model", help="model id, e.g. 'anthropic/claude-sonnet-4.6'"
)
parser.add_argument("--protocol", choices=PROTOCOLS)
parser.add_argument("args", nargs="+", metavar="ARG")
Expand Down
11 changes: 11 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Examples

Examples are grouped by the main API surface they demonstrate.

- `apps/` contains end-to-end demos.
- `agents/` contains agent loops, tools, hooks, and MCP examples.
- `media/` contains image, video, and multimodal examples.
- `models/` contains model streaming, structured output, and provider examples.

Provider-specific model examples live under `models/<provider>/`, such as
`models/gateway/`, `models/openai/`, and `models/anthropic/`.
57 changes: 0 additions & 57 deletions examples/agent_hooks.py

This file was deleted.

Loading
Loading