Skip to main content

C# SDK: Graph & Relationships

Build knowledge graphs with relationships using the Trix .NET SDK.

Overview

Relationships connect memories to form a knowledge graph. Each relationship has a type and strength, allowing you to model complex connections between pieces of information.

// Access via the client
client.Relationships

Relationships are automatically created when memories reference each other. Use the explicit API when you need fine-grained control over the graph structure.

Create Relationship

Connect two memories with a typed relationship:

CreateRelationship.cs
var relationship = await client.Relationships.CreateAsync(
    sourceId: "mem_source",
    targetId: "mem_target",
    relationshipType: RelationshipTypes.RelatedTo,
    strength: 0.8,
    metadata: new Dictionary<string, object>
    {
        ["reason"] = "Both discuss C# patterns"
    }
);

Console.WriteLine($"Created relationship: {relationship.Id}");
Console.WriteLine($"Type: {relationship.RelationshipType}");
Console.WriteLine($"Strength: {relationship.Strength}");

Relationship Types

Trix exposes 10 built-in relationship type constants via RelationshipTypes:

ConstantValueDescription
RelationshipTypes.RelatedTorelated_toGeneral relationship
RelationshipTypes.SupportssupportsSource supports/reinforces target
RelationshipTypes.ContradictscontradictsSource contradicts target
RelationshipTypes.ReferencesreferencesSource references target
RelationshipTypes.ExtendsextendsSource extends target
RelationshipTypes.DependsOndepends_onSource depends on target
RelationshipTypes.ContainscontainsSource contains target
RelationshipTypes.PartOfpart_ofSource is part of target
RelationshipTypes.CausescausesSource causes target
RelationshipTypes.CausedBycaused_bySource is caused by target
RelationshipTypes.cs
// Using relationship types
await client.Relationships.CreateAsync(
    sourceId: "mem_concept",
    targetId: "mem_example",
    relationshipType: RelationshipTypes.Contains
);

await client.Relationships.CreateAsync(
    sourceId: "mem_v2",
    targetId: "mem_v1",
    relationshipType: RelationshipTypes.Extends
);

// Custom relationship type
await client.Relationships.CreateAsync(
    sourceId: "mem_a",
    targetId: "mem_b",
    relationshipType: "custom:influenced_by",
    strength: 0.7
);

Get Relationships

Retrieve relationships for a memory:

GetRelationships.cs
// Get incoming relationships (pointing to this memory)
var incoming = await client.Relationships.GetIncomingAsync("mem_xyz");

// Get outgoing relationships (from this memory)
var outgoing = await client.Relationships.GetOutgoingAsync("mem_xyz");

foreach (var rel in outgoing.Data)
{
    Console.WriteLine($"{rel.RelationshipType} -> {rel.TargetId}");
    Console.WriteLine($"  Strength: {rel.Strength:F2}");
    Console.WriteLine($"  Created: {rel.CreatedAt}");
}

Console.WriteLine($"Incoming: {incoming.Data.Count}");
Console.WriteLine($"Outgoing: {outgoing.Data.Count}");

Filter Relationships

FilterRelationships.cs
// Filter by type
var related = await client.Relationships.GetOutgoingAsync(
    "mem_xyz",
    type: RelationshipTypes.RelatedTo
);

Update Relationship Strength

Reinforce or weaken relationships based on their usefulness:

UpdateRelationship.cs
// Reinforce relationship (increase strength)
await client.Relationships.ReinforceAsync("rel_abc", amount: 0.1);

// Weaken relationship (decrease strength)
await client.Relationships.WeakenAsync("rel_abc", amount: 0.1);

// Update with bounds checking
var rel = await client.Relationships.ReinforceAsync("rel_abc", amount: 0.2);
Console.WriteLine($"New strength: {rel.Strength}");  // Max 1.0

Relationship strength is clamped between 0 and 1. Reinforcing a relationship at 1.0 strength has no effect, and weakening at 0.0 strength has no effect.

Delete Relationship

Remove a relationship between memories:

DeleteRelationship.cs
// Delete a relationship by ID
await client.Relationships.DeleteAsync("rel_abc");

Graph Traversal

Traverse the knowledge graph to discover connected memories across multiple hops:

Traversal.cs
// Multi-hop traversal
var result = await client.Graph.TraverseAsync(new TraverseRequest
{
    StartNodeIds = new List<string> { "mem_abc123" },
    MaxDepth = 3,
    Limit = 50,
    RelationshipTypes = new List<string>
    {
        RelationshipTypes.RelatedTo,
        RelationshipTypes.Supports
    },
    MinWeight = 0.5,
    Direction = TraversalDirection.Outgoing,
    IncludeContent = true
});

Console.WriteLine($"Found {result.Nodes.Count} nodes");
Console.WriteLine($"Found {result.Edges.Count} edges");

