Files
Neo-ZQYY/apps/backend/app/schemas/admin_ai.py
Neo 3c8d72edd4 feat(ai): F1-5b Wave A admin-web sandbox 透出 UI-1/2/4 (W1)
完成 F1-5b Wave A admin-web 改造:

UI-1 AIRunLogs 列表加 runtime_mode + sandbox_instance_id 列
- 后端 schema RunLogItem 补 runtime_mode / sandbox_instance_id 字段
- 后端 SQL list_run_logs SELECT 加这两列
- 前端 columns 加"运行模式"(orange/blue Tag) + "沙箱实例"(短哈希 + tooltip)

UI-2 AIRunLogs 详情 Drawer 加 runtime 字段
- 后端 SQL get_run_log SELECT 加 runtime 列
- 前端 Descriptions 加"运行模式" + "沙箱实例"两项

UI-4 全局 sandbox 徽章(覆盖所有 admin-web 页面)
- App.tsx Footer 三段式: 左 sandbox 徽章 / 中 任务状态 / 右 占位
- 30s 轮询 fetchRuntimeContext(userSiteId)
- sandbox: 橙色"沙箱"+ 业务日 + 短哈希实例 ID(monospace)
- live: 绿色"实时"+ 真实今天

双口径 4a/4b 验证(MCP Playwright 实地走查):
- UI-1 4a live: 列表全行 live 蓝 Tag
- UI-1 4b sandbox: SQL INSERT walkthrough_ui12 → 列表显示 sandbox 橙 Tag + 短哈希
- UI-2 4b: Drawer 详情 runtime_mode='sandbox' 橙 Tag + sandbox_instance_id monospace 全 ID
- UI-4 4a: footer 左侧绿"实时"+ 2026-05-05
- UI-4 4b: 切 sandbox=2026-04-20 后 footer 显示橙"沙箱"+ 业务日 + sbx_e7a7e5c5...
- 截图归档 docs/audit/changes/screenshots/2026-05-05_f1_5b_wave_a/

剩余 Wave A: MP-3/5 小程序 sandbox / MP-1 board-finance 字段复核 / BE-1 task-list 403

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 15:14:29 +08:00

