reactifact 0.11.0__tar.gz → 0.11.2__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.11.0/reactifact.egg-info → reactifact-0.11.2}/PKG-INFO +1 -1
- {reactifact-0.11.0 → reactifact-0.11.2}/pyproject.toml +1 -1
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/__init__.py +1 -1
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/checkpoints.py +56 -27
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/tracing/web.py +5 -1
- {reactifact-0.11.0 → reactifact-0.11.2/reactifact.egg-info}/PKG-INFO +1 -1
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_checkpoint.py +43 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_tracing.py +17 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/LICENSE +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/README.md +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/__main__.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/_extras.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/_httpx.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/agent_tool.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/agents.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/artifacts.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/audit.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/branching.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/budget.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/chat.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/cli/__init__.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/cli/branch.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/cli/common.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/cli/context.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/cli/graph.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/cli/replay.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/cli/scenario.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/cli/trace.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/commit.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/commit_log.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/consume.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/context.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/context_builder.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/effects.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/eval.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/events.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/interrupt.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/llm_agent.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/mcp/__init__.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/mcp/client.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/mcp/oauth.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/mcp/server.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/native_tool_use.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/operations.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/patches.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/produce.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/prompts.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/__init__.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/_retry.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/anthropic.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/azure.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/cerebras.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/chat.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/contracts.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/deepseek.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/fake.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/fireworks.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/gemini.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/github_models.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/groq.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/image.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/mistral.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/nvidia.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/ollama.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/openai.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/openrouter.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/perplexity.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/qwen.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/speech.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/together.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/video.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/xai.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/zai.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/py.typed +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/quick/__init__.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/quick/_shared.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/quick/agent.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/quick/chat.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/quick/models.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/quick/rag.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/quick/tools_agent.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/recipes/__init__.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/recipes/cleanup.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/recipes/identity.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/recipes/inputs.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/recipes/memory.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/recipes/plan_execute.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/recipes/reflection.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/recipes/resolve.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/recipes/rollback.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/recipes/search.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/recipes/skills.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/recipes/status.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/recipes/supervisor.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/recipes/text.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/recipes/tool_loop.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/redaction.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/relations.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/replay.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/request.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/resources.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/runtime.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/scheduler.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/session.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/sources.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/streaming.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/structured.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/testing/__init__.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/testing/assertions.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/testing/exceptions.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/testing/fault.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/testing/golden.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/testing/lab.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/testing/mock.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/testing/record.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/testing/registry.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/tool_use.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/tools.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/tracing/__init__.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/tracing/_otlp.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/tracing/langfuse.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/tracing/models.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/tracing/otlp.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/tracing/postgres.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/tracing/store.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/tracing/templates/app.css +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/tracing/templates/sessions.html +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/tracing/templates/ui.html +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/tracing/templates/ui_run.html +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/tracing/tracer.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/triggers.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/verify.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/viz.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/web.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact.egg-info/SOURCES.txt +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact.egg-info/dependency_links.txt +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact.egg-info/entry_points.txt +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact.egg-info/requires.txt +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/reactifact.egg-info/top_level.txt +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/setup.cfg +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_absent_consume.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_adaptive.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_agent_tool.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_anthropic_provider.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_artifacts.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_audit.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_backbone.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_branching.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_budget.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_canonical_ports.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_chat_web.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_checkpoints_concurrency.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_cli.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_cli_scenario.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_commit_log.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_concurrency.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_consumes_produces.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_context_builder.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_correlated_consume.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_debounce.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_determinism.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_devops.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_devops_web.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_effects.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_eval.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_fintech_audit.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_forklab.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_forklab_web.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_friendly_api.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_gemini_provider.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_golden.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_hitl.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_image_provider.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_incident_commander.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_invalidation.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_join_consume.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_knowledge.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_knowledge_web.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_llm_ladder.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_loop_bound_client.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_mcp.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_medic_lab.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_medic_lab_web.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_multisource.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_native_tool_use.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_openai_provider.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_patches.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_ports.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_produce_styles.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_produce_trigger.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_prompts.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_provider_auth.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_provider_retry.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_providers_integration.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_quick.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_reacts_to.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_recipe_plan_execute.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_recipe_reflection.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_recipe_supervisor.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_recipe_tool_loop.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_recipes.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_recipes_inputs.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_recipes_memory.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_recipes_skills.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_redaction.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_relation_graph.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_relations.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_repair.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_repair_web.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_replay.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_repo_agent.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_request.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_research.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_resources.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_retry.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_runtime.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_runtime_errors.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_scheduler_semantics.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_sessions.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_sources.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_sources_search.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_sources_vector.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_speech_provider.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_starter_app.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_streaming.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_structured.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_support_copilot.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_testing_assertions.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_testing_lab.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_testing_mock.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_testing_registry.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_tools.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_trace_tags.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_vendor_factories.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_vendor_multimodal_factories.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_verify.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_video_provider.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_view.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_viz.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_web_source.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_workspace.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_workspace_with_sources.py +0 -0
- {reactifact-0.11.0 → reactifact-0.11.2}/tests/tests_checkpoints_sqlite.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: reactifact
|
|
3
|
-
Version: 0.11.
|
|
3
|
+
Version: 0.11.2
|
|
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.11.
|
|
7
|
+
version = "0.11.2"
|
|
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"
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
+
import contextlib
|
|
4
5
|
import copy
|
|
5
6
|
import json
|
|
6
7
|
import sqlite3
|
|
@@ -301,6 +302,12 @@ class PostgreSQLKVBackend(KVBackend):
|
|
|
301
302
|
enough for session-checkpoint traffic (small, infrequent writes); reach for
|
|
302
303
|
`psycopg_pool.AsyncConnectionPool` yourself if you need many concurrent
|
|
303
304
|
writers sharing one DSN.
|
|
305
|
+
|
|
306
|
+
The connection **self-heals**: a query cancelled mid-flight (a client that
|
|
307
|
+
drops a streaming turn) or any protocol error leaves the shared libpq
|
|
308
|
+
connection busy/aborted, and a single poisoned connection would otherwise
|
|
309
|
+
fail every later session read/write. Each statement reconnects and retries
|
|
310
|
+
once, so one bad query cannot wedge the whole store.
|
|
304
311
|
"""
|
|
305
312
|
|
|
306
313
|
def __init__(self, dsn: str):
|
|
@@ -324,42 +331,64 @@ class PostgreSQLKVBackend(KVBackend):
|
|
|
324
331
|
self._conn = conn
|
|
325
332
|
return conn
|
|
326
333
|
|
|
327
|
-
async def
|
|
334
|
+
async def _reset(self) -> None:
|
|
335
|
+
"""Drops the cached connection so the next statement reconnects.
|
|
336
|
+
|
|
337
|
+
Called after any error: a connection that raised is never trusted again
|
|
338
|
+
(it may be mid-command or in an aborted transaction).
|
|
339
|
+
"""
|
|
340
|
+
conn, self._conn = self._conn, None
|
|
341
|
+
if conn is not None and not conn.closed:
|
|
342
|
+
with contextlib.suppress(Exception):
|
|
343
|
+
await conn.close()
|
|
344
|
+
|
|
345
|
+
async def _run(
|
|
346
|
+
self,
|
|
347
|
+
sql: str,
|
|
348
|
+
params: tuple[Any, ...] = (),
|
|
349
|
+
*,
|
|
350
|
+
fetch: str | None = None,
|
|
351
|
+
) -> Any:
|
|
352
|
+
"""Runs one statement under the lock, reconnecting once on failure.
|
|
353
|
+
|
|
354
|
+
All `kv_entries` statements are idempotent (upsert/delete/select), so a
|
|
355
|
+
retry after a dropped connection is safe.
|
|
356
|
+
"""
|
|
328
357
|
async with self._lock:
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
358
|
+
for attempt in (0, 1):
|
|
359
|
+
try:
|
|
360
|
+
conn = await self._connection()
|
|
361
|
+
async with conn.cursor() as cur:
|
|
362
|
+
await cur.execute(sql, params)
|
|
363
|
+
result = await getattr(cur, fetch)() if fetch else None
|
|
364
|
+
await conn.commit()
|
|
365
|
+
return result
|
|
366
|
+
except self._psycopg.Error:
|
|
367
|
+
await self._reset()
|
|
368
|
+
if attempt:
|
|
369
|
+
raise
|
|
370
|
+
raise AssertionError("unreachable") # pragma: no cover
|
|
371
|
+
|
|
372
|
+
async def set(self, key: str, data: dict[str, Any]) -> None:
|
|
373
|
+
await self._run(
|
|
374
|
+
"INSERT INTO kv_entries (key, data) VALUES (%s, %s) "
|
|
375
|
+
"ON CONFLICT (key) DO UPDATE SET data = EXCLUDED.data",
|
|
376
|
+
(key, json.dumps(data)),
|
|
377
|
+
)
|
|
337
378
|
|
|
338
379
|
async def get(self, key: str) -> dict[str, Any] | None:
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
await cur.execute("SELECT data FROM kv_entries WHERE key = %s", (key,))
|
|
343
|
-
row = await cur.fetchone()
|
|
380
|
+
row = await self._run(
|
|
381
|
+
"SELECT data FROM kv_entries WHERE key = %s", (key,), fetch="fetchone"
|
|
382
|
+
)
|
|
344
383
|
return cast(dict[str, Any], json.loads(row[0])) if row is not None else None
|
|
345
384
|
|
|
346
385
|
async def delete(self, key: str) -> None:
|
|
347
|
-
|
|
348
|
-
conn = await self._connection()
|
|
349
|
-
async with conn.cursor() as cur:
|
|
350
|
-
await cur.execute("DELETE FROM kv_entries WHERE key = %s", (key,))
|
|
351
|
-
await conn.commit()
|
|
386
|
+
await self._run("DELETE FROM kv_entries WHERE key = %s", (key,))
|
|
352
387
|
|
|
353
388
|
async def keys(self) -> list[str]:
|
|
354
|
-
|
|
355
|
-
conn = await self._connection()
|
|
356
|
-
async with conn.cursor() as cur:
|
|
357
|
-
await cur.execute("SELECT key FROM kv_entries")
|
|
358
|
-
rows = await cur.fetchall()
|
|
389
|
+
rows = await self._run("SELECT key FROM kv_entries", fetch="fetchall")
|
|
359
390
|
return [cast(str, r[0]) for r in rows]
|
|
360
391
|
|
|
361
392
|
async def aclose(self) -> None:
|
|
362
393
|
async with self._lock:
|
|
363
|
-
|
|
364
|
-
await self._conn.close()
|
|
365
|
-
self._conn = None
|
|
394
|
+
await self._reset()
|
|
@@ -141,7 +141,11 @@ def create_trace_router(
|
|
|
141
141
|
offset=offset,
|
|
142
142
|
)
|
|
143
143
|
|
|
144
|
-
|
|
144
|
+
# ``response_model=None`` is required: the module uses ``from __future__
|
|
145
|
+
# import annotations`` and imports fastapi lazily here, so the ``->
|
|
146
|
+
# JSONResponse`` return annotation stays an unresolvable forward ref and
|
|
147
|
+
# FastAPI would fail to build ``/openapi.json``.
|
|
148
|
+
@router.get("/api/traces/export", response_model=None)
|
|
145
149
|
async def export_traces(
|
|
146
150
|
session_id: str | None = Query(default=None),
|
|
147
151
|
outcome: str | None = Query(default=None),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: reactifact
|
|
3
|
-
Version: 0.11.
|
|
3
|
+
Version: 0.11.2
|
|
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
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
import contextlib
|
|
2
3
|
import os
|
|
3
4
|
import tempfile
|
|
4
5
|
|
|
@@ -109,6 +110,48 @@ async def _test_postgres_kv_roundtrip(dsn: str) -> None:
|
|
|
109
110
|
await backend.aclose()
|
|
110
111
|
|
|
111
112
|
|
|
113
|
+
def test_postgres_kv_self_heals_after_a_cancelled_query():
|
|
114
|
+
"""A poisoned shared connection must not wedge the store.
|
|
115
|
+
|
|
116
|
+
A query cancelled mid-flight (a client that drops a streaming turn) leaves
|
|
117
|
+
libpq busy/aborted; before the fix every later session read/write failed
|
|
118
|
+
forever with ``InFailedSqlTransaction`` / "another command is already in
|
|
119
|
+
progress". The backend must reconnect and retry instead.
|
|
120
|
+
"""
|
|
121
|
+
dsn = os.environ.get("TEST_PG_DSN")
|
|
122
|
+
if not dsn:
|
|
123
|
+
pytest.skip("TEST_PG_DSN not set — skipping Postgres KV integration")
|
|
124
|
+
import psycopg # noqa: F401 (ensure the extra is installed)
|
|
125
|
+
|
|
126
|
+
asyncio.run(_test_postgres_kv_self_heal(dsn))
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
async def _test_postgres_kv_self_heal(dsn: str) -> None:
|
|
130
|
+
from reactifact.checkpoints import PostgreSQLKVBackend
|
|
131
|
+
|
|
132
|
+
backend = PostgreSQLKVBackend(dsn)
|
|
133
|
+
try:
|
|
134
|
+
await backend.set("heal", {"v": 1})
|
|
135
|
+
conn = await backend._connection()
|
|
136
|
+
|
|
137
|
+
async def slow() -> None:
|
|
138
|
+
async with conn.cursor() as cur:
|
|
139
|
+
await cur.execute("SELECT pg_sleep(10)")
|
|
140
|
+
|
|
141
|
+
task = asyncio.create_task(slow())
|
|
142
|
+
await asyncio.sleep(0.3)
|
|
143
|
+
task.cancel()
|
|
144
|
+
with contextlib.suppress(asyncio.CancelledError):
|
|
145
|
+
await task
|
|
146
|
+
|
|
147
|
+
assert await backend.get("heal") == {"v": 1}
|
|
148
|
+
assert backend._conn is not conn # reconnected, not the poisoned one
|
|
149
|
+
await backend.delete("heal")
|
|
150
|
+
assert await backend.get("heal") is None
|
|
151
|
+
finally:
|
|
152
|
+
await backend.aclose()
|
|
153
|
+
|
|
154
|
+
|
|
112
155
|
def test_require_extra_readable_error():
|
|
113
156
|
"""Missing extra dependency → a hint, not a bare ModuleNotFoundError."""
|
|
114
157
|
from unittest import mock
|
|
@@ -576,6 +576,23 @@ def test_trace_router_open_without_auth(tmp_path):
|
|
|
576
576
|
assert client.get("/api/traces").status_code == 200
|
|
577
577
|
|
|
578
578
|
|
|
579
|
+
def test_trace_router_openapi_schema_builds(tmp_path):
|
|
580
|
+
from fastapi import FastAPI
|
|
581
|
+
from fastapi.testclient import TestClient
|
|
582
|
+
from reactifact.tracing.web import create_trace_router
|
|
583
|
+
|
|
584
|
+
store = TraceStore(str(tmp_path / "schema.db"))
|
|
585
|
+
app = FastAPI()
|
|
586
|
+
app.include_router(create_trace_router(store))
|
|
587
|
+
client = TestClient(app)
|
|
588
|
+
|
|
589
|
+
# regression: the ``-> JSONResponse`` export handler used to make schema
|
|
590
|
+
# generation raise on an unresolvable forward ref (no module-level import)
|
|
591
|
+
spec = client.get("/openapi.json")
|
|
592
|
+
assert spec.status_code == 200
|
|
593
|
+
assert "/api/traces/export" in spec.json()["paths"]
|
|
594
|
+
|
|
595
|
+
|
|
579
596
|
def test_trace_run_page_embeds_mermaid_diagram(tmp_path):
|
|
580
597
|
from fastapi import FastAPI
|
|
581
598
|
from fastapi.testclient import TestClient
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|