初始化提交

This commit is contained in:
chenxin committed 2026-08-21 16:00:13 +08:00
commit 887c1f449e
963 files changed
+166869

No files matched your search

+127
View File
@@ -0,0 +1,127 @@
"""沙箱调用的一炁事件构造与回写接口。
事件结构对齐 docs/architecture/data-and-security-architecture.md:``type / source / payload / trace_id /
sensitivity / created_at``。当前一炁事件存储尚未实现,``EventSink`` 用 Protocol 抽象,
默认实现写 loguru 日志;后续接入真实事件存储时实现 ``EventSink`` 注入即可,无需改动客户端。
payload 仅记录摘要(argv、输出长度等),不含密钥、Authorization、签名 URL 或完整输出,
遵循事件 payload 脱敏约定。
"""
from __future__ import annotations
from dataclasses import dataclass, field
from datetime import UTC, datetime
from typing import Any, Protocol
from app.core.logging import logger
@dataclass(frozen=True)
class SandboxEvent:
"""沙箱领域事件(派生自一炁事件契约)。"""
type: str # tool.started / tool.completed / tool.failed
source: str = "tool"
trace_id: str | None = None
sensitivity: str = "low" # none / low / medium / high
payload: dict[str, Any] = field(default_factory=dict)
created_at: str = field(default_factory=lambda: datetime.now(UTC).isoformat())
class EventSink(Protocol):
"""事件回写接口。"""
async def emit(self, event: SandboxEvent) -> None: ...
class LoggingEventSink:
"""默认事件回写:输出到 loguru(结构化字段,不含敏感原文)。"""
async def emit(self, event: SandboxEvent) -> None:
logger.bind(
event_type=event.type,
source=event.source,
trace_id=event.trace_id,
sensitivity=event.sensitivity,
payload=event.payload,
).info("sandbox event: {}", event.type)
class CompositeEventSink:
"""扇出到多个 sink:一次 emit 顺序广播给所有子 sink。
供 ToolRuntime 同时落 EventStore(一炁流程事件)与 AuditEvent(治理审计),互不感知。
任一子 sink 抛异常会中断后续 sink——审计写入失败应显式暴露(宁可事件漏写也不静默吞
异常,便于及时发现 DB 问题)。
"""
def __init__(self, sinks: list[EventSink]) -> None:
self._sinks = sinks
async def emit(self, event: SandboxEvent) -> None:
for sink in self._sinks:
await sink.emit(event)
def started_event(
*,
tool: str,
argv: list[str],
risk_level: str,
trace_id: str | None,
run_id: str | None,
) -> SandboxEvent:
return SandboxEvent(
type="tool.started",
trace_id=trace_id,
payload={"tool": tool, "risk_level": risk_level, "run_id": run_id, "argv": argv},
)
def completed_event(
*,
tool: str,
trace_id: str | None,
run_id: str | None,
exit_code: int | None,
duration_ms: int,
timeout: bool,
truncated: bool,
stdout_len: int,
stderr_len: int,
) -> SandboxEvent:
return SandboxEvent(
type="tool.completed",
trace_id=trace_id,
payload={
"tool": tool,
"run_id": run_id,
"exit_code": exit_code,
"duration_ms": duration_ms,
"timeout": timeout,
"truncated": truncated,
"stdout_len": stdout_len,
"stderr_len": stderr_len,
},
)
def failed_event(
*,
tool: str,
trace_id: str | None,
run_id: str | None,
error_code: str,
error_message: str,
) -> SandboxEvent:
return SandboxEvent(
type="tool.failed",
trace_id=trace_id,
payload={
"tool": tool,
"run_id": run_id,
"error_code": error_code,
"error_message": error_message,
},
)