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.
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/PKG-INFO +79 -8
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/README.md +67 -7
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/__init__.py +3 -1
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/behaviors/base.py +13 -2
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/behaviors/decorators.py +87 -2
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/llm/__init__.py +10 -1
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/llm/anthropic.py +9 -52
- activegraph-1.0.2/activegraph/llm/openai.py +369 -0
- activegraph-1.0.2/activegraph/llm/parsing.py +84 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/llm/prompt.py +108 -3
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/llm/provider.py +34 -3
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/llm/recorded.py +26 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/runtime/runtime.py +120 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/store/sqlite.py +22 -2
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph.egg-info/PKG-INFO +79 -8
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph.egg-info/SOURCES.txt +5 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph.egg-info/requires.txt +13 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/pyproject.toml +11 -2
- activegraph-1.0.2/tests/test_llm_default_model.py +328 -0
- activegraph-1.0.2/tests/test_llm_openai.py +263 -0
- activegraph-1.0.2/tests/test_v1_0_1_patches.py +226 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/__main__.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/behaviors/__init__.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/cli/__init__.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/cli/main.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/cli/quickstart.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/core/__init__.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/core/clock.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/core/event.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/core/graph.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/core/ids.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/core/patch.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/core/view.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/errors.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/frame.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/llm/cache.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/llm/errors.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/llm/types.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/observability/__init__.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/observability/logging.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/observability/metrics.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/observability/migration.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/observability/prometheus.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/observability/status.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/packs/__init__.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/packs/diligence/__init__.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/packs/diligence/behaviors.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/packs/diligence/fixtures/__init__.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/packs/diligence/fixtures/companies.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/packs/diligence/object_types.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/packs/diligence/prompts/document_researcher.md +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/packs/diligence/prompts/memo_synthesizer.md +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/packs/diligence/prompts/question_generator.md +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/packs/diligence/prompts/risk_identifier.md +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/packs/diligence/settings.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/packs/diligence/tools.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/packs/loader.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/packs/scaffold.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/policy.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/runtime/__init__.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/runtime/behavior_graph.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/runtime/budget.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/runtime/config_errors.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/runtime/diff.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/runtime/errors.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/runtime/exec_errors.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/runtime/patterns.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/runtime/queue.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/runtime/registration_errors.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/runtime/registry.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/runtime/scheduler.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/runtime/view_builder.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/store/__init__.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/store/base.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/store/conformance.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/store/errors.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/store/memory.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/store/postgres.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/store/serde.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/store/url.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/tools/__init__.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/tools/base.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/tools/cache.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/tools/context.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/tools/decorators.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/tools/errors.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/tools/graph_query.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/tools/recorded.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/tools/web_fetch.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/trace/__init__.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/trace/causal.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph/trace/printer.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph.egg-info/dependency_links.txt +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph.egg-info/entry_points.txt +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/activegraph.egg-info/top_level.txt +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/setup.cfg +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_activate_after.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_causal_cross_tool.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_cli.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_clock.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_diff.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_diligence_pack.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_diligence_with_tools.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_doc_links.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_doc_site_reachable.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_errors_format.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_event.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_fork.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_graph.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_ids.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_llm_anthropic.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_llm_behavior.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_llm_budget.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_llm_causal.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_llm_claim_extraction.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_llm_determinism.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_llm_failure.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_llm_prompt.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_llm_provider_fixtures.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_llm_replay.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_llm_tool_loop.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_llm_trace.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_llm_trace_snapshot.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_llm_types.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_migration.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_observability_logging.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_observability_metrics.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_operate_example.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_pack_scaffold.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_packs.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_patch.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_pattern_matcher.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_pattern_parser.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_pattern_subscriptions.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_persistence.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_postgres_store.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_quickstart.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_quickstart_snapshot.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_replay.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_replay_trace_snapshot.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_requeue_unfired.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_resume_example.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_runtime.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_runtime_status.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_serde.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_store_conformance.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_store_url.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_tool_replay.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_tool_trace_snapshot.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_tools.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_trace.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_tutorial_snippets.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_version_sync.py +0 -0
- {activegraph-1.0.0rc3 → activegraph-1.0.2}/tests/test_view.py +0 -0
- {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.
|
|
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
|
|
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
|
|
186
|
-
|
|
187
|
-
|
|
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
|
|
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
|
|
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
|
|
145
|
-
|
|
146
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
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=
|
|
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() ->
|
|
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 =
|
|
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):
|