Merge commit 'd803bfe2b1fe7f5e219e50ac20d6801a0a58ac75' as 'vendor/ruvector'

This commit is contained in:
ruv
2026-02-28 14:39:40 -05:00
7854 changed files with 3522914 additions and 0 deletions

397
vendor/ruvector/examples/edge/src/gun.rs vendored Normal file
View File

@@ -0,0 +1,397 @@
//! GUN Decentralized Database Integration
//!
//! Provides decentralized P2P sync for swarm intelligence using GUN protocol.
//!
//! GUN Features:
//! - Offline-first: Works without internet
//! - Real-time sync: Changes propagate instantly
//! - Decentralized: No central server needed
//! - Graph database: Perfect for relationship data
//! - Conflict resolution: HAM (Hypothetical Amnesia Machine)
#[cfg(feature = "gun")]
use gundb::Node;
use crate::Result;
use crate::intelligence::{LearningState, Pattern};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
/// GUN-backed swarm state that syncs across all peers
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GunSwarmState {
/// Swarm identifier
pub swarm_id: String,
/// Connected peers
pub peers: HashMap<String, GunPeerInfo>,
/// Shared learning patterns (synced via GUN)
pub patterns: HashMap<String, Pattern>,
/// Shared memories (synced via GUN)
pub memories: Vec<GunMemory>,
/// Global swarm config
pub config: GunSwarmConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GunPeerInfo {
pub agent_id: String,
pub public_key: Option<String>,
pub last_seen: u64,
pub patterns_count: usize,
pub memories_count: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GunMemory {
pub id: String,
pub content: String,
pub embedding_hash: String, // Hash of embedding for dedup
pub owner: String,
pub timestamp: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GunSwarmConfig {
/// GUN relay peers to connect to
pub relays: Vec<String>,
/// Enable encryption (SEA)
pub encrypted: bool,
/// Sync interval in ms
pub sync_interval_ms: u64,
/// Max patterns to sync
pub max_patterns: usize,
}
impl Default for GunSwarmConfig {
fn default() -> Self {
Self {
relays: vec![
"https://gun-manhattan.herokuapp.com/gun".to_string(),
"https://gun-us.herokuapp.com/gun".to_string(),
],
encrypted: true,
sync_interval_ms: 1000,
max_patterns: 10000,
}
}
}
/// GUN-powered decentralized swarm sync
pub struct GunSync {
swarm_id: String,
agent_id: String,
state: Arc<RwLock<GunSwarmState>>,
#[cfg(feature = "gun")]
node: Option<Node>,
config: GunSwarmConfig,
}
impl GunSync {
/// Create new GUN sync manager
pub fn new(swarm_id: &str, agent_id: &str, config: GunSwarmConfig) -> Self {
let state = GunSwarmState {
swarm_id: swarm_id.to_string(),
peers: HashMap::new(),
patterns: HashMap::new(),
memories: Vec::new(),
config: config.clone(),
};
Self {
swarm_id: swarm_id.to_string(),
agent_id: agent_id.to_string(),
state: Arc::new(RwLock::new(state)),
#[cfg(feature = "gun")]
node: None,
config,
}
}
/// Connect to GUN network
#[cfg(feature = "gun")]
pub async fn connect(&mut self) -> Result<()> {
use gundb::Node;
tracing::info!("Connecting to GUN network for swarm: {}", self.swarm_id);
// Create GUN node
let node = Node::new_with_config(gundb::Config {
peers: self.config.relays.clone(),
..Default::default()
});
self.node = Some(node);
// Subscribe to swarm updates
self.subscribe_to_swarm().await?;
tracing::info!("Connected to GUN network");
Ok(())
}
#[cfg(not(feature = "gun"))]
pub async fn connect(&mut self) -> Result<()> {
tracing::warn!("GUN feature not enabled, using local-only mode");
Ok(())
}
/// Subscribe to swarm data changes
#[cfg(feature = "gun")]
async fn subscribe_to_swarm(&self) -> Result<()> {
if let Some(ref node) = self.node {
let path = format!("swarms/{}", self.swarm_id);
// In real implementation, set up GUN subscriptions
// node.get(&path).on(|data| { ... });
tracing::info!("Subscribed to GUN path: {}", path);
}
Ok(())
}
/// Publish pattern to GUN network
pub async fn publish_pattern(&self, pattern: &Pattern) -> Result<()> {
let key = format!("{}|{}", pattern.state, pattern.action);
// Update local state
{
let mut state = self.state.write().await;
state.patterns.insert(key.clone(), pattern.clone());
}
// Publish to GUN (if connected)
#[cfg(feature = "gun")]
if let Some(ref node) = self.node {
let path = format!("swarms/{}/patterns/{}", self.swarm_id, key);
let data = serde_json::to_string(pattern)
.map_err(|e| SwarmError::Serialization(e.to_string()))?;
// node.get(&path).put(&data);
tracing::debug!("Published pattern to GUN: {}", path);
}
Ok(())
}
/// Publish memory to GUN network
pub async fn publish_memory(&self, memory: GunMemory) -> Result<()> {
// Update local state
{
let mut state = self.state.write().await;
state.memories.push(memory.clone());
}
// Publish to GUN
#[cfg(feature = "gun")]
if let Some(ref node) = self.node {
let path = format!("swarms/{}/memories/{}", self.swarm_id, memory.id);
let data = serde_json::to_string(&memory)
.map_err(|e| SwarmError::Serialization(e.to_string()))?;
// node.get(&path).put(&data);
tracing::debug!("Published memory to GUN: {}", path);
}
Ok(())
}
/// Announce peer presence
pub async fn announce_peer(&self) -> Result<()> {
let peer_info = GunPeerInfo {
agent_id: self.agent_id.clone(),
public_key: None, // Would be set with SEA encryption
last_seen: chrono::Utc::now().timestamp_millis() as u64,
patterns_count: self.state.read().await.patterns.len(),
memories_count: self.state.read().await.memories.len(),
};
// Update local state
{
let mut state = self.state.write().await;
state.peers.insert(self.agent_id.clone(), peer_info.clone());
}
// Publish to GUN
#[cfg(feature = "gun")]
if let Some(ref node) = self.node {
let path = format!("swarms/{}/peers/{}", self.swarm_id, self.agent_id);
let data = serde_json::to_string(&peer_info)
.map_err(|e| SwarmError::Serialization(e.to_string()))?;
// node.get(&path).put(&data);
tracing::debug!("Announced peer to GUN: {}", path);
}
Ok(())
}
/// Get all patterns from swarm
pub async fn get_patterns(&self) -> HashMap<String, Pattern> {
self.state.read().await.patterns.clone()
}
/// Get all peers in swarm
pub async fn get_peers(&self) -> Vec<GunPeerInfo> {
self.state.read().await.peers.values().cloned().collect()
}
/// Get swarm statistics
pub async fn get_stats(&self) -> GunSwarmStats {
let state = self.state.read().await;
GunSwarmStats {
swarm_id: state.swarm_id.clone(),
total_peers: state.peers.len(),
total_patterns: state.patterns.len(),
total_memories: state.memories.len(),
relays: self.config.relays.len(),
encrypted: self.config.encrypted,
}
}
/// Sync learning state to GUN
pub async fn sync_learning_state(&self, learning_state: &LearningState) -> Result<usize> {
let mut synced = 0;
for (_key, pattern) in &learning_state.patterns {
self.publish_pattern(pattern).await?;
synced += 1;
}
Ok(synced)
}
/// Import patterns from GUN to local learning state
pub async fn import_to_learning_state(&self, learning_state: &mut LearningState) -> usize {
let state = self.state.read().await;
let mut imported = 0;
for (key, pattern) in &state.patterns {
if !learning_state.patterns.contains_key(key) {
learning_state.patterns.insert(key.clone(), pattern.clone());
imported += 1;
} else {
// Merge patterns (take higher Q-value or more visits)
if let Some(existing) = learning_state.patterns.get_mut(key) {
if pattern.visits > existing.visits {
*existing = pattern.clone();
imported += 1;
}
}
}
}
imported
}
}
/// GUN swarm statistics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GunSwarmStats {
pub swarm_id: String,
pub total_peers: usize,
pub total_patterns: usize,
pub total_memories: usize,
pub relays: usize,
pub encrypted: bool,
}
/// Builder for GUN-enabled swarm
pub struct GunSwarmBuilder {
swarm_id: String,
relays: Vec<String>,
encrypted: bool,
sync_interval_ms: u64,
}
impl GunSwarmBuilder {
pub fn new(swarm_id: &str) -> Self {
Self {
swarm_id: swarm_id.to_string(),
relays: vec![],
encrypted: true,
sync_interval_ms: 1000,
}
}
/// Add a GUN relay peer
pub fn with_relay(mut self, url: &str) -> Self {
self.relays.push(url.to_string());
self
}
/// Add default public relays
pub fn with_public_relays(mut self) -> Self {
self.relays.extend(vec![
"https://gun-manhattan.herokuapp.com/gun".to_string(),
"https://gun-us.herokuapp.com/gun".to_string(),
"https://gun-eu.herokuapp.com/gun".to_string(),
]);
self
}
/// Enable/disable encryption
pub fn encrypted(mut self, enabled: bool) -> Self {
self.encrypted = enabled;
self
}
/// Set sync interval
pub fn sync_interval(mut self, ms: u64) -> Self {
self.sync_interval_ms = ms;
self
}
/// Build GunSync instance
pub fn build(self, agent_id: &str) -> GunSync {
let config = GunSwarmConfig {
relays: if self.relays.is_empty() {
GunSwarmConfig::default().relays
} else {
self.relays
},
encrypted: self.encrypted,
sync_interval_ms: self.sync_interval_ms,
max_patterns: 10000,
};
GunSync::new(&self.swarm_id, agent_id, config)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_gun_sync_local() {
let sync = GunSwarmBuilder::new("test-swarm")
.encrypted(false)
.build("agent-001");
// Test pattern publishing (local only, no GUN feature)
let pattern = Pattern::new("edit_ts", "typescript-developer");
sync.publish_pattern(&pattern).await.unwrap();
let patterns = sync.get_patterns().await;
assert_eq!(patterns.len(), 1);
// Test stats
let stats = sync.get_stats().await;
assert_eq!(stats.swarm_id, "test-swarm");
assert_eq!(stats.total_patterns, 1);
}
#[tokio::test]
async fn test_gun_peer_announce() {
let sync = GunSwarmBuilder::new("test-swarm")
.build("agent-001");
sync.announce_peer().await.unwrap();
let peers = sync.get_peers().await;
assert_eq!(peers.len(), 1);
assert_eq!(peers[0].agent_id, "agent-001");
}
}