mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 17:30:23 +08:00
feat: 视频计费增强与影子计费系统
This commit is contained in:
@@ -100,10 +100,7 @@ class StreamTelemetryRecorder:
|
||||
return
|
||||
actual_request_body = ctx.provider_request_body or original_request_body
|
||||
response_body = None
|
||||
if (
|
||||
not isinstance(writer, QueueTelemetryWriter)
|
||||
or config.usage_queue_include_bodies
|
||||
):
|
||||
if not isinstance(writer, QueueTelemetryWriter) or writer.include_bodies:
|
||||
response_body = ctx.build_response_body(response_time_ms)
|
||||
|
||||
try:
|
||||
@@ -403,10 +400,25 @@ class StreamTelemetryRecorder:
|
||||
self, bg_db: Session, ctx: StreamContext, response_time_ms: int
|
||||
) -> TelemetryWriter | None:
|
||||
if config.usage_queue_enabled and self.user_id and self.api_key_id:
|
||||
from src.services.system.config import SystemConfigService
|
||||
|
||||
# Queue payload detail follows system config request_record_level.
|
||||
log_level = SystemConfigService.get_request_record_level(bg_db).value
|
||||
sensitive_headers = SystemConfigService.get_sensitive_headers(bg_db) or []
|
||||
max_request_body_size = int(
|
||||
SystemConfigService.get_config(bg_db, "max_request_body_size", 5242880) or 0
|
||||
)
|
||||
max_response_body_size = int(
|
||||
SystemConfigService.get_config(bg_db, "max_response_body_size", 5242880) or 0
|
||||
)
|
||||
return QueueTelemetryWriter(
|
||||
request_id=self.request_id,
|
||||
user_id=self.user_id,
|
||||
api_key_id=self.api_key_id,
|
||||
log_level=log_level,
|
||||
sensitive_headers=sensitive_headers,
|
||||
max_request_body_size=max_request_body_size,
|
||||
max_response_body_size=max_response_body_size,
|
||||
)
|
||||
db_writer = self._build_db_writer(bg_db)
|
||||
if db_writer is None:
|
||||
|
||||
@@ -75,6 +75,17 @@ class GeminiVeoHandler(VideoHandlerBase):
|
||||
)
|
||||
self._normalizer = GeminiNormalizer()
|
||||
|
||||
@staticmethod
|
||||
def _get_request_base_url(http_request: Request) -> str:
|
||||
"""从 HTTP 请求中获取基础 URL(协议 + 主机)"""
|
||||
# 优先使用 X-Forwarded-Proto 和 X-Forwarded-Host(代理场景)
|
||||
proto = http_request.headers.get("x-forwarded-proto") or http_request.url.scheme
|
||||
host = http_request.headers.get("x-forwarded-host") or http_request.headers.get("host")
|
||||
if host:
|
||||
return f"{proto}://{host}"
|
||||
# 回退到 request.url
|
||||
return f"{http_request.url.scheme}://{http_request.url.netloc}"
|
||||
|
||||
async def handle_create_task(
|
||||
self,
|
||||
*,
|
||||
@@ -259,7 +270,8 @@ class GeminiVeoHandler(VideoHandlerBase):
|
||||
created_at=task.created_at,
|
||||
original_request=internal_request,
|
||||
)
|
||||
response_body = self._normalizer.video_task_from_internal(internal_task)
|
||||
base_url = self._get_request_base_url(http_request)
|
||||
response_body = self._normalizer.video_task_from_internal(internal_task, base_url=base_url)
|
||||
|
||||
# 提交成功后立即结算 Usage(费用暂时为 0,轮询完成后更新)
|
||||
response_time_ms = int((time.time() - self.start_time) * 1000)
|
||||
@@ -313,7 +325,8 @@ class GeminiVeoHandler(VideoHandlerBase):
|
||||
|
||||
# 直接从数据库返回任务状态(后台轮询服务会持续更新状态)
|
||||
internal_task = self._task_to_internal(task)
|
||||
response_body = self._normalizer.video_task_from_internal(internal_task)
|
||||
base_url = self._get_request_base_url(http_request)
|
||||
response_body = self._normalizer.video_task_from_internal(internal_task, base_url=base_url)
|
||||
return JSONResponse(response_body)
|
||||
|
||||
async def handle_list_tasks(
|
||||
@@ -331,8 +344,10 @@ class GeminiVeoHandler(VideoHandlerBase):
|
||||
.limit(100)
|
||||
.all()
|
||||
)
|
||||
base_url = self._get_request_base_url(http_request)
|
||||
items = [
|
||||
self._normalizer.video_task_from_internal(self._task_to_internal(t)) for t in tasks
|
||||
self._normalizer.video_task_from_internal(self._task_to_internal(t), base_url=base_url)
|
||||
for t in tasks
|
||||
]
|
||||
return JSONResponse({"operations": items})
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ from datetime import datetime, timedelta, timezone
|
||||
from typing import Any, AsyncIterator
|
||||
from uuid import uuid4
|
||||
|
||||
import httpx
|
||||
from fastapi import HTTPException, Request
|
||||
from fastapi.responses import JSONResponse, Response, StreamingResponse
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
@@ -650,8 +651,10 @@ class OpenAIVideoHandler(VideoHandlerBase):
|
||||
try:
|
||||
# 使用 httpx 的 stream 方法并正确管理上下文
|
||||
# 视频下载可能较大,设置 5 分钟超时
|
||||
request = client.build_request("GET", upstream_url, headers=headers)
|
||||
response = await client.send(request, stream=True, timeout=300.0)
|
||||
request = client.build_request(
|
||||
"GET", upstream_url, headers=headers, timeout=httpx.Timeout(300.0)
|
||||
)
|
||||
response = await client.send(request, stream=True)
|
||||
except Exception as exc:
|
||||
logger.warning(
|
||||
"[VideoDownload] Upstream connection failed task={} url={}: {}",
|
||||
@@ -723,8 +726,8 @@ class OpenAIVideoHandler(VideoHandlerBase):
|
||||
"""代理直接的视频 URL(如 CDN URL),保持与官方 API 一致的流式返回行为"""
|
||||
client = await HTTPClientPool.get_default_client_async()
|
||||
try:
|
||||
request = client.build_request("GET", url)
|
||||
response = await client.send(request, stream=True, timeout=300.0)
|
||||
request = client.build_request("GET", url, timeout=httpx.Timeout(300.0))
|
||||
response = await client.send(request, stream=True)
|
||||
except Exception as exc:
|
||||
logger.warning(
|
||||
"[VideoDownload] Direct URL connection failed task={} url={}: {}",
|
||||
|
||||
Reference in New Issue
Block a user