activegraph 1.0.0rc2__tar.gz → 1.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. {activegraph-1.0.0rc2 → activegraph-1.0.1}/PKG-INFO +88 -13
  2. {activegraph-1.0.0rc2 → activegraph-1.0.1}/README.md +72 -12
  3. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/__init__.py +3 -1
  4. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/behaviors/decorators.py +73 -1
  5. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/errors.py +10 -7
  6. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/llm/__init__.py +10 -1
  7. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/llm/anthropic.py +1 -52
  8. activegraph-1.0.1/activegraph/llm/openai.py +349 -0
  9. activegraph-1.0.1/activegraph/llm/parsing.py +84 -0
  10. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/llm/prompt.py +108 -3
  11. activegraph-1.0.1/activegraph/packs/diligence/prompts/document_researcher.md +25 -0
  12. activegraph-1.0.1/activegraph/packs/diligence/prompts/memo_synthesizer.md +35 -0
  13. activegraph-1.0.1/activegraph/packs/diligence/prompts/question_generator.md +25 -0
  14. activegraph-1.0.1/activegraph/packs/diligence/prompts/risk_identifier.md +26 -0
  15. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/store/sqlite.py +22 -2
  16. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph.egg-info/PKG-INFO +88 -13
  17. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph.egg-info/SOURCES.txt +11 -1
  18. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph.egg-info/requires.txt +18 -0
  19. {activegraph-1.0.0rc2 → activegraph-1.0.1}/pyproject.toml +45 -2
  20. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_doc_links.py +17 -7
  21. activegraph-1.0.1/tests/test_doc_site_reachable.py +121 -0
  22. activegraph-1.0.1/tests/test_llm_openai.py +263 -0
  23. activegraph-1.0.1/tests/test_v1_0_1_patches.py +226 -0
  24. activegraph-1.0.1/tests/test_wheel_completeness.py +99 -0
  25. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/__main__.py +0 -0
  26. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/behaviors/__init__.py +0 -0
  27. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/behaviors/base.py +0 -0
  28. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/cli/__init__.py +0 -0
  29. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/cli/main.py +0 -0
  30. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/cli/quickstart.py +0 -0
  31. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/core/__init__.py +0 -0
  32. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/core/clock.py +0 -0
  33. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/core/event.py +0 -0
  34. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/core/graph.py +0 -0
  35. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/core/ids.py +0 -0
  36. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/core/patch.py +0 -0
  37. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/core/view.py +0 -0
  38. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/frame.py +0 -0
  39. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/llm/cache.py +0 -0
  40. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/llm/errors.py +0 -0
  41. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/llm/provider.py +0 -0
  42. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/llm/recorded.py +0 -0
  43. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/llm/types.py +0 -0
  44. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/observability/__init__.py +0 -0
  45. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/observability/logging.py +0 -0
  46. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/observability/metrics.py +0 -0
  47. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/observability/migration.py +0 -0
  48. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/observability/prometheus.py +0 -0
  49. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/observability/status.py +0 -0
  50. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/packs/__init__.py +0 -0
  51. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/packs/diligence/__init__.py +0 -0
  52. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/packs/diligence/behaviors.py +0 -0
  53. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/packs/diligence/fixtures/__init__.py +0 -0
  54. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/packs/diligence/fixtures/companies.py +0 -0
  55. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/packs/diligence/object_types.py +0 -0
  56. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/packs/diligence/settings.py +0 -0
  57. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/packs/diligence/tools.py +0 -0
  58. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/packs/loader.py +0 -0
  59. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/packs/scaffold.py +0 -0
  60. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/policy.py +0 -0
  61. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/runtime/__init__.py +0 -0
  62. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/runtime/behavior_graph.py +0 -0
  63. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/runtime/budget.py +0 -0
  64. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/runtime/config_errors.py +0 -0
  65. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/runtime/diff.py +0 -0
  66. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/runtime/errors.py +0 -0
  67. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/runtime/exec_errors.py +0 -0
  68. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/runtime/patterns.py +0 -0
  69. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/runtime/queue.py +0 -0
  70. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/runtime/registration_errors.py +0 -0
  71. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/runtime/registry.py +0 -0
  72. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/runtime/runtime.py +0 -0
  73. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/runtime/scheduler.py +0 -0
  74. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/runtime/view_builder.py +0 -0
  75. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/store/__init__.py +0 -0
  76. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/store/base.py +0 -0
  77. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/store/conformance.py +0 -0
  78. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/store/errors.py +0 -0
  79. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/store/memory.py +0 -0
  80. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/store/postgres.py +0 -0
  81. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/store/serde.py +0 -0
  82. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/store/url.py +0 -0
  83. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/tools/__init__.py +0 -0
  84. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/tools/base.py +0 -0
  85. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/tools/cache.py +0 -0
  86. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/tools/context.py +0 -0
  87. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/tools/decorators.py +0 -0
  88. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/tools/errors.py +0 -0
  89. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/tools/graph_query.py +0 -0
  90. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/tools/recorded.py +0 -0
  91. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/tools/web_fetch.py +0 -0
  92. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/trace/__init__.py +0 -0
  93. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/trace/causal.py +0 -0
  94. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph/trace/printer.py +0 -0
  95. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph.egg-info/dependency_links.txt +0 -0
  96. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph.egg-info/entry_points.txt +0 -0
  97. {activegraph-1.0.0rc2 → activegraph-1.0.1}/activegraph.egg-info/top_level.txt +0 -0
  98. {activegraph-1.0.0rc2 → activegraph-1.0.1}/setup.cfg +0 -0
  99. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_activate_after.py +0 -0
  100. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_causal_cross_tool.py +0 -0
  101. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_cli.py +0 -0
  102. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_clock.py +0 -0
  103. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_diff.py +0 -0
  104. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_diligence_pack.py +0 -0
  105. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_diligence_with_tools.py +0 -0
  106. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_errors_format.py +0 -0
  107. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_event.py +0 -0
  108. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_fork.py +0 -0
  109. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_graph.py +0 -0
  110. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_ids.py +0 -0
  111. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_llm_anthropic.py +0 -0
  112. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_llm_behavior.py +0 -0
  113. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_llm_budget.py +0 -0
  114. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_llm_causal.py +0 -0
  115. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_llm_claim_extraction.py +0 -0
  116. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_llm_determinism.py +0 -0
  117. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_llm_failure.py +0 -0
  118. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_llm_prompt.py +0 -0
  119. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_llm_provider_fixtures.py +0 -0
  120. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_llm_replay.py +0 -0
  121. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_llm_tool_loop.py +0 -0
  122. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_llm_trace.py +0 -0
  123. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_llm_trace_snapshot.py +0 -0
  124. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_llm_types.py +0 -0
  125. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_migration.py +0 -0
  126. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_observability_logging.py +0 -0
  127. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_observability_metrics.py +0 -0
  128. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_operate_example.py +0 -0
  129. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_pack_scaffold.py +0 -0
  130. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_packs.py +0 -0
  131. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_patch.py +0 -0
  132. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_pattern_matcher.py +0 -0
  133. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_pattern_parser.py +0 -0
  134. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_pattern_subscriptions.py +0 -0
  135. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_persistence.py +0 -0
  136. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_postgres_store.py +0 -0
  137. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_quickstart.py +0 -0
  138. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_quickstart_snapshot.py +0 -0
  139. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_replay.py +0 -0
  140. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_replay_trace_snapshot.py +0 -0
  141. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_requeue_unfired.py +0 -0
  142. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_resume_example.py +0 -0
  143. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_runtime.py +0 -0
  144. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_runtime_status.py +0 -0
  145. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_serde.py +0 -0
  146. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_store_conformance.py +0 -0
  147. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_store_url.py +0 -0
  148. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_tool_replay.py +0 -0
  149. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_tool_trace_snapshot.py +0 -0
  150. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_tools.py +0 -0
  151. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_trace.py +0 -0
  152. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_tutorial_snippets.py +0 -0
  153. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_version_sync.py +0 -0
  154. {activegraph-1.0.0rc2 → activegraph-1.0.1}/tests/test_view.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: activegraph
