Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 30 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
pull_request:

jobs:
unittest:
unittest-python:
runs-on: ubuntu-latest
strategy:
fail-fast: false
Expand All @@ -18,9 +18,36 @@ jobs:
with:
python-version: ${{ matrix.python-version }}
# 零第三方依赖(纯标准库),无需 pip install
- name: 离线单测(44 项)
- name: 离线单测(56 项)
run: python -m unittest tests.test_offline
- name: 端到端冒烟(11 项,子进程级)
- name: 端到端冒烟(25 项,子进程级)
run: python tools/smoke_e2e.py
- name: 实验框架 dry-run
run: python experiments/run_experiments.py --dry-run

unittest-ts:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node-version: ["22"]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: npm
cache-dependency-path: ts/package-lock.json
# TS 移植版:运行零第三方依赖(Node 22 类型擦除 + node:sqlite)
- name: 安装开发依赖(typescript)
run: npm ci
working-directory: ts
- name: 类型检查(strict + erasableSyntaxOnly)
run: npm run typecheck
working-directory: ts
- name: 离线单测(56 项)
run: npm test
working-directory: ts
- name: 端到端冒烟(25 项,子进程级)
run: npm run smoke
working-directory: ts
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ data/*
metrics.jsonl
experiments/report.json

# Node(ts/ 移植版)
node_modules/

# 系统
Thumbs.db
.DS_Store
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,11 @@
- 客户端中途断开:已收到的部分照常落账(费用已实际发生)
- mock:`/chat/completions` 路径发 Chat 形状 chunk(role 首帧 + finish + usage + [DONE])
- 测试:`TestChatStreamCollector` 3 项单测;冒烟第 7 组 7 项(两个透传方向文本拼合 / 逐块到达 ~31ms / [DONE] / 501)。56 项单测 + 25 项冒烟全过

## 2026-09-09 · v2.0 TypeScript 全量移植(本提交)

- **`ts/` 目录:对齐上游 TencentDB-Agent-Memory 技术栈(Node 22 + TypeScript)的全量移植**,功能对齐 Python 版 v1.7。IR 契约 / 三对 adapter / 状态层(node:sqlite)/ 网关 / SSE 流式(转换 + 透传 + 501)/ 埋点 / 预热 / mock / 冒烟,模块与 Python 版一一对应
- **测试镜像**:56 项离线单测(node:test)+ 25 项端到端冒烟,用例与 Python 版逐条对应;strict + erasableSyntaxOnly 类型检查全过;运行零第三方依赖(Node 22 类型擦除,无需编译),开发依赖仅 typescript
- 差异清单(有意为之,见 ts/README.md):不读系统代理(恒直连 ≡ Python 版 PB_DIRECT=1)、node:sqlite 实验警告、property 测试 PRNG 换 mulberry32(性质不变);webui / verify_cache / experiments 未移植(后续 PR)
- CI 加 `unittest-ts` job(typecheck + 单测 + 冒烟);Python job 测试数口径同步 56/25
- Python 版保留为参考实现;docs/ 契约与实验文档对两个实现同样适用
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ OpenAI Chat / OpenAI Response ↔ Anthropic 的协议转换层(网关/代理 +
| TRACK04 边界参数对齐稿 | ✅ 已对齐 | `docs/TRACK04_签字确认稿.md`(single_task 等三参数已定稿) |
| 弹网页(本地仪表盘) | ✅ 记忆编排界面 | `src/webui/` 安全骨架 + 记忆勾选/裁剪 + 缓存前缀可视化 |
| 已知限制(边界自觉) | 📋 12 项四要素(3 项已闭环留档) | `docs/LIMITATIONS.md`(含结论隔离证明) |
| **TypeScript 移植版** | ✅ **v2.0**(对齐上游技术栈) | `ts/`:56 项单测 + 25 项冒烟 + strict 类型检查全过(用例与 Python 版一一镜像),差异清单见 `ts/README.md` |

## 快速开始(离线)

Expand Down Expand Up @@ -104,6 +105,7 @@ experiments/ 三组对照 + rich_experiments.py(E20–E28)+ rich_experim
tests/ 离线单测(录制样例 + 自查回归 + 多轮链 E2E)
tools/ mock_backend + verify_cache + smoke_e2e + recordings
data/ 34 组实验原始数据(逐轮 jsonl + 汇总 csv,溯源表见 data/README.md)
ts/ TypeScript 全量移植(对齐上游 Node 22 + TS 技术栈,v2.0)
docs/ ir-schema.md / capability-matrix.md / experiment-results.md / LIMITATIONS.md / REVIEW.md / TRACK04_签字确认稿.md
CHANGELOG.md 按日期演进记录(v0 契约冻结 → v1.2 → 实验交付 → 覆盖补全)
```
46 changes: 46 additions & 0 deletions ts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# protocol-bridge · TypeScript 移植版

> 与上游 TencentDB-Agent-Memory(Node 22 / TypeScript 技术栈)对齐的全量移植。
> 功能对齐 Python 版 v1.7:**56 项离线单测 + 25 项端到端冒烟全过**(与 Python 版用例一一镜像)。
> Python 版保留在仓库根目录作参考实现;docs/ 下的契约、实验与取舍文档对两个实现同样适用。

## 快速开始

```bash
cd ts
npm install # 仅开发依赖(typescript + @types/node);运行零第三方依赖
npm test # 56 项离线单测(node:test + Node 22 类型擦除,无需编译)
npm run smoke # 25 项端到端冒烟:真起 mock + 网关两个子进程
npm run typecheck # tsc --noEmit(strict + erasableSyntaxOnly)
npm run gateway # 起网关(127.0.0.1:8080,转发到 PB_BACKEND,默认 mock)
npm run mock # 起 mock backend(127.0.0.1:9100)
```

环境要求:**Node.js ≥ 22.18**(type stripping 默认可用 + `node:sqlite` 无 flag)。
环境变量与 Python 版一致:`PB_BACKEND` / `PB_API_KEY` / `PB_PORT` / `PB_METRICS` / `PB_DB` / `PB_CONCURRENCY` / `PB_ANTHROPIC_VERSION` / `PB_CHAT_PATH` / `PB_RESPONSE_PATH` / `PB_ANTHROPIC_PATH`。

## 目录映射(Python → TS)

| Python | TypeScript | 说明 |
| --- | --- | --- |
| `src/ir/model.py` | `src/ir/model.ts` | IR 三层契约(Block/Message/IRRequest/IRUsage/SessionContext) |
| `src/adapters/*.py` | `src/adapters/*.ts` | 三对 adapter + 降级记录 + `assistantFromUpstream` |
| `src/state/*.py` | `src/state/*.ts` | SessionStore(node:sqlite)/ ToolIdMap / SessionConfig |
| `src/gateway/server.py` | `src/gateway/server.ts` | 网关主进程(convert / finalizeTurn / 两条 SSE 路径 / 501) |
| `src/gateway/sse.py` | `src/gateway/sse.ts` | SSE 分帧 + Anthropic→Chat 转换 + Chat 旁路收集 |
| `src/observability/metrics.py` | `src/observability/metrics.ts` | 5 项埋点 + hitRate |
| `src/warmup/prewarm.py` | `src/warmup/prewarm.ts` | max_tokens:0 预热 |
| `tools/mock_backend.py` | `tools/mock_backend.ts` | 三协议形状 + 有状态缓存模拟 + SSE |
| `tools/smoke_e2e.py` | `tools/smoke_e2e.ts` | 7 组 25 项断言 |
| `tests/test_offline.py`(56 项) | `tests/offline.test.ts`(56 项) | 用例一一镜像 |

## 差异清单(有意为之,均不影响语义)

1. **不读系统代理**:Node 的 `http.request` 不读 `http_proxy` 环境变量,恒直连——与 Python 版 `PB_DIRECT=1` 的行为一致;`PB_DIRECT` 保留兼容但为 no-op。
2. **SQLite 用 `node:sqlite`**(Node 内置,experimental warning 属正常):表结构、SQL、惰性淘汰语义与 Python 版完全一致;单线程同步驱动,Python 版 `threading.Lock` 与「锁外调用防死锁」的注释在 TS 版天然不适用。
3. **property 测试 PRNG**:Python 用 `random.Random`(Mersenne Twister),TS 用 mulberry32——种子固定、序列不同,断言的四条性质(断点 [3,4] / 未知字段必降级 / 工具参数守恒 / signature 守恒)完全一致。
4. **未移植**(后续 PR 补):`src/webui/`(弹网页仪表盘)、`tools/verify_cache.py`(验站脚本)、`experiments/`(真实 API 实验脚本——研究代码,跟随 Python 版即可)。

## 与上游 MemoryProxy 的关系

本目录按上游技术栈(Node 22 + TypeScript + 零重型依赖)组织,模块边界与上游 `MemoryProxy/src/` 的 handler/adapter 结构对应——断点布局(3 固定 + 1 滚动)、usage 归一、命中率埋点、状态层重放均可整体或按模块迁入。34 组实验的量化结论见根目录 `docs/experiment-results.md`。
12 changes: 12 additions & 0 deletions ts/config/session.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"_comment": "Session 边界可切换配置(机器读)。等价于执行方案 3.9.1 的 session.yaml;因离线环境不装 pyyaml,统一用 JSON。改配置即可切换重放策略,不动 adapter 与实验脚本。",
"session": {
"key_granularity": "single_task",
"key_fields": ["task-id"],
"replay_from": "full",
"sliding_window_n": 20,
"end_policy": "ttl",
"ttl_seconds": 1800,
"on_end": "archive"
}
}
50 changes: 50 additions & 0 deletions ts/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions ts/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "protocol-bridge-ts",
"version": "2.0.0",
"private": true,
"description": "OpenAI Chat / Responses 与 Anthropic 协议转换层(TypeScript 移植版,对应 Python v1.7)",
"type": "module",
"engines": {
"node": ">=22.18"
},
"scripts": {
"test": "node --test \"tests/*.test.ts\"",
"smoke": "node tools/smoke_e2e.ts",
"typecheck": "tsc --noEmit",
"gateway": "node src/gateway/server.ts",
"mock": "node tools/mock_backend.ts"
},
"devDependencies": {
"@types/node": "^22.10.0",
"typescript": "^5.7.0"
}
}
Loading
Loading