Skip to navigation

Manually Updating the Graph

Add nodes and fact triplets, or update existing nodes and edges

Overview

Zep provides several ways to manually change a graph:

  • Use graph.node.add to add one or more nodes without creating relationships.
  • Use graph.edge.add to add a relationship and its source and target nodes together.
  • Use graph.node.update to update an existing node by UUID.
  • Use graph.edge.update to update an existing edge by UUID.

Each method takes the graph_uuid of the graph. For a user graph, use the graph_uuid from the user create response. For a shared Context Graph, use the uuid from the graph create response.

Adding Nodes

Use graph.node.add when you already have structured entity data and do not need Zep to extract nodes from an episode or create relationships between them. Zep stores the node contents you provide and derives the search representation from each node’s name.

The same method handles both single-node and batch creation. Each request must contain between 1 and 100 nodes and targets the one graph in its graph_uuid argument.

Adding a Single Node

Pass one item in the nodes list to add a single node:

from zep_cloud.client import Zep
from zep_cloud.types import NodeInput
client = Zep(api_key=API_KEY)
result = client.graph.node.add(
graph_uuid,
nodes=[
NodeInput(
name="Acme Corp",
summary="Enterprise customer on the annual plan.",
label="Company",
attributes={"industry": "manufacturing"},
),
],
)
# Zep assigns the node UUIDs before the asynchronous task begins.
print(result.nodes[0].uuid_)
print(result.task.uuid_)

Adding Nodes in a Batch

Pass up to 100 items in the nodes list to add nodes in a batch:

result = client.graph.node.add(
graph_uuid,
nodes=[
NodeInput(name="Alice Smith", label="Person", summary="Account owner at Acme Corp."),
NodeInput(name="Bob Jones", label="Person", summary="Support engineer."),
NodeInput(name="Atlas Gateway", label="Product"),
],
)
for node in result.nodes:
print(node.name, node.uuid_)

Node creation behavior

graph.node.add returns the accepted nodes and a task immediately. Each returned node includes the UUID Zep assigned to it, but the node is not available to read or search until the asynchronous task succeeds. See Check data ingestion status for polling instructions.

Zep assigns node UUIDs. The node input has no uuid field. Every accepted node is a new node, and nodes are not deduplicated by name or content, so two identical requests create two sets of nodes. Keep the UUIDs from the response if you need to reference the nodes later. To protect a request against retries, you can send an idempotency key with the request.

To change a node after it is created (rename it, replace its summary, or edit attributes), use graph.node.update with a UUID from the graph.node.add response.

Each node supports the following fields:

FieldRequiredDescription
nameYesNode name, 1 to 50 characters.
summaryNoNode summary, up to 500 characters.
labelNoA single entity type. The base Entity label is implicit. If the label matches an ontology type with properties, attributes are validated against it.
attributesNoCustom attributes. When label matches an ontology type with properties, the attributes are validated against it.
metadataNoUp to 10 metadata fields attached to the node’s provenance episode. Zep does not store them as node metadata.

Updating a Node

Use graph.node.update to update an existing node by UUID. You can change its name, summary, and attributes. Only the fields you provide are changed, and the updated node is returned synchronously.

from zep_cloud.client import Zep
client = Zep(api_key=API_KEY)
node = client.graph.node.update(
graph_uuid,
node_uuid,
name="Acme Corporation",
summary="Enterprise customer on the annual plan since 2024.",
attributes={"industry": "manufacturing", "region": None},
)
print(node.name, node.attributes)

A summary can be up to 5,000 characters. Node attributes are merged with the existing attributes. Set an attribute to null to delete it.

Updating an Edge

Use graph.edge.update to update an existing entity edge by UUID. You can change its fact and its attributes. The source and target node UUIDs cannot be changed.

from zep_cloud.client import Zep
client = Zep(api_key=API_KEY)
edge = client.graph.edge.update(
graph_uuid,
edge_uuid,
fact="Alice Smith is the primary account owner at Acme Corp.",
attributes={"start_year": 2023},
)
print(edge.fact, edge.attributes)

Edge attributes are merged with the existing attributes. Set an attribute to null to delete it.

Adding a Fact Triplet

You can add manually specified fact/node triplets to the graph with graph.edge.add. One call accepts 1 to 100 edges. Each edge carries the fact, a relationship name (fact_name), and a source_node and a target_node. Each node reference contains a node name or a node uuid. When deduplicate is false (the default), a name creates a node. When deduplicate is true, Zep matches a node by name first, and can merge a duplicate edge or invalidate a contradicted edge. If the new fact invalidates an existing fact, Zep marks the existing fact as invalid and adds the new fact triplet.

The graph.edge.add method returns the accepted edges in request order and a task that you can use to track the operation. See Check data ingestion status for polling instructions.

