Skip to content

Tracking: code reduction roadmap (providers, FFI, bindings, docs) #166

Description

@cunninghamcard-bit

Baseline: master @ 5e78b54 (PR #164 branch @ 03880e0). Every item below went through an adversarial verification pass: line counts were re-measured, dependents were grepped, and anything that would be lost is written next to the item. Items marked revised are directions that hold, but whose original proposal missed a dependency or a behaviour; the revised form is what is written here.

Each item is its own PR against master, with no overlap with #164. We will go through them one at a time in this issue; tick the box and link the PR when it lands.

Ground rules

  • Cassettes are not deleted. The 2,799 recordings are the byte-level replay gate for every PR here, and later PRs lean on them harder, not less.
  • Docs are reorganized, not deleted. Regroup by audience, archive history, keep only what users read on the public surface.
  • One direction, one PR. Providers first, then bindings; the highest-confidence deletions land first.

Ledger

Net code lines only. Cassettes and doc moves are not counted. "Build" is what has to be written in order to delete, and is already subtracted from the net.

Track Scope Delete Build PRs Status
A · Mechanical dead scripts, unused deps, generated enum, type copies, example scaffolding, inventory data −11,100 +10 5 verified
B · Providers 33 wrappers, chat forks, responses family, vertex copies −10,000 to −12,000 +2,400 8 6 of the original proposals revised
C · FFI 109 exports collapsed into 9 JSON exports −7,000 +2,200 4 verified; 4 "delete export" items became "turn into op"
D · Bindings per-provider constructors, error decoders, stream pumps in 7 languages −22,300 +4,200 7 (+1 generation) verified; Kotlin revised, shared binding core shrunk
E · PR #164 internal duplicated preludes, abort written six times, spec doc −1,500 0 1 code read
Total −52,000 to −54,000 +8,800 25 net ≈ −44,000 lines of code, plus 42,599 lines of inventory data moved to archive/

A good share of the C and D "delete" column is rewrite; the residual is counted under "Build".

A · Mechanical cleanup

Five independent PRs. None touches provider logic. Can start today.

  • A1 — dead scripts and deps (−1,644, verified). Delete 15 dead scripts: fix_profiles{,2..5}.rs, generate_all_providers.py, update_lib_rs.py, list_wrong_providers.py, revert_wrong_inventory.py, update_inventory_status.py, gen_provider_registry.py, gen_vertex_maas_providers.py, generate_thin_wrapper_cassettes.py, migrate_openai_compatible_*.py. Delete the scripts/fix_tool workspace member (hello-world). Delete the unused root Cargo.toml deps schemars, proc-macro2, syn, quote. Note: gen_provider_registry.py reads openai_compat_registry.rs, which was deleted in the same commit that created it, so it never ran. The hints at provider.rs:4 and provider.rs:88 must change to "edit the JSON directly".
  • A2 — generated ProviderName enum (−3,483, revised). Delete the generated enum and its language copies; the registry is keyed by string. aimux-web needs a 4-line provider_names() iterator. The generator's output table misses a 9th static copy, tools/aimux-web/web/src/types/ProviderName.ts (262 lines); delete it too. gen_provider_names.py --check is the first step of the contract-tests job in ci.yml; remove those two lines with the script. Node autocomplete can come from a string-literal union emitted by gen_ts_types.py (~260 lines), no gate needed.
  • A3 — archive provider-inventory/ (42,599 lines archived, verified). Move the 4 files to archive/; mark rfc/0004 historical. No code reads it; provider_registry.json is the declared single source of truth.
  • A4 — aimux-web type copies (−3,018, revised). Delete the 140 hand-copied ts-rs type files in tools/aimux-web/web/src/types/ (138 are byte-identical with bindings/node/src/types; HttpRecord.ts and JsonValue.ts have already drifted). Point tsconfig paths at the generated set, or add the directory to the sync targets of gen_ts_types.py. Keep the 7 web-only Wire*.ts. All 16 references are import type, no runtime behaviour.
  • A5 — Flutter example desktop scaffolding (−2,988, revised). Delete the macOS, Windows and Linux flutter create scaffolding under bindings/flutter/example/ (the macOS half of the two pbxproj files, MainMenu.xib, win32_window.cpp, CMakeLists). The original proposal deleted all five platforms and was rejected: the flutter-example-build CI job runs nm -g on the iOS simulator build looking for aimux_openai_new, which is the regression gate for xcframework force-linking (iOS: CocoaPods fails to link vendored aimux_ffi.xcframework ('Framework aimux_ffi not found') #25 / fix(flutter): stage xcframework slice via script_phase — iOS CocoaPods link (issue #25) #26). iOS and Android stay.

Rejected after verification: per-modality cargo features (media / audio / rerank / search) delete nothing, only add a CI axis, and the search one breaks bindings/node, which is its own workspace with default features. Deleting the anya2a model catalogue in catalogue.rs is a product decision, not cleanup; all four bindings expose it.

B · Providers → protocol registry

Backbone: rfc/0032-provider-protocol-registry.md. It folds the 17 LanguageModel implementations into 7 protocols, adds protocol / auth / models[] to registry rows, retires 33 wrappers, and merges the responses family.

Corrections to write back into RFC-0032

  1. Step 5 depends on step 1; they are not parallel. Today provider_handle() only ever returns the chat-completions OpenAIProvider, Provider::name() hardcodes "openai", and the responses provider_options key hardcodes "openai". Merging open_responses / HF / xai responses steps on all three, so the RFC's claim that "the first four steps have no dependency inversion" does not hold for step 5.
  2. The responses request builder does not apply body_overrides. Only the chat builder does (openai/convert.rs:1500). Codex's store: false cannot ride in on a flag until that gap is filled.
  3. xai chat and mistral chat are not pure forks. xai: flat error body {"error":"msg"} on HTTP 200, zero-valued usage when absent, non-inclusive cache tokens. mistral: content arrays, tool_choice: any, model_length finish reason. These become new profile hooks, and openai's ChatCompletionResponse fields have no serde(default), so switching paths naively fails to parse. Merged savings are 500 to 1,700 lines depending on whether convert.rs folds in, not the fixed 650 the RFC estimates.
  4. Collapsing vertex_ai_* into one row loses eu/us regional host derivation. Registry placeholders only support static substitution like {project}; {location} needs three host shapes derived from global / eu / us / other. bedrock_mantle loses AWS_REGION the same way.
  5. Profiles must deserialize from JSON. OpenAICompatProfile.stream_usage_key: Option<&'static str> cannot live in a registry row. Switch to an owned-string serde struct first (pi-ai's compat shape), then fold xai / mistral differences in; otherwise every new flag needs a mapping table in provider.rs.
  6. Deleting mistral/model.rs and xai/model.rs breaks the workspace. aimux-ffi/src/lib.rs (1190–1242), bindings/node/src/lib.rs (1127, 1165), bindings/python/src/lib.rs (497, 518), tools/aimux-web/src/model_builder.rs and tools/aimux-cli/src/probe/provider.rs call MistralProvider::model() / XAIProvider::model() directly. So the C-track forwarding shim (C2) and those five call sites switching to provider() must land before B4 / B5; RFC step 4 ("bindings last") cannot be last.
  7. A registry row is not 7 lines. The existing 251 rows are flat 5-key objects; with protocol, nested auth{kind,env} and params{} a row is ~11 lines, so 77 new entries cost ~850 lines. The responses family's 4,557 lines share only 95 to 190 lines with the target implementation; the parameterized rewrite is ~1,000 lines, not 600.

PRs

  • B1 — protocol enum and from_resolved (+350, pure addition, RFC-0032 §3). Protocol enum (6–7 arms), AuthKind, params, one XxxConfig::from_resolved per protocol; provider("anthropic", …) works; ProviderRecord gains protocol so replay rebuilds by protocol. All cassettes green. In the same PR make Provider::name() return the configured name (1 line), otherwise after B2 openrouter reports itself as "openai".
  • B2 — auth kinds and 33 wrappers → rows (−4,300 / +850, revised). auth.kind = none | bearer_env; OpenAIConfig.api_key may be empty and then no Authorization header is sent (separate commit with a test). 33 wrappers become registry rows: delete the files, delegate_list_models!, and thin_wrapper_config_test.rs. Loses the "env var holds a URL" convention (OLLAMA_BASE_URL etc.); add a base_url_env field to get it back. Vertex {location} host derivation must land first, and params expansion must run before base_url_has_placeholder() rejects {. The replay.rs allowlist only drops 20 names; keep "openrouter". 44 pub types disappear: Rust API break.
  • B3 — serde compat flags plus CI grep (+200, both reviewers required it). Compat flags become a serde struct. Add a CI grep: no provider-name literals inside openai/, anthropic/, google/, bedrock/, cohere/ (today openai/ already has 14 "groq"). pi-ai has no such rule and drifted to 25 flags plus 31 provider === branches and 19 baseUrl sniffs.
  • B4 — mistral chat over openai::model (−530 to −1,160 / +150, revised). mistral chat uses openai's execute_* with 4 profile hooks; delete mistral/{model,convert,types}.rs. Prerequisite: C2 shim landed and the ffi / node / python / aimux-web / aimux-cli call sites switched to provider(). "Keep convert.rs" does not hold: execute_* calls openai's build_request_body internally, so mistral's builder becomes dead code. Either fold content arrays and tool_choice: any into openai as flags, or delete only model.rs. 76 tests see reasoning ids change from rc-{nanos} to reasoning-0. 86 cassettes byte-compared.
  • B5 — xai chat over openai::model (−490 to −1,700 / +250, revised). Same shape: response_handlers, citations → Source, search_parameters, error body inside HTTP 200, optional fields get serde(default). Same prerequisite as B4. xai/responses/convert.rs imports two helpers from xai::convert that openai lacks (remove_additional_properties_false, supports_reasoning_effort, 21 lines); move them into openai/ first, otherwise B5 must wait for B6's xai step. 86 tests pin text-{chunk_id} / xai-source-N ids and zero-valued usage and will all change; 62 cassettes byte-compared.
  • B6 — responses family (−3,500 / +1,000, revised). Fix three prerequisites first: build_headers ignores config.headers, provider_options_name() must read config, the builder must apply body_overrides. Then in order: azure shell (non-breaking, −203) → open_responses.rs becomes a registry row (−1,396) → HF (−1,264) → codex subscription loop (−259) → xai responses: delete only the stream loop, keep the provider-tool adapter (−400). HF is not a subset of open_responses: {"huggingface":{"itemId"}} metadata, response.createdResponseMetadata, mcp_call items, and base64 media-type sniffing must all move into the shared implementation, otherwise 22 behaviours are lost. xai's reasoning_text.delta and response.done need two arms in the shared reducer. 360 tests redirected.
  • B7 — vertex over shared cores (−870 / +250, revised). anthropic_model.rs goes through anthropic_*_core (as anthropic_aws already does), with a failed_response_handler parameter added to the core; vertex/model.rs goes through google's execute_* seam. google's core hardcodes the "google" metadata namespace in 8 places and vertex uses "googleVertex": parameterize. After an in-stream {type:error} vertex currently does not emit Finish; the core does. Behaviour change.
  • B8 — small duplicates (−170, revised). 6 identical build_header_list copies (the one in openai/image.rs deliberately omits Content-Type; keep it), AWS credential loading dedup, 3 copies of the "JSON error inside a 2xx" guard lifted into provider-utils. Provider name in error text becomes a parameter.
  • B9 (optional) — single-modality provider shells (−940 / +150, revised). 30 image / speech / transcription / search providers each repeat XxxConfig { api_key, base_url } plus an XxxProvider shell, 1,811 lines. "The registry can already express this" was rejected: searxng has no key, only SEARXNG_URL, and today's resolve_key errors on an empty env_var. Wait for B2's auth: none and base_url_env, and expect to recover about half.

C · FFI

Of three designs, review chose "design from the C ABI inward": one spec_json constructs any model, one call(op) and one stream(op) carry every operation, errors are a JSON string. Today 109 exports (115 on the branch); target 9. Old and new exports coexist for one minor version so the 7 binding migrations do not have to share a release.

typedef void (*aimux_event_cb)(const char* event_json, void* ctx); /* NULL = end of stream */

char*    aimux_model_new(const char* spec_json, uint64_t* out_handle);
char*    aimux_session_new(uint64_t model, uint64_t abort, const char* opts_json, uint64_t* out);
char*    aimux_call(uint64_t handle, const char* op, const char* request_json, char** out_json);
char*    aimux_stream(uint64_t handle, uint64_t abort, const char* op, const char* request_json,
                      aimux_event_cb on_event, void* ctx);
char*    aimux_session_push(uint64_t session, const uint8_t* data, size_t len);
uint64_t aimux_abort_new(void);
void     aimux_abort(uint64_t abort);
void     aimux_drop(uint64_t handle);
void     aimux_free_string(char* s);

/* spec_json: {"provider":"anthropic","model":"claude-sonnet-4-5","kind":"language",
               "api_key"?,"base_url"?,"headers"?,"protocol"?,"params"?:{region,project,location,…}}
   composite models are virtual providers: {"provider":"router","models":[h1,h2],"config":{…}}
   error JSON: {"kind":"aimux","code":<int>,"message":"…","error":<AiMuxError JSON pinned by golden tests>} */
  • C1 — add the 9 exports and dispatch.rs (+2,200, verified). op → aimux-core call, JSON in and out, coexisting with the old exports. aimux_call(0, "configure", …) carries logging, recording, sessions, proxy, and external provider registration. Budget: lib.rs rewrite ~1,150 (858 lines of helpers between exports are still needed by the 9 new ones), dispatch.rs 300, header plus tests 720. The op strings need an exhaustive table test (every op × every handle kind) and must be exported as constants in aimux.h and every binding.
  • C2 — 40 per-provider constructors become one-line forwards to model(spec) (−682 / +120, revised). Symbols unchanged. Keep the deterministic FFI error when api_key is NULL (Java / Kotlin / Swift tests use it as their "real C ABI error" sample). Bedrock key/secret/region and vertex project/location go through params. This step lets the providers track delete types while bindings are still on the old ABI. Same PR must rewrite exports_smoke_test.rs (1,102 lines, references the 40 symbols 29 times). aimux-ffi.h is hand-written with no cbindgen and no drift gate; the 40 declarations are removed by hand.
  • C3 — 16 aimux_error_* field accessors → aimux_error_json (−766 / +40, revised). Keep code / message for C callers. The original missed retry_ms: it is computed by retry_after_hint() (including HTTP-date parsing) and is not in the serde JSON, so the envelope needs a derived retry_after_ms. The branch adds 6 Retry accessors; the envelope needs the Retry variant.
  • C4 — delete the old exports, old header, 2,073 lines of old FFI tests (−7,000, revised). The trace / session / recording families are not deleted; they become ops. Deleting aimux_trace_* and aimux_session_* outright was rejected: Go and Flutter docs present them as the only entry point for the five pure-C-ABI languages. As ops they stay reachable from C. next_part's out_state stays as is, not wrapped in the JSON envelope (one extra allocation and parse on the busy-poll path).

Two review concerns checked and dismissed: one-shot transcription and file upload as base64 inside JSON do not add a copy, because both exports are already base64 C strings today (lib.rs:2126, lib.rs:2381); no 10th byte-carrying export is needed, and the streaming path keeps raw bytes via aimux_session_push. Composite models take child handles as integers in the spec; construction must Arc-clone the children like aimux_router_new does today, with a test that dropping the child handles after construction still generates.

D · Bindings

Seven languages each hand-write per-provider constructors, per-field error decoding, type mirrors, and stream pumps. After the 9-symbol ABI each binding is four objects: Model.new(spec), model.call(op, request), model.stream(op, request, abort?), and AimuxError (subclass chosen from the error JSON tag). Type mirrors stay as an optional typed layer and are not in this round.

Language Facade today After Path Status
Go 2,965 450 cgo unchanged; delete 44 New* and per-field error decoding measured
Java 3,450 600 JNA declarations 91 → 9; one Model class; AimuxException picks 13 subclasses from the JSON tag. The 100 Builder inner classes (1,594 lines) can go, but only after making the private all-args constructors public or moving to Java 17 records. A NativeHandle base class for the nine modality classes saves 192 more lines. The 40 requireNonNull calls in factories are a documented NPE contract; keep them Builder revised
Kotlin 4,744 300 Today a second complete JVM binding (own JNA interface, facade, exception hierarchy) with no gradle dependency on Java. "Depend on aimux-java wholesale" was rejected: Errors.kt's sealed AimuxException is a documented exhaustive-when contract and Java 8 has no sealed classes (Java uses 13 nested subclasses). Revised: the JNA interface, Model facade and multimodal facades (~1,700 lines) depend on the Java artifact; Kotlin keeps its sealed exception hierarchy (350 lines) as a mapping layer over the Java exceptions, plus coroutine / Flow sugar. CI needs a kotlin → java artifact dependency edge revised
Swift 1,832 400 delete 31 static factories and 13 fromC getters; AsyncThrowingStream wraps aimux_stream measured
Flutter 2,795 630 91 dart:ffi lookups → 9. Interim: ffigen from the header (−458 hand-written declarations, pre-authorized by RFC-0001 §227, all 91 symbols have prototypes). Two pieces stay hand-written: the openAimuxLibrary loader (iOS DynamicLibrary.process() path) and dropHandlePtr (reinterprets aimux_drop_handle as a NativeFinalizer; ffigen only emits a private pointer field) revised
Node 3,514 1,200 keep napi-rs (a ctypes-style call would block the event loop), but depend on aimux_ffi::dispatch as an rlib and share op / spec / error JSON with the C path. aimux-ffi's crate-type already includes rlib. Node currently bypasses aimux-ffi entirely, so this is a new dependency edge, and whether napi's tokio bridge conflicts with the FFI global runtime's re-entrancy guard is unmeasured. The "shared binding core for the three Rust glue crates" shrank from 3,400 to 1,500 lines on verification: only prompt / options parsing, spec, and dispatch can be shared; Node's structured error classes need Env to build JS exceptions, and AimuxResult plus the stream pump stay on the napi side; FFI has its own runtime and ffi_block_on revised
Python 2,611 400 design C proposes replacing the pyo3 crate with stdlib ctypes. Both reviewers require a prototype first: streaming needs a helper thread plus queue, exceptions inside ctypes callbacks are swallowed, and the wheel must bundle libaimux_ffi per platform. If the prototype fails, fall back to a thin pyo3 layer over dispatch prototype first
  • D1 — Kotlin over the Java artifact (~−1,700, revised as above). Depends only on the Java artifact, not the new ABI; can go first.
  • D2 — Go (after C1)
  • D3 — Java (after C1)
  • D4 — Swift (after C1)
  • D5 — Flutter (ffigen interim can go before C1; 9-symbol rewrite after)
  • D6 — Node (after C1; measure the runtime re-entrancy question first)
  • D7 — Python (prototype ctypes first; after C1)
  • D8 — generate the type mirrors (0 lines removed, hand maintenance goes to zero). aimux-core's serde types are mechanically exported once (ts-rs → bindings/node/src/types, guarded by --check), then hand-mirrored in Java, Flutter, Swift, Kotlin, Go and Python, plus the ungenerated aimux-web copy: 18,944 lines, ~10% of Rust plus binding source. Every file header says which ts output it was copied from; this is why Align the request pipeline with the AI SDK #164's error-type change touches 8 languages. Cannot delete first and fill in later: Go's 8 multimodal entry points only accept typed *XxxCallOptions with no JSON-string path, and Python's wrapper.py (1,221 lines) is all-or-nothing. Emit a JSON Schema from the same #[derive(TS)], generate per language, and extend the gen_ts_types.py --check gate to all six outputs. Last PR of the track.

Each binding PR runs only its own CI job.

E · PR #164 internal

  • E1 (−1,500, one PR on the branch before merge; existing CI covers it)
    • docs/ai-sdk-request-pipeline.md (1,037 lines): keep §1–§3 and §14 (~115 lines), move to rfc/0031. §4–§10 restate rustdoc; §11–§13 are a completed migration checklist. −920
    • The eight modality generate_* wrappers and generate_text / stream_text repeat the same prelude (timeout, prepare_retries, RecordingContext, session, span); extract one run_operation. −150
    • Abort handling is written six times (retry loop, retry::delay, timeout::run_until, sleep_or_abort, send_one_request, two body readers). Every entry point is inside timeout::run_until, and dropping the future cancels; keep one, the rest become plain awaits. −120
    • StreamTextResult::text() equals consume().map(|a| a.text). −45
    • PreparedRetries is RetryConfig in different units; the generic retry_with_exponential_backoff has one production caller. −70
    • provider-utils copies the TS one-function-per-file layout (get_from_api.rs 25 lines, handle_fetch_error.rs 21, retry.rs 3-line re-export, …); fold into http.rs. −90, −7 files
    • Two identical body-read loops merged; DEFAULT_MAX_DOWNLOAD_SIZE = 2 GiB read into a Vec is no limit at all. −50
    • Not a deletion, but noted: fal / luma / BFL / revai / gladia still do submit+poll inside one do_generate, so core retry re-submits a billed job (the same bug fixed for video); is_retryable() and the inline predicate in retry.rs are written twice.

Docs reorganization (no deletion)

Three layers by audience. Move with git mv, fix links with one grep pass.

docs/ keeps only what users read

  • README.md index; guides/ holds session-affinity, provider-config, codex-subscription, error-model.
  • api/ keeps only reference.md and the generated providers.md; the 9 per-language guides move into bindings/<lang>/README.md (today only flutter / go / python have one) so each binding maintains its own docs.
  • api/gaps.md is an issue, not a doc: file it as an issue, then delete.
  • PERF-RESULTS.md, aimux-vs-aisdk-node.md and bindings/node/bench go under docs/benchmarks/.

rfc/ gets an index

  • New rfc/README.md: one table of number, title, status (implemented / superseded / draft); move the RFC list out of the root README.
  • docs/ai-sdk-request-pipeline.mdrfc/0031-*.md (with the E1 trim); update the 0032 draft with the corrections above and commit it.
  • Two number collisions (0005 ×2, 0027 ×2): do not renumber, mark them in the index, avoid breaking links.

archive/ for history

  • docs/internal/ (105 files), docs/quality-audit/ (34), docs/plan/ (17), provider-inventory/ move to a root archive/ with one README ("no longer maintained, see git history").
  • docs/internal/cache-tracing/prototype is the ancestor copy of aimux-core/src/trace; keep only its README when archiving.
  • quality-audit/round4/ holds a 119k-line clippy log and an lcov file; both are build artifacts, the one suggested exception to "no deletion" (git history keeps them).

Language. Public guides are mixed: provider-config-manual, session-affinity-guide and error-model are Chinese, api/*.md is English, RFCs are mostly Chinese. Suggested rule for CONTRIBUTING.md: user-facing docs/ and bindings/*/README in English, rfc/ stays Chinese. This round only moves files, no translation.

Comparison with pi-ai

pi-ai is 23,720 lines, half of it the 10 protocol implementations in src/api/. Its 40 providers average 24 lines; 27 are exactly 14–15 lines of createProvider({ id, baseUrl, auth, models, api }). The model list is generated from models.dev. Its 30 test suites hit real keys across a provider × capability matrix. aimux already has half of this: the 251-row registry is pi-ai's providers/, missing only the protocol column.

Adopt

  • Protocol implementation is code, protocol selection is data (RFC-0032 already says this).
  • Per-model overlay models[]: per-model quirks like deepseek-reasoner are 3–6 lines of JSON, not Rust.
  • Compat flags as a deserializable struct, plus the CI grep forbidding provider names inside protocol directories. pi-ai skipped this and drifted to 31 provider === branches.
  • Table-driven conformance test: one (provider, protocol, cassette dir) table replays all 32 cassette directories, replacing 15 forks' individual constructor tests.
  • In-band stream termination (a NULL callback after the Finish / Error event); delete every pump's "return error but never call on_done" branch.

Do not adopt

  • The models.dev catalogue: only 140 of the 251 registry names are in it, and it is a 4.4 MB runtime fetch; RFC-0032 §6 already rules it out.
  • Deleting the search modality: 11 providers, 24 consuming files, RFC-0007; product feature.
  • Cross-provider message transforms (transform-messages.ts): new behaviour, out of scope.
  • OAuth login flows: pi-ai wrote 2,664 lines for 7 vendors; aimux keeps "string key or env".

Honest expectation: the 6 Rust protocol implementations total ~16,000 lines, more than pi-ai's 10, because pi-ai hands transport and types to each vendor's official SDK. The bulk of the reduction is not in the protocol implementations but around them: the 33 wrappers, 5 responses copies, 40 FFI constructors and 7 binding facades.

Sequencing

  1. A1–A5: any time, in any order.
  2. B1 (pure addition) and C2 (constructor forwards) are prerequisites for every provider deletion. B4 / B5 also need the five direct call sites of MistralProvider::model() / XAIProvider::model() switched to provider().
  3. B2 → B3 → B4 / B5 → B6 (after its three prerequisites) → B7 → B8 → B9.
  4. C1 → C3 → C4, with C4 only after every binding has migrated.
  5. D1 Kotlin first (Java artifact only). D2–D6 after C1, one PR each. D7 Python after the ctypes prototype. D8 type-mirror generation last.
  6. E1 on the Align the request pipeline with the AI SDK #164 branch before it merges.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions