Repository navigation
refactor(web): generate the web wire types with ts-rs - #247
Conversation
Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-01a0eb91-5e79-775e-975e-b85e2ac49ab6 (cherry picked from commit 7f68ac3)
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)
Amp-Thread-ID: https://ampcode.com/threads/T-01a0eb91-5e79-775e-975e-b85e2ac49ab6 Co-authored-by: Amp <amp@ampcode.com> (cherry picked from commit dc1cd14)
…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)
…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 改为逐层合入
* 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
left a comment
There was a problem hiding this comment.
评审结论:需小改后可合并(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 该项验收未关闭。
建议(非阻塞):
- aimux-core 的
#[ts(export)]无 feature 门控(content.rs 等多处),普通cargo test仍会经TS_RS_EXPORT_DIR改写受跟踪的bindings/node/src/types(master 既有、非本 PR 退化,Wire 目标已做正确门控)——建议对齐或登记为 D8 前已知债务; rfc/0029-web-console.md§9.1 的 TS_RS_EXPORT_DIR 输出路径描述已过时,建议加"后续变更"注记;- 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
left a comment
There was a problem hiding this comment.
Request Changes —— 合并前需完成(详细核验见上一条评审):
- 在 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
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
|
这次更新:本 PR 合入了 #246(2c867927),所以它的基改成 #246 的分支;#246 合并后再改回 master。
Generated by Claude Code |
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
Requested by li
Summary
Before: the web console's
Wire*.tsfiles are hand-written mirrors oftools/aimux-web/src/wire.rsand have already drifted (WireContentPartlacksprovider_executed). The core types it uses (Recording,StreamPart,TraceRecord,SessionViewand everything they reference) are hand-made copies of the ts-rs output inbindings/node/src/types/, kept intools/aimux-web/web/src/types/(139 files) with nothing checking them; 19 of them already differ from the generated versions (for exampleStreamPart.ts).After: the wire structs derive their TypeScript with ts-rs and are exported to
web/src/types/generated/byscripts/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 ats-exportfeature, so a plaincargo testwrites nothing;scripts/gen_ts_types.py --checkalso fails on an export attribute outside the gate. Core types are no longer copied: the web imports them type-only frombindings/node/src/types/through the@aimux/typestsconfig 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
Checklist
cargo fmt --all -- --checkpassescargo clippy --workspace --all-targets -- -D warningspassescargo test --workspacepasses (cassette playback — no network/keys)README.md,docs/,rfc/,bindings/README.md)Notes for reviewers
refactor/remove-generated-provider-name-types); next is [1/8] ([1/8] chore: CI on every PR and test fixture cleanup #249). Split out of [1/8].ts-exportfeature, as the web wire types already were. Onlyscripts/gen_ts_types.pyenables it, and--checkalso fails on an export attribute outside the gate, naming the file and line; a plaincargo testwrites nothing. This PR now merges refactor!: remove the generated ProviderName types #246, so it is based on refactor!: remove the generated ProviderName types #246's branch and the generator'sEXCLUDEDexception is gone.