feat: all types working

This commit is contained in:
Ibis Prevedello 2023-02-17 18:09:34 -03:00
commit 04ac50e9ef
6 changed files with 131 additions and 121 deletions

View file

@ -1,5 +1,5 @@
#! /bin/bash #! /bin/bash
poetry remove langchain poetry remove langchain
docker build -t ibiscp/expert:v0.0.6 . && docker push ibiscp/expert:v0.0.6 docker build -t ibiscp/expert:v0.0.11 . && docker push ibiscp/expert:v0.0.11
poetry add --editable ../langchain poetry add --editable ../langchain

View file

@ -2,11 +2,27 @@ from fastapi import FastAPI
from endpoints import router as endpoints_router from endpoints import router as endpoints_router
from list import router as list_router from list import router as list_router
from signature import router as signatures_router from signature import router as signatures_router
from fastapi.middleware.cors import CORSMiddleware
def create_app(): def create_app():
"""Create the FastAPI app and include the router.""" """Create the FastAPI app and include the router."""
app = FastAPI() app = FastAPI()
origins = [
"http://localhost",
"http://localhost:8080",
"http://localhost:3000",
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
app.include_router(endpoints_router) app.include_router(endpoints_router)
app.include_router(list_router) app.include_router(list_router)
app.include_router(signatures_router) app.include_router(signatures_router)

View file

@ -19,7 +19,7 @@ AGENT_TYPE = "conversational-react-description"
@router.get("/") @router.get("/")
def get_all(): def get_all():
tools = list.list_tools() # tools = list.list_tools()
return { return {
"chains": {chain: signature.chain(chain) for chain in list.list_chains()}, "chains": {chain: signature.chain(chain) for chain in list.list_chains()},
"agents": {agent: signature.agent(agent) for agent in list.list_agents()}, "agents": {agent: signature.agent(agent) for agent in list.list_agents()},
@ -41,13 +41,19 @@ def get_all():
# }, # },
# "vectorstores": {"template": {}}, # "vectorstores": {"template": {}},
# "docstores": {"template": {}}, # "docstores": {"template": {}},
"tools": { # "tools": {
tool: {"template": signature.tool(tool), **values} # tool: {"template": signature.tool(tool), **values}
for tool, values in tools.items() # for tool, values in tools.items()
}, # },
"tools": {tool: signature.tool(tool) for tool in list.list_tools()},
} }
@router.post("/load")
def get_load(data: dict[str, str]) -> str:
return "Hello Otávio!"
# @router.get("/chain") # @router.get("/chain")
# def get_chain(): # def get_chain():
# llm = OpenAI(temperature=0) # llm = OpenAI(temperature=0)

View file

@ -9,12 +9,7 @@ from langchain.chains.conversation import memory as memories
from langchain import document_loaders from langchain import document_loaders
from langchain import vectorstores from langchain import vectorstores
from langchain import docstore from langchain import docstore
from langchain.agents.load_tools import ( from langchain.agents.load_tools import get_all_tool_names
_BASE_TOOLS,
_LLM_TOOLS,
_EXTRA_LLM_TOOLS,
_EXTRA_OPTIONAL_TOOLS,
)
import util import util
@ -106,11 +101,4 @@ def list_memories():
def list_tools(): def list_tools():
"""List all load tools""" """List all load tools"""
merged_dict = { return get_all_tool_names()
**_BASE_TOOLS,
**_LLM_TOOLS,
**{k: v[0] for k, v in _EXTRA_LLM_TOOLS.items()},
**{k: v[0] for k, v in _EXTRA_OPTIONAL_TOOLS.items()},
}
return {k: util.get_tool_params(v) for k, v in merged_dict.items()}

View file

@ -24,8 +24,6 @@ router = APIRouter(
tags=["signatures"], tags=["signatures"],
) )
KEYS_TO_REMOVE = ["name", "default_factory"]
def build_template_from_function(name: str, dict: dict): def build_template_from_function(name: str, dict: dict):
classes = [item.__annotations__["return"].__name__ for item in dict.values()] classes = [item.__annotations__["return"].__name__ for item in dict.values()]
@ -34,41 +32,38 @@ def build_template_from_function(name: str, dict: dict):
if name not in classes: if name not in classes:
raise Exception(f"{name} not found.") raise Exception(f"{name} not found.")
for k, v in dict.items(): for _type, v in dict.items():
if v.__annotations__["return"].__name__ == name: if v.__annotations__["return"].__name__ == name:
_type = k
_class = v.__annotations__["return"] _class = v.__annotations__["return"]
docs = util.get_class_doc(_class) docs = util.get_class_doc(_class)
variables = {} variables = {"_type": _type}
for name, value in _class.__fields__.items(): for name, value in _class.__fields__.items():
if name in ["callback_manager", "requests_wrapper"]:
continue
variables[name] = {} variables[name] = {}
for name_, value_ in value.__repr_args__(): for name_, value_ in value.__repr_args__():
if name_ not in KEYS_TO_REMOVE: if name_ == "default_factory":
try:
variables[name]["default"] = util.get_default_factory(
module=_class.__base__.__module__, function=value_
)
except:
variables[name]["default"] = None
elif name_ not in ["name"]:
variables[name][name_] = value_ variables[name][name_] = value_
variables[name]["placeholder"] = docs["Attributes"][name] if name in docs["Attributes"] else ""
variables[name]["placeholder"] = (
docs["Attributes"][name] if name in docs["Attributes"] else ""
)
return { return {
"template": util.format_dict(variables), "template": util.format_dict(variables),
"_type": _type,
"description": docs["Description"], "description": docs["Description"],
"base_classes": util.get_base_classes(_class),
} }
# return {
# "template": util.format_dict(
# {
# name: {
# name: value
# for (name, value) in value.__repr_args__()
# if name not in KEYS_TO_REMOVE
# }
# for name, value in _class.__fields__.items()
# }
# ),
# "_type": _type,
# "description": _class.__doc__,
# }
def build_template_from_class(name: str, dict: dict): def build_template_from_class(name: str, dict: dict):
classes = [item.__name__ for item in dict.values()] classes = [item.__name__ for item in dict.values()]
@ -77,42 +72,38 @@ def build_template_from_class(name: str, dict: dict):
if name not in classes: if name not in classes:
raise Exception(f"{name} not found.") raise Exception(f"{name} not found.")
for k, v in dict.items(): for _type, v in dict.items():
if v.__name__ == name: if v.__name__ == name:
_type = k
_class = v _class = v
docs = util.get_class_doc(_class) docs = util.get_class_doc(_class)
variables = {} variables = {"_type": _type}
for name, value in _class.__fields__.items(): for name, value in _class.__fields__.items():
if name in ["callback_manager"]:
continue
variables[name] = {} variables[name] = {}
for name_, value_ in value.__repr_args__(): for name_, value_ in value.__repr_args__():
if name_ not in KEYS_TO_REMOVE: if name_ == "default_factory":
try:
variables[name]["default"] = util.get_default_factory(
module=_class.__base__.__module__, function=value_
)
except:
variables[name]["default"] = None
elif name_ not in ["name"]:
variables[name][name_] = value_ variables[name][name_] = value_
variables[name]["placeholder"] = docs["Attributes"][name] if name in docs["Attributes"] else ""
variables[name]["placeholder"] = (
docs["Attributes"][name] if name in docs["Attributes"] else ""
)
return { return {
"template": util.format_dict(variables), "template": util.format_dict(variables),
"_type": _type,
"description": docs["Description"], "description": docs["Description"],
"base_classes": util.get_base_classes(_class),
} }
# return {
# "template": util.format_dict(
# {
# name: {
# name: value
# for (name, value) in value.__repr_args__()
# if name not in KEYS_TO_REMOVE
# }
# for name, value in _class.__fields__.items()
# }
# ),
# "_type": _type,
# "description": _class.__doc__,
# }
@router.get("/chain") @router.get("/chain")
def chain(name: str): def chain(name: str):
@ -193,6 +184,16 @@ def tool(name: str):
params = extra_keys params = extra_keys
return { return {
"template": {
param: (type_dict[param] if param == "llm" else type_dict["str"]) param: (type_dict[param] if param == "llm" else type_dict["str"])
for param in params for param in params
},
**util.get_tool_params(util.get_tools_dict(name)),
"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

@ -1,5 +1,47 @@
import ast import ast
import inspect import inspect
import re
import importlib
from langchain.agents.load_tools import (
_BASE_TOOLS,
_LLM_TOOLS,
_EXTRA_LLM_TOOLS,
_EXTRA_OPTIONAL_TOOLS,
)
from typing import Optional
def get_base_classes(cls):
bases = cls.__bases__
if not bases:
return []
else:
result = []
for base in bases:
if any(type in base.__module__ for type in ["pydantic", "abc"]):
continue
result.append(base.__name__)
result.extend(get_base_classes(base))
return result
def get_default_factory(module: str, function: str):
pattern = r"<function (\w+)>"
if match := re.search(pattern, function):
module = importlib.import_module(module)
return getattr(module, match[1])()
return None
def get_tools_dict(name: Optional[str] = None):
tools = {
**_BASE_TOOLS,
**_LLM_TOOLS,
**{k: v[0] for k, v in _EXTRA_LLM_TOOLS.items()},
**{k: v[0] for k, v in _EXTRA_OPTIONAL_TOOLS.items()},
}
return tools[name] if name else tools
def get_tool_params(func): def get_tool_params(func):
@ -69,61 +111,15 @@ def get_class_doc(class_name):
current_section = line[:-1] current_section = line[:-1]
continue continue
if current_section == "Description": if current_section in ["Description", "Example"]:
data[current_section] += line
elif current_section == "Example":
data[current_section] += line data[current_section] += line
else: else:
try:
param, desc = line.split(":") param, desc = line.split(":")
except:
param, desc = "", ""
data[current_section][param.strip()] = desc.strip() data[current_section][param.strip()] = desc.strip()
return data return data
# def format_dict(d):
# # Remove from keys
# keys_to_remove = ["callback_manager"]
# for key in keys_to_remove:
# if key in d:
# d.pop(key)
# for key, value in d.items():
# _type = value["type"]
# # Add optional parameter
# if "Optional" in _type:
# _type = _type.replace("Optional[", "")[:-1]
# # Add list parameter
# if "List" in _type:
# _type = _type.replace("List[", "")[:-1]
# value["list"] = True
# else:
# value["list"] = False
# if "Mapping" in _type:
# _type = _type.replace("Mapping", "dict")
# value["type"] = _type
# # Show if required
# if value["required"] or key in ["allowed_tools", "verbose", "Memory"]:
# value["show"] = True
# else:
# value["show"] = False
# # If default, change to value
# if value['type'] == 'str':
# value["value"] = value["default"] if 'default' in value else ''
# if 'default' in value:
# value.pop("default")
# return {key: value for key, value in d.items() if value["show"]}
def format_dict(d): def format_dict(d):
""" """
Formats a dictionary by removing certain keys and modifying the values of other keys. Formats a dictionary by removing certain keys and modifying the values of other keys.
@ -134,12 +130,11 @@ def format_dict(d):
Returns: Returns:
A new dictionary with the desired modifications applied. A new dictionary with the desired modifications applied.
""" """
# Remove keys to exclude
keys_to_exclude = ["callback_manager"]
d = {key: value for key, value in d.items() if key not in keys_to_exclude}
# Process remaining keys # Process remaining keys
for key, value in d.items(): for key, value in d.items():
if key == "_type":
continue
_type = value["type"] _type = value["type"]
# Remove 'Optional' wrapper # Remove 'Optional' wrapper
@ -157,18 +152,22 @@ def format_dict(d):
if "Mapping" in _type: if "Mapping" in _type:
_type = _type.replace("Mapping", "dict") _type = _type.replace("Mapping", "dict")
value["type"] = _type value["type"] = "Tool" if key == "allowed_tools" else _type
# Show if required # Show if required
value["show"] = bool( value["show"] = bool(
value["required"] or key in ["allowed_tools", "verbose", "Memory"] (value["required"] and key not in ["input_variables"])
or key in ["allowed_tools", "verbose", "Memory", "memory", "prefix"]
or "api_key" in key
) )
# Replace default value with actual value # Replace default value with actual value
if _type == "str": if _type in ["str", "bool"]:
value["value"] = value.get("default", "") value["value"] = value.get("default", "")
if "default" in value: if "default" in value:
value.pop("default") value.pop("default")
# Filter out keys that should not be shown # Filter out keys that should not be shown
return {key: value for key, value in d.items() if value["show"]} return (
d # {key: value for key, value in d.items() if key == "_type" or value["show"]}
)