Merge branch 'dev' into refactor/utils
This commit is contained in:
commit
1dccd33f48
26 changed files with 586 additions and 332 deletions
|
|
@ -1,3 +1,4 @@
|
|||
import os
|
||||
import warnings
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
|
@ -140,7 +141,10 @@ def get_file_path_value(file_path):
|
|||
# If the path is not in the cache dir, return empty string
|
||||
# This is to prevent access to files outside the cache dir
|
||||
# If the path is not a file, return empty string
|
||||
if not path.exists() or not str(path).startswith(user_cache_dir("langflow", "langflow")):
|
||||
if not str(path).startswith(user_cache_dir("langflow", "langflow")):
|
||||
return ""
|
||||
|
||||
if not path.exists():
|
||||
return ""
|
||||
return file_path
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ async def try_running_celery_task(vertex, user_id):
|
|||
|
||||
@router.post("/build/{flow_id}/vertices", response_model=VerticesOrderResponse)
|
||||
async def retrieve_vertices_order(
|
||||
flow_id: str,
|
||||
flow_id: uuid.UUID,
|
||||
data: Optional[Annotated[Optional[FlowDataRequest], Body(embed=True)]] = None,
|
||||
stop_component_id: Optional[str] = None,
|
||||
start_component_id: Optional[str] = None,
|
||||
|
|
@ -78,6 +78,7 @@ async def retrieve_vertices_order(
|
|||
HTTPException: If there is an error checking the build status.
|
||||
"""
|
||||
try:
|
||||
flow_id = str(flow_id)
|
||||
# First, we need to check if the flow_id is in the cache
|
||||
if not data:
|
||||
graph = await build_and_cache_graph_from_db(flow_id=flow_id, session=session, chat_service=chat_service)
|
||||
|
|
@ -119,7 +120,7 @@ async def retrieve_vertices_order(
|
|||
|
||||
@router.post("/build/{flow_id}/vertices/{vertex_id}")
|
||||
async def build_vertex(
|
||||
flow_id: str,
|
||||
flow_id: uuid.UUID,
|
||||
vertex_id: str,
|
||||
background_tasks: BackgroundTasks,
|
||||
inputs: Annotated[Optional[InputValueRequest], Body(embed=True)] = None,
|
||||
|
|
@ -143,8 +144,8 @@ async def build_vertex(
|
|||
HTTPException: If there is an error building the vertex.
|
||||
|
||||
"""
|
||||
flow_id = str(flow_id)
|
||||
|
||||
start_time = time.perf_counter()
|
||||
next_runnable_vertices = []
|
||||
top_level_vertices = []
|
||||
try:
|
||||
|
|
@ -158,8 +159,7 @@ async def build_vertex(
|
|||
)
|
||||
else:
|
||||
graph = cache.get("result")
|
||||
result_data_response = ResultDataResponse(results={})
|
||||
duration = ""
|
||||
ResultDataResponse(results={})
|
||||
vertex = graph.get_vertex(vertex_id)
|
||||
try:
|
||||
lock = chat_service._cache_locks[flow_id]
|
||||
|
|
@ -240,7 +240,7 @@ async def build_vertex(
|
|||
|
||||
@router.get("/build/{flow_id}/{vertex_id}/stream", response_class=StreamingResponse)
|
||||
async def build_vertex_stream(
|
||||
flow_id: str,
|
||||
flow_id: uuid.UUID,
|
||||
vertex_id: str,
|
||||
session_id: Optional[str] = None,
|
||||
chat_service: "ChatService" = Depends(get_chat_service),
|
||||
|
|
@ -272,6 +272,7 @@ async def build_vertex_stream(
|
|||
HTTPException: If an error occurs while building the vertex.
|
||||
"""
|
||||
try:
|
||||
flow_id = str(flow_id)
|
||||
|
||||
async def stream_vertex():
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
from http import HTTPStatus
|
||||
from typing import Annotated, List, Optional, Union
|
||||
from uuid import UUID
|
||||
|
||||
import sqlalchemy as sa
|
||||
from fastapi import APIRouter, Body, Depends, HTTPException, UploadFile, status
|
||||
|
|
@ -54,7 +55,7 @@ def get_all(
|
|||
@router.post("/run/{flow_id}", response_model=RunResponse, response_model_exclude_none=True)
|
||||
async def simplified_run_flow(
|
||||
db: Annotated[Session, Depends(get_session)],
|
||||
flow_id: str,
|
||||
flow_id: UUID,
|
||||
input_request: SimplifiedAPIRequest = SimplifiedAPIRequest(),
|
||||
stream: bool = False,
|
||||
api_key_user: User = Depends(api_key_security),
|
||||
|
|
@ -111,6 +112,7 @@ async def simplified_run_flow(
|
|||
session_id = input_request.session_id
|
||||
|
||||
try:
|
||||
flow_id = str(flow_id)
|
||||
task_result: List[RunOutputs] = []
|
||||
artifacts = {}
|
||||
if input_request.session_id:
|
||||
|
|
@ -187,7 +189,7 @@ async def simplified_run_flow(
|
|||
@router.post("/run/advanced/{flow_id}", response_model=RunResponse, response_model_exclude_none=True)
|
||||
async def experimental_run_flow(
|
||||
session: Annotated[Session, Depends(get_session)],
|
||||
flow_id: str,
|
||||
flow_id: UUID,
|
||||
inputs: Optional[List[InputValueRequest]] = [InputValueRequest(components=[], input_value="")],
|
||||
outputs: Optional[List[str]] = [],
|
||||
tweaks: Annotated[Optional[Tweaks], Body(embed=True)] = None, # noqa: F821
|
||||
|
|
@ -235,6 +237,7 @@ async def experimental_run_flow(
|
|||
This endpoint facilitates complex flow executions with customized inputs, outputs, and configurations, catering to diverse application requirements.
|
||||
"""
|
||||
try:
|
||||
flow_id = str(flow_id)
|
||||
if outputs is None:
|
||||
outputs = []
|
||||
|
||||
|
|
@ -357,9 +360,10 @@ async def get_task_status(task_id: str):
|
|||
)
|
||||
async def create_upload_file(
|
||||
file: UploadFile,
|
||||
flow_id: str,
|
||||
flow_id: UUID,
|
||||
):
|
||||
try:
|
||||
flow_id = str(flow_id)
|
||||
file_path = save_uploaded_file(file, folder_name=flow_id)
|
||||
|
||||
return UploadFileResponse(
|
||||
|
|
@ -400,23 +404,6 @@ async def custom_component(
|
|||
return built_frontend_node
|
||||
|
||||
|
||||
@router.post("/custom_component/reload", status_code=HTTPStatus.OK)
|
||||
async def reload_custom_component(path: str, user: User = Depends(get_current_active_user)):
|
||||
from langflow.interface.custom.utils import build_custom_component_template
|
||||
|
||||
try:
|
||||
reader = DirectoryReader("")
|
||||
valid, content = reader.process_file(path)
|
||||
if not valid:
|
||||
raise ValueError(content)
|
||||
|
||||
extractor = CustomComponent(code=content)
|
||||
frontend_node, _ = build_custom_component_template(extractor, user_id=user.id)
|
||||
return frontend_node
|
||||
except Exception as exc:
|
||||
raise HTTPException(status_code=400, detail=str(exc))
|
||||
|
||||
|
||||
@router.post("/custom_component/update", status_code=HTTPStatus.OK)
|
||||
async def custom_component_update(
|
||||
code_request: UpdateCustomComponentRequest,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import hashlib
|
||||
from http import HTTPStatus
|
||||
from io import BytesIO
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, UploadFile
|
||||
from fastapi.responses import StreamingResponse
|
||||
|
|
@ -20,10 +21,11 @@ router = APIRouter(tags=["Files"], prefix="/files")
|
|||
# then finds it in the database and returns it while
|
||||
# using the current user as the owner
|
||||
def get_flow_id(
|
||||
flow_id: str,
|
||||
flow_id: UUID,
|
||||
current_user=Depends(get_current_active_user),
|
||||
session=Depends(get_session),
|
||||
):
|
||||
flow_id = str(flow_id)
|
||||
# AttributeError: 'SelectOfScalar' object has no attribute 'first'
|
||||
flow = session.get(Flow, flow_id)
|
||||
if not flow:
|
||||
|
|
@ -36,10 +38,11 @@ def get_flow_id(
|
|||
@router.post("/upload/{flow_id}", status_code=HTTPStatus.CREATED)
|
||||
async def upload_file(
|
||||
file: UploadFile,
|
||||
flow_id: str = Depends(get_flow_id),
|
||||
flow_id: UUID = Depends(get_flow_id),
|
||||
storage_service: StorageService = Depends(get_storage_service),
|
||||
):
|
||||
try:
|
||||
flow_id = str(flow_id)
|
||||
file_content = await file.read()
|
||||
file_name = file.filename or hashlib.sha256(file_content).hexdigest()
|
||||
folder = flow_id
|
||||
|
|
@ -50,8 +53,9 @@ async def upload_file(
|
|||
|
||||
|
||||
@router.get("/download/{flow_id}/{file_name}")
|
||||
async def download_file(file_name: str, flow_id: str, storage_service: StorageService = Depends(get_storage_service)):
|
||||
async def download_file(file_name: str, flow_id: UUID, storage_service: StorageService = Depends(get_storage_service)):
|
||||
try:
|
||||
flow_id = str(flow_id)
|
||||
extension = file_name.split(".")[-1]
|
||||
|
||||
if not extension:
|
||||
|
|
@ -74,9 +78,10 @@ async def download_file(file_name: str, flow_id: str, storage_service: StorageSe
|
|||
|
||||
|
||||
@router.get("/images/{flow_id}/{file_name}")
|
||||
async def download_image(file_name: str, flow_id: str, storage_service: StorageService = Depends(get_storage_service)):
|
||||
async def download_image(file_name: str, flow_id: UUID, storage_service: StorageService = Depends(get_storage_service)):
|
||||
try:
|
||||
extension = file_name.split(".")[-1]
|
||||
flow_id = str(flow_id)
|
||||
|
||||
if not extension:
|
||||
raise HTTPException(status_code=500, detail=f"Extension not found for file {file_name}")
|
||||
|
|
@ -96,9 +101,10 @@ async def download_image(file_name: str, flow_id: str, storage_service: StorageS
|
|||
|
||||
@router.get("/list/{flow_id}")
|
||||
async def list_files(
|
||||
flow_id: str = Depends(get_flow_id), storage_service: StorageService = Depends(get_storage_service)
|
||||
flow_id: UUID = Depends(get_flow_id), storage_service: StorageService = Depends(get_storage_service)
|
||||
):
|
||||
try:
|
||||
flow_id = str(flow_id)
|
||||
files = await storage_service.list_files(flow_id=flow_id)
|
||||
return {"files": files}
|
||||
except Exception as e:
|
||||
|
|
@ -107,9 +113,10 @@ async def list_files(
|
|||
|
||||
@router.delete("/delete/{flow_id}/{file_name}")
|
||||
async def delete_file(
|
||||
file_name: str, flow_id: str = Depends(get_flow_id), storage_service: StorageService = Depends(get_storage_service)
|
||||
file_name: str, flow_id: UUID = Depends(get_flow_id), storage_service: StorageService = Depends(get_storage_service)
|
||||
):
|
||||
try:
|
||||
flow_id = str(flow_id)
|
||||
await storage_service.delete_file(flow_id=flow_id, file_name=file_name)
|
||||
return {"message": f"File {file_name} deleted successfully"}
|
||||
except Exception as e:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,95 @@
|
|||
from typing import Optional, cast
|
||||
|
||||
from langchain_astradb.chat_message_histories import AstraDBChatMessageHistory
|
||||
|
||||
from langflow.base.memory.memory import BaseMemoryComponent
|
||||
from langflow.field_typing import Text
|
||||
from langflow.schema.schema import Record
|
||||
|
||||
|
||||
class AstraDBMessageReaderComponent(BaseMemoryComponent):
|
||||
display_name = "Astra DB Message Reader"
|
||||
description = "Retrieves stored chat messages from Astra DB."
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"session_id": {
|
||||
"display_name": "Session ID",
|
||||
"info": "Session ID of the chat history.",
|
||||
"input_types": ["Text"],
|
||||
},
|
||||
"collection_name": {
|
||||
"display_name": "Collection Name",
|
||||
"info": "Collection name for Astra DB.",
|
||||
"input_types": ["Text"],
|
||||
},
|
||||
"token": {
|
||||
"display_name": "Astra DB Application Token",
|
||||
"info": "Token for the Astra DB instance.",
|
||||
"password": True,
|
||||
},
|
||||
"api_endpoint": {
|
||||
"display_name": "Astra DB API Endpoint",
|
||||
"info": "API Endpoint for the Astra DB instance.",
|
||||
"password": True,
|
||||
},
|
||||
"namespace": {
|
||||
"display_name": "Namespace",
|
||||
"info": "Namespace for the Astra DB instance.",
|
||||
"input_types": ["Text"],
|
||||
"advanced": True,
|
||||
},
|
||||
}
|
||||
|
||||
def get_messages(self, **kwargs) -> list[Record]:
|
||||
"""
|
||||
Retrieves messages from the AstraDBChatMessageHistory memory.
|
||||
|
||||
Args:
|
||||
memory (AstraDBChatMessageHistory): The AstraDBChatMessageHistory instance to retrieve messages from.
|
||||
|
||||
Returns:
|
||||
list[Record]: A list of Record objects representing the search results.
|
||||
"""
|
||||
memory: AstraDBChatMessageHistory = cast(
|
||||
AstraDBChatMessageHistory, kwargs.get("memory")
|
||||
)
|
||||
if not memory:
|
||||
raise ValueError("AstraDBChatMessageHistory instance is required.")
|
||||
|
||||
# Get messages from the memory
|
||||
messages = memory.messages
|
||||
results = [Record.from_lc_message(message) for message in messages]
|
||||
|
||||
return list(results)
|
||||
|
||||
def build(
|
||||
self,
|
||||
session_id: Text,
|
||||
collection_name: str,
|
||||
token: str,
|
||||
api_endpoint: str,
|
||||
namespace: Optional[str] = None,
|
||||
) -> list[Record]:
|
||||
try:
|
||||
from langchain_community.chat_message_histories.astradb import (
|
||||
AstraDBChatMessageHistory,
|
||||
)
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"Could not import langchain Astra DB integration package. "
|
||||
"Please install it with `pip install langchain-astradb`."
|
||||
)
|
||||
|
||||
memory = AstraDBChatMessageHistory(
|
||||
session_id=session_id,
|
||||
collection_name=collection_name,
|
||||
token=token,
|
||||
api_endpoint=api_endpoint,
|
||||
namespace=namespace,
|
||||
)
|
||||
|
||||
records = self.get_messages(memory=memory)
|
||||
self.status = records
|
||||
|
||||
return records
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
from typing import Optional
|
||||
|
||||
from langflow.base.memory.memory import BaseMemoryComponent
|
||||
from langflow.field_typing import Text
|
||||
from langflow.schema.schema import Record
|
||||
|
||||
from langchain_core.messages import BaseMessage
|
||||
from langchain_community.chat_message_histories.astradb import AstraDBChatMessageHistory
|
||||
|
||||
|
||||
class AstraDBMessageWriterComponent(BaseMemoryComponent):
|
||||
display_name = "Astra DB Message Writer"
|
||||
description = "Writes a message to Astra DB."
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"input_value": {
|
||||
"display_name": "Input Record",
|
||||
"info": "Record to write to Astra DB.",
|
||||
},
|
||||
"session_id": {
|
||||
"display_name": "Session ID",
|
||||
"info": "Session ID of the chat history.",
|
||||
"input_types": ["Text"],
|
||||
},
|
||||
"collection_name": {
|
||||
"display_name": "Collection Name",
|
||||
"info": "Collection name for Astra DB.",
|
||||
"input_types": ["Text"],
|
||||
},
|
||||
"token": {
|
||||
"display_name": "Astra DB Application Token",
|
||||
"info": "Token for the Astra DB instance.",
|
||||
"password": True,
|
||||
},
|
||||
"api_endpoint": {
|
||||
"display_name": "Astra DB API Endpoint",
|
||||
"info": "API Endpoint for the Astra DB instance.",
|
||||
"password": True,
|
||||
},
|
||||
"namespace": {
|
||||
"display_name": "Namespace",
|
||||
"info": "Namespace for the Astra DB instance.",
|
||||
"input_types": ["Text"],
|
||||
"advanced": True,
|
||||
},
|
||||
}
|
||||
|
||||
def add_message(
|
||||
self,
|
||||
sender: str,
|
||||
sender_name: str,
|
||||
text: Text,
|
||||
session_id: str,
|
||||
metadata: Optional[dict] = None,
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
Adds a message to the AstraDBChatMessageHistory 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 AstraDBChatMessageHistory instance is not provided.
|
||||
|
||||
"""
|
||||
memory: AstraDBChatMessageHistory | None = kwargs.pop("memory", None)
|
||||
if memory is None:
|
||||
raise ValueError("AstraDBChatMessageHistory instance is required.")
|
||||
|
||||
text_list = [BaseMessage(
|
||||
content=text,
|
||||
sender=sender,
|
||||
sender_name=sender_name,
|
||||
metadata=metadata,
|
||||
session_id=session_id,
|
||||
)]
|
||||
|
||||
memory.add_messages(text_list)
|
||||
|
||||
def build(
|
||||
self,
|
||||
input_value: Record,
|
||||
session_id: Text,
|
||||
collection_name: str,
|
||||
token: str,
|
||||
api_endpoint: str,
|
||||
namespace: Optional[str] = None,
|
||||
) -> Record:
|
||||
try:
|
||||
from langchain_community.chat_message_histories.astradb import (
|
||||
AstraDBChatMessageHistory,
|
||||
)
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"Could not import langchain Astra DB integration package. "
|
||||
"Please install it with `pip install langchain-astradb`."
|
||||
)
|
||||
|
||||
memory = AstraDBChatMessageHistory(
|
||||
session_id=session_id,
|
||||
collection_name=collection_name,
|
||||
token=token,
|
||||
api_endpoint=api_endpoint,
|
||||
namespace=namespace,
|
||||
)
|
||||
|
||||
self.add_message(**input_value.data, memory=memory)
|
||||
self.status = f"Added message to Astra DB memory for session {session_id}"
|
||||
|
||||
return input_value
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
from typing import List, Optional
|
||||
|
||||
from langflow.components.vectorstores.base.model import LCVectorStoreComponent
|
||||
from langflow.components.vectorstores.Couchbase import CouchbaseComponent
|
||||
from langflow.field_typing import Embeddings, NestedDict, Text
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
class CouchbaseSearchComponent(LCVectorStoreComponent):
|
||||
display_name = "Couchbase Search"
|
||||
description = "Search a Couchbase Vector Store for similar documents."
|
||||
documentation = "https://python.langchain.com/docs/integrations/vectorstores/couchbase"
|
||||
icon = "Couchbase"
|
||||
field_order = [
|
||||
"couchbase_connection_string",
|
||||
"couchbase_username",
|
||||
"couchbase_password",
|
||||
"bucket_name",
|
||||
"scope_name",
|
||||
"collection_name",
|
||||
"index_name",
|
||||
]
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"input_value": {"display_name": "Input"},
|
||||
"embedding": {"display_name": "Embedding"},
|
||||
"couchbase_connection_string": {"display_name": "Couchbase Cluster connection string","required": True},
|
||||
"couchbase_username": {"display_name": "Couchbase username","required": True},
|
||||
"couchbase_password": {
|
||||
"display_name": "Couchbase password",
|
||||
"password": True,
|
||||
"required": True
|
||||
},
|
||||
"bucket_name": {"display_name": "Bucket Name","required": True},
|
||||
"scope_name": {"display_name": "Scope Name","required": True},
|
||||
"collection_name": {"display_name": "Collection Name","required": True},
|
||||
"index_name": {"display_name": "Index Name","required": True},
|
||||
"number_of_results": {
|
||||
"display_name": "Number of Results",
|
||||
"info": "Number of results to return.",
|
||||
"advanced": True,
|
||||
},
|
||||
}
|
||||
|
||||
def build( # type: ignore[override]
|
||||
self,
|
||||
input_value: Text,
|
||||
embedding: Embeddings,
|
||||
number_of_results: int = 4,
|
||||
bucket_name: str = "",
|
||||
scope_name: str = "",
|
||||
collection_name: str = "",
|
||||
index_name: str = "",
|
||||
couchbase_connection_string: str = "",
|
||||
couchbase_username: str = "",
|
||||
couchbase_password: str = "",
|
||||
) -> List[Record]:
|
||||
vector_store = CouchbaseComponent().build(
|
||||
couchbase_connection_string=couchbase_connection_string,
|
||||
couchbase_username=couchbase_username,
|
||||
couchbase_password=couchbase_password,
|
||||
bucket_name=bucket_name,
|
||||
scope_name=scope_name,
|
||||
collection_name=collection_name,
|
||||
embedding=embedding,
|
||||
index_name=index_name,
|
||||
)
|
||||
if not vector_store:
|
||||
raise ValueError("Failed to create Couchbase Vector Store")
|
||||
return self.search_with_vector_store(
|
||||
vector_store=vector_store, input_value=input_value, search_type="similarity", k=number_of_results
|
||||
)
|
||||
|
|
@ -9,10 +9,12 @@ from .SupabaseVectorStoreSearch import SupabaseSearchComponent
|
|||
from .VectaraSearch import VectaraSearchComponent
|
||||
from .WeaviateSearch import WeaviateSearchVectorStore
|
||||
from .pgvectorSearch import PGVectorSearchComponent
|
||||
from .Couchbase import CouchbaseSearchComponent # type: ignore
|
||||
|
||||
__all__ = [
|
||||
"AstraDBSearchComponent",
|
||||
"ChromaSearchComponent",
|
||||
"CouchbaseSearchComponent",
|
||||
"FAISSSearchComponent",
|
||||
"MongoDBAtlasSearchComponent",
|
||||
"PineconeSearchComponent",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,95 @@
|
|||
from typing import List, Optional, Union
|
||||
|
||||
from langchain.schema import BaseRetriever
|
||||
|
||||
from langchain_community.vectorstores import CouchbaseVectorStore
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import Embeddings, VectorStore
|
||||
from langflow.schema import Record
|
||||
|
||||
from datetime import timedelta
|
||||
|
||||
from couchbase.auth import PasswordAuthenticator # type: ignore
|
||||
from couchbase.cluster import Cluster # type: ignore
|
||||
from couchbase.options import ClusterOptions # type: ignore
|
||||
|
||||
|
||||
class CouchbaseComponent(CustomComponent):
|
||||
display_name = "Couchbase"
|
||||
description = "Construct a `Couchbase Vector Search` vector store from raw documents."
|
||||
documentation = "https://python.langchain.com/docs/integrations/vectorstores/couchbase"
|
||||
icon = "Couchbase"
|
||||
field_order = [
|
||||
"couchbase_connection_string",
|
||||
"couchbase_username",
|
||||
"couchbase_password",
|
||||
"bucket_name",
|
||||
"scope_name",
|
||||
"collection_name",
|
||||
"index_name",
|
||||
]
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"inputs": {"display_name": "Input", "input_types": ["Document", "Record"]},
|
||||
"embedding": {"display_name": "Embedding"},
|
||||
"couchbase_connection_string": {"display_name": "Couchbase Cluster connection string","required": True},
|
||||
"couchbase_username": {"display_name": "Couchbase username","required": True},
|
||||
"couchbase_password": {
|
||||
"display_name": "Couchbase password",
|
||||
"password": True,
|
||||
"required": True
|
||||
},
|
||||
"bucket_name": {"display_name": "Bucket Name","required": True},
|
||||
"scope_name": {"display_name": "Scope Name","required": True},
|
||||
"collection_name": {"display_name": "Collection Name","required": True},
|
||||
"index_name": {"display_name": "Index Name","required": True},
|
||||
}
|
||||
|
||||
def build(
|
||||
self,
|
||||
embedding: Embeddings,
|
||||
inputs: Optional[List[Record]] = None,
|
||||
bucket_name: str = "",
|
||||
scope_name: str = "",
|
||||
collection_name: str = "",
|
||||
index_name: str = "",
|
||||
couchbase_connection_string: str = "",
|
||||
couchbase_username: str = "",
|
||||
couchbase_password: str = "",
|
||||
) -> Union[VectorStore, BaseRetriever]:
|
||||
try:
|
||||
auth = PasswordAuthenticator(couchbase_username, couchbase_password)
|
||||
options = ClusterOptions(auth)
|
||||
cluster = Cluster(couchbase_connection_string, options)
|
||||
|
||||
cluster.wait_until_ready(timedelta(seconds=5))
|
||||
except Exception as e:
|
||||
raise ValueError(f"Failed to connect to Couchbase: {e}")
|
||||
documents = []
|
||||
for _input in inputs or []:
|
||||
if isinstance(_input, Record):
|
||||
documents.append(_input.to_lc_document())
|
||||
else:
|
||||
documents.append(_input)
|
||||
if documents:
|
||||
vector_store = CouchbaseVectorStore.from_documents(
|
||||
documents=documents,
|
||||
cluster=cluster,
|
||||
bucket_name=bucket_name,
|
||||
scope_name=scope_name,
|
||||
collection_name=collection_name,
|
||||
embedding=embedding,
|
||||
index_name=index_name,
|
||||
)
|
||||
else:
|
||||
vector_store = CouchbaseVectorStore(
|
||||
cluster=cluster,
|
||||
bucket_name=bucket_name,
|
||||
scope_name=scope_name,
|
||||
collection_name=collection_name,
|
||||
embedding=embedding,
|
||||
index_name=index_name,
|
||||
)
|
||||
return vector_store
|
||||
|
|
@ -9,10 +9,12 @@ from .SupabaseVectorStore import SupabaseComponent
|
|||
from .Vectara import VectaraComponent
|
||||
from .Weaviate import WeaviateVectorStoreComponent
|
||||
from .pgvector import PGVectorComponent
|
||||
from .Couchbase import CouchbaseComponent
|
||||
|
||||
__all__ = [
|
||||
"AstraDBVectorStoreComponent",
|
||||
"ChromaComponent",
|
||||
"CouchbaseComponent",
|
||||
"FAISSComponent",
|
||||
"MongoDBAtlasComponent",
|
||||
"PineconeComponent",
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ export default function InputGlobalComponent({
|
|||
useEffect(() => {
|
||||
if (data.node?.template[name])
|
||||
if (
|
||||
globalVariablesEntries &&
|
||||
!globalVariablesEntries.includes(data.node?.template[name].value) &&
|
||||
data.node?.template[name].load_from_db
|
||||
) {
|
||||
|
|
@ -138,6 +139,7 @@ export default function InputGlobalComponent({
|
|||
)}
|
||||
selectedOption={
|
||||
data?.node?.template[name].load_from_db &&
|
||||
globalVariablesEntries &&
|
||||
globalVariablesEntries.includes(data?.node?.template[name].value ?? "")
|
||||
? data?.node?.template[name].value
|
||||
: ""
|
||||
|
|
|
|||
17
src/frontend/src/icons/Couchbase/Couchbase.jsx
Normal file
17
src/frontend/src/icons/Couchbase/Couchbase.jsx
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
const SvgCouchbaseIcon = (props) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
preserveAspectRatio="xMidYMid"
|
||||
viewBox="0 0 256 256"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
fill="#ED2226"
|
||||
d="M128 0C57.426 0 0 57.233 0 128c0 70.574 57.233 128 128 128 70.574 0 128-57.233 128-128S198.574 0 128 0zm86.429 150.429c0 7.734-4.447 14.502-13.148 16.048-15.082 2.707-46.792 4.254-73.281 4.254-26.49 0-58.2-1.547-73.281-4.254-8.7-1.546-13.148-8.314-13.148-16.048v-49.885c0-7.734 5.994-14.888 13.148-16.049 4.447-.773 14.888-1.546 23.01-1.546 3.093 0 5.606 2.32 5.606 5.994v34.997l44.858-.967 44.858.967V88.943c0-3.674 2.514-5.994 5.608-5.994 8.12 0 18.562.773 23.009 1.546 7.347 1.16 13.148 8.315 13.148 16.049-.387 16.435-.387 33.257-.387 49.885z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default SvgCouchbaseIcon;
|
||||
1
src/frontend/src/icons/Couchbase/couchbase.svg
Normal file
1
src/frontend/src/icons/Couchbase/couchbase.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="2500" height="2500" preserveAspectRatio="xMidYMid" viewBox="0 0 256 256" id="couchbase"><path fill="#ED2226" d="M128 0C57.426 0 0 57.233 0 128c0 70.574 57.233 128 128 128 70.574 0 128-57.233 128-128S198.574 0 128 0zm86.429 150.429c0 7.734-4.447 14.502-13.148 16.048-15.082 2.707-46.792 4.254-73.281 4.254-26.49 0-58.2-1.547-73.281-4.254-8.7-1.546-13.148-8.314-13.148-16.048v-49.885c0-7.734 5.994-14.888 13.148-16.049 4.447-.773 14.888-1.546 23.01-1.546 3.093 0 5.606 2.32 5.606 5.994v34.997l44.858-.967 44.858.967V88.943c0-3.674 2.514-5.994 5.608-5.994 8.12 0 18.562.773 23.009 1.546 7.347 1.16 13.148 8.315 13.148 16.049-.387 16.435-.387 33.257-.387 49.885z"></path></svg>
|
||||
|
After Width: | Height: | Size: 720 B |
9
src/frontend/src/icons/Couchbase/index.tsx
Normal file
9
src/frontend/src/icons/Couchbase/index.tsx
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import React, { forwardRef } from "react";
|
||||
import SvgCouchbaseIcon from "./Couchbase";
|
||||
|
||||
export const CouchbaseIcon = forwardRef<
|
||||
SVGSVGElement,
|
||||
React.PropsWithChildren<{}>
|
||||
>((props, ref) => {
|
||||
return <SvgCouchbaseIcon ref={ref} {...props} />;
|
||||
});
|
||||
|
|
@ -19,6 +19,11 @@ const HeaderTabsSearchComponent = ({}: HeaderTabsSearchComponentProps) => {
|
|||
const [tabActive, setTabActive] = useState("Flows");
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
const allFlows = useFlowsManagerStore((state) => state.allFlows);
|
||||
const [inputValue, setInputValue] = useState("");
|
||||
|
||||
const setSearchFlowsComponents = useFlowsManagerStore(
|
||||
(state) => state.setSearchFlowsComponents,
|
||||
);
|
||||
|
||||
const handleDownloadFolder = () => {
|
||||
if (allFlows.length === 0) {
|
||||
|
|
@ -34,8 +39,19 @@ const HeaderTabsSearchComponent = ({}: HeaderTabsSearchComponentProps) => {
|
|||
return (
|
||||
<>
|
||||
<div className="relative flex items-end gap-4">
|
||||
<InputSearchComponent loading={isLoading} />
|
||||
|
||||
<InputSearchComponent
|
||||
loading={isLoading}
|
||||
value={inputValue}
|
||||
onChange={(e) => {
|
||||
setSearchFlowsComponents(e.target.value);
|
||||
setInputValue(e.target.value);
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
setSearchFlowsComponents(inputValue);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<TabsSearchComponent
|
||||
tabsOptions={["All", "Flows", "Components"]}
|
||||
setActiveTab={setTabActive}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,27 @@
|
|||
import { useState } from "react";
|
||||
import { ChangeEvent, KeyboardEvent } from "react";
|
||||
import { Input } from "../../../../../../components/ui/input";
|
||||
import useFlowsManagerStore from "../../../../../../stores/flowsManagerStore";
|
||||
import ForwardedIconComponent from "../../../../../../components/genericIconComponent";
|
||||
|
||||
type InputSearchComponentProps = {
|
||||
loading: boolean;
|
||||
divClasses?: string;
|
||||
onChange: (e: ChangeEvent<HTMLInputElement>) => void;
|
||||
onClick?: () => void;
|
||||
value: string;
|
||||
onKeyDown: (e: KeyboardEvent<HTMLInputElement>) => void;
|
||||
};
|
||||
|
||||
const InputSearchComponent = ({ loading }: InputSearchComponentProps) => {
|
||||
const InputSearchComponent = ({
|
||||
loading,
|
||||
divClasses,
|
||||
onChange,
|
||||
onClick,
|
||||
value,
|
||||
onKeyDown,
|
||||
}: InputSearchComponentProps) => {
|
||||
const pagePath = window.location.pathname;
|
||||
|
||||
const [inputValue, setInputValue] = useState("");
|
||||
const allFlows = useFlowsManagerStore((state) => state.allFlows);
|
||||
|
||||
const setSearchFlowsComponents = useFlowsManagerStore(
|
||||
(state) => state.setSearchFlowsComponents,
|
||||
);
|
||||
|
||||
const searchFlowsComponents = useFlowsManagerStore(
|
||||
(state) => state.searchFlowsComponents,
|
||||
);
|
||||
|
|
@ -38,24 +43,18 @@ const InputSearchComponent = ({ loading }: InputSearchComponentProps) => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<div className="relative h-12 w-[60%]">
|
||||
<div className={`${divClasses ? divClasses : "relative h-12 w-[60%]"}`}>
|
||||
<Input
|
||||
data-testid="search-store-input"
|
||||
disabled={disableInputSearch}
|
||||
placeholder={getSearchPlaceholder()}
|
||||
className="absolute h-12 pl-5 pr-12"
|
||||
onChange={(e) => {
|
||||
setSearchFlowsComponents(e.target.value);
|
||||
setInputValue(e.target.value);
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
setSearchFlowsComponents(inputValue);
|
||||
}
|
||||
}}
|
||||
value={inputValue}
|
||||
onChange={onChange}
|
||||
onKeyDown={onKeyDown}
|
||||
value={value}
|
||||
/>
|
||||
<button
|
||||
onClick={onClick}
|
||||
disabled={loading}
|
||||
className="absolute bottom-0 right-4 top-0 my-auto h-6 cursor-pointer stroke-1 text-muted-foreground"
|
||||
data-testid="search-store-button"
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ export default function GlobalVariablesPage() {
|
|||
name: string;
|
||||
default_fields: string | undefined;
|
||||
}> = [];
|
||||
if (globalVariablesEntries === undefined) return;
|
||||
globalVariablesEntries.forEach((entrie) => {
|
||||
const globalVariableObj = globalVariables[entrie];
|
||||
rows.push({
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
|||
import { useStoreStore } from "../../stores/storeStore";
|
||||
import { storeComponent } from "../../types/store";
|
||||
import { cn } from "../../utils/utils";
|
||||
import InputSearchComponent from "../MainPage/components/myCollectionComponent/components/inputSearchComponent";
|
||||
|
||||
export default function StorePage(): JSX.Element {
|
||||
const hasApiKey = useStoreStore((state) => state.hasApiKey);
|
||||
|
|
@ -47,7 +48,7 @@ export default function StorePage(): JSX.Element {
|
|||
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
const setCurrentFlowId = useFlowsManagerStore(
|
||||
(state) => state.setCurrentFlowId
|
||||
(state) => state.setCurrentFlowId,
|
||||
);
|
||||
const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
|
@ -144,7 +145,7 @@ export default function StorePage(): JSX.Element {
|
|||
setTotalRowsCount(
|
||||
filteredCategories?.length === 0
|
||||
? Number(res?.count ?? 0)
|
||||
: res?.results?.length ?? 0
|
||||
: res?.results?.length ?? 0,
|
||||
);
|
||||
}
|
||||
})
|
||||
|
|
@ -187,7 +188,7 @@ export default function StorePage(): JSX.Element {
|
|||
disabled={loading}
|
||||
className={cn(
|
||||
`${!validApiKey ? "animate-pulse border-error" : ""}`,
|
||||
loading ? "cursor-not-allowed" : ""
|
||||
loading ? "cursor-not-allowed" : "",
|
||||
)}
|
||||
variant="primary"
|
||||
>
|
||||
|
|
@ -202,36 +203,20 @@ export default function StorePage(): JSX.Element {
|
|||
<div className="flex h-full w-full flex-col justify-between">
|
||||
<div className="flex w-full flex-col gap-4 p-0">
|
||||
<div className="flex items-end gap-4">
|
||||
<div className="relative h-12 w-[40%]">
|
||||
<Input
|
||||
data-testid="search-store-input"
|
||||
disabled={loading}
|
||||
placeholder="Search Flows and Components"
|
||||
className="absolute h-12 pl-5 pr-12"
|
||||
onChange={(e) => {
|
||||
setInputText(e.target.value);
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
setSearchNow(uniqueId());
|
||||
}
|
||||
}}
|
||||
value={inputText}
|
||||
/>
|
||||
<button
|
||||
disabled={loading}
|
||||
className="absolute bottom-0 right-4 top-0 my-auto h-6 cursor-pointer stroke-1 text-muted-foreground"
|
||||
onClick={() => {
|
||||
<InputSearchComponent
|
||||
loading={loading}
|
||||
divClasses="relative h-12 w-[40%]"
|
||||
value={inputText}
|
||||
onChange={(e) => setInputText(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
setSearchNow(uniqueId());
|
||||
}}
|
||||
data-testid="search-store-button"
|
||||
>
|
||||
<IconComponent
|
||||
name={loading ? "Loader2" : "Search"}
|
||||
className={loading ? " animate-spin cursor-not-allowed" : ""}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
}}
|
||||
onClick={() => {
|
||||
setSearchNow(uniqueId());
|
||||
}}
|
||||
/>
|
||||
<div className="ml-4 flex w-full gap-2 border-b border-border">
|
||||
<button
|
||||
data-testid="all-button-store"
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ export const useGlobalVariablesStore = create<GlobalVariablesStore>(
|
|||
delete newFields[field];
|
||||
set({ unavaliableFields: newFields });
|
||||
},
|
||||
globalVariablesEntries: [],
|
||||
globalVariablesEntries: undefined,
|
||||
globalVariables: {},
|
||||
setGlobalVariables: (variables) => {
|
||||
set({
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
export type GlobalVariablesStore = {
|
||||
globalVariablesEntries: Array<string>;
|
||||
globalVariablesEntries: Array<string> | undefined;
|
||||
globalVariables: {
|
||||
[name: string]: {
|
||||
id: string;
|
||||
|
|
|
|||
|
|
@ -153,6 +153,7 @@ import { AzureIcon } from "../icons/Azure";
|
|||
import { BingIcon } from "../icons/Bing";
|
||||
import { BotMessageSquareIcon } from "../icons/BotMessageSquare";
|
||||
import { ChromaIcon } from "../icons/ChromaIcon";
|
||||
import { CouchbaseIcon } from "../icons/Couchbase";
|
||||
import { CohereIcon } from "../icons/Cohere";
|
||||
import { ElasticsearchIcon } from "../icons/ElasticsearchStore";
|
||||
import { EvernoteIcon } from "../icons/Evernote";
|
||||
|
|
@ -324,6 +325,7 @@ export const nodeIconsLucide: iconsType = {
|
|||
Vectara: VectaraIcon,
|
||||
ArrowUpToLine: ArrowUpToLine,
|
||||
Chroma: ChromaIcon,
|
||||
Couchbase: CouchbaseIcon,
|
||||
AirbyteJSONLoader: AirbyteIcon,
|
||||
AmazonBedrockEmbeddings: AWSIcon,
|
||||
Amazon: AWSIcon,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue