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
4 changes: 2 additions & 2 deletions pydantic_ai_examples/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "pydantic-ai-examples"
version = "0.0.10"
version = "0.0.11"
description = "Examples of how to use PydanticAI and what it can do."
authors = [
{ name = "Samuel Colvin", email = "samuel@pydantic.dev" },
Expand Down Expand Up @@ -34,7 +34,7 @@ classifiers = [
]
requires-python = ">=3.9"
dependencies = [
"pydantic-ai-slim[openai,vertexai,groq]==0.0.10",
"pydantic-ai-slim[openai,vertexai,groq]==0.0.11",
"asyncpg>=0.30.0",
"fastapi>=0.115.4",
"logfire[asyncpg,fastapi]>=2.3",
Expand Down
12 changes: 10 additions & 2 deletions pydantic_ai_slim/pydantic_ai/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,11 +171,12 @@ async def run(
deps = self._get_deps(deps)

with _logfire.span(
'{agent.name} run {prompt=}',
'{agent_name} run {prompt=}',
prompt=user_prompt,
agent=self,
custom_model=custom_model,
model_name=model_used.name(),
agent_name=self.name or 'agent',
) as run_span:
new_message_index, messages = await self._prepare_messages(deps, user_prompt, message_history)
self.last_run_messages = messages
Expand Down Expand Up @@ -277,11 +278,12 @@ async def run_stream(
deps = self._get_deps(deps)

with _logfire.span(
'{agent.name} run stream {prompt=}',
'{agent_name} run stream {prompt=}',
prompt=user_prompt,
agent=self,
custom_model=custom_model,
model_name=model_used.name(),
agent_name=self.name or 'agent',
) as run_span:
new_message_index, messages = await self._prepare_messages(deps, user_prompt, message_history)
self.last_run_messages = messages
Expand Down Expand Up @@ -837,6 +839,12 @@ def _infer_name(self, function_frame: FrameType | None) -> None:
if item is self:
self.name = name
return
if parent_frame.f_locals != parent_frame.f_globals:
# if we couldn't find the agent in locals and globals are a different dict, try globals
for name, item in parent_frame.f_globals.items():
if item is self:
self.name = name
return


@dataclass
Expand Down
2 changes: 1 addition & 1 deletion pydantic_ai_slim/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "pydantic-ai-slim"
version = "0.0.10"
version = "0.0.11"
description = "Agent Framework / shim to use Pydantic with LLMs, slim package"
authors = [
{ name = "Samuel Colvin", email = "samuel@pydantic.dev" },
Expand Down
6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "pydantic-ai"
version = "0.0.10"
version = "0.0.11"
description = "Agent Framework / shim to use Pydantic with LLMs"
authors = [
{ name = "Samuel Colvin", email = "samuel@pydantic.dev" },
Expand Down Expand Up @@ -36,7 +36,7 @@ classifiers = [
"Framework :: Pytest",
]
requires-python = ">=3.9"
dependencies = ["pydantic-ai-slim[openai,vertexai,groq]==0.0.10"]
dependencies = ["pydantic-ai-slim[openai,vertexai,groq]==0.0.11"]

[project.urls]
Homepage = "https://ai.pydantic.dev"
Expand All @@ -45,7 +45,7 @@ Documentation = "https://ai.pydantic.dev"
Changelog = "https://github.com/pydantic/pydantic-ai/releases"

[project.optional-dependencies]
examples = ["pydantic-ai-examples==0.0.10"]
examples = ["pydantic-ai-examples==0.0.11"]
logfire = ["logfire>=2.3"]

[tool.uv.sources]
Expand Down
18 changes: 18 additions & 0 deletions tests/test_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -574,3 +574,21 @@ async def test_agent_name_changes():

await new_agent.run('Hello')
assert new_agent.name == 'my_agent'


def test_name_from_global(set_event_loop: None, create_module: Callable[[str], Any]):
module_code = """
from pydantic_ai import Agent

my_agent = Agent('test')

def foo():
result = my_agent.run_sync('Hello')
return result.data
"""

mod = create_module(module_code)

assert mod.my_agent.name is None
assert mod.foo() == snapshot('success (no tool calls)')
assert mod.my_agent.name == 'my_agent'
164 changes: 160 additions & 4 deletions tests/test_logfire.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,21 +60,21 @@ def get_summary() -> LogfireSummary:

@pytest.mark.skipif(not logfire_installed, reason='logfire not installed')
def test_logfire(get_logfire_summary: Callable[[], LogfireSummary], set_event_loop: None) -> None:
agent = Agent(model=TestModel())
my_agent = Agent(model=TestModel())

@agent.tool_plain
@my_agent.tool_plain
async def my_ret(x: int) -> str:
return str(x + 1)

result = agent.run_sync('Hello')
result = my_agent.run_sync('Hello')
assert result.data == snapshot('{"my_ret":"1"}')

summary = get_logfire_summary()
assert summary.traces == snapshot(
[
{
'id': 0,
'message': 'agent run prompt=Hello',
'message': 'my_agent run prompt=Hello',
'children': [
{'id': 1, 'message': 'model request -> model-structured-response'},
{
Expand All @@ -88,6 +88,162 @@ async def my_ret(x: int) -> str:
}
]
)
assert summary.attributes[0] == snapshot(
{
'code.filepath': 'agent.py',
'code.function': 'run',
'code.lineno': 123,
'prompt': 'Hello',
'agent': IsJson(
{
'model': {
'call_tools': 'all',
'custom_result_text': None,
'custom_result_args': None,
'seed': 0,
'agent_model_tools': {
'my_ret': {
'function': IsStr(regex='<function test_logfire.<locals>.my_ret at 0x.+>'),
'takes_ctx': False,
'max_retries': 1,
'name': 'my_ret',
'description': '',
'_is_async': True,
'_single_arg_name': None,
'_positional_fields': [],
'_var_positional_field': None,
'_json_schema': {
'properties': {'x': {'title': 'X', 'type': 'integer'}},
'required': ['x'],
'type': 'object',
'additionalProperties': False,
},
'_current_retry': 0,
}
},
'agent_model_allow_text_result': True,
'agent_model_result_tools': None,
},
'name': 'my_agent',
'last_run_messages': None,
}
),
'logfire.null_args': ('custom_model',),
'model_name': 'test-model',
'agent_name': 'my_agent',
'logfire.msg_template': '{agent_name} run {prompt=}',
'logfire.msg': 'my_agent run prompt=Hello',
'logfire.span_type': 'span',
'all_messages': IsJson(
[
{'content': 'Hello', 'timestamp': IsStr(regex=r'\d{4}-\d{2}-.+'), 'role': 'user'},
{
'calls': [{'tool_name': 'my_ret', 'args': {'args_dict': {'x': 0}}, 'tool_id': None}],
'timestamp': IsStr(regex=r'\d{4}-\d{2}-.+'),
'role': 'model-structured-response',
},
{
'tool_name': 'my_ret',
'content': '1',
'tool_id': None,
'timestamp': IsStr(regex=r'\d{4}-\d{2}-.+'),
'role': 'tool-return',
},
{
'content': '{"my_ret":"1"}',
'timestamp': IsStr(regex=r'\d{4}-\d{2}-.+'),
'role': 'model-text-response',
},
]
),
'cost': IsJson({'request_tokens': None, 'response_tokens': None, 'total_tokens': None, 'details': None}),
'logfire.json_schema': IsJson(
{
'type': 'object',
'properties': {
'prompt': {},
'agent': {
'type': 'object',
'title': 'Agent',
'x-python-datatype': 'dataclass',
'properties': {
'model': {
'type': 'object',
'title': 'TestModel',
'x-python-datatype': 'dataclass',
'properties': {
'agent_model_tools': {
'type': 'object',
'properties': {
'my_ret': {
'type': 'object',
'title': 'Tool',
'x-python-datatype': 'dataclass',
'properties': {
'function': {'type': 'object', 'x-python-datatype': 'unknown'}
},
}
},
}
},
}
},
},
'custom_model': {},
'model_name': {},
'agent_name': {},
'all_messages': {
'type': 'array',
'prefixItems': [
{
'type': 'object',
'title': 'UserPrompt',
'x-python-datatype': 'dataclass',
'properties': {'timestamp': {'type': 'string', 'format': 'date-time'}},
},
{
'type': 'object',
'title': 'ModelStructuredResponse',
'x-python-datatype': 'dataclass',
'properties': {
'calls': {
'type': 'array',
'items': {
'type': 'object',
'title': 'ToolCall',
'x-python-datatype': 'dataclass',
'properties': {
'args': {
'type': 'object',
'title': 'ArgsDict',
'x-python-datatype': 'dataclass',
}
},
},
},
'timestamp': {'type': 'string', 'format': 'date-time'},
},
},
{
'type': 'object',
'title': 'ToolReturn',
'x-python-datatype': 'dataclass',
'properties': {'timestamp': {'type': 'string', 'format': 'date-time'}},
},
{
'type': 'object',
'title': 'ModelTextResponse',
'x-python-datatype': 'dataclass',
'properties': {'timestamp': {'type': 'string', 'format': 'date-time'}},
},
],
},
'cost': {'type': 'object', 'title': 'Cost', 'x-python-datatype': 'dataclass'},
},
}
),
}
)
assert summary.attributes[1] == snapshot(
{
'code.filepath': 'agent.py',
Expand Down
6 changes: 3 additions & 3 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.