activegraph 1.0.0rc3__tar.gz → 1.0.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 (155) hide show
  1. {activegraph-1.0.0rc3 → activegraph-1.0.2}/PKG-INFO +79 -8
  2. {activegraph-1.0.0rc3 → activegraph-1.0.2}/README.md +67 -7
  3. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/__init__.py +3 -1
  4. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/behaviors/base.py +13 -2
  5. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/behaviors/decorators.py +87 -2
  6. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/llm/__init__.py +10 -1
  7. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/llm/anthropic.py +9 -52
  8. activegraph-1.0.2/activegraph/llm/openai.py +369 -0
  9. activegraph-1.0.2/activegraph/llm/parsing.py +84 -0
  10. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/llm/prompt.py +108 -3
  11. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/llm/provider.py +34 -3
  12. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/llm/recorded.py +26 -0
  13. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/runtime/runtime.py +120 -0
  14. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/store/sqlite.py +22 -2
  15. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph.egg-info/PKG-INFO +79 -8
  16. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph.egg-info/SOURCES.txt +5 -0
  17. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph.egg-info/requires.txt +13 -0
  18. {activegraph-1.0.0rc3 → activegraph-1.0.2}/pyproject.toml +11 -2
  19. activegraph-1.0.2/tests/test_llm_default_model.py +328 -0
  20. activegraph-1.0.2/tests/test_llm_openai.py +263 -0
  21. activegraph-1.0.2/tests/test_v1_0_1_patches.py +226 -0
  22. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/__main__.py +0 -0
  23. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/behaviors/__init__.py +0 -0
  24. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/cli/__init__.py +0 -0
  25. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/cli/main.py +0 -0
  26. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/cli/quickstart.py +0 -0
  27. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/core/__init__.py +0 -0
  28. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/core/clock.py +0 -0
  29. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/core/event.py +0 -0
  30. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/core/graph.py +0 -0
  31. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/core/ids.py +0 -0
  32. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/core/patch.py +0 -0
  33. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/core/view.py +0 -0
  34. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/errors.py +0 -0
  35. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/frame.py +0 -0
  36. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/llm/cache.py +0 -0
  37. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/llm/errors.py +0 -0
  38. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/llm/types.py +0 -0
  39. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/observability/__init__.py +0 -0
  40. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/observability/logging.py +0 -0
  41. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/observability/metrics.py +0 -0
  42. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/observability/migration.py +0 -0
  43. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/observability/prometheus.py +0 -0
  44. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/observability/status.py +0 -0
  45. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/packs/__init__.py +0 -0
  46. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/packs/diligence/__init__.py +0 -0
  47. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/packs/diligence/behaviors.py +0 -0
  48. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/packs/diligence/fixtures/__init__.py +0 -0
  49. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/packs/diligence/fixtures/companies.py +0 -0
  50. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/packs/diligence/object_types.py +0 -0
  51. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/packs/diligence/prompts/document_researcher.md +0 -0
  52. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/packs/diligence/prompts/memo_synthesizer.md +0 -0
  53. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/packs/diligence/prompts/question_generator.md +0 -0
  54. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/packs/diligence/prompts/risk_identifier.md +0 -0
  55. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/packs/diligence/settings.py +0 -0
  56. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/packs/diligence/tools.py +0 -0
  57. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/packs/loader.py +0 -0
  58. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/packs/scaffold.py +0 -0
  59. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/policy.py +0 -0
  60. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/runtime/__init__.py +0 -0
  61. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/runtime/behavior_graph.py +0 -0
  62. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/runtime/budget.py +0 -0
  63. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/runtime/config_errors.py +0 -0
  64. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/runtime/diff.py +0 -0
  65. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/runtime/errors.py +0 -0
  66. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/runtime/exec_errors.py +0 -0
  67. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/runtime/patterns.py +0 -0
  68. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/runtime/queue.py +0 -0
  69. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/runtime/registration_errors.py +0 -0
  70. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/runtime/registry.py +0 -0
  71. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/runtime/scheduler.py +0 -0
  72. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/runtime/view_builder.py +0 -0
  73. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/store/__init__.py +0 -0
  74. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/store/base.py +0 -0
  75. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/store/conformance.py +0 -0
  76. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/store/errors.py +0 -0
  77. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/store/memory.py +0 -0
  78. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/store/postgres.py +0 -0
  79. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/store/serde.py +0 -0
  80. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/store/url.py +0 -0
  81. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/tools/__init__.py +0 -0
  82. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/tools/base.py +0 -0
  83. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/tools/cache.py +0 -0
  84. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/tools/context.py +0 -0
  85. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/tools/decorators.py +0 -0
  86. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/tools/errors.py +0 -0
  87. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/tools/graph_query.py +0 -0
  88. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/tools/recorded.py +0 -0
  89. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/tools/web_fetch.py +0 -0
  90. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/trace/__init__.py +0 -0
  91. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/trace/causal.py +0 -0
  92. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/trace/printer.py +0 -0
  93. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph.egg-info/dependency_links.txt +0 -0
  94. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph.egg-info/entry_points.txt +0 -0
  95. {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph.egg-info/top_level.txt +0 -0
  96. {activegraph-1.0.0rc3 → activegraph-1.0.2}/setup.cfg +0 -0
  97. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_activate_after.py +0 -0
  98. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_causal_cross_tool.py +0 -0
  99. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_cli.py +0 -0
  100. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_clock.py +0 -0
  101. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_diff.py +0 -0
  102. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_diligence_pack.py +0 -0
  103. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_diligence_with_tools.py +0 -0
  104. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_doc_links.py +0 -0
  105. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_doc_site_reachable.py +0 -0
  106. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_errors_format.py +0 -0
  107. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_event.py +0 -0
  108. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_fork.py +0 -0
  109. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_graph.py +0 -0
  110. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_ids.py +0 -0
  111. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_llm_anthropic.py +0 -0
  112. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_llm_behavior.py +0 -0
  113. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_llm_budget.py +0 -0
  114. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_llm_causal.py +0 -0
  115. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_llm_claim_extraction.py +0 -0
  116. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_llm_determinism.py +0 -0
  117. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_llm_failure.py +0 -0
  118. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_llm_prompt.py +0 -0
  119. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_llm_provider_fixtures.py +0 -0
  120. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_llm_replay.py +0 -0
  121. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_llm_tool_loop.py +0 -0
  122. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_llm_trace.py +0 -0
  123. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_llm_trace_snapshot.py +0 -0
  124. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_llm_types.py +0 -0
  125. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_migration.py +0 -0
  126. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_observability_logging.py +0 -0
  127. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_observability_metrics.py +0 -0
  128. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_operate_example.py +0 -0
  129. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_pack_scaffold.py +0 -0
  130. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_packs.py +0 -0
  131. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_patch.py +0 -0
  132. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_pattern_matcher.py +0 -0
  133. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_pattern_parser.py +0 -0
  134. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_pattern_subscriptions.py +0 -0
  135. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_persistence.py +0 -0
  136. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_postgres_store.py +0 -0
  137. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_quickstart.py +0 -0
  138. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_quickstart_snapshot.py +0 -0
  139. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_replay.py +0 -0
  140. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_replay_trace_snapshot.py +0 -0
  141. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_requeue_unfired.py +0 -0
  142. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_resume_example.py +0 -0
  143. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_runtime.py +0 -0
  144. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_runtime_status.py +0 -0
  145. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_serde.py +0 -0
  146. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_store_conformance.py +0 -0
  147. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_store_url.py +0 -0
  148. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_tool_replay.py +0 -0
  149. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_tool_trace_snapshot.py +0 -0
  150. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_tools.py +0 -0
  151. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_trace.py +0 -0
  152. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_tutorial_snippets.py +0 -0
  153. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_version_sync.py +0 -0
  154. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_view.py +0 -0
  155. {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_wheel_completeness.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: activegraph
3
- Version: 1.0.0rc3
3
+ Version: 1.0.2
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"
@@ -77,12 +88,20 @@ capability.
77
88
 
78
89
  ```bash
79
90
  pip install activegraph # core runtime + SQLite store + Diligence pack
80
- 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)
81
94
  pip install "activegraph[postgres]" # Postgres-backed event store
82
95
  pip install "activegraph[prometheus]" # Prometheus metrics
83
96
  pip install "activegraph[all]" # everything
84
97
  ```
85
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
+
86
105
  Python 3.11+. Two hard dependencies (`click` for the CLI, `pydantic`
87
106
  for the pack format); persistence backends and provider integrations
88
107
  are opt-in extras.
@@ -108,6 +127,53 @@ are opt-in extras.
108
127
  `More:` link to a page that explains when it fires, why, and how to
109
128
  fix it. Catalog at [docs.activegraph.ai/reference/errors](https://docs.activegraph.ai/reference/errors/).
110
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/)
176
+
111
177
  ## A small example
112
178
 
113
179
  The relation-behavior primitive — coordination logic on the edge,
@@ -163,7 +229,9 @@ in [`docs/concepts/relations.md`](https://docs.activegraph.ai/concepts/relations
163
229
  [`resume_and_fork.py`](examples/resume_and_fork.py),
164
230
  [`llm_claim_extraction.py`](examples/llm_claim_extraction.py),
165
231
  [`diligence_with_tools.py`](examples/diligence_with_tools.py),
166
- [`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.
167
235
 
168
236
  ## What this is not
169
237
 
@@ -182,17 +250,20 @@ in [`docs/concepts/relations.md`](https://docs.activegraph.ai/concepts/relations
182
250
 
183
251
  ## Status
184
252
 
185
- **v1.0-rc1** (2026-05). v1.0 final ships after the first-time-user
186
- gate per [CONTRACT v1.0 #C4](CONTRACT.md#v10-c4-v10-ships-as-v10-rc1-first-time-user-gate-is-owned-externally).
187
- 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
188
258
  per-version migration notes.
189
259
 
190
260
  Major shipped milestones:
191
261
 
192
- - **v1.0 (rc1)** — error hierarchy rewrite with per-error reference
262
+ - **v1.0** — error hierarchy rewrite with per-error reference
193
263
  pages, doc site at [docs.activegraph.ai](https://docs.activegraph.ai/),
194
264
  `activegraph quickstart` command, mypy `--strict` and docstring
195
- coverage CI gates.
265
+ coverage CI gates, wheel-completeness and deploy-verification CI
266
+ gates.
196
267
  - **v0.9** — pack format and the Diligence reference pack (8 object
197
268
  types, 7 behaviors, 3 tools, recorded fixtures).
198
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.
@@ -67,6 +75,53 @@ are opt-in extras.
67
75
  `More:` link to a page that explains when it fires, why, and how to
68
76
  fix it. Catalog at [docs.activegraph.ai/reference/errors](https://docs.activegraph.ai/reference/errors/).
69
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/)
124
+
70
125
  ## A small example
71
126
 
72
127
  The relation-behavior primitive — coordination logic on the edge,
@@ -122,7 +177,9 @@ in [`docs/concepts/relations.md`](https://docs.activegraph.ai/concepts/relations
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.ai/concepts/relations
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
210
+ - **v1.0** — error hierarchy rewrite with per-error reference
152
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.0rc3"
224
+ __version__ = "1.0.2"
@@ -93,7 +93,11 @@ class LLMBehavior(Behavior):
93
93
 
94
94
  handler: Optional[Callable[..., None]] = None
95
95
  description: str = ""
96
- model: str = "claude-sonnet-4-5"
96
+ # v1.0.2 #1: model is optional. None = "use the configured provider's
97
+ # default_model"; the runtime resolves and stamps the concrete string
98
+ # at registration time. Existing call sites that pass an explicit
99
+ # string keep working unchanged.
100
+ model: Optional[str] = None
97
101
  output_schema: Optional[type] = None
98
102
  deterministic: bool = False
99
103
  max_tokens: int = 4096
@@ -134,10 +138,17 @@ class LLMBehavior(Behavior):
134
138
  if ar_expr:
135
139
  around_id = _resolve_event_path(ar_expr, event)
136
140
  depth = self.view_spec.get("depth")
141
+ # v1.0.2 #1: if model is still None, the runtime hasn't stamped
142
+ # a provider default yet — `build_prompt` is for inspection
143
+ # (CONTRACT v0.6 #20) and must work without a Runtime, so fall
144
+ # back to the v1.0.1 hardcoded default for inspection-time hash
145
+ # stability. Real provider calls always go through Runtime,
146
+ # which resolves the configured provider's default_model first.
147
+ inspection_model = self.model or "claude-sonnet-4-5"
137
148
  return assemble_prompt(
138
149
  behavior_name=self.name,
139
150
  description=self.description,
140
- model=self.model,
151
+ model=inspection_model,
141
152
  output_schema=self.output_schema,
142
153
  creates=self.creates,
143
154
  view=view,
@@ -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,
@@ -95,7 +147,7 @@ def llm_behavior(
95
147
  on: Optional[list[str]] = None,
96
148
  where: Optional[dict[str, Any]] = None,
97
149
  description: str = "",
98
- model: str = "claude-sonnet-4-5",
150
+ model: Optional[str] = None,
99
151
  output_schema: Optional[type] = None,
100
152
  view: Optional[dict[str, Any]] = None,
101
153
  creates: Optional[list[str]] = None,
@@ -124,6 +176,39 @@ 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
+ ``model=`` is optional (v1.0.2 #1). Omitted, the runtime resolves
181
+ it to the configured provider's ``default_model`` at registration
182
+ time — ``"claude-sonnet-4-5"`` for ``AnthropicProvider``,
183
+ ``"gpt-4o-mini"`` for ``OpenAIProvider``. Passing an explicit
184
+ model string still works byte-identically; the runtime additionally
185
+ validates the name against the configured provider's
186
+ ``recognizes_model()`` and raises
187
+ :class:`InvalidRuntimeConfiguration` at registration time if the
188
+ name belongs to a different shipped provider's family (e.g.
189
+ ``model="gpt-4o-mini"`` on a runtime configured with
190
+ ``AnthropicProvider``). Names no shipped provider recognizes
191
+ (custom or fine-tuned models) pass through silently.
192
+
193
+ `prompt_template=` is the only escape hatch from the
194
+ runtime-assembled prompt. It is a `str.format`-style template that
195
+ receives four placeholders:
196
+
197
+ - ``{system}`` — the system block: behavior name, frame goal and
198
+ constraints, role description, and (when `output_schema=` is set)
199
+ the schema with an example instance.
200
+ - ``{view}`` — the scoped graph view: objects, relations, and
201
+ recent events, rendered as Markdown (format locked per
202
+ CONTRACT v0.6 #13).
203
+ - ``{event}`` — the triggering event as id, type, actor, and
204
+ pretty-printed JSON payload with volatile keys stripped.
205
+ - ``{instruction}`` — the one-sentence task derived from `creates=`
206
+ and `output_schema=`.
207
+
208
+ The four placeholders carry the same runtime-assembled content
209
+ whether or not a template is set; the template only re-arranges
210
+ them. Omitting `prompt_template=` (the default) uses the
211
+ runtime's canonical layout.
127
212
  """
128
213
 
129
214
  from activegraph.runtime.patterns import parse as _parse_pattern
@@ -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
 
@@ -62,6 +62,10 @@ def _pricing_for(model: str, pricing: Mapping[str, Mapping[str, str]]) -> tuple[
62
62
 
63
63
 
64
64
  class AnthropicProvider(LLMProvider):
65
+ # v1.0.2 #1: provider-aware default model. @llm_behavior(model=None)
66
+ # resolves to this string at registration time.
67
+ default_model: str = "claude-sonnet-4-5"
68
+
65
69
  def __init__(
66
70
  self,
67
71
  *,
@@ -205,6 +209,10 @@ class AnthropicProvider(LLMProvider):
205
209
  result = client.messages.count_tokens(**kwargs)
206
210
  return int(getattr(result, "input_tokens", 0) or 0)
207
211
 
212
+ def recognizes_model(self, name: str) -> bool:
213
+ """True for the ``claude-*`` model family. v1.0.2 #1."""
214
+ return name.startswith("claude-")
215
+
208
216
 
209
217
  # ---- helpers ---------------------------------------------------------------
210
218
 
@@ -265,57 +273,6 @@ def _message_to_anthropic(m: LLMMessage) -> dict[str, Any]:
265
273
  return {"role": m.role, "content": m.content}
266
274
 
267
275
 
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
276
  def _classify_provider_exception(e: Exception) -> str:
320
277
  name = type(e).__name__.lower()
321
278
  if "ratelimit" in name or "429" in str(e):