Merge branch 'zustand/io/migration' of github.com:logspace-ai/langflow into zustand/io/migration
This commit is contained in:
commit
f57af2da52
13 changed files with 203 additions and 109 deletions
|
|
@ -10,6 +10,9 @@ from fastapi import (
|
|||
WebSocketException,
|
||||
status,
|
||||
)
|
||||
from loguru import logger
|
||||
from sqlmodel import Session
|
||||
|
||||
from langflow.api.utils import build_and_cache_graph, format_elapsed_time
|
||||
from langflow.api.v1.schemas import (
|
||||
ResultData,
|
||||
|
|
@ -24,8 +27,6 @@ from langflow.services.auth.utils import (
|
|||
from langflow.services.chat.service import ChatService
|
||||
from langflow.services.deps import get_chat_service, get_session
|
||||
from langflow.services.monitor.utils import log_vertex_build
|
||||
from loguru import logger
|
||||
from sqlmodel import Session
|
||||
|
||||
router = APIRouter(tags=["Chat"])
|
||||
|
||||
|
|
@ -172,7 +173,7 @@ async def build_vertex(
|
|||
raise ValueError(f"No result found for vertex {vertex_id}")
|
||||
chat_service.set_cache(flow_id, graph)
|
||||
except Exception as exc:
|
||||
params = repr(exc)
|
||||
params = str(exc)
|
||||
valid = False
|
||||
result_dict = ResultData(results={})
|
||||
artifacts = {}
|
||||
|
|
|
|||
|
|
@ -23,19 +23,25 @@ class ConversationChainComponent(CustomComponent):
|
|||
|
||||
def build(
|
||||
self,
|
||||
inputs: str,
|
||||
llm: BaseLanguageModel,
|
||||
memory: Optional[BaseMemory] = None,
|
||||
inputs: dict = {},
|
||||
) -> Union[Chain, Callable, Text]:
|
||||
if memory is None:
|
||||
chain = ConversationChain(llm=llm)
|
||||
chain = ConversationChain(llm=llm, memory=memory)
|
||||
else:
|
||||
chain = ConversationChain(llm=llm, memory=memory)
|
||||
result = chain.invoke(inputs)
|
||||
# result is an AIMessage which is a subclass of BaseMessage
|
||||
# We need to check if it is a string or a BaseMessage
|
||||
if hasattr(result, "content") and isinstance(result.content, str):
|
||||
return result.content
|
||||
self.status = "is message"
|
||||
result = result.content
|
||||
elif isinstance(result, str):
|
||||
return result
|
||||
|
||||
return str(result)
|
||||
self.status = "is_string"
|
||||
result = result
|
||||
else:
|
||||
# is dict
|
||||
result = result.get("response")
|
||||
self.status = result
|
||||
return result
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import os
|
||||
from typing import Any, Callable, Dict, Optional, Union
|
||||
|
||||
from langchain_community.chat_models.litellm import ChatLiteLLM, ChatLiteLLMException
|
||||
|
|
@ -27,6 +26,18 @@ class ChatLiteLLMComponent(CustomComponent):
|
|||
"required": False,
|
||||
"password": True,
|
||||
},
|
||||
"provider": {
|
||||
"display_name": "Provider",
|
||||
"info": "The provider of the API key.",
|
||||
"options": [
|
||||
"OpenAI",
|
||||
"Azure",
|
||||
"Anthropic",
|
||||
"Replicate",
|
||||
"Cohere",
|
||||
"OpenRouter",
|
||||
],
|
||||
},
|
||||
"streaming": {
|
||||
"display_name": "Streaming",
|
||||
"field_type": "bool",
|
||||
|
|
@ -96,7 +107,8 @@ class ChatLiteLLMComponent(CustomComponent):
|
|||
def build(
|
||||
self,
|
||||
model: str,
|
||||
api_key: str,
|
||||
provider: str,
|
||||
api_key: Optional[str] = None,
|
||||
streaming: bool = True,
|
||||
temperature: Optional[float] = 0.7,
|
||||
model_kwargs: Optional[Dict[str, Any]] = {},
|
||||
|
|
@ -114,13 +126,19 @@ class ChatLiteLLMComponent(CustomComponent):
|
|||
litellm.set_verbose = verbose
|
||||
except ImportError:
|
||||
raise ChatLiteLLMException(
|
||||
"Could not import litellm python package. " "Please install it with `pip install litellm`"
|
||||
"Could not import litellm python package. "
|
||||
"Please install it with `pip install litellm`"
|
||||
)
|
||||
if api_key:
|
||||
if "perplexity" in model:
|
||||
os.environ["PERPLEXITYAI_API_KEY"] = api_key
|
||||
elif "replicate" in model:
|
||||
os.environ["REPLICATE_API_KEY"] = api_key
|
||||
provider_map = {
|
||||
"OpenAI": "openai_api_key",
|
||||
"Azure": "azure_api_key",
|
||||
"Anthropic": "anthropic_api_key",
|
||||
"Replicate": "replicate_api_key",
|
||||
"Cohere": "cohere_api_key",
|
||||
"OpenRouter": "openrouter_api_key",
|
||||
}
|
||||
# Set the API key based on the provider
|
||||
kwarg = {provider_map[provider]: api_key}
|
||||
|
||||
LLM = ChatLiteLLM(
|
||||
model=model,
|
||||
|
|
@ -133,5 +151,6 @@ class ChatLiteLLMComponent(CustomComponent):
|
|||
n=n,
|
||||
max_tokens=max_tokens,
|
||||
max_retries=max_retries,
|
||||
**kwarg,
|
||||
)
|
||||
return LLM
|
||||
|
|
|
|||
31
src/backend/langflow/interface/custom/attributes.py
Normal file
31
src/backend/langflow/interface/custom/attributes.py
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
def validate_icon(value: str, *args, **kwargs):
|
||||
# we are going to use the emoji library to validate the emoji
|
||||
# emojis can be defined using the :emoji_name: syntax
|
||||
if not value.startswith(":") or not value.endswith(":"):
|
||||
warnings.warn("Invalid emoji. Please use the :emoji_name: syntax.")
|
||||
return value
|
||||
emoji_value = emoji.emojize(value, variant="emoji_type")
|
||||
if value == emoji_value:
|
||||
warnings.warn(f"Invalid emoji. {value} is not a valid emoji.")
|
||||
return value
|
||||
return emoji_value
|
||||
|
||||
|
||||
def getattr_return_str(value):
|
||||
|
||||
return str(value) if value else ""
|
||||
|
||||
|
||||
def getattr_return_bool(value):
|
||||
if isinstance(value, bool):
|
||||
return value
|
||||
|
||||
|
||||
ATTR_FUNC_MAPPING = {
|
||||
"display_name": getattr_return_str,
|
||||
"description": getattr_return_str,
|
||||
"beta": getattr_return_str,
|
||||
"documentation": getattr_return_str,
|
||||
"icon": validate_icon,
|
||||
"pinned": getattr_return_bool,
|
||||
}
|
||||
|
|
@ -2,9 +2,10 @@ import operator
|
|||
import warnings
|
||||
from typing import Any, ClassVar, Optional
|
||||
|
||||
import emoji
|
||||
from cachetools import TTLCache, cachedmethod
|
||||
from fastapi import HTTPException
|
||||
|
||||
from langflow.interface.custom.attributes import ATTR_FUNC_MAPPING
|
||||
from langflow.interface.custom.code_parser import CodeParser
|
||||
from langflow.interface.custom.eval import eval_custom_component_code
|
||||
from langflow.utils import validate
|
||||
|
|
@ -65,14 +66,6 @@ class Component:
|
|||
|
||||
return validate.create_function(self.code, self._function_entrypoint_name)
|
||||
|
||||
def getattr_return_str(self, value):
|
||||
|
||||
return str(value) if value else ""
|
||||
|
||||
def getattr_return_bool(self, value):
|
||||
if isinstance(value, bool):
|
||||
return value
|
||||
|
||||
def build_template_config(self) -> dict:
|
||||
if not self.code:
|
||||
return {}
|
||||
|
|
@ -80,15 +73,8 @@ class Component:
|
|||
cc_class = eval_custom_component_code(self.code)
|
||||
component_instance = cc_class()
|
||||
template_config = {}
|
||||
attributes_func_mapping = {
|
||||
"display_name": self.getattr_return_str,
|
||||
"description": self.getattr_return_str,
|
||||
"beta": self.getattr_return_str,
|
||||
"documentation": self.getattr_return_str,
|
||||
"icon": self.validate_icon,
|
||||
}
|
||||
|
||||
for attribute, func in attributes_func_mapping.items():
|
||||
for attribute, func in ATTR_FUNC_MAPPING.items():
|
||||
if hasattr(component_instance, attribute):
|
||||
value = getattr(component_instance, attribute)
|
||||
if value is not None:
|
||||
|
|
@ -96,17 +82,5 @@ class Component:
|
|||
|
||||
return template_config
|
||||
|
||||
def validate_icon(self, value: str, *args, **kwargs):
|
||||
# we are going to use the emoji library to validate the emoji
|
||||
# emojis can be defined using the :emoji_name: syntax
|
||||
if not value.startswith(":") or not value.endswith(":"):
|
||||
warnings.warn("Invalid emoji. Please use the :emoji_name: syntax.")
|
||||
return value
|
||||
emoji_value = emoji.emojize(value, variant="emoji_type")
|
||||
if value == emoji_value:
|
||||
warnings.warn(f"Invalid emoji. {value} is not a valid emoji.")
|
||||
return value
|
||||
return emoji_value
|
||||
|
||||
def build(self, *args: Any, **kwargs: Any) -> Any:
|
||||
raise NotImplementedError
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ from uuid import UUID
|
|||
import yaml
|
||||
from cachetools import TTLCache, cachedmethod
|
||||
from fastapi import HTTPException
|
||||
|
||||
from langflow.interface.custom.code_parser.utils import (
|
||||
extract_inner_type_from_generic_alias,
|
||||
extract_union_types_from_generic_alias,
|
||||
|
|
@ -35,6 +36,8 @@ class CustomComponent(Component):
|
|||
"""The field configuration of the component. Defaults to an empty dictionary."""
|
||||
field_order: Optional[List[str]] = None
|
||||
"""The field order of the component. Defaults to an empty list."""
|
||||
pinned: Optional[bool] = False
|
||||
"""The default pinned state of the component. Defaults to False."""
|
||||
code_class_base_inheritance: ClassVar[str] = "CustomComponent"
|
||||
function_entrypoint_name: ClassVar[str] = "build"
|
||||
function: Optional[Callable] = None
|
||||
|
|
|
|||
|
|
@ -7,7 +7,10 @@ from typing import Any, Dict, List, Optional, Union
|
|||
from uuid import UUID
|
||||
|
||||
from fastapi import HTTPException
|
||||
from loguru import logger
|
||||
|
||||
from langflow.field_typing.range_spec import RangeSpec
|
||||
from langflow.interface.custom.attributes import ATTR_FUNC_MAPPING
|
||||
from langflow.interface.custom.code_parser.utils import extract_inner_type
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langflow.interface.custom.directory_reader.utils import (
|
||||
|
|
@ -22,7 +25,6 @@ from langflow.template.frontend_node.custom_components import (
|
|||
)
|
||||
from langflow.utils import validate
|
||||
from langflow.utils.util import get_base_classes
|
||||
from loguru import logger
|
||||
|
||||
|
||||
def add_output_types(
|
||||
|
|
@ -263,16 +265,9 @@ def run_build_config(
|
|||
|
||||
def sanitize_template_config(template_config):
|
||||
"""Sanitize the template config"""
|
||||
attributes = {
|
||||
"display_name",
|
||||
"description",
|
||||
"beta",
|
||||
"documentation",
|
||||
"output_types",
|
||||
"icon",
|
||||
}
|
||||
|
||||
for key in template_config.copy():
|
||||
if key not in attributes:
|
||||
if key not in ATTR_FUNC_MAPPING.keys():
|
||||
template_config.pop(key, None)
|
||||
|
||||
return template_config
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import re
|
|||
from collections import defaultdict
|
||||
from typing import ClassVar, Dict, List, Optional, Union
|
||||
|
||||
from pydantic import BaseModel, Field, field_serializer, model_serializer
|
||||
|
||||
from langflow.template.field.base import TemplateField
|
||||
from langflow.template.frontend_node.constants import (
|
||||
CLASSES_TO_REMOVE,
|
||||
|
|
@ -10,7 +12,6 @@ from langflow.template.frontend_node.constants import (
|
|||
from langflow.template.frontend_node.formatter import field_formatters
|
||||
from langflow.template.template.base import Template
|
||||
from langflow.utils import constants
|
||||
from pydantic import BaseModel, Field, field_serializer, model_serializer
|
||||
|
||||
|
||||
class FieldFormatters(BaseModel):
|
||||
|
|
@ -44,17 +45,31 @@ class FieldFormatters(BaseModel):
|
|||
class FrontendNode(BaseModel):
|
||||
_format_template: bool = True
|
||||
template: Template
|
||||
"""Template for the frontend node."""
|
||||
description: Optional[str] = None
|
||||
"""Description of the frontend node."""
|
||||
icon: Optional[str] = None
|
||||
"""Icon of the frontend node."""
|
||||
is_composition: Optional[bool] = None
|
||||
"""Whether the frontend node is used for composition."""
|
||||
base_classes: List[str]
|
||||
"""List of base classes for the frontend node."""
|
||||
name: str = ""
|
||||
"""Name of the frontend node."""
|
||||
display_name: Optional[str] = ""
|
||||
"""Display name of the frontend node."""
|
||||
documentation: str = ""
|
||||
"""Documentation of the frontend node."""
|
||||
custom_fields: Optional[Dict] = defaultdict(list)
|
||||
"""Custom fields of the frontend node."""
|
||||
output_types: List[str] = []
|
||||
"""List of output types for the frontend node."""
|
||||
full_path: Optional[str] = None
|
||||
"""Full path of the frontend node."""
|
||||
field_formatters: FieldFormatters = Field(default_factory=FieldFormatters)
|
||||
"""Field formatters for the frontend node."""
|
||||
pinned: bool = False
|
||||
"""Whether the frontend node is pinned."""
|
||||
|
||||
beta: bool = False
|
||||
error: Optional[str] = None
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ from langchain_community.chat_message_histories.mongodb import (
|
|||
DEFAULT_COLLECTION_NAME,
|
||||
DEFAULT_DBNAME,
|
||||
)
|
||||
from langchain_community.chat_message_histories.postgres import DEFAULT_CONNECTION_STRING
|
||||
from langchain_community.chat_message_histories.postgres import (
|
||||
DEFAULT_CONNECTION_STRING,
|
||||
)
|
||||
|
||||
from langflow.template.field.base import TemplateField
|
||||
from langflow.template.frontend_node.base import FrontendNode
|
||||
|
|
@ -13,7 +15,9 @@ from langflow.template.template.base import Template
|
|||
|
||||
|
||||
class MemoryFrontendNode(FrontendNode):
|
||||
#! Needs testing
|
||||
|
||||
pinned: bool = True
|
||||
|
||||
def add_extra_fields(self) -> None:
|
||||
# chat history should have another way to add common field?
|
||||
# prevent adding incorect field in ChatMessageHistory
|
||||
|
|
@ -77,7 +81,9 @@ class MemoryFrontendNode(FrontendNode):
|
|||
field.show = True
|
||||
field.advanced = False
|
||||
field.value = ""
|
||||
field.info = INPUT_KEY_INFO if field.name == "input_key" else OUTPUT_KEY_INFO
|
||||
field.info = (
|
||||
INPUT_KEY_INFO if field.name == "input_key" else OUTPUT_KEY_INFO
|
||||
)
|
||||
|
||||
if field.name == "memory_key":
|
||||
field.value = "chat_history"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue