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
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

View file

@ -2,11 +2,27 @@ from fastapi import FastAPI
from endpoints import router as endpoints_router
from list import router as list_router
from signature import router as signatures_router
from fastapi.middleware.cors import CORSMiddleware
def create_app():
"""Create the FastAPI app and include the router."""
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(list_router)
app.include_router(signatures_router)

View file

@ -19,7 +19,7 @@ AGENT_TYPE = "conversational-react-description"
@router.get("/")
def get_all():
tools = list.list_tools()
# tools = list.list_tools()
return {
"chains": {chain: signature.chain(chain) for chain in list.list_chains()},
"agents": {agent: signature.agent(agent) for agent in list.list_agents()},
@ -41,13 +41,19 @@ def get_all():
# },
# "vectorstores": {"template": {}},
# "docstores": {"template": {}},
"tools": {
tool: {"template": signature.tool(tool), **values}
for tool, values in tools.items()
},
# "tools": {
# tool: {"template": signature.tool(tool), **values}
# 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")
# def get_chain():
# 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 vectorstores
from langchain import docstore
from langchain.agents.load_tools import (
_BASE_TOOLS,
_LLM_TOOLS,
_EXTRA_LLM_TOOLS,
_EXTRA_OPTIONAL_TOOLS,
)
from langchain.agents.load_tools import get_all_tool_names
import util
@ -106,11 +101,4 @@ def list_memories():
def list_tools():
"""List all load tools"""
merged_dict = {
**_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()}
return get_all_tool_names()

View file

@ -24,8 +24,6 @@ router = APIRouter(
tags=["signatures"],
)
KEYS_TO_REMOVE = ["name", "default_factory"]
def build_template_from_function(name: str, dict: dict):
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:
raise Exception(f"{name} not found.")
for k, v in dict.items():
for _type, v in dict.items():
if v.__annotations__["return"].__name__ == name:
_type = k
_class = v.__annotations__["return"]
docs = util.get_class_doc(_class)
variables = {}
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_ 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]["placeholder"] = docs["Attributes"][name] if name in docs["Attributes"] else ""
variables[name]["placeholder"] = (
docs["Attributes"][name] if name in docs["Attributes"] else ""
)
return {
"template": util.format_dict(variables),
"_type": _type,
"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):
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:
raise Exception(f"{name} not found.")
for k, v in dict.items():
for _type, v in dict.items():
if v.__name__ == name:
_type = k
_class = v
docs = util.get_class_doc(_class)
variables = {}
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_ 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]["placeholder"] = docs["Attributes"][name] if name in docs["Attributes"] else ""
variables[name]["placeholder"] = (
docs["Attributes"][name] if name in docs["Attributes"] else ""
)
return {
"template": util.format_dict(variables),
"_type": _type,
"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")
def chain(name: str):
@ -193,6 +184,16 @@ def tool(name: str):
params = extra_keys
return {
param: (type_dict[param] if param == "llm" else type_dict["str"])
for param in params
"template": {
param: (type_dict[param] if param == "llm" else type_dict["str"])
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 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):
@ -69,61 +111,15 @@ def get_class_doc(class_name):
current_section = line[:-1]
continue
if current_section == "Description":
data[current_section] += line
elif current_section == "Example":
if current_section in ["Description", "Example"]:
data[current_section] += line
else:
try:
param, desc = line.split(":")
except:
param, desc = "", ""
param, desc = line.split(":")
data[current_section][param.strip()] = desc.strip()
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):
"""
Formats a dictionary by removing certain keys and modifying the values of other keys.
@ -134,12 +130,11 @@ def format_dict(d):
Returns:
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
for key, value in d.items():
if key == "_type":
continue
_type = value["type"]
# Remove 'Optional' wrapper
@ -157,18 +152,22 @@ def format_dict(d):
if "Mapping" in _type:
_type = _type.replace("Mapping", "dict")
value["type"] = _type
value["type"] = "Tool" if key == "allowed_tools" else _type
# Show if required
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
if _type == "str":
if _type in ["str", "bool"]:
value["value"] = value.get("default", "")
if "default" in value:
value.pop("default")
# 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"]}
)