mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 17:30:23 +08:00
fix: 统一缓存token计费口径,避免OpenAI错误计费 (#144)
* fix: 统一缓存token计费口径,避免OpenAI错误计费 * fix: 添加 Gemini 缓存 token 归一化支持,优化代码结构 - Gemini 的 promptTokenCount 包含 cachedContentTokenCount,需要扣除 - 合并重复的 helper 函数为 _get_api_family() - 将 import 移到文件顶部 - 补充 Gemini 测试用例 --------- Co-authored-by: fawney19 <elky0401@gmail.com>
This commit is contained in:
@@ -126,16 +126,7 @@ class MessageTelemetry:
|
||||
# Provider 响应元数据(如 Gemini 的 modelVersion)
|
||||
response_metadata: dict[str, Any] | None = None,
|
||||
) -> float:
|
||||
total_cost = await self.calculate_cost(
|
||||
provider,
|
||||
model,
|
||||
input_tokens=input_tokens,
|
||||
output_tokens=output_tokens,
|
||||
cache_creation_tokens=cache_creation_tokens,
|
||||
cache_read_tokens=cache_read_tokens,
|
||||
)
|
||||
|
||||
await UsageService.record_usage(
|
||||
usage = await UsageService.record_usage(
|
||||
db=self.db,
|
||||
user=self.user,
|
||||
api_key=self.api_key,
|
||||
@@ -170,6 +161,8 @@ class MessageTelemetry:
|
||||
metadata=response_metadata,
|
||||
)
|
||||
|
||||
total_cost = float(getattr(usage, "total_cost_usd", 0.0) or 0.0)
|
||||
|
||||
if self.user and self.api_key:
|
||||
audit_service.log_api_request(
|
||||
db=self.db,
|
||||
@@ -181,8 +174,8 @@ class MessageTelemetry:
|
||||
success=True,
|
||||
ip_address=self.client_ip,
|
||||
status_code=status_code,
|
||||
input_tokens=input_tokens,
|
||||
output_tokens=output_tokens,
|
||||
input_tokens=getattr(usage, "input_tokens", input_tokens),
|
||||
output_tokens=getattr(usage, "output_tokens", output_tokens),
|
||||
cost_usd=total_cost,
|
||||
)
|
||||
|
||||
|
||||
@@ -1903,10 +1903,6 @@ class CliMessageHandlerBase(BaseMessageHandler):
|
||||
logger.warning(f"[{ctx.request_id}] 流式请求失败,未选中提供商")
|
||||
return
|
||||
|
||||
# Claude API 的 input_tokens 已经是非缓存部分,不需要再减去 cached_tokens
|
||||
# 实际计费的输入 tokens = input_tokens + cache_creation_tokens(缓存读取免费或折扣)
|
||||
actual_input_tokens = ctx.input_tokens
|
||||
|
||||
# 获取新的 DB session
|
||||
db_gen = get_db()
|
||||
bg_db = next(db_gen)
|
||||
@@ -1961,7 +1957,7 @@ class CliMessageHandlerBase(BaseMessageHandler):
|
||||
is_stream=True,
|
||||
api_format=ctx.api_format,
|
||||
provider_request_headers=ctx.provider_request_headers,
|
||||
input_tokens=actual_input_tokens,
|
||||
input_tokens=ctx.input_tokens,
|
||||
output_tokens=ctx.output_tokens,
|
||||
cache_creation_tokens=ctx.cache_creation_tokens,
|
||||
cache_read_tokens=ctx.cached_tokens,
|
||||
@@ -1975,7 +1971,7 @@ class CliMessageHandlerBase(BaseMessageHandler):
|
||||
logger.debug(f"{self.FORMAT_ID} 流式响应被客户端取消")
|
||||
logger.info(
|
||||
f"[CANCEL] {self.request_id[:8]} | {ctx.model} | {ctx.provider_name} | {response_time_ms}ms | "
|
||||
f"{ctx.status_code} | in:{actual_input_tokens} out:{ctx.output_tokens} cache:{ctx.cached_tokens}"
|
||||
f"{ctx.status_code} | in:{ctx.input_tokens} out:{ctx.output_tokens} cache:{ctx.cached_tokens}"
|
||||
)
|
||||
else:
|
||||
# 服务端/上游异常:记录为失败
|
||||
@@ -1991,7 +1987,7 @@ class CliMessageHandlerBase(BaseMessageHandler):
|
||||
api_format=ctx.api_format,
|
||||
provider_request_headers=ctx.provider_request_headers,
|
||||
# 预估 token 信息(来自 message_start 事件)
|
||||
input_tokens=actual_input_tokens,
|
||||
input_tokens=ctx.input_tokens,
|
||||
output_tokens=ctx.output_tokens,
|
||||
cache_creation_tokens=ctx.cache_creation_tokens,
|
||||
cache_read_tokens=ctx.cached_tokens,
|
||||
@@ -2007,7 +2003,7 @@ class CliMessageHandlerBase(BaseMessageHandler):
|
||||
logger.debug(f"{self.FORMAT_ID} 流式响应中断")
|
||||
logger.info(
|
||||
f"[FAIL] {self.request_id[:8]} | {ctx.model} | {ctx.provider_name} | {response_time_ms}ms | "
|
||||
f"{ctx.status_code} | in:{actual_input_tokens} out:{ctx.output_tokens} cache:{ctx.cached_tokens}"
|
||||
f"{ctx.status_code} | in:{ctx.input_tokens} out:{ctx.output_tokens} cache:{ctx.cached_tokens}"
|
||||
)
|
||||
else:
|
||||
# 在记录统计前,允许子类从 parsed_chunks 中提取额外的元数据
|
||||
@@ -2026,12 +2022,12 @@ class CliMessageHandlerBase(BaseMessageHandler):
|
||||
logger.debug(
|
||||
f"[{ctx.request_id}] 开始记录 Usage: "
|
||||
f"provider={ctx.provider_name}, model={ctx.model}, "
|
||||
f"in={actual_input_tokens}, out={ctx.output_tokens}"
|
||||
f"in={ctx.input_tokens}, out={ctx.output_tokens}"
|
||||
)
|
||||
total_cost = await bg_telemetry.record_success(
|
||||
provider=ctx.provider_name,
|
||||
model=ctx.model,
|
||||
input_tokens=actual_input_tokens,
|
||||
input_tokens=ctx.input_tokens,
|
||||
output_tokens=ctx.output_tokens,
|
||||
response_time_ms=response_time_ms,
|
||||
first_byte_time_ms=ctx.first_byte_time_ms, # 传递首字时间
|
||||
@@ -2492,8 +2488,6 @@ class CliMessageHandlerBase(BaseMessageHandler):
|
||||
output_tokens = usage.get("output_tokens", 0)
|
||||
cached_tokens = usage.get("cache_read_tokens", 0)
|
||||
cache_creation_tokens = usage.get("cache_creation_tokens", 0)
|
||||
# Claude API 的 input_tokens 已经是非缓存部分,不需要再减去 cached_tokens
|
||||
actual_input_tokens = input_tokens
|
||||
|
||||
output_text = self.parser.extract_text_content(response_json)[:200]
|
||||
|
||||
@@ -2507,7 +2501,7 @@ class CliMessageHandlerBase(BaseMessageHandler):
|
||||
total_cost = await self.telemetry.record_success(
|
||||
provider=provider_name,
|
||||
model=model,
|
||||
input_tokens=actual_input_tokens,
|
||||
input_tokens=input_tokens,
|
||||
output_tokens=output_tokens,
|
||||
response_time_ms=response_time_ms,
|
||||
status_code=status_code,
|
||||
|
||||
Reference in New Issue
Block a user