Skip to content

Commit

Permalink
Merge branch 'release' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
ogabrielluiz committed Aug 10, 2023
2 parents 5e370f3 + fe5a35a commit 3d8d445
Show file tree
Hide file tree
Showing 18 changed files with 472 additions and 287 deletions.
1 change: 0 additions & 1 deletion docs/docs/guidelines/custom-component.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ class DocumentProcessor(CustomComponent):
light: "img/document_processor.png",
}}
style={{
width: "40%",
margin: "0 auto",
display: "flex",
justifyContent: "center",
Expand Down
493 changes: 259 additions & 234 deletions poetry.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "langflow"
version = "0.4.3"
version = "0.4.7"
description = "A Python package with a built-in web application"
authors = ["Logspace <contact@logspace.ai>"]
maintainers = [
Expand Down Expand Up @@ -78,6 +78,7 @@ psycopg-binary = "^3.1.9"
fastavro = "^1.8.0"
langchain-experimental = "^0.0.8"
alembic = "^1.11.2"
metaphor-python = "^0.1.11"

[tool.poetry.group.dev.dependencies]
black = "^23.1.0"
Expand Down
56 changes: 56 additions & 0 deletions src/backend/langflow/components/toolkits/Metaphor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
from typing import List, Union
from langflow import CustomComponent

from metaphor_python import Metaphor # type: ignore
from langchain.tools import Tool
from langchain.agents import tool
from langchain.agents.agent_toolkits.base import BaseToolkit


class MetaphorToolkit(CustomComponent):
display_name: str = "Metaphor"
description: str = "Metaphor Toolkit"
documentation = (
"https://python.langchain.com/docs/integrations/tools/metaphor_search"
)
beta = True
# api key should be password = True
field_config = {
"metaphor_api_key": {"display_name": "Metaphor API Key", "password": True},
"code": {"advanced": True},
}

def build(
self,
metaphor_api_key: str,
use_autoprompt: bool = True,
search_num_results: int = 5,
similar_num_results: int = 5,
) -> Union[Tool, BaseToolkit]:
# If documents, then we need to create a Vectara instance using .from_documents
client = Metaphor(api_key=metaphor_api_key)

@tool
def search(query: str):
"""Call search engine with a query."""
return client.search(
query, use_autoprompt=use_autoprompt, num_results=search_num_results
)

@tool
def get_contents(ids: List[str]):
"""Get contents of a webpage.
The ids passed in should be a list of ids as fetched from `search`.
"""
return client.get_contents(ids)

@tool
def find_similar(url: str):
"""Get search results similar to a given URL.
The url passed in should be a URL returned from `search`
"""
return client.find_similar(url, num_results=similar_num_results)

return [search, get_contents, find_similar] # type: ignore
Empty file.
50 changes: 50 additions & 0 deletions src/backend/langflow/components/vectorstores/Vectara.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from typing import Optional, Union
from langflow import CustomComponent

from langchain.vectorstores import Vectara
from langchain.schema import Document
from langchain.vectorstores.base import VectorStore
from langchain.schema import BaseRetriever
from langchain.embeddings.base import Embeddings


class VectaraComponent(CustomComponent):
display_name: str = "Vectara"
description: str = "Implementation of Vector Store using Vectara"
documentation = (
"https://python.langchain.com/docs/integrations/vectorstores/vectara"
)
beta = True
# api key should be password = True
field_config = {
"vectara_customer_id": {"display_name": "Vectara Customer ID"},
"vectara_corpus_id": {"display_name": "Vectara Corpus ID"},
"vectara_api_key": {"display_name": "Vectara API Key", "password": True},
"code": {"show": False},
"documents": {"display_name": "Documents"},
"embedding": {"display_name": "Embedding"},
}

def build(
self,
vectara_customer_id: str,
vectara_corpus_id: str,
vectara_api_key: str,
embedding: Optional[Embeddings] = None,
documents: Optional[Document] = None,
) -> Union[VectorStore, BaseRetriever]:
# If documents, then we need to create a Vectara instance using .from_documents
if documents is not None and embedding is not None:
return Vectara.from_documents(
documents=documents, # type: ignore
vectara_customer_id=vectara_customer_id,
vectara_corpus_id=vectara_corpus_id,
vectara_api_key=vectara_api_key,
embedding=embedding,
)

return Vectara(
vectara_customer_id=vectara_customer_id,
vectara_corpus_id=vectara_corpus_id,
vectara_api_key=vectara_api_key,
)
Empty file.
3 changes: 3 additions & 0 deletions src/backend/langflow/interface/custom/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ def build_template_config(self, attributes) -> dict:
elif "beta" in item_name:
template_config["beta"] = ast.literal_eval(item_value)

elif "documentation" in item_name:
template_config["documentation"] = ast.literal_eval(item_value)

return template_config

def build(self, *args: Any, **kwargs: Any) -> Any:
Expand Down
26 changes: 19 additions & 7 deletions src/backend/langflow/interface/custom/custom_component.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ def _class_template_validation(self, code: str):
reader = DirectoryReader("", False)

for type_hint in TYPE_HINT_LIST:
if reader.is_type_hint_used_but_not_imported(type_hint, code):
if reader._is_type_hint_used_in_args(
"Optional", code
) and not reader._is_type_hint_imported("Optional", code):
error_detail = {
"error": "Type hint Error",
"traceback": f"Type hint '{type_hint}' is used but not imported in the code.",
Expand Down Expand Up @@ -93,9 +95,9 @@ def get_function_entrypoint_args(self) -> str:
return build_method["args"]

@property
def get_function_entrypoint_return_type(self) -> str:
def get_function_entrypoint_return_type(self) -> List[str]:
if not self.code:
return ""
return []
tree = self.get_code_tree(self.code)

component_classes = [
Expand All @@ -104,7 +106,7 @@ def get_function_entrypoint_return_type(self) -> str:
if self.code_class_base_inheritance in cls["bases"]
]
if not component_classes:
return ""
return []

# Assume the first Component class is the one we're interested in
component_class = component_classes[0]
Expand All @@ -115,11 +117,21 @@ def get_function_entrypoint_return_type(self) -> str:
]

if not build_methods:
return ""
return []

build_method = build_methods[0]

return build_method["return_type"]
return_type = build_method["return_type"]
if not return_type:
return []
# If the return type is not a Union, then we just return it as a list
if "Union" not in return_type:
return [return_type] if return_type in self.return_type_valid_list else []

# If the return type is a Union, then we need to parse it
return_type = return_type.replace("Union", "").replace("[", "").replace("]", "")
return_type = return_type.split(",")
return_type = [item.strip() for item in return_type]
return [item for item in return_type if item in self.return_type_valid_list]

@property
def get_main_class_name(self):
Expand Down
31 changes: 20 additions & 11 deletions src/backend/langflow/interface/custom/directory_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,15 +152,19 @@ def _is_type_hint_used_in_args(self, type_hint_name: str, code: str) -> bool:
Check if a specific type hint is used in the
function definitions within the given code.
"""
module = ast.parse(code)

for node in ast.walk(module):
if isinstance(node, ast.FunctionDef):
for arg in node.args.args:
if self._is_type_hint_in_arg_annotation(
arg.annotation, type_hint_name
):
return True
try:
module = ast.parse(code)

for node in ast.walk(module):
if isinstance(node, ast.FunctionDef):
for arg in node.args.args:
if self._is_type_hint_in_arg_annotation(
arg.annotation, type_hint_name
):
return True
except SyntaxError:
# Returns False if the code is not valid Python
return False
return False

def _is_type_hint_in_arg_annotation(self, annotation, type_hint_name: str) -> bool:
Expand Down Expand Up @@ -204,8 +208,13 @@ def process_file(self, file_path):
return False, "Syntax error"
elif not self.validate_build(file_content):
return False, "Missing build function"
elif self.is_type_hint_used_but_not_imported("Optional", file_content):
return False, "Type hint 'Optional' is used but not imported in the code."
elif self._is_type_hint_used_in_args(
"Optional", file_content
) and not self._is_type_hint_imported("Optional", file_content):
return (
False,
"Type hint 'Optional' is used but not imported in the code.",
)
else:
if self.compress_code_field:
file_content = str(StringCompressor(file_content).compress_string())
Expand Down
7 changes: 5 additions & 2 deletions src/backend/langflow/interface/initialize/loading.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,12 @@ def instantiate_based_on_type(class_object, base_type, node_type, params):


def instantiate_custom_component(node_type, class_object, params):
class_object = get_function_custom(params.pop("code"))
# we need to make a copy of the params because we will be
# modifying it
params_copy = params.copy()
class_object = get_function_custom(params_copy.pop("code"))
custom_component = class_object()
built_object = custom_component.build(**params)
built_object = custom_component.build(**params_copy)
return built_object, {"repr": custom_component.custom_repr()}


Expand Down
4 changes: 2 additions & 2 deletions src/backend/langflow/interface/initialize/vector_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,8 @@ def initialize_pinecone(class_object: Type[Pinecone], params: dict):

import pinecone # type: ignore

pinecone_api_key = params.get("pinecone_api_key")
pinecone_env = params.get("pinecone_env")
pinecone_api_key = params.pop("pinecone_api_key")
pinecone_env = params.pop("pinecone_env")

if pinecone_api_key is None or pinecone_env is None:
if os.getenv("PINECONE_API_KEY") is not None:
Expand Down
44 changes: 24 additions & 20 deletions src/backend/langflow/interface/types.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import ast
import contextlib
from typing import Any
from typing import Any, List
from langflow.api.utils import merge_nested_dicts_with_renaming
from langflow.interface.agents.base import agent_creator
from langflow.interface.chains.base import chain_creator
Expand Down Expand Up @@ -199,6 +199,9 @@ def update_attributes(frontend_node, template_config):
if "beta" in template_config:
frontend_node["beta"] = template_config["beta"]

if "documentation" in template_config:
frontend_node["documentation"] = template_config["documentation"]


def build_field_config(custom_component: CustomComponent):
"""Build the field configuration for a custom component"""
Expand Down Expand Up @@ -257,26 +260,27 @@ def get_field_properties(extra_field):
return field_name, field_type, field_value, field_required


def add_base_classes(frontend_node, return_type):
def add_base_classes(frontend_node, return_types: List[str]):
"""Add base classes to the frontend node"""
if return_type not in CUSTOM_COMPONENT_SUPPORTED_TYPES or return_type is None:
raise HTTPException(
status_code=400,
detail={
"error": (
"Invalid return type should be one of: "
f"{list(CUSTOM_COMPONENT_SUPPORTED_TYPES.keys())}"
),
"traceback": traceback.format_exc(),
},
)

return_type_instance = CUSTOM_COMPONENT_SUPPORTED_TYPES.get(return_type)
base_classes = get_base_classes(return_type_instance)

for base_class in base_classes:
if base_class not in CLASSES_TO_REMOVE:
frontend_node.get("base_classes").append(base_class)
for return_type in return_types:
if return_type not in CUSTOM_COMPONENT_SUPPORTED_TYPES or return_type is None:
raise HTTPException(
status_code=400,
detail={
"error": (
"Invalid return type should be one of: "
f"{list(CUSTOM_COMPONENT_SUPPORTED_TYPES.keys())}"
),
"traceback": traceback.format_exc(),
},
)

return_type_instance = CUSTOM_COMPONENT_SUPPORTED_TYPES.get(return_type)
base_classes = get_base_classes(return_type_instance)

for base_class in base_classes:
if base_class not in CLASSES_TO_REMOVE:
frontend_node.get("base_classes").append(base_class)


def build_langchain_template_custom_component(custom_component: CustomComponent):
Expand Down
26 changes: 21 additions & 5 deletions src/frontend/src/components/headerComponent/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,27 @@ export default function Header() {
return (
<div className="header-arrangement">
<div className="header-start-display">
<Link to="/">
<span className="ml-4 text-2xl">⛓️</span>
</Link>
{flows.findIndex((flow) => tabId === flow.id) !== -1 &&
tabId !== "" && <MenuBar flows={flows} tabId={tabId} />}
{tabId === "" || !tabId ? (
<div className="ml-2">
<a
href="https://www.langflow.org/"
target="_blank"
rel="noreferrer"
className="header-waitlist-link-box"
>
<span className="pr-1 text-2xl">⛓️</span>
<span>Join The Waitlist</span>
</a>
</div>
) : (
<Link to="/">
<span className="ml-4 text-2xl">⛓️</span>
</Link>
)}

{flows.findIndex((f) => tabId === f.id) !== -1 && tabId !== "" && (
<MenuBar flows={flows} tabId={tabId} />
)}
</div>
<div className="round-button-div">
<Link to="/">
Expand Down
6 changes: 6 additions & 0 deletions src/frontend/src/style/applies.css
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,12 @@
.header-github-link-box {
@apply inline-flex h-9 items-center justify-center rounded-md border border-input px-3 pr-0 shadow-sm;
}
.header-waitlist-link-box {
@apply inline-flex h-9 items-center justify-center rounded-md border border-input px-2 shadow-sm text-sm font-medium text-muted-foreground ring-offset-background disabled:pointer-events-none disabled:opacity-50 whitespace-nowrap;
}
.header-waitlist-link-box:hover {
@apply hover:bg-accent hover:text-accent-foreground;
}
.header-github-link {
@apply header-github-link-box text-sm font-medium text-muted-foreground ring-offset-background disabled:pointer-events-none disabled:opacity-50;
}
Expand Down
Loading

0 comments on commit 3d8d445

Please sign in to comment.