reactifact 0.14.1__tar.gz → 0.14.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 (315) hide show
  1. {reactifact-0.14.1/reactifact.egg-info → reactifact-0.14.2}/PKG-INFO +1 -1
  2. {reactifact-0.14.1 → reactifact-0.14.2}/pyproject.toml +1 -1
  3. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/__init__.py +1 -1
  4. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/tracing/templates/sessions.html +6 -6
  5. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/tracing/templates/ui.html +14 -14
  6. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/tracing/templates/ui_run.html +10 -10
  7. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/tracing/web.py +49 -13
  8. {reactifact-0.14.1 → reactifact-0.14.2/reactifact.egg-info}/PKG-INFO +1 -1
  9. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_tracing.py +84 -0
  10. {reactifact-0.14.1 → reactifact-0.14.2}/LICENSE +0 -0
  11. {reactifact-0.14.1 → reactifact-0.14.2}/README.md +0 -0
  12. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/__main__.py +0 -0
  13. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/_extras.py +0 -0
  14. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/_httpx.py +0 -0
  15. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/a2a/__init__.py +0 -0
  16. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/a2a/client.py +0 -0
  17. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/a2a/models.py +0 -0
  18. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/a2a/remote.py +0 -0
  19. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/a2a/server.py +0 -0
  20. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/agent_tool.py +0 -0
  21. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/agents.py +0 -0
  22. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/artifacts.py +0 -0
  23. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/audit.py +0 -0
  24. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/authz.py +0 -0
  25. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/branching.py +0 -0
  26. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/budget.py +0 -0
  27. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/cache.py +0 -0
  28. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/chat.py +0 -0
  29. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/checkpoints.py +0 -0
  30. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/cli/__init__.py +0 -0
  31. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/cli/branch.py +0 -0
  32. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/cli/common.py +0 -0
  33. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/cli/context.py +0 -0
  34. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/cli/eval.py +0 -0
  35. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/cli/graph.py +0 -0
  36. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/cli/replay.py +0 -0
  37. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/cli/scenario.py +0 -0
  38. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/cli/skills.py +0 -0
  39. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/cli/trace.py +0 -0
  40. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/commit.py +0 -0
  41. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/commit_log.py +0 -0
  42. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/consume.py +0 -0
  43. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/context.py +0 -0
  44. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/context_builder.py +0 -0
  45. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/effects.py +0 -0
  46. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/eval/__init__.py +0 -0
  47. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/eval/dataset.py +0 -0
  48. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/eval/evaluators.py +0 -0
  49. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/eval/judge.py +0 -0
  50. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/eval/online.py +0 -0
  51. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/eval/runner.py +0 -0
  52. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/eval/scoring.py +0 -0
  53. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/events.py +0 -0
  54. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/guardrails.py +0 -0
  55. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/interrupt.py +0 -0
  56. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/llm_agent.py +0 -0
  57. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/logging.py +0 -0
  58. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/mcp/__init__.py +0 -0
  59. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/mcp/client.py +0 -0
  60. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/mcp/oauth.py +0 -0
  61. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/mcp/server.py +0 -0
  62. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/metrics.py +0 -0
  63. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/native_tool_use.py +0 -0
  64. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/operations.py +0 -0
  65. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/patches.py +0 -0
  66. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/pricing.py +0 -0
  67. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/produce.py +0 -0
  68. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/prompts.py +0 -0
  69. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/providers/__init__.py +0 -0
  70. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/providers/_retry.py +0 -0
  71. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/providers/anthropic.py +0 -0
  72. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/providers/azure.py +0 -0
  73. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/providers/cerebras.py +0 -0
  74. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/providers/chat.py +0 -0
  75. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/providers/contracts.py +0 -0
  76. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/providers/deepseek.py +0 -0
  77. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/providers/fake.py +0 -0
  78. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/providers/fireworks.py +0 -0
  79. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/providers/gemini.py +0 -0
  80. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/providers/github_models.py +0 -0
  81. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/providers/groq.py +0 -0
  82. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/providers/image.py +0 -0
  83. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/providers/mistral.py +0 -0
  84. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/providers/nvidia.py +0 -0
  85. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/providers/ollama.py +0 -0
  86. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/providers/openai.py +0 -0
  87. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/providers/openrouter.py +0 -0
  88. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/providers/perplexity.py +0 -0
  89. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/providers/qwen.py +0 -0
  90. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/providers/speech.py +0 -0
  91. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/providers/together.py +0 -0
  92. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/providers/video.py +0 -0
  93. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/providers/xai.py +0 -0
  94. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/providers/zai.py +0 -0
  95. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/py.typed +0 -0
  96. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/quick/__init__.py +0 -0
  97. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/quick/_shared.py +0 -0
  98. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/quick/agent.py +0 -0
  99. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/quick/chat.py +0 -0
  100. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/quick/models.py +0 -0
  101. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/quick/rag.py +0 -0
  102. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/quick/tools_agent.py +0 -0
  103. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/quota.py +0 -0
  104. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/recipes/__init__.py +0 -0
  105. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/recipes/cleanup.py +0 -0
  106. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/recipes/conversation.py +0 -0
  107. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/recipes/identity.py +0 -0
  108. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/recipes/inputs.py +0 -0
  109. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/recipes/memory.py +0 -0
  110. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/recipes/plan_execute.py +0 -0
  111. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/recipes/reflection.py +0 -0
  112. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/recipes/resolve.py +0 -0
  113. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/recipes/rollback.py +0 -0
  114. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/recipes/search.py +0 -0
  115. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/recipes/skills.py +0 -0
  116. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/recipes/status.py +0 -0
  117. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/recipes/supervisor.py +0 -0
  118. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/recipes/text.py +0 -0
  119. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/recipes/tool_loop.py +0 -0
  120. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/redaction.py +0 -0
  121. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/relations.py +0 -0
  122. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/replay.py +0 -0
  123. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/request.py +0 -0
  124. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/resources.py +0 -0
  125. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/routing.py +0 -0
  126. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/runtime.py +0 -0
  127. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/scheduler.py +0 -0
  128. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/session.py +0 -0
  129. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/skills/reactifact/SKILL.md +0 -0
  130. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/skills/reactifact/references/effects-and-state.md +0 -0
  131. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/skills/reactifact/references/trust-and-safety.md +0 -0
  132. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/skills/reactifact-agents/SKILL.md +0 -0
  133. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/skills/reactifact-agents/references/consume.md +0 -0
  134. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/skills/reactifact-agents/references/effects.md +0 -0
  135. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/skills/reactifact-eval/SKILL.md +0 -0
  136. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/skills/reactifact-eval/references/online.md +0 -0
  137. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/skills/reactifact-from-langchain/SKILL.md +0 -0
  138. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/skills/reactifact-llm/SKILL.md +0 -0
  139. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/skills/reactifact-llm/references/providers.md +0 -0
  140. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/skills/reactifact-llm/references/tools.md +0 -0
  141. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/skills/reactifact-observability/SKILL.md +0 -0
  142. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/skills/reactifact-rag/SKILL.md +0 -0
  143. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/skills/reactifact-rag/references/sources.md +0 -0
  144. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/skills/reactifact-testing/SKILL.md +0 -0
  145. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/sources.py +0 -0
  146. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/streaming.py +0 -0
  147. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/structured.py +0 -0
  148. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/testing/__init__.py +0 -0
  149. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/testing/assertions.py +0 -0
  150. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/testing/exceptions.py +0 -0
  151. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/testing/fault.py +0 -0
  152. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/testing/golden.py +0 -0
  153. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/testing/lab.py +0 -0
  154. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/testing/mock.py +0 -0
  155. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/testing/pytest_plugin.py +0 -0
  156. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/testing/record.py +0 -0
  157. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/testing/registry.py +0 -0
  158. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/tool_use.py +0 -0
  159. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/tools.py +0 -0
  160. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/tracing/__init__.py +0 -0
  161. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/tracing/_otlp.py +0 -0
  162. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/tracing/columns.py +0 -0
  163. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/tracing/langfuse.py +0 -0
  164. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/tracing/models.py +0 -0
  165. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/tracing/otlp.py +0 -0
  166. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/tracing/postgres.py +0 -0
  167. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/tracing/store.py +0 -0
  168. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/tracing/templates/app.css +0 -0
  169. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/tracing/tracer.py +0 -0
  170. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/triggers.py +0 -0
  171. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/types.py +0 -0
  172. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/verify.py +0 -0
  173. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/viz.py +0 -0
  174. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact/web.py +0 -0
  175. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact.egg-info/SOURCES.txt +0 -0
  176. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact.egg-info/dependency_links.txt +0 -0
  177. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact.egg-info/entry_points.txt +0 -0
  178. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact.egg-info/requires.txt +0 -0
  179. {reactifact-0.14.1 → reactifact-0.14.2}/reactifact.egg-info/top_level.txt +0 -0
  180. {reactifact-0.14.1 → reactifact-0.14.2}/setup.cfg +0 -0
  181. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_a2a.py +0 -0
  182. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_a2a_example.py +0 -0
  183. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_a2a_interop.py +0 -0
  184. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_absent_consume.py +0 -0
  185. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_adaptive.py +0 -0
  186. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_agent_tool.py +0 -0
  187. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_anthropic_provider.py +0 -0
  188. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_artifacts.py +0 -0
  189. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_audit.py +0 -0
  190. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_authz.py +0 -0
  191. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_backbone.py +0 -0
  192. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_branching.py +0 -0
  193. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_budget.py +0 -0
  194. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_cache.py +0 -0
  195. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_canonical_ports.py +0 -0
  196. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_chat_example.py +0 -0
  197. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_chat_memory.py +0 -0
  198. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_chat_outbox.py +0 -0
  199. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_chat_web.py +0 -0
  200. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_checkpoint.py +0 -0
  201. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_checkpoints_concurrency.py +0 -0
  202. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_cli.py +0 -0
  203. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_cli_scenario.py +0 -0
  204. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_commit_log.py +0 -0
  205. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_compaction.py +0 -0
  206. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_concurrency.py +0 -0
  207. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_consumes_produces.py +0 -0
  208. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_context_builder.py +0 -0
  209. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_correlated_consume.py +0 -0
  210. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_debounce.py +0 -0
  211. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_determinism.py +0 -0
  212. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_devops.py +0 -0
  213. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_devops_online.py +0 -0
  214. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_devops_web.py +0 -0
  215. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_effects.py +0 -0
  216. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_eval.py +0 -0
  217. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_eval_dataset.py +0 -0
  218. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_eval_judge.py +0 -0
  219. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_eval_online.py +0 -0
  220. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_eval_trajectory.py +0 -0
  221. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_fintech_audit.py +0 -0
  222. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_forklab.py +0 -0
  223. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_forklab_web.py +0 -0
  224. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_friendly_api.py +0 -0
  225. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_gemini_provider.py +0 -0
  226. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_golden.py +0 -0
  227. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_guardrails.py +0 -0
  228. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_hitl.py +0 -0
  229. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_image_provider.py +0 -0
  230. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_incident_commander.py +0 -0
  231. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_invalidation.py +0 -0
  232. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_join_consume.py +0 -0
  233. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_knowledge.py +0 -0
  234. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_knowledge_web.py +0 -0
  235. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_llm_ladder.py +0 -0
  236. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_logging.py +0 -0
  237. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_loop_bound_client.py +0 -0
  238. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_mcp.py +0 -0
  239. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_medic_lab.py +0 -0
  240. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_medic_lab_web.py +0 -0
  241. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_metrics.py +0 -0
  242. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_multisource.py +0 -0
  243. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_native_tool_use.py +0 -0
  244. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_openai_provider.py +0 -0
  245. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_outbox.py +0 -0
  246. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_patches.py +0 -0
  247. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_pending_actions.py +0 -0
  248. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_ports.py +0 -0
  249. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_produce_styles.py +0 -0
  250. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_produce_trigger.py +0 -0
  251. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_prompts.py +0 -0
  252. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_provider_auth.py +0 -0
  253. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_provider_retry.py +0 -0
  254. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_providers_integration.py +0 -0
  255. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_public_api.py +0 -0
  256. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_quick.py +0 -0
  257. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_quota.py +0 -0
  258. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_reacts_to.py +0 -0
  259. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_recipe_conversation.py +0 -0
  260. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_recipe_plan_execute.py +0 -0
  261. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_recipe_reflection.py +0 -0
  262. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_recipe_supervisor.py +0 -0
  263. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_recipe_tool_loop.py +0 -0
  264. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_recipes.py +0 -0
  265. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_recipes_inputs.py +0 -0
  266. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_recipes_memory.py +0 -0
  267. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_recipes_skills.py +0 -0
  268. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_redaction.py +0 -0
  269. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_relation_graph.py +0 -0
  270. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_relations.py +0 -0
  271. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_repair.py +0 -0
  272. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_repair_web.py +0 -0
  273. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_replay.py +0 -0
  274. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_repo_agent.py +0 -0
  275. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_request.py +0 -0
  276. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_research.py +0 -0
  277. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_resources.py +0 -0
  278. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_retry.py +0 -0
  279. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_routing.py +0 -0
  280. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_runtime.py +0 -0
  281. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_runtime_errors.py +0 -0
  282. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_scheduler_semantics.py +0 -0
  283. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_sessions.py +0 -0
  284. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_shutdown.py +0 -0
  285. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_skills.py +0 -0
  286. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_sources.py +0 -0
  287. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_sources_search.py +0 -0
  288. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_sources_vector.py +0 -0
  289. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_speech_provider.py +0 -0
  290. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_starter_app.py +0 -0
  291. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_streaming.py +0 -0
  292. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_structured.py +0 -0
  293. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_support_copilot.py +0 -0
  294. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_testing_assertions.py +0 -0
  295. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_testing_golden.py +0 -0
  296. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_testing_lab.py +0 -0
  297. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_testing_mock.py +0 -0
  298. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_testing_provenance.py +0 -0
  299. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_testing_pytest_plugin.py +0 -0
  300. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_testing_registry.py +0 -0
  301. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_token_budget.py +0 -0
  302. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_tools.py +0 -0
  303. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_trace_columns.py +0 -0
  304. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_trace_tags.py +0 -0
  305. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_type_registry.py +0 -0
  306. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_vendor_factories.py +0 -0
  307. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_vendor_multimodal_factories.py +0 -0
  308. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_verify.py +0 -0
  309. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_video_provider.py +0 -0
  310. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_view.py +0 -0
  311. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_viz.py +0 -0
  312. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_web_source.py +0 -0
  313. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_workspace.py +0 -0
  314. {reactifact-0.14.1 → reactifact-0.14.2}/tests/test_workspace_with_sources.py +0 -0
  315. {reactifact-0.14.1 → reactifact-0.14.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.14.1
3
+ Version: 0.14.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.14.1"
7
+ version = "0.14.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"
@@ -51,7 +51,7 @@ from .tools import FunctionTool, Tool, ToolOutput, tool
51
51
  from .triggers import Trigger
52
52
  from .types import register as register_type
53
53
 
54
- __version__ = "0.14.1"
54
+ __version__ = "0.14.2"
55
55
 
56
56
  __all__ = [
57
57
  "Agent",
@@ -4,22 +4,22 @@
4
4
  <meta charset="utf-8"/>
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1"/>
6
6
  <title>reactifact sessions</title>
7
- <link rel="stylesheet" href="/traces/assets/app.css"/>
7
+ <link rel="stylesheet" href="__BASE__/traces/assets/app.css"/>
8
8
  <script>(function(){try{var s=localStorage.getItem('reactifact-theme');var d=s?s==='dark':window.matchMedia('(prefers-color-scheme: dark)').matches;document.documentElement.classList.toggle('dark',d);}catch(e){document.documentElement.classList.add('dark');}})();</script>
9
9
  </head>
10
10
  <body>
11
11
  <div class="app" id="app">
12
12
  <aside class="sidebar">
13
- <a class="brand" href="/traces">
13
+ <a class="brand" href="__BASE__/traces">
14
14
  <svg width="22" height="22" viewBox="0 0 24 24" fill="none" aria-hidden="true"><circle cx="5" cy="12" r="2.6" fill="currentColor"/><circle cx="19" cy="5" r="2.6" fill="currentColor"/><circle cx="19" cy="19" r="2.6" fill="currentColor"/><path d="M7.4 11 16.6 6M7.4 13l9.2 5" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
15
15
  <span class="name">reactifact <em>traces</em></span>
16
16
  </a>
17
17
  <nav class="nav">
18
- <a class="nav-item" href="/traces">
18
+ <a class="nav-item" href="__BASE__/traces">
19
19
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="5" cy="12" r="2.2"/><circle cx="19" cy="5" r="2.2"/><circle cx="19" cy="19" r="2.2"/><path d="M7.2 11 16.8 6M7.2 13l9.6 5"/></svg>
20
20
  <span class="nav-label">Traces</span>
21
21
  </a>
22
- <a class="nav-item active" href="/sessions">
22
+ <a class="nav-item active" href="__BASE__/sessions">
23
23
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 5h16v11H8l-4 4z"/></svg>
24
24
  <span class="nav-label">Sessions</span>
25
25
  </a>
@@ -97,7 +97,7 @@ async function load(){
97
97
  const p=new URLSearchParams({limit:state.limit,offset:state.offset});
98
98
  if(state.q)p.set('q',state.q);
99
99
  try{
100
- const res=await fetch('/api/sessions?'+p);
100
+ const res=await fetch('__BASE__/api/sessions?'+p);
101
101
  if(!res.ok)throw new Error('HTTP '+res.status);
102
102
  const data=await res.json();
103
103
  state.items=data.items||[];state.total=data.total||0;render();
@@ -133,7 +133,7 @@ function updatePager(){
133
133
  }
134
134
  document.getElementById('rows').addEventListener('click',(e)=>{
135
135
  const row=e.target.closest('tr.row');if(!row)return;
136
- window.location.href='/traces?session_id='+encodeURIComponent(row.dataset.id);
136
+ window.location.href='__BASE__/traces?session_id='+encodeURIComponent(row.dataset.id);
137
137
  });
138
138
  document.getElementById('f-q').addEventListener('input',debounce((e)=>{state.q=e.target.value.trim();state.offset=0;load();}));
139
139
  document.getElementById('prev').addEventListener('click',()=>{state.offset=Math.max(0,state.offset-state.limit);load();});
@@ -4,22 +4,22 @@
4
4
  <meta charset="utf-8"/>
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1"/>
6
6
  <title>reactifact traces</title>
7
- <link rel="stylesheet" href="/traces/assets/app.css"/>
7
+ <link rel="stylesheet" href="__BASE__/traces/assets/app.css"/>
8
8
  <script>(function(){try{var s=localStorage.getItem('reactifact-theme');var d=s?s==='dark':window.matchMedia('(prefers-color-scheme: dark)').matches;document.documentElement.classList.toggle('dark',d);}catch(e){document.documentElement.classList.add('dark');}})();</script>
9
9
  </head>
10
10
  <body>
11
11
  <div class="app" id="app">
12
12
  <aside class="sidebar">
13
- <a class="brand" href="/traces">
13
+ <a class="brand" href="__BASE__/traces">
14
14
  <svg width="22" height="22" viewBox="0 0 24 24" fill="none" aria-hidden="true"><circle cx="5" cy="12" r="2.6" fill="currentColor"/><circle cx="19" cy="5" r="2.6" fill="currentColor"/><circle cx="19" cy="19" r="2.6" fill="currentColor"/><path d="M7.4 11 16.6 6M7.4 13l9.2 5" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
15
15
  <span class="name">reactifact <em>traces</em></span>
16
16
  </a>
17
17
  <nav class="nav">
18
- <a class="nav-item active" href="/traces">
18
+ <a class="nav-item active" href="__BASE__/traces">
19
19
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="5" cy="12" r="2.2"/><circle cx="19" cy="5" r="2.2"/><circle cx="19" cy="19" r="2.2"/><path d="M7.2 11 16.8 6M7.2 13l9.6 5"/></svg>
20
20
  <span class="nav-label">Traces</span>
21
21
  </a>
22
- <a class="nav-item" href="/sessions">
22
+ <a class="nav-item" href="__BASE__/sessions">
23
23
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 5h16v11H8l-4 4z"/></svg>
24
24
  <span class="nav-label">Sessions</span>
25
25
  </a>
@@ -188,7 +188,7 @@ function loadColumnPref(){try{const raw=localStorage.getItem(COLS_KEY);VISIBLE_C
188
188
  function saveColumnPref(){try{localStorage.setItem(COLS_KEY,JSON.stringify(VISIBLE_COLS||[]));}catch(_){}}
189
189
  function visibleColumns(){const all=state.columns.map(c=>c.label);if(VISIBLE_COLS===null)return all;return all.filter(l=>VISIBLE_COLS.includes(l));}
190
190
  async function loadColumns(){
191
- try{const r=await fetch('/api/columns');state.columns=(await r.json()).items||[];}
191
+ try{const r=await fetch('__BASE__/api/columns');state.columns=(await r.json()).items||[];}
192
192
  catch(_){state.columns=[];}
193
193
  render();
194
194
  }
@@ -287,7 +287,7 @@ function filters(){
287
287
  }
288
288
 
289
289
  async function loadFacets(){
290
- try{const r=await fetch('/api/tags');const d=await r.json();state.facets=d.items||[];}
290
+ try{const r=await fetch('__BASE__/api/tags');const d=await r.json();state.facets=d.items||[];}
291
291
  catch(_){state.facets=[];}
292
292
  renderFacets();
293
293
  }
@@ -308,7 +308,7 @@ function renderFacets(){
308
308
  async function load(){
309
309
  const q=filters();q.set('limit',state.limit);q.set('offset',state.offset);
310
310
  try{
311
- const res=await fetch('/api/traces?'+q);
311
+ const res=await fetch('__BASE__/api/traces?'+q);
312
312
  if(!res.ok)throw new Error('HTTP '+res.status);
313
313
  const data=await res.json();
314
314
  state.runs=data.items||[];state.total=data.total||0;state.stats=data.stats||null;
@@ -454,7 +454,7 @@ document.getElementById('view-del').addEventListener('click',()=>{
454
454
  const sel=document.getElementById('views');if(sel.value==='')return;
455
455
  const views=getViews();views.splice(Number(sel.value),1);setViews(views);renderViews();toast('View deleted');
456
456
  });
457
- document.getElementById('export').addEventListener('click',()=>{const q=filters();q.set('limit',500);window.location.href='/api/traces/export?'+q;});
457
+ document.getElementById('export').addEventListener('click',()=>{const q=filters();q.set('limit',500);window.location.href='__BASE__/api/traces/export?'+q;});
458
458
  document.getElementById('live').addEventListener('click',(e)=>{
459
459
  state.live=!state.live;e.currentTarget.classList.toggle('on',state.live);
460
460
  if(state.live){timer=setInterval(()=>{if(!document.hidden)load();},3000);}else{clearInterval(timer);}
@@ -462,7 +462,7 @@ document.getElementById('live').addEventListener('click',(e)=>{
462
462
  document.getElementById('runs').addEventListener('click',(e)=>{
463
463
  if(e.target.closest('.rowcheck'))return;
464
464
  const row=e.target.closest('tr.row');if(!row)return;
465
- window.location.href='/traces/'+row.dataset.id;
465
+ window.location.href='__BASE__/traces/'+row.dataset.id;
466
466
  });
467
467
  document.getElementById('runs').addEventListener('change',(e)=>{
468
468
  const c=e.target.closest('.rowcheck');if(!c)return;
@@ -476,13 +476,13 @@ document.getElementById('bulk-clear').addEventListener('click',()=>{state.select
476
476
  document.getElementById('bulk-apply').addEventListener('click',async()=>{
477
477
  const tag=document.getElementById('bulk-tag').value.trim();if(!tag)return;
478
478
  const note=document.getElementById('bulk-note').value;
479
- await fetch('/api/traces/tags',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({run_ids:Array.from(state.selected),tag:tag,note:note})});
479
+ await fetch('__BASE__/api/traces/tags',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({run_ids:Array.from(state.selected),tag:tag,note:note})});
480
480
  document.getElementById('bulk-tag').value='';document.getElementById('bulk-note').value='';
481
481
  state.selected.clear();toast('Tagged');await refresh();
482
482
  });
483
483
  document.getElementById('bulk-remove').addEventListener('click',async()=>{
484
484
  const tag=document.getElementById('bulk-remove-sel').value;if(!tag)return;
485
- await fetch('/api/traces/tags/remove',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({run_ids:Array.from(state.selected),tag:tag})});
485
+ await fetch('__BASE__/api/traces/tags/remove',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({run_ids:Array.from(state.selected),tag:tag})});
486
486
  state.selected.clear();toast('Tag removed');await refresh();
487
487
  });
488
488
 
@@ -507,18 +507,18 @@ document.getElementById('tagrows').addEventListener('click',async(e)=>{
507
507
  if(e.target.closest('.row-save')){
508
508
  const name=row.querySelector('.row-name').value.trim();
509
509
  const color=row.querySelector('.row-color').value;
510
- await fetch('/api/tags/'+encodeURIComponent(old),{method:'PATCH',headers:{'Content-Type':'application/json'},body:JSON.stringify({new_name:name,color:color})});
510
+ await fetch('__BASE__/api/tags/'+encodeURIComponent(old),{method:'PATCH',headers:{'Content-Type':'application/json'},body:JSON.stringify({new_name:name,color:color})});
511
511
  await refresh();renderTagRows();toast('Tag updated');
512
512
  }else if(e.target.closest('.row-del')){
513
513
  if(!confirm('Delete tag "'+old+'" from every run?'))return;
514
- await fetch('/api/tags/'+encodeURIComponent(old),{method:'DELETE'});
514
+ await fetch('__BASE__/api/tags/'+encodeURIComponent(old),{method:'DELETE'});
515
515
  await refresh();renderTagRows();toast('Tag deleted');
516
516
  }
517
517
  });
518
518
  document.getElementById('new-tag-add').addEventListener('click',async()=>{
519
519
  const name=document.getElementById('new-tag-name').value.trim();if(!name)return;
520
520
  const color=document.getElementById('new-tag-color').value;
521
- await fetch('/api/tags',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({name:name,color:color})});
521
+ await fetch('__BASE__/api/tags',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({name:name,color:color})});
522
522
  document.getElementById('new-tag-name').value='';await refresh();renderTagRows();toast('Tag added');
523
523
  });
524
524
 
@@ -4,22 +4,22 @@
4
4
  <meta charset="utf-8"/>
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1"/>
6
6
  <title>reactifact trace</title>
7
- <link rel="stylesheet" href="/traces/assets/app.css"/>
7
+ <link rel="stylesheet" href="__BASE__/traces/assets/app.css"/>
8
8
  <script>(function(){try{var s=localStorage.getItem('reactifact-theme');var d=s?s==='dark':window.matchMedia('(prefers-color-scheme: dark)').matches;document.documentElement.classList.toggle('dark',d);}catch(e){document.documentElement.classList.add('dark');}})();</script>
9
9
  </head>
10
10
  <body>
11
11
  <div class="app" id="app">
12
12
  <aside class="sidebar">
13
- <a class="brand" href="/traces">
13
+ <a class="brand" href="__BASE__/traces">
14
14
  <svg width="22" height="22" viewBox="0 0 24 24" fill="none" aria-hidden="true"><circle cx="5" cy="12" r="2.6" fill="currentColor"/><circle cx="19" cy="5" r="2.6" fill="currentColor"/><circle cx="19" cy="19" r="2.6" fill="currentColor"/><path d="M7.4 11 16.6 6M7.4 13l9.2 5" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
15
15
  <span class="name">reactifact <em>traces</em></span>
16
16
  </a>
17
17
  <nav class="nav">
18
- <a class="nav-item active" href="/traces">
18
+ <a class="nav-item active" href="__BASE__/traces">
19
19
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="5" cy="12" r="2.2"/><circle cx="19" cy="5" r="2.2"/><circle cx="19" cy="19" r="2.2"/><path d="M7.2 11 16.8 6M7.2 13l9.6 5"/></svg>
20
20
  <span class="nav-label">Traces</span>
21
21
  </a>
22
- <a class="nav-item" href="/sessions">
22
+ <a class="nav-item" href="__BASE__/sessions">
23
23
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 5h16v11H8l-4 4z"/></svg>
24
24
  <span class="nav-label">Sessions</span>
25
25
  </a>
@@ -42,7 +42,7 @@
42
42
  <button class="icon-btn" id="menu-btn" aria-label="open navigation">
43
43
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M3 6h18M3 12h18M3 18h18"/></svg>
44
44
  </button>
45
- <div class="crumb"><a href="/traces">traces</a><span class="sep">/</span><b id="cid">#…</b><span id="head"></span></div>
45
+ <div class="crumb"><a href="__BASE__/traces">traces</a><span class="sep">/</span><b id="cid">#…</b><span id="head"></span></div>
46
46
  <div class="spacer"></div>
47
47
  <button class="btn sm" id="copy-link"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.1 0l3-3a5 5 0 0 0-7.1-7.1L11 4.9"/><path d="M14 11a5 5 0 0 0-7.1 0l-3 3A5 5 0 0 0 11 21.1l1.9-1.9"/></svg><span data-label>link</span></button>
48
48
  <button class="btn sm" id="copy-json"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="12" height="12" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg><span data-label>json</span></button>
@@ -104,7 +104,7 @@ function toast(msg){let t=document.getElementById('toast');if(!t){t=document.cre
104
104
 
105
105
  async function load(){
106
106
  try{
107
- const res=await fetch('/api/traces/'+RUN_ID);
107
+ const res=await fetch('__BASE__/api/traces/'+RUN_ID);
108
108
  if(res.status===404){document.getElementById('cid').textContent='#'+RUN_ID+' — not found';return;}
109
109
  if(!res.ok)throw new Error('HTTP '+res.status);
110
110
  render(await res.json());
@@ -122,7 +122,7 @@ function hex(c){return /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(
122
122
  let VOCAB=[];
123
123
  let CURRENT=[];
124
124
  async function loadVocab(){
125
- try{const r=await fetch('/api/tags');const d=await r.json();VOCAB=(d.items||[]).map(x=>x.name);}
125
+ try{const r=await fetch('__BASE__/api/tags');const d=await r.json();VOCAB=(d.items||[]).map(x=>x.name);}
126
126
  catch(_){VOCAB=[];}
127
127
  const dl=document.getElementById('taglist');
128
128
  if(dl)dl.innerHTML=VOCAB.map(n=>'<option value="'+escapeHtml(n)+'">').join('');
@@ -152,18 +152,18 @@ function renderAnnot(list){
152
152
  document.getElementById('annot-note').addEventListener('keydown',(e)=>{if(e.key==='Enter'){e.preventDefault();addTag();}});
153
153
  }
154
154
  async function reloadTrace(){
155
- const res=await fetch('/api/traces/'+RUN_ID);
155
+ const res=await fetch('__BASE__/api/traces/'+RUN_ID);
156
156
  if(res.ok)renderAnnot((await res.json()).annotations||[]);
157
157
  await loadVocab();
158
158
  }
159
159
  async function addTag(){
160
160
  const tag=document.getElementById('annot-tag').value.trim();if(!tag)return;
161
161
  const note=document.getElementById('annot-note').value;
162
- await fetch('/api/traces/'+RUN_ID+'/tags',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({tag:tag,note:note})});
162
+ await fetch('__BASE__/api/traces/'+RUN_ID+'/tags',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({tag:tag,note:note})});
163
163
  toast('Tag added');await reloadTrace();
164
164
  }
165
165
  async function removeTag(tag){
166
- await fetch('/api/traces/'+RUN_ID+'/tags/'+encodeURIComponent(tag),{method:'DELETE'});
166
+ await fetch('__BASE__/api/traces/'+RUN_ID+'/tags/'+encodeURIComponent(tag),{method:'DELETE'});
167
167
  toast('Tag removed');await reloadTrace();
168
168
  }
169
169
 
@@ -33,11 +33,41 @@ from .columns import TraceColumn
33
33
  from .store import TraceStoreProtocol
34
34
 
35
35
  if TYPE_CHECKING:
36
- from fastapi import APIRouter
36
+ from fastapi import APIRouter, Request
37
37
 
38
38
  _TEMPLATES = Path(__file__).parent / "templates"
39
39
 
40
40
 
41
+ def _mount_base(request: Request, logical_path: str) -> str:
42
+ """Path prefix this router is mounted under — ``include_router(prefix=…)``
43
+ and/or the ASGI ``root_path`` — so templates can emit prefix-aware
44
+ absolute URLs (``/*`` links and ``fetch`` paths) instead of assuming the
45
+ service sits at the origin root.
46
+
47
+ The prefix is everything before the *last* occurrence of this route's first
48
+ logical segment (``/traces`` / ``/sessions``) in the request path. The last
49
+ occurrence — not the first — because the mount prefix may itself contain
50
+ that segment (``prefix="/api/traces"`` → ``/api/traces/traces``), while the
51
+ route's own segment is always the one nearest the path parameters.
52
+ """
53
+ root_path = request.scope.get("root_path", "") or ""
54
+ marker = "/" + logical_path.strip("/").split("/")[0]
55
+ index = request.url.path.rfind(marker)
56
+ return root_path + (request.url.path[:index] if index > 0 else "")
57
+
58
+
59
+ def _render_page(
60
+ request: Request, filename: str, logical_path: str, **replacements: str
61
+ ) -> str:
62
+ """Read a UI template and fill ``__BASE__`` (mount prefix) plus any
63
+ caller placeholders (e.g. ``__RUN_ID__``)."""
64
+ text = (_TEMPLATES / filename).read_text(encoding="utf-8")
65
+ text = text.replace("__BASE__", _mount_base(request, logical_path))
66
+ for key, value in replacements.items():
67
+ text = text.replace(key, value)
68
+ return text
69
+
70
+
41
71
  class TagAssign(BaseModel):
42
72
  tag: str = Field(min_length=1, max_length=64)
43
73
  note: str = ""
@@ -93,8 +123,13 @@ def create_trace_router(
93
123
 
94
124
  require_extra("tracing.web.create_trace_router", "fastapi", "web")
95
125
 
96
- from fastapi import APIRouter, Depends, HTTPException, Query
126
+ from fastapi import APIRouter, Depends, HTTPException, Query, Request
97
127
  from fastapi.responses import HTMLResponse, JSONResponse, Response
128
+
129
+ # `from __future__ import annotations` keeps every hint a string, resolved
130
+ # against this module's globals at decoration time; fastapi is imported
131
+ # lazily here, so publish the names the page handlers annotate with.
132
+ globals()["Request"] = Request
98
133
  from fastapi.security import HTTPBasic, HTTPBasicCredentials
99
134
 
100
135
  dependencies = []
@@ -286,26 +321,27 @@ def create_trace_router(
286
321
  )
287
322
 
288
323
  @router.get("/traces", response_class=HTMLResponse)
289
- async def traces_list_page() -> str:
290
- return (_TEMPLATES / "ui.html").read_text(encoding="utf-8")
324
+ async def traces_list_page(request: Request) -> str:
325
+ return _render_page(request, "ui.html", "/traces")
291
326
 
292
327
  @router.get("/sessions", response_class=HTMLResponse)
293
- async def sessions_list_page() -> str:
294
- return (_TEMPLATES / "sessions.html").read_text(encoding="utf-8")
328
+ async def sessions_list_page(request: Request) -> str:
329
+ return _render_page(request, "sessions.html", "/sessions")
295
330
 
296
331
  @router.get("/traces/{trace_id}", response_class=HTMLResponse)
297
- async def traces_run_page(trace_id: str) -> str:
332
+ async def traces_run_page(request: Request, trace_id: str) -> str:
298
333
  from ..viz import trace_provenance_to_mermaid, trace_to_mermaid
299
334
 
300
335
  trace = await store.get(trace_id)
301
336
  mermaid = trace_to_mermaid(trace) if trace is not None else ""
302
337
  provenance = trace_provenance_to_mermaid(trace) if trace is not None else ""
303
- return (
304
- (_TEMPLATES / "ui_run.html")
305
- .read_text(encoding="utf-8")
306
- .replace("__RUN_ID__", json.dumps(trace_id))
307
- .replace("__MERMAID__", json.dumps(mermaid))
308
- .replace("__MERMAID_GRAPH__", json.dumps(provenance))
338
+ return _render_page(
339
+ request,
340
+ "ui_run.html",
341
+ "/traces/{trace_id}",
342
+ __RUN_ID__=json.dumps(trace_id),
343
+ __MERMAID__=json.dumps(mermaid),
344
+ __MERMAID_GRAPH__=json.dumps(provenance),
309
345
  )
310
346
 
311
347
  return router
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reactifact
3
- Version: 0.14.1
3
+ Version: 0.14.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,5 +1,6 @@
1
1
  import asyncio
2
2
  import json
3
+ import re
3
4
 
4
5
  from pydantic import BaseModel
5
6
  from reactifact import Agent, Consume, Context, Patch, Runtime, RuntimeResources
@@ -868,3 +869,86 @@ def test_recording_llm_prompt_hash_defaults_empty():
868
869
  llm = RecordingLLM(FakeLLM("x"), on_call=recorded.append, agent_of=lambda: "a")
869
870
  asyncio.run(llm.complete(LLMRequest(messages=[Message.user("hi")])))
870
871
  assert recorded[0].prompt_hash == ""
872
+
873
+
874
+ def test_trace_pages_are_prefix_aware(tmp_path):
875
+ from fastapi import FastAPI
876
+ from fastapi.testclient import TestClient
877
+ from reactifact.tracing.web import create_trace_router
878
+
879
+ store = TraceStore(str(tmp_path / "prefix.db"))
880
+ run(store.export(RunTrace(id="r1", session_id="s", outcome="completed")))
881
+ app = FastAPI()
882
+ app.include_router(create_trace_router(store), prefix="/ai")
883
+ client = TestClient(app)
884
+
885
+ listing = client.get("/ai/traces")
886
+ assert listing.status_code == 200
887
+ html = listing.text
888
+ assert "__BASE__" not in html
889
+ assert 'href="/ai/traces/assets/app.css"' in html
890
+ assert 'href="/ai/traces"' in html
891
+ assert 'href="/ai/sessions"' in html
892
+ assert "'/ai/api/traces" in html
893
+ assert "'/ai/api/columns" in html
894
+ assert "'/ai/traces/'" in html
895
+
896
+ sessions = client.get("/ai/sessions").text
897
+ assert "__BASE__" not in sessions
898
+ assert "'/ai/api/sessions" in sessions
899
+ assert 'href="/ai/traces"' in sessions
900
+
901
+ run_page = client.get("/ai/traces/r1").text
902
+ assert "__BASE__" not in run_page
903
+ assert "'/ai/api/traces/" in run_page
904
+ assert 'href="/ai/traces/assets/app.css"' in run_page
905
+
906
+
907
+ def test_trace_pages_without_prefix_keep_root_paths(tmp_path):
908
+ from fastapi import FastAPI
909
+ from fastapi.testclient import TestClient
910
+ from reactifact.tracing.web import create_trace_router
911
+
912
+ store = TraceStore(str(tmp_path / "root.db"))
913
+ app = FastAPI()
914
+ app.include_router(create_trace_router(store))
915
+ client = TestClient(app)
916
+ html = client.get("/traces").text
917
+ assert "__BASE__" not in html
918
+ assert 'href="/traces/assets/app.css"' in html
919
+ assert "'/api/traces" in html
920
+
921
+
922
+ def test_trace_pages_honor_asgi_root_path(tmp_path):
923
+ from fastapi import FastAPI
924
+ from fastapi.testclient import TestClient
925
+ from reactifact.tracing.web import create_trace_router
926
+
927
+ store = TraceStore(str(tmp_path / "rootpath.db"))
928
+ app = FastAPI(root_path="/ai")
929
+ app.include_router(create_trace_router(store))
930
+ client = TestClient(app)
931
+ html = client.get("/traces").text
932
+ assert "__BASE__" not in html
933
+ assert 'href="/ai/traces/assets/app.css"' in html
934
+ assert "'/ai/api/traces" in html
935
+
936
+
937
+ def test_trace_pages_stay_prefixed_when_prefix_contains_a_route_segment(tmp_path):
938
+ """A mount prefix that itself contains ``/traces`` (or ``/sessions``) must
939
+ not confuse the base: every absolute ref stays inside the prefix."""
940
+ from fastapi import FastAPI
941
+ from fastapi.testclient import TestClient
942
+ from reactifact.tracing.web import create_trace_router
943
+
944
+ store = TraceStore(str(tmp_path / "tricky.db"))
945
+ run(store.export(RunTrace(id="r1", session_id="s", outcome="completed")))
946
+ for prefix in ("/api/traces", "/traces-app", "/sessions", "/traces"):
947
+ app = FastAPI()
948
+ app.include_router(create_trace_router(store), prefix=prefix)
949
+ client = TestClient(app)
950
+ for logical in ("/traces", "/sessions", "/traces/r1"):
951
+ html = client.get(prefix + logical).text
952
+ assert "__BASE__" not in html
953
+ refs = re.findall(r'(?:href="|fetch\(\')(/[^"\'?]*)', html)
954
+ assert all(u.startswith(prefix) for u in refs), (prefix, logical, refs[:5])
File without changes
File without changes