1use std::cmp::Ordering;
2use std::collections::{BTreeMap, HashMap, HashSet};
3
4pub use capabilities::{
5 check_version_compat, op_ordering_contract, required_capabilities,
6 validate_plan_against_capabilities, CapabilityError, EngineCapabilities,
7 EngineCapabilityDocument, ExprKind, OpKind, OpOrderingContract, OpOrderingDocument, PlanSemver,
8 RequiredCapabilities, VersionRange, ALL_EXPR_KINDS, ALL_OP_KINDS,
9};
10pub use embedded_profile::{
11 assess_embedded_graph_rag_capabilities, assess_embedded_graph_rag_plan,
12 validate_embedded_graph_rag_plan, EmbeddedGraphRagAssessment, EmbeddedGraphRagCapabilityGap,
13 EmbeddedGraphRagProfileError, EMBEDDED_GRAPH_RAG_PROFILE, EMBEDDED_PHASE_1_RELEASE_CANDIDATE,
14 PLEXUS_CAPABILITY_REJECTION_SCENARIO,
15};
16pub use independent_consumer::{proof_fixture_graph, IndependentConsumerEngine};
17use plexus_serde::{
18 deserialize_plan, validate_plan_structure, AggFn, CmpOp, ExpandDir, Expr, Op, Plan, SortDir,
19};
20pub use reference_topology::{
21 build_compiled_plan_cache_descriptor, flagship_graph_rag_reference_contract,
22 CompiledPlanCacheContract, CompiledPlanCacheDescriptor, CompiledPlanCacheRequest,
23 FlagshipGraphRagReferenceContract, COMPILED_PLAN_CACHE_NAMESPACE, COMPILED_PLAN_CACHE_SCHEMA,
24 FLAGSHIP_COMPATIBILITY_PROFILE, FLAGSHIP_GRAPH_RAG_REFERENCE_TOPOLOGY,
25 FLAGSHIP_PHASE_3_RELEASE_CANDIDATE, RHODIUM_COMPILED_PLAN_CACHE_ROLE,
26};
27
28#[derive(Debug, Clone, PartialEq)]
29pub enum Value {
30 Null,
31 Bool(bool),
32 Int(i64),
33 Float(f64),
34 String(String),
35 NodeRef(u64),
36 RelRef(u64),
37 List(Vec<Value>),
38 Map(BTreeMap<String, Value>),
39}
40
41pub type Row = Vec<Value>;
42
43type RowSet = Vec<Row>;
44
45#[derive(Debug, Clone, PartialEq)]
46pub struct QueryResult {
47 pub rows: Vec<Row>,
48}
49
50impl QueryResult {
51 pub fn empty() -> Self {
52 Self { rows: Vec::new() }
53 }
54}
55
56#[derive(Debug, Clone, PartialEq)]
57pub struct Node {
58 pub id: u64,
59 pub labels: HashSet<String>,
60 pub props: HashMap<String, Value>,
61}
62
63#[derive(Debug, Clone, PartialEq)]
64pub struct Relationship {
65 pub id: u64,
66 pub src: u64,
67 pub dst: u64,
68 pub typ: String,
69 pub props: HashMap<String, Value>,
70}
71
72#[derive(Debug, Clone, PartialEq, Default)]
79pub struct Graph {
80 pub nodes: Vec<Node>,
81 pub rels: Vec<Relationship>,
82}
83
84impl Graph {
85 pub fn node_by_id(&self, id: u64) -> Option<&Node> {
87 self.nodes.iter().find(|n| n.id == id)
88 }
89
90 pub fn node_by_id_mut(&mut self, id: u64) -> Option<&mut Node> {
92 self.nodes.iter_mut().find(|n| n.id == id)
93 }
94
95 pub fn rel_by_id(&self, id: u64) -> Option<&Relationship> {
97 self.rels.iter().find(|r| r.id == id)
98 }
99
100 pub fn rel_by_id_mut(&mut self, id: u64) -> Option<&mut Relationship> {
102 self.rels.iter_mut().find(|r| r.id == id)
103 }
104}
105
106#[derive(Debug, thiserror::Error)]
107pub enum ExecutionError {
108 #[error("invalid op reference index {0}")]
109 InvalidOpRef(u32),
110 #[error("missing op output at index {0}")]
111 MissingOpOutput(u32),
112 #[error("invalid root op index {0}")]
113 InvalidRootOp(u32),
114 #[error("row column {idx} out of bounds (len={len})")]
115 ColumnOutOfBounds { idx: usize, len: usize },
116 #[error("expected a boolean value")]
117 ExpectedBool,
118 #[error("expected a numeric value")]
119 ExpectedNumeric,
120 #[error("expected aggregate expression")]
121 ExpectedAggregateExpr,
122 #[error("sort keys/dirs length mismatch ({keys} vs {dirs})")]
123 SortArityMismatch { keys: usize, dirs: usize },
124 #[error("unknown node id {0}")]
125 UnknownNode(u64),
126 #[error("unknown relationship id {0}")]
127 UnknownRel(u64),
128 #[error("expected node reference in column {idx}")]
129 ExpectedNodeRef { idx: usize },
130 #[error("expected relationship reference in column {idx}")]
131 ExpectedRelRef { idx: usize },
132 #[error("expected node or relationship reference in column {idx}")]
133 ExpectedEntityRef { idx: usize },
134 #[error("cannot delete node {0} without detach while relationships exist")]
135 DeleteRequiresDetach(u64),
136 #[error("expected map or null for properties payload")]
137 ExpectedMapPayload,
138 #[error("unsupported op in reference engine: {0}")]
139 UnsupportedOp(&'static str),
140 #[error("unsupported expression in reference engine: {0}")]
141 UnsupportedExpr(&'static str),
142 #[error("unbound parameter: {0}")]
143 UnboundParam(String),
144 #[error("plan requires multi-graph support but engine declares supports_multi_graph=false")]
145 MultiGraphUnsupported,
146}
147
148#[derive(Debug, thiserror::Error)]
149pub enum EngineError<E: std::error::Error + 'static> {
150 #[error("invalid serialized Plexus plan: {0}")]
151 Deserialize(#[from] plexus_serde::SerdeError),
152 #[error("invalid plan structure: {0}")]
153 InvalidStructure(plexus_serde::PlanValidationError),
154 #[error("engine execution failed: {0}")]
155 Engine(E),
156}
157
158pub trait PlanEngine {
160 type Error: std::error::Error + Send + Sync + 'static;
161
162 fn execute_plan(&mut self, plan: &Plan) -> Result<QueryResult, Self::Error>;
164}
165
166pub trait MutationEngine {
168 type Error: std::error::Error + Send + Sync + 'static;
169
170 fn create_node(
171 &mut self,
172 labels: &[String],
173 props: HashMap<String, Value>,
174 ) -> Result<u64, Self::Error>;
175 fn create_rel(
176 &mut self,
177 src: u64,
178 dst: u64,
179 rel_type: &str,
180 props: HashMap<String, Value>,
181 ) -> Result<u64, Self::Error>;
182 fn merge_pattern(
183 &mut self,
184 pattern: &Expr,
185 on_create_props: &Expr,
186 on_match_props: &Expr,
187 schema: &[plexus_serde::ColDef],
188 row: &Row,
189 ) -> Result<(), Self::Error>;
190 fn delete_entity(&mut self, target: &Value, detach: bool) -> Result<(), Self::Error>;
191 fn set_property(&mut self, target: &Value, key: &str, value: Value) -> Result<(), Self::Error>;
192 fn remove_property(&mut self, target: &Value, key: &str) -> Result<(), Self::Error>;
193}
194
195pub fn execute_serialized<E: PlanEngine>(
200 engine: &mut E,
201 bytes: &[u8],
202) -> Result<QueryResult, EngineError<E::Error>> {
203 let plan = deserialize_plan(bytes)?;
204 if let Err(errors) = validate_plan_structure(&plan) {
205 return Err(EngineError::InvalidStructure(
206 errors.into_iter().next().unwrap(),
207 ));
208 }
209 engine.execute_plan(&plan).map_err(EngineError::Engine)
210}
211
212#[derive(Debug, Clone, Default)]
222pub struct InMemoryEngine {
223 pub graph: Graph,
224 pub params: HashMap<String, Value>,
225}
226
227impl InMemoryEngine {
228 pub fn new(graph: Graph) -> Self {
229 Self {
230 graph,
231 params: HashMap::new(),
232 }
233 }
234
235 pub fn with_params(graph: Graph, params: HashMap<String, Value>) -> Self {
236 Self { graph, params }
237 }
238
239 pub fn set_param(&mut self, name: impl Into<String>, value: Value) {
240 self.params.insert(name.into(), value);
241 }
242
243 pub fn clear_params(&mut self) {
244 self.params.clear();
245 }
246}
247
248#[derive(Debug, Clone, PartialEq)]
249pub struct VectorCollectionEntry {
250 pub node_id: u64,
251 pub embedding: Vec<f64>,
252}
253
254#[derive(Debug, Clone, Default)]
255pub struct MockVectorEngine {
256 pub base: InMemoryEngine,
257 pub collections: HashMap<String, Vec<VectorCollectionEntry>>,
258}
259
260impl MockVectorEngine {
261 pub fn new(graph: Graph) -> Self {
262 Self {
263 base: InMemoryEngine::new(graph),
264 collections: HashMap::new(),
265 }
266 }
267
268 pub fn with_collections(
269 graph: Graph,
270 collections: HashMap<String, Vec<VectorCollectionEntry>>,
271 ) -> Self {
272 Self {
273 base: InMemoryEngine::new(graph),
274 collections,
275 }
276 }
277
278 pub fn insert_collection(
279 &mut self,
280 name: impl Into<String>,
281 entries: Vec<VectorCollectionEntry>,
282 ) {
283 self.collections.insert(name.into(), entries);
284 }
285
286 pub fn set_param(&mut self, name: impl Into<String>, value: Value) {
287 self.base.set_param(name, value);
288 }
289
290 pub fn clear_params(&mut self) {
291 self.base.clear_params();
292 }
293}
294
295mod capabilities;
296mod embedded_profile;
297mod independent_consumer;
298mod reference_topology;
299mod storage;
300pub use storage::{EntityRef, StorageAdapter};
301mod engine;
302
303#[cfg(test)]
304mod tests;