3
- Version: 1.0.0rc2
3
+ Version: 1.0.1
4
4
  Summary: An event-sourced reactive graph runtime for long-running, auditable, agentic systems.
5
5
  Author: Active Graph contributors
6
6
  License: MIT
@@ -18,7 +18,16 @@ Requires-Dist: click<9,>=8
18
18
  Requires-Dist: pydantic>=2
19
19
  Provides-Extra: llm
20
20
  Requires-Dist: anthropic>=0.40; extra == "llm"
21
+ Requires-Dist: openai>=1.0; extra == "llm"
22
+ Requires-Dist: tiktoken>=0.7; extra == "llm"
21
23
  Requires-Dist: pydantic>=2; extra == "llm"
24
+ Provides-Extra: anthropic
25
+ Requires-Dist: anthropic>=0.40; extra == "anthropic"
26
+ Requires-Dist: pydantic>=2; extra == "anthropic"
27
+ Provides-Extra: openai
28
+ Requires-Dist: openai>=1.0; extra == "openai"
29
+ Requires-Dist: tiktoken>=0.7; extra == "openai"
30
+ Requires-Dist: pydantic>=2; extra == "openai"
22
31
  Provides-Extra: sqlite
23
32
  Provides-Extra: postgres
24
33
  Requires-Dist: psycopg[binary]<4,>=3.1; extra == "postgres"
@@ -26,6 +35,8 @@ Provides-Extra: prometheus
26
35
  Requires-Dist: prometheus_client>=0.20; extra == "prometheus"
27
36
  Provides-Extra: all
28
37
  Requires-Dist: anthropic>=0.40; extra == "all"
38
+ Requires-Dist: openai>=1.0; extra == "all"
39
+ Requires-Dist: tiktoken>=0.7; extra == "all"
29
40
  Requires-Dist: pydantic>=2; extra == "all"
30
41
  Requires-Dist: psycopg[binary]<4,>=3.1; extra == "all"
31
42
  Requires-Dist: prometheus_client>=0.20; extra == "all"
@@ -34,6 +45,10 @@ Requires-Dist: pytest>=7; extra == "dev"
34
45
  Requires-Dist: pydantic>=2; extra == "dev"
35
46
  Requires-Dist: prometheus_client>=0.20; extra == "dev"
