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.
Files changed (261) hide show
  1. hexdag/__init__.py +116 -0
  2. hexdag/__main__.py +30 -0
  3. hexdag/adapters/executors/__init__.py +5 -0
  4. hexdag/adapters/executors/local_executor.py +316 -0
  5. hexdag/builtin/__init__.py +6 -0
  6. hexdag/builtin/adapters/__init__.py +51 -0
  7. hexdag/builtin/adapters/anthropic/__init__.py +5 -0
  8. hexdag/builtin/adapters/anthropic/anthropic_adapter.py +151 -0
  9. hexdag/builtin/adapters/database/__init__.py +6 -0
  10. hexdag/builtin/adapters/database/csv/csv_adapter.py +249 -0
  11. hexdag/builtin/adapters/database/pgvector/__init__.py +5 -0
  12. hexdag/builtin/adapters/database/pgvector/pgvector_adapter.py +478 -0
  13. hexdag/builtin/adapters/database/sqlalchemy/sqlalchemy_adapter.py +252 -0
  14. hexdag/builtin/adapters/database/sqlite/__init__.py +5 -0
  15. hexdag/builtin/adapters/database/sqlite/sqlite_adapter.py +410 -0
  16. hexdag/builtin/adapters/local/README.md +59 -0
  17. hexdag/builtin/adapters/local/__init__.py +7 -0
  18. hexdag/builtin/adapters/local/local_observer_manager.py +696 -0
  19. hexdag/builtin/adapters/memory/__init__.py +47 -0
  20. hexdag/builtin/adapters/memory/file_memory_adapter.py +297 -0
  21. hexdag/builtin/adapters/memory/in_memory_memory.py +216 -0
  22. hexdag/builtin/adapters/memory/schemas.py +57 -0
  23. hexdag/builtin/adapters/memory/session_memory.py +178 -0
  24. hexdag/builtin/adapters/memory/sqlite_memory_adapter.py +215 -0
  25. hexdag/builtin/adapters/memory/state_memory.py +280 -0
  26. hexdag/builtin/adapters/mock/README.md +89 -0
  27. hexdag/builtin/adapters/mock/__init__.py +15 -0
  28. hexdag/builtin/adapters/mock/hexdag.toml +50 -0
  29. hexdag/builtin/adapters/mock/mock_database.py +225 -0
  30. hexdag/builtin/adapters/mock/mock_embedding.py +223 -0
  31. hexdag/builtin/adapters/mock/mock_llm.py +177 -0
  32. hexdag/builtin/adapters/mock/mock_tool_adapter.py +192 -0
  33. hexdag/builtin/adapters/mock/mock_tool_router.py +232 -0
  34. hexdag/builtin/adapters/openai/__init__.py +5 -0
  35. hexdag/builtin/adapters/openai/openai_adapter.py +634 -0
  36. hexdag/builtin/adapters/secret/__init__.py +7 -0
  37. hexdag/builtin/adapters/secret/local_secret_adapter.py +248 -0
  38. hexdag/builtin/adapters/unified_tool_router.py +280 -0
  39. hexdag/builtin/macros/__init__.py +17 -0
  40. hexdag/builtin/macros/conversation_agent.py +390 -0
  41. hexdag/builtin/macros/llm_macro.py +151 -0
  42. hexdag/builtin/macros/reasoning_agent.py +423 -0
  43. hexdag/builtin/macros/tool_macro.py +380 -0
  44. hexdag/builtin/nodes/__init__.py +38 -0
  45. hexdag/builtin/nodes/_discovery.py +123 -0
  46. hexdag/builtin/nodes/agent_node.py +696 -0
  47. hexdag/builtin/nodes/base_node_factory.py +242 -0
  48. hexdag/builtin/nodes/composite_node.py +926 -0
  49. hexdag/builtin/nodes/data_node.py +201 -0
  50. hexdag/builtin/nodes/expression_node.py +487 -0
  51. hexdag/builtin/nodes/function_node.py +454 -0
  52. hexdag/builtin/nodes/llm_node.py +491 -0
  53. hexdag/builtin/nodes/loop_node.py +920 -0
  54. hexdag/builtin/nodes/mapped_input.py +518 -0
  55. hexdag/builtin/nodes/port_call_node.py +269 -0
  56. hexdag/builtin/nodes/tool_call_node.py +195 -0
  57. hexdag/builtin/nodes/tool_utils.py +390 -0
  58. hexdag/builtin/prompts/__init__.py +68 -0
  59. hexdag/builtin/prompts/base.py +422 -0
  60. hexdag/builtin/prompts/chat_prompts.py +303 -0
  61. hexdag/builtin/prompts/error_correction_prompts.py +320 -0
  62. hexdag/builtin/prompts/tool_prompts.py +160 -0
  63. hexdag/builtin/tools/builtin_tools.py +84 -0
  64. hexdag/builtin/tools/database_tools.py +164 -0
  65. hexdag/cli/__init__.py +17 -0
  66. hexdag/cli/__main__.py +7 -0
  67. hexdag/cli/commands/__init__.py +27 -0
  68. hexdag/cli/commands/build_cmd.py +812 -0
  69. hexdag/cli/commands/create_cmd.py +208 -0
  70. hexdag/cli/commands/docs_cmd.py +293 -0
  71. hexdag/cli/commands/generate_types_cmd.py +252 -0
  72. hexdag/cli/commands/init_cmd.py +188 -0
  73. hexdag/cli/commands/pipeline_cmd.py +494 -0
  74. hexdag/cli/commands/plugin_dev_cmd.py +529 -0
  75. hexdag/cli/commands/plugins_cmd.py +441 -0
  76. hexdag/cli/commands/studio_cmd.py +101 -0
  77. hexdag/cli/commands/validate_cmd.py +221 -0
  78. hexdag/cli/main.py +84 -0
  79. hexdag/core/__init__.py +83 -0
  80. hexdag/core/config/__init__.py +20 -0
  81. hexdag/core/config/loader.py +479 -0
  82. hexdag/core/config/models.py +150 -0
  83. hexdag/core/configurable.py +294 -0
  84. hexdag/core/context/__init__.py +37 -0
  85. hexdag/core/context/execution_context.py +378 -0
  86. hexdag/core/docs/__init__.py +26 -0
  87. hexdag/core/docs/extractors.py +678 -0
  88. hexdag/core/docs/generators.py +890 -0
  89. hexdag/core/docs/models.py +120 -0
  90. hexdag/core/domain/__init__.py +10 -0
  91. hexdag/core/domain/dag.py +1225 -0
  92. hexdag/core/exceptions.py +234 -0
  93. hexdag/core/expression_parser.py +569 -0
  94. hexdag/core/logging.py +449 -0
  95. hexdag/core/models/__init__.py +17 -0
  96. hexdag/core/models/base.py +138 -0
  97. hexdag/core/orchestration/__init__.py +46 -0
  98. hexdag/core/orchestration/body_executor.py +481 -0
  99. hexdag/core/orchestration/components/__init__.py +97 -0
  100. hexdag/core/orchestration/components/adapter_lifecycle_manager.py +113 -0
  101. hexdag/core/orchestration/components/checkpoint_manager.py +134 -0
  102. hexdag/core/orchestration/components/execution_coordinator.py +360 -0
  103. hexdag/core/orchestration/components/health_check_manager.py +176 -0
  104. hexdag/core/orchestration/components/input_mapper.py +143 -0
  105. hexdag/core/orchestration/components/lifecycle_manager.py +583 -0
  106. hexdag/core/orchestration/components/node_executor.py +377 -0
  107. hexdag/core/orchestration/components/secret_manager.py +202 -0
  108. hexdag/core/orchestration/components/wave_executor.py +158 -0
  109. hexdag/core/orchestration/constants.py +17 -0
  110. hexdag/core/orchestration/events/README.md +312 -0
  111. hexdag/core/orchestration/events/__init__.py +104 -0
  112. hexdag/core/orchestration/events/batching.py +330 -0
  113. hexdag/core/orchestration/events/decorators.py +139 -0
  114. hexdag/core/orchestration/events/events.py +573 -0
  115. hexdag/core/orchestration/events/observers/__init__.py +30 -0
  116. hexdag/core/orchestration/events/observers/core_observers.py +690 -0
  117. hexdag/core/orchestration/events/observers/models.py +111 -0
  118. hexdag/core/orchestration/events/taxonomy.py +269 -0
  119. hexdag/core/orchestration/hook_context.py +237 -0
  120. hexdag/core/orchestration/hooks.py +437 -0
  121. hexdag/core/orchestration/models.py +418 -0
  122. hexdag/core/orchestration/orchestrator.py +910 -0
  123. hexdag/core/orchestration/orchestrator_factory.py +275 -0
  124. hexdag/core/orchestration/port_wrappers.py +327 -0
  125. hexdag/core/orchestration/prompt/__init__.py +32 -0
  126. hexdag/core/orchestration/prompt/template.py +332 -0
  127. hexdag/core/pipeline_builder/__init__.py +21 -0
  128. hexdag/core/pipeline_builder/component_instantiator.py +386 -0
  129. hexdag/core/pipeline_builder/include_tag.py +265 -0
  130. hexdag/core/pipeline_builder/pipeline_config.py +133 -0
  131. hexdag/core/pipeline_builder/py_tag.py +223 -0
  132. hexdag/core/pipeline_builder/tag_discovery.py +268 -0
  133. hexdag/core/pipeline_builder/yaml_builder.py +1196 -0
  134. hexdag/core/pipeline_builder/yaml_validator.py +569 -0
  135. hexdag/core/ports/__init__.py +65 -0
  136. hexdag/core/ports/api_call.py +133 -0
  137. hexdag/core/ports/database.py +489 -0
  138. hexdag/core/ports/embedding.py +215 -0
  139. hexdag/core/ports/executor.py +237 -0
  140. hexdag/core/ports/file_storage.py +117 -0
  141. hexdag/core/ports/healthcheck.py +87 -0
  142. hexdag/core/ports/llm.py +551 -0
  143. hexdag/core/ports/memory.py +70 -0
  144. hexdag/core/ports/observer_manager.py +130 -0
  145. hexdag/core/ports/secret.py +145 -0
  146. hexdag/core/ports/tool_router.py +94 -0
  147. hexdag/core/ports_builder.py +623 -0
  148. hexdag/core/protocols.py +273 -0
  149. hexdag/core/resolver.py +304 -0
  150. hexdag/core/schema/__init__.py +9 -0
  151. hexdag/core/schema/generator.py +742 -0
  152. hexdag/core/secrets.py +242 -0
  153. hexdag/core/types.py +413 -0
  154. hexdag/core/utils/async_warnings.py +206 -0
  155. hexdag/core/utils/schema_conversion.py +78 -0
  156. hexdag/core/utils/sql_validation.py +86 -0
  157. hexdag/core/validation/secure_json.py +148 -0
  158. hexdag/core/yaml_macro.py +517 -0
  159. hexdag/mcp_server.py +3120 -0
  160. hexdag/studio/__init__.py +10 -0
  161. hexdag/studio/build_ui.py +92 -0
  162. hexdag/studio/server/__init__.py +1 -0
  163. hexdag/studio/server/main.py +100 -0
  164. hexdag/studio/server/routes/__init__.py +9 -0
  165. hexdag/studio/server/routes/execute.py +208 -0
  166. hexdag/studio/server/routes/export.py +558 -0
  167. hexdag/studio/server/routes/files.py +207 -0
  168. hexdag/studio/server/routes/plugins.py +419 -0
  169. hexdag/studio/server/routes/validate.py +220 -0
  170. hexdag/studio/ui/index.html +13 -0
  171. hexdag/studio/ui/package-lock.json +2992 -0
  172. hexdag/studio/ui/package.json +31 -0
  173. hexdag/studio/ui/postcss.config.js +6 -0
  174. hexdag/studio/ui/public/hexdag.svg +5 -0
  175. hexdag/studio/ui/src/App.tsx +251 -0
  176. hexdag/studio/ui/src/components/Canvas.tsx +408 -0
  177. hexdag/studio/ui/src/components/ContextMenu.tsx +187 -0
  178. hexdag/studio/ui/src/components/FileBrowser.tsx +123 -0
  179. hexdag/studio/ui/src/components/Header.tsx +181 -0
  180. hexdag/studio/ui/src/components/HexdagNode.tsx +193 -0
  181. hexdag/studio/ui/src/components/NodeInspector.tsx +512 -0
  182. hexdag/studio/ui/src/components/NodePalette.tsx +262 -0
  183. hexdag/studio/ui/src/components/NodePortsSection.tsx +403 -0
  184. hexdag/studio/ui/src/components/PluginManager.tsx +347 -0
  185. hexdag/studio/ui/src/components/PortsEditor.tsx +481 -0
  186. hexdag/studio/ui/src/components/PythonEditor.tsx +195 -0
  187. hexdag/studio/ui/src/components/ValidationPanel.tsx +105 -0
  188. hexdag/studio/ui/src/components/YamlEditor.tsx +196 -0
  189. hexdag/studio/ui/src/components/index.ts +8 -0
  190. hexdag/studio/ui/src/index.css +92 -0
  191. hexdag/studio/ui/src/main.tsx +10 -0
  192. hexdag/studio/ui/src/types/index.ts +123 -0
  193. hexdag/studio/ui/src/vite-env.d.ts +1 -0
  194. hexdag/studio/ui/tailwind.config.js +29 -0
  195. hexdag/studio/ui/tsconfig.json +37 -0
  196. hexdag/studio/ui/tsconfig.node.json +13 -0
  197. hexdag/studio/ui/vite.config.ts +35 -0
  198. hexdag/visualization/__init__.py +69 -0
  199. hexdag/visualization/dag_visualizer.py +1020 -0
  200. hexdag-0.5.0.dev1.dist-info/METADATA +369 -0
  201. hexdag-0.5.0.dev1.dist-info/RECORD +261 -0
  202. hexdag-0.5.0.dev1.dist-info/WHEEL +4 -0
  203. hexdag-0.5.0.dev1.dist-info/entry_points.txt +4 -0
  204. hexdag-0.5.0.dev1.dist-info/licenses/LICENSE +190 -0
  205. hexdag_plugins/.gitignore +43 -0
  206. hexdag_plugins/README.md +73 -0
  207. hexdag_plugins/__init__.py +1 -0
  208. hexdag_plugins/azure/LICENSE +21 -0
  209. hexdag_plugins/azure/README.md +414 -0
  210. hexdag_plugins/azure/__init__.py +21 -0
  211. hexdag_plugins/azure/azure_blob_adapter.py +450 -0
  212. hexdag_plugins/azure/azure_cosmos_adapter.py +383 -0
  213. hexdag_plugins/azure/azure_keyvault_adapter.py +314 -0
  214. hexdag_plugins/azure/azure_openai_adapter.py +415 -0
  215. hexdag_plugins/azure/pyproject.toml +107 -0
  216. hexdag_plugins/azure/tests/__init__.py +1 -0
  217. hexdag_plugins/azure/tests/test_azure_blob_adapter.py +350 -0
  218. hexdag_plugins/azure/tests/test_azure_cosmos_adapter.py +323 -0
  219. hexdag_plugins/azure/tests/test_azure_keyvault_adapter.py +330 -0
  220. hexdag_plugins/azure/tests/test_azure_openai_adapter.py +329 -0
  221. hexdag_plugins/hexdag_etl/README.md +168 -0
  222. hexdag_plugins/hexdag_etl/__init__.py +53 -0
  223. hexdag_plugins/hexdag_etl/examples/01_simple_pandas_transform.py +270 -0
  224. hexdag_plugins/hexdag_etl/examples/02_simple_pandas_only.py +149 -0
  225. hexdag_plugins/hexdag_etl/examples/03_file_io_pipeline.py +109 -0
  226. hexdag_plugins/hexdag_etl/examples/test_pandas_transform.py +84 -0
  227. hexdag_plugins/hexdag_etl/hexdag.toml +25 -0
  228. hexdag_plugins/hexdag_etl/hexdag_etl/__init__.py +48 -0
  229. hexdag_plugins/hexdag_etl/hexdag_etl/nodes/__init__.py +13 -0
  230. hexdag_plugins/hexdag_etl/hexdag_etl/nodes/api_extract.py +230 -0
  231. hexdag_plugins/hexdag_etl/hexdag_etl/nodes/base_node_factory.py +181 -0
  232. hexdag_plugins/hexdag_etl/hexdag_etl/nodes/file_io.py +415 -0
  233. hexdag_plugins/hexdag_etl/hexdag_etl/nodes/outlook.py +492 -0
  234. hexdag_plugins/hexdag_etl/hexdag_etl/nodes/pandas_transform.py +563 -0
  235. hexdag_plugins/hexdag_etl/hexdag_etl/nodes/sql_extract_load.py +112 -0
  236. hexdag_plugins/hexdag_etl/pyproject.toml +82 -0
  237. hexdag_plugins/hexdag_etl/test_transform.py +54 -0
  238. hexdag_plugins/hexdag_etl/tests/test_plugin_integration.py +62 -0
  239. hexdag_plugins/mysql_adapter/LICENSE +21 -0
  240. hexdag_plugins/mysql_adapter/README.md +224 -0
  241. hexdag_plugins/mysql_adapter/__init__.py +6 -0
  242. hexdag_plugins/mysql_adapter/mysql_adapter.py +408 -0
  243. hexdag_plugins/mysql_adapter/pyproject.toml +93 -0
  244. hexdag_plugins/mysql_adapter/tests/test_mysql_adapter.py +259 -0
  245. hexdag_plugins/storage/README.md +184 -0
  246. hexdag_plugins/storage/__init__.py +19 -0
  247. hexdag_plugins/storage/file/__init__.py +5 -0
  248. hexdag_plugins/storage/file/local.py +325 -0
  249. hexdag_plugins/storage/ports/__init__.py +5 -0
  250. hexdag_plugins/storage/ports/vector_store.py +236 -0
  251. hexdag_plugins/storage/sql/__init__.py +7 -0
  252. hexdag_plugins/storage/sql/base.py +187 -0
  253. hexdag_plugins/storage/sql/mysql.py +27 -0
  254. hexdag_plugins/storage/sql/postgresql.py +27 -0
  255. hexdag_plugins/storage/tests/__init__.py +1 -0
  256. hexdag_plugins/storage/tests/test_local_file_storage.py +161 -0
  257. hexdag_plugins/storage/tests/test_sql_adapters.py +212 -0
  258. hexdag_plugins/storage/vector/__init__.py +7 -0
  259. hexdag_plugins/storage/vector/chromadb.py +223 -0
  260. hexdag_plugins/storage/vector/in_memory.py +285 -0
  261. hexdag_plugins/storage/vector/pgvector.py +502 -0
