merge playground
This commit is contained in:
commit
aa3ea07c6a
39 changed files with 1850 additions and 1333 deletions
1
.github/workflows/pre-release-langflow.yml
vendored
1
.github/workflows/pre-release-langflow.yml
vendored
|
|
@ -63,6 +63,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
needs: release
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Set up Docker Buildx
|
||||
|
|
|
|||
1155
poetry.lock
generated
1155
poetry.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,6 @@
|
|||
[tool.poetry]
|
||||
name = "langflow"
|
||||
version = "1.0.0a24"
|
||||
version = "1.0.0a26"
|
||||
description = "A Python package with a built-in web application"
|
||||
authors = ["Langflow <contact@langflow.org>"]
|
||||
maintainers = [
|
||||
|
|
@ -81,6 +81,7 @@ chromadb = "^0.4.24"
|
|||
langchain-anthropic = "^0.1.6"
|
||||
langchain-astradb = "^0.1.0"
|
||||
langchain-openai = "^0.1.1"
|
||||
zep-python = { version = "^2.0.0rc5", allow-prereleases = true }
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
types-redis = "^4.6.0.5"
|
||||
|
|
|
|||
|
|
@ -51,10 +51,10 @@ async def try_running_celery_task(vertex, user_id):
|
|||
return vertex
|
||||
|
||||
|
||||
@router.get("/build/{flow_id}/vertices", response_model=VerticesOrderResponse)
|
||||
async def get_vertices(
|
||||
@router.post("/build/{flow_id}/vertices", response_model=VerticesOrderResponse)
|
||||
async def retrieve_vertices_order(
|
||||
flow_id: str,
|
||||
data: Optional[FlowDataRequest] = None,
|
||||
data: Annotated[Optional[FlowDataRequest], Body(embed=True)] = None,
|
||||
stop_component_id: Optional[str] = None,
|
||||
start_component_id: Optional[str] = None,
|
||||
chat_service: "ChatService" = Depends(get_chat_service),
|
||||
|
|
@ -65,6 +65,7 @@ async def get_vertices(
|
|||
|
||||
Args:
|
||||
flow_id (str): The ID of the flow.
|
||||
data (Optional[FlowDataRequest], optional): The flow data. Defaults to None.
|
||||
stop_component_id (str, optional): The ID of the stop component. Defaults to None.
|
||||
start_component_id (str, optional): The ID of the start component. Defaults to None.
|
||||
chat_service (ChatService, optional): The chat service dependency. Defaults to Depends(get_chat_service).
|
||||
|
|
@ -86,7 +87,9 @@ async def get_vertices(
|
|||
flow_id=flow_id, session=session, chat_service=chat_service, graph=graph
|
||||
)
|
||||
else:
|
||||
graph = await build_and_cache_graph_from_data(flow_id=flow_id, data=data, chat_service=chat_service)
|
||||
graph = await build_and_cache_graph_from_data(
|
||||
flow_id=flow_id, graph_data=data.model_dump(), chat_service=chat_service
|
||||
)
|
||||
if stop_component_id or start_component_id:
|
||||
try:
|
||||
first_layer = graph.sort_vertices(stop_component_id, start_component_id)
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ async def simplified_run_flow(
|
|||
graph_data = flow.data
|
||||
|
||||
graph_data = process_tweaks(graph_data, input_request.tweaks or {}, stream=stream)
|
||||
graph = Graph.from_payload(graph_data, flow_id=flow_id, user_id=api_key_user.id)
|
||||
graph = Graph.from_payload(graph_data, flow_id=flow_id, user_id=str(api_key_user.id))
|
||||
inputs = [
|
||||
InputValueRequest(components=[], input_value=input_request.input_value, type=input_request.input_type)
|
||||
]
|
||||
|
|
|
|||
0
src/backend/base/langflow/base/memory/__init__.py
Normal file
0
src/backend/base/langflow/base/memory/__init__.py
Normal file
51
src/backend/base/langflow/base/memory/memory.py
Normal file
51
src/backend/base/langflow/base/memory/memory.py
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
from typing import Optional
|
||||
|
||||
from langflow.field_typing import Text
|
||||
from langflow.helpers.record import records_to_text
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langflow.schema.schema import Record
|
||||
|
||||
|
||||
class BaseMemoryComponent(CustomComponent):
|
||||
display_name = "Chat Memory"
|
||||
description = "Retrieves stored chat messages given a specific Session ID."
|
||||
beta: bool = True
|
||||
icon = "history"
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"sender": {
|
||||
"options": ["Machine", "User", "Machine and User"],
|
||||
"display_name": "Sender Type",
|
||||
},
|
||||
"sender_name": {"display_name": "Sender Name", "advanced": True},
|
||||
"n_messages": {
|
||||
"display_name": "Number of Messages",
|
||||
"info": "Number of messages to retrieve.",
|
||||
},
|
||||
"session_id": {
|
||||
"display_name": "Session ID",
|
||||
"info": "Session ID of the chat history.",
|
||||
"input_types": ["Text"],
|
||||
},
|
||||
"order": {
|
||||
"options": ["Ascending", "Descending"],
|
||||
"display_name": "Order",
|
||||
"info": "Order of the messages.",
|
||||
"advanced": True,
|
||||
},
|
||||
"record_template": {
|
||||
"display_name": "Record Template",
|
||||
"multiline": True,
|
||||
"info": "Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.",
|
||||
"advanced": True,
|
||||
},
|
||||
}
|
||||
|
||||
def get_messages(self, **kwargs) -> list[Record]:
|
||||
raise NotImplementedError
|
||||
|
||||
def add_message(
|
||||
self, sender: str, sender_name: str, text: str, session_id: str, metadata: Optional[dict] = None, **kwargs
|
||||
):
|
||||
raise NotImplementedError
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
from typing import Optional
|
||||
|
||||
from langflow.base.memory.memory import BaseMemoryComponent
|
||||
from langflow.field_typing import Text
|
||||
from langflow.helpers.record import records_to_text
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langflow.memory import get_messages
|
||||
from langflow.schema.schema import Record
|
||||
|
||||
|
||||
class MemoryComponent(CustomComponent):
|
||||
class MemoryComponent(BaseMemoryComponent):
|
||||
display_name = "Chat Memory"
|
||||
description = "Retrieves stored chat messages given a specific Session ID."
|
||||
beta: bool = True
|
||||
|
|
@ -42,6 +43,24 @@ class MemoryComponent(CustomComponent):
|
|||
},
|
||||
}
|
||||
|
||||
def get_messages(self, **kwargs) -> list[Record]:
|
||||
# Validate kwargs by checking if it contains the correct keys
|
||||
if "sender" not in kwargs:
|
||||
kwargs["sender"] = None
|
||||
if "sender_name" not in kwargs:
|
||||
kwargs["sender_name"] = None
|
||||
if "session_id" not in kwargs:
|
||||
kwargs["session_id"] = None
|
||||
if "limit" not in kwargs:
|
||||
kwargs["limit"] = 5
|
||||
if "order" not in kwargs:
|
||||
kwargs["order"] = "Descending"
|
||||
|
||||
kwargs["order"] = "DESC" if kwargs["order"] == "Descending" else "ASC"
|
||||
if kwargs["sender"] == "Machine and User":
|
||||
kwargs["sender"] = None
|
||||
return get_messages(**kwargs)
|
||||
|
||||
def build(
|
||||
self,
|
||||
sender: Optional[str] = "Machine and User",
|
||||
|
|
@ -51,10 +70,7 @@ class MemoryComponent(CustomComponent):
|
|||
order: Optional[str] = "Descending",
|
||||
record_template: Optional[str] = "{sender_name}: {text}",
|
||||
) -> Text:
|
||||
order = "DESC" if order == "Descending" else "ASC"
|
||||
if sender == "Machine and User":
|
||||
sender = None
|
||||
messages = get_messages(
|
||||
messages = self.get_messages(
|
||||
sender=sender,
|
||||
sender_name=sender_name,
|
||||
session_id=session_id,
|
||||
|
|
|
|||
48
src/backend/base/langflow/components/inputs/FileInput.py
Normal file
48
src/backend/base/langflow/components/inputs/FileInput.py
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
from pathlib import Path
|
||||
from typing import Any, Dict
|
||||
|
||||
from langflow.base.data.utils import TEXT_FILE_TYPES, parse_text_file_to_record
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
class FileInput(CustomComponent):
|
||||
display_name = "File Input"
|
||||
description = "A generic file input."
|
||||
icon = "file-text"
|
||||
|
||||
def build_config(self) -> Dict[str, Any]:
|
||||
return {
|
||||
"path": {
|
||||
"display_name": "Path",
|
||||
"field_type": "file",
|
||||
"file_types": TEXT_FILE_TYPES,
|
||||
"info": f"Supported file types: {', '.join(TEXT_FILE_TYPES)}",
|
||||
},
|
||||
"silent_errors": {
|
||||
"display_name": "Silent Errors",
|
||||
"advanced": True,
|
||||
"info": "If true, errors will not raise an exception.",
|
||||
},
|
||||
}
|
||||
|
||||
def load_file(self, path: str, silent_errors: bool = False) -> Record:
|
||||
resolved_path = self.resolve_path(path)
|
||||
path_obj = Path(resolved_path)
|
||||
extension = path_obj.suffix[1:].lower()
|
||||
if extension == "doc":
|
||||
raise ValueError("doc files are not supported. Please save as .docx")
|
||||
if extension not in TEXT_FILE_TYPES:
|
||||
raise ValueError(f"Unsupported file type: {extension}")
|
||||
record = parse_text_file_to_record(resolved_path, silent_errors)
|
||||
self.status = record if record else "No data"
|
||||
return record or Record()
|
||||
|
||||
def build(
|
||||
self,
|
||||
path: str,
|
||||
silent_errors: bool = False,
|
||||
) -> Record:
|
||||
record = self.load_file(path, silent_errors)
|
||||
self.status = record
|
||||
return record
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
from typing import Optional, cast
|
||||
|
||||
from langchain_community.chat_message_histories.zep import SearchScope, SearchType, ZepChatMessageHistory
|
||||
|
||||
from langflow.base.memory.memory import BaseMemoryComponent
|
||||
from langflow.field_typing import Text
|
||||
from langflow.schema.schema import Record
|
||||
|
||||
|
||||
class ZepMessageReaderComponent(BaseMemoryComponent):
|
||||
display_name = "Zep Message Reader"
|
||||
description = "Retrieves stored chat messages from Zep."
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"session_id": {
|
||||
"display_name": "Session ID",
|
||||
"info": "Session ID of the chat history.",
|
||||
"input_types": ["Text"],
|
||||
},
|
||||
"url": {
|
||||
"display_name": "Zep URL",
|
||||
"info": "URL of the Zep instance.",
|
||||
"input_types": ["Text"],
|
||||
},
|
||||
"api_key": {
|
||||
"display_name": "Zep API Key",
|
||||
"info": "API Key for the Zep instance.",
|
||||
"password": True,
|
||||
},
|
||||
"query": {
|
||||
"display_name": "Query",
|
||||
"info": "Query to search for in the chat history.",
|
||||
},
|
||||
"metadata": {
|
||||
"display_name": "Metadata",
|
||||
"info": "Optional metadata to attach to the message.",
|
||||
"advanced": True,
|
||||
},
|
||||
"search_scope": {
|
||||
"options": ["Messages", "Summary"],
|
||||
"display_name": "Search Scope",
|
||||
"info": "Scope of the search.",
|
||||
"advanced": True,
|
||||
},
|
||||
"search_type": {
|
||||
"options": ["Similarity", "MMR"],
|
||||
"display_name": "Search Type",
|
||||
"info": "Type of search.",
|
||||
"advanced": True,
|
||||
},
|
||||
"limit": {
|
||||
"display_name": "Limit",
|
||||
"info": "Limit of search results.",
|
||||
"advanced": True,
|
||||
},
|
||||
}
|
||||
|
||||
def get_messages(self, **kwargs) -> list[Record]:
|
||||
"""
|
||||
Retrieves messages from the ZepChatMessageHistory memory.
|
||||
|
||||
If a query is provided, the search method is used to search for messages in the memory, otherwise all messages are returned.
|
||||
|
||||
Args:
|
||||
memory (ZepChatMessageHistory): The ZepChatMessageHistory instance to retrieve messages from.
|
||||
query (str, optional): The query string to search for messages. Defaults to None.
|
||||
metadata (dict, optional): Additional metadata to filter the search results. Defaults to None.
|
||||
search_scope (str, optional): The scope of the search. Can be 'messages' or 'summary'. Defaults to 'messages'.
|
||||
search_type (str, optional): The type of search. Can be 'similarity' or 'exact'. Defaults to 'similarity'.
|
||||
limit (int, optional): The maximum number of search results to return. Defaults to None.
|
||||
|
||||
Returns:
|
||||
list[Record]: A list of Record objects representing the search results.
|
||||
"""
|
||||
memory: ZepChatMessageHistory = cast(ZepChatMessageHistory, kwargs.get("memory"))
|
||||
if not memory:
|
||||
raise ValueError("ZepChatMessageHistory instance is required.")
|
||||
query = kwargs.get("query")
|
||||
search_scope = kwargs.get("search_scope", SearchScope.messages).lower()
|
||||
search_type = kwargs.get("search_type", SearchType.similarity).lower()
|
||||
limit = kwargs.get("limit")
|
||||
|
||||
if query:
|
||||
memory_search_results = memory.search(
|
||||
query,
|
||||
search_scope=search_scope,
|
||||
search_type=search_type,
|
||||
limit=limit,
|
||||
)
|
||||
# Get the messages from the search results if the search scope is messages
|
||||
result_dicts = []
|
||||
for result in memory_search_results:
|
||||
result_dict = {}
|
||||
if search_scope == SearchScope.messages:
|
||||
result_dict["text"] = result.message
|
||||
else:
|
||||
result_dict["text"] = result.summary
|
||||
result_dict["metadata"] = result.metadata
|
||||
result_dict["score"] = result.score
|
||||
result_dicts.append(result_dict)
|
||||
results = [Record(data=result_dict) for result_dict in result_dicts]
|
||||
else:
|
||||
messages = memory.messages
|
||||
results = [Record.from_lc_message(message) for message in messages]
|
||||
return results
|
||||
|
||||
def build(
|
||||
self,
|
||||
session_id: Text,
|
||||
url: Optional[Text] = None,
|
||||
api_key: Optional[Text] = None,
|
||||
query: Optional[Text] = None,
|
||||
search_scope: SearchScope = SearchScope.messages,
|
||||
search_type: SearchType = SearchType.similarity,
|
||||
limit: Optional[int] = None,
|
||||
) -> list[Record]:
|
||||
try:
|
||||
from zep_python import ZepClient
|
||||
from zep_python.langchain import ZepChatMessageHistory
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"Could not import zep-python package. " "Please install it with `pip install zep-python`."
|
||||
)
|
||||
if url == "":
|
||||
url = None
|
||||
zep_client = ZepClient(api_url=url, api_key=api_key)
|
||||
memory = ZepChatMessageHistory(session_id=session_id, zep_client=zep_client)
|
||||
records = self.get_messages(
|
||||
memory=memory,
|
||||
query=query,
|
||||
search_scope=search_scope,
|
||||
search_type=search_type,
|
||||
limit=limit,
|
||||
)
|
||||
self.status = records
|
||||
return records
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
from typing import Optional, TYPE_CHECKING
|
||||
|
||||
|
||||
from langflow.base.memory.memory import BaseMemoryComponent
|
||||
from langflow.field_typing import Text
|
||||
from langflow.schema.schema import Record
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from zep_python.langchain import ZepChatMessageHistory
|
||||
|
||||
|
||||
class ZepMessageWriterComponent(BaseMemoryComponent):
|
||||
display_name = "Zep Message Writer"
|
||||
description = "Writes a message to Zep."
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"session_id": {
|
||||
"display_name": "Session ID",
|
||||
"info": "Session ID of the chat history.",
|
||||
"input_types": ["Text"],
|
||||
},
|
||||
"url": {
|
||||
"display_name": "Zep URL",
|
||||
"info": "URL of the Zep instance.",
|
||||
"input_types": ["Text"],
|
||||
},
|
||||
"api_key": {
|
||||
"display_name": "Zep API Key",
|
||||
"info": "API Key for the Zep instance.",
|
||||
"password": True,
|
||||
},
|
||||
"limit": {
|
||||
"display_name": "Limit",
|
||||
"info": "Limit of search results.",
|
||||
"advanced": True,
|
||||
},
|
||||
"input_value": {
|
||||
"display_name": "Input Record",
|
||||
"info": "Record to write to Zep.",
|
||||
},
|
||||
}
|
||||
|
||||
def add_message(
|
||||
self, sender: Text, sender_name: Text, text: Text, session_id: Text, metadata: dict | None = None, **kwargs
|
||||
):
|
||||
"""
|
||||
Adds a message to the ZepChatMessageHistory memory.
|
||||
|
||||
Args:
|
||||
sender (Text): The type of the message sender. Valid values are "Machine" or "User".
|
||||
sender_name (Text): The name of the message sender.
|
||||
text (Text): The content of the message.
|
||||
session_id (Text): The session ID associated with the message.
|
||||
metadata (dict | None, optional): Additional metadata for the message. Defaults to None.
|
||||
**kwargs: Additional keyword arguments.
|
||||
|
||||
Raises:
|
||||
ValueError: If the ZepChatMessageHistory instance is not provided.
|
||||
|
||||
"""
|
||||
memory: ZepChatMessageHistory | None = kwargs.pop("memory", None)
|
||||
if memory is None:
|
||||
raise ValueError("ZepChatMessageHistory instance is required.")
|
||||
if metadata is None:
|
||||
metadata = {}
|
||||
metadata["sender_name"] = sender_name
|
||||
metadata.update(kwargs)
|
||||
if sender == "Machine":
|
||||
memory.add_ai_message(text, metadata=metadata)
|
||||
elif sender == "User":
|
||||
memory.add_user_message(text, metadata=metadata)
|
||||
else:
|
||||
raise ValueError(f"Invalid sender type: {sender}")
|
||||
|
||||
def build(
|
||||
self,
|
||||
input_value: Record,
|
||||
session_id: Text,
|
||||
url: Optional[Text] = None,
|
||||
api_key: Optional[Text] = None,
|
||||
) -> Record:
|
||||
try:
|
||||
from zep_python import ZepClient
|
||||
from zep_python.langchain import ZepChatMessageHistory
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"Could not import zep-python package. " "Please install it with `pip install zep-python`."
|
||||
)
|
||||
if url == "":
|
||||
url = None
|
||||
zep_client = ZepClient(api_url=url, api_key=api_key)
|
||||
memory = ZepChatMessageHistory(session_id=session_id, zep_client=zep_client)
|
||||
self.add_message(**input_value.data, memory=memory)
|
||||
self.status = f"Added message to Zep memory for session {session_id}"
|
||||
return input_value
|
||||
|
|
@ -61,10 +61,10 @@ class WeaviateSearchVectorStore(WeaviateVectorStoreComponent, LCVectorStoreCompo
|
|||
input_value: Text,
|
||||
search_type: str,
|
||||
url: str,
|
||||
index_name: str,
|
||||
number_of_results: int = 4,
|
||||
search_by_text: bool = False,
|
||||
api_key: Optional[str] = None,
|
||||
index_name: Optional[str] = None,
|
||||
text_key: str = "text",
|
||||
embedding: Optional[Embeddings] = None,
|
||||
attributes: Optional[list] = None,
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import weaviate # type: ignore
|
|||
from langchain.embeddings.base import Embeddings
|
||||
from langchain.schema import BaseRetriever
|
||||
from langchain_community.vectorstores import VectorStore, Weaviate
|
||||
from langchain_core.documents import Document
|
||||
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langflow.schema.schema import Record
|
||||
|
|
@ -50,9 +51,9 @@ class WeaviateVectorStoreComponent(CustomComponent):
|
|||
def build(
|
||||
self,
|
||||
url: str,
|
||||
index_name: str,
|
||||
search_by_text: bool = False,
|
||||
api_key: Optional[str] = None,
|
||||
index_name: Optional[str] = None,
|
||||
text_key: str = "text",
|
||||
embedding: Optional[Embeddings] = None,
|
||||
inputs: Optional[Record] = None,
|
||||
|
|
@ -78,11 +79,13 @@ class WeaviateVectorStoreComponent(CustomComponent):
|
|||
return pascal_case_word
|
||||
|
||||
index_name = _to_pascal_case(index_name) if index_name else None
|
||||
documents = []
|
||||
if not index_name:
|
||||
raise ValueError("Index name is required")
|
||||
documents: list[Document] = []
|
||||
for _input in inputs or []:
|
||||
if isinstance(_input, Record):
|
||||
documents.append(_input.to_lc_document())
|
||||
else:
|
||||
elif isinstance(_input, Document):
|
||||
documents.append(_input)
|
||||
|
||||
if documents and embedding is not None:
|
||||
|
|
|
|||
|
|
@ -377,7 +377,7 @@
|
|||
"list": false,
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"value": "from typing import Optional\n\nfrom langflow.field_typing import Text\nfrom langflow.helpers.record import records_to_text\nfrom langflow.interface.custom.custom_component import CustomComponent\nfrom langflow.memory import get_messages\n\n\nclass MemoryComponent(CustomComponent):\n display_name = \"Chat Memory\"\n description = \"Retrieves stored chat messages given a specific Session ID.\"\n beta: bool = True\n icon = \"history\"\n\n def build_config(self):\n return {\n \"sender\": {\n \"options\": [\"Machine\", \"User\", \"Machine and User\"],\n \"display_name\": \"Sender Type\",\n },\n \"sender_name\": {\"display_name\": \"Sender Name\", \"advanced\": True},\n \"n_messages\": {\n \"display_name\": \"Number of Messages\",\n \"info\": \"Number of messages to retrieve.\",\n },\n \"session_id\": {\n \"display_name\": \"Session ID\",\n \"info\": \"Session ID of the chat history.\",\n \"input_types\": [\"Text\"],\n },\n \"order\": {\n \"options\": [\"Ascending\", \"Descending\"],\n \"display_name\": \"Order\",\n \"info\": \"Order of the messages.\",\n \"advanced\": True,\n },\n \"record_template\": {\n \"display_name\": \"Record Template\",\n \"multiline\": True,\n \"info\": \"Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n sender: Optional[str] = \"Machine and User\",\n sender_name: Optional[str] = None,\n session_id: Optional[str] = None,\n n_messages: int = 5,\n order: Optional[str] = \"Descending\",\n record_template: Optional[str] = \"{sender_name}: {text}\",\n ) -> Text:\n order = \"DESC\" if order == \"Descending\" else \"ASC\"\n if sender == \"Machine and User\":\n sender = None\n messages = get_messages(\n sender=sender,\n sender_name=sender_name,\n session_id=session_id,\n limit=n_messages,\n order=order,\n )\n messages_str = records_to_text(template=record_template or \"\", records=messages)\n self.status = messages_str\n return messages_str\n",
|
||||
"value": "from typing import Optional\n\nfrom langflow.base.memory.memory import BaseMemoryComponent\nfrom langflow.field_typing import Text\nfrom langflow.helpers.record import records_to_text\nfrom langflow.memory import get_messages\nfrom langflow.schema.schema import Record\n\n\nclass MemoryComponent(BaseMemoryComponent):\n display_name = \"Chat Memory\"\n description = \"Retrieves stored chat messages given a specific Session ID.\"\n beta: bool = True\n icon = \"history\"\n\n def build_config(self):\n return {\n \"sender\": {\n \"options\": [\"Machine\", \"User\", \"Machine and User\"],\n \"display_name\": \"Sender Type\",\n },\n \"sender_name\": {\"display_name\": \"Sender Name\", \"advanced\": True},\n \"n_messages\": {\n \"display_name\": \"Number of Messages\",\n \"info\": \"Number of messages to retrieve.\",\n },\n \"session_id\": {\n \"display_name\": \"Session ID\",\n \"info\": \"Session ID of the chat history.\",\n \"input_types\": [\"Text\"],\n },\n \"order\": {\n \"options\": [\"Ascending\", \"Descending\"],\n \"display_name\": \"Order\",\n \"info\": \"Order of the messages.\",\n \"advanced\": True,\n },\n \"record_template\": {\n \"display_name\": \"Record Template\",\n \"multiline\": True,\n \"info\": \"Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.\",\n \"advanced\": True,\n },\n }\n\n def get_messages(self, **kwargs) -> list[Record]:\n # Validate kwargs by checking if it contains the correct keys\n if \"sender\" not in kwargs:\n kwargs[\"sender\"] = None\n if \"sender_name\" not in kwargs:\n kwargs[\"sender_name\"] = None\n if \"session_id\" not in kwargs:\n kwargs[\"session_id\"] = None\n if \"limit\" not in kwargs:\n kwargs[\"limit\"] = 5\n if \"order\" not in kwargs:\n kwargs[\"order\"] = \"Descending\"\n\n kwargs[\"order\"] = \"DESC\" if kwargs[\"order\"] == \"Descending\" else \"ASC\"\n if kwargs[\"sender\"] == \"Machine and User\":\n kwargs[\"sender\"] = None\n return get_messages(**kwargs)\n\n def build(\n self,\n sender: Optional[str] = \"Machine and User\",\n sender_name: Optional[str] = None,\n session_id: Optional[str] = None,\n n_messages: int = 5,\n order: Optional[str] = \"Descending\",\n record_template: Optional[str] = \"{sender_name}: {text}\",\n ) -> Text:\n messages = self.get_messages(\n sender=sender,\n sender_name=sender_name,\n session_id=session_id,\n limit=n_messages,\n order=order,\n )\n messages_str = records_to_text(template=record_template or \"\", records=messages)\n self.status = messages_str\n return messages_str\n",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
|
|
|
|||
|
|
@ -227,34 +227,11 @@ def initialize_qdrant(class_object: Type[Qdrant], params: dict):
|
|||
return class_object.from_documents(**params)
|
||||
|
||||
|
||||
def initialize_elasticsearch(class_object: Type[ElasticsearchStore], params: dict):
|
||||
"""Initialize elastic and return the class object"""
|
||||
if "index_name" not in params:
|
||||
raise ValueError("Elasticsearch Index must be provided in the params")
|
||||
if "es_url" not in params:
|
||||
raise ValueError("Elasticsearch URL must be provided in the params")
|
||||
if not docs_in_params(params):
|
||||
existing_index_params = {
|
||||
"embedding": params.pop("embedding"),
|
||||
}
|
||||
if "index_name" in params:
|
||||
existing_index_params["index_name"] = params.pop("index_name")
|
||||
if "es_url" in params:
|
||||
existing_index_params["es_url"] = params.pop("es_url")
|
||||
|
||||
return class_object.from_existing_index(**existing_index_params)
|
||||
# If there are docs in the params, create a new index
|
||||
if "texts" in params:
|
||||
params["documents"] = params.pop("texts")
|
||||
return class_object.from_documents(**params)
|
||||
|
||||
|
||||
vecstore_initializer: Dict[str, Callable[[Type[Any], dict], Any]] = {
|
||||
"Pinecone": initialize_pinecone,
|
||||
"Chroma": initialize_chroma,
|
||||
"Qdrant": initialize_qdrant,
|
||||
"Weaviate": initialize_weaviate,
|
||||
"ElasticsearchStore": initialize_elasticsearch,
|
||||
"FAISS": initialize_faiss,
|
||||
"SupabaseVectorStore": initialize_supabase,
|
||||
"MongoDBAtlasVectorSearch": initialize_mongodb,
|
||||
|
|
|
|||
|
|
@ -269,16 +269,19 @@ def process_tweaks(
|
|||
:return: The modified graph_data dictionary.
|
||||
:raises ValueError: If the input is not in the expected format.
|
||||
"""
|
||||
tweaks_dict = {}
|
||||
if not isinstance(tweaks, dict):
|
||||
tweaks = tweaks.model_dump()
|
||||
if "stream" not in tweaks:
|
||||
tweaks["stream"] = stream
|
||||
nodes = validate_input(graph_data, tweaks)
|
||||
tweaks_dict = tweaks.model_dump()
|
||||
else:
|
||||
tweaks_dict = tweaks
|
||||
if "stream" not in tweaks_dict:
|
||||
tweaks_dict["stream"] = stream
|
||||
nodes = validate_input(graph_data, tweaks_dict)
|
||||
nodes_map = {node.get("id"): node for node in nodes}
|
||||
nodes_display_name_map = {node.get("data", {}).get("node", {}).get("display_name"): node for node in nodes}
|
||||
|
||||
all_nodes_tweaks = {}
|
||||
for key, value in tweaks.items():
|
||||
for key, value in tweaks_dict.items():
|
||||
if isinstance(value, dict):
|
||||
if node := nodes_map.get(key):
|
||||
apply_tweaks(node, value)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
import copy
|
||||
from typing import Literal, Optional
|
||||
from typing import Literal, Optional, cast
|
||||
|
||||
from langchain_core.documents import Document
|
||||
from langchain_core.messages import BaseMessage
|
||||
from langchain_core.messages import AIMessage, BaseMessage, HumanMessage
|
||||
from pydantic import BaseModel, model_validator
|
||||
from langchain_core.messages import HumanMessage, AIMessage, BaseMessage
|
||||
|
||||
|
||||
class Record(BaseModel):
|
||||
|
|
@ -67,8 +66,8 @@ class Record(BaseModel):
|
|||
Returns:
|
||||
Record: The converted Record.
|
||||
"""
|
||||
data = {"text": message.content}
|
||||
data["metadata"] = message.to_json()
|
||||
data: dict = {"text": message.content}
|
||||
data["metadata"] = cast(dict, message.to_json())
|
||||
return cls(data=data, text_key="text")
|
||||
|
||||
def __add__(self, other: "Record") -> "Record":
|
||||
|
|
|
|||
|
|
@ -49,6 +49,27 @@ async def user_data_context(store_service: "StoreService", api_key: Optional[str
|
|||
user_data_var.set(None)
|
||||
|
||||
|
||||
def get_id_from_search_string(search_string: str) -> Optional[str]:
|
||||
"""
|
||||
Extracts the ID from a search string.
|
||||
|
||||
Args:
|
||||
search_string (str): The search string to extract the ID from.
|
||||
|
||||
Returns:
|
||||
Optional[str]: The extracted ID, or None if no ID is found.
|
||||
"""
|
||||
possible_id = search_string
|
||||
if "www.langflow.store/store/" in search_string:
|
||||
possible_id = search_string.split("/")[-1]
|
||||
|
||||
try:
|
||||
possible_id = UUID(search_string)
|
||||
except ValueError:
|
||||
possible_id = None
|
||||
return possible_id
|
||||
|
||||
|
||||
class StoreService(Service):
|
||||
"""This is a service that integrates langflow with the store which
|
||||
is a Directus instance. It allows to search, get and post components to
|
||||
|
|
@ -183,7 +204,10 @@ class StoreService(Service):
|
|||
):
|
||||
filter_conditions = []
|
||||
|
||||
if search is not None:
|
||||
if component_id is None:
|
||||
component_id = get_id_from_search_string(search) if search else None
|
||||
|
||||
if search is not None and component_id is None:
|
||||
search_conditions = self.build_search_filter_conditions(search)
|
||||
filter_conditions.append(search_conditions)
|
||||
|
||||
|
|
|
|||
215
src/backend/base/poetry.lock
generated
215
src/backend/base/poetry.lock
generated
|
|
@ -624,13 +624,13 @@ gmpy2 = ["gmpy2"]
|
|||
|
||||
[[package]]
|
||||
name = "emoji"
|
||||
version = "2.11.0"
|
||||
version = "2.11.1"
|
||||
description = "Emoji for Python"
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
|
||||
files = [
|
||||
{file = "emoji-2.11.0-py2.py3-none-any.whl", hash = "sha256:63fc9107f06c6c2e48e5078ce9575cef98518f5ac09474f6148a43e989989582"},
|
||||
{file = "emoji-2.11.0.tar.gz", hash = "sha256:772eaa30f4e0b1ce95148a092df4c7dc97644532c03225326b0fd05e8a9f72a3"},
|
||||
{file = "emoji-2.11.1-py2.py3-none-any.whl", hash = "sha256:b7ba25299bbf520cc8727848ae66b986da32aee27dc2887eaea2bff07226ce49"},
|
||||
{file = "emoji-2.11.1.tar.gz", hash = "sha256:062ff0b3154b6219143f8b9f4b3e5c64c35bc2b146e6e2349ab5f29e218ce1ee"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
|
|
@ -638,13 +638,13 @@ dev = ["coverage", "coveralls", "pytest"]
|
|||
|
||||
[[package]]
|
||||
name = "exceptiongroup"
|
||||
version = "1.2.0"
|
||||
version = "1.2.1"
|
||||
description = "Backport of PEP 654 (exception groups)"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"},
|
||||
{file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"},
|
||||
{file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"},
|
||||
{file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
|
|
@ -652,13 +652,13 @@ test = ["pytest (>=6)"]
|
|||
|
||||
[[package]]
|
||||
name = "fastapi"
|
||||
version = "0.110.1"
|
||||
version = "0.110.2"
|
||||
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "fastapi-0.110.1-py3-none-any.whl", hash = "sha256:5df913203c482f820d31f48e635e022f8cbfe7350e4830ef05a3163925b1addc"},
|
||||
{file = "fastapi-0.110.1.tar.gz", hash = "sha256:6feac43ec359dfe4f45b2c18ec8c94edb8dc2dfc461d417d9e626590c071baad"},
|
||||
{file = "fastapi-0.110.2-py3-none-any.whl", hash = "sha256:239403f2c0a3dda07a9420f95157a7f014ddb2b770acdbc984f9bdf3ead7afdb"},
|
||||
{file = "fastapi-0.110.2.tar.gz", hash = "sha256:b53d673652da3b65e8cd787ad214ec0fe303cad00d2b529b86ce7db13f17518d"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -1064,19 +1064,19 @@ text-helpers = ["chardet (>=5.1.0,<6.0.0)"]
|
|||
|
||||
[[package]]
|
||||
name = "langchain-community"
|
||||
version = "0.0.33"
|
||||
version = "0.0.34"
|
||||
description = "Community contributed LangChain integrations."
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.8.1"
|
||||
files = [
|
||||
{file = "langchain_community-0.0.33-py3-none-any.whl", hash = "sha256:830f0d5f4ff9638b99ca01820c26abfa4b65fa705ef89b5ce55ac9aa3a7d83af"},
|
||||
{file = "langchain_community-0.0.33.tar.gz", hash = "sha256:bb56dbc1ef11ca09f258468e11368781adda9219e144073e30cda69496d342b2"},
|
||||
{file = "langchain_community-0.0.34-py3-none-any.whl", hash = "sha256:bc13b21a44bbfca01bff8b35c10a26d71485b57c1d284f499b577ba6e1a5d84a"},
|
||||
{file = "langchain_community-0.0.34.tar.gz", hash = "sha256:96e9a807d9b4777820df5a970996f6bf3ad5632137bf0f4d863bd832bdeb2b0f"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
aiohttp = ">=3.8.3,<4.0.0"
|
||||
dataclasses-json = ">=0.5.7,<0.7"
|
||||
langchain-core = ">=0.1.43,<0.2.0"
|
||||
langchain-core = ">=0.1.45,<0.2.0"
|
||||
langsmith = ">=0.1.0,<0.2.0"
|
||||
numpy = ">=1,<2"
|
||||
PyYAML = ">=5.3"
|
||||
|
|
@ -1090,13 +1090,13 @@ extended-testing = ["aiosqlite (>=0.19.0,<0.20.0)", "aleph-alpha-client (>=2.15.
|
|||
|
||||
[[package]]
|
||||
name = "langchain-core"
|
||||
version = "0.1.44"
|
||||
version = "0.1.45"
|
||||
description = "Building applications with LLMs through composability"
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.8.1"
|
||||
files = [
|
||||
{file = "langchain_core-0.1.44-py3-none-any.whl", hash = "sha256:d8772dccef95fc97bfa2dcd19412e620ebe14def1f0e218374971f6e30a46a49"},
|
||||
{file = "langchain_core-0.1.44.tar.gz", hash = "sha256:e313975d9ae2926342e6f2ad760338d31f18b1223e9b8b4dc408daeeade46a83"},
|
||||
{file = "langchain_core-0.1.45-py3-none-any.whl", hash = "sha256:91eff20de0bcf5f025e1d8c4582cb597a9c17527965eb03b314486e7c834e7df"},
|
||||
{file = "langchain_core-0.1.45.tar.gz", hash = "sha256:526532c1af279a9e2debe7a4e143ba6e980cf90b5ab2e0991c2230ee04c693e2"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -1162,13 +1162,13 @@ types-requests = ">=2.31.0.2,<3.0.0.0"
|
|||
|
||||
[[package]]
|
||||
name = "langsmith"
|
||||
version = "0.1.48"
|
||||
version = "0.1.50"
|
||||
description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform."
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.8.1"
|
||||
files = [
|
||||
{file = "langsmith-0.1.48-py3-none-any.whl", hash = "sha256:2f8967e2aaaed8881efe6f346590681243b315af8ba8a037d969c299d42071d3"},
|
||||
{file = "langsmith-0.1.48.tar.gz", hash = "sha256:9cd21cd0928123b2bd2363f03515cb1f6a833d9a9f00420240d5132861d15fcc"},
|
||||
{file = "langsmith-0.1.50-py3-none-any.whl", hash = "sha256:a81e9809fcaa277bfb314d729e58116554f186d1478fcfdf553b1c2ccce54b85"},
|
||||
{file = "langsmith-0.1.50.tar.gz", hash = "sha256:9fd22df8c689c044058536ea5af66f5302067e7551b60d7a335fede8d479572b"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -1943,18 +1943,19 @@ xmp = ["defusedxml"]
|
|||
|
||||
[[package]]
|
||||
name = "platformdirs"
|
||||
version = "4.2.0"
|
||||
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
||||
version = "4.2.1"
|
||||
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"},
|
||||
{file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"},
|
||||
{file = "platformdirs-4.2.1-py3-none-any.whl", hash = "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1"},
|
||||
{file = "platformdirs-4.2.1.tar.gz", hash = "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"]
|
||||
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"]
|
||||
type = ["mypy (>=1.8)"]
|
||||
|
||||
[[package]]
|
||||
name = "pyasn1"
|
||||
|
|
@ -1980,18 +1981,18 @@ files = [
|
|||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "2.7.0"
|
||||
version = "2.7.1"
|
||||
description = "Data validation using Python type hints"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pydantic-2.7.0-py3-none-any.whl", hash = "sha256:9dee74a271705f14f9a1567671d144a851c675b072736f0a7b2608fd9e495352"},
|
||||
{file = "pydantic-2.7.0.tar.gz", hash = "sha256:b5ecdd42262ca2462e2624793551e80911a1e989f462910bb81aef974b4bb383"},
|
||||
{file = "pydantic-2.7.1-py3-none-any.whl", hash = "sha256:e029badca45266732a9a79898a15ae2e8b14840b1eabbb25844be28f0b33f3d5"},
|
||||
{file = "pydantic-2.7.1.tar.gz", hash = "sha256:e9dbb5eada8abe4d9ae5f46b9939aead650cd2b68f249bb3a8139dbe125803cc"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
annotated-types = ">=0.4.0"
|
||||
pydantic-core = "2.18.1"
|
||||
pydantic-core = "2.18.2"
|
||||
typing-extensions = ">=4.6.1"
|
||||
|
||||
[package.extras]
|
||||
|
|
@ -1999,90 +2000,90 @@ email = ["email-validator (>=2.0.0)"]
|
|||
|
||||
[[package]]
|
||||
name = "pydantic-core"
|
||||
version = "2.18.1"
|
||||
version = "2.18.2"
|
||||
description = "Core functionality for Pydantic validation and serialization"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pydantic_core-2.18.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:ee9cf33e7fe14243f5ca6977658eb7d1042caaa66847daacbd2117adb258b226"},
|
||||
{file = "pydantic_core-2.18.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6b7bbb97d82659ac8b37450c60ff2e9f97e4eb0f8a8a3645a5568b9334b08b50"},
|
||||
{file = "pydantic_core-2.18.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df4249b579e75094f7e9bb4bd28231acf55e308bf686b952f43100a5a0be394c"},
|
||||
{file = "pydantic_core-2.18.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d0491006a6ad20507aec2be72e7831a42efc93193d2402018007ff827dc62926"},
|
||||
{file = "pydantic_core-2.18.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ae80f72bb7a3e397ab37b53a2b49c62cc5496412e71bc4f1277620a7ce3f52b"},
|
||||
{file = "pydantic_core-2.18.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:58aca931bef83217fca7a390e0486ae327c4af9c3e941adb75f8772f8eeb03a1"},
|
||||
{file = "pydantic_core-2.18.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1be91ad664fc9245404a789d60cba1e91c26b1454ba136d2a1bf0c2ac0c0505a"},
|
||||
{file = "pydantic_core-2.18.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:667880321e916a8920ef49f5d50e7983792cf59f3b6079f3c9dac2b88a311d17"},
|
||||
{file = "pydantic_core-2.18.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f7054fdc556f5421f01e39cbb767d5ec5c1139ea98c3e5b350e02e62201740c7"},
|
||||
{file = "pydantic_core-2.18.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:030e4f9516f9947f38179249778709a460a3adb516bf39b5eb9066fcfe43d0e6"},
|
||||
{file = "pydantic_core-2.18.1-cp310-none-win32.whl", hash = "sha256:2e91711e36e229978d92642bfc3546333a9127ecebb3f2761372e096395fc649"},
|
||||
{file = "pydantic_core-2.18.1-cp310-none-win_amd64.whl", hash = "sha256:9a29726f91c6cb390b3c2338f0df5cd3e216ad7a938762d11c994bb37552edb0"},
|
||||
{file = "pydantic_core-2.18.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:9ece8a49696669d483d206b4474c367852c44815fca23ac4e48b72b339807f80"},
|
||||
{file = "pydantic_core-2.18.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7a5d83efc109ceddb99abd2c1316298ced2adb4570410defe766851a804fcd5b"},
|
||||
{file = "pydantic_core-2.18.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f7973c381283783cd1043a8c8f61ea5ce7a3a58b0369f0ee0ee975eaf2f2a1b"},
|
||||
{file = "pydantic_core-2.18.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:54c7375c62190a7845091f521add19b0f026bcf6ae674bdb89f296972272e86d"},
|
||||
{file = "pydantic_core-2.18.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd63cec4e26e790b70544ae5cc48d11b515b09e05fdd5eff12e3195f54b8a586"},
|
||||
{file = "pydantic_core-2.18.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:561cf62c8a3498406495cfc49eee086ed2bb186d08bcc65812b75fda42c38294"},
|
||||
{file = "pydantic_core-2.18.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68717c38a68e37af87c4da20e08f3e27d7e4212e99e96c3d875fbf3f4812abfc"},
|
||||
{file = "pydantic_core-2.18.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2d5728e93d28a3c63ee513d9ffbac9c5989de8c76e049dbcb5bfe4b923a9739d"},
|
||||
{file = "pydantic_core-2.18.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f0f17814c505f07806e22b28856c59ac80cee7dd0fbb152aed273e116378f519"},
|
||||
{file = "pydantic_core-2.18.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d816f44a51ba5175394bc6c7879ca0bd2be560b2c9e9f3411ef3a4cbe644c2e9"},
|
||||
{file = "pydantic_core-2.18.1-cp311-none-win32.whl", hash = "sha256:09f03dfc0ef8c22622eaa8608caa4a1e189cfb83ce847045eca34f690895eccb"},
|
||||
{file = "pydantic_core-2.18.1-cp311-none-win_amd64.whl", hash = "sha256:27f1009dc292f3b7ca77feb3571c537276b9aad5dd4efb471ac88a8bd09024e9"},
|
||||
{file = "pydantic_core-2.18.1-cp311-none-win_arm64.whl", hash = "sha256:48dd883db92e92519201f2b01cafa881e5f7125666141a49ffba8b9facc072b0"},
|
||||
{file = "pydantic_core-2.18.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:b6b0e4912030c6f28bcb72b9ebe4989d6dc2eebcd2a9cdc35fefc38052dd4fe8"},
|
||||
{file = "pydantic_core-2.18.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3202a429fe825b699c57892d4371c74cc3456d8d71b7f35d6028c96dfecad31"},
|
||||
{file = "pydantic_core-2.18.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3982b0a32d0a88b3907e4b0dc36809fda477f0757c59a505d4e9b455f384b8b"},
|
||||
{file = "pydantic_core-2.18.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25595ac311f20e5324d1941909b0d12933f1fd2171075fcff763e90f43e92a0d"},
|
||||
{file = "pydantic_core-2.18.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:14fe73881cf8e4cbdaded8ca0aa671635b597e42447fec7060d0868b52d074e6"},
|
||||
{file = "pydantic_core-2.18.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca976884ce34070799e4dfc6fbd68cb1d181db1eefe4a3a94798ddfb34b8867f"},
|
||||
{file = "pydantic_core-2.18.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:684d840d2c9ec5de9cb397fcb3f36d5ebb6fa0d94734f9886032dd796c1ead06"},
|
||||
{file = "pydantic_core-2.18.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:54764c083bbe0264f0f746cefcded6cb08fbbaaf1ad1d78fb8a4c30cff999a90"},
|
||||
{file = "pydantic_core-2.18.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:201713f2f462e5c015b343e86e68bd8a530a4f76609b33d8f0ec65d2b921712a"},
|
||||
{file = "pydantic_core-2.18.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fd1a9edb9dd9d79fbeac1ea1f9a8dd527a6113b18d2e9bcc0d541d308dae639b"},
|
||||
{file = "pydantic_core-2.18.1-cp312-none-win32.whl", hash = "sha256:d5e6b7155b8197b329dc787356cfd2684c9d6a6b1a197f6bbf45f5555a98d411"},
|
||||
{file = "pydantic_core-2.18.1-cp312-none-win_amd64.whl", hash = "sha256:9376d83d686ec62e8b19c0ac3bf8d28d8a5981d0df290196fb6ef24d8a26f0d6"},
|
||||
{file = "pydantic_core-2.18.1-cp312-none-win_arm64.whl", hash = "sha256:c562b49c96906b4029b5685075fe1ebd3b5cc2601dfa0b9e16c2c09d6cbce048"},
|
||||
{file = "pydantic_core-2.18.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:3e352f0191d99fe617371096845070dee295444979efb8f27ad941227de6ad09"},
|
||||
{file = "pydantic_core-2.18.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c0295d52b012cbe0d3059b1dba99159c3be55e632aae1999ab74ae2bd86a33d7"},
|
||||
{file = "pydantic_core-2.18.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56823a92075780582d1ffd4489a2e61d56fd3ebb4b40b713d63f96dd92d28144"},
|
||||
{file = "pydantic_core-2.18.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dd3f79e17b56741b5177bcc36307750d50ea0698df6aa82f69c7db32d968c1c2"},
|
||||
{file = "pydantic_core-2.18.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:38a5024de321d672a132b1834a66eeb7931959c59964b777e8f32dbe9523f6b1"},
|
||||
{file = "pydantic_core-2.18.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d2ce426ee691319d4767748c8e0895cfc56593d725594e415f274059bcf3cb76"},
|
||||
{file = "pydantic_core-2.18.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2adaeea59849ec0939af5c5d476935f2bab4b7f0335b0110f0f069a41024278e"},
|
||||
{file = "pydantic_core-2.18.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9b6431559676a1079eac0f52d6d0721fb8e3c5ba43c37bc537c8c83724031feb"},
|
||||
{file = "pydantic_core-2.18.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:85233abb44bc18d16e72dc05bf13848a36f363f83757541f1a97db2f8d58cfd9"},
|
||||
{file = "pydantic_core-2.18.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:641a018af4fe48be57a2b3d7a1f0f5dbca07c1d00951d3d7463f0ac9dac66622"},
|
||||
{file = "pydantic_core-2.18.1-cp38-none-win32.whl", hash = "sha256:63d7523cd95d2fde0d28dc42968ac731b5bb1e516cc56b93a50ab293f4daeaad"},
|
||||
{file = "pydantic_core-2.18.1-cp38-none-win_amd64.whl", hash = "sha256:907a4d7720abfcb1c81619863efd47c8a85d26a257a2dbebdb87c3b847df0278"},
|
||||
{file = "pydantic_core-2.18.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:aad17e462f42ddbef5984d70c40bfc4146c322a2da79715932cd8976317054de"},
|
||||
{file = "pydantic_core-2.18.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:94b9769ba435b598b547c762184bcfc4783d0d4c7771b04a3b45775c3589ca44"},
|
||||
{file = "pydantic_core-2.18.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80e0e57cc704a52fb1b48f16d5b2c8818da087dbee6f98d9bf19546930dc64b5"},
|
||||
{file = "pydantic_core-2.18.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:76b86e24039c35280ceee6dce7e62945eb93a5175d43689ba98360ab31eebc4a"},
|
||||
{file = "pydantic_core-2.18.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12a05db5013ec0ca4a32cc6433f53faa2a014ec364031408540ba858c2172bb0"},
|
||||
{file = "pydantic_core-2.18.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:250ae39445cb5475e483a36b1061af1bc233de3e9ad0f4f76a71b66231b07f88"},
|
||||
{file = "pydantic_core-2.18.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a32204489259786a923e02990249c65b0f17235073149d0033efcebe80095570"},
|
||||
{file = "pydantic_core-2.18.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6395a4435fa26519fd96fdccb77e9d00ddae9dd6c742309bd0b5610609ad7fb2"},
|
||||
{file = "pydantic_core-2.18.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2533ad2883f001efa72f3d0e733fb846710c3af6dcdd544fe5bf14fa5fe2d7db"},
|
||||
{file = "pydantic_core-2.18.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b560b72ed4816aee52783c66854d96157fd8175631f01ef58e894cc57c84f0f6"},
|
||||
{file = "pydantic_core-2.18.1-cp39-none-win32.whl", hash = "sha256:582cf2cead97c9e382a7f4d3b744cf0ef1a6e815e44d3aa81af3ad98762f5a9b"},
|
||||
{file = "pydantic_core-2.18.1-cp39-none-win_amd64.whl", hash = "sha256:ca71d501629d1fa50ea7fa3b08ba884fe10cefc559f5c6c8dfe9036c16e8ae89"},
|
||||
{file = "pydantic_core-2.18.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e178e5b66a06ec5bf51668ec0d4ac8cfb2bdcb553b2c207d58148340efd00143"},
|
||||
{file = "pydantic_core-2.18.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:72722ce529a76a4637a60be18bd789d8fb871e84472490ed7ddff62d5fed620d"},
|
||||
{file = "pydantic_core-2.18.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fe0c1ce5b129455e43f941f7a46f61f3d3861e571f2905d55cdbb8b5c6f5e2c"},
|
||||
{file = "pydantic_core-2.18.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4284c621f06a72ce2cb55f74ea3150113d926a6eb78ab38340c08f770eb9b4d"},
|
||||
{file = "pydantic_core-2.18.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a0c3e718f4e064efde68092d9d974e39572c14e56726ecfaeebbe6544521f47"},
|
||||
{file = "pydantic_core-2.18.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:2027493cc44c23b598cfaf200936110433d9caa84e2c6cf487a83999638a96ac"},
|
||||
{file = "pydantic_core-2.18.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:76909849d1a6bffa5a07742294f3fa1d357dc917cb1fe7b470afbc3a7579d539"},
|
||||
{file = "pydantic_core-2.18.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ee7ccc7fb7e921d767f853b47814c3048c7de536663e82fbc37f5eb0d532224b"},
|
||||
{file = "pydantic_core-2.18.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ee2794111c188548a4547eccc73a6a8527fe2af6cf25e1a4ebda2fd01cdd2e60"},
|
||||
{file = "pydantic_core-2.18.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a139fe9f298dc097349fb4f28c8b81cc7a202dbfba66af0e14be5cfca4ef7ce5"},
|
||||
{file = "pydantic_core-2.18.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d074b07a10c391fc5bbdcb37b2f16f20fcd9e51e10d01652ab298c0d07908ee2"},
|
||||
{file = "pydantic_core-2.18.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c69567ddbac186e8c0aadc1f324a60a564cfe25e43ef2ce81bcc4b8c3abffbae"},
|
||||
{file = "pydantic_core-2.18.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:baf1c7b78cddb5af00971ad5294a4583188bda1495b13760d9f03c9483bb6203"},
|
||||
{file = "pydantic_core-2.18.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:2684a94fdfd1b146ff10689c6e4e815f6a01141781c493b97342cdc5b06f4d5d"},
|
||||
{file = "pydantic_core-2.18.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:73c1bc8a86a5c9e8721a088df234265317692d0b5cd9e86e975ce3bc3db62a59"},
|
||||
{file = "pydantic_core-2.18.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e60defc3c15defb70bb38dd605ff7e0fae5f6c9c7cbfe0ad7868582cb7e844a6"},
|
||||
{file = "pydantic_core-2.18.1.tar.gz", hash = "sha256:de9d3e8717560eb05e28739d1b35e4eac2e458553a52a301e51352a7ffc86a35"},
|
||||
{file = "pydantic_core-2.18.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:9e08e867b306f525802df7cd16c44ff5ebbe747ff0ca6cf3fde7f36c05a59a81"},
|
||||
{file = "pydantic_core-2.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f0a21cbaa69900cbe1a2e7cad2aa74ac3cf21b10c3efb0fa0b80305274c0e8a2"},
|
||||
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0680b1f1f11fda801397de52c36ce38ef1c1dc841a0927a94f226dea29c3ae3d"},
|
||||
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:95b9d5e72481d3780ba3442eac863eae92ae43a5f3adb5b4d0a1de89d42bb250"},
|
||||
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fcf5cd9c4b655ad666ca332b9a081112cd7a58a8b5a6ca7a3104bc950f2038"},
|
||||
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b5155ff768083cb1d62f3e143b49a8a3432e6789a3abee8acd005c3c7af1c74"},
|
||||
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:553ef617b6836fc7e4df130bb851e32fe357ce36336d897fd6646d6058d980af"},
|
||||
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b89ed9eb7d616ef5714e5590e6cf7f23b02d0d539767d33561e3675d6f9e3857"},
|
||||
{file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:75f7e9488238e920ab6204399ded280dc4c307d034f3924cd7f90a38b1829563"},
|
||||
{file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ef26c9e94a8c04a1b2924149a9cb081836913818e55681722d7f29af88fe7b38"},
|
||||
{file = "pydantic_core-2.18.2-cp310-none-win32.whl", hash = "sha256:182245ff6b0039e82b6bb585ed55a64d7c81c560715d1bad0cbad6dfa07b4027"},
|
||||
{file = "pydantic_core-2.18.2-cp310-none-win_amd64.whl", hash = "sha256:e23ec367a948b6d812301afc1b13f8094ab7b2c280af66ef450efc357d2ae543"},
|
||||
{file = "pydantic_core-2.18.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:219da3f096d50a157f33645a1cf31c0ad1fe829a92181dd1311022f986e5fbe3"},
|
||||
{file = "pydantic_core-2.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cc1cfd88a64e012b74e94cd00bbe0f9c6df57049c97f02bb07d39e9c852e19a4"},
|
||||
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05b7133a6e6aeb8df37d6f413f7705a37ab4031597f64ab56384c94d98fa0e90"},
|
||||
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:224c421235f6102e8737032483f43c1a8cfb1d2f45740c44166219599358c2cd"},
|
||||
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b14d82cdb934e99dda6d9d60dc84a24379820176cc4a0d123f88df319ae9c150"},
|
||||
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2728b01246a3bba6de144f9e3115b532ee44bd6cf39795194fb75491824a1413"},
|
||||
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:470b94480bb5ee929f5acba6995251ada5e059a5ef3e0dfc63cca287283ebfa6"},
|
||||
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:997abc4df705d1295a42f95b4eec4950a37ad8ae46d913caeee117b6b198811c"},
|
||||
{file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75250dbc5290e3f1a0f4618db35e51a165186f9034eff158f3d490b3fed9f8a0"},
|
||||
{file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4456f2dca97c425231d7315737d45239b2b51a50dc2b6f0c2bb181fce6207664"},
|
||||
{file = "pydantic_core-2.18.2-cp311-none-win32.whl", hash = "sha256:269322dcc3d8bdb69f054681edff86276b2ff972447863cf34c8b860f5188e2e"},
|
||||
{file = "pydantic_core-2.18.2-cp311-none-win_amd64.whl", hash = "sha256:800d60565aec896f25bc3cfa56d2277d52d5182af08162f7954f938c06dc4ee3"},
|
||||
{file = "pydantic_core-2.18.2-cp311-none-win_arm64.whl", hash = "sha256:1404c69d6a676245199767ba4f633cce5f4ad4181f9d0ccb0577e1f66cf4c46d"},
|
||||
{file = "pydantic_core-2.18.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:fb2bd7be70c0fe4dfd32c951bc813d9fe6ebcbfdd15a07527796c8204bd36242"},
|
||||
{file = "pydantic_core-2.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6132dd3bd52838acddca05a72aafb6eab6536aa145e923bb50f45e78b7251043"},
|
||||
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d904828195733c183d20a54230c0df0eb46ec746ea1a666730787353e87182"},
|
||||
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c9bd70772c720142be1020eac55f8143a34ec9f82d75a8e7a07852023e46617f"},
|
||||
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b8ed04b3582771764538f7ee7001b02e1170223cf9b75dff0bc698fadb00cf3"},
|
||||
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e6dac87ddb34aaec85f873d737e9d06a3555a1cc1a8e0c44b7f8d5daeb89d86f"},
|
||||
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ca4ae5a27ad7a4ee5170aebce1574b375de390bc01284f87b18d43a3984df72"},
|
||||
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:886eec03591b7cf058467a70a87733b35f44707bd86cf64a615584fd72488b7c"},
|
||||
{file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ca7b0c1f1c983e064caa85f3792dd2fe3526b3505378874afa84baf662e12241"},
|
||||
{file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b4356d3538c3649337df4074e81b85f0616b79731fe22dd11b99499b2ebbdf3"},
|
||||
{file = "pydantic_core-2.18.2-cp312-none-win32.whl", hash = "sha256:8b172601454f2d7701121bbec3425dd71efcb787a027edf49724c9cefc14c038"},
|
||||
{file = "pydantic_core-2.18.2-cp312-none-win_amd64.whl", hash = "sha256:b1bd7e47b1558ea872bd16c8502c414f9e90dcf12f1395129d7bb42a09a95438"},
|
||||
{file = "pydantic_core-2.18.2-cp312-none-win_arm64.whl", hash = "sha256:98758d627ff397e752bc339272c14c98199c613f922d4a384ddc07526c86a2ec"},
|
||||
{file = "pydantic_core-2.18.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:9fdad8e35f278b2c3eb77cbdc5c0a49dada440657bf738d6905ce106dc1de439"},
|
||||
{file = "pydantic_core-2.18.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1d90c3265ae107f91a4f279f4d6f6f1d4907ac76c6868b27dc7fb33688cfb347"},
|
||||
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:390193c770399861d8df9670fb0d1874f330c79caaca4642332df7c682bf6b91"},
|
||||
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:82d5d4d78e4448683cb467897fe24e2b74bb7b973a541ea1dcfec1d3cbce39fb"},
|
||||
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4774f3184d2ef3e14e8693194f661dea5a4d6ca4e3dc8e39786d33a94865cefd"},
|
||||
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4d938ec0adf5167cb335acb25a4ee69a8107e4984f8fbd2e897021d9e4ca21b"},
|
||||
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0e8b1be28239fc64a88a8189d1df7fad8be8c1ae47fcc33e43d4be15f99cc70"},
|
||||
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:868649da93e5a3d5eacc2b5b3b9235c98ccdbfd443832f31e075f54419e1b96b"},
|
||||
{file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:78363590ef93d5d226ba21a90a03ea89a20738ee5b7da83d771d283fd8a56761"},
|
||||
{file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:852e966fbd035a6468fc0a3496589b45e2208ec7ca95c26470a54daed82a0788"},
|
||||
{file = "pydantic_core-2.18.2-cp38-none-win32.whl", hash = "sha256:6a46e22a707e7ad4484ac9ee9f290f9d501df45954184e23fc29408dfad61350"},
|
||||
{file = "pydantic_core-2.18.2-cp38-none-win_amd64.whl", hash = "sha256:d91cb5ea8b11607cc757675051f61b3d93f15eca3cefb3e6c704a5d6e8440f4e"},
|
||||
{file = "pydantic_core-2.18.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:ae0a8a797a5e56c053610fa7be147993fe50960fa43609ff2a9552b0e07013e8"},
|
||||
{file = "pydantic_core-2.18.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:042473b6280246b1dbf530559246f6842b56119c2926d1e52b631bdc46075f2a"},
|
||||
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a388a77e629b9ec814c1b1e6b3b595fe521d2cdc625fcca26fbc2d44c816804"},
|
||||
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25add29b8f3b233ae90ccef2d902d0ae0432eb0d45370fe315d1a5cf231004b"},
|
||||
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f459a5ce8434614dfd39bbebf1041952ae01da6bed9855008cb33b875cb024c0"},
|
||||
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eff2de745698eb46eeb51193a9f41d67d834d50e424aef27df2fcdee1b153845"},
|
||||
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8309f67285bdfe65c372ea3722b7a5642680f3dba538566340a9d36e920b5f0"},
|
||||
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f93a8a2e3938ff656a7c1bc57193b1319960ac015b6e87d76c76bf14fe0244b4"},
|
||||
{file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:22057013c8c1e272eb8d0eebc796701167d8377441ec894a8fed1af64a0bf399"},
|
||||
{file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cfeecd1ac6cc1fb2692c3d5110781c965aabd4ec5d32799773ca7b1456ac636b"},
|
||||
{file = "pydantic_core-2.18.2-cp39-none-win32.whl", hash = "sha256:0d69b4c2f6bb3e130dba60d34c0845ba31b69babdd3f78f7c0c8fae5021a253e"},
|
||||
{file = "pydantic_core-2.18.2-cp39-none-win_amd64.whl", hash = "sha256:d9319e499827271b09b4e411905b24a426b8fb69464dfa1696258f53a3334641"},
|
||||
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a1874c6dd4113308bd0eb568418e6114b252afe44319ead2b4081e9b9521fe75"},
|
||||
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ccdd111c03bfd3666bd2472b674c6899550e09e9f298954cfc896ab92b5b0e6d"},
|
||||
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e18609ceaa6eed63753037fc06ebb16041d17d28199ae5aba0052c51449650a9"},
|
||||
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e5c584d357c4e2baf0ff7baf44f4994be121e16a2c88918a5817331fc7599d7"},
|
||||
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43f0f463cf89ace478de71a318b1b4f05ebc456a9b9300d027b4b57c1a2064fb"},
|
||||
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e1b395e58b10b73b07b7cf740d728dd4ff9365ac46c18751bf8b3d8cca8f625a"},
|
||||
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0098300eebb1c837271d3d1a2cd2911e7c11b396eac9661655ee524a7f10587b"},
|
||||
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:36789b70d613fbac0a25bb07ab3d9dba4d2e38af609c020cf4d888d165ee0bf3"},
|
||||
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3f9a801e7c8f1ef8718da265bba008fa121243dfe37c1cea17840b0944dfd72c"},
|
||||
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:3a6515ebc6e69d85502b4951d89131ca4e036078ea35533bb76327f8424531ce"},
|
||||
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20aca1e2298c56ececfd8ed159ae4dde2df0781988c97ef77d5c16ff4bd5b400"},
|
||||
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:223ee893d77a310a0391dca6df00f70bbc2f36a71a895cecd9a0e762dc37b349"},
|
||||
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2334ce8c673ee93a1d6a65bd90327588387ba073c17e61bf19b4fd97d688d63c"},
|
||||
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:cbca948f2d14b09d20268cda7b0367723d79063f26c4ffc523af9042cad95592"},
|
||||
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b3ef08e20ec49e02d5c6717a91bb5af9b20f1805583cb0adfe9ba2c6b505b5ae"},
|
||||
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c6fdc8627910eed0c01aed6a390a252fe3ea6d472ee70fdde56273f198938374"},
|
||||
{file = "pydantic_core-2.18.2.tar.gz", hash = "sha256:2e29d20810dfc3043ee13ac7d9e25105799817683348823f305ab3f349b9386e"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[tool.poetry]
|
||||
name = "langflow-base"
|
||||
version = "0.0.36"
|
||||
version = "0.0.37"
|
||||
description = "A Python package with a built-in web application"
|
||||
authors = ["Langflow <contact@langflow.org>"]
|
||||
maintainers = [
|
||||
|
|
|
|||
633
src/frontend/package-lock.json
generated
633
src/frontend/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -20,18 +20,21 @@ import {
|
|||
CardHeader,
|
||||
CardTitle,
|
||||
} from "../ui/card";
|
||||
import Loading from "../ui/loading";
|
||||
|
||||
export default function CollectionCardComponent({
|
||||
data,
|
||||
authorized = true,
|
||||
disabled = false,
|
||||
button,
|
||||
onClick,
|
||||
onDelete,
|
||||
playground,
|
||||
}: {
|
||||
data: storeComponent;
|
||||
authorized?: boolean;
|
||||
disabled?: boolean;
|
||||
onClick?: () => void;
|
||||
button?: JSX.Element;
|
||||
playground?: boolean;
|
||||
onDelete?: () => void;
|
||||
|
|
@ -40,6 +43,7 @@ export default function CollectionCardComponent({
|
|||
const setSuccessData = useAlertStore((state) => state.setSuccessData);
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
const setValidApiKey = useStoreStore((state) => state.updateValidApiKey);
|
||||
const cleanFlowPool = useFlowStore((state) => state.CleanFlowPool);
|
||||
const isStore = false;
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [loadingLike, setLoadingLike] = useState(false);
|
||||
|
|
@ -51,6 +55,8 @@ export default function CollectionCardComponent({
|
|||
data?.downloads_count ?? 0
|
||||
);
|
||||
const currentFlow = useFlowsManagerStore((state) => state.currentFlow);
|
||||
const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow);
|
||||
const getFlowById = useFlowsManagerStore((state) => state.getFlowById);
|
||||
const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId);
|
||||
const setNodes = useFlowStore((state) => state.setNodes);
|
||||
const setEdges = useFlowStore((state) => state.setEdges);
|
||||
|
|
@ -58,15 +64,28 @@ export default function CollectionCardComponent({
|
|||
const setCurrentFlowId = useFlowsManagerStore(
|
||||
(state) => state.setCurrentFlowId
|
||||
);
|
||||
const [loadingPlayground, setLoadingPlayground] = useState(false);
|
||||
|
||||
const name = data.is_component ? "Component" : "Flow";
|
||||
|
||||
async function getFlowData() {
|
||||
const res = await getComponent(data.id);
|
||||
const newFlow = cloneFLowWithParent(res, res.id, data.is_component, true);
|
||||
return newFlow;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (currentFlowId && playground) {
|
||||
setNodes(currentFlow?.data?.nodes ?? []);
|
||||
setEdges(currentFlow?.data?.edges ?? []);
|
||||
if (openPlayground) {
|
||||
setNodes(currentFlow?.data?.nodes ?? [], true);
|
||||
setEdges(currentFlow?.data?.edges ?? [], true);
|
||||
} else {
|
||||
setNodes([], true);
|
||||
setEdges([], true);
|
||||
cleanFlowPool();
|
||||
}
|
||||
}
|
||||
}, [currentFlowId]);
|
||||
}, [openPlayground]);
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
|
|
@ -147,250 +166,325 @@ export default function CollectionCardComponent({
|
|||
}
|
||||
|
||||
return (
|
||||
<Card
|
||||
className={cn(
|
||||
"group relative flex min-h-[11rem] flex-col justify-between overflow-hidden transition-all hover:shadow-md",
|
||||
disabled ? "pointer-events-none opacity-50" : ""
|
||||
)}
|
||||
>
|
||||
<div>
|
||||
<CardHeader>
|
||||
<div>
|
||||
<CardTitle className="flex w-full items-center justify-between gap-3 text-xl">
|
||||
<IconComponent
|
||||
className={cn(
|
||||
"flex-shrink-0",
|
||||
data.is_component
|
||||
? "mx-0.5 h-6 w-6 text-component-icon"
|
||||
: "h-7 w-7 flex-shrink-0 text-flow-icon"
|
||||
)}
|
||||
name={data.is_component ? "ToyBrick" : "Group"}
|
||||
/>
|
||||
<ShadTooltip content={data.name}>
|
||||
<div className="w-full truncate">{data.name}</div>
|
||||
</ShadTooltip>
|
||||
{data?.metadata !== undefined && (
|
||||
<div className="flex gap-3">
|
||||
{data.private && (
|
||||
<ShadTooltip content="Private">
|
||||
<span className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
||||
<IconComponent name="Lock" className="h-4 w-4" />
|
||||
</span>
|
||||
</ShadTooltip>
|
||||
<>
|
||||
<Card
|
||||
className={cn(
|
||||
"group relative flex min-h-[11rem] flex-col justify-between overflow-hidden transition-all hover:shadow-md",
|
||||
disabled ? "pointer-events-none opacity-50" : "",
|
||||
onClick ? "cursor-pointer" : ""
|
||||
)}
|
||||
onClick={onClick}
|
||||
>
|
||||
<div>
|
||||
<CardHeader>
|
||||
<div>
|
||||
<CardTitle className="flex w-full items-center justify-between gap-3 text-xl">
|
||||
<IconComponent
|
||||
className={cn(
|
||||
"flex-shrink-0",
|
||||
data.is_component
|
||||
? "mx-0.5 h-6 w-6 text-component-icon"
|
||||
: "h-7 w-7 flex-shrink-0 text-flow-icon"
|
||||
)}
|
||||
{!data.is_component && (
|
||||
<ShadTooltip content="Components">
|
||||
name={data.is_component ? "ToyBrick" : "Group"}
|
||||
/>
|
||||
<ShadTooltip content={data.name}>
|
||||
<div className="w-full truncate">{data.name}</div>
|
||||
</ShadTooltip>
|
||||
{data?.metadata !== undefined && (
|
||||
<div className="flex gap-3">
|
||||
{data.private && (
|
||||
<ShadTooltip content="Private">
|
||||
<span className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
||||
<IconComponent name="Lock" className="h-4 w-4" />
|
||||
</span>
|
||||
</ShadTooltip>
|
||||
)}
|
||||
{!data.is_component && (
|
||||
<ShadTooltip content="Components">
|
||||
<span className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
||||
<IconComponent name="ToyBrick" className="h-4 w-4" />
|
||||
<span data-testid={`total-${data.name}`}>
|
||||
{data?.metadata?.total ?? 0}
|
||||
</span>
|
||||
</span>
|
||||
</ShadTooltip>
|
||||
)}
|
||||
<ShadTooltip content="Likes">
|
||||
<span className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
||||
<IconComponent name="ToyBrick" className="h-4 w-4" />
|
||||
<span data-testid={`total-${data.name}`}>
|
||||
{data?.metadata?.total ?? 0}
|
||||
<IconComponent
|
||||
name="Heart"
|
||||
className={cn("h-4 w-4 ")}
|
||||
/>
|
||||
<span data-testid={`likes-${data.name}`}>
|
||||
{likes_count ?? 0}
|
||||
</span>
|
||||
</span>
|
||||
</ShadTooltip>
|
||||
)}
|
||||
<ShadTooltip content="Likes">
|
||||
<span className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
||||
<IconComponent name="Heart" className={cn("h-4 w-4 ")} />
|
||||
<span data-testid={`likes-${data.name}`}>
|
||||
{likes_count ?? 0}
|
||||
<ShadTooltip content="Downloads">
|
||||
<span className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
||||
<IconComponent
|
||||
name="DownloadCloud"
|
||||
className="h-4 w-4"
|
||||
/>
|
||||
<span data-testid={`downloads-${data.name}`}>
|
||||
{downloads_count ?? 0}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</ShadTooltip>
|
||||
<ShadTooltip content="Downloads">
|
||||
<span className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
||||
<IconComponent name="DownloadCloud" className="h-4 w-4" />
|
||||
<span data-testid={`downloads-${data.name}`}>
|
||||
{downloads_count ?? 0}
|
||||
</span>
|
||||
</span>
|
||||
</ShadTooltip>
|
||||
</div>
|
||||
)}
|
||||
</ShadTooltip>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{onDelete && data?.metadata === undefined && (
|
||||
<DeleteConfirmationModal
|
||||
onConfirm={() => {
|
||||
onDelete();
|
||||
{onDelete && data?.metadata === undefined && (
|
||||
<DeleteConfirmationModal
|
||||
onConfirm={() => {
|
||||
onDelete();
|
||||
}}
|
||||
>
|
||||
<IconComponent
|
||||
name="Trash2"
|
||||
className="h-5 w-5 text-primary opacity-0 transition-all hover:text-destructive group-hover:opacity-100"
|
||||
/>
|
||||
</DeleteConfirmationModal>
|
||||
)}
|
||||
</CardTitle>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
{data.user_created && data.user_created.username && (
|
||||
<span className="text-sm text-primary">
|
||||
by <b>{data.user_created.username}</b>
|
||||
{data.last_tested_version && (
|
||||
<>
|
||||
{" "}
|
||||
|{" "}
|
||||
<span className="text-xs">
|
||||
{" "}
|
||||
⛓︎ v{data.last_tested_version}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
<div className="flex w-full flex-1 flex-wrap gap-2">
|
||||
{data.tags &&
|
||||
data.tags.length > 0 &&
|
||||
data.tags.map((tag, index) => (
|
||||
<Badge
|
||||
key={index}
|
||||
variant="outline"
|
||||
size="xq"
|
||||
className="text-muted-foreground"
|
||||
>
|
||||
{tag.name}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<CardDescription className="pb-2 pt-2">
|
||||
<div className="truncate-doubleline">{data.description}</div>
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
</div>
|
||||
|
||||
<CardFooter>
|
||||
<div className="flex w-full items-center justify-between gap-2">
|
||||
<div className="flex w-full flex-wrap items-end justify-between gap-2">
|
||||
{playground && data?.metadata !== undefined ? (
|
||||
<Button
|
||||
disabled={loadingPlayground}
|
||||
key={data.id}
|
||||
tabIndex={-1}
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="gap-2 whitespace-nowrap"
|
||||
data-testid={"playground-flow-button-" + data.id}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setLoadingPlayground(true);
|
||||
if (getFlowById(data.id)) {
|
||||
setCurrentFlowId(data.id);
|
||||
setOpenPlayground(true);
|
||||
setLoadingPlayground(false);
|
||||
} else {
|
||||
getFlowData().then((res) => {
|
||||
setCurrentFlow(res);
|
||||
setOpenPlayground(true);
|
||||
setLoadingPlayground(false);
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<IconComponent
|
||||
name="Trash2"
|
||||
className="h-5 w-5 text-primary opacity-0 transition-all hover:text-destructive group-hover:opacity-100"
|
||||
/>
|
||||
</DeleteConfirmationModal>
|
||||
{!loadingPlayground ? (
|
||||
<IconComponent
|
||||
name="BotMessageSquareIcon"
|
||||
className="h-4 w-4 select-none"
|
||||
/>
|
||||
) : (
|
||||
<Loading className="h-4 w-4 text-medium-indigo" />
|
||||
)}
|
||||
Playground
|
||||
</Button>
|
||||
) : (
|
||||
<div></div>
|
||||
)}
|
||||
</CardTitle>
|
||||
</div>
|
||||
{data.user_created && data.user_created.username && (
|
||||
<span className="text-sm text-primary">
|
||||
by <b>{data.user_created.username}</b>
|
||||
{data.last_tested_version && (
|
||||
<>
|
||||
{" "}
|
||||
|{" "}
|
||||
<span className="text-xs">
|
||||
{" "}
|
||||
⛓︎ v{data.last_tested_version}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
|
||||
<CardDescription className="pb-2 pt-2">
|
||||
<div className="truncate-doubleline">{data.description}</div>
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
</div>
|
||||
|
||||
<CardFooter>
|
||||
<div className="flex w-full items-center justify-between gap-2">
|
||||
<div className="flex w-full flex-wrap items-end justify-between gap-2">
|
||||
<div className="flex w-full flex-1 flex-wrap gap-2">
|
||||
{data.tags &&
|
||||
data.tags.length > 0 &&
|
||||
data.tags.map((tag, index) => (
|
||||
<Badge
|
||||
key={index}
|
||||
variant="outline"
|
||||
size="xq"
|
||||
className="text-muted-foreground"
|
||||
>
|
||||
{tag.name}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
{data.liked_by_count != undefined && (
|
||||
<div className="flex gap-0.5">
|
||||
{onDelete && data?.metadata !== undefined ? (
|
||||
<ShadTooltip
|
||||
content={
|
||||
authorized ? "Delete" : "Please review your API key."
|
||||
}
|
||||
>
|
||||
<DeleteConfirmationModal
|
||||
onConfirm={() => {
|
||||
onDelete();
|
||||
}}
|
||||
{data.liked_by_count != undefined && (
|
||||
<div className="flex gap-0.5">
|
||||
{onDelete && data?.metadata !== undefined ? (
|
||||
<ShadTooltip
|
||||
content={
|
||||
authorized ? "Delete" : "Please review your API key."
|
||||
}
|
||||
>
|
||||
<DeleteConfirmationModal
|
||||
onConfirm={() => {
|
||||
onDelete();
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className={
|
||||
"whitespace-nowrap" +
|
||||
(!authorized ? " cursor-not-allowed" : "")
|
||||
}
|
||||
>
|
||||
<IconComponent
|
||||
name="Trash2"
|
||||
className={cn(
|
||||
"h-5 w-5",
|
||||
!authorized ? " text-ring" : ""
|
||||
)}
|
||||
/>
|
||||
</Button>
|
||||
</DeleteConfirmationModal>
|
||||
</ShadTooltip>
|
||||
) : (
|
||||
<ShadTooltip
|
||||
content={
|
||||
authorized ? "Like" : "Please review your API key."
|
||||
}
|
||||
>
|
||||
<Button
|
||||
disabled={loadingLike}
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className={
|
||||
"whitespace-nowrap" +
|
||||
(!authorized ? " cursor-not-allowed" : "")
|
||||
}
|
||||
onClick={() => {
|
||||
if (!authorized) {
|
||||
return;
|
||||
}
|
||||
handleLike();
|
||||
}}
|
||||
data-testid={`like-${data.name}`}
|
||||
>
|
||||
<IconComponent
|
||||
name="Trash2"
|
||||
name="Heart"
|
||||
className={cn(
|
||||
"h-5 w-5",
|
||||
liked_by_user
|
||||
? "fill-destructive stroke-destructive"
|
||||
: "",
|
||||
!authorized ? " text-ring" : ""
|
||||
)}
|
||||
/>
|
||||
</Button>
|
||||
</DeleteConfirmationModal>
|
||||
</ShadTooltip>
|
||||
) : (
|
||||
</ShadTooltip>
|
||||
)}
|
||||
<ShadTooltip
|
||||
content={
|
||||
authorized ? "Like" : "Please review your API key."
|
||||
authorized
|
||||
? isStore
|
||||
? "Download"
|
||||
: "Install Locally"
|
||||
: "Please review your API key."
|
||||
}
|
||||
>
|
||||
<Button
|
||||
disabled={loadingLike}
|
||||
disabled={loading}
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className={
|
||||
"whitespace-nowrap" +
|
||||
(!authorized ? " cursor-not-allowed" : "")
|
||||
(!authorized ? " cursor-not-allowed" : "") +
|
||||
(!loading ? " p-0.5" : "")
|
||||
}
|
||||
onClick={() => {
|
||||
if (!authorized) {
|
||||
if (loading || !authorized) {
|
||||
return;
|
||||
}
|
||||
handleLike();
|
||||
handleInstall();
|
||||
}}
|
||||
data-testid={`like-${data.name}`}
|
||||
data-testid={`install-${data.name}`}
|
||||
>
|
||||
<IconComponent
|
||||
name="Heart"
|
||||
name={
|
||||
loading ? "Loader2" : isStore ? "Download" : "Plus"
|
||||
}
|
||||
className={cn(
|
||||
"h-5 w-5",
|
||||
liked_by_user
|
||||
? "fill-destructive stroke-destructive"
|
||||
: "",
|
||||
loading ? "h-5 w-5 animate-spin" : "h-5 w-5",
|
||||
!authorized ? " text-ring" : ""
|
||||
)}
|
||||
/>
|
||||
</Button>
|
||||
</ShadTooltip>
|
||||
)}
|
||||
<ShadTooltip
|
||||
content={
|
||||
authorized
|
||||
? isStore
|
||||
? "Download"
|
||||
: "Install Locally"
|
||||
: "Please review your API key."
|
||||
}
|
||||
>
|
||||
<Button
|
||||
disabled={loading}
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className={
|
||||
"whitespace-nowrap" +
|
||||
(!authorized ? " cursor-not-allowed" : "") +
|
||||
(!loading ? " p-0.5" : "")
|
||||
</div>
|
||||
)}
|
||||
{button && button}
|
||||
{playground && data?.metadata === undefined && (
|
||||
<Button
|
||||
disabled={loadingPlayground}
|
||||
key={data.id}
|
||||
tabIndex={-1}
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="gap-2 whitespace-nowrap"
|
||||
data-testid={"playground-flow-button-" + data.id}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setLoadingPlayground(true);
|
||||
if (getFlowById(data.id)) {
|
||||
setCurrentFlowId(data.id);
|
||||
setOpenPlayground(true);
|
||||
setLoadingPlayground(false);
|
||||
} else {
|
||||
getFlowData().then((res) => {
|
||||
setCurrentFlow(res);
|
||||
setOpenPlayground(true);
|
||||
setLoadingPlayground(false);
|
||||
});
|
||||
}
|
||||
onClick={() => {
|
||||
if (loading || !authorized) {
|
||||
return;
|
||||
}
|
||||
handleInstall();
|
||||
}}
|
||||
data-testid={`install-${data.name}`}
|
||||
>
|
||||
}}
|
||||
>
|
||||
{!loadingPlayground ? (
|
||||
<IconComponent
|
||||
name={loading ? "Loader2" : isStore ? "Download" : "Plus"}
|
||||
className={cn(
|
||||
loading ? "h-5 w-5 animate-spin" : "h-5 w-5",
|
||||
!authorized ? " text-ring" : ""
|
||||
)}
|
||||
name="BotMessageSquareIcon"
|
||||
className="h-4 w-4 select-none"
|
||||
/>
|
||||
</Button>
|
||||
</ShadTooltip>
|
||||
</div>
|
||||
)}
|
||||
{button && button}
|
||||
{playground && (
|
||||
<Button
|
||||
tabIndex={-1}
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="whitespace-nowrap "
|
||||
data-testid={"playground-flow-button-" + data.id}
|
||||
onClick={() => {
|
||||
setCurrentFlowId(data.id);
|
||||
setOpenPlayground(true);
|
||||
}}
|
||||
>
|
||||
<IconComponent
|
||||
name="ExternalLink"
|
||||
className="main-page-nav-button select-none"
|
||||
/>
|
||||
Playground
|
||||
{openPlayground && (
|
||||
<IOModal open={openPlayground} setOpen={setOpenPlayground}>
|
||||
<></>
|
||||
</IOModal>
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
) : (
|
||||
<Loading className="h-4 w-4 text-medium-indigo" />
|
||||
)}
|
||||
Playground
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
{openPlayground && (
|
||||
<IOModal
|
||||
cleanOnClose={true}
|
||||
open={openPlayground}
|
||||
setOpen={setOpenPlayground}
|
||||
>
|
||||
<></>
|
||||
</IOModal>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,15 +87,15 @@ export default function FlowToolbar(): JSX.Element {
|
|||
}
|
||||
>
|
||||
<div className="flex">
|
||||
<div className="flex h-full w-full gap-1 rounded-sm text-medium-indigo transition-all">
|
||||
<div className="flex h-full w-full gap-1 rounded-sm transition-all">
|
||||
{hasIO ? (
|
||||
<IOModal open={open} setOpen={setOpen} disable={!hasIO}>
|
||||
<div className="relative inline-flex w-full items-center justify-center gap-1 px-5 py-3 text-sm font-semibold text-medium-indigo transition-all transition-all duration-500 ease-in-out ease-in-out hover:bg-hover">
|
||||
<div className="relative inline-flex w-full items-center justify-center gap-1 px-5 py-3 text-sm font-semibold transition-all duration-500 ease-in-out hover:bg-hover">
|
||||
<ForwardedIconComponent
|
||||
name="Zap"
|
||||
className={"message-button-icon h-5 w-5 transition-all"}
|
||||
name="BotMessageSquareIcon"
|
||||
className={" h-5 w-5 transition-all"}
|
||||
/>
|
||||
Run
|
||||
Playground
|
||||
</div>
|
||||
</IOModal>
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { AxiosResponse } from "axios";
|
||||
import { ReactFlowJsonObject } from "reactflow";
|
||||
import { AxiosRequestConfig, AxiosResponse } from "axios";
|
||||
import { Edge, ReactFlowJsonObject,Node } from "reactflow";
|
||||
import { BASE_URL_API } from "../../constants/constants";
|
||||
import { api } from "../../controllers/API/api";
|
||||
import {
|
||||
|
|
@ -906,17 +906,26 @@ export async function updateGlobalVariable(
|
|||
export async function getVerticesOrder(
|
||||
flowId: string,
|
||||
startNodeId?: string | null,
|
||||
stopNodeId?: string | null
|
||||
stopNodeId?: string | null,
|
||||
nodes?:Node[],
|
||||
Edges?:Edge[]
|
||||
): Promise<AxiosResponse<VerticesOrderTypeAPI>> {
|
||||
// nodeId is optional and is a query parameter
|
||||
// if nodeId is not provided, the API will return all vertices
|
||||
const config = {};
|
||||
const config:AxiosRequestConfig<any> = {};
|
||||
if (stopNodeId) {
|
||||
config["params"] = { stop_component_id: stopNodeId };
|
||||
} else if (startNodeId) {
|
||||
config["params"] = { start_component_id: startNodeId };
|
||||
}
|
||||
return await api.get(`${BASE_URL_API}build/${flowId}/vertices`, config);
|
||||
const data = {
|
||||
data:{}
|
||||
}
|
||||
if(nodes && Edges){
|
||||
data["data"]["nodes"] = nodes
|
||||
data["data"]["edges"] = Edges
|
||||
}
|
||||
return await api.post(`${BASE_URL_API}build/${flowId}/vertices`,data, config);
|
||||
}
|
||||
|
||||
export async function postBuildVertex(
|
||||
|
|
|
|||
|
|
@ -83,12 +83,6 @@ export default function IOModal({
|
|||
return updateVerticesOrder(currentFlow!.id, null);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
updateVertices();
|
||||
}
|
||||
}, [open, currentFlow]);
|
||||
|
||||
async function sendMessage(count = 1): Promise<void> {
|
||||
if (isBuilding) return;
|
||||
setIsBuilding(true);
|
||||
|
|
@ -132,9 +126,9 @@ export default function IOModal({
|
|||
{/* TODO ADAPT TO ALL TYPES OF INPUTS AND OUTPUTS */}
|
||||
<BaseModal.Header description={CHAT_FORM_DIALOG_SUBTITLE}>
|
||||
<div className="flex items-center">
|
||||
<span className="pr-2">Interaction Panel</span>
|
||||
<span className="pr-2">Playground</span>
|
||||
<IconComponent
|
||||
name="prompts"
|
||||
name="BotMessageSquareIcon"
|
||||
className="h-6 w-6 pl-1 text-foreground"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -86,6 +86,7 @@ export default function StoreApiKeyModal({
|
|||
<Form.Root
|
||||
onSubmit={(event) => {
|
||||
event.preventDefault();
|
||||
handleSaveKey();
|
||||
}}
|
||||
>
|
||||
<div className="grid gap-5">
|
||||
|
|
@ -131,9 +132,6 @@ export default function StoreApiKeyModal({
|
|||
<Button
|
||||
data-testid="api-key-save-button-store"
|
||||
className="mt-8"
|
||||
onClick={() => {
|
||||
handleSaveKey();
|
||||
}}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -6,18 +6,25 @@ import { useDarkStore } from "../../stores/darkStore";
|
|||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||
import Page from "./components/PageComponent";
|
||||
import ExtraSidebar from "./components/extraSidebarComponent";
|
||||
import useFlowStore from "../../stores/flowStore";
|
||||
|
||||
export default function FlowPage({ view }: { view?: boolean }): JSX.Element {
|
||||
const setCurrentFlowId = useFlowsManagerStore(
|
||||
(state) => state.setCurrentFlowId
|
||||
);
|
||||
const version = useDarkStore((state) => state.version);
|
||||
const setOnFlowPage = useFlowStore((state) => state.setOnFlowPage);
|
||||
const currentFlow = useFlowsManagerStore((state) => state.currentFlow);
|
||||
const { id } = useParams();
|
||||
|
||||
// Set flow tab id
|
||||
useEffect(() => {
|
||||
setCurrentFlowId(id!);
|
||||
setOnFlowPage(true);
|
||||
|
||||
return () => {
|
||||
setOnFlowPage(false);
|
||||
};
|
||||
}, [id]);
|
||||
return (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ export default function ComponentsComponent({
|
|||
const start = (pageIndex - 1) * pageSize;
|
||||
const end = start + pageSize;
|
||||
setData(all.slice(start, end));
|
||||
}, [flows, isLoading, pageIndex, pageSize]);
|
||||
}, [flows,isLoading, pageIndex, pageSize]);
|
||||
|
||||
const [data, setData] = useState<FlowType[]>([]);
|
||||
|
||||
|
|
@ -146,30 +146,11 @@ export default function ComponentsComponent({
|
|||
}}
|
||||
key={idx}
|
||||
data={item}
|
||||
disabled={isLoading}
|
||||
button={
|
||||
!is_component ? (
|
||||
<Link to={"/flow/" + item.id}>
|
||||
<Button
|
||||
tabIndex={-1}
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="whitespace-nowrap "
|
||||
data-testid={
|
||||
"edit-flow-button-" + item.id + "-" + idx
|
||||
}
|
||||
>
|
||||
<IconComponent
|
||||
name="ExternalLink"
|
||||
className="main-page-nav-button select-none"
|
||||
/>
|
||||
Edit Flow
|
||||
</Button>
|
||||
</Link>
|
||||
) : (
|
||||
<></>
|
||||
)
|
||||
data-testid={
|
||||
"edit-flow-button-" + item.id + "-" + idx
|
||||
}
|
||||
onClick={!is_component ? () => {navigate("/flow/" + item.id);} : undefined}
|
||||
disabled={isLoading}
|
||||
playground={!is_component}
|
||||
/>
|
||||
))
|
||||
|
|
|
|||
64
src/frontend/src/pages/Playground/index.tsx
Normal file
64
src/frontend/src/pages/Playground/index.tsx
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||
import { getComponent } from "../../controllers/API";
|
||||
import cloneFLowWithParent from "../../utils/storeUtils";
|
||||
import LoadingComponent from "../../components/loadingComponent";
|
||||
import useFlowStore from "../../stores/flowStore";
|
||||
import IOModal from "../../modals/IOModal";
|
||||
|
||||
export default function PlaygroundPage() {
|
||||
const currentFlow = useFlowsManagerStore((state) => state.currentFlow);
|
||||
const getFlowById = useFlowsManagerStore((state) => state.getFlowById);
|
||||
const setCurrentFlowId = useFlowsManagerStore((state) => state.setCurrentFlowId);
|
||||
const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId);
|
||||
const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow);
|
||||
const setNodes = useFlowStore((state) => state.setNodes);
|
||||
const setEdges = useFlowStore((state) => state.setEdges);
|
||||
const cleanFlowPool = useFlowStore((state) => state.CleanFlowPool);
|
||||
|
||||
const { id } = useParams();
|
||||
const [loading, setLoading] = useState(true);
|
||||
async function getFlowData() {
|
||||
const res = await getComponent(id!);
|
||||
const newFlow = cloneFLowWithParent(res, res.id, false, true);
|
||||
return newFlow;
|
||||
}
|
||||
|
||||
// Set flow tab id
|
||||
useEffect(() => {
|
||||
console.log("id", id);
|
||||
if (getFlowById(id!)) {
|
||||
setCurrentFlowId(id!);
|
||||
}
|
||||
else {
|
||||
getFlowData().then((flow) => {
|
||||
setCurrentFlow(flow);
|
||||
});
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
useEffect(() => {
|
||||
if (currentFlow) {
|
||||
setNodes(currentFlow?.data?.nodes ?? [], true);
|
||||
setEdges(currentFlow?.data?.edges ?? [], true);
|
||||
cleanFlowPool();
|
||||
setLoading(false);
|
||||
}
|
||||
return () => {
|
||||
setNodes([], true);
|
||||
setEdges([], true);
|
||||
cleanFlowPool();
|
||||
};
|
||||
}, [currentFlow]);
|
||||
|
||||
|
||||
return (
|
||||
<div className="w-full h-full flex flex-col align-middle items-center justify-center">
|
||||
{loading ? <div><LoadingComponent remSize={24}></LoadingComponent></div> :
|
||||
<IOModal open={true}setOpen={()=>{}} isPlayground>
|
||||
<></>
|
||||
</IOModal>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ import { SkeletonCardComponent } from "../../components/skeletonCardComponent";
|
|||
import { Button } from "../../components/ui/button";
|
||||
import { Input } from "../../components/ui/input";
|
||||
|
||||
import { Link, useParams } from "react-router-dom";
|
||||
import { Link, useNavigate, useParams } from "react-router-dom";
|
||||
import { TagsSelector } from "../../components/tagsSelectorComponent";
|
||||
import { Badge } from "../../components/ui/badge";
|
||||
import {
|
||||
|
|
@ -65,6 +65,8 @@ export default function StorePage(): JSX.Element {
|
|||
const [searchNow, setSearchNow] = useState("");
|
||||
const [selectFilter, setSelectFilter] = useState("all");
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
if (!loadingApiKey) {
|
||||
if (!hasApiKey) {
|
||||
|
|
@ -149,6 +151,11 @@ export default function StorePage(): JSX.Element {
|
|||
.catch((err) => {
|
||||
if (err.response?.status === 403 || err.response?.status === 401) {
|
||||
setValidApiKey(false);
|
||||
} else if (
|
||||
err.response?.data?.detail ===
|
||||
"Unexpected error: Object of type UUID is not JSON serializable"
|
||||
) {
|
||||
navigate("/store/" + inputText);
|
||||
} else {
|
||||
setSearchData([]);
|
||||
setTotalRowsCount(0);
|
||||
|
|
@ -371,6 +378,10 @@ export default function StorePage(): JSX.Element {
|
|||
data={item}
|
||||
authorized={validApiKey}
|
||||
disabled={loading}
|
||||
playground={
|
||||
item.last_tested_version?.includes("1.0.0") &&
|
||||
!item.is_component
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import ViewPage from "./pages/ViewPage";
|
|||
import DeleteAccountPage from "./pages/deleteAccountPage";
|
||||
import LoginPage from "./pages/loginPage";
|
||||
import SignUp from "./pages/signUpPage";
|
||||
import PlaygroundPage from "./pages/Playground";
|
||||
|
||||
const Router = () => {
|
||||
const navigate = useNavigate();
|
||||
|
|
@ -65,7 +66,13 @@ const Router = () => {
|
|||
</ProtectedRoute>
|
||||
}
|
||||
/>
|
||||
|
||||
<Route path="/playground/:id/">
|
||||
element={
|
||||
<Route path="" element={<ProtectedRoute>
|
||||
<PlaygroundPage />
|
||||
</ProtectedRoute>} />
|
||||
}
|
||||
</Route>
|
||||
<Route path="/flow/:id/">
|
||||
<Route
|
||||
path=""
|
||||
|
|
|
|||
|
|
@ -44,9 +44,12 @@ import { getInputsAndOutputs } from "../utils/storeUtils";
|
|||
import useAlertStore from "./alertStore";
|
||||
import { useDarkStore } from "./darkStore";
|
||||
import useFlowsManagerStore from "./flowsManagerStore";
|
||||
import FlowPage from "../pages/FlowPage";
|
||||
|
||||
// this is our useStore hook that we can use in our components to get parts of the store and call actions
|
||||
const useFlowStore = create<FlowStoreType>((set, get) => ({
|
||||
onFlowPage: false,
|
||||
setOnFlowPage:(FlowPage=>set({onFlowPage:FlowPage})),
|
||||
flowState: undefined,
|
||||
flowBuildStatus: {},
|
||||
nodes: [],
|
||||
|
|
@ -149,7 +152,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
edges: applyEdgeChanges(changes, get().edges),
|
||||
});
|
||||
},
|
||||
setNodes: (change) => {
|
||||
setNodes: (change,skipSave=false) => {
|
||||
let newChange = typeof change === "function" ? change(get().nodes) : change;
|
||||
let newEdges = cleanEdges(newChange, get().edges);
|
||||
const { inputs, outputs } = getInputsAndOutputs(newChange);
|
||||
|
|
@ -164,7 +167,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
});
|
||||
|
||||
const flowsManager = useFlowsManagerStore.getState();
|
||||
if (!get().isBuilding) {
|
||||
if (!get().isBuilding && !skipSave && get().onFlowPage) {
|
||||
flowsManager.autoSaveCurrentFlow(
|
||||
newChange,
|
||||
newEdges,
|
||||
|
|
@ -172,7 +175,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
);
|
||||
}
|
||||
},
|
||||
setEdges: (change) => {
|
||||
setEdges: (change,skipSave=false) => {
|
||||
let newChange = typeof change === "function" ? change(get().edges) : change;
|
||||
set({
|
||||
edges: newChange,
|
||||
|
|
@ -180,7 +183,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
});
|
||||
|
||||
const flowsManager = useFlowsManagerStore.getState();
|
||||
if (!get().isBuilding) {
|
||||
if (!get().isBuilding && !skipSave && get().onFlowPage) {
|
||||
flowsManager.autoSaveCurrentFlow(
|
||||
get().nodes,
|
||||
newChange,
|
||||
|
|
@ -560,6 +563,8 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
useFlowStore.getState().updateBuildStatus(idList, BuildStatus.BUILDING);
|
||||
},
|
||||
onValidateNodes: validateSubgraph,
|
||||
nodes: !get().onFlowPage ? get().nodes : undefined,
|
||||
edges: !get().onFlowPage ? get().edges : undefined,
|
||||
});
|
||||
get().setIsBuilding(false);
|
||||
get().revertBuiltStatusFromBuilding();
|
||||
|
|
|
|||
|
|
@ -46,6 +46,16 @@ const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
|
|||
set({ examples });
|
||||
},
|
||||
currentFlowId: "",
|
||||
setCurrentFlow: (flow: FlowType) => {
|
||||
set((state) => ({
|
||||
currentFlow: flow,
|
||||
currentFlowId: flow.id,
|
||||
}));
|
||||
|
||||
},
|
||||
getFlowById: (id: string) => {
|
||||
return get().flows.find((flow) => flow.id === id);
|
||||
},
|
||||
setCurrentFlowId: (currentFlowId: string) => {
|
||||
set((state) => ({
|
||||
currentFlowId,
|
||||
|
|
|
|||
|
|
@ -594,6 +594,8 @@ export type IOModalPropsType = {
|
|||
open: boolean;
|
||||
setOpen: (open: boolean) => void;
|
||||
disable?: boolean;
|
||||
isPlayground?: boolean;
|
||||
cleanOnClose?: boolean;
|
||||
};
|
||||
|
||||
export type buttonBoxPropsType = {
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@ export type FlowPoolType = {
|
|||
};
|
||||
|
||||
export type FlowStoreType = {
|
||||
onFlowPage: boolean;
|
||||
setOnFlowPage: (onFlowPage: boolean) => void;
|
||||
flowPool: FlowPoolType;
|
||||
inputs: Array<{ type: string; id: string; displayName: string }>;
|
||||
outputs: Array<{ type: string; id: string; displayName: string }>;
|
||||
|
|
@ -74,8 +76,8 @@ export type FlowStoreType = {
|
|||
edges: Edge[];
|
||||
onNodesChange: OnNodesChange;
|
||||
onEdgesChange: OnEdgesChange;
|
||||
setNodes: (update: Node[] | ((oldState: Node[]) => Node[])) => void;
|
||||
setEdges: (update: Edge[] | ((oldState: Edge[]) => Edge[])) => void;
|
||||
setNodes: (update: Node[] | ((oldState: Node[]) => Node[]),skipSave?:boolean) => void;
|
||||
setEdges: (update: Edge[] | ((oldState: Edge[]) => Edge[]),skipSave?:boolean) => void;
|
||||
setNode: (id: string, update: Node | ((oldState: Node) => Node)) => void;
|
||||
getNode: (id: string) => Node | undefined;
|
||||
deleteNode: (nodeId: string | Array<string>) => void;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { Edge, Node, Viewport, XYPosition } from "reactflow";
|
|||
import { FlowType } from "../../flow";
|
||||
|
||||
export type FlowsManagerStoreType = {
|
||||
getFlowById: (id: string) => FlowType | undefined;
|
||||
flows: Array<FlowType>;
|
||||
setFlows: (flows: FlowType[]) => void;
|
||||
currentFlow: FlowType | undefined;
|
||||
|
|
@ -50,6 +51,7 @@ export type FlowsManagerStoreType = {
|
|||
takeSnapshot: () => void;
|
||||
examples: Array<FlowType>;
|
||||
setExamples: (examples: FlowType[]) => void;
|
||||
setCurrentFlow: (flow: FlowType) => void;
|
||||
};
|
||||
|
||||
export type UseUndoRedoOptions = {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import useAlertStore from "../stores/alertStore";
|
|||
import useFlowStore from "../stores/flowStore";
|
||||
import { VertexBuildTypeAPI } from "../types/api";
|
||||
import { VertexLayerElementType } from "../types/zustand/flow";
|
||||
import { Edge, Node } from "reactflow";
|
||||
|
||||
type BuildVerticesParams = {
|
||||
flowId: string; // Assuming FlowType is the type for your flow
|
||||
|
|
@ -21,6 +22,8 @@ type BuildVerticesParams = {
|
|||
onBuildError?: (title, list, idList: VertexLayerElementType[]) => void;
|
||||
onBuildStart?: (idList: VertexLayerElementType[]) => void;
|
||||
onValidateNodes?: (nodes: string[]) => void;
|
||||
nodes?: Node[];
|
||||
edges?: Edge[];
|
||||
};
|
||||
|
||||
function getInactiveVertexData(vertexId: string): VertexBuildTypeAPI {
|
||||
|
|
@ -48,7 +51,9 @@ function getInactiveVertexData(vertexId: string): VertexBuildTypeAPI {
|
|||
export async function updateVerticesOrder(
|
||||
flowId: string,
|
||||
startNodeId?: string | null,
|
||||
stopNodeId?: string | null
|
||||
stopNodeId?: string | null,
|
||||
nodes?:Node[],
|
||||
edges?:Edge[]
|
||||
): Promise<{
|
||||
verticesLayers: VertexLayerElementType[][];
|
||||
verticesIds: string[];
|
||||
|
|
@ -59,7 +64,7 @@ export async function updateVerticesOrder(
|
|||
const setErrorData = useAlertStore.getState().setErrorData;
|
||||
let orderResponse;
|
||||
try {
|
||||
orderResponse = await getVerticesOrder(flowId, startNodeId, stopNodeId);
|
||||
orderResponse = await getVerticesOrder(flowId, startNodeId, stopNodeId, nodes, edges);
|
||||
} catch (error: any) {
|
||||
setErrorData({
|
||||
title: "Oops! Looks like you missed something",
|
||||
|
|
@ -101,6 +106,8 @@ export async function buildVertices({
|
|||
onBuildError,
|
||||
onBuildStart,
|
||||
onValidateNodes,
|
||||
nodes,
|
||||
edges,
|
||||
}: BuildVerticesParams) {
|
||||
let verticesBuild = useFlowStore.getState().verticesBuild;
|
||||
// if startNodeId and stopNodeId are provided
|
||||
|
|
@ -113,7 +120,9 @@ export async function buildVertices({
|
|||
let verticesOrderResponse = await updateVerticesOrder(
|
||||
flowId,
|
||||
startNodeId,
|
||||
stopNodeId
|
||||
stopNodeId,
|
||||
nodes,
|
||||
edges
|
||||
);
|
||||
if (onValidateNodes) {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { cloneDeep } from "lodash";
|
||||
import { cloneDeep, uniqueId } from "lodash";
|
||||
import { Node } from "reactflow";
|
||||
import { FlowType, NodeDataType } from "../types/flow";
|
||||
import { isInputNode, isOutputNode } from "./reactflowUtils";
|
||||
|
|
@ -6,11 +6,17 @@ import { isInputNode, isOutputNode } from "./reactflowUtils";
|
|||
export default function cloneFLowWithParent(
|
||||
flow: FlowType,
|
||||
parent: string,
|
||||
is_component: boolean
|
||||
is_component: boolean,
|
||||
keepId=false
|
||||
) {
|
||||
let childFLow = cloneDeep(flow);
|
||||
childFLow.parent = parent;
|
||||
childFLow.id = "";
|
||||
if(!keepId){
|
||||
childFLow.id = "";
|
||||
}
|
||||
else{
|
||||
childFLow.id = uniqueId()+"-"+childFLow.id;
|
||||
}
|
||||
childFLow.is_component = is_component;
|
||||
return childFLow;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -136,6 +136,7 @@ import {
|
|||
X,
|
||||
XCircle,
|
||||
Zap,
|
||||
PlaySquare
|
||||
} from "lucide-react";
|
||||
import { FaApple, FaGithub } from "react-icons/fa";
|
||||
import { AWSIcon } from "../icons/AWS";
|
||||
|
|
@ -144,7 +145,7 @@ import { AnthropicIcon } from "../icons/Anthropic";
|
|||
import { AstraDBIcon } from "../icons/AstraDB";
|
||||
import { AzureIcon } from "../icons/Azure";
|
||||
import { BingIcon } from "../icons/Bing";
|
||||
import { BotMessageSquareIcon } from "../icons/BotMessageSquare";
|
||||
import { BotMessageSquareIcon} from "../icons/BotMessageSquare";
|
||||
import { ChromaIcon } from "../icons/ChromaIcon";
|
||||
import { CohereIcon } from "../icons/Cohere";
|
||||
import { ElasticsearchIcon } from "../icons/ElasticsearchStore";
|
||||
|
|
@ -292,7 +293,8 @@ export const nodeIconsLucide: iconsType = {
|
|||
ListFlows: Group,
|
||||
ClearMessageHistory: FileClock,
|
||||
Python: PythonIcon,
|
||||
ChatOutput: BotMessageSquareIcon,
|
||||
ChatOutput: MessagesSquare,
|
||||
BotMessageSquareIcon,
|
||||
ChatInput: MessagesSquare,
|
||||
inputs: Download,
|
||||
outputs: Upload,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue