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,89 @@
|
|
|
1
|
+
# Mock Adapters
|
|
2
|
+
|
|
3
|
+
Mock adapter implementations for testing and development without external dependencies.
|
|
4
|
+
|
|
5
|
+
## Loading Mock Adapters
|
|
6
|
+
|
|
7
|
+
Mock adapters are **not loaded by default**. To use them, you have two options:
|
|
8
|
+
|
|
9
|
+
### Option 1: Load via Configuration File
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
from hexdag.core.bootstrap import bootstrap_registry
|
|
13
|
+
|
|
14
|
+
# Load mock adapters explicitly
|
|
15
|
+
bootstrap_registry("hexdag/adapters/mock/hexdag.toml")
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### Option 2: Direct Import
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
from hexdag.adapters.mock.mock_llm import MockLLM
|
|
22
|
+
from hexdag.adapters.mock.mock_database import MockDatabaseAdapter
|
|
23
|
+
from hexdag.adapters.mock.mock_tool_router import MockToolRouter
|
|
24
|
+
|
|
25
|
+
# Create instances directly
|
|
26
|
+
llm = MockLLM()
|
|
27
|
+
db = MockDatabaseAdapter()
|
|
28
|
+
router = MockToolRouter()
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Available Mock Adapters
|
|
32
|
+
|
|
33
|
+
### MockLLM
|
|
34
|
+
Simulates LLM responses without API calls.
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from hexdag.core.ports.llm import Message
|
|
38
|
+
|
|
39
|
+
llm = registry.get("mock_llm", namespace="plugin")
|
|
40
|
+
messages = [Message(role="user", content="Hello!")]
|
|
41
|
+
response = await llm.aresponse(messages)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### MockDatabaseAdapter
|
|
45
|
+
Provides sample e-commerce data for testing database operations.
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
db = registry.get("mock_database", namespace="plugin")
|
|
49
|
+
schemas = await db.aget_table_schemas()
|
|
50
|
+
results = await db.aexecute_query("SELECT * FROM customers LIMIT 5")
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### MockToolRouter
|
|
54
|
+
Simulates tool execution with predefined responses.
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
router = registry.get("mock_tool_router", namespace="plugin")
|
|
58
|
+
tools = router.get_available_tools()
|
|
59
|
+
result = await router.acall_tool("calculate", {"expression": "2 + 2"})
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Configuration
|
|
63
|
+
|
|
64
|
+
Mock adapters can be configured via the `hexdag.toml` file:
|
|
65
|
+
|
|
66
|
+
```toml
|
|
67
|
+
[settings.mock_llm]
|
|
68
|
+
responses = ["Custom response 1", "Custom response 2"]
|
|
69
|
+
delay_seconds = 0.1
|
|
70
|
+
|
|
71
|
+
[settings.mock_database]
|
|
72
|
+
enable_sample_data = true
|
|
73
|
+
delay_seconds = 0.0
|
|
74
|
+
|
|
75
|
+
[settings.mock_tool_router]
|
|
76
|
+
available_tools = ["search", "calculate", "get_weather"]
|
|
77
|
+
raise_on_unknown_tool = true
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Use Cases
|
|
81
|
+
|
|
82
|
+
- **Unit Testing**: Test workflows without external dependencies
|
|
83
|
+
- **Development**: Rapid prototyping without API keys or database setup
|
|
84
|
+
- **CI/CD**: Reliable, deterministic tests in pipelines
|
|
85
|
+
- **Demos**: Showcase functionality without infrastructure
|
|
86
|
+
|
|
87
|
+
## Example
|
|
88
|
+
|
|
89
|
+
See `examples/22_mock_adapters_demo.py` for a complete demonstration of using mock adapters in workflows.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Enhanced mock implementations for testing purposes."""
|
|
2
|
+
|
|
3
|
+
from .mock_database import MockDatabaseAdapter
|
|
4
|
+
from .mock_embedding import MockEmbedding
|
|
5
|
+
from .mock_llm import MockLLM
|
|
6
|
+
from .mock_tool_adapter import MockToolAdapter
|
|
7
|
+
from .mock_tool_router import MockToolRouter
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"MockLLM",
|
|
11
|
+
"MockEmbedding",
|
|
12
|
+
"MockDatabaseAdapter",
|
|
13
|
+
"MockToolRouter",
|
|
14
|
+
"MockToolAdapter",
|
|
15
|
+
]
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# HexDAG Configuration
|
|
2
|
+
# This configuration loads the mock adapters as our first plugin
|
|
3
|
+
|
|
4
|
+
# Core modules to load - these are the essential framework components
|
|
5
|
+
modules = [
|
|
6
|
+
"hexdag.core.ports", # Core port definitions (must load before adapters)
|
|
7
|
+
"hexdag.builtin.nodes", # Core node factories
|
|
8
|
+
"hexdag.builtin.adapters.mock", # Mock implementations of all ports
|
|
9
|
+
"hexdag.builtin.adapters.memory", # Memory adapter implementations
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
# Development mode - allows post-bootstrap registration for testing
|
|
13
|
+
dev_mode = true
|
|
14
|
+
|
|
15
|
+
[settings]
|
|
16
|
+
# Logging configuration
|
|
17
|
+
log_level = "INFO"
|
|
18
|
+
enable_metrics = true
|
|
19
|
+
|
|
20
|
+
# Mock adapter default configurations
|
|
21
|
+
[settings.mock_llm]
|
|
22
|
+
# Default responses for the mock LLM
|
|
23
|
+
responses = [
|
|
24
|
+
"I understand your request.",
|
|
25
|
+
"Let me help you with that.",
|
|
26
|
+
"Here's what I found.",
|
|
27
|
+
]
|
|
28
|
+
delay_seconds = 0.0
|
|
29
|
+
|
|
30
|
+
[settings.mock_database]
|
|
31
|
+
# Enable sample e-commerce data by default
|
|
32
|
+
enable_sample_data = true
|
|
33
|
+
delay_seconds = 0.0
|
|
34
|
+
|
|
35
|
+
[settings.mock_memory]
|
|
36
|
+
# In-memory storage settings
|
|
37
|
+
delay_seconds = 0.0
|
|
38
|
+
max_size = 1000 # Maximum number of items to store
|
|
39
|
+
|
|
40
|
+
[settings.mock_tool_router]
|
|
41
|
+
# Available mock tools
|
|
42
|
+
available_tools = [
|
|
43
|
+
"search",
|
|
44
|
+
"calculate",
|
|
45
|
+
"get_weather",
|
|
46
|
+
"get_time",
|
|
47
|
+
"translate",
|
|
48
|
+
]
|
|
49
|
+
delay_seconds = 0.0
|
|
50
|
+
raise_on_unknown_tool = true
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"""Mock database port implementation for testing."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from hexdag.core.ports.database import DatabasePort
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class MockDatabaseAdapter(DatabasePort):
|
|
10
|
+
"""Mock implementation of DatabasePort for testing and demos."""
|
|
11
|
+
|
|
12
|
+
# Type annotations for attributes
|
|
13
|
+
enable_sample_data: bool
|
|
14
|
+
delay_seconds: float
|
|
15
|
+
|
|
16
|
+
def __init__(self, **kwargs: Any) -> None:
|
|
17
|
+
"""Initialize with configuration.
|
|
18
|
+
|
|
19
|
+
Args
|
|
20
|
+
----
|
|
21
|
+
**kwargs: Configuration options (enable_sample_data, delay_seconds)
|
|
22
|
+
"""
|
|
23
|
+
self.enable_sample_data = kwargs.get("enable_sample_data", True)
|
|
24
|
+
self.delay_seconds = kwargs.get("delay_seconds", 0.0)
|
|
25
|
+
|
|
26
|
+
if not self.enable_sample_data:
|
|
27
|
+
self._table_schemas: dict[str, dict[str, Any]] = {}
|
|
28
|
+
self._relationships: list[dict[str, Any]] = []
|
|
29
|
+
self._indexes: list[dict[str, Any]] = []
|
|
30
|
+
self._table_statistics: dict[str, dict[str, Any]] = {}
|
|
31
|
+
return
|
|
32
|
+
|
|
33
|
+
self._table_schemas = {
|
|
34
|
+
"customers": {
|
|
35
|
+
"table_name": "customers",
|
|
36
|
+
"columns": {
|
|
37
|
+
"id": "INTEGER PRIMARY KEY",
|
|
38
|
+
"customer_name": "VARCHAR(255)",
|
|
39
|
+
"email": "VARCHAR(255)",
|
|
40
|
+
"segment": "VARCHAR(50)",
|
|
41
|
+
"created_date": "TIMESTAMP",
|
|
42
|
+
"status": "VARCHAR(20)",
|
|
43
|
+
},
|
|
44
|
+
"primary_keys": ["id"],
|
|
45
|
+
"foreign_keys": [],
|
|
46
|
+
},
|
|
47
|
+
"orders": {
|
|
48
|
+
"table_name": "orders",
|
|
49
|
+
"columns": {
|
|
50
|
+
"id": "INTEGER PRIMARY KEY",
|
|
51
|
+
"customer_id": "INTEGER",
|
|
52
|
+
"order_date": "DATE",
|
|
53
|
+
"order_value": "DECIMAL(10,2)",
|
|
54
|
+
"status": "VARCHAR(20)",
|
|
55
|
+
"created_at": "TIMESTAMP",
|
|
56
|
+
},
|
|
57
|
+
"primary_keys": ["id"],
|
|
58
|
+
"foreign_keys": [
|
|
59
|
+
{
|
|
60
|
+
"column": "customer_id",
|
|
61
|
+
"references_table": "customers",
|
|
62
|
+
"references_column": "id",
|
|
63
|
+
}
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
"products": {
|
|
67
|
+
"table_name": "products",
|
|
68
|
+
"columns": {
|
|
69
|
+
"id": "INTEGER PRIMARY KEY",
|
|
70
|
+
"product_name": "VARCHAR(255)",
|
|
71
|
+
"category": "VARCHAR(100)",
|
|
72
|
+
"price": "DECIMAL(8,2)",
|
|
73
|
+
"created_at": "TIMESTAMP",
|
|
74
|
+
},
|
|
75
|
+
"primary_keys": ["id"],
|
|
76
|
+
"foreign_keys": [],
|
|
77
|
+
},
|
|
78
|
+
"order_items": {
|
|
79
|
+
"table_name": "order_items",
|
|
80
|
+
"columns": {
|
|
81
|
+
"id": "INTEGER PRIMARY KEY",
|
|
82
|
+
"order_id": "INTEGER",
|
|
83
|
+
"product_id": "INTEGER",
|
|
84
|
+
"quantity": "INTEGER",
|
|
85
|
+
"unit_price": "DECIMAL(8,2)",
|
|
86
|
+
},
|
|
87
|
+
"primary_keys": ["id"],
|
|
88
|
+
"foreign_keys": [
|
|
89
|
+
{
|
|
90
|
+
"column": "order_id",
|
|
91
|
+
"references_table": "orders",
|
|
92
|
+
"references_column": "id",
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"column": "product_id",
|
|
96
|
+
"references_table": "products",
|
|
97
|
+
"references_column": "id",
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
},
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
self._relationships = [
|
|
104
|
+
{
|
|
105
|
+
"from_table": "orders",
|
|
106
|
+
"from_column": "customer_id",
|
|
107
|
+
"to_table": "customers",
|
|
108
|
+
"to_column": "id",
|
|
109
|
+
"relationship_type": "many_to_one",
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"from_table": "order_items",
|
|
113
|
+
"from_column": "order_id",
|
|
114
|
+
"to_table": "orders",
|
|
115
|
+
"to_column": "id",
|
|
116
|
+
"relationship_type": "many_to_one",
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"from_table": "order_items",
|
|
120
|
+
"from_column": "product_id",
|
|
121
|
+
"to_table": "products",
|
|
122
|
+
"to_column": "id",
|
|
123
|
+
"relationship_type": "many_to_one",
|
|
124
|
+
},
|
|
125
|
+
]
|
|
126
|
+
|
|
127
|
+
self._indexes = [
|
|
128
|
+
{
|
|
129
|
+
"index_name": "idx_customers_email",
|
|
130
|
+
"table_name": "customers",
|
|
131
|
+
"columns": ["email"],
|
|
132
|
+
"index_type": "btree",
|
|
133
|
+
"is_unique": True,
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"index_name": "idx_orders_customer_id",
|
|
137
|
+
"table_name": "orders",
|
|
138
|
+
"columns": ["customer_id"],
|
|
139
|
+
"index_type": "btree",
|
|
140
|
+
"is_unique": False,
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"index_name": "idx_orders_date",
|
|
144
|
+
"table_name": "orders",
|
|
145
|
+
"columns": ["order_date"],
|
|
146
|
+
"index_type": "btree",
|
|
147
|
+
"is_unique": False,
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
"index_name": "idx_order_items_order_id",
|
|
151
|
+
"table_name": "order_items",
|
|
152
|
+
"columns": ["order_id"],
|
|
153
|
+
"index_type": "btree",
|
|
154
|
+
"is_unique": False,
|
|
155
|
+
},
|
|
156
|
+
]
|
|
157
|
+
|
|
158
|
+
self._table_statistics = {
|
|
159
|
+
"customers": {
|
|
160
|
+
"row_count": 10000,
|
|
161
|
+
"size_bytes": 2048000,
|
|
162
|
+
"last_updated": "2024-01-15T10:30:00Z",
|
|
163
|
+
},
|
|
164
|
+
"orders": {
|
|
165
|
+
"row_count": 50000,
|
|
166
|
+
"size_bytes": 8192000,
|
|
167
|
+
"last_updated": "2024-01-15T11:45:00Z",
|
|
168
|
+
},
|
|
169
|
+
"products": {
|
|
170
|
+
"row_count": 1000,
|
|
171
|
+
"size_bytes": 512000,
|
|
172
|
+
"last_updated": "2024-01-10T09:00:00Z",
|
|
173
|
+
},
|
|
174
|
+
"order_items": {
|
|
175
|
+
"row_count": 150000,
|
|
176
|
+
"size_bytes": 12288000,
|
|
177
|
+
"last_updated": "2024-01-15T11:45:00Z",
|
|
178
|
+
},
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
# Required methods from DatabasePort
|
|
182
|
+
async def aget_table_schemas(self) -> dict[str, dict[str, Any]]:
|
|
183
|
+
"""Get schema information for all tables."""
|
|
184
|
+
if self.delay_seconds > 0:
|
|
185
|
+
await asyncio.sleep(self.delay_seconds)
|
|
186
|
+
return self._table_schemas.copy()
|
|
187
|
+
|
|
188
|
+
async def aexecute_query(
|
|
189
|
+
self, query: str, params: dict[str, Any] | None = None
|
|
190
|
+
) -> list[dict[str, Any]]:
|
|
191
|
+
"""Execute a SQL query and return results."""
|
|
192
|
+
if self.delay_seconds > 0:
|
|
193
|
+
await asyncio.sleep(self.delay_seconds)
|
|
194
|
+
|
|
195
|
+
# Mock implementation - returns sample data based on query keywords
|
|
196
|
+
if "customers" in query.lower():
|
|
197
|
+
return [
|
|
198
|
+
{"id": 1, "customer_name": "John Doe", "segment": "Premium"},
|
|
199
|
+
{"id": 2, "customer_name": "Jane Smith", "segment": "Standard"},
|
|
200
|
+
]
|
|
201
|
+
if "orders" in query.lower():
|
|
202
|
+
return [
|
|
203
|
+
{"id": 101, "customer_id": 1, "order_value": 299.99},
|
|
204
|
+
{"id": 102, "customer_id": 2, "order_value": 149.50},
|
|
205
|
+
]
|
|
206
|
+
return []
|
|
207
|
+
|
|
208
|
+
# Optional methods from DatabasePort
|
|
209
|
+
async def aget_relationships(self) -> list[dict[str, Any]]:
|
|
210
|
+
"""Get foreign key relationships between tables."""
|
|
211
|
+
if self.delay_seconds > 0:
|
|
212
|
+
await asyncio.sleep(self.delay_seconds)
|
|
213
|
+
return self._relationships.copy()
|
|
214
|
+
|
|
215
|
+
async def aget_indexes(self) -> list[dict[str, Any]]:
|
|
216
|
+
"""Get index information for performance optimization."""
|
|
217
|
+
if self.delay_seconds > 0:
|
|
218
|
+
await asyncio.sleep(self.delay_seconds)
|
|
219
|
+
return self._indexes.copy()
|
|
220
|
+
|
|
221
|
+
async def aget_table_statistics(self) -> dict[str, dict[str, Any]]:
|
|
222
|
+
"""Get table statistics for query optimization."""
|
|
223
|
+
if self.delay_seconds > 0:
|
|
224
|
+
await asyncio.sleep(self.delay_seconds)
|
|
225
|
+
return self._table_statistics.copy()
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
"""Mock Embedding implementation for testing purposes."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import hashlib
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
|
+
|
|
7
|
+
from hexdag.core.ports.embedding import ImageInput
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from hexdag.core.ports.healthcheck import HealthStatus
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class MockEmbedding:
|
|
14
|
+
"""Mock implementation of the Embedding interface for testing.
|
|
15
|
+
|
|
16
|
+
This mock generates deterministic embeddings based on the input text's hash,
|
|
17
|
+
making tests predictable and reproducible. It also provides utilities for
|
|
18
|
+
testing like call inspection and configurable delays.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
# Type annotations for attributes
|
|
22
|
+
delay_seconds: float
|
|
23
|
+
dimensions: int
|
|
24
|
+
call_count: int
|
|
25
|
+
last_texts: list[str]
|
|
26
|
+
last_images: list[ImageInput]
|
|
27
|
+
should_raise: bool
|
|
28
|
+
|
|
29
|
+
def __init__(self, **kwargs: Any) -> None:
|
|
30
|
+
"""Initialize with configuration.
|
|
31
|
+
|
|
32
|
+
Args
|
|
33
|
+
----
|
|
34
|
+
**kwargs: Configuration options
|
|
35
|
+
- dimensions: Embedding vector dimensions (default: 1536)
|
|
36
|
+
- delay_seconds: Delay before returning embeddings (default: 0.0)
|
|
37
|
+
"""
|
|
38
|
+
self.delay_seconds = kwargs.get("delay_seconds", 0.0)
|
|
39
|
+
self.dimensions = kwargs.get("dimensions", 1536)
|
|
40
|
+
|
|
41
|
+
# Non-config state
|
|
42
|
+
self.call_count = 0
|
|
43
|
+
self.last_texts: list[str] = []
|
|
44
|
+
self.last_images: list[ImageInput] = []
|
|
45
|
+
self.should_raise = False
|
|
46
|
+
|
|
47
|
+
def _generate_embedding(self, text: str) -> list[float]:
|
|
48
|
+
"""Generate a deterministic embedding vector from text.
|
|
49
|
+
|
|
50
|
+
Uses text hash to generate consistent embeddings for the same input.
|
|
51
|
+
"""
|
|
52
|
+
# Use hash to generate deterministic values (not for security)
|
|
53
|
+
text_hash = hashlib.md5(text.encode(), usedforsecurity=False).hexdigest() # noqa: S324
|
|
54
|
+
|
|
55
|
+
# Generate embedding values from hash
|
|
56
|
+
embedding = []
|
|
57
|
+
for i in range(self.dimensions):
|
|
58
|
+
# Use chunks of the hash to generate values
|
|
59
|
+
chunk_idx = (i * 4) % len(text_hash)
|
|
60
|
+
chunk = text_hash[chunk_idx : chunk_idx + 4]
|
|
61
|
+
# Convert to float in range [-1, 1]
|
|
62
|
+
value = (int(chunk, 16) / 65535.0) * 2 - 1
|
|
63
|
+
embedding.append(value)
|
|
64
|
+
|
|
65
|
+
return embedding
|
|
66
|
+
|
|
67
|
+
def _generate_image_embedding(self, image: ImageInput) -> list[float]:
|
|
68
|
+
"""Generate a deterministic embedding vector from image.
|
|
69
|
+
|
|
70
|
+
Uses image data hash to generate consistent embeddings for the same input.
|
|
71
|
+
"""
|
|
72
|
+
# Convert image to bytes for hashing
|
|
73
|
+
image_bytes = image.encode() if isinstance(image, str) else image
|
|
74
|
+
|
|
75
|
+
# Use hash to generate deterministic values (not for security)
|
|
76
|
+
image_hash = hashlib.md5(image_bytes, usedforsecurity=False).hexdigest() # noqa: S324
|
|
77
|
+
|
|
78
|
+
# Generate embedding values from hash
|
|
79
|
+
embedding = []
|
|
80
|
+
for i in range(self.dimensions):
|
|
81
|
+
# Use chunks of the hash to generate values
|
|
82
|
+
chunk_idx = (i * 4) % len(image_hash)
|
|
83
|
+
chunk = image_hash[chunk_idx : chunk_idx + 4]
|
|
84
|
+
# Convert to float in range [-1, 1]
|
|
85
|
+
value = (int(chunk, 16) / 65535.0) * 2 - 1
|
|
86
|
+
embedding.append(value)
|
|
87
|
+
|
|
88
|
+
return embedding
|
|
89
|
+
|
|
90
|
+
async def aembed(self, text: str) -> list[float]:
|
|
91
|
+
"""Generate embedding vector for a single text input.
|
|
92
|
+
|
|
93
|
+
Parameters
|
|
94
|
+
----------
|
|
95
|
+
text : str
|
|
96
|
+
Text string to embed
|
|
97
|
+
|
|
98
|
+
Returns
|
|
99
|
+
-------
|
|
100
|
+
list[float]
|
|
101
|
+
Mock embedding vector
|
|
102
|
+
|
|
103
|
+
Raises
|
|
104
|
+
------
|
|
105
|
+
Exception
|
|
106
|
+
When should_raise is True for testing error conditions
|
|
107
|
+
"""
|
|
108
|
+
self.last_texts = [text]
|
|
109
|
+
|
|
110
|
+
if self.delay_seconds > 0:
|
|
111
|
+
await asyncio.sleep(self.delay_seconds)
|
|
112
|
+
|
|
113
|
+
if self.should_raise:
|
|
114
|
+
raise Exception("Mock Embedding error for testing")
|
|
115
|
+
|
|
116
|
+
self.call_count += 1
|
|
117
|
+
return self._generate_embedding(text)
|
|
118
|
+
|
|
119
|
+
async def aembed_batch(self, texts: list[str]) -> list[list[float]]:
|
|
120
|
+
"""Generate embeddings for multiple texts.
|
|
121
|
+
|
|
122
|
+
Parameters
|
|
123
|
+
----------
|
|
124
|
+
texts : list[str]
|
|
125
|
+
List of text strings to embed
|
|
126
|
+
|
|
127
|
+
Returns
|
|
128
|
+
-------
|
|
129
|
+
list[list[float]]
|
|
130
|
+
List of mock embedding vectors
|
|
131
|
+
|
|
132
|
+
Raises
|
|
133
|
+
------
|
|
134
|
+
Exception
|
|
135
|
+
When should_raise is True for testing error conditions
|
|
136
|
+
"""
|
|
137
|
+
self.last_texts = texts
|
|
138
|
+
|
|
139
|
+
if self.delay_seconds > 0:
|
|
140
|
+
await asyncio.sleep(self.delay_seconds)
|
|
141
|
+
|
|
142
|
+
if self.should_raise:
|
|
143
|
+
raise Exception("Mock Embedding batch error for testing")
|
|
144
|
+
|
|
145
|
+
self.call_count += len(texts)
|
|
146
|
+
return [self._generate_embedding(text) for text in texts]
|
|
147
|
+
|
|
148
|
+
async def aembed_image(self, image: ImageInput) -> list[float]:
|
|
149
|
+
"""Generate embedding vector for a single image input.
|
|
150
|
+
|
|
151
|
+
Parameters
|
|
152
|
+
----------
|
|
153
|
+
image : ImageInput
|
|
154
|
+
Image to embed (file path, base64 string, or bytes)
|
|
155
|
+
|
|
156
|
+
Returns
|
|
157
|
+
-------
|
|
158
|
+
list[float]
|
|
159
|
+
Mock embedding vector
|
|
160
|
+
|
|
161
|
+
Raises
|
|
162
|
+
------
|
|
163
|
+
Exception
|
|
164
|
+
When should_raise is True for testing error conditions
|
|
165
|
+
"""
|
|
166
|
+
self.last_images = [image]
|
|
167
|
+
|
|
168
|
+
if self.delay_seconds > 0:
|
|
169
|
+
await asyncio.sleep(self.delay_seconds)
|
|
170
|
+
|
|
171
|
+
if self.should_raise:
|
|
172
|
+
raise Exception("Mock Image Embedding error for testing")
|
|
173
|
+
|
|
174
|
+
self.call_count += 1
|
|
175
|
+
return self._generate_image_embedding(image)
|
|
176
|
+
|
|
177
|
+
async def aembed_image_batch(self, images: list[ImageInput]) -> list[list[float]]:
|
|
178
|
+
"""Generate embeddings for multiple images.
|
|
179
|
+
|
|
180
|
+
Parameters
|
|
181
|
+
----------
|
|
182
|
+
images : list[ImageInput]
|
|
183
|
+
List of images to embed (file paths, base64 strings, or bytes)
|
|
184
|
+
|
|
185
|
+
Returns
|
|
186
|
+
-------
|
|
187
|
+
list[list[float]]
|
|
188
|
+
List of mock embedding vectors
|
|
189
|
+
|
|
190
|
+
Raises
|
|
191
|
+
------
|
|
192
|
+
Exception
|
|
193
|
+
When should_raise is True for testing error conditions
|
|
194
|
+
"""
|
|
195
|
+
self.last_images = images
|
|
196
|
+
|
|
197
|
+
if self.delay_seconds > 0:
|
|
198
|
+
await asyncio.sleep(self.delay_seconds)
|
|
199
|
+
|
|
200
|
+
if self.should_raise:
|
|
201
|
+
raise Exception("Mock Image Embedding batch error for testing")
|
|
202
|
+
|
|
203
|
+
self.call_count += len(images)
|
|
204
|
+
return [self._generate_image_embedding(image) for image in images]
|
|
205
|
+
|
|
206
|
+
async def ahealth_check(self) -> "HealthStatus":
|
|
207
|
+
"""Health check for Mock Embedding (always healthy)."""
|
|
208
|
+
from hexdag.core.ports.healthcheck import HealthStatus
|
|
209
|
+
|
|
210
|
+
return HealthStatus(
|
|
211
|
+
status="healthy",
|
|
212
|
+
adapter_name="MockEmbedding",
|
|
213
|
+
latency_ms=0.1,
|
|
214
|
+
details={"dimensions": self.dimensions},
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
# Testing utilities (not part of the Embedding port interface)
|
|
218
|
+
def reset(self) -> None:
|
|
219
|
+
"""Reset the mock state for testing."""
|
|
220
|
+
self.call_count = 0
|
|
221
|
+
self.last_texts = []
|
|
222
|
+
self.last_images = []
|
|
223
|
+
self.should_raise = False
|