lfx-oracle 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
lfx_oracle/__init__.py ADDED
@@ -0,0 +1,21 @@
1
+ """lfx-oracle: Oracle Database bundle.
2
+
3
+ Distribution unit ``lfx-oracle``. At runtime Langflow's loader
4
+ discovers ``extension.json`` shipped alongside this ``__init__.py`` and
5
+ registers the bundle's components under the namespaced IDs
6
+ ``ext:oracle:<Class>@official``.
7
+ """
8
+
9
+ from lfx_oracle.components.oracle.oracledb_embeddings import OracleEmbeddingsComponent
10
+ from lfx_oracle.components.oracle.oracledb_loaders import (
11
+ OracleAutonomousDatabaseLoaderComponent,
12
+ OracleDocLoaderComponent,
13
+ )
14
+ from lfx_oracle.components.oracle.oraclevs import OracleVectorStoreComponent
15
+
16
+ __all__ = [
17
+ "OracleAutonomousDatabaseLoaderComponent",
18
+ "OracleDocLoaderComponent",
19
+ "OracleEmbeddingsComponent",
20
+ "OracleVectorStoreComponent",
21
+ ]
@@ -0,0 +1,18 @@
1
+ """Component re-exports for the ``oracle`` bundle.
2
+
3
+ Saved-flow migration entries that target the legacy in-tree
4
+ ``lfx.components.oracledb.<Class>`` import paths resolve through this
5
+ package, so the moved Component class(es) must be importable from here
6
+ by name.
7
+ """
8
+
9
+ from .oracledb_embeddings import OracleEmbeddingsComponent
10
+ from .oracledb_loaders import OracleAutonomousDatabaseLoaderComponent, OracleDocLoaderComponent
11
+ from .oraclevs import OracleVectorStoreComponent
12
+
13
+ __all__ = [
14
+ "OracleAutonomousDatabaseLoaderComponent",
15
+ "OracleDocLoaderComponent",
16
+ "OracleEmbeddingsComponent",
17
+ "OracleVectorStoreComponent",
18
+ ]
@@ -0,0 +1,83 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from typing import Any
5
+
6
+ _SENSITIVE_CONNECTION_PARAM_KEYS = {
7
+ "access_token",
8
+ "connection_string",
9
+ "credential",
10
+ "credentials",
11
+ "dsn",
12
+ "newpassword",
13
+ "passwd",
14
+ "password",
15
+ "private_key",
16
+ "proxy_user",
17
+ "pwd",
18
+ "secret",
19
+ "tns",
20
+ "token",
21
+ "user",
22
+ "username",
23
+ "wallet_password",
24
+ }
25
+
26
+
27
+ def _is_sensitive_connection_param_key(key: str) -> bool:
28
+ normalized_key = re.sub(r"[^0-9a-zA-Z]+", "_", key.strip().lower()).strip("_")
29
+ compact_key = normalized_key.replace("_", "")
30
+ return any(
31
+ normalized_key == sensitive_key or compact_key == sensitive_key.replace("_", "")
32
+ for sensitive_key in _SENSITIVE_CONNECTION_PARAM_KEYS
33
+ )
34
+
35
+
36
+ def split_credentialized_dsn(dsn: str) -> tuple[str | None, str | None, str]:
37
+ if "@" not in dsn:
38
+ return None, None, dsn
39
+
40
+ credentials, dsn_without_credentials = dsn.rsplit("@", 1)
41
+ if "/" not in credentials:
42
+ return None, None, dsn
43
+
44
+ user, password = credentials.split("/", 1)
45
+ return user or None, password or None, dsn_without_credentials
46
+
47
+
48
+ def build_connection_params(
49
+ connection_params: dict[str, Any] | None = None,
50
+ *,
51
+ user: str | None = None,
52
+ password: str | None = None,
53
+ dsn: str | None = None,
54
+ wallet_password: str | None = None,
55
+ ) -> dict[str, Any]:
56
+ raw_params = {key: value for key, value in (connection_params or {}).items() if value not in (None, "")}
57
+ sensitive_keys = sorted(key for key in raw_params if _is_sensitive_connection_param_key(key))
58
+ if sensitive_keys:
59
+ keys = ", ".join(sensitive_keys)
60
+ msg = (
61
+ f"connection_params contains sensitive keys: {keys}. "
62
+ "Use the dedicated secret fields for user, password, dsn, wallet_password, and proxy."
63
+ )
64
+ raise ValueError(msg)
65
+
66
+ if isinstance(dsn, str) and (not user or not password):
67
+ parsed_user, parsed_password, parsed_dsn = split_credentialized_dsn(dsn)
68
+ user = user or parsed_user
69
+ password = password or parsed_password
70
+ dsn = parsed_dsn
71
+
72
+ params = dict(raw_params)
73
+
74
+ if user:
75
+ params["user"] = user
76
+ if password:
77
+ params["password"] = password
78
+ if dsn:
79
+ params["dsn"] = dsn
80
+ if wallet_password:
81
+ params["wallet_password"] = wallet_password
82
+
83
+ return params
@@ -0,0 +1,70 @@
1
+ from contextlib import suppress
2
+ from typing import Any
3
+
4
+ import oracledb
5
+ from langchain_oracledb import OracleEmbeddings
6
+ from lfx.base.models.model import LCModelComponent
7
+ from lfx.field_typing import Embeddings
8
+ from lfx.io import DictInput, Output, SecretStrInput
9
+
10
+ from .connection import build_connection_params
11
+
12
+
13
+ class OracleEmbeddingsComponent(LCModelComponent):
14
+ display_name = "Oracle Embeddings"
15
+ description = "Generate embeddings using Oracle AI Vector Search."
16
+ icon = "Oracle"
17
+ name = "OracleEmbeddings"
18
+
19
+ inputs = [
20
+ SecretStrInput(name="user", display_name="User", required=False),
21
+ SecretStrInput(name="password", display_name="Password", required=False),
22
+ SecretStrInput(name="dsn", display_name="DSN", required=True),
23
+ SecretStrInput(name="wallet_password", display_name="Wallet Password", required=False, advanced=True),
24
+ DictInput(
25
+ name="connection_params",
26
+ display_name="Additional Connection Parameters",
27
+ info="Non-secret arguments passed to python-oracledb connect(), such as config_dir and wallet_location.",
28
+ list=True,
29
+ required=False,
30
+ advanced=True,
31
+ ),
32
+ DictInput(
33
+ name="embedding_params",
34
+ display_name="Embedding Parameters",
35
+ list=True,
36
+ info=(
37
+ "https://docs.oracle.com/en/database/oracle/oracle-database/23/vecse/utl_to_embedding-and-utl_to_embeddings-dbms_vector.html"
38
+ ),
39
+ ),
40
+ SecretStrInput(name="proxy", display_name="Proxy", required=False, advanced=True),
41
+ ]
42
+
43
+ outputs = [
44
+ Output(display_name="Embeddings", name="embeddings", method="build_embeddings"),
45
+ ]
46
+
47
+ def build_template_config(self) -> dict[str, Any]:
48
+ return self.get_template_config(self)
49
+
50
+ def build_embeddings(self) -> Embeddings:
51
+ connection_params = build_connection_params(
52
+ self.connection_params,
53
+ user=self.user,
54
+ password=self.password,
55
+ dsn=self.dsn,
56
+ wallet_password=self.wallet_password,
57
+ )
58
+ connection = oracledb.connect(**connection_params)
59
+
60
+ try:
61
+ return OracleEmbeddings(
62
+ conn=connection,
63
+ params=self.embedding_params,
64
+ proxy=self.proxy or None,
65
+ )
66
+ except Exception as e:
67
+ with suppress(Exception):
68
+ connection.close()
69
+ msg = "Unable to create OracleEmbeddings."
70
+ raise ValueError(msg) from e
@@ -0,0 +1,178 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ import oracledb
6
+ from langchain_oracledb.document_loaders import OracleAutonomousDatabaseLoader, OracleDocLoader
7
+ from lfx.custom.custom_component.component import Component
8
+ from lfx.io import DictInput, MultilineInput, Output, SecretStrInput, StrInput
9
+ from lfx.schema.data import Data
10
+
11
+ from .connection import build_connection_params, split_credentialized_dsn
12
+
13
+
14
+ class OracleDocLoaderComponent(Component):
15
+ display_name = "Oracle Doc Loader"
16
+ description = "Read documents from Oracle Database using OracleDocLoader."
17
+ documentation = "https://docs.langchain.com/oss/python/integrations/document_loaders/oracleai"
18
+ trace_type = "tool"
19
+ icon = "Oracle"
20
+ name = "OracleDocLoader"
21
+
22
+ inputs = [
23
+ SecretStrInput(name="user", display_name="User", required=False),
24
+ SecretStrInput(name="password", display_name="Password", required=False),
25
+ SecretStrInput(name="dsn", display_name="DSN", required=True),
26
+ SecretStrInput(name="wallet_password", display_name="Wallet Password", required=False, advanced=True),
27
+ DictInput(
28
+ name="connection_params",
29
+ display_name="Additional Connection Parameters",
30
+ info="Non-secret arguments passed to python-oracledb connect(), such as config_dir and wallet_location.",
31
+ required=False,
32
+ advanced=True,
33
+ ),
34
+ DictInput(
35
+ name="params",
36
+ display_name="Loader Parameters",
37
+ info="Arguments passed to langchain-oracledb OracleDocLoader.",
38
+ required=True,
39
+ ),
40
+ ]
41
+
42
+ outputs = [
43
+ Output(name="data", display_name="JSON", method="load_documents"),
44
+ ]
45
+
46
+ def build_template_config(self) -> dict[str, Any]:
47
+ return self.get_template_config(self)
48
+
49
+ def load_documents(self) -> list[Data]:
50
+ connection_params = build_connection_params(
51
+ self.connection_params,
52
+ user=self.user,
53
+ password=self.password,
54
+ dsn=self.dsn,
55
+ wallet_password=self.wallet_password,
56
+ )
57
+ with oracledb.connect(**connection_params) as connection:
58
+ loader = OracleDocLoader(connection, self.params)
59
+ data = [Data.from_document(doc) for doc in loader.load()]
60
+
61
+ self.status = data
62
+ return data
63
+
64
+
65
+ class OracleAutonomousDatabaseLoaderComponent(Component):
66
+ display_name = "Oracle Autonomous Database Loader"
67
+ description = "Load rows from Oracle Autonomous Database as documents."
68
+ documentation = (
69
+ "https://github.com/oracle/langchain-oracle/blob/main/libs/oracledb/"
70
+ "langchain_oracledb/document_loaders/oracleadb_loader.py"
71
+ )
72
+ trace_type = "tool"
73
+ icon = "Oracle"
74
+ name = "OracleAutonomousDatabaseLoader"
75
+
76
+ inputs = [
77
+ StrInput(
78
+ name="query",
79
+ display_name="SQL Query",
80
+ required=True,
81
+ info="SQL query to execute. Each row becomes a document.",
82
+ ),
83
+ SecretStrInput(name="user", display_name="User", required=False),
84
+ SecretStrInput(name="password", display_name="Password", required=False),
85
+ SecretStrInput(name="dsn", display_name="DSN", required=True),
86
+ SecretStrInput(name="wallet_password", display_name="Wallet Password", required=False, advanced=True),
87
+ DictInput(
88
+ name="connection_params",
89
+ display_name="Additional Connection Parameters",
90
+ info="Non-secret Oracle options such as schema, config_dir, and wallet_location.",
91
+ required=False,
92
+ advanced=True,
93
+ ),
94
+ MultilineInput(
95
+ name="metadata",
96
+ display_name="Metadata Columns",
97
+ required=False,
98
+ advanced=True,
99
+ info="Comma-separated list of result columns to copy into document metadata.",
100
+ ),
101
+ DictInput(
102
+ name="parameter",
103
+ display_name="Bind Parameters",
104
+ required=False,
105
+ advanced=True,
106
+ info="Bind variables for the SQL query.",
107
+ ),
108
+ ]
109
+
110
+ outputs = [
111
+ Output(name="data", display_name="JSON", method="load_documents"),
112
+ ]
113
+
114
+ def build_template_config(self) -> dict[str, Any]:
115
+ """Build template config from this class to avoid cross-class metadata bleed."""
116
+ return self.get_template_config(self)
117
+
118
+ def _parse_metadata(self) -> list[str] | None:
119
+ metadata = getattr(self, "metadata", None)
120
+ if metadata is None:
121
+ return None
122
+ if isinstance(metadata, str):
123
+ values = [column.strip() for column in metadata.split(",") if column.strip()]
124
+ return values or None
125
+ if isinstance(metadata, list):
126
+ return metadata or None
127
+ return None
128
+
129
+ def _parse_connection_params(self) -> dict[str, Any]:
130
+ connection_params = build_connection_params(
131
+ self.connection_params,
132
+ user=self.user,
133
+ password=self.password,
134
+ dsn=self.dsn,
135
+ wallet_password=self.wallet_password,
136
+ )
137
+ dsn = connection_params.get("dsn") or connection_params.get("connection_string") or connection_params.get("tns")
138
+
139
+ user = connection_params.get("user") or connection_params.get("username")
140
+ password = connection_params.get("password")
141
+
142
+ if isinstance(dsn, str) and (not user or not password):
143
+ parsed_user, parsed_password, parsed_dsn = split_credentialized_dsn(dsn)
144
+ user = user or parsed_user
145
+ password = password or parsed_password
146
+ dsn = parsed_dsn
147
+
148
+ if not user or not password:
149
+ msg = (
150
+ "Connection settings must include user and password, either in the dedicated fields or inside the DSN."
151
+ )
152
+ raise ValueError(msg)
153
+
154
+ return {
155
+ "user": user,
156
+ "password": password,
157
+ "dsn": dsn,
158
+ "schema": connection_params.get("schema"),
159
+ "config_dir": connection_params.get("config_dir"),
160
+ "wallet_location": connection_params.get("wallet_location"),
161
+ "wallet_password": connection_params.get("wallet_password"),
162
+ }
163
+
164
+ def _parse_parameter(self) -> Any:
165
+ parameter = getattr(self, "parameter", None)
166
+ return parameter or None
167
+
168
+ def load_documents(self) -> list[Data]:
169
+ loader = OracleAutonomousDatabaseLoader(
170
+ query=self.query,
171
+ metadata=self._parse_metadata(),
172
+ parameter=self._parse_parameter(),
173
+ **self._parse_connection_params(),
174
+ )
175
+
176
+ data = [Data.from_document(doc) for doc in loader.load()]
177
+ self.status = data
178
+ return data
@@ -0,0 +1,246 @@
1
+ import hashlib
2
+ import json
3
+ from contextlib import suppress
4
+ from decimal import Decimal
5
+ from importlib.metadata import version
6
+ from typing import Any
7
+
8
+ import oracledb
9
+ from langchain_community.vectorstores.utils import DistanceStrategy
10
+ from langchain_oracledb.vectorstores import OracleVS
11
+ from langchain_oracledb.vectorstores.oraclevs import create_index
12
+ from lfx.base.vectorstores.model import LCVectorStoreComponent, check_cached_vector_store
13
+ from lfx.helpers.data import docs_to_data
14
+ from lfx.io import BoolInput, DictInput, DropdownInput, HandleInput, IntInput, Output, SecretStrInput, StrInput
15
+ from lfx.schema.data import Data
16
+ from pydantic import BaseModel
17
+
18
+ from .connection import build_connection_params
19
+
20
+
21
+ def serialize(obj: Any) -> dict | list:
22
+ if isinstance(obj, BaseModel):
23
+ return obj.model_dump(mode="json")
24
+ if isinstance(obj, dict):
25
+ return {k: serialize(v) for k, v in obj.items()}
26
+ if isinstance(obj, list):
27
+ return [serialize(item) for item in obj]
28
+ if isinstance(obj, tuple):
29
+ return [serialize(item) for item in obj]
30
+ return obj
31
+
32
+
33
+ def normalize_search_result(obj: Any) -> Any:
34
+ if isinstance(obj, Decimal):
35
+ return float(obj)
36
+ if isinstance(obj, dict):
37
+ return {key: normalize_search_result(value) for key, value in obj.items()}
38
+ if isinstance(obj, list):
39
+ return [normalize_search_result(item) for item in obj]
40
+ if isinstance(obj, tuple):
41
+ return [normalize_search_result(item) for item in obj]
42
+ return obj
43
+
44
+
45
+ class OracleVectorStoreComponent(LCVectorStoreComponent):
46
+ display_name = "Oracle Vector Store"
47
+ description = "Oracle vector store with search capabilities"
48
+ name = "OracleVS"
49
+ icon = "Oracle"
50
+
51
+ inputs = [
52
+ SecretStrInput(name="user", display_name="User", required=False),
53
+ SecretStrInput(name="password", display_name="Password", required=False),
54
+ SecretStrInput(name="dsn", display_name="DSN", required=True),
55
+ SecretStrInput(name="wallet_password", display_name="Wallet Password", required=False, advanced=True),
56
+ DictInput(
57
+ name="connection_params",
58
+ display_name="Additional Connection Parameters",
59
+ info="Non-secret arguments passed to python-oracledb connect(), such as config_dir and wallet_location.",
60
+ list=True,
61
+ required=False,
62
+ advanced=True,
63
+ ),
64
+ StrInput(name="table_name", display_name="Table Name", required=True),
65
+ *LCVectorStoreComponent.inputs,
66
+ HandleInput(name="embedding", display_name="Embedding", input_types=["Embeddings"], required=True),
67
+ IntInput(
68
+ name="number_of_results",
69
+ display_name="Number of Results",
70
+ info="Number of results to return.",
71
+ value=4,
72
+ advanced=True,
73
+ ),
74
+ DropdownInput(
75
+ name="search_type",
76
+ display_name="Search Type",
77
+ info="Search type to use",
78
+ options=["Similarity", "MMR (Max Marginal Relevance)"],
79
+ value="Similarity",
80
+ advanced=True,
81
+ ),
82
+ DropdownInput(
83
+ name="distance_strategy",
84
+ display_name="Distance Strategy",
85
+ options=["EUCLIDEAN", "DOT", "COSINE"],
86
+ value="COSINE",
87
+ advanced=True,
88
+ ),
89
+ BoolInput(
90
+ name="create_index",
91
+ display_name="Create Vector Index",
92
+ info=(
93
+ "Boolean flag to determine whether to create a vector index. "
94
+ "Check `Controls` to use advanced parameters."
95
+ ),
96
+ value=True,
97
+ ),
98
+ DictInput(
99
+ name="index_params",
100
+ display_name="Vector Index Parameters",
101
+ list=True,
102
+ advanced=True,
103
+ ),
104
+ BoolInput(
105
+ name="mutate_on_duplicate",
106
+ display_name="Mutate on Duplicate Inserts",
107
+ info="Whether to update existing rows when duplicate inserts are encountered.",
108
+ value=False,
109
+ advanced=True,
110
+ ),
111
+ ]
112
+
113
+ # Mirrors LCVectorStoreComponent.outputs verbatim: the extension
114
+ # validator statically scans this file and cannot see inherited
115
+ # outputs, so the declaration must be repeated here.
116
+ outputs = [
117
+ Output(
118
+ display_name="Search Results",
119
+ name="search_results",
120
+ method="search_documents",
121
+ info=(
122
+ "Performs semantic similarity search in the vector store to find relevant documents/files. "
123
+ "Input: A natural language search query (str). "
124
+ "Returns: A list of Data objects (dicts). Each result has structure: {'data': {'text': '...', ...}}. "
125
+ "The 'text' field always contains the matched document content. "
126
+ "Other metadata fields depend on what was indexed "
127
+ "(may include: 'file_path', 'source', 'page', etc.). "
128
+ "IMPORTANT: To extract metadata like file_path, use: "
129
+ "result['data'].get('file_path') or result.data.get('file_path'). "
130
+ "Check what fields are available by inspecting result['data'].keys() or the first search result."
131
+ ),
132
+ tool_mode=True,
133
+ ),
134
+ Output(
135
+ display_name="Table",
136
+ name="dataframe",
137
+ method="as_dataframe",
138
+ ),
139
+ ]
140
+
141
+ def build_template_config(self) -> dict[str, Any]:
142
+ return self.get_template_config(self)
143
+
144
+ @check_cached_vector_store
145
+ def build_vector_store(self) -> OracleVS:
146
+ # Convert DataFrame to Data if needed using parent's method
147
+ self.ingest_data = self._prepare_ingest_data()
148
+
149
+ documents = []
150
+ for _input in self.ingest_data or []:
151
+ inp = _input
152
+ if isinstance(_input, Data):
153
+ inp = _input.to_lc_document()
154
+
155
+ if hasattr(inp, "metadata"):
156
+ inp.metadata = serialize(inp.metadata)
157
+
158
+ documents.append(inp)
159
+
160
+ opened_connection = False
161
+ if (not hasattr(self, "connection")) or (not self.connection):
162
+ connection_params = build_connection_params(
163
+ self.connection_params,
164
+ user=self.user,
165
+ password=self.password,
166
+ dsn=self.dsn,
167
+ wallet_password=self.wallet_password,
168
+ )
169
+ self.connection = oracledb.connect(**connection_params)
170
+ opened_connection = True
171
+
172
+ distance_strategy2function = {
173
+ "EUCLIDEAN": DistanceStrategy.EUCLIDEAN_DISTANCE,
174
+ "DOT": DistanceStrategy.DOT_PRODUCT,
175
+ "COSINE": DistanceStrategy.COSINE,
176
+ }
177
+ distance_strategy = distance_strategy2function[self.distance_strategy]
178
+
179
+ params = {
180
+ "client": self.connection,
181
+ "table_name": self.table_name,
182
+ "distance_strategy": distance_strategy,
183
+ }
184
+
185
+ if not version("langchain_oracledb").startswith("1.0"):
186
+ params["mutate_on_duplicate"] = self.mutate_on_duplicate
187
+
188
+ try:
189
+ if documents:
190
+ vs = OracleVS.from_documents(embedding=self.embedding, documents=documents, **params)
191
+ else:
192
+ vs = OracleVS(embedding_function=self.embedding, **params)
193
+
194
+ if self.create_index:
195
+ index_params = (self.index_params or {}).copy()
196
+
197
+ if "idx_name" not in index_params:
198
+ params_str = json.dumps(index_params, sort_keys=True)
199
+ distance_strategy = self.distance_strategy
200
+ params_str = params_str + distance_strategy
201
+ index_name = params["table_name"] + "_idx_" + hashlib.sha256(params_str.encode()).hexdigest()[:16]
202
+ index_params["idx_name"] = index_name
203
+
204
+ if "idx_type" not in index_params:
205
+ index_params["idx_type"] = "HNSW"
206
+
207
+ if "" in index_params:
208
+ del index_params[""]
209
+ create_index(self.connection, vs, index_params)
210
+ except Exception:
211
+ if opened_connection:
212
+ with suppress(Exception):
213
+ self.connection.close()
214
+ self.connection = None
215
+ raise
216
+
217
+ return vs
218
+
219
+ def _map_search_type(self) -> str:
220
+ if self.search_type == "MMR (Max Marginal Relevance)":
221
+ return "mmr"
222
+ return "similarity"
223
+
224
+ def _build_search_args(self) -> dict[str, Any]:
225
+ return {"k": self.number_of_results}
226
+
227
+ def search_documents(self) -> list[Data]:
228
+ raw_search_query = getattr(self, "search_query", None)
229
+ search_query = str(raw_search_query).strip() if raw_search_query is not None else ""
230
+
231
+ if not search_query:
232
+ self.status = []
233
+ return []
234
+
235
+ vector_store = self.build_vector_store()
236
+ docs = vector_store.search(
237
+ query=search_query,
238
+ search_type=self._map_search_type(),
239
+ **self._build_search_args(),
240
+ )
241
+
242
+ data = docs_to_data(docs)
243
+ for item in data:
244
+ item.data = normalize_search_result(item.data)
245
+ self.status = data
246
+ return data
@@ -0,0 +1,16 @@
1
+ {
2
+ "$schema": "https://schemas.langflow.org/extension/v1.json",
3
+ "id": "lfx-oracle",
4
+ "version": "0.1.0",
5
+ "name": "Oracle",
6
+ "description": "Oracle component(s) as a standalone Langflow Extension Bundle.",
7
+ "lfx": {
8
+ "compat": ["1"]
9
+ },
10
+ "bundles": [
11
+ {
12
+ "name": "oracle",
13
+ "path": "components/oracle"
14
+ }
15
+ ]
16
+ }
@@ -0,0 +1,46 @@
1
+ Metadata-Version: 2.4
2
+ Name: lfx-oracle
3
+ Version: 0.1.0
4
+ Summary: Oracle Database components (Doc Loader, Autonomous Database Loader, Embeddings, Vector Store) as a standalone Langflow Extension Bundle.
5
+ Project-URL: Homepage, https://github.com/langflow-ai/langflow
6
+ Project-URL: Documentation, https://docs.langflow.org/extensions
7
+ Project-URL: Repository, https://github.com/langflow-ai/langflow
8
+ Author-email: Langflow <contact@langflow.org>
9
+ License: MIT
10
+ Keywords: bundle,extension,langflow,lfx,oracle,oracledb,vector-store
11
+ Requires-Python: <3.15,>=3.10
12
+ Requires-Dist: langchain-community<1.0.0,>=0.4.1
13
+ Requires-Dist: langchain-oracledb<2.0.0,>=1.0.1
14
+ Requires-Dist: lfx<2.0.0,>=1.11.0.dev0
15
+ Requires-Dist: oracledb<4.0.0,>=2.2.0
16
+ Description-Content-Type: text/markdown
17
+
18
+ # lfx-oracle
19
+
20
+ Oracle Database components (Doc Loader, Autonomous Database Loader,
21
+ Embeddings, Vector Store) as a standalone Langflow Extension Bundle.
22
+
23
+ ## Install
24
+
25
+ ```bash
26
+ pip install lfx-oracle
27
+ ```
28
+
29
+ The bundle is registered automatically via the `langflow.extensions`
30
+ entry-point. After install, restart your Langflow server; the bundle's
31
+ components will appear in the palette under the `Oracle` group.
32
+
33
+ ## Develop
34
+
35
+ ```bash
36
+ cd src/bundles/oracle
37
+ pip install -e .
38
+ lfx extension validate src/lfx_oracle
39
+ ```
40
+
41
+ ## Migration
42
+
43
+ Saved flows referencing the legacy class name(s) or the old import paths
44
+ under `lfx.components.oracledb.*` are rewritten to the new namespaced
45
+ IDs by the migration table in
46
+ `src/lfx/src/lfx/extension/migration/migration_table.json`.
@@ -0,0 +1,11 @@
1
+ lfx_oracle/__init__.py,sha256=YFjqnOfX-NwVbqX_sUbODKfHtfaGpOf-In3RrmSs2lg,735
2
+ lfx_oracle/extension.json,sha256=mKatK928YRWMnI7FpxlIR6Etjo56WJUvhmXLMUb29c8,339
3
+ lfx_oracle/components/oracle/__init__.py,sha256=j6jLNObRB-mfQlnemFPWN81sZ1r-xjz1e0onvpnPWrA,632
4
+ lfx_oracle/components/oracle/connection.py,sha256=6zFIFTsvsnUUVkfKwMytlZ9eYYdcGN2etPxZ9-eUu74,2377
5
+ lfx_oracle/components/oracle/oracledb_embeddings.py,sha256=SIY1OjOMYXR_sX7X9A5S4d416qziecDyC47GtKdxqgs,2562
6
+ lfx_oracle/components/oracle/oracledb_loaders.py,sha256=GRCPuuO1b9MxoF6V-1fKAuxbBokRxbBZ9hM0si8D2Hw,6742
7
+ lfx_oracle/components/oracle/oraclevs.py,sha256=MxBd9hWOy4OFupLyrADiXUbXJEZfEhcZxKueQsJDss8,9258
8
+ lfx_oracle-0.1.0.dist-info/METADATA,sha256=15gA-JmbYUZjRUFII4194aWwXOAD9hZ7Le9bG40wIBk,1521
9
+ lfx_oracle-0.1.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
10
+ lfx_oracle-0.1.0.dist-info/entry_points.txt,sha256=BJ7HwFhchI9erTGgLTs--q9cHK2xQVBgQLL3g5DXbZA,46
11
+ lfx_oracle-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.30.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [langflow.extensions]
2
+ lfx-oracle = lfx_oracle