graph

Graph service for committing documents and segments to the context graph

GraphService

This service wraps the SQLite graph plugin to provide storage for the decomposed document structure. It converts TextSegment objects into graph nodes and edges, then commits them to the context graph.


GraphService


def GraphService(
    plugin_manager:PluginManager, # Plugin manager for accessing graph plugin
    plugin_name:str='cjm-graph-plugin-sqlite', # Name of the graph plugin
):

Service for committing structure to context graph.

Tests

The following cells demonstrate the GraphService with the SQLite Graph plugin.

# Test GraphService with SQLite Graph plugin
from pathlib import Path
from cjm_plugin_system.core.manager import PluginManager

# Calculate project root from notebook location (nbs/services/ -> project root)
project_root = Path.cwd().parent.parent
manifests_dir = project_root / ".cjm" / "manifests"

# Create plugin manager with explicit search path
manager = PluginManager(search_paths=[manifests_dir])
manager.discover_manifests()

print(f"Discovered {len(manager.discovered)} plugins from {manifests_dir}")

# Check if Graph plugin is available
graph_meta = manager.get_discovered_meta("cjm-graph-plugin-sqlite")
if graph_meta:
    print(f"Found plugin: {graph_meta.name} v{graph_meta.version}")
else:
    print("SQLite Graph plugin not found - install via plugins.yaml")
[PluginManager] Discovered manifest: cjm-graph-plugin-sqlite from /mnt/SN850X_8TB_EXT4/Projects/GitHub/cj-mills/cjm-transcript-review/.cjm/manifests/cjm-graph-plugin-sqlite.json
Discovered 1 plugins from /mnt/SN850X_8TB_EXT4/Projects/GitHub/cj-mills/cjm-transcript-review/.cjm/manifests
Found plugin: cjm-graph-plugin-sqlite v0.0.3
# Initialize and test GraphService
if graph_meta:
    # Load the plugin
    # manager.load_plugin(graph_meta)
    manager.load_plugin(graph_meta, {
        "db_path": graph_meta.manifest.get("db_path")
    })
    
    graph_service = GraphService(manager)
    print(f"Plugin available: {graph_service.is_available()}")
[PluginManager] Launching worker for cjm-graph-plugin-sqlite...
[cjm-graph-plugin-sqlite] Starting worker on port 57931...
[cjm-graph-plugin-sqlite] Logs: /home/innom-dt/.cjm/logs/cjm-graph-plugin-sqlite.log
[PluginManager] HTTP Request: GET http://127.0.0.1:57931/health "HTTP/1.1 200 OK"
[PluginManager] HTTP Request: POST http://127.0.0.1:57931/initialize "HTTP/1.1 200 OK"
[PluginManager] Loaded plugin: cjm-graph-plugin-sqlite
[cjm-graph-plugin-sqlite] Worker ready.
Plugin available: True
# Test committing a document to the graph with separate text segments and VAD chunks
from cjm_transcript_segmentation.models import TextSegment
from cjm_transcript_vad_align.models import VADChunk

if graph_meta and graph_service.is_available():
    # Create text segments (no time fields)
    text_segments = [
        TextSegment(
            index=0,
            text="Laying Plans",
            source_id="job_123",
            source_provider_id="test-plugin",
            start_char=0,
            end_char=12,
        ),
        TextSegment(
            index=1,
            text="Sun Tzu said: The art of war is of vital importance to the state.",
            source_id="job_123",
            source_provider_id="test-plugin",
            start_char=13,
            end_char=79,
        ),
        TextSegment(
            index=2,
            text="It is a matter of life and death, a road either to safety or to ruin.",
            source_id="job_123",
            source_provider_id="test-plugin",
            start_char=80,
            end_char=150,
        )
    ]
    
    # Create VAD chunks with timing (1:1 with segments)
    vad_chunks = [
        VADChunk(index=0, start_time=0.0, end_time=1.5),
        VADChunk(index=1, start_time=1.5, end_time=5.0),
        VADChunk(index=2, start_time=5.0, end_time=9.0),
    ]
    
    print(f"Committing document with {len(text_segments)} segments and {len(vad_chunks)} VAD chunks...")
    
    # Use await directly (Jupyter supports top-level await)
    result = await graph_service.commit_document_async(
        title="The Art of War - Chapter 1",
        text_segments=text_segments,
        vad_chunks=vad_chunks,
        media_type="audio",
    )
    
    print(f"\nCommit result:")
    print(f"  Document ID: {result['document_id']}")
    print(f"  Segment IDs: {result['segment_ids']}")
    print(f"  Edge count: {result['edge_count']}")
[PluginManager] HTTP Request: POST http://127.0.0.1:57931/execute "HTTP/1.1 200 OK"
[PluginManager] HTTP Request: POST http://127.0.0.1:57931/execute "HTTP/1.1 200 OK"
Committing document with 3 segments and 3 VAD chunks...

Commit result:
  Document ID: 6eeda707-4438-4094-8d44-6272ff5da8a3
  Segment IDs: ['70567737-0663-4f74-8e9b-18e507249e3c', '82e8b884-a3c2-4411-9c5e-d606277e6e83', '8d773f77-9c54-40d6-8a60-3c6e7c45509d']
  Edge count: 6
# Verify the graph structure (use await directly - Jupyter supports top-level await)
if graph_meta and graph_service.is_available():
    # Get graph schema
    schema = await manager.execute_plugin_async(
        "cjm-graph-plugin-sqlite", 
        action="get_schema"
    )
    print(f"Graph schema:")
    print(f"  Node labels: {schema.get('node_labels', [])}")
    print(f"  Relation types: {schema.get('relation_types', [])}")
[PluginManager] HTTP Request: POST http://127.0.0.1:57931/execute "HTTP/1.1 200 OK"
Graph schema:
  Node labels: ['Document', 'Segment']
  Relation types: []
# Cleanup
if graph_meta:
    manager.unload_all()
    print("Plugins unloaded")
[PluginManager] HTTP Request: POST http://127.0.0.1:57931/cleanup "HTTP/1.1 200 OK"
[PluginManager] Unloaded plugin: cjm-graph-plugin-sqlite
Plugins unloaded