enya-agent 0.1.0
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.
- package/.env.example +20 -0
- package/.github/workflows/ci.yml +70 -0
- package/.github/workflows/publish.yml +250 -0
- package/.gitmodules +3 -0
- package/Cargo.lock +3584 -0
- package/Cargo.toml +97 -0
- package/crates/enact/Cargo.toml +27 -0
- package/crates/enact/src/lib.rs +60 -0
- package/crates/enact-a2a/Cargo.toml +25 -0
- package/crates/enact-a2a/src/lib.rs +411 -0
- package/crates/enact-channels/Cargo.toml +64 -0
- package/crates/enact-channels/examples/README.md +80 -0
- package/crates/enact-channels/examples/channel_bot.rs +169 -0
- package/crates/enact-channels/examples/telegram-echo.rs +34 -0
- package/crates/enact-channels/examples/whatsapp-echo.rs +142 -0
- package/crates/enact-channels/src/config.rs +213 -0
- package/crates/enact-channels/src/lib.rs +25 -0
- package/crates/enact-channels/src/runtime.rs +237 -0
- package/crates/enact-channels/src/security/mod.rs +5 -0
- package/crates/enact-channels/src/security/pairing.rs +205 -0
- package/crates/enact-channels/src/teams.rs +601 -0
- package/crates/enact-channels/src/telegram.rs +2833 -0
- package/crates/enact-channels/src/traits.rs +200 -0
- package/crates/enact-channels/src/webhook.rs +262 -0
- package/crates/enact-channels/src/whatsapp.rs +310 -0
- package/crates/enact-cli/Cargo.toml +40 -0
- package/crates/enact-cli/src/commands/doctor.rs +62 -0
- package/crates/enact-cli/src/commands/mod.rs +3 -0
- package/crates/enact-cli/src/commands/run.rs +69 -0
- package/crates/enact-cli/src/commands/serve.rs +81 -0
- package/crates/enact-cli/src/config.rs +2 -0
- package/crates/enact-cli/src/main.rs +79 -0
- package/crates/enact-config/Cargo.toml +36 -0
- package/crates/enact-config/ENV_VAR_MAPPING.md +135 -0
- package/crates/enact-config/QUICK_REFERENCE.md +92 -0
- package/crates/enact-config/README.md +107 -0
- package/crates/enact-config/TESTING.md +161 -0
- package/crates/enact-config/examples/test-env-vars.rs +100 -0
- package/crates/enact-config/src/config.rs +399 -0
- package/crates/enact-config/src/encrypted_store.rs +211 -0
- package/crates/enact-config/src/lib.rs +298 -0
- package/crates/enact-config/src/secrets.rs +149 -0
- package/crates/enact-config/src/sync.rs +260 -0
- package/crates/enact-config/test-env-vars.sh +34 -0
- package/crates/enact-config/tests/README.md +99 -0
- package/crates/enact-config/tests/config_integration_test.rs +202 -0
- package/crates/enact-config/tests/security_test.rs +140 -0
- package/crates/enact-context/Cargo.toml +41 -0
- package/crates/enact-context/src/budget.rs +314 -0
- package/crates/enact-context/src/calibrator.rs +535 -0
- package/crates/enact-context/src/compactor.rs +392 -0
- package/crates/enact-context/src/condenser.rs +826 -0
- package/crates/enact-context/src/lib.rs +94 -0
- package/crates/enact-context/src/segment.rs +238 -0
- package/crates/enact-context/src/step_context.rs +645 -0
- package/crates/enact-context/src/token_counter.rs +148 -0
- package/crates/enact-context/src/window.rs +372 -0
- package/crates/enact-core/Cargo.toml +42 -0
- package/crates/enact-core/README.md +98 -0
- package/crates/enact-core/src/background/executor.rs +524 -0
- package/crates/enact-core/src/background/mod.rs +48 -0
- package/crates/enact-core/src/background/target_binding.rs +390 -0
- package/crates/enact-core/src/background/trigger.rs +511 -0
- package/crates/enact-core/src/callable/callable.rs +152 -0
- package/crates/enact-core/src/callable/composite.rs +817 -0
- package/crates/enact-core/src/callable/graph.rs +104 -0
- package/crates/enact-core/src/callable/llm.rs +211 -0
- package/crates/enact-core/src/callable/mod.rs +64 -0
- package/crates/enact-core/src/callable/registry.rs +206 -0
- package/crates/enact-core/src/context/execution_context.rs +757 -0
- package/crates/enact-core/src/context/invocation.rs +99 -0
- package/crates/enact-core/src/context/mod.rs +50 -0
- package/crates/enact-core/src/context/tenant.rs +175 -0
- package/crates/enact-core/src/context/trace.rs +127 -0
- package/crates/enact-core/src/flow/conditional.rs +293 -0
- package/crates/enact-core/src/flow/mod.rs +43 -0
- package/crates/enact-core/src/flow/parallel.rs +437 -0
- package/crates/enact-core/src/flow/repeat.rs +534 -0
- package/crates/enact-core/src/flow/sequential.rs +248 -0
- package/crates/enact-core/src/graph/checkpoint.rs +79 -0
- package/crates/enact-core/src/graph/checkpoint_store.rs +76 -0
- package/crates/enact-core/src/graph/compiled.rs +189 -0
- package/crates/enact-core/src/graph/edge.rs +59 -0
- package/crates/enact-core/src/graph/graph_schema.rs +218 -0
- package/crates/enact-core/src/graph/loader.rs +155 -0
- package/crates/enact-core/src/graph/mod.rs +18 -0
- package/crates/enact-core/src/graph/node/function.rs +49 -0
- package/crates/enact-core/src/graph/node/mod.rs +48 -0
- package/crates/enact-core/src/graph/schema.rs +62 -0
- package/crates/enact-core/src/inbox/message.rs +405 -0
- package/crates/enact-core/src/inbox/mod.rs +31 -0
- package/crates/enact-core/src/inbox/store.rs +355 -0
- package/crates/enact-core/src/kernel/artifact/filesystem.rs +546 -0
- package/crates/enact-core/src/kernel/artifact/metadata.rs +283 -0
- package/crates/enact-core/src/kernel/artifact/mod.rs +27 -0
- package/crates/enact-core/src/kernel/artifact/store.rs +427 -0
- package/crates/enact-core/src/kernel/enforcement.rs +1315 -0
- package/crates/enact-core/src/kernel/error.rs +1200 -0
- package/crates/enact-core/src/kernel/event.rs +1394 -0
- package/crates/enact-core/src/kernel/execution_model.rs +831 -0
- package/crates/enact-core/src/kernel/execution_state.rs +189 -0
- package/crates/enact-core/src/kernel/execution_strategy.rs +117 -0
- package/crates/enact-core/src/kernel/ids.rs +2086 -0
- package/crates/enact-core/src/kernel/interrupt.rs +125 -0
- package/crates/enact-core/src/kernel/kernel.rs +1283 -0
- package/crates/enact-core/src/kernel/mod.rs +205 -0
- package/crates/enact-core/src/kernel/persistence/event_store.rs +270 -0
- package/crates/enact-core/src/kernel/persistence/message_store.rs +908 -0
- package/crates/enact-core/src/kernel/persistence/mod.rs +102 -0
- package/crates/enact-core/src/kernel/persistence/state_store.rs +228 -0
- package/crates/enact-core/src/kernel/persistence/vector_store.rs +299 -0
- package/crates/enact-core/src/kernel/reducer.rs +808 -0
- package/crates/enact-core/src/kernel/replay.rs +153 -0
- package/crates/enact-core/src/lib.rs +413 -0
- package/crates/enact-core/src/memory/episodic.rs +0 -0
- package/crates/enact-core/src/memory/mod.rs +6 -0
- package/crates/enact-core/src/memory/semantic.rs +0 -0
- package/crates/enact-core/src/memory/trait.rs +0 -0
- package/crates/enact-core/src/memory/vector_db.rs +0 -0
- package/crates/enact-core/src/memory/working.rs +0 -0
- package/crates/enact-core/src/policy/execution_policy.rs +292 -0
- package/crates/enact-core/src/policy/filters.rs +458 -0
- package/crates/enact-core/src/policy/input_processor.rs +407 -0
- package/crates/enact-core/src/policy/long_running.rs +134 -0
- package/crates/enact-core/src/policy/mod.rs +193 -0
- package/crates/enact-core/src/policy/pii_input.rs +274 -0
- package/crates/enact-core/src/policy/tenant_policy.rs +453 -0
- package/crates/enact-core/src/policy/tool_policy.rs +407 -0
- package/crates/enact-core/src/providers/mod.rs +63 -0
- package/crates/enact-core/src/providers/trait.rs +292 -0
- package/crates/enact-core/src/runner/callbacks.rs +6 -0
- package/crates/enact-core/src/runner/execution_runner.rs +476 -0
- package/crates/enact-core/src/runner/loop.rs +117 -0
- package/crates/enact-core/src/runner/mod.rs +58 -0
- package/crates/enact-core/src/runner/protected_runner.rs +280 -0
- package/crates/enact-core/src/signal/inmemory.rs +231 -0
- package/crates/enact-core/src/signal/mod.rs +108 -0
- package/crates/enact-core/src/streaming/event_logger.rs +195 -0
- package/crates/enact-core/src/streaming/event_stream.rs +1423 -0
- package/crates/enact-core/src/streaming/mod.rs +108 -0
- package/crates/enact-core/src/streaming/pause_cancel.rs +0 -0
- package/crates/enact-core/src/streaming/protected_emitter.rs +173 -0
- package/crates/enact-core/src/streaming/protection/context.rs +136 -0
- package/crates/enact-core/src/streaming/protection/encryption.rs +289 -0
- package/crates/enact-core/src/streaming/protection/mod.rs +43 -0
- package/crates/enact-core/src/streaming/protection/pii_protection.rs +243 -0
- package/crates/enact-core/src/streaming/protection/processor.rs +166 -0
- package/crates/enact-core/src/streaming/sse.rs +0 -0
- package/crates/enact-core/src/telemetry/exporter.rs +0 -0
- package/crates/enact-core/src/telemetry/init.rs +0 -0
- package/crates/enact-core/src/telemetry/mod.rs +49 -0
- package/crates/enact-core/src/telemetry/spans.rs +245 -0
- package/crates/enact-core/src/tool/agent_tool.rs +177 -0
- package/crates/enact-core/src/tool/browser/mod.rs +0 -0
- package/crates/enact-core/src/tool/browser/webdriver.rs +0 -0
- package/crates/enact-core/src/tool/cost.rs +247 -0
- package/crates/enact-core/src/tool/discovery.rs +0 -0
- package/crates/enact-core/src/tool/dispatcher.rs +347 -0
- package/crates/enact-core/src/tool/filesystem.rs +231 -0
- package/crates/enact-core/src/tool/function.rs +99 -0
- package/crates/enact-core/src/tool/git.rs +162 -0
- package/crates/enact-core/src/tool/http.rs +214 -0
- package/crates/enact-core/src/tool/mcp/client.rs +0 -0
- package/crates/enact-core/src/tool/mcp/mod.rs +0 -0
- package/crates/enact-core/src/tool/mod.rs +51 -0
- package/crates/enact-core/src/tool/reasoning/debugging.rs +0 -0
- package/crates/enact-core/src/tool/reasoning/mcts.rs +0 -0
- package/crates/enact-core/src/tool/reasoning/mod.rs +0 -0
- package/crates/enact-core/src/tool/reasoning/sequential.rs +0 -0
- package/crates/enact-core/src/tool/sandbox/dagger.rs +0 -0
- package/crates/enact-core/src/tool/sandbox/mod.rs +0 -0
- package/crates/enact-core/src/tool/shell.rs +147 -0
- package/crates/enact-core/src/tool/trait.rs +33 -0
- package/crates/enact-core/src/tool/web_search.rs +277 -0
- package/crates/enact-core/src/util/config.rs +0 -0
- package/crates/enact-core/src/util/errors.rs +0 -0
- package/crates/enact-core/src/util/mod.rs +6 -0
- package/crates/enact-core/tests/airgapped_e2e_test.rs +291 -0
- package/crates/enact-core/tests/e2e_agentic_loop.rs +119 -0
- package/crates/enact-core/tests/e2e_test.rs +259 -0
- package/crates/enact-core/tests/graph_test.rs +130 -0
- package/crates/enact-core/tests/stream_event_id_validation.rs +435 -0
- package/crates/enact-cron/Cargo.toml +28 -0
- package/crates/enact-cron/src/lib.rs +44 -0
- package/crates/enact-cron/src/schedule.rs +156 -0
- package/crates/enact-cron/src/store.rs +589 -0
- package/crates/enact-cron/src/types.rs +148 -0
- package/crates/enact-gateway/Cargo.toml +31 -0
- package/crates/enact-gateway/README.md +30 -0
- package/crates/enact-gateway/examples/whatsapp-gateway-runner-mock.rs +59 -0
- package/crates/enact-gateway/examples/whatsapp-gateway.rs +42 -0
- package/crates/enact-gateway/src/lib.rs +582 -0
- package/crates/enact-mcp/Cargo.toml +24 -0
- package/crates/enact-mcp/src/lib.rs +178 -0
- package/crates/enact-memory/Cargo.toml +25 -0
- package/crates/enact-memory/src/backend.rs +20 -0
- package/crates/enact-memory/src/chunker.rs +230 -0
- package/crates/enact-memory/src/embeddings.rs +221 -0
- package/crates/enact-memory/src/lib.rs +67 -0
- package/crates/enact-memory/src/markdown.rs +127 -0
- package/crates/enact-memory/src/none.rs +61 -0
- package/crates/enact-memory/src/sqlite.rs +276 -0
- package/crates/enact-memory/src/traits.rs +65 -0
- package/crates/enact-memory/src/vector.rs +198 -0
- package/crates/enact-oauth/Cargo.toml +27 -0
- package/crates/enact-oauth/src/lib.rs +584 -0
- package/crates/enact-observability/Cargo.toml +22 -0
- package/crates/enact-observability/src/lib.rs +197 -0
- package/crates/enact-providers/Cargo.toml +33 -0
- package/crates/enact-providers/examples/hello-agent.rs +33 -0
- package/crates/enact-providers/src/anthropic.rs +182 -0
- package/crates/enact-providers/src/azure.rs +96 -0
- package/crates/enact-providers/src/bridge.rs +221 -0
- package/crates/enact-providers/src/gemini.rs +227 -0
- package/crates/enact-providers/src/http.rs +78 -0
- package/crates/enact-providers/src/lib.rs +53 -0
- package/crates/enact-providers/src/openai_compatible.rs +167 -0
- package/crates/enact-providers/src/openrouter.rs +33 -0
- package/crates/enact-runner/Cargo.toml +24 -0
- package/crates/enact-runner/README.md +76 -0
- package/crates/enact-runner/src/compaction.rs +225 -0
- package/crates/enact-runner/src/config.rs +118 -0
- package/crates/enact-runner/src/lib.rs +63 -0
- package/crates/enact-runner/src/loop_driver.rs +414 -0
- package/crates/enact-runner/src/parser.rs +421 -0
- package/crates/enact-runner/src/retry.rs +262 -0
- package/crates/enact-runner/tests/integration.rs +278 -0
- package/crates/enact-security/Cargo.toml +22 -0
- package/crates/enact-security/src/audit.rs +375 -0
- package/crates/enact-security/src/lib.rs +37 -0
- package/crates/enact-security/src/policy.rs +406 -0
- package/crates/enact-skills/Cargo.toml +25 -0
- package/crates/enact-skills/src/lib.rs +506 -0
- package/crates/enact-tools/Cargo.toml +22 -0
- package/crates/enact-tools/src/file_read.rs +166 -0
- package/crates/enact-tools/src/file_write.rs +216 -0
- package/crates/enact-tools/src/git_operations.rs +513 -0
- package/crates/enact-tools/src/http_request.rs +417 -0
- package/crates/enact-tools/src/lib.rs +104 -0
- package/crates/enact-tools/src/security.rs +227 -0
- package/crates/enact-tools/src/shell.rs +191 -0
- package/crates/enact-tools/src/traits.rs +159 -0
- package/docs/Makefile +74 -0
- package/docs/config.toml +62 -0
- package/docs/content/_index.md +174 -0
- package/docs/content/a2a/_index.md +431 -0
- package/docs/content/api/_index.md +323 -0
- package/docs/content/channels/_index.md +160 -0
- package/docs/content/channels/teams.md +205 -0
- package/docs/content/channels/telegram.md +182 -0
- package/docs/content/channels/webhook.md +423 -0
- package/docs/content/channels/whatsapp.md +240 -0
- package/docs/content/cli/_index.md +261 -0
- package/docs/content/concepts/_index.md +273 -0
- package/docs/content/configuration/_index.md +241 -0
- package/docs/content/cron/_index.md +248 -0
- package/docs/content/developers/_index.md +278 -0
- package/docs/content/getting-started/_index.md +180 -0
- package/docs/content/installation/_index.md +186 -0
- package/docs/content/installation/uninstall.md +101 -0
- package/docs/content/installation/updating.md +120 -0
- package/docs/content/mcp/_index.md +215 -0
- package/docs/content/memory/_index.md +163 -0
- package/docs/content/oauth/_index.md +515 -0
- package/docs/content/providers/_index.md +206 -0
- package/docs/content/roadmap/_index.md +199 -0
- package/docs/content/security/_index.md +219 -0
- package/docs/content/skills/_index.md +228 -0
- package/docs/content/tools/_index.md +485 -0
- package/docs/content/troubleshooting/_index.md +259 -0
- package/docs/content/yaml-schema/_index.md +294 -0
- package/docs/static/giallo-dark.css +91 -0
- package/docs/static/giallo-light.css +91 -0
- package/docs/themes/tanuki/.github/workflows/deploy.yml +44 -0
- package/docs/themes/tanuki/LICENSE +21 -0
- package/docs/themes/tanuki/README.md +166 -0
- package/docs/themes/tanuki/examples/blog/config.toml +58 -0
- package/docs/themes/tanuki/examples/blog/content/_index.md +4 -0
- package/docs/themes/tanuki/examples/blog/content/about.md +33 -0
- package/docs/themes/tanuki/examples/blog/content/blog/_index.md +7 -0
- package/docs/themes/tanuki/examples/blog/content/blog/api-design-best-practices.md +245 -0
- package/docs/themes/tanuki/examples/blog/content/blog/building-accessible-websites.md +147 -0
- package/docs/themes/tanuki/examples/blog/content/blog/css-grid-vs-flexbox.md +165 -0
- package/docs/themes/tanuki/examples/blog/content/blog/customizing-catppuccin-colors.md +137 -0
- package/docs/themes/tanuki/examples/blog/content/blog/dark-mode-best-practices.md +82 -0
- package/docs/themes/tanuki/examples/blog/content/blog/docker-essentials.md +301 -0
- package/docs/themes/tanuki/examples/blog/content/blog/getting-started-with-zola.md +129 -0
- package/docs/themes/tanuki/examples/blog/content/blog/git-workflow-for-content.md +112 -0
- package/docs/themes/tanuki/examples/blog/content/blog/introduction-to-webassembly.md +183 -0
- package/docs/themes/tanuki/examples/blog/content/blog/modern-javascript-features.md +234 -0
- package/docs/themes/tanuki/examples/blog/content/blog/testing-strategies.md +311 -0
- package/docs/themes/tanuki/examples/blog/content/blog/typography-for-developers.md +104 -0
- package/docs/themes/tanuki/examples/blog/content/blog/welcome-to-tanuki.md +67 -0
- package/docs/themes/tanuki/examples/blog/content/blog/why-static-sites.md +85 -0
- package/docs/themes/tanuki/examples/blog/content/projects.md +64 -0
- package/docs/themes/tanuki/examples/book/config.toml +17 -0
- package/docs/themes/tanuki/examples/book/content/_index.md +12 -0
- package/docs/themes/tanuki/examples/book/content/chapter-1.md +90 -0
- package/docs/themes/tanuki/examples/book/content/chapter-2.md +143 -0
- package/docs/themes/tanuki/examples/book/content/chapter-3.md +217 -0
- package/docs/themes/tanuki/examples/book/content/chapter-4.md +224 -0
- package/docs/themes/tanuki/examples/book/content/chapter-5.md +297 -0
- package/docs/themes/tanuki/examples/book/content/print.md +6 -0
- package/docs/themes/tanuki/examples/docs/config.toml +28 -0
- package/docs/themes/tanuki/examples/docs/content/_index.md +20 -0
- package/docs/themes/tanuki/examples/docs/content/components.md +156 -0
- package/docs/themes/tanuki/examples/docs/content/configuration.md +94 -0
- package/docs/themes/tanuki/examples/docs/content/customization.md +202 -0
- package/docs/themes/tanuki/examples/docs/content/deployment.md +204 -0
- package/docs/themes/tanuki/examples/docs/content/installation.md +59 -0
- package/docs/themes/tanuki/examples/docs/content/print.md +6 -0
- package/docs/themes/tanuki/examples/docs/static/img/tanuki-icon.avif +0 -0
- package/docs/themes/tanuki/examples/index.html +2104 -0
- package/docs/themes/tanuki/mise.toml +108 -0
- package/docs/themes/tanuki/sass/base/_catppuccin.scss +164 -0
- package/docs/themes/tanuki/sass/base/_fonts.scss +64 -0
- package/docs/themes/tanuki/sass/base/_reset.scss +152 -0
- package/docs/themes/tanuki/sass/base/_typography.scss +523 -0
- package/docs/themes/tanuki/sass/components/_buttons.scss +209 -0
- package/docs/themes/tanuki/sass/components/_code.scss +457 -0
- package/docs/themes/tanuki/sass/components/_landing.scss +633 -0
- package/docs/themes/tanuki/sass/components/_layout.scss +294 -0
- package/docs/themes/tanuki/sass/components/_navigation.scss +1200 -0
- package/docs/themes/tanuki/sass/components/_print.scss +237 -0
- package/docs/themes/tanuki/sass/components/_search.scss +224 -0
- package/docs/themes/tanuki/sass/components/_sidebar.scss +473 -0
- package/docs/themes/tanuki/sass/components/_theme-toggle.scss +186 -0
- package/docs/themes/tanuki/sass/modes/_blog.scss +366 -0
- package/docs/themes/tanuki/sass/modes/_product.scss +875 -0
- package/docs/themes/tanuki/sass/modes/_raskell.scss +1696 -0
- package/docs/themes/tanuki/sass/patterns/_buttons.scss +183 -0
- package/docs/themes/tanuki/sass/patterns/_cards.scss +144 -0
- package/docs/themes/tanuki/sass/patterns/_index.scss +9 -0
- package/docs/themes/tanuki/sass/patterns/_lists.scss +259 -0
- package/docs/themes/tanuki/sass/patterns/_sections.scss +243 -0
- package/docs/themes/tanuki/sass/style.scss +47 -0
- package/docs/themes/tanuki/sass/tokens/_colors.scss +139 -0
- package/docs/themes/tanuki/sass/tokens/_spacing.scss +100 -0
- package/docs/themes/tanuki/sass/tokens/_typography.scss +186 -0
- package/docs/themes/tanuki/screenshot.png +0 -0
- package/docs/themes/tanuki/sentinel.kdl +59 -0
- package/docs/themes/tanuki/static/elasticlunr.min.js +10 -0
- package/docs/themes/tanuki/static/fonts/GEIST-LICENSE.txt +92 -0
- package/docs/themes/tanuki/static/fonts/Geist-Variable.woff2 +0 -0
- package/docs/themes/tanuki/static/fonts/GeistMono-Variable.woff2 +0 -0
- package/docs/themes/tanuki/static/img/tanuki-icon.avif +0 -0
- package/docs/themes/tanuki/static/img/tanuki-icon.png +0 -0
- package/docs/themes/tanuki/static/js/anchors.js +18 -0
- package/docs/themes/tanuki/static/js/app.js +274 -0
- package/docs/themes/tanuki/static/js/code.js +394 -0
- package/docs/themes/tanuki/static/js/navigation.js +778 -0
- package/docs/themes/tanuki/static/js/scroll-to-top.js +33 -0
- package/docs/themes/tanuki/static/js/search-raskell.js +240 -0
- package/docs/themes/tanuki/static/js/search.js +215 -0
- package/docs/themes/tanuki/static/js/theme.js +169 -0
- package/docs/themes/tanuki/static/syntax-dark.css +151 -0
- package/docs/themes/tanuki/static/syntax-light.css +151 -0
- package/docs/themes/tanuki/static/wasm/sentinel_playground_wasm.js +486 -0
- package/docs/themes/tanuki/static/wasm/sentinel_playground_wasm_bg.wasm +0 -0
- package/docs/themes/tanuki/templates/404.html +52 -0
- package/docs/themes/tanuki/templates/base.html +428 -0
- package/docs/themes/tanuki/templates/blog.html +66 -0
- package/docs/themes/tanuki/templates/home.html +108 -0
- package/docs/themes/tanuki/templates/index.html +178 -0
- package/docs/themes/tanuki/templates/landing.html +168 -0
- package/docs/themes/tanuki/templates/macros/nav.html +128 -0
- package/docs/themes/tanuki/templates/macros/posts.html +101 -0
- package/docs/themes/tanuki/templates/macros/ui.html +159 -0
- package/docs/themes/tanuki/templates/page.html +135 -0
- package/docs/themes/tanuki/templates/partials/footer.html +38 -0
- package/docs/themes/tanuki/templates/partials/header.html +366 -0
- package/docs/themes/tanuki/templates/partials/nav-buttons.html +55 -0
- package/docs/themes/tanuki/templates/partials/nav-overlay.html +81 -0
- package/docs/themes/tanuki/templates/partials/page-toc-panel.html +43 -0
- package/docs/themes/tanuki/templates/partials/search.html +52 -0
- package/docs/themes/tanuki/templates/partials/sidebar.html +107 -0
- package/docs/themes/tanuki/templates/partials/theme-toggle.html +35 -0
- package/docs/themes/tanuki/templates/partials/toc-overlay.html +146 -0
- package/docs/themes/tanuki/templates/partials/version-picker.html +38 -0
- package/docs/themes/tanuki/templates/print.html +244 -0
- package/docs/themes/tanuki/templates/section.html +186 -0
- package/docs/themes/tanuki/templates/taxonomy_list.html +18 -0
- package/docs/themes/tanuki/templates/taxonomy_single.html +31 -0
- package/docs/themes/tanuki/theme.toml +58 -0
- package/examples/hello-agent.rs +55 -0
- package/package.json +36 -0
- package/proto/config.proto +60 -0
- package/proto/events.proto +0 -0
- package/proto/runtime.proto +215 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
//! StateGraph - builder for creating graphs
|
|
2
|
+
//!
|
|
3
|
+
//! Includes cycle detection to prevent infinite loops (DAG invariant).
|
|
4
|
+
|
|
5
|
+
use super::edge::{ConditionalEdge, Edge, EdgeTarget};
|
|
6
|
+
use super::node::{DynNode, FunctionNode, Node, NodeState};
|
|
7
|
+
use super::CompiledGraph;
|
|
8
|
+
use std::collections::{HashMap, HashSet};
|
|
9
|
+
use std::future::Future;
|
|
10
|
+
use std::sync::Arc;
|
|
11
|
+
|
|
12
|
+
/// StateGraph - fluent builder for creating DAGs
|
|
13
|
+
pub struct StateGraph {
|
|
14
|
+
nodes: HashMap<String, DynNode>,
|
|
15
|
+
edges: Vec<Edge>,
|
|
16
|
+
conditional_edges: Vec<ConditionalEdge>,
|
|
17
|
+
entry_point: Option<String>,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
impl StateGraph {
|
|
21
|
+
/// Create a new empty graph
|
|
22
|
+
pub fn new() -> Self {
|
|
23
|
+
Self {
|
|
24
|
+
nodes: HashMap::new(),
|
|
25
|
+
edges: Vec::new(),
|
|
26
|
+
conditional_edges: Vec::new(),
|
|
27
|
+
entry_point: None,
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/// Add a node with a function
|
|
32
|
+
pub fn add_node<F, Fut>(mut self, name: impl Into<String>, func: F) -> Self
|
|
33
|
+
where
|
|
34
|
+
F: Fn(NodeState) -> Fut + Send + Sync + 'static,
|
|
35
|
+
Fut: Future<Output = anyhow::Result<NodeState>> + Send + 'static,
|
|
36
|
+
{
|
|
37
|
+
let name = name.into();
|
|
38
|
+
let node = Arc::new(FunctionNode::new(name.clone(), func));
|
|
39
|
+
self.nodes.insert(name, node);
|
|
40
|
+
self
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/// Add a pre-built node
|
|
44
|
+
pub fn add_node_impl(mut self, node: impl Node + 'static) -> Self {
|
|
45
|
+
let name = node.name().to_string();
|
|
46
|
+
self.nodes.insert(name, Arc::new(node));
|
|
47
|
+
self
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/// Add a direct edge between two nodes
|
|
51
|
+
pub fn add_edge(mut self, from: impl Into<String>, to: impl Into<String>) -> Self {
|
|
52
|
+
self.edges.push(Edge::new(from, EdgeTarget::node(to)));
|
|
53
|
+
self
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/// Add an edge to END
|
|
57
|
+
pub fn add_edge_to_end(mut self, from: impl Into<String>) -> Self {
|
|
58
|
+
self.edges.push(Edge::new(from, EdgeTarget::End));
|
|
59
|
+
self
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/// Add a conditional edge with a router function
|
|
63
|
+
pub fn add_conditional_edge<F>(mut self, from: impl Into<String>, router: F) -> Self
|
|
64
|
+
where
|
|
65
|
+
F: Fn(&str) -> EdgeTarget + Send + Sync + 'static,
|
|
66
|
+
{
|
|
67
|
+
self.conditional_edges.push(ConditionalEdge {
|
|
68
|
+
from: from.into(),
|
|
69
|
+
router: Arc::new(router),
|
|
70
|
+
});
|
|
71
|
+
self
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/// Set the entry point node
|
|
75
|
+
pub fn set_entry_point(mut self, name: impl Into<String>) -> Self {
|
|
76
|
+
self.entry_point = Some(name.into());
|
|
77
|
+
self
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/// Compile the graph for execution
|
|
81
|
+
pub fn compile(self) -> anyhow::Result<CompiledGraph> {
|
|
82
|
+
// Validate: must have at least one node
|
|
83
|
+
if self.nodes.is_empty() {
|
|
84
|
+
anyhow::bail!("Graph must have at least one node");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Determine entry point
|
|
88
|
+
let entry_point = self.entry_point.clone().or_else(|| {
|
|
89
|
+
// If no entry point set, use first node added
|
|
90
|
+
self.nodes.keys().next().cloned()
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
let entry_point = entry_point.ok_or_else(|| anyhow::anyhow!("No entry point defined"))?;
|
|
94
|
+
|
|
95
|
+
// Validate: entry point must exist
|
|
96
|
+
if !self.nodes.contains_key(&entry_point) {
|
|
97
|
+
anyhow::bail!("Entry point '{}' does not exist", entry_point);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Validate: all edge sources/targets exist
|
|
101
|
+
for edge in &self.edges {
|
|
102
|
+
if !self.nodes.contains_key(&edge.from) {
|
|
103
|
+
anyhow::bail!("Edge source '{}' does not exist", edge.from);
|
|
104
|
+
}
|
|
105
|
+
if let EdgeTarget::Node(ref target) = edge.to {
|
|
106
|
+
if !self.nodes.contains_key(target) {
|
|
107
|
+
anyhow::bail!("Edge target '{}' does not exist", target);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Validate: no cycles (DAG invariant)
|
|
113
|
+
// Build adjacency list from edges
|
|
114
|
+
let adjacency = self.build_adjacency_list();
|
|
115
|
+
if let Some(cycle) = self.detect_cycle(&adjacency, &entry_point) {
|
|
116
|
+
anyhow::bail!(
|
|
117
|
+
"Graph contains a cycle: {} -> ... -> {}. Cycles are not allowed in DAGs.",
|
|
118
|
+
cycle.first().unwrap_or(&"?".to_string()),
|
|
119
|
+
cycle.last().unwrap_or(&"?".to_string())
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
Ok(CompiledGraph {
|
|
124
|
+
nodes: self.nodes,
|
|
125
|
+
edges: self.edges,
|
|
126
|
+
conditional_edges: self.conditional_edges,
|
|
127
|
+
entry_point,
|
|
128
|
+
})
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/// Build adjacency list from edges
|
|
132
|
+
fn build_adjacency_list(&self) -> HashMap<String, Vec<String>> {
|
|
133
|
+
let mut adjacency: HashMap<String, Vec<String>> = HashMap::new();
|
|
134
|
+
|
|
135
|
+
// Initialize all nodes with empty neighbor lists
|
|
136
|
+
for node_name in self.nodes.keys() {
|
|
137
|
+
adjacency.entry(node_name.clone()).or_default();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Add edges
|
|
141
|
+
for edge in &self.edges {
|
|
142
|
+
if let EdgeTarget::Node(ref target) = edge.to {
|
|
143
|
+
adjacency
|
|
144
|
+
.entry(edge.from.clone())
|
|
145
|
+
.or_default()
|
|
146
|
+
.push(target.clone());
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
adjacency
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/// Detect cycle using DFS
|
|
154
|
+
/// Returns Some(cycle_path) if a cycle is found, None otherwise
|
|
155
|
+
fn detect_cycle(
|
|
156
|
+
&self,
|
|
157
|
+
adjacency: &HashMap<String, Vec<String>>,
|
|
158
|
+
entry_point: &str,
|
|
159
|
+
) -> Option<Vec<String>> {
|
|
160
|
+
let mut visited = HashSet::new();
|
|
161
|
+
let mut rec_stack = HashSet::new();
|
|
162
|
+
let mut path = Vec::new();
|
|
163
|
+
|
|
164
|
+
if self.dfs_cycle_detect(entry_point, adjacency, &mut visited, &mut rec_stack, &mut path) {
|
|
165
|
+
return Some(path);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Also check from all nodes in case graph has disconnected components
|
|
169
|
+
for node in self.nodes.keys() {
|
|
170
|
+
if !visited.contains(node) {
|
|
171
|
+
path.clear();
|
|
172
|
+
if self.dfs_cycle_detect(node, adjacency, &mut visited, &mut rec_stack, &mut path) {
|
|
173
|
+
return Some(path);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
None
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/// DFS helper for cycle detection
|
|
182
|
+
fn dfs_cycle_detect(
|
|
183
|
+
&self,
|
|
184
|
+
node: &str,
|
|
185
|
+
adjacency: &HashMap<String, Vec<String>>,
|
|
186
|
+
visited: &mut HashSet<String>,
|
|
187
|
+
rec_stack: &mut HashSet<String>,
|
|
188
|
+
path: &mut Vec<String>,
|
|
189
|
+
) -> bool {
|
|
190
|
+
visited.insert(node.to_string());
|
|
191
|
+
rec_stack.insert(node.to_string());
|
|
192
|
+
path.push(node.to_string());
|
|
193
|
+
|
|
194
|
+
if let Some(neighbors) = adjacency.get(node) {
|
|
195
|
+
for neighbor in neighbors {
|
|
196
|
+
if !visited.contains(neighbor) {
|
|
197
|
+
if self.dfs_cycle_detect(neighbor, adjacency, visited, rec_stack, path) {
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
} else if rec_stack.contains(neighbor) {
|
|
201
|
+
// Found a cycle
|
|
202
|
+
path.push(neighbor.clone());
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
rec_stack.remove(node);
|
|
209
|
+
path.pop();
|
|
210
|
+
false
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
impl Default for StateGraph {
|
|
215
|
+
fn default() -> Self {
|
|
216
|
+
Self::new()
|
|
217
|
+
}
|
|
218
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
use super::schema::{GraphDefinition, NodeDefinition};
|
|
2
|
+
use super::{EdgeTarget, NodeState, StateGraph};
|
|
3
|
+
use anyhow::{anyhow, Context, Result};
|
|
4
|
+
|
|
5
|
+
pub struct GraphLoader;
|
|
6
|
+
|
|
7
|
+
impl GraphLoader {
|
|
8
|
+
pub fn load_from_str(yaml: &str) -> Result<StateGraph> {
|
|
9
|
+
let def: GraphDefinition =
|
|
10
|
+
serde_yaml::from_str(yaml).context("Failed to parse graph definition YAML")?;
|
|
11
|
+
|
|
12
|
+
let mut graph = StateGraph::new();
|
|
13
|
+
|
|
14
|
+
// 1. Add all nodes
|
|
15
|
+
for (name, node_def) in &def.nodes {
|
|
16
|
+
match node_def {
|
|
17
|
+
NodeDefinition::Llm {
|
|
18
|
+
model,
|
|
19
|
+
system_prompt,
|
|
20
|
+
..
|
|
21
|
+
} => {
|
|
22
|
+
// Placeholder for LLM node
|
|
23
|
+
let name_clone = name.clone();
|
|
24
|
+
let model = model.clone().unwrap_or_else(|| "default".to_string());
|
|
25
|
+
let prompt = system_prompt.clone();
|
|
26
|
+
|
|
27
|
+
graph = graph.add_node(name, move |state: NodeState| {
|
|
28
|
+
let n = name_clone.clone();
|
|
29
|
+
let m = model.clone();
|
|
30
|
+
let p = prompt.clone();
|
|
31
|
+
async move {
|
|
32
|
+
println!("🤖 [LLM Node: {}] Model: {}, Prompt: {:.30}...", n, m, p);
|
|
33
|
+
// In a real implementation, this would call LlmCallable
|
|
34
|
+
Ok(state)
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
NodeDefinition::Function { action, .. } => {
|
|
39
|
+
let name_clone = name.clone();
|
|
40
|
+
let action = action.clone();
|
|
41
|
+
|
|
42
|
+
graph = graph.add_node(name, move |state: NodeState| {
|
|
43
|
+
let n = name_clone.clone();
|
|
44
|
+
let a = action.clone();
|
|
45
|
+
async move {
|
|
46
|
+
println!("⚙️ [Function Node: {}] Action: {}", n, a);
|
|
47
|
+
// In a real implementation, this would execute the actionCommand
|
|
48
|
+
// For now, allow simple "echo" for testing
|
|
49
|
+
if a.starts_with("echo ") {
|
|
50
|
+
let output = a.trim_start_matches("echo ").to_string();
|
|
51
|
+
return Ok(NodeState::from_str(&output));
|
|
52
|
+
}
|
|
53
|
+
Ok(state)
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
NodeDefinition::Condition { expr, .. } => {
|
|
58
|
+
let name_clone = name.clone();
|
|
59
|
+
let expr = expr.clone();
|
|
60
|
+
|
|
61
|
+
// Condition node evaluates expression and returns the result key
|
|
62
|
+
// (which matches an edge key)
|
|
63
|
+
graph = graph.add_node(name, move |state: NodeState| {
|
|
64
|
+
let n = name_clone.clone();
|
|
65
|
+
let e = expr.clone();
|
|
66
|
+
async move {
|
|
67
|
+
println!("❓ [Condition Node: {}] Expr: {}", n, e);
|
|
68
|
+
// Simple mock evaluation
|
|
69
|
+
// If input contains "error", return "error", else "ok"
|
|
70
|
+
let input = state.as_str().unwrap_or("");
|
|
71
|
+
if e.contains("contains('error')") {
|
|
72
|
+
if input.contains("error") {
|
|
73
|
+
return Ok(NodeState::from_str("error"));
|
|
74
|
+
} else {
|
|
75
|
+
return Ok(NodeState::from_str("ok"));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
Ok(NodeState::from_str("default"))
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
_ => {
|
|
83
|
+
return Err(anyhow!("Unsupported node type in yaml"));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 2. Add edges
|
|
89
|
+
for (name, node_def) in &def.nodes {
|
|
90
|
+
let edges = node_def.edges();
|
|
91
|
+
|
|
92
|
+
// Check if this is a conditional node (router)
|
|
93
|
+
// If it has multiple edges with keys other than "_default",
|
|
94
|
+
// valid keys are the outputs of the previous node.
|
|
95
|
+
|
|
96
|
+
// For Llm/Function nodes, usually they have a single "_default" edge
|
|
97
|
+
// or specific keys if they return structured data?
|
|
98
|
+
// The schema implies simple string matching on output.
|
|
99
|
+
|
|
100
|
+
let is_conditional = matches!(node_def, NodeDefinition::Condition { .. });
|
|
101
|
+
|
|
102
|
+
if is_conditional {
|
|
103
|
+
// Conditional edges based on node output
|
|
104
|
+
let edges_clone = edges.clone();
|
|
105
|
+
let router = move |output: &str| -> EdgeTarget {
|
|
106
|
+
if let Some(target) = edges_clone.get(output) {
|
|
107
|
+
if target == "END" {
|
|
108
|
+
EdgeTarget::End
|
|
109
|
+
} else {
|
|
110
|
+
EdgeTarget::Node(target.clone())
|
|
111
|
+
}
|
|
112
|
+
} else if let Some(default) = edges_clone.get("_default") {
|
|
113
|
+
if default == "END" {
|
|
114
|
+
EdgeTarget::End
|
|
115
|
+
} else {
|
|
116
|
+
EdgeTarget::Node(default.clone())
|
|
117
|
+
}
|
|
118
|
+
} else {
|
|
119
|
+
EdgeTarget::End
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
graph = graph.add_conditional_edge(name, router);
|
|
124
|
+
} else {
|
|
125
|
+
// Standard edges
|
|
126
|
+
// TODO: Support branching from non-condition nodes?
|
|
127
|
+
// For now, assume "_default" is the main edge
|
|
128
|
+
if let Some(target) = edges.get("_default") {
|
|
129
|
+
if target == "END" {
|
|
130
|
+
graph = graph.add_edge_to_end(name);
|
|
131
|
+
} else {
|
|
132
|
+
graph = graph.add_edge(name, target);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// 3. Set entry point
|
|
139
|
+
// Ideally schema allows defining it, or we use first node?
|
|
140
|
+
// Current StateGraph defaults to first node if not set.
|
|
141
|
+
// We could look for "start" or "input" node?
|
|
142
|
+
// The implementation_plan example didn't specify entry point explicitly.
|
|
143
|
+
// Let's assume the first defined node in YAML (but HashMap is unordered).
|
|
144
|
+
// Use "start" or "input" if present, else random?
|
|
145
|
+
// Better: require `triggers` or look for a node named "start".
|
|
146
|
+
|
|
147
|
+
if def.nodes.contains_key("start") {
|
|
148
|
+
graph = graph.set_entry_point("start");
|
|
149
|
+
} else if def.nodes.contains_key("input") {
|
|
150
|
+
graph = graph.set_entry_point("input");
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
Ok(graph)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//! Graph module - DAG execution engine and flow semantics
|
|
2
|
+
|
|
3
|
+
mod checkpoint;
|
|
4
|
+
mod checkpoint_store;
|
|
5
|
+
mod compiled;
|
|
6
|
+
mod edge;
|
|
7
|
+
mod graph_schema;
|
|
8
|
+
pub mod loader;
|
|
9
|
+
pub mod schema;
|
|
10
|
+
|
|
11
|
+
pub mod node;
|
|
12
|
+
|
|
13
|
+
pub use checkpoint::Checkpoint;
|
|
14
|
+
pub use checkpoint_store::{CheckpointStore, InMemoryCheckpointStore};
|
|
15
|
+
pub use compiled::CompiledGraph;
|
|
16
|
+
pub use edge::{Edge, EdgeTarget};
|
|
17
|
+
pub use graph_schema::StateGraph;
|
|
18
|
+
pub use node::{FunctionNode, Node, NodeState};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
//! Function node - wraps an async function as a node
|
|
2
|
+
|
|
3
|
+
use super::{Node, NodeState};
|
|
4
|
+
use async_trait::async_trait;
|
|
5
|
+
use std::future::Future;
|
|
6
|
+
use std::pin::Pin;
|
|
7
|
+
use std::sync::Arc;
|
|
8
|
+
|
|
9
|
+
/// Type alias for async node functions
|
|
10
|
+
pub type NodeFn = Arc<
|
|
11
|
+
dyn Fn(NodeState) -> Pin<Box<dyn Future<Output = anyhow::Result<NodeState>> + Send>>
|
|
12
|
+
+ Send
|
|
13
|
+
+ Sync,
|
|
14
|
+
>;
|
|
15
|
+
|
|
16
|
+
/// Function node - wraps an async closure
|
|
17
|
+
pub struct FunctionNode {
|
|
18
|
+
name: String,
|
|
19
|
+
func: NodeFn,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
impl FunctionNode {
|
|
23
|
+
pub fn new<F, Fut>(name: impl Into<String>, func: F) -> Self
|
|
24
|
+
where
|
|
25
|
+
F: Fn(NodeState) -> Fut + Send + Sync + 'static,
|
|
26
|
+
Fut: Future<Output = anyhow::Result<NodeState>> + Send + 'static,
|
|
27
|
+
{
|
|
28
|
+
let func = Arc::new(move |state: NodeState| {
|
|
29
|
+
let fut = func(state);
|
|
30
|
+
Box::pin(fut) as Pin<Box<dyn Future<Output = anyhow::Result<NodeState>> + Send>>
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
Self {
|
|
34
|
+
name: name.into(),
|
|
35
|
+
func,
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
#[async_trait]
|
|
41
|
+
impl Node for FunctionNode {
|
|
42
|
+
fn name(&self) -> &str {
|
|
43
|
+
&self.name
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async fn execute(&self, state: NodeState) -> anyhow::Result<NodeState> {
|
|
47
|
+
(self.func)(state).await
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
//! Node module - units of execution in a graph
|
|
2
|
+
|
|
3
|
+
mod function;
|
|
4
|
+
|
|
5
|
+
use async_trait::async_trait;
|
|
6
|
+
use serde_json::Value;
|
|
7
|
+
use std::sync::Arc;
|
|
8
|
+
|
|
9
|
+
pub use function::FunctionNode;
|
|
10
|
+
|
|
11
|
+
/// Node state - input/output passed between nodes
|
|
12
|
+
#[derive(Debug, Clone, Default)]
|
|
13
|
+
pub struct NodeState {
|
|
14
|
+
pub data: Value,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
impl NodeState {
|
|
18
|
+
pub fn new() -> Self {
|
|
19
|
+
Self { data: Value::Null }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
pub fn from_value(data: Value) -> Self {
|
|
23
|
+
Self { data }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
pub fn from_str(s: &str) -> Self {
|
|
27
|
+
Self {
|
|
28
|
+
data: Value::String(s.to_string()),
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
pub fn as_str(&self) -> Option<&str> {
|
|
33
|
+
self.data.as_str()
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/// Node trait - a single step in a graph
|
|
38
|
+
#[async_trait]
|
|
39
|
+
pub trait Node: Send + Sync {
|
|
40
|
+
/// Node name (unique within graph)
|
|
41
|
+
fn name(&self) -> &str;
|
|
42
|
+
|
|
43
|
+
/// Execute the node
|
|
44
|
+
async fn execute(&self, state: NodeState) -> anyhow::Result<NodeState>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/// Boxed node for dynamic dispatch
|
|
48
|
+
pub type DynNode = Arc<dyn Node>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
use serde::{Deserialize, Serialize};
|
|
2
|
+
use std::collections::HashMap;
|
|
3
|
+
|
|
4
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
5
|
+
pub struct GraphDefinition {
|
|
6
|
+
pub name: String,
|
|
7
|
+
#[serde(default = "default_version")]
|
|
8
|
+
pub version: String,
|
|
9
|
+
pub description: Option<String>,
|
|
10
|
+
#[serde(default)]
|
|
11
|
+
pub triggers: Vec<String>, // "manual", "cron:...", etc.
|
|
12
|
+
#[serde(default)]
|
|
13
|
+
pub inputs: HashMap<String, String>, // name -> description or type
|
|
14
|
+
pub nodes: HashMap<String, NodeDefinition>,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
fn default_version() -> String {
|
|
18
|
+
"1.0".to_string()
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
22
|
+
#[serde(tag = "type", rename_all = "snake_case")]
|
|
23
|
+
pub enum NodeDefinition {
|
|
24
|
+
/// LLM-based agent node
|
|
25
|
+
Llm {
|
|
26
|
+
#[serde(default)]
|
|
27
|
+
model: Option<String>, // e.g., "gpt-4o"
|
|
28
|
+
system_prompt: String,
|
|
29
|
+
#[serde(default)]
|
|
30
|
+
tools: Vec<String>, // tool names
|
|
31
|
+
#[serde(default)]
|
|
32
|
+
edges: HashMap<String, String>, // output matching -> target node
|
|
33
|
+
},
|
|
34
|
+
/// Function execution node (e.g., shell command, script)
|
|
35
|
+
Function {
|
|
36
|
+
action: String, // e.g., "scripts/check_style.sh"
|
|
37
|
+
#[serde(default)]
|
|
38
|
+
edges: HashMap<String, String>,
|
|
39
|
+
},
|
|
40
|
+
/// Conditional branching node (no LLM, just logic)
|
|
41
|
+
Condition {
|
|
42
|
+
expr: String, // e.g. "input.contains('error')"
|
|
43
|
+
edges: HashMap<String, String>, // "true" -> nodeA, "false" -> nodeB
|
|
44
|
+
},
|
|
45
|
+
/// Sub-graph execution
|
|
46
|
+
Graph {
|
|
47
|
+
graph_name: String,
|
|
48
|
+
#[serde(default)]
|
|
49
|
+
edges: HashMap<String, String>,
|
|
50
|
+
},
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
impl NodeDefinition {
|
|
54
|
+
pub fn edges(&self) -> &HashMap<String, String> {
|
|
55
|
+
match self {
|
|
56
|
+
NodeDefinition::Llm { edges, .. } => edges,
|
|
57
|
+
NodeDefinition::Function { edges, .. } => edges,
|
|
58
|
+
NodeDefinition::Condition { edges, .. } => edges,
|
|
59
|
+
NodeDefinition::Graph { edges, .. } => edges,
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|