kailash 0.3.0__tar.gz → 0.3.1__tar.gz

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.
Files changed (144) hide show
  1. {kailash-0.3.0/src/kailash.egg-info → kailash-0.3.1}/PKG-INFO +53 -5
  2. {kailash-0.3.0 → kailash-0.3.1}/README.md +52 -4
  3. {kailash-0.3.0 → kailash-0.3.1}/pyproject.toml +17 -1
  4. {kailash-0.3.0 → kailash-0.3.1}/setup.py +1 -1
  5. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/__init__.py +1 -1
  6. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/access_control.py +40 -39
  7. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/api/auth.py +26 -32
  8. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/api/custom_nodes.py +29 -29
  9. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/api/custom_nodes_secure.py +35 -35
  10. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/api/database.py +17 -17
  11. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/api/gateway.py +19 -19
  12. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/api/mcp_integration.py +24 -23
  13. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/api/studio.py +45 -45
  14. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/api/workflow_api.py +8 -8
  15. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/cli/commands.py +5 -8
  16. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/manifest.py +42 -42
  17. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/mcp/__init__.py +1 -1
  18. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/mcp/ai_registry_server.py +20 -20
  19. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/mcp/client.py +9 -11
  20. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/mcp/client_new.py +10 -10
  21. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/mcp/server.py +1 -2
  22. kailash-0.3.1/src/kailash/mcp/server_enhanced.py +449 -0
  23. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/mcp/servers/ai_registry.py +6 -6
  24. kailash-0.3.1/src/kailash/mcp/utils/__init__.py +31 -0
  25. kailash-0.3.1/src/kailash/mcp/utils/cache.py +267 -0
  26. kailash-0.3.1/src/kailash/mcp/utils/config.py +263 -0
  27. kailash-0.3.1/src/kailash/mcp/utils/formatters.py +293 -0
  28. kailash-0.3.1/src/kailash/mcp/utils/metrics.py +418 -0
  29. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/ai/agents.py +9 -9
  30. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/ai/ai_providers.py +33 -34
  31. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/ai/embedding_generator.py +31 -32
  32. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/ai/intelligent_agent_orchestrator.py +62 -66
  33. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/ai/iterative_llm_agent.py +48 -48
  34. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/ai/llm_agent.py +32 -33
  35. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/ai/models.py +13 -13
  36. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/ai/self_organizing.py +44 -44
  37. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/api/auth.py +11 -11
  38. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/api/graphql.py +13 -13
  39. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/api/http.py +19 -19
  40. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/api/monitoring.py +20 -20
  41. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/api/rate_limiting.py +9 -13
  42. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/api/rest.py +29 -29
  43. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/api/security.py +44 -47
  44. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/base.py +21 -23
  45. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/base_async.py +7 -7
  46. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/base_cycle_aware.py +12 -12
  47. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/base_with_acl.py +5 -5
  48. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/code/python.py +56 -55
  49. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/data/directory.py +6 -6
  50. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/data/event_generation.py +10 -10
  51. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/data/file_discovery.py +28 -31
  52. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/data/readers.py +8 -8
  53. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/data/retrieval.py +10 -10
  54. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/data/sharepoint_graph.py +17 -17
  55. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/data/sources.py +5 -5
  56. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/data/sql.py +13 -13
  57. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/data/streaming.py +25 -25
  58. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/data/vector_db.py +22 -22
  59. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/data/writers.py +7 -7
  60. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/logic/async_operations.py +17 -17
  61. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/logic/convergence.py +11 -11
  62. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/logic/loop.py +4 -4
  63. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/logic/operations.py +11 -11
  64. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/logic/workflow.py +8 -9
  65. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/mixins/mcp.py +17 -17
  66. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/mixins.py +8 -10
  67. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/transform/chunkers.py +3 -3
  68. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/transform/formatters.py +7 -7
  69. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/transform/processors.py +10 -10
  70. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/runtime/access_controlled.py +18 -18
  71. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/runtime/async_local.py +17 -19
  72. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/runtime/docker.py +20 -22
  73. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/runtime/local.py +16 -16
  74. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/runtime/parallel.py +23 -23
  75. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/runtime/parallel_cyclic.py +27 -27
  76. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/runtime/runner.py +6 -6
  77. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/runtime/testing.py +20 -20
  78. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/sdk_exceptions.py +0 -58
  79. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/security.py +14 -26
  80. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/tracking/manager.py +38 -38
  81. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/tracking/metrics_collector.py +15 -14
  82. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/tracking/models.py +53 -53
  83. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/tracking/storage/base.py +7 -17
  84. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/tracking/storage/database.py +22 -23
  85. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/tracking/storage/filesystem.py +38 -40
  86. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/utils/export.py +21 -21
  87. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/utils/templates.py +2 -3
  88. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/visualization/api.py +30 -34
  89. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/visualization/dashboard.py +17 -17
  90. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/visualization/performance.py +16 -16
  91. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/visualization/reports.py +25 -27
  92. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/workflow/builder.py +8 -8
  93. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/workflow/convergence.py +13 -12
  94. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/workflow/cycle_analyzer.py +30 -32
  95. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/workflow/cycle_builder.py +12 -12
  96. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/workflow/cycle_config.py +16 -15
  97. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/workflow/cycle_debugger.py +40 -40
  98. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/workflow/cycle_exceptions.py +29 -29
  99. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/workflow/cycle_profiler.py +21 -21
  100. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/workflow/cycle_state.py +20 -22
  101. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/workflow/cyclic_runner.py +44 -44
  102. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/workflow/graph.py +40 -40
  103. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/workflow/mermaid_visualizer.py +9 -11
  104. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/workflow/migration.py +22 -22
  105. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/workflow/mock_registry.py +6 -6
  106. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/workflow/runner.py +9 -9
  107. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/workflow/safety.py +12 -13
  108. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/workflow/state.py +8 -11
  109. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/workflow/templates.py +19 -19
  110. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/workflow/validation.py +14 -14
  111. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/workflow/visualization.py +22 -22
  112. {kailash-0.3.0 → kailash-0.3.1/src/kailash.egg-info}/PKG-INFO +53 -5
  113. {kailash-0.3.0 → kailash-0.3.1}/src/kailash.egg-info/SOURCES.txt +6 -0
  114. {kailash-0.3.0 → kailash-0.3.1}/LICENSE +0 -0
  115. {kailash-0.3.0 → kailash-0.3.1}/MANIFEST.in +0 -0
  116. {kailash-0.3.0 → kailash-0.3.1}/setup.cfg +0 -0
  117. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/__main__.py +0 -0
  118. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/api/__init__.py +0 -0
  119. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/api/__main__.py +0 -0
  120. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/api/studio_secure.py +0 -0
  121. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/cli/__init__.py +0 -0
  122. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/mcp/__main__.py +0 -0
  123. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/mcp/server_new.py +0 -0
  124. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/mcp/servers/__init__.py +0 -0
  125. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/__init__.py +0 -0
  126. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/ai/__init__.py +0 -0
  127. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/ai/a2a.py +0 -0
  128. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/api/__init__.py +0 -0
  129. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/code/__init__.py +0 -0
  130. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/data/__init__.py +0 -0
  131. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/logic/__init__.py +0 -0
  132. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/mixins/__init__.py +0 -0
  133. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/nodes/transform/__init__.py +0 -0
  134. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/runtime/__init__.py +0 -0
  135. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/tracking/__init__.py +0 -0
  136. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/tracking/storage/__init__.py +0 -0
  137. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/utils/__init__.py +0 -0
  138. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/visualization/__init__.py +0 -0
  139. {kailash-0.3.0 → kailash-0.3.1}/src/kailash/workflow/__init__.py +0 -0
  140. {kailash-0.3.0 → kailash-0.3.1}/src/kailash.egg-info/dependency_links.txt +0 -0
  141. {kailash-0.3.0 → kailash-0.3.1}/src/kailash.egg-info/entry_points.txt +0 -0
  142. {kailash-0.3.0 → kailash-0.3.1}/src/kailash.egg-info/not-zip-safe +0 -0
  143. {kailash-0.3.0 → kailash-0.3.1}/src/kailash.egg-info/requires.txt +0 -0
  144. {kailash-0.3.0 → kailash-0.3.1}/src/kailash.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kailash
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: Python SDK for the Kailash container-node architecture
5
5
  Home-page: https://github.com/integrum/kailash-python-sdk
