feat: add image route usage telemetry
This commit is contained in:
@@ -1168,6 +1168,43 @@ class GenerateApiTests(TestCase):
|
||||
with patch("apps.api.generation.get_provider", return_value=provider or self.provider):
|
||||
return self.client.post(path, payload, format="json", **self.auth_header(), **extra)
|
||||
|
||||
def telemetry_event_from_logs(self, captured):
|
||||
events = [
|
||||
getattr(record, "generation_route_usage", None)
|
||||
for record in captured.records
|
||||
if getattr(record, "generation_route_usage", None)
|
||||
]
|
||||
self.assertEqual(len(events), 1)
|
||||
return events[0]
|
||||
|
||||
def assert_generation_telemetry_is_safe(self, event, *, payload=None):
|
||||
self.assertEqual(
|
||||
set(event),
|
||||
{
|
||||
"event",
|
||||
"route_type",
|
||||
"api_key_id",
|
||||
"api_key_prefix",
|
||||
"user_id",
|
||||
"client_version",
|
||||
"alias",
|
||||
"status",
|
||||
"latency_ms",
|
||||
"error_code",
|
||||
"http_status",
|
||||
},
|
||||
)
|
||||
serialized = json.dumps(event, ensure_ascii=False)
|
||||
self.assertNotIn(self.raw_key, serialized)
|
||||
self.assertNotIn("SECRET_RAW", serialized)
|
||||
self.assertNotIn("prompt", serialized)
|
||||
self.assertNotIn("image_base64", serialized)
|
||||
if payload:
|
||||
self.assertNotIn(str(payload.get("prompt") or ""), serialized)
|
||||
encoded_image = str(payload.get("image_base64") or "")
|
||||
if encoded_image:
|
||||
self.assertNotIn(encoded_image, serialized)
|
||||
|
||||
def assert_generation_not_charged(self):
|
||||
self.wallet.refresh_from_db()
|
||||
self.assertEqual(self.wallet.points_balance, 100)
|
||||
@@ -1288,6 +1325,92 @@ class GenerateApiTests(TestCase):
|
||||
self.assertEqual(call.result_summary, "image_bytes=21")
|
||||
self.assertNotIn("SECRET_RAW", call.result_ref + call.result_summary)
|
||||
|
||||
def test_sync_image_usage_telemetry_logs_safe_client_version_and_key_identity(self):
|
||||
encoded = base64.b64encode(b"input-image").decode("ascii")
|
||||
payload = {
|
||||
"prompt": "生成图片遥测测试",
|
||||
"model": self.image_alias,
|
||||
"image_base64": f"data:image/png;base64,{encoded}",
|
||||
"resolution": "1K",
|
||||
"aspect_ratio": "1:1",
|
||||
}
|
||||
|
||||
with self.assertLogs("cmhub.api.generation_usage", level="INFO") as captured:
|
||||
response = self.post_with_provider(
|
||||
"/api/v1/generate/image",
|
||||
payload,
|
||||
HTTP_X_CLIENT_VERSION="0.1.1",
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
event = self.telemetry_event_from_logs(captured)
|
||||
self.assertEqual(event["event"], "generation_route_usage")
|
||||
self.assertEqual(event["route_type"], "sync")
|
||||
self.assertEqual(event["api_key_id"], self.api_key.id)
|
||||
self.assertEqual(event["api_key_prefix"], self.api_key.key_prefix)
|
||||
self.assertEqual(event["user_id"], self.user.id)
|
||||
self.assertEqual(event["client_version"], "0.1.1")
|
||||
self.assertEqual(event["alias"], self.image_alias)
|
||||
self.assertEqual(event["status"], "success")
|
||||
self.assertEqual(event["error_code"], "")
|
||||
self.assertEqual(event["http_status"], 200)
|
||||
self.assertIsInstance(event["latency_ms"], int)
|
||||
self.assertGreaterEqual(event["latency_ms"], 0)
|
||||
self.assert_generation_telemetry_is_safe(event, payload=payload)
|
||||
|
||||
def test_async_image_submit_usage_telemetry_logs_safe_success_event(self):
|
||||
payload = {
|
||||
"prompt": "生成异步图片遥测测试",
|
||||
"model": self.image_alias,
|
||||
"resolution": "1K",
|
||||
}
|
||||
|
||||
with self.assertLogs("cmhub.api.generation_usage", level="INFO") as captured:
|
||||
response = self.post_with_provider(
|
||||
"/api/v1/generate/image/tasks",
|
||||
payload,
|
||||
HTTP_X_CLIENT_VERSION="0.1.2",
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 202)
|
||||
event = self.telemetry_event_from_logs(captured)
|
||||
self.assertEqual(event["route_type"], "async")
|
||||
self.assertEqual(event["api_key_id"], self.api_key.id)
|
||||
self.assertEqual(event["api_key_prefix"], self.api_key.key_prefix)
|
||||
self.assertEqual(event["user_id"], self.user.id)
|
||||
self.assertEqual(event["client_version"], "0.1.2")
|
||||
self.assertEqual(event["alias"], self.image_alias)
|
||||
self.assertEqual(event["status"], "success")
|
||||
self.assertEqual(event["error_code"], "")
|
||||
self.assertEqual(event["http_status"], 202)
|
||||
self.assert_generation_telemetry_is_safe(event, payload=payload)
|
||||
|
||||
def test_async_image_submit_usage_telemetry_logs_error_code_without_sensitive_data(self):
|
||||
self.wallet.points_balance = 1
|
||||
self.wallet.save(update_fields=("points_balance", "updated_at"))
|
||||
payload = {
|
||||
"prompt": "余额不足遥测测试",
|
||||
"model": self.image_alias,
|
||||
"resolution": "1K",
|
||||
}
|
||||
|
||||
with self.assertLogs("cmhub.api.generation_usage", level="INFO") as captured:
|
||||
response = self.post_with_provider(
|
||||
"/api/v1/generate/image/tasks",
|
||||
payload,
|
||||
HTTP_X_CLIENT_VERSION="0.1.3",
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 402)
|
||||
event = self.telemetry_event_from_logs(captured)
|
||||
self.assertEqual(event["route_type"], "async")
|
||||
self.assertEqual(event["status"], "error")
|
||||
self.assertEqual(event["error_code"], "insufficient_points")
|
||||
self.assertEqual(event["http_status"], 402)
|
||||
self.assertEqual(event["client_version"], "0.1.3")
|
||||
self.assertEqual(event["alias"], self.image_alias)
|
||||
self.assert_generation_telemetry_is_safe(event, payload=payload)
|
||||
|
||||
@override_settings(
|
||||
MODERATION_ENABLED=True,
|
||||
MODERATION_PROVIDER="keyword",
|
||||
|
||||
Reference in New Issue
Block a user