Skip to content

v0.17.15

Choose a tag to compare

@Goldziher Goldziher released this 21 May 09:17
· 5404 commits to main since this release
v0.17.15
d53087a

[0.17.15] - 2026-05-21

Fixed

  • alef-e2e/swift: guard String.contains("") assertions with an isEmpty short-circuit. Swift's String.contains(_:) returns false when the argument is the empty string, unlike every other language we target ("foo".contains("") is true in Python, JS, Ruby, etc.). Fixtures asserting contains: "" on a response field (e.g. transcribe returning empty text for silent audio) therefore always failed with XCTAssertTrue failed - expected to contain:. The swift e2e codegen now emits XCTAssertTrue({val}.isEmpty || {expr}.contains({val}), …) for the non-array, non-traversal contains cases, preserving the original semantic ("any string trivially contains the empty string") while leaving non-empty containment checks unchanged. Surfaced on liter-llm Swift e2e — TranscribeTests.testEdgeTranscribeEmptyAudio. (crates/alef-e2e/src/codegen/swift.rs)

  • alef-backend-swift: fall back to a Value::String wrapper when from_str fails for string-like fields. The default_field_string_like_serde.jinja / default_field_string_like_optional_serde.jinja templates wrapped raw String constructor arguments in serde_json::from_str(&v).ok()from_value(v).ok(). For inputs that are NOT valid JSON tokens — bare values like tts-1, whisper-1, dall-e-3from_str silently returns Err, the if let Ok branch is skipped, and the target field stays at the struct's Default value (empty String). The high-level Swift intoRust() therefore round-tripped fixtures through a swift-bridge constructor that silently zeroed the model field, causing the Rust client to reject the call with bad request: model must not be empty. Both templates now first try from_str; on parse failure they fall back to serde_json::Value::String(v) and then from_value, so raw strings deserialize correctly into plain String (and string-like enum) fields while structured JSON (objects / arrays / string-like enums encoded as JSON strings) continues to work. Surfaced on liter-llm Swift e2e — 13 failures across SpeechTests, TranscribeTests, ImageGenerateTests where the test request JSON serialized model as a bare string. (crates/alef-backend-swift/templates/default_field_string_like_serde.jinja, crates/alef-backend-swift/templates/default_field_string_like_optional_serde.jinja)

  • alef-backend-csharp: emit separate JsonSerializationOptions for FFI parameter serialization. The C# generator used a single JsonSerializerOptions with DefaultIgnoreCondition.WhenWritingDefault for both deserializing FFI responses and serializing input parameters (like ConversionOptions) to pass to Rust. When a test explicitly set an option to false/0/null, the serializer skipped writing that field — Rust received incomplete JSON and applied defaults, overwriting the caller's intent. Surfaced on html-to-markdown C# e2e: 3 tests failed because options like preprocessing.enabled=false were omitted from serialization. Added a second JsonSerializationOptions (without WhenWritingDefault) used when serializing Named parameters and config objects in wrappers and streaming methods; deserialization continues to use the original JsonOptions for sparse response handling. (crates/alef-backend-csharp/src/gen_bindings/types.rs, methods.rs, and templates)

  • alef-backend-java: change ObjectMapper serialization inclusion from NON_NULL to ALWAYS. When Java code called Rust via FFI, the ObjectMapper's JsonInclude.Include.NON_NULL configuration silently omitted all null-valued fields from the JSON sent to htm_conversion_options_from_json. The Rust deserializer then saw an incomplete JSON object and applied defaults for all missing fields, overwriting any explicitly-set false/0/null values from Java. Effectively, any test setting an option to its "falsy" default (e.g., highlightStyle=null, includeDocumentStructure=false) would be ignored because those fields disappeared from the JSON. Changed to JsonInclude.Include.ALWAYS so all fields serialize regardless of value, ensuring the Rust FFI layer sees exactly what the Java caller set. Surfaced on html-to-markdown Java e2e tests — 33 tests failed because option setters like withHighlightStyle() were being overwritten by Rust defaults; 7 additional tests failed with NullPointerException on result.document() because includeDocumentStructure=false was omitted. (crates/alef-backend-java/templates/helper_object_mapper.jinja)

  • alef-e2e/csharp: append Async suffix to non-overridden async method names. render_test_method / render_chat_stream_test_method derived the C# call site from call_config.function.to_upper_camel_case() without appending the conventional Async suffix that the C# generator emits on async wrappers. When function: "chat" and async: true were declared in alef.toml (with no explicit csharp.function override), the test invoked client.Chat(...) but the C# binding only exposed client.ChatAsync(...). Now the suffix is appended automatically when call_config.r#async is true and the name doesn't already end in Async; explicit cs_overrides.function overrides still win. (crates/alef-e2e/src/codegen/csharp.rs)

  • alef-e2e/zig: implement the equals assertion path for string results. The Zig assertion renderer matched on result_is_simple strings (null, true, false, …) but had no arm for equals, so fixtures asserting an exact string value silently emitted no try testing.expect… line — the generated test compiled and passed without ever checking the result. Added an equals arm that lowers the JSON value via json_to_zig and emits try testing.expectEqualStrings(expected, result.?). (crates/alef-e2e/src/codegen/zig.rs)

  • alef-e2e/swift: prevent UInt() wrapping of floating-point numeric literals in comparisons. The swift_numeric_literal_cast function unconditionally wrapped all numeric method-call accessor comparisons (e.g., result.relevanceScore()) in UInt(...), assuming all opaque swift-bridge methods return UInt. However, some accessors return Double (e.g., relevanceScore() -> Double), causing compilation errors when comparing against float literals like 0.9. Fixed by detecting float-valued literals (containing .) and skipping the UInt() wrapper; integer literals continue to be wrapped to match UInt-returning accessors. Also applied the numeric-literal-cast function consistently across all numeric comparison assertions (equals, greater_than, less_than, etc.) instead of only some, and refactored the optional-field coalesce paths to use the cast function. Surfaced on liter-llm Swift e2e — RerankTests and similar fixtures with float field assertions. (crates/alef-e2e/src/codegen/swift.rs)

  • alef-e2e: treat inline href="/…" anchors in fixture HTML as a has_host_root_route trigger. Fixture::has_host_root_route() previously only fired for /robots* / /sitemap* paths or 3xx Location / Refresh / <meta refresh> redirects targeting /…. Multi-page crawl fixtures that traversed between mock responses via plain <a href="/page1"> links did not qualify, so language-specific e2e codegens (Python, Node, Ruby, …) emitted the shared MOCK_SERVER_URL + /fixtures/<id> URL rather than the per-fixture MOCK_SERVER_<FIXTURE_ID> env-var lookup. The crawl engine then resolved linked /page paths against the host root and 404'd against the namespaced shared listener. Detecting href="/ / href='/ in any body_inline mirrors the runtime mock-server's has_inline_host_link predicate (60deac3) so compile-time and runtime decisions stay in sync. Surfaced on kreuzcrawl Python e2e — test_crawl_concurrent_depth got 1 page instead of 3. (crates/alef-e2e/src/fixture.rs)

  • alef-e2e/php: honor options_via = "from_json" when constructing visitor options. When a fixture used a visitor callback and the PHP e2e call override specified options_via = "from_json", the test code generator unconditionally emitted ConversionOptions::builder()->visitor($visitor)->build(), which does not exist. The correct behavior is to honor options_via and use ConversionOptions::from_json(json_encode(['visitor' => $visitor])) instead. This caused 54 php e2e test failures in html-to-markdown (ConversionTests, OptionsTests, VisitorTests) with "Call to undefined method ConversionOptions::builder()". (crates/alef-e2e/src/codegen/php.rs)

Changed

  • maintenance: record a clean full-project prek run --all-files verification.