308 lines
8.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""
管理端 — AI 监控后台 Pydantic Schema。
覆盖Dashboard 总览、调度任务、调用记录、缓存失效、Token 预算、批量执行、告警管理。
需求: A1.1, A2.1, A4.1, A5.1, A6.1, A7.1, A8.1
"""
from __future__ import annotations
from pydantic import BaseModel
# ── Dashboard ─────────────────────────────────────────────
class DailyTrend(BaseModel):
"""近 7 天按日聚合趋势项。"""
date: str # YYYY-MM-DD
calls: int
success_rate: float
class AppDistItem(BaseModel):
"""各 App 调用占比分布项。"""
app_type: str
count: int
percentage: float
class BudgetInfo(BaseModel):
"""日/月 Token 预算进度。"""
daily_used: int
daily_limit: int
daily_pct: float
monthly_used: int
monthly_limit: int
monthly_pct: float
class AlertItem(BaseModel):
"""告警事件项(失败/超时/熔断)。"""
id: int
app_type: str
status: str # failed / timeout / circuit_open
alert_status: str | None # pending / acknowledged / ignored
error_message: str | None
created_at: str
class AppHealthItem(BaseModel):
"""各 App 最近一次调用状态。"""
app_type: str
last_status: str | None
last_call_at: str | None
class DashboardResponse(BaseModel):
"""Dashboard 总览统计响应。"""
today_calls: int
today_success_rate: float # 0.0 ~ 1.0
today_tokens: int
today_avg_latency_ms: float
trend_7d: list[DailyTrend]
app_distribution: list[AppDistItem]
budget: BudgetInfo
recent_alerts: list[AlertItem]
app_health: list[AppHealthItem]
# ── 调度任务 ──────────────────────────────────────────────
class TriggerJobItem(BaseModel):
"""调度任务列表项。"""
id: int
event_type: str
member_id: int | None
status: str
app_chain: str | None
is_forced: bool
site_id: int
started_at: str | None
finished_at: str | None
created_at: str
class TriggerJobListResponse(BaseModel):
"""调度任务分页列表响应。"""
items: list[TriggerJobItem]
total: int
page: int
page_size: int
today_skipped_duplicates: int # 今日去重跳过数
class TriggerJobDetailResponse(TriggerJobItem):
"""调度任务详情响应(含 payload、error_message"""
payload: dict | None
error_message: str | None
connector_type: str
class RetryResponse(BaseModel):
"""手动重跑响应。"""
trigger_job_id: int
status: str # "pending"
# ── 调用记录 ──────────────────────────────────────────────
class RunLogItem(BaseModel):
"""调用记录列表项。F1-5b UI-1: 加 runtime_mode + sandbox_instance_id 透出 sandbox 状态。"""
id: int
app_type: str
trigger_type: str
member_id: int | None
tokens_used: int
latency_ms: int | None
status: str
site_id: int
created_at: str
runtime_mode: str | None = None
sandbox_instance_id: str | None = None
class RunLogListResponse(BaseModel):
"""调用记录分页列表响应。"""
items: list[RunLogItem]
total: int
page: int
page_size: int
class RunLogDetailResponse(RunLogItem):
"""调用记录详情响应(含完整 prompt/response不脱敏"""
request_prompt: str | None
response_text: str | None
error_message: str | None
session_id: str | None
finished_at: str | None
# ── 缓存失效 ─────────────────────────────────────────────
class CacheInvalidateRequest(BaseModel):
"""缓存失效请求site_id 必填)。"""
site_id: int
app_type: str | None = None
member_id: int | None = None
class CacheInvalidateResponse(BaseModel):
"""缓存失效响应。"""
affected_count: int
# ── Token 预算 ────────────────────────────────────────────
class BudgetResponse(BaseModel):
"""Token 预算使用情况响应。"""
daily_used: int
daily_limit: int
daily_pct: float
monthly_used: int
monthly_limit: int
monthly_pct: float
# ── 批量执行 ──────────────────────────────────────────────
class BatchRunRequest(BaseModel):
"""批量执行请求。"""
app_types: list[str]
member_ids: list[int]
site_id: int
class BatchRunEstimate(BaseModel):
"""批量执行预估响应(不立即执行)。"""
batch_id: str
estimated_calls: int
estimated_tokens: int
class BatchRunConfirm(BaseModel):
"""批量执行确认请求。"""
batch_id: str
class BatchRunConfirmResponse(BaseModel):
"""批量执行确认响应。"""
status: str # "started"
# ── 按需单 App 执行(/run/{app_type})──────────────────────
class RunAppRequest(BaseModel):
"""按需执行单个 App 请求体。
context 字段根据 app_type 不同有不同约束:
- app2_finance: site_id + time_dimension + areaarea 默认 all
- app3_clue / app7_customer: site_id + member_id
- app4_analysis / app5_tactics: site_id + member_id + assistant_id
- app6_note: site_id + member_id + note_content + noted_by_name
- app8_consolidation: site_id + member_id
"""
site_id: int
member_id: int | None = None
assistant_id: int | None = None
time_dimension: str | None = None
area: str | None = None # App2 专用,默认 all
note_content: str | None = None
noted_by_name: str | None = None
noted_by_created_at: str | None = None
class RunAppResponse(BaseModel):
"""按需执行单个 App 响应。"""
app_type: str
success: bool
result: dict | None = None # 百炼返回的 JSON成功时
error: str | None = None # 错误描述(失败时)
# ── 告警 ──────────────────────────────────────────────────
class AlertListResponse(BaseModel):
"""告警分页列表响应。"""
items: list[AlertItem]
total: int
page: int
page_size: int
class AlertActionResponse(BaseModel):
"""告警操作(确认/忽略)响应。"""
id: int
alert_status: str
# ── 触发器管理biz.trigger_jobs─────────────────────────
class TriggerItem(BaseModel):
"""触发器单条记录。"""
id: int
job_name: str
job_type: str
trigger_condition: str # event / cron / interval
trigger_config: dict # {"event_name": ...} 或 {"cron_expression": ...}
status: str # enabled / disabled
description: str | None = None
last_run_at: str | None = None
next_run_at: str | None = None
last_error: str | None = None
class TriggerUpdateRequest(BaseModel):
"""触发器更新请求3 个字段至少填一个)。"""
status: str | None = None # enabled / disabled
cron_expression: str | None = None # 标准 5 段 cron
description: str | None = None
# ── 预热进度app2_finance 72 组合)───────────────────────
class PrewarmMissingItem(BaseModel):
"""缺失的预热组合项。"""
target_id: str # this_month__all
time_dimension: str
area: str
class PrewarmProgressResponse(BaseModel):
"""app2_finance 预热进度响应。"""
total: int # 固定 72
done: int
missing: list[PrewarmMissingItem]
last_updated: str | None = None
# ── 手动事件触发(越过去重)───────────────────────────────
class ManualTriggerRequest(BaseModel):
"""手动触发 AI 事件请求。"""
event_type: str # consumption / dws_completed / note_created / task_assigned
site_id: int
member_id: int | None = None
assistant_id: int | None = None
payload: dict | None = None
is_forced: bool = True # 默认跳过去重
class ManualTriggerResponse(BaseModel):
"""手动事件触发响应。"""
trigger_job_id: int
status: str = "pending"