From d8fad69cf6ffe6bb73e5a8e399bc6f447f73b70d Mon Sep 17 00:00:00 2001 From: "Javier M. Torres" Date: Mon, 1 Jun 2026 16:35:41 +0200 Subject: [PATCH 1/7] Initial windows support --- .cargo/config.toml | 4 ++++ Cargo.lock | 2 -- encoderfile-py/Cargo.toml | 2 +- encoderfile/src/builder/base_binary/mod.rs | 11 +++++++---- encoderfile/tests/integration/test_build.rs | 8 ++++++++ test_config.yml | 1 + 6 files changed, 21 insertions(+), 7 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 62b1cdfc..a7b1e82b 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,3 +1,7 @@ [env] RUST_LOG = "info,tower_http=debug,ort=warn,encoderfile=debug" + +[patch.crates-io] +# esaxx-rs = { git = "https://github.com/thewh1teagle/esaxx-rs.git", branch = "feat/dynamic-msvc-link" } +esaxx-rs = { path = "../esaxx-rs-dyn-msvc" } \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 5fe17a05..f067ac6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -937,8 +937,6 @@ dependencies = [ [[package]] name = "esaxx-rs" version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d817e038c30374a4bcb22f94d0a8a0e216958d4c3dcde369b1439fec4bdda6e6" dependencies = [ "cc", ] diff --git a/encoderfile-py/Cargo.toml b/encoderfile-py/Cargo.toml index 36ae6462..e8f6d4be 100644 --- a/encoderfile-py/Cargo.toml +++ b/encoderfile-py/Cargo.toml @@ -12,7 +12,7 @@ crate-type = ["cdylib"] [dependencies.pyo3] version = "0.27.0" -features = ["abi3"] +features = ["abi3", "extension-module"] [dependencies.encoderfile] path = "../encoderfile" diff --git a/encoderfile/src/builder/base_binary/mod.rs b/encoderfile/src/builder/base_binary/mod.rs index 43c72483..317bc561 100644 --- a/encoderfile/src/builder/base_binary/mod.rs +++ b/encoderfile/src/builder/base_binary/mod.rs @@ -105,7 +105,6 @@ impl BaseBinaryResolver<'_> { fn validate_binary(&self, path: &Path) -> Result<()> { terminal::info("Validating binary..."); - use std::os::unix::fs::PermissionsExt; let meta = fs::metadata(path) .with_context(|| format!("base binary missing at {}", path.display()))?; @@ -114,9 +113,13 @@ impl BaseBinaryResolver<'_> { anyhow::bail!("base binary is not a file: {}", path.display()); } - let mode = meta.permissions().mode(); - if mode & 0o111 == 0 { - anyhow::bail!("base binary is not executable: {}", path.display()); + #[cfg(not(target_os = "windows"))] + { + use std::os::unix::fs::PermissionsExt; + let mode = meta.permissions().mode(); + if mode & 0o111 == 0 { + anyhow::bail!("base binary is not executable: {}", path.display()); + } } terminal::success("Binary validated"); diff --git a/encoderfile/tests/integration/test_build.rs b/encoderfile/tests/integration/test_build.rs index 60ba0149..195c4efa 100644 --- a/encoderfile/tests/integration/test_build.rs +++ b/encoderfile/tests/integration/test_build.rs @@ -104,6 +104,11 @@ async fn test_build_encoderfile() -> Result<()> { .status() .expect("Failed to build encoderfile-runtime"); + #[cfg(target_os = "windows")] + let base_binary_path = fs::canonicalize("../target/debug/encoderfile-runtime.exe") + .expect("Failed to canonicalize base binary path"); + + #[cfg(not(target_os = "windows"))] let base_binary_path = fs::canonicalize("../target/debug/encoderfile-runtime") .expect("Failed to canonicalize base binary path"); @@ -159,11 +164,14 @@ async fn test_build_encoderfile() -> Result<()> { grpc_port, )?; + println!("encoderfile spawned, waiting for it to become ready..."); + wait_for_http( format!("http://localhost:{http_port}/health").as_str(), Duration::from_secs(10), ) .await?; + println!("encoderfile is ready, sending inference requests..."); send_http_inference(&sample_text, http_port.to_string()).await?; send_grpc_inference(&sample_text, grpc_port.to_string()).await?; diff --git a/test_config.yml b/test_config.yml index d2b751aa..9b7de133 100644 --- a/test_config.yml +++ b/test_config.yml @@ -1,6 +1,7 @@ encoderfile: name: my-model-2 path: models/token_classification + base_binary_path: target/debug/encoderfile-runtime.exe model_type: token_classification output_path: ./test-model.encoderfile transform: | From 72593fcdcc6057176895e67bef93b6d1a10d674d Mon Sep 17 00:00:00 2001 From: "Javier M. Torres" Date: Tue, 9 Jun 2026 11:55:28 +0200 Subject: [PATCH 2/7] Tweak ipv6 url, minor win issue --- encoderfile/tests/integration/test_build.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/encoderfile/tests/integration/test_build.rs b/encoderfile/tests/integration/test_build.rs index 195c4efa..15f25abb 100644 --- a/encoderfile/tests/integration/test_build.rs +++ b/encoderfile/tests/integration/test_build.rs @@ -16,6 +16,10 @@ tonic::include_proto!("encoderfile.metadata"); use encoderfile::generated::token_classification; +#[cfg(target_os = "windows")] +const BINARY_NAME: &str = "test.encoderfile.exe"; + +#[cfg(not(target_os = "windows"))] const BINARY_NAME: &str = "test.encoderfile"; fn config(model_name: &String, model_path: &Path, output_path: &Path) -> String { @@ -173,7 +177,9 @@ async fn test_build_encoderfile() -> Result<()> { .await?; println!("encoderfile is ready, sending inference requests..."); send_http_inference(&sample_text, http_port.to_string()).await?; + println!("http inference request successful"); send_grpc_inference(&sample_text, grpc_port.to_string()).await?; + println!("grpc inference request successful"); child.kill()?; child.wait().ok(); @@ -220,7 +226,7 @@ async fn send_http_inference(sample_text: &str, http_port: String) -> Result<()> } async fn send_grpc_inference(sample_text: &str, grpc_port: String) -> Result<()> { - let mut client = token_classification::token_classification_inference_client::TokenClassificationInferenceClient::connect(format!("http://[::]:{grpc_port}/predict")).await?; + let mut client = token_classification::token_classification_inference_client::TokenClassificationInferenceClient::connect(format!("http://[::1]:{grpc_port}/predict")).await?; let req = token_classification::TokenClassificationRequest { inputs: vec![sample_text.to_owned()], metadata: std::collections::HashMap::new(), From 21ca30f7cafbde03ec67a9b20d78a6be8b84b457 Mon Sep 17 00:00:00 2001 From: "Javier M. Torres" Date: Tue, 9 Jun 2026 12:03:17 +0200 Subject: [PATCH 3/7] Switch to own copy of esaxx --- .cargo/config.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index a7b1e82b..efb151af 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -3,5 +3,4 @@ RUST_LOG = "info,tower_http=debug,ort=warn,encoderfile=debug" [patch.crates-io] -# esaxx-rs = { git = "https://github.com/thewh1teagle/esaxx-rs.git", branch = "feat/dynamic-msvc-link" } -esaxx-rs = { path = "../esaxx-rs-dyn-msvc" } \ No newline at end of file +esaxx-rs = { git = "https://github.com/mozilla-ai/esaxx-rs-dyn-msvc.git" } From cb5872f27f257849c50790af03bc416a99147501 Mon Sep 17 00:00:00 2001 From: "Javier M. Torres" Date: Tue, 9 Jun 2026 16:20:26 +0200 Subject: [PATCH 4/7] Update esaxx dependency --- Cargo.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.lock b/Cargo.lock index f067ac6b..bc40e13c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -937,6 +937,7 @@ dependencies = [ [[package]] name = "esaxx-rs" version = "0.1.10" +source = "git+https://github.com/mozilla-ai/esaxx-rs-dyn-msvc.git#2d72deeef87d0f1b3afd38599418cb8acc3aa19f" dependencies = [ "cc", ] From 44638f4376fb233c3fecf6ea68547a3658c2f197 Mon Sep 17 00:00:00 2001 From: Javier Torres Date: Tue, 9 Jun 2026 18:10:57 +0200 Subject: [PATCH 5/7] Fix lint --- encoderfile/src/builder/config.rs | 2 +- encoderfile/src/builder/tokenizer.rs | 6 +++--- encoderfile/src/transport/cli.rs | 4 ++-- encoderfile/src/transport/server.rs | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/encoderfile/src/builder/config.rs b/encoderfile/src/builder/config.rs index d57c3b68..0da0261c 100644 --- a/encoderfile/src/builder/config.rs +++ b/encoderfile/src/builder/config.rs @@ -244,7 +244,7 @@ impl Transform { match self { Self::Path { path } => { if !path.exists() { - bail!("No such file: {:?}", &path); + bail!("No such file: {:?}", path); } let mut code = String::new(); diff --git a/encoderfile/src/builder/tokenizer.rs b/encoderfile/src/builder/tokenizer.rs index cc472a7d..0652995f 100644 --- a/encoderfile/src/builder/tokenizer.rs +++ b/encoderfile/src/builder/tokenizer.rs @@ -117,7 +117,7 @@ fn from_tokenizer(tokenizer: &Tokenizer) -> Result { eprintln!( "WARNING: No padding params found in `tokenizer.json`. Using defaults: {:?}", - &padding_params + padding_params ); padding_params @@ -130,8 +130,8 @@ fn from_tokenizer(tokenizer: &Tokenizer) -> Result { let truncation_params = TruncationParams::default(); eprintln!( - "WARNING: No padding params found in `tokenizer.json`. Using defaults: {:?}", - &truncation_params, + "WARNING: No truncation params found in `tokenizer.json`. Using defaults: {:?}", + truncation_params ); truncation_params diff --git a/encoderfile/src/transport/cli.rs b/encoderfile/src/transport/cli.rs index 8a934fb4..13b0bc9c 100644 --- a/encoderfile/src/transport/cli.rs +++ b/encoderfile/src/transport/cli.rs @@ -175,9 +175,9 @@ impl Commands { let banner = crate::get_banner(state.model_id().as_str()); if disable_grpc && disable_http { - return Err(crate::error::ApiError::ConfigError( + Err(crate::error::ApiError::ConfigError( "Cannot disable both gRPC and HTTP", - ))?; + ))? } match enable_otel { diff --git a/encoderfile/src/transport/server.rs b/encoderfile/src/transport/server.rs index 6f6cda1f..8adf08eb 100644 --- a/encoderfile/src/transport/server.rs +++ b/encoderfile/src/transport/server.rs @@ -103,7 +103,7 @@ async fn serve_with_optional_tls( state: S, into_service_fn: impl Fn(&S) -> IntoMakeServiceWithConnectInfo, ) -> Result<()> { - let addr = format!("{}:{}", &hostname, &port); + let addr = format!("{}:{}", hostname, port); let router = into_service_fn(&state); From 107f4beeed86dd63fb4d0c6425e34c7bcfe7d590 Mon Sep 17 00:00:00 2001 From: Javier Torres Date: Tue, 9 Jun 2026 18:18:25 +0200 Subject: [PATCH 6/7] Fix lint --- encoderfile/tests/integration/test_build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/encoderfile/tests/integration/test_build.rs b/encoderfile/tests/integration/test_build.rs index 15f25abb..4fabd154 100644 --- a/encoderfile/tests/integration/test_build.rs +++ b/encoderfile/tests/integration/test_build.rs @@ -250,7 +250,7 @@ fn copy_dir_all(src: impl AsRef, dst: impl AsRef) -> anyhow::Result< let src = src.as_ref(); let dst = dst.as_ref(); - fs::create_dir_all(dst).context(format!("Failed to create directory {:?}", &dst))?; + fs::create_dir_all(dst).context(format!("Failed to create directory {:?}", dst))?; for entry in fs::read_dir(src)? { let entry = entry?; From 3e8cae40ba179d05882482843419cb19903ccdff Mon Sep 17 00:00:00 2001 From: Javier Torres Date: Tue, 9 Jun 2026 18:20:21 +0200 Subject: [PATCH 7/7] Fix lint --- encoderfile/tests/integration/test_inspect.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/encoderfile/tests/integration/test_inspect.rs b/encoderfile/tests/integration/test_inspect.rs index 806f0550..d3f249a6 100644 --- a/encoderfile/tests/integration/test_inspect.rs +++ b/encoderfile/tests/integration/test_inspect.rs @@ -134,7 +134,7 @@ fn copy_dir_all(src: impl AsRef, dst: impl AsRef) -> anyhow::Result< let src = src.as_ref(); let dst = dst.as_ref(); - fs::create_dir_all(dst).context(format!("Failed to create directory {:?}", &dst))?; + fs::create_dir_all(dst).context(format!("Failed to create directory {:?}", dst))?; for entry in fs::read_dir(src)? { let entry = entry?;