feat: fix integration with newest langchain version

This commit is contained in:
Ibis Prevedello 2023-03-04 11:22:19 -03:00
commit 3ec4257a63
5 changed files with 156 additions and 126 deletions

View file

@ -98,7 +98,15 @@ def list_memories():
def list_tools():
"""List all load tools"""
return [
util.get_tool_params(util.get_tools_dict(tool))["name"]
for tool in get_all_tool_names()
]
tools = []
for tool in get_all_tool_names():
if tool_params := util.get_tool_params(util.get_tools_dict(tool)):
tools.append(tool_params["name"])
return tools
# return [
# util.get_tool_params(util.get_tools_dict(tool))["name"]
# for tool in get_all_tool_names()
# ]

View file

@ -524,12 +524,12 @@ test = ["ipykernel", "pre-commit", "pytest", "pytest-cov", "pytest-timeout"]
[[package]]
name = "langchain"
version = "0.0.86"
version = "0.0.100"
description = "Building applications with LLMs through composability"
category = "main"
optional = false
python-versions = ">=3.8.1,<4.0"
develop = false
develop = true
[package.dependencies]
aiohttp = "^3.8.3"
@ -542,14 +542,12 @@ SQLAlchemy = "^1"
tenacity = "^8.1.0"
[package.extras]
all = ["anthropic (>=0.2.2,<0.3.0)", "beautifulsoup4 (>=4,<5)", "cohere (>=3,<4)", "elasticsearch (>=8,<9)", "faiss-cpu (>=1,<2)", "google-api-python-client (==2.70.0)", "google-search-results (>=2,<3)", "huggingface_hub (>=0,<1)", "jinja2 (>=3,<4)", "manifest-ml (>=0.0.1,<0.0.2)", "networkx (>=2.6.3,<3.0.0)", "nlpcloud (>=1,<2)", "nltk (>=3,<4)", "openai (>=0,<1)", "pinecone-client (>=2,<3)", "pypdf (>=3.4.0,<4.0.0)", "qdrant-client (>=0.11.7,<0.12.0)", "redis (>=4,<5)", "sentence-transformers (>=2,<3)", "spacy (>=3,<4)", "tensorflow-text (>=2.11.0,<3.0.0)", "tiktoken (>=0,<1)", "torch (>=1,<2)", "transformers (>=4,<5)", "weaviate-client (>=3,<4)", "wikipedia (>=1,<2)", "wolframalpha (==5.0.0)"]
all = ["anthropic (>=0.2.2,<0.3.0)", "beautifulsoup4 (>=4,<5)", "cohere (>=3,<4)", "elasticsearch (>=8,<9)", "faiss-cpu (>=1,<2)", "google-api-python-client (==2.70.0)", "google-search-results (>=2,<3)", "huggingface_hub (>=0,<1)", "jinja2 (>=3,<4)", "manifest-ml (>=0.0.1,<0.0.2)", "networkx (>=2.6.3,<3.0.0)", "nlpcloud (>=1,<2)", "nltk (>=3,<4)", "nomic (>=1.0.43,<2.0.0)", "openai (>=0,<1)", "opensearch-py (>=2.0.0,<3.0.0)", "pinecone-client (>=2,<3)", "pypdf (>=3.4.0,<4.0.0)", "qdrant-client (>=1.0.4,<2.0.0)", "redis (>=4,<5)", "sentence-transformers (>=2,<3)", "spacy (>=3,<4)", "tensorflow-text (>=2.11.0,<3.0.0)", "tiktoken (>=0,<1)", "torch (>=1,<2)", "transformers (>=4,<5)", "weaviate-client (>=3,<4)", "wikipedia (>=1,<2)", "wolframalpha (==5.0.0)"]
llms = ["anthropic (>=0.2.2,<0.3.0)", "cohere (>=3,<4)", "huggingface_hub (>=0,<1)", "manifest-ml (>=0.0.1,<0.0.2)", "nlpcloud (>=1,<2)", "openai (>=0,<1)", "torch (>=1,<2)", "transformers (>=4,<5)"]
[package.source]
type = "git"
url = "https://github.com/ibiscp/langchain.git"
reference = "ibis"
resolved_reference = "059e90090639e3c8c23928f1799e715b5635f681"
type = "directory"
url = "../../../langchain"
[[package]]
name = "marshmallow"
@ -1101,7 +1099,7 @@ multidict = ">=4.0"
[metadata]
lock-version = "1.1"
python-versions = "^3.10"
content-hash = "fb4b97be211d190c4feff42124fce6062b8717373d32b4894ae53f760742faaa"
content-hash = "37ef2fda1358fdb0b52d9584f0fc903a9581310cc271c5bbfb79b6085176bf2e"
[metadata.files]
aiohttp = [

View file

@ -16,7 +16,7 @@ uvicorn = "^0.20.0"
beautifulsoup4 = "^4.11.2"
google-search-results = "^2.4.1"
google-api-python-client = "^2.79.0"
langchain = {git = "https://github.com/ibiscp/langchain.git", rev = "ibis"}
langchain = {path = "../../../langchain", develop = true}
[tool.poetry.group.dev.dependencies]
black = "^23.1.0"

View file

@ -18,93 +18,13 @@ router = APIRouter(
)
def build_template_from_function(name: str, type_to_loader_dict: dict):
classes = [
item.__annotations__["return"].__name__ for item in type_to_loader_dict.values()
]
# Raise error if name is not in chains
if name not in classes:
raise ValueError(f"{name} not found")
for _type, v in type_to_loader_dict.items():
if v.__annotations__["return"].__name__ == name:
_class = v.__annotations__["return"]
docs = util.get_class_doc(_class)
variables = {"_type": _type}
for name, value in _class.__fields__.items():
if name in ["callback_manager", "requests_wrapper"]:
continue
variables[name] = {}
for name_, value_ in value.__repr_args__():
if name_ == "default_factory":
try:
variables[name]["default"] = util.get_default_factory(
module=_class.__base__.__module__, function=value_
)
except Exception:
variables[name]["default"] = None
elif name_ not in ["name"]:
variables[name][name_] = value_
variables[name]["placeholder"] = (
docs["Attributes"][name] if name in docs["Attributes"] else ""
)
return {
"template": util.format_dict(variables),
"description": docs["Description"],
"base_classes": util.get_base_classes(_class),
}
def build_template_from_class(name: str, type_to_cls_dict: dict):
classes = [item.__name__ for item in type_to_cls_dict.values()]
# Raise error if name is not in chains
if name not in classes:
raise ValueError(f"{name} not found.")
for _type, v in type_to_cls_dict.items():
if v.__name__ == name:
_class = v
docs = util.get_class_doc(_class)
variables = {"_type": _type}
for name, value in _class.__fields__.items():
if name in ["callback_manager"]:
continue
variables[name] = {}
for name_, value_ in value.__repr_args__():
if name_ == "default_factory":
try:
variables[name]["default"] = util.get_default_factory(
module=_class.__base__.__module__, function=value_
)
except Exception:
variables[name]["default"] = None
elif name_ not in ["name"]:
variables[name][name_] = value_
variables[name]["placeholder"] = (
docs["Attributes"][name] if name in docs["Attributes"] else ""
)
return {
"template": util.format_dict(variables),
"description": docs["Description"],
"base_classes": util.get_base_classes(_class),
}
@router.get("/chain")
def get_chain(name: str):
"""Get the signature of a chain."""
try:
return build_template_from_function(name, chains.loading.type_to_loader_dict)
return util.build_template_from_function(
name, chains.loading.type_to_loader_dict
)
except ValueError as exc:
raise HTTPException(status_code=404, detail="Chain not found") from exc
@ -113,7 +33,7 @@ def get_chain(name: str):
def get_agent(name: str):
"""Get the signature of an agent."""
try:
return build_template_from_class(name, agents.loading.AGENT_TO_CLASS)
return util.build_template_from_class(name, agents.loading.AGENT_TO_CLASS)
except ValueError as exc:
raise HTTPException(status_code=404, detail="Agent not found") from exc
@ -122,7 +42,9 @@ def get_agent(name: str):
def get_prompt(name: str):
"""Get the signature of a prompt."""
try:
return build_template_from_function(name, prompts.loading.type_to_loader_dict)
return util.build_template_from_function(
name, prompts.loading.type_to_loader_dict
)
except ValueError as exc:
raise HTTPException(status_code=404, detail="Prompt not found") from exc
@ -131,7 +53,7 @@ def get_prompt(name: str):
def get_llm(name: str):
"""Get the signature of an llm."""
try:
return build_template_from_class(name, llms.type_to_cls_dict)
return util.build_template_from_class(name, llms.type_to_cls_dict)
except ValueError as exc:
raise HTTPException(status_code=404, detail="LLM not found") from exc
@ -152,7 +74,7 @@ def get_llm(name: str):
def get_memory(name: str):
"""Get the signature of a memory."""
try:
return build_template_from_class(name, memories.type_to_cls_dict)
return util.build_template_from_class(name, memories.type_to_cls_dict)
except ValueError as exc:
raise HTTPException(status_code=404, detail="Memory not found") from exc
@ -173,10 +95,10 @@ def get_memory(name: str):
def get_tool(name: str):
"""Get the signature of a tool."""
all_tools = {
util.get_tool_params(util.get_tools_dict(tool))["name"]: tool
for tool in get_all_tool_names()
}
all_tools = {}
for tool in get_all_tool_names():
if tool_params := util.get_tool_params(util.get_tools_dict(tool)):
all_tools[tool_params["name"]] = tool
# Raise error if name is not in tools
if name not in all_tools.keys():
@ -185,7 +107,7 @@ def get_tool(name: str):
type_dict = {
"str": {
"type": "str",
"required": True,
"required": False,
"list": False,
"show": True,
"placeholder": "",
@ -218,9 +140,3 @@ def get_tool(name: str):
**util.get_tool_params(util.get_tools_dict(tool_type)),
"base_classes": ["Tool"],
}
# {"template": signature.tool(tool), **values}
# for tool, values in tools.items()
# }
# return {k: util.get_tool_params(v) for k, v in merged_dict.items()}

View file

@ -2,6 +2,8 @@ import ast
import inspect
import re
import importlib
from langchain.agents.load_tools import *
from langchain.agents.load_tools import (
_BASE_TOOLS,
_LLM_TOOLS,
@ -11,6 +13,88 @@ from langchain.agents.load_tools import (
from typing import Optional
def build_template_from_function(name: str, type_to_loader_dict: dict):
classes = [
item.__annotations__["return"].__name__ for item in type_to_loader_dict.values()
]
# Raise error if name is not in chains
if name not in classes:
raise ValueError(f"{name} not found")
for _type, v in type_to_loader_dict.items():
if v.__annotations__["return"].__name__ == name:
_class = v.__annotations__["return"]
docs = get_class_doc(_class)
variables = {"_type": _type}
for name, value in _class.__fields__.items():
if name in ["callback_manager", "requests_wrapper"]:
continue
variables[name] = {}
for name_, value_ in value.__repr_args__():
if name_ == "default_factory":
try:
variables[name]["default"] = get_default_factory(
module=_class.__base__.__module__, function=value_
)
except Exception:
variables[name]["default"] = None
elif name_ not in ["name"]:
variables[name][name_] = value_
variables[name]["placeholder"] = (
docs["Attributes"][name] if name in docs["Attributes"] else ""
)
return {
"template": format_dict(variables),
"description": docs["Description"],
"base_classes": get_base_classes(_class),
}
def build_template_from_class(name: str, type_to_cls_dict: dict):
classes = [item.__name__ for item in type_to_cls_dict.values()]
# Raise error if name is not in chains
if name not in classes:
raise ValueError(f"{name} not found.")
for _type, v in type_to_cls_dict.items():
if v.__name__ == name:
_class = v
docs = get_class_doc(_class)
variables = {"_type": _type}
for name, value in _class.__fields__.items():
if name in ["callback_manager"]:
continue
variables[name] = {}
for name_, value_ in value.__repr_args__():
if name_ == "default_factory":
try:
variables[name]["default"] = get_default_factory(
module=_class.__base__.__module__, function=value_
)
except Exception:
variables[name]["default"] = None
elif name_ not in ["name"]:
variables[name][name_] = value_
variables[name]["placeholder"] = (
docs["Attributes"][name] if name in docs["Attributes"] else ""
)
return {
"template": format_dict(variables),
"description": docs["Description"],
"base_classes": get_base_classes(_class),
}
def get_base_classes(cls):
bases = cls.__bases__
if not bases:
@ -45,7 +129,7 @@ def get_tools_dict(name: Optional[str] = None):
return tools[name] if name else tools
def get_tool_params(func):
def get_tool_params(func, **kwargs):
# Parse the function code into an abstract syntax tree
tree = ast.parse(inspect.getsource(func))
@ -54,19 +138,35 @@ def get_tool_params(func):
# Find the first return statement
if isinstance(node, ast.Return):
tool = node.value
if isinstance(tool, ast.Call) and tool.func.id == "Tool":
if tool.keywords:
tool_params = {}
for keyword in tool.keywords:
if keyword.arg == "name":
tool_params["name"] = ast.literal_eval(keyword.value)
elif keyword.arg == "description":
tool_params["description"] = ast.literal_eval(keyword.value)
return tool_params
return {
"name": ast.literal_eval(tool.args[0]),
"description": ast.literal_eval(tool.args[2]),
}
if isinstance(tool, ast.Call):
if tool.func.id == "Tool":
if tool.keywords:
tool_params = {}
for keyword in tool.keywords:
if keyword.arg == "name":
tool_params["name"] = ast.literal_eval(keyword.value)
elif keyword.arg == "description":
tool_params["description"] = ast.literal_eval(
keyword.value
)
return tool_params
return {
"name": ast.literal_eval(tool.args[0]),
"description": ast.literal_eval(tool.args[2]),
}
else:
# get the class object from the return statement
try:
class_obj = eval(
compile(ast.Expression(tool), "<string>", "eval")
)
except Exception:
return None
return {
"name": getattr(class_obj, "name"),
"description": getattr(class_obj, "description"),
}
# Return None if no return statement was found
return None
@ -162,7 +262,15 @@ def format_dict(d):
value["show"] = bool(
(value["required"] and key not in ["input_variables"])
or key
in ["allowed_tools", "verbose", "Memory", "memory", "prefix", "examples"]
in [
"allowed_tools",
"verbose",
"Memory",
"memory",
"prefix",
"examples",
"temperature",
]
or "api_key" in key
)