36
47
  Requires-Dist: psycopg[binary]<4,>=3.1; extra == "dev"
48
+ Provides-Extra: docs
49
+ Requires-Dist: mkdocs>=1.6; extra == "docs"
50
+ Requires-Dist: mkdocs-material>=9.5; extra == "docs"
51
+ Requires-Dist: mkdocstrings[python]>=0.24; extra == "docs"
37
52
 
38
53
  # Active Graph
39
54
 
@@ -73,12 +88,20 @@ capability.
73
88
 
74
89
  ```bash
75
90
  pip install activegraph # core runtime + SQLite store + Diligence pack
76
- pip install "activegraph[llm]" # Anthropic provider
91
+ pip install "activegraph[llm]" # Anthropic + OpenAI providers
92
+ pip install "activegraph[anthropic]" # Anthropic provider only
93
+ pip install "activegraph[openai]" # OpenAI provider only (+ tiktoken)
77
94
  pip install "activegraph[postgres]" # Postgres-backed event store
78
95
  pip install "activegraph[prometheus]" # Prometheus metrics
79
96
  pip install "activegraph[all]" # everything
80
97
  ```
81
98
 
99
+ Both LLM providers expose the same `LLMProvider` Protocol surface;
100
+ swap one for the other without touching `@llm_behavior` definitions.
101
+ The [LLM providers reference](https://docs.activegraph.ai/reference/llm-providers/)
102
+ covers the side-by-side surface and the v1.0.1 limitations
103
+ (OpenAI tool use is a v1.1 candidate).
104
+
82
105
  Python 3.11+. Two hard dependencies (`click` for the CLI, `pydantic`
83
106
  for the pack format); persistence backends and provider integrations
84
107
  are opt-in extras.
@@ -102,7 +125,54 @@ are opt-in extras.
102
125
  8 object types, 7 behaviors, 3 tools, recorded fixtures.
103
126
  - **Per-error reference pages.** Every error message ends with a
104
127
  `More:` link to a page that explains when it fires, why, and how to
105
- fix it. Catalog at [docs.activegraph.dev/reference/errors](https://docs.activegraph.dev/reference/errors/).
128
+ fix it. Catalog at [docs.activegraph.ai/reference/errors](https://docs.activegraph.ai/reference/errors/).
129
+
130
+ ## Concepts at a glance
131
+
132
+ The framework's twelve primitives, in roughly the order you meet them
133
+ when reading a trace. Each links to its concept page on the doc site;
134
+ read those when you want depth on one piece.
135
+
136
+ - **Graph** — objects and typed relations forming the world the
137
+ framework reasons about. The graph is a projection of the event log;
138
+ every mutation is an event. [→ concepts/graph](https://docs.activegraph.ai/concepts/graph/)
139
+ - **Events** — the append-only history. Every behavior fires in
140
+ response to events and produces more events; the trace is the
141
+ ordered log of all of them. [→ concepts/events](https://docs.activegraph.ai/concepts/events/)
142
+ - **Behaviors** — the unit of reactive code. Function, class, or
143
+ LLM-backed; declares what events it subscribes to and what it
144
+ produces. The determinism contract is per-behavior. [→ concepts/behaviors](https://docs.activegraph.ai/concepts/behaviors/)
145
+ - **Relations** — typed edges between objects, with their own
146
+ behaviors. The relation-behavior primitive — coordination logic on
147
+ the edge, not on either endpoint — is uncommon in other agent
148
+ frameworks. [→ concepts/relations](https://docs.activegraph.ai/concepts/relations/)
149
+ - **Patches** — proposed mutations with optimistic concurrency.
150
+ Behaviors propose patches; the runtime applies or rejects them;
151
+ rejections are events in their own right. [→ concepts/patches](https://docs.activegraph.ai/concepts/patches/)
152
+ - **Views** — scoped reads of the graph for behavior context. Type
153
+ filters, depth filters, recent-event windows. Views are how
154
+ pattern-driven behaviors see only what they need to. [→ concepts/views](https://docs.activegraph.ai/concepts/views/)
155
+ - **Frames** — bounded contexts for a run. Goal, constraints, budget,
156
+ and the registered behaviors for this frame. A run can have one
157
+ frame or many. [→ concepts/frames](https://docs.activegraph.ai/concepts/frames/)
158
+ - **Policies** — approval and gating for behavior capabilities. Which
159
+ behaviors can call which tools, which mutations require human
160
+ approval, what the runtime refuses. [→ concepts/policies](https://docs.activegraph.ai/concepts/policies/)
161
+ - **Patterns** — the Cypher subset for pattern subscriptions. Beyond
162
+ event-type + predicate, behaviors can subscribe to graph shapes
163
+ (claim-cited-by-evidence, task-blocks-task, …) with `NOT EXISTS`
164
+ and temporal predicates. [→ concepts/patterns](https://docs.activegraph.ai/concepts/patterns/)
165
+ - **Replay** — re-execute a run from its event log. Strict mode
166
+ re-fires every behavior and fails on divergence; permissive mode
167
+ reconstructs state without re-firing. The LLM replay cache is what
168
+ makes fork cheap. [→ concepts/replay](https://docs.activegraph.ai/concepts/replay/)
169
+ - **Forking** — branch any run at any event into an independent
170
+ fork; structurally diff the fork against the parent. The framework's
171
+ mechanism for hypothesis testing on agentic systems. [→ concepts/forking](https://docs.activegraph.ai/concepts/forking/)
172
+ - **Failure model** — a behavior failure is a `behavior.failed`
173
+ event, not an exception. The audit trail captures failures as
174
+ first-class history. Exceptions live at runtime entry points only.
175
+ [→ concepts/failure-model](https://docs.activegraph.ai/concepts/failure-model/)
106
176
 
107
177
  ## A small example
108
178
 
@@ -140,14 +210,14 @@ runtime.print_trace()
140
210
 
141
211
  The `unblock` relation behavior fires only for events touching one of
142
212
  its edge endpoints. The conceptual deep-dive on edges-with-logic is
143
- in [`docs/concepts/relations.md`](https://docs.activegraph.dev/concepts/relations/).
213
+ in [`docs/concepts/relations.md`](https://docs.activegraph.ai/concepts/relations/).
144
214
 
145
215
  ## Documentation
146
216
 
147
- - **[docs.activegraph.dev](https://docs.activegraph.dev/)** — full doc site:
217
+ - **[docs.activegraph.ai](https://docs.activegraph.ai/)** — full doc site:
148
218
  concepts, guides, cookbook, CLI reference, API reference, the
149
219
  per-error catalog.
150
- - **[10-minute tutorial](https://docs.activegraph.dev/quickstart/)** — install
220
+ - **[10-minute tutorial](https://docs.activegraph.ai/quickstart/)** — install
151
221
  to a working custom behavior, including fork-and-diff.
152
222
  - **[CHANGELOG.md](CHANGELOG.md)** — every release, with per-version
153
223
  migration notes.
@@ -159,7 +229,9 @@ in [`docs/concepts/relations.md`](https://docs.activegraph.dev/concepts/relation
159
229
  [`resume_and_fork.py`](examples/resume_and_fork.py),
160
230
  [`llm_claim_extraction.py`](examples/llm_claim_extraction.py),
161
231
  [`diligence_with_tools.py`](examples/diligence_with_tools.py),
162
- [`operate_a_run.py`](examples/operate_a_run.py).
232
+ [`operate_a_run.py`](examples/operate_a_run.py),
233
+ [`babyagi.py`](examples/babyagi.py) — BabyAGI's autonomous agent loop,
234
+ rebuilt as three reactive behaviors over a shared graph.
163
235
 
164
236
  ## What this is not
165
237
 
@@ -178,17 +250,20 @@ in [`docs/concepts/relations.md`](https://docs.activegraph.dev/concepts/relation
178
250
 
179
251
  ## Status
180
252
 
181
- **v1.0-rc1** (2026-05). v1.0 final ships after the first-time-user
182
- gate per [CONTRACT v1.0 #C4](CONTRACT.md#v10-c4-v10-ships-as-v10-rc1-first-time-user-gate-is-owned-externally).
183
- See [CHANGELOG.md](CHANGELOG.md) for the full v0 → v1.0 history and
253
+ **v1.0 (stable)** (2026-05). The first-time-user gate per
254
+ [CONTRACT v1.0 #C4](CONTRACT.md#v10-c4-v10-ships-as-v10-rc1-first-time-user-gate-is-owned-externally)
255
+ ran through three rcs; v1.0 final ships rc3 plus a tutorial-step-7
256
+ output fix and a README "Concepts at a glance" index. See
257
+ [CHANGELOG.md](CHANGELOG.md) for the full v0 → v1.0 history and
184
258
  per-version migration notes.
185
259
 
186
260
  Major shipped milestones:
187
261
 
188
- - **v1.0 (rc1)** — error hierarchy rewrite with per-error reference
189
- pages, doc site at [docs.activegraph.dev](https://docs.activegraph.dev/),
262
+ - **v1.0** — error hierarchy rewrite with per-error reference
263
+ pages, doc site at [docs.activegraph.ai](https://docs.activegraph.ai/),
190
264
  `activegraph quickstart` command, mypy `--strict` and docstring
191
- coverage CI gates.
265
+ coverage CI gates, wheel-completeness and deploy-verification CI
266
+ gates.
192
267
  - **v0.9** — pack format and the Diligence reference pack (8 object
193
268
  types, 7 behaviors, 3 tools, recorded fixtures).
194
269
  - **v0.8** — operator surface: structured logging, Prometheus
@@ -36,12 +36,20 @@ capability.
36
36
 
37
37
  ```bash
38
38
  pip install activegraph # core runtime + SQLite store + Diligence pack
39
- pip install "activegraph[llm]" # Anthropic provider
39
+ pip install "activegraph[llm]" # Anthropic + OpenAI providers
40
+ pip install "activegraph[anthropic]" # Anthropic provider only
41
+ pip install "activegraph[openai]" # OpenAI provider only (+ tiktoken)
40
42
  pip install "activegraph[postgres]" # Postgres-backed event store
41
43
  pip install "activegraph[prometheus]" # Prometheus metrics
42
44
  pip install "activegraph[all]" # everything
43
45
  ```
44
46
 
47
+ Both LLM providers expose the same `LLMProvider` Protocol surface;
48
+ swap one for the other without touching `@llm_behavior` definitions.
49
+ The [LLM providers reference](https://docs.activegraph.ai/reference/llm-providers/)
50
+ covers the side-by-side surface and the v1.0.1 limitations
51
+ (OpenAI tool use is a v1.1 candidate).
52
+
45
53
  Python 3.11+. Two hard dependencies (`click` for the CLI, `pydantic`
46
54
  for the pack format); persistence backends and provider integrations
47
55
  are opt-in extras.
@@ -65,7 +73,54 @@ are opt-in extras.
65
73
  8 object types, 7 behaviors, 3 tools, recorded fixtures.
66
74
  - **Per-error reference pages.** Every error message ends with a
67
75
  `More:` link to a page that explains when it fires, why, and how to
68
- fix it. Catalog at [docs.activegraph.dev/reference/errors](https://docs.activegraph.dev/reference/errors/).
76
+ fix it. Catalog at [docs.activegraph.ai/reference/errors](https://docs.activegraph.ai/reference/errors/).
77
+
78
+ ## Concepts at a glance
79
+
80
+ The framework's twelve primitives, in roughly the order you meet them
81
+ when reading a trace. Each links to its concept page on the doc site;
82
+ read those when you want depth on one piece.
83
+
84
+ - **Graph** — objects and typed relations forming the world the
85
+ framework reasons about. The graph is a projection of the event log;
86
+ every mutation is an event. [→ concepts/graph](https://docs.activegraph.ai/concepts/graph/)
87
+ - **Events** — the append-only history. Every behavior fires in
88
+ response to events and produces more events; the trace is the
89
+ ordered log of all of them. [→ concepts/events](https://docs.activegraph.ai/concepts/events/)
90
+ - **Behaviors** — the unit of reactive code. Function, class, or
91
+ LLM-backed; declares what events it subscribes to and what it
92
+ produces. The determinism contract is per-behavior. [→ concepts/behaviors](https://docs.activegraph.ai/concepts/behaviors/)
93
+ - **Relations** — typed edges between objects, with their own
94
+ behaviors. The relation-behavior primitive — coordination logic on
95
+ the edge, not on either endpoint — is uncommon in other agent
96
+ frameworks. [→ concepts/relations](https://docs.activegraph.ai/concepts/relations/)
97
+ - **Patches** — proposed mutations with optimistic concurrency.
98
+ Behaviors propose patches; the runtime applies or rejects them;
99
+ rejections are events in their own right. [→ concepts/patches](https://docs.activegraph.ai/concepts/patches/)
100
+ - **Views** — scoped reads of the graph for behavior context. Type
101
+ filters, depth filters, recent-event windows. Views are how
102
+ pattern-driven behaviors see only what they need to. [→ concepts/views](https://docs.activegraph.ai/concepts/views/)
103
+ - **Frames** — bounded contexts for a run. Goal, constraints, budget,
104
+ and the registered behaviors for this frame. A run can have one
105
+ frame or many. [→ concepts/frames](https://docs.activegraph.ai/concepts/frames/)
106
+ - **Policies** — approval and gating for behavior capabilities. Which
107
+ behaviors can call which tools, which mutations require human
108
+ approval, what the runtime refuses. [→ concepts/policies](https://docs.activegraph.ai/concepts/policies/)
109
+ - **Patterns** — the Cypher subset for pattern subscriptions. Beyond
110
+ event-type + predicate, behaviors can subscribe to graph shapes
111
+ (claim-cited-by-evidence, task-blocks-task, …) with `NOT EXISTS`
112
+ and temporal predicates. [→ concepts/patterns](https://docs.activegraph.ai/concepts/patterns/)
113
+ - **Replay** — re-execute a run from its event log. Strict mode
114
+ re-fires every behavior and fails on divergence; permissive mode
115
+ reconstructs state without re-firing. The LLM replay cache is what
116
+ makes fork cheap. [→ concepts/replay](https://docs.activegraph.ai/concepts/replay/)
117
+ - **Forking** — branch any run at any event into an independent
118
+ fork; structurally diff the fork against the parent. The framework's
119
+ mechanism for hypothesis testing on agentic systems. [→ concepts/forking](https://docs.activegraph.ai/concepts/forking/)
120
+ - **Failure model** — a behavior failure is a `behavior.failed`
121
+ event, not an exception. The audit trail captures failures as
122
+ first-class history. Exceptions live at runtime entry points only.
123
+ [→ concepts/failure-model](https://docs.activegraph.ai/concepts/failure-model/)
69
124
 
70
125
  ## A small example
71
126
 
@@ -103,14 +158,14 @@ runtime.print_trace()
103
158
 
104
159
  The `unblock` relation behavior fires only for events touching one of
105
160
  its edge endpoints. The conceptual deep-dive on edges-with-logic is
106
- in [`docs/concepts/relations.md`](https://docs.activegraph.dev/concepts/relations/).
161
+ in [`docs/concepts/relations.md`](https://docs.activegraph.ai/concepts/relations/).
107
162
 
108
163
  ## Documentation
109
164
 
110
- - **[docs.activegraph.dev](https://docs.activegraph.dev/)** — full doc site:
165
+ - **[docs.activegraph.ai](https://docs.activegraph.ai/)** — full doc site:
111
166
  concepts, guides, cookbook, CLI reference, API reference, the
112
167
  per-error catalog.
113
- - **[10-minute tutorial](https://docs.activegraph.dev/quickstart/)** — install
168
+ - **[10-minute tutorial](https://docs.activegraph.ai/quickstart/)** — install
114
169
  to a working custom behavior, including fork-and-diff.
115
170
  - **[CHANGELOG.md](CHANGELOG.md)** — every release, with per-version
116
171
  migration notes.
@@ -122,7 +177,9 @@ in [`docs/concepts/relations.md`](https://docs.activegraph.dev/concepts/relation
122
177
  [`resume_and_fork.py`](examples/resume_and_fork.py),
123
178
  [`llm_claim_extraction.py`](examples/llm_claim_extraction.py),
124
179
  [`diligence_with_tools.py`](examples/diligence_with_tools.py),
125
- [`operate_a_run.py`](examples/operate_a_run.py).
180
+ [`operate_a_run.py`](examples/operate_a_run.py),
181
+ [`babyagi.py`](examples/babyagi.py) — BabyAGI's autonomous agent loop,
182
+ rebuilt as three reactive behaviors over a shared graph.
126
183
 
127
184
  ## What this is not
128
185
 
@@ -141,17 +198,20 @@ in [`docs/concepts/relations.md`](https://docs.activegraph.dev/concepts/relation
141
198
 
142
199
  ## Status
143
200
 
144
- **v1.0-rc1** (2026-05). v1.0 final ships after the first-time-user
145
- gate per [CONTRACT v1.0 #C4](CONTRACT.md#v10-c4-v10-ships-as-v10-rc1-first-time-user-gate-is-owned-externally).
146
- See [CHANGELOG.md](CHANGELOG.md) for the full v0 → v1.0 history and
201
+ **v1.0 (stable)** (2026-05). The first-time-user gate per
202
+ [CONTRACT v1.0 #C4](CONTRACT.md#v10-c4-v10-ships-as-v10-rc1-first-time-user-gate-is-owned-externally)
203
+ ran through three rcs; v1.0 final ships rc3 plus a tutorial-step-7
204
+ output fix and a README "Concepts at a glance" index. See
205
+ [CHANGELOG.md](CHANGELOG.md) for the full v0 → v1.0 history and
147
206
  per-version migration notes.
148
207
 
149
208
  Major shipped milestones:
150
209
 
151
- - **v1.0 (rc1)** — error hierarchy rewrite with per-error reference
152
- pages, doc site at [docs.activegraph.dev](https://docs.activegraph.dev/),
210
+ - **v1.0** — error hierarchy rewrite with per-error reference
211
+ pages, doc site at [docs.activegraph.ai](https://docs.activegraph.ai/),
153
212
  `activegraph quickstart` command, mypy `--strict` and docstring
154
- coverage CI gates.
213
+ coverage CI gates, wheel-completeness and deploy-verification CI
214
+ gates.
155
215
  - **v0.9** — pack format and the Diligence reference pack (8 object
156
216
  types, 7 behaviors, 3 tools, recorded fixtures).
157
217
  - **v0.8** — operator surface: structured logging, Prometheus
@@ -9,6 +9,7 @@ from activegraph.behaviors.decorators import (
9
9
  clear_registry,
10
10
  get_registry,
11
11
  llm_behavior,
12
+ register,
12
13
  relation_behavior,
13
14
  )
14
15
  from activegraph.core.clock import Clock, FrozenClock, TickingClock
@@ -215,8 +216,9 @@ __all__ = [
215
216
  "migrate",
216
217
  "open_store",
217
218
  "parse_store_url",
219
+ "register",
218
220
  "relation_behavior",
219
221
  "tool",
220
222
  ]
221
223
 
222
- __version__ = "1.0.0rc2"
224
+ __version__ = "1.0.1"
@@ -27,14 +27,66 @@ from activegraph.behaviors.base import (
27
27
  _REGISTRY: list[Union[Behavior, RelationBehavior]] = []
28
28
 
29
29
 
30
- def clear_registry() -> None:
30
+ def clear_registry() -> list[Union[Behavior, RelationBehavior]]:
31
+ """Empty the global behavior registry and return what was cleared.
32
+
33
+ Tests that need isolation between cases call this in a fixture; the
34
+ return value is the list of removed behaviors in registration
35
+ order, so multi-run scripts can capture them once and re-register
36
+ via :func:`register` on each subsequent run without re-importing
37
+ the modules whose ``@behavior`` decorators populated the registry
38
+ in the first place. See the *Multi-run scripts* cookbook recipe.
39
+
40
+ v1.0.1: the return value is new. v1.0 returned ``None``; callers
41
+ that ignored the return still work unchanged.
42
+ """
43
+ cleared: list[Union[Behavior, RelationBehavior]] = list(_REGISTRY)
31
44
  _REGISTRY.clear()
45
+ return cleared
32
46
 
33
47
 
34
48
  def get_registry() -> list[Union[Behavior, RelationBehavior]]:
49
+ """Snapshot of the global behavior registry (a shallow copy)."""
35
50
  return list(_REGISTRY)
36
51
 
37
52
 
53
+ def register(behavior_obj: Union[Behavior, RelationBehavior]) -> None:
54
+ """Append an already-constructed behavior to the global registry.
55
+
56
+ The decorators (:func:`behavior`, :func:`relation_behavior`,
57
+ :func:`llm_behavior`) register on definition; this function exists
58
+ for the case where definition and registration are decoupled —
59
+ most commonly, multi-run scripts that call :func:`clear_registry`
60
+ between runs and need to re-populate the registry without
61
+ re-importing the decorator-bearing modules:
62
+
63
+ .. code-block:: python
64
+
65
+ from activegraph import clear_registry, register
66
+
67
+ cleared = clear_registry() # capture before the first run
68
+ rt1 = Runtime(graph1); rt1.run_goal("first")
69
+
70
+ for b in cleared: # restore for the next run
71
+ register(b)
72
+ rt2 = Runtime(graph2); rt2.run_goal("second")
73
+
74
+ See the *Multi-run scripts* cookbook recipe.
75
+
76
+ v1.0.1: new. v1.0 required reaching into the private
77
+ ``_REGISTRY`` list — the user-test gate surfaced that as a rough
78
+ edge.
79
+ """
80
+ if not isinstance(behavior_obj, (Behavior, RelationBehavior)):
81
+ raise TypeError(
82
+ f"register() expected a Behavior, RelationBehavior, or "
83
+ f"LLMBehavior instance; got {type(behavior_obj).__name__}. "
84
+ f"Use the @behavior / @relation_behavior / @llm_behavior "
85
+ f"decorators to construct one."
86
+ )
87
+ _REGISTRY.append(behavior_obj)
88
+
89
+
38
90
  def behavior(
39
91
  name: Optional[str] = None,
40
92
  on: Optional[list[str]] = None,
@@ -124,6 +176,26 @@ def llm_behavior(
124
176
 
125
177
  Keyword-only on purpose — `@llm_behavior` carries enough
126
178
  parameters that positional binding would be a footgun.
179
+
180
+ `prompt_template=` is the only escape hatch from the
181
+ runtime-assembled prompt. It is a `str.format`-style template that
182
+ receives four placeholders:
183
+
184
+ - ``{system}`` — the system block: behavior name, frame goal and
185
+ constraints, role description, and (when `output_schema=` is set)
186
+ the schema with an example instance.
187
+ - ``{view}`` — the scoped graph view: objects, relations, and
188
+ recent events, rendered as Markdown (format locked per
189
+ CONTRACT v0.6 #13).
190
+ - ``{event}`` — the triggering event as id, type, actor, and
191
+ pretty-printed JSON payload with volatile keys stripped.
192
+ - ``{instruction}`` — the one-sentence task derived from `creates=`
193
+ and `output_schema=`.
194
+
195
+ The four placeholders carry the same runtime-assembled content
196
+ whether or not a template is set; the template only re-arranges
197
+ them. Omitting `prompt_template=` (the default) uses the
198
+ runtime's canonical layout.
127
199
  """
128
200
 
129
201
  from activegraph.runtime.patterns import parse as _parse_pattern
@@ -16,7 +16,7 @@ locked format:
16
16
  <concrete action>
17
17
 
18
18
  More:
19
- https://docs.activegraph.dev/errors/<slug>
19
+ https://docs.activegraph.ai/errors/<slug>
20
20
 
21
21
  The seven category bases are stable. Concrete leaves are migrated under
22
22
  their categories one PR at a time (CONTRACT v1.0 #C1 — the rewrite ships
@@ -25,8 +25,8 @@ as the reference category; subsequent PRs migrate the other categories
25
25
  without changing the bases.
26
26
 
27
27
  `_doc_slug` on each class is the URL slug for the error's doc page. The
28
- base URL is the github.io fallback (CONTRACT v1.0 #C6); once DNS for
29
- `docs.activegraph.dev` is live, the constant is swapped in one place.
28
+ base URL is the primary `docs.activegraph.ai` domain (CONTRACT v1.0 #C6,
29
+ v1.0-rc3 amendment); the constant is the single swap point.
30
30
  """
31
31
 
32
32
  from __future__ import annotations
@@ -34,10 +34,13 @@ from __future__ import annotations
34
34
  from typing import Any, ClassVar
35
35
 
36
36
 
37
- # CONTRACT v1.0 #C6: until docs.activegraph.dev DNS is live, error URLs
38
- # point at the github.io fallback. The cutover is a one-line edit here
39
- # plus a README update; the URLs in every error message follow.
40
- DOCS_BASE_URL = "https://yoheinakajima.github.io/activegraph"
37
+ # CONTRACT v1.0 #C6 (v1.0-rc3 amendment): primary doc-site domain is
38
+ # docs.activegraph.ai. Until the user-owned Pages enablement and DNS
39
+ # land, the URL renders the same 404 the rc2 user-test surfaced; the
40
+ # v1.1 #9 deploy-verification gate fails until that lands, which is
41
+ # the correct signal. One-line swap point: update this constant and
42
+ # every error message URL and `DOCS_BASE_URL`-derived f-string follows.
43
+ DOCS_BASE_URL = "https://docs.activegraph.ai"
41
44
 
42
45
 
43
46
  def _indent_continuation(text: str, indent: str = " ") -> str:
@@ -1,4 +1,4 @@
1
- """LLM behaviors subpackage. CONTRACT v0.6.
1
+ """LLM behaviors subpackage. CONTRACT v0.6; OpenAIProvider added v1.0.1 #5.
2
2
 
3
3
  Public surface:
4
4
 
@@ -6,6 +6,8 @@ Public surface:
6
6
  LLMMessage — single role-tagged message
7
7
  LLMResponse — what `complete()` returns
8
8
  AnthropicProvider — reference implementation
9
+ OpenAIProvider — second concrete provider, surface parity
10
+ with AnthropicProvider (v1.0.1 #5)
9
11
  RecordedLLMProvider — fixture-backed provider for tests
10
12
  RecordingLLMProvider — wraps another provider, persists responses
11
13
  as fixtures (for first-time test seed)
@@ -17,11 +19,16 @@ Public surface:
17
19
  LLMBehaviorError — structured failure carrier from @llm_behavior
18
20
  wrappers; runtime turns it into
19
21
  `behavior.failed` with a `reason`
22
+ parse_structured_response — JSON-extraction-then-Pydantic-validate
23
+ helper shared by every provider that uses
24
+ the framework's instruction-based path
20
25
  """
21
26
 
22
27
  from activegraph.llm.anthropic import AnthropicProvider
23
28
  from activegraph.llm.cache import LLMCache
24
29
  from activegraph.llm.errors import LLMBehaviorError, MissingProviderError
30
+ from activegraph.llm.openai import OpenAIProvider
31
+ from activegraph.llm.parsing import parse_structured_response
25
32
  from activegraph.llm.prompt import (
26
33
  AssembledPrompt,
27
34
  assemble_prompt,
@@ -42,10 +49,12 @@ __all__ = [
42
49
  "LLMProvider",
43
50
  "LLMResponse",
44
51
  "MissingProviderError",
52
+ "OpenAIProvider",
45
53
  "RecordedLLMProvider",
46
54
  "RecordingLLMProvider",
47
55
  "ToolCall",
48
56
  "assemble_prompt",
57
+ "parse_structured_response",
49
58
  "schema_to_json",
50
59
  "serialize_view",
51
60
  ]
@@ -23,12 +23,12 @@ keeps the surface narrow on purpose.
23
23
  from __future__ import annotations
24
24
 
25
25
  import json
26
- import re
27
26
  import time
28
27
  from decimal import Decimal
29
28
  from typing import Any, Mapping, Optional
30
29
 
31
30
  from activegraph.llm.errors import LLMBehaviorError
31
+ from activegraph.llm.parsing import parse_structured_response as _parse_structured
32
32
  from activegraph.llm.provider import LLMProvider
33
33
  from activegraph.llm.types import LLMMessage, LLMResponse, ToolCall
34
34
 
@@ -265,57 +265,6 @@ def _message_to_anthropic(m: LLMMessage) -> dict[str, Any]:
265
265
  return {"role": m.role, "content": m.content}
266
266
 
267
267
 
268
- _FENCED_JSON_RE = re.compile(r"```(?:json)?\s*(\{.*?\}|\[.*?\])\s*```", re.DOTALL)
269
- _BRACE_RE = re.compile(r"(\{.*\}|\[.*\])", re.DOTALL)
270
-
271
-
272
- def _parse_structured(text: str, schema: type) -> Any:
273
- """Parse JSON out of an LLM response and validate against `schema`.
274
-
275
- Strategy: try direct JSON first; on failure, extract a fenced
276
- ```json``` block; on failure, grab the first {...}/[...] span.
277
- Two distinct failure modes flow back as `LLMBehaviorError`:
278
-
279
- reason=llm.parse_error — no JSON found / json.loads failed
280
- reason=llm.schema_violation — JSON found but Pydantic rejected it
281
- """
282
-
283
- candidate = text.strip()
284
- obj: Any = None
285
- parse_err: Optional[Exception] = None
286
- try:
287
- obj = json.loads(candidate)
288
- except Exception as e:
289
- parse_err = e
290
- if obj is None:
291
- m = _FENCED_JSON_RE.search(text) or _BRACE_RE.search(text)
292
- if m:
293
- try:
294
- obj = json.loads(m.group(1))
295
- parse_err = None
296
- except Exception as e:
297
- parse_err = e
298
- if obj is None:
299
- raise LLMBehaviorError(
300
- "llm.parse_error",
301
- f"no JSON found in response: {parse_err}",
302
- payload_extras={"raw_text": text, "underlying": str(parse_err)},
303
- )
304
-
305
- try:
306
- return schema.model_validate(obj)
307
- except Exception as e:
308
- raise LLMBehaviorError(
309
- "llm.schema_violation",
310
- f"response did not match schema {schema.__name__}: {e}",
311
- payload_extras={
312
- "raw_text": text,
313
- "schema": schema.__name__,
314
- "validation_errors": str(e),
315
- },
316
- ) from e
317
-
318
-
319
268
  def _classify_provider_exception(e: Exception) -> str:
320
269
  name = type(e).__name__.lower()
321
270
  if "ratelimit" in name or "429" in str(e):