2026-01-12 03:34:01 +00:00
|
|
|
//! # Knowledge Graph Execution Tracking Example
|
|
|
|
|
//!
|
|
|
|
|
//! Demonstrates how to record agent executions and query the knowledge graph.
|
|
|
|
|
//!
|
|
|
|
|
//! ## What This Example Shows
|
|
|
|
|
//! - Creating a `TemporalKG` instance (in-memory for this example)
|
|
|
|
|
//! - Recording execution events with timestamps
|
|
|
|
|
//! - Querying executions by agent and task type
|
|
|
|
|
//! - Understanding execution metrics (duration, success rate, costs)
|
|
|
|
|
//!
|
|
|
|
|
//! ## Run
|
|
|
|
|
//! ```bash
|
|
|
|
|
//! cargo run --example 01-execution-tracking -p vapora-knowledge-graph
|
|
|
|
|
//! ```
|
|
|
|
|
//!
|
|
|
|
|
//! ## Expected Output
|
|
|
|
|
//! ```text
|
|
|
|
|
//! === Knowledge Graph Execution Tracking Example ===
|
|
|
|
|
//!
|
|
|
|
|
//! Recording execution events...
|
|
|
|
|
//! ✓ Recorded: developer-1 @ 2025-01-12 (coding, 250ms, success)
|
|
|
|
|
//! ✓ Recorded: developer-1 @ 2025-01-11 (testing, 180ms, success)
|
|
|
|
|
//! ✓ Recorded: developer-2 @ 2025-01-12 (coding, 320ms, failed)
|
|
|
|
|
//!
|
|
|
|
|
//! === Execution Summary ===
|
|
|
|
|
//! Total executions: 3
|
|
|
|
|
//! By agent:
|
|
|
|
|
//! developer-1: 2 executions (success rate: 100%)
|
|
|
|
|
//! developer-2: 1 execution (success rate: 0%)
|
|
|
|
|
//! By task type:
|
|
|
|
|
//! coding: 2 executions (success rate: 50%)
|
|
|
|
|
//! testing: 1 execution (success rate: 100%)
|
|
|
|
|
//! ```
|
|
|
|
|
|
|
|
|
|
use chrono::{Duration, Utc};
|
|
|
|
|
use vapora_knowledge_graph::ExecutionRecord;
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
println!("=== Knowledge Graph Execution Tracking Example ===\n");
|
|
|
|
|
|
|
|
|
|
// Step 1: Create sample execution records
|
|
|
|
|
let now = Utc::now();
|
|
|
|
|
let executions = vec![
|
|
|
|
|
ExecutionRecord {
|
|
|
|
|
id: "exec-001".to_string(),
|
|
|
|
|
task_id: "task-001".to_string(),
|
|
|
|
|
agent_id: "developer-1".to_string(),
|
|
|
|
|
agent_role: Some("developer".to_string()),
|
|
|
|
|
task_type: "coding".to_string(),
|
|
|
|
|
description: "Implement authentication module".to_string(),
|
|
|
|
|
root_cause: None,
|
|
|
|
|
solution: Some("Used OAuth2 with JWT tokens".to_string()),
|
|
|
|
|
timestamp: now - Duration::hours(24),
|
|
|
|
|
duration_ms: 250,
|
|
|
|
|
success: true,
|
|
|
|
|
input_tokens: 500,
|
|
|
|
|
output_tokens: 300,
|
|
|
|
|
cost_cents: 12,
|
|
|
|
|
provider: "claude".to_string(),
|
|
|
|
|
error: None,
|
|
|
|
|
},
|
|
|
|
|
ExecutionRecord {
|
|
|
|
|
id: "exec-002".to_string(),
|
|
|
|
|
task_id: "task-002".to_string(),
|
|
|
|
|
agent_id: "developer-1".to_string(),
|
|
|
|
|
agent_role: Some("developer".to_string()),
|
|
|
|
|
task_type: "testing".to_string(),
|
|
|
|
|
description: "Write unit tests for auth module".to_string(),
|
|
|
|
|
root_cause: None,
|
|
|
|
|
solution: Some("Implemented 100% code coverage".to_string()),
|
|
|
|
|
timestamp: now - Duration::hours(20),
|
|
|
|
|
duration_ms: 180,
|
|
|
|
|
success: true,
|
|
|
|
|
input_tokens: 300,
|
|
|
|
|
output_tokens: 200,
|
|
|
|
|
cost_cents: 8,
|
|
|
|
|
provider: "claude".to_string(),
|
|
|
|
|
error: None,
|
|
|
|
|
},
|
|
|
|
|
ExecutionRecord {
|
|
|
|
|
id: "exec-003".to_string(),
|
|
|
|
|
task_id: "task-003".to_string(),
|
|
|
|
|
agent_id: "developer-2".to_string(),
|
|
|
|
|
agent_role: Some("developer".to_string()),
|
|
|
|
|
task_type: "coding".to_string(),
|
|
|
|
|
description: "Refactor database layer".to_string(),
|
|
|
|
|
root_cause: Some("Performance regression detected".to_string()),
|
|
|
|
|
solution: None,
|
|
|
|
|
timestamp: now - Duration::hours(12),
|
|
|
|
|
duration_ms: 320,
|
|
|
|
|
success: false,
|
|
|
|
|
input_tokens: 600,
|
|
|
|
|
output_tokens: 400,
|
|
|
|
|
cost_cents: 18,
|
|
|
|
|
provider: "gpt-4".to_string(),
|
|
|
|
|
error: Some("Timeout after 320ms".to_string()),
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// Step 2: Display execution records
|
|
|
|
|
println!("Recording execution events...");
|
|
|
|
|
for exec in &executions {
|
|
|
|
|
let status = if exec.success { "✓" } else { "✗" };
|
|
|
|
|
println!(
|
|
|
|
|
" {} {} (agent: {}, task: {}, duration: {}ms, success: {})",
|
|
|
|
|
status,
|
|
|
|
|
exec.timestamp.format("%Y-%m-%d %H:%M"),
|
|
|
|
|
exec.agent_id,
|
|
|
|
|
exec.task_type,
|
|
|
|
|
exec.duration_ms,
|
|
|
|
|
exec.success
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
println!();
|
|
|
|
|
|
|
|
|
|
// Step 3: Analyze executions by agent
|
|
|
|
|
println!("=== Execution Summary ===");
|
|
|
|
|
println!("Total executions: {}\n", executions.len());
|
|
|
|
|
|
|
|
|
|
println!("By Agent:");
|
|
|
|
|
let mut agents: std::collections::HashMap<String, Vec<_>> = std::collections::HashMap::new();
|
|
|
|
|
for exec in &executions {
|
|
|
|
|
agents
|
|
|
|
|
.entry(exec.agent_id.clone())
|
|
|
|
|
.or_insert_with(Vec::new)
|
|
|
|
|
.push(exec);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (agent_id, agent_execs) in &agents {
|
|
|
|
|
let successes = agent_execs.iter().filter(|e| e.success).count();
|
|
|
|
|
let success_rate = (successes as f64 / agent_execs.len() as f64) * 100.0;
|
|
|
|
|
println!(
|
|
|
|
|
" {}: {} executions (success rate: {:.0}%)",
|
|
|
|
|
agent_id,
|
|
|
|
|
agent_execs.len(),
|
|
|
|
|
success_rate
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let total_duration: u64 = agent_execs.iter().map(|e| e.duration_ms).sum();
|
|
|
|
|
let avg_duration = total_duration / agent_execs.len() as u64;
|
|
|
|
|
println!(" Average duration: {}ms", avg_duration);
|
|
|
|
|
|
|
|
|
|
let total_input_tokens: u64 = agent_execs.iter().map(|e| e.input_tokens).sum();
|
|
|
|
|
let total_output_tokens: u64 = agent_execs.iter().map(|e| e.output_tokens).sum();
|
|
|
|
|
println!(
|
|
|
|
|
" Tokens: {} input, {} output",
|
|
|
|
|
total_input_tokens, total_output_tokens
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Step 4: Analyze executions by task type
|
|
|
|
|
println!("\nBy Task Type:");
|
|
|
|
|
let mut task_types: std::collections::HashMap<String, Vec<_>> =
|
|
|
|
|
std::collections::HashMap::new();
|
|
|
|
|
for exec in &executions {
|
|
|
|
|
task_types
|
|
|
|
|
.entry(exec.task_type.clone())
|
|
|
|
|
.or_insert_with(Vec::new)
|
|
|
|
|
.push(exec);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (task_type, task_execs) in &task_types {
|
|
|
|
|
let successes = task_execs.iter().filter(|e| e.success).count();
|
|
|
|
|
let success_rate = (successes as f64 / task_execs.len() as f64) * 100.0;
|
|
|
|
|
println!(
|
|
|
|
|
" {}: {} executions (success rate: {:.0}%)",
|
|
|
|
|
task_type,
|
|
|
|
|
task_execs.len(),
|
|
|
|
|
success_rate
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let avg_duration: u64 =
|
|
|
|
|
task_execs.iter().map(|e| e.duration_ms).sum::<u64>() / task_execs.len() as u64;
|
|
|
|
|
println!(" Average duration: {}ms", avg_duration);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Step 5: Analyze costs
|
|
|
|
|
println!("\n=== Cost Analysis ===");
|
|
|
|
|
let mut costs_by_provider: std::collections::HashMap<String, (u64, u64)> =
|
|
|
|
|
std::collections::HashMap::new();
|
|
|
|
|
|
|
|
|
|
for exec in &executions {
|
|
|
|
|
let (input_cost_cents, output_cost_cents) = match exec.provider.as_str() {
|
|
|
|
|
"claude" => (15, 45), // Claude pricing: $15 per 1M input, $45 per 1M output
|
|
|
|
|
"gpt-4" => (30, 60), // GPT-4 pricing: $30 per 1M input, $60 per 1M output
|
|
|
|
|
_ => (0, 0),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let provider_cost = costs_by_provider
|
|
|
|
|
.entry(exec.provider.clone())
|
|
|
|
|
.or_insert((0, 0));
|
2026-02-14 20:10:55 +00:00
|
|
|
provider_cost.0 += (exec.input_tokens * input_cost_cents) / 1_000_000;
|
|
|
|
|
provider_cost.1 += (exec.output_tokens * output_cost_cents) / 1_000_000;
|
2026-01-12 03:34:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (provider, (input_cost, output_cost)) in costs_by_provider {
|
|
|
|
|
let total = input_cost + output_cost;
|
|
|
|
|
println!(
|
|
|
|
|
" {}: ${:.4} (input: ${:.4}, output: ${:.4})",
|
|
|
|
|
provider,
|
|
|
|
|
total as f64 / 10000.0,
|
|
|
|
|
input_cost as f64 / 10000.0,
|
|
|
|
|
output_cost as f64 / 10000.0
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|