Skip to content

fix(ffi): constructors return JSON handle/error envelope, replacing aimux_last_error (issue #17) - #21

Merged
eric8810 merged 7 commits into
arcships:masterfrom
cunninghamcard-bit:explore/aimux-ffi-json-constructors
Aug 3, 2026
Merged

fix(ffi): constructors return JSON handle/error envelope, replacing aimux_last_error (issue #17)#21
eric8810 merged 7 commits into
arcships:masterfrom
cunninghamcard-bit:explore/aimux-ffi-json-constructors

Conversation

@cunninghamcard-bit

@cunninghamcard-bit cunninghamcard-bit commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Follow-up simplification of #19: constructors return the JSON envelope the rest of the ABI already uses, instead of a bare u64 plus the aimux_last_error() side channel:

{"handle":<u64>}                                     on success
{"error":"...","error_type":"...","status_code":...} on failure

Success and failure arrive in the same return value of the same call, so there is no second call to correlate — the entire last_error mechanism (TLS slot, begin_constructor/record_error, the aimux_last_error symbol and its thread-affinity contract) is removed. No binding needs thread pinning (LockOSThread gone in Go; nothing for Kotlin coroutines / virtual threads to get wrong when they adopt it). The convention matches aimux_generate_text / aimux_embed, so bindings reuse their existing envelope parsers.

Net −168 lines vs master (+1426/−1594) while extending coverage from 3 bindings to all 6.

Error envelopes are byte-identical to #19's (InvalidArgument/Json/UnknownProvider, serde detail for prompt_json/config_json) — the only change is the retrieval mechanism.

Cost: breaking C ABI change — 40+ constructor signatures change from uint64_t to char * (freed with aimux_free_string). 0.2.x is the cheapest moment this will ever be.

Verification

  • cargo test -p aimux-ffi --release: 14 passed — fix(ffi): preserve detailed errors across the C ABI (issue #17) #19's error_detail regression cases kept (reading the error from the constructor result); the TLS-semantics cases have no counterpart and are replaced with envelope-semantics tests (per-call error independence, 8-thread concurrent constructors, no pinning)
  • Go: go vet + go test ./... with a real cgo link; C/C++ examples compile; swift build passes
  • E2E against a live OpenAI-compatible endpoint through the Go and C bindings: unknown-provider detail (full provider list), invalid prompt_json serde detail, real generate, real streaming — all pass
  • Java/Kotlin/Flutter: static-only changes following existing patterns; CI to verify

🤖 Generated with Claude Code

@cunninghamcard-bit
cunninghamcard-bit force-pushed the explore/aimux-ffi-json-constructors branch from 85bde11 to cc26ba6 Compare August 3, 2026 12:07
@eric8810

eric8810 commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

is this in draft or complete?

@cunninghamcard-bit

Copy link
Copy Markdown
Contributor Author

是完整的,不是 draft——6 个绑定(Go/Java/Kotlin/Swift/Flutter/C)全部迁移完,#19 的 error_detail 回归测试也移植过来了(14 个通过,信封内容对齐:InvalidArgument/Json/UnknownProvider)。本地验证:Rust 测试、Go 真实 cgo 链接、C/C++ example 编译、swift build 都通过,另外用真实的 OpenAI 兼容 endpoint 走 Go 和 C 绑定跑了端到端(未知 provider 详情、invalid prompt_json 的 serde 详情、真实生成、流式)。Java/Kotlin/Flutter 是纯静态改动,以 CI 为准。

不过 #19 合入后这个 PR 现在有冲突,我 rebase 一下。rebase 之后这个 PR 的性质就变成:在 #19 的基础上进一步简化——删掉 aimux_last_error() 的线程局部机制,构造函数直接返回 JSON 信封,成功和失败在同一次调用的返回值里,没有第二次调用需要关联,各绑定也就不需要处理线程亲和(Go 不需要 LockOSThread,Kotlin 协程/虚拟线程接入时也没有坑)。#19 的测试会改写成信封版保留。rebase 完我在这里说一声。

@cunninghamcard-bit
cunninghamcard-bit force-pushed the explore/aimux-ffi-json-constructors branch from cc26ba6 to 5bb2a5a Compare August 3, 2026 12:18
@cunninghamcard-bit cunninghamcard-bit changed the title fix(ffi): constructors return JSON handle/error envelope (issue #17, alternative to #19) fix(ffi): constructors return JSON handle/error envelope, replacing aimux_last_error (issue #17) Aug 3, 2026
@cunninghamcard-bit

Copy link
Copy Markdown
Contributor Author

rebase 完成。现在这个 PR 基于合入 #19 之后的 master,内容就是把 last_error 侧通道整套换成信封返回:相对 master 净删 168 行(+1426/−1594),同时把绑定覆盖从 3 个扩到 6 个。错误信封内容和 #19 完全一致,测试 14 个全过(#19 的回归用例保留,TLS 语义的几个换成了信封语义的等价测试)。rebase 后重新跑了 Rust 测试、Go 真实链接测试、C/C++ 编译、swift build,以及 Go/C 两个绑定的真实 endpoint 端到端,全部通过。

@eric8810

eric8810 commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

ci failed

cunninghamcard-bit and others added 7 commits August 3, 2026 20:22
…imux_last_error (issue arcships#17)

Follow-up simplification of arcships#19: instead of a bare u64 handle plus the
aimux_last_error() thread-local side channel, every constructor returns
the JSON envelope the rest of the ABI already uses:

    {"handle":<u64>}                                    on success
    {"error":"...","error_type":"...","status_code":..} on failure

Success and failure arrive in the same return value of the same call, so
there is no second call to correlate — the entire last_error mechanism
(LAST_ERROR TLS slot, begin_constructor, record_error/record_error_msg,
the aimux_last_error symbol and its thread-affinity contract) is removed.
No binding runtime needs thread pinning.

Error envelopes are unchanged from arcships#19 (InvalidArgument for null /
invalid-UTF-8 args, Json for config parse failures, UnknownProvider with
the available-provider list), and Problem A's serde detail for
prompt_json/config_json is preserved.

error_detail_test.rs: the envelope-relevant cases from arcships#19 are kept and
read the error from the constructor result; the TLS-semantics cases
(clear-on-success, last-write-wins, read-once, thread-local isolation)
have no counterpart — replaced with envelope-semantics tests (per-call
error independence, 8-thread concurrent constructors, no pinning).

cargo test -p aimux-ffi --release: 14 passed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
parseHandleJSON reads the envelope, frees the C string, and surfaces the
detailed error; a degenerate envelope with neither error nor handle is
rejected. The takeLastError/callConstructor dispatch machinery and
runtime.LockOSThread pinning from arcships#19 are removed — constructors are
plain direct C calls, since the result is self-contained.

Also removes stale StreamTextContext doc references (no such API); the
per-call timeout (RFC-0016 H3) is the only interruption mechanism.

go vet + go test ./... pass with a real cgo link; E2E against a live
OpenAI-compatible endpoint verifies unknown-provider detail, prompt_json
serde detail, real generate, and real streaming.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
All JNA constructor declarations return Pointer; AimuxResult.extractHandle
frees exactly once (try/finally), null-checks, and throws with the
detailed engine error. The aimux_last_error declaration and
constructorFailure helper from arcships#19 are removed. Static-only change; CI
to verify.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Same shape as the Java binding: Pointer? externs, extractHandle with
free-in-finally, detailed errors surfaced. Static-only change; CI to
verify.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
extractHandle copies the string, frees once, then throws AimuxError with
the detailed message on the error envelope. swift build passes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Constructor typedefs return Pointer<Utf8>; _extractHandle frees once and
throws StateError with the detailed message. Static-only change; CI to
verify (no dart SDK in the authoring environment).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
example.c / example.cpp parse the envelope and print the detailed engine
error on constructor failure (replacing arcships#19's aimux_last_error read in
example.c); example.cpp includes <stdexcept> for its throw sites. Both
compile against the new header.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@cunninghamcard-bit
cunninghamcard-bit force-pushed the explore/aimux-ffi-json-constructors branch from 5bb2a5a to f747784 Compare August 3, 2026 12:22
@cunninghamcard-bit

Copy link
Copy Markdown
Contributor Author

CI 绿了——之前挂的是新增测试文件的 rustfmt 格式问题,已修(折进了原 commit,历史不变)。现在全部 job 通过,包括我本地没法验证的 Java(JDK 8/11/17/21)。

@eric8810
eric8810 merged commit 5b352e1 into arcships:master Aug 3, 2026
18 checks passed
eric8810 added a commit that referenced this pull request Aug 3, 2026
Resolve conflicts with #19/#21 (FFI constructor JSON-envelope rework): adopt master's reworked FFI/bindings and re-apply init_logging entries (aimux-ffi C ABI + Go InitLogging). Auto-merged bindings retain both sides.
cunninghamcard-bit added a commit to cunninghamcard-bit/aimux that referenced this pull request Aug 4, 2026
… generator paths

- aimux-ffi.h / AimuxFFI.java: remove aimux_last_error declaration and JNA
  binding — the symbol was removed in arcships#21 (constructors now return JSON
  envelopes); the header declared a function that no longer exists, and
  calling the JNA binding would fail at lookup.
- scripts/gen_provider_names.py: Java/Kotlin outputs still targeted the old
  io.aimux / aimux packages; the checked-in files moved to ai.arcships.aimux
  for the Maven Central release. Running the generator would have recreated
  stale duplicate classes. Also emit gofmt-aligned Go consts so generator
  output matches the checked-in (gofmt-ed) file byte-for-byte.
- ci: add a generator-drift step (run generator, git diff --exit-code) so
  checked-in ProviderName files can't silently diverge again.
- docs/internal/phase4-audit.md: add a resolution-status section — P1-1/P1-2/
  P2-3/P2-5 and the ffi-has-no-tests finding are resolved; P2-4/P2-6 remain.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
cunninghamcard-bit added a commit to cunninghamcard-bit/aimux that referenced this pull request Aug 4, 2026
…ecls, stale header contracts

arcships#21 replaced the thread-local last_error side channel with JSON handle/error
envelopes; this cleans up everything it left behind:

- aimux-ffi.h / AimuxFFI.java: remove the aimux_last_error declaration and
  JNA binding — the symbol no longer exists (undefined symbol for C callers,
  crash on first call via JNA).
- aimux-ffi.h: align documented contracts with the post-arcships#21 implementation
  (verified against lib.rs line by line): three-key error envelope
  everywhere; Azure _with_base takes a REQUIRED base_url in place of
  resource_name; multimodal opts_json is required while generate/stream
  accept NULL; detailed invalid prompt_json/opts_json envelopes; _with_base
  NULL-means-default notes; embedding-onward envelope convention note;
  ignored opts_json/model_id params flagged; NULL-return and timeout
  semantics.
- lib.rs: module/free_string docs now cover all char* returns, not just
  aimux_generate_text.
- C/C++ examples: build commands gain the missing -I../../aimux-ffi.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
cunninghamcard-bit added a commit to cunninghamcard-bit/aimux that referenced this pull request Aug 4, 2026
…ecls, stale header contracts

arcships#21 replaced the thread-local last_error side channel with JSON handle/error
envelopes; this cleans up everything it left behind:

- aimux-ffi.h / AimuxFFI.java: remove the aimux_last_error declaration and
  JNA binding — the symbol no longer exists (undefined symbol for C callers,
  crash on first call via JNA).
- aimux-ffi.h: align documented contracts with the post-arcships#21 implementation
  (verified against lib.rs line by line): three-key error envelope
  everywhere; Azure _with_base takes a REQUIRED base_url in place of
  resource_name; multimodal opts_json is required while generate/stream
  accept NULL; detailed invalid prompt_json/opts_json envelopes; _with_base
  NULL-means-default notes; embedding-onward envelope convention note;
  ignored opts_json/model_id params flagged; NULL-return and timeout
  semantics.
- lib.rs: module/free_string docs now cover all char* returns, not just
  aimux_generate_text.
- C/C++ examples: build commands gain the missing -I../../aimux-ffi.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants