sqlite-graphrag 1.2.0

Persistent GraphRAG memory for Claude Code, Codex, Cursor, and 27 AI agents — one self-contained ~19 MiB Rust binary, zero daemon. Never re-explain your codebase again. Hybrid retrieval (FTS5 BM25 + cosine similarity + multi-hop graph traversal) surfaces the right memory in milliseconds. Embedding and entity enrichment run as parallel REST calls against your cloud LLM — no fragile headless subprocesses, no ONNX runtime, no model downloads. Soft-delete with full version history, transactional atomic writes, BLAKE3-tracked mutations. OAuth-only: raw API keys ABORT the spawn.
Documentation
//! Graph payload parsing and validation for `remember --graph-stdin` / `--graph-file`.
//!
//! Holds the curated `{body, entities, relationships}` wire shape and the
//! relation-format / strength guards applied before persistence.

use crate::errors::AppError;
use crate::storage::entities::{NewEntity, NewRelationship};
use serde::Deserialize;

/// Curated graph payload accepted on `--graph-stdin` / `--graph-file`
/// (and the optional body field when no explicit body source is given).
#[derive(Deserialize, Default)]
#[serde(deny_unknown_fields)]
pub(super) struct GraphInput {
    #[serde(default)]
    pub(super) body: Option<String>,
    #[serde(default)]
    pub(super) entities: Vec<NewEntity>,
    #[serde(default)]
    pub(super) relationships: Vec<NewRelationship>,
}

/// Normalize relation labels and validate strength / format for every edge.
pub(super) fn normalize_and_validate_graph_input(graph: &mut GraphInput) -> Result<(), AppError> {
    for rel in &mut graph.relationships {
        rel.relation = crate::parsers::normalize_relation(&rel.relation);
        if let Err(e) = crate::parsers::validate_relation_format(&rel.relation) {
            return Err(AppError::Validation(
                crate::i18n::validation::relation_format_for_relationship(
                    &e,
                    &rel.source,
                    &rel.target,
                ),
            ));
        }
        crate::parsers::warn_if_non_canonical(&rel.relation);
        if !(0.0..=1.0).contains(&rel.strength) {
            return Err(AppError::Validation(
                crate::i18n::validation::invalid_relationship_strength(
                    rel.strength,
                    &rel.source,
                    &rel.target,
                ),
            ));
        }
    }

    Ok(())
}