fix(cli): 统一 CLI handler pending usage 的 api_format 来源

CLI stream/sync mixin 中创建 pending usage 记录时,api_format 取值
来源不一致(部分用 FORMAT_ID,部分用 allowed_api_formats[0])。
新增 primary_api_format 属性并统一使用,确保 pending 记录的格式
与实际客户端请求格式一致。
This commit is contained in:
fawney19
2026-03-02 15:13:28 +08:00
parent 310355bcc1
commit 5f1c74aca0
4 changed files with 80 additions and 4 deletions

View File

@@ -49,6 +49,7 @@ class CliHandlerProtocol(Protocol):
user_agent: str user_agent: str
start_time: float start_time: float
allowed_api_formats: list[str] allowed_api_formats: list[str]
primary_api_format: str
redis: Redis # type: ignore[type-arg] redis: Redis # type: ignore[type-arg]
telemetry: MessageTelemetry telemetry: MessageTelemetry
perf_metrics: dict[str, Any] | None perf_metrics: dict[str, Any] | None

View File

@@ -99,13 +99,14 @@ class CliStreamMixin:
# 使用子类实现的方法提取 model不同 API 格式的 model 位置不同) # 使用子类实现的方法提取 model不同 API 格式的 model 位置不同)
# 注意:使用 original_request_body因为整流只修改 messages不影响 model 字段 # 注意:使用 original_request_body因为整流只修改 messages不影响 model 字段
model = self.extract_model_from_request(original_request_body, path_params) model = self.extract_model_from_request(original_request_body, path_params)
client_api_format = self.primary_api_format
# 提前创建 pending 记录,让前端可以立即看到"处理中" # 提前创建 pending 记录,让前端可以立即看到"处理中"
self._create_pending_usage( self._create_pending_usage(
model=model, model=model,
is_stream=True, is_stream=True,
request_type="chat", request_type="chat",
api_format=self.FORMAT_ID, api_format=client_api_format,
request_headers=original_headers, request_headers=original_headers,
request_body=original_request_body, request_body=original_request_body,
) )
@@ -113,7 +114,7 @@ class CliStreamMixin:
# 创建流上下文 # 创建流上下文
ctx = StreamContext( ctx = StreamContext(
model=model, model=model,
api_format=self.allowed_api_formats[0], api_format=client_api_format,
api_family=self.api_family, api_family=self.api_family,
endpoint_kind=self.endpoint_kind, endpoint_kind=self.endpoint_kind,
request_id=self.request_id, request_id=self.request_id,

View File

@@ -78,7 +78,7 @@ class CliSyncMixin:
# 使用子类实现的方法提取 model不同 API 格式的 model 位置不同) # 使用子类实现的方法提取 model不同 API 格式的 model 位置不同)
model = self.extract_model_from_request(original_request_body, path_params) model = self.extract_model_from_request(original_request_body, path_params)
api_format = self.allowed_api_formats[0] api_format = self.primary_api_format
sync_start_time = time.time() sync_start_time = time.time()
# 提前创建 pending 记录,让前端可以立即看到"处理中" # 提前创建 pending 记录,让前端可以立即看到"处理中"
@@ -86,7 +86,7 @@ class CliSyncMixin:
model=model, model=model,
is_stream=False, is_stream=False,
request_type="chat", request_type="chat",
api_format=self.FORMAT_ID, api_format=api_format,
request_headers=original_headers, request_headers=original_headers,
request_body=original_request_body, request_body=original_request_body,
) )

View File

@@ -0,0 +1,74 @@
from __future__ import annotations
import pytest
from src.api.handlers.base.cli_stream_mixin import CliStreamMixin
from src.api.handlers.base.cli_sync_mixin import CliSyncMixin
class _StopExecution(Exception):
pass
class _DummySyncHandler(CliSyncMixin):
FORMAT_ID = "openai:cli"
def __init__(self) -> None:
self.allowed_api_formats = ["openai:compact"]
self.primary_api_format = "openai:compact"
self.pending_calls: list[dict[str, object]] = []
def extract_model_from_request(
self, request_body: dict[str, object], path_params: dict[str, object] | None
) -> str:
return str(request_body.get("model") or "unknown")
def _create_pending_usage(self, **kwargs: object) -> None:
self.pending_calls.append(kwargs)
raise _StopExecution()
class _DummyStreamHandler(CliStreamMixin):
FORMAT_ID = "openai:cli"
def __init__(self) -> None:
self.allowed_api_formats = ["openai:compact"]
self.primary_api_format = "openai:compact"
self.pending_calls: list[dict[str, object]] = []
def extract_model_from_request(
self, request_body: dict[str, object], path_params: dict[str, object] | None
) -> str:
return str(request_body.get("model") or "unknown")
def _create_pending_usage(self, **kwargs: object) -> None:
self.pending_calls.append(kwargs)
raise _StopExecution()
@pytest.mark.asyncio
async def test_sync_pending_usage_uses_primary_api_format() -> None:
handler = _DummySyncHandler()
with pytest.raises(_StopExecution):
await handler.process_sync( # type: ignore[misc]
original_request_body={"model": "gpt-5.3-codex"},
original_headers={},
)
assert handler.pending_calls
assert handler.pending_calls[0]["api_format"] == "openai:compact"
@pytest.mark.asyncio
async def test_stream_pending_usage_uses_primary_api_format() -> None:
handler = _DummyStreamHandler()
with pytest.raises(_StopExecution):
await handler.process_stream( # type: ignore[misc]
original_request_body={"model": "gpt-5.3-codex"},
original_headers={},
)
assert handler.pending_calls
assert handler.pending_calls[0]["api_format"] == "openai:compact"