activegraph 1.0.1__tar.gz → 1.0.2.post1__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.1 → activegraph-1.0.2.post1}/PKG-INFO +1 -1
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/__init__.py +1 -1
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/behaviors/base.py +13 -2
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/behaviors/decorators.py +26 -1
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/llm/anthropic.py +8 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/llm/openai.py +20 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/llm/provider.py +34 -3
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/llm/recorded.py +26 -0
- activegraph-1.0.2.post1/activegraph/runtime/_live.py +173 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/runtime/runtime.py +54 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph.egg-info/PKG-INFO +1 -1
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph.egg-info/SOURCES.txt +2 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/pyproject.toml +1 -1
- activegraph-1.0.2.post1/tests/test_llm_default_model.py +554 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/README.md +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/__main__.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/behaviors/__init__.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/cli/__init__.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/cli/main.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/cli/quickstart.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/core/__init__.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/core/clock.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/core/event.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/core/graph.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/core/ids.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/core/patch.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/core/view.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/errors.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/frame.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/llm/__init__.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/llm/cache.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/llm/errors.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/llm/parsing.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/llm/prompt.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/llm/types.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/observability/__init__.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/observability/logging.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/observability/metrics.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/observability/migration.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/observability/prometheus.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/observability/status.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/packs/__init__.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/packs/diligence/__init__.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/packs/diligence/behaviors.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/packs/diligence/fixtures/__init__.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/packs/diligence/fixtures/companies.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/packs/diligence/object_types.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/packs/diligence/prompts/document_researcher.md +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/packs/diligence/prompts/memo_synthesizer.md +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/packs/diligence/prompts/question_generator.md +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/packs/diligence/prompts/risk_identifier.md +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/packs/diligence/settings.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/packs/diligence/tools.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/packs/loader.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/packs/scaffold.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/policy.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/runtime/__init__.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/runtime/behavior_graph.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/runtime/budget.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/runtime/config_errors.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/runtime/diff.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/runtime/errors.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/runtime/exec_errors.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/runtime/patterns.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/runtime/queue.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/runtime/registration_errors.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/runtime/registry.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/runtime/scheduler.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/runtime/view_builder.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/store/__init__.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/store/base.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/store/conformance.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/store/errors.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/store/memory.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/store/postgres.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/store/serde.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/store/sqlite.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/store/url.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/tools/__init__.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/tools/base.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/tools/cache.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/tools/context.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/tools/decorators.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/tools/errors.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/tools/graph_query.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/tools/recorded.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/tools/web_fetch.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/trace/__init__.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/trace/causal.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph/trace/printer.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph.egg-info/dependency_links.txt +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph.egg-info/entry_points.txt +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph.egg-info/requires.txt +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/activegraph.egg-info/top_level.txt +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/setup.cfg +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_activate_after.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_causal_cross_tool.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_cli.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_clock.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_diff.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_diligence_pack.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_diligence_with_tools.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_doc_links.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_doc_site_reachable.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_errors_format.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_event.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_fork.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_graph.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_ids.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_llm_anthropic.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_llm_behavior.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_llm_budget.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_llm_causal.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_llm_claim_extraction.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_llm_determinism.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_llm_failure.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_llm_openai.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_llm_prompt.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_llm_provider_fixtures.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_llm_replay.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_llm_tool_loop.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_llm_trace.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_llm_trace_snapshot.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_llm_types.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_migration.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_observability_logging.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_observability_metrics.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_operate_example.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_pack_scaffold.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_packs.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_patch.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_pattern_matcher.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_pattern_parser.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_pattern_subscriptions.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_persistence.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_postgres_store.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_quickstart.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_quickstart_snapshot.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_replay.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_replay_trace_snapshot.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_requeue_unfired.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_resume_example.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_runtime.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_runtime_status.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_serde.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_store_conformance.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_store_url.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_tool_replay.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_tool_trace_snapshot.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_tools.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_trace.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_tutorial_snippets.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_v1_0_1_patches.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_version_sync.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_view.py +0 -0
- {activegraph-1.0.1 → activegraph-1.0.2.post1}/tests/test_wheel_completeness.py +0 -0
|
@@ -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,
|
|
@@ -85,6 +85,12 @@ def register(behavior_obj: Union[Behavior, RelationBehavior]) -> None:
|
|
|
85
85
|
f"decorators to construct one."
|
|
86
86
|
)
|
|
87
87
|
_REGISTRY.append(behavior_obj)
|
|
88
|
+
# v1.0.2.post1 #1 (b): if any Runtime is already alive, validate the
|
|
89
|
+
# new behavior against each one's configured provider so cross-
|
|
90
|
+
# provider model mismatches fire at the @llm_behavior / register()
|
|
91
|
+
# line rather than at first run_goal.
|
|
92
|
+
from activegraph.runtime._live import validate_behavior_against_live_runtimes
|
|
93
|
+
validate_behavior_against_live_runtimes(behavior_obj)
|
|
88
94
|
|
|
89
95
|
|
|
90
96
|
def behavior(
|
|
@@ -147,7 +153,7 @@ def llm_behavior(
|
|
|
147
153
|
on: Optional[list[str]] = None,
|
|
148
154
|
where: Optional[dict[str, Any]] = None,
|
|
149
155
|
description: str = "",
|
|
150
|
-
model: str =
|
|
156
|
+
model: Optional[str] = None,
|
|
151
157
|
output_schema: Optional[type] = None,
|
|
152
158
|
view: Optional[dict[str, Any]] = None,
|
|
153
159
|
creates: Optional[list[str]] = None,
|
|
@@ -177,6 +183,19 @@ def llm_behavior(
|
|
|
177
183
|
Keyword-only on purpose — `@llm_behavior` carries enough
|
|
178
184
|
parameters that positional binding would be a footgun.
|
|
179
185
|
|
|
186
|
+
``model=`` is optional (v1.0.2 #1). Omitted, the runtime resolves
|
|
187
|
+
it to the configured provider's ``default_model`` at registration
|
|
188
|
+
time — ``"claude-sonnet-4-5"`` for ``AnthropicProvider``,
|
|
189
|
+
``"gpt-4o-mini"`` for ``OpenAIProvider``. Passing an explicit
|
|
190
|
+
model string still works byte-identically; the runtime additionally
|
|
191
|
+
validates the name against the configured provider's
|
|
192
|
+
``recognizes_model()`` and raises
|
|
193
|
+
:class:`InvalidRuntimeConfiguration` at registration time if the
|
|
194
|
+
name belongs to a different shipped provider's family (e.g.
|
|
195
|
+
``model="gpt-4o-mini"`` on a runtime configured with
|
|
196
|
+
``AnthropicProvider``). Names no shipped provider recognizes
|
|
197
|
+
(custom or fine-tuned models) pass through silently.
|
|
198
|
+
|
|
180
199
|
`prompt_template=` is the only escape hatch from the
|
|
181
200
|
runtime-assembled prompt. It is a `str.format`-style template that
|
|
182
201
|
receives four placeholders:
|
|
@@ -235,6 +254,12 @@ def llm_behavior(
|
|
|
235
254
|
max_tool_turns=max_tool_turns,
|
|
236
255
|
)
|
|
237
256
|
_REGISTRY.append(b)
|
|
257
|
+
# v1.0.2.post1 #1 (b): validate against any live Runtime's
|
|
258
|
+
# provider. Mirrors the eager check in register() so decorated
|
|
259
|
+
# behaviors with cross-provider model names fire at the
|
|
260
|
+
# @llm_behavior line instead of at first run_goal.
|
|
261
|
+
from activegraph.runtime._live import validate_behavior_against_live_runtimes
|
|
262
|
+
validate_behavior_against_live_runtimes(b)
|
|
238
263
|
return b
|
|
239
264
|
|
|
240
265
|
return wrap
|
|
@@ -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
|
|
|
@@ -82,6 +82,20 @@ def _pricing_for(
|
|
|
82
82
|
|
|
83
83
|
|
|
84
84
|
class OpenAIProvider(LLMProvider):
|
|
85
|
+
# v1.0.2 #1: provider-aware default model. @llm_behavior(model=None)
|
|
86
|
+
# resolves to this string at registration time. gpt-4o-mini is the
|
|
87
|
+
# cheap, fast member of the GPT-4o family — matches Anthropic's
|
|
88
|
+
# default-model shape (cheapest sensible default; override for prod).
|
|
89
|
+
default_model: str = "gpt-4o-mini"
|
|
90
|
+
|
|
91
|
+
# v1.0.2 #1: the model-name prefixes this provider recognizes.
|
|
92
|
+
# `gpt-` covers the GPT-3.5 / GPT-4 / GPT-4o families; the
|
|
93
|
+
# `o1-` / `o3-` / `o4-` prefixes cover the reasoning-model line
|
|
94
|
+
# (o1, o3, o4 and their dated variants). Names matching none of
|
|
95
|
+
# these pass through registration silently per v1.0.2 #1 (b)'s
|
|
96
|
+
# permissive-default rule.
|
|
97
|
+
_RECOGNIZED_PREFIXES: tuple[str, ...] = ("gpt-", "o1-", "o3-", "o4-")
|
|
98
|
+
|
|
85
99
|
def __init__(
|
|
86
100
|
self,
|
|
87
101
|
*,
|
|
@@ -267,6 +281,12 @@ class OpenAIProvider(LLMProvider):
|
|
|
267
281
|
total += 4
|
|
268
282
|
return max(1, total)
|
|
269
283
|
|
|
284
|
+
def recognizes_model(self, name: str) -> bool:
|
|
285
|
+
"""True for OpenAI model families (``gpt-*``, ``o1-*``, ``o3-*``,
|
|
286
|
+
``o4-*``). v1.0.2 #1.
|
|
287
|
+
"""
|
|
288
|
+
return any(name.startswith(p) for p in self._RECOGNIZED_PREFIXES)
|
|
289
|
+
|
|
270
290
|
def _heuristic_count(self, system: str, messages: list[LLMMessage]) -> int:
|
|
271
291
|
if not self._heuristic_warned:
|
|
272
292
|
_log.debug(
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
"""The `LLMProvider` Protocol every provider implements.
|
|
2
2
|
|
|
3
|
-
CONTRACT v0.6 #3, extended in v0.7
|
|
4
|
-
|
|
3
|
+
CONTRACT v0.6 #3, extended in v0.7, additively widened in v1.0.2 #1.
|
|
4
|
+
Narrow, explicit, keyword-only. Shipped reference implementations are
|
|
5
|
+
`AnthropicProvider` and `OpenAIProvider`. Tests use
|
|
5
6
|
`RecordedLLMProvider` + `RecordingLLMProvider`. The demo ships its
|
|
6
7
|
own scripted provider.
|
|
7
8
|
|
|
8
|
-
A provider does three things:
|
|
9
|
+
A provider does three things plus two declarations:
|
|
10
|
+
|
|
9
11
|
* `complete()`: run a single non-streaming completion. v0.7 adds
|
|
10
12
|
an optional `tools=` parameter; when non-empty, the model is
|
|
11
13
|
allowed to return tool_use blocks in the response.
|
|
@@ -14,6 +16,18 @@ A provider does three things:
|
|
|
14
16
|
prompt that's about to be sent. Used for pre-call budget gating
|
|
15
17
|
when `budget.max_cost_usd` is set; otherwise skipped (see
|
|
16
18
|
CONTRACT v0.6 #4 / decision 10).
|
|
19
|
+
* `default_model`: the model name to use when an `@llm_behavior`
|
|
20
|
+
didn't pin one (v1.0.2 #1).
|
|
21
|
+
* `recognizes_model(name)`: True when `name` belongs to a model
|
|
22
|
+
family this provider serves. Used by the runtime at
|
|
23
|
+
registration time to flag cross-provider mismatches before the
|
|
24
|
+
first network call (v1.0.2 #1).
|
|
25
|
+
|
|
26
|
+
`default_model` and `recognizes_model` are additive: the runtime
|
|
27
|
+
guards their use with `getattr(...)`, so custom providers that
|
|
28
|
+
pre-date v1.0.2 keep working — they just require an explicit
|
|
29
|
+
`model=` on every `@llm_behavior` and don't participate in
|
|
30
|
+
cross-provider validation.
|
|
17
31
|
|
|
18
32
|
No streaming, no multi-model orchestration — those are deferred to
|
|
19
33
|
v0.8+. Tool use IS in v0.7, but the loop is orchestrated by the
|
|
@@ -32,6 +46,11 @@ from activegraph.llm.types import LLMMessage, LLMResponse
|
|
|
32
46
|
|
|
33
47
|
@runtime_checkable
|
|
34
48
|
class LLMProvider(Protocol):
|
|
49
|
+
# v1.0.2 #1: declared as an attribute on the Protocol. Concrete
|
|
50
|
+
# providers set it as a class attribute; custom providers may
|
|
51
|
+
# omit it (the runtime falls back to getattr-with-default).
|
|
52
|
+
default_model: str
|
|
53
|
+
|
|
35
54
|
def complete(
|
|
36
55
|
self,
|
|
37
56
|
*,
|
|
@@ -64,3 +83,15 @@ class LLMProvider(Protocol):
|
|
|
64
83
|
model: str,
|
|
65
84
|
) -> int:
|
|
66
85
|
...
|
|
86
|
+
|
|
87
|
+
def recognizes_model(self, name: str) -> bool:
|
|
88
|
+
"""True when `name` belongs to a model family this provider serves.
|
|
89
|
+
|
|
90
|
+
v1.0.2 #1. Used at registration time to flag cross-provider
|
|
91
|
+
mismatches (e.g. ``model="claude-sonnet-4-5"`` on a runtime
|
|
92
|
+
configured with ``OpenAIProvider``). Permissive by default:
|
|
93
|
+
unknown names — fine-tuned models, internal deployment names,
|
|
94
|
+
experimental prefixes — should return False so the runtime
|
|
95
|
+
passes them through without a diagnostic.
|
|
96
|
+
"""
|
|
97
|
+
...
|
|
@@ -110,9 +110,21 @@ class RecordedLLMProvider(LLMProvider):
|
|
|
110
110
|
silent fallthrough to a real call.
|
|
111
111
|
"""
|
|
112
112
|
|
|
113
|
+
# v1.0.2 #1: fixtures already carry their own model name in the hash
|
|
114
|
+
# input, so default-model resolution only matters when an
|
|
115
|
+
# @llm_behavior omits model=. The recorded provider has no opinion
|
|
116
|
+
# about which family it's replaying; declare the historical default
|
|
117
|
+
# so existing fixtures stay reachable.
|
|
118
|
+
default_model: str = "claude-sonnet-4-5"
|
|
119
|
+
|
|
113
120
|
def __init__(self, fixtures_dir: str) -> None:
|
|
114
121
|
self._dir = fixtures_dir
|
|
115
122
|
|
|
123
|
+
def recognizes_model(self, name: str) -> bool:
|
|
124
|
+
# Fixture-backed: claim every name so cross-provider validation
|
|
125
|
+
# doesn't fire against recorded responses.
|
|
126
|
+
return True
|
|
127
|
+
|
|
116
128
|
# ---- LLMProvider methods ----
|
|
117
129
|
|
|
118
130
|
def complete(
|
|
@@ -207,6 +219,20 @@ class RecordingLLMProvider(LLMProvider):
|
|
|
207
219
|
self._dir = fixtures_dir
|
|
208
220
|
os.makedirs(self._dir, exist_ok=True)
|
|
209
221
|
|
|
222
|
+
# v1.0.2 #1: delegate default_model + recognizes_model to the inner
|
|
223
|
+
# provider so wrapping doesn't change the resolution surface. Falls
|
|
224
|
+
# back to the historical default if the inner provider pre-dates
|
|
225
|
+
# v1.0.2 and doesn't declare one.
|
|
226
|
+
@property
|
|
227
|
+
def default_model(self) -> str:
|
|
228
|
+
return getattr(self._inner, "default_model", "claude-sonnet-4-5")
|
|
229
|
+
|
|
230
|
+
def recognizes_model(self, name: str) -> bool:
|
|
231
|
+
recognizes = getattr(self._inner, "recognizes_model", None)
|
|
232
|
+
if recognizes is None:
|
|
233
|
+
return True
|
|
234
|
+
return bool(recognizes(name))
|
|
235
|
+
|
|
210
236
|
def complete(
|
|
211
237
|
self,
|
|
212
238
|
*,
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"""Live-Runtime tracking + single-behavior cross-provider validation.
|
|
2
|
+
|
|
3
|
+
CONTRACT v1.0.2 #1 (b). The validation fires at *both* binding
|
|
4
|
+
moments: ``Runtime(graph, llm_provider=...)`` construction (against
|
|
5
|
+
the existing registry) and ``register()`` / ``@llm_behavior``
|
|
6
|
+
decoration (against any live Runtime via the WeakSet below).
|
|
7
|
+
|
|
8
|
+
The WeakSet is module-level so the registration decorators can
|
|
9
|
+
look it up without importing ``Runtime`` at module load. Runtimes
|
|
10
|
+
self-register inside ``__init__`` after wiring; the WeakSet
|
|
11
|
+
auto-cleans GC'd Runtimes — no explicit ``Runtime.close()`` is
|
|
12
|
+
required.
|
|
13
|
+
|
|
14
|
+
The single-behavior validator here is intentionally narrow: it
|
|
15
|
+
only does the cross-provider mismatch check (recognized name
|
|
16
|
+
belonging to a different shipped provider). It does *not* stamp
|
|
17
|
+
provider defaults onto ``model=None`` behaviors — that side
|
|
18
|
+
effect lives in ``_resolve_and_validate_llm_models`` in
|
|
19
|
+
``runtime.py``, which still runs at first ``_ensure_registry``.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import weakref
|
|
25
|
+
from typing import TYPE_CHECKING, Any
|
|
26
|
+
|
|
27
|
+
from activegraph.behaviors.base import LLMBehavior
|
|
28
|
+
|
|
29
|
+
if TYPE_CHECKING:
|
|
30
|
+
from activegraph.runtime.runtime import Runtime
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# WeakSet so abandoned Runtimes don't leak validation calls. The
|
|
34
|
+
# fork/replay paths construct fresh Runtimes that self-register; the
|
|
35
|
+
# parent stays in the set until GC'd.
|
|
36
|
+
_LIVE_RUNTIMES: "weakref.WeakSet[Runtime]" = weakref.WeakSet()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def track_runtime(rt: "Runtime") -> None:
|
|
40
|
+
"""Register a live Runtime for cross-provider validation. Called
|
|
41
|
+
by ``Runtime.__init__`` after the provider and graph are wired."""
|
|
42
|
+
_LIVE_RUNTIMES.add(rt)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def live_runtimes() -> list["Runtime"]:
|
|
46
|
+
"""Snapshot of currently-alive Runtimes (a list of strong refs that
|
|
47
|
+
the caller releases at end-of-call). Used by ``register()`` and the
|
|
48
|
+
decorators to validate a new behavior against each live provider."""
|
|
49
|
+
return list(_LIVE_RUNTIMES)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _clear_for_test() -> None:
|
|
53
|
+
"""Empty the live-Runtime set. Test-only — used by the conftest
|
|
54
|
+
fixture that isolates the global registry between tests. Production
|
|
55
|
+
code shouldn't call this: the WeakSet cleans itself on GC."""
|
|
56
|
+
_LIVE_RUNTIMES.clear()
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def validate_behavior_against_live_runtimes(behavior: Any) -> None:
|
|
60
|
+
"""Validate a freshly-registered behavior against every live
|
|
61
|
+
Runtime's provider. Raises :class:`InvalidRuntimeConfiguration` on
|
|
62
|
+
the first cross-provider mismatch.
|
|
63
|
+
|
|
64
|
+
No-op when ``behavior`` isn't an :class:`LLMBehavior`, when no
|
|
65
|
+
Runtime is live, or when the behavior has no explicit model (model
|
|
66
|
+
resolution against the provider's default happens at Runtime
|
|
67
|
+
construction or first-run, not here).
|
|
68
|
+
"""
|
|
69
|
+
if not isinstance(behavior, LLMBehavior):
|
|
70
|
+
return
|
|
71
|
+
if behavior.model is None:
|
|
72
|
+
return
|
|
73
|
+
for rt in live_runtimes():
|
|
74
|
+
provider = getattr(rt, "llm_provider", None)
|
|
75
|
+
if provider is None:
|
|
76
|
+
continue
|
|
77
|
+
_validate_one(behavior, provider)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _validate_one(behavior: LLMBehavior, provider: Any) -> None:
|
|
81
|
+
"""Cross-provider mismatch check for a single behavior against a
|
|
82
|
+
single provider. Pure: no side effects, no model-default stamping.
|
|
83
|
+
|
|
84
|
+
Permissive by default per v1.0.2 #1 (b): names no shipped provider
|
|
85
|
+
recognizes pass silently. Only recognized cross-provider mismatches
|
|
86
|
+
raise. The runtime-side ``_resolve_and_validate_llm_models``
|
|
87
|
+
delegates to this for its per-behavior pass so the check lives in
|
|
88
|
+
one place.
|
|
89
|
+
"""
|
|
90
|
+
from activegraph.runtime.config_errors import InvalidRuntimeConfiguration
|
|
91
|
+
|
|
92
|
+
model = behavior.model
|
|
93
|
+
if model is None:
|
|
94
|
+
return
|
|
95
|
+
recognizes = getattr(provider, "recognizes_model", None)
|
|
96
|
+
if recognizes is None or recognizes(model):
|
|
97
|
+
return
|
|
98
|
+
claimed_by = _which_shipped_provider_claims(model, exclude=type(provider))
|
|
99
|
+
if claimed_by is None:
|
|
100
|
+
return
|
|
101
|
+
|
|
102
|
+
provider_class = type(provider).__name__
|
|
103
|
+
provider_default = getattr(provider, "default_model", None) or "claude-sonnet-4-5"
|
|
104
|
+
claimed_by_default = getattr(claimed_by, "default_model", "")
|
|
105
|
+
provider_default_hint = (
|
|
106
|
+
f"or remove the model= argument to use {provider_class}'s "
|
|
107
|
+
f"default ({provider_default!r})"
|
|
108
|
+
if provider_default
|
|
109
|
+
else f"or set a {provider_class}-compatible model name"
|
|
110
|
+
)
|
|
111
|
+
raise InvalidRuntimeConfiguration(
|
|
112
|
+
(
|
|
113
|
+
f"@llm_behavior(name={behavior.name!r}, model={model!r}) "
|
|
114
|
+
f"names a {claimed_by.__name__}-family model, but the "
|
|
115
|
+
f"runtime is configured with {provider_class}"
|
|
116
|
+
),
|
|
117
|
+
what_failed=(
|
|
118
|
+
f"The behavior {behavior.name!r} pinned model={model!r}. "
|
|
119
|
+
f"That name belongs to {claimed_by.__name__}'s model "
|
|
120
|
+
f"family, but this Runtime was constructed with a "
|
|
121
|
+
f"{provider_class} instance. Sending the name to the "
|
|
122
|
+
f"wrong provider produces an HTTP 404 (or equivalent "
|
|
123
|
+
f"'unknown model' response) at first LLM call, with no "
|
|
124
|
+
f"hint that the mismatch is the cause."
|
|
125
|
+
),
|
|
126
|
+
why=(
|
|
127
|
+
"v1.0.2 #1 validates explicit model names at both binding "
|
|
128
|
+
"moments (Runtime construction and register()/decoration) "
|
|
129
|
+
"against each shipped provider's recognizes_model() "
|
|
130
|
+
"method. The configured provider doesn't claim this name, "
|
|
131
|
+
"but another shipped provider does — that's a "
|
|
132
|
+
"configuration mismatch worth surfacing before the first "
|
|
133
|
+
"network call rather than after."
|
|
134
|
+
),
|
|
135
|
+
how_to_fix=(
|
|
136
|
+
f"Either swap the provider — Runtime(graph, "
|
|
137
|
+
f"llm_provider={claimed_by.__name__}()) — "
|
|
138
|
+
f"{provider_default_hint}, or pass an explicit name "
|
|
139
|
+
f"from {provider_class}'s model families."
|
|
140
|
+
),
|
|
141
|
+
context={
|
|
142
|
+
"behavior": behavior.name,
|
|
143
|
+
"model": model,
|
|
144
|
+
"configured_provider": provider_class,
|
|
145
|
+
"claimed_by_provider": claimed_by.__name__,
|
|
146
|
+
"claimed_by_default_model": claimed_by_default,
|
|
147
|
+
},
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _which_shipped_provider_claims(name: str, *, exclude: type) -> Any:
|
|
152
|
+
"""Return the first shipped provider class that recognizes `name`,
|
|
153
|
+
excluding `exclude`. Returns None when no other shipped provider
|
|
154
|
+
claims the name (permissive default per v1.0.2 #1 (b))."""
|
|
155
|
+
from activegraph.llm.anthropic import AnthropicProvider
|
|
156
|
+
from activegraph.llm.openai import OpenAIProvider
|
|
157
|
+
|
|
158
|
+
candidates = [AnthropicProvider, OpenAIProvider]
|
|
159
|
+
for cls in candidates:
|
|
160
|
+
if cls is exclude:
|
|
161
|
+
continue
|
|
162
|
+
recognizes = getattr(cls, "recognizes_model", None)
|
|
163
|
+
if recognizes is None:
|
|
164
|
+
continue
|
|
165
|
+
try:
|
|
166
|
+
inst = cls()
|
|
167
|
+
except Exception:
|
|
168
|
+
# Defensive: if a provider's no-arg constructor ever requires
|
|
169
|
+
# real credentials, skip it for the validation lookup.
|
|
170
|
+
continue
|
|
171
|
+
if inst.recognizes_model(name):
|
|
172
|
+
return cls
|
|
173
|
+
return None
|
|
@@ -303,6 +303,23 @@ class Runtime:
|
|
|
303
303
|
self._pack_behaviors: list = []
|
|
304
304
|
self._pack_tools: list = []
|
|
305
305
|
|
|
306
|
+
# ---- v1.0.2.post1 #1 (b): eager cross-provider validation ----
|
|
307
|
+
# First run the bulk validation pass against whatever's already
|
|
308
|
+
# in the registry; only if it passes do we add this Runtime to
|
|
309
|
+
# the live-set. Failed constructions stay out of the WeakSet so
|
|
310
|
+
# they can't influence subsequent register() / @llm_behavior
|
|
311
|
+
# validation calls. The lazy path inside _ensure_registry()
|
|
312
|
+
# stays as a defensive double-check.
|
|
313
|
+
if self.llm_provider is not None:
|
|
314
|
+
source = (
|
|
315
|
+
self._explicit_behaviors
|
|
316
|
+
if self._explicit_behaviors is not None
|
|
317
|
+
else get_registry()
|
|
318
|
+
)
|
|
319
|
+
_resolve_and_validate_llm_models(source, self.llm_provider)
|
|
320
|
+
from activegraph.runtime._live import track_runtime
|
|
321
|
+
track_runtime(self)
|
|
322
|
+
|
|
306
323
|
# ---------- public surface ----------
|
|
307
324
|
|
|
308
325
|
@property
|
|
@@ -376,6 +393,11 @@ class Runtime:
|
|
|
376
393
|
for b in source:
|
|
377
394
|
if isinstance(b, LLMBehavior):
|
|
378
395
|
raise MissingProviderError(behavior_name=b.name)
|
|
396
|
+
else:
|
|
397
|
+
# CONTRACT v1.0.2 #1: resolve the provider's default model for
|
|
398
|
+
# behaviors that didn't pin one, and validate explicit model
|
|
399
|
+
# names against cross-provider mismatches.
|
|
400
|
+
_resolve_and_validate_llm_models(source, self.llm_provider)
|
|
379
401
|
self.registry = Registry(source)
|
|
380
402
|
|
|
381
403
|
# v0.7: assemble the tool registry. Explicit tools= override the
|
|
@@ -2180,6 +2202,38 @@ _BUDGET_REASON_MAP = {
|
|
|
2180
2202
|
}
|
|
2181
2203
|
|
|
2182
2204
|
|
|
2205
|
+
def _resolve_and_validate_llm_models(source, provider) -> None:
|
|
2206
|
+
"""Stamp provider defaults onto LLMBehaviors with model=None, and
|
|
2207
|
+
raise InvalidRuntimeConfiguration for explicit names that belong to
|
|
2208
|
+
a different shipped provider. CONTRACT v1.0.2 #1.
|
|
2209
|
+
|
|
2210
|
+
The cross-provider mismatch check delegates to
|
|
2211
|
+
:func:`activegraph.runtime._live._validate_one` so the same check
|
|
2212
|
+
fires from both binding moments (Runtime construction here,
|
|
2213
|
+
register()/decoration in ``_live.validate_behavior_against_live_runtimes``).
|
|
2214
|
+
"""
|
|
2215
|
+
from activegraph.runtime._live import _validate_one
|
|
2216
|
+
|
|
2217
|
+
# Custom providers that pre-date v1.0.2 don't declare `default_model`.
|
|
2218
|
+
# Fall back to the v1.0.1 hardcoded default — keeps every pre-v1.0.2
|
|
2219
|
+
# call site (which silently inherited "claude-sonnet-4-5") working
|
|
2220
|
+
# byte-identically without code changes. The runtime only swaps in a
|
|
2221
|
+
# different default when the provider opts in by declaring one.
|
|
2222
|
+
provider_default = getattr(provider, "default_model", None) or "claude-sonnet-4-5"
|
|
2223
|
+
|
|
2224
|
+
for b in source:
|
|
2225
|
+
if not isinstance(b, LLMBehavior):
|
|
2226
|
+
continue
|
|
2227
|
+
if b.model is None:
|
|
2228
|
+
# v1.0.2 #1 (a): resolve to the provider's default_model
|
|
2229
|
+
# (or the v1.0.1 fallback for custom providers that don't
|
|
2230
|
+
# declare one).
|
|
2231
|
+
b.model = provider_default
|
|
2232
|
+
continue
|
|
2233
|
+
# v1.0.2 #1 (b): cross-provider mismatch check.
|
|
2234
|
+
_validate_one(b, provider)
|
|
2235
|
+
|
|
2236
|
+
|
|
2183
2237
|
def _budget_reason(name: Optional[str]) -> str:
|
|
2184
2238
|
if name is None:
|
|
2185
2239
|
return "budget.exhausted"
|
|
@@ -55,6 +55,7 @@ activegraph/packs/diligence/prompts/memo_synthesizer.md
|
|
|
55
55
|
activegraph/packs/diligence/prompts/question_generator.md
|
|
56
56
|
activegraph/packs/diligence/prompts/risk_identifier.md
|
|
57
57
|
activegraph/runtime/__init__.py
|
|
58
|
+
activegraph/runtime/_live.py
|
|
58
59
|
activegraph/runtime/behavior_graph.py
|
|
59
60
|
activegraph/runtime/budget.py
|
|
60
61
|
activegraph/runtime/config_errors.py
|
|
@@ -108,6 +109,7 @@ tests/test_llm_behavior.py
|
|
|
108
109
|
tests/test_llm_budget.py
|
|
109
110
|
tests/test_llm_causal.py
|
|
110
111
|
tests/test_llm_claim_extraction.py
|
|
112
|
+
tests/test_llm_default_model.py
|
|
111
113
|
tests/test_llm_determinism.py
|
|
112
114
|
tests/test_llm_failure.py
|
|
113
115
|
tests/test_llm_openai.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "activegraph"
|
|
7
|
-
version = "1.0.
|
|
7
|
+
version = "1.0.2.post1"
|
|
8
8
|
description = "An event-sourced reactive graph runtime for long-running, auditable, agentic systems."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|