@@ -0,0 +1,105 @@
1
+ import { CheckCircle, XCircle, AlertTriangle, Info } from 'lucide-react'
2
+ import { useStudioStore } from '../lib/store'
3
+
4
+ export default function ValidationPanel() {
5
+ const { validation } = useStudioStore()
6
+
7
+ if (!validation) {
8
+ return (
9
+ <div className="h-full flex items-center justify-center text-hex-text-muted">
10
+ <div className="text-center">
11
+ <Info size={32} className="mx-auto mb-2 opacity-50" />
12
+ <p className="text-xs">Edit a file to see validation</p>
13
+ </div>
14
+ </div>
15
+ )
16
+ }
17
+
18
+ const errorCount = validation.errors.filter((e) => e.severity === 'error').length
19
+ const warningCount = validation.errors.filter((e) => e.severity === 'warning').length
20
+
21
+ return (
22
+ <div className="h-full flex flex-col">
23
+ {/* Summary */}
24
+ <div className="p-3 border-b border-hex-border flex items-center gap-3">
25
+ {validation.valid ? (
26
+ <div className="flex items-center gap-2 text-hex-success">
27
+ <CheckCircle size={16} />
28
+ <span className="text-xs font-medium">Valid</span>
29
+ </div>
30
+ ) : (
31
+ <div className="flex items-center gap-2 text-hex-error">
32
+ <XCircle size={16} />
33
+ <span className="text-xs font-medium">Invalid</span>
34
+ </div>
35
+ )}
36
+
37
+ {validation.node_count !== undefined && (
38
+ <div className="text-xs text-hex-text-muted">
39
+ {validation.node_count} node{validation.node_count !== 1 ? 's' : ''}
40
+ </div>
41
+ )}
42
+
43
+ <div className="ml-auto flex items-center gap-2 text-xs">
44
+ {errorCount > 0 && (
45
+ <span className="text-hex-error">{errorCount} error{errorCount !== 1 ? 's' : ''}</span>
46
+ )}
47
+ {warningCount > 0 && (
48
+ <span className="text-hex-warning">{warningCount} warning{warningCount !== 1 ? 's' : ''}</span>
49
+ )}
50
+ </div>
51
+ </div>
52
+
53
+ {/* Errors list */}
54
+ <div className="flex-1 overflow-y-auto p-2 space-y-1">
55
+ {validation.errors.length === 0 && validation.valid && (
56
+ <div className="p-3 rounded-md bg-hex-success/10 border border-hex-success/20">
57
+ <div className="flex items-start gap-2">
58
+ <CheckCircle size={14} className="text-hex-success mt-0.5" />
59
+ <div>
60
+ <p className="text-xs text-hex-success font-medium">Pipeline is valid</p>
61
+ {validation.nodes && validation.nodes.length > 0 && (
62
+ <p className="text-[10px] text-hex-text-muted mt-1">
63
+ Execution order: {validation.nodes.join(' → ')}
64
+ </p>
65
+ )}
66
+ </div>
67
+ </div>
68
+ </div>
69
+ )}
70
+
71
+ {validation.errors.map((error, index) => (
72
+ <div
73
+ key={index}
74
+ className={`
75
+ p-2 rounded-md border-l-2
76
+ ${error.severity === 'error'
77
+ ? 'bg-hex-error/10 border-hex-error'
78
+ : error.severity === 'warning'
79
+ ? 'bg-hex-warning/10 border-hex-warning'
80
+ : 'bg-hex-accent/10 border-hex-accent'}
81
+ `}
82
+ >
83
+ <div className="flex items-start gap-2">
84
+ {error.severity === 'error' ? (
85
+ <XCircle size={14} className="text-hex-error mt-0.5 flex-shrink-0" />
86
+ ) : error.severity === 'warning' ? (
87
+ <AlertTriangle size={14} className="text-hex-warning mt-0.5 flex-shrink-0" />
88
+ ) : (
89
+ <Info size={14} className="text-hex-accent mt-0.5 flex-shrink-0" />
90
+ )}
91
+ <div className="flex-1 min-w-0">
92
+ <p className="text-xs text-hex-text break-words">{error.message}</p>
93
+ {error.line && (
94
+ <p className="text-[10px] text-hex-text-muted mt-0.5">
95
+ Line {error.line}{error.column ? `:${error.column}` : ''}
96
+ </p>
97
+ )}
98
+ </div>
99
+ </div>
100
+ </div>
101
+ ))}
102
+ </div>
103
+ </div>
104
+ )
105
+ }
@@ -0,0 +1,196 @@
1
+ import { useEffect, useRef, useCallback } from 'react'
2
+ import Editor, { type OnMount, type OnChange } from '@monaco-editor/react'
3
+ import type * as Monaco from 'monaco-editor'
4
+ import { useStudioStore } from '../lib/store'
5
+ import { validateYaml } from '../lib/api'
6
+
7
+ export default function YamlEditor() {
8
+ const editorRef = useRef<Monaco.editor.IStandaloneCodeEditor | null>(null)
9
+ const monacoRef = useRef<typeof Monaco | null>(null)
10
+ const validationTimeoutRef = useRef<number | null>(null)
11
+
12
+ const {
13
+ yamlContent,
14
+ setYamlContent,
15
+ setValidation,
16
+ syncYamlToCanvas,
17
+ currentFile,
18
+ } = useStudioStore()
19
+
20
+ const handleEditorMount: OnMount = (editor, monaco) => {
21
+ editorRef.current = editor
22
+ monacoRef.current = monaco
23
+
24
+ // Configure YAML language
25
+ monaco.languages.registerCompletionItemProvider('yaml', {
26
+ provideCompletionItems: (model: Monaco.editor.ITextModel, position: Monaco.Position) => {
27
+ const word = model.getWordUntilPosition(position)
28
+ const range = {
29
+ startLineNumber: position.lineNumber,
30
+ endLineNumber: position.lineNumber,
31
+ startColumn: word.startColumn,
32
+ endColumn: word.endColumn,
33
+ }
34
+
35
+ const suggestions: Monaco.languages.CompletionItem[] = [
36
+ {
37
+ label: 'apiVersion',
38
+ kind: monaco.languages.CompletionItemKind.Property,
39
+ insertText: 'apiVersion: hexdag/v1',
40
+ range,
41
+ },
42
+ {
43
+ label: 'kind',
44
+ kind: monaco.languages.CompletionItemKind.Property,
45
+ insertText: 'kind: Pipeline',
46
+ range,
47
+ },
48
+ {
49
+ label: 'metadata',
50
+ kind: monaco.languages.CompletionItemKind.Property,
51
+ insertText: 'metadata:\n name: ${1:pipeline-name}',
52
+ insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
53
+ range,
54
+ },
55
+ {
56
+ label: 'spec',
57
+ kind: monaco.languages.CompletionItemKind.Property,
58
+ insertText: 'spec:\n nodes:\n - kind: ${1:function_node}\n metadata:\n name: ${2:node-name}\n spec:\n ${3:}\n dependencies: []',
59
+ insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
60
+ range,
61
+ },
62
+ {
63
+ label: 'llm_node',
64
+ kind: monaco.languages.CompletionItemKind.Value,
65
+ insertText: 'kind: llm_node\n metadata:\n name: ${1:llm}\n spec:\n prompt_template: "${2:Process: {{input}}}"\n dependencies: [${3}]',
66
+ insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
67
+ range,
68
+ },
69
+ {
70
+ label: 'function_node',
71
+ kind: monaco.languages.CompletionItemKind.Value,
72
+ insertText: 'kind: function_node\n metadata:\n name: ${1:function}\n spec:\n fn: "${2:json.loads}"\n dependencies: [${3}]',
73
+ insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
74
+ range,
75
+ },
76
+ ]
77
+
78
+ return { suggestions }
79
+ },
80
+ })
81
+
82
+ // Define custom theme
83
+ monaco.editor.defineTheme('hexdag-dark', {
84
+ base: 'vs-dark',
85
+ inherit: true,
86
+ rules: [
87
+ { token: 'key', foreground: '6366f1' },
88
+ { token: 'string.yaml', foreground: '22c55e' },
89
+ { token: 'number.yaml', foreground: 'f59e0b' },
90
+ { token: 'keyword.yaml', foreground: 'ec4899' },
91
+ ],
92
+ colors: {
93
+ 'editor.background': '#0f0f1a',
94
+ 'editor.foreground': '#e4e4e7',
95
+ 'editor.lineHighlightBackground': '#1a1a2e',
96
+ 'editorCursor.foreground': '#6366f1',
97
+ 'editor.selectionBackground': '#6366f140',
98
+ 'editorLineNumber.foreground': '#4a4a5e',
99
+ 'editorLineNumber.activeForeground': '#6366f1',
100
+ },
101
+ })
102
+
103
+ monaco.editor.setTheme('hexdag-dark')
104
+
105
+ // Keyboard shortcut for save
106
+ editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, () => {
107
+ // Trigger save via parent component
108
+ const event = new CustomEvent('hexdag-save')
109
+ window.dispatchEvent(event)
110
+ })
111
+ }
112
+
113
+ const runValidation = useCallback(async (content: string) => {
114
+ if (!content.trim()) {
115
+ setValidation(null)
116
+ return
117
+ }
118
+
119
+ try {
120
+ const result = await validateYaml(content, currentFile || undefined)
121
+ setValidation(result)
122
+
123
+ // Update editor markers
124
+ if (monacoRef.current && editorRef.current) {
125
+ const model = editorRef.current.getModel()
126
+ if (model) {
127
+ const markers: Monaco.editor.IMarkerData[] = result.errors.map((err) => ({
128
+ severity: err.severity === 'error'
129
+ ? monacoRef.current!.MarkerSeverity.Error
130
+ : err.severity === 'warning'
131
+ ? monacoRef.current!.MarkerSeverity.Warning
132
+ : monacoRef.current!.MarkerSeverity.Info,
133
+ message: err.message,
134
+ startLineNumber: err.line || 1,
135
+ startColumn: err.column || 1,
136
+ endLineNumber: err.line || 1,
137
+ endColumn: (err.column || 1) + 10,
138
+ }))
139
+ monacoRef.current.editor.setModelMarkers(model, 'hexdag', markers)
140
+ }
141
+ }
142
+ } catch (error) {
143
+ console.error('Validation error:', error)
144
+ }
145
+ }, [currentFile, setValidation])
146
+
147
+ const handleChange: OnChange = useCallback((value) => {
148
+ const content = value || ''
149
+ setYamlContent(content)
150
+
151
+ // Debounced validation
152
+ if (validationTimeoutRef.current) {
153
+ clearTimeout(validationTimeoutRef.current)
154
+ }
155
+ validationTimeoutRef.current = window.setTimeout(() => {
156
+ runValidation(content)
157
+ syncYamlToCanvas()
158
+ }, 500)
159
+ }, [setYamlContent, runValidation, syncYamlToCanvas])
160
+
161
+ // Initial validation
162
+ useEffect(() => {
163
+ if (yamlContent) {
164
+ runValidation(yamlContent)
165
+ }
166
+ }, []) // Only run once on mount
167
+
168
+ return (
169
+ <div className="h-full w-full">
170
+ <Editor
171
+ height="100%"
172
+ language="yaml"
173
+ value={yamlContent}
174
+ onChange={handleChange}
175
+ onMount={handleEditorMount}
176
+ options={{
177
+ minimap: { enabled: false },
178
+ fontSize: 13,
179
+ fontFamily: "'SF Mono', 'Fira Code', Consolas, monospace",
180
+ lineNumbers: 'on',
181
+ renderWhitespace: 'selection',
182
+ scrollBeyondLastLine: false,
183
+ automaticLayout: true,
184
+ tabSize: 2,
185
+ wordWrap: 'on',
186
+ folding: true,
187
+ bracketPairColorization: { enabled: true },
188
+ suggest: {
189
+ showWords: false,
190
+ },
191
+ }}
192
+ theme="hexdag-dark"
193
+ />
194
+ </div>
195
+ )
196
+ }
@@ -0,0 +1,8 @@
1
+ export { default as Header } from './Header'
2
+ export { default as FileBrowser } from './FileBrowser'
3
+ export { default as NodePalette } from './NodePalette'
4
+ export { default as Canvas } from './Canvas'
5
+ export { default as YamlEditor } from './YamlEditor'
6
+ export { default as ValidationPanel } from './ValidationPanel'
7
+ export { default as HexdagNode } from './HexdagNode'
8
+ export { default as NodeInspector } from './NodeInspector'
@@ -0,0 +1,92 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ * {
6
+ margin: 0;
7
+ padding: 0;
8
+ box-sizing: border-box;
9
+ }
10
+
11
+ body {
12
+ font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
13
+ background: #0f0f1a;
14
+ color: #e4e4e7;
15
+ overflow: hidden;
16
+ }
17
+
18
+ /* React Flow customization */
19
+ .react-flow__node {
20
+ border-radius: 8px;
21
+ font-size: 12px;
22
+ }
23
+
24
+ .react-flow__edge-path {
25
+ stroke: #6366f1;
26
+ stroke-width: 2;
27
+ }
28
+
29
+ .react-flow__edge.selected .react-flow__edge-path {
30
+ stroke: #818cf8;
31
+ }
32
+
33
+ .react-flow__handle {
34
+ width: 8px;
35
+ height: 8px;
36
+ background: #6366f1;
37
+ border: 2px solid #1a1a2e;
38
+ }
39
+
40
+ .react-flow__handle:hover {
41
+ background: #818cf8;
42
+ }
43
+
44
+ .react-flow__background {
45
+ background: #0f0f1a;
46
+ }
47
+
48
+ .react-flow__minimap {
49
+ background: #1a1a2e;
50
+ border: 1px solid #2a2a3e;
51
+ border-radius: 4px;
52
+ }
53
+
54
+ .react-flow__controls {
55
+ background: #1a1a2e;
56
+ border: 1px solid #2a2a3e;
57
+ border-radius: 4px;
58
+ }
59
+
60
+ .react-flow__controls-button {
61
+ background: #1a1a2e;
62
+ border-bottom: 1px solid #2a2a3e;
63
+ fill: #a1a1aa;
64
+ }
65
+
66
+ .react-flow__controls-button:hover {
67
+ background: #2a2a3e;
68
+ }
69
+
70
+ /* Monaco editor */
71
+ .monaco-editor {
72
+ padding-top: 8px;
73
+ }
74
+
75
+ /* Scrollbar */
76
+ ::-webkit-scrollbar {
77
+ width: 8px;
78
+ height: 8px;
79
+ }
80
+
81
+ ::-webkit-scrollbar-track {
82
+ background: #1a1a2e;
83
+ }
84
+
85
+ ::-webkit-scrollbar-thumb {
86
+ background: #3a3a4e;
87
+ border-radius: 4px;
88
+ }
89
+
90
+ ::-webkit-scrollbar-thumb:hover {
91
+ background: #4a4a5e;
92
+ }
@@ -0,0 +1,10 @@
1
+ import React from 'react'
2
+ import ReactDOM from 'react-dom/client'
3
+ import App from './App'
4
+ import './index.css'
5
+
6
+ ReactDOM.createRoot(document.getElementById('root')!).render(
7
+ <React.StrictMode>
8
+ <App />
9
+ </React.StrictMode>,
10
+ )
@@ -0,0 +1,123 @@
1
+ import type { Node, Edge } from '@xyflow/react'
2
+
3
+ // File types
4
+ export interface FileInfo {
5
+ name: string
6
+ path: string
7
+ is_directory: boolean
8
+ size?: number
9
+ modified?: number
10
+ }
11
+
12
+ export interface FileContent {
13
+ path: string
14
+ content: string
15
+ modified: number
16
+ }
17
+
18
+ // Validation types
19
+ export interface ValidationError {
20
+ line?: number
21
+ column?: number
22
+ message: string
23
+ severity: 'error' | 'warning' | 'info'
24
+ }
25
+
26
+ export interface ValidationResult {
27
+ valid: boolean
28
+ errors: ValidationError[]
29
+ node_count?: number
30
+ nodes?: string[]
31
+ }
32
+
33
+ // Pipeline types
34
+ export interface PipelineMetadata {
35
+ name: string
36
+ description?: string
37
+ }
38
+
39
+ export interface NodeSpec {
40
+ kind: string
41
+ metadata: {
42
+ name: string
43
+ description?: string
44
+ }
45
+ spec: Record<string, unknown>
46
+ dependencies: string[]
47
+ }
48
+
49
+ export interface PipelineSpec {
50
+ ports?: Record<string, unknown>
51
+ nodes: NodeSpec[]
52
+ }
53
+
54
+ export interface Pipeline {
55
+ apiVersion: string
56
+ kind: string
57
+ metadata: PipelineMetadata
58
+ spec: PipelineSpec
59
+ }
60
+
61
+ // Canvas node types - must have index signature for React Flow compatibility
62
+ export interface HexdagNodeData extends Record<string, unknown> {
63
+ kind: string
64
+ label: string
65
+ spec: Record<string, unknown>
66
+ isValid: boolean
67
+ errors: string[]
68
+ }
69
+
70
+ export type HexdagNode = Node<HexdagNodeData>
71
+ export type HexdagEdge = Edge
72
+
73
+ // Node palette types
74
+ export interface NodeTemplate {
75
+ kind: string
76
+ label: string
77
+ description: string
78
+ icon: string
79
+ color: string
80
+ defaultSpec: Record<string, unknown>
81
+ requiredPorts?: string[] // Ports this node type requires (e.g., ['llm', 'tool_router'])
82
+ }
83
+
84
+ // Port configuration for a node
85
+ export interface NodePortConfig {
86
+ adapter: string // e.g., 'core:openai' or 'plugin:azure_openai'
87
+ config?: Record<string, unknown>
88
+ }
89
+
90
+ // Store types
91
+ export interface StudioState {
92
+ // Files
93
+ files: FileInfo[]
94
+ currentFile: string | null
95
+ yamlContent: string
96
+
97
+ // Canvas
98
+ nodes: HexdagNode[]
99
+ edges: HexdagEdge[]
100
+
101
+ // Validation
102
+ validation: ValidationResult | null
103
+
104
+ // UI state
105
+ isLoading: boolean
106
+ isSaving: boolean
107
+ isDirty: boolean
108
+
109
+ // Actions
110
+ setFiles: (files: FileInfo[]) => void
111
+ setCurrentFile: (path: string | null) => void
112
+ setYamlContent: (content: string) => void
113
+ setNodes: (nodes: HexdagNode[]) => void
114
+ setEdges: (edges: HexdagEdge[]) => void
115
+ setValidation: (result: ValidationResult | null) => void
116
+ setIsLoading: (loading: boolean) => void
117
+ setIsSaving: (saving: boolean) => void
118
+ setIsDirty: (dirty: boolean) => void
119
+
120
+ // Complex actions
121
+ syncYamlToCanvas: () => void
122
+ syncCanvasToYaml: () => void
123
+ }
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
@@ -0,0 +1,29 @@
1
+ /** @type {import('tailwindcss').Config} */
2
+ export default {
3
+ content: [
4
+ "./index.html",
5
+ "./src/**/*.{js,ts,jsx,tsx}",
6
+ ],
7
+ theme: {
8
+ extend: {
9
+ colors: {
10
+ 'hex': {
11
+ 'bg': '#0f0f1a',
12
+ 'surface': '#1a1a2e',
13
+ 'border': '#2a2a3e',
14
+ 'accent': '#6366f1',
15
+ 'accent-hover': '#818cf8',
16
+ 'text': '#e4e4e7',
17
+ 'text-muted': '#a1a1aa',
18
+ 'success': '#22c55e',
19
+ 'error': '#ef4444',
20
+ 'warning': '#f59e0b',
21
+ }
22
+ },
23
+ fontFamily: {
24
+ 'mono': ['SF Mono', 'Fira Code', 'Consolas', 'monospace'],
25
+ }
26
+ },
27
+ },
28
+ plugins: [],
29
+ }
@@ -0,0 +1,37 @@
1
+ {
2
+ "compilerOptions": {
3
+ "allowImportingTsExtensions": true,
4
+ "baseUrl": ".",
5
+ "isolatedModules": true,
6
+ "jsx": "react-jsx",
7
+ "lib": [
8
+ "ES2020",
9
+ "DOM",
10
+ "DOM.Iterable"
11
+ ],
12
+ "module": "ESNext",
13
+ "moduleResolution": "bundler",
14
+ "noEmit": true,
15
+ "noFallthroughCasesInSwitch": true,
16
+ "noUnusedLocals": true,
17
+ "noUnusedParameters": true,
18
+ "paths": {
19
+ "@/*": [
20
+ "src/*"
21
+ ]
22
+ },
23
+ "resolveJsonModule": true,
24
+ "skipLibCheck": true,
25
+ "strict": true,
26
+ "target": "ES2020",
27
+ "useDefineForClassFields": true
28
+ },
29
+ "include": [
30
+ "src"
31
+ ],
32
+ "references": [
33
+ {
34
+ "path": "./tsconfig.node.json"
35
+ }
36
+ ]
37
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "compilerOptions": {
3
+ "allowSyntheticDefaultImports": true,
4
+ "composite": true,
5
+ "module": "ESNext",
6
+ "moduleResolution": "bundler",
7
+ "skipLibCheck": true,
8
+ "strict": true
9
+ },
10
+ "include": [
11
+ "vite.config.ts"
12
+ ]
13
+ }
@@ -0,0 +1,35 @@
1
+ import { defineConfig } from 'vite'
2
+ import react from '@vitejs/plugin-react'
3
+ import path from 'path'
4
+
5
+ export default defineConfig({
6
+ plugins: [react()],
7
+ base: '/',
8
+ resolve: {
9
+ alias: {
10
+ '@': path.resolve(__dirname, './src'),
11
+ },
12
+ },
13
+ build: {
14
+ outDir: 'dist',
15
+ emptyOutDir: true,
16
+ rollupOptions: {
17
+ output: {
18
+ manualChunks: {
19
+ 'react-vendor': ['react', 'react-dom'],
20
+ 'flow-vendor': ['@xyflow/react'],
21
+ 'monaco-vendor': ['@monaco-editor/react'],
22
+ },
23
+ },
24
+ },
25
+ },
26
+ server: {
27
+ port: 5173,
28
+ proxy: {
29
+ '/api': {
30
+ target: 'http://localhost:3141',
31
+ changeOrigin: true,
32
+ },
33
+ },
34
+ },
35
+ })