diff --git a/.gitignore b/.gitignore index e18c707908..73c84030ba 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,6 @@ env*/ /TODO.md /postgres-data/ .DS_Store -/pydantic_ai_examples/.chat_app_messages.sqlite +examples/pydantic_ai_examples/.chat_app_messages.sqlite .cache/ .vscode/ diff --git a/docs/.hooks/main.py b/docs/.hooks/main.py index 7e6dc78271..2eedc5e433 100644 --- a/docs/.hooks/main.py +++ b/docs/.hooks/main.py @@ -48,11 +48,11 @@ def sub_run(m: re.Match[str]) -> str: ```""" -EXAMPLES_DIR = Path(__file__).parent.parent.parent / 'pydantic_ai_examples' +EXAMPLES_DIR = Path(__file__).parent.parent.parent / 'examples' def render_examples(markdown: str) -> str: - return re.sub(r'^#! *pydantic_ai_examples/(.+)', sub_example, markdown, flags=re.M) + return re.sub(r'^#! *examples/(.+)', sub_example, markdown, flags=re.M) def sub_example(m: re.Match[str]) -> str: diff --git a/docs/examples/bank-support.md b/docs/examples/bank-support.md index 5a05b87a6b..80ecbf415a 100644 --- a/docs/examples/bank-support.md +++ b/docs/examples/bank-support.md @@ -19,5 +19,5 @@ python/uv-run -m pydantic_ai_examples.bank_support ## Example Code ```python {title="bank_support.py"} -#! pydantic_ai_examples/bank_support.py +#! examples/pydantic_ai_examples/bank_support.py ``` diff --git a/docs/examples/chat-app.md b/docs/examples/chat-app.md index 337729975c..9b921d456e 100644 --- a/docs/examples/chat-app.md +++ b/docs/examples/chat-app.md @@ -30,17 +30,17 @@ TODO screenshot. Python code that runs the chat app: ```python {title="chat_app.py"} -#! pydantic_ai_examples/chat_app.py +#! examples/pydantic_ai_examples/chat_app.py ``` Simple HTML page to render the app: ```html {title="chat_app.html"} -#! pydantic_ai_examples/chat_app.html +#! examples/pydantic_ai_examples/chat_app.html ``` TypeScript to handle rendering the messages, to keep this simple (and at the risk of offending frontend developers) the typescript code is passed to the browser as plain text and transpiled in the browser. ```ts {title="chat_app.ts"} -#! pydantic_ai_examples/chat_app.ts +#! examples/pydantic_ai_examples/chat_app.ts ``` diff --git a/docs/examples/pydantic-model.md b/docs/examples/pydantic-model.md index adeda6d5fc..85ae2da6ae 100644 --- a/docs/examples/pydantic-model.md +++ b/docs/examples/pydantic-model.md @@ -26,5 +26,5 @@ PYDANTIC_AI_MODEL=gemini-1.5-pro python/uv-run -m pydantic_ai_examples.pydantic_ ## Example Code ```python {title="pydantic_model.py"} -#! pydantic_ai_examples/pydantic_model.py +#! examples/pydantic_ai_examples/pydantic_model.py ``` diff --git a/docs/examples/rag.md b/docs/examples/rag.md index 735a61722c..4a6512b374 100644 --- a/docs/examples/rag.md +++ b/docs/examples/rag.md @@ -45,5 +45,5 @@ python/uv-run -m pydantic_ai_examples.rag search "How do I configure logfire to ## Example Code ```python {title="rag.py"} -#! pydantic_ai_examples/rag.py +#! examples/pydantic_ai_examples/rag.py ``` diff --git a/docs/examples/sql-gen.md b/docs/examples/sql-gen.md index 78b1cbcb38..32a05b454b 100644 --- a/docs/examples/sql-gen.md +++ b/docs/examples/sql-gen.md @@ -35,5 +35,5 @@ This model uses `gemini-1.5-flash` by default since Gemini is good at single sho ## Example Code ```python {title="sql_gen.py"} -#! pydantic_ai_examples/sql_gen.py +#! examples/pydantic_ai_examples/sql_gen.py ``` diff --git a/docs/examples/stream-markdown.md b/docs/examples/stream-markdown.md index 8aa443f0d3..abd5890f2d 100644 --- a/docs/examples/stream-markdown.md +++ b/docs/examples/stream-markdown.md @@ -17,5 +17,5 @@ python/uv-run -m pydantic_ai_examples.stream_markdown ## Example Code ```python -#! pydantic_ai_examples/stream_markdown.py +#! examples/pydantic_ai_examples/stream_markdown.py ``` diff --git a/docs/examples/stream-whales.md b/docs/examples/stream-whales.md index 7a9b38a5ac..43203371e5 100644 --- a/docs/examples/stream-whales.md +++ b/docs/examples/stream-whales.md @@ -22,5 +22,5 @@ Should give an output like this: ## Example Code ```python {title="stream_whales.py"} -#! pydantic_ai_examples/stream_whales.py +#! examples/pydantic_ai_examples/stream_whales.py ``` diff --git a/docs/examples/weather-agent.md b/docs/examples/weather-agent.md index 4f5d62a20e..7504711aef 100644 --- a/docs/examples/weather-agent.md +++ b/docs/examples/weather-agent.md @@ -26,5 +26,5 @@ python/uv-run -m pydantic_ai_examples.weather_agent ## Example Code ```python {title="pydantic_ai_examples/weather_agent.py"} -#! pydantic_ai_examples/weather_agent.py +#! examples/pydantic_ai_examples/weather_agent.py ``` diff --git a/pydantic_ai_examples/README.md b/examples/README.md similarity index 100% rename from pydantic_ai_examples/README.md rename to examples/README.md diff --git a/pydantic_ai_examples/__main__.py b/examples/pydantic_ai_examples/__main__.py similarity index 100% rename from pydantic_ai_examples/__main__.py rename to examples/pydantic_ai_examples/__main__.py diff --git a/pydantic_ai_examples/bank_support.py b/examples/pydantic_ai_examples/bank_support.py similarity index 77% rename from pydantic_ai_examples/bank_support.py rename to examples/pydantic_ai_examples/bank_support.py index eda1226dc3..9623f63f84 100644 --- a/pydantic_ai_examples/bank_support.py +++ b/examples/pydantic_ai_examples/bank_support.py @@ -74,15 +74,16 @@ async def customer_balance( return f'${balance:.2f}' -deps = SupportDependencies(customer_id=123, db=DatabaseConn()) -result = support_agent.run_sync('What is my balance?', deps=deps) -print(result.data) -""" -support_advice='Hello John, your current account balance, including pending transactions, is $123.45.' block_card=False risk=1 -""" +if __name__ == '__main__': + deps = SupportDependencies(customer_id=123, db=DatabaseConn()) + result = support_agent.run_sync('What is my balance?', deps=deps) + print(result.data) + """ + support_advice='Hello John, your current account balance, including pending transactions, is $123.45.' block_card=False risk=1 + """ -result = support_agent.run_sync('I just lost my card!', deps=deps) -print(result.data) -""" -support_advice="I'm sorry to hear that, John. We are temporarily blocking your card to prevent unauthorized transactions." block_card=True risk=8 -""" + result = support_agent.run_sync('I just lost my card!', deps=deps) + print(result.data) + """ + support_advice="I'm sorry to hear that, John. We are temporarily blocking your card to prevent unauthorized transactions." block_card=True risk=8 + """ diff --git a/pydantic_ai_examples/chat_app.html b/examples/pydantic_ai_examples/chat_app.html similarity index 100% rename from pydantic_ai_examples/chat_app.html rename to examples/pydantic_ai_examples/chat_app.html diff --git a/pydantic_ai_examples/chat_app.py b/examples/pydantic_ai_examples/chat_app.py similarity index 100% rename from pydantic_ai_examples/chat_app.py rename to examples/pydantic_ai_examples/chat_app.py diff --git a/pydantic_ai_examples/chat_app.ts b/examples/pydantic_ai_examples/chat_app.ts similarity index 100% rename from pydantic_ai_examples/chat_app.ts rename to examples/pydantic_ai_examples/chat_app.ts diff --git a/pydantic_ai_examples/pydantic_model.py b/examples/pydantic_ai_examples/pydantic_model.py similarity index 100% rename from pydantic_ai_examples/pydantic_model.py rename to examples/pydantic_ai_examples/pydantic_model.py diff --git a/pydantic_ai_examples/rag.py b/examples/pydantic_ai_examples/rag.py similarity index 100% rename from pydantic_ai_examples/rag.py rename to examples/pydantic_ai_examples/rag.py diff --git a/pydantic_ai_examples/roulette_wheel.py b/examples/pydantic_ai_examples/roulette_wheel.py similarity index 100% rename from pydantic_ai_examples/roulette_wheel.py rename to examples/pydantic_ai_examples/roulette_wheel.py diff --git a/pydantic_ai_examples/sql_gen.py b/examples/pydantic_ai_examples/sql_gen.py similarity index 100% rename from pydantic_ai_examples/sql_gen.py rename to examples/pydantic_ai_examples/sql_gen.py diff --git a/pydantic_ai_examples/stream_markdown.py b/examples/pydantic_ai_examples/stream_markdown.py similarity index 100% rename from pydantic_ai_examples/stream_markdown.py rename to examples/pydantic_ai_examples/stream_markdown.py diff --git a/pydantic_ai_examples/stream_whales.py b/examples/pydantic_ai_examples/stream_whales.py similarity index 94% rename from pydantic_ai_examples/stream_whales.py rename to examples/pydantic_ai_examples/stream_whales.py index 5c6ffa3b99..d0b76e585a 100644 --- a/pydantic_ai_examples/stream_whales.py +++ b/examples/pydantic_ai_examples/stream_whales.py @@ -8,14 +8,14 @@ uv run -m pydantic_ai_examples.whales """ -from typing import Annotated, NotRequired, TypedDict +from typing import Annotated -import devtools import logfire from pydantic import Field, ValidationError from rich.console import Console from rich.live import Live from rich.table import Table +from typing_extensions import NotRequired, TypedDict from pydantic_ai import Agent @@ -41,11 +41,6 @@ class Whale(TypedDict): agent = Agent('openai:gpt-4', result_type=list[Whale]) -def check_validation_error(e: ValidationError) -> bool: - devtools.debug(e.errors()) - return False - - async def main(): console = Console() with Live('\n' * 36, console=console) as live: diff --git a/pydantic_ai_examples/weather_agent.py b/examples/pydantic_ai_examples/weather_agent.py similarity index 100% rename from pydantic_ai_examples/weather_agent.py rename to examples/pydantic_ai_examples/weather_agent.py diff --git a/pydantic_ai_examples/pyproject.toml b/examples/pyproject.toml similarity index 92% rename from pydantic_ai_examples/pyproject.toml rename to examples/pyproject.toml index 00dec02ab0..4ee1825151 100644 --- a/pydantic_ai_examples/pyproject.toml +++ b/examples/pyproject.toml @@ -43,11 +43,8 @@ dependencies = [ "uvicorn>=0.32.0", ] -[tool.hatch.build] -include = ["*.py", "*.html", "*.ts"] - -[tool.hatch.build.targets.wheel.sources] -"" = "pydantic_ai_examples" +[tool.hatch.build.targets.wheel] +packages = ["pydantic_ai_examples"] [tool.uv.sources] pydantic-ai-slim = { workspace = true } diff --git a/mkdocs.yml b/mkdocs.yml index 5d603a6e30..92c19961ad 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -145,7 +145,7 @@ markdown_extensions: watch: - pydantic_ai_slim - - pydantic_ai_examples + - examples plugins: - search diff --git a/pyproject.toml b/pyproject.toml index b1a3ffb3fe..af6bf09e8a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,7 +54,7 @@ pydantic-ai-slim = { workspace = true } pydantic-ai-examples = { workspace = true } [tool.uv.workspace] -members = ["pydantic_ai_slim", "pydantic_ai_examples"] +members = ["pydantic_ai_slim", "examples"] [dependency-groups] # dev dependencies are defined in `pydantic-ai-slim/pyproject.toml` to allow for minimal testing @@ -82,7 +82,7 @@ line-length = 120 target-version = "py39" include = [ "pydantic_ai_slim/**/*.py", - "pydantic_ai_examples/**/*.py", + "examples/**/*.py", "tests/**/*.py", "docs/**/*.py", ] @@ -118,14 +118,14 @@ quote-style = "single" [tool.ruff.lint.per-file-ignores] "tests/**/*.py" = ["D"] "docs/**/*.py" = ["D"] -"pydantic_ai_examples/**/*.py" = ["D101", "D103"] +"examples/**/*.py" = ["D101", "D103"] [tool.pyright] typeCheckingMode = "strict" reportMissingTypeStubs = false reportUnnecessaryIsInstance = false reportUnnecessaryTypeIgnoreComment = true -include = ["pydantic_ai_slim", "tests", "pydantic_ai_examples"] +include = ["pydantic_ai_slim", "tests", "examples"] venvPath = ".venv" # see https://github.com/microsoft/pyright/issues/7771 - we don't want to error on decorated functions in tests # which are not otherwise used diff --git a/tests/import_examples.py b/tests/import_examples.py index 31b0a1f400..bc0636c371 100644 --- a/tests/import_examples.py +++ b/tests/import_examples.py @@ -5,10 +5,21 @@ """ import os +import sys from pathlib import Path -os.environ.update(OPENAI_API_KEY='fake-key', GEMINI_API_KEY='fake-key') +if sys.version_info < (3, 11): + print('Skipping import_examples.py because it requires Python 3.11+') +else: + os.environ.update(OPENAI_API_KEY='fake-key', GEMINI_API_KEY='fake-key', GROQ_API_KEY='fake-key') -examples_dir = Path(__file__).parent.parent / 'examples' -for example in examples_dir.glob('*.py'): - __import__(f'examples.{example.stem}') + examples_dir = Path(__file__).parent.parent / 'examples' / 'pydantic_ai_examples' + assert examples_dir.is_dir(), f'No examples directory found at {examples_dir}' + count = 0 + for example in examples_dir.glob('*.py'): + print(f'Importing {example.stem}...') + __import__(f'pydantic_ai_examples.{example.stem}') + count += 1 + + print(f'Imported {count} examples') + assert count > 5, 'No examples found' diff --git a/uprev.py b/uprev.py index 5dae1492dd..7c3f2c89af 100644 --- a/uprev.py +++ b/uprev.py @@ -59,7 +59,7 @@ def replace_deps_version(text: str) -> tuple[str, int]: root_pp_text, count_root = replace_deps_version(root_pp_text) -examples_pp = ROOT_DIR / 'pydantic_ai_examples' / 'pyproject.toml' +examples_pp = ROOT_DIR / 'examples' / 'pyproject.toml' examples_pp_text = examples_pp.read_text() examples_pp_text, count_ex = replace_deps_version(examples_pp_text) diff --git a/uv.lock b/uv.lock index cb7a7f10d6..f60c51ff9c 100644 --- a/uv.lock +++ b/uv.lock @@ -1611,7 +1611,7 @@ lint = [ [package.metadata] requires-dist = [ { name = "logfire", marker = "extra == 'logfire'", specifier = ">=2.3" }, - { name = "pydantic-ai-examples", marker = "extra == 'examples'", editable = "pydantic_ai_examples" }, + { name = "pydantic-ai-examples", marker = "extra == 'examples'", editable = "examples" }, { name = "pydantic-ai-slim", extras = ["openai", "vertexai", "groq", "anthropic", "mistral"], editable = "pydantic_ai_slim" }, ] @@ -1632,7 +1632,7 @@ lint = [ [[package]] name = "pydantic-ai-examples" version = "0.0.14" -source = { editable = "pydantic_ai_examples" } +source = { editable = "examples" } dependencies = [ { name = "asyncpg" }, { name = "fastapi" },