fix: restore admin recent activity feed

parent b81ef423
......@@ -356,7 +356,28 @@ async def api_status(username: str = Depends(require_auth)):
recent_activity = []
try:
from codai.api.log import get_recent_activity
recent_activity = get_recent_activity()
for row in get_recent_activity():
if not isinstance(row, dict):
continue
try:
ts = int(row.get("time", 0) or 0)
except (TypeError, ValueError):
ts = 0
try:
status = int(row.get("status", 0) or 0)
except (TypeError, ValueError):
status = 0
try:
duration = round(float(row.get("duration", 0) or 0), 2)
except (TypeError, ValueError):
duration = 0.0
recent_activity.append({
"time": ts,
"model": str(row.get("model") or "—"),
"type": str(row.get("type") or "unknown"),
"status": status,
"duration": duration,
})
except Exception:
pass
......
......@@ -175,6 +175,7 @@ td code{font-family:var(--mono);font-size:11.5px;background:var(--raised);paddin
.badge-admin{background:var(--accent-s);color:#A5B4FC;border:1px solid rgba(99,102,241,.2)}
.badge-user{background:var(--raised);color:var(--text-3);border:1px solid var(--border)}
.badge-ok{background:rgba(52,211,153,.08);color:var(--green);border:1px solid rgba(52,211,153,.2)}
.badge-danger{background:rgba(248,113,113,.08);color:var(--red);border:1px solid rgba(248,113,113,.2)}
/* ── Modals ──────────────────────────────────────────────────────── */
.modal{display:none;position:fixed;inset:0;background:rgba(0,0,0,.6);backdrop-filter:blur(2px);z-index:500;align-items:center;justify-content:center}
......
......@@ -35,9 +35,36 @@ _TRACKED_PATHS = {
"/v1/chat/completions": "chat",
"/v1/completions": "completion",
"/v1/images/generations": "image",
"/v1/images/edits": "image-edit",
"/v1/images/inpaint": "image-inpaint",
"/v1/images/upscale": "image-upscale",
"/v1/images/deblur": "image-deblur",
"/v1/images/unpixelate": "image-unpixelate",
"/v1/images/outfit": "image-outfit",
"/v1/images/faceswap": "image-faceswap",
"/v1/images/depth": "image-depth",
"/v1/images/segment": "image-segment",
"/v1/video/generations": "video",
"/v1/video/upscale": "video-upscale",
"/v1/video/subtitle": "video-subtitle",
"/v1/video/interpolate": "video-interpolate",
"/v1/video/dub": "video-dub",
"/v1/audio/speech": "tts",
"/v1/audio/transcriptions": "transcription",
"/v1/audio/generate": "audio-generate",
"/v1/audio/clone": "voice-clone",
"/v1/audio/convert": "voice-convert",
"/v1/audio/stems": "audio-stems",
"/v1/audio/cleanup": "audio-cleanup",
"/v1/embeddings": "embedding",
"/v1/pipelines/image-to-video": "pipeline-image-to-video",
"/v1/pipelines/video-dub": "pipeline-video-dub",
"/v1/pipelines/story": "pipeline-story",
"/v1/pipelines/audio-dub": "pipeline-audio-dub",
"/v1/pipelines/audio-understand": "pipeline-audio-understand",
"/v1/pipelines/audio-music-dub": "pipeline-audio-music-dub",
"/v1/pipelines/custom": "pipeline-custom",
"/v1/pipelines/run": "pipeline-run",
}
......
import base64
import os
import sys
import types
import wave
from io import BytesIO
from pathlib import Path
......@@ -262,8 +263,49 @@ def test_audio_cleanup_returns_artifact_and_applied_operations(monkeypatch, stud
assert body["backend"]["engine"] == "ffmpeg-afftdn"
assert body["backend"]["quality"] == "best-effort"
assert body["applied"] == ["noise_reduction", "normalize"]
assert "/v1/files/" in body["data"][0]["url"]
assert "not-ml-restoration" in body["limitations"]
assert body["limitations"] == ["not-ml-restoration"]
def test_admin_status_includes_recent_activity(studio_client, monkeypatch):
from codai.admin import routes as admin_routes
from codai.api import log as api_log
api_log._activity.clear()
api_log._activity.appendleft({
"time": 1715000000,
"model": "demo-model",
"type": "chat",
"status": 200,
"duration": 1.23,
})
api_log._activity.appendleft({
"time": "bad-time",
"model": None,
"type": None,
"status": "500",
"duration": "2.5",
})
monkeypatch.setattr(admin_routes, "config_manager", types.SimpleNamespace(models_data={}, pipelines_data=[]), raising=False)
response = studio_client.get("/admin/api/status")
assert response.status_code == 200
payload = response.json()
assert payload["recent_activity"][0] == {
"time": 0,
"model": "—",
"type": "unknown",
"status": 500,
"duration": 2.5,
}
assert payload["recent_activity"][1] == {
"time": 1715000000,
"model": "demo-model",
"type": "chat",
"status": 200,
"duration": 1.23,
}
def test_chat_template_wires_preview_shells_for_new_runnable_panels():
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment