Skip to content
This repository was archived by the owner on Jun 3, 2026. It is now read-only.

Commit ab9c205

Browse files
committed
add weaver in rust
1 parent cebee66 commit ab9c205

7 files changed

Lines changed: 1266 additions & 0 deletions

File tree

rust/xmem-weaver/Cargo.toml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[package]
2+
name = "xmem-weaver"
3+
version = "0.1.0"
4+
edition = "2021"
5+
description = "Standalone deterministic XMem weaver core"
6+
license = "MIT"
7+
8+
[lib]
9+
name = "xmem_weaver"
10+
path = "src/lib.rs"
11+
12+
[dependencies]
13+
serde = { version = "1.0", features = ["derive"] }
14+
thiserror = "1.0"
15+
16+
[dev-dependencies]
17+
serde_json = "1.0"

rust/xmem-weaver/README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# xmem-weaver
2+
3+
Standalone Rust implementation of the deterministic XMem weaver core.
4+
5+
This crate is intentionally not wired into the Python pipeline yet. It mirrors
6+
the Python weaver's core contract:
7+
8+
- consume `JudgeResult`
9+
- execute `ADD`, `UPDATE`, `DELETE`, `NOOP`
10+
- return `WeaverResult`
11+
- delegate storage writes through traits
12+
13+
The intended boundary is:
14+
15+
```text
16+
Python orchestration / agents / prompts
17+
|
18+
v
19+
Rust xmem-weaver deterministic execution
20+
|
21+
v
22+
Vector store / graph store adapters
23+
```
24+
25+
Current scope:
26+
27+
- vector domains: `profile`, `summary`, `image`
28+
- graph domains: `temporal`
29+
- code and snippet domains through vector execution hooks
30+
- no Python imports
31+
- no FastAPI or pipeline integration
32+
33+
Run tests from this directory:
34+
35+
```bash
36+
cargo test
37+
```

rust/xmem-weaver/src/lib.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
pub mod model;
2+
pub mod parser;
3+
pub mod storage;
4+
pub mod weaver;
5+
6+
pub use model::{
7+
ExecutedOp, JudgeDomain, JudgeResult, OpStatus, Operation, OperationType, WeaverResult,
8+
};
9+
pub use parser::{
10+
extract_structured_metadata, parse_code_annotation_content, parse_snippet_content,
11+
parse_temporal_content,
12+
};
13+
pub use storage::{
14+
EmbeddingProvider, GraphAnnotationStore, TemporalGraphStore, VectorDocument, VectorStore,
15+
};
16+
pub use weaver::{Weaver, WeaverError};

rust/xmem-weaver/src/model.rs

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
use serde::{Deserialize, Serialize};
2+
3+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
4+
pub enum OperationType {
5+
#[serde(rename = "ADD")]
6+
Add,
7+
#[serde(rename = "UPDATE")]
8+
Update,
9+
#[serde(rename = "DELETE")]
10+
Delete,
11+
#[serde(rename = "NOOP")]
12+
Noop,
13+
}
14+
15+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
16+
pub enum JudgeDomain {
17+
#[serde(rename = "profile")]
18+
Profile,
19+
#[serde(rename = "temporal")]
20+
Temporal,
21+
#[serde(rename = "summary")]
22+
Summary,
23+
#[serde(rename = "image")]
24+
Image,
25+
#[serde(rename = "code")]
26+
Code,
27+
#[serde(rename = "snippet")]
28+
Snippet,
29+
}
30+
31+
impl JudgeDomain {
32+
pub fn as_str(self) -> &'static str {
33+
match self {
34+
Self::Profile => "profile",
35+
Self::Temporal => "temporal",
36+
Self::Summary => "summary",
37+
Self::Image => "image",
38+
Self::Code => "code",
39+
Self::Snippet => "snippet",
40+
}
41+
}
42+
43+
pub fn is_batched_vector_domain(self) -> bool {
44+
matches!(self, Self::Profile | Self::Summary | Self::Image)
45+
}
46+
}
47+
48+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
49+
pub struct Operation {
50+
#[serde(rename = "type")]
51+
pub operation_type: OperationType,
52+
#[serde(default)]
53+
pub content: String,
54+
#[serde(default)]
55+
pub embedding_id: Option<String>,
56+
#[serde(default)]
57+
pub reason: String,
58+
}
59+
60+
impl Operation {
61+
pub fn add(content: impl Into<String>) -> Self {
62+
Self {
63+
operation_type: OperationType::Add,
64+
content: content.into(),
65+
embedding_id: None,
66+
reason: String::new(),
67+
}
68+
}
69+
70+
pub fn update(content: impl Into<String>, embedding_id: impl Into<String>) -> Self {
71+
Self {
72+
operation_type: OperationType::Update,
73+
content: content.into(),
74+
embedding_id: Some(embedding_id.into()),
75+
reason: String::new(),
76+
}
77+
}
78+
79+
pub fn delete(embedding_id: impl Into<String>) -> Self {
80+
Self {
81+
operation_type: OperationType::Delete,
82+
content: String::new(),
83+
embedding_id: Some(embedding_id.into()),
84+
reason: String::new(),
85+
}
86+
}
87+
88+
pub fn noop() -> Self {
89+
Self {
90+
operation_type: OperationType::Noop,
91+
content: String::new(),
92+
embedding_id: None,
93+
reason: String::new(),
94+
}
95+
}
96+
97+
pub fn as_add(mut self) -> Self {
98+
self.operation_type = OperationType::Add;
99+
self.embedding_id = None;
100+
self
101+
}
102+
}
103+
104+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
105+
pub struct JudgeResult {
106+
#[serde(default)]
107+
pub operations: Vec<Operation>,
108+
#[serde(default)]
109+
pub confidence: f32,
110+
}
111+
112+
impl JudgeResult {
113+
pub fn is_empty(&self) -> bool {
114+
self.operations.is_empty()
115+
}
116+
117+
pub fn has_writes(&self) -> bool {
118+
self.operations
119+
.iter()
120+
.any(|op| op.operation_type != OperationType::Noop)
121+
}
122+
}
123+
124+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
125+
pub enum OpStatus {
126+
#[serde(rename = "success")]
127+
Success,
128+
#[serde(rename = "skipped")]
129+
Skipped,
130+
#[serde(rename = "failed")]
131+
Failed,
132+
}
133+
134+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
135+
pub struct ExecutedOp {
136+
#[serde(rename = "type")]
137+
pub operation_type: OperationType,
138+
pub status: OpStatus,
139+
#[serde(default)]
140+
pub content: String,
141+
#[serde(default)]
142+
pub embedding_id: Option<String>,
143+
#[serde(default)]
144+
pub new_id: Option<String>,
145+
#[serde(default)]
146+
pub error: Option<String>,
147+
}
148+
149+
impl ExecutedOp {
150+
pub fn success(op: &Operation) -> Self {
151+
Self {
152+
operation_type: op.operation_type,
153+
status: OpStatus::Success,
154+
content: op.content.clone(),
155+
embedding_id: op.embedding_id.clone(),
156+
new_id: None,
157+
error: None,
158+
}
159+
}
160+
161+
pub fn success_with_new_id(op: &Operation, new_id: Option<String>) -> Self {
162+
Self {
163+
new_id,
164+
..Self::success(op)
165+
}
166+
}
167+
168+
pub fn skipped(op: &Operation, error: Option<String>) -> Self {
169+
Self {
170+
operation_type: op.operation_type,
171+
status: OpStatus::Skipped,
172+
content: op.content.clone(),
173+
embedding_id: op.embedding_id.clone(),
174+
new_id: None,
175+
error,
176+
}
177+
}
178+
179+
pub fn failed(op: &Operation, error: impl Into<String>) -> Self {
180+
Self {
181+
operation_type: op.operation_type,
182+
status: OpStatus::Failed,
183+
content: op.content.clone(),
184+
embedding_id: op.embedding_id.clone(),
185+
new_id: None,
186+
error: Some(error.into()),
187+
}
188+
}
189+
}
190+
191+
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
192+
pub struct WeaverResult {
193+
#[serde(default)]
194+
pub executed: Vec<ExecutedOp>,
195+
}
196+
197+
impl WeaverResult {
198+
pub fn total(&self) -> usize {
199+
self.executed.len()
200+
}
201+
202+
pub fn succeeded(&self) -> usize {
203+
self.executed
204+
.iter()
205+
.filter(|op| op.status == OpStatus::Success)
206+
.count()
207+
}
208+
209+
pub fn skipped(&self) -> usize {
210+
self.executed
211+
.iter()
212+
.filter(|op| op.status == OpStatus::Skipped)
213+
.count()
214+
}
215+
216+
pub fn failed(&self) -> usize {
217+
self.executed
218+
.iter()
219+
.filter(|op| op.status == OpStatus::Failed)
220+
.count()
221+
}
222+
}

0 commit comments

Comments
 (0)