//! Basic usage example for dirigent_archivist //! //! This example demonstrates: //! - Creating a Archivist //! - Registering a connector //! - Registering a session //! - Appending messages to a session //! - Listing sessions for a connector //! - Retrieving messages for a session use chrono::Utc; use dirigent_archivist::{ Archivist, MessageRecord, RegisterConnectorRequest, RegisterSessionRequest, Result, }; use std::path::PathBuf; use uuid::Uuid; #[tokio::main] async fn main() -> Result<()> { // Create a temporary archive directory for this example let temp_dir = std::env::temp_dir().join(format!("dirigent_example_{}", Uuid::now_v7())); println!("Creating archive at: {}", temp_dir.display()); // Step 1: Create a Archivist let archivist = Archivist::new_with_single_archive(temp_dir.clone()).await?; println!("Archivist created successfully"); // Step 2: Register a connector println!("\n--- Registering Connector ---"); let connector_req = RegisterConnectorRequest { r#type: "OpenCode".to_string(), title: "OpenCode Local".to_string(), client_native_id: "opencode@http://localhost:12225".to_string(), custom_uid: None, // Let archivist generate a UID metadata: serde_json::json!({ "version": "0.1.0", "protocol": "OpenCode HTTP API" }), fingerprint: None, }; let connector_resp = archivist.register_connector(connector_req, None).await?; println!("Connector registered: {:?}", connector_resp); let connector_uid = connector_resp.connector_uid; // Step 3: Register a session println!("\n--- Registering Session ---"); let session_req = RegisterSessionRequest { connector_uid, native_session_id: "session-abc123".to_string(), title: Some("Example chat session".to_string()), custom_scroll_id: None, // Let archivist generate a scroll ID metadata: serde_json::json!({ "project_path": "/home/user/projects/example", "model": "claude-3-5-sonnet" }), completeness: Default::default(), parent_scroll_id: None, is_subagent: false, continuation: None, agent_id: None, subagent_type: None, spawning_tool_use_id: None, }; let session_resp = archivist.register_session(session_req, None).await?; println!("Session registered: {:?}", session_resp); let scroll_id = session_resp.scroll_id; // Step 4: Append messages to the session println!("\n--- Appending Messages ---"); // User message let user_msg = MessageRecord { version: 1, message_id: Uuid::now_v7(), session: scroll_id, parent_id: None, ts: Utc::now(), role: "user".to_string(), author: Some("alice".to_string()), content_md: "Hello! Can you help me write a function to calculate fibonacci numbers?" .to_string(), content_parts: None, attachments: vec![], metadata: serde_json::json!({}), }; // Assistant message let assistant_msg = MessageRecord { version: 1, message_id: Uuid::now_v7(), session: scroll_id, parent_id: Some(user_msg.message_id), ts: Utc::now(), role: "assistant".to_string(), author: Some("claude".to_string()), content_md: r#"Sure! Here's a recursive fibonacci function in Rust: ```rust fn fibonacci(n: u32) -> u64 { match n { 0 => 0, 1 => 1, _ => fibonacci(n - 1) + fibonacci(n - 2), } } ``` This is the classic recursive implementation, though it's not the most efficient for large values of n."# .to_string(), content_parts: None, attachments: vec![], metadata: serde_json::json!({ "model": "claude-3-5-sonnet", "latency_ms": 1245 }), }; archivist .append_messages(scroll_id, vec![user_msg.clone(), assistant_msg.clone()], None) .await?; println!("Appended 2 messages to session"); // Step 5: List all sessions for the connector println!("\n--- Listing Sessions ---"); let page = archivist .list_sessions_paged( dirigent_archivist::SessionListQuery::default() .with_connector(connector_uid) .with_limit(100), ) .await?; let sessions = page.items; println!("Found {} session(s) for connector:", sessions.len()); for session in &sessions { println!( " - {} ({}): {:?}", session.scroll_id, session.created_at.format("%Y-%m-%d %H:%M:%S"), session.title ); } // Step 6: Retrieve all messages for the session println!("\n--- Retrieving Messages ---"); let messages = archivist.get_messages(scroll_id, None).await?; println!("Retrieved {} message(s):", messages.len()); for msg in &messages { println!("\n[{}] {}", msg.role, msg.ts.format("%Y-%m-%d %H:%M:%S")); println!("{}", msg.content_md); } // Step 7: Demonstrate session resolution println!("\n--- Resolving Session ---"); let resolved_scroll_id = archivist .resolve_session(connector_uid, "session-abc123", None) .await?; println!( "Resolved native session 'session-abc123' to scroll_id: {}", resolved_scroll_id ); assert_eq!(resolved_scroll_id, scroll_id); // Step 8: Show archive structure println!("\n--- Archive Structure ---"); println!("Archive root: {}", temp_dir.display()); println!("\nDirectory structure:"); show_directory_tree(&temp_dir, 0)?; // Cleanup println!("\n--- Cleanup ---"); std::fs::remove_dir_all(&temp_dir)?; println!("Removed temporary archive"); Ok(()) } /// Helper function to display directory tree fn show_directory_tree(path: &PathBuf, depth: usize) -> Result<()> { let indent = " ".repeat(depth); if path.is_dir() { println!("{}{}/", indent, path.file_name().unwrap().to_string_lossy()); let mut entries: Vec<_> = std::fs::read_dir(path)?.filter_map(|e| e.ok()).collect(); entries.sort_by_key(|e| e.path()); for entry in entries { show_directory_tree(&entry.path(), depth + 1)?; } } else { println!("{}{}", indent, path.file_name().unwrap().to_string_lossy()); } Ok(()) }