Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
472e6cd
fix (cli + core): remove statics from config generation, adding rust …
xav-db Dec 11, 2025
d29d2c3
feat (core): pushing prod feature in helix container (#752)
xav-db Dec 11, 2025
d0b6fd7
impr (hql + cli + core): fixing concurrent write crashes, making cli …
xav-db Dec 16, 2025
795d148
impr (cli + core): implementing feedback command, bm25 null result bu…
xav-db Dec 16, 2025
8dc1fce
First pass on the vector_core rewrite
el-yawd Nov 19, 2025
14fc90f
Define maximum edges cannot be defined at compile time (unfortunately)
el-yawd Nov 19, 2025
2f5b034
Switch from f64 to f32 for vector representation
el-yawd Nov 19, 2025
e5ee5d4
Make indexes thread-safe by saving it inside a RwLock
el-yawd Nov 19, 2025
b1a57eb
Adjust spaces
el-yawd Nov 19, 2025
01c5296
Implement hnsw search
el-yawd Nov 19, 2025
d5f054a
Implement nns_to_hvector
el-yawd Nov 21, 2025
2b6c88a
Properly implement get_full_vector
el-yawd Nov 21, 2025
89220d5
Fix (some) clippy complaints
el-yawd Nov 22, 2025
aaaf9ff
Implement v_from_type and improve iterator implementation
el-yawd Nov 22, 2025
321b122
Temporarily rebuild on every insertion
el-yawd Nov 22, 2025
36b23be
Get serialization right
el-yawd Nov 22, 2025
d87c80b
Make copy and zero-copy HVector functions explicity
el-yawd Nov 22, 2025
3149410
Implement distance_to
el-yawd Nov 22, 2025
ec4b44d
Update compiler to default f32 vector type
el-yawd Nov 22, 2025
8887317
Cargo fmt
el-yawd Nov 22, 2025
45852de
Do not build madvise dependency for windows
el-yawd Nov 22, 2025
66f2f2e
Do not normalize cosine distance
el-yawd Nov 24, 2025
e78e1d0
Fix some broken tests
el-yawd Nov 24, 2025
2fc0874
Change in memory local to global id hashmap to persistent database
el-yawd Nov 24, 2025
435bdc9
Make creating a HVector from raw bytes a failable operation
el-yawd Nov 24, 2025
7252ea5
Add support for property filter queries
el-yawd Nov 24, 2025
46d6ba8
Use f32 as default value for traversal
el-yawd Nov 24, 2025
4ae62f7
Fix some bugs
el-yawd Nov 24, 2025
88559a4
Unbind nns_to_hvectors 'txn lifetime
el-yawd Nov 25, 2025
d2f39e3
Basic migration from old to new vector storage format
el-yawd Dec 8, 2025
7c540b1
Makes Clippy happy
el-yawd Dec 8, 2025
29331ac
Add error on `helix check` for old vector datatype (F64)
el-yawd Dec 8, 2025
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
1,429 changes: 729 additions & 700 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions helix-cli/src/cleanup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,7 @@ impl CleanupTracker {
};

// Step 1: Restore config from in-memory backup if modified
if let (Some(original_config), Some(config_path)) =
(self.original_config, self.config_path)
if let (Some(original_config), Some(config_path)) = (self.original_config, self.config_path)
{
match original_config.save_to_file(&config_path) {
Ok(_) => {
Expand Down
21 changes: 15 additions & 6 deletions helix-cli/src/commands/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ use crate::metrics_sender::MetricsSender;
use crate::project::{ProjectContext, get_helix_repo_cache};
use crate::prompts;
use crate::utils::{
copy_dir_recursive_excluding, diagnostic_source,
helixc_utils::{collect_hx_contents, collect_hx_files},
print_confirm, print_error, print_status, print_success, print_warning, Spinner,
Spinner, copy_dir_recursive_excluding, diagnostic_source, helixc_utils::collect_hx_contents,
helixc_utils::collect_hx_files, print_confirm, print_error, print_status, print_success,
print_warning,
};
use eyre::Result;
use std::time::Instant;
Expand Down Expand Up @@ -262,7 +262,10 @@ fn update_git_cache(repo_cache: &std::path::Path) -> Result<()> {
Ok(())
}

pub(crate) async fn prepare_instance_workspace(project: &ProjectContext, instance_name: &str) -> Result<()> {
pub(crate) async fn prepare_instance_workspace(
project: &ProjectContext,
instance_name: &str,
) -> Result<()> {
print_status(
"PREPARE",
&format!("Preparing workspace for '{instance_name}'"),
Expand Down Expand Up @@ -292,7 +295,10 @@ pub(crate) async fn prepare_instance_workspace(project: &ProjectContext, instanc
Ok(())
}

pub(crate) async fn compile_project(project: &ProjectContext, instance_name: &str) -> Result<MetricsData> {
pub(crate) async fn compile_project(
project: &ProjectContext,
instance_name: &str,
) -> Result<MetricsData> {
print_status("COMPILE", "Compiling Helix queries...");

// Create helix-container directory in instance workspace for generated files
Expand Down Expand Up @@ -456,7 +462,10 @@ fn read_config(instance_src_dir: &std::path::Path) -> Result<Config> {
}

/// Handle Rust compilation failure during Docker build - print errors and offer GitHub issue creation.
fn handle_docker_rust_compilation_failure(docker_output: &str, project: &ProjectContext) -> Result<()> {
fn handle_docker_rust_compilation_failure(
docker_output: &str,
project: &ProjectContext,
) -> Result<()> {
print_error("Rust compilation failed during Docker build");
println!();
println!("This may indicate a bug in the Helix code generator.");
Expand Down
211 changes: 209 additions & 2 deletions helix-cli/src/commands/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::utils::helixc_utils::{
};
use crate::utils::{print_confirm, print_error, print_status, print_success, print_warning};
use eyre::Result;
use helix_db::helixc::parser::types::FieldType;
use std::fs;
use std::path::Path;
use std::process::Command;
Expand Down Expand Up @@ -49,6 +50,10 @@ async fn check_instance(
validate_project_syntax(project)?;
print_success("Syntax validation passed");

// Step 1.5: Validate vector data types
print_status("VECTORS", "Validating vector data types...");
validate_vector_data_types(project)?;

// Step 2: Ensure helix repo is cached (reuse from build.rs)
build::ensure_helix_repo_cached().await?;

Expand Down Expand Up @@ -115,14 +120,23 @@ async fn check_all_instances(
) -> Result<()> {
print_status("CHECK", "Checking all instances");

let instances: Vec<String> = project.config.list_instances().into_iter().map(String::from).collect();
let instances: Vec<String> = project
.config
.list_instances()
.into_iter()
.map(String::from)
.collect();

if instances.is_empty() {
return Err(eyre::eyre!(
"No instances found in helix.toml. Add at least one instance to check."
));
}

// Validate vector data types once for all instances
print_status("VECTORS", "Validating vector data types...");
validate_vector_data_types(project)?;

// Check each instance
for instance_name in &instances {
check_instance(project, instance_name, metrics_sender).await?;
Expand All @@ -132,6 +146,63 @@ async fn check_all_instances(
Ok(())
}

/// Validate vector data types and warn about F64 usage
fn validate_vector_data_types(project: &ProjectContext) -> Result<()> {
// Collect all .hx files for validation
let hx_files = collect_hx_files(&project.root, &project.config.project.queries)?;

// Generate content and parse
let content = generate_content(&hx_files)?;
let source = parse_content(&content)?;

let mut found_f64_vectors = false;
let mut f64_vector_names = Vec::new();

// Check all vector schemas for F64 usage
for schema in source.get_schemas_in_order() {
for vector_schema in &schema.vector_schemas {
for field in &vector_schema.fields {
if contains_f64_type(&field.field_type) {
found_f64_vectors = true;
f64_vector_names.push(format!("V::{}.{}", vector_schema.name, field.name));
}
}
}
}

if found_f64_vectors {
print_warning("Found F64 data types in vector fields");
println!();
println!(" Vector fields using F64:");
for vector_name in &f64_vector_names {
println!(" • {}", vector_name);
}
println!();
println!(" ⚠️ F64 vectors are deprecated.");
println!(
" For vectors, use [F32] instead of [F64] for better performance and compatibility."
);
println!(" F32 provides sufficient precision for most vector similarity use cases.");
return Err(eyre::eyre!(
"Vectors with F64 data types are deprecated. Use F32 instead."
));
} else {
print_success("Vector data types validation passed");
}

Ok(())
}

/// Recursively check if a FieldType contains F64
fn contains_f64_type(field_type: &FieldType) -> bool {
match field_type {
FieldType::F64 => true,
FieldType::Array(inner) => contains_f64_type(inner),
FieldType::Object(obj) => obj.values().any(contains_f64_type),
_ => false,
}
}

/// Validate project syntax by parsing queries and schema (similar to build.rs but without generating files)
fn validate_project_syntax(project: &ProjectContext) -> Result<()> {
// Collect all .hx files for validation
Expand Down Expand Up @@ -193,7 +264,8 @@ fn handle_cargo_check_failure(
print_warning("You can report this issue to help improve Helix.");
println!();

let should_create = print_confirm("Would you like to create a GitHub issue with diagnostic information?")?;
let should_create =
print_confirm("Would you like to create a GitHub issue with diagnostic information?")?;

if !should_create {
return Ok(());
Expand All @@ -217,3 +289,138 @@ fn handle_cargo_check_failure(

Ok(())
}

#[cfg(test)]
mod tests {
use super::*;
use crate::config::{ContainerRuntime, HelixConfig, ProjectConfig};
use std::fs;
use tempfile::tempdir;

#[test]
fn test_validate_vector_data_types_with_f64() {
let temp_dir = tempdir().unwrap();
let project_root = temp_dir.path();

// Create a helix.toml config
let config = HelixConfig {
project: ProjectConfig {
name: "test_project".to_string(),
queries: std::path::PathBuf::from("./db/"),
container_runtime: ContainerRuntime::Docker,
},
local: std::collections::HashMap::new(),
cloud: std::collections::HashMap::new(),
};

// Create project context
let project = ProjectContext {
root: project_root.to_path_buf(),
config,
helix_dir: project_root.join(".helix"),
};

// Create db directory
let db_dir = project_root.join("db");
fs::create_dir_all(&db_dir).unwrap();

// Create a .hx file with F64 vector fields
let schema_content = r#"
V::Document {
content: String,
embedding: [F64],
scores: [F64]
}

QUERY test() =>
d <- V<Document>
RETURN d
"#;

fs::write(db_dir.join("schema.hx"), schema_content).unwrap();

// Test validation - should detect F64 usage
let result = validate_vector_data_types(&project);
assert!(
result.is_ok(),
"Validation should succeed but warn about F64"
);
}

#[test]
fn test_validate_vector_data_types_with_f32() {
let temp_dir = tempdir().unwrap();
let project_root = temp_dir.path();

// Create a helix.toml config
let config = HelixConfig {
project: ProjectConfig {
name: "test_project".to_string(),
queries: std::path::PathBuf::from("./db/"),
container_runtime: ContainerRuntime::Docker,
},
local: std::collections::HashMap::new(),
cloud: std::collections::HashMap::new(),
};

// Create project context
let project = ProjectContext {
root: project_root.to_path_buf(),
config,
helix_dir: project_root.join(".helix"),
};

// Create db directory
let db_dir = project_root.join("db");
fs::create_dir_all(&db_dir).unwrap();

// Create a .hx file with F32 vector fields (correct)
let schema_content = r#"
V::Document {
content: String,
embedding: [F32],
scores: [F32]
}

QUERY test() =>
d <- V<Document>
RETURN d
"#;

fs::write(db_dir.join("schema.hx"), schema_content).unwrap();

// Test validation - should pass without warnings
let result = validate_vector_data_types(&project);
assert!(result.is_ok(), "Validation should succeed with F32");
}

#[test]
fn test_contains_f64_type() {
use helix_db::helixc::parser::types::FieldType;

// Test direct F64
assert!(contains_f64_type(&FieldType::F64));

// Test F32 (should be false)
assert!(!contains_f64_type(&FieldType::F32));

// Test Array of F64
assert!(contains_f64_type(&FieldType::Array(Box::new(
FieldType::F64
))));

// Test Array of F32 (should be false)
assert!(!contains_f64_type(&FieldType::Array(Box::new(
FieldType::F32
))));

// Test nested object with F64
let mut obj = std::collections::HashMap::new();
obj.insert("score".to_string(), FieldType::F64);
assert!(contains_f64_type(&FieldType::Object(obj)));

// Test other types
assert!(!contains_f64_type(&FieldType::String));
assert!(!contains_f64_type(&FieldType::Boolean));
}
}
5 changes: 4 additions & 1 deletion helix-cli/src/commands/integrations/fly.rs
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,10 @@ impl<'a> FlyManager<'a> {
}

// Check if fly.toml already exists for this instance
let fly_toml_path = self.project.instance_workspace(instance_name).join("fly.toml");
let fly_toml_path = self
.project
.instance_workspace(instance_name)
.join("fly.toml");
if let Some(existing_app_name) = Self::read_app_name_from_fly_toml(&fly_toml_path)? {
// Check if the app in fly.toml exists on Fly.io
if self.app_exists(&existing_app_name).await? {
Expand Down
3 changes: 2 additions & 1 deletion helix-cli/src/commands/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ async fn show_metrics_status() -> Result<()> {
Ok(())
}

static EMAIL_REGEX: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$").unwrap());
static EMAIL_REGEX: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$").unwrap());

fn ask_for_email() -> String {
print_line("Please enter your email address:");
Expand Down
19 changes: 6 additions & 13 deletions helix-cli/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ impl CliError {
self
}


pub fn render(&self) -> String {
let mut output = String::new();

Expand Down Expand Up @@ -186,36 +185,30 @@ pub type CliResult<T> = Result<T, CliError>;
// Convenience functions for common error patterns with error codes
#[allow(unused)]
pub fn config_error<S: Into<String>>(message: S) -> CliError {
CliError::new(message)
.with_hint("run `helix init` if you need to create a new project")
CliError::new(message).with_hint("run `helix init` if you need to create a new project")
}

#[allow(unused)]
pub fn file_error<S: Into<String>>(message: S, file_path: S) -> CliError {
CliError::new(message)
.with_file_path(file_path)
CliError::new(message).with_file_path(file_path)
}

#[allow(unused)]
pub fn docker_error<S: Into<String>>(message: S) -> CliError {
CliError::new(message)
.with_hint("ensure Docker is running and accessible")
CliError::new(message).with_hint("ensure Docker is running and accessible")
}

#[allow(unused)]
pub fn network_error<S: Into<String>>(message: S) -> CliError {
CliError::new(message)
.with_hint("check your internet connection and try again")
CliError::new(message).with_hint("check your internet connection and try again")
}

#[allow(unused)]
pub fn project_error<S: Into<String>>(message: S) -> CliError {
CliError::new(message)
.with_hint("ensure you're in a valid helix project directory")
CliError::new(message).with_hint("ensure you're in a valid helix project directory")
}

#[allow(unused)]
pub fn cloud_error<S: Into<String>>(message: S) -> CliError {
CliError::new(message)
.with_hint("run `helix auth login` to authenticate with Helix Cloud")
CliError::new(message).with_hint("run `helix auth login` to authenticate with Helix Cloud")
}
2 changes: 1 addition & 1 deletion helix-cli/src/metrics_sender.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use chrono::{Local, NaiveDate};
use dirs::home_dir;
use eyre::{eyre, OptionExt, Result};
use eyre::{OptionExt, Result, eyre};
use flume::{Receiver, Sender, unbounded};
use helix_metrics::events::{
CompileEvent, DeployCloudEvent, DeployLocalEvent, EventData, EventType, RawEvent,
Expand Down
Loading
Loading