6
6
  Author: Integrum
@@ -109,7 +109,8 @@ Dynamic: requires-python
109
109
  - 🔁 **Cyclic Workflows (v0.2.0)**: Universal Hybrid Cyclic Graph Architecture with 30,000+ iterations/second performance
110
110
  - 🛠️ **Developer Tools**: CycleAnalyzer, CycleDebugger, CycleProfiler for production-ready cyclic workflows
111
111
  - 📈 **High Performance**: Optimized execution engine supporting 100,000+ iteration workflows
112
- - 📁 **Enhanced Documentation (v0.2.2)**: Reorganized structure with production-ready workflow library
112
+ - 📁 **Complete Finance Workflow Library (v0.3.1)**: Production-ready financial workflows with AI analysis
113
+ - 💼 **Enterprise Workflow Patterns**: Credit risk, portfolio optimization, trading signals, fraud detection
113
114
 
114
115
  ## 🎯 Who Is This For?
115
116
 
@@ -197,6 +198,52 @@ from kailash.utils.export import export_workflow
197
198
  export_workflow(workflow, "customer_analysis.yaml")
198
199
  ```
199
200
 
201
+ ## 💼 Finance Workflow Library (New in v0.3.1)
202
+
203
+ Complete production-ready financial workflows using AI and modern quantitative methods:
204
+
205
+ ### Credit Risk Assessment
206
+
207
+ ```python
208
+ from kailash.workflow import Workflow
209
+ from kailash.nodes.data import CSVReaderNode
210
+ from kailash.nodes.code import PythonCodeNode
211
+ from kailash.nodes.ai import LLMAgentNode
212
+
213
+ def calculate_risk_metrics(customers, transactions):
214
+ """Calculate comprehensive risk metrics."""
215
+ # Modern risk scoring with AI analysis
216
+ # 100+ lines of production risk calculation
217
+ return {"result": risk_scores}
218
+
219
+ workflow = Workflow("credit-risk", "Credit Risk Assessment")
220
+ workflow.add_node("customer_reader", CSVReaderNode())
221
+ workflow.add_node("risk_calculator", PythonCodeNode.from_function(func=calculate_risk_metrics))
222
+ workflow.add_node("ai_analyzer", LLMAgentNode(model="gpt-4",
223
+ system_prompt="You are a financial risk expert..."))
224
+ ```
225
+
226
+ ### Portfolio Optimization
227
+
228
+ ```python
229
+ def optimize_portfolio(holdings, market_data, risk_profile="moderate"):
230
+ """Modern Portfolio Theory optimization with rebalancing."""
231
+ # Sharpe ratio optimization, correlation analysis
232
+ # Risk-adjusted returns with AI market insights
233
+ return {"result": optimization_plan}
234
+
235
+ workflow = Workflow("portfolio-opt", "Portfolio Optimization")
236
+ workflow.add_node("optimizer", PythonCodeNode.from_function(func=optimize_portfolio))
237
+ # Generates rebalancing trades, risk metrics, AI market analysis
238
+ ```
239
+
240
+ ### Trading Signals & Fraud Detection
241
+
242
+ - **Trading Signals**: Technical indicators (RSI, MACD, Bollinger Bands) + AI sentiment
243
+ - **Fraud Detection**: Real-time transaction monitoring with velocity analysis
244
+
245
+ **See complete examples**: `sdk-users/workflows/by-industry/finance/`
246
+
200
247
  ## 📚 Documentation
201
248
 
202
249
  ### For SDK Users
@@ -204,9 +251,10 @@ export_workflow(workflow, "customer_analysis.yaml")
204
251
  **Build solutions with the SDK:**
205
252
  - `sdk-users/` - Everything you need to build with Kailash
206
253
  - `developer/` - Node creation patterns and troubleshooting
207
- - `workflows/` - Production-ready workflow library (NEW in v0.2.2)
254
+ - `workflows/` - Complete production workflow library (v0.3.1)
255
+ - Finance workflows: Credit risk, portfolio optimization, trading signals, fraud detection
208
256
  - Quick-start patterns (30-second workflows)
209
- - Industry-specific solutions (healthcare, finance)
257
+ - Industry-specific solutions by vertical
210
258
  - Enterprise integration patterns
211
259
  - `essentials/` - Quick reference and cheatsheets
212
260
  - `nodes/` - Comprehensive node catalog (66+ nodes)
@@ -347,7 +395,7 @@ assert results["analyze"]["result"]["total_customers"] == len(test_data)
347
395
  3. **Monitor in real-time**:
348
396
  ```python
