Repository navigation
fix(runtime): harden UTF-8 continuations and stream timeouts - #1129
mergify[bot] merged 4 commits into
Conversation
XuPeng-SH
left a comment
There was a problem hiding this comment.
Independent manual deep review of ab6892b8dd3246d623f08068dee7900d7fc3064a (all four commits and all five changed files). No actionable regression found; recommend approval.
I traced the Server lifecycle/agentic-loop entrypoint through the real provider client, both text/reasoning continuation callers, SSE parsing and collection, and durable admission/settlement ownership. The UTF-8 guard preserves the longest valid overlap, the 512-byte search bound, and existing whitespace/full-replay/no-overlap behavior. Its comparison cost stays bounded by the existing scan, with output allocation proportional to the merged text.
The shared semantic-progress default reaches OpenAI-compatible, Anthropic and Bedrock. Positive configuration overrides still win. Meaningful text/reasoning and authorized advancing tool delivery retain their existing liveness rules; heartbeats do not extend the semantic or invocation-wide deadline. The default logical budget still leaves 290s of provider work plus a 10s settlement reserve; explicit execution/run budgets retain their existing ownership. Longer stalled waits retain provider/admission capacity and continue existing lease renewals, which is the operational cost of this policy change. Cancellation, delivery-unknown settlement, retry restrictions and resource-release ownership are preserved.
The lockfile changes only ctutils 0.4.3 → 0.4.4 and its checksum. I verified the downloaded package checksum and RUSTSEC-2026-0385's patched range; audit-ignore policy is unchanged.
Scope clarification: the actual merge-base is target 8cbabcce326e4cdb8f1f84cf5f5a37964ae242fd. The head tree exactly equals Git's automatic merge of 630f7bf9 with that target, retaining #1120. I found no unintended reversal/omission. Please update the stale description claiming that all post-c048 main changes are excluded; this is nonblocking.
Independently executed validation:
- Extracted exact old/head continuation helpers: old 1 passed / 5 failed (including the byte-169 em-dash panic); head 6 passed, including 672,400 generated Unicode pairs, multibyte boundaries around 512, longest overlaps, whitespace and long full replays.
- Exact-source configuration getter: unset, positive/trimmed overrides, zero, negative, malformed and empty values behaved as expected.
- Rustfmt on all three changed Rust files and
git diff --checkpassed. cargo test --locked -p astra-runtime --features e2e-hooks --lib --test cache_provider_matrix_e2ewith focused output-cap/semantic-timeout/reasoning/cancellation filters: 31 unit tests + 1 real HTTP/Server/WAL test passed.- 30 additional existing tests passed from that same exact-head unit-test executable: Bedrock collectors, response-start versus stream/work deadlines, cancellation/admission/retry races, delivery-unknown settlement, user/session fairness and capacity release.
- With only
ASTRA_LLM_SEMANTIC_PROGRESS_TIMEOUT_S=120set in a disposable test subprocess, all three new timeout tests failed as expected, includingSemanticProgressTimeout { elapsed_ms: 120000 }before buffered delivery at 180s. This emulates the old timeout value; it is not a claim that I ran the entire old runtime. The unmodified head defaults passed all three.
Line delta (comments/blanks included within each category; no pure moves):
| Category | Added | Deleted | Net |
|---|---|---|---|
| Implementation | 8 | 9 | -1 |
| Configuration | 0 | 0 | 0 |
| Dependency lockfile | 2 | 2 | 0 |
| Tests | 206 | 5 | +201 |
| Documentation | 1 | 1 | 0 |
| Total | 217 | 17 | +200 |
Immediately before submission I revalidated: open/non-draft, same head/base, authenticated XuPeng-SH distinct from the author, and no existing reviews or inline comments. Exact-head checks (fully enumerated): 36 success, 2 skipped, 1 neutral, from GitHub Actions (App 15368) and Mergify (App 10562); no classic commit-status contexts were returned. Both accessible inherited-inclusive rulesets were read. Classic branch protection and required-context configuration remain unknown; the previously denied administration read was not retried. This approval is a code review, not certification of merge gates.
Limits: local validation used synthetic text/SSE and loopback HTTP with an in-memory ledger. I did not run paid/live models, production/shared database access, a multi-user load test, the full workspace suite, or a fresh cargo-audit scan. CI results and author-reported validation are distinct from the independently executed checks above.
Merge Queue Status
This pull request spent 37 seconds in the queue, with no time running CI. Required conditions to merge
|
Capability assessment: Unchanged: Fixes UTF-8 output-cap continuation merging and adjusts the existing stream semantic-progress timeout default; capability availability, runtime profiles, model output limits, and tool contracts are unchanged; also patches the existing ctutils dependency without changing capability contracts.
Summary
修复两类生产智能体运行失败:输出上限续写合并中的 UTF-8 panic,以及供应商缓冲长输出时被过短的语义进展阈值提前中断。
按请求,以
c048e2435150af663c632dc4098b636868b36635为精确代码基线;不带入该提交之后main的其他修改。merge_output_cap_continuation原先逐字节枚举重叠长度,并直接执行&continuation[..overlap]。中文、破折号、emoji 等多字节字符会让候选位置落在字符内部,触发not a char boundarypanic。现在仅在合法 UTF-8 字符边界比较重叠片段,保留最长重叠优先、512 字节扫描上限及原有拼接规则。正文和 reasoning 的两个调用点共用这一修复。
新增 4 个续写合并单元测试,覆盖与生产相同的
index 169/破折号位置、中文/emoji 去重、511/512/513 字节边界、无重叠、空文本和完整重放。复用现有 HTTP provider 回归测试,改用中文和破折号覆盖真实
finish_reason=length→ 续写请求 → 合并输出的 Server 执行路径。将既有
ASTRA_LLM_SEMANTIC_PROGRESS_TIMEOUT_S的默认值由 120 秒调整为 300 秒,容忍供应商暂时只发送 SSE 心跳、随后集中返回长工具参数的情况。OpenAI-compatible 和 Anthropic 共用该默认值;不改变任何解析或重试逻辑。保持单次逻辑模型调用总预算默认 300 秒以及原有收尾预留不变;默认 provider 工作窗口约 290 秒。心跳不刷新语义计时,不能延长总预算;显式配置的语义进展超时仍然优先生效。
新增默认值断言及两组虚拟时间回归,覆盖两个协议下的 180 秒心跳后完整工具参数交付,以及只有心跳时仍在 provider 工作预算耗尽后结束。更新配置参考中的实际配置名、默认值和预算边界。
仅更新
Cargo.lock中既有传递依赖ctutils 0.4.3 → 0.4.4的版本及校验和,修复 RUSTSEC-2026-0385 导致的 CI 安全扫描失败;没有连带更新其他 crate,也没有新增或扩大 advisory 忽略规则。Related issue
无独立 issue;来自生产故障排查。提交中的复现和回归仅使用合成文本,不包含生产对话、账号、供应商凭证或原始流数据。
Change type
User and compatibility impact
多字节文本续写不再因字符串切片 panic 而导致任务失败。ASCII、空文本、空白开头、完整重放和去重规则保持不变。
没有显式设置语义进展超时的部署会从 120 秒采用 300 秒默认值,减少缓冲输出造成的提前中断;真正停滞的请求也可能等待更久,但仍受现有总预算约束。这不等于在最后一个 token 后保证额外等待完整 5 分钟,也不保证供应商一定成功。已有超时覆盖值保持不变。
没有新增 fallback、重试、API、配置项、依赖或数据库 schema,不需要数据迁移;既有传递依赖
ctutils仅升级到安全修复版0.4.4;不把心跳当作有效回答或工具参数,不改变工具执行边界。Architecture and complexity delta
crates/runtime/src/server/server_loop_host.rs中既有的merge_output_cap_continuation,以及crates/runtime/src/turn/llm/client.rs中两个流式协议共用的语义进展超时默认值;不新建平行实现。crates/runtime/tests/cache_provider_matrix_e2e.rs、docs/reference/configuration.md,以及仅更新ctutils版本与校验和的Cargo.lock。Verification
c048e243…上,将原始合并函数与同一组回归测试单独编译运行,旧实现 3 项失败、2 项通过;复现end byte index 169 is not a char boundary; it is inside '—' (bytes 167..170 of string)。CARGO_BUILD_JOBS=2 CARGO_INCREMENTAL=0 cargo test --locked -p astra-runtime --features e2e-hooks --lib --test cache_provider_matrix_e2e output_cap_ -- --nocapture:18 项相关单测 + 1 项 HTTP provider 链路测试通过。cargo test --locked -p astra-runtime --features e2e-hooks --lib default_semantic_progress_timeout -- --nocapture,新增 3 项测试均按预期失败,捕捉 180 秒缓冲交付被SemanticProgressTimeout { elapsed_ms: 120000 }提前中断;调整为 300 秒后通过。CARGO_BUILD_JOBS=2 CARGO_INCREMENTAL=0 cargo test --locked -p astra-runtime --features e2e-hooks --lib --test cache_provider_matrix_e2e -- default_semantic_progress_timeout openai_reasoning_alias_ keepalives_after_progress user_cancel_wins_the_semantic_progress_deadline_race continuous_reasoning_cannot_bypass_provider_work_budget anthropic_reasoning_cannot_bypass_provider_work_budget reasoning_only_stream_uses_idle_watchdog whitespace_only_text_is_not_semantic_progress output_cap_ --nocapture:31 项单测 + 1 项 HTTP provider 链路测试通过。rustfmt --edition 2024 --check crates/runtime/src/turn/llm/client.rs crates/runtime/src/server/server_loop_host.rs crates/runtime/tests/cache_provider_matrix_e2e.rs:通过;git diff --check:通过。cargo update -p ctutils --precise 0.4.4,确认仅修改该 crate 的版本与校验和;cargo audit使用最新 RustSec 数据库通过,cargo audit --no-fetch --json确认 0 个未忽略漏洞,保留原有 4 条 allowed warnings,不修改.cargo/audit.toml。Test: MOI fresh-Astra compatibilityCI 使用隔离 MatrixOne 验证。run_agentic_loop_with_host+ 本地 OpenAI-compatible HTTP provider fixture;超时回归直接经过 OpenAI-compatible/Anthropic SSE parser 和流式 collector,不调用真实模型。Final checklist