Merge branch 'two_edges' of https://github.com/langflow-ai/langflow into two_edges
This commit is contained in:
commit
26b7db4ec6
9 changed files with 73 additions and 23 deletions
|
|
@ -4,7 +4,7 @@ from langflow.inputs import MultilineInput, StrInput
|
||||||
from langflow.template import Output
|
from langflow.template import Output
|
||||||
|
|
||||||
|
|
||||||
class TextInput(TextComponent):
|
class TextInputComponent(TextComponent):
|
||||||
display_name = "Text Input"
|
display_name = "Text Input"
|
||||||
description = "Get text inputs from the Playground."
|
description = "Get text inputs from the Playground."
|
||||||
icon = "type"
|
icon = "type"
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
from .ChatInput import ChatInput
|
from .ChatInput import ChatInput
|
||||||
from .TextInput import TextInput
|
from .TextInput import TextInputComponent
|
||||||
|
|
||||||
__all__ = ["ChatInput", "TextInput"]
|
__all__ = ["ChatInput", "TextInputComponent"]
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ from langflow.field_typing import Text
|
||||||
from langflow.template import Input, Output
|
from langflow.template import Input, Output
|
||||||
|
|
||||||
|
|
||||||
class TextOutput(TextComponent):
|
class TextOutputComponent(TextComponent):
|
||||||
display_name = "Text Output"
|
display_name = "Text Output"
|
||||||
description = "Display a text output in the Playground."
|
description = "Display a text output in the Playground."
|
||||||
icon = "type"
|
icon = "type"
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
from .ChatOutput import ChatOutput
|
from .ChatOutput import ChatOutput
|
||||||
from .TextOutput import TextOutput
|
from .TextOutput import TextOutputComponent
|
||||||
|
|
||||||
__all__ = ["ChatOutput", "TextOutput"]
|
__all__ = ["ChatOutput", "TextOutputComponent"]
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,17 @@
|
||||||
import inspect
|
import inspect
|
||||||
from typing import (
|
from typing import AsyncIterator, Awaitable, Callable, ClassVar, Generator, Iterator, List, Optional, Union
|
||||||
TYPE_CHECKING,
|
|
||||||
AsyncIterator,
|
|
||||||
Awaitable,
|
|
||||||
Callable,
|
|
||||||
ClassVar,
|
|
||||||
Generator,
|
|
||||||
Iterator,
|
|
||||||
List,
|
|
||||||
Optional,
|
|
||||||
Union,
|
|
||||||
)
|
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
|
from git import TYPE_CHECKING
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from langflow.inputs.inputs import InputTypes
|
||||||
from langflow.schema.artifact import get_artifact_type, post_process_raw
|
from langflow.schema.artifact import get_artifact_type, post_process_raw
|
||||||
from langflow.schema.data import Data
|
from langflow.schema.data import Data
|
||||||
from langflow.template.field.base import UNDEFINED, Input, Output
|
from langflow.template.field.base import UNDEFINED, Input, Output
|
||||||
|
from langflow.utils.util import is_class_method
|
||||||
|
|
||||||
from .custom_component import CustomComponent
|
from .custom_component import CustomComponent
|
||||||
|
|
||||||
|
|
@ -36,7 +28,6 @@ def recursive_serialize_or_str(obj):
|
||||||
elif isinstance(obj, BaseModel):
|
elif isinstance(obj, BaseModel):
|
||||||
return {k: recursive_serialize_or_str(v) for k, v in obj.model_dump().items()}
|
return {k: recursive_serialize_or_str(v) for k, v in obj.model_dump().items()}
|
||||||
elif isinstance(obj, (AsyncIterator, Generator, Iterator)):
|
elif isinstance(obj, (AsyncIterator, Generator, Iterator)):
|
||||||
# Turn it into something readable that does not
|
|
||||||
# contain memory addresses
|
# contain memory addresses
|
||||||
# without consuming the iterator
|
# without consuming the iterator
|
||||||
# return list(obj) consumes the iterator
|
# return list(obj) consumes the iterator
|
||||||
|
|
@ -49,13 +40,35 @@ def recursive_serialize_or_str(obj):
|
||||||
|
|
||||||
|
|
||||||
class Component(CustomComponent):
|
class Component(CustomComponent):
|
||||||
inputs: Optional[List[Input]] = None
|
inputs: Optional[List[InputTypes]] = None
|
||||||
outputs: Optional[List[Output]] = None
|
outputs: Optional[List[Output]] = None
|
||||||
code_class_base_inheritance: ClassVar[str] = "Component"
|
code_class_base_inheritance: ClassVar[str] = "Component"
|
||||||
_results: dict = {}
|
_results: dict = {}
|
||||||
_arguments: dict = {}
|
_arguments: dict = {}
|
||||||
|
_inputs: dict[str, InputTypes] = {}
|
||||||
|
|
||||||
|
def __init__(self, **data):
|
||||||
|
super().__init__(**data)
|
||||||
|
if self.inputs is not None:
|
||||||
|
self.map_inputs(self.inputs)
|
||||||
|
|
||||||
|
def map_inputs(self, inputs: List[Input]):
|
||||||
|
self.inputs = inputs
|
||||||
|
for input_ in inputs:
|
||||||
|
self._inputs[input_.name] = input_
|
||||||
|
|
||||||
|
def _validate_inputs(self, params: dict):
|
||||||
|
# Params keys are the `name` attribute of the Input objects
|
||||||
|
for key, value in params.items():
|
||||||
|
if key not in self._inputs:
|
||||||
|
raise ValueError(f"Input {key} not found in arguments")
|
||||||
|
input_ = self._inputs[key]
|
||||||
|
# validate_inputs must be a classmethod
|
||||||
|
if hasattr(input_, "validate_value") and is_class_method(func=input_.validate_value, cls=input_):
|
||||||
|
input_.validate_value(value)
|
||||||
|
|
||||||
def set_attributes(self, params: dict):
|
def set_attributes(self, params: dict):
|
||||||
|
self._validate_inputs(params)
|
||||||
for key, value in params.items():
|
for key, value in params.items():
|
||||||
if key in self.__dict__:
|
if key in self.__dict__:
|
||||||
raise ValueError(f"Key {key} already exists in {self.__class__.__name__}")
|
raise ValueError(f"Key {key} already exists in {self.__class__.__name__}")
|
||||||
|
|
@ -149,3 +162,4 @@ class Component(CustomComponent):
|
||||||
return [field.name for field in inputs]
|
return [field.name for field in inputs]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return []
|
return []
|
||||||
|
return []
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,14 @@ from .inputs import (
|
||||||
DropdownInput,
|
DropdownInput,
|
||||||
FileInput,
|
FileInput,
|
||||||
FloatInput,
|
FloatInput,
|
||||||
|
HandleInput,
|
||||||
IntInput,
|
IntInput,
|
||||||
MultilineInput,
|
MultilineInput,
|
||||||
NestedDictInput,
|
NestedDictInput,
|
||||||
PromptInput,
|
PromptInput,
|
||||||
SecretStrInput,
|
SecretStrInput,
|
||||||
StrInput,
|
StrInput,
|
||||||
HandleInput,
|
TextInput,
|
||||||
)
|
)
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
|
@ -26,4 +27,5 @@ __all__ = [
|
||||||
"PromptInput",
|
"PromptInput",
|
||||||
"MultilineInput",
|
"MultilineInput",
|
||||||
"HandleInput",
|
"HandleInput",
|
||||||
|
"TextInput",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
from typing import Callable, Optional, Union
|
from typing import Any, Callable, Optional, Union
|
||||||
|
|
||||||
from pydantic import Field, model_validator
|
from pydantic import Field, field_validator, model_validator
|
||||||
|
|
||||||
from langflow.inputs.validators import StrictBoolean
|
from langflow.inputs.validators import StrictBoolean
|
||||||
|
from langflow.schema.data import Data
|
||||||
|
from langflow.schema.message import Message
|
||||||
|
|
||||||
from .input_mixin import (
|
from .input_mixin import (
|
||||||
BaseInputMixin,
|
BaseInputMixin,
|
||||||
|
|
@ -39,6 +41,30 @@ class StrInput(BaseInputMixin, ListableInputMixin, DatabaseLoadMixin): # noqa:
|
||||||
"""Defines if the field will allow the user to open a text editor. Default is False."""
|
"""Defines if the field will allow the user to open a text editor. Default is False."""
|
||||||
|
|
||||||
|
|
||||||
|
class TextInput(StrInput):
|
||||||
|
input_types: list[str] = ["Data", "Message", "Text"]
|
||||||
|
|
||||||
|
@field_validator("value")
|
||||||
|
@classmethod
|
||||||
|
def validate_value(cls, v: Any, _info):
|
||||||
|
if isinstance(v, str):
|
||||||
|
return v
|
||||||
|
elif isinstance(v, Message):
|
||||||
|
return v.text
|
||||||
|
elif isinstance(v, Data):
|
||||||
|
if v.text_key in v.data:
|
||||||
|
return v.data[v.text_key]
|
||||||
|
else:
|
||||||
|
keys = ", ".join(v.data.keys())
|
||||||
|
input_name = _info.data["name"]
|
||||||
|
raise ValueError(
|
||||||
|
f"The input to '{input_name}' must contain the key '{v.text_key}'."
|
||||||
|
f"You can set `text_key` to one of the following keys: {keys} or set the value using another Component."
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Invalid input type {type(v)}")
|
||||||
|
|
||||||
|
|
||||||
class MultilineInput(BaseInputMixin):
|
class MultilineInput(BaseInputMixin):
|
||||||
field_type: Optional[SerializableFieldTypes] = FieldTypes.TEXT
|
field_type: Optional[SerializableFieldTypes] = FieldTypes.TEXT
|
||||||
multiline: StrictBoolean = True
|
multiline: StrictBoolean = True
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ def _timestamp_to_str(timestamp: datetime) -> str:
|
||||||
class Message(Data):
|
class Message(Data):
|
||||||
model_config = ConfigDict(arbitrary_types_allowed=True)
|
model_config = ConfigDict(arbitrary_types_allowed=True)
|
||||||
# Helper class to deal with image data
|
# Helper class to deal with image data
|
||||||
|
text_key: str = "text"
|
||||||
text: Optional[str | AsyncIterator | Iterator] = Field(default="")
|
text: Optional[str | AsyncIterator | Iterator] = Field(default="")
|
||||||
sender: str
|
sender: str
|
||||||
sender_name: str
|
sender_name: str
|
||||||
|
|
|
||||||
|
|
@ -449,3 +449,10 @@ def update_settings(
|
||||||
if not store:
|
if not store:
|
||||||
logger.debug("Setting store to False")
|
logger.debug("Setting store to False")
|
||||||
settings_service.settings.update_settings(store=False)
|
settings_service.settings.update_settings(store=False)
|
||||||
|
|
||||||
|
|
||||||
|
def is_class_method(func, cls):
|
||||||
|
"""
|
||||||
|
Check if a function is a class method.
|
||||||
|
"""
|
||||||
|
return inspect.ismethod(func) and func.__self__ is cls.__class__
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue