reactifact 0.12.0__tar.gz → 0.14.0__tar.gz
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.
- {reactifact-0.12.0/reactifact.egg-info → reactifact-0.14.0}/PKG-INFO +62 -31
- {reactifact-0.12.0 → reactifact-0.14.0}/README.md +59 -30
- {reactifact-0.12.0 → reactifact-0.14.0}/pyproject.toml +14 -2
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/__init__.py +11 -3
- reactifact-0.14.0/reactifact/a2a/__init__.py +77 -0
- reactifact-0.14.0/reactifact/a2a/client.py +282 -0
- reactifact-0.14.0/reactifact/a2a/models.py +294 -0
- reactifact-0.14.0/reactifact/a2a/remote.py +178 -0
- reactifact-0.14.0/reactifact/a2a/server.py +381 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/artifacts.py +37 -12
- reactifact-0.14.0/reactifact/authz.py +112 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/branching.py +30 -2
- reactifact-0.14.0/reactifact/budget.py +166 -0
- reactifact-0.14.0/reactifact/cache.py +167 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/chat.py +108 -7
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/cli/__init__.py +7 -2
- reactifact-0.14.0/reactifact/cli/eval.py +126 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/cli/replay.py +10 -0
- reactifact-0.14.0/reactifact/cli/scenario.py +192 -0
- reactifact-0.14.0/reactifact/cli/skills.py +173 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/commit_log.py +142 -16
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/context.py +138 -2
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/effects.py +27 -0
- reactifact-0.14.0/reactifact/eval/__init__.py +165 -0
- reactifact-0.14.0/reactifact/eval/dataset.py +144 -0
- reactifact-0.14.0/reactifact/eval/evaluators.py +208 -0
- reactifact-0.14.0/reactifact/eval/judge.py +223 -0
- reactifact-0.14.0/reactifact/eval/online.py +435 -0
- reactifact-0.14.0/reactifact/eval/runner.py +322 -0
- reactifact-0.12.0/reactifact/eval.py → reactifact-0.14.0/reactifact/eval/scoring.py +88 -5
- reactifact-0.14.0/reactifact/events.py +77 -0
- reactifact-0.14.0/reactifact/guardrails.py +272 -0
- reactifact-0.14.0/reactifact/interrupt.py +54 -0
- reactifact-0.14.0/reactifact/logging.py +222 -0
- reactifact-0.14.0/reactifact/metrics.py +294 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/operations.py +18 -16
- reactifact-0.14.0/reactifact/pricing.py +33 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/anthropic.py +18 -1
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/chat.py +15 -4
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/contracts.py +4 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/fake.py +10 -2
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/gemini.py +9 -0
- reactifact-0.14.0/reactifact/quota.py +215 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/recipes/__init__.py +27 -0
- reactifact-0.14.0/reactifact/recipes/conversation.py +302 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/replay.py +7 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/resources.py +92 -4
- reactifact-0.14.0/reactifact/routing.py +94 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/runtime.py +492 -37
- reactifact-0.14.0/reactifact/skills/reactifact/SKILL.md +165 -0
- reactifact-0.14.0/reactifact/skills/reactifact/references/effects-and-state.md +102 -0
- reactifact-0.14.0/reactifact/skills/reactifact/references/trust-and-safety.md +96 -0
- reactifact-0.14.0/reactifact/skills/reactifact-agents/SKILL.md +156 -0
- reactifact-0.14.0/reactifact/skills/reactifact-agents/references/consume.md +109 -0
- reactifact-0.14.0/reactifact/skills/reactifact-agents/references/effects.md +82 -0
- reactifact-0.14.0/reactifact/skills/reactifact-eval/SKILL.md +160 -0
- reactifact-0.14.0/reactifact/skills/reactifact-eval/references/online.md +72 -0
- reactifact-0.14.0/reactifact/skills/reactifact-from-langchain/SKILL.md +102 -0
- reactifact-0.14.0/reactifact/skills/reactifact-llm/SKILL.md +159 -0
- reactifact-0.14.0/reactifact/skills/reactifact-llm/references/providers.md +77 -0
- reactifact-0.14.0/reactifact/skills/reactifact-llm/references/tools.md +83 -0
- reactifact-0.14.0/reactifact/skills/reactifact-observability/SKILL.md +154 -0
- reactifact-0.14.0/reactifact/skills/reactifact-rag/SKILL.md +150 -0
- reactifact-0.14.0/reactifact/skills/reactifact-rag/references/sources.md +103 -0
- reactifact-0.14.0/reactifact/skills/reactifact-testing/SKILL.md +114 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/testing/__init__.py +13 -2
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/testing/assertions.py +116 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/testing/fault.py +26 -7
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/testing/golden.py +71 -1
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/testing/lab.py +250 -12
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/testing/mock.py +80 -25
- reactifact-0.14.0/reactifact/testing/pytest_plugin.py +69 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/tool_use.py +29 -3
- reactifact-0.14.0/reactifact/types.py +168 -0
- {reactifact-0.12.0 → reactifact-0.14.0/reactifact.egg-info}/PKG-INFO +62 -31
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact.egg-info/SOURCES.txt +70 -1
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact.egg-info/requires.txt +3 -0
- reactifact-0.14.0/tests/test_a2a.py +347 -0
- reactifact-0.14.0/tests/test_a2a_example.py +16 -0
- reactifact-0.14.0/tests/test_a2a_interop.py +94 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_anthropic_provider.py +31 -0
- reactifact-0.14.0/tests/test_authz.py +120 -0
- reactifact-0.14.0/tests/test_cache.py +171 -0
- reactifact-0.14.0/tests/test_chat_example.py +18 -0
- reactifact-0.14.0/tests/test_chat_memory.py +142 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_cli.py +2 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_cli_scenario.py +24 -6
- reactifact-0.14.0/tests/test_compaction.py +153 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_consumes_produces.py +1 -1
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_devops.py +81 -1
- reactifact-0.14.0/tests/test_devops_online.py +114 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_devops_web.py +15 -0
- reactifact-0.14.0/tests/test_eval_dataset.py +293 -0
- reactifact-0.14.0/tests/test_eval_judge.py +131 -0
- reactifact-0.14.0/tests/test_eval_online.py +319 -0
- reactifact-0.14.0/tests/test_eval_trajectory.py +140 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_gemini_provider.py +27 -0
- reactifact-0.14.0/tests/test_guardrails.py +138 -0
- reactifact-0.14.0/tests/test_logging.py +147 -0
- reactifact-0.14.0/tests/test_metrics.py +207 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_openai_provider.py +40 -0
- reactifact-0.14.0/tests/test_outbox.py +65 -0
- reactifact-0.14.0/tests/test_pending_actions.py +256 -0
- reactifact-0.14.0/tests/test_public_api.py +285 -0
- reactifact-0.14.0/tests/test_quota.py +214 -0
- reactifact-0.14.0/tests/test_recipe_conversation.py +154 -0
- reactifact-0.14.0/tests/test_routing.py +160 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_runtime_errors.py +86 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_sessions.py +68 -3
- reactifact-0.14.0/tests/test_shutdown.py +200 -0
- reactifact-0.14.0/tests/test_skills.py +106 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_starter_app.py +5 -0
- reactifact-0.14.0/tests/test_testing_golden.py +135 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_testing_lab.py +98 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_testing_mock.py +74 -0
- reactifact-0.14.0/tests/test_testing_provenance.py +91 -0
- reactifact-0.14.0/tests/test_testing_pytest_plugin.py +48 -0
- reactifact-0.14.0/tests/test_token_budget.py +182 -0
- reactifact-0.14.0/tests/test_type_registry.py +178 -0
- reactifact-0.12.0/reactifact/budget.py +0 -43
- reactifact-0.12.0/reactifact/cli/scenario.py +0 -94
- reactifact-0.12.0/reactifact/events.py +0 -34
- reactifact-0.12.0/reactifact/interrupt.py +0 -22
- {reactifact-0.12.0 → reactifact-0.14.0}/LICENSE +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/__main__.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/_extras.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/_httpx.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/agent_tool.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/agents.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/audit.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/checkpoints.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/cli/branch.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/cli/common.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/cli/context.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/cli/graph.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/cli/trace.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/commit.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/consume.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/context_builder.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/llm_agent.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/mcp/__init__.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/mcp/client.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/mcp/oauth.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/mcp/server.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/native_tool_use.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/patches.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/produce.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/prompts.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/__init__.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/_retry.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/azure.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/cerebras.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/deepseek.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/fireworks.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/github_models.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/groq.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/image.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/mistral.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/nvidia.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/ollama.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/openai.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/openrouter.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/perplexity.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/qwen.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/speech.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/together.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/video.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/xai.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/zai.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/py.typed +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/quick/__init__.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/quick/_shared.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/quick/agent.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/quick/chat.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/quick/models.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/quick/rag.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/quick/tools_agent.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/recipes/cleanup.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/recipes/identity.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/recipes/inputs.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/recipes/memory.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/recipes/plan_execute.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/recipes/reflection.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/recipes/resolve.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/recipes/rollback.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/recipes/search.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/recipes/skills.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/recipes/status.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/recipes/supervisor.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/recipes/text.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/recipes/tool_loop.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/redaction.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/relations.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/request.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/scheduler.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/session.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/sources.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/streaming.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/structured.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/testing/exceptions.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/testing/record.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/testing/registry.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/tools.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/tracing/__init__.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/tracing/_otlp.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/tracing/columns.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/tracing/langfuse.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/tracing/models.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/tracing/otlp.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/tracing/postgres.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/tracing/store.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/tracing/templates/app.css +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/tracing/templates/sessions.html +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/tracing/templates/ui.html +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/tracing/templates/ui_run.html +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/tracing/tracer.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/tracing/web.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/triggers.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/verify.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/viz.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/web.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact.egg-info/dependency_links.txt +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact.egg-info/entry_points.txt +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/reactifact.egg-info/top_level.txt +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/setup.cfg +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_absent_consume.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_adaptive.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_agent_tool.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_artifacts.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_audit.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_backbone.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_branching.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_budget.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_canonical_ports.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_chat_web.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_checkpoint.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_checkpoints_concurrency.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_commit_log.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_concurrency.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_context_builder.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_correlated_consume.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_debounce.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_determinism.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_effects.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_eval.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_fintech_audit.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_forklab.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_forklab_web.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_friendly_api.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_golden.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_hitl.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_image_provider.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_incident_commander.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_invalidation.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_join_consume.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_knowledge.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_knowledge_web.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_llm_ladder.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_loop_bound_client.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_mcp.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_medic_lab.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_medic_lab_web.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_multisource.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_native_tool_use.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_patches.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_ports.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_produce_styles.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_produce_trigger.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_prompts.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_provider_auth.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_provider_retry.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_providers_integration.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_quick.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_reacts_to.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_recipe_plan_execute.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_recipe_reflection.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_recipe_supervisor.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_recipe_tool_loop.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_recipes.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_recipes_inputs.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_recipes_memory.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_recipes_skills.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_redaction.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_relation_graph.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_relations.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_repair.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_repair_web.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_replay.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_repo_agent.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_request.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_research.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_resources.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_retry.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_runtime.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_scheduler_semantics.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_sources.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_sources_search.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_sources_vector.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_speech_provider.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_streaming.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_structured.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_support_copilot.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_testing_assertions.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_testing_registry.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_tools.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_trace_columns.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_trace_tags.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_tracing.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_vendor_factories.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_vendor_multimodal_factories.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_verify.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_video_provider.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_view.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_viz.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_web_source.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_workspace.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_workspace_with_sources.py +0 -0
- {reactifact-0.12.0 → reactifact-0.14.0}/tests/tests_checkpoints_sqlite.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: reactifact
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.14.0
|
|
4
4
|
Summary: Reactive, artifact-driven agent runtime: agents transform versioned, typed, provenance-aware artifacts inside an evolving context
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/bzdvdn/reactifact
|
|
@@ -36,6 +36,8 @@ Provides-Extra: pg
|
|
|
36
36
|
Requires-Dist: psycopg[binary]>=3.2; extra == "pg"
|
|
37
37
|
Provides-Extra: mcp
|
|
38
38
|
Requires-Dist: mcp>=2.2; extra == "mcp"
|
|
39
|
+
Provides-Extra: a2a
|
|
40
|
+
Requires-Dist: a2a-sdk>=1.0; extra == "a2a"
|
|
39
41
|
Provides-Extra: docs
|
|
40
42
|
Requires-Dist: mkdocs>=1.6; extra == "docs"
|
|
41
43
|
Requires-Dist: mkdocs-material>=9.5; extra == "docs"
|
|
@@ -46,7 +48,7 @@ Dynamic: license-file
|
|
|
46
48
|
<img src="docs/img/reactifact-hero.png" alt="reactifact — Agents that react to artifacts, not graphs" width="800">
|
|
47
49
|
</p>
|
|
48
50
|
|
|
49
|
-
**
|
|
51
|
+
**Auditable agents for Python.** Build **knowledge assistants** over docs, spreadsheets and the web — where the path per question isn't a graph you can draw. Every answer is computed, versioned and reproducible — not a string you have to trust.
|
|
50
52
|
|
|
51
53
|
[](https://github.com/bzdvdn/reactifact/actions/workflows/ci.yml)
|
|
52
54
|
[](https://codecov.io/gh/bzdvdn/reactifact)
|
|
@@ -56,29 +58,15 @@ Dynamic: license-file
|
|
|
56
58
|
[](https://deepwiki.com/bzdvdn/reactifact)
|
|
57
59
|
[](https://bzdvdn.github.io/reactifact/)
|
|
58
60
|
|
|
59
|
-
Python
|
|
60
|
-
|
|
61
|
-
reacts to a **typed, versioned artifact** appearing in the context, not to a
|
|
62
|
-
queue message you push or a graph edge you draw. The runtime derives what runs
|
|
63
|
-
next from state.
|
|
61
|
+
> **Is:** a Python library (3 core deps) · single-process · typed, versioned artifacts with provenance · deterministic replay · runs offline, no API key
|
|
62
|
+
> **Isn't:** a managed platform · a distributed task queue · a pre-built agent/tool marketplace
|
|
64
63
|
|
|
65
|
-
|
|
66
|
-
| --- | --- |
|
|
67
|
-
| a task | `@produce(Model)` — a unit of work that writes an artifact |
|
|
68
|
-
| `delay()` / `apply_async()` | you don't call it: creating the input artifact **is** the trigger |
|
|
69
|
-
| routing key / queue | `Consume(Type)` — which artifact type wakes the task |
|
|
70
|
-
| chain / group / chord | several `consumes` / `produces`; the runtime derives the order |
|
|
71
|
-
| retries, `acks_late` | guards + `Budget`, an honest `None` instead of a wrong result |
|
|
72
|
-
| result backend | the `Context` — typed, versioned artifacts |
|
|
73
|
-
| worker | `Runtime` |
|
|
64
|
+
A question like *"why did infra costs jump in Q2?"* needs a spreadsheet, a policy doc and a page off the web — and the *next* question needs a different subset. That is not one graph you can draw up front. You declare what artifacts exist and what agents can do with them; the runtime derives the order from state.
|
|
74
65
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
**versioned with provenance**, so a run is reproducible (`context_hash`) and
|
|
80
|
-
auditable (`audit.report`) for free. The model reasons; the arithmetic stays
|
|
81
|
-
deterministic; every claim carries provenance.
|
|
66
|
+
Every derived artifact then links to what produced it
|
|
67
|
+
(`Answer —supported_by→ Evidence —extracted_from→ Doc`), so *"why did it say
|
|
68
|
+
that?"* is a query, not a guess — and the run is reproducible (`context_hash`)
|
|
69
|
+
and verifiable (`reactifact replay --verify`).
|
|
82
70
|
|
|
83
71
|
## On top: a provable answer
|
|
84
72
|
|
|
@@ -130,6 +118,14 @@ print(report_to_markdown(build_report(ctx, answer))) # hash per artifact + edge
|
|
|
130
118
|
print(context_hash(ctx)) # reproducible fingerprint
|
|
131
119
|
```
|
|
132
120
|
|
|
121
|
+
## Knowledge assistants, live
|
|
122
|
+
|
|
123
|
+
The same shape over many sources, not a fixed path — the
|
|
124
|
+
[`knowledge`](examples/knowledge) CLI answers a harder, multi-source question
|
|
125
|
+
(docs + a CSV) with a real computed number and its sources, no LLM key required:
|
|
126
|
+
|
|
127
|
+

|
|
128
|
+
|
|
133
129
|

|
|
134
130
|
|
|
135
131
|
Two agents explore independently on their own forks and merge back automatically
|
|
@@ -137,6 +133,31 @@ Two agents explore independently on their own forks and merge back automatically
|
|
|
137
133
|
|
|
138
134
|

|
|
139
135
|
|
|
136
|
+
<details>
|
|
137
|
+
<summary><strong>If you know Celery…</strong> — the programming model, not its distributed runtime</summary>
|
|
138
|
+
|
|
139
|
+
Python developers already know this model from Celery: define a **task**,
|
|
140
|
+
declare what triggers it, let the runtime run it. reactifact applies it to
|
|
141
|
+
agents — a task reacts to a **typed, versioned artifact** appearing in the
|
|
142
|
+
context, not to a queue message you push or a graph edge you draw. The runtime
|
|
143
|
+
derives what runs next from state.
|
|
144
|
+
|
|
145
|
+
| Celery | reactifact |
|
|
146
|
+
| --- | --- |
|
|
147
|
+
| a task | `@produce(Model)` — a unit of work that writes an artifact |
|
|
148
|
+
| `delay()` / `apply_async()` | you don't call it: creating the input artifact **is** the trigger |
|
|
149
|
+
| routing key / queue | `Consume(Type)` — which artifact type wakes the task |
|
|
150
|
+
| chain / group / chord | several `consumes` / `produces`; the runtime derives the order |
|
|
151
|
+
| result backend | the `Context` — typed, versioned artifacts |
|
|
152
|
+
| worker | `Runtime` |
|
|
153
|
+
|
|
154
|
+
**Not inherited:** a broker, a worker pool, `acks_late`/redelivery, cross-process
|
|
155
|
+
exactly-once. reactifact is a library with an in-process runtime — the *model* is
|
|
156
|
+
Celery-shaped, the deployment is not. (Session-backed runs do survive a restart;
|
|
157
|
+
see [Durability & resume](docs/en/durability.md).)
|
|
158
|
+
|
|
159
|
+
</details>
|
|
160
|
+
|
|
140
161
|
```bash
|
|
141
162
|
pip install reactifact
|
|
142
163
|
```
|
|
@@ -203,12 +224,6 @@ print(answer.data.text) # "Refunds are available within 14 d
|
|
|
203
224
|
print("supported_by:", evidence.data.text) # provenance you can trace, not just a string in a log
|
|
204
225
|
```
|
|
205
226
|
|
|
206
|
-
The same idea, live — the [`knowledge`](examples/knowledge) example's CLI answering a
|
|
207
|
-
harder, multi-source question (docs + a CSV) with a real computed number and
|
|
208
|
-
its sources, no LLM key required:
|
|
209
|
-
|
|
210
|
-

|
|
211
|
-
|
|
212
227
|
## How it works
|
|
213
228
|
|
|
214
229
|
```text
|
|
@@ -276,13 +291,19 @@ by hand. Run it yourself: `uv run python -m examples.ledger.main`.
|
|
|
276
291
|
- **Provenance** — every derived artifact links to what produced it
|
|
277
292
|
(`Answer —supported_by→ Claim —derived_from→ Evidence —extracted_from→ Doc`).
|
|
278
293
|
- **HITL** — humans as `effects.ask(...)` → `PendingQuestion`, answered via `effects.resume(...)` like any agent.
|
|
294
|
+
- **Outbox** — outbound side effects as `effects.act(...)` → `PendingAction`, performed once *after* the commit by `Runtime(dispatcher=...)`; safe across replay, retries and merged branches.
|
|
279
295
|
|
|
280
296
|
## In the box
|
|
281
297
|
|
|
282
298
|
- **Deterministic by design** — calculations over structured data, honest `None`
|
|
283
299
|
fallbacks instead of hallucinated answers; the model reasons, never "knows".
|
|
284
300
|
- **Observability** — every run traces agent spans, reads/writes, LLM calls,
|
|
285
|
-
tokens: SQLite store + web dashboard, exportable to Langfuse/Postgres (async sinks)
|
|
301
|
+
tokens: SQLite store + web dashboard, exportable to Langfuse/Postgres (async sinks);
|
|
302
|
+
**correlated logs** (`configure_logging()`, silent until called) carry
|
|
303
|
+
`run_id`/`session_id`/`agent` on every line.
|
|
304
|
+
- **Lifecycle** — `Budget.max_seconds` is a hard deadline (a hung LLM call is
|
|
305
|
+
cancelled); `request_stop()`/`ashutdown()` stop at a generation boundary
|
|
306
|
+
(`RunOutcome.STOPPED`); `active_runs()`/`in_flight` give a readiness view.
|
|
286
307
|
- **MCP, both ways** — call any MCP server's tools as a `Tool`
|
|
287
308
|
(`mcp_stdio_tools`/`mcp_http_tools`), or expose your own `Tool`s and a live
|
|
288
309
|
`Context` as an MCP server (`create_mcp_server`) for Claude Desktop, Claude
|
|
@@ -313,7 +334,7 @@ uv run python ./examples/devops/web.py # HITL ops assistant + trace dash
|
|
|
313
334
|
```
|
|
314
335
|
|
|
315
336
|
Classic-pattern ports run as one-liners too:
|
|
316
|
-
`python -m examples.{reflection,map_reduce,supervisor,summarize,time_travel,plan_execute,adaptive,ledger}.main`.
|
|
337
|
+
`python -m examples.{reflection,map_reduce,supervisor,summarize,time_travel,plan_execute,adaptive,ledger,outbox}.main`.
|
|
317
338
|
|
|
318
339
|
Prefer a notebook? [](https://colab.research.google.com/github/bzdvdn/reactifact/blob/master/examples/quickstart.ipynb)
|
|
319
340
|
— `examples/quickstart.ipynb` covers the four `quick` cases (`agent`/`rag`/`tools_agent`/`chat_agent`), offline by default.
|
|
@@ -325,8 +346,10 @@ Prefer a notebook? [.
|
|
349
|
+
- `chat` — minimal offline single-model multi-turn conversation (`Conversation` + bounded `ChatMemory`).
|
|
328
350
|
- `forklab` — deterministic branch & merge: two strategies on their own forks, three-way merge.
|
|
329
351
|
- `ledger` — offline proof of reactive recompute: edit one fact, only its real `Consume`rs re-run.
|
|
352
|
+
- `outbox` — one intent, one send: `effects.act(...)` + `Runtime(dispatcher=...)`, safe across replays, retries and merged branches (§42).
|
|
330
353
|
- `llm_ladder` — the workflow from one LLM call to state-changing patches (3 levels).
|
|
331
354
|
- `adaptive` — hybrid scheduler: rule filters + deterministic rank + LLM tie-break + `rank_limit`.
|
|
332
355
|
- `{reflection,map_reduce,supervisor,summarize,time_travel,plan_execute}` — canonical ports (see [port-matrix](docs/en/port-matrix.md)).
|
|
@@ -355,6 +378,14 @@ uv sync --extra dev --extra web
|
|
|
355
378
|
.venv/bin/ruff check
|
|
356
379
|
```
|
|
357
380
|
|
|
381
|
+
## Status & contributing
|
|
382
|
+
|
|
383
|
+
Active, `0.14.x`, pre-1.0 — the API is stabilizing and `1.0` will freeze it
|
|
384
|
+
(see the [roadmap](docs/roadmap.md)); releases follow
|
|
385
|
+
[Keep a Changelog](CHANGELOG.md). Questions and design discussion live in
|
|
386
|
+
[GitHub Discussions](https://github.com/bzdvdn/reactifact/discussions); a star,
|
|
387
|
+
or a `good first issue`, helps more than you'd think.
|
|
388
|
+
|
|
358
389
|
## License
|
|
359
390
|
|
|
360
391
|
MIT — see [LICENSE](LICENSE).
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<img src="docs/img/reactifact-hero.png" alt="reactifact — Agents that react to artifacts, not graphs" width="800">
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
|
-
**
|
|
5
|
+
**Auditable agents for Python.** Build **knowledge assistants** over docs, spreadsheets and the web — where the path per question isn't a graph you can draw. Every answer is computed, versioned and reproducible — not a string you have to trust.
|
|
6
6
|
|
|
7
7
|
[](https://github.com/bzdvdn/reactifact/actions/workflows/ci.yml)
|
|
8
8
|
[](https://codecov.io/gh/bzdvdn/reactifact)
|
|
@@ -12,29 +12,15 @@
|
|
|
12
12
|
[](https://deepwiki.com/bzdvdn/reactifact)
|
|
13
13
|
[](https://bzdvdn.github.io/reactifact/)
|
|
14
14
|
|
|
15
|
-
Python
|
|
16
|
-
|
|
17
|
-
reacts to a **typed, versioned artifact** appearing in the context, not to a
|
|
18
|
-
queue message you push or a graph edge you draw. The runtime derives what runs
|
|
19
|
-
next from state.
|
|
15
|
+
> **Is:** a Python library (3 core deps) · single-process · typed, versioned artifacts with provenance · deterministic replay · runs offline, no API key
|
|
16
|
+
> **Isn't:** a managed platform · a distributed task queue · a pre-built agent/tool marketplace
|
|
20
17
|
|
|
21
|
-
|
|
22
|
-
| --- | --- |
|
|
23
|
-
| a task | `@produce(Model)` — a unit of work that writes an artifact |
|
|
24
|
-
| `delay()` / `apply_async()` | you don't call it: creating the input artifact **is** the trigger |
|
|
25
|
-
| routing key / queue | `Consume(Type)` — which artifact type wakes the task |
|
|
26
|
-
| chain / group / chord | several `consumes` / `produces`; the runtime derives the order |
|
|
27
|
-
| retries, `acks_late` | guards + `Budget`, an honest `None` instead of a wrong result |
|
|
28
|
-
| result backend | the `Context` — typed, versioned artifacts |
|
|
29
|
-
| worker | `Runtime` |
|
|
18
|
+
A question like *"why did infra costs jump in Q2?"* needs a spreadsheet, a policy doc and a page off the web — and the *next* question needs a different subset. That is not one graph you can draw up front. You declare what artifacts exist and what agents can do with them; the runtime derives the order from state.
|
|
30
19
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
**versioned with provenance**, so a run is reproducible (`context_hash`) and
|
|
36
|
-
auditable (`audit.report`) for free. The model reasons; the arithmetic stays
|
|
37
|
-
deterministic; every claim carries provenance.
|
|
20
|
+
Every derived artifact then links to what produced it
|
|
21
|
+
(`Answer —supported_by→ Evidence —extracted_from→ Doc`), so *"why did it say
|
|
22
|
+
that?"* is a query, not a guess — and the run is reproducible (`context_hash`)
|
|
23
|
+
and verifiable (`reactifact replay --verify`).
|
|
38
24
|
|
|
39
25
|
## On top: a provable answer
|
|
40
26
|
|
|
@@ -86,6 +72,14 @@ print(report_to_markdown(build_report(ctx, answer))) # hash per artifact + edge
|
|
|
86
72
|
print(context_hash(ctx)) # reproducible fingerprint
|
|
87
73
|
```
|
|
88
74
|
|
|
75
|
+
## Knowledge assistants, live
|
|
76
|
+
|
|
77
|
+
The same shape over many sources, not a fixed path — the
|
|
78
|
+
[`knowledge`](examples/knowledge) CLI answers a harder, multi-source question
|
|
79
|
+
(docs + a CSV) with a real computed number and its sources, no LLM key required:
|
|
80
|
+
|
|
81
|
+

|
|
82
|
+
|
|
89
83
|

|
|
90
84
|
|
|
91
85
|
Two agents explore independently on their own forks and merge back automatically
|
|
@@ -93,6 +87,31 @@ Two agents explore independently on their own forks and merge back automatically
|
|
|
93
87
|
|
|
94
88
|

|
|
95
89
|
|
|
90
|
+
<details>
|
|
91
|
+
<summary><strong>If you know Celery…</strong> — the programming model, not its distributed runtime</summary>
|
|
92
|
+
|
|
93
|
+
Python developers already know this model from Celery: define a **task**,
|
|
94
|
+
declare what triggers it, let the runtime run it. reactifact applies it to
|
|
95
|
+
agents — a task reacts to a **typed, versioned artifact** appearing in the
|
|
96
|
+
context, not to a queue message you push or a graph edge you draw. The runtime
|
|
97
|
+
derives what runs next from state.
|
|
98
|
+
|
|
99
|
+
| Celery | reactifact |
|
|
100
|
+
| --- | --- |
|
|
101
|
+
| a task | `@produce(Model)` — a unit of work that writes an artifact |
|
|
102
|
+
| `delay()` / `apply_async()` | you don't call it: creating the input artifact **is** the trigger |
|
|
103
|
+
| routing key / queue | `Consume(Type)` — which artifact type wakes the task |
|
|
104
|
+
| chain / group / chord | several `consumes` / `produces`; the runtime derives the order |
|
|
105
|
+
| result backend | the `Context` — typed, versioned artifacts |
|
|
106
|
+
| worker | `Runtime` |
|
|
107
|
+
|
|
108
|
+
**Not inherited:** a broker, a worker pool, `acks_late`/redelivery, cross-process
|
|
109
|
+
exactly-once. reactifact is a library with an in-process runtime — the *model* is
|
|
110
|
+
Celery-shaped, the deployment is not. (Session-backed runs do survive a restart;
|
|
111
|
+
see [Durability & resume](docs/en/durability.md).)
|
|
112
|
+
|
|
113
|
+
</details>
|
|
114
|
+
|
|
96
115
|
```bash
|
|
97
116
|
pip install reactifact
|
|
98
117
|
```
|
|
@@ -159,12 +178,6 @@ print(answer.data.text) # "Refunds are available within 14 d
|
|
|
159
178
|
print("supported_by:", evidence.data.text) # provenance you can trace, not just a string in a log
|
|
160
179
|
```
|
|
161
180
|
|
|
162
|
-
The same idea, live — the [`knowledge`](examples/knowledge) example's CLI answering a
|
|
163
|
-
harder, multi-source question (docs + a CSV) with a real computed number and
|
|
164
|
-
its sources, no LLM key required:
|
|
165
|
-
|
|
166
|
-

|
|
167
|
-
|
|
168
181
|
## How it works
|
|
169
182
|
|
|
170
183
|
```text
|
|
@@ -232,13 +245,19 @@ by hand. Run it yourself: `uv run python -m examples.ledger.main`.
|
|
|
232
245
|
- **Provenance** — every derived artifact links to what produced it
|
|
233
246
|
(`Answer —supported_by→ Claim —derived_from→ Evidence —extracted_from→ Doc`).
|
|
234
247
|
- **HITL** — humans as `effects.ask(...)` → `PendingQuestion`, answered via `effects.resume(...)` like any agent.
|
|
248
|
+
- **Outbox** — outbound side effects as `effects.act(...)` → `PendingAction`, performed once *after* the commit by `Runtime(dispatcher=...)`; safe across replay, retries and merged branches.
|
|
235
249
|
|
|
236
250
|
## In the box
|
|
237
251
|
|
|
238
252
|
- **Deterministic by design** — calculations over structured data, honest `None`
|
|
239
253
|
fallbacks instead of hallucinated answers; the model reasons, never "knows".
|
|
240
254
|
- **Observability** — every run traces agent spans, reads/writes, LLM calls,
|
|
241
|
-
tokens: SQLite store + web dashboard, exportable to Langfuse/Postgres (async sinks)
|
|
255
|
+
tokens: SQLite store + web dashboard, exportable to Langfuse/Postgres (async sinks);
|
|
256
|
+
**correlated logs** (`configure_logging()`, silent until called) carry
|
|
257
|
+
`run_id`/`session_id`/`agent` on every line.
|
|
258
|
+
- **Lifecycle** — `Budget.max_seconds` is a hard deadline (a hung LLM call is
|
|
259
|
+
cancelled); `request_stop()`/`ashutdown()` stop at a generation boundary
|
|
260
|
+
(`RunOutcome.STOPPED`); `active_runs()`/`in_flight` give a readiness view.
|
|
242
261
|
- **MCP, both ways** — call any MCP server's tools as a `Tool`
|
|
243
262
|
(`mcp_stdio_tools`/`mcp_http_tools`), or expose your own `Tool`s and a live
|
|
244
263
|
`Context` as an MCP server (`create_mcp_server`) for Claude Desktop, Claude
|
|
@@ -269,7 +288,7 @@ uv run python ./examples/devops/web.py # HITL ops assistant + trace dash
|
|
|
269
288
|
```
|
|
270
289
|
|
|
271
290
|
Classic-pattern ports run as one-liners too:
|
|
272
|
-
`python -m examples.{reflection,map_reduce,supervisor,summarize,time_travel,plan_execute,adaptive,ledger}.main`.
|
|
291
|
+
`python -m examples.{reflection,map_reduce,supervisor,summarize,time_travel,plan_execute,adaptive,ledger,outbox}.main`.
|
|
273
292
|
|
|
274
293
|
Prefer a notebook? [](https://colab.research.google.com/github/bzdvdn/reactifact/blob/master/examples/quickstart.ipynb)
|
|
275
294
|
— `examples/quickstart.ipynb` covers the four `quick` cases (`agent`/`rag`/`tools_agent`/`chat_agent`), offline by default.
|
|
@@ -281,8 +300,10 @@ Prefer a notebook? [.
|
|
303
|
+
- `chat` — minimal offline single-model multi-turn conversation (`Conversation` + bounded `ChatMemory`).
|
|
284
304
|
- `forklab` — deterministic branch & merge: two strategies on their own forks, three-way merge.
|
|
285
305
|
- `ledger` — offline proof of reactive recompute: edit one fact, only its real `Consume`rs re-run.
|
|
306
|
+
- `outbox` — one intent, one send: `effects.act(...)` + `Runtime(dispatcher=...)`, safe across replays, retries and merged branches (§42).
|
|
286
307
|
- `llm_ladder` — the workflow from one LLM call to state-changing patches (3 levels).
|
|
287
308
|
- `adaptive` — hybrid scheduler: rule filters + deterministic rank + LLM tie-break + `rank_limit`.
|
|
288
309
|
- `{reflection,map_reduce,supervisor,summarize,time_travel,plan_execute}` — canonical ports (see [port-matrix](docs/en/port-matrix.md)).
|
|
@@ -311,6 +332,14 @@ uv sync --extra dev --extra web
|
|
|
311
332
|
.venv/bin/ruff check
|
|
312
333
|
```
|
|
313
334
|
|
|
335
|
+
## Status & contributing
|
|
336
|
+
|
|
337
|
+
Active, `0.14.x`, pre-1.0 — the API is stabilizing and `1.0` will freeze it
|
|
338
|
+
(see the [roadmap](docs/roadmap.md)); releases follow
|
|
339
|
+
[Keep a Changelog](CHANGELOG.md). Questions and design discussion live in
|
|
340
|
+
[GitHub Discussions](https://github.com/bzdvdn/reactifact/discussions); a star,
|
|
341
|
+
or a `good first issue`, helps more than you'd think.
|
|
342
|
+
|
|
314
343
|
## License
|
|
315
344
|
|
|
316
345
|
MIT — see [LICENSE](LICENSE).
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "reactifact"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.14.0"
|
|
8
8
|
description = "Reactive, artifact-driven agent runtime: agents transform versioned, typed, provenance-aware artifacts inside an evolving context"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -52,6 +52,9 @@ pg = [
|
|
|
52
52
|
mcp = [
|
|
53
53
|
"mcp>=2.2",
|
|
54
54
|
]
|
|
55
|
+
a2a = [
|
|
56
|
+
"a2a-sdk>=1.0",
|
|
57
|
+
]
|
|
55
58
|
docs = [
|
|
56
59
|
"mkdocs>=1.6",
|
|
57
60
|
"mkdocs-material>=9.5",
|
|
@@ -81,6 +84,9 @@ pg = [
|
|
|
81
84
|
mcp = [
|
|
82
85
|
"mcp>=2.2",
|
|
83
86
|
]
|
|
87
|
+
a2a = [
|
|
88
|
+
"a2a-sdk>=1.0",
|
|
89
|
+
]
|
|
84
90
|
docs = [
|
|
85
91
|
"mkdocs>=1.6",
|
|
86
92
|
"mkdocs-material>=9.5",
|
|
@@ -99,7 +105,7 @@ reactifact = "reactifact.__main__:main"
|
|
|
99
105
|
packages = { find = { where = ["."], include = ["reactifact*"], exclude = ["examples*", "tests*"] } }
|
|
100
106
|
|
|
101
107
|
[tool.setuptools.package-data]
|
|
102
|
-
reactifact = ["py.typed"]
|
|
108
|
+
reactifact = ["py.typed", "skills/*/SKILL.md", "skills/*/references/*.md"]
|
|
103
109
|
"reactifact.tracing" = ["templates/*.html", "templates/*.css"]
|
|
104
110
|
|
|
105
111
|
[tool.pytest.ini_options]
|
|
@@ -152,3 +158,9 @@ python_version = "3.11"
|
|
|
152
158
|
packages = ["reactifact", "examples"]
|
|
153
159
|
strict = true
|
|
154
160
|
warn_unused_ignores = true
|
|
161
|
+
|
|
162
|
+
# pyright defaults its target to the interpreter it finds, which can resolve
|
|
163
|
+
# below the 3.11 floor this package requires — making `StrEnum`/`datetime.UTC`
|
|
164
|
+
# "unknown import symbol" and cascading into false positives on `RunOutcome`.
|
|
165
|
+
[tool.pyright]
|
|
166
|
+
pythonVersion = "3.11"
|
|
@@ -24,12 +24,13 @@ of the names you see by default even though they were always cheap to import
|
|
|
24
24
|
from .agents import Agent, create_agent
|
|
25
25
|
from .artifacts import Artifact
|
|
26
26
|
from .branching import MergeConflict
|
|
27
|
-
from .budget import Budget, RunOutcome, RunStats
|
|
27
|
+
from .budget import Budget, BudgetTracker, RunOutcome, RunStats
|
|
28
28
|
from .consume import Consume, consume
|
|
29
29
|
from .context import Context, View
|
|
30
30
|
from .effects import Effects, Handle
|
|
31
31
|
from .events import Event, EventType
|
|
32
|
-
from .interrupt import PendingQuestion
|
|
32
|
+
from .interrupt import PendingAction, PendingQuestion
|
|
33
|
+
from .logging import bind, configure_logging, get_logger
|
|
33
34
|
from .patches import Create, Delete, Link, Patch, Relation, Unlink, Update
|
|
34
35
|
from .produce import Produce, ProduceCall, produce
|
|
35
36
|
from .providers import (
|
|
@@ -48,13 +49,15 @@ from .runtime import Runtime
|
|
|
48
49
|
from .session import Session, SessionStore
|
|
49
50
|
from .tools import FunctionTool, Tool, ToolOutput, tool
|
|
50
51
|
from .triggers import Trigger
|
|
52
|
+
from .types import register as register_type
|
|
51
53
|
|
|
52
|
-
__version__ = "0.
|
|
54
|
+
__version__ = "0.14.0"
|
|
53
55
|
|
|
54
56
|
__all__ = [
|
|
55
57
|
"Agent",
|
|
56
58
|
"Artifact",
|
|
57
59
|
"Budget",
|
|
60
|
+
"BudgetTracker",
|
|
58
61
|
"Consume",
|
|
59
62
|
"Context",
|
|
60
63
|
"Create",
|
|
@@ -71,6 +74,7 @@ __all__ = [
|
|
|
71
74
|
"Message",
|
|
72
75
|
"MergeConflict",
|
|
73
76
|
"Patch",
|
|
77
|
+
"PendingAction",
|
|
74
78
|
"PendingQuestion",
|
|
75
79
|
"Produce",
|
|
76
80
|
"ProduceCall",
|
|
@@ -93,9 +97,13 @@ __all__ = [
|
|
|
93
97
|
"Unlink",
|
|
94
98
|
"Update",
|
|
95
99
|
"View",
|
|
100
|
+
"bind",
|
|
101
|
+
"configure_logging",
|
|
96
102
|
"consume",
|
|
97
103
|
"create_agent",
|
|
98
104
|
"current_request",
|
|
105
|
+
"get_logger",
|
|
99
106
|
"produce",
|
|
107
|
+
"register_type",
|
|
100
108
|
"tool",
|
|
101
109
|
]
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""A2A (Agent2Agent) integration: call remote agents, or serve reactifact ones.
|
|
2
|
+
|
|
3
|
+
No extra dependency: the client uses `httpx` (core), the server uses FastAPI
|
|
4
|
+
from the existing `web` extra and imports it lazily — so importing this package
|
|
5
|
+
never needs FastAPI.
|
|
6
|
+
|
|
7
|
+
- Client (`reactifact.a2a.client`): `A2AClient` (Agent Card + `message/send`,
|
|
8
|
+
`message/stream`, `tasks/get`, `tasks/cancel`) and `A2AAgentTool`/`a2a_tool`,
|
|
9
|
+
a reactifact `Tool` that delegates to a remote agent.
|
|
10
|
+
- Server (`reactifact.a2a.server`): `create_a2a_router(agents, ...)` — an Agent
|
|
11
|
+
Card plus a JSON-RPC endpoint where a `Task` is a conversation and a HITL
|
|
12
|
+
`PendingQuestion` maps to `input-required`.
|
|
13
|
+
|
|
14
|
+
The JSON-RPC binding implements the A2A `0.3` wire shape (camelCase fields,
|
|
15
|
+
`kind`-discriminated parts).
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
from .client import (
|
|
21
|
+
AGENT_CARD_PATH,
|
|
22
|
+
LEGACY_AGENT_CARD_PATH,
|
|
23
|
+
A2AAgentTool,
|
|
24
|
+
A2AClient,
|
|
25
|
+
A2AError,
|
|
26
|
+
a2a_tool,
|
|
27
|
+
)
|
|
28
|
+
from .models import (
|
|
29
|
+
AgentCapabilities,
|
|
30
|
+
AgentCard,
|
|
31
|
+
AgentProvider,
|
|
32
|
+
AgentSkill,
|
|
33
|
+
Artifact,
|
|
34
|
+
ErrorCode,
|
|
35
|
+
JSONRPCError,
|
|
36
|
+
JSONRPCRequest,
|
|
37
|
+
JSONRPCResponse,
|
|
38
|
+
Message,
|
|
39
|
+
MessageSendParams,
|
|
40
|
+
Part,
|
|
41
|
+
Task,
|
|
42
|
+
TaskState,
|
|
43
|
+
TaskStatus,
|
|
44
|
+
UserMessage,
|
|
45
|
+
)
|
|
46
|
+
from .remote import A2ARemoteProduce, remote_agent
|
|
47
|
+
from .server import create_a2a_router, default_create_message, default_reply
|
|
48
|
+
|
|
49
|
+
__all__ = [
|
|
50
|
+
"AGENT_CARD_PATH",
|
|
51
|
+
"LEGACY_AGENT_CARD_PATH",
|
|
52
|
+
"A2AAgentTool",
|
|
53
|
+
"A2AClient",
|
|
54
|
+
"A2AError",
|
|
55
|
+
"A2ARemoteProduce",
|
|
56
|
+
"AgentCapabilities",
|
|
57
|
+
"AgentCard",
|
|
58
|
+
"AgentProvider",
|
|
59
|
+
"AgentSkill",
|
|
60
|
+
"Artifact",
|
|
61
|
+
"ErrorCode",
|
|
62
|
+
"JSONRPCError",
|
|
63
|
+
"JSONRPCRequest",
|
|
64
|
+
"JSONRPCResponse",
|
|
65
|
+
"Message",
|
|
66
|
+
"MessageSendParams",
|
|
67
|
+
"Part",
|
|
68
|
+
"Task",
|
|
69
|
+
"TaskState",
|
|
70
|
+
"TaskStatus",
|
|
71
|
+
"UserMessage",
|
|
72
|
+
"a2a_tool",
|
|
73
|
+
"create_a2a_router",
|
|
74
|
+
"default_create_message",
|
|
75
|
+
"default_reply",
|
|
76
|
+
"remote_agent",
|
|
77
|
+
]
|