reactifact 0.16.0__tar.gz → 0.16.1__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.16.0/reactifact.egg-info → reactifact-0.16.1}/PKG-INFO +1 -1
- {reactifact-0.16.0 → reactifact-0.16.1}/pyproject.toml +1 -1
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/__init__.py +1 -1
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/tracing/__init__.py +2 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/tracing/models.py +18 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/tracing/postgres.py +22 -4
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/tracing/store.py +20 -4
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/tracing/templates/app.css +20 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/tracing/templates/ui_run.html +109 -37
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/tracing/tracer.py +30 -1
- {reactifact-0.16.0 → reactifact-0.16.1/reactifact.egg-info}/PKG-INFO +1 -1
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_public_api.py +1 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_trace_ui.py +12 -1
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_tracing.py +7 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/LICENSE +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/README.md +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/__main__.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/_extras.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/_httpx.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/_persistence.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/a2a/__init__.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/a2a/client.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/a2a/models.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/a2a/remote.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/a2a/server.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/agent_tool.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/agents.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/artifacts.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/audit.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/authz.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/branching.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/budget.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/cache.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/chat.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/checkpoints.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/cli/__init__.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/cli/branch.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/cli/common.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/cli/context.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/cli/eval.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/cli/graph.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/cli/replay.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/cli/scenario.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/cli/skills.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/cli/trace.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/commit.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/commit_log.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/consume.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/context.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/context_builder.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/effects.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/eval/__init__.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/eval/dataset.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/eval/evaluators.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/eval/judge.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/eval/online.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/eval/runner.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/eval/scoring.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/events.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/guardrails.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/interrupt.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/llm_agent.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/logging.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/mcp/__init__.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/mcp/client.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/mcp/oauth.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/mcp/server.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/metrics.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/native_tool_use.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/operations.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/patches.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/pricing.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/produce.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/prompts.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/providers/__init__.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/providers/_retry.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/providers/anthropic.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/providers/azure.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/providers/cerebras.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/providers/chat.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/providers/contracts.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/providers/deepseek.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/providers/fake.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/providers/fireworks.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/providers/gemini.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/providers/github_models.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/providers/groq.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/providers/image.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/providers/mistral.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/providers/nvidia.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/providers/ollama.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/providers/openai.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/providers/openrouter.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/providers/perplexity.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/providers/qwen.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/providers/speech.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/providers/together.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/providers/video.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/providers/xai.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/providers/zai.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/py.typed +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/quick/__init__.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/quick/_shared.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/quick/agent.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/quick/chat.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/quick/models.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/quick/rag.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/quick/tools_agent.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/quota.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/recipes/__init__.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/recipes/cleanup.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/recipes/conversation.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/recipes/identity.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/recipes/inputs.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/recipes/memory.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/recipes/plan_execute.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/recipes/reflection.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/recipes/resolve.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/recipes/rollback.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/recipes/search.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/recipes/skills.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/recipes/status.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/recipes/supervisor.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/recipes/text.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/recipes/tool_loop.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/redaction.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/relations.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/replay.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/request.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/resources.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/routing.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/runtime.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/scheduler.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/session.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/skills/reactifact/SKILL.md +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/skills/reactifact/references/effects-and-state.md +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/skills/reactifact/references/trust-and-safety.md +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/skills/reactifact-agents/SKILL.md +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/skills/reactifact-agents/references/consume.md +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/skills/reactifact-agents/references/effects.md +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/skills/reactifact-eval/SKILL.md +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/skills/reactifact-eval/references/online.md +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/skills/reactifact-from-langchain/SKILL.md +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/skills/reactifact-llm/SKILL.md +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/skills/reactifact-llm/references/providers.md +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/skills/reactifact-llm/references/tools.md +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/skills/reactifact-observability/SKILL.md +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/skills/reactifact-rag/SKILL.md +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/skills/reactifact-rag/references/sources.md +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/skills/reactifact-testing/SKILL.md +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/sources.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/streaming.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/structured.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/testing/__init__.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/testing/assertions.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/testing/exceptions.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/testing/fault.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/testing/golden.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/testing/lab.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/testing/mock.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/testing/pytest_plugin.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/testing/record.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/testing/registry.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/tool_use.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/tools.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/tracing/_otlp.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/tracing/columns.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/tracing/langfuse.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/tracing/otlp.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/tracing/templates/sessions.html +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/tracing/templates/ui.html +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/tracing/web.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/triggers.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/types.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/verify.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/viz.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact/web.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact.egg-info/SOURCES.txt +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact.egg-info/dependency_links.txt +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact.egg-info/entry_points.txt +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact.egg-info/requires.txt +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/reactifact.egg-info/top_level.txt +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/setup.cfg +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_a2a.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_a2a_example.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_a2a_interop.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_absent_consume.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_adaptive.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_agent_tool.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_anthropic_provider.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_artifacts.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_audit.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_authz.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_backbone.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_branching.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_budget.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_cache.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_canonical_ports.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_chat_example.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_chat_memory.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_chat_outbox.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_chat_web.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_checkpoint.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_checkpoints_concurrency.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_cli.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_cli_scenario.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_commit_log.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_compaction.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_concurrency.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_consumes_produces.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_context_builder.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_correlated_consume.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_debounce.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_determinism.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_devops.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_devops_online.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_devops_web.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_effects.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_eval.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_eval_dataset.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_eval_judge.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_eval_online.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_eval_trajectory.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_fintech_audit.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_forklab.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_forklab_web.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_friendly_api.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_gemini_provider.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_golden.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_guardrails.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_hitl.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_image_provider.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_incident_commander.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_invalidation.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_join_consume.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_knowledge.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_knowledge_web.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_llm_ladder.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_logging.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_loop_bound_client.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_mcp.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_medic_lab.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_medic_lab_web.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_metrics.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_multisource.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_native_tool_use.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_openai_provider.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_outbox.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_patches.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_pending_actions.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_ports.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_produce_styles.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_produce_trigger.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_prompts.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_provider_auth.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_provider_retry.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_providers_integration.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_quick.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_quota.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_reacts_to.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_recipe_conversation.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_recipe_plan_execute.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_recipe_reflection.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_recipe_supervisor.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_recipe_tool_loop.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_recipes.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_recipes_inputs.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_recipes_memory.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_recipes_skills.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_redaction.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_relation_graph.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_relations.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_repair.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_repair_web.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_replay.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_repo_agent.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_request.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_research.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_resources.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_retry.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_routing.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_runtime.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_runtime_errors.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_scheduler_semantics.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_session_append.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_sessions.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_shutdown.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_skills.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_sources.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_sources_search.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_sources_vector.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_speech_provider.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_starter_app.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_streaming.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_structured.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_support_copilot.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_testing_assertions.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_testing_golden.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_testing_lab.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_testing_mock.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_testing_provenance.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_testing_pytest_plugin.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_testing_registry.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_token_budget.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_tools.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_trace_columns.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_trace_tags.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_type_registry.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_vendor_factories.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_vendor_multimodal_factories.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_verify.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_video_provider.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_view.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_viz.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_web_source.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_workspace.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/test_workspace_with_sources.py +0 -0
- {reactifact-0.16.0 → reactifact-0.16.1}/tests/tests_checkpoints_sqlite.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: reactifact
|
|
3
|
-
Version: 0.16.
|
|
3
|
+
Version: 0.16.1
|
|
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
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "reactifact"
|
|
7
|
-
version = "0.16.
|
|
7
|
+
version = "0.16.1"
|
|
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"
|
|
@@ -9,6 +9,7 @@ from ..tools import ToolRun
|
|
|
9
9
|
from .columns import TraceColumn
|
|
10
10
|
from .langfuse import LangfuseTracer
|
|
11
11
|
from .models import (
|
|
12
|
+
AgentContract,
|
|
12
13
|
AgentSpan,
|
|
13
14
|
ArtifactRef,
|
|
14
15
|
LLMCall,
|
|
@@ -30,6 +31,7 @@ from .store import (
|
|
|
30
31
|
from .tracer import CompositeTracer, RecordingLLM, RunTracer, Tracer
|
|
31
32
|
|
|
32
33
|
__all__ = [
|
|
34
|
+
"AgentContract",
|
|
33
35
|
"AgentSpan",
|
|
34
36
|
"ArtifactRef",
|
|
35
37
|
"CompositeTracer",
|
|
@@ -90,6 +90,20 @@ class TagAssignment(BaseModel):
|
|
|
90
90
|
updated_at: datetime | None = None
|
|
91
91
|
|
|
92
92
|
|
|
93
|
+
class AgentContract(BaseModel):
|
|
94
|
+
"""The agent's declared contract, recorded on each span for the UI.
|
|
95
|
+
|
|
96
|
+
`consumes`/`produces`/`tools` are the *declared* names (not only what ran),
|
|
97
|
+
so the dashboard can show a consume/produce that never fired as a muted
|
|
98
|
+
chip instead of hiding it.
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
consumes: list[str] = Field(default_factory=list)
|
|
102
|
+
produces: list[str] = Field(default_factory=list)
|
|
103
|
+
tools: list[str] = Field(default_factory=list)
|
|
104
|
+
priority: int = 0
|
|
105
|
+
|
|
106
|
+
|
|
93
107
|
class ProduceRun(BaseModel):
|
|
94
108
|
"""One produce execution within an agent span (§54).
|
|
95
109
|
|
|
@@ -113,6 +127,10 @@ class AgentSpan(BaseModel):
|
|
|
113
127
|
#: ``Consume``) — e.g. ``DraftAnswer`` vs ``FinalResponse`` for an agent that
|
|
114
128
|
#: consumes both. ``event_type`` alone (created/updated) can't tell them apart.
|
|
115
129
|
event_artifact_type: str = ""
|
|
130
|
+
#: The concrete artifact whose change triggered this span.
|
|
131
|
+
event_artifact_id: str = ""
|
|
132
|
+
#: The agent's declared consumes/produces/tools (for the UI contract view).
|
|
133
|
+
contract: AgentContract = Field(default_factory=AgentContract)
|
|
116
134
|
#: The produces that ran for this event, in execution order.
|
|
117
135
|
produces: list[ProduceRun] = Field(default_factory=list)
|
|
118
136
|
#: Tool executions that happened during this agent run (name/latency/status).
|
|
@@ -25,6 +25,7 @@ from typing import Any
|
|
|
25
25
|
from ..tools import ToolRun
|
|
26
26
|
from .columns import SpanArtifacts, TraceColumn, extract_columns
|
|
27
27
|
from .models import (
|
|
28
|
+
AgentContract,
|
|
28
29
|
AgentSpan,
|
|
29
30
|
ArtifactRef,
|
|
30
31
|
LLMCall,
|
|
@@ -81,7 +82,9 @@ class PostgresStore:
|
|
|
81
82
|
relations JSONB NOT NULL DEFAULT '[]',
|
|
82
83
|
llm_calls JSONB NOT NULL DEFAULT '[]',
|
|
83
84
|
produces JSONB NOT NULL DEFAULT '[]',
|
|
84
|
-
tools JSONB NOT NULL DEFAULT '[]'
|
|
85
|
+
tools JSONB NOT NULL DEFAULT '[]',
|
|
86
|
+
event_artifact_id TEXT NOT NULL DEFAULT '',
|
|
87
|
+
contract JSONB NOT NULL DEFAULT '{}'
|
|
85
88
|
)
|
|
86
89
|
"""
|
|
87
90
|
)
|
|
@@ -139,6 +142,14 @@ class PostgresStore:
|
|
|
139
142
|
"ALTER TABLE spans ADD COLUMN IF NOT EXISTS "
|
|
140
143
|
"tools JSONB NOT NULL DEFAULT '[]'"
|
|
141
144
|
)
|
|
145
|
+
await cur.execute(
|
|
146
|
+
"ALTER TABLE spans ADD COLUMN IF NOT EXISTS "
|
|
147
|
+
"event_artifact_id TEXT NOT NULL DEFAULT ''"
|
|
148
|
+
)
|
|
149
|
+
await cur.execute(
|
|
150
|
+
"ALTER TABLE spans ADD COLUMN IF NOT EXISTS "
|
|
151
|
+
"contract JSONB NOT NULL DEFAULT '{}'"
|
|
152
|
+
)
|
|
142
153
|
await conn.commit()
|
|
143
154
|
finally:
|
|
144
155
|
await conn.close()
|
|
@@ -178,8 +189,8 @@ class PostgresStore:
|
|
|
178
189
|
await cur.execute(
|
|
179
190
|
"INSERT INTO spans (run_id, agent, event_type, event_artifact_type, "
|
|
180
191
|
"latency_ms, error, started_at, reads, writes, relations, llm_calls, "
|
|
181
|
-
"produces, tools) "
|
|
182
|
-
"VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)",
|
|
192
|
+
"produces, tools, event_artifact_id, contract) "
|
|
193
|
+
"VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)",
|
|
183
194
|
(
|
|
184
195
|
trace.id,
|
|
185
196
|
span.agent,
|
|
@@ -206,6 +217,10 @@ class PostgresStore:
|
|
|
206
217
|
psycopg.types.json.Jsonb(
|
|
207
218
|
[tr.model_dump(mode="json") for tr in span.tools]
|
|
208
219
|
),
|
|
220
|
+
span.event_artifact_id,
|
|
221
|
+
psycopg.types.json.Jsonb(
|
|
222
|
+
span.contract.model_dump(mode="json")
|
|
223
|
+
),
|
|
209
224
|
),
|
|
210
225
|
)
|
|
211
226
|
await conn.commit()
|
|
@@ -413,7 +428,8 @@ class PostgresStore:
|
|
|
413
428
|
return None
|
|
414
429
|
await cur.execute(
|
|
415
430
|
"SELECT agent, event_type, event_artifact_type, latency_ms, error, "
|
|
416
|
-
"started_at, reads, writes, relations, llm_calls, produces, tools "
|
|
431
|
+
"started_at, reads, writes, relations, llm_calls, produces, tools, "
|
|
432
|
+
"event_artifact_id, contract "
|
|
417
433
|
"FROM spans WHERE run_id = %s ORDER BY id",
|
|
418
434
|
(trace_id,),
|
|
419
435
|
)
|
|
@@ -443,6 +459,8 @@ class PostgresStore:
|
|
|
443
459
|
llm_calls=[LLMCall(**d) for d in r[9]],
|
|
444
460
|
produces=[ProduceRun(**d) for d in r[10]],
|
|
445
461
|
tools=[ToolRun(**d) for d in r[11]],
|
|
462
|
+
event_artifact_id=r[12],
|
|
463
|
+
contract=AgentContract(**r[13]),
|
|
446
464
|
)
|
|
447
465
|
for r in span_rows
|
|
448
466
|
]
|
|
@@ -28,6 +28,7 @@ from typing import Any, Protocol
|
|
|
28
28
|
from ..tools import ToolRun
|
|
29
29
|
from .columns import SpanArtifacts, TraceColumn, extract_columns
|
|
30
30
|
from .models import (
|
|
31
|
+
AgentContract,
|
|
31
32
|
AgentSpan,
|
|
32
33
|
ArtifactRef,
|
|
33
34
|
LLMCall,
|
|
@@ -185,7 +186,9 @@ class TraceStore:
|
|
|
185
186
|
relations TEXT NOT NULL DEFAULT '[]',
|
|
186
187
|
llm_calls TEXT NOT NULL DEFAULT '[]',
|
|
187
188
|
produces TEXT NOT NULL DEFAULT '[]',
|
|
188
|
-
tools TEXT NOT NULL DEFAULT '[]'
|
|
189
|
+
tools TEXT NOT NULL DEFAULT '[]',
|
|
190
|
+
event_artifact_id TEXT NOT NULL DEFAULT '',
|
|
191
|
+
contract TEXT NOT NULL DEFAULT '{}'
|
|
189
192
|
);
|
|
190
193
|
CREATE TABLE IF NOT EXISTS tags (
|
|
191
194
|
name TEXT PRIMARY KEY,
|
|
@@ -235,6 +238,14 @@ class TraceStore:
|
|
|
235
238
|
self._conn.execute(
|
|
236
239
|
"ALTER TABLE spans ADD COLUMN tools TEXT NOT NULL DEFAULT '[]'"
|
|
237
240
|
)
|
|
241
|
+
if "event_artifact_id" not in cols:
|
|
242
|
+
self._conn.execute(
|
|
243
|
+
"ALTER TABLE spans ADD COLUMN event_artifact_id TEXT NOT NULL DEFAULT ''"
|
|
244
|
+
)
|
|
245
|
+
if "contract" not in cols:
|
|
246
|
+
self._conn.execute(
|
|
247
|
+
"ALTER TABLE spans ADD COLUMN contract TEXT NOT NULL DEFAULT '{}'"
|
|
248
|
+
)
|
|
238
249
|
run_cols = {row[1] for row in self._conn.execute("PRAGMA table_info(runs)")}
|
|
239
250
|
if "prompt_tokens" not in run_cols:
|
|
240
251
|
self._conn.execute(
|
|
@@ -347,8 +358,8 @@ class TraceStore:
|
|
|
347
358
|
self._conn.execute(
|
|
348
359
|
"INSERT INTO spans (run_id, agent, event_type, event_artifact_type, "
|
|
349
360
|
"latency_ms, error, started_at, reads, writes, relations, llm_calls, "
|
|
350
|
-
"produces, tools) "
|
|
351
|
-
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
361
|
+
"produces, tools, event_artifact_id, contract) "
|
|
362
|
+
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
352
363
|
(
|
|
353
364
|
trace.id,
|
|
354
365
|
span.agent,
|
|
@@ -363,6 +374,8 @@ class TraceStore:
|
|
|
363
374
|
json.dumps([c.model_dump(mode="json") for c in span.llm_calls]),
|
|
364
375
|
json.dumps([p.model_dump(mode="json") for p in span.produces]),
|
|
365
376
|
json.dumps([t.model_dump(mode="json") for t in span.tools]),
|
|
377
|
+
span.event_artifact_id,
|
|
378
|
+
json.dumps(span.contract.model_dump(mode="json")),
|
|
366
379
|
),
|
|
367
380
|
)
|
|
368
381
|
self._conn.commit()
|
|
@@ -617,7 +630,8 @@ class TraceStore:
|
|
|
617
630
|
return None
|
|
618
631
|
span_rows = self._conn.execute(
|
|
619
632
|
"SELECT agent, event_type, event_artifact_type, latency_ms, error, started_at, "
|
|
620
|
-
"reads, writes, relations, llm_calls, produces, tools "
|
|
633
|
+
"reads, writes, relations, llm_calls, produces, tools, "
|
|
634
|
+
"event_artifact_id, contract "
|
|
621
635
|
"FROM spans WHERE run_id = ? ORDER BY id",
|
|
622
636
|
(trace_id,),
|
|
623
637
|
).fetchall()
|
|
@@ -637,6 +651,8 @@ class TraceStore:
|
|
|
637
651
|
llm_calls=[LLMCall(**d) for d in json.loads(r[9])],
|
|
638
652
|
produces=[ProduceRun(**d) for d in json.loads(r[10])],
|
|
639
653
|
tools=[ToolRun(**d) for d in json.loads(r[11])],
|
|
654
|
+
event_artifact_id=r[12],
|
|
655
|
+
contract=AgentContract(**json.loads(r[13])),
|
|
640
656
|
)
|
|
641
657
|
for r in span_rows
|
|
642
658
|
]
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
--text:#17171f; --muted:#6b6b7a; --faint:#9a9aae;
|
|
7
7
|
--accent:#4f46e5; --accent-soft:#eef0ff; --accent-strong:#4338ca;
|
|
8
8
|
--ok:#15803d; --ok-soft:#e9f7ef; --err:#b91c1c; --err-soft:#fdeeee;
|
|
9
|
+
--tool:#b45309; --tool-soft:#fdf1e3;
|
|
9
10
|
--warn:#b45309; --warn-soft:#fef3c7; --hover:#f3f3f7; --chip:#eef0ff; --chip-text:#4338ca;
|
|
10
11
|
--shadow-sm:0 1px 2px rgba(16,16,30,.05);
|
|
11
12
|
--shadow:0 1px 2px rgba(16,16,30,.04),0 4px 14px rgba(16,16,30,.06);
|
|
@@ -20,6 +21,7 @@ html.dark{
|
|
|
20
21
|
--text:#e8e8f0; --muted:#9a9aae; --faint:#6f6f82;
|
|
21
22
|
--accent:#8b93ff; --accent-soft:#232448; --accent-strong:#a9b1ff;
|
|
22
23
|
--ok:#3ecf7c; --ok-soft:#123522; --err:#f97c7c; --err-soft:#3c1718;
|
|
24
|
+
--tool:#fbbf24; --tool-soft:#3a2a10;
|
|
23
25
|
--warn:#fbbf24; --warn-soft:#3a2c0c; --hover:#1e1f29; --chip:#262a48; --chip-text:#a9b1ff;
|
|
24
26
|
--shadow-sm:0 1px 2px rgba(0,0,0,.4);
|
|
25
27
|
--shadow:0 1px 2px rgba(0,0,0,.4),0 8px 24px rgba(0,0,0,.35);
|
|
@@ -274,6 +276,24 @@ details.agent>summary:hover{background:var(--hover);}
|
|
|
274
276
|
.chip.produce.noop{background:var(--panel-alt);color:var(--muted);border:1px solid var(--border);}
|
|
275
277
|
.chip.produce .ops{background:rgba(21,128,61,.16);color:var(--ok);border-radius:7px;padding:0 6px;font-size:10px;font-variant-numeric:tabular-nums;}
|
|
276
278
|
.chip.produce.noop .ops.zero{background:var(--border);color:var(--faint);}
|
|
279
|
+
.chip.tool{background:var(--tool-soft);color:var(--tool);}
|
|
280
|
+
.consume{display:inline-flex;align-items:center;gap:4px;font-family:var(--mono);font-size:11.5px;color:var(--chip-text);background:var(--chip);border-radius:6px;padding:1px 7px;white-space:nowrap;}
|
|
281
|
+
.consume.muted{color:var(--muted);background:transparent;border:1px dashed var(--border-strong);}
|
|
282
|
+
.tl-track-wrap{min-width:0;}
|
|
283
|
+
.chip.muted,.consume.muted{opacity:.55;}
|
|
284
|
+
[data-filter-kind]{cursor:pointer;}
|
|
285
|
+
[data-filter-kind].active{outline:2px solid var(--accent);outline-offset:1px;}
|
|
286
|
+
.contract-flow{display:inline-flex;align-items:center;gap:6px;flex-wrap:wrap;min-width:0;}
|
|
287
|
+
.cf-label{font-size:10px;text-transform:uppercase;letter-spacing:.45px;color:var(--muted);white-space:nowrap;}
|
|
288
|
+
.trigger{display:inline-flex;align-items:center;gap:5px;font-size:11px;color:var(--muted);white-space:nowrap;}
|
|
289
|
+
.trigger code{font-size:10.5px;}
|
|
290
|
+
.produce-row{display:flex;align-items:center;gap:8px;padding:3px 0;font-size:12px;}
|
|
291
|
+
.produce-row.muted{opacity:.6;}
|
|
292
|
+
.filter-note{display:flex;align-items:center;gap:8px;margin:0 0 10px;font-size:12px;color:var(--muted);}
|
|
293
|
+
.tl-subs{position:relative;height:8px;margin-top:2px;}
|
|
294
|
+
.tl-sub{position:absolute;top:0;height:6px;border-radius:3px;opacity:.9;min-width:2px;}
|
|
295
|
+
.tl-sub.produce{background:var(--ok);}
|
|
296
|
+
.tl-sub.tool{background:var(--tool);}
|
|
277
297
|
.spans{display:flex;flex-direction:column;gap:8px;}
|
|
278
298
|
details.node{border:1px solid var(--border);border-left:3px solid var(--accent);border-radius:var(--r-sm);background:var(--panel);overflow:hidden;}
|
|
279
299
|
details.node summary{display:flex;align-items:center;gap:10px;padding:9px 14px;cursor:pointer;font-size:13px;}
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
<h2>Work</h2>
|
|
58
58
|
<div class="spacer"></div>
|
|
59
59
|
<input class="input" id="trace-search" type="search" placeholder="search spans…" style="min-width:180px"/>
|
|
60
|
+
<span id="chip-filter" class="hint"></span>
|
|
60
61
|
<div class="seg" id="view-seg">
|
|
61
62
|
<button class="seg-btn active" data-view="tree">Tree</button>
|
|
62
63
|
<button class="seg-btn" data-view="timeline">Timeline</button>
|
|
@@ -186,7 +187,8 @@ function artsBlock(arts){
|
|
|
186
187
|
return chips+'<div class="art-list">'+first+'<details class="io-more" open><summary>… + '+(n-SHOWN)+' more</summary>'+rest+'</details></div>';
|
|
187
188
|
}
|
|
188
189
|
|
|
189
|
-
let TRACE=null,VIEW='tree',FILTER='';
|
|
190
|
+
let TRACE=null,VIEW='tree',FILTER='',CHIP_FILTER=null;
|
|
191
|
+
const AGENT_CONTRACTS={};
|
|
190
192
|
function render(t){
|
|
191
193
|
TRACE=t;
|
|
192
194
|
document.title='Trace #'+t.id+' · reactifact';
|
|
@@ -218,8 +220,30 @@ function renderFields(fields){
|
|
|
218
220
|
'<div class="fv'+(empty?' empty':'')+'">'+escapeHtml(empty?'—':String(v))+'</div></div>';
|
|
219
221
|
}).join('');
|
|
220
222
|
}
|
|
223
|
+
function uniq(a){return [...new Set(a.filter(Boolean))];}
|
|
224
|
+
function shortId(id){return id?String(id).slice(0,8):'';}
|
|
225
|
+
function chipHtml(kind,value,opts){
|
|
226
|
+
opts=opts||{};
|
|
227
|
+
const cls=kind==='consume'?'consume':'chip '+kind;
|
|
228
|
+
const active=CHIP_FILTER&&CHIP_FILTER.kind===kind&&CHIP_FILTER.value===value;
|
|
229
|
+
const count=opts.count?' <span class="ops">'+opts.count+'</span>':'';
|
|
230
|
+
return '<span class="'+cls+(opts.muted?' muted':'')+(active?' active':'')+'" '+
|
|
231
|
+
'data-filter-kind="'+kind+'" data-filter-value="'+escapeHtml(value)+'" '+
|
|
232
|
+
'title="'+escapeHtml(opts.title||value)+'">'+escapeHtml(value)+count+'</span>';
|
|
233
|
+
}
|
|
234
|
+
function contractOf(agent,list){
|
|
235
|
+
const withContract=list.find(s=>s.contract&&((s.contract.consumes||[]).length||(s.contract.produces||[]).length||(s.contract.tools||[]).length));
|
|
236
|
+
return withContract?withContract.contract:{consumes:[],produces:[],tools:[],priority:0};
|
|
237
|
+
}
|
|
221
238
|
function spanMatches(s,f){
|
|
222
|
-
|
|
239
|
+
if(CHIP_FILTER){
|
|
240
|
+
const k=CHIP_FILTER.kind,v=CHIP_FILTER.value,c=s.contract||{};
|
|
241
|
+
if(k==='consume'&&!(s.event_artifact_type===v||(s.reads||[]).some(a=>a.data_type===v)||(c.consumes||[]).includes(v)))return false;
|
|
242
|
+
if(k==='produce'&&!((s.produces||[]).some(p=>p.name===v)||(c.produces||[]).includes(v)))return false;
|
|
243
|
+
if(k==='tool'&&!((s.tools||[]).some(t=>t.tool===v)||(c.tools||[]).includes(v)))return false;
|
|
244
|
+
}
|
|
245
|
+
if(!f)return true;
|
|
246
|
+
const hay=[s.agent,s.event_type,s.event_artifact_type||'',s.event_artifact_id||'',s.error||''];
|
|
223
247
|
for(const p of (s.produces||[]))hay.push(p.name||'');
|
|
224
248
|
for(const a of (s.reads||[]).concat(s.writes||[]))hay.push(a.data_type||'',a.data||'');
|
|
225
249
|
for(const c of (s.llm_calls||[]))hay.push(c.provider||'',c.model||'',c.response||'',...(c.messages||[]).map(m=>m.content||''));
|
|
@@ -227,7 +251,11 @@ function spanMatches(s,f){
|
|
|
227
251
|
}
|
|
228
252
|
function renderWork(){
|
|
229
253
|
const spans=(TRACE&&TRACE.spans)||[];const f=FILTER.trim().toLowerCase();
|
|
230
|
-
const filtered=
|
|
254
|
+
const filtered=spans.filter(s=>spanMatches(s,f));
|
|
255
|
+
const cf=document.getElementById('chip-filter');
|
|
256
|
+
if(cf)cf.innerHTML=CHIP_FILTER
|
|
257
|
+
? 'filter: '+escapeHtml(CHIP_FILTER.kind)+' '+escapeHtml(CHIP_FILTER.value)+' <button class="btn sm" data-clear-filter>clear</button>'
|
|
258
|
+
: '';
|
|
231
259
|
if(VIEW==='timeline')renderTimeline(filtered);else renderTree(filtered);
|
|
232
260
|
}
|
|
233
261
|
function renderTree(spans){
|
|
@@ -253,57 +281,85 @@ function renderTimeline(spans){
|
|
|
253
281
|
const prods=(s.produces||[]).map(p=>p.name).join('+');
|
|
254
282
|
const toolCount=(s.tools||[]).length;
|
|
255
283
|
const label=escapeHtml(s.agent)+' · '+escapeHtml(s.event_artifact_type||s.event_type||'span')+(prods?' → '+escapeHtml(prods):'')+(toolCount?' · '+toolCount+' tools':'');const llm=(s.llm_calls||[]).length;
|
|
284
|
+
let subs='';let pcursor=r.start;
|
|
285
|
+
for(const p of (s.produces||[])){
|
|
286
|
+
const w=Math.max(0.3,(Number(p.latency_ms)||0)/total*100);
|
|
287
|
+
subs+='<span class="tl-sub produce" style="left:'+(pcursor/total*100)+'%;width:'+w+'%" title="'+escapeHtml(p.name)+' — '+Number(p.latency_ms||0).toFixed(1)+' ms"></span>';
|
|
288
|
+
pcursor+=Number(p.latency_ms)||0;
|
|
289
|
+
}
|
|
290
|
+
for(const tool of (s.tools||[])){
|
|
291
|
+
let ts=r.start;
|
|
292
|
+
if(tool.started_at){const d=new Date(tool.started_at).getTime()-origin;if(isFinite(d))ts=Math.max(0,d);}
|
|
293
|
+
const w=Math.max(0.3,(Number(tool.latency_ms)||0)/total*100);
|
|
294
|
+
subs+='<span class="tl-sub tool" style="left:'+(ts/total*100)+'%;width:'+w+'%" title="'+escapeHtml(tool.tool)+' — '+Number(tool.latency_ms||0).toFixed(1)+' ms"></span>';
|
|
295
|
+
}
|
|
256
296
|
html+='<div class="tl-row"><div class="tl-label" title="'+label+'">'+label+(llm?' <span class="sub">'+llm+' llm</span>':'')+'</div>'+
|
|
257
|
-
'<div class="tl-track"><div class="tl-bar'+(s.error?' err':'')+'" style="left:'+left+'%;width:'+width+'%" title="'+label+' — '+r.dur.toFixed(1)+' ms"><span class="lbl">'+r.dur.toFixed(0)+' ms</span></div></div
|
|
297
|
+
'<div class="tl-track-wrap"><div class="tl-track"><div class="tl-bar'+(s.error?' err':'')+'" style="left:'+left+'%;width:'+width+'%" title="'+label+' — '+r.dur.toFixed(1)+' ms"><span class="lbl">'+r.dur.toFixed(0)+' ms</span></div></div>'+
|
|
298
|
+
(subs?'<div class="tl-subs">'+subs+'</div>':'')+'</div></div>';
|
|
258
299
|
}
|
|
259
300
|
el.innerHTML=html+'</div>';
|
|
260
301
|
}
|
|
261
302
|
function agentHtml(agent,list){
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
const
|
|
266
|
-
const consumes=[...
|
|
267
|
-
const
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
303
|
+
const contract=contractOf(agent,list);AGENT_CONTRACTS[agent]=contract;
|
|
304
|
+
const actualConsumes=uniq(list.flatMap(s=>[s.event_artifact_type,...(s.reads||[]).map(r=>r.data_type)]));
|
|
305
|
+
const actualProduces=uniq(list.flatMap(s=>(s.produces||[]).map(p=>p.name)));
|
|
306
|
+
const actualTools=uniq(list.flatMap(s=>(s.tools||[]).map(t=>t.tool)));
|
|
307
|
+
const consumes=uniq([...(contract.consumes||[]),...actualConsumes]);
|
|
308
|
+
const produces=uniq([...(contract.produces||[]),...actualProduces]);
|
|
309
|
+
const tools=uniq([...(contract.tools||[]),...actualTools]);
|
|
310
|
+
const consumeChips=consumes.map(v=>chipHtml('consume',v,{muted:!actualConsumes.includes(v)})).join('');
|
|
311
|
+
const produceChips=produces.map(v=>chipHtml('produce',v,{muted:!actualProduces.includes(v)})).join('');
|
|
312
|
+
const toolChips=tools.map(v=>chipHtml('tool',v,{muted:!actualTools.includes(v)})).join('');
|
|
313
|
+
let writes=0,llm=0,total=0;
|
|
314
|
+
for(const s of list){writes+=(s.writes||[]).length;llm+=(s.llm_calls||[]).length;total+=Number(s.latency_ms||0);}
|
|
315
|
+
const flow='<span class="contract-flow"><span class="cf-label">consumes</span>'+consumeChips+
|
|
316
|
+
'<span class="arrow">→</span><span class="cf-label">produces</span>'+produceChips+'</span>';
|
|
272
317
|
return '<details class="agent" open><summary><span class="caret">▸</span><b>'+escapeHtml(agent)+'</b>'+
|
|
273
318
|
flow+
|
|
274
|
-
'<span class="muted">'+list.length+' spans · '+
|
|
275
|
-
'<span class="stat">'+writes+' writes · '+llm+' llm</span
|
|
276
|
-
'<
|
|
277
|
-
'<div class="
|
|
319
|
+
'<span class="muted">'+list.length+' spans · '+(total/list.length).toFixed(1)+' ms avg</span>'+
|
|
320
|
+
'<span class="stat">'+writes+' writes · '+llm+' llm'+(actualTools.length?' · '+actualTools.length+' tools':'')+'</span>'+
|
|
321
|
+
'<button class="btn sm" data-contract-agent="'+escapeHtml(agent)+'">contract</button></summary>'+
|
|
322
|
+
'<div class="agent-body">'+
|
|
323
|
+
(toolChips?'<div class="chips"><span class="cf-label">tools</span>'+toolChips+'</div>':'')+
|
|
324
|
+
'<div class="spans">'+list.map(spanHtml).join('')+'</div></div></details>';
|
|
278
325
|
}
|
|
279
326
|
function spanHtml(s){
|
|
280
327
|
const ok=!s.error;const errBlock=s.error?'<div class="err-box">'+escapeHtml(s.error)+'</div>':'';
|
|
281
|
-
const
|
|
282
|
-
const
|
|
328
|
+
const c=s.contract||{};
|
|
329
|
+
const actualConsumes=uniq([s.event_artifact_type,...(s.reads||[]).map(r=>r.data_type)]);
|
|
330
|
+
const actualProduces=uniq((s.produces||[]).map(p=>p.name));
|
|
331
|
+
const consumes=uniq([...(c.consumes||[]),...actualConsumes]);
|
|
332
|
+
const produces=uniq([...(c.produces||[]),...actualProduces]);
|
|
333
|
+
const consumeChips=consumes.map(v=>chipHtml('consume',v,{muted:!actualConsumes.includes(v)})).join('');
|
|
334
|
+
const produceChips=produces.map(v=>chipHtml('produce',v,{muted:!actualProduces.includes(v)})).join('');
|
|
335
|
+
const trigger=s.event_artifact_type
|
|
336
|
+
? '<span class="trigger">triggered by '+chipHtml('consume',s.event_artifact_type,{title:'artifact '+(s.event_artifact_id||'')})+
|
|
337
|
+
(s.event_artifact_id?' <code>'+escapeHtml(shortId(s.event_artifact_id))+'</code>':'')+'</span>'
|
|
338
|
+
: '<span class="trigger">event '+escapeHtml(s.event_type||'')+'</span>';
|
|
339
|
+
const flow='<span class="contract-flow"><span class="cf-label">consumes</span>'+consumeChips+
|
|
340
|
+
'<span class="arrow">→</span><span class="cf-label">produces</span>'+produceChips+'</span>';
|
|
341
|
+
const reads=(s.reads||[]).length?('<div class="io"><h3>Consumes / inputs</h3>'+artsBlock(s.reads)+'</div>'):'';
|
|
342
|
+
const writes=(s.writes||[]).length?('<div class="io"><h3>Outputs / writes</h3>'+artsBlock(s.writes)+'</div>'):'';
|
|
283
343
|
const io=(reads||writes)?'<div class="io-grid">'+reads+writes+'</div>':'';
|
|
284
344
|
const llm=(s.llm_calls||[]).map(llmHtml).join('');
|
|
285
345
|
const toolCount=(s.tools||[]).length;
|
|
286
346
|
return '<details class="node"><summary>'+
|
|
287
347
|
'<span class="caret">▸</span><span class="s-dot '+(ok?'ok':'err')+'"></span>'+
|
|
288
|
-
'<span class="nid">'+escapeHtml(s.agent)+'</span>'+
|
|
289
|
-
|
|
348
|
+
'<span class="nid">'+escapeHtml(s.agent)+'</span>'+trigger+
|
|
349
|
+
flow+
|
|
290
350
|
'<span class="stat">'+Number(s.latency_ms).toFixed(1)+' ms · '+(s.llm_calls||[]).length+' llm'+(toolCount?' · '+toolCount+' tools':'')+(ok?'':' · failed')+'</span></summary>'+
|
|
291
|
-
'<div class="node-body">'+errBlock+io+toolsBlock(s.tools)+(llm?'<div class="llm"><h3>LLM calls</h3>'+llm+'</div>':'')+'</div></details>';
|
|
351
|
+
'<div class="node-body">'+errBlock+io+producesBlock(s.produces,c)+toolsBlock(s.tools)+(llm?'<div class="llm"><h3>LLM calls</h3>'+llm+'</div>':'')+'</div></details>';
|
|
292
352
|
}
|
|
293
|
-
function
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
const
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
function produceChip(p){
|
|
304
|
-
const ops=Number(p.operations||0);const ms=Number(p.latency_ms||0);
|
|
305
|
-
return '<span class="chip produce'+(ops?'':' noop')+'" title="'+escapeHtml(p.name)+' — '+ms.toFixed(1)+' ms">'+
|
|
306
|
-
escapeHtml(p.name)+'<span class="ops'+(ops?'':' zero')+'">'+(ops?('+'+ops):'0')+'</span></span>';
|
|
353
|
+
function producesBlock(list,contract){
|
|
354
|
+
const actual=list||[];const declared=(contract&&contract.produces)||[];
|
|
355
|
+
const names=uniq([...declared,...actual.map(p=>p.name)]);
|
|
356
|
+
if(!names.length)return '';
|
|
357
|
+
const rows=names.map(n=>{
|
|
358
|
+
const p=actual.find(x=>x.name===n);
|
|
359
|
+
if(!p)return '<div class="produce-row muted"><span class="chip produce muted">'+escapeHtml(n)+'</span><span class="muted">did not run</span></div>';
|
|
360
|
+
return '<div class="produce-row"><span class="chip produce" data-filter-kind="produce" data-filter-value="'+escapeHtml(n)+'">'+escapeHtml(n)+'</span><span class="muted">'+Number(p.operations||0)+' ops · '+Number(p.latency_ms||0).toFixed(1)+' ms</span></div>';
|
|
361
|
+
}).join('');
|
|
362
|
+
return '<div class="llm"><h3>Produces</h3>'+rows+'</div>';
|
|
307
363
|
}
|
|
308
364
|
function artHtml(a){
|
|
309
365
|
const op=('<span class="tag">'+escapeHtml(a.op_type||'read')+'</span>');
|
|
@@ -453,10 +509,26 @@ document.getElementById('data-modal-close').addEventListener('click',closeDataMo
|
|
|
453
509
|
document.getElementById('data-modal').addEventListener('click',(e)=>{if(e.target.id==='data-modal')closeDataModal();});
|
|
454
510
|
document.addEventListener('keydown',(e)=>{if(e.key==='Escape')closeDataModal();});
|
|
455
511
|
document.getElementById('agents').addEventListener('click',(e)=>{
|
|
512
|
+
const chip=e.target.closest('[data-filter-kind]');
|
|
513
|
+
if(chip){
|
|
514
|
+
e.preventDefault();e.stopPropagation();
|
|
515
|
+
const kind=chip.getAttribute('data-filter-kind'),value=chip.getAttribute('data-filter-value');
|
|
516
|
+
CHIP_FILTER=(CHIP_FILTER&&CHIP_FILTER.kind===kind&&CHIP_FILTER.value===value)?null:{kind:kind,value:value};
|
|
517
|
+
renderWork();return;
|
|
518
|
+
}
|
|
519
|
+
const contractBtn=e.target.closest('[data-contract-agent]');
|
|
520
|
+
if(contractBtn){
|
|
521
|
+
e.preventDefault();e.stopPropagation();
|
|
522
|
+
const a=contractBtn.getAttribute('data-contract-agent');
|
|
523
|
+
openDataModal('Contract: '+a,JSON.stringify(AGENT_CONTRACTS[a]||{},null,2));return;
|
|
524
|
+
}
|
|
456
525
|
const el=e.target.closest('[data-art-key]');if(!el)return;
|
|
457
526
|
const key=el.getAttribute('data-art-key');
|
|
458
527
|
if(ARTIFACTS[key]!==undefined)openDataModal('Artifact '+key,ARTIFACTS[key]);
|
|
459
528
|
});
|
|
529
|
+
document.addEventListener('click',(e)=>{
|
|
530
|
+
if(e.target.closest('[data-clear-filter]')){CHIP_FILTER=null;renderWork();}
|
|
531
|
+
});
|
|
460
532
|
document.getElementById('copy-link').addEventListener('click',()=>copyTo('copy-link',location.href,'link'));
|
|
461
533
|
document.getElementById('copy-json').addEventListener('click',()=>copyTo('copy-json',TRACE?JSON.stringify(TRACE,null,2):'','json'));
|
|
462
534
|
document.getElementById('toggle-json').addEventListener('click',(e)=>{
|
|
@@ -25,7 +25,15 @@ from typing import TYPE_CHECKING, Any
|
|
|
25
25
|
|
|
26
26
|
from ..patches import Create, Link, Update
|
|
27
27
|
from ..providers import LLMProvider, LLMRequest, LLMResponse, LLMResponseChunk
|
|
28
|
-
from .models import
|
|
28
|
+
from .models import (
|
|
29
|
+
AgentContract,
|
|
30
|
+
AgentSpan,
|
|
31
|
+
ArtifactRef,
|
|
32
|
+
LLMCall,
|
|
33
|
+
ProduceRun,
|
|
34
|
+
RelationRef,
|
|
35
|
+
RunTrace,
|
|
36
|
+
)
|
|
29
37
|
from .store import TraceSink, TraceStore
|
|
30
38
|
|
|
31
39
|
if TYPE_CHECKING:
|
|
@@ -460,6 +468,20 @@ class RunTracer:
|
|
|
460
468
|
if self.tracer is None:
|
|
461
469
|
return None
|
|
462
470
|
try:
|
|
471
|
+
consumes = [
|
|
472
|
+
getattr(c.artifact_type, "__name__", str(c.artifact_type))
|
|
473
|
+
for c in (agent.consumes or [])
|
|
474
|
+
if c.artifact_type is not None
|
|
475
|
+
]
|
|
476
|
+
produces_declared = [
|
|
477
|
+
getattr(p, "name", type(p).__name__) for p in (agent.produces or [])
|
|
478
|
+
]
|
|
479
|
+
declared_tools = getattr(agent, "tools", ()) or ()
|
|
480
|
+
if isinstance(declared_tools, dict):
|
|
481
|
+
declared_tools = declared_tools.values()
|
|
482
|
+
tools_declared = [
|
|
483
|
+
getattr(t, "name", type(t).__name__) for t in declared_tools
|
|
484
|
+
]
|
|
463
485
|
span = AgentSpan(
|
|
464
486
|
agent=agent.name,
|
|
465
487
|
event_type=event.type.value,
|
|
@@ -468,6 +490,13 @@ class RunTracer:
|
|
|
468
490
|
if isinstance(event.artifact_type, type)
|
|
469
491
|
else str(event.artifact_type)
|
|
470
492
|
),
|
|
493
|
+
event_artifact_id=event.artifact_id,
|
|
494
|
+
contract=AgentContract(
|
|
495
|
+
consumes=consumes,
|
|
496
|
+
produces=produces_declared,
|
|
497
|
+
tools=tools_declared,
|
|
498
|
+
priority=agent.priority,
|
|
499
|
+
),
|
|
471
500
|
produces=[
|
|
472
501
|
ProduceRun(name=name, operations=operations, latency_ms=latency)
|
|
473
502
|
for name, operations, latency in (produces or [])
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: reactifact
|
|
3
|
-
Version: 0.16.
|
|
3
|
+
Version: 0.16.1
|
|
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
|
|
@@ -17,7 +17,7 @@ def test_span_renderer_defines_the_tool_count_it_uses():
|
|
|
17
17
|
threw a ReferenceError and left the whole Work section empty."""
|
|
18
18
|
html = _ui_run_html()
|
|
19
19
|
start = html.index("function spanHtml(s){")
|
|
20
|
-
end = html.index("function
|
|
20
|
+
end = html.index("function producesBlock", start)
|
|
21
21
|
body = html[start:end]
|
|
22
22
|
|
|
23
23
|
assert "const toolCount" in body
|
|
@@ -31,3 +31,14 @@ def test_artifact_preview_opens_the_full_data_modal():
|
|
|
31
31
|
assert "openDataModal" in html
|
|
32
32
|
assert 'id="data-modal"' in html
|
|
33
33
|
assert "const ARTIFACTS={}" in html
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_work_ui_has_contract_flow_and_chip_filters():
|
|
37
|
+
html = _ui_run_html()
|
|
38
|
+
assert "contract-flow" in html
|
|
39
|
+
assert "data-contract-agent" in html
|
|
40
|
+
assert "data-filter-kind" in html
|
|
41
|
+
assert "Consumes / inputs" in html
|
|
42
|
+
assert "Outputs / writes" in html
|
|
43
|
+
assert "tl-sub produce" in html
|
|
44
|
+
assert "tl-sub tool" in html
|
|
@@ -146,6 +146,10 @@ def test_runtime_records_spans_and_trace(tmp_path):
|
|
|
146
146
|
assert trace.outcome == "completed"
|
|
147
147
|
# greeter span: read the question, created the answer
|
|
148
148
|
greeter = next(s for s in trace.spans if s.agent == "Greeter")
|
|
149
|
+
assert greeter.event_artifact_id == ctx.list_artifacts(Question)[0].id
|
|
150
|
+
assert greeter.contract.consumes == ["Question"]
|
|
151
|
+
assert greeter.contract.produces == []
|
|
152
|
+
assert greeter.contract.tools == []
|
|
149
153
|
assert any(
|
|
150
154
|
r.artifact_id == ctx.list_artifacts(Question)[0].id for r in greeter.reads
|
|
151
155
|
)
|
|
@@ -187,6 +191,7 @@ def test_runtime_records_which_produce_ran(tmp_path):
|
|
|
187
191
|
assert span.event_artifact_type == "Question"
|
|
188
192
|
assert [p.name for p in span.produces] == ["Emit"]
|
|
189
193
|
assert span.produces[0].operations >= 1
|
|
194
|
+
assert span.contract.produces == ["Emit", "NeverRuns"]
|
|
190
195
|
|
|
191
196
|
|
|
192
197
|
def test_runtime_records_a_produce_that_raised(tmp_path):
|
|
@@ -1100,6 +1105,8 @@ def test_runtime_records_tool_runs(tmp_path):
|
|
|
1100
1105
|
assert span.tools[0].chars == len("found")
|
|
1101
1106
|
assert span.tools[0].produce == "UsesTool"
|
|
1102
1107
|
assert span.tools[0].output == "found"
|
|
1108
|
+
assert span.contract.consumes == ["Question"]
|
|
1109
|
+
assert span.contract.produces == ["UsesTool"]
|
|
1103
1110
|
|
|
1104
1111
|
|
|
1105
1112
|
def test_tool_args_are_opt_in_and_redacted(tmp_path):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|