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,248 @@
|
|
|
1
|
+
//! Sequential Flow - Execute callables one after another
|
|
2
|
+
//!
|
|
3
|
+
//! The output of each step becomes the input to the next.
|
|
4
|
+
|
|
5
|
+
use crate::callable::Callable;
|
|
6
|
+
use std::sync::Arc;
|
|
7
|
+
|
|
8
|
+
/// Sequential execution flow
|
|
9
|
+
pub struct SequentialFlow<C: Callable> {
|
|
10
|
+
/// Ordered list of callables to execute
|
|
11
|
+
steps: Vec<Arc<C>>,
|
|
12
|
+
/// Flow name
|
|
13
|
+
name: String,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
impl<C: Callable> SequentialFlow<C> {
|
|
17
|
+
/// Create a new sequential flow
|
|
18
|
+
pub fn new(name: impl Into<String>) -> Self {
|
|
19
|
+
Self {
|
|
20
|
+
steps: Vec::new(),
|
|
21
|
+
name: name.into(),
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/// Add a step to the flow
|
|
26
|
+
pub fn add_step(mut self, callable: Arc<C>) -> Self {
|
|
27
|
+
self.steps.push(callable);
|
|
28
|
+
self
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/// Add multiple steps
|
|
32
|
+
pub fn with_steps(mut self, callables: Vec<Arc<C>>) -> Self {
|
|
33
|
+
self.steps.extend(callables);
|
|
34
|
+
self
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/// Execute the flow
|
|
38
|
+
pub async fn execute(&self, input: &str) -> anyhow::Result<String> {
|
|
39
|
+
let mut current_output = input.to_string();
|
|
40
|
+
|
|
41
|
+
for step in &self.steps {
|
|
42
|
+
current_output = step.run(¤t_output).await?;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
Ok(current_output)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/// Get the flow name
|
|
49
|
+
pub fn name(&self) -> &str {
|
|
50
|
+
&self.name
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/// Get the number of steps
|
|
54
|
+
pub fn len(&self) -> usize {
|
|
55
|
+
self.steps.len()
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/// Check if empty
|
|
59
|
+
pub fn is_empty(&self) -> bool {
|
|
60
|
+
self.steps.is_empty()
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
#[cfg(test)]
|
|
65
|
+
mod tests {
|
|
66
|
+
use super::*;
|
|
67
|
+
use async_trait::async_trait;
|
|
68
|
+
|
|
69
|
+
/// Mock callable for testing - transforms input in a predictable way
|
|
70
|
+
struct MockCallable {
|
|
71
|
+
name: String,
|
|
72
|
+
transform: Box<dyn Fn(&str) -> String + Send + Sync>,
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
impl MockCallable {
|
|
76
|
+
fn new(name: &str, transform: impl Fn(&str) -> String + Send + Sync + 'static) -> Self {
|
|
77
|
+
Self {
|
|
78
|
+
name: name.to_string(),
|
|
79
|
+
transform: Box::new(transform),
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
fn uppercase(name: &str) -> Self {
|
|
84
|
+
Self::new(name, |s| s.to_uppercase())
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
fn append(name: &str, suffix: &'static str) -> Self {
|
|
88
|
+
Self::new(name, move |s| format!("{}{}", s, suffix))
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
fn prepend(name: &str, prefix: &'static str) -> Self {
|
|
92
|
+
Self::new(name, move |s| format!("{}{}", prefix, s))
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
#[async_trait]
|
|
97
|
+
impl Callable for MockCallable {
|
|
98
|
+
fn name(&self) -> &str {
|
|
99
|
+
&self.name
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async fn run(&self, input: &str) -> anyhow::Result<String> {
|
|
103
|
+
Ok((self.transform)(input))
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
#[tokio::test]
|
|
108
|
+
async fn test_sequential_empty() {
|
|
109
|
+
let flow: SequentialFlow<MockCallable> = SequentialFlow::new("empty");
|
|
110
|
+
assert!(flow.is_empty());
|
|
111
|
+
assert_eq!(flow.len(), 0);
|
|
112
|
+
|
|
113
|
+
let result = flow.execute("input").await.unwrap();
|
|
114
|
+
assert_eq!(result, "input"); // No steps = input passes through
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
#[tokio::test]
|
|
118
|
+
async fn test_sequential_single_step() {
|
|
119
|
+
let step = Arc::new(MockCallable::uppercase("upper"));
|
|
120
|
+
let flow = SequentialFlow::new("single").add_step(step);
|
|
121
|
+
|
|
122
|
+
assert_eq!(flow.len(), 1);
|
|
123
|
+
assert!(!flow.is_empty());
|
|
124
|
+
assert_eq!(flow.name(), "single");
|
|
125
|
+
|
|
126
|
+
let result = flow.execute("hello").await.unwrap();
|
|
127
|
+
assert_eq!(result, "HELLO");
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
#[tokio::test]
|
|
131
|
+
async fn test_sequential_multiple_steps() {
|
|
132
|
+
let flow = SequentialFlow::new("chain")
|
|
133
|
+
.add_step(Arc::new(MockCallable::uppercase("step1")))
|
|
134
|
+
.add_step(Arc::new(MockCallable::append("step2", "!")))
|
|
135
|
+
.add_step(Arc::new(MockCallable::prepend("step3", ">> ")));
|
|
136
|
+
|
|
137
|
+
assert_eq!(flow.len(), 3);
|
|
138
|
+
|
|
139
|
+
let result = flow.execute("hello").await.unwrap();
|
|
140
|
+
assert_eq!(result, ">> HELLO!");
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
#[tokio::test]
|
|
144
|
+
async fn test_sequential_with_steps() {
|
|
145
|
+
let steps = vec![
|
|
146
|
+
Arc::new(MockCallable::uppercase("s1")),
|
|
147
|
+
Arc::new(MockCallable::append("s2", "_done")),
|
|
148
|
+
];
|
|
149
|
+
let flow = SequentialFlow::new("batch").with_steps(steps);
|
|
150
|
+
|
|
151
|
+
assert_eq!(flow.len(), 2);
|
|
152
|
+
let result = flow.execute("test").await.unwrap();
|
|
153
|
+
assert_eq!(result, "TEST_done");
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
#[tokio::test]
|
|
157
|
+
async fn test_sequential_error_propagation() {
|
|
158
|
+
struct FailingCallable {
|
|
159
|
+
name: String,
|
|
160
|
+
fail_on_call: usize,
|
|
161
|
+
call_count: std::sync::atomic::AtomicUsize,
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
impl FailingCallable {
|
|
165
|
+
fn new(name: &str, fail_on: usize) -> Self {
|
|
166
|
+
Self {
|
|
167
|
+
name: name.to_string(),
|
|
168
|
+
fail_on_call: fail_on,
|
|
169
|
+
call_count: std::sync::atomic::AtomicUsize::new(0),
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
#[async_trait]
|
|
175
|
+
impl Callable for FailingCallable {
|
|
176
|
+
fn name(&self) -> &str {
|
|
177
|
+
&self.name
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async fn run(&self, input: &str) -> anyhow::Result<String> {
|
|
181
|
+
let n = self.call_count.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
|
|
182
|
+
if n >= self.fail_on_call {
|
|
183
|
+
anyhow::bail!("Intentional failure at step {}", n)
|
|
184
|
+
}
|
|
185
|
+
Ok(input.to_uppercase())
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Flow of 3 FailingCallable steps, second one fails
|
|
190
|
+
let flow = SequentialFlow::new("with_error")
|
|
191
|
+
.add_step(Arc::new(FailingCallable::new("step1", 10))) // Won't fail
|
|
192
|
+
.add_step(Arc::new(FailingCallable::new("step2", 0))) // Fails immediately
|
|
193
|
+
.add_step(Arc::new(FailingCallable::new("step3", 10))); // Never reached
|
|
194
|
+
|
|
195
|
+
let result = flow.execute("hello").await;
|
|
196
|
+
assert!(result.is_err());
|
|
197
|
+
assert!(result.unwrap_err().to_string().contains("Intentional failure"));
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
#[tokio::test]
|
|
201
|
+
async fn test_sequential_preserves_order() {
|
|
202
|
+
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
203
|
+
|
|
204
|
+
let counter = Arc::new(AtomicUsize::new(0));
|
|
205
|
+
let execution_order = Arc::new(std::sync::Mutex::new(Vec::new()));
|
|
206
|
+
|
|
207
|
+
struct OrderTracker {
|
|
208
|
+
name: String,
|
|
209
|
+
counter: Arc<AtomicUsize>,
|
|
210
|
+
order: Arc<std::sync::Mutex<Vec<usize>>>,
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
#[async_trait]
|
|
214
|
+
impl Callable for OrderTracker {
|
|
215
|
+
fn name(&self) -> &str {
|
|
216
|
+
&self.name
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async fn run(&self, input: &str) -> anyhow::Result<String> {
|
|
220
|
+
let n = self.counter.fetch_add(1, Ordering::SeqCst);
|
|
221
|
+
self.order.lock().unwrap().push(n);
|
|
222
|
+
Ok(input.to_string())
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
let flow = SequentialFlow::new("ordered")
|
|
227
|
+
.add_step(Arc::new(OrderTracker {
|
|
228
|
+
name: "first".to_string(),
|
|
229
|
+
counter: counter.clone(),
|
|
230
|
+
order: execution_order.clone(),
|
|
231
|
+
}))
|
|
232
|
+
.add_step(Arc::new(OrderTracker {
|
|
233
|
+
name: "second".to_string(),
|
|
234
|
+
counter: counter.clone(),
|
|
235
|
+
order: execution_order.clone(),
|
|
236
|
+
}))
|
|
237
|
+
.add_step(Arc::new(OrderTracker {
|
|
238
|
+
name: "third".to_string(),
|
|
239
|
+
counter: counter.clone(),
|
|
240
|
+
order: execution_order.clone(),
|
|
241
|
+
}));
|
|
242
|
+
|
|
243
|
+
flow.execute("test").await.unwrap();
|
|
244
|
+
|
|
245
|
+
let order = execution_order.lock().unwrap();
|
|
246
|
+
assert_eq!(*order, vec![0, 1, 2]);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
//! Checkpoint types for save/resume execution
|
|
2
|
+
|
|
3
|
+
use crate::kernel::{GraphId, RunId};
|
|
4
|
+
use serde::{Deserialize, Serialize};
|
|
5
|
+
use serde_json::Value;
|
|
6
|
+
use std::collections::HashMap;
|
|
7
|
+
use svix_ksuid::KsuidLike;
|
|
8
|
+
|
|
9
|
+
/// Checkpoint - saved state of execution
|
|
10
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
11
|
+
pub struct Checkpoint {
|
|
12
|
+
/// Unique checkpoint ID
|
|
13
|
+
pub id: String,
|
|
14
|
+
/// Run this checkpoint belongs to
|
|
15
|
+
pub run_id: RunId,
|
|
16
|
+
/// Graph being executed
|
|
17
|
+
pub graph_id: Option<GraphId>,
|
|
18
|
+
/// Current node in execution
|
|
19
|
+
pub current_node: Option<String>,
|
|
20
|
+
/// State at this checkpoint
|
|
21
|
+
pub state: Value,
|
|
22
|
+
/// Messages history (for LLM agents)
|
|
23
|
+
pub messages: Vec<MessageRecord>,
|
|
24
|
+
/// Tool results collected so far
|
|
25
|
+
pub tool_results: HashMap<String, Value>,
|
|
26
|
+
/// Created timestamp
|
|
27
|
+
pub created_at: chrono::DateTime<chrono::Utc>,
|
|
28
|
+
/// Metadata
|
|
29
|
+
pub metadata: HashMap<String, Value>,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/// Message record for checkpoint
|
|
33
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
34
|
+
pub struct MessageRecord {
|
|
35
|
+
pub role: String,
|
|
36
|
+
pub content: String,
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
impl Checkpoint {
|
|
40
|
+
/// Create a new checkpoint
|
|
41
|
+
pub fn new(run_id: RunId) -> Self {
|
|
42
|
+
Self {
|
|
43
|
+
id: format!("ckpt_{}", svix_ksuid::Ksuid::new(None, None)),
|
|
44
|
+
run_id,
|
|
45
|
+
graph_id: None,
|
|
46
|
+
current_node: None,
|
|
47
|
+
state: Value::Null,
|
|
48
|
+
messages: Vec::new(),
|
|
49
|
+
tool_results: HashMap::new(),
|
|
50
|
+
created_at: chrono::Utc::now(),
|
|
51
|
+
metadata: HashMap::new(),
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/// Set the current state
|
|
56
|
+
pub fn with_state(mut self, state: Value) -> Self {
|
|
57
|
+
self.state = state;
|
|
58
|
+
self
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/// Set the current node
|
|
62
|
+
pub fn with_node(mut self, node: impl Into<String>) -> Self {
|
|
63
|
+
self.current_node = Some(node.into());
|
|
64
|
+
self
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/// Add a message to history
|
|
68
|
+
pub fn add_message(&mut self, role: impl Into<String>, content: impl Into<String>) {
|
|
69
|
+
self.messages.push(MessageRecord {
|
|
70
|
+
role: role.into(),
|
|
71
|
+
content: content.into(),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/// Add a tool result
|
|
76
|
+
pub fn add_tool_result(&mut self, tool_name: impl Into<String>, result: Value) {
|
|
77
|
+
self.tool_results.insert(tool_name.into(), result);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
//! Checkpoint store trait and implementations
|
|
2
|
+
|
|
3
|
+
use super::Checkpoint;
|
|
4
|
+
use async_trait::async_trait;
|
|
5
|
+
use std::collections::HashMap;
|
|
6
|
+
use std::sync::{Arc, RwLock};
|
|
7
|
+
|
|
8
|
+
/// Checkpoint store trait
|
|
9
|
+
#[async_trait]
|
|
10
|
+
pub trait CheckpointStore: Send + Sync {
|
|
11
|
+
/// Save a checkpoint
|
|
12
|
+
async fn save(&self, checkpoint: Checkpoint) -> anyhow::Result<()>;
|
|
13
|
+
|
|
14
|
+
/// Load a checkpoint by ID
|
|
15
|
+
async fn load(&self, id: &str) -> anyhow::Result<Option<Checkpoint>>;
|
|
16
|
+
|
|
17
|
+
/// Load latest checkpoint for a run
|
|
18
|
+
async fn load_latest(&self, run_id: &str) -> anyhow::Result<Option<Checkpoint>>;
|
|
19
|
+
|
|
20
|
+
/// List checkpoints for a run
|
|
21
|
+
async fn list(&self, run_id: &str) -> anyhow::Result<Vec<Checkpoint>>;
|
|
22
|
+
|
|
23
|
+
/// Delete a checkpoint
|
|
24
|
+
async fn delete(&self, id: &str) -> anyhow::Result<()>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/// In-memory checkpoint store (for testing/development)
|
|
28
|
+
#[derive(Default)]
|
|
29
|
+
pub struct InMemoryCheckpointStore {
|
|
30
|
+
checkpoints: Arc<RwLock<HashMap<String, Checkpoint>>>,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
impl InMemoryCheckpointStore {
|
|
34
|
+
pub fn new() -> Self {
|
|
35
|
+
Self::default()
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
#[async_trait]
|
|
40
|
+
impl CheckpointStore for InMemoryCheckpointStore {
|
|
41
|
+
async fn save(&self, checkpoint: Checkpoint) -> anyhow::Result<()> {
|
|
42
|
+
let mut store = self.checkpoints.write().unwrap();
|
|
43
|
+
store.insert(checkpoint.id.clone(), checkpoint);
|
|
44
|
+
Ok(())
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async fn load(&self, id: &str) -> anyhow::Result<Option<Checkpoint>> {
|
|
48
|
+
let store = self.checkpoints.read().unwrap();
|
|
49
|
+
Ok(store.get(id).cloned())
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async fn load_latest(&self, run_id: &str) -> anyhow::Result<Option<Checkpoint>> {
|
|
53
|
+
let store = self.checkpoints.read().unwrap();
|
|
54
|
+
let latest = store
|
|
55
|
+
.values()
|
|
56
|
+
.filter(|c| c.run_id.as_str() == run_id)
|
|
57
|
+
.max_by_key(|c| c.created_at);
|
|
58
|
+
Ok(latest.cloned())
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async fn list(&self, run_id: &str) -> anyhow::Result<Vec<Checkpoint>> {
|
|
62
|
+
let store = self.checkpoints.read().unwrap();
|
|
63
|
+
let checkpoints: Vec<_> = store
|
|
64
|
+
.values()
|
|
65
|
+
.filter(|c| c.run_id.as_str() == run_id)
|
|
66
|
+
.cloned()
|
|
67
|
+
.collect();
|
|
68
|
+
Ok(checkpoints)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async fn delete(&self, id: &str) -> anyhow::Result<()> {
|
|
72
|
+
let mut store = self.checkpoints.write().unwrap();
|
|
73
|
+
store.remove(id);
|
|
74
|
+
Ok(())
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
//! CompiledGraph - validated and ready-to-execute graph
|
|
2
|
+
//!
|
|
3
|
+
//! Supports parallel execution when multiple targets are available.
|
|
4
|
+
|
|
5
|
+
use super::edge::{ConditionalEdge, Edge, EdgeTarget};
|
|
6
|
+
use super::node::{DynNode, NodeState};
|
|
7
|
+
use futures::future::join_all;
|
|
8
|
+
use std::collections::HashMap;
|
|
9
|
+
|
|
10
|
+
/// Compiled graph - validated and ready to execute
|
|
11
|
+
pub struct CompiledGraph {
|
|
12
|
+
pub(crate) nodes: HashMap<String, DynNode>,
|
|
13
|
+
pub(crate) edges: Vec<Edge>,
|
|
14
|
+
pub(crate) conditional_edges: Vec<ConditionalEdge>,
|
|
15
|
+
pub(crate) entry_point: String,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
impl CompiledGraph {
|
|
19
|
+
/// Get a node by name
|
|
20
|
+
pub fn get_node(&self, name: &str) -> Option<&DynNode> {
|
|
21
|
+
self.nodes.get(name)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/// Get the entry point
|
|
25
|
+
pub fn entry_point(&self) -> &str {
|
|
26
|
+
&self.entry_point
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/// Get the next node(s) after the given node
|
|
30
|
+
pub fn get_next(&self, from: &str, output: &str) -> Vec<EdgeTarget> {
|
|
31
|
+
let mut targets = Vec::new();
|
|
32
|
+
|
|
33
|
+
// Check conditional edges first
|
|
34
|
+
for ce in &self.conditional_edges {
|
|
35
|
+
if ce.from == from {
|
|
36
|
+
targets.push((ce.router)(output));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Then check regular edges
|
|
41
|
+
for edge in &self.edges {
|
|
42
|
+
if edge.from == from {
|
|
43
|
+
targets.push(edge.to.clone());
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
targets
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/// Run the graph with an initial input
|
|
51
|
+
pub async fn run(&self, input: impl Into<String>) -> anyhow::Result<NodeState> {
|
|
52
|
+
let initial_state = NodeState::from_str(&input.into());
|
|
53
|
+
self.run_with_state(initial_state).await
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/// Run the graph with an initial state
|
|
57
|
+
///
|
|
58
|
+
/// When multiple targets are available, executes them in parallel.
|
|
59
|
+
/// This implements the Agentic DAG execution model where independent
|
|
60
|
+
/// nodes can run concurrently.
|
|
61
|
+
pub async fn run_with_state(&self, initial_state: NodeState) -> anyhow::Result<NodeState> {
|
|
62
|
+
let mut current_node = self.entry_point.clone();
|
|
63
|
+
let mut state = initial_state;
|
|
64
|
+
|
|
65
|
+
loop {
|
|
66
|
+
// Get the current node
|
|
67
|
+
let node = self
|
|
68
|
+
.nodes
|
|
69
|
+
.get(¤t_node)
|
|
70
|
+
.ok_or_else(|| anyhow::anyhow!("Node '{}' not found", current_node))?;
|
|
71
|
+
|
|
72
|
+
// Execute the node
|
|
73
|
+
tracing::debug!(node = %current_node, "Executing node");
|
|
74
|
+
state = node.execute(state).await?;
|
|
75
|
+
|
|
76
|
+
// Get the output for routing
|
|
77
|
+
let output = state.as_str().unwrap_or_default().to_string();
|
|
78
|
+
|
|
79
|
+
// Find next node(s)
|
|
80
|
+
let next_targets = self.get_next(¤t_node, &output);
|
|
81
|
+
|
|
82
|
+
if next_targets.is_empty() {
|
|
83
|
+
// No outgoing edges - end execution
|
|
84
|
+
tracing::debug!(node = %current_node, "No outgoing edges, ending");
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Check for END target
|
|
89
|
+
let has_end = next_targets.iter().any(|t| matches!(t, EdgeTarget::End));
|
|
90
|
+
if has_end {
|
|
91
|
+
tracing::debug!("Reached END");
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Collect node targets (filter out End)
|
|
96
|
+
let node_targets: Vec<String> = next_targets
|
|
97
|
+
.iter()
|
|
98
|
+
.filter_map(|t| match t {
|
|
99
|
+
EdgeTarget::Node(n) => Some(n.clone()),
|
|
100
|
+
EdgeTarget::End => None,
|
|
101
|
+
})
|
|
102
|
+
.collect();
|
|
103
|
+
|
|
104
|
+
if node_targets.is_empty() {
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Single target - sequential execution
|
|
109
|
+
if node_targets.len() == 1 {
|
|
110
|
+
current_node = node_targets[0].clone();
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Multiple targets - PARALLEL EXECUTION
|
|
115
|
+
tracing::debug!(
|
|
116
|
+
targets = ?node_targets,
|
|
117
|
+
"Executing {} nodes in parallel",
|
|
118
|
+
node_targets.len()
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
// Execute all target nodes in parallel
|
|
122
|
+
let parallel_results = self
|
|
123
|
+
.execute_nodes_parallel(&node_targets, state.clone())
|
|
124
|
+
.await?;
|
|
125
|
+
|
|
126
|
+
// Aggregate results: combine all outputs
|
|
127
|
+
// For now, we use the last successful result as the state
|
|
128
|
+
// In a full implementation, this would support custom aggregation strategies
|
|
129
|
+
if let Some(last_state) = parallel_results.into_iter().last() {
|
|
130
|
+
state = last_state;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// After parallel execution, check if any nodes have outgoing edges
|
|
134
|
+
// For simplicity, we end after parallel execution
|
|
135
|
+
// A full implementation would continue with fan-in logic
|
|
136
|
+
tracing::debug!("Parallel execution complete");
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
Ok(state)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/// Execute multiple nodes in parallel
|
|
144
|
+
///
|
|
145
|
+
/// Returns results from all nodes that completed successfully.
|
|
146
|
+
async fn execute_nodes_parallel(
|
|
147
|
+
&self,
|
|
148
|
+
node_names: &[String],
|
|
149
|
+
input_state: NodeState,
|
|
150
|
+
) -> anyhow::Result<Vec<NodeState>> {
|
|
151
|
+
let futures: Vec<_> = node_names
|
|
152
|
+
.iter()
|
|
153
|
+
.filter_map(|name| {
|
|
154
|
+
self.nodes.get(name).map(|node| {
|
|
155
|
+
let state = input_state.clone();
|
|
156
|
+
let node_name = name.clone();
|
|
157
|
+
async move {
|
|
158
|
+
tracing::debug!(node = %node_name, "Executing parallel node");
|
|
159
|
+
node.execute(state).await
|
|
160
|
+
}
|
|
161
|
+
})
|
|
162
|
+
})
|
|
163
|
+
.collect();
|
|
164
|
+
|
|
165
|
+
let results = join_all(futures).await;
|
|
166
|
+
|
|
167
|
+
// Collect successful results
|
|
168
|
+
let successful: Vec<NodeState> = results
|
|
169
|
+
.into_iter()
|
|
170
|
+
.filter_map(|r| r.ok())
|
|
171
|
+
.collect();
|
|
172
|
+
|
|
173
|
+
if successful.is_empty() {
|
|
174
|
+
anyhow::bail!("All parallel nodes failed");
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
Ok(successful)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/// Get node count
|
|
181
|
+
pub fn node_count(&self) -> usize {
|
|
182
|
+
self.nodes.len()
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/// Get edge count
|
|
186
|
+
pub fn edge_count(&self) -> usize {
|
|
187
|
+
self.edges.len() + self.conditional_edges.len()
|
|
188
|
+
}
|
|
189
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
//! Edge types for graph connections
|
|
2
|
+
|
|
3
|
+
/// Target for an edge - either a specific node or END
|
|
4
|
+
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
5
|
+
pub enum EdgeTarget {
|
|
6
|
+
/// Target a specific node by name
|
|
7
|
+
Node(String),
|
|
8
|
+
/// End the graph execution
|
|
9
|
+
End,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
impl EdgeTarget {
|
|
13
|
+
pub fn node(name: impl Into<String>) -> Self {
|
|
14
|
+
Self::Node(name.into())
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
pub fn end() -> Self {
|
|
18
|
+
Self::End
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
pub fn is_end(&self) -> bool {
|
|
22
|
+
matches!(self, Self::End)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/// Edge in the graph
|
|
27
|
+
#[derive(Debug, Clone)]
|
|
28
|
+
pub struct Edge {
|
|
29
|
+
pub from: String,
|
|
30
|
+
pub to: EdgeTarget,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
impl Edge {
|
|
34
|
+
pub fn new(from: impl Into<String>, to: EdgeTarget) -> Self {
|
|
35
|
+
Self {
|
|
36
|
+
from: from.into(),
|
|
37
|
+
to,
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/// Conditional edge - routes based on a function
|
|
43
|
+
#[derive(Clone)]
|
|
44
|
+
pub struct ConditionalEdge {
|
|
45
|
+
pub from: String,
|
|
46
|
+
pub router: ConditionalRouter,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/// Router function type
|
|
50
|
+
pub type ConditionalRouter = std::sync::Arc<dyn Fn(&str) -> EdgeTarget + Send + Sync>;
|
|
51
|
+
|
|
52
|
+
impl std::fmt::Debug for ConditionalEdge {
|
|
53
|
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
54
|
+
f.debug_struct("ConditionalEdge")
|
|
55
|
+
.field("from", &self.from)
|
|
56
|
+
.field("router", &"<fn>")
|
|
57
|
+
.finish()
|
|
58
|
+
}
|
|
59
|
+
}
|