Skip to content

refactor(web): generate the web wire types with ts-rs - #247

Merged
eric8810 merged 18 commits into
masterfrom
refactor/generate-web-wire-types-with-ts-rs
Oct 17, 2026
Merged

eric8810 merged 18 commits into
masterfrom
refactor/generate-web-wire-types-with-ts-rs

Conversation

@cunninghamcard-bit

@cunninghamcard-bit cunninghamcard-bit commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Requested by li

Summary

Before: the web console's Wire*.ts files are hand-written mirrors of tools/aimux-web/src/wire.rs and have already drifted (WireContentPart lacks provider_executed). The core types it uses (Recording, StreamPart, TraceRecord, SessionView and everything they reference) are hand-made copies of the ts-rs output in bindings/node/src/types/, kept in tools/aimux-web/web/src/types/ (139 files) with nothing checking them; 19 of them already differ from the generated versions (for example StreamPart.ts).

After: the wire structs derive their TypeScript with ts-rs and are exported to web/src/types/generated/ by scripts/gen_ts_types.py, which now handles two targets and checks both in CI. Every ts-rs export, the wire types' and aimux-core's, runs only behind a ts-export feature, so a plain cargo test writes nothing; scripts/gen_ts_types.py --check also fails on an export attribute outside the gate. Core types are no longer copied: the web imports them type-only from bindings/node/src/types/ through the @aimux/types tsconfig alias.

This PR merges #246, so it is based on #246's branch, and [1/8] (#249) merges this PR, so the AI SDK migration series carries both from its first layer. Merge order: #246, #247, then the series.

Type of change

  • Bug fix (non-breaking)
  • New feature (non-breaking)
  • Breaking change
  • New provider
  • New / updated binding
  • Docs / RFC

Checklist

  • cargo fmt --all -- --check passes
  • cargo clippy --workspace --all-targets -- -D warnings passes
  • cargo test --workspace passes (cassette playback — no network/keys)
  • Added or updated cassette tests / contract fixtures for behavior changes
  • Updated relevant docs (README.md, docs/, rfc/, bindings/README.md)
  • Commit messages follow Conventional Commits

Notes for reviewers

cunninghamcard-bit and others added 8 commits October 11, 2026 16:30
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
(cherry picked from commit 758d0bc)
The returned impl Iterator is already must_use, so clippy's
double_must_use rejected it.

(provider.rs part of commit 8397424)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
…e removal

- scripts/gen_providers_doc.py: the `provider_name` module no longer exists;
  the docstrings and the section-header guard now name only `provider`.
  `--check` output is unchanged.
- docs/internal/phase4-audit.md: add a follow-up note (same style as
  RFC-0020) so the historical audit no longer reads as if the generated
  ProviderName types and their drift tests still exist.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
(cherry picked from commit dfcd54f)
The A2 line said Node would keep a generated string-literal union for
completion. It does not: overlay-registered provider names can never be
part of a compile-time union, so keeping one would keep the second name
path this PR removes. Record the actual A2 scope.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
(cherry picked from commit 3e2d9fc)
…ia alias

- Wire*.ts were hand-maintained mirrors of wire.rs and had already drifted
  (WireContentPart lacked provider_executed). Derive TS on the wire structs
  and export them to web/src/types/generated/ through gen_ts_types.py, which
  now handles two targets and checks both in CI.
- Core types resolve through the @aimux/types tsconfig alias instead of
  five-level relative paths into bindings/node.
- gen_ts_types.py sync no longer rewrites files that differ only by ts-rs
  end-of-line spaces.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
(cherry picked from commit 69c1351)
- Drop the redundant #[must_use] on provider_names(); the returned
  impl Iterator is already must_use, so clippy::double_must_use failed CI.
- Gate the wire types' ts-rs export tests behind a ts-export feature that
  only gen_ts_types.py enables. A plain cargo test previously wrote
  Wire*.ts into bindings/node/src/types via the workspace-wide
  TS_RS_EXPORT_DIR, which made gen_ts_types.py --check fail on stale files.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
(cherry picked from commit 8397424)
@cunninghamcard-bit
cunninghamcard-bit marked this pull request as ready for review October 12, 2026 11:09
eric8810 added a commit that referenced this pull request Oct 15, 2026
…ker/parseToolCall, per-package baseline

