pydantic-ai-examples 1.0.13__tar.gz → 1.49.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/.gitignore +3 -1
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/PKG-INFO +5 -5
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/ag_ui/api/agentic_chat.py +5 -2
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/ag_ui/api/agentic_generative_ui.py +6 -3
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/ag_ui/api/human_in_the_loop.py +3 -2
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/ag_ui/api/predictive_state_updates.py +4 -3
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/ag_ui/api/shared_state.py +11 -10
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/ag_ui/api/tool_based_generative_ui.py +3 -2
- pydantic_ai_examples-1.49.0/pydantic_ai_examples/bank_support.py +101 -0
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/chat_app.py +1 -1
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/data_analyst.py +4 -4
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/evals/agent.py +1 -1
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/evals/example_04_compare_models.py +2 -2
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/flight_booking.py +3 -3
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/pydantic_model.py +1 -1
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/question_graph.py +6 -4
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/rag.py +6 -5
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/slack_lead_qualifier/agent.py +1 -1
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/sql_gen.py +1 -1
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/stream_markdown.py +2 -2
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/weather_agent.py +1 -1
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/weather_agent_gradio.py +0 -1
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pyproject.toml +2 -2
- pydantic_ai_examples-1.0.13/pydantic_ai_examples/bank_support.py +0 -95
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/LICENSE +0 -0
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/README.md +0 -0
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/__main__.py +0 -0
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/ag_ui/__init__.py +0 -0
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/ag_ui/__main__.py +0 -0
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/ag_ui/api/__init__.py +0 -0
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/chat_app.html +0 -0
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/chat_app.ts +0 -0
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/evals/__init__.py +0 -0
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/evals/custom_evaluators.py +0 -0
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/evals/datasets/time_range_v1.yaml +0 -0
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/evals/datasets/time_range_v1_schema.json +0 -0
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/evals/datasets/time_range_v2.yaml +0 -0
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/evals/datasets/time_range_v2_schema.json +0 -0
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/evals/example_01_generate_dataset.py +0 -0
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/evals/example_02_add_custom_evaluators.py +0 -0
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/evals/example_03_unit_testing.py +0 -0
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/evals/models.py +0 -0
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/py.typed +0 -0
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/roulette_wheel.py +0 -0
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/slack_lead_qualifier/__init__.py +0 -0
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/slack_lead_qualifier/app.py +0 -0
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/slack_lead_qualifier/functions.py +0 -0
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/slack_lead_qualifier/modal.py +0 -0
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/slack_lead_qualifier/models.py +0 -0
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/slack_lead_qualifier/slack.py +0 -0
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/slack_lead_qualifier/store.py +0 -0
- {pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/stream_whales.py +0 -0
|
@@ -10,7 +10,7 @@ env*/
|
|
|
10
10
|
/TODO.md
|
|
11
11
|
/postgres-data/
|
|
12
12
|
.DS_Store
|
|
13
|
-
|
|
13
|
+
.chat_app_messages.sqlite
|
|
14
14
|
.cache/
|
|
15
15
|
.vscode/
|
|
16
16
|
/question_graph_history.json
|
|
@@ -21,3 +21,5 @@ node_modules/
|
|
|
21
21
|
/test_tmp/
|
|
22
22
|
.mcp.json
|
|
23
23
|
.claude/
|
|
24
|
+
/.cursor/
|
|
25
|
+
/.devcontainer/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pydantic-ai-examples
|
|
3
|
-
Version: 1.0
|
|
3
|
+
Version: 1.49.0
|
|
4
4
|
Summary: Examples of how to use Pydantic AI and what it can do.
|
|
5
5
|
Author-email: Samuel Colvin <samuel@pydantic.dev>, Marcelo Trylesinski <marcelotryle@gmail.com>, David Montague <david@pydantic.dev>, Alex Hall <alex@pydantic.dev>, Douwe Maan <douwe@pydantic.dev>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -29,13 +29,13 @@ Requires-Dist: datasets>=4.0.0
|
|
|
29
29
|
Requires-Dist: devtools>=0.12.2
|
|
30
30
|
Requires-Dist: duckdb>=1.3.2
|
|
31
31
|
Requires-Dist: fastapi>=0.115.4
|
|
32
|
-
Requires-Dist: gradio>=5.
|
|
32
|
+
Requires-Dist: gradio>=5.31.0
|
|
33
33
|
Requires-Dist: logfire[asyncpg,fastapi,httpx,sqlite3]>=3.14.1
|
|
34
|
-
Requires-Dist: mcp[cli]
|
|
34
|
+
Requires-Dist: mcp[cli]<2.0,>=1.25.0
|
|
35
35
|
Requires-Dist: modal>=1.0.4
|
|
36
36
|
Requires-Dist: pandas>=2.2.3
|
|
37
|
-
Requires-Dist: pydantic-ai-slim[ag-ui,anthropic,groq,openai,vertexai]==1.0
|
|
38
|
-
Requires-Dist: pydantic-evals==1.0
|
|
37
|
+
Requires-Dist: pydantic-ai-slim[ag-ui,anthropic,groq,openai,vertexai]==1.49.0
|
|
38
|
+
Requires-Dist: pydantic-evals==1.49.0
|
|
39
39
|
Requires-Dist: python-multipart>=0.0.17
|
|
40
40
|
Requires-Dist: rich>=13.9.2
|
|
41
41
|
Requires-Dist: uvicorn>=0.32.0
|
|
@@ -6,9 +6,9 @@ from datetime import datetime
|
|
|
6
6
|
from zoneinfo import ZoneInfo
|
|
7
7
|
|
|
8
8
|
from pydantic_ai import Agent
|
|
9
|
+
from pydantic_ai.ui.ag_ui.app import AGUIApp
|
|
9
10
|
|
|
10
|
-
agent = Agent('openai:gpt-
|
|
11
|
-
app = agent.to_ag_ui()
|
|
11
|
+
agent = Agent('openai:gpt-5-mini')
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
@agent.tool_plain
|
|
@@ -23,3 +23,6 @@ async def current_time(timezone: str = 'UTC') -> str:
|
|
|
23
23
|
"""
|
|
24
24
|
tz: ZoneInfo = ZoneInfo(timezone)
|
|
25
25
|
return datetime.now(tz=tz).isoformat()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
app = AGUIApp(agent)
|
|
@@ -9,6 +9,7 @@ from pydantic import BaseModel, Field
|
|
|
9
9
|
|
|
10
10
|
from ag_ui.core import EventType, StateDeltaEvent, StateSnapshotEvent
|
|
11
11
|
from pydantic_ai import Agent
|
|
12
|
+
from pydantic_ai.ui.ag_ui.app import AGUIApp
|
|
12
13
|
|
|
13
14
|
StepStatus = Literal['pending', 'completed']
|
|
14
15
|
|
|
@@ -26,7 +27,9 @@ class Step(BaseModel):
|
|
|
26
27
|
class Plan(BaseModel):
|
|
27
28
|
"""Represents a plan with multiple steps."""
|
|
28
29
|
|
|
29
|
-
steps: list[Step] = Field(
|
|
30
|
+
steps: list[Step] = Field(
|
|
31
|
+
default_factory=list[Step], description='The steps in the plan'
|
|
32
|
+
)
|
|
30
33
|
|
|
31
34
|
|
|
32
35
|
class JSONPatchOp(BaseModel):
|
|
@@ -48,7 +51,7 @@ class JSONPatchOp(BaseModel):
|
|
|
48
51
|
|
|
49
52
|
|
|
50
53
|
agent = Agent(
|
|
51
|
-
'openai:gpt-
|
|
54
|
+
'openai:gpt-5-mini',
|
|
52
55
|
instructions=dedent(
|
|
53
56
|
"""
|
|
54
57
|
When planning use tools only, without any other messages.
|
|
@@ -116,4 +119,4 @@ async def update_plan_step(
|
|
|
116
119
|
)
|
|
117
120
|
|
|
118
121
|
|
|
119
|
-
app = agent
|
|
122
|
+
app = AGUIApp(agent)
|
|
@@ -8,9 +8,10 @@ from __future__ import annotations
|
|
|
8
8
|
from textwrap import dedent
|
|
9
9
|
|
|
10
10
|
from pydantic_ai import Agent
|
|
11
|
+
from pydantic_ai.ui.ag_ui.app import AGUIApp
|
|
11
12
|
|
|
12
13
|
agent = Agent(
|
|
13
|
-
'openai:gpt-
|
|
14
|
+
'openai:gpt-5-mini',
|
|
14
15
|
instructions=dedent(
|
|
15
16
|
"""
|
|
16
17
|
When planning tasks use tools only, without any other messages.
|
|
@@ -23,4 +24,4 @@ agent = Agent(
|
|
|
23
24
|
),
|
|
24
25
|
)
|
|
25
26
|
|
|
26
|
-
app = agent
|
|
27
|
+
app = AGUIApp(agent)
|
|
@@ -8,7 +8,8 @@ from pydantic import BaseModel
|
|
|
8
8
|
|
|
9
9
|
from ag_ui.core import CustomEvent, EventType
|
|
10
10
|
from pydantic_ai import Agent, RunContext
|
|
11
|
-
from pydantic_ai.
|
|
11
|
+
from pydantic_ai.ui import StateDeps
|
|
12
|
+
from pydantic_ai.ui.ag_ui.app import AGUIApp
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
class DocumentState(BaseModel):
|
|
@@ -17,7 +18,7 @@ class DocumentState(BaseModel):
|
|
|
17
18
|
document: str = ''
|
|
18
19
|
|
|
19
20
|
|
|
20
|
-
agent = Agent('openai:gpt-
|
|
21
|
+
agent = Agent('openai:gpt-5-mini', deps_type=StateDeps[DocumentState])
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
# Tools which return AG-UI events will be sent to the client as part of the
|
|
@@ -74,4 +75,4 @@ async def story_instructions(ctx: RunContext[StateDeps[DocumentState]]) -> str:
|
|
|
74
75
|
)
|
|
75
76
|
|
|
76
77
|
|
|
77
|
-
app = agent
|
|
78
|
+
app = AGUIApp(agent, deps=StateDeps(DocumentState()))
|
|
@@ -2,17 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from enum import
|
|
5
|
+
from enum import Enum
|
|
6
6
|
from textwrap import dedent
|
|
7
7
|
|
|
8
8
|
from pydantic import BaseModel, Field
|
|
9
9
|
|
|
10
10
|
from ag_ui.core import EventType, StateSnapshotEvent
|
|
11
11
|
from pydantic_ai import Agent, RunContext
|
|
12
|
-
from pydantic_ai.
|
|
12
|
+
from pydantic_ai.ui import StateDeps
|
|
13
|
+
from pydantic_ai.ui.ag_ui.app import AGUIApp
|
|
13
14
|
|
|
14
15
|
|
|
15
|
-
class SkillLevel(
|
|
16
|
+
class SkillLevel(str, Enum):
|
|
16
17
|
"""The level of skill required for the recipe."""
|
|
17
18
|
|
|
18
19
|
BEGINNER = 'Beginner'
|
|
@@ -20,7 +21,7 @@ class SkillLevel(StrEnum):
|
|
|
20
21
|
ADVANCED = 'Advanced'
|
|
21
22
|
|
|
22
23
|
|
|
23
|
-
class SpecialPreferences(
|
|
24
|
+
class SpecialPreferences(str, Enum):
|
|
24
25
|
"""Special preferences for the recipe."""
|
|
25
26
|
|
|
26
27
|
HIGH_PROTEIN = 'High Protein'
|
|
@@ -32,7 +33,7 @@ class SpecialPreferences(StrEnum):
|
|
|
32
33
|
VEGAN = 'Vegan'
|
|
33
34
|
|
|
34
35
|
|
|
35
|
-
class CookingTime(
|
|
36
|
+
class CookingTime(str, Enum):
|
|
36
37
|
"""The cooking time of the recipe."""
|
|
37
38
|
|
|
38
39
|
FIVE_MIN = '5 min'
|
|
@@ -61,18 +62,18 @@ class Recipe(BaseModel):
|
|
|
61
62
|
description='The skill level required for the recipe',
|
|
62
63
|
)
|
|
63
64
|
special_preferences: list[SpecialPreferences] = Field(
|
|
64
|
-
default_factory=list,
|
|
65
|
+
default_factory=list[SpecialPreferences],
|
|
65
66
|
description='Any special preferences for the recipe',
|
|
66
67
|
)
|
|
67
68
|
cooking_time: CookingTime = Field(
|
|
68
69
|
default=CookingTime.FIVE_MIN, description='The cooking time of the recipe'
|
|
69
70
|
)
|
|
70
71
|
ingredients: list[Ingredient] = Field(
|
|
71
|
-
default_factory=list,
|
|
72
|
+
default_factory=list[Ingredient],
|
|
72
73
|
description='Ingredients for the recipe',
|
|
73
74
|
)
|
|
74
75
|
instructions: list[str] = Field(
|
|
75
|
-
default_factory=list, description='Instructions for the recipe'
|
|
76
|
+
default_factory=list[str], description='Instructions for the recipe'
|
|
76
77
|
)
|
|
77
78
|
|
|
78
79
|
|
|
@@ -84,7 +85,7 @@ class RecipeSnapshot(BaseModel):
|
|
|
84
85
|
)
|
|
85
86
|
|
|
86
87
|
|
|
87
|
-
agent = Agent('openai:gpt-
|
|
88
|
+
agent = Agent('openai:gpt-5-mini', deps_type=StateDeps[RecipeSnapshot])
|
|
88
89
|
|
|
89
90
|
|
|
90
91
|
@agent.tool_plain
|
|
@@ -135,4 +136,4 @@ async def recipe_instructions(ctx: RunContext[StateDeps[RecipeSnapshot]]) -> str
|
|
|
135
136
|
)
|
|
136
137
|
|
|
137
138
|
|
|
138
|
-
app = agent
|
|
139
|
+
app = AGUIApp(agent, deps=StateDeps(RecipeSnapshot()))
|
|
@@ -6,6 +6,7 @@ No special handling is required for this feature.
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
8
|
from pydantic_ai import Agent
|
|
9
|
+
from pydantic_ai.ui.ag_ui.app import AGUIApp
|
|
9
10
|
|
|
10
|
-
agent = Agent('openai:gpt-
|
|
11
|
-
app = agent
|
|
11
|
+
agent = Agent('openai:gpt-5-mini')
|
|
12
|
+
app = AGUIApp(agent)
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""Small but complete example of using Pydantic AI to build a support agent for a bank.
|
|
2
|
+
|
|
3
|
+
Run with:
|
|
4
|
+
|
|
5
|
+
uv run -m pydantic_ai_examples.bank_support
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import sqlite3
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
|
|
11
|
+
from pydantic import BaseModel
|
|
12
|
+
|
|
13
|
+
from pydantic_ai import Agent, RunContext
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class DatabaseConn:
|
|
18
|
+
"""A wrapper over the SQLite connection."""
|
|
19
|
+
|
|
20
|
+
sqlite_conn: sqlite3.Connection
|
|
21
|
+
|
|
22
|
+
async def customer_name(self, *, id: int) -> str | None:
|
|
23
|
+
res = cur.execute('SELECT name FROM customers WHERE id=?', (id,))
|
|
24
|
+
row = res.fetchone()
|
|
25
|
+
if row:
|
|
26
|
+
return row[0]
|
|
27
|
+
return None
|
|
28
|
+
|
|
29
|
+
async def customer_balance(self, *, id: int) -> float:
|
|
30
|
+
res = cur.execute('SELECT balance FROM customers WHERE id=?', (id,))
|
|
31
|
+
row = res.fetchone()
|
|
32
|
+
if row:
|
|
33
|
+
return row[0]
|
|
34
|
+
else:
|
|
35
|
+
raise ValueError('Customer not found')
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class SupportDependencies:
|
|
40
|
+
customer_id: int
|
|
41
|
+
db: DatabaseConn
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class SupportOutput(BaseModel):
|
|
45
|
+
support_advice: str
|
|
46
|
+
"""Advice returned to the customer"""
|
|
47
|
+
block_card: bool
|
|
48
|
+
"""Whether to block their card or not"""
|
|
49
|
+
risk: int
|
|
50
|
+
"""Risk level of query"""
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
support_agent = Agent(
|
|
54
|
+
'openai:gpt-5',
|
|
55
|
+
deps_type=SupportDependencies,
|
|
56
|
+
output_type=SupportOutput,
|
|
57
|
+
instructions=(
|
|
58
|
+
'You are a support agent in our bank, give the '
|
|
59
|
+
'customer support and judge the risk level of their query. '
|
|
60
|
+
"Reply using the customer's name."
|
|
61
|
+
),
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@support_agent.instructions
|
|
66
|
+
async def add_customer_name(ctx: RunContext[SupportDependencies]) -> str:
|
|
67
|
+
customer_name = await ctx.deps.db.customer_name(id=ctx.deps.customer_id)
|
|
68
|
+
return f"The customer's name is {customer_name!r}"
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@support_agent.tool
|
|
72
|
+
async def customer_balance(ctx: RunContext[SupportDependencies]) -> str:
|
|
73
|
+
"""Returns the customer's current account balance."""
|
|
74
|
+
balance = await ctx.deps.db.customer_balance(
|
|
75
|
+
id=ctx.deps.customer_id,
|
|
76
|
+
)
|
|
77
|
+
return f'${balance:.2f}'
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
if __name__ == '__main__':
|
|
81
|
+
with sqlite3.connect(':memory:') as con:
|
|
82
|
+
cur = con.cursor()
|
|
83
|
+
cur.execute('CREATE TABLE customers(id, name, balance)')
|
|
84
|
+
cur.execute("""
|
|
85
|
+
INSERT INTO customers VALUES
|
|
86
|
+
(123, 'John', 123.45)
|
|
87
|
+
""")
|
|
88
|
+
con.commit()
|
|
89
|
+
|
|
90
|
+
deps = SupportDependencies(customer_id=123, db=DatabaseConn(sqlite_conn=con))
|
|
91
|
+
result = support_agent.run_sync('What is my balance?', deps=deps)
|
|
92
|
+
print(result.output)
|
|
93
|
+
"""
|
|
94
|
+
support_advice='Hello John, your current account balance, including pending transactions, is $123.45.' block_card=False risk=1
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
result = support_agent.run_sync('I just lost my card!', deps=deps)
|
|
98
|
+
print(result.output)
|
|
99
|
+
"""
|
|
100
|
+
support_advice="I'm sorry to hear that, John. We are temporarily blocking your card to prevent unauthorized transactions." block_card=True risk=8
|
|
101
|
+
"""
|
{pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/data_analyst.py
RENAMED
|
@@ -9,7 +9,7 @@ from pydantic_ai import Agent, ModelRetry, RunContext
|
|
|
9
9
|
|
|
10
10
|
@dataclass
|
|
11
11
|
class AnalystAgentDeps:
|
|
12
|
-
output: dict[str, pd.DataFrame] = field(default_factory=dict)
|
|
12
|
+
output: dict[str, pd.DataFrame] = field(default_factory=dict[str, pd.DataFrame])
|
|
13
13
|
|
|
14
14
|
def store(self, value: pd.DataFrame) -> str:
|
|
15
15
|
"""Store the output in deps and return the reference such as Out[1] to be used by the LLM."""
|
|
@@ -26,7 +26,7 @@ class AnalystAgentDeps:
|
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
analyst_agent = Agent(
|
|
29
|
-
'openai:gpt-
|
|
29
|
+
'openai:gpt-5',
|
|
30
30
|
deps_type=AnalystAgentDeps,
|
|
31
31
|
instructions='You are a data analyst and your job is to analyze the data according to the user request.',
|
|
32
32
|
)
|
|
@@ -47,7 +47,7 @@ def load_dataset(
|
|
|
47
47
|
"""
|
|
48
48
|
# begin load data from hf
|
|
49
49
|
builder = datasets.load_dataset_builder(path) # pyright: ignore[reportUnknownMemberType]
|
|
50
|
-
splits: dict[str, datasets.SplitInfo] = builder.info.splits or {}
|
|
50
|
+
splits: dict[str, datasets.SplitInfo] = builder.info.splits or {}
|
|
51
51
|
if split not in splits:
|
|
52
52
|
raise ModelRetry(
|
|
53
53
|
f'{split} is not valid for dataset {path}. Valid splits are {",".join(splits.keys())}'
|
|
@@ -87,7 +87,7 @@ def run_duckdb(ctx: RunContext[AnalystAgentDeps], dataset: str, sql: str) -> str
|
|
|
87
87
|
data = ctx.deps.get(dataset)
|
|
88
88
|
result = duckdb.query_df(df=data, virtual_table_name='dataset', sql_query=sql)
|
|
89
89
|
# pass the result as ref (because DuckDB SQL can select many rows, creating another huge dataframe)
|
|
90
|
-
ref = ctx.deps.store(result.df())
|
|
90
|
+
ref = ctx.deps.store(result.df())
|
|
91
91
|
return f'Executed SQL, result is `{ref}`'
|
|
92
92
|
|
|
93
93
|
|
{pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/evals/agent.py
RENAMED
|
@@ -21,7 +21,7 @@ class TimeRangeDeps:
|
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
time_range_agent = Agent[TimeRangeDeps, TimeRangeResponse](
|
|
24
|
-
'gpt-
|
|
24
|
+
'gpt-5',
|
|
25
25
|
output_type=TimeRangeResponse, # type: ignore # we can't yet annotate something as receiving a TypeForm
|
|
26
26
|
deps_type=TimeRangeDeps,
|
|
27
27
|
system_prompt="Convert the user's request into a structured time range.",
|
|
@@ -28,8 +28,8 @@ def compare_models():
|
|
|
28
28
|
dataset_path, custom_evaluator_types=CUSTOM_EVALUATOR_TYPES
|
|
29
29
|
)
|
|
30
30
|
with logfire.span('Comparing different models for time_range_agent'):
|
|
31
|
-
with time_range_agent.override(model='openai:gpt-
|
|
32
|
-
dataset.evaluate_sync(infer_time_range, name='openai:gpt-
|
|
31
|
+
with time_range_agent.override(model='openai:gpt-5'):
|
|
32
|
+
dataset.evaluate_sync(infer_time_range, name='openai:gpt-5')
|
|
33
33
|
with time_range_agent.override(model='openai:o1'):
|
|
34
34
|
dataset.evaluate_sync(infer_time_range, name='openai:o1')
|
|
35
35
|
|
{pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/flight_booking.py
RENAMED
|
@@ -49,7 +49,7 @@ class Deps:
|
|
|
49
49
|
|
|
50
50
|
# This agent is responsible for controlling the flow of the conversation.
|
|
51
51
|
search_agent = Agent[Deps, FlightDetails | NoFlightFound](
|
|
52
|
-
'openai:gpt-
|
|
52
|
+
'openai:gpt-5',
|
|
53
53
|
output_type=FlightDetails | NoFlightFound, # type: ignore
|
|
54
54
|
retries=4,
|
|
55
55
|
system_prompt=(
|
|
@@ -60,7 +60,7 @@ search_agent = Agent[Deps, FlightDetails | NoFlightFound](
|
|
|
60
60
|
|
|
61
61
|
# This agent is responsible for extracting flight details from web page text.
|
|
62
62
|
extraction_agent = Agent(
|
|
63
|
-
'openai:gpt-
|
|
63
|
+
'openai:gpt-5',
|
|
64
64
|
output_type=list[FlightDetails],
|
|
65
65
|
system_prompt='Extract all the flight details from the given text.',
|
|
66
66
|
)
|
|
@@ -112,7 +112,7 @@ class Failed(BaseModel):
|
|
|
112
112
|
|
|
113
113
|
# This agent is responsible for extracting the user's seat selection
|
|
114
114
|
seat_preference_agent = Agent[None, SeatPreference | Failed](
|
|
115
|
-
'openai:gpt-
|
|
115
|
+
'openai:gpt-5',
|
|
116
116
|
output_type=SeatPreference | Failed,
|
|
117
117
|
system_prompt=(
|
|
118
118
|
"Extract the user's seat preference. "
|
{pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/question_graph.py
RENAMED
|
@@ -26,14 +26,16 @@ from pydantic_graph.persistence.file import FileStatePersistence
|
|
|
26
26
|
logfire.configure(send_to_logfire='if-token-present')
|
|
27
27
|
logfire.instrument_pydantic_ai()
|
|
28
28
|
|
|
29
|
-
ask_agent = Agent('openai:gpt-
|
|
29
|
+
ask_agent = Agent('openai:gpt-5', output_type=str)
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
@dataclass
|
|
33
33
|
class QuestionState:
|
|
34
34
|
question: str | None = None
|
|
35
|
-
ask_agent_messages: list[ModelMessage] = field(default_factory=list)
|
|
36
|
-
evaluate_agent_messages: list[ModelMessage] = field(
|
|
35
|
+
ask_agent_messages: list[ModelMessage] = field(default_factory=list[ModelMessage])
|
|
36
|
+
evaluate_agent_messages: list[ModelMessage] = field(
|
|
37
|
+
default_factory=list[ModelMessage]
|
|
38
|
+
)
|
|
37
39
|
|
|
38
40
|
|
|
39
41
|
@dataclass
|
|
@@ -65,7 +67,7 @@ class EvaluationOutput(BaseModel, use_attribute_docstrings=True):
|
|
|
65
67
|
|
|
66
68
|
|
|
67
69
|
evaluate_agent = Agent(
|
|
68
|
-
'openai:gpt-
|
|
70
|
+
'openai:gpt-5',
|
|
69
71
|
output_type=EvaluationOutput,
|
|
70
72
|
system_prompt='Given a question and answer, evaluate if the answer is correct.',
|
|
71
73
|
)
|
|
@@ -30,6 +30,7 @@ import asyncpg
|
|
|
30
30
|
import httpx
|
|
31
31
|
import logfire
|
|
32
32
|
import pydantic_core
|
|
33
|
+
from anyio import create_task_group
|
|
33
34
|
from openai import AsyncOpenAI
|
|
34
35
|
from pydantic import TypeAdapter
|
|
35
36
|
from typing_extensions import AsyncGenerator
|
|
@@ -48,7 +49,7 @@ class Deps:
|
|
|
48
49
|
pool: asyncpg.Pool
|
|
49
50
|
|
|
50
51
|
|
|
51
|
-
agent = Agent('openai:gpt-
|
|
52
|
+
agent = Agent('openai:gpt-5', deps_type=Deps)
|
|
52
53
|
|
|
53
54
|
|
|
54
55
|
@agent.tool
|
|
@@ -114,7 +115,7 @@ async def build_search_db():
|
|
|
114
115
|
async with httpx.AsyncClient() as client:
|
|
115
116
|
response = await client.get(DOCS_JSON)
|
|
116
117
|
response.raise_for_status()
|
|
117
|
-
sections =
|
|
118
|
+
sections = sections_ta.validate_json(response.content)
|
|
118
119
|
|
|
119
120
|
openai = AsyncOpenAI()
|
|
120
121
|
logfire.instrument_openai(openai)
|
|
@@ -126,9 +127,9 @@ async def build_search_db():
|
|
|
126
127
|
await conn.execute(DB_SCHEMA)
|
|
127
128
|
|
|
128
129
|
sem = asyncio.Semaphore(10)
|
|
129
|
-
async with
|
|
130
|
+
async with create_task_group() as tg:
|
|
130
131
|
for section in sections:
|
|
131
|
-
tg.
|
|
132
|
+
tg.start_soon(insert_doc_section, sem, openai, pool, section)
|
|
132
133
|
|
|
133
134
|
|
|
134
135
|
async def insert_doc_section(
|
|
@@ -182,7 +183,7 @@ class DocsSection:
|
|
|
182
183
|
return '\n\n'.join((f'path: {self.path}', f'title: {self.title}', self.content))
|
|
183
184
|
|
|
184
185
|
|
|
185
|
-
|
|
186
|
+
sections_ta = TypeAdapter(list[DocsSection])
|
|
186
187
|
|
|
187
188
|
|
|
188
189
|
# pyright: reportUnknownMemberType=false
|
|
@@ -92,7 +92,7 @@ class InvalidRequest(BaseModel):
|
|
|
92
92
|
|
|
93
93
|
Response: TypeAlias = Success | InvalidRequest
|
|
94
94
|
agent = Agent[Deps, Response](
|
|
95
|
-
'google-gla:gemini-
|
|
95
|
+
'google-gla:gemini-2.5-flash',
|
|
96
96
|
# Type ignore while we wait for PEP-0747, nonetheless unions will work fine everywhere else
|
|
97
97
|
output_type=Response, # type: ignore
|
|
98
98
|
deps_type=Deps,
|
{pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/stream_markdown.py
RENAMED
|
@@ -26,8 +26,8 @@ agent = Agent()
|
|
|
26
26
|
|
|
27
27
|
# models to try, and the appropriate env var
|
|
28
28
|
models: list[tuple[KnownModelName, str]] = [
|
|
29
|
-
('google-gla:gemini-2.
|
|
30
|
-
('openai:gpt-
|
|
29
|
+
('google-gla:gemini-2.5-flash', 'GEMINI_API_KEY'),
|
|
30
|
+
('openai:gpt-5-mini', 'OPENAI_API_KEY'),
|
|
31
31
|
('groq:llama-3.3-70b-versatile', 'GROQ_API_KEY'),
|
|
32
32
|
]
|
|
33
33
|
|
{pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/weather_agent.py
RENAMED
|
@@ -32,7 +32,7 @@ class Deps:
|
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
weather_agent = Agent(
|
|
35
|
-
'openai:gpt-
|
|
35
|
+
'openai:gpt-5-mini',
|
|
36
36
|
# 'Be concise, reply with one sentence.' is enough for some models (like openai) to use
|
|
37
37
|
# the below tools appropriately, but others like anthropic and gemini require a bit more direction.
|
|
38
38
|
instructions='Be concise, reply with one sentence.',
|
|
@@ -99,7 +99,6 @@ with gr.Blocks() as demo:
|
|
|
99
99
|
past_messages = gr.State([])
|
|
100
100
|
chatbot = gr.Chatbot(
|
|
101
101
|
label='Packing Assistant',
|
|
102
|
-
type='messages',
|
|
103
102
|
avatar_images=(None, 'https://ai.pydantic.dev/img/logo-white.svg'),
|
|
104
103
|
examples=[
|
|
105
104
|
{'text': 'What is the weather like in Miami?'},
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
"""Small but complete example of using Pydantic AI to build a support agent for a bank.
|
|
2
|
-
|
|
3
|
-
Run with:
|
|
4
|
-
|
|
5
|
-
uv run -m pydantic_ai_examples.bank_support
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from dataclasses import dataclass
|
|
9
|
-
|
|
10
|
-
from pydantic import BaseModel
|
|
11
|
-
|
|
12
|
-
from pydantic_ai import Agent, RunContext
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class DatabaseConn:
|
|
16
|
-
"""This is a fake database for example purposes.
|
|
17
|
-
|
|
18
|
-
In reality, you'd be connecting to an external database
|
|
19
|
-
(e.g. PostgreSQL) to get information about customers.
|
|
20
|
-
"""
|
|
21
|
-
|
|
22
|
-
@classmethod
|
|
23
|
-
async def customer_name(cls, *, id: int) -> str | None:
|
|
24
|
-
if id == 123:
|
|
25
|
-
return 'John'
|
|
26
|
-
|
|
27
|
-
@classmethod
|
|
28
|
-
async def customer_balance(cls, *, id: int, include_pending: bool) -> float:
|
|
29
|
-
if id == 123:
|
|
30
|
-
if include_pending:
|
|
31
|
-
return 123.45
|
|
32
|
-
else:
|
|
33
|
-
return 100.00
|
|
34
|
-
else:
|
|
35
|
-
raise ValueError('Customer not found')
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
@dataclass
|
|
39
|
-
class SupportDependencies:
|
|
40
|
-
customer_id: int
|
|
41
|
-
db: DatabaseConn
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
class SupportOutput(BaseModel):
|
|
45
|
-
support_advice: str
|
|
46
|
-
"""Advice returned to the customer"""
|
|
47
|
-
block_card: bool
|
|
48
|
-
"""Whether to block their card or not"""
|
|
49
|
-
risk: int
|
|
50
|
-
"""Risk level of query"""
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
support_agent = Agent(
|
|
54
|
-
'openai:gpt-4o',
|
|
55
|
-
deps_type=SupportDependencies,
|
|
56
|
-
output_type=SupportOutput,
|
|
57
|
-
instructions=(
|
|
58
|
-
'You are a support agent in our bank, give the '
|
|
59
|
-
'customer support and judge the risk level of their query. '
|
|
60
|
-
"Reply using the customer's name."
|
|
61
|
-
),
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
@support_agent.instructions
|
|
66
|
-
async def add_customer_name(ctx: RunContext[SupportDependencies]) -> str:
|
|
67
|
-
customer_name = await ctx.deps.db.customer_name(id=ctx.deps.customer_id)
|
|
68
|
-
return f"The customer's name is {customer_name!r}"
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
@support_agent.tool
|
|
72
|
-
async def customer_balance(
|
|
73
|
-
ctx: RunContext[SupportDependencies], include_pending: bool
|
|
74
|
-
) -> str:
|
|
75
|
-
"""Returns the customer's current account balance."""
|
|
76
|
-
balance = await ctx.deps.db.customer_balance(
|
|
77
|
-
id=ctx.deps.customer_id,
|
|
78
|
-
include_pending=include_pending,
|
|
79
|
-
)
|
|
80
|
-
return f'${balance:.2f}'
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if __name__ == '__main__':
|
|
84
|
-
deps = SupportDependencies(customer_id=123, db=DatabaseConn())
|
|
85
|
-
result = support_agent.run_sync('What is my balance?', deps=deps)
|
|
86
|
-
print(result.output)
|
|
87
|
-
"""
|
|
88
|
-
support_advice='Hello John, your current account balance, including pending transactions, is $123.45.' block_card=False risk=1
|
|
89
|
-
"""
|
|
90
|
-
|
|
91
|
-
result = support_agent.run_sync('I just lost my card!', deps=deps)
|
|
92
|
-
print(result.output)
|
|
93
|
-
"""
|
|
94
|
-
support_advice="I'm sorry to hear that, John. We are temporarily blocking your card to prevent unauthorized transactions." block_card=True risk=8
|
|
95
|
-
"""
|
|
File without changes
|
|
File without changes
|
{pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/__main__.py
RENAMED
|
File without changes
|
{pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/ag_ui/__init__.py
RENAMED
|
File without changes
|
{pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/ag_ui/__main__.py
RENAMED
|
File without changes
|
|
File without changes
|
{pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/chat_app.html
RENAMED
|
File without changes
|
{pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/chat_app.ts
RENAMED
|
File without changes
|
{pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/evals/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/evals/models.py
RENAMED
|
File without changes
|
|
File without changes
|
{pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/roulette_wheel.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pydantic_ai_examples-1.0.13 → pydantic_ai_examples-1.49.0}/pydantic_ai_examples/stream_whales.py
RENAMED
|
File without changes
|