fix: address completion verdict review findings

- fidelity cacheability also requires a non-empty projected body, so
  think-tag-only answers are no longer cached
- a no-argument tool sent with arguments null only is a deterministic
  dialect: ResultInvalid tool_arguments_null instead of retried
- text mode treats choice index null as 0 and no longer lets False pose
  as 0
- null-only tool_calls elements no longer produce an empty tool_calls
  list; non-stream fidelity checks role before the verdict; warning
  paths are length-bounded
- docs: ARCHITECTURE 7.8 field counts, README cache/watchdog wording,
  CHANGELOG notes, review record in the design, hook note in the plan

Red: 8 new tests failed before this change (e.g.
test_openai_cacheable_requires_projected_body,
test_null_arguments_tool_is_result_invalid_stream,
test_text_mode_choice_index_null_is_zero[None-True]).
This commit is contained in:
2026-10-15 00:22:07 -04:00
parent 31942f55f1
commit abcdf6930e
12 changed files with 179 additions and 27 deletions
+3 -1
View File
@@ -66,6 +66,7 @@ _THINK_PATTERN = re.compile(r"<think>(.*?)</think>", re.DOTALL)
# 方言告警节流上限与 finish 值截断(设计 §5.4): 键由上游决定,必须有界
_WARN_CAP = 1024
_FINISH_WARN_CHARS = 64
_PATH_WARN_CHARS = 128
# —— SSE 纯函数(VT llm.py 同款)——
@@ -845,7 +846,8 @@ class OpenAICompatTransport:
self, source: SourceConfig, conflicts: frozenset[str], finish_reason: str | None
) -> None:
"""方言告警: 冲突删键与未登记 finish 各按 (源, 路径/值) 只喊一次。"""
for path in sorted(conflicts):
for raw_path in sorted(conflicts):
path = raw_path[:_PATH_WARN_CHARS] # 键名由上游决定,长度同样须有界
self._warn_once(
source.name, f"field:{path}", f"字段 {path} 合并冲突已删除,保真结果缺该字段"
)