05LynShenRust service · React consoleRust 服务 · React 控制台
LynShen GatewayLynShen 网关
A Rust gateway that translates between three AI API dialects by meaning, not field names, and routes each request with retries and health checks.一个 Rust 网关:在三种 AI API 协议之间按含义转换,而不是按字段名;每条请求都有重试和健康检查的路由。

01The problem问题
Three APIs that almost agree.三套几乎一样、又不太一样的 API。
Different shapes结构不同
OpenAI Responses, Chat Completions and Anthropic Messages model reasoning, tool calls, usage and streaming in different ways.OpenAI Responses、Chat Completions 和 Anthropic Messages 对推理、工具调用、用量和流式输出的建模各不相同。
Lossy converters转换会丢东西
Field-by-field mapping drops encrypted reasoning, breaks stream lifecycles, or quietly switches upstream halfway through a reply.逐字段映射会丢掉加密的推理内容、打乱流的生命周期,或者在回复中途悄悄切换上游。
Failure is normal失败是常态
Upstreams time out, rate-limit and fail mid-stream. Retrying must never send a client two half-answers stitched together.上游会超时、限流,也会在流到一半时出错。重试绝不能把两个半截回答拼在一起发给客户端。
02What I built我做了什么
Convert the meaning, not the field names.转换的是含义,不是字段名。
Every request is decoded into URP, a typed internal format, then encoded for whichever upstream serves it. Streams are re-emitted as events in the client’s own dialect.每条请求先解码成带类型的内部格式 URP,再按负责它的上游编码。流式回复按客户端自己的协议重新发出事件。
- Provider waterfall routing with bounded retries, circuit breakers, passive and active health probes, cooldowns and model affinity.按顺序的服务商瀑布路由:有限重试、熔断、被动和主动健康探测、冷却和模型亲和。
- 43 transform modules: prompt-cache breakpoints, reasoning mapping, role repair, SSE frame splitting, image compression (JPEG, PNG, WebP, JPEG XL).43 个变换模块:提示缓存断点、推理映射、角色修复、SSE 分帧、图片压缩(JPEG、PNG、WebP、JPEG XL)。
- Usage is counted per request in integer units and rounded once, so totals never drift.用量按请求以整数单位计量,只舍入一次,合计不会漂移。
- A React console (keys, request logs with a capture viewer, a Playground, provider health) in 4 UI languages.React 控制台(密钥、带抓包查看器的请求日志、Playground、服务商健康状态),界面有 4 种语言。
- Public docs: a Next.js static site with 240 MDX pages in 4 locales, including one page per transform.公开文档:Next.js 静态站,4 种语言、240 个 MDX 页面,每个变换都有单独一页。
03How it works怎么做的
One binary, one request path.一个程序,一条请求路径。
- STEP 01
Your app你的应用
Any SDK: Responses, Chat or Messages.任意 SDK:Responses、Chat 或 Messages。
- STEP 02
Decode解码
Into URP, a typed internal format.转成带类型的内部格式 URP。
- STEP 03
Transform变换
Cache hints, reasoning, role repair.缓存提示、推理映射、角色修复。
- STEP 04
Route路由
Waterfall with retries and breakers.瀑布路由,带重试和熔断。
- STEP 05
Stream back流式返回
In the client’s own dialect.按客户端自己的协议。
04Interface and details界面与细节
Make the invisible path visible.把看不见的路径摆出来。
- 01 / 03

Every request, accounted for每条请求都有账可查
Request logs show the model, the channel that actually served it, time to first token and tokens in and out. A capture viewer opens the exact request and response for debugging.请求日志显示模型、实际服务的渠道、首字延迟和输入输出 Token。抓包查看器能打开完整的请求和响应,方便排查。
- 02 / 03

A Playground that speaks every dialect能讲所有协议的 Playground
The Playground sends real requests through the gateway and renders reasoning summaries, tool calls and streamed output, so you can see what a client would see.Playground 通过网关发真实请求,渲染推理摘要、工具调用和流式输出,让你看到客户端会看到的东西。
- 03 / 03

Docs that state the rules把规则写清楚的文档
The docs explain the contract precisely, for example: once the first byte is sent, the gateway will not switch to another upstream; errors after that arrive as stream events.文档把约定写得很精确,比如:第一个字节发出后,网关不会再换上游;之后的错误以流事件的形式返回。
Video视频
One task, in the logs.一个任务,落在日志里。
The request log as the desktop agent works: each subagent call lands as a row with its model, latency and tokens. Recorded with demo data.桌面智能体干活时的请求日志:每次子智能体调用都变成一行,带模型、延迟和 Token。用演示数据录制。
05Tech used用到的技术
What it’s made of.用什么做的。
- Server服务端
- Rust 2024
- axum 0.8
- tokio
- reqwest + rustls
- tokio-tungstenite
- tower-http
- Data数据
- SeaORM
- sqlx
- SQLite
- PostgreSQL
- rust_decimal
- Prometheus
- Console控制台
- React 19
- Vite 7
- TypeScript
- Tailwind
- shadcn/ui
- SWR
- i18next
- Recharts
- Docs文档
- Next.js 16 (static export)
- Fumadocs
- MDX
- KaTeX
- Quality质量
- 1,500+ Rust tests added新增 1500+ Rust 测试
- ~330 front-end tests约 330 个前端测试
- Playwright browser scriptsPlaywright 浏览器脚本
- PostgreSQL migration tests in CICI 中的 PostgreSQL 迁移测试
06What I learned我学到了什么
Lessons I’ll keep.会一直记得的几件事。
- Write the spec before the code. Routing and usage accounting each have a written spec; the tests follow the spec, so edge cases are decided once, on paper.先写规格,再写代码。路由和用量统计都有成文的规格;测试照规格写,边界情况只在纸上决定一次。
- Round once. Storing counts as integers and rounding at a single, named step removed a whole class of off-by-one totals.只舍入一次。用整数存计数、只在一个明确的步骤舍入,一整类「差一点点」的合计问题就消失了。
- Streams are contracts. The hardest bugs were not in parsing but in lifecycle: when a stream may still fail over, and when it may not.流就是约定。最难的问题不在解析,而在生命周期:流什么时候还能切换,什么时候不能。