-
Notifications
You must be signed in to change notification settings - Fork 15.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
make sure that @tool decorator can be applied to a method (regression?) #27471
Comments
Hey @eyurtsev! I'm part of a group of UofT developers interested in this issue. Can we tackle it? If so, what specific requirements and output are you looking for? |
Yes, you're welcome to try to tackle it. What kind of timeline do you have in mind? We have a set of unit tests for tools: https://github.com/langchain-ai/langchain/blob/master/libs/core/tests/unit_tests/test_tools.py We want to make sure that the @tool decorator has the correct behavior whether it's a standalone function or a method. @tool
def foo(x: int, y: float) -> str:
"""bar""" @tool
async def foo(x: int, y: float) -> str:
"""bar""" class A:
@tool
def foo(self, x: int, y: float) -> str:
"""bar""" class A:
@tool
async def foo(self, x: int, y: float) -> str:
"""bar""" All behave the same.
|
Thank you for the succinct requirements, that will really help us get started. Our timeline to get this finished is around 2 or 3 weeks from now, though the sooner the better. Does that work for you? |
Could you check in before getting started. This is in |
Hey @eyurtsev, the team and I are good to go! We are planning to finish the implementation and testing by November 8th and submit a PR. |
Sounds good. If you need feedback earlier than that you can send a PR in draft mode. |
Hey @eyurtsev, Just wanted to follow up with the issue progress. We are finding difficulties in modifying the @tool decorator as described above in a way that allows for the same syntax you specified. The main issue is the self parameter of a method and the lack of reference to the class/instance itself. Our original idea was to have some sort of wrapper for invoke that captures the value of self and will pass it in for you automatically. So .invoke({'arg': val}) turns into .invoke({'self': self, 'arg': val}). However, when decorating a method, you are actually just decorating a function that has a self parameter. This computation takes place before object initialization, and so there is no self object to capture. That is why, as far as we have found, this: class A:
@tool
def foo(self, x: int):
"""bar"""
a = A()
a.foo.invoke({'x': 5}) Is not able to be done. The tool decorator is not able to get any self value. We have an alternative idea that we would be happy to implement which solves half of the original issue. This idea is to implement the invoke wrapper as described above, then users would be able to use it like this: class A:
def __init__(self):
self.foo = tool(self.foo, selfRef=self) # tool decorator is modified to capture self value
def foo(self, x: int):
"""bar"""
a = A()
a.foo.invoke({'x': 5})
print(a.foo.args) # { 'x': int } ...
# Inside of a.foo.invoke:
internal_tool.invoke({'self': self, 'x': 5}) By moving the computation into the init, the self value is able to be captured and such a wrapper can be properly implemented. What do you think? Are we overlooking something here, and the original issue is able to be solved using the syntax you asked for? If not, do you think it is worth implementing this? |
Hey @eyurtsev, just wanted to follow up on the above comment. |
Would this example help? This should be achievable!
|
@efriis Thank you for your comment. We have thought about using a wrapper in this way, but unfortunately it still won't work out in the way that Eugene originally asked for it to work. The reason this is the case is because the Here is an example of an idea that we had that was similar to what you suggested, but would not match the syntax that was originally asked for: def my_decorator(func):
def wrapper(self):
return tool_with_self_reference(self, func) # the magic happens in here
return wrapper
class A:
def __init__(self, c):
self.c = c
@my_decorator
def add(self, a, b):
return a + b
a = A(10)
print(a.add().invoke({'a': 1, 'b': 2})) # 13 In this case, the The solution we proposed in a previous comment moved that The question is whether or not this should be implemented, or if it is not worth it since it does not technically fix everything originally proposed. |
something like this? python is very flexible, so I feel pretty confident it is possible.
|
Wow @efriis I was not aware of this magic? I think this is the missing piece we needed. I'm going to try to see if we can throw something together with this. Thank you! |
Hello @efriis I have created a draft PR with the implementation for this. Thank you very much for your help. |
Hey, any progress on this issue? I've been looking around for a way to do this but seems it's still not possible. I'm curious why this is not a more common desire for tools. |
Hello @dskarbrevik, yes there is progress on this issue. There is a PR I’ve made implementing this feature which is currently in the review process. |
Amazing, thanks for your work to make it happen! Hope it gets merged soon. FWIW I tested your fork for my use case and it works :) |
Privileged issue
Issue Content
The text was updated successfully, but these errors were encountered: