Skip to content

Commit 8168a0a

Browse files
add support for langchain tools as functions
1 parent 63d5fdb commit 8168a0a

File tree

3 files changed

+73
-2
lines changed

3 files changed

+73
-2
lines changed

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,26 @@ A preset can be edited by using the `/preset-edit` command:
606606
Note the special `return_on_function_call` and `return_on_function_response` metadata attributes, which can be used to
607607
control the return value, useful when using the `ApiBackend`module, or via [workflows](#workflows)
608608

609+
#### Support for Langchain tools
610+
611+
[Langchain](https://docs.langchain.com) has many useful [tools](https://python.langchain.com/docs/modules/agents/tools/) that
612+
can be used in function calls.
613+
614+
To use a Langchain tool as function:
615+
616+
1. Find the name of the tool class, e.g. `MoveFileTool` or `ShellTool`.
617+
2. Prefix that class name with `Langchain-`
618+
3. Add it to the `functions` list for the preset:
619+
```yaml
620+
metadata:
621+
# Usual preset metadata.
622+
model_customizations:
623+
# Other attributes.
624+
model_kwargs:
625+
functions:
626+
- Langchain-ShellTool
627+
```
628+
609629

610630
### Flask API (experimental)
611631

lwe/backends/api/backend.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,12 @@ def init_function_cache(self):
164164
self.function_cache.append(function)
165165

166166
def function_cache_add(self, function_name):
167-
if function_name not in self.function_manager.functions:
168-
return False
167+
if self.function_manager.is_langchain_tool(function_name):
168+
if not self.function_manager.get_langchain_tool(function_name):
169+
return False
170+
else:
171+
if function_name not in self.function_manager.functions:
172+
return False
169173
if function_name not in self.function_cache:
170174
self.function_cache.append(function_name)
171175
return True

lwe/core/function_manager.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,14 @@
44

55
from pathlib import Path
66

7+
import langchain.tools
8+
79
from lwe.core.config import Config
810
from lwe.core.logger import Logger
911
import lwe.core.util as util
1012

13+
LANGCHAIN_TOOL_PREFIX = "Langchain-"
14+
1115
class FunctionManager():
1216
"""
1317
Manage functions.
@@ -58,6 +62,45 @@ def load_function(self, function_name):
5862
return True, function_filepath, message
5963
return False, None, f"Function {function_name} not found"
6064

65+
def is_langchain_tool(self, function_name):
66+
self.log.debug(f"Checking for Langchain tool: {function_name}")
67+
return function_name.lower().startswith(LANGCHAIN_TOOL_PREFIX.lower())
68+
69+
def get_langchain_tool(self, function_name):
70+
self.log.debug(f"Loading Langchain tool: {function_name}")
71+
tool_name = util.remove_prefix(function_name, LANGCHAIN_TOOL_PREFIX)
72+
tool = getattr(langchain.tools, tool_name)
73+
try:
74+
tool_instance = tool()
75+
return tool_instance
76+
except Exception as e:
77+
self.log.warning(f"Could not load Langchaine tool: {function_name}: {str(e)}")
78+
return None
79+
80+
def get_langchain_tool_spec(self, function_name):
81+
self.log.debug(f"Loading tool spec for Langchain tool: {function_name}")
82+
tool_instance = self.get_langchain_tool(function_name)
83+
if not tool_instance:
84+
raise RuntimeError(f"Langchain tool {function_name} not found")
85+
spec = langchain.tools.format_tool_to_openai_function(tool_instance)
86+
spec['name'] = function_name
87+
return spec
88+
89+
def run_langchain_tool(self, function_name, input_data):
90+
self.log.debug(f"Running langchaing tool: {function_name} with data: {input_data}")
91+
tool_instance = self.get_langchain_tool(function_name)
92+
if not tool_instance:
93+
raise RuntimeError(f"Langchain tool {function_name} not found")
94+
try:
95+
result = tool_instance.run(input_data)
96+
except Exception as e:
97+
message = f"Error: Exception occurred while running langchain tool {function_name}: {str(e)}"
98+
self.log.error(message)
99+
return False, None, message
100+
message = f"Langchain tool {function_name} executed successfully, output data: {result}"
101+
self.log.info(message)
102+
return True, result, message
103+
61104
def load_functions(self):
62105
self.log.debug("Loading functions from dirs: %s" % ", ".join(self.all_function_dirs))
63106
self.functions = {}
@@ -99,6 +142,8 @@ def setup_function_instance(self, function_name, function_path):
99142

100143
def get_function_config(self, function_name):
101144
self.log.debug(f"Getting config for function: {function_name}")
145+
if self.is_langchain_tool(function_name):
146+
return self.get_langchain_tool_spec(function_name)
102147
try:
103148
_success, function_path, user_message = self.load_function(function_name)
104149
function_instance = self.setup_function_instance(function_name, function_path)
@@ -111,6 +156,8 @@ def get_function_config(self, function_name):
111156
def run_function(self, function_name, input_data):
112157
if isinstance(input_data, str):
113158
input_data = json.loads(input_data)
159+
if self.is_langchain_tool(function_name):
160+
return self.run_langchain_tool(function_name, input_data)
114161
self.log.debug(f"Running function: {function_name} with data: {input_data}")
115162
success, function_path, user_message = self.load_function(function_name)
116163
if not success:

0 commit comments

Comments
 (0)