refactor(conversion): body_rules 保护 cache-sensitive 字段,normalizer 保真优化与诊断日志

- RequestBuilder 新增 protected_body_keys 机制,按 provider API 格式阻止 body_rules
  改写 prompt cache 相关的顶层请求字段(messages/tools/system 等)
- Claude/Gemini normalizer 优先复用原始 raw tool_choice,避免 round-trip 丢失信息
- Claude normalizer 修复 content blocks 输出顺序(flush_text_parts),
  _coerce_claude_message_sequence 返回结构化诊断
- OpenAI normalizer 保留 raw tool call arguments 字符串与原始 tool 定义 extra 字段
- schema_utils allOf 合并保持 required 字段插入顺序
- 各转换环节增加结构化 debug 日志用于调试
This commit is contained in:
fawney19
2026-03-17 01:25:29 +08:00
parent c97c9332eb
commit 4ecaefbade
22 changed files with 1191 additions and 115 deletions

View File

@@ -1,6 +1,7 @@
from __future__ import annotations
from types import SimpleNamespace
from typing import cast
import jwt
import pytest
@@ -12,6 +13,7 @@ from src.services.provider.adapters.codex.request_patching import (
maybe_patch_request_for_codex,
patch_openai_cli_request_for_codex,
)
from src.services.provider.envelope import ProviderEnvelope
def test_patch_openai_cli_request_for_codex_is_passthrough_except_internal_sentinel() -> None:
@@ -146,6 +148,21 @@ def test_openai_cli_normalizer_request_from_internal_codex_variant_defaults_stor
assert out["store"] is False
def test_openai_cli_normalizer_codex_variant_keeps_instructions_missing_for_default_rule() -> None:
from src.api.handlers.base.request_builder import apply_body_rules
from src.core.api_format.conversion.normalizers.openai_cli import OpenAICliNormalizer
from src.core.api_format.metadata import CODEX_DEFAULT_BODY_RULES
normalizer = OpenAICliNormalizer()
internal = normalizer.request_to_internal({"model": "gpt-test", "input": []})
out = normalizer.request_from_internal(internal, target_variant="codex")
assert "instructions" not in out
patched = apply_body_rules(out, list(CODEX_DEFAULT_BODY_RULES))
assert patched["instructions"] == "You are GPT-5."
def test_codex_envelope_extra_headers_does_not_inject_synthetic_headers() -> None:
from src.services.provider.adapters.codex.envelope import codex_oauth_envelope
@@ -233,7 +250,7 @@ def test_codex_passthrough_builder_preserves_real_codex_headers() -> None:
endpoint=endpoint,
key=key,
pre_computed_auth=("Authorization", "Bearer upstream-token"),
envelope=codex_oauth_envelope,
envelope=cast(ProviderEnvelope, codex_oauth_envelope),
)
assert headers["accept"] == "text/event-stream"