f3a8a38acd
web-platform-ci / Standalone decision service (no cloud credentials) (push) Has been cancelled
web-platform-ci / TypeScript, lint, unit, build (push) Has been cancelled
web-platform-ci / Playwright E2E (push) Has been cancelled
lekiwi-compatibility / cpu-compatibility (push) Has been cancelled
web-platform-ci / Standalone decision service (no cloud credentials) (pull_request) Has been cancelled
web-platform-ci / TypeScript, lint, unit, build (pull_request) Has been cancelled
web-platform-ci / Playwright E2E (pull_request) Has been cancelled
lekiwi-compatibility / cpu-compatibility (pull_request) Has been cancelled
集成同源 BYOK 会话隔离、精简模型设置、官方订阅入口和 HTTPS 发布运维;保留本地训练/调参与控制能力。同步 npm 版本及 CHANGELOG,记录公网真实 API 验收仍待用户凭据。
64 lines
2.1 KiB
Python
64 lines
2.1 KiB
Python
"""Bounded, non-redirecting HTTP. No SDK retries or environment credential discovery."""
|
|
|
|
import asyncio
|
|
import math
|
|
|
|
import aiohttp
|
|
|
|
from ..protocol import DecisionError, loads
|
|
|
|
|
|
async def post(session, connection, path, payload):
|
|
try:
|
|
async with session.post(
|
|
connection.base_url + path,
|
|
json=payload,
|
|
headers={"Authorization": "Bearer " + connection.key},
|
|
allow_redirects=False,
|
|
timeout=aiohttp.ClientTimeout(total=45),
|
|
) as response:
|
|
if response.status != 200:
|
|
raise DecisionError(f"upstream_http_{response.status}", 502)
|
|
# read(n) may return a partial chunk: accumulate with an explicit byte bound.
|
|
data = bytearray()
|
|
async for chunk in response.content.iter_chunked(16384):
|
|
data.extend(chunk)
|
|
if len(data) > 262144:
|
|
raise DecisionError("upstream_response_too_large", 502)
|
|
try:
|
|
result = loads(data.decode("utf-8"))
|
|
except UnicodeError as exc:
|
|
raise DecisionError("invalid_upstream_encoding", 502) from exc
|
|
if not isinstance(result, dict):
|
|
raise DecisionError("invalid_upstream_response", 502)
|
|
return result
|
|
except TimeoutError as exc:
|
|
raise DecisionError("upstream_timeout", 504) from exc
|
|
except (aiohttp.ClientError, OSError) as exc:
|
|
raise DecisionError("upstream_transport_error", 502) from exc
|
|
except asyncio.CancelledError:
|
|
raise
|
|
|
|
|
|
def usage(result):
|
|
"""Only real numeric counters, no inferred price, upstream strings or raw body."""
|
|
raw = result.get("usage", {})
|
|
if not isinstance(raw, dict):
|
|
return {}
|
|
return {
|
|
k: v
|
|
for k, v in raw.items()
|
|
if k
|
|
in (
|
|
"input_tokens",
|
|
"output_tokens",
|
|
"total_tokens",
|
|
"prompt_tokens",
|
|
"completion_tokens",
|
|
"cost",
|
|
)
|
|
and type(v) in (int, float)
|
|
and math.isfinite(v)
|
|
and 0 <= v <= 1e9
|
|
}
|