feat: 动态 block 索引分配、OAuth 自动启停联动与 Antigravity 签名注入增强

- Gemini/OpenAI normalizer 改为按实际出现顺序延迟分配 thinking/text block 索引
- OAuth 失效标记时自动停用 Key,清除/刷新成功时自动启用
- Antigravity thought signature 注入支持 tool-specific 签名优先,再回退 session 级签名
- Claude normalizer 对非字符串 thinking block 降级为 UnknownBlock
- Gemini normalizer 反序列化时为 Antigravity 目标补充 signature
- signature_cache _prune 增加超限驱逐
- model/provider 包 __init__ 改为惰性导入避免循环依赖
- build_antigravity_url 复制 query_params 防止修改调用方原始字典
- upstream_fetcher build_all_format_configs 兼容非 EndpointFetchConfig 对象
- Antigravity HTTP 状态判定扩展与 Gemini endpoint check 支持 v1internal
- 新增 thought signature 注入与 signature cache 驱逐测试

Closes #165

Co-authored-by: AAEE86 <ppk0227@hotmail.com>
This commit is contained in:
fawney19
2026-02-10 17:53:04 +08:00
parent 0bb17a1502
commit 8949ad6d9e
19 changed files with 467 additions and 87 deletions

View File

@@ -204,3 +204,63 @@ async def test_antigravity_forces_conversion_path_in_stream_with_prefetch() -> N
assert mock_convert.call_count >= 1
assert any(b"data: {}" in c for c in out)
def test_wrap_v1internal_request_injects_thought_signature_from_tool_cache() -> None:
from src.services.provider.adapters.antigravity.signature_cache import signature_cache
signature_cache.clear()
tool_id = "toolu_123"
sig = "a" * 60
signature_cache.cache_tool_signature(tool_id, sig)
gemini_request = {
"model": "gemini-2.0-flash",
"contents": [
{"role": "user", "parts": [{"text": "hi"}]},
{"role": "model", "parts": [{"function_call": {"name": "do", "id": tool_id}}]},
],
}
wrapped = wrap_v1internal_request(
gemini_request,
project_id="project-123",
model="gemini-2.0-flash",
)
model_turn = wrapped["request"]["contents"][1]
part = model_turn["parts"][0]
assert part["thoughtSignature"] == sig
def test_wrap_v1internal_request_injects_session_signature_when_tool_cache_missing() -> None:
from src.services.provider.adapters.antigravity.signature_cache import signature_cache
signature_cache.clear()
session_id = "sid-123"
sig = "b" * 60
signature_cache.cache_session_signature(session_id, sig, message_count=2)
gemini_request = {
"model": "gemini-2.0-flash",
"sessionId": session_id,
"contents": [
{"role": "user", "parts": [{"text": "hi"}]},
{
"role": "model",
"parts": [{"functionCall": {"name": "do", "id": "toolu_missing"}}],
},
],
}
wrapped = wrap_v1internal_request(
gemini_request,
project_id="project-123",
model="gemini-2.0-flash",
)
model_turn = wrapped["request"]["contents"][1]
part = model_turn["parts"][0]
assert part["thoughtSignature"] == sig