From 11ef216c0ae1cfe062f0d7807b7f593fdff92c28 Mon Sep 17 00:00:00 2001
From: Jordan Frazier <122494242+jordanrfrazier@users.noreply.github.com>
Date: Mon, 10 Jun 2024 15:04:24 -0700
Subject: [PATCH] feat: add cassandra components (#2056)
* Add cassandra store component
* Add cassandra search component
* revert poetry changes
* fix type
* Add cassandra icon
* Add Cassandra Message Writer
* Add cassandra message reader
* poetry
* Fix init of cass reader
* move cassio import to base project and inline imports in backend
* running make format
* remove file
* remove cassio import
* update lockfile
* Actually update lockfile:
* merge fixes
---
poetry.lock | 97 +++++++-------
pyproject.toml | 1 +
.../memories/AstraDBMessageReader.py | 14 +-
.../memories/AstraDBMessageWriter.py | 28 ++--
.../memories/CassandraMessageReader.py | 86 ++++++++++++
.../memories/CassandraMessageWriter.py | 122 ++++++++++++++++++
.../vectorsearch/CassandraSearch.py | 94 ++++++++++++++
.../components/vectorstores/AstraDB.py | 11 +-
.../components/vectorstores/Cassandra.py | 110 ++++++++++++++++
src/frontend/package-lock.json | 1 +
.../src/icons/Cassandra/Cassandra.jsx | 73 +++++++++++
.../src/icons/Cassandra/cassandra.svg | 35 +++++
src/frontend/src/icons/Cassandra/index.tsx | 9 ++
src/frontend/src/utils/styleUtils.ts | 2 +
14 files changed, 620 insertions(+), 63 deletions(-)
create mode 100644 src/backend/base/langflow/components/memories/CassandraMessageReader.py
create mode 100644 src/backend/base/langflow/components/memories/CassandraMessageWriter.py
create mode 100644 src/backend/base/langflow/components/vectorsearch/CassandraSearch.py
create mode 100644 src/backend/base/langflow/components/vectorstores/Cassandra.py
create mode 100644 src/frontend/src/icons/Cassandra/Cassandra.jsx
create mode 100644 src/frontend/src/icons/Cassandra/cassandra.svg
create mode 100644 src/frontend/src/icons/Cassandra/index.tsx
diff --git a/poetry.lock b/poetry.lock
index 942402398..0ee09bb52 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -471,17 +471,17 @@ files = [
[[package]]
name = "boto3"
-version = "1.34.121"
+version = "1.34.122"
description = "The AWS SDK for Python"
optional = false
python-versions = ">=3.8"
files = [
- {file = "boto3-1.34.121-py3-none-any.whl", hash = "sha256:4e79e400d6d44b4eee5deda6ac0ecd08a3f5a30c45a0d30712795cdc4459fd79"},
- {file = "boto3-1.34.121.tar.gz", hash = "sha256:ec89f3e0b0dc959c418df29e14d3748c0b05ab7acf7c0b90c839e9f340a659fa"},
+ {file = "boto3-1.34.122-py3-none-any.whl", hash = "sha256:b2d7400ff84fa547e53b3d9acfa3c95d65d45b5886ba1ede1f7df4768d1cc0b1"},
+ {file = "boto3-1.34.122.tar.gz", hash = "sha256:56840d8ce91654d182f1c113f0791fa2113c3aa43230c50b4481f235348a6037"},
]
[package.dependencies]
-botocore = ">=1.34.121,<1.35.0"
+botocore = ">=1.34.122,<1.35.0"
jmespath = ">=0.7.1,<2.0.0"
s3transfer = ">=0.10.0,<0.11.0"
@@ -490,13 +490,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"]
[[package]]
name = "botocore"
-version = "1.34.121"
+version = "1.34.122"
description = "Low-level, data-driven core of boto 3."
optional = false
python-versions = ">=3.8"
files = [
- {file = "botocore-1.34.121-py3-none-any.whl", hash = "sha256:25b05c7646a9f240cde1c8f839552a43f27e71e15c42600275dea93e219f7dd9"},
- {file = "botocore-1.34.121.tar.gz", hash = "sha256:1a8f94b917c47dfd84a0b531ab607dc53570efb0d073d8686600f2d2be985323"},
+ {file = "botocore-1.34.122-py3-none-any.whl", hash = "sha256:6d75df3af831b62f0c7baa109728d987e0a8d34bfadf0476eb32e2f29a079a36"},
+ {file = "botocore-1.34.122.tar.gz", hash = "sha256:9374e16a36f1062c3e27816e8599b53eba99315dfac71cc84fc3aee3f5d3cbe3"},
]
[package.dependencies]
@@ -1450,13 +1450,13 @@ tests = ["pytest"]
[[package]]
name = "dataclasses-json"
-version = "0.6.6"
+version = "0.6.7"
description = "Easily serialize dataclasses to and from JSON."
optional = false
python-versions = "<4.0,>=3.7"
files = [
- {file = "dataclasses_json-0.6.6-py3-none-any.whl", hash = "sha256:e54c5c87497741ad454070ba0ed411523d46beb5da102e221efb873801b0ba85"},
- {file = "dataclasses_json-0.6.6.tar.gz", hash = "sha256:0c09827d26fffda27f1be2fed7a7a01a29c5ddcd2eb6393ad5ebf9d77e9deae8"},
+ {file = "dataclasses_json-0.6.7-py3-none-any.whl", hash = "sha256:0dbf33f26c8d5305befd61b39d2b3414e8a407bedc2834dea9b8d642666fb40a"},
+ {file = "dataclasses_json-0.6.7.tar.gz", hash = "sha256:b6b3e528266ea45b9535223bc53ca645f5208833c29229e847b3f26a1cc55fc0"},
]
[package.dependencies]
@@ -2623,13 +2623,13 @@ httplib2 = ">=0.19.0"
[[package]]
name = "google-cloud-aiplatform"
-version = "1.54.0"
+version = "1.54.1"
description = "Vertex AI API client library"
optional = false
python-versions = ">=3.8"
files = [
- {file = "google-cloud-aiplatform-1.54.0.tar.gz", hash = "sha256:6f5187d35a32951028465804fbb42b478362bf41e2b634ddd22b150299f6e1d8"},
- {file = "google_cloud_aiplatform-1.54.0-py2.py3-none-any.whl", hash = "sha256:7b3ed849b9fb59a01bd6f44444ccbb7d18495b867a26f913542f6b2d4c3de252"},
+ {file = "google-cloud-aiplatform-1.54.1.tar.gz", hash = "sha256:01c231961cc1a1a3b049ea3ef71fb11e77b2d56d632d020ce09e419b27ff77f2"},
+ {file = "google_cloud_aiplatform-1.54.1-py2.py3-none-any.whl", hash = "sha256:43f70fcd572f15317d769e5a0e04cfb7c0e259ead3fe581d2fba4f203ace5617"},
]
[package.dependencies]
@@ -4331,13 +4331,13 @@ extended-testing = ["beautifulsoup4 (>=4.12.3,<5.0.0)", "lxml (>=4.9.3,<6.0)"]
[[package]]
name = "langchainhub"
-version = "0.1.17"
+version = "0.1.18"
description = "The LangChain Hub API client"
optional = false
python-versions = "<4.0,>=3.8.1"
files = [
- {file = "langchainhub-0.1.17-py3-none-any.whl", hash = "sha256:4c609b3948252c71670f0d98f73413b515cfd2f6701a7b40ce959203e6133e04"},
- {file = "langchainhub-0.1.17.tar.gz", hash = "sha256:af7df0cb1cebc7a6e0864e8632ae48ecad39ed96568f699c78657b9d04e50b46"},
+ {file = "langchainhub-0.1.18-py3-none-any.whl", hash = "sha256:11501f15e7f34715ecc8892587daa35c6f2a3005e1f2926c9bcabd31fc2c100c"},
+ {file = "langchainhub-0.1.18.tar.gz", hash = "sha256:f2d0d8bf3abe4ca5e70511d8220bdc9ccea28d5267bcfd0e5ef9c53bd5bd3bad"},
]
[package.dependencies]
@@ -4417,13 +4417,13 @@ url = "src/backend/base"
[[package]]
name = "langfuse"
-version = "2.35.0"
+version = "2.35.2"
description = "A client library for accessing langfuse"
optional = false
python-versions = "<4.0,>=3.8.1"
files = [
- {file = "langfuse-2.35.0-py3-none-any.whl", hash = "sha256:e9df2474a01f8e167b7b13674c554915415b27064e48ad207054475f7fa8f82d"},
- {file = "langfuse-2.35.0.tar.gz", hash = "sha256:b1d4b478233eefbc8a6fc63ca00ca82f6afecf2b0fdc1835ca65e751cf901577"},
+ {file = "langfuse-2.35.2-py3-none-any.whl", hash = "sha256:d01a23842cab484594f03878aacb9732ef8fd361158eb819c7bf43f758a0954b"},
+ {file = "langfuse-2.35.2.tar.gz", hash = "sha256:32b2e6c5bc71b4efdc430c6b964ab1c1e1ba1e105a4a73912c38b3959dc4502d"},
]
[package.dependencies]
@@ -4457,13 +4457,13 @@ requests = ">=2,<3"
[[package]]
name = "litellm"
-version = "1.40.4"
+version = "1.40.7"
description = "Library to easily interface with LLM API providers"
optional = false
python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8"
files = [
- {file = "litellm-1.40.4-py3-none-any.whl", hash = "sha256:b3b8e4401f717c3a18595446bfdb80fc6bb74974aac4eae537fb7b3be37fbf9e"},
- {file = "litellm-1.40.4.tar.gz", hash = "sha256:3edaa1189742afd7c7df2b122f77373d47154a8fb6df6187ff5875e188baa3e1"},
+ {file = "litellm-1.40.7-py3-none-any.whl", hash = "sha256:c98dd8733e632aba16f14bf82e56f7159222097a6d085b242a3140b5d3e7baa4"},
+ {file = "litellm-1.40.7.tar.gz", hash = "sha256:557bb19e8e484d0dfe8e4eaa9ccefc888617852988a46d6e7adc41585a2c0600"},
]
[package.dependencies]
@@ -4483,12 +4483,12 @@ proxy = ["PyJWT (>=2.8.0,<3.0.0)", "apscheduler (>=3.10.4,<4.0.0)", "backoff", "
[[package]]
name = "llama-cpp-python"
-version = "0.2.77"
+version = "0.2.78"
description = "Python bindings for the llama.cpp library"
optional = true
python-versions = ">=3.8"
files = [
- {file = "llama_cpp_python-0.2.77.tar.gz", hash = "sha256:5d2f87df941a72ad6d122c3ffd91d8fe58542db350bd169c07b025d625a26803"},
+ {file = "llama_cpp_python-0.2.78.tar.gz", hash = "sha256:3df7cfde84287faaf29675fba8939060c3ab3f0ce8db875dabf7df5d83bd8751"},
]
[package.dependencies]
@@ -4505,13 +4505,13 @@ test = ["httpx (>=0.24.1)", "pytest (>=7.4.0)", "scipy (>=1.10)"]
[[package]]
name = "locust"
-version = "2.28.0"
+version = "2.29.0"
description = "Developer-friendly load testing framework"
optional = false
python-versions = ">=3.9"
files = [
- {file = "locust-2.28.0-py3-none-any.whl", hash = "sha256:766be879db030c0118e7d9fca712f3538c4e628bdebf59468fa1c6c2fab217d3"},
- {file = "locust-2.28.0.tar.gz", hash = "sha256:260557eec866f7e34a767b6c916b5b278167562a280480aadb88f43d962fbdeb"},
+ {file = "locust-2.29.0-py3-none-any.whl", hash = "sha256:aa9d94d3604ed9f2aab3248460d91e55d3de980a821dffdf8658b439b049d03f"},
+ {file = "locust-2.29.0.tar.gz", hash = "sha256:649c99ce49d00720a3084c0109547035ad9021222835386599a8b545d31ebe51"},
]
[package.dependencies]
@@ -4525,7 +4525,10 @@ msgpack = ">=1.0.0"
psutil = ">=5.9.1"
pywin32 = {version = "*", markers = "platform_system == \"Windows\""}
pyzmq = ">=25.0.0"
-requests = ">=2.26.0"
+requests = [
+ {version = ">=2.26.0", markers = "python_version <= \"3.11\""},
+ {version = ">=2.32.2", markers = "python_version > \"3.11\""},
+]
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
Werkzeug = ">=2.0.0"
@@ -5646,13 +5649,13 @@ sympy = "*"
[[package]]
name = "openai"
-version = "1.32.0"
+version = "1.33.0"
description = "The official Python library for the openai API"
optional = false
python-versions = ">=3.7.1"
files = [
- {file = "openai-1.32.0-py3-none-any.whl", hash = "sha256:953d57669f309002044fd2f678aba9f07a43256d74b3b00cd04afb5b185568ea"},
- {file = "openai-1.32.0.tar.gz", hash = "sha256:a6df15a7ab9344b1bc2bc8d83639f68b7a7e2453c0f5e50c1666547eee86f0bd"},
+ {file = "openai-1.33.0-py3-none-any.whl", hash = "sha256:621163b56570897ab8389d187f686a53d4771fd6ce95d481c0a9611fe8bc4229"},
+ {file = "openai-1.33.0.tar.gz", hash = "sha256:1169211a7b326ecbc821cafb427c29bfd0871f9a3e0947dd9e51acb3b0f1df78"},
]
[package.dependencies]
@@ -6334,13 +6337,13 @@ twisted = ["twisted"]
[[package]]
name = "prompt-toolkit"
-version = "3.0.46"
+version = "3.0.47"
description = "Library for building powerful interactive command lines in Python"
optional = false
python-versions = ">=3.7.0"
files = [
- {file = "prompt_toolkit-3.0.46-py3-none-any.whl", hash = "sha256:45abe60a8300f3c618b23c16c4bb98c6fc80af8ce8b17c7ae92db48db3ee63c1"},
- {file = "prompt_toolkit-3.0.46.tar.gz", hash = "sha256:869c50d682152336e23c4db7f74667639b5047494202ffe7670817053fd57795"},
+ {file = "prompt_toolkit-3.0.47-py3-none-any.whl", hash = "sha256:0d7bfa67001d5e39d02c224b663abc33687405033a8c422d0d675a5a13361d10"},
+ {file = "prompt_toolkit-3.0.47.tar.gz", hash = "sha256:1e1b29cb58080b1e69f207c893a1a7bf16d127a5c30c9d17a25a5d77792e5360"},
]
[package.dependencies]
@@ -9292,13 +9295,13 @@ files = [
[[package]]
name = "typing-extensions"
-version = "4.12.1"
+version = "4.12.2"
description = "Backported and Experimental Type Hints for Python 3.8+"
optional = false
python-versions = ">=3.8"
files = [
- {file = "typing_extensions-4.12.1-py3-none-any.whl", hash = "sha256:6024b58b69089e5a89c347397254e35f1bf02a907728ec7fee9bf0fe837d203a"},
- {file = "typing_extensions-4.12.1.tar.gz", hash = "sha256:915f5e35ff76f56588223f15fdd5938f9a1cf9195c0de25130c627e4d597f6d1"},
+ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
+ {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
]
[[package]]
@@ -9431,13 +9434,13 @@ six = "*"
[[package]]
name = "unstructured"
-version = "0.14.4"
+version = "0.14.5"
description = "A library that prepares raw documents for downstream ML tasks."
optional = false
python-versions = "<3.13,>=3.9.0"
files = [
- {file = "unstructured-0.14.4-py3-none-any.whl", hash = "sha256:d79104a574fd4de07548ee12ef63c34fab0a454a514d6252a261c7e8f15b9b1f"},
- {file = "unstructured-0.14.4.tar.gz", hash = "sha256:f4f3b5f18fcc174d3d606cbc3a9affa418494a8ea96424a112d0080fc9e93560"},
+ {file = "unstructured-0.14.5-py3-none-any.whl", hash = "sha256:2286180ac089b691e1effb73c2ab17b7809011fbf7f751439301b12a4c984131"},
+ {file = "unstructured-0.14.5.tar.gz", hash = "sha256:f37975273cdcf8f05768ef2f86abaf3d8f805992f6acb0441c2be00fa4ef6588"},
]
[package.dependencies]
@@ -9465,7 +9468,7 @@ wrapt = "*"
[package.extras]
airtable = ["pyairtable"]
-all-docs = ["effdet", "google-cloud-vision", "markdown", "msg-parser", "networkx", "onnx", "openpyxl", "pandas", "pdf2image", "pdfminer.six", "pikepdf", "pillow-heif", "pypandoc", "pypdf", "pytesseract", "python-docx (>=1.1.2)", "python-pptx (<=0.6.23)", "unstructured-inference (==0.7.33)", "unstructured.pytesseract (>=0.3.12)", "xlrd"]
+all-docs = ["effdet", "google-cloud-vision", "markdown", "networkx", "onnx", "openpyxl", "pandas", "pdf2image", "pdfminer.six", "pikepdf", "pillow-heif", "pypandoc", "pypdf", "pytesseract", "python-docx (>=1.1.2)", "python-oxmsg", "python-pptx (<=0.6.23)", "unstructured-inference (==0.7.33)", "unstructured.pytesseract (>=0.3.12)", "xlrd"]
astra = ["astrapy"]
azure = ["adlfs", "fsspec"]
azure-cognitive-search = ["azure-search-documents"]
@@ -9496,10 +9499,10 @@ hubspot = ["hubspot-api-client", "urllib3"]
huggingface = ["langdetect", "sacremoses", "sentencepiece", "torch", "transformers"]
image = ["effdet", "google-cloud-vision", "onnx", "pdf2image", "pdfminer.six", "pikepdf", "pillow-heif", "pypdf", "pytesseract", "unstructured-inference (==0.7.33)", "unstructured.pytesseract (>=0.3.12)"]
jira = ["atlassian-python-api"]
-local-inference = ["effdet", "google-cloud-vision", "markdown", "msg-parser", "networkx", "onnx", "openpyxl", "pandas", "pdf2image", "pdfminer.six", "pikepdf", "pillow-heif", "pypandoc", "pypdf", "pytesseract", "python-docx (>=1.1.2)", "python-pptx (<=0.6.23)", "unstructured-inference (==0.7.33)", "unstructured.pytesseract (>=0.3.12)", "xlrd"]
+local-inference = ["effdet", "google-cloud-vision", "markdown", "networkx", "onnx", "openpyxl", "pandas", "pdf2image", "pdfminer.six", "pikepdf", "pillow-heif", "pypandoc", "pypdf", "pytesseract", "python-docx (>=1.1.2)", "python-oxmsg", "python-pptx (<=0.6.23)", "unstructured-inference (==0.7.33)", "unstructured.pytesseract (>=0.3.12)", "xlrd"]
md = ["markdown"]
mongodb = ["pymongo"]
-msg = ["msg-parser"]
+msg = ["python-oxmsg"]
notion = ["htmlBuilder", "notion-client"]
odt = ["pypandoc", "python-docx (>=1.1.2)"]
onedrive = ["Office365-REST-Python-Client", "bs4", "msal"]
@@ -9529,13 +9532,13 @@ xlsx = ["networkx", "openpyxl", "pandas", "xlrd"]
[[package]]
name = "unstructured-client"
-version = "0.23.1"
+version = "0.23.2"
description = "Python Client SDK for Unstructured API"
optional = false
python-versions = ">=3.8"
files = [
- {file = "unstructured-client-0.23.1.tar.gz", hash = "sha256:844c0355e27d97ed87fa489fcf24e38f3c4d42fb8245645aa58629cbf5b8c08e"},
- {file = "unstructured_client-0.23.1-py3-none-any.whl", hash = "sha256:93a8729d9dfbe9fe18560288fcd69043946f85b4333ea1af189d5309a671008a"},
+ {file = "unstructured-client-0.23.2.tar.gz", hash = "sha256:26864737a6c27471cba8bcb714e4e31038f62f84552b3ead5f241bc56fa42288"},
+ {file = "unstructured_client-0.23.2-py3-none-any.whl", hash = "sha256:b3be91e7c2498aa9108a43dde4cbb75e450a87fabb450f0687c1900da7da73f6"},
]
[package.dependencies]
@@ -10449,4 +10452,4 @@ local = ["ctransformers", "llama-cpp-python", "sentence-transformers"]
[metadata]
lock-version = "2.0"
python-versions = ">=3.10,<3.13"
-content-hash = "2a5cccb4fcd7feab46c9560d3add946ee2134ff225b8d393561ff2e874d2571a"
+content-hash = "4e16ddf83311fa2c894623b76832a9dda98eec2b88975c087297364954dbdac6"
diff --git a/pyproject.toml b/pyproject.toml
index 58a6f2872..c766ac13f 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -86,6 +86,7 @@ youtube-transcript-api = "^0.6.2"
markdown = "^3.6"
langchain-chroma = "^0.1.1"
upstash-vector = "^0.4.0"
+cassio = "^0.1.7"
unstructured = {extras = ["docx", "md", "pptx"], version = "^0.14.4"}
diff --git a/src/backend/base/langflow/components/memories/AstraDBMessageReader.py b/src/backend/base/langflow/components/memories/AstraDBMessageReader.py
index bbb732f16..c61909887 100644
--- a/src/backend/base/langflow/components/memories/AstraDBMessageReader.py
+++ b/src/backend/base/langflow/components/memories/AstraDBMessageReader.py
@@ -1,9 +1,7 @@
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
@@ -51,6 +49,14 @@ class AstraDBMessageReaderComponent(BaseMemoryComponent):
Returns:
list[Record]: A list of Record objects representing the search results.
"""
+ try:
+ from langchain_astradb.chat_message_histories import AstraDBChatMessageHistory
+ except ImportError:
+ raise ImportError(
+ "Could not import langchain Astra DB integration package. "
+ "Please install it with `pip install langchain-astradb`."
+ )
+
memory: AstraDBChatMessageHistory = cast(AstraDBChatMessageHistory, kwargs.get("memory"))
if not memory:
raise ValueError("AstraDBChatMessageHistory instance is required.")
@@ -63,14 +69,14 @@ class AstraDBMessageReaderComponent(BaseMemoryComponent):
def build(
self,
- session_id: Text,
+ session_id: str,
collection_name: str,
token: str,
api_endpoint: str,
namespace: Optional[str] = None,
) -> list[Record]:
try:
- pass
+ from langchain_astradb.chat_message_histories import AstraDBChatMessageHistory
except ImportError:
raise ImportError(
"Could not import langchain Astra DB integration package. "
diff --git a/src/backend/base/langflow/components/memories/AstraDBMessageWriter.py b/src/backend/base/langflow/components/memories/AstraDBMessageWriter.py
index 265f60cf4..30c98e16c 100644
--- a/src/backend/base/langflow/components/memories/AstraDBMessageWriter.py
+++ b/src/backend/base/langflow/components/memories/AstraDBMessageWriter.py
@@ -1,11 +1,9 @@
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_astradb import AstraDBChatMessageHistory
class AstraDBMessageWriterComponent(BaseMemoryComponent):
@@ -50,7 +48,7 @@ class AstraDBMessageWriterComponent(BaseMemoryComponent):
self,
sender: str,
sender_name: str,
- text: Text,
+ text: str,
session_id: str,
metadata: Optional[dict] = None,
**kwargs,
@@ -59,17 +57,27 @@ class AstraDBMessageWriterComponent(BaseMemoryComponent):
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.
+ sender (str): The type of the message sender. Typically "ai" or "human".
+ sender_name (str): The name of the message sender.
+ text (str): The content of the message.
+ session_id (str): The session ID associated with the message.
metadata (dict | None, optional): Additional metadata for the message. Defaults to None.
- **kwargs: Additional keyword arguments.
+ **kwargs: Additional keyword arguments, including:
+ memory (AstraDBChatMessageHistory | None): The memory instance to add the message to.
+
Raises:
ValueError: If the AstraDBChatMessageHistory instance is not provided.
"""
+ try:
+ from langchain_astradb.chat_message_histories import AstraDBChatMessageHistory
+ except ImportError:
+ raise ImportError(
+ "Could not import langchain Astra DB integration package. "
+ "Please install it with `pip install langchain-astradb`."
+ )
+
memory: AstraDBChatMessageHistory | None = kwargs.pop("memory", None)
if memory is None:
raise ValueError("AstraDBChatMessageHistory instance is required.")
@@ -89,14 +97,14 @@ class AstraDBMessageWriterComponent(BaseMemoryComponent):
def build(
self,
input_value: Record,
- session_id: Text,
+ session_id: str,
collection_name: str,
token: str,
api_endpoint: str,
namespace: Optional[str] = None,
) -> Record:
try:
- pass
+ from langchain_astradb.chat_message_histories import AstraDBChatMessageHistory
except ImportError:
raise ImportError(
"Could not import langchain Astra DB integration package. "
diff --git a/src/backend/base/langflow/components/memories/CassandraMessageReader.py b/src/backend/base/langflow/components/memories/CassandraMessageReader.py
new file mode 100644
index 000000000..3545d444a
--- /dev/null
+++ b/src/backend/base/langflow/components/memories/CassandraMessageReader.py
@@ -0,0 +1,86 @@
+from typing import Optional, cast
+
+from langchain_community.chat_message_histories import CassandraChatMessageHistory
+
+from langflow.base.memory.memory import BaseMemoryComponent
+from langflow.schema.schema import Record
+
+
+class CassandraMessageReaderComponent(BaseMemoryComponent):
+ display_name = "Cassandra Message Reader"
+ description = "Retrieves stored chat messages from a Cassandra table on Astra DB."
+
+ def build_config(self):
+ return {
+ "session_id": {
+ "display_name": "Session ID",
+ "info": "Session ID of the chat history.",
+ "input_types": ["Text"],
+ },
+ "database_id": {
+ "display_name": "Database ID",
+ "info": "The Astra database ID.",
+ },
+ "table_name": {
+ "display_name": "Table Name",
+ "info": "The name of the table where messages are stored.",
+ },
+ "token": {
+ "display_name": "Token",
+ "info": "Authentication token for accessing Cassandra on Astra DB.",
+ "password": True,
+ },
+ "keyspace": {
+ "display_name": "Keyspace",
+ "info": "Optional key space within Astra DB. The keyspace should already be created.",
+ "input_types": ["Text"],
+ "advanced": True,
+ },
+ }
+
+ def get_messages(self, **kwargs) -> list[Record]:
+ """
+ Retrieves messages from the CassandraChatMessageHistory memory.
+
+ Args:
+ memory (CassandraChatMessageHistory): The CassandraChatMessageHistory instance to retrieve messages from.
+
+ Returns:
+ list[Record]: A list of Record objects representing the search results.
+ """
+ memory: CassandraChatMessageHistory = cast(CassandraChatMessageHistory, kwargs.get("memory"))
+ if not memory:
+ raise ValueError("CassandraChatMessageHistory 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: str,
+ table_name: str,
+ token: str,
+ database_id: str,
+ keyspace: Optional[str] = None,
+ ) -> list[Record]:
+ try:
+ import cassio
+ except ImportError:
+ raise ImportError(
+ "Could not import cassio integration package. " "Please install it with `pip install cassio`."
+ )
+
+ cassio.init(token=token, database_id=database_id)
+ memory = CassandraChatMessageHistory(
+ session_id=session_id,
+ table_name=table_name,
+ keyspace=keyspace,
+ )
+
+ records = self.get_messages(memory=memory)
+ self.status = records
+
+ return records
diff --git a/src/backend/base/langflow/components/memories/CassandraMessageWriter.py b/src/backend/base/langflow/components/memories/CassandraMessageWriter.py
new file mode 100644
index 000000000..716364b64
--- /dev/null
+++ b/src/backend/base/langflow/components/memories/CassandraMessageWriter.py
@@ -0,0 +1,122 @@
+from typing import Optional
+
+from langflow.base.memory.memory import BaseMemoryComponent
+from langflow.schema.schema import Record
+
+from langchain_core.messages import BaseMessage
+from langchain_community.chat_message_histories import CassandraChatMessageHistory
+
+
+class CassandraMessageWriterComponent(BaseMemoryComponent):
+ display_name = "Cassandra Message Writer"
+ description = "Writes a message to a Cassandra table on Astra DB."
+
+ def build_config(self):
+ return {
+ "input_value": {
+ "display_name": "Input Record",
+ "info": "Record to write to Cassandra.",
+ },
+ "session_id": {
+ "display_name": "Session ID",
+ "info": "Session ID of the chat history.",
+ "input_types": ["Text"],
+ },
+ "database_id": {
+ "display_name": "Database ID",
+ "info": "The Astra database ID.",
+ },
+ "table_name": {
+ "display_name": "Table Name",
+ "info": "The name of the table where messages will be stored.",
+ },
+ "token": {
+ "display_name": "Token",
+ "info": "Authentication token for accessing Cassandra on Astra DB.",
+ "password": True,
+ },
+ "keyspace": {
+ "display_name": "Keyspace",
+ "info": "Optional key space within Astra DB. The keyspace should already be created.",
+ "input_types": ["Text"],
+ "advanced": True,
+ },
+ "ttl_seconds": {
+ "display_name": "TTL Seconds",
+ "info": "Optional time-to-live for the messages.",
+ "input_types": ["Number"],
+ "advanced": True,
+ },
+ }
+
+ def add_message(
+ self,
+ sender: str,
+ sender_name: str,
+ text: str,
+ session_id: str,
+ metadata: Optional[dict] = None,
+ **kwargs,
+ ):
+ """
+ Adds a message to the CassandraChatMessageHistory memory.
+
+ Args:
+ sender (str): The type of the message sender. Typically "ai" or "human".
+ sender_name (str): The name of the message sender.
+ text (str): The content of the message.
+ session_id (str): The session ID associated with the message.
+ metadata (dict | None, optional): Additional metadata for the message. Defaults to None.
+ **kwargs: Additional keyword arguments, including:
+ memory (CassandraChatMessageHistory | None): The memory instance to add the message to.
+
+
+ Raises:
+ ValueError: If the CassandraChatMessageHistory instance is not provided.
+
+ """
+ memory: CassandraChatMessageHistory | None = kwargs.pop("memory", None)
+ if memory is None:
+ raise ValueError("CassandraChatMessageHistory 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: str,
+ table_name: str,
+ token: str,
+ database_id: str,
+ keyspace: Optional[str] = None,
+ ttl_seconds: Optional[int] = None,
+ ) -> Record:
+ try:
+ import cassio
+ except ImportError:
+ raise ImportError(
+ "Could not import cassio integration package. " "Please install it with `pip install cassio`."
+ )
+
+ cassio.init(token=token, database_id=database_id)
+ memory = CassandraChatMessageHistory(
+ session_id=session_id,
+ table_name=table_name,
+ keyspace=keyspace,
+ ttl_seconds=ttl_seconds,
+ )
+
+ self.add_message(**input_value.data, memory=memory)
+ self.status = f"Added message to Cassandra memory for session {session_id}"
+
+ return input_value
diff --git a/src/backend/base/langflow/components/vectorsearch/CassandraSearch.py b/src/backend/base/langflow/components/vectorsearch/CassandraSearch.py
new file mode 100644
index 000000000..8ee558276
--- /dev/null
+++ b/src/backend/base/langflow/components/vectorsearch/CassandraSearch.py
@@ -0,0 +1,94 @@
+from typing import Any, List, Optional, Tuple
+
+from langflow.components.vectorstores.Cassandra import CassandraVectorStoreComponent
+from langflow.components.vectorstores.base.model import LCVectorStoreComponent
+from langflow.field_typing import Embeddings, Text
+from langflow.schema import Record
+from langchain_community.utilities.cassandra import SetupMode
+
+
+class CassandraSearchComponent(LCVectorStoreComponent):
+ display_name = "Cassandra Search"
+ description = "Searches an existing Cassandra Vector Store."
+ icon = "Cassandra"
+ field_order = ["token", "database_id", "table_name", "input_value", "embedding"]
+
+ def build_config(self):
+ return {
+ "search_type": {
+ "display_name": "Search Type",
+ "options": ["Similarity", "MMR"],
+ },
+ "input_value": {
+ "display_name": "Input Value",
+ "info": "Input value to search",
+ },
+ "embedding": {"display_name": "Embedding", "info": "Embedding to use"},
+ "token": {
+ "display_name": "Token",
+ "info": "Authentication token for accessing Cassandra on Astra DB.",
+ "password": True,
+ },
+ "database_id": {
+ "display_name": "Database ID",
+ "info": "The Astra database ID.",
+ },
+ "table_name": {
+ "display_name": "Table Name",
+ "info": "The name of the table where vectors will be stored.",
+ },
+ "keyspace": {
+ "display_name": "Keyspace",
+ "info": "Optional key space within Astra DB. The keyspace should already be created.",
+ "advanced": True,
+ },
+ "body_index_options": {
+ "display_name": "Body Index Options",
+ "info": "Optional options used to create the body index.",
+ "advanced": True,
+ },
+ "setup_mode": {
+ "display_name": "Setup Mode",
+ "info": "Configuration mode for setting up the Cassandra table, with options like 'Sync', 'Async', or 'Off'.",
+ "options": ["Sync", "Async", "Off"],
+ "advanced": True,
+ },
+ "number_of_results": {
+ "display_name": "Number of Results",
+ "info": "Number of results to return.",
+ "advanced": True,
+ },
+ }
+
+ def build(
+ self,
+ embedding: Embeddings,
+ table_name: str,
+ input_value: Text,
+ token: str,
+ database_id: str,
+ search_type: str = "similarity",
+ number_of_results: int = 4,
+ keyspace: Optional[str] = None,
+ body_index_options: Optional[List[Tuple[str, Any]]] = None,
+ setup_mode: SetupMode = SetupMode.SYNC,
+ ) -> List[Record]:
+ vector_store = CassandraVectorStoreComponent().build(
+ embedding=embedding,
+ table_name=table_name,
+ token=token,
+ database_id=database_id,
+ keyspace=keyspace,
+ body_index_options=body_index_options,
+ setup_mode=setup_mode,
+ )
+
+ try:
+ return self.search_with_vector_store(input_value, search_type, vector_store, k=number_of_results)
+ except KeyError as e:
+ if "content" in str(e):
+ raise ValueError(
+ "You should ingest data through Langflow (or LangChain) to query it in Langflow. Your collection does not contain a field name 'content'."
+ )
+ else:
+ raise e
diff --git a/src/backend/base/langflow/components/vectorstores/AstraDB.py b/src/backend/base/langflow/components/vectorstores/AstraDB.py
index 07ded028e..d13b489c6 100644
--- a/src/backend/base/langflow/components/vectorstores/AstraDB.py
+++ b/src/backend/base/langflow/components/vectorstores/AstraDB.py
@@ -1,6 +1,4 @@
from typing import List, Optional, Union
-from langchain_astradb import AstraDBVectorStore
-from langchain_astradb.utils.astradb import SetupMode
from langflow.custom import CustomComponent
from langflow.field_typing import Embeddings, VectorStore
@@ -111,6 +109,15 @@ class AstraDBVectorStoreComponent(CustomComponent):
metadata_indexing_exclude: Optional[List[str]] = None,
collection_indexing_policy: Optional[dict] = None,
) -> Union[VectorStore, BaseRetriever]:
+ try:
+ from langchain_astradb import AstraDBVectorStore
+ from langchain_astradb.utils.astradb import SetupMode
+ except ImportError:
+ raise ImportError(
+ "Could not import langchain Astra DB integration package. "
+ "Please install it with `pip install langchain-astradb`."
+ )
+
try:
setup_mode_value = SetupMode[setup_mode.upper()]
except KeyError:
diff --git a/src/backend/base/langflow/components/vectorstores/Cassandra.py b/src/backend/base/langflow/components/vectorstores/Cassandra.py
new file mode 100644
index 000000000..34c21ccd0
--- /dev/null
+++ b/src/backend/base/langflow/components/vectorstores/Cassandra.py
@@ -0,0 +1,110 @@
+from typing import Any, List, Optional, Tuple
+from langchain_community.vectorstores import Cassandra
+from langchain_community.utilities.cassandra import SetupMode
+
+from langflow.custom import CustomComponent
+from langflow.field_typing import Embeddings, VectorStore
+from langflow.schema import Record
+
+
+class CassandraVectorStoreComponent(CustomComponent):
+ display_name = "Cassandra"
+ description = "Builds or loads a Cassandra Vector Store."
+ icon = "Cassandra"
+ field_order = ["token", "database_id", "table_name", "inputs", "embedding"]
+
+ def build_config(self):
+ return {
+ "inputs": {
+ "display_name": "Inputs",
+ "info": "Optional list of records to be processed and stored in the vector store.",
+ },
+ "embedding": {"display_name": "Embedding", "info": "Embedding to use"},
+ "token": {
+ "display_name": "Token",
+ "info": "Authentication token for accessing Cassandra on Astra DB.",
+ "password": True,
+ },
+ "database_id": {
+ "display_name": "Database ID",
+ "info": "The Astra database ID.",
+ },
+ "table_name": {
+ "display_name": "Table Name",
+ "info": "The name of the table where vectors will be stored.",
+ },
+ "keyspace": {
+ "display_name": "Keyspace",
+ "info": "Optional key space within Astra DB. The keyspace should already be created.",
+ "advanced": True,
+ },
+ "ttl_seconds": {
+ "display_name": "TTL Seconds",
+ "info": "Optional time-to-live for the added texts.",
+ "advanced": True,
+ },
+ "batch_size": {
+ "display_name": "Batch Size",
+ "info": "Optional number of records to process in a single batch.",
+ "advanced": True,
+ },
+ "body_index_options": {
+ "display_name": "Body Index Options",
+ "info": "Optional options used to create the body index.",
+ "advanced": True,
+ },
+ "setup_mode": {
+ "display_name": "Setup Mode",
+ "info": "Configuration mode for setting up the Cassandra table, with options like 'Sync', 'Async', or 'Off'.",
+ "options": ["Sync", "Async", "Off"],
+ "advanced": True,
+ },
+ }
+
+ def build(
+ self,
+ embedding: Embeddings,
+ token: str,
+ database_id: str,
+ inputs: Optional[List[Record]] = None,
+ keyspace: Optional[str] = None,
+ table_name: str = "",
+ ttl_seconds: Optional[int] = None,
+ batch_size: int = 16,
+ body_index_options: Optional[List[Tuple[str, Any]]] = None,
+ setup_mode: SetupMode = SetupMode.SYNC,
+ ) -> VectorStore:
+ try:
+ import cassio
+ except ImportError:
+ raise ImportError(
+ "Could not import cassio integration package. " "Please install it with `pip install cassio`."
+ )
+
+ cassio.init(
+ database_id=database_id,
+ token=token,
+ )
+
+ if inputs:
+ documents = [_input.to_lc_document() for _input in inputs]
+ table = Cassandra.from_documents(
+ documents=documents,
+ embedding=embedding,
+ table_name=table_name,
+ keyspace=keyspace,
+ ttl_seconds=ttl_seconds,
+ batch_size=batch_size,
+ body_index_options=body_index_options,
+ )
+ else:
+ table = Cassandra(
+ embedding=embedding,
+ table_name=table_name,
+ keyspace=keyspace,
+ ttl_seconds=ttl_seconds,
+ body_index_options=body_index_options,
+ setup_mode=setup_mode,
+ )
+
+ return table
diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json
index c809b6ad5..1f50cfedc 100644
--- a/src/frontend/package-lock.json
+++ b/src/frontend/package-lock.json
@@ -480,6 +480,7 @@
},
"node_modules/@clack/prompts/node_modules/is-unicode-supported": {
"version": "1.3.0",
+ "extraneous": true,
"inBundle": true,
"license": "MIT",
"engines": {
diff --git a/src/frontend/src/icons/Cassandra/Cassandra.jsx b/src/frontend/src/icons/Cassandra/Cassandra.jsx
new file mode 100644
index 000000000..9a8d48d38
--- /dev/null
+++ b/src/frontend/src/icons/Cassandra/Cassandra.jsx
@@ -0,0 +1,73 @@
+const CassandraSVG = (props) => (
+
+);
+export default CassandraSVG;
diff --git a/src/frontend/src/icons/Cassandra/cassandra.svg b/src/frontend/src/icons/Cassandra/cassandra.svg
new file mode 100644
index 000000000..6d6cdf024
--- /dev/null
+++ b/src/frontend/src/icons/Cassandra/cassandra.svg
@@ -0,0 +1,35 @@
+
+
\ No newline at end of file
diff --git a/src/frontend/src/icons/Cassandra/index.tsx b/src/frontend/src/icons/Cassandra/index.tsx
new file mode 100644
index 000000000..7fe917b72
--- /dev/null
+++ b/src/frontend/src/icons/Cassandra/index.tsx
@@ -0,0 +1,9 @@
+import React, { forwardRef } from "react";
+import CassandraSVG from "./Cassandra";
+
+export const CassandraIcon = forwardRef<
+ SVGSVGElement,
+ React.PropsWithChildren<{}>
+>((props, ref) => {
+ return ;
+});
diff --git a/src/frontend/src/utils/styleUtils.ts b/src/frontend/src/utils/styleUtils.ts
index 329ca1fa0..d507aecd8 100644
--- a/src/frontend/src/utils/styleUtils.ts
+++ b/src/frontend/src/utils/styleUtils.ts
@@ -155,6 +155,7 @@ import { AstraDBIcon } from "../icons/AstraDB";
import { AzureIcon } from "../icons/Azure";
import { BingIcon } from "../icons/Bing";
import { BotMessageSquareIcon } from "../icons/BotMessageSquare";
+import { CassandraIcon } from "../icons/Cassandra";
import { ChromaIcon } from "../icons/ChromaIcon";
import { CohereIcon } from "../icons/Cohere";
import { CouchbaseIcon } from "../icons/Couchbase";
@@ -328,6 +329,7 @@ export const nodeIconsLucide: iconsType = {
Play,
Vectara: VectaraIcon,
ArrowUpToLine: ArrowUpToLine,
+ Cassandra: CassandraIcon,
Chroma: ChromaIcon,
Couchbase: CouchbaseIcon,
AirbyteJSONLoader: AirbyteIcon,