chore: project structure refactoring

This commit is contained in:
Dmitry Ng
2026-02-03 23:21:15 +03:00
parent d26dfda6ed
commit a1540b4a65
13 changed files with 306 additions and 548 deletions
+140 -157
View File
@@ -5,6 +5,14 @@ A Go client library for the Graphiti HTTP API.
## Features
- Full coverage of Graphiti HTTP API endpoints
- **7 specialized advanced search methods** for different query patterns:
- Temporal Window Search - search within time ranges
- Entity Relationships Search - explore entity connections
- Diverse Results Search - get non-redundant results using MMR
- Episode Context Search - search conversation history
- Successful Tools Search - find frequently mentioned techniques
- Recent Context Search - get recent information with recency bias
- Entity By Label Search - filter entities by type/label
- Optional Langfuse observation tracking for monitoring and debugging
- Configurable HTTP client and timeouts
- Type-safe request and response structures
@@ -15,6 +23,12 @@ A Go client library for the Graphiti HTTP API.
go get github.com/vxcontrol/graphiti-go-client
```
## Quick Start
See the complete working examples:
- **[Base Usage Example](./examples/base-usage-example/main.go)** - Basic operations and common patterns
- **[Advanced Search Example](./examples/advanced-search-example/main.go)** - All 7 advanced search methods
## Usage
### Creating a Client
@@ -253,6 +267,108 @@ if err != nil {
fmt.Printf("Fact: %s\n", fact.Fact)
```
### Advanced Search Methods
The client provides specialized search methods for different use cases:
#### Temporal Window Search
Search for context within a specific time window:
```go
result, err := client.TemporalWindowSearch(graphiti.TemporalSearchRequest{
Query: "user activities",
GroupID: &groupID,
TimeStart: time.Now().Add(-24 * time.Hour),
TimeEnd: time.Now(),
MaxResults: 10,
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Found %d edges, %d nodes, %d episodes\n",
len(result.Edges), len(result.Nodes), len(result.Episodes))
```
#### Entity Relationships Search
Find relationships and related entities from a center node:
```go
result, err := client.EntityRelationshipsSearch(graphiti.EntityRelationshipSearchRequest{
Query: "related entities",
GroupID: &groupID,
CenterNodeUUID: "entity-uuid-123",
MaxDepth: 2,
NodeLabels: &[]string{"PERSON", "ORGANIZATION"},
MaxResults: 20,
})
```
#### Diverse Results Search
Get diverse, non-redundant results using Maximal Marginal Relevance (MMR):
```go
result, err := client.DiverseResultsSearch(graphiti.DiverseSearchRequest{
Query: "user interests and hobbies",
GroupID: &groupID,
DiversityLevel: "medium", // "low", "medium", or "high"
MaxResults: 10,
})
```
#### Episode Context Search
Search through agent responses and conversation context:
```go
result, err := client.EpisodeContextSearch(graphiti.EpisodeContextSearchRequest{
Query: "tool execution results",
GroupID: &groupID,
MaxResults: 5,
})
```
#### Successful Tools Search
Find frequently mentioned successful tools or techniques:
```go
result, err := client.SuccessfulToolsSearch(graphiti.SuccessfulToolsSearchRequest{
Query: "successful exploits",
GroupID: &groupID,
MinMentions: 2,
MaxResults: 10,
})
```
#### Recent Context Search
Get most recent relevant context with recency bias:
```go
result, err := client.RecentContextSearch(graphiti.RecentContextSearchRequest{
Query: "recent discoveries",
GroupID: &groupID,
RecencyWindow: "6h", // e.g., "1h", "24h", "7d"
MaxResults: 10,
})
```
#### Entity By Label Search
Search for entities by their labels/types:
```go
result, err := client.EntityByLabelSearch(graphiti.EntityByLabelSearchRequest{
Query: "vulnerable services",
GroupID: &groupID,
NodeLabels: []string{"SERVICE", "VULNERABILITY"},
MaxResults: 20,
})
```
### Delete Operations
```go
@@ -269,161 +385,6 @@ result, err := client.DeleteGroup("group-id-123")
result, err := client.Clear()
```
## Advanced Search Methods
The client provides specialized search methods for advanced querying and analysis.
### Temporal Window Search
Search for all relevant context (facts, entities, and agent responses) from a specific time window:
```go
result, err := client.TemporalWindowSearch(graphiti.TemporalSearchRequest{
Query: "What attacks were performed?",
GroupID: &groupID,
TimeStart: time.Now().Add(-24 * time.Hour),
TimeEnd: time.Now(),
MaxResults: 15,
})
if err != nil {
log.Fatal(err)
}
for _, edge := range result.Edges {
fmt.Printf("Fact: %s (score: %.2f)\n", edge.Fact, result.EdgeScores[i])
}
```
### Entity Relationships Search
Find all relationships and related entities starting from a specific discovered entity using graph traversal:
```go
result, err := client.EntityRelationshipsSearch(graphiti.EntityRelationshipSearchRequest{
Query: "What vulnerabilities are related to this service?",
GroupID: &groupID,
CenterNodeUUID: "service-uuid-123",
MaxDepth: 2,
NodeLabels: &[]string{"VULNERABILITY", "EXPLOIT"},
MaxResults: 20,
})
if err != nil {
log.Fatal(err)
}
if result.CenterNode != nil {
fmt.Printf("Center: %s\n", result.CenterNode.Name)
}
for i, node := range result.Nodes {
fmt.Printf("Related: %s (distance: %.2f)\n", node.Name, result.NodeDistances[i])
}
```
### Diverse Results Search
Get diverse, non-redundant results using Maximal Marginal Relevance (MMR) to prevent receiving repetitive information:
```go
result, err := client.DiverseResultsSearch(graphiti.DiverseSearchRequest{
Query: "Find different attack vectors",
GroupID: &groupID,
DiversityLevel: "high", // "low", "medium", or "high"
MaxResults: 10,
})
if err != nil {
log.Fatal(err)
}
for i, edge := range result.Edges {
fmt.Printf("Fact: %s (MMR score: %.2f)\n", edge.Fact, result.EdgeMMRScores[i])
}
```
### Episode Context Search
Search through complete agent responses, reasoning, and tool execution records:
```go
result, err := client.EpisodeContextSearch(graphiti.EpisodeContextSearchRequest{
Query: "Show me nmap scan results",
GroupID: &groupID,
AgentTypes: &[]string{"pentester"},
IncludeToolCalls: true,
MaxResults: 10,
})
if err != nil {
log.Fatal(err)
}
for i, episode := range result.Episodes {
fmt.Printf("Episode: %s (score: %.2f)\n", episode.Content, result.RerankerScores[i])
}
```
### Successful Tools Search
Find successful tool executions and attack patterns, prioritizing facts that led to successful exploitation:
```go
result, err := client.SuccessfulToolsSearch(graphiti.SuccessfulToolsSearchRequest{
Query: "Find successful exploits",
GroupID: &groupID,
ToolNames: &[]string{"metasploit", "sqlmap"},
MinMentions: 2,
MaxResults: 15,
})
if err != nil {
log.Fatal(err)
}
for i, edge := range result.Edges {
fmt.Printf("Fact: %s (mentions: %.0f)\n", edge.Fact, result.EdgeMentionCounts[i])
}
```
### Recent Context Search
Retrieve the most recent relevant context, biased toward recent actions and discoveries:
```go
result, err := client.RecentContextSearch(graphiti.RecentContextSearchRequest{
Query: "What was discovered recently?",
GroupID: &groupID,
RecencyWindow: "24h", // "1h", "6h", "24h", or "7d"
MaxResults: 10,
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Searching from %s to %s\n", result.TimeWindow.Start, result.TimeWindow.End)
for i, edge := range result.Edges {
fmt.Printf("Recent fact: %s (score: %.2f)\n", edge.Fact, result.EdgeScores[i])
}
```
### Entity By Label Search
Search for specific entity types (IPs, services, vulnerabilities, tools, etc.) with label-based filtering:
```go
result, err := client.EntityByLabelSearch(graphiti.EntityByLabelSearchRequest{
Query: "Find all vulnerable services",
GroupID: &groupID,
NodeLabels: []string{"SERVICE", "VULNERABILITY"},
EdgeTypes: &[]string{"HAS_VULNERABILITY", "EXPLOITS"},
MaxResults: 25,
})
if err != nil {
log.Fatal(err)
}
for i, node := range result.Nodes {
fmt.Printf("Entity: %s [%s] (score: %.2f)\n",
node.Name, strings.Join(node.Labels, ", "), result.NodeScores[i])
}
```
## Types
### Observation
@@ -580,5 +541,27 @@ if err != nil {
## Examples
- **[Basic Example](./example/main.go)**: Complete working demonstration of the client, including proper handling of asynchronous operations and data verification.
- **[Advanced Search Example](./advanced_search_example/advanced_search_example.go)**: Comprehensive demonstration of all advanced search methods including temporal queries, entity relationships, diverse results, episode context, successful tools, recent context, and entity label filtering.
Two complete working examples are available:
### [Base Usage Example](./examples/base-usage-example/main.go)
Demonstrates basic operations:
- Client initialization and health checks
- Adding messages with Langfuse observation tracking
- Basic search and memory retrieval
- Entity node creation
- Episode management
- Proper handling of asynchronous operations
### [Advanced Search Example](./examples/advanced-search-example/main.go)
Comprehensive demonstration of all 7 advanced search methods:
- **Temporal Window Search** - query within specific time ranges
- **Entity Relationships Search** - explore entity connections and graph traversal
- **Diverse Results Search** - get non-redundant results using MMR
- **Episode Context Search** - search through conversation history and tool outputs
- **Successful Tools Search** - find frequently mentioned successful techniques
- **Recent Context Search** - retrieve recent information with recency bias
- **Entity By Label Search** - filter entities by type/label
This example uses a realistic penetration testing scenario with detailed test data to demonstrate each search method's capabilities.
Binary file not shown.
-9
View File
@@ -1,9 +0,0 @@
module advanced_search_example
go 1.25.3
require github.com/vxcontrol/graphiti-go-client v0.0.0
require github.com/google/uuid v1.6.0
replace github.com/vxcontrol/graphiti-go-client => ../
-275
View File
@@ -1,275 +0,0 @@
package main
import (
"fmt"
"log"
"time"
"github.com/google/uuid"
graphiti "github.com/vxcontrol/graphiti-go-client"
)
// This example demonstrates the advanced search capabilities of the Graphiti Go client.
// It shows how to use specialized search methods for temporal queries, entity relationships,
// diverse results, episodes, successful tools, recent context, and entity label filtering.
func main() {
// Create a client
client := graphiti.NewClient("http://localhost:8000", graphiti.WithTimeout(60*time.Second))
// Health check
fmt.Println("=== Health Check ===")
health, err := client.HealthCheck()
if err != nil {
log.Fatalf("Health check failed: %v", err)
}
fmt.Printf("Status: %s\n\n", health.Status)
// Create a unique group ID for this example
groupID := uuid.New().String()
fmt.Printf("Using group ID: %s\n\n", groupID)
// Add some security test messages to demonstrate advanced search
fmt.Println("=== Adding Security Test Messages ===")
messages := []graphiti.Message{
{
Content: "Performed nmap scan on target 192.168.1.100. Found open ports: 22 (SSH), 80 (HTTP), 443 (HTTPS).",
Author: "pentester",
Timestamp: time.Now().Add(-2 * time.Hour),
},
{
Content: "Detected Apache 2.4.41 running on port 80 with potential CVE-2021-41773 vulnerability.",
Author: "scanner",
Timestamp: time.Now().Add(-90 * time.Minute),
},
{
Content: "Successfully exploited CVE-2021-41773 using metasploit. Gained shell access.",
Author: "exploit_tool",
Timestamp: time.Now().Add(-60 * time.Minute),
},
{
Content: "Performed privilege escalation using CVE-2021-3156 (sudo vulnerability).",
Author: "exploit_tool",
Timestamp: time.Now().Add(-45 * time.Minute),
},
}
addResult, err := client.AddMessages(graphiti.AddMessagesRequest{
GroupID: groupID,
Messages: messages,
})
if err != nil {
log.Fatalf("Failed to add messages: %v", err)
}
fmt.Printf("%s: %v\n\n", addResult.Message, addResult.Success)
// Wait for processing
fmt.Println("Waiting for messages to be processed...")
maxAttempts := 12
pollInterval := 5 * time.Second
for attempt := 1; attempt <= maxAttempts; attempt++ {
fmt.Printf(" Polling (attempt %d/%d)...\n", attempt, maxAttempts)
episodes, err := client.GetEpisodes(groupID, 10)
if err != nil {
log.Printf(" Warning: Failed to get episodes: %v", err)
} else if len(episodes) > 0 {
fmt.Printf(" ✓ Found %d episodes, ready for advanced search!\n\n", len(episodes))
break
}
if attempt == maxAttempts {
log.Printf("Warning: No episodes found after waiting. Continuing anyway...\n\n")
}
time.Sleep(pollInterval)
}
// Example 1: Temporal Window Search
fmt.Println("=== Example 1: Temporal Window Search ===")
fmt.Println("Searching for activities in the last 3 hours...")
temporalResult, err := client.TemporalWindowSearch(graphiti.TemporalSearchRequest{
Query: "What security tests were performed?",
GroupID: &groupID,
TimeStart: time.Now().Add(-3 * time.Hour),
TimeEnd: time.Now(),
MaxResults: 10,
})
if err != nil {
log.Printf("Temporal search failed: %v\n", err)
} else {
fmt.Printf("Found %d edges, %d nodes, %d episodes\n",
len(temporalResult.Edges), len(temporalResult.Nodes), len(temporalResult.Episodes))
fmt.Printf("Time window: %s to %s\n",
temporalResult.TimeWindow.Start.Format(time.RFC3339),
temporalResult.TimeWindow.End.Format(time.RFC3339))
for i, episode := range temporalResult.Episodes {
score := 0.0
if i < len(temporalResult.EpisodeScores) {
score = temporalResult.EpisodeScores[i]
}
fmt.Printf(" - Episode (score: %.2f): %s\n", score, episode.Content[:min(80, len(episode.Content))])
}
}
fmt.Println()
// Example 2: Diverse Results Search
fmt.Println("=== Example 2: Diverse Results Search ===")
fmt.Println("Getting diverse attack vectors with high diversity...")
diverseResult, err := client.DiverseResultsSearch(graphiti.DiverseSearchRequest{
Query: "Show different attack methods and vulnerabilities",
GroupID: &groupID,
DiversityLevel: "high",
MaxResults: 10,
})
if err != nil {
log.Printf("Diverse search failed: %v\n", err)
} else {
fmt.Printf("Found %d diverse edges, %d nodes, %d episodes\n",
len(diverseResult.Edges), len(diverseResult.Nodes), len(diverseResult.Episodes))
for i, edge := range diverseResult.Edges {
score := 0.0
if i < len(diverseResult.EdgeMMRScores) {
score = diverseResult.EdgeMMRScores[i]
}
fmt.Printf(" - Edge (MMR: %.2f): %s\n", score, edge.Fact[:min(80, len(edge.Fact))])
}
}
fmt.Println()
// Example 3: Episode Context Search
fmt.Println("=== Example 3: Episode Context Search ===")
fmt.Println("Searching for tool execution records...")
episodeResult, err := client.EpisodeContextSearch(graphiti.EpisodeContextSearchRequest{
Query: "Show tool execution results",
GroupID: &groupID,
IncludeToolCalls: true,
MaxResults: 10,
})
if err != nil {
log.Printf("Episode search failed: %v\n", err)
} else {
fmt.Printf("Found %d episodes with %d mentioned nodes\n",
len(episodeResult.Episodes), len(episodeResult.MentionedNodes))
for i, episode := range episodeResult.Episodes {
score := 0.0
if i < len(episodeResult.RerankerScores) {
score = episodeResult.RerankerScores[i]
}
fmt.Printf(" - Episode (score: %.2f) from %s: %s\n",
score, episode.Source, episode.Content[:min(80, len(episode.Content))])
}
}
fmt.Println()
// Example 4: Successful Tools Search
fmt.Println("=== Example 4: Successful Tools Search ===")
fmt.Println("Finding successful exploits with min 1 mention...")
toolsResult, err := client.SuccessfulToolsSearch(graphiti.SuccessfulToolsSearchRequest{
Query: "Find successful exploits",
GroupID: &groupID,
MinMentions: 1,
MaxResults: 10,
})
if err != nil {
log.Printf("Successful tools search failed: %v\n", err)
} else {
fmt.Printf("Found %d edges with sufficient mentions\n", len(toolsResult.Edges))
for i, edge := range toolsResult.Edges {
mentions := 0.0
if i < len(toolsResult.EdgeMentionCounts) {
mentions = toolsResult.EdgeMentionCounts[i]
}
fmt.Printf(" - Edge (mentions: %.0f): %s\n", mentions, edge.Fact[:min(80, len(edge.Fact))])
}
}
fmt.Println()
// Example 5: Recent Context Search
fmt.Println("=== Example 5: Recent Context Search ===")
fmt.Println("Getting recent discoveries from the last 6 hours...")
recentResult, err := client.RecentContextSearch(graphiti.RecentContextSearchRequest{
Query: "What was discovered recently?",
GroupID: &groupID,
RecencyWindow: "6h",
MaxResults: 10,
})
if err != nil {
log.Printf("Recent context search failed: %v\n", err)
} else {
fmt.Printf("Found %d recent edges, %d nodes, %d episodes\n",
len(recentResult.Edges), len(recentResult.Nodes), len(recentResult.Episodes))
fmt.Printf("Recency window: %s to %s\n",
recentResult.TimeWindow.Start.Format(time.RFC3339),
recentResult.TimeWindow.End.Format(time.RFC3339))
for i, edge := range recentResult.Edges {
score := 0.0
if i < len(recentResult.EdgeScores) {
score = recentResult.EdgeScores[i]
}
fmt.Printf(" - Recent edge (score: %.2f): %s\n", score, edge.Fact[:min(80, len(edge.Fact))])
}
}
fmt.Println()
// Example 6: Entity By Label Search
fmt.Println("=== Example 6: Entity By Label Search ===")
fmt.Println("Searching for SERVICE and VULNERABILITY entities...")
labelResult, err := client.EntityByLabelSearch(graphiti.EntityByLabelSearchRequest{
Query: "Find vulnerable services",
GroupID: &groupID,
NodeLabels: []string{"SERVICE", "VULNERABILITY", "IP_ADDRESS"},
MaxResults: 20,
})
if err != nil {
log.Printf("Entity label search failed: %v\n", err)
} else {
fmt.Printf("Found %d nodes, %d edges\n", len(labelResult.Nodes), len(labelResult.Edges))
for i, node := range labelResult.Nodes {
score := 0.0
if i < len(labelResult.NodeScores) {
score = labelResult.NodeScores[i]
}
fmt.Printf(" - Node (score: %.2f): %s [labels: %v]\n",
score, node.Name, node.Labels)
}
}
fmt.Println()
// Optional: Entity Relationships Search (requires a known entity UUID)
// This example shows the pattern, but won't execute without a real entity UUID
fmt.Println("=== Example 7: Entity Relationships Search (Pattern) ===")
fmt.Println("To use EntityRelationshipsSearch, you need a center node UUID.")
fmt.Println("Example usage:")
fmt.Println(`
result, err := client.EntityRelationshipsSearch(graphiti.EntityRelationshipSearchRequest{
Query: "What is connected to this service?",
GroupID: &groupID,
CenterNodeUUID: "your-entity-uuid-here",
MaxDepth: 2,
NodeLabels: &[]string{"VULNERABILITY", "EXPLOIT"},
MaxResults: 20,
})
if err != nil {
log.Printf("Entity relationships search failed: %v\n", err)
} else {
fmt.Printf("Found %d related nodes at various distances\n", len(result.Nodes))
}
`)
fmt.Println()
// Cleanup: delete the group
fmt.Println("=== Cleanup ===")
deleteResult, err := client.DeleteGroup(groupID)
if err != nil {
log.Printf("Warning: Failed to delete group: %v", err)
} else {
fmt.Printf("%s: %v\n", deleteResult.Message, deleteResult.Success)
}
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
BIN
View File
Binary file not shown.
-11
View File
@@ -1,11 +0,0 @@
module github.com/vxcontrol/graphiti-go-client/example
go 1.23
replace github.com/vxcontrol/graphiti-go-client => ../
require (
github.com/google/uuid v1.6.0
github.com/vxcontrol/graphiti-go-client v0.0.0-00010101000000-000000000000
)
+10
View File
@@ -0,0 +1,10 @@
module github.com/vxcontrol/graphiti-go-client/examples/advanced-search-example
go 1.23
replace github.com/vxcontrol/graphiti-go-client => ../../
require (
github.com/google/uuid v1.6.0
github.com/vxcontrol/graphiti-go-client v0.0.0-00010101000000-000000000000
)
@@ -7,6 +7,8 @@ import (
"time"
graphiti "github.com/vxcontrol/graphiti-go-client"
"github.com/google/uuid"
)
// This example demonstrates the advanced search capabilities of the Graphiti Go client.
@@ -35,19 +37,25 @@ func main() {
}
fmt.Printf("✓ Server is running (status: %s)\n\n", health.Status)
observation := &graphiti.Observation{
ID: uuid.New().String(),
TraceID: uuid.New().String(),
Time: time.Now(),
}
// Add test data
if !addTestData(client) {
log.Fatal("Failed to add test data")
}
// Run all search tests
testTemporalWindowSearch(client)
testEntityRelationshipsSearch(client)
testDiverseResultsSearch(client)
testEpisodeContextSearch(client)
testSuccessfulToolsSearch(client)
testRecentContextSearch(client)
testEntityByLabelSearch(client)
testTemporalWindowSearch(client, observation)
testEntityRelationshipsSearch(client, observation)
testDiverseResultsSearch(client, observation)
testEpisodeContextSearch(client, observation)
testSuccessfulToolsSearch(client, observation)
testRecentContextSearch(client, observation)
testEntityByLabelSearch(client, observation)
fmt.Println("\n" + strings.Repeat("=", 80))
fmt.Println("Test Suite Complete")
@@ -282,7 +290,7 @@ RECOMMENDATIONS:
return true
}
func testTemporalWindowSearch(client *graphiti.Client) {
func testTemporalWindowSearch(client *graphiti.Client, observation *graphiti.Observation) {
fmt.Println("\n" + strings.Repeat("=", 80))
fmt.Println("TEST 1: Temporal Window Search")
fmt.Println(strings.Repeat("=", 80) + "\n")
@@ -293,11 +301,12 @@ func testTemporalWindowSearch(client *graphiti.Client) {
timeEnd := now.Add(-2 * time.Hour)
result, err := client.TemporalWindowSearch(graphiti.TemporalSearchRequest{
Query: "vulnerability exploitation attempts",
GroupID: stringPtr(groupID),
TimeStart: timeStart,
TimeEnd: timeEnd,
MaxResults: 10,
Query: "vulnerability exploitation attempts",
GroupID: stringPtr(groupID),
TimeStart: timeStart,
TimeEnd: timeEnd,
MaxResults: 10,
Observation: observation,
})
if err != nil {
@@ -324,7 +333,7 @@ func testTemporalWindowSearch(client *graphiti.Client) {
}
}
func testEntityRelationshipsSearch(client *graphiti.Client) {
func testEntityRelationshipsSearch(client *graphiti.Client, observation *graphiti.Observation) {
fmt.Println("\n" + strings.Repeat("=", 80))
fmt.Println("TEST 2: Entity Relationships Search")
fmt.Println(strings.Repeat("=", 80) + "\n")
@@ -336,11 +345,12 @@ func testEntityRelationshipsSearch(client *graphiti.Client) {
// First, do a temporal window search to get edges with node UUIDs
tempResult, err := client.TemporalWindowSearch(graphiti.TemporalSearchRequest{
Query: "192.168.1.10 Linux server",
GroupID: stringPtr(groupID),
TimeStart: timeStart,
TimeEnd: timeEnd,
MaxResults: 5,
Query: "192.168.1.10 Linux server",
GroupID: stringPtr(groupID),
TimeStart: timeStart,
TimeEnd: timeEnd,
MaxResults: 5,
Observation: observation,
})
if err != nil {
@@ -374,6 +384,7 @@ func testEntityRelationshipsSearch(client *graphiti.Client) {
CenterNodeUUID: centerNodeUUID,
MaxDepth: 2,
MaxResults: 20,
Observation: observation,
})
if err != nil {
@@ -406,7 +417,7 @@ func testEntityRelationshipsSearch(client *graphiti.Client) {
}
}
func testDiverseResultsSearch(client *graphiti.Client) {
func testDiverseResultsSearch(client *graphiti.Client, observation *graphiti.Observation) {
fmt.Println("\n" + strings.Repeat("=", 80))
fmt.Println("TEST 3: Diverse Results Search (MMR)")
fmt.Println(strings.Repeat("=", 80) + "\n")
@@ -417,6 +428,7 @@ func testDiverseResultsSearch(client *graphiti.Client) {
GroupID: stringPtr(groupID),
DiversityLevel: "medium",
MaxResults: 10,
Observation: observation,
})
if err != nil {
@@ -445,16 +457,17 @@ func testDiverseResultsSearch(client *graphiti.Client) {
}
}
func testEpisodeContextSearch(client *graphiti.Client) {
func testEpisodeContextSearch(client *graphiti.Client, observation *graphiti.Observation) {
fmt.Println("\n" + strings.Repeat("=", 80))
fmt.Println("TEST 4: Episode Context Search")
fmt.Println(strings.Repeat("=", 80) + "\n")
fmt.Println(" Searching for full agent responses about Metasploit...")
result, err := client.EpisodeContextSearch(graphiti.EpisodeContextSearchRequest{
Query: "Metasploit EternalBlue exploitation",
GroupID: stringPtr(groupID),
MaxResults: 5,
Query: "Metasploit EternalBlue exploitation",
GroupID: stringPtr(groupID),
MaxResults: 5,
Observation: observation,
})
if err != nil {
@@ -478,7 +491,7 @@ func testEpisodeContextSearch(client *graphiti.Client) {
}
}
func testSuccessfulToolsSearch(client *graphiti.Client) {
func testSuccessfulToolsSearch(client *graphiti.Client, observation *graphiti.Observation) {
fmt.Println("\n" + strings.Repeat("=", 80))
fmt.Println("TEST 5: Successful Tools Search")
fmt.Println(strings.Repeat("=", 80) + "\n")
@@ -489,6 +502,7 @@ func testSuccessfulToolsSearch(client *graphiti.Client) {
GroupID: stringPtr(groupID),
MinMentions: 1,
MaxResults: 15,
Observation: observation,
})
if err != nil {
@@ -511,7 +525,7 @@ func testSuccessfulToolsSearch(client *graphiti.Client) {
fmt.Printf(" - top_mention_count: %.0f\n", topMentionCount)
}
func testRecentContextSearch(client *graphiti.Client) {
func testRecentContextSearch(client *graphiti.Client, observation *graphiti.Observation) {
fmt.Println("\n" + strings.Repeat("=", 80))
fmt.Println("TEST 6: Recent Context Search")
fmt.Println(strings.Repeat("=", 80) + "\n")
@@ -522,6 +536,7 @@ func testRecentContextSearch(client *graphiti.Client) {
GroupID: stringPtr(groupID),
RecencyWindow: "24h",
MaxResults: 10,
Observation: observation,
})
if err != nil {
@@ -543,7 +558,7 @@ func testRecentContextSearch(client *graphiti.Client) {
}
}
func testEntityByLabelSearch(client *graphiti.Client) {
func testEntityByLabelSearch(client *graphiti.Client, observation *graphiti.Observation) {
fmt.Println("\n" + strings.Repeat("=", 80))
fmt.Println("TEST 7: Entity By Label Search")
fmt.Println(strings.Repeat("=", 80) + "\n")
@@ -555,11 +570,12 @@ func testEntityByLabelSearch(client *graphiti.Client) {
// First, use temporal search to get some nodes and discover their labels
tempResult, err := client.TemporalWindowSearch(graphiti.TemporalSearchRequest{
Query: "all entities",
GroupID: stringPtr(groupID),
TimeStart: timeStart,
TimeEnd: timeEnd,
MaxResults: 15,
Query: "all entities",
GroupID: stringPtr(groupID),
TimeStart: timeStart,
TimeEnd: timeEnd,
MaxResults: 15,
Observation: observation,
})
if err != nil {
@@ -615,10 +631,11 @@ func testEntityByLabelSearch(client *graphiti.Client) {
fmt.Printf(" Testing entity-by-label search with labels: %v\n", searchLabels)
result, err := client.EntityByLabelSearch(graphiti.EntityByLabelSearchRequest{
Query: "tools and systems",
GroupID: stringPtr(groupID),
NodeLabels: searchLabels,
MaxResults: 15,
Query: "tools and systems",
GroupID: stringPtr(groupID),
NodeLabels: searchLabels,
MaxResults: 15,
Observation: observation,
})
if err != nil {
+11
View File
@@ -0,0 +1,11 @@
module github.com/vxcontrol/graphiti-go-client/examples/base-usage-example
go 1.23
replace github.com/vxcontrol/graphiti-go-client => ../../
require (
github.com/google/uuid v1.6.0
github.com/vxcontrol/graphiti-go-client v0.0.0-00010101000000-000000000000
)
@@ -10,7 +10,15 @@ import (
"github.com/google/uuid"
)
// This example demonstrates how to use the Graphiti Go client.
// This example demonstrates basic usage of the Graphiti Go client.
//
// It covers:
// - Basic operations (health check, add messages, search, get memory)
// - Entity node management
// - Episode retrieval
// - Langfuse observation tracking (optional)
//
// For advanced search methods, see ../advanced-search-example/main.go
//
// Important: The /messages endpoint processes data asynchronously. This example
// polls for episodes to verify data was successfully created before searching.
@@ -37,32 +45,45 @@ func main() {
groupID := uuid.New().String()
fmt.Printf("Using group ID: %s\n\n", groupID)
// Add messages
fmt.Println("=== Adding Messages ===")
messages := []graphiti.Message{
{
Content: "I love hiking in the mountains on weekends.",
Author: "Alice",
Timestamp: time.Now().Add(-2 * time.Hour),
},
{
Content: "That sounds great! Do you have a favorite trail?",
Author: "Assistant",
Timestamp: time.Now().Add(-90 * time.Minute),
},
{
Content: "Yes, I particularly enjoy the Pacific Crest Trail. I try to go there every summer.",
Author: "Alice",
Timestamp: time.Now().Add(-60 * time.Minute),
},
}
// Optional: Create Langfuse observation for tracking
// Note: In production, these IDs should come from your Langfuse instance
observation := &graphiti.Observation{
ID: uuid.New().String(),
TraceID: uuid.New().String(),
Time: time.Now(),
}
// Add messages with rich context for better demonstration
fmt.Println("=== Adding Messages ===")
now := time.Now()
messages := []graphiti.Message{
{
Content: "I love hiking in the mountains on weekends. My favorite trail is the Pacific Crest Trail.",
Author: "Alice",
Timestamp: now.Add(-5 * time.Hour),
},
{
Content: "That sounds amazing! Do you go camping as well?",
Author: "Assistant",
Timestamp: now.Add(-4 * time.Hour),
},
{
Content: "Yes! I usually camp near the trail. I also enjoy photography, especially nature and landscape shots.",
Author: "Alice",
Timestamp: now.Add(-3 * time.Hour),
},
{
Content: "I recently bought a new DSLR camera for my trips. It's a Canon EOS R5.",
Author: "Alice",
Timestamp: now.Add(-2 * time.Hour),
},
{
Content: "Last summer, I hiked the John Muir Trail and took some incredible photos of Yosemite.",
Author: "Alice",
Timestamp: now.Add(-1 * time.Hour),
},
}
addResult, err := client.AddMessages(graphiti.AddMessagesRequest{
GroupID: groupID,
Messages: messages,
@@ -75,7 +96,7 @@ func main() {
// Wait for processing and verify data exists (poll for episodes)
fmt.Println("Waiting for messages to be processed...")
maxAttempts := 10
maxAttempts := 12
pollInterval := 5 * time.Second
var episodes []graphiti.Episode
@@ -98,8 +119,8 @@ func main() {
log.Fatalf("Timeout: No episodes were created after %v. The async job may have failed.", time.Duration(maxAttempts)*pollInterval)
}
// Search for facts
fmt.Println("=== Searching for Facts ===")
// Basic Search
fmt.Println("=== Basic Search ===")
searchResult, err := client.Search(graphiti.SearchQuery{
Query: "What does the user like to do?",
MaxFacts: 5,
@@ -120,7 +141,7 @@ func main() {
fmt.Println("=== Getting Memory ===")
memoryMessages := []graphiti.Message{
{
Content: "What hobbies does the user have?",
Content: "What hobbies and equipment does the user have?",
Author: "User",
Timestamp: time.Now(),
},
@@ -147,7 +168,7 @@ func main() {
UUID: entityUUID,
GroupID: groupID,
Name: "User Interests",
Summary: "The user's hobbies and interests",
Summary: "The user's hobbies and interests including hiking, camping, and photography",
Observation: observation,
})
if err != nil {
@@ -155,11 +176,15 @@ func main() {
}
fmt.Printf("Created entity node: %s (UUID: %s)\n\n", node.Name, node.UUID)
// Display episodes (already fetched during polling)
// Display episodes
fmt.Println("=== Episodes Summary ===")
fmt.Printf("Total episodes: %d\n", len(episodes))
for i, episode := range episodes {
fmt.Printf("%d. %s: %s\n", i+1, episode.Name, episode.Content)
preview := episode.Content
if len(preview) > 80 {
preview = preview[:80] + "..."
}
fmt.Printf("%d. %s: %s\n", i+1, episode.Name, preview)
}
fmt.Println()
+39 -32
View File
@@ -159,11 +159,12 @@ type TimeWindow struct {
// TemporalSearchRequest represents a temporal window search request
type TemporalSearchRequest struct {
Query string `json:"query"`
GroupID *string `json:"group_id,omitempty"`
TimeStart time.Time `json:"time_start"`
TimeEnd time.Time `json:"time_end"`
MaxResults int `json:"max_results,omitempty"`
Query string `json:"query"`
GroupID *string `json:"group_id,omitempty"`
TimeStart time.Time `json:"time_start"`
TimeEnd time.Time `json:"time_end"`
MaxResults int `json:"max_results,omitempty"`
Observation *Observation `json:"observation,omitempty"`
}
// TemporalSearchResponse represents a temporal window search response
@@ -179,13 +180,14 @@ type TemporalSearchResponse struct {
// EntityRelationshipSearchRequest represents an entity relationships search request
type EntityRelationshipSearchRequest struct {
Query string `json:"query"`
GroupID *string `json:"group_id,omitempty"`
CenterNodeUUID string `json:"center_node_uuid"`
MaxDepth int `json:"max_depth,omitempty"`
NodeLabels *[]string `json:"node_labels,omitempty"`
EdgeTypes *[]string `json:"edge_types,omitempty"`
MaxResults int `json:"max_results,omitempty"`
Query string `json:"query"`
GroupID *string `json:"group_id,omitempty"`
CenterNodeUUID string `json:"center_node_uuid"`
MaxDepth int `json:"max_depth,omitempty"`
NodeLabels *[]string `json:"node_labels,omitempty"`
EdgeTypes *[]string `json:"edge_types,omitempty"`
MaxResults int `json:"max_results,omitempty"`
Observation *Observation `json:"observation,omitempty"`
}
// EntityRelationshipSearchResponse represents an entity relationships search response
@@ -199,10 +201,11 @@ type EntityRelationshipSearchResponse struct {
// DiverseSearchRequest represents a diverse results search request
type DiverseSearchRequest struct {
Query string `json:"query"`
GroupID *string `json:"group_id,omitempty"`
DiversityLevel string `json:"diversity_level,omitempty"`
MaxResults int `json:"max_results,omitempty"`
Query string `json:"query"`
GroupID *string `json:"group_id,omitempty"`
DiversityLevel string `json:"diversity_level,omitempty"`
MaxResults int `json:"max_results,omitempty"`
Observation *Observation `json:"observation,omitempty"`
}
// DiverseSearchResponse represents a diverse results search response
@@ -219,9 +222,10 @@ type DiverseSearchResponse struct {
// EpisodeContextSearchRequest represents an episode context search request
type EpisodeContextSearchRequest struct {
Query string `json:"query"`
GroupID *string `json:"group_id,omitempty"`
MaxResults int `json:"max_results,omitempty"`
Query string `json:"query"`
GroupID *string `json:"group_id,omitempty"`
MaxResults int `json:"max_results,omitempty"`
Observation *Observation `json:"observation,omitempty"`
}
// EpisodeContextSearchResponse represents an episode context search response
@@ -234,10 +238,11 @@ type EpisodeContextSearchResponse struct {
// SuccessfulToolsSearchRequest represents a successful tools search request
type SuccessfulToolsSearchRequest struct {
Query string `json:"query"`
GroupID *string `json:"group_id,omitempty"`
MinMentions int `json:"min_mentions,omitempty"`
MaxResults int `json:"max_results,omitempty"`
Query string `json:"query"`
GroupID *string `json:"group_id,omitempty"`
MinMentions int `json:"min_mentions,omitempty"`
MaxResults int `json:"max_results,omitempty"`
Observation *Observation `json:"observation,omitempty"`
}
// SuccessfulToolsSearchResponse represents a successful tools search response
@@ -252,10 +257,11 @@ type SuccessfulToolsSearchResponse struct {
// RecentContextSearchRequest represents a recent context search request
type RecentContextSearchRequest struct {
Query string `json:"query"`
GroupID *string `json:"group_id,omitempty"`
RecencyWindow string `json:"recency_window,omitempty"`
MaxResults int `json:"max_results,omitempty"`
Query string `json:"query"`
GroupID *string `json:"group_id,omitempty"`
RecencyWindow string `json:"recency_window,omitempty"`
MaxResults int `json:"max_results,omitempty"`
Observation *Observation `json:"observation,omitempty"`
}
// RecentContextSearchResponse represents a recent context search response
@@ -271,11 +277,12 @@ type RecentContextSearchResponse struct {
// EntityByLabelSearchRequest represents an entity by label search request
type EntityByLabelSearchRequest struct {
Query string `json:"query"`
GroupID *string `json:"group_id,omitempty"`
NodeLabels []string `json:"node_labels"`
EdgeTypes *[]string `json:"edge_types,omitempty"`
MaxResults int `json:"max_results,omitempty"`
Query string `json:"query"`
GroupID *string `json:"group_id,omitempty"`
NodeLabels []string `json:"node_labels"`
EdgeTypes *[]string `json:"edge_types,omitempty"`
MaxResults int `json:"max_results,omitempty"`
Observation *Observation `json:"observation,omitempty"`
}
// EntityByLabelSearchResponse represents an entity by label search response