349
397
  from kailash.visualization import DashboardServer
350
-
398
+
351
399
  server = DashboardServer(port=8080)
352
400
  server.start()
353
401
  # Open http://localhost:8080 for live monitoring
@@ -39,7 +39,8 @@
39
39
  - 🔁 **Cyclic Workflows (v0.2.0)**: Universal Hybrid Cyclic Graph Architecture with 30,000+ iterations/second performance
40
40
  - 🛠️ **Developer Tools**: CycleAnalyzer, CycleDebugger, CycleProfiler for production-ready cyclic workflows
41
41
  - 📈 **High Performance**: Optimized execution engine supporting 100,000+ iteration workflows
42
- - 📁 **Enhanced Documentation (v0.2.2)**: Reorganized structure with production-ready workflow library
42
+ - 📁 **Complete Finance Workflow Library (v0.3.1)**: Production-ready financial workflows with AI analysis
43
+ - 💼 **Enterprise Workflow Patterns**: Credit risk, portfolio optimization, trading signals, fraud detection
43
44
 
44
45
  ## 🎯 Who Is This For?
45
46
 
@@ -127,6 +128,52 @@ from kailash.utils.export import export_workflow
127
128
  export_workflow(workflow, "customer_analysis.yaml")
128
129
  ```
129
130
 
131
+ ## 💼 Finance Workflow Library (New in v0.3.1)
132
+
133
+ Complete production-ready financial workflows using AI and modern quantitative methods:
134
+
135
+ ### Credit Risk Assessment
136
+
137
+ ```python
138
+ from kailash.workflow import Workflow
139
+ from kailash.nodes.data import CSVReaderNode
140
+ from kailash.nodes.code import PythonCodeNode
141
+ from kailash.nodes.ai import LLMAgentNode
142
+
143
+ def calculate_risk_metrics(customers, transactions):
144
+ """Calculate comprehensive risk metrics."""
145
+ # Modern risk scoring with AI analysis
146
+ # 100+ lines of production risk calculation
147
+ return {"result": risk_scores}
148
+
149
+ workflow = Workflow("credit-risk", "Credit Risk Assessment")
150
+ workflow.add_node("customer_reader", CSVReaderNode())
151
+ workflow.add_node("risk_calculator", PythonCodeNode.from_function(func=calculate_risk_metrics))
152
+ workflow.add_node("ai_analyzer", LLMAgentNode(model="gpt-4",
153
+ system_prompt="You are a financial risk expert..."))
154
+ ```
155
+
156
+ ### Portfolio Optimization
157
+
158
+ ```python
159
+ def optimize_portfolio(holdings, market_data, risk_profile="moderate"):
160
+ """Modern Portfolio Theory optimization with rebalancing."""
161
+ # Sharpe ratio optimization, correlation analysis
162
+ # Risk-adjusted returns with AI market insights
163
+ return {"result": optimization_plan}
164
+
165
+ workflow = Workflow("portfolio-opt", "Portfolio Optimization")
166
+ workflow.add_node("optimizer", PythonCodeNode.from_function(func=optimize_portfolio))
167
+ # Generates rebalancing trades, risk metrics, AI market analysis
168
+ ```
169
+
170
+ ### Trading Signals & Fraud Detection
171
+
172
+ - **Trading Signals**: Technical indicators (RSI, MACD, Bollinger Bands) + AI sentiment
173
+ - **Fraud Detection**: Real-time transaction monitoring with velocity analysis
174
+
175
+ **See complete examples**: `sdk-users/workflows/by-industry/finance/`
176
+
130
177
  ## 📚 Documentation
131
178
 
132
179
  ### For SDK Users
@@ -134,9 +181,10 @@ export_workflow(workflow, "customer_analysis.yaml")
134
181
  **Build solutions with the SDK:**
135
182
  - `sdk-users/` - Everything you need to build with Kailash
136
183
  - `developer/` - Node creation patterns and troubleshooting
137
- - `workflows/` - Production-ready workflow library (NEW in v0.2.2)
184
+ - `workflows/` - Complete production workflow library (v0.3.1)
185
+ - Finance workflows: Credit risk, portfolio optimization, trading signals, fraud detection
138
186
  - Quick-start patterns (30-second workflows)
139
- - Industry-specific solutions (healthcare, finance)
187
+ - Industry-specific solutions by vertical
140
188
  - Enterprise integration patterns
141
189
  - `essentials/` - Quick reference and cheatsheets
142
190
  - `nodes/` - Comprehensive node catalog (66+ nodes)
@@ -277,7 +325,7 @@ assert results["analyze"]["result"]["total_customers"] == len(test_data)
277
325
  3. **Monitor in real-time**:
278
326
  ```python
