pydantic-ai-slim 0.0.27__tar.gz → 0.0.29__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.
Potentially problematic release.
This version of pydantic-ai-slim might be problematic. Click here for more details.
- {pydantic_ai_slim-0.0.27 → pydantic_ai_slim-0.0.29}/PKG-INFO +6 -2
- {pydantic_ai_slim-0.0.27 → pydantic_ai_slim-0.0.29}/pydantic_ai/agent.py +2 -2
- pydantic_ai_slim-0.0.29/pydantic_ai/common_tools/duckduckgo.py +73 -0
- pydantic_ai_slim-0.0.29/pydantic_ai/common_tools/tavily.py +82 -0
- {pydantic_ai_slim-0.0.27 → pydantic_ai_slim-0.0.29}/pydantic_ai/models/anthropic.py +30 -6
- pydantic_ai_slim-0.0.29/pydantic_ai/py.typed +0 -0
- {pydantic_ai_slim-0.0.27 → pydantic_ai_slim-0.0.29}/pydantic_ai/tools.py +8 -1
- {pydantic_ai_slim-0.0.27 → pydantic_ai_slim-0.0.29}/pyproject.toml +6 -2
- {pydantic_ai_slim-0.0.27 → pydantic_ai_slim-0.0.29}/.gitignore +0 -0
- {pydantic_ai_slim-0.0.27 → pydantic_ai_slim-0.0.29}/README.md +0 -0
- {pydantic_ai_slim-0.0.27 → pydantic_ai_slim-0.0.29}/pydantic_ai/__init__.py +0 -0
- {pydantic_ai_slim-0.0.27 → pydantic_ai_slim-0.0.29}/pydantic_ai/_agent_graph.py +0 -0
- {pydantic_ai_slim-0.0.27 → pydantic_ai_slim-0.0.29}/pydantic_ai/_griffe.py +0 -0
- {pydantic_ai_slim-0.0.27 → pydantic_ai_slim-0.0.29}/pydantic_ai/_parts_manager.py +0 -0
- {pydantic_ai_slim-0.0.27 → pydantic_ai_slim-0.0.29}/pydantic_ai/_pydantic.py +0 -0
- {pydantic_ai_slim-0.0.27 → pydantic_ai_slim-0.0.29}/pydantic_ai/_result.py +0 -0
- {pydantic_ai_slim-0.0.27 → pydantic_ai_slim-0.0.29}/pydantic_ai/_system_prompt.py +0 -0
- {pydantic_ai_slim-0.0.27 → pydantic_ai_slim-0.0.29}/pydantic_ai/_utils.py +0 -0
- /pydantic_ai_slim-0.0.27/pydantic_ai/py.typed → /pydantic_ai_slim-0.0.29/pydantic_ai/common_tools/__init__.py +0 -0
- {pydantic_ai_slim-0.0.27 → pydantic_ai_slim-0.0.29}/pydantic_ai/exceptions.py +0 -0
- {pydantic_ai_slim-0.0.27 → pydantic_ai_slim-0.0.29}/pydantic_ai/format_as_xml.py +0 -0
- {pydantic_ai_slim-0.0.27 → pydantic_ai_slim-0.0.29}/pydantic_ai/messages.py +0 -0
- {pydantic_ai_slim-0.0.27 → pydantic_ai_slim-0.0.29}/pydantic_ai/models/__init__.py +0 -0
- {pydantic_ai_slim-0.0.27 → pydantic_ai_slim-0.0.29}/pydantic_ai/models/cohere.py +0 -0
- {pydantic_ai_slim-0.0.27 → pydantic_ai_slim-0.0.29}/pydantic_ai/models/fallback.py +0 -0
- {pydantic_ai_slim-0.0.27 → pydantic_ai_slim-0.0.29}/pydantic_ai/models/function.py +0 -0
- {pydantic_ai_slim-0.0.27 → pydantic_ai_slim-0.0.29}/pydantic_ai/models/gemini.py +0 -0
- {pydantic_ai_slim-0.0.27 → pydantic_ai_slim-0.0.29}/pydantic_ai/models/groq.py +0 -0
- {pydantic_ai_slim-0.0.27 → pydantic_ai_slim-0.0.29}/pydantic_ai/models/instrumented.py +0 -0
- {pydantic_ai_slim-0.0.27 → pydantic_ai_slim-0.0.29}/pydantic_ai/models/mistral.py +0 -0
- {pydantic_ai_slim-0.0.27 → pydantic_ai_slim-0.0.29}/pydantic_ai/models/openai.py +0 -0
- {pydantic_ai_slim-0.0.27 → pydantic_ai_slim-0.0.29}/pydantic_ai/models/test.py +0 -0
- {pydantic_ai_slim-0.0.27 → pydantic_ai_slim-0.0.29}/pydantic_ai/models/vertexai.py +0 -0
- {pydantic_ai_slim-0.0.27 → pydantic_ai_slim-0.0.29}/pydantic_ai/models/wrapper.py +0 -0
- {pydantic_ai_slim-0.0.27 → pydantic_ai_slim-0.0.29}/pydantic_ai/result.py +0 -0
- {pydantic_ai_slim-0.0.27 → pydantic_ai_slim-0.0.29}/pydantic_ai/settings.py +0 -0
- {pydantic_ai_slim-0.0.27 → pydantic_ai_slim-0.0.29}/pydantic_ai/usage.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pydantic-ai-slim
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.29
|
|
4
4
|
Summary: Agent Framework / shim to use Pydantic with LLMs, slim package
|
|
5
5
|
Author-email: Samuel Colvin <samuel@pydantic.dev>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -29,12 +29,14 @@ Requires-Dist: exceptiongroup; python_version < '3.11'
|
|
|
29
29
|
Requires-Dist: griffe>=1.3.2
|
|
30
30
|
Requires-Dist: httpx>=0.27
|
|
31
31
|
Requires-Dist: logfire-api>=1.2.0
|
|
32
|
-
Requires-Dist: pydantic-graph==0.0.
|
|
32
|
+
Requires-Dist: pydantic-graph==0.0.29
|
|
33
33
|
Requires-Dist: pydantic>=2.10
|
|
34
34
|
Provides-Extra: anthropic
|
|
35
35
|
Requires-Dist: anthropic>=0.40.0; extra == 'anthropic'
|
|
36
36
|
Provides-Extra: cohere
|
|
37
37
|
Requires-Dist: cohere>=5.13.11; extra == 'cohere'
|
|
38
|
+
Provides-Extra: duckduckgo
|
|
39
|
+
Requires-Dist: duckduckgo-search>=7.0.0; extra == 'duckduckgo'
|
|
38
40
|
Provides-Extra: groq
|
|
39
41
|
Requires-Dist: groq>=0.12.0; extra == 'groq'
|
|
40
42
|
Provides-Extra: logfire
|
|
@@ -43,6 +45,8 @@ Provides-Extra: mistral
|
|
|
43
45
|
Requires-Dist: mistralai>=1.2.5; extra == 'mistral'
|
|
44
46
|
Provides-Extra: openai
|
|
45
47
|
Requires-Dist: openai>=1.61.0; extra == 'openai'
|
|
48
|
+
Provides-Extra: tavily
|
|
49
|
+
Requires-Dist: tavily-python>=0.5.0; extra == 'tavily'
|
|
46
50
|
Provides-Extra: vertexai
|
|
47
51
|
Requires-Dist: google-auth>=2.36.0; extra == 'vertexai'
|
|
48
52
|
Requires-Dist: requests>=2.32.3; extra == 'vertexai'
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations as _annotations
|
|
2
2
|
|
|
3
|
-
import asyncio
|
|
4
3
|
import dataclasses
|
|
5
4
|
import inspect
|
|
6
5
|
from collections.abc import AsyncIterator, Awaitable, Iterator, Sequence
|
|
@@ -13,6 +12,7 @@ import logfire_api
|
|
|
13
12
|
from typing_extensions import TypeVar, deprecated
|
|
14
13
|
|
|
15
14
|
from pydantic_graph import BaseNode, End, Graph, GraphRun, GraphRunContext
|
|
15
|
+
from pydantic_graph._utils import get_event_loop
|
|
16
16
|
|
|
17
17
|
from . import (
|
|
18
18
|
_agent_graph,
|
|
@@ -538,7 +538,7 @@ class Agent(Generic[AgentDepsT, ResultDataT]):
|
|
|
538
538
|
"""
|
|
539
539
|
if infer_name and self.name is None:
|
|
540
540
|
self._infer_name(inspect.currentframe())
|
|
541
|
-
return
|
|
541
|
+
return get_event_loop().run_until_complete(
|
|
542
542
|
self.run(
|
|
543
543
|
user_prompt,
|
|
544
544
|
result_type=result_type,
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import TypedDict
|
|
4
|
+
|
|
5
|
+
import anyio
|
|
6
|
+
import anyio.to_thread
|
|
7
|
+
from pydantic import TypeAdapter
|
|
8
|
+
|
|
9
|
+
from pydantic_ai.tools import Tool
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
from duckduckgo_search import DDGS
|
|
13
|
+
except ImportError as _import_error:
|
|
14
|
+
raise ImportError(
|
|
15
|
+
'Please install `duckduckgo-search` to use the DuckDuckGo search tool, '
|
|
16
|
+
"you can use the `duckduckgo` optional group — `pip install 'pydantic-ai-slim[duckduckgo]'`"
|
|
17
|
+
) from _import_error
|
|
18
|
+
|
|
19
|
+
__all__ = ('duckduckgo_search_tool',)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class DuckDuckGoResult(TypedDict):
|
|
23
|
+
"""A DuckDuckGo search result."""
|
|
24
|
+
|
|
25
|
+
title: str
|
|
26
|
+
"""The title of the search result."""
|
|
27
|
+
href: str
|
|
28
|
+
"""The URL of the search result."""
|
|
29
|
+
body: str
|
|
30
|
+
"""The body of the search result."""
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
duckduckgo_ta = TypeAdapter(list[DuckDuckGoResult])
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class DuckDuckGoSearchTool:
|
|
38
|
+
"""The DuckDuckGo search tool."""
|
|
39
|
+
|
|
40
|
+
client: DDGS
|
|
41
|
+
"""The DuckDuckGo search client."""
|
|
42
|
+
|
|
43
|
+
max_results: int | None = None
|
|
44
|
+
"""The maximum number of results. If None, returns results only from the first response."""
|
|
45
|
+
|
|
46
|
+
async def __call__(self, query: str) -> list[DuckDuckGoResult]:
|
|
47
|
+
"""Searches DuckDuckGo for the given query and returns the results.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
query: The query to search for.
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
The search results.
|
|
54
|
+
"""
|
|
55
|
+
search = functools.partial(self.client.text, max_results=self.max_results)
|
|
56
|
+
results = await anyio.to_thread.run_sync(search, query)
|
|
57
|
+
if len(results) == 0:
|
|
58
|
+
raise RuntimeError('No search results found.')
|
|
59
|
+
return duckduckgo_ta.validate_python(results)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def duckduckgo_search_tool(duckduckgo_client: DDGS | None = None, max_results: int | None = None):
|
|
63
|
+
"""Creates a DuckDuckGo search tool.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
duckduckgo_client: The DuckDuckGo search client.
|
|
67
|
+
max_results: The maximum number of results. If None, returns results only from the first response.
|
|
68
|
+
"""
|
|
69
|
+
return Tool(
|
|
70
|
+
DuckDuckGoSearchTool(client=duckduckgo_client or DDGS(), max_results=max_results).__call__,
|
|
71
|
+
name='duckduckgo_search',
|
|
72
|
+
description='Searches DuckDuckGo for the given query and returns the results.',
|
|
73
|
+
)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Literal, TypedDict
|
|
3
|
+
|
|
4
|
+
from pydantic import TypeAdapter
|
|
5
|
+
|
|
6
|
+
from pydantic_ai.tools import Tool
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
from tavily import AsyncTavilyClient
|
|
10
|
+
except ImportError as _import_error:
|
|
11
|
+
raise ImportError(
|
|
12
|
+
'Please install `tavily-python` to use the Tavily search tool, '
|
|
13
|
+
"you can use the `tavily` optional group — `pip install 'pydantic-ai-slim[tavily]'`"
|
|
14
|
+
) from _import_error
|
|
15
|
+
|
|
16
|
+
__all__ = ('tavily_search_tool',)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class TavilySearchResult(TypedDict):
|
|
20
|
+
"""A Tavily search result.
|
|
21
|
+
|
|
22
|
+
See [Tavily Search Endpoint documentation](https://docs.tavily.com/api-reference/endpoint/search)
|
|
23
|
+
for more information.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
title: str
|
|
27
|
+
"""The title of the search result."""
|
|
28
|
+
url: str
|
|
29
|
+
"""The URL of the search result.."""
|
|
30
|
+
content: str
|
|
31
|
+
"""A short description of the search result."""
|
|
32
|
+
score: float
|
|
33
|
+
"""The relevance score of the search result."""
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
tavily_search_ta = TypeAdapter(list[TavilySearchResult])
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
class TavilySearchTool:
|
|
41
|
+
"""The Tavily search tool."""
|
|
42
|
+
|
|
43
|
+
client: AsyncTavilyClient
|
|
44
|
+
"""The Tavily search client."""
|
|
45
|
+
|
|
46
|
+
async def __call__(
|
|
47
|
+
self,
|
|
48
|
+
query: str,
|
|
49
|
+
search_deep: Literal['basic', 'advanced'] = 'basic',
|
|
50
|
+
topic: Literal['general', 'news'] = 'general',
|
|
51
|
+
time_range: Literal['day', 'week', 'month', 'year', 'd', 'w', 'm', 'y'] | None = None,
|
|
52
|
+
):
|
|
53
|
+
"""Searches Tavily for the given query and returns the results.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
query: The search query to execute with Tavily.
|
|
57
|
+
search_deep: The depth of the search.
|
|
58
|
+
topic: The category of the search.
|
|
59
|
+
time_range: The time range back from the current date to filter results.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
The search results.
|
|
63
|
+
"""
|
|
64
|
+
results = await self.client.search(query, search_depth=search_deep, topic=topic, time_range=time_range) # type: ignore[reportUnknownMemberType]
|
|
65
|
+
if not results['results']:
|
|
66
|
+
raise RuntimeError('No search results found.')
|
|
67
|
+
return tavily_search_ta.validate_python(results['results'])
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def tavily_search_tool(api_key: str):
|
|
71
|
+
"""Creates a Tavily search tool.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
api_key: The Tavily API key.
|
|
75
|
+
|
|
76
|
+
You can get one by signing up at [https://app.tavily.com/home](https://app.tavily.com/home).
|
|
77
|
+
"""
|
|
78
|
+
return Tool(
|
|
79
|
+
TavilySearchTool(client=AsyncTavilyClient(api_key)).__call__,
|
|
80
|
+
name='tavily_search',
|
|
81
|
+
description='Searches Tavily for the given query and returns the results.',
|
|
82
|
+
)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations as _annotations
|
|
2
2
|
|
|
3
|
+
import base64
|
|
3
4
|
import io
|
|
4
5
|
from collections.abc import AsyncGenerator, AsyncIterable, AsyncIterator
|
|
5
6
|
from contextlib import asynccontextmanager
|
|
@@ -343,12 +344,35 @@ class AnthropicModel(Model):
|
|
|
343
344
|
else:
|
|
344
345
|
raise RuntimeError('Only images are supported for binary content')
|
|
345
346
|
elif isinstance(item, ImageUrl):
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
347
|
+
try:
|
|
348
|
+
response = await cached_async_http_client().get(item.url)
|
|
349
|
+
response.raise_for_status()
|
|
350
|
+
yield ImageBlockParam(
|
|
351
|
+
source={
|
|
352
|
+
'data': io.BytesIO(response.content),
|
|
353
|
+
'media_type': item.media_type,
|
|
354
|
+
'type': 'base64',
|
|
355
|
+
},
|
|
356
|
+
type='image',
|
|
357
|
+
)
|
|
358
|
+
except ValueError:
|
|
359
|
+
# Download the file if can't find the mime type.
|
|
360
|
+
client = cached_async_http_client()
|
|
361
|
+
response = await client.get(item.url, follow_redirects=True)
|
|
362
|
+
response.raise_for_status()
|
|
363
|
+
base64_encoded = base64.b64encode(response.content).decode('utf-8')
|
|
364
|
+
if (mime_type := response.headers['Content-Type']) in (
|
|
365
|
+
'image/jpeg',
|
|
366
|
+
'image/png',
|
|
367
|
+
'image/gif',
|
|
368
|
+
'image/webp',
|
|
369
|
+
):
|
|
370
|
+
yield ImageBlockParam(
|
|
371
|
+
source={'data': base64_encoded, 'media_type': mime_type, 'type': 'base64'},
|
|
372
|
+
type='image',
|
|
373
|
+
)
|
|
374
|
+
else: # pragma: no cover
|
|
375
|
+
raise RuntimeError(f'Unsupported image type: {mime_type}')
|
|
352
376
|
else:
|
|
353
377
|
raise RuntimeError(f'Unsupported content type: {type(item)}')
|
|
354
378
|
|
|
File without changes
|
|
@@ -49,6 +49,8 @@ class RunContext(Generic[AgentDepsT]):
|
|
|
49
49
|
"""The original user prompt passed to the run."""
|
|
50
50
|
messages: list[_messages.ModelMessage] = field(default_factory=list)
|
|
51
51
|
"""Messages exchanged in the conversation so far."""
|
|
52
|
+
tool_call_id: str | None = None
|
|
53
|
+
"""The ID of the tool call."""
|
|
52
54
|
tool_name: str | None = None
|
|
53
55
|
"""Name of the tool being called."""
|
|
54
56
|
retry: int = 0
|
|
@@ -301,7 +303,12 @@ class Tool(Generic[AgentDepsT]):
|
|
|
301
303
|
if self._single_arg_name:
|
|
302
304
|
args_dict = {self._single_arg_name: args_dict}
|
|
303
305
|
|
|
304
|
-
ctx = dataclasses.replace(
|
|
306
|
+
ctx = dataclasses.replace(
|
|
307
|
+
run_context,
|
|
308
|
+
retry=self.current_retry,
|
|
309
|
+
tool_name=message.tool_name,
|
|
310
|
+
tool_call_id=message.tool_call_id,
|
|
311
|
+
)
|
|
305
312
|
args = [ctx] if self.takes_ctx else []
|
|
306
313
|
for positional_field in self._positional_fields:
|
|
307
314
|
args.append(args_dict.pop(positional_field))
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pydantic-ai-slim"
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.29"
|
|
8
8
|
description = "Agent Framework / shim to use Pydantic with LLMs, slim package"
|
|
9
9
|
authors = [{ name = "Samuel Colvin", email = "samuel@pydantic.dev" }]
|
|
10
10
|
license = "MIT"
|
|
@@ -37,19 +37,23 @@ dependencies = [
|
|
|
37
37
|
"httpx>=0.27",
|
|
38
38
|
"logfire-api>=1.2.0",
|
|
39
39
|
"pydantic>=2.10",
|
|
40
|
-
"pydantic-graph==0.0.
|
|
40
|
+
"pydantic-graph==0.0.29",
|
|
41
41
|
"exceptiongroup; python_version < '3.11'",
|
|
42
42
|
]
|
|
43
43
|
|
|
44
44
|
[project.optional-dependencies]
|
|
45
45
|
# WARNING if you add optional groups, please update docs/install.md
|
|
46
46
|
logfire = ["logfire>=2.3"]
|
|
47
|
+
# Models
|
|
47
48
|
openai = ["openai>=1.61.0"]
|
|
48
49
|
cohere = ["cohere>=5.13.11"]
|
|
49
50
|
vertexai = ["google-auth>=2.36.0", "requests>=2.32.3"]
|
|
50
51
|
anthropic = ["anthropic>=0.40.0"]
|
|
51
52
|
groq = ["groq>=0.12.0"]
|
|
52
53
|
mistral = ["mistralai>=1.2.5"]
|
|
54
|
+
# Tools
|
|
55
|
+
duckduckgo = ["duckduckgo-search>=7.0.0"]
|
|
56
|
+
tavily = ["tavily-python>=0.5.0"]
|
|
53
57
|
|
|
54
58
|
[dependency-groups]
|
|
55
59
|
dev = [
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|