kailash 0.1.2__py3-none-any.whl → 0.1.4__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.
- kailash/__init__.py +1 -1
- kailash/api/__init__.py +17 -0
- kailash/api/gateway.py +394 -0
- kailash/api/mcp_integration.py +478 -0
- kailash/api/workflow_api.py +399 -0
- kailash/nodes/ai/__init__.py +4 -4
- kailash/nodes/ai/agents.py +4 -4
- kailash/nodes/ai/ai_providers.py +18 -22
- kailash/nodes/ai/embedding_generator.py +34 -38
- kailash/nodes/ai/llm_agent.py +351 -356
- kailash/nodes/api/http.py +0 -4
- kailash/nodes/api/rest.py +1 -1
- kailash/nodes/base.py +60 -64
- kailash/nodes/code/python.py +61 -42
- kailash/nodes/data/__init__.py +10 -10
- kailash/nodes/data/readers.py +27 -29
- kailash/nodes/data/retrieval.py +1 -1
- kailash/nodes/data/sharepoint_graph.py +23 -25
- kailash/nodes/data/sql.py +27 -29
- kailash/nodes/data/vector_db.py +2 -2
- kailash/nodes/data/writers.py +41 -44
- kailash/nodes/logic/__init__.py +10 -3
- kailash/nodes/logic/async_operations.py +14 -14
- kailash/nodes/logic/operations.py +18 -22
- kailash/nodes/logic/workflow.py +439 -0
- kailash/nodes/mcp/client.py +29 -33
- kailash/nodes/mcp/resource.py +1 -1
- kailash/nodes/mcp/server.py +10 -4
- kailash/nodes/transform/formatters.py +1 -1
- kailash/nodes/transform/processors.py +5 -3
- kailash/runtime/docker.py +2 -0
- kailash/tracking/metrics_collector.py +6 -7
- kailash/tracking/models.py +0 -20
- kailash/tracking/storage/database.py +4 -4
- kailash/tracking/storage/filesystem.py +0 -1
- kailash/utils/export.py +2 -2
- kailash/utils/templates.py +16 -16
- kailash/visualization/performance.py +7 -7
- kailash/visualization/reports.py +1 -1
- kailash/workflow/graph.py +4 -4
- kailash/workflow/mock_registry.py +1 -1
- {kailash-0.1.2.dist-info → kailash-0.1.4.dist-info}/METADATA +198 -27
- kailash-0.1.4.dist-info/RECORD +85 -0
- kailash-0.1.2.dist-info/RECORD +0 -80
- {kailash-0.1.2.dist-info → kailash-0.1.4.dist-info}/WHEEL +0 -0
- {kailash-0.1.2.dist-info → kailash-0.1.4.dist-info}/entry_points.txt +0 -0
- {kailash-0.1.2.dist-info → kailash-0.1.4.dist-info}/licenses/LICENSE +0 -0
- {kailash-0.1.2.dist-info → kailash-0.1.4.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,399 @@
|
|
1
|
+
"""
|
2
|
+
Lean API wrapper for Kailash workflows using FastAPI.
|
3
|
+
|
4
|
+
This module provides a general-purpose API wrapper that can expose any Kailash
|
5
|
+
workflow as a REST API with minimal configuration.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import asyncio
|
9
|
+
from contextlib import asynccontextmanager
|
10
|
+
from enum import Enum
|
11
|
+
from typing import Any, Dict, List, Optional, Union
|
12
|
+
|
13
|
+
import uvicorn
|
14
|
+
from fastapi import BackgroundTasks, FastAPI, HTTPException
|
15
|
+
from fastapi.responses import StreamingResponse
|
16
|
+
from pydantic import BaseModel, Field
|
17
|
+
|
18
|
+
from kailash.runtime.local import LocalRuntime
|
19
|
+
from kailash.workflow.builder import WorkflowBuilder
|
20
|
+
from kailash.workflow.graph import Workflow
|
21
|
+
|
22
|
+
|
23
|
+
class ExecutionMode(str, Enum):
|
24
|
+
"""Execution modes for workflow API."""
|
25
|
+
|
26
|
+
SYNC = "sync"
|
27
|
+
ASYNC = "async"
|
28
|
+
STREAM = "stream"
|
29
|
+
|
30
|
+
|
31
|
+
class WorkflowRequest(BaseModel):
|
32
|
+
"""Base request model for workflow execution."""
|
33
|
+
|
34
|
+
inputs: Dict[str, Any] = Field(..., description="Input data for workflow nodes")
|
35
|
+
config: Optional[Dict[str, Any]] = Field(
|
36
|
+
None, description="Node configuration overrides"
|
37
|
+
)
|
38
|
+
mode: ExecutionMode = Field(ExecutionMode.SYNC, description="Execution mode")
|
39
|
+
|
40
|
+
|
41
|
+
class WorkflowResponse(BaseModel):
|
42
|
+
"""Base response model for workflow execution."""
|
43
|
+
|
44
|
+
outputs: Dict[str, Any] = Field(..., description="Output data from workflow nodes")
|
45
|
+
execution_time: float = Field(..., description="Execution time in seconds")
|
46
|
+
workflow_id: str = Field(..., description="Workflow identifier")
|
47
|
+
version: str = Field(..., description="Workflow version")
|
48
|
+
|
49
|
+
|
50
|
+
class WorkflowAPI:
|
51
|
+
"""
|
52
|
+
Lean API wrapper for Kailash workflows.
|
53
|
+
|
54
|
+
This class provides a minimal, efficient way to expose any Kailash workflow
|
55
|
+
as a REST API with support for synchronous, asynchronous, and streaming execution.
|
56
|
+
|
57
|
+
Example:
|
58
|
+
>>> # For any workflow
|
59
|
+
>>> from my_workflows import rag_workflow
|
60
|
+
>>> api = WorkflowAPI(rag_workflow)
|
61
|
+
>>> api.run(port=8000)
|
62
|
+
"""
|
63
|
+
|
64
|
+
def __init__(
|
65
|
+
self,
|
66
|
+
workflow: Union[WorkflowBuilder, Workflow],
|
67
|
+
app_name: str = "Kailash Workflow API",
|
68
|
+
version: str = "1.0.0",
|
69
|
+
description: str = "API wrapper for Kailash workflow execution",
|
70
|
+
):
|
71
|
+
"""
|
72
|
+
Initialize the API wrapper.
|
73
|
+
|
74
|
+
Args:
|
75
|
+
workflow: The WorkflowBuilder or Workflow instance to expose
|
76
|
+
app_name: Name of the API application
|
77
|
+
version: API version
|
78
|
+
description: API description
|
79
|
+
"""
|
80
|
+
if isinstance(workflow, WorkflowBuilder):
|
81
|
+
self.workflow = workflow
|
82
|
+
self.workflow_graph = workflow.build()
|
83
|
+
self.workflow_id = getattr(workflow, "workflow_id", "unnamed")
|
84
|
+
self.version = getattr(workflow, "version", "1.0.0")
|
85
|
+
else: # Workflow instance
|
86
|
+
self.workflow = workflow
|
87
|
+
self.workflow_graph = workflow
|
88
|
+
self.workflow_id = workflow.workflow_id
|
89
|
+
self.version = workflow.version
|
90
|
+
|
91
|
+
self.runtime = LocalRuntime()
|
92
|
+
|
93
|
+
# Create FastAPI app with lifespan management
|
94
|
+
self.app = FastAPI(
|
95
|
+
title=app_name,
|
96
|
+
version=version,
|
97
|
+
description=description,
|
98
|
+
lifespan=self._lifespan,
|
99
|
+
)
|
100
|
+
|
101
|
+
# Setup routes
|
102
|
+
self._setup_routes()
|
103
|
+
|
104
|
+
# Cache for async executions
|
105
|
+
self._execution_cache: Dict[str, Dict[str, Any]] = {}
|
106
|
+
|
107
|
+
@asynccontextmanager
|
108
|
+
async def _lifespan(self, app: FastAPI):
|
109
|
+
"""Manage app lifecycle."""
|
110
|
+
# Startup
|
111
|
+
yield
|
112
|
+
# Shutdown - cleanup cache
|
113
|
+
self._execution_cache.clear()
|
114
|
+
|
115
|
+
def _setup_routes(self):
|
116
|
+
"""Setup API routes dynamically based on workflow."""
|
117
|
+
|
118
|
+
# Main execution endpoint
|
119
|
+
@self.app.post("/execute")
|
120
|
+
async def execute_workflow(
|
121
|
+
request: WorkflowRequest, background_tasks: BackgroundTasks
|
122
|
+
):
|
123
|
+
"""Execute the workflow with provided inputs."""
|
124
|
+
|
125
|
+
if request.mode == ExecutionMode.SYNC:
|
126
|
+
return await self._execute_sync(request)
|
127
|
+
elif request.mode == ExecutionMode.ASYNC:
|
128
|
+
return await self._execute_async(request, background_tasks)
|
129
|
+
else: # STREAM
|
130
|
+
return StreamingResponse(
|
131
|
+
self._execute_stream(request), media_type="application/json"
|
132
|
+
)
|
133
|
+
|
134
|
+
# Status endpoint for async executions
|
135
|
+
@self.app.get("/status/{execution_id}")
|
136
|
+
async def get_execution_status(execution_id: str):
|
137
|
+
"""Get status of async execution."""
|
138
|
+
if execution_id not in self._execution_cache:
|
139
|
+
raise HTTPException(status_code=404, detail="Execution not found")
|
140
|
+
return self._execution_cache[execution_id]
|
141
|
+
|
142
|
+
# Workflow metadata endpoint
|
143
|
+
@self.app.get("/workflow/info")
|
144
|
+
async def get_workflow_info():
|
145
|
+
"""Get workflow metadata and structure."""
|
146
|
+
workflow = self.workflow_graph
|
147
|
+
|
148
|
+
# Get node information
|
149
|
+
nodes = []
|
150
|
+
for node_id, node_instance in workflow.nodes.items():
|
151
|
+
nodes.append({"id": node_id, "type": node_instance.node_type})
|
152
|
+
|
153
|
+
# Get edge information
|
154
|
+
edges = []
|
155
|
+
for conn in workflow.connections:
|
156
|
+
edges.append(
|
157
|
+
{
|
158
|
+
"source": conn.source_node,
|
159
|
+
"target": conn.target_node,
|
160
|
+
"source_output": conn.source_output,
|
161
|
+
"target_input": conn.target_input,
|
162
|
+
}
|
163
|
+
)
|
164
|
+
|
165
|
+
return {
|
166
|
+
"workflow_id": workflow.workflow_id,
|
167
|
+
"name": workflow.name,
|
168
|
+
"description": workflow.description,
|
169
|
+
"version": workflow.version,
|
170
|
+
"nodes": nodes,
|
171
|
+
"edges": edges,
|
172
|
+
"node_count": len(nodes),
|
173
|
+
"edge_count": len(edges),
|
174
|
+
}
|
175
|
+
|
176
|
+
# Health check
|
177
|
+
@self.app.get("/health")
|
178
|
+
async def health_check():
|
179
|
+
"""Check API health."""
|
180
|
+
return {"status": "healthy", "workflow": self.workflow_id}
|
181
|
+
|
182
|
+
async def _execute_sync(self, request: WorkflowRequest) -> WorkflowResponse:
|
183
|
+
"""Execute workflow synchronously."""
|
184
|
+
import time
|
185
|
+
|
186
|
+
start_time = time.time()
|
187
|
+
|
188
|
+
try:
|
189
|
+
# Apply configuration overrides if provided
|
190
|
+
if request.config:
|
191
|
+
for node_id, config in request.config.items():
|
192
|
+
# This would need workflow builder enhancement to support
|
193
|
+
# dynamic config updates
|
194
|
+
pass
|
195
|
+
|
196
|
+
# Execute workflow with inputs
|
197
|
+
results = await asyncio.to_thread(
|
198
|
+
self.runtime.execute, self.workflow_graph, parameters=request.inputs
|
199
|
+
)
|
200
|
+
|
201
|
+
# Handle tuple return from runtime
|
202
|
+
if isinstance(results, tuple):
|
203
|
+
results = results[0] if results else {}
|
204
|
+
|
205
|
+
execution_time = time.time() - start_time
|
206
|
+
|
207
|
+
return WorkflowResponse(
|
208
|
+
outputs=results,
|
209
|
+
execution_time=execution_time,
|
210
|
+
workflow_id=self.workflow_id,
|
211
|
+
version=self.version,
|
212
|
+
)
|
213
|
+
|
214
|
+
except Exception as e:
|
215
|
+
raise HTTPException(status_code=500, detail=str(e))
|
216
|
+
|
217
|
+
async def _execute_async(
|
218
|
+
self, request: WorkflowRequest, background_tasks: BackgroundTasks
|
219
|
+
):
|
220
|
+
"""Execute workflow asynchronously."""
|
221
|
+
import uuid
|
222
|
+
|
223
|
+
execution_id = str(uuid.uuid4())
|
224
|
+
|
225
|
+
# Initialize cache entry
|
226
|
+
self._execution_cache[execution_id] = {
|
227
|
+
"status": "pending",
|
228
|
+
"workflow_id": self.workflow_id,
|
229
|
+
"version": self.version,
|
230
|
+
}
|
231
|
+
|
232
|
+
# Schedule background execution
|
233
|
+
background_tasks.add_task(self._run_async_execution, execution_id, request)
|
234
|
+
|
235
|
+
return {
|
236
|
+
"execution_id": execution_id,
|
237
|
+
"status": "pending",
|
238
|
+
"message": f"Execution started. Check status at /status/{execution_id}",
|
239
|
+
}
|
240
|
+
|
241
|
+
async def _run_async_execution(self, execution_id: str, request: WorkflowRequest):
|
242
|
+
"""Run async execution in background."""
|
243
|
+
try:
|
244
|
+
self._execution_cache[execution_id]["status"] = "running"
|
245
|
+
|
246
|
+
result = await self._execute_sync(request)
|
247
|
+
|
248
|
+
self._execution_cache[execution_id].update(
|
249
|
+
{"status": "completed", "result": result.dict()}
|
250
|
+
)
|
251
|
+
|
252
|
+
except Exception as e:
|
253
|
+
self._execution_cache[execution_id].update(
|
254
|
+
{"status": "failed", "error": str(e)}
|
255
|
+
)
|
256
|
+
|
257
|
+
async def _execute_stream(self, request: WorkflowRequest):
|
258
|
+
"""Execute workflow with streaming response."""
|
259
|
+
import json
|
260
|
+
import time
|
261
|
+
|
262
|
+
try:
|
263
|
+
# For streaming, we'd need workflow runner enhancement
|
264
|
+
# to support progress callbacks. For now, simulate with
|
265
|
+
# start/end events
|
266
|
+
|
267
|
+
yield json.dumps(
|
268
|
+
{
|
269
|
+
"event": "start",
|
270
|
+
"workflow_id": self.workflow_id,
|
271
|
+
"timestamp": time.time(),
|
272
|
+
}
|
273
|
+
) + "\n"
|
274
|
+
|
275
|
+
result = await self._execute_sync(request)
|
276
|
+
|
277
|
+
yield json.dumps(
|
278
|
+
{"event": "complete", "result": result.dict(), "timestamp": time.time()}
|
279
|
+
) + "\n"
|
280
|
+
|
281
|
+
except Exception as e:
|
282
|
+
yield json.dumps(
|
283
|
+
{"event": "error", "error": str(e), "timestamp": time.time()}
|
284
|
+
) + "\n"
|
285
|
+
|
286
|
+
def run(self, host: str = "0.0.0.0", port: int = 8000, **kwargs):
|
287
|
+
"""Run the API server."""
|
288
|
+
uvicorn.run(self.app, host=host, port=port, **kwargs)
|
289
|
+
|
290
|
+
|
291
|
+
# Specialized API wrapper for Hierarchical RAG workflows
|
292
|
+
class HierarchicalRAGAPI(WorkflowAPI):
|
293
|
+
"""
|
294
|
+
Specialized API wrapper for Hierarchical RAG workflows.
|
295
|
+
|
296
|
+
Provides RAG-specific endpoints and models for better developer experience.
|
297
|
+
"""
|
298
|
+
|
299
|
+
def __init__(self, workflow: WorkflowBuilder, **kwargs):
|
300
|
+
super().__init__(workflow, **kwargs)
|
301
|
+
self._setup_rag_routes()
|
302
|
+
|
303
|
+
def _setup_rag_routes(self):
|
304
|
+
"""Setup RAG-specific routes."""
|
305
|
+
|
306
|
+
class Document(BaseModel):
|
307
|
+
id: str
|
308
|
+
title: str
|
309
|
+
content: str
|
310
|
+
|
311
|
+
class RAGQuery(BaseModel):
|
312
|
+
query: str
|
313
|
+
top_k: int = 3
|
314
|
+
similarity_method: str = "cosine"
|
315
|
+
temperature: float = 0.7
|
316
|
+
max_tokens: int = 500
|
317
|
+
|
318
|
+
class RAGResponse(BaseModel):
|
319
|
+
answer: str
|
320
|
+
sources: List[Dict[str, Any]]
|
321
|
+
query: str
|
322
|
+
execution_time: float
|
323
|
+
|
324
|
+
@self.app.post("/documents")
|
325
|
+
async def add_documents(documents: List[Document]):
|
326
|
+
"""Add documents to the knowledge base."""
|
327
|
+
# This would integrate with document storage
|
328
|
+
return {"message": f"Added {len(documents)} documents"}
|
329
|
+
|
330
|
+
@self.app.post("/query", response_model=RAGResponse)
|
331
|
+
async def query_rag(request: RAGQuery):
|
332
|
+
"""Query the RAG system."""
|
333
|
+
import time
|
334
|
+
|
335
|
+
start_time = time.time()
|
336
|
+
|
337
|
+
# Transform to workflow format
|
338
|
+
workflow_request = WorkflowRequest(
|
339
|
+
inputs={
|
340
|
+
"query": request.query,
|
341
|
+
"config": {
|
342
|
+
"relevance_scorer": {
|
343
|
+
"top_k": request.top_k,
|
344
|
+
"similarity_method": request.similarity_method,
|
345
|
+
},
|
346
|
+
"llm_agent": {
|
347
|
+
"temperature": request.temperature,
|
348
|
+
"max_tokens": request.max_tokens,
|
349
|
+
},
|
350
|
+
},
|
351
|
+
}
|
352
|
+
)
|
353
|
+
|
354
|
+
result = await self._execute_sync(workflow_request)
|
355
|
+
|
356
|
+
# Extract RAG-specific outputs
|
357
|
+
outputs = result.outputs
|
358
|
+
answer = (
|
359
|
+
outputs.get("llm_response", {})
|
360
|
+
.get("choices", [{}])[0]
|
361
|
+
.get("message", {})
|
362
|
+
.get("content", "")
|
363
|
+
)
|
364
|
+
sources = outputs.get("relevant_chunks", [])
|
365
|
+
|
366
|
+
return RAGResponse(
|
367
|
+
answer=answer,
|
368
|
+
sources=sources,
|
369
|
+
query=request.query,
|
370
|
+
execution_time=time.time() - start_time,
|
371
|
+
)
|
372
|
+
|
373
|
+
|
374
|
+
# Factory function for creating API wrappers
|
375
|
+
def create_workflow_api(
|
376
|
+
workflow: WorkflowBuilder, api_type: str = "generic", **kwargs
|
377
|
+
) -> WorkflowAPI:
|
378
|
+
"""
|
379
|
+
Factory function to create appropriate API wrapper.
|
380
|
+
|
381
|
+
Args:
|
382
|
+
workflow: The workflow to wrap
|
383
|
+
api_type: Type of API wrapper ("generic", "rag", etc.)
|
384
|
+
**kwargs: Additional arguments for API initialization
|
385
|
+
|
386
|
+
Returns:
|
387
|
+
Configured WorkflowAPI instance
|
388
|
+
|
389
|
+
Example:
|
390
|
+
>>> api = create_workflow_api(my_workflow, api_type="rag")
|
391
|
+
>>> api.run(port=8000)
|
392
|
+
"""
|
393
|
+
api_classes = {
|
394
|
+
"generic": WorkflowAPI,
|
395
|
+
"rag": HierarchicalRAGAPI,
|
396
|
+
}
|
397
|
+
|
398
|
+
api_class = api_classes.get(api_type, WorkflowAPI)
|
399
|
+
return api_class(workflow, **kwargs)
|
kailash/nodes/ai/__init__.py
CHANGED
@@ -13,8 +13,8 @@ from .ai_providers import (
|
|
13
13
|
get_available_providers,
|
14
14
|
get_provider,
|
15
15
|
)
|
16
|
-
from .embedding_generator import
|
17
|
-
from .llm_agent import
|
16
|
+
from .embedding_generator import EmbeddingGeneratorNode
|
17
|
+
from .llm_agent import LLMAgentNode
|
18
18
|
from .models import (
|
19
19
|
ModelPredictor,
|
20
20
|
NamedEntityRecognizer,
|
@@ -30,9 +30,9 @@ __all__ = [
|
|
30
30
|
"RetrievalAgent",
|
31
31
|
"FunctionCallingAgent",
|
32
32
|
"PlanningAgent",
|
33
|
-
"
|
33
|
+
"LLMAgentNode",
|
34
34
|
# Embedding and Vector Operations
|
35
|
-
"
|
35
|
+
"EmbeddingGeneratorNode",
|
36
36
|
# Provider Infrastructure
|
37
37
|
"LLMProvider",
|
38
38
|
"OllamaProvider",
|
kailash/nodes/ai/agents.py
CHANGED
@@ -333,7 +333,7 @@ class PlanningAgent(Node):
|
|
333
333
|
# Data processing workflow
|
334
334
|
potential_steps = [
|
335
335
|
{
|
336
|
-
"tool": "
|
336
|
+
"tool": "CSVReaderNode",
|
337
337
|
"description": "Read input data",
|
338
338
|
"parameters": {"file_path": "input.csv"},
|
339
339
|
},
|
@@ -348,7 +348,7 @@ class PlanningAgent(Node):
|
|
348
348
|
"parameters": {"group_by": "category", "operation": "sum"},
|
349
349
|
},
|
350
350
|
{
|
351
|
-
"tool": "
|
351
|
+
"tool": "CSVWriterNode",
|
352
352
|
"description": "Write results",
|
353
353
|
"parameters": {"file_path": "output.csv"},
|
354
354
|
},
|
@@ -357,7 +357,7 @@ class PlanningAgent(Node):
|
|
357
357
|
# Text analysis workflow
|
358
358
|
potential_steps = [
|
359
359
|
{
|
360
|
-
"tool": "
|
360
|
+
"tool": "TextReaderNode",
|
361
361
|
"description": "Read text data",
|
362
362
|
"parameters": {"file_path": "text.txt"},
|
363
363
|
},
|
@@ -372,7 +372,7 @@ class PlanningAgent(Node):
|
|
372
372
|
"parameters": {"max_length": 200},
|
373
373
|
},
|
374
374
|
{
|
375
|
-
"tool": "
|
375
|
+
"tool": "JSONWriterNode",
|
376
376
|
"description": "Save analysis results",
|
377
377
|
"parameters": {"file_path": "analysis.json"},
|
378
378
|
},
|
kailash/nodes/ai/ai_providers.py
CHANGED
@@ -1159,18 +1159,17 @@ def get_provider(
|
|
1159
1159
|
ValueError: If the provider name is not recognized or doesn't support the requested type.
|
1160
1160
|
|
1161
1161
|
Examples:
|
1162
|
-
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
1166
|
-
|
1167
|
-
|
1168
|
-
|
1169
|
-
|
1170
|
-
|
1171
|
-
Get chat-only provider
|
1172
|
-
|
1173
|
-
chat_provider = get_provider("anthropic", "chat")
|
1162
|
+
>>> # Get any provider
|
1163
|
+
>>> provider = get_provider("openai")
|
1164
|
+
>>> if provider.supports_chat():
|
1165
|
+
... # Use for chat
|
1166
|
+
... pass
|
1167
|
+
>>> if provider.supports_embeddings():
|
1168
|
+
... # Use for embeddings
|
1169
|
+
... pass
|
1170
|
+
|
1171
|
+
>>> # Get chat-only provider
|
1172
|
+
>>> chat_provider = get_provider("anthropic", "chat")
|
1174
1173
|
response = chat_provider.chat(messages, model="claude-3-sonnet")
|
1175
1174
|
|
1176
1175
|
Get embedding-only provider:
|
@@ -1223,18 +1222,15 @@ def get_available_providers(
|
|
1223
1222
|
Dict mapping provider names to their availability and capabilities.
|
1224
1223
|
|
1225
1224
|
Examples:
|
1225
|
+
>>> # Get all providers
|
1226
|
+
>>> all_providers = get_available_providers()
|
1227
|
+
>>> for name, info in all_providers.items():
|
1228
|
+
... print(f"{name}: Available={info['available']}, Chat={info['chat']}, Embeddings={info['embeddings']}")
|
1226
1229
|
|
1227
|
-
Get
|
1228
|
-
|
1229
|
-
all_providers = get_available_providers()
|
1230
|
-
for name, info in all_providers.items():
|
1231
|
-
print(f"{name}: Available={info['available']}, Chat={info['chat']}, Embeddings={info['embeddings']}")
|
1232
|
-
|
1233
|
-
Get only chat providers:
|
1234
|
-
|
1235
|
-
chat_providers = get_available_providers("chat")
|
1230
|
+
>>> # Get only chat providers
|
1231
|
+
>>> chat_providers = get_available_providers("chat")
|
1236
1232
|
|
1237
|
-
Get only embedding providers
|
1233
|
+
>>> # Get only embedding providers
|
1238
1234
|
|
1239
1235
|
embed_providers = get_available_providers("embeddings")
|
1240
1236
|
"""
|
@@ -7,7 +7,7 @@ from kailash.nodes.base import Node, NodeParameter, register_node
|
|
7
7
|
|
8
8
|
|
9
9
|
@register_node()
|
10
|
-
class
|
10
|
+
class EmbeddingGeneratorNode(Node):
|
11
11
|
"""
|
12
12
|
Vector embedding generator for RAG systems and semantic similarity operations.
|
13
13
|
|
@@ -61,46 +61,42 @@ class EmbeddingGenerator(Node):
|
|
61
61
|
- Updates usage statistics and cost tracking
|
62
62
|
|
63
63
|
Examples:
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
)
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
embedding_1=[0.1, 0.2, 0.3, ...],
|
97
|
-
embedding_2=[0.15, 0.25, 0.35, ...],
|
98
|
-
similarity_metric="cosine"
|
99
|
-
)
|
64
|
+
>>> # Single text embedding
|
65
|
+
>>> embedder = EmbeddingGeneratorNode()
|
66
|
+
>>> result = embedder.run(
|
67
|
+
... provider="openai",
|
68
|
+
... model="text-embedding-3-large",
|
69
|
+
... input_text="This is a sample document to embed",
|
70
|
+
... operation="embed_text"
|
71
|
+
... )
|
72
|
+
|
73
|
+
>>> # Batch document embedding
|
74
|
+
>>> batch_embedder = EmbeddingGeneratorNode()
|
75
|
+
>>> result = batch_embedder.run(
|
76
|
+
... provider="huggingface",
|
77
|
+
... model="sentence-transformers/all-MiniLM-L6-v2",
|
78
|
+
... input_texts=[
|
79
|
+
... "First document content...",
|
80
|
+
... "Second document content...",
|
81
|
+
... "Third document content..."
|
82
|
+
... ],
|
83
|
+
... operation="embed_batch",
|
84
|
+
... batch_size=32,
|
85
|
+
... cache_enabled=True
|
86
|
+
... )
|
87
|
+
|
88
|
+
>>> # Similarity calculation
|
89
|
+
>>> similarity = EmbeddingGeneratorNode()
|
90
|
+
>>> result = similarity.run(
|
91
|
+
... operation="calculate_similarity",
|
92
|
+
... embedding_1=[0.1, 0.2, 0.3], # ... removed for doctest
|
93
|
+
... embedding_2=[0.15, 0.25, 0.35], # ... removed for doctest
|
94
|
+
... similarity_metric="cosine"
|
95
|
+
... )
|
100
96
|
|
101
97
|
Cached embedding with MCP integration:
|
102
98
|
|
103
|
-
mcp_embedder =
|
99
|
+
mcp_embedder = EmbeddingGeneratorNode()
|
104
100
|
result = mcp_embedder.run(
|
105
101
|
provider="azure",
|
106
102
|
model="text-embedding-3-small",
|