279
327
  from kailash.visualization import DashboardServer
280
-
328
+
281
329
  server = DashboardServer(port=8080)
282
330
  server.start()
283
331
  # Open http://localhost:8080 for live monitoring
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "kailash"
7
- version = "0.3.0"
7
+ version = "0.3.1"
8
8
  description = "Python SDK for the Kailash container-node architecture"
9
9
  authors = [
10
10
  {name = "Integrum", email = "info@integrum.com"}
@@ -89,3 +89,19 @@ dev = [
89
89
 
90
90
  [project.scripts]
91
91
  kailash = "kailash.cli:main"
92
+
93
+ [tool.ruff]
94
+ line-length = 88
95
+ target-version = "py311"
96
+
97
+ [tool.ruff.lint]
98
+ select = ["E", "F"]
99
+ ignore = ["E203", "E501", "E722", "F401", "F841"]
100
+
101
+ [tool.ruff.lint.per-file-ignores]
102
+ "sdk-users/workflows/by-industry/finance/scripts/*.py" = ["E402"]
103
+ "examples/feature_examples/workflows/cyclic/*.py" = ["E722"]
104
+ "examples/feature_examples/workflows/parallel/*.py" = ["E722"]
105
+ "scripts/refactor-pythoncode-to-functions.py" = ["E722"]
106
+ "src/kailash/nodes/api/security.py" = ["E722"]
107
+ "tests/conftest_sdk_dev.py" = ["E722"]
@@ -13,7 +13,7 @@ with open(os.path.join(this_directory, "README.md"), encoding="utf-8") as f:
13
13
  # Package configuration
14
14
  setup(
15
15
  name="kailash",
16
- version="0.3.0",
16
+ version="0.3.1",
17
17
  author="Integrum",
18
18
  author_email="info@integrum.com",
19
19
  description="Python SDK for the Kailash container-node architecture",
@@ -15,7 +15,7 @@ from kailash.workflow.visualization import WorkflowVisualizer
15
15
  # For backward compatibility
16
16
  WorkflowGraph = Workflow
17
17
 
18
- __version__ = "0.3.0"
18
+ __version__ = "0.3.1"
19
19
 
20
20
  __all__ = [
21
21
  "Workflow",
@@ -51,10 +51,11 @@ Future Enhancements:
51
51
 
52
52
  import logging
53
53
  import threading
54
+ from collections.abc import Callable
54
55
  from dataclasses import dataclass, field
55
- from datetime import datetime, timezone
56
+ from datetime import UTC, datetime
56
57
  from enum import Enum
57
- from typing import Any, Callable, Dict, List, Optional, Set, Union
58
+ from typing import Any
58
59
 
59
60
  logger = logging.getLogger(__name__)
60
61
 
@@ -136,11 +137,11 @@ class UserContext:
136
137
  user_id: str
137
138
  tenant_id: str
138
139
  email: str
139
- roles: List[str] = field(default_factory=list)
140
- permissions: List[str] = field(default_factory=list)
141
- attributes: Dict[str, Any] = field(default_factory=dict) # Custom attributes
142
- session_id: Optional[str] = None
143
- ip_address: Optional[str] = None
140
+ roles: list[str] = field(default_factory=list)
141
+ permissions: list[str] = field(default_factory=list)
142
+ attributes: dict[str, Any] = field(default_factory=dict) # Custom attributes
143
+ session_id: str | None = None
144
+ ip_address: str | None = None
144
145
 
145
146
 
146
147
  @dataclass
@@ -195,21 +196,21 @@ class PermissionRule:
195
196
  id: str
196
197
  resource_type: str # "workflow" or "node"
197
198
  resource_id: str # workflow_id or node_id
198
- permission: Union[WorkflowPermission, NodePermission]
199
+ permission: WorkflowPermission | NodePermission
199
200
  effect: PermissionEffect
200
201
 
201
202
  # Who does this apply to?
202
- user_id: Optional[str] = None # Specific user
203
- role: Optional[str] = None # Any user with role
204
- tenant_id: Optional[str] = None # All users in tenant
203
+ user_id: str | None = None # Specific user
204
+ role: str | None = None # Any user with role
205
+ tenant_id: str | None = None # All users in tenant
205
206
 
206
207
  # Conditions
207
- conditions: Dict[str, Any] = field(default_factory=dict)
208
+ conditions: dict[str, Any] = field(default_factory=dict)
208
209
 
209
210
  # Metadata
210
- created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
211
- created_by: Optional[str] = None
212
- expires_at: Optional[datetime] = None
211
+ created_at: datetime = field(default_factory=lambda: datetime.now(UTC))
212
+ created_by: str | None = None
213
+ expires_at: datetime | None = None
213
214
  priority: int = 0 # Higher priority rules evaluated first
214
215
 
215
216
 
@@ -262,17 +263,17 @@ class AccessDecision:
262
263
 
263
264
  allowed: bool
264
265
  reason: str
265
- applied_rules: List[PermissionRule] = field(default_factory=list)
266
- conditions_met: Dict[str, bool] = field(default_factory=dict)
267
- masked_fields: List[str] = field(default_factory=list) # Fields to mask in output
268
- redirect_node: Optional[str] = None # Alternative node to execute
266
+ applied_rules: list[PermissionRule] = field(default_factory=list)
267
+ conditions_met: dict[str, bool] = field(default_factory=dict)
268
+ masked_fields: list[str] = field(default_factory=list) # Fields to mask in output
269
+ redirect_node: str | None = None # Alternative node to execute
269
270
 
270
271
 
271
272
  class ConditionEvaluator:
272
273
  """Evaluates conditions for conditional permissions"""
273
274
 
274
275
  def __init__(self):
275
- self.evaluators: Dict[str, Callable] = {
276
+ self.evaluators: dict[str, Callable] = {
276
277
  "time_range": self._eval_time_range,
277
278
  "data_contains": self._eval_data_contains,
278
279
  "user_attribute": self._eval_user_attribute,
@@ -281,7 +282,7 @@ class ConditionEvaluator:
281
282
  }
282
283
 
283
284
  def evaluate(
284
- self, condition_type: str, condition_value: Any, context: Dict[str, Any]
285
+ self, condition_type: str, condition_value: Any, context: dict[str, Any]
285
286
  ) -> bool:
286
287
  """Evaluate a condition"""
287
288
  evaluator = self.evaluators.get(condition_type)
@@ -295,7 +296,7 @@ class ConditionEvaluator:
295
296
  logger.error(f"Error evaluating condition {condition_type}: {e}")
296
297
  return False
297
298
 
298
- def _eval_time_range(self, value: Dict[str, str], context: Dict[str, Any]) -> bool:
299
+ def _eval_time_range(self, value: dict[str, str], context: dict[str, Any]) -> bool:
299
300
  """Check if current time is within range"""
300
301
  from datetime import datetime, time
301
302
 
@@ -305,7 +306,7 @@ class ConditionEvaluator:
305
306
  return start <= now <= end
306
307
 
307
308
  def _eval_data_contains(
308
- self, value: Dict[str, Any], context: Dict[str, Any]
309
+ self, value: dict[str, Any], context: dict[str, Any]
309
310
  ) -> bool:
310
311
  """Check if data contains specific values"""
311
312
  data = context.get("data", {})
@@ -317,7 +318,7 @@ class ConditionEvaluator:
317
318
  return False
318
319
 
319
320
  def _eval_user_attribute(
320
- self, value: Dict[str, Any], context: Dict[str, Any]
321
+ self, value: dict[str, Any], context: dict[str, Any]
321
322
  ) -> bool:
322
323
  """Check user attributes"""
323
324
  user = context.get("user")
@@ -329,7 +330,7 @@ class ConditionEvaluator:
329
330
 
330
331
  return user.attributes.get(attr_name) == expected
331
332
 
332
- def _eval_ip_range(self, value: Dict[str, Any], context: Dict[str, Any]) -> bool:
333
+ def _eval_ip_range(self, value: dict[str, Any], context: dict[str, Any]) -> bool:
333
334
  """Check if IP is in allowed range"""
334
335
  # Simplified IP check - in production use ipaddress module
335
336
  allowed_ips = value.get("allowed", [])
@@ -337,7 +338,7 @@ class ConditionEvaluator:
337
338
 
338
339
  return user_ip in allowed_ips
339
340
 
340
- def _eval_custom(self, value: Dict[str, Any], context: Dict[str, Any]) -> bool:
341
+ def _eval_custom(self, value: dict[str, Any], context: dict[str, Any]) -> bool:
341
342
  """Evaluate custom condition"""
342
343
  # This would call a custom function registered by the user
343
344
  return True
@@ -409,7 +410,7 @@ class AccessControlManager:
409
410
 
410
411
  def __init__(self, enabled: bool = False):
411
412
  self.enabled = enabled # Disabled by default
412
- self.rules: List[PermissionRule] = []
413
+ self.rules: list[PermissionRule] = []
413
414
  self.condition_evaluator = ConditionEvaluator()
414
415
  self._cache = {} # Cache access decisions
415
416
  self._cache_lock = threading.Lock()
@@ -458,7 +459,7 @@ class AccessControlManager:
458
459
  user: UserContext,
459
460
  node_id: str,
460
461
  permission: NodePermission,
461
- runtime_context: Dict[str, Any] = None,
462
+ runtime_context: dict[str, Any] = None,
462
463
  ) -> AccessDecision:
463
464
  """Check if user has permission on node"""
464
465
  cache_key = f"node:{node_id}:{user.user_id}:{permission.value}"
@@ -494,7 +495,7 @@ class AccessControlManager:
494
495
 
495
496
  def get_accessible_nodes(
496
497
  self, user: UserContext, workflow_id: str, permission: NodePermission
497
- ) -> Set[str]:
498
+ ) -> set[str]:
498
499
  """Get all nodes user can access in a workflow"""
499
500
  accessible = set()
500
501
 
@@ -518,9 +519,9 @@ class AccessControlManager:
518
519
  self,
519
520
  user: UserContext,
520
521
  conditional_node_id: str,
521
- true_path_nodes: List[str],
522
- false_path_nodes: List[str],
523
- ) -> List[str]:
522
+ true_path_nodes: list[str],
523
+ false_path_nodes: list[str],
524
+ ) -> list[str]:
524
525
  """Determine which path user should take based on permissions"""
525
526
  # Check if user has access to nodes in true path
526
527
  true_path_accessible = all(
@@ -544,8 +545,8 @@ class AccessControlManager:
544
545
  return []
545
546
 
546
547
  def mask_node_output(
547
- self, user: UserContext, node_id: str, output: Dict[str, Any]
548
- ) -> Dict[str, Any]:
548
+ self, user: UserContext, node_id: str, output: dict[str, Any]
549
+ ) -> dict[str, Any]:
549
550
  """Mask sensitive fields in node output"""
550
551
  decision = self.check_node_access(user, node_id, NodePermission.READ_OUTPUT)
551
552
 
@@ -568,8 +569,8 @@ class AccessControlManager:
568
569
  user: UserContext,
569
570
  resource_type: str,
570
571
  resource_id: str,
571
- permission: Union[WorkflowPermission, NodePermission],
572
- runtime_context: Dict[str, Any],
572
+ permission: WorkflowPermission | NodePermission,
573
+ runtime_context: dict[str, Any],
573
574
  ) -> AccessDecision:
