graphrefly 0.18.0__tar.gz → 0.20.0__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.
- {graphrefly-0.18.0 → graphrefly-0.20.0}/CHANGELOG.md +50 -0
- graphrefly-0.20.0/CLAUDE.md +61 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/PKG-INFO +21 -4
- {graphrefly-0.18.0 → graphrefly-0.20.0}/README.md +20 -3
- graphrefly-0.20.0/llms.txt +114 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/pyproject.toml +2 -1
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/__init__.py +0 -6
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/compat/async_utils.py +7 -9
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/compat/asyncio_runner.py +34 -7
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/compat/trio_runner.py +29 -5
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/core/__init__.py +0 -3
- graphrefly-0.20.0/src/graphrefly/core/dynamic_node.py +382 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/core/meta.py +5 -1
- graphrefly-0.20.0/src/graphrefly/core/node.py +540 -0
- graphrefly-0.20.0/src/graphrefly/core/node_base.py +739 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/core/protocol.py +51 -26
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/core/runner.py +15 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/extra/__init__.py +8 -2
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/extra/adapters.py +16 -8
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/extra/cascading_cache.py +17 -7
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/extra/data_structures.py +1 -3
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/extra/resilience.py +13 -6
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/extra/sources.py +186 -10
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/extra/tier1.py +37 -41
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/extra/tier2.py +28 -29
- graphrefly-0.20.0/src/graphrefly/graph/__init__.py +67 -0
- graphrefly-0.20.0/src/graphrefly/graph/codec.py +293 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/graph/graph.py +172 -144
- graphrefly-0.20.0/src/graphrefly/graph/profile.py +120 -0
- graphrefly-0.20.0/src/graphrefly/graph/sizeof.py +115 -0
- graphrefly-0.20.0/src/graphrefly/patterns/_internal.py +64 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/patterns/ai.py +984 -79
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/patterns/cqrs.py +4 -5
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/patterns/domain_templates.py +7 -9
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/patterns/harness/__init__.py +10 -0
- graphrefly-0.20.0/src/graphrefly/patterns/harness/bridge.py +473 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/patterns/harness/loop.py +153 -69
- graphrefly-0.20.0/src/graphrefly/patterns/harness/profile.py +84 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/patterns/harness/strategy.py +3 -4
- graphrefly-0.20.0/src/graphrefly/patterns/harness/trace.py +239 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/patterns/harness/types.py +7 -12
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/patterns/messaging.py +3 -8
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/patterns/orchestration.py +19 -11
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/patterns/reduction.py +108 -4
- graphrefly-0.20.0/tests/__init__.py +1 -0
- graphrefly-0.20.0/tests/conftest.py +123 -0
- graphrefly-0.20.0/tests/helpers/__init__.py +1 -0
- graphrefly-0.20.0/tests/helpers/mock_llm.py +122 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_adapters_ingest.py +17 -69
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_adapters_storage.py +8 -16
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_core.py +20 -9
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_domain_templates.py +3 -2
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_dynamic_node.py +3 -3
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_edge_cases.py +19 -34
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_extra_data_structures.py +2 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_extra_resilience.py +23 -39
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_extra_sources.py +5 -2
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_extra_sources_http.py +16 -17
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_extra_tier1.py +102 -88
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_extra_tier2.py +155 -90
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_fastapi.py +2 -2
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_graph.py +36 -21
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_operator_protocol_matrix.py +4 -3
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_patterns_ai.py +726 -4
- graphrefly-0.20.0/tests/test_patterns_harness.py +1085 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_patterns_orchestration.py +14 -10
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_patterns_reduction.py +9 -9
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_protocol.py +1 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_reduction.py +10 -10
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_sugar.py +3 -2
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_versioning.py +2 -1
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/astro.config.mjs +12 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/scripts/gen_api_docs.py +1 -1
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/components/Header.astro +6 -2
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/components/Sidebar.astro +4 -2
- graphrefly-0.20.0/website/src/content/docs/api/ReactiveCounterBundle.md +14 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/first_value_from.md +6 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/from_any.md +1 -1
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/index.md +33 -36
- graphrefly-0.20.0/website/src/content/docs/api/is_local_only.md +16 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/is_terminal_message.md +2 -2
- graphrefly-0.20.0/website/src/content/docs/api/keepalive.md +21 -0
- graphrefly-0.20.0/website/src/content/docs/api/message_tier.md +20 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/partition_for_batch.md +1 -1
- graphrefly-0.20.0/website/src/content/docs/api/reactive_counter.md +19 -0
- graphrefly-0.20.0/website/src/content/docs/recipes/index.md +8 -0
- graphrefly-0.18.0/.claude/skills/dev-dispatch/SKILL.md +0 -119
- graphrefly-0.18.0/.claude/skills/parity/SKILL.md +0 -139
- graphrefly-0.18.0/.claude/skills/qa/SKILL.md +0 -104
- graphrefly-0.18.0/.gemini/skills/dev-dispatch/SKILL.md +0 -171
- graphrefly-0.18.0/.gemini/skills/parity/SKILL.md +0 -188
- graphrefly-0.18.0/CLAUDE.md +0 -77
- graphrefly-0.18.0/TRASH/EmitStrategy.md +0 -10
- graphrefly-0.18.0/TRASH/emit_with_batch.md +0 -43
- graphrefly-0.18.0/TRASH-FILES.md +0 -2
- graphrefly-0.18.0/archive/docs/DESIGN-ARCHIVE-INDEX.md +0 -79
- graphrefly-0.18.0/archive/docs/SESSION-access-control-actor-guard.md +0 -210
- graphrefly-0.18.0/archive/docs/SESSION-cross-repo-implementation-audit.md +0 -205
- graphrefly-0.18.0/archive/docs/SESSION-demo-test-strategy.md +0 -71
- graphrefly-0.18.0/archive/docs/SESSION-graphrefly-spec-design.md +0 -111
- graphrefly-0.18.0/archive/docs/SESSION-serialization-memory-footprint.md +0 -69
- graphrefly-0.18.0/archive/docs/SESSION-tier2-parity-nonlocal-forward-inner.md +0 -54
- graphrefly-0.18.0/archive/docs/SESSION-universal-reduction-layer.md +0 -65
- graphrefly-0.18.0/archive/docs/design-archive-index.jsonl +0 -14
- graphrefly-0.18.0/archive/optimizations/built-in-optimizations.jsonl +0 -8
- graphrefly-0.18.0/archive/optimizations/cross-language-notes.jsonl +0 -35
- graphrefly-0.18.0/archive/optimizations/parity-fixes.jsonl +0 -6
- graphrefly-0.18.0/archive/optimizations/qa-design-decisions.jsonl +0 -11
- graphrefly-0.18.0/archive/optimizations/resolved-decisions.jsonl +0 -95
- graphrefly-0.18.0/archive/optimizations/summary-table.jsonl +0 -49
- graphrefly-0.18.0/archive/roadmap/phase-0-foundation.jsonl +0 -7
- graphrefly-0.18.0/archive/roadmap/phase-1-graph-container.jsonl +0 -7
- graphrefly-0.18.0/archive/roadmap/phase-2-extra.jsonl +0 -3
- graphrefly-0.18.0/archive/roadmap/phase-3-resilience-data.jsonl +0 -4
- graphrefly-0.18.0/archive/roadmap/phase-4-domain-layers.jsonl +0 -5
- graphrefly-0.18.0/archive/roadmap/phase-5-framework-distribution.jsonl +0 -6
- graphrefly-0.18.0/archive/roadmap/phase-6-versioning.jsonl +0 -3
- graphrefly-0.18.0/archive/roadmap/phase-7-polish.jsonl +0 -2
- graphrefly-0.18.0/archive/roadmap/phase-8-reduction-layer.jsonl +0 -3
- graphrefly-0.18.0/archive/roadmap/phase-9-harness-sprint.jsonl +0 -1
- graphrefly-0.18.0/docs/docs-guidance.md +0 -247
- graphrefly-0.18.0/docs/optimizations.md +0 -80
- graphrefly-0.18.0/docs/roadmap.md +0 -161
- graphrefly-0.18.0/docs/test-guidance.md +0 -138
- graphrefly-0.18.0/llms.txt +0 -272
- graphrefly-0.18.0/src/graphrefly/core/dynamic_node.py +0 -774
- graphrefly-0.18.0/src/graphrefly/core/node.py +0 -1046
- graphrefly-0.18.0/src/graphrefly/graph/__init__.py +0 -33
- graphrefly-0.18.0/src/graphrefly/patterns/harness/bridge.py +0 -118
- graphrefly-0.18.0/tests/conftest.py +0 -46
- graphrefly-0.18.0/tests/test_patterns_harness.py +0 -383
- graphrefly-0.18.0/website/src/content/docs/api/message_tier.md +0 -19
- {graphrefly-0.18.0 → graphrefly-0.20.0}/.github/workflows/pages.yml +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/.github/workflows/release.yml +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/.gitignore +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/.mise.toml +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/CONTRIBUTING.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/GEMINI.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/LICENSE +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/benchmarks/py-baseline.json +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/docs/ADAPTER-CONTRACT.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/docs/benchmark.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/examples/README.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/examples/basic_counter.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/compat/__init__.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/core/bridge.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/core/cancellation.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/core/clock.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/core/guard.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/core/subgraph_locks.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/core/sugar.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/core/timer.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/core/versioning.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/extra/backoff.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/extra/backpressure.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/extra/checkpoint.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/extra/composite.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/extra/cron.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/integrations/__init__.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/integrations/django.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/integrations/fastapi.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/patterns/__init__.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/patterns/graphspec.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/patterns/memory.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/patterns/reactive_layout/__init__.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/patterns/reactive_layout/measurement_adapters.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/patterns/reactive_layout/reactive_block_layout.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/patterns/reactive_layout/reactive_layout.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/src/graphrefly/py.typed +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/bench_core.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_adapter_contract.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_backpressure.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_bridge.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_cascading_cache.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_concurrency.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_django.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_extra_composite.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_graphspec.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_guard.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_measurement_adapters.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_patterns_cqrs.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_patterns_memory.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_patterns_messaging.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_perf_smoke.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_reactive_block_layout.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_reactive_layout.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_regressions.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_runner.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/tests/test_smoke.py +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/.gitignore +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/README.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/content.config.ts +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/package.json +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/pnpm-lock.yaml +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/public/llms.txt +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/py-api-sidebar.mjs +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/scripts/sync-docs.mjs +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/components/GraphreflyHero.astro +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/components/MobileMenuFooter.astro +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/components/PyodidePlayground.tsx +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/components/SiteTitle.astro +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/BackoffPreset.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/BackoffStrategy.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/CheckpointAdapter.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/CircuitBreaker.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/CircuitOpenError.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/CompactEntry.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/DeferWhen.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/DictCheckpointAdapter.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/DistillBundle.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/DownStrategy.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/Extraction.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/FileCheckpointAdapter.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/HttpBundle.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/JitterMode.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/MemoryCheckpointAdapter.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/Message.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/MessageType.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/Messages.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/NodeActions.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/NodeFn.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/NodeImpl.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/NodeStatus.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/PipeOperator.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/PubSubHub.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/ReactiveIndexBundle.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/ReactiveListBundle.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/ReactiveLogBundle.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/ReactiveMapBundle.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/SqliteCheckpointAdapter.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/SubscribeHints.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/TimeoutError.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/TokenBucket.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/VerifiableBundle.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/Versioned.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/WithBreakerBundle.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/WithStatusBundle.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/audit.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/batch.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/buffer.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/buffer_count.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/buffer_time.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/cache.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/cached.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/checkpoint_node_value.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/circuit_breaker.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/combine.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/concat.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/concat_map.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/constant.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/debounce.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/decorrelated_jitter.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/delay.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/derived.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/dispatch_messages.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/distill.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/distinct_until_changed.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/down_with_batch.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/effect.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/element_at.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/empty.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/exhaust_map.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/exponential.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/fallback.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/fibonacci.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/filter.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/find.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/first.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/flat_map.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/for_each.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/from_async_iter.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/from_awaitable.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/from_cron.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/from_event_emitter.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/from_fs_watch.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/from_git_hook.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/from_http.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/from_iter.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/from_mcp.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/from_timer.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/from_webhook.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/from_websocket.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/gate.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/interval.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/is_batching.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/is_phase2_message.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/last.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/linear.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/log_slice.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/map.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/merge.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/never.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/node.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/of.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/operator.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/pairwise.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/pausable.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/pipe.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/producer.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/propagates_to_meta.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/pubsub.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/race.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/rate_limiter.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/reactive_index.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/reactive_list.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/reactive_log.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/reactive_map.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/reduce.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/repeat.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/replay.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/rescue.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/resolve_backoff_preset.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/restore_graph_checkpoint.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/retry.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/sample.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/save_graph_checkpoint.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/scan.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/share.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/skip.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/start_with.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/state.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/subscribe.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/switch_map.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/take.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/take_until.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/take_while.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/tap.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/throttle.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/throw_error.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/timeout.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/timeout_node.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/to_array.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/to_list.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/to_sse.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/to_websocket.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/token_bucket.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/token_tracker.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/valve.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/verifiable.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/window.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/window_count.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/window_time.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/with_breaker.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/with_latest_from.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/with_max_attempts.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/with_status.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/api/zip.md +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/index.mdx +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content/docs/lab/python.mdx +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/content.config.ts +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/env.d.ts +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/src/styles/custom.css +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/theme-prototypes.html +0 -0
- {graphrefly-0.18.0 → graphrefly-0.20.0}/website/tsconfig.json +0 -0
|
@@ -2,6 +2,56 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- version list -->
|
|
4
4
|
|
|
5
|
+
## v0.20.0 (2026-04-11)
|
|
6
|
+
|
|
7
|
+
### Chores
|
|
8
|
+
|
|
9
|
+
- Add integrations page
|
|
10
|
+
([`7f16d1b`](https://github.com/graphrefly/graphrefly-py/commit/7f16d1b9d9b5aadb5875e4bcb08e6decae469d1f))
|
|
11
|
+
|
|
12
|
+
- Update docs
|
|
13
|
+
([`0f2d779`](https://github.com/graphrefly/graphrefly-py/commit/0f2d779eeddcf9eec9ed339bcae4dbf6899363d3))
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
- Fix bug
|
|
18
|
+
([`e5f9cc4`](https://github.com/graphrefly/graphrefly-py/commit/e5f9cc440a854cde0aac1060f02121b85ce06831))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
## v0.19.0 (2026-04-10)
|
|
22
|
+
|
|
23
|
+
### Chores
|
|
24
|
+
|
|
25
|
+
- 9.0 initial test (needs revisit)
|
|
26
|
+
([`f47a8b4`](https://github.com/graphrefly/graphrefly-py/commit/f47a8b4d32e999d4f867465f678d576184fc111d))
|
|
27
|
+
|
|
28
|
+
### Features
|
|
29
|
+
|
|
30
|
+
- Add start
|
|
31
|
+
([`4a008dc`](https://github.com/graphrefly/graphrefly-py/commit/4a008dcda7704e14be12a7111b1d0b9188c736b3))
|
|
32
|
+
|
|
33
|
+
- Address optimization items
|
|
34
|
+
([`371f9e2`](https://github.com/graphrefly/graphrefly-py/commit/371f9e25e590ded4150fd6aed7e0de3933b8f0f3))
|
|
35
|
+
|
|
36
|
+
- Consolidate inspection tools
|
|
37
|
+
([`94aa9af`](https://github.com/graphrefly/graphrefly-py/commit/94aa9af3e3d793103496bfcc71a0a4bbb38f5ea3))
|
|
38
|
+
|
|
39
|
+
- Fix
|
|
40
|
+
([`222e66f`](https://github.com/graphrefly/graphrefly-py/commit/222e66f4d8954aa772534c9fc796eea3ba30a304))
|
|
41
|
+
|
|
42
|
+
- Fix async pytest
|
|
43
|
+
([`f25ae08`](https://github.com/graphrefly/graphrefly-py/commit/f25ae0885fd824320b498fd2d923f6e4f6ebb0f5))
|
|
44
|
+
|
|
45
|
+
- Fix inspection tool
|
|
46
|
+
([`09c90f2`](https://github.com/graphrefly/graphrefly-py/commit/09c90f273613fbae1fd7987397d85d4b6fabec8d))
|
|
47
|
+
|
|
48
|
+
- Fix retries and reingestions
|
|
49
|
+
([`55d1a3d`](https://github.com/graphrefly/graphrefly-py/commit/55d1a3d0beff848a6ecefb054e42d194050d5c37))
|
|
50
|
+
|
|
51
|
+
- Reusable harness patterns
|
|
52
|
+
([`84bafbb`](https://github.com/graphrefly/graphrefly-py/commit/84bafbb8476c1d00ae373bbc757d4bf813ce16c8))
|
|
53
|
+
|
|
54
|
+
|
|
5
55
|
## v0.18.0 (2026-04-08)
|
|
6
56
|
|
|
7
57
|
### Features
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# graphrefly-py
|
|
2
|
+
|
|
3
|
+
Python implementation of the GraphReFly reactive graph protocol.
|
|
4
|
+
|
|
5
|
+
**All operational docs (roadmap, optimizations, test guidance, docs guidance, skills, archive) live in `~/src/graphrefly-ts`.** See that repo's `CLAUDE.md` for the full agent context.
|
|
6
|
+
|
|
7
|
+
## Commands
|
|
8
|
+
|
|
9
|
+
uv workspace managed by mise. `mise trust && mise install` to set up uv. `uv sync` to install dependencies.
|
|
10
|
+
|
|
11
|
+
- Test: `uv run pytest`
|
|
12
|
+
- Lint: `uv run ruff check src/ tests/`
|
|
13
|
+
- Lint fix: `uv run ruff check --fix src/ tests/`
|
|
14
|
+
- Format: `uv run ruff format src/ tests/`
|
|
15
|
+
- Type check: `uv run mypy src/`
|
|
16
|
+
|
|
17
|
+
## Documentation workflow (critical)
|
|
18
|
+
|
|
19
|
+
- Follow cross-language standard in `~/src/graphrefly-ts/docs/docs-guidance.md`.
|
|
20
|
+
- `website/src/content/docs/api/*.md` pages are generated; do not hand-edit.
|
|
21
|
+
- For docs updates in this repo:
|
|
22
|
+
1. Update source docstrings.
|
|
23
|
+
2. Run `pnpm --dir website docs:gen`.
|
|
24
|
+
3. Validate with `pnpm --dir website docs:gen:check` and `pnpm --dir website sync-docs:check`.
|
|
25
|
+
- Keep `llms.txt` concise and source-oriented; avoid long static API inventories that drift.
|
|
26
|
+
|
|
27
|
+
## Package naming
|
|
28
|
+
|
|
29
|
+
- Distribution name: `graphrefly-py`
|
|
30
|
+
- Import path: `graphrefly`
|
|
31
|
+
|
|
32
|
+
## Layout
|
|
33
|
+
|
|
34
|
+
- `src/graphrefly/core/` — message protocol, `node` primitive, batch, sugar constructors (Phase 0)
|
|
35
|
+
- `src/graphrefly/graph/` — `Graph` container, describe/observe, snapshot (Phase 1+)
|
|
36
|
+
- `src/graphrefly/extra/` — operators, sources, data structures, resilience (Phase 2–3)
|
|
37
|
+
- `src/graphrefly/patterns/` — domain-layer APIs: orchestration, messaging, memory, AI, CQRS, reactive layout (Phase 4+)
|
|
38
|
+
- `src/graphrefly/compat/` — async runners: asyncio, trio (Phase 5+)
|
|
39
|
+
- `src/graphrefly/integrations/` — framework integrations: FastAPI (Phase 5+)
|
|
40
|
+
|
|
41
|
+
## Key references
|
|
42
|
+
|
|
43
|
+
| Doc | Location |
|
|
44
|
+
|-----|----------|
|
|
45
|
+
| Behavior spec | `~/src/graphrefly/GRAPHREFLY-SPEC.md` |
|
|
46
|
+
| Composition guide | `~/src/graphrefly/COMPOSITION-GUIDE.md` |
|
|
47
|
+
| Roadmap, optimizations, test/docs guidance, skills, archive | `~/src/graphrefly-ts/` (single source of truth) |
|
|
48
|
+
| Reactive collaboration harness (§9.0) — 7-stage loop, gate, `promptNode`, strategy model | `~/src/graphrefly-ts/archive/docs/SESSION-reactive-collaboration-harness.md` |
|
|
49
|
+
| Harness engineering strategy — 8 requirements, wave plan, MCP priority | `~/src/graphrefly-ts/archive/docs/SESSION-harness-engineering-strategy.md` |
|
|
50
|
+
| Marketing & positioning — pillars, wave plan, reply playbooks, blog plan | `~/src/graphrefly-ts/archive/docs/SESSION-marketing-promotion-strategy.md` |
|
|
51
|
+
| Predecessor (patterns, concurrency) | `~/src/callbag-recharge-py` (reference only, not spec) |
|
|
52
|
+
|
|
53
|
+
## Design invariants (spec §5.8–5.12)
|
|
54
|
+
|
|
55
|
+
1. **No polling.** Use reactive timer sources (`from_timer`, `from_cron`).
|
|
56
|
+
2. **No imperative triggers.** Use reactive `NodeInput` signals.
|
|
57
|
+
3. **No raw async primitives.** Async boundaries belong in sources and runners, not node fns.
|
|
58
|
+
4. **Central timer and `message_tier`.** Use `core/clock.py`; never hardcode type checks.
|
|
59
|
+
5. **Phase 4+ APIs must be developer-friendly.** No protocol internals in primary surface.
|
|
60
|
+
6. **Thread safety.** Per-subgraph `RLock`, per-node `_cache_lock`. Design for GIL and free-threaded Python.
|
|
61
|
+
7. **No `async def` in public APIs.** Return `Node[T]`, `Graph`, `None`, or plain synchronous values.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: graphrefly
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.20.0
|
|
4
4
|
Summary: Reactive harness layer for agent workflows. Describe automations in plain language, trace every decision, enforce policies, persist checkpoints. Zero dependencies.
|
|
5
5
|
Project-URL: Homepage, https://py.graphrefly.dev
|
|
6
6
|
Project-URL: Repository, https://github.com/graphrefly/graphrefly-py
|
|
@@ -25,15 +25,15 @@ Description-Content-Type: text/markdown
|
|
|
25
25
|
|
|
26
26
|
# GraphReFly
|
|
27
27
|
|
|
28
|
-
**Describe
|
|
28
|
+
**The reactive harness layer for agent workflows.** Describe in plain language, review visually, run persistently, trace every decision.
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
GraphReFly makes long-running human + LLM co-operation reactive, resumable, and causally explainable. State pushes downstream on change (no re-reading), nodes have lifecycles (not infinite append), and every decision has a traceable causal chain — the substrate underneath tools, agents, and personal automations.
|
|
31
31
|
|
|
32
32
|
[](https://pypi.org/project/graphrefly/)
|
|
33
33
|
[](./LICENSE)
|
|
34
34
|
[](https://pypi.org/project/graphrefly/)
|
|
35
35
|
|
|
36
|
-
[Docs](https://
|
|
36
|
+
[Docs](https://graphrefly.dev/py/) | [Spec](https://graphrefly.dev/spec/) | [TypeScript](https://graphrefly.dev) | [Python API](https://graphrefly.dev/py/api/)
|
|
37
37
|
|
|
38
38
|
---
|
|
39
39
|
|
|
@@ -72,6 +72,23 @@ count.push(3)
|
|
|
72
72
|
|
|
73
73
|
You describe what you need — an LLM composes a reactive graph (like SQL for data flows). The graph runs persistently, checkpoints its state, and traces every decision through a causal chain. Ask "why?" at any point and get a human-readable explanation from source to conclusion.
|
|
74
74
|
|
|
75
|
+
## Harness engineering coverage
|
|
76
|
+
|
|
77
|
+
The eight requirements of a production agent harness cluster into a handful of composed blocks that sit on top of the reactive graph primitives:
|
|
78
|
+
|
|
79
|
+
| Need | GraphReFly |
|
|
80
|
+
|---|---|
|
|
81
|
+
| Context & state | `persistent_state()` — `auto_checkpoint` + `snapshot` / `restore` + incremental diff |
|
|
82
|
+
| Agent memory | `agent_memory()` — `distill` + vectors + knowledge graph + tiers, OpenViking decay |
|
|
83
|
+
| Control flow & resilience | `resilient_pipeline()` — `rate_limiter → breaker → retry → timeout → fallback`, correct ordering built in |
|
|
84
|
+
| Execution & policy | `guarded_execution()` — Actor / Guard ABAC + `policy()` + `budget_gate` + scoped describe |
|
|
85
|
+
| Observability & causality | `graph_lens()` — reactive topology, health, flow, and `why(node)` causal chains as structured data |
|
|
86
|
+
| Human governance | `gate` — reactive `pending` / `is_open` with `approve` / `reject` / `modify(fn, n)` |
|
|
87
|
+
| Verification | Multi-model eval harness with regression gates |
|
|
88
|
+
| Continuous improvement | Strategy model: `root_cause × intervention → success_rate` |
|
|
89
|
+
|
|
90
|
+
The library computes structured facts reactively; LLMs and UIs render them. Natural language is never the library's job — which keeps the whole stack model-agnostic and testable.
|
|
91
|
+
|
|
75
92
|
## Why GraphReFly?
|
|
76
93
|
|
|
77
94
|
| | Redux / Zustand | RxPY | Pydantic AI | LangGraph | TC39 Signals | **GraphReFly** |
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# GraphReFly
|
|
2
2
|
|
|
3
|
-
**Describe
|
|
3
|
+
**The reactive harness layer for agent workflows.** Describe in plain language, review visually, run persistently, trace every decision.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
GraphReFly makes long-running human + LLM co-operation reactive, resumable, and causally explainable. State pushes downstream on change (no re-reading), nodes have lifecycles (not infinite append), and every decision has a traceable causal chain — the substrate underneath tools, agents, and personal automations.
|
|
6
6
|
|
|
7
7
|
[](https://pypi.org/project/graphrefly/)
|
|
8
8
|
[](./LICENSE)
|
|
9
9
|
[](https://pypi.org/project/graphrefly/)
|
|
10
10
|
|
|
11
|
-
[Docs](https://
|
|
11
|
+
[Docs](https://graphrefly.dev/py/) | [Spec](https://graphrefly.dev/spec/) | [TypeScript](https://graphrefly.dev) | [Python API](https://graphrefly.dev/py/api/)
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
|
@@ -47,6 +47,23 @@ count.push(3)
|
|
|
47
47
|
|
|
48
48
|
You describe what you need — an LLM composes a reactive graph (like SQL for data flows). The graph runs persistently, checkpoints its state, and traces every decision through a causal chain. Ask "why?" at any point and get a human-readable explanation from source to conclusion.
|
|
49
49
|
|
|
50
|
+
## Harness engineering coverage
|
|
51
|
+
|
|
52
|
+
The eight requirements of a production agent harness cluster into a handful of composed blocks that sit on top of the reactive graph primitives:
|
|
53
|
+
|
|
54
|
+
| Need | GraphReFly |
|
|
55
|
+
|---|---|
|
|
56
|
+
| Context & state | `persistent_state()` — `auto_checkpoint` + `snapshot` / `restore` + incremental diff |
|
|
57
|
+
| Agent memory | `agent_memory()` — `distill` + vectors + knowledge graph + tiers, OpenViking decay |
|
|
58
|
+
| Control flow & resilience | `resilient_pipeline()` — `rate_limiter → breaker → retry → timeout → fallback`, correct ordering built in |
|
|
59
|
+
| Execution & policy | `guarded_execution()` — Actor / Guard ABAC + `policy()` + `budget_gate` + scoped describe |
|
|
60
|
+
| Observability & causality | `graph_lens()` — reactive topology, health, flow, and `why(node)` causal chains as structured data |
|
|
61
|
+
| Human governance | `gate` — reactive `pending` / `is_open` with `approve` / `reject` / `modify(fn, n)` |
|
|
62
|
+
| Verification | Multi-model eval harness with regression gates |
|
|
63
|
+
| Continuous improvement | Strategy model: `root_cause × intervention → success_rate` |
|
|
64
|
+
|
|
65
|
+
The library computes structured facts reactively; LLMs and UIs render them. Natural language is never the library's job — which keeps the whole stack model-agnostic and testable.
|
|
66
|
+
|
|
50
67
|
## Why GraphReFly?
|
|
51
68
|
|
|
52
69
|
| | Redux / Zustand | RxPY | Pydantic AI | LangGraph | TC39 Signals | **GraphReFly** |
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# GraphReFly — Python package (`graphrefly`)
|
|
2
|
+
|
|
3
|
+
The reactive harness layer for agent workflows. Reactive graph runtime for human + LLM co-operation — causal tracing, persistent checkpoints, policy enforcement, zero runtime dependencies.
|
|
4
|
+
|
|
5
|
+
Keywords: harness engineering, agent harness, reactive graph, causal trace, explain_path, agent orchestration, LLM co-operation, human-in-the-loop, reactive middleware, universal reduction layer.
|
|
6
|
+
|
|
7
|
+
This file is an AI-oriented index for the Python repo. It points to source-of-truth docs and generated API references instead of maintaining a long, drift-prone inline API catalog.
|
|
8
|
+
|
|
9
|
+
## Canonical behavior and docs authority
|
|
10
|
+
|
|
11
|
+
- Behavior spec: `~/src/graphrefly/GRAPHREFLY-SPEC.md`
|
|
12
|
+
- Composition guidance: `~/src/graphrefly/COMPOSITION-GUIDE.md`
|
|
13
|
+
- Docs conventions (cross-language): `~/src/graphrefly-ts/docs/docs-guidance.md`
|
|
14
|
+
- Roadmap/state (cross-language): `~/src/graphrefly-ts/docs/roadmap.md`
|
|
15
|
+
- Testing standard (cross-language): `~/src/graphrefly-ts/docs/test-guidance.md`
|
|
16
|
+
|
|
17
|
+
## Active strategy docs (cross-language, live in graphrefly-ts)
|
|
18
|
+
|
|
19
|
+
- `~/src/graphrefly-ts/archive/docs/SESSION-reactive-collaboration-harness.md` — 7-stage reactive collaboration loop, gate port, `prompt_node`, `valve` rename, strategy model, `harness_loop()` factory
|
|
20
|
+
- `~/src/graphrefly-ts/archive/docs/SESSION-harness-engineering-strategy.md` — 8-requirement coverage, three-wave announcement plan, MCP Server priority, eval system design
|
|
21
|
+
- `~/src/graphrefly-ts/archive/docs/SESSION-marketing-promotion-strategy.md` — positioning pillars, wave plan, reply-marketing playbooks, competitive intel, prompt optimization algorithms, blog content plan
|
|
22
|
+
|
|
23
|
+
## Primary links
|
|
24
|
+
|
|
25
|
+
- Python docs: https://graphrefly.dev/py/
|
|
26
|
+
- Python API reference: https://graphrefly.dev/py/api/
|
|
27
|
+
- TypeScript docs: https://graphrefly.dev
|
|
28
|
+
|
|
29
|
+
## Package entry points
|
|
30
|
+
|
|
31
|
+
- Root: `from graphrefly import ...`
|
|
32
|
+
- Subpackages: `graphrefly.core`, `graphrefly.graph`, `graphrefly.extra`, `graphrefly.patterns`, `graphrefly.compat`, `graphrefly.integrations`
|
|
33
|
+
|
|
34
|
+
## Public API by subpackage
|
|
35
|
+
|
|
36
|
+
Full export lists live in the generated API pages under `website/src/content/docs/api/`. The descriptions below explain *what lives in each subpackage and why*, so an LLM reading this cold can navigate without consuming the drift-prone catalog. Python uses snake_case (`from_timer`, `switch_map`, `agent_loop`) — the behavior parity with TypeScript holds, the naming does not.
|
|
37
|
+
|
|
38
|
+
### `graphrefly.core` — `src/graphrefly/core/`
|
|
39
|
+
|
|
40
|
+
The minimal reactive substrate. One primitive (`node`) plus sugar constructors (`state`, `derived`, `producer`, `effect`, `pipe`, `dynamic_node`). Protocol internals: `MessageType` enum (`DATA`, `DIRTY`, `RESOLVED`, `COMPLETE`, `ERROR`, `TEARDOWN`, `INVALIDATE`, `PAUSE`, `RESUME`), dispatch (`dispatch_messages`, `is_phase2_message`), two-phase batch semantics (`batch`, `is_batching`, `partition_for_batch`, `down_with_batch`), guard / policy (`policy`, `policy_from_rules`, `compose_guards`, `GuardDenied`, `access_hint_for_guard`), actor identity (`Actor`, `system_actor`, `normalize_actor`), meta snapshots (`meta_snapshot`, `describe_node`), versioning (`create_versioning`, `advance_version`, `default_hash`, `is_v1`), and the central clock (`monotonic_ns`, `wall_clock_ns`). **Anything outside this package must not call `time.time_ns()` / `time.monotonic_ns()` directly — use `src/graphrefly/core/clock.py`.** Also holds the `Runner` protocol and default-runner resolution (`get_default_runner`, `set_default_runner`, `resolve_runner`) for asyncio/trio backends. Thread-safety invariants (per-subgraph `RLock`, per-node `_cache_lock`) are enforced here.
|
|
41
|
+
|
|
42
|
+
### `graphrefly.graph` — `src/graphrefly/graph/`
|
|
43
|
+
|
|
44
|
+
The `Graph` container: reactive node registry with `register` / `resolve` / `describe` / `observe` / `snapshot` / `diff` / `diagram` / `spy` / `mount` / `unmount` / `teardown`. `reachable(graph, start_path, options=None)` computes the dependency closure from a node path — foundation for `explain_path` (causal walkback) and persistence scope.
|
|
45
|
+
|
|
46
|
+
### `graphrefly.extra` — `src/graphrefly/extra/`
|
|
47
|
+
|
|
48
|
+
Everything reactive that isn't the primitive. Organized into focused modules:
|
|
49
|
+
|
|
50
|
+
- **Tier 1 operators** (`tier1.py`) — transform (`map`, `filter`, `scan`, `reduce`), limiting (`take`, `skip`, `take_while`, `take_until`), selection (`first`, `last`, `find`, `element_at`), utility (`start_with`, `tap`, `distinct_until_changed`, `pairwise`), combination (`combine`, `with_latest_from`), merging (`merge`, `zip`, `concat`, `race`).
|
|
51
|
+
- **Tier 2 operators** (`tier2.py`) — higher-order (`switch_map`, `concat_map`, `flat_map`, `exhaust_map`), timing (`debounce`, `throttle`, `delay`), sampling (`sample`, `audit`, `timeout`), buffering (`buffer`, `buffer_count`, `buffer_time`), windowing (`window`, `window_count`, `window_time`), `interval`, `repeat`, `pausable`, `rescue`. **Note:** the existing `gate(control)` here is a boolean control gate and is being renamed to `valve` per §9.0; the new human-approval `gate` lives under `patterns.orchestration`.
|
|
52
|
+
- **Sources** (`sources.py`) — synchronous (`of`, `empty`, `never`, `throw_error`, `from_iter`, `from_timer`, `from_cron`), async bridges (`from_awaitable`, `from_async_iter`), network / IO (`from_http`, `from_event_emitter`, `from_fs_watch`, `from_webhook`, `from_websocket`, `from_mcp`, `from_git_hook`), multicast / caching (`share`, `cached`, `replay`), collectors (`for_each`, `to_list`, `to_array`, `first_value_from`), sinks (`to_sse`, `to_websocket`). **All async boundaries must enter through sources** — node fns and operators never contain `async def` or raw awaitables.
|
|
53
|
+
- **Data structures** (`data_structures.py` + dedicated files) — `reactive_map`, `reactive_log` + `log_slice`, `reactive_index`, `reactive_list`, `pubsub`.
|
|
54
|
+
- **Resilience** (`resilience.py` + `backoff.py`) — `retry`, `circuit_breaker` + `CircuitOpenError`, `token_bucket`, `token_tracker`, `rate_limiter`, `with_breaker`, `with_status`; backoff strategies (`constant`, `linear`, `exponential`, `fibonacci`, `decorrelated_jitter`, `with_max_attempts`, `resolve_backoff_preset`).
|
|
55
|
+
- **Cron** (`cron.py`) — `parse_cron`, `matches_cron`.
|
|
56
|
+
- **Checkpoint** (`checkpoint.py`) — `Memory` / `Dict` / `File` / `Sqlite` adapters, `save_graph_checkpoint` / `restore_graph_checkpoint`, `checkpoint_node_value`. Auto-checkpoint is gated by `message_tier >= 3`. See `docs/ADAPTER-CONTRACT.md` for the adapter contract.
|
|
57
|
+
- **Composite** (`composite.py`) — `verifiable(source, opts)` wraps a node with verification tracking; `distill(raw, extract, opts)` compacts raw data into a budget-bounded summary (the reactive memory primitive that `agent_memory` plugs into).
|
|
58
|
+
- **Backpressure** (`backpressure.py`) — watermark controller for PAUSE/RESUME flow control.
|
|
59
|
+
|
|
60
|
+
### `graphrefly.patterns` — `src/graphrefly/patterns/` (Phase 4+ domain APIs)
|
|
61
|
+
|
|
62
|
+
Developer-facing factories that compose primitives into recognizable patterns. **Protocol internals never surface here** — sensible defaults, minimal boilerplate, clear errors. Python uses `with batch():` context managers instead of TypeScript's callback form, and `Node.__or__` maps to the TS `pipe()`.
|
|
63
|
+
|
|
64
|
+
- **Orchestration** (`orchestration.py`) — `pipeline`, `task`, `branch`, the boolean control `gate` *(being renamed to `valve`)*, `approval`, `for_each`, `join`, `loop`, `sub_pipeline`, `sensor`, `wait`, `on_failure`. The §9.0 port of the human-approval gate (`pending` / `count` / `is_open` reactive nodes and `approve` / `reject` / `modify(fn, n=1)` / `open` / `close` methods) also lands here.
|
|
65
|
+
- **Messaging** (`messaging.py`) — `topic` (TopicGraph), `subscription` (SubscriptionGraph with cursor / `pull` / `ack`), `job_queue`, `job_flow`, `topic_bridge`. These are the primitives the §9.0 harness loop uses for static-topology sinks and cursor-driven readers.
|
|
66
|
+
- **Memory** (`memory.py`) — `collection`, `light_collection`, `vector_index`, `knowledge_graph`, `decay(source, opts)` (OpenViking exponential decay, 7-day default half-life). `decay` is the priority-scoring primitive for the §9.0 QUEUE stage.
|
|
67
|
+
- **AI** (`ai.py`) — `from_llm`, `streaming_prompt_node`, `stream_extractor`, `chat_stream`, `tool_registry`, `system_prompt_builder`, `llm_extractor`, `llm_consolidator`, `agent_memory` (default: distill + vectors + KG + tiers), `agent_loop` (ReAct inner loop), `knobs_as_tools`, `gauges_as_context`, `validate_graph_def`. The §9.0 `prompt_node` factory (wraps any LLM call in a derived node with retry / cache / structured output) also lives here and is what plugs into `distill` as `extract_fn` / `consolidate_fn`.
|
|
68
|
+
- **CQRS** (`cqrs.py`) — `cqrs(name, opts=None)` creates a commands / events / projections / sagas Graph.
|
|
69
|
+
- **Reactive layout** (`reactive_layout/`) — `reactive_layout`, `reactive_block_layout`, `analyze_and_measure`, `compute_line_breaks`, `compute_char_positions`. DOM-free Knuth-Plass line breaking as a `state → derived` graph.
|
|
70
|
+
|
|
71
|
+
### `graphrefly.compat` — `src/graphrefly/compat/`
|
|
72
|
+
|
|
73
|
+
Async runner bindings. `AsyncioRunner` wraps a Graph in an asyncio event loop; `TrioRunner` does the same for trio. These are the only places allowed to orchestrate coroutines — node fns and operators stay synchronous. Runners resolve via `graphrefly.core.resolve_runner` so downstream code can inject a specific backend.
|
|
74
|
+
|
|
75
|
+
### `graphrefly.integrations` — `src/graphrefly/integrations/`
|
|
76
|
+
|
|
77
|
+
Framework integrations. `fastapi.GraphReflyRouter(graph)` exposes `GET /describe`, `GET /snapshot`, `WS /observe` over a registered `Graph`. Keep these thin — all logic belongs in `core` / `extra` / `patterns`.
|
|
78
|
+
|
|
79
|
+
### Design docs that back the above
|
|
80
|
+
|
|
81
|
+
- Phase 4+ factory authors must read `~/src/graphrefly/COMPOSITION-GUIDE.md` before composing primitives — covers lazy activation, subscription ordering, null guards, feedback cycles, `prompt_node` SENTINEL, wiring order.
|
|
82
|
+
- Backlog / anti-patterns / deferred follow-ups: `~/src/graphrefly-ts/docs/optimizations.md` (cross-language).
|
|
83
|
+
- Historical design decisions and parity notes: `~/src/graphrefly-ts/archive/optimizations/` (cross-language).
|
|
84
|
+
|
|
85
|
+
## Generated docs workflow
|
|
86
|
+
|
|
87
|
+
Python API pages are generated from docstrings:
|
|
88
|
+
|
|
89
|
+
- Generator: `website/scripts/gen_api_docs.py`
|
|
90
|
+
- Output: `website/src/content/docs/api/*.md`
|
|
91
|
+
- Commands:
|
|
92
|
+
- `pnpm --dir website docs:gen`
|
|
93
|
+
- `pnpm --dir website docs:gen:check`
|
|
94
|
+
- `pnpm --dir website sync-docs`
|
|
95
|
+
- `pnpm --dir website sync-docs:check`
|
|
96
|
+
|
|
97
|
+
Do not hand-edit generated API pages.
|
|
98
|
+
|
|
99
|
+
## Core design invariants
|
|
100
|
+
|
|
101
|
+
- No polling; propagation is reactive.
|
|
102
|
+
- No imperative triggers outside graph/message flow.
|
|
103
|
+
- No raw async primitives inside reactive layer logic.
|
|
104
|
+
- Use `src/graphrefly/core/clock.py` and `message_tier` helpers for timing/tier semantics.
|
|
105
|
+
- Keep Phase 4+ surfaces developer-oriented (hide protocol internals from primary API docs).
|
|
106
|
+
- Thread safety: per-subgraph `RLock`, per-node `_cache_lock`; no `async def` / `Awaitable` in public APIs.
|
|
107
|
+
|
|
108
|
+
## Harness engineering coverage
|
|
109
|
+
|
|
110
|
+
GraphReFly covers the 8 requirements of a production agent harness — context/state control, execution boundary, control flow, verification, observability, policy/safety, human governance, continuous improvement. Mapping lives in `README.md` and `~/src/graphrefly-ts/archive/docs/SESSION-harness-engineering-strategy.md`.
|
|
111
|
+
|
|
112
|
+
## Reactive collaboration loop (§9.0)
|
|
113
|
+
|
|
114
|
+
`harness_loop()` wires a static topology: INTAKE → TRIAGE → QUEUE → GATE → EXECUTE → VERIFY → REFLECT. Static topology + flowing data (the Kafka insight). Humans steer via `gate.modify(fn, n)`. Strategy model (`root_cause × intervention → success_rate`) feeds TRIAGE routing. Design source: `~/src/graphrefly-ts/archive/docs/SESSION-reactive-collaboration-harness.md`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "graphrefly"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.20.0"
|
|
4
4
|
description = "Reactive harness layer for agent workflows. Describe automations in plain language, trace every decision, enforce policies, persist checkpoints. Zero dependencies."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
@@ -90,6 +90,7 @@ quote-style = "double"
|
|
|
90
90
|
|
|
91
91
|
[tool.pytest.ini_options]
|
|
92
92
|
testpaths = ["tests"]
|
|
93
|
+
pythonpath = ["tests"]
|
|
93
94
|
asyncio_mode = "auto"
|
|
94
95
|
addopts = "-m 'not benchmark'"
|
|
95
96
|
markers = [
|
|
@@ -37,7 +37,6 @@ from graphrefly.core import (
|
|
|
37
37
|
defer_down,
|
|
38
38
|
defer_set,
|
|
39
39
|
derived,
|
|
40
|
-
describe_node,
|
|
41
40
|
dispatch_messages,
|
|
42
41
|
down_with_batch,
|
|
43
42
|
dynamic_node,
|
|
@@ -47,7 +46,6 @@ from graphrefly.core import (
|
|
|
47
46
|
is_batching,
|
|
48
47
|
is_phase2_message,
|
|
49
48
|
is_v1,
|
|
50
|
-
meta_snapshot,
|
|
51
49
|
monotonic_ns,
|
|
52
50
|
node,
|
|
53
51
|
normalize_actor,
|
|
@@ -74,7 +72,6 @@ from graphrefly.graph import (
|
|
|
74
72
|
GraphDiffResult,
|
|
75
73
|
GraphObserveSource,
|
|
76
74
|
ObserveResult,
|
|
77
|
-
SpyHandle,
|
|
78
75
|
TraceEntry,
|
|
79
76
|
reachable,
|
|
80
77
|
)
|
|
@@ -93,7 +90,6 @@ __all__ = [
|
|
|
93
90
|
"NodeVersionInfo",
|
|
94
91
|
"ObserveResult",
|
|
95
92
|
"PATH_SEP",
|
|
96
|
-
"SpyHandle",
|
|
97
93
|
"TraceEntry",
|
|
98
94
|
"V0",
|
|
99
95
|
"V1",
|
|
@@ -138,13 +134,11 @@ __all__ = [
|
|
|
138
134
|
"compose_guards",
|
|
139
135
|
"defer_down",
|
|
140
136
|
"defer_set",
|
|
141
|
-
"describe_node",
|
|
142
137
|
"dispatch_messages",
|
|
143
138
|
"down_with_batch",
|
|
144
139
|
"ensure_registered",
|
|
145
140
|
"is_batching",
|
|
146
141
|
"is_phase2_message",
|
|
147
|
-
"meta_snapshot",
|
|
148
142
|
"node",
|
|
149
143
|
"normalize_actor",
|
|
150
144
|
"partition_for_batch",
|
|
@@ -11,6 +11,7 @@ import asyncio
|
|
|
11
11
|
import contextlib
|
|
12
12
|
from typing import TYPE_CHECKING, Any
|
|
13
13
|
|
|
14
|
+
from graphrefly.core.node import NO_VALUE
|
|
14
15
|
from graphrefly.core.protocol import MessageType
|
|
15
16
|
|
|
16
17
|
if TYPE_CHECKING:
|
|
@@ -101,13 +102,11 @@ async def first_value_from_async(source: Node[Any]) -> Any:
|
|
|
101
102
|
assert value == 42
|
|
102
103
|
"""
|
|
103
104
|
# Fast path: already settled with a cached value.
|
|
104
|
-
#
|
|
105
|
-
# ``is not None`` doubles as the "has value" sentinel while still
|
|
106
|
-
# returning falsy values like ``0``, ``False``, ``""``.
|
|
105
|
+
# Uses NO_VALUE sentinel so ``None`` as a real domain value is not skipped.
|
|
107
106
|
status = source.status
|
|
108
107
|
if status in ("settled", "resolved"):
|
|
109
|
-
v = source
|
|
110
|
-
if v is not
|
|
108
|
+
v = getattr(source, "_cached", NO_VALUE)
|
|
109
|
+
if v is not NO_VALUE:
|
|
111
110
|
return v
|
|
112
111
|
|
|
113
112
|
loop = asyncio.get_running_loop()
|
|
@@ -163,12 +162,11 @@ async def settled(source: Node[Any]) -> Any:
|
|
|
163
162
|
value = await settled(b)
|
|
164
163
|
assert value == 20
|
|
165
164
|
"""
|
|
166
|
-
# Fast path: already settled
|
|
167
|
-
# ``is not None`` rationale).
|
|
165
|
+
# Fast path: already settled — uses NO_VALUE sentinel so ``None`` is not skipped.
|
|
168
166
|
status = source.status
|
|
169
167
|
if status in ("settled", "resolved"):
|
|
170
|
-
v = source
|
|
171
|
-
if v is not
|
|
168
|
+
v = getattr(source, "_cached", NO_VALUE)
|
|
169
|
+
if v is not NO_VALUE:
|
|
172
170
|
return v
|
|
173
171
|
|
|
174
172
|
loop = asyncio.get_running_loop()
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
|
+
import threading
|
|
6
7
|
from typing import TYPE_CHECKING, Any
|
|
7
8
|
|
|
8
9
|
if TYPE_CHECKING:
|
|
@@ -29,10 +30,12 @@ class AsyncioRunner:
|
|
|
29
30
|
asyncio.run(main())
|
|
30
31
|
"""
|
|
31
32
|
|
|
32
|
-
__slots__ = ("_loop",)
|
|
33
|
+
__slots__ = ("_loop", "_scheduled", "_completed")
|
|
33
34
|
|
|
34
35
|
def __init__(self, loop: asyncio.AbstractEventLoop) -> None:
|
|
35
36
|
self._loop = loop
|
|
37
|
+
self._scheduled = 0
|
|
38
|
+
self._completed = 0
|
|
36
39
|
|
|
37
40
|
@classmethod
|
|
38
41
|
def from_running(cls) -> AsyncioRunner:
|
|
@@ -50,11 +53,12 @@ class AsyncioRunner:
|
|
|
50
53
|
on_error: Callable[[BaseException], None],
|
|
51
54
|
) -> Callable[[], None]:
|
|
52
55
|
task: asyncio.Task[Any] | None = None
|
|
53
|
-
cancelled =
|
|
56
|
+
cancelled = threading.Event()
|
|
54
57
|
|
|
55
58
|
def _create_task() -> None:
|
|
56
59
|
nonlocal task
|
|
57
|
-
if cancelled:
|
|
60
|
+
if cancelled.is_set():
|
|
61
|
+
self._completed += 1
|
|
58
62
|
coro.close()
|
|
59
63
|
return
|
|
60
64
|
|
|
@@ -71,19 +75,42 @@ class AsyncioRunner:
|
|
|
71
75
|
on_error(err)
|
|
72
76
|
else:
|
|
73
77
|
on_result(result)
|
|
78
|
+
finally:
|
|
79
|
+
self._completed += 1
|
|
74
80
|
|
|
75
81
|
task = self._loop.create_task(_wrapper())
|
|
76
82
|
|
|
77
|
-
#
|
|
78
|
-
self.
|
|
83
|
+
# Increment eagerly on the calling thread so __repr__ is always consistent.
|
|
84
|
+
self._scheduled += 1
|
|
85
|
+
try:
|
|
86
|
+
self._loop.call_soon_threadsafe(_create_task)
|
|
87
|
+
except RuntimeError:
|
|
88
|
+
# Loop closed — cannot schedule; close the coroutine and balance the counter.
|
|
89
|
+
self._completed += 1
|
|
90
|
+
coro.close()
|
|
79
91
|
|
|
80
92
|
def cancel() -> None:
|
|
81
|
-
|
|
82
|
-
cancelled = True
|
|
93
|
+
cancelled.set()
|
|
83
94
|
if task is not None:
|
|
84
95
|
task.cancel()
|
|
85
96
|
|
|
86
97
|
return cancel
|
|
87
98
|
|
|
99
|
+
def would_block_deadlock(self) -> bool:
|
|
100
|
+
"""True if blocking the current thread would starve this runner's loop."""
|
|
101
|
+
try:
|
|
102
|
+
loop = asyncio.get_running_loop()
|
|
103
|
+
except RuntimeError:
|
|
104
|
+
return False
|
|
105
|
+
return loop is self._loop
|
|
106
|
+
|
|
107
|
+
def __repr__(self) -> str:
|
|
108
|
+
pending = self._scheduled - self._completed
|
|
109
|
+
running = self._loop.is_running()
|
|
110
|
+
return (
|
|
111
|
+
f"AsyncioRunner(scheduled={self._scheduled}, completed={self._completed}, "
|
|
112
|
+
f"pending={pending}, loop_running={running})"
|
|
113
|
+
)
|
|
114
|
+
|
|
88
115
|
|
|
89
116
|
__all__ = ["AsyncioRunner"]
|
|
@@ -8,6 +8,7 @@ Usage::
|
|
|
8
8
|
from graphrefly.compat.trio_runner import TrioRunner
|
|
9
9
|
from graphrefly.core.runner import set_default_runner
|
|
10
10
|
|
|
11
|
+
|
|
11
12
|
async def main():
|
|
12
13
|
async with trio.open_nursery() as nursery:
|
|
13
14
|
runner = TrioRunner(nursery)
|
|
@@ -19,6 +20,7 @@ Usage::
|
|
|
19
20
|
|
|
20
21
|
from __future__ import annotations
|
|
21
22
|
|
|
23
|
+
import threading
|
|
22
24
|
from typing import TYPE_CHECKING, Any
|
|
23
25
|
|
|
24
26
|
if TYPE_CHECKING:
|
|
@@ -34,10 +36,12 @@ class TrioRunner:
|
|
|
34
36
|
Cancel scopes provide best-effort cancellation.
|
|
35
37
|
"""
|
|
36
38
|
|
|
37
|
-
__slots__ = ("_nursery",)
|
|
39
|
+
__slots__ = ("_nursery", "_scheduled", "_completed")
|
|
38
40
|
|
|
39
41
|
def __init__(self, nursery: trio.Nursery) -> None:
|
|
40
42
|
self._nursery = nursery
|
|
43
|
+
self._scheduled = 0
|
|
44
|
+
self._completed = 0
|
|
41
45
|
|
|
42
46
|
def schedule(
|
|
43
47
|
self,
|
|
@@ -48,11 +52,12 @@ class TrioRunner:
|
|
|
48
52
|
import trio as _trio
|
|
49
53
|
|
|
50
54
|
cancel_scope = _trio.CancelScope()
|
|
51
|
-
cancelled =
|
|
55
|
+
cancelled = threading.Event()
|
|
52
56
|
|
|
53
57
|
async def _wrapper() -> None:
|
|
54
58
|
with cancel_scope:
|
|
55
|
-
if cancelled:
|
|
59
|
+
if cancelled.is_set():
|
|
60
|
+
self._completed += 1
|
|
56
61
|
coro.close()
|
|
57
62
|
return
|
|
58
63
|
try:
|
|
@@ -67,15 +72,34 @@ class TrioRunner:
|
|
|
67
72
|
on_error(err)
|
|
68
73
|
else:
|
|
69
74
|
on_result(result)
|
|
75
|
+
finally:
|
|
76
|
+
self._completed += 1
|
|
70
77
|
|
|
78
|
+
self._scheduled += 1
|
|
71
79
|
self._nursery.start_soon(_wrapper)
|
|
72
80
|
|
|
73
81
|
def cancel() -> None:
|
|
74
|
-
|
|
75
|
-
cancelled = True
|
|
82
|
+
cancelled.set()
|
|
76
83
|
cancel_scope.cancel()
|
|
77
84
|
|
|
78
85
|
return cancel
|
|
79
86
|
|
|
87
|
+
def would_block_deadlock(self) -> bool:
|
|
88
|
+
"""True if blocking the current thread would starve the trio nursery."""
|
|
89
|
+
try:
|
|
90
|
+
import trio as _trio
|
|
91
|
+
|
|
92
|
+
_trio.lowlevel.current_trio_token()
|
|
93
|
+
return True
|
|
94
|
+
except RuntimeError:
|
|
95
|
+
return False
|
|
96
|
+
|
|
97
|
+
def __repr__(self) -> str:
|
|
98
|
+
pending = self._scheduled - self._completed
|
|
99
|
+
return (
|
|
100
|
+
f"TrioRunner(scheduled={self._scheduled}, completed={self._completed}, "
|
|
101
|
+
f"pending={pending})"
|
|
102
|
+
)
|
|
103
|
+
|
|
80
104
|
|
|
81
105
|
__all__ = ["TrioRunner"]
|
|
@@ -17,7 +17,6 @@ from graphrefly.core.guard import (
|
|
|
17
17
|
record_mutation,
|
|
18
18
|
system_actor,
|
|
19
19
|
)
|
|
20
|
-
from graphrefly.core.meta import describe_node, meta_snapshot
|
|
21
20
|
from graphrefly.core.node import (
|
|
22
21
|
NO_VALUE,
|
|
23
22
|
Node,
|
|
@@ -112,7 +111,6 @@ __all__ = [
|
|
|
112
111
|
"compose_guards",
|
|
113
112
|
"defer_down",
|
|
114
113
|
"defer_set",
|
|
115
|
-
"describe_node",
|
|
116
114
|
"dispatch_messages",
|
|
117
115
|
"down_with_batch",
|
|
118
116
|
"ensure_registered",
|
|
@@ -121,7 +119,6 @@ __all__ = [
|
|
|
121
119
|
"is_terminal_message",
|
|
122
120
|
"message_tier",
|
|
123
121
|
"propagates_to_meta",
|
|
124
|
-
"meta_snapshot",
|
|
125
122
|
"node",
|
|
126
123
|
"normalize_actor",
|
|
127
124
|
"partition_for_batch",
|