langchain-arcade 0.1.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.
- langchain_arcade-0.1.0/LICENSE +21 -0
- langchain_arcade-0.1.0/PKG-INFO +73 -0
- langchain_arcade-0.1.0/README.md +51 -0
- langchain_arcade-0.1.0/langchain_arcade/__init__.py +3 -0
- langchain_arcade-0.1.0/langchain_arcade/_utilities.py +182 -0
- langchain_arcade-0.1.0/langchain_arcade/manager.py +181 -0
- langchain_arcade-0.1.0/langchain_arcade/py.typed +0 -0
- langchain_arcade-0.1.0/pyproject.toml +49 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024, Arcade AI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: langchain-arcade
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: An integration package connecting Arcade AI and LangChain/LangGraph
|
|
5
|
+
Home-page: https://github.com/arcadeai/arcade-ai/tree/main/contrib/langchain
|
|
6
|
+
License: MIT
|
|
7
|
+
Author: Arcade AI
|
|
8
|
+
Author-email: dev@arcade-ai.com
|
|
9
|
+
Requires-Python: >=3.10,<3.13
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Provides-Extra: langgraph
|
|
16
|
+
Requires-Dist: arcadepy (>=0.1.1,<0.2.0)
|
|
17
|
+
Requires-Dist: langchain-core (>=0.3.0,<0.4.0)
|
|
18
|
+
Requires-Dist: langgraph (>=0.2.32,<0.3.0) ; extra == "langgraph"
|
|
19
|
+
Project-URL: Repository, https://github.com/arcadeai/arcade-ai/tree/main/contrib/langchain
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
<div align="center">
|
|
23
|
+
|
|
24
|
+
<img width="400px" src="https://docs-112.pages.dev/images/logo/arcade-ai-logo.png" />
|
|
25
|
+
|
|
26
|
+
### LLM Tool Calling Platform
|
|
27
|
+
|
|
28
|
+
<p>
|
|
29
|
+
<img alt="GitHub Last Commit" src="https://img.shields.io/github/last-commit/arcadeai/arcade-ai" />
|
|
30
|
+
<img alt="GitHub Issues" src="https://img.shields.io/github/issues/arcadeai/arcade-ai" />
|
|
31
|
+
<img alt="GitHub Pull Requests" src="https://img.shields.io/github/issues-pr/arcadeai/arcade-ai" />
|
|
32
|
+
<img alt="GitHub License" src="https://img.shields.io/badge/License-MIT-yellow.svg" />
|
|
33
|
+
<img alt="Discord" src="https://img.shields.io/discord/1110910277110743103?label=Discord&logo=discord&logoColor=white&style=plastic&color=d7b023)](https://discord.gg/" />
|
|
34
|
+
</p>
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
<p align="center">
|
|
39
|
+
<a href="https://docs-112.pages.dev" target="_blank">Docs</a> •
|
|
40
|
+
<a href="https://docs-112.pages.dev/guides" target="_blank">Guides</a> •
|
|
41
|
+
<a href="https://docs-112.pages.dev/integrations" target="_blank">Integrations</a> •
|
|
42
|
+
<a href="https://discord.com/invite/" target="_blank">Discord</a>
|
|
43
|
+
|
|
44
|
+
</p>
|
|
45
|
+
|
|
46
|
+
<br />
|
|
47
|
+
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
Arcade AI is the developer platform for building tools designed to be used with language models. With Arcade, developers can create, deploy, and easily integrate new tools with language models to enhance their capabilities.
|
|
51
|
+
|
|
52
|
+
## Setup
|
|
53
|
+
|
|
54
|
+
Follow [these instructions](https://docs-112.pages.dev/home/quickstart) to Install Arcade AI and create an API key.
|
|
55
|
+
|
|
56
|
+
This example is using OpenAI, as the LLM provider. Ensure you have an [OpenAI API key](https://platform.openai.com/docs/quickstart).
|
|
57
|
+
|
|
58
|
+
Copy the `.env.example` file to `.env` and supply your API keys for `OPENAI_API_KEY` and `ARCADE_API_KEY`.
|
|
59
|
+
|
|
60
|
+
## Usage with LangGraph API
|
|
61
|
+
|
|
62
|
+
### Local testing with LangGraph Studio
|
|
63
|
+
|
|
64
|
+
For testing locally (e.g., currently supported only on MacOS), you can use the LangGraph Studio desktop application.
|
|
65
|
+
|
|
66
|
+
[Download LangGraph Studio](https://github.com/langchain-ai/langgraph-studio?tab=readme-ov-file#download) and open this directory in the Studio application.
|
|
67
|
+
|
|
68
|
+
The `langgraph.json` file in this directory specifies the graph that will be loaded in Studio.
|
|
69
|
+
|
|
70
|
+
### Deploying to LangGraph Cloud
|
|
71
|
+
|
|
72
|
+
Follow [these instructions](https://langchain-ai.github.io/langgraph/cloud/quick_start/#deploy-to-cloud) to deploy your graph to LangGraph Cloud.
|
|
73
|
+
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<img width="400px" src="https://docs-112.pages.dev/images/logo/arcade-ai-logo.png" />
|
|
4
|
+
|
|
5
|
+
### LLM Tool Calling Platform
|
|
6
|
+
|
|
7
|
+
<p>
|
|
8
|
+
<img alt="GitHub Last Commit" src="https://img.shields.io/github/last-commit/arcadeai/arcade-ai" />
|
|
9
|
+
<img alt="GitHub Issues" src="https://img.shields.io/github/issues/arcadeai/arcade-ai" />
|
|
10
|
+
<img alt="GitHub Pull Requests" src="https://img.shields.io/github/issues-pr/arcadeai/arcade-ai" />
|
|
11
|
+
<img alt="GitHub License" src="https://img.shields.io/badge/License-MIT-yellow.svg" />
|
|
12
|
+
<img alt="Discord" src="https://img.shields.io/discord/1110910277110743103?label=Discord&logo=discord&logoColor=white&style=plastic&color=d7b023)](https://discord.gg/" />
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
<p align="center">
|
|
18
|
+
<a href="https://docs-112.pages.dev" target="_blank">Docs</a> •
|
|
19
|
+
<a href="https://docs-112.pages.dev/guides" target="_blank">Guides</a> •
|
|
20
|
+
<a href="https://docs-112.pages.dev/integrations" target="_blank">Integrations</a> •
|
|
21
|
+
<a href="https://discord.com/invite/" target="_blank">Discord</a>
|
|
22
|
+
|
|
23
|
+
</p>
|
|
24
|
+
|
|
25
|
+
<br />
|
|
26
|
+
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
Arcade AI is the developer platform for building tools designed to be used with language models. With Arcade, developers can create, deploy, and easily integrate new tools with language models to enhance their capabilities.
|
|
30
|
+
|
|
31
|
+
## Setup
|
|
32
|
+
|
|
33
|
+
Follow [these instructions](https://docs-112.pages.dev/home/quickstart) to Install Arcade AI and create an API key.
|
|
34
|
+
|
|
35
|
+
This example is using OpenAI, as the LLM provider. Ensure you have an [OpenAI API key](https://platform.openai.com/docs/quickstart).
|
|
36
|
+
|
|
37
|
+
Copy the `.env.example` file to `.env` and supply your API keys for `OPENAI_API_KEY` and `ARCADE_API_KEY`.
|
|
38
|
+
|
|
39
|
+
## Usage with LangGraph API
|
|
40
|
+
|
|
41
|
+
### Local testing with LangGraph Studio
|
|
42
|
+
|
|
43
|
+
For testing locally (e.g., currently supported only on MacOS), you can use the LangGraph Studio desktop application.
|
|
44
|
+
|
|
45
|
+
[Download LangGraph Studio](https://github.com/langchain-ai/langgraph-studio?tab=readme-ov-file#download) and open this directory in the Studio application.
|
|
46
|
+
|
|
47
|
+
The `langgraph.json` file in this directory specifies the graph that will be loaded in Studio.
|
|
48
|
+
|
|
49
|
+
### Deploying to LangGraph Cloud
|
|
50
|
+
|
|
51
|
+
Follow [these instructions](https://langchain-ai.github.io/langgraph/cloud/quick_start/#deploy-to-cloud) to deploy your graph to LangGraph Cloud.
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
from typing import Any, Callable
|
|
2
|
+
|
|
3
|
+
from arcadepy import NOT_GIVEN, Arcade
|
|
4
|
+
from arcadepy.types.shared import ToolDefinition
|
|
5
|
+
from langchain_core.runnables import RunnableConfig
|
|
6
|
+
from langchain_core.tools import StructuredTool
|
|
7
|
+
from pydantic import BaseModel, Field, create_model
|
|
8
|
+
|
|
9
|
+
# Check if LangGraph is enabled
|
|
10
|
+
LANGGRAPH_ENABLED = True
|
|
11
|
+
try:
|
|
12
|
+
from langgraph.errors import NodeInterrupt
|
|
13
|
+
except ImportError:
|
|
14
|
+
LANGGRAPH_ENABLED = False
|
|
15
|
+
|
|
16
|
+
# Mapping of Arcade value types to Python types
|
|
17
|
+
TYPE_MAPPING = {
|
|
18
|
+
"string": str,
|
|
19
|
+
"number": float,
|
|
20
|
+
"integer": int,
|
|
21
|
+
"boolean": bool,
|
|
22
|
+
"array": list,
|
|
23
|
+
"json": dict,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def get_python_type(val_type: str) -> Any:
|
|
28
|
+
"""Map Arcade value types to Python types.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
val_type: The value type as a string.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
Corresponding Python type.
|
|
35
|
+
"""
|
|
36
|
+
_type = TYPE_MAPPING.get(val_type)
|
|
37
|
+
if _type is None:
|
|
38
|
+
raise ValueError(f"Invalid value type: {val_type}")
|
|
39
|
+
return _type
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def tool_definition_to_pydantic_model(tool_def: ToolDefinition) -> type[BaseModel]:
|
|
43
|
+
"""Convert a ToolDefinition's inputs into a Pydantic BaseModel.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
tool_def: The ToolDefinition object to convert.
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
A Pydantic BaseModel class representing the tool's input schema.
|
|
50
|
+
"""
|
|
51
|
+
try:
|
|
52
|
+
fields: dict[str, Any] = {}
|
|
53
|
+
for param in tool_def.inputs.parameters or []:
|
|
54
|
+
param_type = get_python_type(param.value_schema.val_type)
|
|
55
|
+
if param_type == list and param.value_schema.inner_val_type: # noqa: E721
|
|
56
|
+
inner_type: type[Any] = get_python_type(param.value_schema.inner_val_type)
|
|
57
|
+
param_type = list[inner_type] # type: ignore[valid-type]
|
|
58
|
+
param_description = param.description or "No description provided."
|
|
59
|
+
default = ... if param.required else None
|
|
60
|
+
fields[param.name] = (
|
|
61
|
+
param_type,
|
|
62
|
+
Field(default=default, description=param_description),
|
|
63
|
+
)
|
|
64
|
+
return create_model(f"{tool_def.name}Args", **fields)
|
|
65
|
+
except ValueError as e:
|
|
66
|
+
raise ValueError(
|
|
67
|
+
f"Error converting {tool_def.name} parameters into pydantic model for langchain: {e}"
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def create_tool_function(
|
|
72
|
+
client: Arcade,
|
|
73
|
+
tool_name: str,
|
|
74
|
+
tool_def: ToolDefinition,
|
|
75
|
+
args_schema: type[BaseModel],
|
|
76
|
+
langgraph: bool = False,
|
|
77
|
+
) -> Callable:
|
|
78
|
+
"""Create a callable function to execute an Arcade tool.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
client: The Arcade client instance.
|
|
82
|
+
tool_name: The name of the tool to wrap.
|
|
83
|
+
tool_def: The ToolDefinition of the tool to wrap.
|
|
84
|
+
args_schema: The Pydantic model representing the tool's arguments.
|
|
85
|
+
langgraph: Whether to enable LangGraph-specific behavior.
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
A callable function that executes the tool.
|
|
89
|
+
"""
|
|
90
|
+
if langgraph and not LANGGRAPH_ENABLED:
|
|
91
|
+
raise ImportError("LangGraph is not installed. Please install it to use this feature.")
|
|
92
|
+
|
|
93
|
+
requires_authorization = (
|
|
94
|
+
tool_def.requirements is not None and tool_def.requirements.authorization is not None
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
def tool_function(config: RunnableConfig, **kwargs: Any) -> Any:
|
|
98
|
+
"""Execute the Arcade tool with the given parameters.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
config: RunnableConfig containing execution context.
|
|
102
|
+
**kwargs: Tool input arguments.
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
The output from the tool execution.
|
|
106
|
+
"""
|
|
107
|
+
user_id = config.get("configurable", {}).get("user_id") if config else None
|
|
108
|
+
|
|
109
|
+
if requires_authorization:
|
|
110
|
+
if user_id is None:
|
|
111
|
+
error_message = f"user_id is required to run {tool_name}"
|
|
112
|
+
if langgraph:
|
|
113
|
+
raise NodeInterrupt(error_message)
|
|
114
|
+
return {"error": error_message}
|
|
115
|
+
|
|
116
|
+
# Authorize the user for the tool
|
|
117
|
+
auth_response = client.tools.authorize(tool_name=tool_name, user_id=user_id)
|
|
118
|
+
if auth_response.status != "completed":
|
|
119
|
+
auth_message = (
|
|
120
|
+
"Please use the following link to authorize: "
|
|
121
|
+
f"{auth_response.authorization_url}"
|
|
122
|
+
)
|
|
123
|
+
if langgraph:
|
|
124
|
+
raise NodeInterrupt(auth_message)
|
|
125
|
+
return {"error": auth_message}
|
|
126
|
+
|
|
127
|
+
# Execute the tool with provided inputs
|
|
128
|
+
execute_response = client.tools.execute(
|
|
129
|
+
tool_name=tool_name,
|
|
130
|
+
inputs=kwargs,
|
|
131
|
+
user_id=user_id if user_id is not None else NOT_GIVEN,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
if execute_response.success:
|
|
135
|
+
return execute_response.output.value # type: ignore[union-attr]
|
|
136
|
+
error_message = str(execute_response.output.error) # type: ignore[union-attr]
|
|
137
|
+
if langgraph:
|
|
138
|
+
raise NodeInterrupt(error_message)
|
|
139
|
+
return {"error": error_message}
|
|
140
|
+
|
|
141
|
+
return tool_function
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def wrap_arcade_tool(
|
|
145
|
+
client: Arcade,
|
|
146
|
+
tool_name: str,
|
|
147
|
+
tool_def: ToolDefinition,
|
|
148
|
+
langgraph: bool = False,
|
|
149
|
+
) -> StructuredTool:
|
|
150
|
+
"""Wrap an Arcade `ToolDefinition` as a LangChain `StructuredTool`.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
client: The Arcade client instance.
|
|
154
|
+
tool_name: The name of the tool to wrap.
|
|
155
|
+
tool_def: The ToolDefinition object to wrap.
|
|
156
|
+
langgraph: Whether to enable LangGraph-specific behavior.
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
A StructuredTool instance representing the Arcade tool.
|
|
160
|
+
"""
|
|
161
|
+
description = tool_def.description or "No description provided."
|
|
162
|
+
|
|
163
|
+
# Create a Pydantic model for the tool's input arguments
|
|
164
|
+
args_schema = tool_definition_to_pydantic_model(tool_def)
|
|
165
|
+
|
|
166
|
+
# Create the action function
|
|
167
|
+
action_func = create_tool_function(
|
|
168
|
+
client=client,
|
|
169
|
+
tool_name=tool_name,
|
|
170
|
+
tool_def=tool_def,
|
|
171
|
+
args_schema=args_schema,
|
|
172
|
+
langgraph=langgraph,
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# Create the StructuredTool instance
|
|
176
|
+
return StructuredTool.from_function(
|
|
177
|
+
func=action_func,
|
|
178
|
+
name=tool_name,
|
|
179
|
+
description=description,
|
|
180
|
+
args_schema=args_schema,
|
|
181
|
+
inject_kwargs={"user_id"},
|
|
182
|
+
)
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from collections.abc import Iterator
|
|
3
|
+
from typing import Any, Optional
|
|
4
|
+
|
|
5
|
+
from arcadepy import Arcade
|
|
6
|
+
from arcadepy.types.shared import AuthorizationResponse, ToolDefinition
|
|
7
|
+
from langchain_core.tools import StructuredTool
|
|
8
|
+
|
|
9
|
+
from langchain_arcade._utilities import (
|
|
10
|
+
wrap_arcade_tool,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ArcadeToolManager:
|
|
15
|
+
"""
|
|
16
|
+
Arcade tool manager for LangChain framework.
|
|
17
|
+
|
|
18
|
+
This class wraps Arcade tools as LangChain `StructuredTool`
|
|
19
|
+
objects for integration.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
client: Optional[Arcade] = None,
|
|
25
|
+
**kwargs: dict[str, Any],
|
|
26
|
+
) -> None:
|
|
27
|
+
"""Initialize the ArcadeToolManager.
|
|
28
|
+
|
|
29
|
+
Example:
|
|
30
|
+
>>> manager = ArcadeToolManager(api_key="...")
|
|
31
|
+
>>>
|
|
32
|
+
>>> # retrieve a specific tool as a langchain tool
|
|
33
|
+
>>> manager.get_tools(tools=["Search.SearchGoogle"])
|
|
34
|
+
>>>
|
|
35
|
+
>>> # retrieve all tools in a toolkit as langchain tools
|
|
36
|
+
>>> manager.get_tools(toolkits=["Search"])
|
|
37
|
+
>>>
|
|
38
|
+
>>> # clear and initialize new tools in the manager
|
|
39
|
+
>>> manager.init_tools(tools=["Search.SearchGoogle"], toolkits=["Search"])
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
client: Optional Arcade client instance.
|
|
43
|
+
"""
|
|
44
|
+
if not client:
|
|
45
|
+
api_key = kwargs.get("api_key", os.getenv("ARCADE_API_KEY", None))
|
|
46
|
+
client = Arcade(api_key=api_key) # type: ignore[arg-type]
|
|
47
|
+
self.client = client
|
|
48
|
+
self._tools: dict[str, ToolDefinition] = {}
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def tools(self) -> list[str]:
|
|
52
|
+
return list(self._tools.keys())
|
|
53
|
+
|
|
54
|
+
def __iter__(self) -> Iterator[tuple[str, ToolDefinition]]:
|
|
55
|
+
yield from self._tools.items()
|
|
56
|
+
|
|
57
|
+
def __len__(self) -> int:
|
|
58
|
+
return len(self._tools)
|
|
59
|
+
|
|
60
|
+
def __getitem__(self, tool_name: str) -> ToolDefinition:
|
|
61
|
+
return self._tools[tool_name]
|
|
62
|
+
|
|
63
|
+
def init_tools(
|
|
64
|
+
self,
|
|
65
|
+
tools: Optional[list[str]] = None,
|
|
66
|
+
toolkits: Optional[list[str]] = None,
|
|
67
|
+
) -> None:
|
|
68
|
+
"""Initialize the tools in the manager.
|
|
69
|
+
|
|
70
|
+
This will clear any existing tools in the manager.
|
|
71
|
+
|
|
72
|
+
Example:
|
|
73
|
+
>>> manager = ArcadeToolManager(api_key="...")
|
|
74
|
+
>>> manager.init_tools(tools=["Search.SearchGoogle"])
|
|
75
|
+
>>> manager.get_tools()
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
tools: Optional list of tool names to include.
|
|
79
|
+
toolkits: Optional list of toolkits to include.
|
|
80
|
+
"""
|
|
81
|
+
self._tools = self._retrieve_tool_definitions(tools, toolkits)
|
|
82
|
+
|
|
83
|
+
def get_tools(
|
|
84
|
+
self,
|
|
85
|
+
tools: Optional[list[str]] = None,
|
|
86
|
+
toolkits: Optional[list[str]] = None,
|
|
87
|
+
langgraph: bool = False,
|
|
88
|
+
) -> list[StructuredTool]:
|
|
89
|
+
"""Return the tools in the manager as LangChain StructuredTool objects.
|
|
90
|
+
|
|
91
|
+
Note: if tools/toolkits are provided, the manager will update it's
|
|
92
|
+
internal tools using a dictionary update by tool name.
|
|
93
|
+
|
|
94
|
+
Example:
|
|
95
|
+
>>> manager = ArcadeToolManager(api_key="...")
|
|
96
|
+
>>>
|
|
97
|
+
>>> # retrieve a specific tool as a langchain tool
|
|
98
|
+
>>> manager.get_tools(tools=["Search.SearchGoogle"])
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
tools: Optional list of tool names to include.
|
|
102
|
+
toolkits: Optional list of toolkits to include.
|
|
103
|
+
langgraph: Whether to use LangGraph-specific behavior
|
|
104
|
+
such as NodeInterrupts for auth.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
List of StructuredTool instances.
|
|
108
|
+
"""
|
|
109
|
+
# TODO account for versioning
|
|
110
|
+
if tools or toolkits:
|
|
111
|
+
new_tools = self._retrieve_tool_definitions(tools, toolkits)
|
|
112
|
+
self._tools.update(new_tools)
|
|
113
|
+
|
|
114
|
+
langchain_tools: list[StructuredTool] = []
|
|
115
|
+
for tool_name, definition in self:
|
|
116
|
+
lc_tool = wrap_arcade_tool(self.client, tool_name, definition, langgraph)
|
|
117
|
+
langchain_tools.append(lc_tool)
|
|
118
|
+
return langchain_tools
|
|
119
|
+
|
|
120
|
+
def authorize(self, tool_name: str, user_id: str) -> AuthorizationResponse:
|
|
121
|
+
"""Authorize a user for a tool.
|
|
122
|
+
|
|
123
|
+
Example:
|
|
124
|
+
>>> manager = ArcadeToolManager(api_key="...")
|
|
125
|
+
>>> manager.authorize("X.PostTweet", "user_123")
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
tool_name: The name of the tool to authorize.
|
|
129
|
+
user_id: The user ID to authorize.
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
AuthorizationResponse
|
|
133
|
+
"""
|
|
134
|
+
return self.client.tools.authorize(tool_name=tool_name, user_id=user_id)
|
|
135
|
+
|
|
136
|
+
def is_authorized(self, authorization_id: str) -> bool:
|
|
137
|
+
"""Check if a tool authorization is complete.
|
|
138
|
+
|
|
139
|
+
Example:
|
|
140
|
+
>>> manager = ArcadeToolManager(api_key="...")
|
|
141
|
+
>>> manager.init_tools(tools=["Searc"])
|
|
142
|
+
>>> manager.is_authorized("auth_123")
|
|
143
|
+
"""
|
|
144
|
+
return self.client.auth.status(authorization_id=authorization_id).status == "completed"
|
|
145
|
+
|
|
146
|
+
def requires_auth(self, tool_name: str) -> bool:
|
|
147
|
+
"""Check if a tool requires authorization."""
|
|
148
|
+
|
|
149
|
+
tool_def = self._get_tool_definition(tool_name)
|
|
150
|
+
if tool_def.requirements is None:
|
|
151
|
+
return False
|
|
152
|
+
return tool_def.requirements.authorization is not None
|
|
153
|
+
|
|
154
|
+
def _get_tool_definition(self, tool_name: str) -> ToolDefinition:
|
|
155
|
+
try:
|
|
156
|
+
return self._tools[tool_name]
|
|
157
|
+
except KeyError:
|
|
158
|
+
raise ValueError(f"Tool '{tool_name}' not found in this ArcadeToolManager instance")
|
|
159
|
+
|
|
160
|
+
def _retrieve_tool_definitions(
|
|
161
|
+
self, tools: Optional[list[str]] = None, toolkits: Optional[list[str]] = None
|
|
162
|
+
) -> dict[str, ToolDefinition]:
|
|
163
|
+
all_tools: list[ToolDefinition] = []
|
|
164
|
+
if tools or toolkits:
|
|
165
|
+
if tools:
|
|
166
|
+
single_tools = [self.client.tools.get(tool_id=tool_id) for tool_id in tools]
|
|
167
|
+
all_tools.extend(single_tools)
|
|
168
|
+
if toolkits:
|
|
169
|
+
for tk in toolkits:
|
|
170
|
+
all_tools.extend(self.client.tools.list(toolkit=tk))
|
|
171
|
+
else:
|
|
172
|
+
# retrieve all tools
|
|
173
|
+
all_tools = self.client.tools.list().items
|
|
174
|
+
|
|
175
|
+
tool_definitions: dict[str, ToolDefinition] = {}
|
|
176
|
+
|
|
177
|
+
for tool in all_tools:
|
|
178
|
+
full_tool_name = f"{tool.toolkit.name}_{tool.name}"
|
|
179
|
+
tool_definitions[full_tool_name] = tool
|
|
180
|
+
|
|
181
|
+
return tool_definitions
|
|
File without changes
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "langchain-arcade"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "An integration package connecting Arcade AI and LangChain/LangGraph"
|
|
5
|
+
authors = ["Arcade AI <dev@arcade-ai.com>"]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
repository = "https://github.com/arcadeai/arcade-ai/tree/main/contrib/langchain"
|
|
8
|
+
license = "MIT"
|
|
9
|
+
|
|
10
|
+
[tool.poetry.dependencies]
|
|
11
|
+
python = ">=3.10,<3.13"
|
|
12
|
+
langchain-core = "^0.3.0"
|
|
13
|
+
arcadepy = "~0.1.1"
|
|
14
|
+
langgraph = {version = ">=0.2.32,<0.3.0", optional = true}
|
|
15
|
+
|
|
16
|
+
[tool.poetry.extras]
|
|
17
|
+
langgraph = ["langgraph"]
|
|
18
|
+
|
|
19
|
+
[tool.poetry.group.dev.dependencies]
|
|
20
|
+
pytest = "^8.1.2"
|
|
21
|
+
pytest-cov = "^4.0.0"
|
|
22
|
+
mypy = "^1.5.1"
|
|
23
|
+
pre-commit = "^3.4.0"
|
|
24
|
+
tox = "^4.11.1"
|
|
25
|
+
pytest-asyncio = "^0.23.7"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
[tool.mypy]
|
|
29
|
+
files = ["langchain_arcade"]
|
|
30
|
+
python_version = "3.10"
|
|
31
|
+
disallow_untyped_defs = "True"
|
|
32
|
+
disallow_any_unimported = "True"
|
|
33
|
+
no_implicit_optional = "True"
|
|
34
|
+
check_untyped_defs = "True"
|
|
35
|
+
warn_return_any = "True"
|
|
36
|
+
warn_unused_ignores = "True"
|
|
37
|
+
show_error_codes = "True"
|
|
38
|
+
ignore_missing_imports = "True"
|
|
39
|
+
|
|
40
|
+
[tool.pytest.ini_options]
|
|
41
|
+
testpaths = ["tests"]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
[tool.coverage.run]
|
|
45
|
+
branch = true
|
|
46
|
+
source = ["langchain_arcade"]
|
|
47
|
+
|
|
48
|
+
[tool.coverage.report]
|
|
49
|
+
skip_empty = true
|