- 基线更新:#200 已合并(d8fb4ac5),superseded-pending-#200 → superseded-by-#200
- T2/T3 定案:彻底删除通用 tracker,累积状态为 OpenAI-chat 一族流处理内部实现;关联语义跟随上游 tracker(基线 provider-utils 5.0.55),歧义增量丢弃不报错
- §4.3:flush 时结束、index/到达顺序、流内 error 帧、EOF 按各包上游处理;上限默认不设
- 工具参数对齐上游 parseToolCall:空文本按 {} 校验、非法调用 core input 规则、修复错误不包装、非对象 input 厂商编码不保值
- §7.3 按包基线引用 fixtures/aisdk/VERSIONS.json(ai 7.0.127/provider 4.0.22/provider-utils 5.0.55),附升级规则
- D8 与新 C ABI/绑定迁移放最后;web 类型先 ts-rs 过渡(#247);§8/§9 改为逐层合入
eric8810 added a commit that referenced this pull request Oct 15, 2026
* docs(rfc): define V4 type boundaries

* docs(rfc): V4 type boundaries per review — merged #200, upstream tracker/parseToolCall, per-package baseline

- 基线更新:#200 已合并(d8fb4ac5),superseded-pending-#200 → superseded-by-#200
- T2/T3 定案:彻底删除通用 tracker,累积状态为 OpenAI-chat 一族流处理内部实现;关联语义跟随上游 tracker(基线 provider-utils 5.0.55),歧义增量丢弃不报错
- §4.3:flush 时结束、index/到达顺序、流内 error 帧、EOF 按各包上游处理;上限默认不设
- 工具参数对齐上游 parseToolCall:空文本按 {} 校验、非法调用 core input 规则、修复错误不包装、非对象 input 厂商编码不保值
- §7.3 按包基线引用 fixtures/aisdk/VERSIONS.json(ai 7.0.127/provider 4.0.22/provider-utils 5.0.55),附升级规则
- D8 与新 C ABI/绑定迁移放最后;web 类型先 ts-rs 过渡(#247);§8/§9 改为逐层合入

@eric8810 eric8810 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

评审结论:需小改后可合并(1 项阻塞)

架构完全符合 RFC-0036 A4 与 RFC-0042 §7.3 的 ts-rs 过渡定位:core 类型经 @aimux/types 直连唯一生成目录(9 处 import 全部 type-only,构建时擦除)、Wire 类型 wire.rs 单一定义 + #[cfg_attr(feature="ts-export", ts(export))] 门控 + temp-dir 导出、--check 三态比对;无 build.rs、无自动生成,没有把过渡方案变成永久架构。三处真实漂移随单一来源消除(WireContentPart 缺 provider_executed、web 副本 ContentPart/StreamPart 落后于 node 生成版、WireCallRequest api_key 文档过期)。

阻塞项:A4 明确要求"每条 import 都需要类型检查;不能只比较文件是否相同",但本 PR 在 CI 只加了文件比对——ci.yml 的 job 里没有任何 web 前端 typecheck(vue-tsc),npm run build(含 vue-tsc -b)只在 release.yml 出现。后果:Rust 侧改类型名 → 重新生成、--check 通过 → web/src 里 @aimux/types/X import 悬空,PR CI 不会失败。请在 ci.yml 加 web typecheck(npm ci && npm run typecheck),或说明推迟并承认 A4 该项验收未关闭。

建议(非阻塞):

  1. aimux-core 的 #[ts(export)] 无 feature 门控(content.rs 等多处),普通 cargo test 仍会经 TS_RS_EXPORT_DIR 改写受跟踪的 bindings/node/src/types(master 既有、非本 PR 退化,Wire 目标已做正确门控)——建议对齐或登记为 D8 前已知债务;
  2. rfc/0029-web-console.md §9.1 的 TS_RS_EXPORT_DIR 输出路径描述已过时,建议加"后续变更"注记;
  3. PR 描述 "Before: Core types are imported through five-level relative paths" 不准确——master 上 core 类型是拷贝进 web/src/types,五层路径只是中间提交的过渡态,请修正。

疑问:gen_ts_types.py 的 EXCLUDED={"ProviderName.ts"} 与 #246 存在已声明的冲突,请确认合并顺序及 EXCLUDED 移除的跟进归属(谁后合谁解决)。checklist 的 cargo test --workspace 是否本地验证过?

评审深度:158 files 全量 stat;gen_ts_types.py 全文、wire.rs 全 diff、tsconfig/README/4 个 .vue 的 import 改写细读;生成文件细读 2 个对照 master 手写版;删除文件抽检 6 个与 bindings/node 版本逐字节 diff;全分支 grep。未运行 cargo/npm/vue-tsc。

@eric8810 eric8810 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Request Changes —— 合并前需完成(详细核验见上一条评审):

  1. 在 ci.yml 增加 web 前端 typecheck 门禁(npm ci && npm run typecheck,即 vue-tsc --noEmit)。RFC-0036 §13.1 A4 明确"每条 import 都需要类型检查;不能只比较文件是否相同"——当前 PR CI 只有 gen_ts_types.py --check 文件比对,Rust 侧改类型名后 web import 悬空 CI 不会失败;或说明推迟并显式承认 A4 该项验收未关闭。

改完即可合并;其余为非阻塞建议(aimux-core #[ts(export)] 门控或登记 D8 债务、rfc/0029 注记、描述措辞修正)。

…redaction (#248)

aimux-providers/src/provider.rs and lib.rs conflicted where #248 adds
`provider_api_key_env` next to this branch's `provider_names`; both are
kept and exported.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
tools/aimux-web/web/src/types/ProviderName.ts was the eighth copy written
by scripts/gen_provider_names.py, which this branch deletes. Nothing
imports it (it is an orphan on master too), and its header still claimed
the deleted generator. RFC-0036 §12.3 A2 requires every Rust, binding
and web copy and the generator to go.

The three quality-audit reports that cite `gen_provider_names.py --check`
as a current gate get a later-change note, as docs/internal/phase4-audit.md
already has.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
… and secret redaction (#248), RFC-0033 to RFC-0043

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
The console imports the core types from bindings/node/src/types through
@aimux/types. gen_ts_types.py --check only compares the generated files,
so a renamed or removed Rust type regenerates cleanly and leaves a
dangling import that no pull request check catches. RFC-0036 §13.1 A4
asks for every import to be type-checked.

A new "Web console typecheck" job runs `npm ci && npm run typecheck`
(vue-tsc --noEmit) in tools/aimux-web/web. It runs when the shared code
changes (which includes tools/aimux-web and the Rust crates whose types
are generated) or when bindings/node changes, where the generated types
live; documentation-only changes skip it. "CI result" checks it.
Removing bindings/node/src/types/Recording.ts locally makes it fail on
the two files that import it.

RFC-0029 §9.1 gets a later-change note: the console no longer has its
own copies, and TS_RS_EXPORT_DIR no longer points at the web directory.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
aimux-core's #[ts(export)] derives are not feature-gated, and
TS_RS_EXPORT_DIR points at bindings/node/src/types/, so a plain cargo
test rewrites that tracked directory. scripts/gen_ts_types.py is the
source of truth; the debt goes away when ts-rs retires with D8.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
The only conflict was scripts/gen_ts_types.py. It keeps #247's
multi-target structure (TARGETS, per-target sync and check) and drops
the EXCLUDED set and manifest's exclude parameter: #246 deletes
bindings/node/src/types/ProviderName.ts, so nothing is left to exclude.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
aimux-core's #[ts(export)] attributes were ungated, so every plain
cargo test rewrote bindings/node/src/types through the TS_RS_EXPORT_DIR
in .cargo/config.toml. aimux-core now has the same ts-export feature as
tools/aimux-web (tools/aimux-web/Cargo.toml:21-23), and each of its 158
exports is #[cfg_attr(feature = "ts-export", ts(export))].

scripts/gen_ts_types.py enables the feature for the core target, and
--check also fails on any ts(export attribute outside that gate in either
crate's source, printing file:line. The .cargo/config.toml comment and the
web README now say that cargo test writes nothing and the generator is
the only writer. gen_ts_types.py --check passes: 159 node files and 8 web
files are unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
Brings in #266 (a blank key on a custom base URL is sent as an empty
key, as the AI SDK does for apiKey: '').

Resolution: no conflicts.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
Brings in #266 (a blank key on a custom base URL is sent as an empty
key, as the AI SDK does for apiKey: '').

Resolution: no conflicts.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
cunninghamcard-bit pushed a commit that referenced this pull request Oct 16, 2026
Brings in #247 (with #246) through the layer below: ts-rs exports run
only under the ts-export feature, and the generated ProviderName types
are gone.

Resolution: aimux-core files keep this layer's code with every export
gated, including the ones this layer adds (RecordingOperation's among
them); the rest merges cleanly.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
@cunninghamcard-bit
cunninghamcard-bit changed the base branch from master to refactor/remove-generated-provider-name-types October 16, 2026 11:03

Copy link
Copy Markdown
Contributor Author

这次更新:本 PR 合入了 #246(2c867927),所以它的基改成 #246 的分支;#246 合并后再改回 master。

eric 的意见 我们的处理 位置
CI 只比对生成文件,不做 web 类型检查(阻塞) 新增 Web console typecheck 任务,在 tools/aimux-web/web 跑 npm ci 和 npm run typecheck(vue-tsc --noEmit),接进 changes 和 CI result。本地删掉一个被引用的生成类型,它会报错(1bd6a558) .github/workflows/ci.yml:600
core 的 #[ts(export)] 没有门控,普通 cargo test 会改写受跟踪的目录 加了门控。aimux-core 新增 ts-export feature,158 处导出都改成 #[cfg_attr(feature = "ts-export", ts(export))],只有 gen_ts_types.py 打开这个 feature。--check 还会扫两个 crate 的源码,发现没门控的导出就报出文件和行号并失败。普通 cargo test 不再写任何文件(0b783442) aimux-core/Cargo.toml:14、scripts/gen_ts_types.py:58、.cargo/config.toml:2
RFC-0029 里导出路径的描述过时 第 9.1 节加了一段"后续变更"(1bd6a558) rfc/0029-web-console.md:479
PR 描述的 Before 段不准 已改正:master 上 core 类型是拷贝在 web/src/types 里的,五层相对路径只出现在一个中间提交 PR 描述
和 #246 谁先合,谁去掉 EXCLUDED 本 PR 直接合入了 #246(2c867927),EXCLUDED 和它的排除参数一起删了,生成器不再有例外。合并顺序是 #246、#247,然后是 [1/8] scripts/gen_ts_types.py
cargo test --workspace 跑过没有 跑过。合入 #246 后全部 Rust 测试 3739 个通过,0 个失败(0b783442) —

Generated by Claude Code

@eric8810 eric8810 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

复核通过,撤回修改请求。逐项核实:web-typecheck job 已加入 ci.yml 并接进 changes/ci-result(A4"每条 import 类型检查"闭合)✓;aimux-core 的 ts-export feature 门控落地(158 处导出改 cfg_attr、--check 扫描未门控导出并报错,普通 cargo test 不再写受跟踪文件)✓;RFC-0029 注记、PR 描述修正 ✓;合入 #246 后 EXCLUDED 连同参数删除,顺序 #246→#247→[1/8] 明确 ✓。批准。

@eric8810
eric8810 changed the base branch from refactor/remove-generated-provider-name-types to master October 17, 2026 01:02
master 带入 #262(CI 提速,含 cargo-ndk@4.1.2 与 changes/ci-result 任务表)与 #246 的 squash;
scripts/gen_ts_types.py 冲突取本分支版本(双 crate ts-export 门控的超集),
ci.yml 自动合并将 web-typecheck 正确接进 #262 的 run-map 与 ci-result。
@eric8810
eric8810 merged commit ec338c9 into master Oct 17, 2026
20 checks passed
eric8810 added a commit that referenced this pull request Oct 17, 2026
master 带入 #262/#246/#247 的 squash;三个冲突文件取本分支版本
(ci.yml 为已调和 #262 任务表 + web-typecheck + pin 脚本测试的超集),
gen_ts_types.py 自动合并。
cunninghamcard-bit pushed a commit that referenced this pull request Oct 17, 2026
Brings in master at ec338c9 (#262, #246 and #247, each squashed into one
commit) through [7/8]. The tree is unchanged: this branch already had
all three.

Resolution: no conflicts.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
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.

3 participants