mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 17:30:23 +08:00
114 lines
3.5 KiB
Python
114 lines
3.5 KiB
Python
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
from typing import Any
|
||
|
|
|
||
|
|
from src.api.handlers.base.parsers import OpenAICliResponseParser, OpenAIResponseParser
|
||
|
|
from src.api.handlers.base.response_parser import (
|
||
|
|
ParsedChunk,
|
||
|
|
ParsedResponse,
|
||
|
|
ResponseParser,
|
||
|
|
StreamStats,
|
||
|
|
)
|
||
|
|
from src.api.handlers.base.stream_context import StreamContext
|
||
|
|
from src.api.handlers.base.stream_processor import StreamProcessor
|
||
|
|
|
||
|
|
|
||
|
|
class _DummyParser(ResponseParser):
|
||
|
|
def parse_sse_line(self, line: str, stats: StreamStats) -> ParsedChunk | None:
|
||
|
|
return None
|
||
|
|
|
||
|
|
def parse_response(self, response: dict[str, Any], status_code: int) -> ParsedResponse:
|
||
|
|
return ParsedResponse(raw_response=response, status_code=status_code)
|
||
|
|
|
||
|
|
def extract_usage_from_response(self, response: dict[str, Any]) -> dict[str, int]:
|
||
|
|
return {}
|
||
|
|
|
||
|
|
def extract_text_content(self, response: dict[str, Any]) -> str:
|
||
|
|
return ""
|
||
|
|
|
||
|
|
|
||
|
|
def test_openai_response_parser_extracts_cached_tokens_from_prompt_tokens_details() -> None:
|
||
|
|
parser = OpenAIResponseParser()
|
||
|
|
|
||
|
|
usage = parser.extract_usage_from_response(
|
||
|
|
{
|
||
|
|
"usage": {
|
||
|
|
"prompt_tokens": 120,
|
||
|
|
"completion_tokens": 18,
|
||
|
|
"prompt_tokens_details": {"cached_tokens": 96},
|
||
|
|
}
|
||
|
|
}
|
||
|
|
)
|
||
|
|
|
||
|
|
assert usage["input_tokens"] == 120
|
||
|
|
assert usage["output_tokens"] == 18
|
||
|
|
assert usage["cache_read_tokens"] == 96
|
||
|
|
|
||
|
|
|
||
|
|
def test_openai_cli_response_parser_extracts_cached_tokens_from_input_tokens_details() -> None:
|
||
|
|
parser = OpenAICliResponseParser()
|
||
|
|
|
||
|
|
usage = parser.extract_usage_from_response(
|
||
|
|
{
|
||
|
|
"type": "response.completed",
|
||
|
|
"response": {
|
||
|
|
"usage": {
|
||
|
|
"input_tokens": 2048,
|
||
|
|
"output_tokens": 128,
|
||
|
|
"input_tokens_details": {"cached_tokens": 1792},
|
||
|
|
}
|
||
|
|
},
|
||
|
|
}
|
||
|
|
)
|
||
|
|
|
||
|
|
assert usage["input_tokens"] == 2048
|
||
|
|
assert usage["output_tokens"] == 128
|
||
|
|
assert usage["cache_read_tokens"] == 1792
|
||
|
|
|
||
|
|
|
||
|
|
def test_stream_processor_extracts_cached_tokens_from_openai_cli_converted_event() -> None:
|
||
|
|
processor = StreamProcessor(request_id="req_test", default_parser=_DummyParser())
|
||
|
|
ctx = StreamContext(model="gpt-5", api_format="openai:chat")
|
||
|
|
|
||
|
|
processor._extract_usage_from_converted_event(
|
||
|
|
ctx,
|
||
|
|
{
|
||
|
|
"type": "response.completed",
|
||
|
|
"response": {
|
||
|
|
"usage": {
|
||
|
|
"input_tokens": 4096,
|
||
|
|
"output_tokens": 64,
|
||
|
|
"input_tokens_details": {"cached_tokens": 3584},
|
||
|
|
}
|
||
|
|
},
|
||
|
|
},
|
||
|
|
"response.completed",
|
||
|
|
)
|
||
|
|
|
||
|
|
assert ctx.input_tokens == 4096
|
||
|
|
assert ctx.output_tokens == 64
|
||
|
|
assert ctx.cached_tokens == 3584
|
||
|
|
|
||
|
|
|
||
|
|
def test_stream_processor_extracts_cached_tokens_from_openai_chat_converted_event() -> None:
|
||
|
|
processor = StreamProcessor(request_id="req_test", default_parser=_DummyParser())
|
||
|
|
ctx = StreamContext(model="gpt-5", api_format="openai:chat")
|
||
|
|
|
||
|
|
processor._extract_usage_from_converted_event(
|
||
|
|
ctx,
|
||
|
|
{
|
||
|
|
"object": "chat.completion.chunk",
|
||
|
|
"choices": [],
|
||
|
|
"usage": {
|
||
|
|
"prompt_tokens": 512,
|
||
|
|
"completion_tokens": 21,
|
||
|
|
"prompt_tokens_details": {"cached_tokens": 480},
|
||
|
|
},
|
||
|
|
},
|
||
|
|
"chat.completion.chunk",
|
||
|
|
)
|
||
|
|
|
||
|
|
assert ctx.input_tokens == 512
|
||
|
|
assert ctx.output_tokens == 21
|
||
|
|
assert ctx.cached_tokens == 480
|