feat(admin-web,backend): F1-5b Wave B UI-3 + UI-5 admin-web sandbox 透出补强 (W1)
UI-3 AIDashboard sandbox 提示 + today_calls 分组: - 后端 schemas/admin_ai.py DashboardResponse 加 today_live_calls / today_sandbox_calls 字段(默认 0,向后兼容) - 后端 services/ai/admin_service.py _get_range_stats SELECT 加 2 个 FILTER COUNT 表达式 - 前端 api/adminAI.ts DashboardResponse 类型补 2 字段 - 前端 pages/AIDashboard.tsx - 顶部加 sandbox Alert 提示条,选中 site sandbox 模式下显示业务日 + 实例 ID - today_calls 卡片下方加分组 Tag(实时 X / 沙箱 Y),feature flag 控制 - import fetchRuntimeContext + useEffect 拉 RuntimeContext - apps/admin-web/.env.example 新建,加 VITE_AI_RUNTIME_GROUPING=false 默认值说明 UI-5 AITriggerJobs runtime 列: - 后端 schemas/admin_ai.py TriggerJobItem 加 runtime_mode / sandbox_instance_id 可选字段 - 后端 admin_service.py list_trigger_jobs / get_trigger_job 各加 SELECT 列 - 前端 adminAI.ts TriggerJobItem 类型补 2 字段 - 前端 pages/AITriggerJobs.tsx 列表 columns 加运行模式 + 沙箱实例(同 UI-1 模式),详情 Modal 加 2 项(同 UI-2 模式) 双口径验证(Playwright + DB 直查): - UI-3 4a live: 选中默认门店,无 Alert,today_card 仅显示总数(flag off) - UI-3 4b sandbox=4-20: Alert 显示"沙箱 + 业务日 + sbx_…",today_calls=93(sandbox 当日) - UI-5 4a/4b: SQL INSERT 注入 walkthrough 测试行(id=9 live, id=10 sandbox),列表正确渲染 Tag + 短哈希 trend_7d 双线 / app_distribution 堆叠分布等更深入分组改造延后到 Wave C(§8.3 风险:破坏图表)。 审计: - docs/audit/changes/2026-05-05__wave1_f1_5b_ui3_aidashboard_sandbox.md - docs/audit/changes/2026-05-05__wave1_f1_5b_ui5_aitriggerjobs_runtime.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -62,6 +62,10 @@ class DashboardResponse(BaseModel):
|
||||
today_success_rate: float # 0.0 ~ 1.0
|
||||
today_tokens: int
|
||||
today_avg_latency_ms: float
|
||||
# F1-5b UI-3: today_calls 按 runtime_mode 分组
|
||||
# live + sandbox = today_calls(总计),供前端 feature flag 控制是否展示
|
||||
today_live_calls: int = 0
|
||||
today_sandbox_calls: int = 0
|
||||
trend_7d: list[DailyTrend]
|
||||
app_distribution: list[AppDistItem]
|
||||
budget: BudgetInfo
|
||||
@@ -84,6 +88,9 @@ class TriggerJobItem(BaseModel):
|
||||
started_at: str | None
|
||||
finished_at: str | None
|
||||
created_at: str
|
||||
# F1-5b UI-5: runtime 透出(同 RunLogItem)
|
||||
runtime_mode: str | None = None
|
||||
sandbox_instance_id: str | None = None
|
||||
|
||||
|
||||
class TriggerJobListResponse(BaseModel):
|
||||
|
||||
@@ -126,6 +126,8 @@ class AdminAIService:
|
||||
conn = get_connection()
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
# F1-5b UI-3: SELECT 加 live/sandbox 分组 COUNT,
|
||||
# 总数 total_calls 与 today_live + today_sandbox 应一致。
|
||||
cur.execute(
|
||||
f"""
|
||||
SELECT
|
||||
@@ -133,7 +135,10 @@ class AdminAIService:
|
||||
COUNT(*) FILTER (WHERE status = 'success') AS success_count,
|
||||
COALESCE(SUM(tokens_used), 0) AS total_tokens,
|
||||
COALESCE(AVG(latency_ms) FILTER (WHERE latency_ms IS NOT NULL), 0)
|
||||
AS avg_latency
|
||||
AS avg_latency,
|
||||
COUNT(*) FILTER (WHERE COALESCE(runtime_mode, 'live') = 'live')
|
||||
AS live_calls,
|
||||
COUNT(*) FILTER (WHERE runtime_mode = 'sandbox') AS sandbox_calls
|
||||
FROM biz.ai_run_logs
|
||||
WHERE {time_clause}
|
||||
{site_clause}
|
||||
@@ -145,13 +150,15 @@ class AdminAIService:
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
total, success, tokens, avg_lat = row if row else (0, 0, 0, 0)
|
||||
total, success, tokens, avg_lat, live_cnt, sandbox_cnt = row if row else (0, 0, 0, 0, 0, 0)
|
||||
rate = round(success / total, 4) if total > 0 else 0.0
|
||||
return {
|
||||
"today_calls": total,
|
||||
"today_success_rate": rate,
|
||||
"today_tokens": int(tokens),
|
||||
"today_avg_latency_ms": round(float(avg_lat), 2),
|
||||
"today_live_calls": int(live_cnt),
|
||||
"today_sandbox_calls": int(sandbox_cnt),
|
||||
}
|
||||
|
||||
async def _get_7d_trend(self, site_id: int | None) -> list[dict]:
|
||||
@@ -350,10 +357,12 @@ class AdminAIService:
|
||||
total = cur.fetchone()[0]
|
||||
|
||||
# 分页数据
|
||||
# F1-5b UI-5: 列表 SELECT 加 runtime_mode + sandbox_instance_id 透出
|
||||
cur.execute(
|
||||
f"""
|
||||
SELECT id, event_type, member_id, status, app_chain,
|
||||
is_forced, site_id, started_at, finished_at, created_at
|
||||
is_forced, site_id, started_at, finished_at, created_at,
|
||||
runtime_mode, sandbox_instance_id
|
||||
FROM biz.ai_trigger_jobs
|
||||
{where_sql}
|
||||
ORDER BY created_at DESC
|
||||
@@ -392,11 +401,13 @@ class AdminAIService:
|
||||
conn = get_connection()
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
# F1-5b UI-5: 详情 SELECT 加 runtime_mode + sandbox_instance_id 透出
|
||||
cur.execute(
|
||||
"""
|
||||
SELECT id, event_type, member_id, status, app_chain,
|
||||
is_forced, site_id, started_at, finished_at,
|
||||
created_at, payload, error_message, connector_type
|
||||
created_at, payload, error_message, connector_type,
|
||||
runtime_mode, sandbox_instance_id
|
||||
FROM biz.ai_trigger_jobs
|
||||
WHERE id = %s
|
||||
""",
|
||||
|
||||
Reference in New Issue
Block a user