574
575
  """Evaluate all applicable rules"""
575
576
  applicable_rules = []
@@ -584,7 +585,7 @@ class AccessControlManager:
584
585
  ):
585
586
 
586
587
  # Check expiration
587
- if rule.expires_at and rule.expires_at < datetime.now(timezone.utc):
588
+ if rule.expires_at and rule.expires_at < datetime.now(UTC):
588
589
  continue
589
590
 
590
591
  applicable_rules.append(rule)
@@ -593,7 +594,7 @@ class AccessControlManager:
593
594
  context = {
594
595
  "user": user,
595
596
  "runtime": runtime_context,
596
- "timestamp": datetime.now(timezone.utc),
597
+ "timestamp": datetime.now(UTC),
597
598
  }
598
599
 
599
600
  final_effect = PermissionEffect.DENY # Default deny
@@ -660,7 +661,7 @@ class AccessControlManager:
660
661
  user: UserContext,
661
662
  resource_type: str,
662
663
  resource_id: str,
663
- permission: Union[WorkflowPermission, NodePermission],
664
+ permission: WorkflowPermission | NodePermission,
664
665
  decision: AccessDecision,
665
666
  ):
666
667
  """Log access attempt for audit"""
@@ -50,8 +50,8 @@ Future Enhancements:
50
50
  import os
