Skip to content

feat(web): 每条持久化回复显示生成它的模型(补齐 #470 切换模型后的归属缺口) - #510

Merged
Qiyuanqiii merged 5 commits into
mainfrom
Qiyuanqiii/reply-model-attribution
Oct 28, 2026
Merged

Qiyuanqiii merged 5 commits into
mainfrom
Qiyuanqiii/reply-model-attribution

Conversation

@Qiyuanqiii

Copy link
Copy Markdown
Member

What this adds

对照 Claude Code(verbose 中的逐轮模型标识)与 Codex(任务 UI 的模型归属):#470 使模型可中途切换后,transcript 无法回答"这条回复来自哪个模型"。本 PR 把模型调用事件已有的身份(modelIdentity():provider / model)作为只读字段穿透四层,并在 web 叙事中显示。

层 变更
internal/runactivity Item.Model:ModelStarted/ModelCompleted 投影提取 attempt_id 与 provider / model(均已在负载中,无新数据源)
internal/threadtranscript Item.Model 透传;构建循环按 run 跟踪最近模型调用 attempt,最终持久回复(SessionMessageEvent)继承之(回复负载本身无 attempt)
internal/httpapi ThreadTranscriptItemView.Model(omitempty,只读投影)
web 叙事投影按 attempt 关联(modelByAttempt),持久化回复操作行显示灰字徽标;流式行不显示;合并的重复收尾回复首见优先、缺失回填

设计边界

  • 纯只读字段 + 展示徽标:无新写路径、无权限/审批/工具语义变化。
  • 不猜测:无 attempt 记录的历史项不显示徽标。
  • docs/openapi.json 与 web/src/api/schema.d.ts 为 go run ./cmd/cyberagent api openapi 纯再生成产物。

Review findings and resolutions(OCR delegate 模式)

按仓库惯例执行 OCR delegate 复审(ocr delegate preview/rule + 独立评审子代理全文核验),4 项发现全部修复,明细见 docs/reviews/reply-model-attribution/README.md:

发现 处理
JOIN-001,P1:首版只设 Model 未提取 attempt_id,真实数据下 web join 永不命中(首版测试手写键掩盖断链) 两个模型调用 case 提取 attempt_id;新增以真实负载形状为基准的 Go 贯穿测试
REPLY-001,P2:最终回复经 SessionMessageEvent 投影无 attempt,主表面无效 transcript 构建循环按 run 继承最近模型调用 attempt,零 store 负载改动;贯穿测试断言 reply 继承
PERF-001,P3:modelIdentity 重复反序列化 提升为单次调用;completedModelDetail 改收 identity 参数
MERGE-001,P3:合并回复丢失后续 model previous.model ??= model

Validation

  • Go:go test ./internal/runactivity/ ./internal/threadtranscript/ ./internal/httpapi/ 全过(含真实负载形状的贯穿测试);go build ./...、go vet 通过。
  • Web:投影+组件 38/38(含关联、空 attempt 不挂徽标、流式不显示);npm test 全量通过;tsc -b 零错误。

Related

AI assistance and contributor review

  • Implementation and review-fix application assisted by Claude (ZCode); commits authored by Claude <claude@users.noreply.github.com>.
  • Contributor self-review of all modified files, test suites, and review records is complete.

claude and others added 5 commits October 28, 2026 22:35
Model switching became a first-class mid-thread action (#470), but the
transcript never says which model produced which reply. Thread the model
identity (provider / model, already recorded on model-call events) through
the activity projection, the thread transcript, and the HTTP view as an
additive omitempty field, then join it per attempt in the web narrative
projection and show it as muted attribution on durable replies.

No behavior change beyond the new read-only field and badge; live streaming
entries stay unattributed until their call record is durable.
P1: extract attempt_id on the model-call projection (started and completed)
so the transcript items actually carry the join key the web projection needs
- the first cut set model only, which real data would never join.
P2: durable replies are SessionMessageEvent projections without attempt
identity; the thread transcript builder now tracks each run's latest
model-call attempt and the reply inherits it, so the primary surface is
attributed without touching store payloads.
P3: hoist the duplicated modelIdentity call (completedModelDetail now takes
the identity) and backfill a merged reply's missing model from its later
sibling.
…rotocol registry

- Add 'model' to parseThreadTranscriptItem optional allowlist and validate bounded text in transcripts.ts.
- Synchronize docs/convergence/protocol-registry.md with protocol registry.
- Provide explicit setup timeout for agent browser journey on macOS desktop runners.

Validation:
- npm run check:api passed (16 required, 24 optional keys matched).
- npm run typecheck passed (0 errors).
- go run ./cmd/protocolregistry -check passed (synchronized).
- go test ./internal/runactivity/ ./internal/threadtranscript/ ./internal/httpapi/ passed.
…d ambiguous fallback

- Runactivity: populate ModelAttempt and ToolRound on ModelStartedEvent, ModelCompletedEvent, and ModelFailedEvent.
- Threadtranscript: track producing model call per modelAttempt so durable SessionMessageEvent replies inherit the concluding model identity and attempt number.
- Threadtranscript tests: add multi-model-attempt supervisor retry sequence test and multi-turn regression tests.
- Narrative: strictly guard fallback attribution by bare attempt_id to only resolve when the attempt uniquely maps to exactly one model identity. Ambiguous attempts without explicit model attribution omit the badge instead of guessing.
- Narrative tests: add test coverage for ambiguous attempt fallback omission and single-model attempt fallback resolution.
@Qiyuanqiii
Qiyuanqiii force-pushed the Qiyuanqiii/reply-model-attribution branch from 51311c6 to d7a75f6 Compare October 28, 2026 14:37
@Qiyuanqiii
Qiyuanqiii merged commit efdba19 into main Oct 28, 2026
23 checks passed
@Qiyuanqiii
Qiyuanqiii deleted the Qiyuanqiii/reply-model-attribution branch October 28, 2026 17:45
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