foreach (var node in result.Nodes)
{
    Console.WriteLine($"Depth {node.Depth}: {node.Id} (type: {node.Type})");
}

foreach (var edge in result.Edges)
{
    Console.WriteLine($"  {edge.Source} --[{edge.RelationshipType}]--> {edge.Target}");
}

Traversal Options

PropertyTypeDescription
StartNodeIdsList<string>Starting memory IDs
MaxDepthintMaximum traversal depth (1-5, default: 2)
LimitintMaximum nodes to return (default: 100)
RelationshipTypesList<string>?Filter by relationship types
MinWeightdouble?Minimum edge weight (0-1)
DirectionTraversalDirectionIncoming, Outgoing, or Both
IncludeContentboolInclude memory content in results

Context Extraction

Extract a contextual subgraph around specific memories, perfect for providing relevant context to LLMs:

Context.cs
var context = await client.Graph.GetContextAsync(new GetContextRequest
{
    Query = "machine learning concepts",
    Depth = 2,
    SemanticLimit = 20,
    SemanticThreshold = 0.6,
    RelationshipTypes = new List<string> { RelationshipTypes.RelatedTo },
    MaxNodes = 50
});

// Access the central memory and its related memories
Console.WriteLine($"Central memory: {context.Central.Content}");

Console.WriteLine("Related memories:");
foreach (var mem in context.Related)
{
    Console.WriteLine($"  - {mem.Content}");
}

// Build LLM context string
var contextText = string.Join("\n",
    context.Related.Select(m => m.Content));

Path Finding

Find the shortest path between two memories in the knowledge graph:

Paths.cs
// Find shortest path between two memories
var path = await client.Graph.ShortestPathAsync("mem_abc123", "mem_xyz789");

if (path.Found)
{
    Console.WriteLine($"Distance: {path.Distance} hops");

    foreach (var step in path.Path ?? new())
    {
        Console.WriteLine($"  Node: {step.Node.Id}");
        if (step.Relationship != null)
        {
            Console.WriteLine($"    via {step.Relationship.RelationshipType}");
        }
    }
}
else
{
    Console.WriteLine("No path found between memories");
}

Complete Example

Building a knowledge graph and querying it:

CompleteExample.cs
using Trix;
using Trix.Models;

// Initialize client
using var client = TrixClient.FromEnvironment();

// Create memories
var concept = await client.Memories.CreateAsync(new CreateMemoryRequest
{
    Content = "Machine learning is a subset of artificial intelligence",
    Tags = new List<string> { "ml", "ai", "concept" }
});

var supervised = await client.Memories.CreateAsync(new CreateMemoryRequest
{
    Content = "Supervised learning uses labeled training data",
    Tags = new List<string> { "ml", "supervised" }
});

var unsupervised = await client.Memories.CreateAsync(new CreateMemoryRequest
{
    Content = "Unsupervised learning finds patterns without labels",
    Tags = new List<string> { "ml", "unsupervised" }
});

// Create relationships
await client.Relationships.CreateAsync(
    concept.Id, supervised.Id,
    RelationshipTypes.Contains, 0.9);

await client.Relationships.CreateAsync(
    concept.Id, unsupervised.Id,
    RelationshipTypes.Contains, 0.9);

await client.Relationships.CreateAsync(
    supervised.Id, unsupervised.Id,
    RelationshipTypes.RelatedTo, 0.7);

// Traverse from the concept
var traversal = await client.Graph.TraverseAsync(new TraverseRequest
{
    StartNodeIds = new List<string> { concept.Id },
    MaxDepth = 2,
    IncludeContent = true
});

Console.WriteLine("Knowledge graph:");
foreach (var node in traversal.Nodes)
{
    var indent = new string(' ', node.Depth * 2);
    Console.WriteLine($"{indent}- {node.Id}");
}

// Find shortest path between supervised and unsupervised
var path = await client.Graph.ShortestPathAsync(supervised.Id, unsupervised.Id);

if (path.Found)
{
    Console.WriteLine($"\nPath distance: {path.Distance} hops");
}

Types Reference

Types.cs
public class Relationship
{
    public string Id { get; set; }
    public string SourceId { get; set; }
    public string TargetId { get; set; }
    public string RelationshipType { get; set; }
    public double Strength { get; set; }  // 0-1
    public Dictionary<string, object>? Metadata { get; set; }
    public DateTime CreatedAt { get; set; }
    public DateTime UpdatedAt { get; set; }
}

public class GraphNode
{
    public string Id { get; set; }
    public string Type { get; set; }                    // "memory", "entity", etc.
    public Dictionary<string, object>? Data { get; set; }
    public int Depth { get; set; }
}

public class GraphEdge
{
    public string Source { get; set; }
    public string Target { get; set; }
    public string RelationshipType { get; set; }
    public double Strength { get; set; }                // 0-1
}

Next Steps