修项目成本按天接口在 Postgres 上的 Decimal 序列化崩溃
2026-10-26 项目成本总览实弹:总览一次通过,但 ?day= 请求让 服务端请求线程直接死掉、客户端只看到连接断开。日志根因: Postgres 的 SUM(bigint) 返回 NUMERIC,psycopg2 映成 Decimal, 按天分支把 SUM 原值塞进 JSON 时 json.dumps 抛 TypeError。 这是按天路径的潜 bug(此前从未在生产 PG 上跑通过按天查询), 不是 PR #45 新加列引入的,但本次实弹把它暴露了出来。 修复:按天两分支与模型总览的数值全部 int() 转换;_send_json 加 Decimal 兜底(整数转 int、非整数转 float),防止同类泄漏 再把请求线程拖死。单测:按天端点 + _json_default。
This commit is contained in:
1 parent
70177989f2
commit
e384245722
2 files changed
+82
-15
No files matched your search
@@ -196,6 +196,55 @@ class EndpointsTestCase(unittest.TestCase):
|
||||
self.assertEqual(status, 200)
|
||||
self.assertEqual(body["overall"]["turns"], 2)
|
||||
|
||||
def test_project_cost_daily(self):
|
||||
from datetime import datetime, timezone
|
||||
status, task = self._req(
|
||||
"POST", "/tasks",
|
||||
{"project_id": self._pid(), "prompt": "做加法"}, self.owner_key)
|
||||
self.assertEqual(status, 201)
|
||||
store = CostStore(self.db_path)
|
||||
try:
|
||||
store.record(task["id"], self._pid(), "gpt-6.1-sol",
|
||||
input_tokens=100, output_tokens=50,
|
||||
cached_input_tokens=40, usd_micros=1234,
|
||||
source="actual", fallback_count=1)
|
||||
finally:
|
||||
store._conn.close()
|
||||
today = datetime.now(timezone.utc).strftime("%Y-%m-%d")
|
||||
status, body = self._req(
|
||||
"GET", "/projects/%s/cost?day=%s" % (self._pid(), today),
|
||||
key=self.owner_key)
|
||||
self.assertEqual(status, 200)
|
||||
self.assertEqual(len(body["daily"]), 1)
|
||||
row = body["daily"][0]
|
||||
self.assertEqual(row["day"], today)
|
||||
self.assertEqual(row["total_tokens"], 150)
|
||||
self.assertEqual(row["usd_micros"], 1234)
|
||||
self.assertEqual(row["turns"], 1)
|
||||
self.assertEqual(row["fallback_count"], 1)
|
||||
self.assertEqual(row["cached_input_tokens"], 40)
|
||||
self.assertEqual(row["unpriced_actual_tokens"], 0)
|
||||
status, body = self._req(
|
||||
"GET", "/projects/%s/cost?days=7" % self._pid(),
|
||||
key=self.owner_key)
|
||||
self.assertEqual(status, 200)
|
||||
self.assertEqual(
|
||||
[r for r in body["daily"] if r["day"] == today][0]["turns"], 1)
|
||||
status, body = self._req(
|
||||
"GET", "/projects/%s/cost?day=2000-01-01" % self._pid(),
|
||||
key=self.owner_key)
|
||||
self.assertEqual(status, 200)
|
||||
self.assertEqual(body["daily"], [])
|
||||
|
||||
def test_json_default_decimal(self):
|
||||
from decimal import Decimal
|
||||
from cloud.server import _json_default
|
||||
self.assertEqual(_json_default(Decimal("10424")), 10424)
|
||||
self.assertIsInstance(_json_default(Decimal("10424")), int)
|
||||
self.assertEqual(_json_default(Decimal("1.5")), 1.5)
|
||||
with self.assertRaises(TypeError):
|
||||
_json_default(object())
|
||||
|
||||
# -- 模型总览 -------------------------------------------------------
|
||||
def test_models_overview_isolated(self):
|
||||
status, task = self._req(
|
||||
|
||||
Reference in new issue
Block a user