Active Graph

repository·main·Indexed 19 days ago

https://github.com/yoheinakajima/activegraph

An event-sourced reactive graph runtime for long-running, auditable, and agentic systems. Version 1.10.0 provides a shared graph and append-only event log to enable behaviors to react to changes, supporting forking, replaying, and diffing of agentic runs. It features a dual-layered type system, LLM provider integration (Anthropic, OpenAI), and primitives such as relation-behaviors, patches, and frames for managing bounded contexts.

Tokens
132.1K
Snippets
331
Records
592
Agent score
63%

What's inside activegraph

  1. Overview of ActiveGraph Public API Surface

    main

    The activegraph public API (Ring 0) is fully documented and includes core abstractions for graph management, event handling, and runtime execution. Key components include:

    • Core Graph & Storage: Graph, GraphStore, InMemoryGraphStore, FalkorDBGraphStore, and open_store for managing graph data.
    • Events & Sinks: Event, EventStore, EventSink, InMemoryEventStore, SQLiteEventStore, and JSONLEventSink for event-driven state changes.
    • Behaviors & Tools: Behavior, Tool, LLMBehavior, and relation_behavior for defining how the graph reacts to inputs and interacts with external systems.
    • Runtime & Configuration: Runtime, RuntimeContextRequiredError, ConfigurationError, and Clock for managing execution lifecycles.
    • Packs: Pack, DiscoveredPack, and pack for modular extensions.
    • Errors: A comprehensive suite of error types such as ActiveGraphError, ExecutionError, PackError, and ToolError.
  2. Navigate the Active Graph API reference

    main

    The Active Graph API is organized into topical modules. You can find detailed documentation for specific components by navigating to their respective modules in the sidebar. The API reference covers the following core areas:

    • Runtime: The runtime loop, frames, budget, and status.
    • Graph: Graph primitives including objects, relations, patches, views, and events.
    • Behaviors: Behavior decorators and base classes.
    • Tools: The @tool decorator and tool primitives.
    • Store: Event stores (in-memory, SQLite, Postgres), URL parsing, and migration.
    • Packs: Pack format primitives.
    • Errors: The ActiveGraphError hierarchy.
    • Observability: Accepted-event sinks, the metrics protocol, and shipped backends.
    • Diligence pack: The v0.9 reference pack.
  3. Understand the Active Graph v1.3 Roadmap and Versioning

    main

    The ROADMAP.md file serves as a historical scoping document for the v1.3 development cycle. It outlines planned features, quality improvements, and architectural shifts.

    Key Versioning Context:

    • Current Status: The v1.3–v1.7 line has largely shipped. For live status of what is currently implemented, refer to CHANGELOG.md and CONTRACT.md.
    • Roadmap Markers:
      • MUST: Required for the scoped version path.
      • SHOULD: Intended if it does not block release-quality MUST items.
      • DEFERRED: Recognized but intentionally not a blocker for the current cycle.
    • Future Ideas: Valid candidates for future development that are not currently blocking the roadmap are maintained in FUTURE_IDEAS.md.
  4. Use the Diligence pack for investment diligence tasks

    main

    The Diligence pack is a reference implementation for investment diligence workflows. It is designed to handle tasks such as analyzing claims, evaluating evidence, identifying contradictions, assessing risks, and generating memos.

    Key features:

    • LLM-backed behaviors: Three core behaviors are powered by Large Language Models.
    • Reproducible fixtures: The pack includes built-in fixtures to allow for reproducible demonstrations without requiring immediate LLM integration.

    For developers looking to build their own packs, this pack serves as a primary reference implementation. See the Authoring packs guide for detailed instructions.

  5. What is Forking in Active Graph?

    main

    A fork is a branch from a parent run at a specific event. It shares the parent's event log up to a specified cutoff point (the fork point) and then maintains its own independent log.

    Key Characteristics

    • Shared Lineage: The fork inherits all events from the parent up to the --at-event cutoff. This cutoff is inclusive.
    • Cheap Execution: Using the shared-lineage model and the cache layer, the framework replays the shared prefix against the fork's in-memory graph without re-executing LLM or tool calls. This makes forking efficient.
    • Independence: Forks can be configured differently, run with different packs, and inspected side-by-side without affecting the parent's state.
    • Verifiability: The fork's lineage is verifiable via the event log.
  6. What is a View in Active Graph

    main

    A view is a scoped, read-only representation of the graph used by behaviors to observe state. In the Active Graph behavior model, views are the read-side counterpart to patches (the write-side).

    Key characteristics:

    • Per-invocation computation: Views are computed fresh for every behavior execution. They are not cached across behavior fires, ensuring behaviors operate on a consistent snapshot of the graph at the time of the event.
    • Read-only contract: Views provide access to graph data via methods like objects(), relations(), and get_object(), but they cannot be used to mutate the graph. Mutations must be performed through the graph object or ctx.propose_object.
    • Cost control: For LLM-based behaviors, views are critical for efficiency. The view determines what data is serialized and sent to the LLM prompt; a narrower view results in smaller prompts and lower token costs.
  7. Understand the difference between CorruptedEventPayloadError and NonSerializableEventError

    main

    It is important to distinguish between these two error types to identify when the failure occurred in the data lifecycle:

    • CorruptedEventPayloadError: Occurs at decode time. It fires when the framework attempts to read bytes from the disk (via Runtime.load, iter_events, activegraph inspect, or activegraph migrate) and finds that the payload column is not valid JSON.
    • NonSerializableEventError: Occurs at encode time. It fires when a Python value is being written to the store and cannot be converted into a JSON-compatible format.
  8. Understand the audit trail and event semantics of a promote operation

    main

    When a promote operation is applied to a parent graph, it emits a sequence of events to maintain a clear audit trail and causal history:

    1. promote.applied (Marker Event): A single marker event with actor="runtime". It contains metadata about the promotion:

      • from_run: The ID of the fork run being promoted.
      • forked_at_event: The event ID where the fork originated.
      • computed_against: The state version used to compute the promotion plan.
      • objects_created, objects_patched, objects_removed: Lists of affected object IDs.
      • relations_created, relations_removed: Lists of affected relation IDs.
      • warnings: Any warnings encountered during the process.
    2. Delta Mutation Events: Following the marker, the system emits ordinary mutation events (creates, patches, removes) with actor="promote:<fork_run_id>". Each of these events includes a caused_by field pointing to the promote.applied marker event. This allows trace.causal_chain() to visually group the adoption as a single block and trace any promoted object back to the specific promotion event.

    Note on Object Patches: Patches use op="replace" with the fork's full data. This ensures the post-promote state is byte-equal to the fork state, allowing for the correct representation of removed fields which a standard update might not express.

  9. Use relation-behaviors for edge-based coordination

    main

    A relation_behavior is a primitive that implements coordination logic directly on a graph edge rather than on the endpoints. It is triggered by events that touch one of the edge's endpoints.

    To use it, decorate a function with @relation_behavior and specify the relation_type it should monitor and the on event types it should react to.

    from activegraph import relation_behavior
    
    @relation_behavior(name="unblock", relation_type="depends_on", on=["task.completed"])
    def unblock(relation, event, graph, ctx):
        # 'relation' provides access to .source and .target
        if event.payload["task_id"] == relation.source:
            graph.patch_object(relation.target, {"status": "open"})
  10. Understand FalkorDB relation and node modeling

    main

    When using FalkorDBGraphStore, the graph schema is optimized for native traversal using Cypher:

    • Relations: Stored as native edges with a single fixed relationship type :AGRelation. The specific relation kind is stored in the type edge property. This allows for efficient traversal using property filters: MATCH (s)-[r:AGRelation {type: $t}]->(t).
    • Nodes: All endpoints carry the :AGNode label. Real objects carry an additional :AGObject label.
    • Placeholders: A 'dangling' endpoint (a node referenced by a relation but not existing as a full object) is identified as :AGNode AND NOT :AGObject. There is no explicit :AGPlaceholder label; nodes are demoted to placeholders automatically when their associated object is removed.
  11. Understand Fork Cache Pre-population Symmetry

    main

    In Active Graph v1.1, the behavior of forking a runtime is being unified to ensure consistent LLM cache usage between in-process forks and persistent forks.

    • In-process forks: Using Runtime.fork(at_event=...) automatically pre-populates the LLM cache from the parent's recorded llm.responded events up to the fork point.
    • Persistent forks: Previously, loading a fork via SQLiteEventStore.fork_run() followed by Runtime.load(..., run_id=<fork>) would only see the fork's own events, causing it to replay LLM behaviors against the live provider instead of using the cache.
    • v1.1 Improvement: Runtime.load for a fork run will now look up the parent's forked_at_event_id and pre-populate the cache from the parent's events through that point, ensuring symmetry between in-process and persistent forks.