51
51
  import secrets
52
52
  import threading
53
- from datetime import datetime, timedelta, timezone
54
- from typing import Any, Dict, List, Optional
53
+ from datetime import UTC, datetime, timedelta
54
+ from typing import Any
55
55
 
56
56
  from fastapi import Depends, HTTPException, Request, status
57
57
  from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
@@ -101,14 +101,14 @@ class User(Base):
101
101
 
102
102
  # User roles and permissions
103
103
  roles = Column(JSON, default=lambda: ["user"])
104
- permissions = Column(JSON, default=lambda: [])
104
+ permissions = Column(JSON, default=list)
105
105
 
106
106
  # Timestamps
107
- created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc))
107
+ created_at = Column(DateTime, default=lambda: datetime.now(UTC))
108
108
  updated_at = Column(
109
109
  DateTime,
110
- default=lambda: datetime.now(timezone.utc),
111
- onupdate=lambda: datetime.now(timezone.utc),
110
+ default=lambda: datetime.now(UTC),
111
+ onupdate=lambda: datetime.now(UTC),
112
112
  )
113
113
  last_login = Column(DateTime)
114
114
 
@@ -150,11 +150,11 @@ class Tenant(Base):
150
150
  subscription_tier = Column(String(50), default="free")
151
151
 
152
152
  # Timestamps
