feat: 接入模型动态 Fallback 与调用审计

This commit is contained in:
hh
2026-07-21 10:35:50 +08:00
parent 9429133d04
commit d0c3690d47
45 changed files with 9253 additions and 173 deletions
+33 -9
View File
@@ -59,7 +59,14 @@ class VolcanoArkProvider:
payload=data,
)
def chat_completion(self, *, model: str, messages: list[dict[str, str]], endpoint: str = "chat/completions") -> dict[str, Any]:
def chat_completion(
self,
*,
model: str,
messages: list[dict[str, str]],
endpoint: str = "chat/completions",
timeout: float = 120,
) -> dict[str, Any]:
if not self.api_key:
raise ValueError("VOLCANO_ARK_API_KEY is not configured")
@@ -67,7 +74,7 @@ class VolcanoArkProvider:
f"{self.base_url.rstrip('/')}/{endpoint.lstrip('/')}",
headers={"Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json"},
json={"model": model, "messages": messages},
timeout=120,
timeout=timeout,
)
response.raise_for_status()
return response.json()
@@ -80,6 +87,7 @@ class VolcanoArkProvider:
endpoint: str = "chat/completions",
temperature: float = 0.8,
extra_body: dict[str, Any] | None = None,
timeout: float = 300,
) -> Iterator[dict[str, Any]]:
"""流式对话:逐块 yield {type:'delta'|'tool_call'|'done', ...}。
OpenAI 兼容 SSE(火山 ARK / 各中转站同构),供脚本 agent 的 SSE 端实时转发。"""
@@ -97,7 +105,7 @@ class VolcanoArkProvider:
},
json=body,
stream=True,
timeout=300,
timeout=timeout,
) as response:
response.raise_for_status()
# SSE 响应常不带 charset,requests 会按 latin-1 解码 → 中文乱码。强制 UTF-8。
@@ -157,6 +165,7 @@ class VolcanoArkProvider:
endpoint: str = "images/generations",
image: str | list[str] | None = None,
size: str = "2K",
timeout: float = 180,
) -> dict[str, Any]:
if not self.api_key:
raise ValueError("VOLCANO_ARK_API_KEY is not configured")
@@ -174,7 +183,7 @@ class VolcanoArkProvider:
f"{self.base_url.rstrip('/')}/{endpoint.lstrip('/')}",
headers={"Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json"},
json=body,
timeout=180,
timeout=timeout,
)
# 非 2xx 抽出火山真实报错(error.code/message)而非裸 HTTP 400 —— 否则 Seedream 的
# 内容审核 / 尺寸过小(历史 4:5 套图 raise_for_status 吞 body 的踩坑)等真因看不见。
@@ -195,6 +204,7 @@ class VolcanoArkProvider:
content_items: list[dict[str, Any]] | None = None,
seed: int | None = None,
search_mode: str = "off",
timeout: float = 120,
) -> dict[str, Any]:
if not self.api_key:
raise ValueError("VOLCANO_ARK_API_KEY is not configured")
@@ -223,18 +233,24 @@ class VolcanoArkProvider:
f"{self.base_url.rstrip('/')}/{endpoint.lstrip('/')}",
headers={"Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json"},
json=body,
timeout=120,
timeout=timeout,
)
_raise_volcano_error(response)
return response.json()
def poll_video_task(self, *, endpoint: str, provider_task_id: str) -> dict[str, Any]:
def poll_video_task(
self,
*,
endpoint: str,
provider_task_id: str,
timeout: float = 60,
) -> dict[str, Any]:
if not self.api_key:
raise ValueError("VOLCANO_ARK_API_KEY is not configured")
response = requests.get(
f"{self.base_url.rstrip('/')}/{endpoint.rstrip('/')}/{provider_task_id}",
headers={"Authorization": f"Bearer {self.api_key}"},
timeout=60,
timeout=timeout,
)
_raise_volcano_error(response)
return response.json()
@@ -305,7 +321,15 @@ class VolcanoTtsProvider:
def configured(self) -> bool:
return bool(self.appid and self.access_token)
def synthesize(self, *, text: str, voice_type: str, speed_ratio: float = 1.0, uid: str = "airshelf") -> tuple[bytes, int]:
def synthesize(
self,
*,
text: str,
voice_type: str,
speed_ratio: float = 1.0,
uid: str = "airshelf",
timeout: float = 60,
) -> tuple[bytes, int]:
"""合成一段语音。返回 (mp3 字节, 时长毫秒;接口没回时长则为 0)。"""
if not self.configured:
raise TtsNotConfigured(
@@ -322,7 +346,7 @@ class VolcanoTtsProvider:
self.base_url,
headers={"Authorization": f"Bearer;{self.access_token}"},
json=body,
timeout=60,
timeout=timeout,
)
response.raise_for_status()
data = response.json()