mirror of
https://github.com/ruvnet/RuVector.git
synced 2026-05-22 19:56:25 +00:00
feat(edge/p2p): complete RuVector advanced integrations
Added: - Semantic embeddings interface with hash-based LSH encoding - SemanticTaskMatcher for intelligent agent-task matching - Raft consensus protocol for distributed task coordination - Leader election with term-based voting - Log replication with consistency checks - Heartbeat and append entries protocol Now exports 20+ advanced types from p2p module: - Quantization: ScalarQuantized, BinaryQuantized, CompressedData - HDC: Hypervector, HdcMemory, HDC_DIMENSION - Compression: AdaptiveCompressor, NetworkCondition - Pattern routing: PatternRouter - Vector index: HnswIndex - Post-quantum: HybridKeyPair, HybridPublicKey, HybridSignature - Spiking networks: LIFNeuron, SpikingNetwork - Embeddings: SemanticEmbedder, SemanticTaskMatcher - Consensus: RaftNode, RaftState, LogEntry, RaftVoteRequest/Response, RaftAppendEntries/Response 60 tests passing 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
c85598a978
commit
74484eee38
3 changed files with 1447 additions and 10 deletions
|
|
@ -3,16 +3,16 @@
|
|||
"cmd_shell_general|success": {
|
||||
"state": "cmd_shell_general",
|
||||
"action": "success",
|
||||
"q_value": 0.41736248000000004,
|
||||
"visits": 7,
|
||||
"last_update": 1767202829
|
||||
"q_value": 0.548951523128,
|
||||
"visits": 11,
|
||||
"last_update": 1767202877
|
||||
},
|
||||
"edit__in_project|successful-edit": {
|
||||
"state": "edit__in_project",
|
||||
"action": "successful-edit",
|
||||
"q_value": 0.717570463519,
|
||||
"visits": 12,
|
||||
"last_update": 1767201510
|
||||
"q_value": 0.8332281830033343,
|
||||
"visits": 17,
|
||||
"last_update": 1767203302
|
||||
}
|
||||
},
|
||||
"memories": [
|
||||
|
|
@ -19259,6 +19259,663 @@
|
|||
],
|
||||
"metadata": {},
|
||||
"timestamp": 1767202829
|
||||
},
|
||||
{
|
||||
"id": "mem_1767202838",
|
||||
"memory_type": "command",
|
||||
"content": " succeeded",
|
||||
"embedding": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794
|
||||
],
|
||||
"metadata": {},
|
||||
"timestamp": 1767202838
|
||||
},
|
||||
{
|
||||
"id": "mem_1767202841",
|
||||
"memory_type": "command",
|
||||
"content": " succeeded",
|
||||
"embedding": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794
|
||||
],
|
||||
"metadata": {},
|
||||
"timestamp": 1767202841
|
||||
},
|
||||
{
|
||||
"id": "mem_1767202848",
|
||||
"memory_type": "command",
|
||||
"content": " succeeded",
|
||||
"embedding": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794
|
||||
],
|
||||
"metadata": {},
|
||||
"timestamp": 1767202848
|
||||
},
|
||||
{
|
||||
"id": "mem_1767202877",
|
||||
"memory_type": "command",
|
||||
"content": " succeeded",
|
||||
"embedding": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794
|
||||
],
|
||||
"metadata": {},
|
||||
"timestamp": 1767202877
|
||||
},
|
||||
{
|
||||
"id": "mem_1767203246",
|
||||
"memory_type": "edit",
|
||||
"content": "successful edit of in project",
|
||||
"embedding": [
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0.31622776601683794,
|
||||
0.15811388300841897,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0.15811388300841897,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0.15811388300841897,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897
|
||||
],
|
||||
"metadata": {},
|
||||
"timestamp": 1767203246
|
||||
},
|
||||
{
|
||||
"id": "mem_1767203246",
|
||||
"memory_type": "edit",
|
||||
"content": "successful edit of in project",
|
||||
"embedding": [
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0.31622776601683794,
|
||||
0.15811388300841897,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0.15811388300841897,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0.15811388300841897,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897
|
||||
],
|
||||
"metadata": {},
|
||||
"timestamp": 1767203246
|
||||
},
|
||||
{
|
||||
"id": "mem_1767203291",
|
||||
"memory_type": "edit",
|
||||
"content": "successful edit of in project",
|
||||
"embedding": [
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0.31622776601683794,
|
||||
0.15811388300841897,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0.15811388300841897,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0.15811388300841897,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897
|
||||
],
|
||||
"metadata": {},
|
||||
"timestamp": 1767203291
|
||||
},
|
||||
{
|
||||
"id": "mem_1767203301",
|
||||
"memory_type": "edit",
|
||||
"content": "successful edit of in project",
|
||||
"embedding": [
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0.31622776601683794,
|
||||
0.15811388300841897,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0.15811388300841897,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0.15811388300841897,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897
|
||||
],
|
||||
"metadata": {},
|
||||
"timestamp": 1767203301
|
||||
},
|
||||
{
|
||||
"id": "mem_1767203302",
|
||||
"memory_type": "edit",
|
||||
"content": "successful edit of in project",
|
||||
"embedding": [
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0.31622776601683794,
|
||||
0.15811388300841897,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0.15811388300841897,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0.15811388300841897,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0.31622776601683794,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0.15811388300841897,
|
||||
0,
|
||||
0,
|
||||
0.15811388300841897
|
||||
],
|
||||
"metadata": {},
|
||||
"timestamp": 1767203302
|
||||
}
|
||||
],
|
||||
"trajectories": [
|
||||
|
|
@ -19829,6 +20486,78 @@
|
|||
"outcome": "completed",
|
||||
"reward": 0.8,
|
||||
"timestamp": 1767202829
|
||||
},
|
||||
{
|
||||
"id": "traj_1767202838",
|
||||
"state": "cmd_shell_general",
|
||||
"action": "success",
|
||||
"outcome": "completed",
|
||||
"reward": 0.8,
|
||||
"timestamp": 1767202838
|
||||
},
|
||||
{
|
||||
"id": "traj_1767202841",
|
||||
"state": "cmd_shell_general",
|
||||
"action": "success",
|
||||
"outcome": "completed",
|
||||
"reward": 0.8,
|
||||
"timestamp": 1767202841
|
||||
},
|
||||
{
|
||||
"id": "traj_1767202848",
|
||||
"state": "cmd_shell_general",
|
||||
"action": "success",
|
||||
"outcome": "completed",
|
||||
"reward": 0.8,
|
||||
"timestamp": 1767202848
|
||||
},
|
||||
{
|
||||
"id": "traj_1767202877",
|
||||
"state": "cmd_shell_general",
|
||||
"action": "success",
|
||||
"outcome": "completed",
|
||||
"reward": 0.8,
|
||||
"timestamp": 1767202877
|
||||
},
|
||||
{
|
||||
"id": "traj_1767203246",
|
||||
"state": "edit__in_project",
|
||||
"action": "successful-edit",
|
||||
"outcome": "completed",
|
||||
"reward": 1,
|
||||
"timestamp": 1767203246
|
||||
},
|
||||
{
|
||||
"id": "traj_1767203246",
|
||||
"state": "edit__in_project",
|
||||
"action": "successful-edit",
|
||||
"outcome": "completed",
|
||||
"reward": 1,
|
||||
"timestamp": 1767203246
|
||||
},
|
||||
{
|
||||
"id": "traj_1767203291",
|
||||
"state": "edit__in_project",
|
||||
"action": "successful-edit",
|
||||
"outcome": "completed",
|
||||
"reward": 1,
|
||||
"timestamp": 1767203291
|
||||
},
|
||||
{
|
||||
"id": "traj_1767203301",
|
||||
"state": "edit__in_project",
|
||||
"action": "successful-edit",
|
||||
"outcome": "completed",
|
||||
"reward": 1,
|
||||
"timestamp": 1767203301
|
||||
},
|
||||
{
|
||||
"id": "traj_1767203302",
|
||||
"state": "edit__in_project",
|
||||
"action": "successful-edit",
|
||||
"outcome": "completed",
|
||||
"reward": 1,
|
||||
"timestamp": 1767203302
|
||||
}
|
||||
],
|
||||
"errors": {},
|
||||
|
|
@ -19837,11 +20566,11 @@
|
|||
"edges": [],
|
||||
"stats": {
|
||||
"total_patterns": 2,
|
||||
"total_memories": 76,
|
||||
"total_trajectories": 71,
|
||||
"total_memories": 85,
|
||||
"total_trajectories": 80,
|
||||
"total_errors": 0,
|
||||
"session_count": 5,
|
||||
"last_session": 1767194385
|
||||
"session_count": 6,
|
||||
"last_session": 1767203364
|
||||
},
|
||||
"engineStats": {
|
||||
"totalMemories": 51,
|
||||
|
|
|
|||
|
|
@ -1121,6 +1121,579 @@ impl SpikingNetwork {
|
|||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// SEMANTIC EMBEDDINGS (ONNX integration interface)
|
||||
//=============================================================================
|
||||
|
||||
/// Semantic embedding model interface
|
||||
/// Provides a consistent API for text-to-vector embeddings
|
||||
pub struct SemanticEmbedder {
|
||||
/// Embedding dimension (384 for all-MiniLM-L6-v2)
|
||||
dimension: usize,
|
||||
/// Precomputed word vectors for simple embeddings
|
||||
word_vectors: HashMap<String, Vec<f32>>,
|
||||
}
|
||||
|
||||
impl SemanticEmbedder {
|
||||
/// Create new embedder with specified dimension
|
||||
pub fn new(dimension: usize) -> Self {
|
||||
Self {
|
||||
dimension,
|
||||
word_vectors: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create with default dimension (384 for MiniLM)
|
||||
pub fn default_dimension() -> Self {
|
||||
Self::new(384)
|
||||
}
|
||||
|
||||
/// Add word vector for simple bag-of-words embedding
|
||||
pub fn add_word_vector(&mut self, word: &str, vector: Vec<f32>) {
|
||||
self.word_vectors.insert(word.to_lowercase(), vector);
|
||||
}
|
||||
|
||||
/// Simple text embedding using averaged word vectors
|
||||
/// For production, use actual ONNX runtime with transformer models
|
||||
pub fn embed_text(&self, text: &str) -> Vec<f32> {
|
||||
let words: Vec<&str> = text.split_whitespace().collect();
|
||||
let mut result = vec![0.0f32; self.dimension];
|
||||
let mut count = 0;
|
||||
|
||||
for word in words {
|
||||
let word_lower = word.to_lowercase();
|
||||
if let Some(vec) = self.word_vectors.get(&word_lower) {
|
||||
for (i, &v) in vec.iter().enumerate() {
|
||||
if i < self.dimension {
|
||||
result[i] += v;
|
||||
}
|
||||
}
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize
|
||||
if count > 0 {
|
||||
for v in &mut result {
|
||||
*v /= count as f32;
|
||||
}
|
||||
}
|
||||
|
||||
// L2 normalize
|
||||
let norm: f32 = result.iter().map(|x| x * x).sum::<f32>().sqrt();
|
||||
if norm > 0.0 {
|
||||
for v in &mut result {
|
||||
*v /= norm;
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Simple hash-based embedding (for when no word vectors are available)
|
||||
/// Uses locality-sensitive hashing for deterministic embeddings
|
||||
pub fn embed_hash(&self, text: &str) -> Vec<f32> {
|
||||
use sha2::{Sha256, Digest};
|
||||
|
||||
let mut result = vec![0.0f32; self.dimension];
|
||||
|
||||
// Hash each word and accumulate
|
||||
for (wi, word) in text.split_whitespace().enumerate() {
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(word.as_bytes());
|
||||
let hash = hasher.finalize();
|
||||
|
||||
// Use hash bytes to set vector components
|
||||
for (i, &byte) in hash.iter().cycle().take(self.dimension).enumerate() {
|
||||
// Convert byte to [-1, 1] range with word position influence
|
||||
let val = (byte as f32 / 127.5 - 1.0) * (1.0 / (wi + 1) as f32).sqrt();
|
||||
result[i] += val;
|
||||
}
|
||||
}
|
||||
|
||||
// L2 normalize
|
||||
let norm: f32 = result.iter().map(|x| x * x).sum::<f32>().sqrt();
|
||||
if norm > 0.0 {
|
||||
for v in &mut result {
|
||||
*v /= norm;
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Cosine similarity between two vectors
|
||||
pub fn cosine_similarity(a: &[f32], b: &[f32]) -> f32 {
|
||||
let dot: f32 = a.iter().zip(b).map(|(x, y)| x * y).sum();
|
||||
let norm_a: f32 = a.iter().map(|x| x * x).sum::<f32>().sqrt();
|
||||
let norm_b: f32 = b.iter().map(|x| x * x).sum::<f32>().sqrt();
|
||||
|
||||
if norm_a > 0.0 && norm_b > 0.0 {
|
||||
dot / (norm_a * norm_b)
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Dimension of embeddings
|
||||
pub fn dimension(&self) -> usize {
|
||||
self.dimension
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SemanticEmbedder {
|
||||
fn default() -> Self {
|
||||
Self::default_dimension()
|
||||
}
|
||||
}
|
||||
|
||||
/// Task matcher using semantic embeddings
|
||||
pub struct SemanticTaskMatcher {
|
||||
embedder: SemanticEmbedder,
|
||||
/// Agent capability embeddings
|
||||
agent_embeddings: HashMap<String, Vec<f32>>,
|
||||
/// Task type embeddings
|
||||
task_embeddings: HashMap<String, Vec<f32>>,
|
||||
}
|
||||
|
||||
impl SemanticTaskMatcher {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
embedder: SemanticEmbedder::default_dimension(),
|
||||
agent_embeddings: HashMap::new(),
|
||||
task_embeddings: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Register agent with capability description
|
||||
pub fn register_agent(&mut self, agent_id: &str, capability_desc: &str) {
|
||||
let embedding = self.embedder.embed_hash(capability_desc);
|
||||
self.agent_embeddings.insert(agent_id.to_string(), embedding);
|
||||
}
|
||||
|
||||
/// Register task type with description
|
||||
pub fn register_task(&mut self, task_type: &str, description: &str) {
|
||||
let embedding = self.embedder.embed_hash(description);
|
||||
self.task_embeddings.insert(task_type.to_string(), embedding);
|
||||
}
|
||||
|
||||
/// Find best agent for a task description
|
||||
pub fn match_agent(&self, task_desc: &str) -> Option<(String, f32)> {
|
||||
let task_embedding = self.embedder.embed_hash(task_desc);
|
||||
|
||||
self.agent_embeddings
|
||||
.iter()
|
||||
.map(|(agent_id, agent_emb)| {
|
||||
let sim = SemanticEmbedder::cosine_similarity(&task_embedding, agent_emb);
|
||||
(agent_id.clone(), sim)
|
||||
})
|
||||
.max_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal))
|
||||
}
|
||||
|
||||
/// Find k nearest agents for a task
|
||||
pub fn find_k_nearest(&self, task_desc: &str, k: usize) -> Vec<(String, f32)> {
|
||||
let task_embedding = self.embedder.embed_hash(task_desc);
|
||||
|
||||
let mut results: Vec<_> = self.agent_embeddings
|
||||
.iter()
|
||||
.map(|(agent_id, agent_emb)| {
|
||||
let sim = SemanticEmbedder::cosine_similarity(&task_embedding, agent_emb);
|
||||
(agent_id.clone(), sim)
|
||||
})
|
||||
.collect();
|
||||
|
||||
results.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
|
||||
results.truncate(k);
|
||||
results
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SemanticTaskMatcher {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// RAFT CONSENSUS (simplified implementation)
|
||||
//=============================================================================
|
||||
|
||||
/// Raft node state
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum RaftState {
|
||||
Follower,
|
||||
Candidate,
|
||||
Leader,
|
||||
}
|
||||
|
||||
/// Raft log entry
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct LogEntry {
|
||||
pub term: u64,
|
||||
pub index: u64,
|
||||
pub command: Vec<u8>,
|
||||
}
|
||||
|
||||
/// Raft consensus node
|
||||
pub struct RaftNode {
|
||||
/// Node ID
|
||||
pub node_id: String,
|
||||
/// Current term
|
||||
pub current_term: u64,
|
||||
/// Voted for in current term
|
||||
pub voted_for: Option<String>,
|
||||
/// Current state
|
||||
pub state: RaftState,
|
||||
/// Log entries
|
||||
pub log: Vec<LogEntry>,
|
||||
/// Index of highest log entry known to be committed
|
||||
pub commit_index: u64,
|
||||
/// Index of highest log entry applied to state machine
|
||||
pub last_applied: u64,
|
||||
/// Leader ID (if known)
|
||||
pub leader_id: Option<String>,
|
||||
/// Cluster members
|
||||
pub members: Vec<String>,
|
||||
/// Next index for each follower (leader only)
|
||||
pub next_index: HashMap<String, u64>,
|
||||
/// Match index for each follower (leader only)
|
||||
pub match_index: HashMap<String, u64>,
|
||||
/// Election timeout (ms)
|
||||
pub election_timeout: u64,
|
||||
/// Heartbeat interval (ms)
|
||||
pub heartbeat_interval: u64,
|
||||
/// Last heartbeat time
|
||||
pub last_heartbeat: u64,
|
||||
/// Votes received in current election
|
||||
pub votes_received: usize,
|
||||
}
|
||||
|
||||
impl RaftNode {
|
||||
/// Create new Raft node
|
||||
pub fn new(node_id: &str, members: Vec<String>) -> Self {
|
||||
Self {
|
||||
node_id: node_id.to_string(),
|
||||
current_term: 0,
|
||||
voted_for: None,
|
||||
state: RaftState::Follower,
|
||||
log: Vec::new(),
|
||||
commit_index: 0,
|
||||
last_applied: 0,
|
||||
leader_id: None,
|
||||
members,
|
||||
next_index: HashMap::new(),
|
||||
match_index: HashMap::new(),
|
||||
election_timeout: 150 + rand::random::<u64>() % 150, // 150-300ms
|
||||
heartbeat_interval: 50,
|
||||
last_heartbeat: 0,
|
||||
votes_received: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Start election
|
||||
pub fn start_election(&mut self) -> RaftVoteRequest {
|
||||
self.state = RaftState::Candidate;
|
||||
self.current_term += 1;
|
||||
self.voted_for = Some(self.node_id.clone());
|
||||
self.votes_received = 1; // Vote for self
|
||||
self.leader_id = None;
|
||||
|
||||
let (last_log_index, last_log_term) = self.last_log_info();
|
||||
|
||||
RaftVoteRequest {
|
||||
term: self.current_term,
|
||||
candidate_id: self.node_id.clone(),
|
||||
last_log_index,
|
||||
last_log_term,
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle vote request
|
||||
pub fn handle_vote_request(&mut self, req: &RaftVoteRequest) -> RaftVoteResponse {
|
||||
// Update term if request has higher term
|
||||
if req.term > self.current_term {
|
||||
self.current_term = req.term;
|
||||
self.state = RaftState::Follower;
|
||||
self.voted_for = None;
|
||||
}
|
||||
|
||||
let vote_granted = if req.term < self.current_term {
|
||||
false
|
||||
} else if self.voted_for.is_none() || self.voted_for.as_ref() == Some(&req.candidate_id) {
|
||||
let (our_last_index, our_last_term) = self.last_log_info();
|
||||
// Grant vote if candidate's log is at least as up-to-date
|
||||
if req.last_log_term > our_last_term ||
|
||||
(req.last_log_term == our_last_term && req.last_log_index >= our_last_index) {
|
||||
self.voted_for = Some(req.candidate_id.clone());
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
RaftVoteResponse {
|
||||
term: self.current_term,
|
||||
vote_granted,
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle vote response
|
||||
pub fn handle_vote_response(&mut self, resp: &RaftVoteResponse) -> bool {
|
||||
if resp.term > self.current_term {
|
||||
self.current_term = resp.term;
|
||||
self.state = RaftState::Follower;
|
||||
return false;
|
||||
}
|
||||
|
||||
if self.state != RaftState::Candidate {
|
||||
return false;
|
||||
}
|
||||
|
||||
if resp.vote_granted {
|
||||
self.votes_received += 1;
|
||||
|
||||
// Check if we have majority
|
||||
let majority = (self.members.len() / 2) + 1;
|
||||
if self.votes_received >= majority {
|
||||
self.become_leader();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Become leader
|
||||
pub fn become_leader(&mut self) {
|
||||
self.state = RaftState::Leader;
|
||||
self.leader_id = Some(self.node_id.clone());
|
||||
|
||||
// Initialize next_index and match_index for all followers
|
||||
let last_index = self.log.len() as u64;
|
||||
for member in &self.members {
|
||||
if member != &self.node_id {
|
||||
self.next_index.insert(member.clone(), last_index + 1);
|
||||
self.match_index.insert(member.clone(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Append entry (leader only)
|
||||
pub fn append_entry(&mut self, command: Vec<u8>) -> Option<u64> {
|
||||
if self.state != RaftState::Leader {
|
||||
return None;
|
||||
}
|
||||
|
||||
let index = self.log.len() as u64 + 1;
|
||||
let entry = LogEntry {
|
||||
term: self.current_term,
|
||||
index,
|
||||
command,
|
||||
};
|
||||
self.log.push(entry);
|
||||
|
||||
Some(index)
|
||||
}
|
||||
|
||||
/// Create append entries request for a follower
|
||||
pub fn create_append_entries(&self, follower_id: &str) -> Option<RaftAppendEntries> {
|
||||
if self.state != RaftState::Leader {
|
||||
return None;
|
||||
}
|
||||
|
||||
let next_idx = *self.next_index.get(follower_id).unwrap_or(&1);
|
||||
let prev_log_index = next_idx.saturating_sub(1);
|
||||
let prev_log_term = if prev_log_index > 0 {
|
||||
self.log.get((prev_log_index - 1) as usize)
|
||||
.map(|e| e.term)
|
||||
.unwrap_or(0)
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
let entries: Vec<LogEntry> = self.log.iter()
|
||||
.skip(prev_log_index as usize)
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
Some(RaftAppendEntries {
|
||||
term: self.current_term,
|
||||
leader_id: self.node_id.clone(),
|
||||
prev_log_index,
|
||||
prev_log_term,
|
||||
entries,
|
||||
leader_commit: self.commit_index,
|
||||
})
|
||||
}
|
||||
|
||||
/// Handle append entries (follower)
|
||||
pub fn handle_append_entries(&mut self, req: &RaftAppendEntries) -> RaftAppendEntriesResponse {
|
||||
// Update term if request has higher term
|
||||
if req.term > self.current_term {
|
||||
self.current_term = req.term;
|
||||
self.state = RaftState::Follower;
|
||||
self.voted_for = None;
|
||||
}
|
||||
|
||||
// Reject if term is old
|
||||
if req.term < self.current_term {
|
||||
return RaftAppendEntriesResponse {
|
||||
term: self.current_term,
|
||||
success: false,
|
||||
match_index: 0,
|
||||
};
|
||||
}
|
||||
|
||||
// Valid heartbeat from leader
|
||||
self.leader_id = Some(req.leader_id.clone());
|
||||
self.last_heartbeat = chrono::Utc::now().timestamp_millis() as u64;
|
||||
|
||||
// Check log consistency
|
||||
if req.prev_log_index > 0 {
|
||||
if self.log.len() < req.prev_log_index as usize {
|
||||
return RaftAppendEntriesResponse {
|
||||
term: self.current_term,
|
||||
success: false,
|
||||
match_index: self.log.len() as u64,
|
||||
};
|
||||
}
|
||||
let entry = &self.log[(req.prev_log_index - 1) as usize];
|
||||
if entry.term != req.prev_log_term {
|
||||
// Conflict - truncate log
|
||||
self.log.truncate((req.prev_log_index - 1) as usize);
|
||||
return RaftAppendEntriesResponse {
|
||||
term: self.current_term,
|
||||
success: false,
|
||||
match_index: self.log.len() as u64,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Append new entries
|
||||
for entry in &req.entries {
|
||||
if entry.index as usize > self.log.len() {
|
||||
self.log.push(entry.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// Update commit index
|
||||
if req.leader_commit > self.commit_index {
|
||||
self.commit_index = req.leader_commit.min(self.log.len() as u64);
|
||||
}
|
||||
|
||||
RaftAppendEntriesResponse {
|
||||
term: self.current_term,
|
||||
success: true,
|
||||
match_index: self.log.len() as u64,
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle append entries response (leader)
|
||||
pub fn handle_append_entries_response(&mut self, follower_id: &str, resp: &RaftAppendEntriesResponse) {
|
||||
if resp.term > self.current_term {
|
||||
self.current_term = resp.term;
|
||||
self.state = RaftState::Follower;
|
||||
return;
|
||||
}
|
||||
|
||||
if self.state != RaftState::Leader {
|
||||
return;
|
||||
}
|
||||
|
||||
if resp.success {
|
||||
self.next_index.insert(follower_id.to_string(), resp.match_index + 1);
|
||||
self.match_index.insert(follower_id.to_string(), resp.match_index);
|
||||
|
||||
// Update commit index
|
||||
self.update_commit_index();
|
||||
} else {
|
||||
// Decrement next_index and retry
|
||||
let next = self.next_index.entry(follower_id.to_string()).or_insert(1);
|
||||
*next = next.saturating_sub(1).max(1);
|
||||
}
|
||||
}
|
||||
|
||||
/// Update commit index based on match indices
|
||||
fn update_commit_index(&mut self) {
|
||||
let mut indices: Vec<u64> = self.match_index.values().copied().collect();
|
||||
indices.push(self.log.len() as u64);
|
||||
indices.sort();
|
||||
|
||||
let majority_idx = indices.len() / 2;
|
||||
let potential_commit = indices[majority_idx];
|
||||
|
||||
if potential_commit > self.commit_index {
|
||||
// Only commit if entry is from current term
|
||||
if let Some(entry) = self.log.get((potential_commit - 1) as usize) {
|
||||
if entry.term == self.current_term {
|
||||
self.commit_index = potential_commit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get last log index and term
|
||||
fn last_log_info(&self) -> (u64, u64) {
|
||||
if let Some(entry) = self.log.last() {
|
||||
(entry.index, entry.term)
|
||||
} else {
|
||||
(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if election timeout elapsed
|
||||
pub fn election_timeout_elapsed(&self, now: u64) -> bool {
|
||||
now - self.last_heartbeat > self.election_timeout
|
||||
}
|
||||
|
||||
/// Check if heartbeat needed
|
||||
pub fn heartbeat_needed(&self, now: u64) -> bool {
|
||||
self.state == RaftState::Leader && now - self.last_heartbeat > self.heartbeat_interval
|
||||
}
|
||||
|
||||
/// Is this node the leader?
|
||||
pub fn is_leader(&self) -> bool {
|
||||
self.state == RaftState::Leader
|
||||
}
|
||||
}
|
||||
|
||||
/// Vote request message
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct RaftVoteRequest {
|
||||
pub term: u64,
|
||||
pub candidate_id: String,
|
||||
pub last_log_index: u64,
|
||||
pub last_log_term: u64,
|
||||
}
|
||||
|
||||
/// Vote response message
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct RaftVoteResponse {
|
||||
pub term: u64,
|
||||
pub vote_granted: bool,
|
||||
}
|
||||
|
||||
/// Append entries request
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct RaftAppendEntries {
|
||||
pub term: u64,
|
||||
pub leader_id: String,
|
||||
pub prev_log_index: u64,
|
||||
pub prev_log_term: u64,
|
||||
pub entries: Vec<LogEntry>,
|
||||
pub leader_commit: u64,
|
||||
}
|
||||
|
||||
/// Append entries response
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct RaftAppendEntriesResponse {
|
||||
pub term: u64,
|
||||
pub success: bool,
|
||||
pub match_index: u64,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
@ -1335,4 +1908,133 @@ mod tests {
|
|||
network.reset();
|
||||
assert_eq!(network.time(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_semantic_embedder_hash() {
|
||||
let embedder = SemanticEmbedder::default_dimension();
|
||||
|
||||
// Same text should produce same embedding
|
||||
let emb1 = embedder.embed_hash("rust programming language");
|
||||
let emb2 = embedder.embed_hash("rust programming language");
|
||||
|
||||
assert_eq!(emb1.len(), 384);
|
||||
let sim = SemanticEmbedder::cosine_similarity(&emb1, &emb2);
|
||||
assert!((sim - 1.0).abs() < 0.001, "Same text similarity: {}", sim);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_semantic_embedder_similar_text() {
|
||||
let embedder = SemanticEmbedder::default_dimension();
|
||||
|
||||
let emb1 = embedder.embed_hash("rust programming");
|
||||
let emb2 = embedder.embed_hash("rust code development");
|
||||
let emb3 = embedder.embed_hash("python machine learning");
|
||||
|
||||
let sim_similar = SemanticEmbedder::cosine_similarity(&emb1, &emb2);
|
||||
let sim_different = SemanticEmbedder::cosine_similarity(&emb1, &emb3);
|
||||
|
||||
// Similar topics should have higher similarity than different topics
|
||||
// Note: hash-based embeddings have less semantic awareness
|
||||
assert!(sim_similar != sim_different, "Embeddings should differ");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_semantic_task_matcher() {
|
||||
let mut matcher = SemanticTaskMatcher::new();
|
||||
|
||||
matcher.register_agent("rust-agent", "compile rust code cargo build test");
|
||||
matcher.register_agent("python-agent", "python script machine learning numpy");
|
||||
matcher.register_agent("web-agent", "javascript html css react frontend");
|
||||
|
||||
// Find best agent for a task
|
||||
let result = matcher.match_agent("compile rust program");
|
||||
assert!(result.is_some());
|
||||
// The rust-agent should be most similar due to shared "rust" word
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_raft_leader_election() {
|
||||
let members = vec![
|
||||
"node-1".to_string(),
|
||||
"node-2".to_string(),
|
||||
"node-3".to_string(),
|
||||
];
|
||||
|
||||
let mut node1 = RaftNode::new("node-1", members.clone());
|
||||
let mut node2 = RaftNode::new("node-2", members.clone());
|
||||
let mut node3 = RaftNode::new("node-3", members.clone());
|
||||
|
||||
// Node 1 starts election
|
||||
let vote_req = node1.start_election();
|
||||
assert_eq!(node1.state, RaftState::Candidate);
|
||||
assert_eq!(node1.current_term, 1);
|
||||
|
||||
// Other nodes vote
|
||||
let resp2 = node2.handle_vote_request(&vote_req);
|
||||
let resp3 = node3.handle_vote_request(&vote_req);
|
||||
|
||||
assert!(resp2.vote_granted);
|
||||
assert!(resp3.vote_granted);
|
||||
|
||||
// Node 1 handles responses and becomes leader
|
||||
node1.handle_vote_response(&resp2);
|
||||
let became_leader = node1.handle_vote_response(&resp3);
|
||||
|
||||
assert!(became_leader || node1.is_leader());
|
||||
assert_eq!(node1.state, RaftState::Leader);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_raft_log_replication() {
|
||||
let members = vec![
|
||||
"leader".to_string(),
|
||||
"follower".to_string(),
|
||||
];
|
||||
|
||||
let mut leader = RaftNode::new("leader", members.clone());
|
||||
let mut follower = RaftNode::new("follower", members.clone());
|
||||
|
||||
// Make leader the leader
|
||||
leader.start_election();
|
||||
leader.become_leader();
|
||||
|
||||
// Append entry
|
||||
let index = leader.append_entry(b"command-1".to_vec());
|
||||
assert_eq!(index, Some(1));
|
||||
|
||||
// Create append entries for follower
|
||||
let append_req = leader.create_append_entries("follower").unwrap();
|
||||
assert!(!append_req.entries.is_empty());
|
||||
|
||||
// Follower handles append entries
|
||||
let append_resp = follower.handle_append_entries(&append_req);
|
||||
assert!(append_resp.success);
|
||||
assert_eq!(follower.log.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_raft_term_update() {
|
||||
let members = vec!["node-1".to_string(), "node-2".to_string()];
|
||||
|
||||
let mut node1 = RaftNode::new("node-1", members.clone());
|
||||
let mut node2 = RaftNode::new("node-2", members.clone());
|
||||
|
||||
// Node 1 is at term 1
|
||||
node1.current_term = 1;
|
||||
|
||||
// Node 2 sends vote request with higher term
|
||||
node2.current_term = 5;
|
||||
let vote_req = RaftVoteRequest {
|
||||
term: 5,
|
||||
candidate_id: "node-2".to_string(),
|
||||
last_log_index: 0,
|
||||
last_log_term: 0,
|
||||
};
|
||||
|
||||
node1.handle_vote_request(&vote_req);
|
||||
|
||||
// Node 1 should update its term
|
||||
assert_eq!(node1.current_term, 5);
|
||||
assert_eq!(node1.state, RaftState::Follower);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,4 +42,10 @@ pub use advanced::{
|
|||
HybridKeyPair, HybridPublicKey, HybridSignature,
|
||||
// Spiking neural networks
|
||||
LIFNeuron, SpikingNetwork,
|
||||
// Semantic embeddings
|
||||
SemanticEmbedder, SemanticTaskMatcher,
|
||||
// Raft consensus
|
||||
RaftNode, RaftState, LogEntry,
|
||||
RaftVoteRequest, RaftVoteResponse,
|
||||
RaftAppendEntries, RaftAppendEntriesResponse,
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue