Closed usersina closed 1 year ago
🤖
Hi @usersina, good to see you again!
The error you're encountering is due to the way you're trying to initialize your custom tool. In LangChain, the BaseTool
class uses Pydantic for data validation and parsing. When you try to assign a value to the config
attribute in your __init__
method, Pydantic attempts to access the __fields_set__
attribute, which is not present because your FakeSearchTool
class is not a Pydantic model.
To resolve this issue, you can make your config
attribute a Pydantic model. Here's an example of how you can do this:
from pydantic import BaseModel
class Config(BaseModel):
init_arg1: str
init_arg2: str
class FakeSearchTool(BaseTool):
name: str | None = "custom_search"
description: str | None = "useful for when you need to answer questions about current events"
config: Config
def __init__(self, config: Config):
self.config = config
def _run(
self, query: str
) -> str:
"""Use the tool."""
print("Query:", query)
print("Obtained config:", self.config.init_arg1, self.config.init_arg2)
return "Used the tool, and got the answer using the config!"
async def _arun(
self, query: str
) -> str:
"""Use the tool asynchronously."""
raise NotImplementedError("custom_search does not support async")
# Test if the tool is working
search_tool = FakeSearchTool(
config=Config(init_arg1="arg1", init_arg2="arg2")
)
print(search_tool("What is my answer?"))
This should resolve the AttributeError
you're seeing. If you encounter any other issues, feel free to ask. Happy coding!
This response is meant to be useful and save you time. It is not meant to be a precise solution, but rather a starting point for your own research.
Help me be more useful! Please leave a 👍 if this is helpful and 👎 if it is irrelevant.
If you want to continue the conversation, start your reply with @dosu-bot.
@usersina which version of pydantic are you using? If you're using v2 follow guidelines here: https://python.langchain.com/docs/guides/pydantic_compatibility
I updated my description to include the installed pydantic
version and the created Config
class (which I previously forgot) hence completing the minimal reproducible example.
@eyurtsev according to the link you sent langchain>=0.0.267
internally uses pydantic v1. I don't use pydantic imports anywhere in the example above so I don't have a version problem if I understand correctly.
That said, I tried the @dosubot's suggestion with a slight tweaking, by importing from pydantic.v1
instead, but I still see the same error:
from langchain.tools.base import BaseTool
from pydantic.v1 import BaseModel
# Create the config class
class Config(BaseModel):
init_arg1: str = "default value"
init_arg1: str = "default value 2"
def __init__(self, init_arg1: str, init_arg2: str):
self.init_arg1 = init_arg1
self.init_arg2 = init_arg2
# Create the tool
class FakeSearchTool(BaseTool):
name: str | None = "custom_search"
description: str | None = "useful for when you need to answer questions about current events"
config: Config
def __init__(self, config: Config):
self.config = config
def _run(
self, query: str
) -> str:
"""Use the tool."""
print("Query:", query)
print("Obtained config:", self.config.init_arg1, self.config.init_arg2)
return "Used the tool, and got the answer using the config!"
async def _arun(
self, query: str
) -> str:
"""Use the tool asynchronously."""
raise NotImplementedError("custom_search does not support async")
# Test the tool
search_tool = FakeSearchTool(
config=Config(init_arg1="arg1", init_arg2="arg2")
)
print(search_tool("What is my answer?"))
AttributeError: 'Config' object has no attribute '__fields_set__'
🤖
The error you're encountering is likely due to the way you're initializing your Config
class. In Pydantic, the __init__
method is not typically overridden. Instead, Pydantic uses the class attributes to automatically generate an __init__
method. When you override the __init__
method, Pydantic's auto-generated __init__
method is not called, which is likely why you're encountering the AttributeError: 'Config' object has no attribute '__fields_set__'
.
To resolve this issue, you should remove the __init__
method from your Config
class and let Pydantic handle the initialization. Here's how you can modify your Config
class:
from pydantic import BaseModel
class Config(BaseModel):
init_arg1: str = "default value"
init_arg2: str = "default value 2"
With this modification, you can initialize a Config
object like so:
config = Config(init_arg1="arg1", init_arg2="arg2")
Pydantic will automatically use the class attributes to generate an __init__
method that accepts the specified arguments.
Also, I noticed a typo in your Config
class. You have init_arg1
defined twice. I assume the second one should be init_arg2
.
I hope this helps! Let me know if you have any other questions.
This response is meant to be useful and save you time. It is not meant to be a precise solution, but rather a starting point for your own research.
Help me be more useful! Please leave a 👍 if this is helpful and 👎 if it is irrelevant.
If you want to continue the conversation, start your reply with @dosu-bot.
Oh, good bot! That actually pointed me to the right answer.
The config can be as is though, I leave it as follows:
class Config:
def __init__(self, init_arg1: str, init_arg2: str):
self.init_arg1 = init_arg1
self.init_arg2 = init_arg2
But then I don't create any __init__
function for my custom tool (the crux of the matter)
class FakeSearchTool(BaseTool):
name: str | None = "custom_search"
description: str | None = "useful for when you need to answer questions about current events"
config: Config
def _run(
self, query: str
) -> str:
"""Use the tool."""
print("Query:", query)
print("Obtained config:", self.config.init_arg1, self.config.init_arg2)
return "Used the tool, and got the answer using the config!"
async def _arun(
self, query: str
) -> str:
"""Use the tool asynchronously."""
raise NotImplementedError("custom_search does not support async")
And then this finally works!
search_tool = FakeSearchTool(
config=Config(init_arg1="arg1", init_arg2="arg2")
)
print(search_tool("What is my answer?"))
Query: What is my answer? Obtained config: arg1 arg2 Used the tool, and got the answer using the config!
You can pass any custom variable to the tool with metadata
argument.
class BaseTool(RunnableSerializable[Union[str, Dict], Any]):
metadata: Optional[Dict[str, Any]] = None
"""Optional metadata associated with the tool. Defaults to None
This metadata will be associated with each call to this tool,
and passed as arguments to the handlers defined in `callbacks`.
You can use these to eg identify a specific instance of a tool with its use case.
"""
class CustomTool(BaseTool)
name = "jamesallen-search"
description = "Use this tool as the primary source of context information. Always search for the answers using this tool first, don't make up answers yourself"
return_direct = True
args_schema: Type[BaseModel] = JamesAllenRetrivalSearchInput
def _run(self, question: str, run_manager: Optional[CallbackManagerForToolRun] = None) -> str:
# External variable return when tool runs
return self.metadata['custom_variable']
async def _arun(self, question: str, run_manager: Optional[AsyncCallbackManagerForToolRun] = None) -> str:
"""Use the tool asynchronously."""
raise NotImplementedError("Not implemented")
class Agent:
def __init__(self):
llm = ChatOpenAI(
model_name='gpt-3.5-turbo-1106',
temperature=0.1,
)
memory = ConversationBufferMemory(output_key='output')
self.custom_variable = True
tools = [
CustomTool(metadata={'custom_variable': self.custom_variable})
]
self.agent = initialize_agent(
tools=tools,
llm=llm,
agent="conversational-react-description",
verbose=True,
memory=memory,
return_intermediate_steps=True,
)
System Info
python=
3.11
langchain=0.0.314
pydantic=2.3.0
pydantic_core=2.6.3
Who can help?
No response
Information
Related Components
Reproduction
From the documentation, I can create a fake tool that works as follows:
My case however requires me to give an initial configuration object to every tool, something like this:
However, this does not work and return the following error:
Stack Trace
``` --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) /ai-service/notebooks/agent-with-elasticsearch-tool.ipynb Cell 18 line 2 20 """Use the tool asynchronously.""" 21 raise NotImplementedError("custom_search does not support async") ---> 23 search_tool = FakeSearchTool( 24 config=Config(init_arg1="arg1", init_arg2="arg2") 25 ) 26 print(search_tool("What is my answer?")) /ai-service/notebooks/agent-with-elasticsearch-tool.ipynb Cell 18 line 7 6 def __init__(self, config: Config): ----> 7 self.config = config File ~/.pyenv/versions/3.11.5/envs/ai-service/lib/python3.11/site-packages/pydantic/v1/main.py:405, in BaseModel.__setattr__(self, name, value) 402 else: 403 self.__dict__[name] = value --> 405 self.__fields_set__.add(name) AttributeError: 'FakeSearchTool' object has no attribute '__fields_set__' ```Expected behavior
I expect to get the following output out of the tool: