interactive/
interactive.rs

1//! VEX Demo: Interactive Chat with Adversarial Verification
2//!
3//! A real-time chatbot demonstrating:
4//! - Interactive input loop
5//! - Multi-agent research
6//! - Adversarial verification
7//! - Temporal memory
8//!
9//! Run with: cargo run -p vex-demo --bin interactive
10
11use std::io::{self, Write};
12use vex_adversarial::{ShadowAgent, ShadowConfig};
13use vex_core::{Agent, AgentConfig, ContextPacket, MerkleTree};
14use vex_llm::{DeepSeekProvider, LlmProvider, LlmRequest, VexConfig};
15use vex_temporal::{EpisodicMemory, HorizonConfig};
16
17#[tokio::main]
18async fn main() {
19    println!("\n╔═══════════════════════════════════════════════════════════════════╗");
20    println!("║           VEX Protocol - Interactive Assistant                    ║");
21    println!("║      Evolutionary | Adversarial | Temporal | Verified             ║");
22    println!("╚═══════════════════════════════════════════════════════════════════╝\n");
23
24    // Initialize
25    let config = VexConfig::from_env();
26    let api_key = config
27        .llm
28        .deepseek_api_key
29        .as_deref()
30        .expect("DEEPSEEK_API_KEY environment variable must be set");
31
32    let llm = DeepSeekProvider::chat(api_key);
33    let mut memory = EpisodicMemory::new(HorizonConfig::for_depth(0));
34    let mut merkle_leaves = Vec::new();
35    let mut turn_count = 0;
36
37    // Create main agents
38    let coordinator = Agent::new(AgentConfig {
39        name: "Assistant".to_string(),
40        role: "You are a helpful, accurate assistant. Provide clear, concise answers.".to_string(),
41        max_depth: 2,
42        spawn_shadow: true,
43    });
44
45    let _verifier = ShadowAgent::new(
46        &coordinator,
47        ShadowConfig {
48            challenge_intensity: 0.6,
49            fact_check: true,
50            logic_check: true,
51        },
52    );
53
54    println!("🤖 Assistant ready! Type your questions below.");
55    println!("   Commands: /help, /memory, /verify, /quit\n");
56
57    // Main interaction loop
58    loop {
59        // Prompt
60        print!("You: ");
61        io::stdout().flush().unwrap();
62
63        // Read input
64        let mut input = String::new();
65        if io::stdin().read_line(&mut input).is_err() {
66            break;
67        }
68        let input = input.trim();
69
70        // Handle empty input
71        if input.is_empty() {
72            continue;
73        }
74
75        // Handle commands
76        match input {
77            "/quit" | "/exit" | "/q" => {
78                println!("\n👋 Goodbye! {} interactions processed.", turn_count);
79                break;
80            }
81            "/help" => {
82                println!("\n📚 Available Commands:");
83                println!("   /memory  - Show episodic memory summary");
84                println!("   /verify  - Show last verification status");
85                println!("   /quit    - Exit the chat\n");
86                continue;
87            }
88            "/memory" => {
89                println!("\n📦 {}", memory.summarize());
90                if !memory.is_empty() {
91                    println!("   Recent episodes:");
92                    for (i, ep) in memory.episodes().take(3).enumerate() {
93                        println!(
94                            "   {}. {}...",
95                            i + 1,
96                            &ep.content[..ep.content.len().min(40)]
97                        );
98                    }
99                }
100                println!();
101                continue;
102            }
103            "/verify" => {
104                if merkle_leaves.is_empty() {
105                    println!("\n⚠️  No verified interactions yet.\n");
106                } else {
107                    let tree = MerkleTree::from_leaves(merkle_leaves.clone());
108                    println!("\n🔐 Verification Status:");
109                    println!("   Interactions: {}", merkle_leaves.len());
110                    println!("   Merkle Root: {}", tree.root_hash().unwrap());
111                    println!("   Integrity: ✅ VERIFIED\n");
112                }
113                continue;
114            }
115            _ => {}
116        }
117
118        turn_count += 1;
119
120        // Get response from main agent
121        print!("\n🤔 Thinking");
122        io::stdout().flush().unwrap();
123
124        let response = match llm
125            .complete(LlmRequest::with_role(&coordinator.config.role, input))
126            .await
127        {
128            Ok(resp) => {
129                print!(".");
130                resp.content
131            }
132            Err(e) => {
133                println!(" ⚠️\n");
134                println!("Error: {}\n", e);
135                continue;
136            }
137        };
138
139        // Adversarial verification (quick check)
140        let verify_request = LlmRequest::with_role(
141            "You are a fact-checker. Rate this response 1-10 for accuracy. Just the number.",
142            &format!(
143                "Response to verify: {}",
144                &response[..response.len().min(150)]
145            ),
146        );
147
148        let verification = match llm.complete(verify_request).await {
149            Ok(resp) => {
150                print!(".");
151                resp.content
152            }
153            Err(_) => "8".to_string(),
154        };
155
156        println!(" ✅\n");
157
158        // Display response
159        println!("🤖 Assistant:");
160        for line in response.lines() {
161            println!("   {}", line);
162        }
163
164        // Show verification score
165        let score = verification
166            .trim()
167            .chars()
168            .find(|c| c.is_ascii_digit())
169            .and_then(|c| c.to_digit(10))
170            .unwrap_or(7);
171        println!("\n   📊 Verification: {}/10", score);
172
173        if score < 6 {
174            println!("   ⚠️  Low confidence - consider fact-checking");
175        }
176        println!();
177
178        // Store in memory
179        memory.remember(
180            &format!(
181                "Q: {} | A: {}...",
182                input,
183                &response[..response.len().min(50)]
184            ),
185            score as f64 / 10.0,
186        );
187
188        // Add to Merkle tree
189        let packet = ContextPacket::new(&response);
190        merkle_leaves.push((format!("turn_{}", turn_count), packet.hash));
191    }
192
193    // Final summary
194    println!("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
195    println!("📈 Session Summary:");
196    println!("   Total turns: {}", turn_count);
197    println!("   {}", memory.summarize());
198    if !merkle_leaves.is_empty() {
199        let tree = MerkleTree::from_leaves(merkle_leaves);
200        println!("   Merkle Root: {}", tree.root_hash().unwrap());
201    }
202    println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
203}