Inline records

kglite.from_records() is the JSON-native loader for agents, APIs, and small in-memory payloads. It builds nodes and relationships without temporary CSVs.

import kglite

graph = kglite.from_records({
    "nodes": [{
        "type": "Person",
        "id_field": "id",
        "title_field": "name",
        "records": [{"id": 1, "name": "Alice", "tags": ["reviewer"]}],
    }],
    "connections": [{
        "type": "KNOWS",
        "source_type": "Person",
        "source_id_field": "source",
        "target_type": "Person",
        "target_id_field": "target",
        "records": [{"source": 1, "target": 2, "since": 2024}],
    }],
}, on_missing_endpoint="vivify")

The key sets above are closed: nodes, connections and on_missing_endpoint at the top level, and the per-spec keys shown.

  • An unknown key raises a ValueError naming a near-miss where there is one.

  • Without that check, a key the loader does not read would be dropped in silence: a spec written with "relationships" would build no relationships at all.

A node spec may also carry "labels": ["Human", "Agent"]. These secondary labels are stamped on every node of that type, including endpoint stubs vivify created for it, so MATCH (:Human) sees the whole type. Listing the type’s own name is a no-op, not a duplicate.

JSON arrays and maps become native list and map values.

on_missing_endpoint can be written in the spec (as above) or passed as the argument. The argument overrides the spec’s value when given. The values are:

  • "vivify" (default) — create provisional endpoint stubs.

  • "drop" — skip relationships whose source/target is absent and report them.

  • "error" — validate the complete input and fail atomically before applying any block when an endpoint is absent.

A connection spec’s target_type also takes a list, for a relationship whose range is a union of node types:

{
    "type": "ASSOCIATED_WITH",
    "source_type": "Microbe",
    "source_id_field": "source",
    "target_type": ["Disease", "Phenotype", "Exposure"],
    "target_type_column": "kind",
    "target_id_field": "target",
    "records": [{"source": "M1", "target": "D1", "kind": "Disease"}],
}

target_type_column names the record field holding each record’s target type. Without it:

  • the listed types are probed for the record’s target id, and the first that has it wins;

  • an id that none has takes the first listed type.

Values are closed like the keys are: a record naming a type outside the list raises. The blueprint loader instead warns and skips the row, because its CSVs are not authored by the caller in the same breath.

Use from_blueprint() for repeatable CSV pipelines with compute operations. Use the DataFrame bulk loaders for already-tabular or high-volume Python data.

Embedded table vs row nodes

Nested lists and maps loaded through from_records can live as embedded table properties or as row nodes. The decision table, the declared-shape validation and the attach_rows helper are in Structured data (tables, nested values, shapes).