Skip to content

v0.19.2

Choose a tag to compare

@Goldziher Goldziher released this 24 May 15:24
· 5608 commits to main since this release
v0.19.2
582802a

Fixed

  • alef-backend-swift: honour the full set of serde_rename_all strategies (lowercase, UPPERCASE, PascalCase, kebab-case) in the unit-enum raw-value emitter and the tagged-Codable variant-tag emitter so generated Swift round-trips match serde's wire format. Both unit_enum_raw_value (src/backends/swift/gen_bindings/mod.rs) and the two serde_rename_all match arms inside emit_serde_tagged_codable only handled snake_case, camelCase, and SCREAMING_SNAKE_CASE. Hitting #[serde(rename_all = "lowercase")] (e.g. EmbeddingFormat::{Float,Base64}) fell through to _ => variant.name.clone() and emitted case float = "Float" — Swift then refused to decode "float" from the JSON wire (Cannot initialize EmbeddingFormat from invalid String value 'float'). Added explicit arms for lowercase (variant.name.to_lowercase()), UPPERCASE (to_uppercase()), and PascalCase/kebab-case (which were missing from the tagged-Codable emitter even though unit_enum_raw_value already handled them). Imported heck::ToKebabCase at module level so both call sites can reach it. Surfaced as testEmbedEncodingFormat failure on liter-llm swift e2e run 26363741106. (src/backends/swift/gen_bindings/mod.rs)

  • alef-e2e-codegen-{csharp,go,kotlin_android,swift,php,r,ruby,rust,zig,dart,python,typescript}: wire the test_backend arg-type dispatcher into each language's CallConfig::args walker, plus drive Plugin super-trait lifecycle methods (name/version/initialize/shutdown) from the IR's MethodDef.trait_source == bridge_cfg.super_trait filter instead of hardcoded literals. Phase 1 added the emit_test_backend dispatcher and Phase 2 filled in per-language stub emitters, but neither phase wired the dispatcher into the actual args walker, so generated tests emitted register_X() with zero args. Each language's render_test_function / build_args now matches arg.arg_type == "test_backend", resolves the matching TraitBridgeConfig, looks up the trait's MethodDef list via TraitBridgeConfig::resolve_methods, and dispatches to emit_test_backend — the returned setup_block prepends to the test's setup_lines and the arg_expr is used as the call argument. Separately, the C# and Go emitters previously hardcoded literal Name/Version/Initialize/Shutdown method stubs when super_trait.is_some(); both now filter methods for entries whose MethodDef::trait_source == bridge_cfg.super_trait.as_deref() so the super-trait method list is driven from the IR (and any rename of Plugin survives without a codegen edit). (src/e2e/codegen/{csharp,dart,go,java,kotlin_android,php,python,r,ruby,rust,swift,typescript,zig}/...rs, src/core/config/e2e.rs)

  • alef-backend-swift: emit a singleValueContainer-based custom Codable conformance for #[serde(untagged)] data-variant enums so JSON round-trips through serde's untagged wire shape (bare value), not Swift's auto-derived {"variant": payload} form. Untagged enums (EmbeddingInput, ModerationInput, UserContent, RerankDocument, StopSequence etc.) used to fall through to the auto-derived Codable path on Swift. Swift's compiler-synthesised init(from:) for enum data variants assumes externally-tagged shape — decoding "hello" (a bare string) against case single(field0: String) fails with typeMismatch(Dictionary<String, Any>, … Expected to decode Dictionary<String, Any> but found a string instead). Surfaced as 19 runtime XCTest failures across the swift e2e Embed/Moderate suites on liter-llm CI run 26363182895. The new emit_serde_untagged_codable helper in src/backends/swift/gen_bindings/mod.rs emits a manual init(from:) that walks the variants in declaration order and tries try? container.decode(T.self) for each payload type — first match wins, mirroring serde's untagged deserialiser; an encode(to:) companion writes the payload directly via singleValueContainer().encode(value). The dispatcher in emit_enum now branches: has_serde_tagemit_serde_tagged_codable, serde_untagged && single-field variantsemit_serde_untagged_codable, else the existing auto-derived path. Multi-field positional variants (zero in the current corpus) fall through and would need further work — gated via the .fields.len() == 1 check. (src/backends/swift/gen_bindings/mod.rs)

  • alef-backend-jni,backend-kotlin-android: remove special-casing of trait-bridge register shim name extraction and bridge class name. The JNI register shim unconditionally called jni_call_string_method(env, impl_obj, "name", ...) regardless of whether the bridge had a super_trait (which provides name() on the impl). For bridges without super_trait, the Kotlin side now passes name as an explicit JString parameter instead of asking the impl to provide it. Updated emit_trait_register_shim to accept has_super_trait: bool and conditionally emit either a single-arg signature (impl) for super_trait bridges or a two-arg signature (impl + name JString) for non-super_trait bridges. On the Kotlin side, gen_trait_bridge_object now accepts a bridge_class_name parameter (derived from config.name in the caller at gen_bindings.rs:emit_trait_interfaces) instead of hardcoding KreuzbergBridge, allowing non-kreuzberg consumers (e.g. liter-llm) to use the generated bridges with their own bridge class names. Updated all seven test cases in kotlin_android::trait_bridge::tests to pass "TestBridge" and verify bridge-class-name substitution in assertions. All calls to gen_trait_bridge_object in actual codegen (both gen_bindings.rs and test fixures) now pass the computed bridge class name. (src/backends/jni/gen_shims.rs, src/backends/kotlin_android/trait_bridge.rs, src/backends/kotlin_android/gen_bindings.rs)

  • alef-backend-kotlin-android,backend-swift: replace hardcoded kreuzberg:: rust_path prefix in trait-bridge test helpers with testcrate::. The make_trait_def helpers in src/backends/kotlin_android/trait_bridge.rs and src/backends/swift/gen_bindings/trait_bridge.rs used format!("kreuzberg::{}", name) for the rust_path of synthetic trait definitions, coupling tests to the kreuzberg domain. Changed to testcrate:: so test assertions remain domain-neutral and future consumers can run the same tests with their own crate names. (src/backends/kotlin_android/trait_bridge.rs, src/backends/swift/gen_bindings/trait_bridge.rs)

  • alef-backend-jni: emit jni 0.22-compatible JNI shims with RuntimeMethodSignature parsing, borrow semantics, and unsafe blocks. The jni_call_string_method helper emitted by src/backends/jni/gen_shims.rs had five misalignments with jni 0.22.4 API: (1) passed raw &str directly to get_method_id which requires AsRef<MethodSignature> (jni 0.22 removed the string-direct path); (2) used the wrong path jni::objects::ReturnType::Object (moved to jni::signature::ReturnType); (3) called JString::from_raw(ptr) with one arg, but jni 0.22 requires two: JString::from_raw(env, ptr) for lifetime binding; (4) moved obj by passing it to call_method_unchecked, but it was already moved by get_object_class(obj) — both expect references in jni 0.22; (5) called unsafe function call_method_unchecked without an unsafe block. Fix: (1) parse method_sig: &str to RuntimeMethodSignature via FromStr, then call .method_signature() to get the MethodSignature required by get_method_id; (2) use the correct jni::signature::ReturnType::Object path; (3) pass both env and result.into_raw() to JString::from_raw; (4) borrow obj with &obj in both get_object_class and call_method_unchecked calls; (5) wrap call_method_unchecked in unsafe block with SAFETY comment explaining the method_id validity. Blocks all Kotlin Android trait-bridge bindings (jni-rs is JNI layer, kreuzcrawl is the test bed). (src/backends/jni/gen_shims.rs)

  • alef-backend-swift: route tagged-data enums through JSONDecoder like untagged ones so generated Swift FFI inits compile. The struct-field init emitter in swift_ffi_read_expr (src/backends/swift/gen_bindings/mod.rs) only short-circuited Named enum fields onto the JSONDecoder path when the enum was #[serde(untagged)]. Tagged enums — #[serde(tag = "type")] (internally tagged, e.g. liter-llm OcrDocument) and the default external-tag form with data variants (e.g. liter-llm AuthHeaderFormat::ApiKey(String)) — bridge as JSON RustString at the swift-bridge boundary too, but the codegen fell through to try {Name}(rb.field()) which expects an init(_ rb: RustBridge.{Name}Ref) throws initializer that swift-bridge never emits for enum types. Swift reported missing argument label 'from:' and argument type 'RustString' does not conform to expected type 'Decoder'. Collapsed the two separate tagged_enum_names / untagged_enum_names sets into a single Codable-data-enum set (kept the untagged_enum_names legacy name so call sites at lines 1148/1151/1165/1186/1208/1213 keep working) by dropping the !e.serde_untagged filter — any Codable enum with at least one data variant now uses the JSONDecoder decode path. Also fixes the closure signature in the Vec map emitters: the RustVec<RustString> iterator yields RustStringRef, not RustString, so (s: RustString) -> {name} in becomes (s: RustStringRef) -> {name} in and s.toString() becomes s.as_str().toString() (mirroring the Vec<String> mapper above). Surfaced as 10 hard swift build errors in liter-llm's RerankRequest/OcrRequest/CustomProviderConfig/ModerationRequest inits after the v0.19.1 regen. (src/backends/swift/gen_bindings/mod.rs)

  • alef-backend-csharp,backend-zig: expose type_map as pub(crate) so the e2e codegen helpers added in ### Added (Phase 2 emit_test_backend impls) can use crate::backends::{csharp,zig}::type_map::… without E0603: module is private. The Go backend already exposed its type_map as pub for the same reason; C# and Zig were the remaining holdouts. No external API change — pub(crate) keeps the symbols invisible outside the crate. (src/backends/csharp/mod.rs, src/backends/zig/mod.rs)

  • alef-backend-kotlin-android: scope the ToSnakeCase heck import to the test module so non-test builds (cargo build --release) do not emit unused_imports warnings. Production code in trait_bridge.rs only uses ToUpperCamelCase; the tests use to_snake_case for synthetic TraitBridgeConfig fixtures. Moved use heck::ToSnakeCase; into mod tests to make cargo clippy -D warnings and cargo build --release clean. (src/backends/kotlin_android/trait_bridge.rs)

