Merge commit 'd803bfe2b1fe7f5e219e50ac20d6801a0a58ac75' as 'vendor/ruvector'
This commit is contained in:
682
vendor/ruvector/examples/edge-net/src/learning-scenarios/attention_patterns.rs
vendored
Normal file
682
vendor/ruvector/examples/edge-net/src/learning-scenarios/attention_patterns.rs
vendored
Normal file
@@ -0,0 +1,682 @@
|
||||
//! Attention Pattern Learning Framework
|
||||
//!
|
||||
//! Four complementary attention mechanisms for intelligent code assistance:
|
||||
//!
|
||||
//! | Attention Type | Question Answered | Application |
|
||||
//! |---------------|-------------------|-------------|
|
||||
//! | **Neural** | What words matter? | Token/semantic relevance |
|
||||
//! | **DAG** | What steps matter? | Execution order, dependencies |
|
||||
//! | **Graph** | What relationships matter? | Code structure, call graphs |
|
||||
//! | **State Space** | What history still matters? | Context persistence |
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
// ============================================================================
|
||||
// NEURAL ATTENTION - "What words matter?"
|
||||
// ============================================================================
|
||||
|
||||
/// Neural attention focuses on token-level and semantic relevance.
|
||||
/// Used for: Code completion, error messages, documentation search.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NeuralAttention {
|
||||
/// Attention weights per token position
|
||||
weights: Vec<f32>,
|
||||
/// Token importance scores
|
||||
token_scores: HashMap<String, f32>,
|
||||
/// Semantic embeddings dimension
|
||||
dim: usize,
|
||||
}
|
||||
|
||||
impl NeuralAttention {
|
||||
pub fn new(dim: usize) -> Self {
|
||||
Self {
|
||||
weights: Vec::new(),
|
||||
token_scores: HashMap::new(),
|
||||
dim,
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute attention weights for tokens
|
||||
/// Q: Query (what we're looking for)
|
||||
/// K: Keys (what we're comparing against)
|
||||
/// V: Values (what we extract)
|
||||
pub fn attend(&mut self, query: &[f32], keys: &[Vec<f32>], values: &[String]) -> Vec<(String, f32)> {
|
||||
if keys.is_empty() || keys.len() != values.len() {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
// Scaled dot-product attention
|
||||
let scale = (self.dim as f32).sqrt();
|
||||
self.weights = keys.iter().map(|k| {
|
||||
let dot: f32 = query.iter().zip(k.iter()).map(|(q, k)| q * k).sum();
|
||||
dot / scale
|
||||
}).collect();
|
||||
|
||||
// Softmax normalization
|
||||
let max_weight = self.weights.iter().cloned().fold(f32::NEG_INFINITY, f32::max);
|
||||
let exp_weights: Vec<f32> = self.weights.iter().map(|w| (w - max_weight).exp()).collect();
|
||||
let sum: f32 = exp_weights.iter().sum();
|
||||
self.weights = exp_weights.iter().map(|w| w / sum).collect();
|
||||
|
||||
// Return weighted values
|
||||
values.iter()
|
||||
.zip(self.weights.iter())
|
||||
.map(|(v, w)| (v.clone(), *w))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Learn which tokens are important from successful completions
|
||||
pub fn learn_token_importance(&mut self, token: &str, success: bool) {
|
||||
let score = self.token_scores.entry(token.to_string()).or_insert(0.5);
|
||||
let reward = if success { 1.0 } else { 0.0 };
|
||||
*score = *score + 0.1 * (reward - *score); // Q-learning update
|
||||
}
|
||||
|
||||
/// Get importance score for a token
|
||||
pub fn token_importance(&self, token: &str) -> f32 {
|
||||
*self.token_scores.get(token).unwrap_or(&0.5)
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// DAG ATTENTION - "What steps matter?"
|
||||
// ============================================================================
|
||||
|
||||
/// DAG (Directed Acyclic Graph) attention for execution order and dependencies.
|
||||
/// Used for: Build systems, test ordering, refactoring sequences.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DagAttention {
|
||||
/// Nodes in the DAG (tasks/steps)
|
||||
nodes: Vec<DagNode>,
|
||||
/// Edges (dependencies)
|
||||
edges: Vec<(usize, usize, f32)>, // (from, to, weight)
|
||||
/// Topological order cache
|
||||
topo_order: Vec<usize>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DagNode {
|
||||
pub id: String,
|
||||
pub step_type: StepType,
|
||||
pub importance: f32,
|
||||
pub completed: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum StepType {
|
||||
/// Configuration/setup step
|
||||
Config,
|
||||
/// Source code modification
|
||||
Source,
|
||||
/// Test execution
|
||||
Test,
|
||||
/// Build/compile step
|
||||
Build,
|
||||
/// Deployment step
|
||||
Deploy,
|
||||
}
|
||||
|
||||
impl DagAttention {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
nodes: Vec::new(),
|
||||
edges: Vec::new(),
|
||||
topo_order: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a step to the DAG
|
||||
pub fn add_step(&mut self, id: &str, step_type: StepType) -> usize {
|
||||
let idx = self.nodes.len();
|
||||
self.nodes.push(DagNode {
|
||||
id: id.to_string(),
|
||||
step_type,
|
||||
importance: 0.5,
|
||||
completed: false,
|
||||
});
|
||||
self.invalidate_topo();
|
||||
idx
|
||||
}
|
||||
|
||||
/// Add a dependency edge
|
||||
pub fn add_dependency(&mut self, from: usize, to: usize, weight: f32) {
|
||||
self.edges.push((from, to, weight));
|
||||
self.invalidate_topo();
|
||||
}
|
||||
|
||||
/// Invalidate topological order cache
|
||||
fn invalidate_topo(&mut self) {
|
||||
self.topo_order.clear();
|
||||
}
|
||||
|
||||
/// Compute topological order (what order to execute steps)
|
||||
pub fn compute_order(&mut self) -> Vec<&DagNode> {
|
||||
if self.topo_order.is_empty() {
|
||||
self.topo_order = self.kahn_sort();
|
||||
}
|
||||
self.topo_order.iter().map(|&i| &self.nodes[i]).collect()
|
||||
}
|
||||
|
||||
/// Kahn's algorithm for topological sort
|
||||
fn kahn_sort(&self) -> Vec<usize> {
|
||||
let n = self.nodes.len();
|
||||
let mut in_degree = vec![0usize; n];
|
||||
let mut adj: Vec<Vec<usize>> = vec![Vec::new(); n];
|
||||
|
||||
for &(from, to, _) in &self.edges {
|
||||
adj[from].push(to);
|
||||
in_degree[to] += 1;
|
||||
}
|
||||
|
||||
let mut queue: Vec<usize> = (0..n).filter(|&i| in_degree[i] == 0).collect();
|
||||
let mut result = Vec::new();
|
||||
|
||||
while let Some(node) = queue.pop() {
|
||||
result.push(node);
|
||||
for &next in &adj[node] {
|
||||
in_degree[next] -= 1;
|
||||
if in_degree[next] == 0 {
|
||||
queue.push(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Get critical path (most important sequence of steps)
|
||||
pub fn critical_path(&self) -> Vec<&DagNode> {
|
||||
// Find path with highest total importance
|
||||
let order = self.kahn_sort();
|
||||
let mut max_path = Vec::new();
|
||||
let mut max_importance = 0.0f32;
|
||||
|
||||
// Simple greedy: follow highest importance edges
|
||||
if let Some(&start) = order.first() {
|
||||
let mut path = vec![start];
|
||||
let mut current = start;
|
||||
let mut importance = self.nodes[start].importance;
|
||||
|
||||
while let Some(&(_, to, weight)) = self.edges.iter()
|
||||
.filter(|(from, _, _)| *from == current)
|
||||
.max_by(|a, b| a.2.partial_cmp(&b.2).unwrap())
|
||||
{
|
||||
path.push(to);
|
||||
importance += self.nodes[to].importance * weight;
|
||||
current = to;
|
||||
}
|
||||
|
||||
if importance > max_importance {
|
||||
max_importance = importance;
|
||||
max_path = path;
|
||||
}
|
||||
}
|
||||
|
||||
max_path.iter().map(|&i| &self.nodes[i]).collect()
|
||||
}
|
||||
|
||||
/// Learn step importance from execution outcomes
|
||||
pub fn learn_step_importance(&mut self, step_id: &str, success: bool) {
|
||||
if let Some(node) = self.nodes.iter_mut().find(|n| n.id == step_id) {
|
||||
let reward = if success { 1.0 } else { 0.0 };
|
||||
node.importance = node.importance + 0.1 * (reward - node.importance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DagAttention {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// GRAPH ATTENTION - "What relationships matter?"
|
||||
// ============================================================================
|
||||
|
||||
/// Graph attention for code structure and relationships.
|
||||
/// Used for: Call graphs, module dependencies, refactoring impact analysis.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GraphAttention {
|
||||
/// Nodes (functions, modules, files)
|
||||
nodes: HashMap<String, GraphNode>,
|
||||
/// Edges with attention weights
|
||||
edges: Vec<GraphEdge>,
|
||||
/// Multi-head attention heads
|
||||
num_heads: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GraphNode {
|
||||
pub id: String,
|
||||
pub node_type: NodeType,
|
||||
pub features: Vec<f32>,
|
||||
pub attention_score: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GraphEdge {
|
||||
pub source: String,
|
||||
pub target: String,
|
||||
pub edge_type: EdgeType,
|
||||
pub weight: f32,
|
||||
pub attention_weights: Vec<f32>, // Per head
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum NodeType {
|
||||
Function,
|
||||
Module,
|
||||
File,
|
||||
Crate,
|
||||
Test,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum EdgeType {
|
||||
Calls,
|
||||
Imports,
|
||||
DependsOn,
|
||||
Tests,
|
||||
Contains,
|
||||
}
|
||||
|
||||
impl GraphAttention {
|
||||
pub fn new(num_heads: usize) -> Self {
|
||||
Self {
|
||||
nodes: HashMap::new(),
|
||||
edges: Vec::new(),
|
||||
num_heads,
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a node to the graph
|
||||
pub fn add_node(&mut self, id: &str, node_type: NodeType, features: Vec<f32>) {
|
||||
self.nodes.insert(id.to_string(), GraphNode {
|
||||
id: id.to_string(),
|
||||
node_type,
|
||||
features,
|
||||
attention_score: 0.0,
|
||||
});
|
||||
}
|
||||
|
||||
/// Add an edge with relationship type
|
||||
pub fn add_edge(&mut self, source: &str, target: &str, edge_type: EdgeType) {
|
||||
self.edges.push(GraphEdge {
|
||||
source: source.to_string(),
|
||||
target: target.to_string(),
|
||||
edge_type,
|
||||
weight: 1.0,
|
||||
attention_weights: vec![1.0 / self.num_heads as f32; self.num_heads],
|
||||
});
|
||||
}
|
||||
|
||||
/// Compute graph attention (simplified GAT-style)
|
||||
pub fn compute_attention(&mut self, focus_node: &str) {
|
||||
// Reset attention scores
|
||||
for node in self.nodes.values_mut() {
|
||||
node.attention_score = 0.0;
|
||||
}
|
||||
|
||||
// Aggregate attention from edges
|
||||
for edge in &self.edges {
|
||||
if edge.source == focus_node || edge.target == focus_node {
|
||||
let other = if edge.source == focus_node { &edge.target } else { &edge.source };
|
||||
if let Some(node) = self.nodes.get_mut(other) {
|
||||
// Sum multi-head attention
|
||||
let attention: f32 = edge.attention_weights.iter().sum();
|
||||
node.attention_score += attention * edge.weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize
|
||||
let max_score = self.nodes.values()
|
||||
.map(|n| n.attention_score)
|
||||
.fold(0.0f32, f32::max);
|
||||
if max_score > 0.0 {
|
||||
for node in self.nodes.values_mut() {
|
||||
node.attention_score /= max_score;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get most important neighbors of a node
|
||||
pub fn important_neighbors(&self, node_id: &str, top_k: usize) -> Vec<(&GraphNode, f32)> {
|
||||
let mut neighbors: Vec<_> = self.edges.iter()
|
||||
.filter(|e| e.source == node_id || e.target == node_id)
|
||||
.filter_map(|e| {
|
||||
let other = if e.source == node_id { &e.target } else { &e.source };
|
||||
self.nodes.get(other).map(|n| (n, e.weight))
|
||||
})
|
||||
.collect();
|
||||
|
||||
neighbors.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
|
||||
neighbors.truncate(top_k);
|
||||
neighbors
|
||||
}
|
||||
|
||||
/// Learn relationship importance from interaction patterns
|
||||
pub fn learn_edge_importance(&mut self, source: &str, target: &str, success: bool) {
|
||||
if let Some(edge) = self.edges.iter_mut()
|
||||
.find(|e| e.source == source && e.target == target)
|
||||
{
|
||||
let reward = if success { 1.0 } else { 0.0 };
|
||||
edge.weight = edge.weight + 0.1 * (reward - edge.weight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// STATE SPACE ATTENTION - "What history still matters?"
|
||||
// ============================================================================
|
||||
|
||||
/// State space model for context persistence and history relevance.
|
||||
/// Used for: Session memory, conversation context, learning trajectories.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StateSpaceAttention {
|
||||
/// Hidden state dimension
|
||||
state_dim: usize,
|
||||
/// Current hidden state
|
||||
hidden_state: Vec<f32>,
|
||||
/// State transition matrix (learned)
|
||||
transition: Vec<Vec<f32>>,
|
||||
/// Input projection
|
||||
input_proj: Vec<Vec<f32>>,
|
||||
/// Output projection
|
||||
output_proj: Vec<Vec<f32>>,
|
||||
/// History buffer with decay
|
||||
history: Vec<HistoryEntry>,
|
||||
/// Maximum history length
|
||||
max_history: usize,
|
||||
/// Decay factor for old history
|
||||
decay: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct HistoryEntry {
|
||||
pub content: String,
|
||||
pub state: Vec<f32>,
|
||||
pub relevance: f32,
|
||||
pub timestamp: u64,
|
||||
}
|
||||
|
||||
impl StateSpaceAttention {
|
||||
pub fn new(state_dim: usize, max_history: usize) -> Self {
|
||||
// Initialize with identity-like transition
|
||||
let mut transition = vec![vec![0.0; state_dim]; state_dim];
|
||||
for i in 0..state_dim {
|
||||
transition[i][i] = 0.9; // Slight decay
|
||||
}
|
||||
|
||||
Self {
|
||||
state_dim,
|
||||
hidden_state: vec![0.0; state_dim],
|
||||
transition,
|
||||
input_proj: vec![vec![0.1; state_dim]; state_dim],
|
||||
output_proj: vec![vec![0.1; state_dim]; state_dim],
|
||||
history: Vec::new(),
|
||||
max_history,
|
||||
decay: 0.95,
|
||||
}
|
||||
}
|
||||
|
||||
/// Update state with new input
|
||||
pub fn update(&mut self, input: &[f32], content: &str) {
|
||||
// State transition: h_t = A * h_{t-1} + B * x_t
|
||||
let mut new_state = vec![0.0; self.state_dim];
|
||||
|
||||
// Apply transition matrix
|
||||
for i in 0..self.state_dim {
|
||||
for j in 0..self.state_dim {
|
||||
new_state[i] += self.transition[i][j] * self.hidden_state[j];
|
||||
}
|
||||
}
|
||||
|
||||
// Add input contribution
|
||||
let input_len = input.len().min(self.state_dim);
|
||||
for i in 0..input_len {
|
||||
for j in 0..self.state_dim {
|
||||
new_state[j] += self.input_proj[j][i % self.state_dim] * input[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize
|
||||
let norm: f32 = new_state.iter().map(|x| x * x).sum::<f32>().sqrt();
|
||||
if norm > 0.0 {
|
||||
for x in &mut new_state {
|
||||
*x /= norm;
|
||||
}
|
||||
}
|
||||
|
||||
// Store in history
|
||||
self.history.push(HistoryEntry {
|
||||
content: content.to_string(),
|
||||
state: self.hidden_state.clone(),
|
||||
relevance: 1.0,
|
||||
timestamp: std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap_or_default()
|
||||
.as_secs(),
|
||||
});
|
||||
|
||||
// Trim history
|
||||
while self.history.len() > self.max_history {
|
||||
self.history.remove(0);
|
||||
}
|
||||
|
||||
// Apply decay to old entries
|
||||
for entry in &mut self.history {
|
||||
entry.relevance *= self.decay;
|
||||
}
|
||||
|
||||
self.hidden_state = new_state;
|
||||
}
|
||||
|
||||
/// Query what history is still relevant
|
||||
pub fn relevant_history(&self, query: &[f32], top_k: usize) -> Vec<&HistoryEntry> {
|
||||
let mut scored: Vec<_> = self.history.iter()
|
||||
.map(|entry| {
|
||||
// Cosine similarity between query and historical state
|
||||
let dot: f32 = query.iter()
|
||||
.zip(entry.state.iter())
|
||||
.map(|(q, s)| q * s)
|
||||
.sum();
|
||||
let query_norm: f32 = query.iter().map(|x| x * x).sum::<f32>().sqrt();
|
||||
let state_norm: f32 = entry.state.iter().map(|x| x * x).sum::<f32>().sqrt();
|
||||
let similarity = if query_norm > 0.0 && state_norm > 0.0 {
|
||||
dot / (query_norm * state_norm)
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
(entry, similarity * entry.relevance)
|
||||
})
|
||||
.collect();
|
||||
|
||||
scored.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
|
||||
scored.truncate(top_k);
|
||||
scored.into_iter().map(|(e, _)| e).collect()
|
||||
}
|
||||
|
||||
/// Get current state
|
||||
pub fn current_state(&self) -> &[f32] {
|
||||
&self.hidden_state
|
||||
}
|
||||
|
||||
/// Learn from successful context usage
|
||||
pub fn reinforce_history(&mut self, content: &str, success: bool) {
|
||||
if let Some(entry) = self.history.iter_mut().find(|e| e.content == content) {
|
||||
let reward = if success { 1.5 } else { 0.5 };
|
||||
entry.relevance = (entry.relevance * reward).min(1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// UNIFIED ATTENTION ORCHESTRATOR
|
||||
// ============================================================================
|
||||
|
||||
/// Combines all four attention mechanisms for comprehensive code understanding.
|
||||
pub struct AttentionOrchestrator {
|
||||
pub neural: NeuralAttention,
|
||||
pub dag: DagAttention,
|
||||
pub graph: GraphAttention,
|
||||
pub state_space: StateSpaceAttention,
|
||||
}
|
||||
|
||||
impl AttentionOrchestrator {
|
||||
pub fn new(embedding_dim: usize, state_dim: usize, max_history: usize) -> Self {
|
||||
Self {
|
||||
neural: NeuralAttention::new(embedding_dim),
|
||||
dag: DagAttention::new(),
|
||||
graph: GraphAttention::new(4), // 4 attention heads
|
||||
state_space: StateSpaceAttention::new(state_dim, max_history),
|
||||
}
|
||||
}
|
||||
|
||||
/// Answer all four attention questions for a given context
|
||||
pub fn analyze(&mut self, query: &str, file: &str) -> AttentionAnalysis {
|
||||
AttentionAnalysis {
|
||||
words_that_matter: self.analyze_words(query),
|
||||
steps_that_matter: self.analyze_steps(file),
|
||||
relationships_that_matter: self.analyze_relationships(file),
|
||||
history_that_matters: self.analyze_history(query),
|
||||
}
|
||||
}
|
||||
|
||||
fn analyze_words(&self, query: &str) -> Vec<(String, f32)> {
|
||||
query.split_whitespace()
|
||||
.map(|word| (word.to_string(), self.neural.token_importance(word)))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn analyze_steps(&self, file: &str) -> Vec<String> {
|
||||
self.dag.compute_order()
|
||||
.iter()
|
||||
.filter(|node| !node.completed)
|
||||
.take(5)
|
||||
.map(|node| node.id.clone())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn analyze_relationships(&self, file: &str) -> Vec<String> {
|
||||
self.graph.important_neighbors(file, 5)
|
||||
.iter()
|
||||
.map(|(node, _)| node.id.clone())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn analyze_history(&self, query: &str) -> Vec<String> {
|
||||
// Simple embedding from query
|
||||
let query_embedding: Vec<f32> = query.chars()
|
||||
.take(64)
|
||||
.map(|c| (c as u8 as f32) / 255.0)
|
||||
.collect();
|
||||
|
||||
self.state_space.relevant_history(&query_embedding, 5)
|
||||
.iter()
|
||||
.map(|entry| entry.content.clone())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// Result of attention analysis
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AttentionAnalysis {
|
||||
/// Neural attention: What words matter?
|
||||
pub words_that_matter: Vec<(String, f32)>,
|
||||
/// DAG attention: What steps matter?
|
||||
pub steps_that_matter: Vec<String>,
|
||||
/// Graph attention: What relationships matter?
|
||||
pub relationships_that_matter: Vec<String>,
|
||||
/// State space: What history still matters?
|
||||
pub history_that_matters: Vec<String>,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for AttentionAnalysis {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
writeln!(f, "🧠 Attention Analysis")?;
|
||||
writeln!(f, "━━━━━━━━━━━━━━━━━━━━")?;
|
||||
|
||||
writeln!(f, "\n📝 Neural Attention (What words matter?):")?;
|
||||
for (word, score) in &self.words_that_matter {
|
||||
writeln!(f, " • {} ({:.2})", word, score)?;
|
||||
}
|
||||
|
||||
writeln!(f, "\n📊 DAG Attention (What steps matter?):")?;
|
||||
for step in &self.steps_that_matter {
|
||||
writeln!(f, " → {}", step)?;
|
||||
}
|
||||
|
||||
writeln!(f, "\n🔗 Graph Attention (What relationships matter?):")?;
|
||||
for rel in &self.relationships_that_matter {
|
||||
writeln!(f, " ↔ {}", rel)?;
|
||||
}
|
||||
|
||||
writeln!(f, "\n📚 State Space (What history still matters?):")?;
|
||||
for hist in &self.history_that_matters {
|
||||
writeln!(f, " ◷ {}", hist)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_neural_attention() {
|
||||
let mut attn = NeuralAttention::new(64);
|
||||
attn.learn_token_importance("fn", true);
|
||||
attn.learn_token_importance("fn", true);
|
||||
assert!(attn.token_importance("fn") > 0.5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dag_attention() {
|
||||
let mut dag = DagAttention::new();
|
||||
let config = dag.add_step("config", StepType::Config);
|
||||
let build = dag.add_step("build", StepType::Build);
|
||||
let test = dag.add_step("test", StepType::Test);
|
||||
|
||||
dag.add_dependency(config, build, 1.0);
|
||||
dag.add_dependency(build, test, 1.0);
|
||||
|
||||
let order = dag.compute_order();
|
||||
assert_eq!(order.len(), 3);
|
||||
assert_eq!(order[0].id, "config");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_graph_attention() {
|
||||
let mut graph = GraphAttention::new(4);
|
||||
graph.add_node("main.rs", NodeType::File, vec![1.0; 64]);
|
||||
graph.add_node("lib.rs", NodeType::File, vec![0.5; 64]);
|
||||
graph.add_edge("main.rs", "lib.rs", EdgeType::Imports);
|
||||
|
||||
graph.compute_attention("main.rs");
|
||||
let neighbors = graph.important_neighbors("main.rs", 5);
|
||||
assert!(!neighbors.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_state_space_attention() {
|
||||
let mut ssm = StateSpaceAttention::new(32, 100);
|
||||
ssm.update(&[0.5; 32], "First context");
|
||||
ssm.update(&[0.7; 32], "Second context");
|
||||
|
||||
let relevant = ssm.relevant_history(&[0.6; 32], 2);
|
||||
assert!(!relevant.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_attention_orchestrator() {
|
||||
let mut orch = AttentionOrchestrator::new(64, 32, 100);
|
||||
let analysis = orch.analyze("implement error handling", "src/lib.rs");
|
||||
|
||||
assert!(!analysis.words_that_matter.is_empty());
|
||||
println!("{}", analysis);
|
||||
}
|
||||
}
|
||||
28
vendor/ruvector/examples/edge-net/src/learning-scenarios/diverse-patterns/config.yaml
vendored
Normal file
28
vendor/ruvector/examples/edge-net/src/learning-scenarios/diverse-patterns/config.yaml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
# Learning Patterns Configuration
|
||||
# This file teaches the hooks system about YAML file handling
|
||||
|
||||
learning:
|
||||
enabled: true
|
||||
rate: 0.1
|
||||
patterns:
|
||||
- type: yaml_config
|
||||
confidence_threshold: 0.7
|
||||
- type: rust_source
|
||||
confidence_threshold: 0.8
|
||||
- type: typescript
|
||||
confidence_threshold: 0.75
|
||||
|
||||
agent_routing:
|
||||
default_agent: coder
|
||||
file_mappings:
|
||||
"*.rs": rust-developer
|
||||
"*.ts": typescript-developer
|
||||
"*.yaml": config-specialist
|
||||
"*.json": data-analyst
|
||||
"*.sh": devops-engineer
|
||||
"*.md": documentation-writer
|
||||
|
||||
metrics:
|
||||
track_q_values: true
|
||||
track_trajectories: true
|
||||
export_interval: 300
|
||||
53
vendor/ruvector/examples/edge-net/src/learning-scenarios/diverse-patterns/patterns.json
vendored
Normal file
53
vendor/ruvector/examples/edge-net/src/learning-scenarios/diverse-patterns/patterns.json
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"schema_version": "1.0.0",
|
||||
"description": "Pre-defined learning patterns for agent routing",
|
||||
"patterns": {
|
||||
"rust_development": {
|
||||
"file_extensions": [".rs", ".toml"],
|
||||
"suggested_agents": ["rust-developer", "coder"],
|
||||
"learned_sequences": [
|
||||
["Cargo.toml", "src/lib.rs", "src/main.rs"],
|
||||
["lib.rs", "mod.rs", "types.rs"]
|
||||
]
|
||||
},
|
||||
"typescript_development": {
|
||||
"file_extensions": [".ts", ".tsx", ".js"],
|
||||
"suggested_agents": ["typescript-developer", "frontend-dev"],
|
||||
"learned_sequences": [
|
||||
["package.json", "tsconfig.json", "src/index.ts"],
|
||||
["types.ts", "utils.ts", "index.ts"]
|
||||
]
|
||||
},
|
||||
"documentation": {
|
||||
"file_extensions": [".md", ".mdx", ".rst"],
|
||||
"suggested_agents": ["documentation-writer", "researcher"],
|
||||
"learned_sequences": [
|
||||
["README.md", "CONTRIBUTING.md", "docs/index.md"]
|
||||
]
|
||||
},
|
||||
"devops": {
|
||||
"file_extensions": [".yaml", ".yml", ".sh", ".dockerfile"],
|
||||
"suggested_agents": ["devops-engineer", "cicd-engineer"],
|
||||
"learned_sequences": [
|
||||
[".github/workflows/ci.yml", "Dockerfile", "docker-compose.yml"]
|
||||
]
|
||||
}
|
||||
},
|
||||
"error_recovery_patterns": {
|
||||
"E0308": {
|
||||
"description": "Rust type mismatch",
|
||||
"suggested_fix": "Check type annotations and conversions",
|
||||
"agent": "rust-developer"
|
||||
},
|
||||
"E0433": {
|
||||
"description": "Rust unresolved import",
|
||||
"suggested_fix": "Add missing use statement or dependency",
|
||||
"agent": "rust-developer"
|
||||
},
|
||||
"TS2339": {
|
||||
"description": "TypeScript property does not exist",
|
||||
"suggested_fix": "Add property to type definition",
|
||||
"agent": "typescript-developer"
|
||||
}
|
||||
}
|
||||
}
|
||||
69
vendor/ruvector/examples/edge-net/src/learning-scenarios/diverse-patterns/setup.sh
vendored
Executable file
69
vendor/ruvector/examples/edge-net/src/learning-scenarios/diverse-patterns/setup.sh
vendored
Executable file
@@ -0,0 +1,69 @@
|
||||
#!/bin/bash
|
||||
# Learning Scenarios Setup Script
|
||||
# This teaches the hooks system about shell script handling
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../../.." && pwd)"
|
||||
|
||||
echo "🧠 RuVector Learning Scenarios Setup"
|
||||
echo "====================================="
|
||||
|
||||
# Function to initialize learning patterns
|
||||
init_patterns() {
|
||||
echo "📊 Initializing learning patterns..."
|
||||
|
||||
# Check if intelligence file exists
|
||||
if [[ -f "$PROJECT_ROOT/.ruvector/intelligence.json" ]]; then
|
||||
local pattern_count=$(jq '.patterns | length' "$PROJECT_ROOT/.ruvector/intelligence.json" 2>/dev/null || echo "0")
|
||||
echo " Found $pattern_count existing patterns"
|
||||
else
|
||||
echo " No existing patterns, starting fresh"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to record a learning event
|
||||
record_event() {
|
||||
local event_type="$1"
|
||||
local file_path="$2"
|
||||
local outcome="${3:-success}"
|
||||
|
||||
echo "📝 Recording: $event_type on $file_path ($outcome)"
|
||||
|
||||
# Use ruvector-cli if available
|
||||
if command -v ruvector-cli &>/dev/null; then
|
||||
ruvector-cli hooks remember "$event_type: $file_path" -t "$event_type" 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to simulate diverse file operations
|
||||
simulate_diversity() {
|
||||
echo "🔄 Simulating diverse file operations..."
|
||||
|
||||
local file_types=(
|
||||
"rs:rust-developer"
|
||||
"ts:typescript-developer"
|
||||
"yaml:config-specialist"
|
||||
"json:data-analyst"
|
||||
"sh:devops-engineer"
|
||||
"md:documentation-writer"
|
||||
)
|
||||
|
||||
for entry in "${file_types[@]}"; do
|
||||
IFS=':' read -r ext agent <<< "$entry"
|
||||
echo " .$ext -> $agent"
|
||||
done
|
||||
}
|
||||
|
||||
# Main execution
|
||||
main() {
|
||||
init_patterns
|
||||
simulate_diversity
|
||||
|
||||
echo ""
|
||||
echo "✅ Learning scenarios initialized"
|
||||
echo " Run 'ruvector hooks stats' to see current patterns"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
76
vendor/ruvector/examples/edge-net/src/learning-scenarios/diverse-patterns/types.ts
vendored
Normal file
76
vendor/ruvector/examples/edge-net/src/learning-scenarios/diverse-patterns/types.ts
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Learning Pattern Types for RuVector Intelligence
|
||||
* This teaches the hooks system about TypeScript file handling
|
||||
*/
|
||||
|
||||
export interface LearningPattern {
|
||||
state: string;
|
||||
action: string;
|
||||
qValue: number;
|
||||
visits: number;
|
||||
lastUpdate: number;
|
||||
}
|
||||
|
||||
export interface VectorMemory {
|
||||
id: string;
|
||||
memoryType: 'edit' | 'file_access' | 'command' | 'search_pattern' | 'agent_spawn';
|
||||
content: string;
|
||||
embedding: number[];
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
export interface Trajectory {
|
||||
id: string;
|
||||
state: string;
|
||||
action: string;
|
||||
outcome: 'completed' | 'failed' | 'partial';
|
||||
reward: number;
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
export interface AgentRouting {
|
||||
filePattern: RegExp;
|
||||
agentType: string;
|
||||
confidence: number;
|
||||
}
|
||||
|
||||
export type CognitivePattern =
|
||||
| 'convergent'
|
||||
| 'divergent'
|
||||
| 'lateral'
|
||||
| 'systems'
|
||||
| 'critical'
|
||||
| 'adaptive';
|
||||
|
||||
export class IntelligenceLayer {
|
||||
private patterns: Map<string, LearningPattern> = new Map();
|
||||
private memories: VectorMemory[] = [];
|
||||
private trajectories: Trajectory[] = [];
|
||||
|
||||
recordPattern(state: string, action: string, reward: number): void {
|
||||
const key = `${state}|${action}`;
|
||||
const existing = this.patterns.get(key);
|
||||
|
||||
if (existing) {
|
||||
// Q-learning update
|
||||
existing.qValue = existing.qValue + 0.1 * (reward - existing.qValue);
|
||||
existing.visits++;
|
||||
existing.lastUpdate = Date.now();
|
||||
} else {
|
||||
this.patterns.set(key, {
|
||||
state,
|
||||
action,
|
||||
qValue: reward * 0.1,
|
||||
visits: 1,
|
||||
lastUpdate: Date.now()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
suggestAgent(filePath: string): string {
|
||||
if (filePath.endsWith('.rs')) return 'rust-developer';
|
||||
if (filePath.endsWith('.ts')) return 'typescript-developer';
|
||||
if (filePath.endsWith('.yaml')) return 'config-specialist';
|
||||
return 'coder';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
//! Error Pattern Learning Module
|
||||
//!
|
||||
//! This module intentionally includes patterns that might cause errors
|
||||
//! to teach the self-learning system about error recovery strategies.
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Error pattern types for learning
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ErrorPattern {
|
||||
/// Type mismatch errors (E0308)
|
||||
TypeMismatch { expected: String, found: String },
|
||||
/// Unresolved import errors (E0433)
|
||||
UnresolvedImport { path: String },
|
||||
/// Borrow checker errors (E0502)
|
||||
BorrowConflict { variable: String },
|
||||
/// Missing trait implementation (E0277)
|
||||
MissingTrait { trait_name: String, type_name: String },
|
||||
}
|
||||
|
||||
/// Recovery strategy for each error type
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RecoveryStrategy {
|
||||
pub error_code: String,
|
||||
pub description: String,
|
||||
pub fix_steps: Vec<String>,
|
||||
pub suggested_agent: String,
|
||||
}
|
||||
|
||||
impl RecoveryStrategy {
|
||||
pub fn for_error(pattern: &ErrorPattern) -> Self {
|
||||
match pattern {
|
||||
ErrorPattern::TypeMismatch { expected, found } => Self {
|
||||
error_code: "E0308".into(),
|
||||
description: format!("Expected {}, found {}", expected, found),
|
||||
fix_steps: vec![
|
||||
"Check variable type annotations".into(),
|
||||
"Add explicit type conversion".into(),
|
||||
"Use .into() or .as_ref() as needed".into(),
|
||||
],
|
||||
suggested_agent: "rust-developer".into(),
|
||||
},
|
||||
ErrorPattern::UnresolvedImport { path } => Self {
|
||||
error_code: "E0433".into(),
|
||||
description: format!("Failed to resolve: {}", path),
|
||||
fix_steps: vec![
|
||||
"Add missing dependency to Cargo.toml".into(),
|
||||
"Check module path spelling".into(),
|
||||
"Ensure pub visibility".into(),
|
||||
],
|
||||
suggested_agent: "rust-developer".into(),
|
||||
},
|
||||
ErrorPattern::BorrowConflict { variable } => Self {
|
||||
error_code: "E0502".into(),
|
||||
description: format!("Borrow conflict on {}", variable),
|
||||
fix_steps: vec![
|
||||
"Clone the value if ownership is needed".into(),
|
||||
"Use RefCell for interior mutability".into(),
|
||||
"Restructure code to limit borrow scope".into(),
|
||||
],
|
||||
suggested_agent: "rust-developer".into(),
|
||||
},
|
||||
ErrorPattern::MissingTrait { trait_name, type_name } => Self {
|
||||
error_code: "E0277".into(),
|
||||
description: format!("{} not implemented for {}", trait_name, type_name),
|
||||
fix_steps: vec![
|
||||
"Derive the trait if possible".into(),
|
||||
"Implement the trait manually".into(),
|
||||
"Use a wrapper type that implements it".into(),
|
||||
],
|
||||
suggested_agent: "rust-developer".into(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Learning tracker for error patterns
|
||||
pub struct ErrorLearningTracker {
|
||||
patterns: HashMap<String, u32>,
|
||||
recoveries: HashMap<String, Vec<RecoveryStrategy>>,
|
||||
}
|
||||
|
||||
impl ErrorLearningTracker {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
patterns: HashMap::new(),
|
||||
recoveries: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Record an error occurrence for learning
|
||||
pub fn record_error(&mut self, error_code: &str) {
|
||||
*self.patterns.entry(error_code.to_string()).or_insert(0) += 1;
|
||||
}
|
||||
|
||||
/// Record a successful recovery for learning
|
||||
pub fn record_recovery(&mut self, error_code: &str, strategy: RecoveryStrategy) {
|
||||
self.recoveries
|
||||
.entry(error_code.to_string())
|
||||
.or_default()
|
||||
.push(strategy);
|
||||
}
|
||||
|
||||
/// Get the most successful recovery strategy for an error
|
||||
pub fn best_recovery(&self, error_code: &str) -> Option<&RecoveryStrategy> {
|
||||
self.recoveries.get(error_code).and_then(|v| v.last())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ErrorLearningTracker {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_recovery_strategy_for_type_mismatch() {
|
||||
let pattern = ErrorPattern::TypeMismatch {
|
||||
expected: "u32".into(),
|
||||
found: "i32".into(),
|
||||
};
|
||||
let strategy = RecoveryStrategy::for_error(&pattern);
|
||||
assert_eq!(strategy.error_code, "E0308");
|
||||
assert_eq!(strategy.suggested_agent, "rust-developer");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_learning_tracker() {
|
||||
let mut tracker = ErrorLearningTracker::new();
|
||||
tracker.record_error("E0308");
|
||||
tracker.record_error("E0308");
|
||||
|
||||
assert_eq!(tracker.patterns.get("E0308"), Some(&2));
|
||||
}
|
||||
}
|
||||
3
vendor/ruvector/examples/edge-net/src/learning-scenarios/error_recovery/mod.rs
vendored
Normal file
3
vendor/ruvector/examples/edge-net/src/learning-scenarios/error_recovery/mod.rs
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
//! Error Recovery Learning Submodule
|
||||
|
||||
pub mod error_patterns;
|
||||
@@ -0,0 +1,219 @@
|
||||
//! File Sequence Learning Module
|
||||
//!
|
||||
//! Tracks the order in which files are edited to learn optimal
|
||||
//! multi-file refactoring patterns.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
/// Represents a file edit event
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FileEdit {
|
||||
pub file_path: String,
|
||||
pub file_type: String,
|
||||
pub crate_name: Option<String>,
|
||||
pub timestamp: u64,
|
||||
pub success: bool,
|
||||
}
|
||||
|
||||
/// A sequence of file edits that form a pattern
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EditSequence {
|
||||
pub id: String,
|
||||
pub files: Vec<String>,
|
||||
pub pattern_type: SequencePattern,
|
||||
pub occurrences: u32,
|
||||
pub avg_success_rate: f64,
|
||||
}
|
||||
|
||||
/// Types of editing patterns we can learn
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum SequencePattern {
|
||||
/// Cargo.toml -> lib.rs -> specific modules
|
||||
RustCrateSetup,
|
||||
/// Types first, then implementation, then tests
|
||||
TypesFirstDevelopment,
|
||||
/// Tests first, then implementation (TDD)
|
||||
TestDrivenDevelopment,
|
||||
/// Config files, then source, then docs
|
||||
FullStackChange,
|
||||
/// Unknown pattern being learned
|
||||
Learning,
|
||||
}
|
||||
|
||||
/// Tracks file sequences for learning
|
||||
pub struct SequenceTracker {
|
||||
current_sequence: Vec<FileEdit>,
|
||||
learned_sequences: HashMap<String, EditSequence>,
|
||||
pattern_confidence: HashMap<SequencePattern, f64>,
|
||||
}
|
||||
|
||||
impl SequenceTracker {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
current_sequence: Vec::new(),
|
||||
learned_sequences: HashMap::new(),
|
||||
pattern_confidence: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Record a file edit in the current sequence
|
||||
pub fn record_edit(&mut self, file_path: &str, success: bool) {
|
||||
let file_type = Self::detect_file_type(file_path);
|
||||
let crate_name = Self::extract_crate_name(file_path);
|
||||
|
||||
let edit = FileEdit {
|
||||
file_path: file_path.to_string(),
|
||||
file_type,
|
||||
crate_name,
|
||||
timestamp: SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs(),
|
||||
success,
|
||||
};
|
||||
|
||||
self.current_sequence.push(edit);
|
||||
|
||||
// Check if we've completed a recognizable pattern
|
||||
if let Some(pattern) = self.detect_pattern() {
|
||||
self.learn_pattern(pattern);
|
||||
}
|
||||
}
|
||||
|
||||
/// Detect file type from extension
|
||||
fn detect_file_type(path: &str) -> String {
|
||||
if path.ends_with(".rs") { "rust".into() }
|
||||
else if path.ends_with(".ts") { "typescript".into() }
|
||||
else if path.ends_with(".toml") { "toml".into() }
|
||||
else if path.ends_with(".json") { "json".into() }
|
||||
else if path.ends_with(".yaml") || path.ends_with(".yml") { "yaml".into() }
|
||||
else if path.ends_with(".md") { "markdown".into() }
|
||||
else if path.ends_with(".sh") { "shell".into() }
|
||||
else { "unknown".into() }
|
||||
}
|
||||
|
||||
/// Extract crate name from path
|
||||
fn extract_crate_name(path: &str) -> Option<String> {
|
||||
// Look for patterns like crates/ruvector-*/
|
||||
if path.contains("crates/") {
|
||||
path.split("crates/")
|
||||
.nth(1)
|
||||
.and_then(|s| s.split('/').next())
|
||||
.map(|s| s.to_string())
|
||||
} else if path.contains("ruvector-") {
|
||||
path.split("ruvector-")
|
||||
.nth(1)
|
||||
.and_then(|s| s.split('/').next())
|
||||
.map(|s| format!("ruvector-{}", s))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Detect if current sequence matches a known pattern
|
||||
fn detect_pattern(&self) -> Option<SequencePattern> {
|
||||
let files: Vec<&str> = self.current_sequence
|
||||
.iter()
|
||||
.map(|e| e.file_path.as_str())
|
||||
.collect();
|
||||
|
||||
if files.len() < 2 {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Detect Rust crate setup pattern
|
||||
if files.iter().any(|f| f.ends_with("Cargo.toml"))
|
||||
&& files.iter().any(|f| f.ends_with("lib.rs")) {
|
||||
return Some(SequencePattern::RustCrateSetup);
|
||||
}
|
||||
|
||||
// Detect TDD pattern
|
||||
if files.iter().any(|f| f.contains("test"))
|
||||
&& files.iter().position(|f| f.contains("test"))
|
||||
< files.iter().position(|f| f.ends_with("lib.rs") || f.ends_with("mod.rs")) {
|
||||
return Some(SequencePattern::TestDrivenDevelopment);
|
||||
}
|
||||
|
||||
// Detect types-first pattern
|
||||
if files.iter().any(|f| f.contains("types"))
|
||||
&& files.iter().position(|f| f.contains("types")).unwrap_or(999) < 2 {
|
||||
return Some(SequencePattern::TypesFirstDevelopment);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Learn from a detected pattern
|
||||
fn learn_pattern(&mut self, pattern: SequencePattern) {
|
||||
let confidence = self.pattern_confidence.entry(pattern.clone()).or_insert(0.5);
|
||||
|
||||
// Increase confidence if all edits in sequence were successful
|
||||
let success_rate = self.current_sequence.iter()
|
||||
.filter(|e| e.success)
|
||||
.count() as f64 / self.current_sequence.len() as f64;
|
||||
|
||||
// Q-learning style update
|
||||
*confidence = *confidence + 0.1 * (success_rate - *confidence);
|
||||
|
||||
// Clear sequence after learning
|
||||
self.current_sequence.clear();
|
||||
}
|
||||
|
||||
/// Suggest the next file to edit based on learned patterns
|
||||
pub fn suggest_next_file(&self, current_file: &str) -> Option<String> {
|
||||
let file_type = Self::detect_file_type(current_file);
|
||||
|
||||
match file_type.as_str() {
|
||||
"toml" if current_file.contains("Cargo") => {
|
||||
Some("src/lib.rs".into())
|
||||
}
|
||||
"rust" if current_file.contains("types") => {
|
||||
Some("src/lib.rs".into())
|
||||
}
|
||||
"rust" if current_file.contains("lib.rs") => {
|
||||
Some("src/tests.rs".into())
|
||||
}
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
/// Get learned patterns with their confidence scores
|
||||
pub fn get_pattern_confidence(&self) -> &HashMap<SequencePattern, f64> {
|
||||
&self.pattern_confidence
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SequenceTracker {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_file_type_detection() {
|
||||
assert_eq!(SequenceTracker::detect_file_type("src/lib.rs"), "rust");
|
||||
assert_eq!(SequenceTracker::detect_file_type("config.yaml"), "yaml");
|
||||
assert_eq!(SequenceTracker::detect_file_type("types.ts"), "typescript");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_crate_name_extraction() {
|
||||
let name = SequenceTracker::extract_crate_name("crates/ruvector-core/src/lib.rs");
|
||||
assert_eq!(name, Some("ruvector-core".into()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sequence_tracking() {
|
||||
let mut tracker = SequenceTracker::new();
|
||||
tracker.record_edit("Cargo.toml", true);
|
||||
tracker.record_edit("src/lib.rs", true);
|
||||
|
||||
assert!(!tracker.current_sequence.is_empty() ||
|
||||
tracker.pattern_confidence.contains_key(&SequencePattern::RustCrateSetup));
|
||||
}
|
||||
}
|
||||
3
vendor/ruvector/examples/edge-net/src/learning-scenarios/file_sequences/mod.rs
vendored
Normal file
3
vendor/ruvector/examples/edge-net/src/learning-scenarios/file_sequences/mod.rs
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
//! File Sequence Learning Submodule
|
||||
|
||||
pub mod sequence_tracker;
|
||||
532
vendor/ruvector/examples/edge-net/src/learning-scenarios/mcp_tools.rs
vendored
Normal file
532
vendor/ruvector/examples/edge-net/src/learning-scenarios/mcp_tools.rs
vendored
Normal file
@@ -0,0 +1,532 @@
|
||||
//! Enhanced MCP Tools for RuVector Learning Intelligence
|
||||
//!
|
||||
//! Provides MCP tool definitions that integrate with the self-learning
|
||||
//! hooks system for intelligent code assistance.
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// MCP Tool definition for RuVector intelligence features
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct McpToolDef {
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub input_schema: ToolInputSchema,
|
||||
pub category: ToolCategory,
|
||||
}
|
||||
|
||||
/// Tool input schema
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ToolInputSchema {
|
||||
pub required: Vec<String>,
|
||||
pub properties: HashMap<String, PropertyDef>,
|
||||
}
|
||||
|
||||
/// Property definition for tool inputs
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PropertyDef {
|
||||
pub prop_type: String,
|
||||
pub description: String,
|
||||
pub default: Option<String>,
|
||||
pub enum_values: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
/// Tool categories for organization
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ToolCategory {
|
||||
/// Vector database operations
|
||||
VectorDb,
|
||||
/// Learning and intelligence
|
||||
Learning,
|
||||
/// Memory and recall
|
||||
Memory,
|
||||
/// Swarm coordination
|
||||
Swarm,
|
||||
/// Telemetry and metrics
|
||||
Telemetry,
|
||||
/// Agent routing
|
||||
AgentRouting,
|
||||
}
|
||||
|
||||
impl ToolCategory {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::VectorDb => "vector_db",
|
||||
Self::Learning => "learning",
|
||||
Self::Memory => "memory",
|
||||
Self::Swarm => "swarm",
|
||||
Self::Telemetry => "telemetry",
|
||||
Self::AgentRouting => "agent_routing",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get all RuVector MCP tools
|
||||
pub fn get_ruvector_tools() -> Vec<McpToolDef> {
|
||||
vec![
|
||||
// === Learning Intelligence Tools ===
|
||||
McpToolDef {
|
||||
name: "ruvector_learn_pattern".into(),
|
||||
description: "Record a Q-learning pattern for agent routing optimization".into(),
|
||||
input_schema: ToolInputSchema {
|
||||
required: vec!["state".into(), "action".into()],
|
||||
properties: [
|
||||
("state".into(), PropertyDef {
|
||||
prop_type: "string".into(),
|
||||
description: "State identifier (e.g., edit_rs_in_crate)".into(),
|
||||
default: None,
|
||||
enum_values: None,
|
||||
}),
|
||||
("action".into(), PropertyDef {
|
||||
prop_type: "string".into(),
|
||||
description: "Action taken (e.g., successful-edit, rust-developer)".into(),
|
||||
default: None,
|
||||
enum_values: None,
|
||||
}),
|
||||
("reward".into(), PropertyDef {
|
||||
prop_type: "number".into(),
|
||||
description: "Reward value (-1.0 to 1.0)".into(),
|
||||
default: Some("1.0".into()),
|
||||
enum_values: None,
|
||||
}),
|
||||
].into_iter().collect(),
|
||||
},
|
||||
category: ToolCategory::Learning,
|
||||
},
|
||||
McpToolDef {
|
||||
name: "ruvector_suggest_agent".into(),
|
||||
description: "Get recommended agent for a task based on learned patterns".into(),
|
||||
input_schema: ToolInputSchema {
|
||||
required: vec!["task".into()],
|
||||
properties: [
|
||||
("task".into(), PropertyDef {
|
||||
prop_type: "string".into(),
|
||||
description: "Task description".into(),
|
||||
default: None,
|
||||
enum_values: None,
|
||||
}),
|
||||
("file".into(), PropertyDef {
|
||||
prop_type: "string".into(),
|
||||
description: "File being worked on".into(),
|
||||
default: None,
|
||||
enum_values: None,
|
||||
}),
|
||||
("crate_name".into(), PropertyDef {
|
||||
prop_type: "string".into(),
|
||||
description: "Crate/module context".into(),
|
||||
default: None,
|
||||
enum_values: None,
|
||||
}),
|
||||
].into_iter().collect(),
|
||||
},
|
||||
category: ToolCategory::AgentRouting,
|
||||
},
|
||||
McpToolDef {
|
||||
name: "ruvector_record_error".into(),
|
||||
description: "Record an error pattern for learning recovery strategies".into(),
|
||||
input_schema: ToolInputSchema {
|
||||
required: vec!["error_code".into(), "message".into()],
|
||||
properties: [
|
||||
("error_code".into(), PropertyDef {
|
||||
prop_type: "string".into(),
|
||||
description: "Error code (e.g., E0308, TS2322)".into(),
|
||||
default: None,
|
||||
enum_values: None,
|
||||
}),
|
||||
("message".into(), PropertyDef {
|
||||
prop_type: "string".into(),
|
||||
description: "Error message".into(),
|
||||
default: None,
|
||||
enum_values: None,
|
||||
}),
|
||||
("file".into(), PropertyDef {
|
||||
prop_type: "string".into(),
|
||||
description: "File with error".into(),
|
||||
default: None,
|
||||
enum_values: None,
|
||||
}),
|
||||
("fix_applied".into(), PropertyDef {
|
||||
prop_type: "string".into(),
|
||||
description: "Fix that resolved the error".into(),
|
||||
default: None,
|
||||
enum_values: None,
|
||||
}),
|
||||
].into_iter().collect(),
|
||||
},
|
||||
category: ToolCategory::Learning,
|
||||
},
|
||||
McpToolDef {
|
||||
name: "ruvector_suggest_fix".into(),
|
||||
description: "Get suggested fixes for an error code based on learned patterns".into(),
|
||||
input_schema: ToolInputSchema {
|
||||
required: vec!["error_code".into()],
|
||||
properties: [
|
||||
("error_code".into(), PropertyDef {
|
||||
prop_type: "string".into(),
|
||||
description: "Error code to get fixes for".into(),
|
||||
default: None,
|
||||
enum_values: None,
|
||||
}),
|
||||
("context".into(), PropertyDef {
|
||||
prop_type: "string".into(),
|
||||
description: "Additional context (file type, crate)".into(),
|
||||
default: None,
|
||||
enum_values: None,
|
||||
}),
|
||||
].into_iter().collect(),
|
||||
},
|
||||
category: ToolCategory::Learning,
|
||||
},
|
||||
|
||||
// === Memory Tools ===
|
||||
McpToolDef {
|
||||
name: "ruvector_remember".into(),
|
||||
description: "Store content in semantic vector memory for later recall".into(),
|
||||
input_schema: ToolInputSchema {
|
||||
required: vec!["content".into(), "memory_type".into()],
|
||||
properties: [
|
||||
("content".into(), PropertyDef {
|
||||
prop_type: "string".into(),
|
||||
description: "Content to remember".into(),
|
||||
default: None,
|
||||
enum_values: None,
|
||||
}),
|
||||
("memory_type".into(), PropertyDef {
|
||||
prop_type: "string".into(),
|
||||
description: "Type of memory".into(),
|
||||
default: None,
|
||||
enum_values: Some(vec![
|
||||
"edit".into(), "command".into(), "decision".into(),
|
||||
"pattern".into(), "error".into(), "agent_spawn".into(),
|
||||
]),
|
||||
}),
|
||||
("metadata".into(), PropertyDef {
|
||||
prop_type: "object".into(),
|
||||
description: "Additional metadata".into(),
|
||||
default: None,
|
||||
enum_values: None,
|
||||
}),
|
||||
].into_iter().collect(),
|
||||
},
|
||||
category: ToolCategory::Memory,
|
||||
},
|
||||
McpToolDef {
|
||||
name: "ruvector_recall".into(),
|
||||
description: "Search semantic memory for relevant information".into(),
|
||||
input_schema: ToolInputSchema {
|
||||
required: vec!["query".into()],
|
||||
properties: [
|
||||
("query".into(), PropertyDef {
|
||||
prop_type: "string".into(),
|
||||
description: "Search query".into(),
|
||||
default: None,
|
||||
enum_values: None,
|
||||
}),
|
||||
("top_k".into(), PropertyDef {
|
||||
prop_type: "integer".into(),
|
||||
description: "Number of results to return".into(),
|
||||
default: Some("5".into()),
|
||||
enum_values: None,
|
||||
}),
|
||||
("memory_type".into(), PropertyDef {
|
||||
prop_type: "string".into(),
|
||||
description: "Filter by memory type".into(),
|
||||
default: None,
|
||||
enum_values: None,
|
||||
}),
|
||||
].into_iter().collect(),
|
||||
},
|
||||
category: ToolCategory::Memory,
|
||||
},
|
||||
|
||||
// === Swarm Coordination Tools ===
|
||||
McpToolDef {
|
||||
name: "ruvector_swarm_register".into(),
|
||||
description: "Register an agent in the coordination swarm".into(),
|
||||
input_schema: ToolInputSchema {
|
||||
required: vec!["agent_id".into(), "agent_type".into()],
|
||||
properties: [
|
||||
("agent_id".into(), PropertyDef {
|
||||
prop_type: "string".into(),
|
||||
description: "Unique agent identifier".into(),
|
||||
default: None,
|
||||
enum_values: None,
|
||||
}),
|
||||
("agent_type".into(), PropertyDef {
|
||||
prop_type: "string".into(),
|
||||
description: "Type of agent".into(),
|
||||
default: None,
|
||||
enum_values: Some(vec![
|
||||
"researcher".into(), "coder".into(), "tester".into(),
|
||||
"reviewer".into(), "planner".into(), "coordinator".into(),
|
||||
]),
|
||||
}),
|
||||
("capabilities".into(), PropertyDef {
|
||||
prop_type: "array".into(),
|
||||
description: "Agent capabilities".into(),
|
||||
default: None,
|
||||
enum_values: None,
|
||||
}),
|
||||
].into_iter().collect(),
|
||||
},
|
||||
category: ToolCategory::Swarm,
|
||||
},
|
||||
McpToolDef {
|
||||
name: "ruvector_swarm_coordinate".into(),
|
||||
description: "Record coordination between agents for graph learning".into(),
|
||||
input_schema: ToolInputSchema {
|
||||
required: vec!["source".into(), "target".into()],
|
||||
properties: [
|
||||
("source".into(), PropertyDef {
|
||||
prop_type: "string".into(),
|
||||
description: "Source agent ID".into(),
|
||||
default: None,
|
||||
enum_values: None,
|
||||
}),
|
||||
("target".into(), PropertyDef {
|
||||
prop_type: "string".into(),
|
||||
description: "Target agent ID".into(),
|
||||
default: None,
|
||||
enum_values: None,
|
||||
}),
|
||||
("weight".into(), PropertyDef {
|
||||
prop_type: "number".into(),
|
||||
description: "Coordination weight (0.0-1.0)".into(),
|
||||
default: Some("1.0".into()),
|
||||
enum_values: None,
|
||||
}),
|
||||
("success".into(), PropertyDef {
|
||||
prop_type: "boolean".into(),
|
||||
description: "Whether coordination was successful".into(),
|
||||
default: Some("true".into()),
|
||||
enum_values: None,
|
||||
}),
|
||||
].into_iter().collect(),
|
||||
},
|
||||
category: ToolCategory::Swarm,
|
||||
},
|
||||
McpToolDef {
|
||||
name: "ruvector_swarm_optimize".into(),
|
||||
description: "Get optimal task distribution across swarm agents".into(),
|
||||
input_schema: ToolInputSchema {
|
||||
required: vec!["tasks".into()],
|
||||
properties: [
|
||||
("tasks".into(), PropertyDef {
|
||||
prop_type: "array".into(),
|
||||
description: "List of tasks to distribute".into(),
|
||||
default: None,
|
||||
enum_values: None,
|
||||
}),
|
||||
("strategy".into(), PropertyDef {
|
||||
prop_type: "string".into(),
|
||||
description: "Distribution strategy".into(),
|
||||
default: Some("balanced".into()),
|
||||
enum_values: Some(vec![
|
||||
"balanced".into(), "specialized".into(), "adaptive".into(),
|
||||
]),
|
||||
}),
|
||||
].into_iter().collect(),
|
||||
},
|
||||
category: ToolCategory::Swarm,
|
||||
},
|
||||
|
||||
// === Telemetry Tools ===
|
||||
McpToolDef {
|
||||
name: "ruvector_telemetry_config".into(),
|
||||
description: "Configure telemetry settings".into(),
|
||||
input_schema: ToolInputSchema {
|
||||
required: vec![],
|
||||
properties: [
|
||||
("disable_telemetry".into(), PropertyDef {
|
||||
prop_type: "boolean".into(),
|
||||
description: "Disable Statsig metrics".into(),
|
||||
default: Some("false".into()),
|
||||
enum_values: None,
|
||||
}),
|
||||
("disable_error_reporting".into(), PropertyDef {
|
||||
prop_type: "boolean".into(),
|
||||
description: "Disable Sentry error reporting".into(),
|
||||
default: Some("false".into()),
|
||||
enum_values: None,
|
||||
}),
|
||||
("retention_days".into(), PropertyDef {
|
||||
prop_type: "integer".into(),
|
||||
description: "Data retention period in days".into(),
|
||||
default: Some("30".into()),
|
||||
enum_values: None,
|
||||
}),
|
||||
].into_iter().collect(),
|
||||
},
|
||||
category: ToolCategory::Telemetry,
|
||||
},
|
||||
McpToolDef {
|
||||
name: "ruvector_intelligence_stats".into(),
|
||||
description: "Get intelligence layer statistics".into(),
|
||||
input_schema: ToolInputSchema {
|
||||
required: vec![],
|
||||
properties: [
|
||||
("detailed".into(), PropertyDef {
|
||||
prop_type: "boolean".into(),
|
||||
description: "Include detailed breakdown".into(),
|
||||
default: Some("false".into()),
|
||||
enum_values: None,
|
||||
}),
|
||||
("format".into(), PropertyDef {
|
||||
prop_type: "string".into(),
|
||||
description: "Output format".into(),
|
||||
default: Some("json".into()),
|
||||
enum_values: Some(vec!["json".into(), "text".into(), "markdown".into()]),
|
||||
}),
|
||||
].into_iter().collect(),
|
||||
},
|
||||
category: ToolCategory::Telemetry,
|
||||
},
|
||||
|
||||
// === File Sequence Tools ===
|
||||
McpToolDef {
|
||||
name: "ruvector_suggest_next_file".into(),
|
||||
description: "Suggest next files to edit based on learned patterns".into(),
|
||||
input_schema: ToolInputSchema {
|
||||
required: vec!["current_file".into()],
|
||||
properties: [
|
||||
("current_file".into(), PropertyDef {
|
||||
prop_type: "string".into(),
|
||||
description: "Currently edited file".into(),
|
||||
default: None,
|
||||
enum_values: None,
|
||||
}),
|
||||
("count".into(), PropertyDef {
|
||||
prop_type: "integer".into(),
|
||||
description: "Number of suggestions".into(),
|
||||
default: Some("3".into()),
|
||||
enum_values: None,
|
||||
}),
|
||||
].into_iter().collect(),
|
||||
},
|
||||
category: ToolCategory::Learning,
|
||||
},
|
||||
McpToolDef {
|
||||
name: "ruvector_record_sequence".into(),
|
||||
description: "Record file edit sequence for pattern learning".into(),
|
||||
input_schema: ToolInputSchema {
|
||||
required: vec!["files".into()],
|
||||
properties: [
|
||||
("files".into(), PropertyDef {
|
||||
prop_type: "array".into(),
|
||||
description: "Sequence of files edited".into(),
|
||||
default: None,
|
||||
enum_values: None,
|
||||
}),
|
||||
("success".into(), PropertyDef {
|
||||
prop_type: "boolean".into(),
|
||||
description: "Whether sequence was successful".into(),
|
||||
default: Some("true".into()),
|
||||
enum_values: None,
|
||||
}),
|
||||
("pattern_type".into(), PropertyDef {
|
||||
prop_type: "string".into(),
|
||||
description: "Type of editing pattern".into(),
|
||||
default: None,
|
||||
enum_values: Some(vec![
|
||||
"rust_crate_setup".into(),
|
||||
"tdd".into(),
|
||||
"types_first".into(),
|
||||
"refactoring".into(),
|
||||
]),
|
||||
}),
|
||||
].into_iter().collect(),
|
||||
},
|
||||
category: ToolCategory::Learning,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
/// Generate MCP tools list JSON
|
||||
pub fn generate_tools_list_json() -> String {
|
||||
let tools = get_ruvector_tools();
|
||||
let tool_entries: Vec<String> = tools.iter().map(|tool| {
|
||||
let props: Vec<String> = tool.input_schema.properties.iter().map(|(name, prop)| {
|
||||
let mut prop_json = format!(
|
||||
r#" "{}": {{
|
||||
"type": "{}",
|
||||
"description": "{}""#,
|
||||
name, prop.prop_type, prop.description
|
||||
);
|
||||
if let Some(default) = &prop.default {
|
||||
prop_json.push_str(&format!(r#",
|
||||
"default": {}"#, default));
|
||||
}
|
||||
if let Some(enums) = &prop.enum_values {
|
||||
let enum_str: Vec<String> = enums.iter().map(|e| format!("\"{}\"", e)).collect();
|
||||
prop_json.push_str(&format!(r#",
|
||||
"enum": [{}]"#, enum_str.join(", ")));
|
||||
}
|
||||
prop_json.push_str("\n }");
|
||||
prop_json
|
||||
}).collect();
|
||||
|
||||
let required: Vec<String> = tool.input_schema.required.iter().map(|r| format!("\"{}\"", r)).collect();
|
||||
|
||||
format!(
|
||||
r#" {{
|
||||
"name": "{}",
|
||||
"description": "{}",
|
||||
"inputSchema": {{
|
||||
"type": "object",
|
||||
"properties": {{
|
||||
{}
|
||||
}},
|
||||
"required": [{}]
|
||||
}}
|
||||
}}"#,
|
||||
tool.name, tool.description, props.join(",\n"), required.join(", ")
|
||||
)
|
||||
}).collect();
|
||||
|
||||
format!(
|
||||
r#"{{
|
||||
"tools": [
|
||||
{}
|
||||
]
|
||||
}}"#,
|
||||
tool_entries.join(",\n")
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_get_ruvector_tools() {
|
||||
let tools = get_ruvector_tools();
|
||||
assert!(!tools.is_empty());
|
||||
|
||||
// Check we have tools in each category
|
||||
let categories: Vec<ToolCategory> = tools.iter().map(|t| t.category).collect();
|
||||
assert!(categories.contains(&ToolCategory::Learning));
|
||||
assert!(categories.contains(&ToolCategory::Memory));
|
||||
assert!(categories.contains(&ToolCategory::Swarm));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tool_has_required_properties() {
|
||||
let tools = get_ruvector_tools();
|
||||
for tool in tools {
|
||||
// All required fields should be in properties
|
||||
for req in &tool.input_schema.required {
|
||||
assert!(
|
||||
tool.input_schema.properties.contains_key(req),
|
||||
"Tool {} missing required property {}", tool.name, req
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_tools_list_json() {
|
||||
let json = generate_tools_list_json();
|
||||
assert!(json.contains("\"tools\""));
|
||||
assert!(json.contains("ruvector_learn_pattern"));
|
||||
assert!(json.contains("ruvector_remember"));
|
||||
}
|
||||
}
|
||||
72
vendor/ruvector/examples/edge-net/src/learning-scenarios/mod.rs
vendored
Normal file
72
vendor/ruvector/examples/edge-net/src/learning-scenarios/mod.rs
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
//! Learning Scenarios Module
|
||||
//!
|
||||
//! This module provides patterns and scenarios for training the
|
||||
//! RuVector self-learning hooks system, with full Claude Agent SDK
|
||||
//! and MCP integration support.
|
||||
//!
|
||||
//! ## Four Attention Mechanisms
|
||||
//!
|
||||
//! | Attention Type | Question Answered | Application |
|
||||
//! |---------------|-------------------|-------------|
|
||||
//! | **Neural** | What words matter? | Token/semantic relevance |
|
||||
//! | **DAG** | What steps matter? | Execution order, dependencies |
|
||||
//! | **Graph** | What relationships matter? | Code structure, call graphs |
|
||||
//! | **State Space** | What history still matters? | Context persistence |
|
||||
|
||||
pub mod error_recovery;
|
||||
pub mod file_sequences;
|
||||
pub mod sdk_integration;
|
||||
pub mod mcp_tools;
|
||||
pub mod attention_patterns;
|
||||
|
||||
pub use error_recovery::error_patterns::{ErrorLearningTracker, ErrorPattern, RecoveryStrategy};
|
||||
pub use file_sequences::sequence_tracker::{EditSequence, FileEdit, SequencePattern, SequenceTracker};
|
||||
pub use sdk_integration::{
|
||||
AgentDefinition, HookEventType, HookMatcher, McpServerConfig,
|
||||
PermissionMode, QueryOptions, TelemetryConfig, generate_settings_json,
|
||||
};
|
||||
pub use mcp_tools::{
|
||||
McpToolDef, PropertyDef, ToolCategory, ToolInputSchema,
|
||||
get_ruvector_tools, generate_tools_list_json,
|
||||
};
|
||||
pub use attention_patterns::{
|
||||
NeuralAttention, DagAttention, GraphAttention, StateSpaceAttention,
|
||||
AttentionOrchestrator, AttentionAnalysis,
|
||||
DagNode, StepType, GraphNode, GraphEdge, NodeType, EdgeType, HistoryEntry,
|
||||
};
|
||||
|
||||
/// Initialize the learning scenarios system
|
||||
pub fn init() {
|
||||
log::info!("🧠 Learning scenarios initialized");
|
||||
}
|
||||
|
||||
/// Learning statistics
|
||||
#[derive(Debug, Default)]
|
||||
pub struct LearningStats {
|
||||
pub patterns_learned: u32,
|
||||
pub errors_recovered: u32,
|
||||
pub sequences_detected: u32,
|
||||
pub agent_routings: u32,
|
||||
}
|
||||
|
||||
impl LearningStats {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn record_pattern(&mut self) {
|
||||
self.patterns_learned += 1;
|
||||
}
|
||||
|
||||
pub fn record_recovery(&mut self) {
|
||||
self.errors_recovered += 1;
|
||||
}
|
||||
|
||||
pub fn record_sequence(&mut self) {
|
||||
self.sequences_detected += 1;
|
||||
}
|
||||
|
||||
pub fn record_routing(&mut self) {
|
||||
self.agent_routings += 1;
|
||||
}
|
||||
}
|
||||
402
vendor/ruvector/examples/edge-net/src/learning-scenarios/sdk_integration.rs
vendored
Normal file
402
vendor/ruvector/examples/edge-net/src/learning-scenarios/sdk_integration.rs
vendored
Normal file
@@ -0,0 +1,402 @@
|
||||
//! Claude Agent SDK Integration for RuVector
|
||||
//!
|
||||
//! Provides patterns and utilities for integrating RuVector's self-learning
|
||||
//! intelligence with the Claude Agent SDK.
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Permission modes matching Claude Code's permission system
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum PermissionMode {
|
||||
/// Default mode - requires approval for most operations
|
||||
Default,
|
||||
/// Accept edits mode - auto-approves file edits
|
||||
AcceptEdits,
|
||||
/// Bypass permissions - runs without prompts (CI/CD)
|
||||
BypassPermissions,
|
||||
/// Plan mode - safe analysis without execution
|
||||
Plan,
|
||||
}
|
||||
|
||||
impl PermissionMode {
|
||||
pub fn from_str(s: &str) -> Self {
|
||||
match s.to_lowercase().as_str() {
|
||||
"acceptedits" | "accept-edits" | "accept_edits" => Self::AcceptEdits,
|
||||
"bypasspermissions" | "bypass-permissions" | "bypass" => Self::BypassPermissions,
|
||||
"plan" => Self::Plan,
|
||||
_ => Self::Default,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Default => "default",
|
||||
Self::AcceptEdits => "acceptEdits",
|
||||
Self::BypassPermissions => "bypassPermissions",
|
||||
Self::Plan => "plan",
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if this mode allows a specific operation
|
||||
pub fn allows(&self, operation: &str) -> bool {
|
||||
match self {
|
||||
Self::BypassPermissions => true,
|
||||
Self::AcceptEdits => matches!(operation, "read" | "edit" | "write" | "glob" | "grep"),
|
||||
Self::Plan => matches!(operation, "read" | "glob" | "grep"),
|
||||
Self::Default => false, // Requires explicit approval
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Telemetry configuration matching Claude Code's telemetry options
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TelemetryConfig {
|
||||
/// Disable Statsig metrics collection
|
||||
pub disable_telemetry: bool,
|
||||
/// Disable Sentry error reporting
|
||||
pub disable_error_reporting: bool,
|
||||
/// Disable /bug command
|
||||
pub disable_bug_command: bool,
|
||||
/// Disable all non-essential network traffic
|
||||
pub disable_nonessential_traffic: bool,
|
||||
/// Custom telemetry endpoint
|
||||
pub custom_endpoint: Option<String>,
|
||||
/// Data retention days (consumer: 5 years or 30 days, commercial: 30 days)
|
||||
pub retention_days: u32,
|
||||
}
|
||||
|
||||
impl Default for TelemetryConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
disable_telemetry: false,
|
||||
disable_error_reporting: false,
|
||||
disable_bug_command: false,
|
||||
disable_nonessential_traffic: false,
|
||||
custom_endpoint: None,
|
||||
retention_days: 30,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TelemetryConfig {
|
||||
/// Create config from environment variables
|
||||
pub fn from_env() -> Self {
|
||||
Self {
|
||||
disable_telemetry: std::env::var("DISABLE_TELEMETRY").is_ok(),
|
||||
disable_error_reporting: std::env::var("DISABLE_ERROR_REPORTING").is_ok(),
|
||||
disable_bug_command: std::env::var("DISABLE_BUG_COMMAND").is_ok(),
|
||||
disable_nonessential_traffic: std::env::var("CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC").is_ok(),
|
||||
custom_endpoint: std::env::var("RUVECTOR_TELEMETRY_ENDPOINT").ok(),
|
||||
retention_days: std::env::var("RUVECTOR_RETENTION_DAYS")
|
||||
.ok()
|
||||
.and_then(|s| s.parse().ok())
|
||||
.unwrap_or(30),
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if telemetry is enabled
|
||||
pub fn is_enabled(&self) -> bool {
|
||||
!self.disable_telemetry && !self.disable_nonessential_traffic
|
||||
}
|
||||
|
||||
/// Export as environment variables
|
||||
pub fn to_env_vars(&self) -> HashMap<String, String> {
|
||||
let mut vars = HashMap::new();
|
||||
if self.disable_telemetry {
|
||||
vars.insert("DISABLE_TELEMETRY".into(), "1".into());
|
||||
}
|
||||
if self.disable_error_reporting {
|
||||
vars.insert("DISABLE_ERROR_REPORTING".into(), "1".into());
|
||||
}
|
||||
if self.disable_bug_command {
|
||||
vars.insert("DISABLE_BUG_COMMAND".into(), "1".into());
|
||||
}
|
||||
if self.disable_nonessential_traffic {
|
||||
vars.insert("CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC".into(), "1".into());
|
||||
}
|
||||
if let Some(endpoint) = &self.custom_endpoint {
|
||||
vars.insert("RUVECTOR_TELEMETRY_ENDPOINT".into(), endpoint.clone());
|
||||
}
|
||||
vars.insert("RUVECTOR_RETENTION_DAYS".into(), self.retention_days.to_string());
|
||||
vars
|
||||
}
|
||||
}
|
||||
|
||||
/// Agent SDK query options
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct QueryOptions {
|
||||
/// Allowed tools for this query
|
||||
pub allowed_tools: Vec<String>,
|
||||
/// Permission mode
|
||||
pub permission_mode: PermissionMode,
|
||||
/// System prompt override
|
||||
pub system_prompt: Option<String>,
|
||||
/// Model to use (sonnet, opus, haiku)
|
||||
pub model: String,
|
||||
/// Session ID to resume
|
||||
pub resume_session: Option<String>,
|
||||
/// Maximum agentic turns
|
||||
pub max_turns: Option<u32>,
|
||||
/// Output format (text, json, stream-json)
|
||||
pub output_format: String,
|
||||
/// Custom agents/subagents
|
||||
pub agents: HashMap<String, AgentDefinition>,
|
||||
/// MCP servers to enable
|
||||
pub mcp_servers: HashMap<String, McpServerConfig>,
|
||||
}
|
||||
|
||||
impl Default for QueryOptions {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
allowed_tools: vec![
|
||||
"Read".into(),
|
||||
"Edit".into(),
|
||||
"Write".into(),
|
||||
"Bash".into(),
|
||||
"Glob".into(),
|
||||
"Grep".into(),
|
||||
],
|
||||
permission_mode: PermissionMode::Default,
|
||||
system_prompt: None,
|
||||
model: "claude-sonnet-4-5-20250929".into(),
|
||||
resume_session: None,
|
||||
max_turns: None,
|
||||
output_format: "text".into(),
|
||||
agents: HashMap::new(),
|
||||
mcp_servers: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Agent definition for custom subagents
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AgentDefinition {
|
||||
pub description: String,
|
||||
pub prompt: String,
|
||||
pub tools: Vec<String>,
|
||||
}
|
||||
|
||||
/// MCP server configuration
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct McpServerConfig {
|
||||
pub command: String,
|
||||
pub args: Vec<String>,
|
||||
pub env: HashMap<String, String>,
|
||||
}
|
||||
|
||||
/// Hook event types matching Claude Code's hook system
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum HookEventType {
|
||||
/// Before a tool is executed
|
||||
PreToolUse,
|
||||
/// After a tool execution completes
|
||||
PostToolUse,
|
||||
/// When a notification is received
|
||||
Notification,
|
||||
/// Before context compaction
|
||||
PreCompact,
|
||||
/// When a session starts
|
||||
SessionStart,
|
||||
/// When execution stops
|
||||
Stop,
|
||||
/// When user submits a prompt
|
||||
UserPromptSubmit,
|
||||
}
|
||||
|
||||
impl HookEventType {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::PreToolUse => "PreToolUse",
|
||||
Self::PostToolUse => "PostToolUse",
|
||||
Self::Notification => "Notification",
|
||||
Self::PreCompact => "PreCompact",
|
||||
Self::SessionStart => "SessionStart",
|
||||
Self::Stop => "Stop",
|
||||
Self::UserPromptSubmit => "UserPromptSubmit",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_str(s: &str) -> Option<Self> {
|
||||
match s {
|
||||
"PreToolUse" => Some(Self::PreToolUse),
|
||||
"PostToolUse" => Some(Self::PostToolUse),
|
||||
"Notification" => Some(Self::Notification),
|
||||
"PreCompact" => Some(Self::PreCompact),
|
||||
"SessionStart" => Some(Self::SessionStart),
|
||||
"Stop" => Some(Self::Stop),
|
||||
"UserPromptSubmit" => Some(Self::UserPromptSubmit),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Hook matcher configuration
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct HookMatcher {
|
||||
pub event_type: HookEventType,
|
||||
pub matcher: String, // Regex pattern for tool matching
|
||||
pub command: String,
|
||||
pub timeout_ms: u32,
|
||||
}
|
||||
|
||||
impl HookMatcher {
|
||||
pub fn new(event_type: HookEventType, matcher: &str, command: &str) -> Self {
|
||||
Self {
|
||||
event_type,
|
||||
matcher: matcher.into(),
|
||||
command: command.into(),
|
||||
timeout_ms: 5000,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_timeout(mut self, timeout_ms: u32) -> Self {
|
||||
self.timeout_ms = timeout_ms;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate Claude Code settings JSON for RuVector integration
|
||||
pub fn generate_settings_json(telemetry: &TelemetryConfig) -> String {
|
||||
let env_vars = telemetry.to_env_vars();
|
||||
let env_json: Vec<String> = env_vars
|
||||
.iter()
|
||||
.map(|(k, v)| format!(" \"{}\": \"{}\"", k, v))
|
||||
.collect();
|
||||
|
||||
format!(
|
||||
r#"{{
|
||||
"env": {{
|
||||
"RUVECTOR_INTELLIGENCE_ENABLED": "true",
|
||||
"RUVECTOR_LEARNING_RATE": "0.1",
|
||||
"RUVECTOR_MEMORY_BACKEND": "rvlite",
|
||||
"INTELLIGENCE_MODE": "treatment",
|
||||
{}
|
||||
}},
|
||||
"permissions": {{
|
||||
"allow": [
|
||||
"Bash(ruvector:*)",
|
||||
"Bash(ruvector-cli:*)",
|
||||
"Bash(npx ruvector:*)",
|
||||
"Bash(cargo test:*)",
|
||||
"Bash(git:*)"
|
||||
],
|
||||
"deny": [
|
||||
"Bash(rm -rf /)"
|
||||
]
|
||||
}},
|
||||
"hooks": {{
|
||||
"PreToolUse": [
|
||||
{{
|
||||
"matcher": "Edit|Write|MultiEdit",
|
||||
"hooks": [{{
|
||||
"type": "command",
|
||||
"command": "ruvector-cli hooks pre-edit \"$TOOL_INPUT_file_path\""
|
||||
}}]
|
||||
}},
|
||||
{{
|
||||
"matcher": "Bash",
|
||||
"hooks": [{{
|
||||
"type": "command",
|
||||
"command": "ruvector-cli hooks pre-command \"$TOOL_INPUT_command\""
|
||||
}}]
|
||||
}},
|
||||
{{
|
||||
"matcher": "Task",
|
||||
"hooks": [{{
|
||||
"type": "command",
|
||||
"timeout": 1000,
|
||||
"command": "ruvector-cli hooks remember \"Agent: $TOOL_INPUT_subagent_type\" -t agent_spawn"
|
||||
}}]
|
||||
}}
|
||||
],
|
||||
"PostToolUse": [
|
||||
{{
|
||||
"matcher": "Edit|Write|MultiEdit",
|
||||
"hooks": [{{
|
||||
"type": "command",
|
||||
"command": "ruvector-cli hooks post-edit \"$TOOL_INPUT_file_path\" --success"
|
||||
}}]
|
||||
}},
|
||||
{{
|
||||
"matcher": "Bash",
|
||||
"hooks": [{{
|
||||
"type": "command",
|
||||
"command": "ruvector-cli hooks post-command \"$TOOL_INPUT_command\" --success"
|
||||
}}]
|
||||
}}
|
||||
],
|
||||
"SessionStart": [{{
|
||||
"hooks": [{{
|
||||
"type": "command",
|
||||
"command": "ruvector-cli hooks session-start"
|
||||
}}]
|
||||
}}],
|
||||
"Stop": [{{
|
||||
"hooks": [{{
|
||||
"type": "command",
|
||||
"command": "ruvector-cli hooks session-end"
|
||||
}}]
|
||||
}}],
|
||||
"UserPromptSubmit": [{{
|
||||
"hooks": [{{
|
||||
"type": "command",
|
||||
"timeout": 2000,
|
||||
"command": "ruvector-cli hooks suggest-context"
|
||||
}}]
|
||||
}}]
|
||||
}}
|
||||
}}"#,
|
||||
env_json.join(",\n")
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_permission_mode_from_str() {
|
||||
assert_eq!(PermissionMode::from_str("acceptEdits"), PermissionMode::AcceptEdits);
|
||||
assert_eq!(PermissionMode::from_str("bypass"), PermissionMode::BypassPermissions);
|
||||
assert_eq!(PermissionMode::from_str("plan"), PermissionMode::Plan);
|
||||
assert_eq!(PermissionMode::from_str("unknown"), PermissionMode::Default);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_permission_mode_allows() {
|
||||
assert!(PermissionMode::BypassPermissions.allows("edit"));
|
||||
assert!(PermissionMode::AcceptEdits.allows("read"));
|
||||
assert!(!PermissionMode::AcceptEdits.allows("bash"));
|
||||
assert!(PermissionMode::Plan.allows("grep"));
|
||||
assert!(!PermissionMode::Plan.allows("edit"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_telemetry_config_from_env() {
|
||||
// Default should have telemetry enabled
|
||||
let config = TelemetryConfig::default();
|
||||
assert!(config.is_enabled());
|
||||
assert!(!config.disable_telemetry);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hook_event_type_roundtrip() {
|
||||
for event in [
|
||||
HookEventType::PreToolUse,
|
||||
HookEventType::PostToolUse,
|
||||
HookEventType::SessionStart,
|
||||
] {
|
||||
let s = event.as_str();
|
||||
assert_eq!(HookEventType::from_str(s), Some(event));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_settings_json() {
|
||||
let config = TelemetryConfig::default();
|
||||
let json = generate_settings_json(&config);
|
||||
assert!(json.contains("RUVECTOR_INTELLIGENCE_ENABLED"));
|
||||
assert!(json.contains("PreToolUse"));
|
||||
assert!(json.contains("PostToolUse"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user