lfx-nightly 0.1.13.dev0__py3-none-any.whl → 0.2.0.dev0__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/_assets/component_index.json +1 -1
- lfx/base/agents/agent.py +109 -29
- lfx/base/agents/events.py +102 -35
- lfx/base/agents/utils.py +15 -2
- lfx/base/composio/composio_base.py +24 -9
- lfx/base/datastax/__init__.py +5 -0
- lfx/{components/vectorstores/astradb.py → base/datastax/astradb_base.py} +84 -473
- lfx/base/io/chat.py +5 -4
- lfx/base/mcp/util.py +101 -15
- lfx/base/models/model_input_constants.py +74 -7
- lfx/base/models/ollama_constants.py +3 -0
- lfx/base/models/watsonx_constants.py +12 -0
- lfx/cli/commands.py +1 -1
- lfx/components/agents/__init__.py +3 -1
- lfx/components/agents/agent.py +47 -4
- lfx/components/agents/altk_agent.py +366 -0
- lfx/components/agents/cuga_agent.py +1 -1
- lfx/components/agents/mcp_component.py +32 -2
- lfx/components/amazon/amazon_bedrock_converse.py +1 -1
- lfx/components/apify/apify_actor.py +3 -3
- lfx/components/datastax/__init__.py +12 -6
- lfx/components/datastax/{astra_assistant_manager.py → astradb_assistant_manager.py} +1 -0
- lfx/components/datastax/astradb_chatmemory.py +40 -0
- lfx/components/datastax/astradb_cql.py +5 -31
- lfx/components/datastax/astradb_graph.py +9 -123
- lfx/components/datastax/astradb_tool.py +12 -52
- lfx/components/datastax/astradb_vectorstore.py +133 -976
- lfx/components/datastax/create_assistant.py +1 -0
- lfx/components/datastax/create_thread.py +1 -0
- lfx/components/datastax/dotenv.py +1 -0
- lfx/components/datastax/get_assistant.py +1 -0
- lfx/components/datastax/getenvvar.py +1 -0
- lfx/components/datastax/graph_rag.py +1 -1
- lfx/components/datastax/list_assistants.py +1 -0
- lfx/components/datastax/run.py +1 -0
- lfx/components/docling/__init__.py +3 -0
- lfx/components/docling/docling_remote_vlm.py +284 -0
- lfx/components/ibm/watsonx.py +25 -21
- lfx/components/input_output/chat.py +8 -0
- lfx/components/input_output/chat_output.py +8 -0
- lfx/components/knowledge_bases/ingestion.py +17 -9
- lfx/components/knowledge_bases/retrieval.py +16 -8
- lfx/components/logic/loop.py +4 -0
- lfx/components/mistral/mistral_embeddings.py +1 -1
- lfx/components/models/embedding_model.py +88 -7
- lfx/components/ollama/ollama.py +221 -14
- lfx/components/openrouter/openrouter.py +49 -147
- lfx/components/processing/parser.py +6 -1
- lfx/components/processing/structured_output.py +55 -17
- lfx/components/vectorstores/__init__.py +0 -6
- lfx/custom/custom_component/component.py +3 -2
- lfx/field_typing/constants.py +1 -0
- lfx/graph/edge/base.py +2 -2
- lfx/graph/graph/base.py +1 -1
- lfx/graph/graph/schema.py +3 -2
- lfx/graph/vertex/vertex_types.py +1 -1
- lfx/io/schema.py +6 -0
- lfx/schema/schema.py +5 -0
- {lfx_nightly-0.1.13.dev0.dist-info → lfx_nightly-0.2.0.dev0.dist-info}/METADATA +1 -1
- {lfx_nightly-0.1.13.dev0.dist-info → lfx_nightly-0.2.0.dev0.dist-info}/RECORD +63 -81
- lfx/components/datastax/astra_db.py +0 -77
- lfx/components/datastax/cassandra.py +0 -92
- lfx/components/vectorstores/astradb_graph.py +0 -326
- lfx/components/vectorstores/cassandra.py +0 -264
- lfx/components/vectorstores/cassandra_graph.py +0 -238
- lfx/components/vectorstores/chroma.py +0 -167
- lfx/components/vectorstores/clickhouse.py +0 -135
- lfx/components/vectorstores/couchbase.py +0 -102
- lfx/components/vectorstores/elasticsearch.py +0 -267
- lfx/components/vectorstores/faiss.py +0 -111
- lfx/components/vectorstores/graph_rag.py +0 -141
- lfx/components/vectorstores/hcd.py +0 -314
- lfx/components/vectorstores/milvus.py +0 -115
- lfx/components/vectorstores/mongodb_atlas.py +0 -213
- lfx/components/vectorstores/opensearch.py +0 -243
- lfx/components/vectorstores/pgvector.py +0 -72
- lfx/components/vectorstores/pinecone.py +0 -134
- lfx/components/vectorstores/qdrant.py +0 -109
- lfx/components/vectorstores/supabase.py +0 -76
- lfx/components/vectorstores/upstash.py +0 -124
- lfx/components/vectorstores/vectara.py +0 -97
- lfx/components/vectorstores/vectara_rag.py +0 -164
- lfx/components/vectorstores/weaviate.py +0 -89
- /lfx/components/datastax/{astra_vectorize.py → astradb_vectorize.py} +0 -0
- {lfx_nightly-0.1.13.dev0.dist-info → lfx_nightly-0.2.0.dev0.dist-info}/WHEEL +0 -0
- {lfx_nightly-0.1.13.dev0.dist-info → lfx_nightly-0.2.0.dev0.dist-info}/entry_points.txt +0 -0
|
@@ -8,6 +8,7 @@ class AssistantsListAssistants(ComponentWithCache):
|
|
|
8
8
|
display_name = "List Assistants"
|
|
9
9
|
description = "Returns a list of assistant id's"
|
|
10
10
|
icon = "AstraDB"
|
|
11
|
+
legacy = True
|
|
11
12
|
outputs = [
|
|
12
13
|
Output(display_name="Assistants", name="assistants", method="process_inputs"),
|
|
13
14
|
]
|
lfx/components/datastax/run.py
CHANGED
|
@@ -8,6 +8,7 @@ if TYPE_CHECKING:
|
|
|
8
8
|
from .chunk_docling_document import ChunkDoclingDocumentComponent
|
|
9
9
|
from .docling_inline import DoclingInlineComponent
|
|
10
10
|
from .docling_remote import DoclingRemoteComponent
|
|
11
|
+
from .docling_remote_vlm import DoclingRemoteVLMComponent
|
|
11
12
|
from .export_docling_document import ExportDoclingDocumentComponent
|
|
12
13
|
|
|
13
14
|
_dynamic_imports = {
|
|
@@ -15,12 +16,14 @@ _dynamic_imports = {
|
|
|
15
16
|
"DoclingInlineComponent": "docling_inline",
|
|
16
17
|
"DoclingRemoteComponent": "docling_remote",
|
|
17
18
|
"ExportDoclingDocumentComponent": "export_docling_document",
|
|
19
|
+
"DoclingRemoteVLMComponent": "docling_remote_vlm",
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
__all__ = [
|
|
21
23
|
"ChunkDoclingDocumentComponent",
|
|
22
24
|
"DoclingInlineComponent",
|
|
23
25
|
"DoclingRemoteComponent",
|
|
26
|
+
"DoclingRemoteVLMComponent",
|
|
24
27
|
"ExportDoclingDocumentComponent",
|
|
25
28
|
]
|
|
26
29
|
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
import requests
|
|
4
|
+
from docling.datamodel.base_models import ConversionStatus, InputFormat
|
|
5
|
+
from docling.datamodel.pipeline_options import (
|
|
6
|
+
ApiVlmOptions,
|
|
7
|
+
ResponseFormat,
|
|
8
|
+
VlmPipelineOptions,
|
|
9
|
+
)
|
|
10
|
+
from docling.document_converter import DocumentConverter, PdfFormatOption
|
|
11
|
+
from docling.pipeline.vlm_pipeline import VlmPipeline
|
|
12
|
+
from langflow.base.data import BaseFileComponent
|
|
13
|
+
from langflow.inputs import DropdownInput, SecretStrInput, StrInput
|
|
14
|
+
from langflow.schema import Data
|
|
15
|
+
from langflow.schema.dotdict import dotdict
|
|
16
|
+
|
|
17
|
+
from lfx.components.ibm.watsonx import WatsonxAIComponent
|
|
18
|
+
from lfx.log.logger import logger
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class DoclingRemoteVLMComponent(BaseFileComponent):
|
|
22
|
+
display_name = "Docling Remote VLM"
|
|
23
|
+
description = (
|
|
24
|
+
"Uses Docling to process input documents running a VLM pipeline with a remote model"
|
|
25
|
+
"(OpenAI-compatible API or IBM Cloud)."
|
|
26
|
+
)
|
|
27
|
+
documentation = "https://docling-project.github.io/docling/examples/vlm_pipeline_api_model/"
|
|
28
|
+
trace_type = "tool"
|
|
29
|
+
icon = "Docling"
|
|
30
|
+
name = "DoclingRemoteVLM"
|
|
31
|
+
|
|
32
|
+
# https://docling-project.github.io/docling/usage/supported_formats/
|
|
33
|
+
VALID_EXTENSIONS = [
|
|
34
|
+
"adoc",
|
|
35
|
+
"asciidoc",
|
|
36
|
+
"asc",
|
|
37
|
+
"bmp",
|
|
38
|
+
"csv",
|
|
39
|
+
"dotx",
|
|
40
|
+
"dotm",
|
|
41
|
+
"docm",
|
|
42
|
+
"docx",
|
|
43
|
+
"htm",
|
|
44
|
+
"html",
|
|
45
|
+
"jpeg",
|
|
46
|
+
"json",
|
|
47
|
+
"md",
|
|
48
|
+
"pdf",
|
|
49
|
+
"png",
|
|
50
|
+
"potx",
|
|
51
|
+
"ppsx",
|
|
52
|
+
"pptm",
|
|
53
|
+
"potm",
|
|
54
|
+
"ppsm",
|
|
55
|
+
"pptx",
|
|
56
|
+
"tiff",
|
|
57
|
+
"txt",
|
|
58
|
+
"xls",
|
|
59
|
+
"xlsx",
|
|
60
|
+
"xhtml",
|
|
61
|
+
"xml",
|
|
62
|
+
"webp",
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
inputs = [
|
|
66
|
+
*BaseFileComponent.get_base_inputs(),
|
|
67
|
+
DropdownInput(
|
|
68
|
+
name="provider",
|
|
69
|
+
display_name="Provider",
|
|
70
|
+
info="Select which remote VLM provider to use.",
|
|
71
|
+
options=["IBM Cloud", "OpenAI-Compatible"],
|
|
72
|
+
value="IBM Cloud",
|
|
73
|
+
real_time_refresh=True,
|
|
74
|
+
),
|
|
75
|
+
# IBM Cloud inputs
|
|
76
|
+
SecretStrInput(
|
|
77
|
+
name="watsonx_api_key",
|
|
78
|
+
display_name="Watsonx API Key",
|
|
79
|
+
info="IBM Cloud API key used for authentication (leave blank to load from .env).",
|
|
80
|
+
required=False,
|
|
81
|
+
),
|
|
82
|
+
StrInput(
|
|
83
|
+
name="watsonx_project_id",
|
|
84
|
+
display_name="Watsonx Project ID",
|
|
85
|
+
required=False,
|
|
86
|
+
info="The Watsonx project ID or deployment space ID associated with the model.",
|
|
87
|
+
value="",
|
|
88
|
+
),
|
|
89
|
+
DropdownInput(
|
|
90
|
+
name="url",
|
|
91
|
+
display_name="Watsonx API Endpoint",
|
|
92
|
+
info="The base URL of the Watsonx API.",
|
|
93
|
+
options=[
|
|
94
|
+
"https://us-south.ml.cloud.ibm.com",
|
|
95
|
+
"https://eu-de.ml.cloud.ibm.com",
|
|
96
|
+
"https://eu-gb.ml.cloud.ibm.com",
|
|
97
|
+
"https://au-syd.ml.cloud.ibm.com",
|
|
98
|
+
"https://jp-tok.ml.cloud.ibm.com",
|
|
99
|
+
"https://ca-tor.ml.cloud.ibm.com",
|
|
100
|
+
],
|
|
101
|
+
real_time_refresh=True,
|
|
102
|
+
),
|
|
103
|
+
DropdownInput(
|
|
104
|
+
name="model_name",
|
|
105
|
+
display_name="Model Name",
|
|
106
|
+
options=[],
|
|
107
|
+
value=None,
|
|
108
|
+
dynamic=True,
|
|
109
|
+
required=False,
|
|
110
|
+
),
|
|
111
|
+
# OpenAI inputs
|
|
112
|
+
StrInput(
|
|
113
|
+
name="openai_base_url",
|
|
114
|
+
display_name="OpenAI-Compatible API Base URL",
|
|
115
|
+
info="Example: https://openrouter.ai/api/",
|
|
116
|
+
required=False,
|
|
117
|
+
show=False,
|
|
118
|
+
),
|
|
119
|
+
SecretStrInput(
|
|
120
|
+
name="openai_api_key",
|
|
121
|
+
display_name="API Key",
|
|
122
|
+
info="API key for OpenAI-compatible endpoints (leave blank if not required).",
|
|
123
|
+
required=False,
|
|
124
|
+
show=False,
|
|
125
|
+
),
|
|
126
|
+
StrInput(
|
|
127
|
+
name="openai_model",
|
|
128
|
+
display_name="OpenAI Model Name",
|
|
129
|
+
info="Model ID for OpenAI-compatible provider (e.g. gpt-4o-mini).",
|
|
130
|
+
required=False,
|
|
131
|
+
show=False,
|
|
132
|
+
),
|
|
133
|
+
StrInput(name="vlm_prompt", display_name="Prompt", info="Prompt for VLM.", required=False),
|
|
134
|
+
]
|
|
135
|
+
|
|
136
|
+
outputs = [*BaseFileComponent.get_base_outputs()]
|
|
137
|
+
|
|
138
|
+
@staticmethod
|
|
139
|
+
def fetch_models(base_url: str) -> list[str]:
|
|
140
|
+
"""Fetch available models from the Watsonx.ai API."""
|
|
141
|
+
try:
|
|
142
|
+
endpoint = f"{base_url}/ml/v1/foundation_model_specs"
|
|
143
|
+
params = {"version": "2024-09-16", "filters": "function_text_chat,!lifecycle_withdrawn"}
|
|
144
|
+
response = requests.get(endpoint, params=params, timeout=10)
|
|
145
|
+
response.raise_for_status()
|
|
146
|
+
data = response.json()
|
|
147
|
+
models = [model["model_id"] for model in data.get("resources", [])]
|
|
148
|
+
return sorted(models)
|
|
149
|
+
except (requests.RequestException, requests.HTTPError, requests.Timeout, ConnectionError, ValueError):
|
|
150
|
+
logger.exception("Error fetching models. Using default models.")
|
|
151
|
+
return WatsonxAIComponent._default_models # noqa: SLF001
|
|
152
|
+
|
|
153
|
+
def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None):
|
|
154
|
+
"""Update shown fields based on chosen provider."""
|
|
155
|
+
logger.info(f"update_build_config called: field_name={field_name}, field_value={field_value}")
|
|
156
|
+
|
|
157
|
+
if field_name == "provider":
|
|
158
|
+
provider_choice = field_value
|
|
159
|
+
|
|
160
|
+
if provider_choice == "IBM Cloud":
|
|
161
|
+
build_config.model_name.show = True
|
|
162
|
+
build_config.watsonx_api_key.show = True
|
|
163
|
+
build_config.watsonx_project_id.show = True
|
|
164
|
+
build_config.url.show = True
|
|
165
|
+
|
|
166
|
+
build_config.openai_base_url.show = False
|
|
167
|
+
build_config.openai_api_key.show = False
|
|
168
|
+
build_config.openai_model.show = False
|
|
169
|
+
|
|
170
|
+
elif provider_choice == "OpenAI-Compatible":
|
|
171
|
+
build_config.model_name.show = False
|
|
172
|
+
build_config.watsonx_api_key.show = False
|
|
173
|
+
build_config.watsonx_project_id.show = False
|
|
174
|
+
build_config.url.show = False
|
|
175
|
+
|
|
176
|
+
build_config.openai_base_url.show = True
|
|
177
|
+
build_config.openai_api_key.show = True
|
|
178
|
+
build_config.openai_model.show = True
|
|
179
|
+
|
|
180
|
+
if field_name == "url":
|
|
181
|
+
provider_value = build_config.provider.value if hasattr(build_config, "provider") else None
|
|
182
|
+
if provider_value == "IBM Cloud" and field_value:
|
|
183
|
+
models = self.fetch_models(base_url=field_value)
|
|
184
|
+
build_config.model_name.options = models
|
|
185
|
+
if models:
|
|
186
|
+
build_config.model_name.value = models[0]
|
|
187
|
+
logger.info(f"Updated Watsonx model list: {len(models)} models found.")
|
|
188
|
+
|
|
189
|
+
def watsonx_vlm_options(self, model: str, prompt: str):
|
|
190
|
+
"""Creates Docling ApiVlmOptions for a watsonx VLM."""
|
|
191
|
+
api_key = getattr(self, "watsonx_api_key", "")
|
|
192
|
+
project_id = getattr(self, "watsonx_project_id", "")
|
|
193
|
+
base_url = getattr(self, "url", "https://us-south.ml.cloud.ibm.com")
|
|
194
|
+
|
|
195
|
+
def _get_iam_access_token(api_key: str) -> str:
|
|
196
|
+
res = requests.post(
|
|
197
|
+
url="https://iam.cloud.ibm.com/identity/token",
|
|
198
|
+
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
|
199
|
+
data=f"grant_type=urn:ibm:params:oauth:grant-type:apikey&apikey={api_key}",
|
|
200
|
+
timeout=90,
|
|
201
|
+
)
|
|
202
|
+
res.raise_for_status()
|
|
203
|
+
return res.json()["access_token"]
|
|
204
|
+
|
|
205
|
+
access_token = _get_iam_access_token(api_key)
|
|
206
|
+
return ApiVlmOptions(
|
|
207
|
+
url=f"{base_url}/ml/v1/text/chat?version=2023-05-29",
|
|
208
|
+
params={"model_id": model, "project_id": project_id, "parameters": {"max_new_tokens": 400}},
|
|
209
|
+
headers={"Authorization": f"Bearer {access_token}"},
|
|
210
|
+
prompt=prompt,
|
|
211
|
+
timeout=60,
|
|
212
|
+
response_format=ResponseFormat.MARKDOWN,
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
def openai_compatible_vlm_options(
|
|
216
|
+
self,
|
|
217
|
+
model: str,
|
|
218
|
+
prompt: str,
|
|
219
|
+
response_format: ResponseFormat,
|
|
220
|
+
url: str,
|
|
221
|
+
temperature: float = 0.7,
|
|
222
|
+
max_tokens: int = 4096,
|
|
223
|
+
api_key: str = "",
|
|
224
|
+
*,
|
|
225
|
+
skip_special_tokens: bool = False,
|
|
226
|
+
):
|
|
227
|
+
"""Create OpenAI-compatible Docling ApiVlmOptions options (e.g., LM Studio, vLLM, Ollama)."""
|
|
228
|
+
api_key = getattr(self, "openai_api_key", api_key)
|
|
229
|
+
model_override = getattr(self, "openai_model", model)
|
|
230
|
+
|
|
231
|
+
headers = {}
|
|
232
|
+
if api_key:
|
|
233
|
+
headers["Authorization"] = f"Bearer {api_key}"
|
|
234
|
+
|
|
235
|
+
return ApiVlmOptions(
|
|
236
|
+
url=f"{url}/v1/chat/completions",
|
|
237
|
+
params={"model": model_override, "max_tokens": max_tokens, "skip_special_tokens": skip_special_tokens},
|
|
238
|
+
headers=headers,
|
|
239
|
+
prompt=prompt,
|
|
240
|
+
timeout=90,
|
|
241
|
+
scale=2.0,
|
|
242
|
+
temperature=temperature,
|
|
243
|
+
response_format=response_format,
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
def process_files(self, file_list: list[BaseFileComponent.BaseFile]) -> list[BaseFileComponent.BaseFile]:
|
|
247
|
+
file_paths = [file.path for file in file_list if file.path]
|
|
248
|
+
if not file_paths:
|
|
249
|
+
logger.warning("No files to process.")
|
|
250
|
+
return file_list
|
|
251
|
+
|
|
252
|
+
provider = getattr(self, "provider", "IBM Cloud")
|
|
253
|
+
prompt = getattr(self, "vlm_prompt", "")
|
|
254
|
+
|
|
255
|
+
if provider == "IBM Cloud":
|
|
256
|
+
model = getattr(self, "model_name", "")
|
|
257
|
+
vlm_opts = self.watsonx_vlm_options(model=model, prompt=prompt)
|
|
258
|
+
else:
|
|
259
|
+
model = getattr(self, "openai_model", "") or getattr(self, "model_name", "")
|
|
260
|
+
base_url = getattr(self, "openai_base_url", "")
|
|
261
|
+
vlm_opts = self.openai_compatible_vlm_options(
|
|
262
|
+
model=model,
|
|
263
|
+
prompt=prompt,
|
|
264
|
+
response_format=ResponseFormat.MARKDOWN,
|
|
265
|
+
url=base_url,
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
pipeline_options = VlmPipelineOptions(enable_remote_services=True)
|
|
269
|
+
pipeline_options.vlm_options = vlm_opts
|
|
270
|
+
|
|
271
|
+
converter = DocumentConverter(
|
|
272
|
+
format_options={
|
|
273
|
+
InputFormat.PDF: PdfFormatOption(pipeline_options=pipeline_options, pipeline_cls=VlmPipeline)
|
|
274
|
+
}
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
results = converter.convert_all(file_paths)
|
|
278
|
+
processed_data = [
|
|
279
|
+
Data(data={"doc": res.document, "file_path": str(res.input.file)})
|
|
280
|
+
if res.status == ConversionStatus.SUCCESS
|
|
281
|
+
else None
|
|
282
|
+
for res in results
|
|
283
|
+
]
|
|
284
|
+
return self.rollup_data(file_list, processed_data)
|
lfx/components/ibm/watsonx.py
CHANGED
|
@@ -21,23 +21,24 @@ class WatsonxAIComponent(LCModelComponent):
|
|
|
21
21
|
beta = False
|
|
22
22
|
|
|
23
23
|
_default_models = ["ibm/granite-3-2b-instruct", "ibm/granite-3-8b-instruct", "ibm/granite-13b-instruct-v2"]
|
|
24
|
-
|
|
24
|
+
_urls = [
|
|
25
|
+
"https://us-south.ml.cloud.ibm.com",
|
|
26
|
+
"https://eu-de.ml.cloud.ibm.com",
|
|
27
|
+
"https://eu-gb.ml.cloud.ibm.com",
|
|
28
|
+
"https://au-syd.ml.cloud.ibm.com",
|
|
29
|
+
"https://jp-tok.ml.cloud.ibm.com",
|
|
30
|
+
"https://ca-tor.ml.cloud.ibm.com",
|
|
31
|
+
]
|
|
25
32
|
inputs = [
|
|
26
33
|
*LCModelComponent.get_base_inputs(),
|
|
27
34
|
DropdownInput(
|
|
28
|
-
name="
|
|
35
|
+
name="base_url",
|
|
29
36
|
display_name="watsonx API Endpoint",
|
|
30
37
|
info="The base URL of the API.",
|
|
31
|
-
value=
|
|
32
|
-
options=
|
|
33
|
-
"https://us-south.ml.cloud.ibm.com",
|
|
34
|
-
"https://eu-de.ml.cloud.ibm.com",
|
|
35
|
-
"https://eu-gb.ml.cloud.ibm.com",
|
|
36
|
-
"https://au-syd.ml.cloud.ibm.com",
|
|
37
|
-
"https://jp-tok.ml.cloud.ibm.com",
|
|
38
|
-
"https://ca-tor.ml.cloud.ibm.com",
|
|
39
|
-
],
|
|
38
|
+
value=[],
|
|
39
|
+
options=_urls,
|
|
40
40
|
real_time_refresh=True,
|
|
41
|
+
required=True,
|
|
41
42
|
),
|
|
42
43
|
StrInput(
|
|
43
44
|
name="project_id",
|
|
@@ -56,8 +57,9 @@ class WatsonxAIComponent(LCModelComponent):
|
|
|
56
57
|
display_name="Model Name",
|
|
57
58
|
options=[],
|
|
58
59
|
value=None,
|
|
59
|
-
|
|
60
|
+
real_time_refresh=True,
|
|
60
61
|
required=True,
|
|
62
|
+
refresh_button=True,
|
|
61
63
|
),
|
|
62
64
|
IntInput(
|
|
63
65
|
name="max_tokens",
|
|
@@ -155,18 +157,20 @@ class WatsonxAIComponent(LCModelComponent):
|
|
|
155
157
|
|
|
156
158
|
def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None):
|
|
157
159
|
"""Update model options when URL or API key changes."""
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
if field_name == "url" and field_value:
|
|
160
|
+
if field_name == "base_url" and field_value:
|
|
161
161
|
try:
|
|
162
|
-
models = self.fetch_models(base_url=
|
|
163
|
-
build_config
|
|
164
|
-
if build_config
|
|
165
|
-
build_config
|
|
166
|
-
info_message = f"Updated model options: {len(models)} models found in {
|
|
162
|
+
models = self.fetch_models(base_url=field_value)
|
|
163
|
+
build_config["model_name"]["options"] = models
|
|
164
|
+
if build_config["model_name"]["value"]:
|
|
165
|
+
build_config["model_name"]["value"] = models[0]
|
|
166
|
+
info_message = f"Updated model options: {len(models)} models found in {field_value}"
|
|
167
167
|
logger.info(info_message)
|
|
168
168
|
except Exception: # noqa: BLE001
|
|
169
169
|
logger.exception("Error updating model options.")
|
|
170
|
+
if field_name == "model_name" and field_value and field_value in WatsonxAIComponent._urls:
|
|
171
|
+
build_config["model_name"]["options"] = self.fetch_models(base_url=field_value)
|
|
172
|
+
build_config["model_name"]["value"] = ""
|
|
173
|
+
return build_config
|
|
170
174
|
|
|
171
175
|
def build_model(self) -> LanguageModel:
|
|
172
176
|
# Parse logit_bias from JSON string if provided
|
|
@@ -195,7 +199,7 @@ class WatsonxAIComponent(LCModelComponent):
|
|
|
195
199
|
|
|
196
200
|
return ChatWatsonx(
|
|
197
201
|
apikey=SecretStr(self.api_key).get_secret_value(),
|
|
198
|
-
url=self.
|
|
202
|
+
url=self.base_url,
|
|
199
203
|
project_id=self.project_id,
|
|
200
204
|
model_id=self.model_name,
|
|
201
205
|
params=chat_params,
|
|
@@ -60,6 +60,13 @@ class ChatInput(ChatComponent):
|
|
|
60
60
|
info="The session ID of the chat. If empty, the current session ID parameter will be used.",
|
|
61
61
|
advanced=True,
|
|
62
62
|
),
|
|
63
|
+
MessageTextInput(
|
|
64
|
+
name="context_id",
|
|
65
|
+
display_name="Context ID",
|
|
66
|
+
info="The context ID of the chat. Adds an extra layer to the local memory.",
|
|
67
|
+
value="",
|
|
68
|
+
advanced=True,
|
|
69
|
+
),
|
|
63
70
|
FileInput(
|
|
64
71
|
name="files",
|
|
65
72
|
display_name="Files",
|
|
@@ -87,6 +94,7 @@ class ChatInput(ChatComponent):
|
|
|
87
94
|
sender=self.sender,
|
|
88
95
|
sender_name=self.sender_name,
|
|
89
96
|
session_id=self.session_id,
|
|
97
|
+
context_id=self.context_id,
|
|
90
98
|
files=files,
|
|
91
99
|
)
|
|
92
100
|
if self.session_id and isinstance(message, Message) and self.should_store_message:
|
|
@@ -63,6 +63,13 @@ class ChatOutput(ChatComponent):
|
|
|
63
63
|
info="The session ID of the chat. If empty, the current session ID parameter will be used.",
|
|
64
64
|
advanced=True,
|
|
65
65
|
),
|
|
66
|
+
MessageTextInput(
|
|
67
|
+
name="context_id",
|
|
68
|
+
display_name="Context ID",
|
|
69
|
+
info="The context ID of the chat. Adds an extra layer to the local memory.",
|
|
70
|
+
value="",
|
|
71
|
+
advanced=True,
|
|
72
|
+
),
|
|
66
73
|
MessageTextInput(
|
|
67
74
|
name="data_template",
|
|
68
75
|
display_name="Data Template",
|
|
@@ -121,6 +128,7 @@ class ChatOutput(ChatComponent):
|
|
|
121
128
|
message.sender = self.sender
|
|
122
129
|
message.sender_name = self.sender_name
|
|
123
130
|
message.session_id = self.session_id
|
|
131
|
+
message.context_id = self.context_id
|
|
124
132
|
message.flow_id = self.graph.flow_id if hasattr(self, "graph") else None
|
|
125
133
|
message.properties.source = self._build_source(source_id, display_name, source)
|
|
126
134
|
|
|
@@ -48,12 +48,20 @@ HUGGINGFACE_MODEL_NAMES = [
|
|
|
48
48
|
]
|
|
49
49
|
COHERE_MODEL_NAMES = ["embed-english-v3.0", "embed-multilingual-v3.0"]
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
51
|
+
_KNOWLEDGE_BASES_ROOT_PATH: Path | None = None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _get_knowledge_bases_root_path() -> Path:
|
|
55
|
+
"""Lazy load the knowledge bases root path from settings."""
|
|
56
|
+
global _KNOWLEDGE_BASES_ROOT_PATH # noqa: PLW0603
|
|
57
|
+
if _KNOWLEDGE_BASES_ROOT_PATH is None:
|
|
58
|
+
settings = get_settings_service().settings
|
|
59
|
+
knowledge_directory = settings.knowledge_bases_dir
|
|
60
|
+
if not knowledge_directory:
|
|
61
|
+
msg = "Knowledge bases directory is not set in the settings."
|
|
62
|
+
raise ValueError(msg)
|
|
63
|
+
_KNOWLEDGE_BASES_ROOT_PATH = Path(knowledge_directory).expanduser()
|
|
64
|
+
return _KNOWLEDGE_BASES_ROOT_PATH
|
|
57
65
|
|
|
58
66
|
|
|
59
67
|
class KnowledgeIngestionComponent(Component):
|
|
@@ -203,7 +211,7 @@ class KnowledgeIngestionComponent(Component):
|
|
|
203
211
|
# ------ Internal helpers ---------------------------------------------
|
|
204
212
|
def _get_kb_root(self) -> Path:
|
|
205
213
|
"""Return the root directory for knowledge bases."""
|
|
206
|
-
return
|
|
214
|
+
return _get_knowledge_bases_root_path()
|
|
207
215
|
|
|
208
216
|
def _validate_column_config(self, df_source: pd.DataFrame) -> list[dict[str, Any]]:
|
|
209
217
|
"""Validate column configuration using Structured Output patterns."""
|
|
@@ -662,7 +670,7 @@ class KnowledgeIngestionComponent(Component):
|
|
|
662
670
|
raise ValueError(msg) from e
|
|
663
671
|
|
|
664
672
|
# Create the new knowledge base directory
|
|
665
|
-
kb_path =
|
|
673
|
+
kb_path = _get_knowledge_bases_root_path() / kb_user / field_value["01_new_kb_name"]
|
|
666
674
|
kb_path.mkdir(parents=True, exist_ok=True)
|
|
667
675
|
|
|
668
676
|
# Save the embedding metadata
|
|
@@ -675,7 +683,7 @@ class KnowledgeIngestionComponent(Component):
|
|
|
675
683
|
|
|
676
684
|
# Update the knowledge base options dynamically
|
|
677
685
|
build_config["knowledge_base"]["options"] = await get_knowledge_bases(
|
|
678
|
-
|
|
686
|
+
_get_knowledge_bases_root_path(),
|
|
679
687
|
user_id=self.user_id,
|
|
680
688
|
)
|
|
681
689
|
|
|
@@ -16,12 +16,20 @@ from lfx.schema.data import Data
|
|
|
16
16
|
from lfx.schema.dataframe import DataFrame
|
|
17
17
|
from lfx.services.deps import get_settings_service, session_scope
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
_KNOWLEDGE_BASES_ROOT_PATH: Path | None = None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _get_knowledge_bases_root_path() -> Path:
|
|
23
|
+
"""Lazy load the knowledge bases root path from settings."""
|
|
24
|
+
global _KNOWLEDGE_BASES_ROOT_PATH # noqa: PLW0603
|
|
25
|
+
if _KNOWLEDGE_BASES_ROOT_PATH is None:
|
|
26
|
+
settings = get_settings_service().settings
|
|
27
|
+
knowledge_directory = settings.knowledge_bases_dir
|
|
28
|
+
if not knowledge_directory:
|
|
29
|
+
msg = "Knowledge bases directory is not set in the settings."
|
|
30
|
+
raise ValueError(msg)
|
|
31
|
+
_KNOWLEDGE_BASES_ROOT_PATH = Path(knowledge_directory).expanduser()
|
|
32
|
+
return _KNOWLEDGE_BASES_ROOT_PATH
|
|
25
33
|
|
|
26
34
|
|
|
27
35
|
class KnowledgeRetrievalComponent(Component):
|
|
@@ -90,7 +98,7 @@ class KnowledgeRetrievalComponent(Component):
|
|
|
90
98
|
if field_name == "knowledge_base":
|
|
91
99
|
# Update the knowledge base options dynamically
|
|
92
100
|
build_config["knowledge_base"]["options"] = await get_knowledge_bases(
|
|
93
|
-
|
|
101
|
+
_get_knowledge_bases_root_path(),
|
|
94
102
|
user_id=self.user_id, # Use the user_id from the component context
|
|
95
103
|
)
|
|
96
104
|
|
|
@@ -186,7 +194,7 @@ class KnowledgeRetrievalComponent(Component):
|
|
|
186
194
|
msg = f"User with ID {self.user_id} not found."
|
|
187
195
|
raise ValueError(msg)
|
|
188
196
|
kb_user = current_user.username
|
|
189
|
-
kb_path =
|
|
197
|
+
kb_path = _get_knowledge_bases_root_path() / kb_user / self.knowledge_base
|
|
190
198
|
|
|
191
199
|
metadata = self._get_kb_metadata(kb_path)
|
|
192
200
|
if not metadata:
|
lfx/components/logic/loop.py
CHANGED
|
@@ -89,6 +89,10 @@ class LoopComponent(Component):
|
|
|
89
89
|
item_dependency_id = self.get_incoming_edge_by_target_param("item")
|
|
90
90
|
if item_dependency_id not in self.graph.run_manager.run_predecessors[self._id]:
|
|
91
91
|
self.graph.run_manager.run_predecessors[self._id].append(item_dependency_id)
|
|
92
|
+
# CRITICAL: Also update run_map so remove_from_predecessors() works correctly
|
|
93
|
+
# run_map[predecessor] = list of vertices that depend on predecessor
|
|
94
|
+
if self._id not in self.graph.run_manager.run_map[item_dependency_id]:
|
|
95
|
+
self.graph.run_manager.run_map[item_dependency_id].append(self._id)
|
|
92
96
|
|
|
93
97
|
def done_output(self) -> DataFrame:
|
|
94
98
|
"""Trigger the done output when iteration is complete."""
|