Added

  • alef-backend-{swift,kotlin-android}: extract canonical bridge-name helpers (swift::naming::bridge_protocol_name, kotlin_android::naming::bridge_object_name) so production wrapper codegen and e2e stub emitter share one source of truth. Both format!("Swift{trait}Bridge", ...) (swift) and format!("{trait}Bridge", ...) (kotlin) were duplicated at the production site and the e2e emitter site, risking silent drift if either ever renames. Both sites now call the new helpers. (src/backends/swift/naming.rs, src/backends/kotlin_android/naming.rs, src/backends/swift/gen_bindings/trait_bridge.rs, src/e2e/codegen/swift.rs, src/e2e/codegen/kotlin_android.rs)

  • alef-scaffold: emit a root-level .gitattributes marking all generated output directories as linguist-generated=true. Running alef scaffold now produces a .gitattributes (create-once seed; not overwritten on re-scaffold unless --clean is passed) that collapses generated files in GitHub PR diffs. Coverage: packages/{lang}/** for language-native packages; crates/{name}-py/**, crates/{name}-php/**, crates/{name}-ffi/**, crates/{name}-jni/** for Rust binding crates that sit alongside their package directories; crates/{name}-node/** (resolves crate_dir override, bypasses scaffold_output); packages/kotlin-native/** or packages/kotlin-mpp/** for non-JVM Kotlin targets; and the configured [e2e] output directory (default e2e/). (src/scaffold/mod.rs)