Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions rust/gp-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ gp-swarm = { path = "../gp-swarm" }
clap = { version = "4", features = ["derive", "env"] }
serde.workspace = true
serde_json.workspace = true
chrono = { workspace = true }
anyhow.workspace = true
toml = "0.8"
rand = { workspace = true }
Expand Down
16 changes: 16 additions & 0 deletions rust/gp-core/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ pub struct PheromoneConfig {
pub recency_decay: f64,
/// How many cycles between bulk decay operations.
pub decay_interval_cycles: usize,
/// Maximum exploitation pheromone value (τ_max). Default 5.0.
pub exploitation_max: f64,
/// Maximum exploration pheromone value (τ_max). Default 3.0.
pub exploration_max: f64,
/// Maximum success pheromone value (τ_max). Default 5.0.
Comment on lines +23 to +27
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve backward-compatible config deserialization

These added pheromone ceiling fields are required by default during Deserialize, so older saved GraphPalaceConfig JSON (which lacks them) will fail to parse. In practice this causes upgrade regressions: when serde_json::from_str::<GraphPalaceConfig> fails, the CLI falls back to defaults and silently drops previously persisted tuning values instead of preserving existing config.

Useful? React with 👍 / 👎.

pub success_max: f64,
/// Maximum traversal pheromone value (τ_max). Default 2.0.
pub traversal_max: f64,
/// Maximum recency pheromone value (τ_max). Default 1.0.
pub recency_max: f64,
}

impl Default for PheromoneConfig {
Expand All @@ -31,6 +41,12 @@ impl Default for PheromoneConfig {
traversal_decay: 0.03,
recency_decay: 0.10,
decay_interval_cycles: 10,
// Saturation ceilings (τ_max)
exploitation_max: 5.0,
exploration_max: 3.0,
success_max: 5.0,
traversal_max: 2.0,
recency_max: 1.0,
}
}
}
Expand Down
96 changes: 96 additions & 0 deletions rust/gp-core/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,19 @@
//! These statements initialize the GraphPalace schema in Kuzu.
//! All definitions map to the types in [`crate::types`].

/// Schema dialect for index creation syntax.
///
/// Different backends require different DDL syntax for index creation.
/// The core node/rel table DDL is shared, but index creation varies.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum SchemaDialect {
/// Standard Cypher DDL (used by InMemoryBackend and Kuzu).
#[default]
Cypher,
/// LadybugDB stored-procedure syntax for index creation.
LadybugDb,
}

/// All Cypher DDL statements for creating the GraphPalace schema.
///
/// Returns statements in the correct execution order:
Expand Down Expand Up @@ -207,6 +220,44 @@ pub fn property_indexes() -> Vec<&'static str> {
]
}

/// Schema DDL for a specific backend dialect.
///
/// Node/rel table DDL is shared; only index creation syntax varies.
pub fn schema_ddl_for(dialect: SchemaDialect) -> Vec<String> {
let mut stmts: Vec<String> = Vec::new();
// Node and rel tables are dialect-independent
stmts.extend(node_tables().iter().map(|s| s.to_string()));
stmts.extend(rel_tables().iter().map(|s| s.to_string()));
// Index syntax is dialect-dependent
stmts.extend(vector_indexes_for(dialect));
stmts.extend(fts_indexes_for(dialect));
stmts.extend(property_indexes().iter().map(|s| s.to_string()));
stmts
}

/// Vector index creation DDL for a specific dialect.
pub fn vector_indexes_for(dialect: SchemaDialect) -> Vec<String> {
match dialect {
SchemaDialect::Cypher => vector_indexes().iter().map(|s| s.to_string()).collect(),
SchemaDialect::LadybugDb => vec![
"CALL CREATE_VECTOR_INDEX('drawer_embedding_idx', 'Drawer', 'embedding', 'cosine', 16, 200)".to_string(),
"CALL CREATE_VECTOR_INDEX('entity_embedding_idx', 'Entity', 'embedding', 'cosine', 16, 200)".to_string(),
"CALL CREATE_VECTOR_INDEX('room_embedding_idx', 'Room', 'embedding', 'cosine', 16, 200)".to_string(),
],
}
}

/// FTS index creation DDL for a specific dialect.
pub fn fts_indexes_for(dialect: SchemaDialect) -> Vec<String> {
match dialect {
SchemaDialect::Cypher => fts_indexes().iter().map(|s| s.to_string()).collect(),
SchemaDialect::LadybugDb => vec![
"CALL CREATE_FTS_INDEX('drawer_content_idx', 'Drawer', ['content'])".to_string(),
"CALL CREATE_FTS_INDEX('entity_name_idx', 'Entity', ['name', 'description'])".to_string(),
],
}
}

/// Cypher statements for bulk pheromone decay operations.
pub fn decay_statements() -> Vec<&'static str> {
vec![
Expand Down Expand Up @@ -286,4 +337,49 @@ mod tests {
assert!(stmts[1].contains("traversal_pheromone"));
assert!(stmts[1].contains("recency_pheromone"));
}

// ─── Schema dialect ──────────────────────────────────────────────────

#[test]
fn test_schema_dialect_default_is_cypher() {
assert_eq!(SchemaDialect::default(), SchemaDialect::Cypher);
}

#[test]
fn test_vector_indexes_cypher_matches_original() {
let original: Vec<String> = vector_indexes().iter().map(|s| s.to_string()).collect();
let dialect = vector_indexes_for(SchemaDialect::Cypher);
assert_eq!(original, dialect);
}

#[test]
fn test_vector_indexes_ladybugdb() {
let stmts = vector_indexes_for(SchemaDialect::LadybugDb);
assert_eq!(stmts.len(), 3);
assert!(stmts[0].starts_with("CALL CREATE_VECTOR_INDEX"));
assert!(stmts[0].contains("'Drawer'"));
assert!(stmts[0].contains("'cosine'"));
}

#[test]
fn test_fts_indexes_ladybugdb() {
let stmts = fts_indexes_for(SchemaDialect::LadybugDb);
assert_eq!(stmts.len(), 2);
assert!(stmts[0].starts_with("CALL CREATE_FTS_INDEX"));
assert!(stmts[1].contains("['name', 'description']"));
}

#[test]
fn test_schema_ddl_for_cypher_count() {
let stmts = schema_ddl_for(SchemaDialect::Cypher);
// Same count as original: 7 + 11 + 3 + 2 + 4 = 27
assert_eq!(stmts.len(), 27);
}

#[test]
fn test_schema_ddl_for_ladybugdb_count() {
let stmts = schema_ddl_for(SchemaDialect::LadybugDb);
// Same count: 7 + 11 + 3 + 2 + 4 = 27
assert_eq!(stmts.len(), 27);
}
}
5 changes: 4 additions & 1 deletion rust/gp-palace/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ pub mod search;

// Re-export primary public types.
pub use export::{ImportMode, ImportStats, PalaceExport};
pub use lifecycle::{ColdSpot, HotPath, KgRelationship, PalaceStatus};
pub use lifecycle::{
ColdSpot, HotPath, HyperstructureMetrics, HyperstructurePhase,
KgRelationship, LifecycleSummary, PalaceStatus,
};
pub use palace::GraphPalace;
pub use search::{DuplicateMatch, SearchResult};
Loading
Loading