from zep_cloud.client import Zep
from zep_cloud.types import EdgeInput, EdgeNodeRef
client = Zep(api_key=API_KEY)
result = client.graph.edge.add(
graph_uuid,
edges=[
EdgeInput(
fact="Paul met Eric",
fact_name="MET",
source_node=EdgeNodeRef(name="Paul", summary="Paul is a software engineer."),
target_node=EdgeNodeRef(name="Eric", summary="Eric is a product manager."),
valid_at="2024-01-15T10:30:00Z",
)
],
)
# The result includes the edge UUID and a task for tracking processing status
print(result.edges[0].uuid_)
print(result.task.uuid_)

Retrieving Created UUIDs

Each edge UUID is in the graph.edge.add response, one acknowledgement per edge in request order. When deduplicate is false, each acknowledgement also carries source_node_uuid and target_node_uuid: the UUID you supplied, or the UUID that Zep assigned to a node it created. When deduplicate is true, Zep resolves the endpoints during the asynchronous task. After the task completes, call task.get with the task UUID. The task result contains an edges list, and each entry has edge_uuid, source_node_uuid, and target_node_uuid.

import time
task = client.task.get(result.task.uuid_)
while task.completed_at is None:
time.sleep(1)
task = client.task.get(result.task.uuid_)
print(task.status)
print(task.result["edges"][0]["edge_uuid"])
print(task.result["edges"][0]["source_node_uuid"])
print(task.result["edges"][0]["target_node_uuid"])

Custom types and attributes

You can attach custom scalar attributes to nodes and edges, and optionally reference custom entity and edge types from your ontology to enforce a schema.

  • source_node.labels / target_node.labels: specify a single entity type name. When it matches an ontology type with properties, the node attributes are validated against it.
  • fact_name: serves as both the relationship display name and the edge type name looked up in your ontology.
  • source_node.attributes / target_node.attributes / attributes: custom scalar properties on those nodes and on the edge, usable for filtering in graph searches.

Attribute validation: specifying types is optional. If a label or fact_name is not found in your ontology, attributes pass through without validation:

SituationResult
Label not in ontologyAll node attributes pass through, with no error
fact_name not in ontologyAll edge attributes pass through, with no error
Label in ontology, matching type has no propertiesAll attributes pass through
Label in ontology and type HAS properties definedAttributes validated strictly

When validation activates, every attribute key and value type must match the schema. Otherwise the call fails with HTTP 400.

from zep_cloud.client import Zep
from zep_cloud.types import EdgeNodeRef
client = Zep(api_key=API_KEY)
# Assumes your ontology defines:
# - Entity type "Person" with properties: { "age": int, "role": text }
# - Edge type "WORKS_AT" with properties: { "start_year": int, "department": text }
result = client.graph.edge.add(
graph_uuid,
edges=[
EdgeInput(
fact="Alice works at Acme Corp in the engineering department",
fact_name="WORKS_AT",
source_node=EdgeNodeRef(
name="Alice",
labels=["Person"],
attributes={"age": 34, "role": "Staff Engineer"},
),
target_node=EdgeNodeRef(name="Acme Corp"),
attributes={"start_year": 2021, "department": "engineering"},
)
],
)

Attribute values must be scalar types: string, number, boolean, or null. Nested objects and arrays are not supported.

Fact triplet metadata

You can attach metadata to a fact triple. Zep attaches the metadata to the provenance episode of the fact triple, which makes the resulting edge and nodes filterable via episode metadata filters in graph search. Zep does not store the metadata as edge or node metadata. See Episode metadata for full details on metadata constraints and update semantics.

Metadata values must be scalars (string, number, boolean, or null) or non-empty arrays of scalars. A maximum of 10 keys are allowed.

result = client.graph.edge.add(
graph_uuid,
edges=[
EdgeInput(
fact="Alice approved the Q3 budget",
fact_name="APPROVED",
source_node=EdgeNodeRef(name="Alice"),
target_node=EdgeNodeRef(name="Q3 budget"),
metadata={"source": "finance_system", "priority": 1},
)
],
)

Specifying Node UUIDs

You can optionally specify source_node.uuid and/or target_node.uuid to control which nodes are used. The behavior depends on whether you provide a UUID:

ScenarioBehavior
UUID provided and node existsUses the existing node with that UUID
UUID omittedSearches for an existing node by name. If no match is found, creates a new node with a UUID that Zep generates.

Use only a node UUID that you read from the same graph.

Field Limits

FieldLimit
factMaximum 250 characters
fact_nameMaximum 50 characters; must be SCREAMING_SNAKE_CASE
source_node.name, target_node.nameMaximum 50 characters each
source_node.summary, target_node.summaryMaximum 500 characters each
valid_at, invalid_at, expired_atRFC 3339 / ISO 8601 format (e.g., 2024-01-15T10:30:00Z)