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.
Files changed (243) hide show
  1. {reactifact-0.11.0/reactifact.egg-info → reactifact-0.11.2}/PKG-INFO +1 -1
  2. {reactifact-0.11.0 → reactifact-0.11.2}/pyproject.toml +1 -1
  3. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/__init__.py +1 -1
  4. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/checkpoints.py +56 -27
  5. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/tracing/web.py +5 -1
  6. {reactifact-0.11.0 → reactifact-0.11.2/reactifact.egg-info}/PKG-INFO +1 -1
  7. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_checkpoint.py +43 -0
  8. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_tracing.py +17 -0
  9. {reactifact-0.11.0 → reactifact-0.11.2}/LICENSE +0 -0
  10. {reactifact-0.11.0 → reactifact-0.11.2}/README.md +0 -0
  11. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/__main__.py +0 -0
  12. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/_extras.py +0 -0
  13. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/_httpx.py +0 -0
  14. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/agent_tool.py +0 -0
  15. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/agents.py +0 -0
  16. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/artifacts.py +0 -0
  17. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/audit.py +0 -0
  18. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/branching.py +0 -0
  19. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/budget.py +0 -0
  20. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/chat.py +0 -0
  21. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/cli/__init__.py +0 -0
  22. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/cli/branch.py +0 -0
  23. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/cli/common.py +0 -0
  24. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/cli/context.py +0 -0
  25. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/cli/graph.py +0 -0
  26. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/cli/replay.py +0 -0
  27. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/cli/scenario.py +0 -0
  28. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/cli/trace.py +0 -0
  29. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/commit.py +0 -0
  30. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/commit_log.py +0 -0
  31. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/consume.py +0 -0
  32. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/context.py +0 -0
  33. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/context_builder.py +0 -0
  34. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/effects.py +0 -0
  35. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/eval.py +0 -0
  36. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/events.py +0 -0
  37. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/interrupt.py +0 -0
  38. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/llm_agent.py +0 -0
  39. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/mcp/__init__.py +0 -0
  40. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/mcp/client.py +0 -0
  41. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/mcp/oauth.py +0 -0
  42. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/mcp/server.py +0 -0
  43. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/native_tool_use.py +0 -0
  44. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/operations.py +0 -0
  45. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/patches.py +0 -0
  46. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/produce.py +0 -0
  47. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/prompts.py +0 -0
  48. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/__init__.py +0 -0
  49. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/_retry.py +0 -0
  50. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/anthropic.py +0 -0
  51. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/azure.py +0 -0
  52. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/cerebras.py +0 -0
  53. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/chat.py +0 -0
  54. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/contracts.py +0 -0
  55. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/deepseek.py +0 -0
  56. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/fake.py +0 -0
  57. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/fireworks.py +0 -0
  58. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/gemini.py +0 -0
  59. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/github_models.py +0 -0
  60. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/groq.py +0 -0
  61. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/image.py +0 -0
  62. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/mistral.py +0 -0
  63. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/nvidia.py +0 -0
  64. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/ollama.py +0 -0
  65. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/openai.py +0 -0
  66. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/openrouter.py +0 -0
  67. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/perplexity.py +0 -0
  68. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/qwen.py +0 -0
  69. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/speech.py +0 -0
  70. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/together.py +0 -0
  71. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/video.py +0 -0
  72. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/xai.py +0 -0
  73. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/providers/zai.py +0 -0
  74. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/py.typed +0 -0
  75. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/quick/__init__.py +0 -0
  76. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/quick/_shared.py +0 -0
  77. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/quick/agent.py +0 -0
  78. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/quick/chat.py +0 -0
  79. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/quick/models.py +0 -0
  80. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/quick/rag.py +0 -0
  81. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/quick/tools_agent.py +0 -0
  82. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/recipes/__init__.py +0 -0
  83. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/recipes/cleanup.py +0 -0
  84. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/recipes/identity.py +0 -0
  85. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/recipes/inputs.py +0 -0
  86. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/recipes/memory.py +0 -0
  87. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/recipes/plan_execute.py +0 -0
  88. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/recipes/reflection.py +0 -0
  89. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/recipes/resolve.py +0 -0
  90. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/recipes/rollback.py +0 -0
  91. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/recipes/search.py +0 -0
  92. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/recipes/skills.py +0 -0
  93. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/recipes/status.py +0 -0
  94. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/recipes/supervisor.py +0 -0
  95. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/recipes/text.py +0 -0
  96. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/recipes/tool_loop.py +0 -0
  97. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/redaction.py +0 -0
  98. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/relations.py +0 -0
  99. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/replay.py +0 -0
  100. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/request.py +0 -0
  101. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/resources.py +0 -0
  102. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/runtime.py +0 -0
  103. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/scheduler.py +0 -0
  104. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/session.py +0 -0
  105. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/sources.py +0 -0
  106. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/streaming.py +0 -0
  107. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/structured.py +0 -0
  108. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/testing/__init__.py +0 -0
  109. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/testing/assertions.py +0 -0
  110. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/testing/exceptions.py +0 -0
  111. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/testing/fault.py +0 -0
  112. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/testing/golden.py +0 -0
  113. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/testing/lab.py +0 -0
  114. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/testing/mock.py +0 -0
  115. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/testing/record.py +0 -0
  116. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/testing/registry.py +0 -0
  117. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/tool_use.py +0 -0
  118. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/tools.py +0 -0
  119. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/tracing/__init__.py +0 -0
  120. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/tracing/_otlp.py +0 -0
  121. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/tracing/langfuse.py +0 -0
  122. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/tracing/models.py +0 -0
  123. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/tracing/otlp.py +0 -0
  124. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/tracing/postgres.py +0 -0
  125. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/tracing/store.py +0 -0
  126. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/tracing/templates/app.css +0 -0
  127. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/tracing/templates/sessions.html +0 -0
  128. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/tracing/templates/ui.html +0 -0
  129. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/tracing/templates/ui_run.html +0 -0
  130. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/tracing/tracer.py +0 -0
  131. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/triggers.py +0 -0
  132. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/verify.py +0 -0
  133. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/viz.py +0 -0
  134. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact/web.py +0 -0
  135. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact.egg-info/SOURCES.txt +0 -0
  136. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact.egg-info/dependency_links.txt +0 -0
  137. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact.egg-info/entry_points.txt +0 -0
  138. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact.egg-info/requires.txt +0 -0
  139. {reactifact-0.11.0 → reactifact-0.11.2}/reactifact.egg-info/top_level.txt +0 -0
  140. {reactifact-0.11.0 → reactifact-0.11.2}/setup.cfg +0 -0
  141. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_absent_consume.py +0 -0
  142. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_adaptive.py +0 -0
  143. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_agent_tool.py +0 -0
  144. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_anthropic_provider.py +0 -0
  145. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_artifacts.py +0 -0
  146. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_audit.py +0 -0
  147. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_backbone.py +0 -0
  148. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_branching.py +0 -0
  149. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_budget.py +0 -0
  150. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_canonical_ports.py +0 -0
  151. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_chat_web.py +0 -0
  152. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_checkpoints_concurrency.py +0 -0
  153. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_cli.py +0 -0
  154. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_cli_scenario.py +0 -0
  155. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_commit_log.py +0 -0
  156. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_concurrency.py +0 -0
  157. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_consumes_produces.py +0 -0
  158. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_context_builder.py +0 -0
  159. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_correlated_consume.py +0 -0
  160. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_debounce.py +0 -0
  161. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_determinism.py +0 -0
  162. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_devops.py +0 -0
  163. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_devops_web.py +0 -0
  164. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_effects.py +0 -0
  165. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_eval.py +0 -0
  166. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_fintech_audit.py +0 -0
  167. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_forklab.py +0 -0
  168. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_forklab_web.py +0 -0
  169. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_friendly_api.py +0 -0
  170. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_gemini_provider.py +0 -0
  171. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_golden.py +0 -0
  172. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_hitl.py +0 -0
  173. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_image_provider.py +0 -0
  174. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_incident_commander.py +0 -0
  175. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_invalidation.py +0 -0
  176. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_join_consume.py +0 -0
  177. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_knowledge.py +0 -0
  178. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_knowledge_web.py +0 -0
  179. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_llm_ladder.py +0 -0
  180. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_loop_bound_client.py +0 -0
  181. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_mcp.py +0 -0
  182. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_medic_lab.py +0 -0
  183. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_medic_lab_web.py +0 -0
  184. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_multisource.py +0 -0
  185. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_native_tool_use.py +0 -0
  186. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_openai_provider.py +0 -0
  187. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_patches.py +0 -0
  188. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_ports.py +0 -0
  189. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_produce_styles.py +0 -0
  190. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_produce_trigger.py +0 -0
  191. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_prompts.py +0 -0
  192. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_provider_auth.py +0 -0
  193. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_provider_retry.py +0 -0
  194. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_providers_integration.py +0 -0
  195. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_quick.py +0 -0
  196. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_reacts_to.py +0 -0
  197. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_recipe_plan_execute.py +0 -0
  198. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_recipe_reflection.py +0 -0
  199. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_recipe_supervisor.py +0 -0
  200. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_recipe_tool_loop.py +0 -0
  201. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_recipes.py +0 -0
  202. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_recipes_inputs.py +0 -0
  203. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_recipes_memory.py +0 -0
  204. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_recipes_skills.py +0 -0
  205. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_redaction.py +0 -0
  206. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_relation_graph.py +0 -0
  207. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_relations.py +0 -0
  208. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_repair.py +0 -0
  209. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_repair_web.py +0 -0
  210. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_replay.py +0 -0
  211. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_repo_agent.py +0 -0
  212. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_request.py +0 -0
  213. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_research.py +0 -0
  214. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_resources.py +0 -0
  215. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_retry.py +0 -0
  216. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_runtime.py +0 -0
  217. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_runtime_errors.py +0 -0
  218. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_scheduler_semantics.py +0 -0
  219. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_sessions.py +0 -0
  220. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_sources.py +0 -0
  221. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_sources_search.py +0 -0
  222. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_sources_vector.py +0 -0
  223. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_speech_provider.py +0 -0
  224. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_starter_app.py +0 -0
  225. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_streaming.py +0 -0
  226. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_structured.py +0 -0
  227. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_support_copilot.py +0 -0
  228. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_testing_assertions.py +0 -0
  229. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_testing_lab.py +0 -0
  230. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_testing_mock.py +0 -0
  231. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_testing_registry.py +0 -0
  232. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_tools.py +0 -0
  233. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_trace_tags.py +0 -0
  234. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_vendor_factories.py +0 -0
  235. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_vendor_multimodal_factories.py +0 -0
  236. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_verify.py +0 -0
  237. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_video_provider.py +0 -0
  238. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_view.py +0 -0
  239. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_viz.py +0 -0
  240. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_web_source.py +0 -0
  241. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_workspace.py +0 -0
  242. {reactifact-0.11.0 → reactifact-0.11.2}/tests/test_workspace_with_sources.py +0 -0
  243. {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.0
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.0"
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"
@@ -49,7 +49,7 @@ from .session import Session, SessionStore
49
49
  from .tools import FunctionTool, Tool, ToolOutput, tool
50
50
  from .triggers import Trigger
51
51
 
52
- __version__ = "0.11.0"
52
+ __version__ = "0.11.2"
53
53
 
54
54
  __all__ = [
55
55
  "Agent",
@@ -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 set(self, key: str, data: dict[str, Any]) -> None:
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
- conn = await self._connection()
330
- async with conn.cursor() as cur:
331
- await cur.execute(
332
- "INSERT INTO kv_entries (key, data) VALUES (%s, %s) "
333
- "ON CONFLICT (key) DO UPDATE SET data = EXCLUDED.data",
334
- (key, json.dumps(data)),
335
- )
336
- await conn.commit()
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
- async with self._lock:
340
- conn = await self._connection()
341
- async with conn.cursor() as cur:
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
- async with self._lock:
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
- async with self._lock:
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
- if self._conn is not None and not self._conn.closed:
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
- @router.get("/api/traces/export")
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.0
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