153
- created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc))
153
+ created_at = Column(DateTime, default=lambda: datetime.now(UTC))
154
154
  updated_at = Column(
155
155
  DateTime,
156
- default=lambda: datetime.now(timezone.utc),
157
- onupdate=lambda: datetime.now(timezone.utc),
156
+ default=lambda: datetime.now(UTC),
157
+ onupdate=lambda: datetime.now(UTC),
158
158
  )
159
159
 
160
160
  # Relationships
@@ -187,7 +187,7 @@ class APIKey(Base):
187
187
  usage_count = Column(JSON, default=lambda: {"total": 0, "monthly": 0})
188
188
 
189
189
  # Timestamps
190
- created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc))
190
+ created_at = Column(DateTime, default=lambda: datetime.now(UTC))
191
191
 
192
192
  # Relationships
193
193
  user = relationship("User", back_populates="api_keys")
@@ -203,7 +203,7 @@ class UserCreate(BaseModel):
203
203
  email: EmailStr
204
204
  username: str = Field(..., min_length=3, max_length=100)
205
205
  password: str = Field(..., min_length=8)
206
- tenant_id: Optional[str] = None # If None, create new tenant
206
+ tenant_id: str | None = None # If None, create new tenant
207
207
 
208
208
 
209
209
  class UserLogin(BaseModel):
