graphzep
Version:
GraphZep: A temporal knowledge graph memory system for AI agents based on the Zep paper
479 lines • 18.1 kB
JavaScript
/**
* RDF Memory Mapper for GraphZep
* Converts Zep memory types to RDF triples and back
*/
import { v4 as uuidv4 } from 'uuid';
import { NamespaceManager, zepMemoryUri, zepEntityUri } from './namespaces.js';
import { MemoryType } from '../zep/types.js';
export class RDFMemoryMapper {
nsManager;
config;
constructor(config = {}) {
this.nsManager = config.namespaceManager || new NamespaceManager();
this.config = {
includeEmbeddings: true,
embeddingSchema: 'vector-ref',
...config
};
}
/**
* Convert Episodic Memory to RDF triples
*/
episodicToRDF(memory) {
const memoryUri = zepMemoryUri(`episodic/${memory.uuid}`);
const triples = [];
// Core memory triples
triples.push({
subject: memoryUri,
predicate: 'rdf:type',
object: 'zep:EpisodicMemory'
}, {
subject: memoryUri,
predicate: 'zep:uuid',
object: { value: memory.uuid, type: 'literal', datatype: 'xsd:string' }
}, {
subject: memoryUri,
predicate: 'zep:content',
object: { value: memory.content, type: 'literal', datatype: 'xsd:string' }
}, {
subject: memoryUri,
predicate: 'zep:sessionId',
object: { value: memory.sessionId, type: 'literal', datatype: 'xsd:string' }
}, {
subject: memoryUri,
predicate: 'zep:createdAt',
object: { value: memory.createdAt.toISOString(), type: 'literal', datatype: 'xsd:dateTime' }
}, {
subject: memoryUri,
predicate: 'zep:validFrom',
object: { value: memory.validFrom.toISOString(), type: 'literal', datatype: 'xsd:dateTime' }
}, {
subject: memoryUri,
predicate: 'zep:accessCount',
object: { value: memory.accessCount.toString(), type: 'literal', datatype: 'xsd:integer' }
});
// Optional properties
if (memory.userId) {
triples.push({
subject: memoryUri,
predicate: 'zep:userId',
object: { value: memory.userId, type: 'literal', datatype: 'xsd:string' }
});
}
if (memory.validUntil) {
triples.push({
subject: memoryUri,
predicate: 'zep:validUntil',
object: { value: memory.validUntil.toISOString(), type: 'literal', datatype: 'xsd:dateTime' }
});
}
if (memory.lastAccessedAt) {
triples.push({
subject: memoryUri,
predicate: 'zep:lastAccessedAt',
object: { value: memory.lastAccessedAt.toISOString(), type: 'literal', datatype: 'xsd:dateTime' }
});
}
if (memory.relevanceScore !== undefined) {
triples.push({
subject: memoryUri,
predicate: 'zep:relevanceScore',
object: { value: memory.relevanceScore.toString(), type: 'literal', datatype: 'xsd:float' }
});
}
if (memory.summary) {
triples.push({
subject: memoryUri,
predicate: 'zep:summary',
object: { value: memory.summary, type: 'literal', datatype: 'xsd:string' }
});
}
// Handle embeddings
if (memory.embedding && this.config.includeEmbeddings) {
triples.push(...this.embeddingToRDF(memoryUri, memory.embedding));
}
// Handle metadata
if (memory.metadata) {
triples.push(...this.metadataToRDF(memoryUri, memory.metadata));
}
// Handle facts
if (memory.facts && memory.facts.length > 0) {
triples.push(...this.factsToRDF(memoryUri, memory.facts));
}
return triples;
}
/**
* Convert Semantic Memory (Facts) to RDF triples using reification
*/
semanticToRDF(fact) {
const factUri = zepMemoryUri(`semantic/${fact.uuid}`);
const statementUri = zepMemoryUri(`statement/${fact.uuid}`);
const triples = [];
// The actual fact as a direct triple
triples.push({
subject: fact.subject,
predicate: fact.predicate,
object: fact.object
});
// Reified statement with metadata
triples.push({
subject: factUri,
predicate: 'rdf:type',
object: 'zep:SemanticMemory'
}, {
subject: factUri,
predicate: 'zep:uuid',
object: { value: fact.uuid, type: 'literal', datatype: 'xsd:string' }
}, {
subject: factUri,
predicate: 'zep:hasStatement',
object: statementUri
}, {
subject: statementUri,
predicate: 'rdf:type',
object: 'rdf:Statement'
}, {
subject: statementUri,
predicate: 'rdf:subject',
object: fact.subject
}, {
subject: statementUri,
predicate: 'rdf:predicate',
object: fact.predicate
}, {
subject: statementUri,
predicate: 'rdf:object',
object: fact.object
}, {
subject: factUri,
predicate: 'zep:confidence',
object: { value: fact.confidence.toString(), type: 'literal', datatype: 'xsd:float' }
}, {
subject: factUri,
predicate: 'zep:validFrom',
object: { value: fact.validFrom.toISOString(), type: 'literal', datatype: 'xsd:dateTime' }
});
// Optional valid until
if (fact.validUntil) {
triples.push({
subject: factUri,
predicate: 'zep:validUntil',
object: { value: fact.validUntil.toISOString(), type: 'literal', datatype: 'xsd:dateTime' }
});
}
// Source memory references
fact.sourceMemoryIds.forEach(memoryId => {
triples.push({
subject: factUri,
predicate: 'zep:derivedFrom',
object: zepMemoryUri(`episodic/${memoryId}`)
});
});
// Handle metadata
if (fact.metadata) {
triples.push(...this.metadataToRDF(factUri, fact.metadata));
}
return triples;
}
/**
* Convert Procedural Memory to RDF triples
*/
proceduralToRDF(memory) {
const memoryUri = zepMemoryUri(`procedural/${memory.uuid}`);
const triples = [];
// Base procedural memory structure
triples.push({
subject: memoryUri,
predicate: 'rdf:type',
object: 'zep:ProceduralMemory'
}, {
subject: memoryUri,
predicate: 'zep:uuid',
object: { value: memory.uuid, type: 'literal', datatype: 'xsd:string' }
}, {
subject: memoryUri,
predicate: 'zep:content',
object: { value: memory.content, type: 'literal', datatype: 'xsd:string' }
}, {
subject: memoryUri,
predicate: 'zep:sessionId',
object: { value: memory.sessionId, type: 'literal', datatype: 'xsd:string' }
}, {
subject: memoryUri,
predicate: 'zep:createdAt',
object: { value: memory.createdAt.toISOString(), type: 'literal', datatype: 'xsd:dateTime' }
}, {
subject: memoryUri,
predicate: 'zep:validFrom',
object: { value: memory.validFrom.toISOString(), type: 'literal', datatype: 'xsd:dateTime' }
});
// Add procedure-specific properties
if (memory.metadata?.steps) {
const stepsUri = `${memoryUri}/steps`;
triples.push({
subject: memoryUri,
predicate: 'zep:hasSteps',
object: stepsUri
});
memory.metadata.steps.forEach((step, index) => {
const stepUri = `${stepsUri}/${index}`;
triples.push({
subject: stepsUri,
predicate: `zep:step${index}`,
object: stepUri
}, {
subject: stepUri,
predicate: 'rdf:type',
object: 'zep:ProcedureStep'
}, {
subject: stepUri,
predicate: 'zep:stepOrder',
object: { value: index.toString(), type: 'literal', datatype: 'xsd:integer' }
}, {
subject: stepUri,
predicate: 'zep:stepContent',
object: { value: step, type: 'literal', datatype: 'xsd:string' }
});
});
}
// Handle other optional properties similar to episodic memory
if (memory.userId) {
triples.push({
subject: memoryUri,
predicate: 'zep:userId',
object: { value: memory.userId, type: 'literal', datatype: 'xsd:string' }
});
}
if (memory.summary) {
triples.push({
subject: memoryUri,
predicate: 'zep:summary',
object: { value: memory.summary, type: 'literal', datatype: 'xsd:string' }
});
}
return triples;
}
/**
* Convert Entity to RDF triples
*/
entityToRDF(entity) {
const entityUri = zepEntityUri(entity.uuid);
const triples = [];
triples.push({
subject: entityUri,
predicate: 'rdf:type',
object: 'zep:Entity'
}, {
subject: entityUri,
predicate: 'zep:uuid',
object: { value: entity.uuid, type: 'literal', datatype: 'xsd:string' }
}, {
subject: entityUri,
predicate: 'zep:name',
object: { value: entity.name, type: 'literal', datatype: 'xsd:string' }
}, {
subject: entityUri,
predicate: 'zep:entityType',
object: { value: entity.entityType, type: 'literal', datatype: 'xsd:string' }
}, {
subject: entityUri,
predicate: 'zep:summary',
object: { value: entity.summary, type: 'literal', datatype: 'xsd:string' }
}, {
subject: entityUri,
predicate: 'zep:createdAt',
object: { value: entity.createdAt.toISOString(), type: 'literal', datatype: 'xsd:dateTime' }
});
// Add entity type-specific class
const entityTypeClass = this.getEntityTypeClass(entity.entityType);
if (entityTypeClass) {
triples.push({
subject: entityUri,
predicate: 'rdf:type',
object: entityTypeClass
});
}
// Handle embeddings
if (entity.summaryEmbedding && this.config.includeEmbeddings) {
triples.push(...this.embeddingToRDF(entityUri, entity.summaryEmbedding));
}
return triples;
}
/**
* Convert RDF triples back to ZepMemory
*/
rdfToZepMemory(triples) {
const memoryMap = this.groupTriplesBySubject(triples);
for (const [subject, subjectTriples] of memoryMap.entries()) {
const typeTriple = subjectTriples.find(t => t.predicate === 'rdf:type');
if (typeTriple &&
(typeTriple.object === 'zep:EpisodicMemory' ||
typeTriple.object === 'zep:SemanticMemory' ||
typeTriple.object === 'zep:ProceduralMemory')) {
return this.constructZepMemoryFromTriples(subject, subjectTriples);
}
}
return null;
}
embeddingToRDF(subjectUri, embedding) {
const triples = [];
switch (this.config.embeddingSchema) {
case 'base64':
const encoded = Buffer.from(new Float32Array(embedding).buffer).toString('base64');
triples.push({
subject: subjectUri,
predicate: 'zep:hasEmbedding',
object: { value: encoded, type: 'literal', datatype: 'xsd:base64Binary' }
});
break;
case 'vector-ref':
// Store reference to external vector store
const vectorId = uuidv4();
triples.push({
subject: subjectUri,
predicate: 'zep:hasEmbedding',
object: { value: `vector://${vectorId}`, type: 'literal', datatype: 'xsd:anyURI' }
});
break;
case 'compressed':
// Simple compression - store as comma-separated values
const compressed = embedding.map(v => v.toFixed(6)).join(',');
triples.push({
subject: subjectUri,
predicate: 'zep:hasEmbedding',
object: { value: compressed, type: 'literal', datatype: 'xsd:string' }
});
break;
}
// Store embedding metadata
triples.push({
subject: subjectUri,
predicate: 'zep:embeddingDimension',
object: { value: embedding.length.toString(), type: 'literal', datatype: 'xsd:integer' }
});
return triples;
}
metadataToRDF(subjectUri, metadata) {
const triples = [];
for (const [key, value] of Object.entries(metadata)) {
const metadataUri = `${subjectUri}/metadata/${key}`;
triples.push({
subject: subjectUri,
predicate: 'zep:hasMetadata',
object: metadataUri
}, {
subject: metadataUri,
predicate: 'rdf:type',
object: 'zep:Metadata'
}, {
subject: metadataUri,
predicate: 'zep:key',
object: { value: key, type: 'literal', datatype: 'xsd:string' }
}, {
subject: metadataUri,
predicate: 'zep:value',
object: { value: JSON.stringify(value), type: 'literal', datatype: 'xsd:string' }
});
}
return triples;
}
factsToRDF(memoryUri, facts) {
const triples = [];
facts.forEach(fact => {
const factTriples = this.semanticToRDF(fact);
triples.push(...factTriples);
// Link fact to memory
triples.push({
subject: memoryUri,
predicate: 'zep:hasFact',
object: zepMemoryUri(`semantic/${fact.uuid}`)
});
});
return triples;
}
getEntityTypeClass(entityType) {
const typeMap = {
'person': 'zep:Person',
'organization': 'zep:Organization',
'location': 'zep:Location',
'event': 'zep:Event',
'concept': 'zep:Concept'
};
return typeMap[entityType.toLowerCase()] || null;
}
groupTriplesBySubject(triples) {
const grouped = new Map();
for (const triple of triples) {
if (!grouped.has(triple.subject)) {
grouped.set(triple.subject, []);
}
grouped.get(triple.subject).push(triple);
}
return grouped;
}
constructZepMemoryFromTriples(subject, triples) {
const memory = {
facts: []
};
for (const triple of triples) {
switch (triple.predicate) {
case 'zep:uuid':
memory.uuid = this.getLiteralValue(triple.object);
break;
case 'zep:content':
memory.content = this.getLiteralValue(triple.object);
break;
case 'zep:sessionId':
memory.sessionId = this.getLiteralValue(triple.object);
break;
case 'zep:createdAt':
memory.createdAt = new Date(this.getLiteralValue(triple.object));
break;
case 'zep:validFrom':
memory.validFrom = new Date(this.getLiteralValue(triple.object));
break;
case 'zep:validUntil':
memory.validUntil = new Date(this.getLiteralValue(triple.object));
break;
case 'zep:accessCount':
memory.accessCount = parseInt(this.getLiteralValue(triple.object));
break;
case 'zep:relevanceScore':
memory.relevanceScore = parseFloat(this.getLiteralValue(triple.object));
break;
case 'rdf:type':
if (triple.object === 'zep:EpisodicMemory') {
memory.memoryType = MemoryType.EPISODIC;
}
else if (triple.object === 'zep:SemanticMemory') {
memory.memoryType = MemoryType.SEMANTIC;
}
else if (triple.object === 'zep:ProceduralMemory') {
memory.memoryType = MemoryType.PROCEDURAL;
}
break;
}
}
return memory;
}
getLiteralValue(object) {
if (typeof object === 'string') {
return object;
}
const { value, datatype } = object;
if (datatype === 'xsd:integer') {
return parseInt(value);
}
else if (datatype === 'xsd:float' || datatype === 'xsd:double') {
return parseFloat(value);
}
else if (datatype === 'xsd:boolean') {
return value === 'true';
}
else if (datatype === 'xsd:dateTime') {
return new Date(value);
}
return value;
}
}
//# sourceMappingURL=memory-mapper.js.map