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,252 @@
|
|
|
1
|
+
"""Generate typed stubs from YAML pipelines for IDE autocomplete support."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Annotated, Any
|
|
5
|
+
|
|
6
|
+
import typer
|
|
7
|
+
from rich.console import Console
|
|
8
|
+
from rich.table import Table
|
|
9
|
+
|
|
10
|
+
app = typer.Typer()
|
|
11
|
+
console = Console()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _python_type_to_str(annotation: Any) -> str:
|
|
15
|
+
"""Convert a Python type annotation to a string representation.
|
|
16
|
+
|
|
17
|
+
Args
|
|
18
|
+
----
|
|
19
|
+
annotation: The type annotation to convert
|
|
20
|
+
|
|
21
|
+
Returns
|
|
22
|
+
-------
|
|
23
|
+
String representation of the type
|
|
24
|
+
"""
|
|
25
|
+
if annotation is None:
|
|
26
|
+
return "Any"
|
|
27
|
+
|
|
28
|
+
# Handle None type
|
|
29
|
+
if annotation is type(None):
|
|
30
|
+
return "None"
|
|
31
|
+
|
|
32
|
+
# Handle string annotations (forward references)
|
|
33
|
+
if isinstance(annotation, str):
|
|
34
|
+
return annotation
|
|
35
|
+
|
|
36
|
+
# Get the type name
|
|
37
|
+
origin = getattr(annotation, "__origin__", None)
|
|
38
|
+
args = getattr(annotation, "__args__", ())
|
|
39
|
+
|
|
40
|
+
# Handle Union types (X | Y)
|
|
41
|
+
is_union_type = (
|
|
42
|
+
hasattr(annotation, "__class__") and annotation.__class__.__name__ == "UnionType"
|
|
43
|
+
)
|
|
44
|
+
if origin is type(None) or is_union_type:
|
|
45
|
+
# Python 3.10+ union syntax
|
|
46
|
+
if hasattr(annotation, "__args__"):
|
|
47
|
+
arg_strs = [_python_type_to_str(arg) for arg in annotation.__args__]
|
|
48
|
+
return " | ".join(arg_strs)
|
|
49
|
+
return "Any"
|
|
50
|
+
|
|
51
|
+
# Handle typing.Union
|
|
52
|
+
if origin is not None:
|
|
53
|
+
origin_name = getattr(origin, "__name__", str(origin))
|
|
54
|
+
if origin_name == "Union":
|
|
55
|
+
arg_strs = [_python_type_to_str(arg) for arg in args]
|
|
56
|
+
return " | ".join(arg_strs)
|
|
57
|
+
|
|
58
|
+
# Handle generic types (list[str], dict[str, int], etc.)
|
|
59
|
+
if args:
|
|
60
|
+
arg_strs = [_python_type_to_str(arg) for arg in args]
|
|
61
|
+
return f"{origin_name}[{', '.join(arg_strs)}]"
|
|
62
|
+
return origin_name
|
|
63
|
+
|
|
64
|
+
# Handle basic types
|
|
65
|
+
if hasattr(annotation, "__name__"):
|
|
66
|
+
return annotation.__name__
|
|
67
|
+
|
|
68
|
+
# Fallback
|
|
69
|
+
return str(annotation).replace("typing.", "").replace("<class '", "").replace("'>", "")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _export_model_stub(model_name: str, fields: dict[str, Any], output_path: Path) -> None:
|
|
73
|
+
"""Generate a .pyi stub file for a Pydantic model.
|
|
74
|
+
|
|
75
|
+
Args
|
|
76
|
+
----
|
|
77
|
+
model_name: Name of the model class
|
|
78
|
+
fields: Dictionary of field names to their type annotations
|
|
79
|
+
output_path: Path to write the stub file
|
|
80
|
+
"""
|
|
81
|
+
from datetime import datetime as dt_type
|
|
82
|
+
|
|
83
|
+
# Determine which imports are needed
|
|
84
|
+
needs_datetime = False
|
|
85
|
+
for field_type in fields.values():
|
|
86
|
+
is_datetime = hasattr(field_type, "__name__") and field_type.__name__ == "datetime"
|
|
87
|
+
if field_type is dt_type or is_datetime:
|
|
88
|
+
needs_datetime = True
|
|
89
|
+
break
|
|
90
|
+
|
|
91
|
+
lines = [
|
|
92
|
+
"# Auto-generated type stub from hexdag pipeline",
|
|
93
|
+
"# Do not edit manually - regenerate with: hexdag generate-types <yaml>",
|
|
94
|
+
"",
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
if needs_datetime:
|
|
98
|
+
lines.append("from datetime import datetime")
|
|
99
|
+
lines.append("from typing import Any")
|
|
100
|
+
lines.append("from pydantic import BaseModel")
|
|
101
|
+
lines.append("")
|
|
102
|
+
lines.append("")
|
|
103
|
+
lines.append(f"class {model_name}(BaseModel):")
|
|
104
|
+
|
|
105
|
+
if not fields:
|
|
106
|
+
lines.append(" pass")
|
|
107
|
+
else:
|
|
108
|
+
for field_name, field_type in fields.items():
|
|
109
|
+
type_str = _python_type_to_str(field_type)
|
|
110
|
+
lines.append(f" {field_name}: {type_str}")
|
|
111
|
+
|
|
112
|
+
lines.append("") # Trailing newline
|
|
113
|
+
output_path.write_text("\n".join(lines))
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _infer_type_from_mapping(source_path: str) -> Any:
|
|
117
|
+
"""Infer a type from a mapping source path.
|
|
118
|
+
|
|
119
|
+
Args
|
|
120
|
+
----
|
|
121
|
+
source_path: The source path string (e.g., "$input.name", "node.field")
|
|
122
|
+
|
|
123
|
+
Returns
|
|
124
|
+
-------
|
|
125
|
+
Inferred type (defaults to Any)
|
|
126
|
+
"""
|
|
127
|
+
# Check for expression patterns that have known return types
|
|
128
|
+
from hexdag.core.expression_parser import ALLOWED_FUNCTIONS
|
|
129
|
+
|
|
130
|
+
for func_name in ALLOWED_FUNCTIONS:
|
|
131
|
+
if f"{func_name}(" in source_path:
|
|
132
|
+
# Infer return type from function
|
|
133
|
+
if func_name in {"len", "int", "abs", "round"}:
|
|
134
|
+
return int
|
|
135
|
+
if func_name in {"float", "sum"}:
|
|
136
|
+
return float
|
|
137
|
+
if func_name in {"str", "upper", "lower", "strip", "join"}:
|
|
138
|
+
return str
|
|
139
|
+
if func_name in {"bool", "all", "any"}:
|
|
140
|
+
return bool
|
|
141
|
+
if func_name in {"list", "sorted", "split"}:
|
|
142
|
+
return list
|
|
143
|
+
if func_name in {"now", "utcnow"}:
|
|
144
|
+
from datetime import datetime
|
|
145
|
+
|
|
146
|
+
return datetime
|
|
147
|
+
return Any
|
|
148
|
+
|
|
149
|
+
# Default to Any for simple field paths
|
|
150
|
+
return Any
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
@app.command()
|
|
154
|
+
def generate_types(
|
|
155
|
+
yaml_path: Annotated[
|
|
156
|
+
Path,
|
|
157
|
+
typer.Argument(
|
|
158
|
+
help="Path to YAML pipeline file",
|
|
159
|
+
exists=True,
|
|
160
|
+
file_okay=True,
|
|
161
|
+
dir_okay=False,
|
|
162
|
+
readable=True,
|
|
163
|
+
),
|
|
164
|
+
],
|
|
165
|
+
output_dir: Annotated[
|
|
166
|
+
Path,
|
|
167
|
+
typer.Option(
|
|
168
|
+
"--output-dir",
|
|
169
|
+
"-o",
|
|
170
|
+
help="Output directory for stub files (default: current directory)",
|
|
171
|
+
),
|
|
172
|
+
] = Path(),
|
|
173
|
+
prefix: Annotated[
|
|
174
|
+
str,
|
|
175
|
+
typer.Option(
|
|
176
|
+
"--prefix",
|
|
177
|
+
"-p",
|
|
178
|
+
help="Prefix for generated file names",
|
|
179
|
+
),
|
|
180
|
+
] = "",
|
|
181
|
+
) -> None:
|
|
182
|
+
"""Generate typed stubs from YAML pipeline definitions.
|
|
183
|
+
|
|
184
|
+
This command analyzes YAML pipelines and generates .pyi stub files
|
|
185
|
+
for nodes with input_mapping. This enables IDE autocomplete for
|
|
186
|
+
the input_data parameter in node functions.
|
|
187
|
+
|
|
188
|
+
Examples
|
|
189
|
+
--------
|
|
190
|
+
hexdag generate-types pipeline.yaml
|
|
191
|
+
hexdag generate-types pipeline.yaml -o ./types
|
|
192
|
+
hexdag generate-types pipeline.yaml --prefix pipeline_
|
|
193
|
+
"""
|
|
194
|
+
import yaml
|
|
195
|
+
|
|
196
|
+
# Read YAML file
|
|
197
|
+
try:
|
|
198
|
+
with Path.open(yaml_path) as f:
|
|
199
|
+
config = yaml.safe_load(f)
|
|
200
|
+
except yaml.YAMLError as e:
|
|
201
|
+
console.print(f"[red]Error:[/red] Invalid YAML syntax: {e}")
|
|
202
|
+
raise typer.Exit(1) from e
|
|
203
|
+
except OSError as e:
|
|
204
|
+
console.print(f"[red]Error:[/red] Cannot read file: {e}")
|
|
205
|
+
raise typer.Exit(1) from e
|
|
206
|
+
|
|
207
|
+
# Ensure output directory exists
|
|
208
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
209
|
+
|
|
210
|
+
# Extract nodes with input_mapping
|
|
211
|
+
nodes = config.get("spec", {}).get("nodes", [])
|
|
212
|
+
generated_files: list[tuple[str, Path]] = []
|
|
213
|
+
|
|
214
|
+
for node in nodes:
|
|
215
|
+
node_name = node.get("metadata", {}).get("name", "")
|
|
216
|
+
spec = node.get("spec", {})
|
|
217
|
+
input_mapping = spec.get("input_mapping")
|
|
218
|
+
|
|
219
|
+
if not input_mapping or not node_name:
|
|
220
|
+
continue
|
|
221
|
+
|
|
222
|
+
# Create model name
|
|
223
|
+
model_name = f"{node_name.replace('-', '_').title().replace('_', '')}Input"
|
|
224
|
+
|
|
225
|
+
# Infer types from mapping
|
|
226
|
+
fields: dict[str, Any] = {}
|
|
227
|
+
for target_field, source_path in input_mapping.items():
|
|
228
|
+
fields[target_field] = _infer_type_from_mapping(source_path)
|
|
229
|
+
|
|
230
|
+
# Generate stub file
|
|
231
|
+
file_name = f"{prefix}{node_name.replace('-', '_')}_types.pyi"
|
|
232
|
+
stub_path = output_dir / file_name
|
|
233
|
+
|
|
234
|
+
_export_model_stub(model_name, fields, stub_path)
|
|
235
|
+
generated_files.append((node_name, stub_path))
|
|
236
|
+
|
|
237
|
+
# Display results
|
|
238
|
+
if not generated_files:
|
|
239
|
+
console.print("[yellow]No nodes with input_mapping found in pipeline.[/yellow]")
|
|
240
|
+
raise typer.Exit(0)
|
|
241
|
+
|
|
242
|
+
console.print(f"\n[green]Generated {len(generated_files)} stub file(s):[/green]\n")
|
|
243
|
+
|
|
244
|
+
table = Table(show_header=True, border_style="green")
|
|
245
|
+
table.add_column("Node", style="cyan")
|
|
246
|
+
table.add_column("Stub File", style="white")
|
|
247
|
+
|
|
248
|
+
for node_name, stub_path in generated_files:
|
|
249
|
+
table.add_row(node_name, str(stub_path))
|
|
250
|
+
|
|
251
|
+
console.print(table)
|
|
252
|
+
console.print()
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"""Initialize command for HexDAG CLI."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Annotated, Any
|
|
5
|
+
|
|
6
|
+
import typer
|
|
7
|
+
from rich.console import Console
|
|
8
|
+
from rich.prompt import Confirm
|
|
9
|
+
|
|
10
|
+
app = typer.Typer()
|
|
11
|
+
console = Console()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@app.callback(invoke_without_command=True)
|
|
15
|
+
def init(
|
|
16
|
+
ctx: typer.Context,
|
|
17
|
+
with_adapters: Annotated[
|
|
18
|
+
str | None,
|
|
19
|
+
typer.Option(
|
|
20
|
+
"--with",
|
|
21
|
+
help="Comma-separated list of adapters to include (e.g., openai,anthropic,local)",
|
|
22
|
+
),
|
|
23
|
+
] = None,
|
|
24
|
+
force: Annotated[
|
|
25
|
+
bool,
|
|
26
|
+
typer.Option(
|
|
27
|
+
"--force",
|
|
28
|
+
"-f",
|
|
29
|
+
help="Overwrite existing configuration",
|
|
30
|
+
),
|
|
31
|
+
] = False,
|
|
32
|
+
path: Annotated[
|
|
33
|
+
Path | None,
|
|
34
|
+
typer.Argument(
|
|
35
|
+
help="Directory to initialize (defaults to current directory)",
|
|
36
|
+
),
|
|
37
|
+
] = None,
|
|
38
|
+
) -> None:
|
|
39
|
+
"""Initialize a new HexDAG project with configuration."""
|
|
40
|
+
if ctx.invoked_subcommand is not None:
|
|
41
|
+
return
|
|
42
|
+
|
|
43
|
+
if path is None:
|
|
44
|
+
path = Path.cwd()
|
|
45
|
+
|
|
46
|
+
config_path = path / "hexdag.toml"
|
|
47
|
+
|
|
48
|
+
if (
|
|
49
|
+
config_path.exists()
|
|
50
|
+
and not force
|
|
51
|
+
and not Confirm.ask(f"[yellow]hexdag.toml already exists in {path}. Overwrite?[/yellow]")
|
|
52
|
+
):
|
|
53
|
+
console.print("[red]Initialization cancelled.[/red]")
|
|
54
|
+
raise typer.Exit(1)
|
|
55
|
+
|
|
56
|
+
# Parse adapters
|
|
57
|
+
adapters = []
|
|
58
|
+
if with_adapters:
|
|
59
|
+
adapters = [a.strip() for a in with_adapters.split(",")]
|
|
60
|
+
|
|
61
|
+
# Generate configuration
|
|
62
|
+
config_content = _generate_config(adapters)
|
|
63
|
+
|
|
64
|
+
# Ensure directory exists
|
|
65
|
+
path.mkdir(parents=True, exist_ok=True)
|
|
66
|
+
|
|
67
|
+
# Write configuration
|
|
68
|
+
config_path.write_text(config_content)
|
|
69
|
+
console.print(f"[green]✓[/green] Created hexdag.toml in {path}")
|
|
70
|
+
|
|
71
|
+
# Show next steps
|
|
72
|
+
console.print("\n[bold]Next steps:[/bold]")
|
|
73
|
+
console.print("1. Review and customize hexdag.toml")
|
|
74
|
+
console.print("2. Set any required environment variables")
|
|
75
|
+
|
|
76
|
+
if adapters:
|
|
77
|
+
console.print("\n[bold]Adapters configured:[/bold]")
|
|
78
|
+
for adapter in adapters:
|
|
79
|
+
_print_adapter_info(adapter)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _generate_config(adapters: list[str]) -> str:
|
|
83
|
+
"""Generate hexdag.toml configuration."""
|
|
84
|
+
lines = [
|
|
85
|
+
"# HexDAG Configuration",
|
|
86
|
+
"# Generated by: hexdag init",
|
|
87
|
+
"",
|
|
88
|
+
"# Core modules to load",
|
|
89
|
+
"modules = [",
|
|
90
|
+
' "hexdag.core.ports",',
|
|
91
|
+
' "hexdag.builtin.nodes",',
|
|
92
|
+
"]",
|
|
93
|
+
"",
|
|
94
|
+
"# Plugins to load",
|
|
95
|
+
"plugins = [",
|
|
96
|
+
' "hexdag.builtin.adapters.local", # Local in-process adapters',
|
|
97
|
+
]
|
|
98
|
+
|
|
99
|
+
lines.append(' "hexdag.adapters.mock", # Mock adapters for testing')
|
|
100
|
+
|
|
101
|
+
if "openai" in adapters:
|
|
102
|
+
lines.append(' # "hexdag.adapters.openai", # OpenAI adapter (requires api key)')
|
|
103
|
+
if "anthropic" in adapters:
|
|
104
|
+
lines.append(' # "hexdag.adapters.anthropic", # Anthropic adapter (requires api key)')
|
|
105
|
+
|
|
106
|
+
lines.extend([
|
|
107
|
+
"]",
|
|
108
|
+
"",
|
|
109
|
+
"# Development mode",
|
|
110
|
+
"dev_mode = true",
|
|
111
|
+
"",
|
|
112
|
+
"[settings]",
|
|
113
|
+
'log_level = "INFO"',
|
|
114
|
+
"enable_metrics = true",
|
|
115
|
+
])
|
|
116
|
+
|
|
117
|
+
if "openai" in adapters:
|
|
118
|
+
lines.extend([
|
|
119
|
+
"",
|
|
120
|
+
"[adapters.openai]",
|
|
121
|
+
"# Set via environment variable or directly",
|
|
122
|
+
'api_key = "${OPENAI_API_KEY}"',
|
|
123
|
+
'model = "gpt-4"',
|
|
124
|
+
"temperature = 0.7",
|
|
125
|
+
"max_tokens = 2000",
|
|
126
|
+
])
|
|
127
|
+
|
|
128
|
+
if "anthropic" in adapters:
|
|
129
|
+
lines.extend([
|
|
130
|
+
"",
|
|
131
|
+
"[adapters.anthropic]",
|
|
132
|
+
"# Set via environment variable or directly",
|
|
133
|
+
'api_key = "${ANTHROPIC_API_KEY}"',
|
|
134
|
+
'model = "claude-3-opus-20240229"',
|
|
135
|
+
"temperature = 0.7",
|
|
136
|
+
"max_tokens = 2000",
|
|
137
|
+
])
|
|
138
|
+
|
|
139
|
+
if "local" in adapters or not adapters:
|
|
140
|
+
lines.extend([
|
|
141
|
+
"",
|
|
142
|
+
"[adapters.local]",
|
|
143
|
+
"# Local adapter settings",
|
|
144
|
+
"memory_max_size = 1000",
|
|
145
|
+
"cache_ttl = 3600",
|
|
146
|
+
])
|
|
147
|
+
|
|
148
|
+
return "\n".join(lines) + "\n"
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _print_adapter_info(adapter: str) -> None:
|
|
152
|
+
"""Print information about an adapter."""
|
|
153
|
+
import shutil
|
|
154
|
+
|
|
155
|
+
# Detect if uv is available
|
|
156
|
+
has_uv = shutil.which("uv") is not None
|
|
157
|
+
prefix = "uv pip install" if has_uv else "pip install"
|
|
158
|
+
|
|
159
|
+
info: dict[str, dict[str, Any]] = {
|
|
160
|
+
"openai": {
|
|
161
|
+
"name": "OpenAI",
|
|
162
|
+
"install": f"{prefix} hexdag[adapters-openai]",
|
|
163
|
+
"env": "OPENAI_API_KEY",
|
|
164
|
+
},
|
|
165
|
+
"anthropic": {
|
|
166
|
+
"name": "Anthropic Claude",
|
|
167
|
+
"install": f"{prefix} hexdag[adapters-anthropic]",
|
|
168
|
+
"env": "ANTHROPIC_API_KEY",
|
|
169
|
+
},
|
|
170
|
+
"mock": {
|
|
171
|
+
"name": "Mock Adapters",
|
|
172
|
+
"install": "Included in core",
|
|
173
|
+
"env": None,
|
|
174
|
+
},
|
|
175
|
+
"local": {
|
|
176
|
+
"name": "Local Adapters",
|
|
177
|
+
"install": "Included in core",
|
|
178
|
+
"env": None,
|
|
179
|
+
},
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if adapter in info:
|
|
183
|
+
details = info[adapter]
|
|
184
|
+
console.print(f" • [cyan]{details['name']}[/cyan]")
|
|
185
|
+
if details["install"] != "Included in core":
|
|
186
|
+
console.print(f" Install: [yellow]{details['install']}[/yellow]")
|
|
187
|
+
if details["env"]:
|
|
188
|
+
console.print(f" Required: [yellow]{details['env']}[/yellow] environment variable")
|