初始化提交
This commit is contained in:
commit
887c1f449e
963 files changed
+166869
No files matched your search
@@ -0,0 +1,102 @@
|
||||
"""审计服务。"""
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.core.errors import current_trace_id
|
||||
from app.core.security import redact_text
|
||||
from app.db.session import AsyncSessionLocal
|
||||
from app.models.safety import AuditEvent
|
||||
from app.repositories.audit import add_audit_event, list_audit_events
|
||||
from app.schemas.admin import AuditEntry
|
||||
from app.schemas.common import Page
|
||||
from app.services.serializers import audit_to_response
|
||||
|
||||
# action 前缀 → 九枢标识(契约字典 §5.9)映射,供起居注按枢过滤与聚合。
|
||||
_ACTION_PIVOT_PREFIXES: list[tuple[str, str]] = [
|
||||
("memory.", "memory"),
|
||||
("tool.", "tool_runtime"),
|
||||
("safety.", "safety_governor"),
|
||||
("personality.", "personality_core"),
|
||||
("goal.", "goal_orchestrator"),
|
||||
("world.", "world_model"),
|
||||
("plan.", "planner"),
|
||||
("perception.", "perception"),
|
||||
("agent.", "agent_society"),
|
||||
]
|
||||
|
||||
|
||||
def infer_pivot(action: str) -> str | None:
|
||||
"""根据 audit action 前缀推断所属枢;未命中返回 None。"""
|
||||
for prefix, pivot in _ACTION_PIVOT_PREFIXES:
|
||||
if action.startswith(prefix):
|
||||
return pivot
|
||||
return None
|
||||
|
||||
|
||||
def _summary(value: str | None) -> str | None:
|
||||
if value is None:
|
||||
return None
|
||||
redacted = redact_text(value)
|
||||
return redacted[:1000]
|
||||
|
||||
|
||||
async def record_audit_event(
|
||||
session: AsyncSession,
|
||||
*,
|
||||
action: str,
|
||||
pivot: str | None = None,
|
||||
character_id: str | None = None,
|
||||
run_id: str | None = None,
|
||||
actor: str = "system",
|
||||
risk_level: str = "L1",
|
||||
decision: str = "auto",
|
||||
reason: str | None = None,
|
||||
input_summary: str | None = None,
|
||||
output_summary: str | None = None,
|
||||
resource_type: str | None = None,
|
||||
resource_id: str | None = None,
|
||||
trace_id: str | None = None,
|
||||
) -> AuditEvent:
|
||||
return await add_audit_event(
|
||||
session,
|
||||
AuditEvent(
|
||||
pivot=pivot or infer_pivot(action),
|
||||
character_id=character_id,
|
||||
run_id=run_id,
|
||||
action=action,
|
||||
actor=actor,
|
||||
risk_level=risk_level,
|
||||
decision=decision,
|
||||
reason=_summary(reason),
|
||||
input_summary=_summary(input_summary),
|
||||
output_summary=_summary(output_summary),
|
||||
# 显式 trace_id 优先(如沙箱事件携带的 trace_id),否则取请求上下文 current_trace_id
|
||||
trace_id=trace_id or current_trace_id(),
|
||||
resource_type=resource_type,
|
||||
resource_id=resource_id,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
async def query_audit_events(
|
||||
*,
|
||||
character_id: str | None = None,
|
||||
pivot: str | None = None,
|
||||
page: int = 1,
|
||||
page_size: int = 50,
|
||||
) -> Page[AuditEntry]:
|
||||
"""审计事件时间线(按创建时间倒序),可选按人物/枢过滤。"""
|
||||
async with AsyncSessionLocal() as session:
|
||||
items, total = await list_audit_events(
|
||||
session,
|
||||
pivot=pivot,
|
||||
character_id=character_id,
|
||||
page=page,
|
||||
page_size=page_size,
|
||||
)
|
||||
return Page(
|
||||
items=[audit_to_response(item) for item in items],
|
||||
page=page,
|
||||
page_size=page_size,
|
||||
total=total,
|
||||
)
|
||||
Reference in new issue
Block a user