@@ -227,9 +227,9 @@ class TokenData(BaseModel):
227
227
 
228
228
  sub: str
229
229
  tenant_id: str
230
- roles: List[str] = ["user"]
231
- permissions: List[str] = []
232
- exp: Optional[datetime] = None
230
+ roles: list[str] = ["user"]
231
+ permissions: list[str] = []
232
+ exp: datetime | None = None
233
233
 
234
234
 
235
235
  class JWTAuth:
@@ -240,33 +240,27 @@ class JWTAuth:
240
240
  self.algorithm = ALGORITHM
241
241
 
242
242
  def create_access_token(
243
- self, data: Dict[str, Any], expires_delta: Optional[timedelta] = None
243
+ self, data: dict[str, Any], expires_delta: timedelta | None = None
244
244
  ) -> str:
245
245
  """Create a JWT access token"""
246
246
  to_encode = data.copy()
247
247
 
248
248
  if expires_delta:
249
- expire = datetime.now(timezone.utc) + expires_delta
249
+ expire = datetime.now(UTC) + expires_delta
250
250
  else:
251
- expire = datetime.now(timezone.utc) + timedelta(
252
- hours=ACCESS_TOKEN_EXPIRE_HOURS
253
- )
251
+ expire = datetime.now(UTC) + timedelta(hours=ACCESS_TOKEN_EXPIRE_HOURS)
254
252
 
255
- to_encode.update(
256
- {"exp": expire, "iat": datetime.now(timezone.utc), "type": "access"}
257
- )
253
+ to_encode.update({"exp": expire, "iat": datetime.now(UTC), "type": "access"})
258
254
 
259
255
  encoded_jwt = jwt.encode(to_encode, self.secret_key, algorithm=self.algorithm)
260
256
  return encoded_jwt
261
257
 
262
- def create_refresh_token(self, data: Dict[str, Any]) -> str:
258
+ def create_refresh_token(self, data: dict[str, Any]) -> str:
263
259
  """Create a JWT refresh token"""
264
260
  to_encode = data.copy()
265
- expire = datetime.now(timezone.utc) + timedelta(days=REFRESH_TOKEN_EXPIRE_DAYS)
261
+ expire = datetime.now(UTC) + timedelta(days=REFRESH_TOKEN_EXPIRE_DAYS)
266
262
 
267
- to_encode.update(
268
- {"exp": expire, "iat": datetime.now(timezone.utc), "type": "refresh"}
269
- )
263
+ to_encode.update({"exp": expire, "iat": datetime.now(UTC), "type": "refresh"})
270
264
 
271
265
  encoded_jwt = jwt.encode(to_encode, self.secret_key, algorithm=self.algorithm)
272
266
  return encoded_jwt
@@ -421,13 +415,13 @@ async def verify_api_key(
421
415
  )
422
416
 
423
417
  # Check expiration
424
- if valid_key.expires_at and valid_key.expires_at < datetime.now(timezone.utc):
418
+ if valid_key.expires_at and valid_key.expires_at < datetime.now(UTC):
425
419
  raise HTTPException(
426
420
  status_code=status.HTTP_401_UNAUTHORIZED, detail="API key expired"
427
421
  )
428
422
 
429
423
  # Update usage
430
- valid_key.last_used_at = datetime.now(timezone.utc)
424
+ valid_key.last_used_at = datetime.now(UTC)
431
425
  valid_key.usage_count["total"] += 1
432
426
  valid_key.usage_count["monthly"] += 1
433
427
  session.commit()
@@ -514,7 +508,7 @@ class TenantContext:
514
508
  _tenant_context = threading.local()
515
509
 
516
510
 
517
- def get_current_tenant_id() -> Optional[str]:
511
+ def get_current_tenant_id() -> str | None:
518
512
  """Get current tenant ID from context"""
519
513
  return getattr(_tenant_context, "tenant_id", None)
520
514
 
@@ -615,7 +609,7 @@ class AuthService:
615
609
  )
616
610
 
617
611
  # Update last login
618
- user.last_login = datetime.now(timezone.utc)
612
+ user.last_login = datetime.now(UTC)
619
613
  self.session.commit()
620
614
 
621
615
  # Generate tokens
@@ -646,7 +640,7 @@ class AuthService:
646
640
  return self.auth.create_tokens(user)
647
641
 
648
642
  def create_api_key(
649
- self, name: str, user: User, scopes: List[str] = None
643
+ self, name: str, user: User, scopes: list[str] = None
650
644
  ) -> tuple[str, APIKey]:
651
645
  """Create an API key for a user"""
652
646
  key, key_hash = create_api_key()