Skip to content

Commit 8862e6e

Browse files
Kamal-MohaDouweM
andauthored
Implemented a convenient way to use ACI.dev Tools in PydanticAI (#2093)
Co-authored-by: Douwe Maan <douwe@pydantic.dev>
1 parent b31c77d commit 8862e6e

File tree

4 files changed

+119
-11
lines changed

4 files changed

+119
-11
lines changed

docs/tools.md

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -721,11 +721,19 @@ def my_flaky_tool(query: str) -> str:
721721
```
722722
Raising `ModelRetry` also generates a `RetryPromptPart` containing the exception message, which is sent back to the LLM to guide its next attempt. Both `ValidationError` and `ModelRetry` respect the `retries` setting configured on the `Tool` or `Agent`.
723723

724-
## Use LangChain Tools {#langchain-tools}
724+
## Third-Party Tools
725725

726-
If you'd like to use a tool from LangChain's [community tool library](https://python.langchain.com/docs/integrations/tools/) with PydanticAI, you can use the `pydancic_ai.ext.langchain.tool_from_langchain` convenience method. Note that PydanticAI will not validate the arguments in this case -- it's up to the model to provide arguments matching the schema specified by the LangChain tool, and up to the LangChain tool to raise an error if the arguments are invalid.
726+
### MCP Tools {#mcp-tools}
727727

728-
Here is how you can use it to augment model responses using a LangChain web search tool. This tool will need you to install the `langchain-community` and `duckduckgo-search` dependencies to work properly.
728+
See the [MCP Client](./mcp/client.md) documentation for how to use MCP servers with Pydantic AI.
729+
730+
### LangChain Tools {#langchain-tools}
731+
732+
If you'd like to use a tool from LangChain's [community tool library](https://python.langchain.com/docs/integrations/tools/) with Pydantic AI, you can use the `pydancic_ai.ext.langchain.tool_from_langchain` convenience method. Note that Pydantic AI will not validate the arguments in this case -- it's up to the model to provide arguments matching the schema specified by the LangChain tool, and up to the LangChain tool to raise an error if the arguments are invalid.
733+
734+
You will need to install the `langchain-community` package and any others required by the tool in question.
735+
736+
Here is how you can use the LangChain `DuckDuckGoSearchRun` tool, which requires the `duckduckgo-search` package:
729737

730738
```python {test="skip"}
731739
from langchain_community.tools import DuckDuckGoSearchRun
@@ -737,15 +745,44 @@ search = DuckDuckGoSearchRun()
737745
search_tool = tool_from_langchain(search)
738746

739747
agent = Agent(
740-
'google-gla:gemini-2.0-flash', # (1)!
748+
'google-gla:gemini-2.0-flash',
741749
tools=[search_tool],
742750
)
743751

744-
result = agent.run_sync('What is the release date of Elden Ring Nightreign?') # (2)!
752+
result = agent.run_sync('What is the release date of Elden Ring Nightreign?') # (1)!
745753
print(result.output)
746754
#> Elden Ring Nightreign is planned to be released on May 30, 2025.
747755
```
748756

757+
1. The release date of this game is the 30th of May 2025, which is after the knowledge cutoff for Gemini 2.0 (August 2024).
758+
759+
### ACI.dev Tools {#aci-tools}
760+
761+
If you'd like to use a tool from the [ACI.dev tool library](https://www.aci.dev/tools) with Pydantic AI, you can use the `pydancic_ai.ext.aci.tool_from_aci` convenience method. Note that Pydantic AI will not validate the arguments in this case -- it's up to the model to provide arguments matching the schema specified by the ACI tool, and up to the ACI tool to raise an error if the arguments are invalid.
762+
763+
You will need to install the `aci-sdk` package, set your ACI API key in the `ACI_API_KEY` environment variable, and pass your ACI "linked account owner ID" to the function.
764+
765+
Here is how you can use the ACI.dev `TAVILY__SEARCH` tool:
766+
767+
```python {test="skip"}
768+
import os
769+
770+
from pydantic_ai import Agent
771+
from pydantic_ai.ext.aci import tool_from_aci
772+
773+
tavily_search = tool_from_aci(
774+
'TAVILY__SEARCH',
775+
linked_account_owner_id=os.getenv('LINKED_ACCOUNT_OWNER_ID')
776+
)
777+
778+
agent = Agent(
779+
'google-gla:gemini-2.0-flash',
780+
tools=[tavily_search]
781+
)
782+
783+
result = agent.run_sync('What is the release date of Elden Ring Nightreign?') # (1)!
784+
print(result.output)
785+
#> Elden Ring Nightreign is planned to be released on May 30, 2025.
786+
```
749787

750-
1. While this task is simple Gemini 1.5 didn't want to use the provided tool. Gemini 2.0 is still fast and cheap.
751-
2. The release date of this game is the 30th of May 2025, which was confirmed after the knowledge cutoff for Gemini 2.0 (August 2024).
788+
1. The release date of this game is the 30th of May 2025, which is after the knowledge cutoff for Gemini 2.0 (August 2024).
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Checking whether aci-sdk is installed
2+
try:
3+
from aci import ACI
4+
except ImportError as _import_error:
5+
raise ImportError('Please install `aci-sdk` to use ACI.dev tools') from _import_error
6+
7+
from typing import Any
8+
9+
from aci import ACI
10+
11+
from pydantic_ai import Tool
12+
13+
14+
def _clean_schema(schema):
15+
if isinstance(schema, dict):
16+
# Remove non-standard keys (e.g., 'visible')
17+
return {k: _clean_schema(v) for k, v in schema.items() if k not in {'visible'}}
18+
elif isinstance(schema, list):
19+
return [_clean_schema(item) for item in schema]
20+
else:
21+
return schema
22+
23+
24+
def tool_from_aci(aci_function: str, linked_account_owner_id: str) -> Tool:
25+
"""Creates a Pydantic AI tool proxy from an ACI function.
26+
27+
Args:
28+
aci_function: The ACI function to wrao.
29+
linked_account_owner_id: The ACI user ID to execute the function on behalf of.
30+
31+
Returns:
32+
A Pydantic AI tool that corresponds to the ACI.dev tool.
33+
"""
34+
aci = ACI()
35+
function_definition = aci.functions.get_definition(aci_function)
36+
function_name = function_definition['function']['name']
37+
function_description = function_definition['function']['description']
38+
inputs = function_definition['function']['parameters']
39+
40+
json_schema = {
41+
'additionalProperties': inputs.get('additionalProperties', False),
42+
'properties': inputs.get('properties', {}),
43+
'required': inputs.get('required', []),
44+
# Default to 'object' if not specified
45+
'type': inputs.get('type', 'object'),
46+
}
47+
48+
# Clean the schema
49+
json_schema = _clean_schema(json_schema)
50+
51+
def implementation(*args: Any, **kwargs: Any) -> str:
52+
if args:
53+
raise TypeError('Positional arguments are not allowed')
54+
return aci.handle_function_call(
55+
function_name,
56+
kwargs,
57+
linked_account_owner_id=linked_account_owner_id,
58+
allowed_apps_only=True,
59+
)
60+
61+
return Tool.from_schema(
62+
function=implementation,
63+
name=function_name,
64+
description=function_description,
65+
json_schema=json_schema,
66+
)

pydantic_ai_slim/pydantic_ai/ext/langchain.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@ def run(self, *args: Any, **kwargs: Any) -> str: ...
2727

2828

2929
def tool_from_langchain(langchain_tool: LangChainTool) -> Tool:
30-
"""Creates a Pydantic tool proxy from a LangChain tool.
30+
"""Creates a Pydantic AI tool proxy from a LangChain tool.
3131
3232
Args:
3333
langchain_tool: The LangChain tool to wrap.
3434
3535
Returns:
36-
A Pydantic tool that corresponds to the LangChain tool.
36+
A Pydantic AI tool that corresponds to the LangChain tool.
3737
"""
3838
function_name = langchain_tool.name
3939
function_description = langchain_tool.description

pyproject.toml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ executionEnvironments = [
180180
exclude = [
181181
"examples/pydantic_ai_examples/weather_agent_gradio.py",
182182
"mcp-run-python/node_modules",
183+
"pydantic_ai_slim/pydantic_ai/ext/aci.py", # aci-sdk requires Python 3.10+ so cannot be added as an (optional) dependency
183184
]
184185
extraPaths = ["mcp-run-python/stubs"]
185186

@@ -219,7 +220,11 @@ include = [
219220
# "fasta2a/**/*.py",
220221
"tests/**/*.py",
221222
]
222-
omit = ["tests/test_live.py", "tests/example_modules/*.py"]
223+
omit = [
224+
"tests/test_live.py",
225+
"tests/example_modules/*.py",
226+
"pydantic_ai_slim/pydantic_ai/ext/aci.py", # aci-sdk requires Python 3.10+ so cannot be added as an (optional) dependency
227+
]
223228
branch = true
224229

225230
# https://coverage.readthedocs.io/en/latest/config.html#report
@@ -265,4 +270,4 @@ skip = '.git*,*.svg,*.lock,*.css,*.yaml'
265270
check-hidden = true
266271
# Ignore "formatting" like **L**anguage
267272
ignore-regex = '\*\*[A-Z]\*\*[a-z]+\b'
268-
ignore-words-list = 'asend'
273+
ignore-words-list = 'asend,aci'

0 commit comments

Comments
 (0)