hexdag 0.5.0.dev1__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.
- hexdag/__init__.py +116 -0
- hexdag/__main__.py +30 -0
- hexdag/adapters/executors/__init__.py +5 -0
- hexdag/adapters/executors/local_executor.py +316 -0
- hexdag/builtin/__init__.py +6 -0
- hexdag/builtin/adapters/__init__.py +51 -0
- hexdag/builtin/adapters/anthropic/__init__.py +5 -0
- hexdag/builtin/adapters/anthropic/anthropic_adapter.py +151 -0
- hexdag/builtin/adapters/database/__init__.py +6 -0
- hexdag/builtin/adapters/database/csv/csv_adapter.py +249 -0
- hexdag/builtin/adapters/database/pgvector/__init__.py +5 -0
- hexdag/builtin/adapters/database/pgvector/pgvector_adapter.py +478 -0
- hexdag/builtin/adapters/database/sqlalchemy/sqlalchemy_adapter.py +252 -0
- hexdag/builtin/adapters/database/sqlite/__init__.py +5 -0
- hexdag/builtin/adapters/database/sqlite/sqlite_adapter.py +410 -0
- hexdag/builtin/adapters/local/README.md +59 -0
- hexdag/builtin/adapters/local/__init__.py +7 -0
- hexdag/builtin/adapters/local/local_observer_manager.py +696 -0
- hexdag/builtin/adapters/memory/__init__.py +47 -0
- hexdag/builtin/adapters/memory/file_memory_adapter.py +297 -0
- hexdag/builtin/adapters/memory/in_memory_memory.py +216 -0
- hexdag/builtin/adapters/memory/schemas.py +57 -0
- hexdag/builtin/adapters/memory/session_memory.py +178 -0
- hexdag/builtin/adapters/memory/sqlite_memory_adapter.py +215 -0
- hexdag/builtin/adapters/memory/state_memory.py +280 -0
- hexdag/builtin/adapters/mock/README.md +89 -0
- hexdag/builtin/adapters/mock/__init__.py +15 -0
- hexdag/builtin/adapters/mock/hexdag.toml +50 -0
- hexdag/builtin/adapters/mock/mock_database.py +225 -0
- hexdag/builtin/adapters/mock/mock_embedding.py +223 -0
- hexdag/builtin/adapters/mock/mock_llm.py +177 -0
- hexdag/builtin/adapters/mock/mock_tool_adapter.py +192 -0
- hexdag/builtin/adapters/mock/mock_tool_router.py +232 -0
- hexdag/builtin/adapters/openai/__init__.py +5 -0
- hexdag/builtin/adapters/openai/openai_adapter.py +634 -0
- hexdag/builtin/adapters/secret/__init__.py +7 -0
- hexdag/builtin/adapters/secret/local_secret_adapter.py +248 -0
- hexdag/builtin/adapters/unified_tool_router.py +280 -0
- hexdag/builtin/macros/__init__.py +17 -0
- hexdag/builtin/macros/conversation_agent.py +390 -0
- hexdag/builtin/macros/llm_macro.py +151 -0
- hexdag/builtin/macros/reasoning_agent.py +423 -0
- hexdag/builtin/macros/tool_macro.py +380 -0
- hexdag/builtin/nodes/__init__.py +38 -0
- hexdag/builtin/nodes/_discovery.py +123 -0
- hexdag/builtin/nodes/agent_node.py +696 -0
- hexdag/builtin/nodes/base_node_factory.py +242 -0
- hexdag/builtin/nodes/composite_node.py +926 -0
- hexdag/builtin/nodes/data_node.py +201 -0
- hexdag/builtin/nodes/expression_node.py +487 -0
- hexdag/builtin/nodes/function_node.py +454 -0
- hexdag/builtin/nodes/llm_node.py +491 -0
- hexdag/builtin/nodes/loop_node.py +920 -0
- hexdag/builtin/nodes/mapped_input.py +518 -0
- hexdag/builtin/nodes/port_call_node.py +269 -0
- hexdag/builtin/nodes/tool_call_node.py +195 -0
- hexdag/builtin/nodes/tool_utils.py +390 -0
- hexdag/builtin/prompts/__init__.py +68 -0
- hexdag/builtin/prompts/base.py +422 -0
- hexdag/builtin/prompts/chat_prompts.py +303 -0
- hexdag/builtin/prompts/error_correction_prompts.py +320 -0
- hexdag/builtin/prompts/tool_prompts.py +160 -0
- hexdag/builtin/tools/builtin_tools.py +84 -0
- hexdag/builtin/tools/database_tools.py +164 -0
- hexdag/cli/__init__.py +17 -0
- hexdag/cli/__main__.py +7 -0
- hexdag/cli/commands/__init__.py +27 -0
- hexdag/cli/commands/build_cmd.py +812 -0
- hexdag/cli/commands/create_cmd.py +208 -0
- hexdag/cli/commands/docs_cmd.py +293 -0
- hexdag/cli/commands/generate_types_cmd.py +252 -0
- hexdag/cli/commands/init_cmd.py +188 -0
- hexdag/cli/commands/pipeline_cmd.py +494 -0
- hexdag/cli/commands/plugin_dev_cmd.py +529 -0
- hexdag/cli/commands/plugins_cmd.py +441 -0
- hexdag/cli/commands/studio_cmd.py +101 -0
- hexdag/cli/commands/validate_cmd.py +221 -0
- hexdag/cli/main.py +84 -0
- hexdag/core/__init__.py +83 -0
- hexdag/core/config/__init__.py +20 -0
- hexdag/core/config/loader.py +479 -0
- hexdag/core/config/models.py +150 -0
- hexdag/core/configurable.py +294 -0
- hexdag/core/context/__init__.py +37 -0
- hexdag/core/context/execution_context.py +378 -0
- hexdag/core/docs/__init__.py +26 -0
- hexdag/core/docs/extractors.py +678 -0
- hexdag/core/docs/generators.py +890 -0
- hexdag/core/docs/models.py +120 -0
- hexdag/core/domain/__init__.py +10 -0
- hexdag/core/domain/dag.py +1225 -0
- hexdag/core/exceptions.py +234 -0
- hexdag/core/expression_parser.py +569 -0
- hexdag/core/logging.py +449 -0
- hexdag/core/models/__init__.py +17 -0
- hexdag/core/models/base.py +138 -0
- hexdag/core/orchestration/__init__.py +46 -0
- hexdag/core/orchestration/body_executor.py +481 -0
- hexdag/core/orchestration/components/__init__.py +97 -0
- hexdag/core/orchestration/components/adapter_lifecycle_manager.py +113 -0
- hexdag/core/orchestration/components/checkpoint_manager.py +134 -0
- hexdag/core/orchestration/components/execution_coordinator.py +360 -0
- hexdag/core/orchestration/components/health_check_manager.py +176 -0
- hexdag/core/orchestration/components/input_mapper.py +143 -0
- hexdag/core/orchestration/components/lifecycle_manager.py +583 -0
- hexdag/core/orchestration/components/node_executor.py +377 -0
- hexdag/core/orchestration/components/secret_manager.py +202 -0
- hexdag/core/orchestration/components/wave_executor.py +158 -0
- hexdag/core/orchestration/constants.py +17 -0
- hexdag/core/orchestration/events/README.md +312 -0
- hexdag/core/orchestration/events/__init__.py +104 -0
- hexdag/core/orchestration/events/batching.py +330 -0
- hexdag/core/orchestration/events/decorators.py +139 -0
- hexdag/core/orchestration/events/events.py +573 -0
- hexdag/core/orchestration/events/observers/__init__.py +30 -0
- hexdag/core/orchestration/events/observers/core_observers.py +690 -0
- hexdag/core/orchestration/events/observers/models.py +111 -0
- hexdag/core/orchestration/events/taxonomy.py +269 -0
- hexdag/core/orchestration/hook_context.py +237 -0
- hexdag/core/orchestration/hooks.py +437 -0
- hexdag/core/orchestration/models.py +418 -0
- hexdag/core/orchestration/orchestrator.py +910 -0
- hexdag/core/orchestration/orchestrator_factory.py +275 -0
- hexdag/core/orchestration/port_wrappers.py +327 -0
- hexdag/core/orchestration/prompt/__init__.py +32 -0
- hexdag/core/orchestration/prompt/template.py +332 -0
- hexdag/core/pipeline_builder/__init__.py +21 -0
- hexdag/core/pipeline_builder/component_instantiator.py +386 -0
- hexdag/core/pipeline_builder/include_tag.py +265 -0
- hexdag/core/pipeline_builder/pipeline_config.py +133 -0
- hexdag/core/pipeline_builder/py_tag.py +223 -0
- hexdag/core/pipeline_builder/tag_discovery.py +268 -0
- hexdag/core/pipeline_builder/yaml_builder.py +1196 -0
- hexdag/core/pipeline_builder/yaml_validator.py +569 -0
- hexdag/core/ports/__init__.py +65 -0
- hexdag/core/ports/api_call.py +133 -0
- hexdag/core/ports/database.py +489 -0
- hexdag/core/ports/embedding.py +215 -0
- hexdag/core/ports/executor.py +237 -0
- hexdag/core/ports/file_storage.py +117 -0
- hexdag/core/ports/healthcheck.py +87 -0
- hexdag/core/ports/llm.py +551 -0
- hexdag/core/ports/memory.py +70 -0
- hexdag/core/ports/observer_manager.py +130 -0
- hexdag/core/ports/secret.py +145 -0
- hexdag/core/ports/tool_router.py +94 -0
- hexdag/core/ports_builder.py +623 -0
- hexdag/core/protocols.py +273 -0
- hexdag/core/resolver.py +304 -0
- hexdag/core/schema/__init__.py +9 -0
- hexdag/core/schema/generator.py +742 -0
- hexdag/core/secrets.py +242 -0
- hexdag/core/types.py +413 -0
- hexdag/core/utils/async_warnings.py +206 -0
- hexdag/core/utils/schema_conversion.py +78 -0
- hexdag/core/utils/sql_validation.py +86 -0
- hexdag/core/validation/secure_json.py +148 -0
- hexdag/core/yaml_macro.py +517 -0
- hexdag/mcp_server.py +3120 -0
- hexdag/studio/__init__.py +10 -0
- hexdag/studio/build_ui.py +92 -0
- hexdag/studio/server/__init__.py +1 -0
- hexdag/studio/server/main.py +100 -0
- hexdag/studio/server/routes/__init__.py +9 -0
- hexdag/studio/server/routes/execute.py +208 -0
- hexdag/studio/server/routes/export.py +558 -0
- hexdag/studio/server/routes/files.py +207 -0
- hexdag/studio/server/routes/plugins.py +419 -0
- hexdag/studio/server/routes/validate.py +220 -0
- hexdag/studio/ui/index.html +13 -0
- hexdag/studio/ui/package-lock.json +2992 -0
- hexdag/studio/ui/package.json +31 -0
- hexdag/studio/ui/postcss.config.js +6 -0
- hexdag/studio/ui/public/hexdag.svg +5 -0
- hexdag/studio/ui/src/App.tsx +251 -0
- hexdag/studio/ui/src/components/Canvas.tsx +408 -0
- hexdag/studio/ui/src/components/ContextMenu.tsx +187 -0
- hexdag/studio/ui/src/components/FileBrowser.tsx +123 -0
- hexdag/studio/ui/src/components/Header.tsx +181 -0
- hexdag/studio/ui/src/components/HexdagNode.tsx +193 -0
- hexdag/studio/ui/src/components/NodeInspector.tsx +512 -0
- hexdag/studio/ui/src/components/NodePalette.tsx +262 -0
- hexdag/studio/ui/src/components/NodePortsSection.tsx +403 -0
- hexdag/studio/ui/src/components/PluginManager.tsx +347 -0
- hexdag/studio/ui/src/components/PortsEditor.tsx +481 -0
- hexdag/studio/ui/src/components/PythonEditor.tsx +195 -0
- hexdag/studio/ui/src/components/ValidationPanel.tsx +105 -0
- hexdag/studio/ui/src/components/YamlEditor.tsx +196 -0
- hexdag/studio/ui/src/components/index.ts +8 -0
- hexdag/studio/ui/src/index.css +92 -0
- hexdag/studio/ui/src/main.tsx +10 -0
- hexdag/studio/ui/src/types/index.ts +123 -0
- hexdag/studio/ui/src/vite-env.d.ts +1 -0
- hexdag/studio/ui/tailwind.config.js +29 -0
- hexdag/studio/ui/tsconfig.json +37 -0
- hexdag/studio/ui/tsconfig.node.json +13 -0
- hexdag/studio/ui/vite.config.ts +35 -0
- hexdag/visualization/__init__.py +69 -0
- hexdag/visualization/dag_visualizer.py +1020 -0
- hexdag-0.5.0.dev1.dist-info/METADATA +369 -0
- hexdag-0.5.0.dev1.dist-info/RECORD +261 -0
- hexdag-0.5.0.dev1.dist-info/WHEEL +4 -0
- hexdag-0.5.0.dev1.dist-info/entry_points.txt +4 -0
- hexdag-0.5.0.dev1.dist-info/licenses/LICENSE +190 -0
- hexdag_plugins/.gitignore +43 -0
- hexdag_plugins/README.md +73 -0
- hexdag_plugins/__init__.py +1 -0
- hexdag_plugins/azure/LICENSE +21 -0
- hexdag_plugins/azure/README.md +414 -0
- hexdag_plugins/azure/__init__.py +21 -0
- hexdag_plugins/azure/azure_blob_adapter.py +450 -0
- hexdag_plugins/azure/azure_cosmos_adapter.py +383 -0
- hexdag_plugins/azure/azure_keyvault_adapter.py +314 -0
- hexdag_plugins/azure/azure_openai_adapter.py +415 -0
- hexdag_plugins/azure/pyproject.toml +107 -0
- hexdag_plugins/azure/tests/__init__.py +1 -0
- hexdag_plugins/azure/tests/test_azure_blob_adapter.py +350 -0
- hexdag_plugins/azure/tests/test_azure_cosmos_adapter.py +323 -0
- hexdag_plugins/azure/tests/test_azure_keyvault_adapter.py +330 -0
- hexdag_plugins/azure/tests/test_azure_openai_adapter.py +329 -0
- hexdag_plugins/hexdag_etl/README.md +168 -0
- hexdag_plugins/hexdag_etl/__init__.py +53 -0
- hexdag_plugins/hexdag_etl/examples/01_simple_pandas_transform.py +270 -0
- hexdag_plugins/hexdag_etl/examples/02_simple_pandas_only.py +149 -0
- hexdag_plugins/hexdag_etl/examples/03_file_io_pipeline.py +109 -0
- hexdag_plugins/hexdag_etl/examples/test_pandas_transform.py +84 -0
- hexdag_plugins/hexdag_etl/hexdag.toml +25 -0
- hexdag_plugins/hexdag_etl/hexdag_etl/__init__.py +48 -0
- hexdag_plugins/hexdag_etl/hexdag_etl/nodes/__init__.py +13 -0
- hexdag_plugins/hexdag_etl/hexdag_etl/nodes/api_extract.py +230 -0
- hexdag_plugins/hexdag_etl/hexdag_etl/nodes/base_node_factory.py +181 -0
- hexdag_plugins/hexdag_etl/hexdag_etl/nodes/file_io.py +415 -0
- hexdag_plugins/hexdag_etl/hexdag_etl/nodes/outlook.py +492 -0
- hexdag_plugins/hexdag_etl/hexdag_etl/nodes/pandas_transform.py +563 -0
- hexdag_plugins/hexdag_etl/hexdag_etl/nodes/sql_extract_load.py +112 -0
- hexdag_plugins/hexdag_etl/pyproject.toml +82 -0
- hexdag_plugins/hexdag_etl/test_transform.py +54 -0
- hexdag_plugins/hexdag_etl/tests/test_plugin_integration.py +62 -0
- hexdag_plugins/mysql_adapter/LICENSE +21 -0
- hexdag_plugins/mysql_adapter/README.md +224 -0
- hexdag_plugins/mysql_adapter/__init__.py +6 -0
- hexdag_plugins/mysql_adapter/mysql_adapter.py +408 -0
- hexdag_plugins/mysql_adapter/pyproject.toml +93 -0
- hexdag_plugins/mysql_adapter/tests/test_mysql_adapter.py +259 -0
- hexdag_plugins/storage/README.md +184 -0
- hexdag_plugins/storage/__init__.py +19 -0
- hexdag_plugins/storage/file/__init__.py +5 -0
- hexdag_plugins/storage/file/local.py +325 -0
- hexdag_plugins/storage/ports/__init__.py +5 -0
- hexdag_plugins/storage/ports/vector_store.py +236 -0
- hexdag_plugins/storage/sql/__init__.py +7 -0
- hexdag_plugins/storage/sql/base.py +187 -0
- hexdag_plugins/storage/sql/mysql.py +27 -0
- hexdag_plugins/storage/sql/postgresql.py +27 -0
- hexdag_plugins/storage/tests/__init__.py +1 -0
- hexdag_plugins/storage/tests/test_local_file_storage.py +161 -0
- hexdag_plugins/storage/tests/test_sql_adapters.py +212 -0
- hexdag_plugins/storage/vector/__init__.py +7 -0
- hexdag_plugins/storage/vector/chromadb.py +223 -0
- hexdag_plugins/storage/vector/in_memory.py +285 -0
- hexdag_plugins/storage/vector/pgvector.py +502 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""Observer Manager Port - Clean interface for event observation systems.
|
|
2
|
+
|
|
3
|
+
This port defines the complete contract for observer managers, including
|
|
4
|
+
all safety features and configuration options from the concrete implementation.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from abc import abstractmethod
|
|
10
|
+
from collections.abc import Callable, Iterable
|
|
11
|
+
from typing import Any, Protocol
|
|
12
|
+
|
|
13
|
+
from hexdag.core.orchestration.events.events import Event
|
|
14
|
+
|
|
15
|
+
# Type aliases for observer functions
|
|
16
|
+
ObserverFunc = Callable[[Event], None]
|
|
17
|
+
AsyncObserverFunc = Callable[[Event], Any] # Returns awaitable
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Observer(Protocol):
|
|
21
|
+
"""Protocol for observers that monitor events."""
|
|
22
|
+
|
|
23
|
+
async def handle(self, event: Event) -> None:
|
|
24
|
+
"""Handle an event (read-only, no return value)."""
|
|
25
|
+
...
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ObserverManagerPort(Protocol):
|
|
29
|
+
"""Port interface for event observation systems.
|
|
30
|
+
|
|
31
|
+
Key Safety Guarantees:
|
|
32
|
+
- Observers must be READ-ONLY and cannot affect execution
|
|
33
|
+
- Observer failures must not crash the pipeline (fault isolation)
|
|
34
|
+
- Fire-and-forget pattern with async, non-blocking execution
|
|
35
|
+
- Event type filtering for performance optimization
|
|
36
|
+
- Configurable concurrency control and timeouts
|
|
37
|
+
- Optional weak reference support for memory management
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
@abstractmethod
|
|
41
|
+
def register(
|
|
42
|
+
self,
|
|
43
|
+
handler: Observer | ObserverFunc | AsyncObserverFunc,
|
|
44
|
+
*,
|
|
45
|
+
observer_id: str | None = None,
|
|
46
|
+
event_types: Iterable[type[Event]] | type[Event] | None = None,
|
|
47
|
+
timeout: float | None = None,
|
|
48
|
+
max_concurrency: int | None = None,
|
|
49
|
+
keep_alive: bool = False,
|
|
50
|
+
) -> str:
|
|
51
|
+
"""Register an observer with optional event type filtering.
|
|
52
|
+
|
|
53
|
+
Args
|
|
54
|
+
----
|
|
55
|
+
handler: Either an Observer protocol implementation or
|
|
56
|
+
a function (sync/async) that takes an event
|
|
57
|
+
observer_id: Optional ID for the observer
|
|
58
|
+
event_types: Event type or collection of types to observe (None = all events)
|
|
59
|
+
timeout: Optional timeout override for this observer
|
|
60
|
+
max_concurrency: Optional per-observer concurrency cap (>= 1)
|
|
61
|
+
keep_alive: Keep a strong reference even when using weak refs
|
|
62
|
+
|
|
63
|
+
Returns
|
|
64
|
+
-------
|
|
65
|
+
str: The ID of the registered observer
|
|
66
|
+
"""
|
|
67
|
+
...
|
|
68
|
+
|
|
69
|
+
@abstractmethod
|
|
70
|
+
def unregister(self, handler_id: str) -> bool:
|
|
71
|
+
"""Unregister an observer by ID.
|
|
72
|
+
|
|
73
|
+
Args
|
|
74
|
+
----
|
|
75
|
+
handler_id: The ID of the observer to unregister
|
|
76
|
+
|
|
77
|
+
Returns
|
|
78
|
+
-------
|
|
79
|
+
bool: True if observer was found and removed, False otherwise
|
|
80
|
+
"""
|
|
81
|
+
...
|
|
82
|
+
|
|
83
|
+
@abstractmethod
|
|
84
|
+
async def notify(self, event: Event) -> None:
|
|
85
|
+
"""Notify all interested observers of an event.
|
|
86
|
+
|
|
87
|
+
Only observers registered for this event type will be notified.
|
|
88
|
+
Errors are handled according to the configured error handler
|
|
89
|
+
but don't affect execution.
|
|
90
|
+
|
|
91
|
+
Args
|
|
92
|
+
----
|
|
93
|
+
event: The event to distribute to observers
|
|
94
|
+
"""
|
|
95
|
+
...
|
|
96
|
+
|
|
97
|
+
@abstractmethod
|
|
98
|
+
def clear(self) -> None:
|
|
99
|
+
"""Remove all registered observers."""
|
|
100
|
+
...
|
|
101
|
+
|
|
102
|
+
@abstractmethod
|
|
103
|
+
async def close(self) -> None:
|
|
104
|
+
"""Close the manager and cleanup resources."""
|
|
105
|
+
...
|
|
106
|
+
|
|
107
|
+
@abstractmethod
|
|
108
|
+
def __len__(self) -> int:
|
|
109
|
+
"""Return number of registered observers."""
|
|
110
|
+
...
|
|
111
|
+
|
|
112
|
+
@abstractmethod
|
|
113
|
+
def __enter__(self) -> ObserverManagerPort:
|
|
114
|
+
"""Context manager entry."""
|
|
115
|
+
...
|
|
116
|
+
|
|
117
|
+
@abstractmethod
|
|
118
|
+
def __exit__(self, _exc_type: Any, _exc_val: Any, _exc_tb: Any) -> None:
|
|
119
|
+
"""Context manager exit with cleanup."""
|
|
120
|
+
...
|
|
121
|
+
|
|
122
|
+
@abstractmethod
|
|
123
|
+
async def __aenter__(self) -> ObserverManagerPort:
|
|
124
|
+
"""Async context manager entry."""
|
|
125
|
+
...
|
|
126
|
+
|
|
127
|
+
@abstractmethod
|
|
128
|
+
async def __aexit__(self, _exc_type: Any, _exc_val: Any, _exc_tb: Any) -> None:
|
|
129
|
+
"""Async context manager exit with cleanup."""
|
|
130
|
+
...
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"""Port interface for secret management (KeyVault, AWS Secrets Manager, etc.)."""
|
|
2
|
+
|
|
3
|
+
from abc import abstractmethod
|
|
4
|
+
from typing import TYPE_CHECKING, Protocol, runtime_checkable
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from hexdag.core.ports.healthcheck import HealthStatus
|
|
8
|
+
from hexdag.core.ports.memory import Memory
|
|
9
|
+
from hexdag.core.types import Secret
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@runtime_checkable
|
|
13
|
+
class SecretPort(Protocol):
|
|
14
|
+
"""Port interface for secret/credential management systems.
|
|
15
|
+
|
|
16
|
+
This port abstracts access to secret management services like:
|
|
17
|
+
- Azure KeyVault
|
|
18
|
+
- AWS Secrets Manager
|
|
19
|
+
- HashiCorp Vault
|
|
20
|
+
- Google Secret Manager
|
|
21
|
+
- Environment variables
|
|
22
|
+
|
|
23
|
+
Secrets are returned as Secret[str] objects to prevent accidental logging.
|
|
24
|
+
|
|
25
|
+
Optional Methods
|
|
26
|
+
----------------
|
|
27
|
+
Adapters may optionally implement:
|
|
28
|
+
- ahealth_check(): Verify secret service connectivity and authentication
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
@abstractmethod
|
|
32
|
+
async def aget_secret(self, key: str) -> "Secret":
|
|
33
|
+
"""Retrieve a single secret by key.
|
|
34
|
+
|
|
35
|
+
Args
|
|
36
|
+
----
|
|
37
|
+
key: Secret identifier (e.g., "OPENAI_API_KEY", "database/password")
|
|
38
|
+
|
|
39
|
+
Returns
|
|
40
|
+
-------
|
|
41
|
+
Secret
|
|
42
|
+
Secret wrapper containing the secret value. Use .get() to retrieve the value.
|
|
43
|
+
|
|
44
|
+
Raises
|
|
45
|
+
------
|
|
46
|
+
KeyError
|
|
47
|
+
If the secret does not exist
|
|
48
|
+
ValueError
|
|
49
|
+
If the secret value is empty or invalid
|
|
50
|
+
|
|
51
|
+
Examples
|
|
52
|
+
--------
|
|
53
|
+
Example usage::
|
|
54
|
+
|
|
55
|
+
# Fetch secret from KeyVault
|
|
56
|
+
secret = await keyvault.aget_secret("OPENAI_API_KEY")
|
|
57
|
+
api_key = secret.get() # Unwrap the secret value
|
|
58
|
+
"""
|
|
59
|
+
...
|
|
60
|
+
|
|
61
|
+
@abstractmethod
|
|
62
|
+
async def aload_secrets_to_memory(
|
|
63
|
+
self,
|
|
64
|
+
memory: "Memory",
|
|
65
|
+
prefix: str = "secret:",
|
|
66
|
+
keys: list[str] | None = None,
|
|
67
|
+
) -> dict[str, str]:
|
|
68
|
+
"""Bulk load secrets into Memory port with a prefix.
|
|
69
|
+
|
|
70
|
+
This is the primary method for pre-DAG hooks to inject secrets
|
|
71
|
+
into the pipeline's memory for nodes to access.
|
|
72
|
+
|
|
73
|
+
Args
|
|
74
|
+
----
|
|
75
|
+
memory: Memory port instance to store secrets in
|
|
76
|
+
prefix: Key prefix for stored secrets (default: "secret:")
|
|
77
|
+
keys: List of secret keys to load. If None, loads all available secrets.
|
|
78
|
+
|
|
79
|
+
Returns
|
|
80
|
+
-------
|
|
81
|
+
dict[str, str]
|
|
82
|
+
Mapping of original key → memory key (e.g., {"API_KEY": "secret:API_KEY"})
|
|
83
|
+
|
|
84
|
+
Examples
|
|
85
|
+
--------
|
|
86
|
+
Example usage::
|
|
87
|
+
|
|
88
|
+
# Load specific secrets
|
|
89
|
+
mapping = await keyvault.aload_secrets_to_memory(
|
|
90
|
+
memory=memory,
|
|
91
|
+
keys=["OPENAI_API_KEY", "DATABASE_PASSWORD"]
|
|
92
|
+
)
|
|
93
|
+
# Returns: {"OPENAI_API_KEY": "secret:OPENAI_API_KEY", ...}
|
|
94
|
+
|
|
95
|
+
# Load all secrets
|
|
96
|
+
mapping = await keyvault.aload_secrets_to_memory(memory=memory)
|
|
97
|
+
|
|
98
|
+
# Retrieve in nodes
|
|
99
|
+
api_key = await memory.aget("secret:OPENAI_API_KEY")
|
|
100
|
+
"""
|
|
101
|
+
...
|
|
102
|
+
|
|
103
|
+
async def alist_secret_names(self) -> list[str]:
|
|
104
|
+
"""List all available secret names (optional).
|
|
105
|
+
|
|
106
|
+
Returns
|
|
107
|
+
-------
|
|
108
|
+
list[str]
|
|
109
|
+
List of secret identifiers available in this secret store
|
|
110
|
+
|
|
111
|
+
Examples
|
|
112
|
+
--------
|
|
113
|
+
Example usage::
|
|
114
|
+
|
|
115
|
+
names = await keyvault.alist_secret_names()
|
|
116
|
+
# ["OPENAI_API_KEY", "DATABASE_PASSWORD", "STRIPE_KEY"]
|
|
117
|
+
"""
|
|
118
|
+
...
|
|
119
|
+
|
|
120
|
+
async def ahealth_check(self) -> "HealthStatus":
|
|
121
|
+
"""Check secret service health and connectivity (optional).
|
|
122
|
+
|
|
123
|
+
Adapters should verify:
|
|
124
|
+
- Authentication/authorization status
|
|
125
|
+
- Service connectivity
|
|
126
|
+
- Access permissions
|
|
127
|
+
|
|
128
|
+
This method is optional. If not implemented, the adapter will be
|
|
129
|
+
considered healthy by default.
|
|
130
|
+
|
|
131
|
+
Returns
|
|
132
|
+
-------
|
|
133
|
+
HealthStatus
|
|
134
|
+
Current health status with details about secret service connectivity
|
|
135
|
+
|
|
136
|
+
Examples
|
|
137
|
+
--------
|
|
138
|
+
Example usage::
|
|
139
|
+
|
|
140
|
+
# Azure KeyVault health check
|
|
141
|
+
status = await keyvault.ahealth_check()
|
|
142
|
+
status.status # "healthy", "degraded", or "unhealthy"
|
|
143
|
+
status.details # {"vault_url": "...", "authenticated": True}
|
|
144
|
+
"""
|
|
145
|
+
...
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""Port interface for Tool Routers."""
|
|
2
|
+
|
|
3
|
+
from abc import abstractmethod
|
|
4
|
+
from typing import Any, Protocol, runtime_checkable
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@runtime_checkable
|
|
8
|
+
class ToolRouter(Protocol):
|
|
9
|
+
"""Protocol for routing tool calls."""
|
|
10
|
+
|
|
11
|
+
# Required methods
|
|
12
|
+
@abstractmethod
|
|
13
|
+
async def acall_tool(self, tool_name: str, params: dict[str, Any]) -> Any:
|
|
14
|
+
"""Call a tool with parameters.
|
|
15
|
+
|
|
16
|
+
Args
|
|
17
|
+
----
|
|
18
|
+
tool_name: The name of the tool to call.
|
|
19
|
+
params: Parameters to pass to the tool.
|
|
20
|
+
|
|
21
|
+
Returns
|
|
22
|
+
-------
|
|
23
|
+
The result of the tool call.
|
|
24
|
+
"""
|
|
25
|
+
...
|
|
26
|
+
|
|
27
|
+
@abstractmethod
|
|
28
|
+
def get_available_tools(self) -> list[str]:
|
|
29
|
+
"""Get list of available tool names (sync version for local adapters).
|
|
30
|
+
|
|
31
|
+
Returns
|
|
32
|
+
-------
|
|
33
|
+
List of tool names that can be called
|
|
34
|
+
"""
|
|
35
|
+
...
|
|
36
|
+
|
|
37
|
+
async def aget_available_tools(self) -> list[str]:
|
|
38
|
+
"""Get list of available tool names (async version for remote/MCP adapters).
|
|
39
|
+
|
|
40
|
+
Default implementation delegates to sync version. Override for true async behavior.
|
|
41
|
+
|
|
42
|
+
Returns
|
|
43
|
+
-------
|
|
44
|
+
List of tool names that can be called
|
|
45
|
+
"""
|
|
46
|
+
return self.get_available_tools()
|
|
47
|
+
|
|
48
|
+
def get_tool_schema(self, tool_name: str) -> dict[str, Any]:
|
|
49
|
+
"""Get schema for a specific tool (sync version).
|
|
50
|
+
|
|
51
|
+
Args
|
|
52
|
+
----
|
|
53
|
+
tool_name: Name of the tool
|
|
54
|
+
|
|
55
|
+
Returns
|
|
56
|
+
-------
|
|
57
|
+
Dictionary containing tool schema (name, description, parameters)
|
|
58
|
+
"""
|
|
59
|
+
...
|
|
60
|
+
|
|
61
|
+
async def aget_tool_schema(self, tool_name: str) -> dict[str, Any]:
|
|
62
|
+
"""Get schema for a specific tool (async version for remote/MCP adapters).
|
|
63
|
+
|
|
64
|
+
Default implementation delegates to sync version. Override for true async behavior.
|
|
65
|
+
|
|
66
|
+
Args
|
|
67
|
+
----
|
|
68
|
+
tool_name: Name of the tool
|
|
69
|
+
|
|
70
|
+
Returns
|
|
71
|
+
-------
|
|
72
|
+
Dictionary containing tool schema (name, description, parameters)
|
|
73
|
+
"""
|
|
74
|
+
return self.get_tool_schema(tool_name)
|
|
75
|
+
|
|
76
|
+
def get_all_tool_schemas(self) -> dict[str, dict[str, Any]]:
|
|
77
|
+
"""Get schemas for all available tools (sync version).
|
|
78
|
+
|
|
79
|
+
Returns
|
|
80
|
+
-------
|
|
81
|
+
Dictionary mapping tool names to their schemas
|
|
82
|
+
"""
|
|
83
|
+
...
|
|
84
|
+
|
|
85
|
+
async def aget_all_tool_schemas(self) -> dict[str, dict[str, Any]]:
|
|
86
|
+
"""Get schemas for all available tools (async version for remote/MCP adapters).
|
|
87
|
+
|
|
88
|
+
Default implementation delegates to sync version. Override for true async behavior.
|
|
89
|
+
|
|
90
|
+
Returns
|
|
91
|
+
-------
|
|
92
|
+
Dictionary mapping tool names to their schemas
|
|
93
|
+
"""
|
|
94
|
+
return self.get_all_tool_schemas()
|