Files
openai--openai-agents-python/src/agents/sandbox/session/events.py
T
2026-07-13 12:39:17 +08:00

97 lines
2.9 KiB
Python

from __future__ import annotations
import uuid
from datetime import datetime, timezone
from typing import Annotated, Literal
from pydantic import BaseModel, Field, TypeAdapter
from ..errors import ErrorCode, OpName
EventPhase = Literal["start", "finish"]
def _utcnow() -> datetime:
return datetime.now(tz=timezone.utc)
class EventPayloadPolicy(BaseModel):
"""Controls how much potentially sensitive/large data is included in events."""
# Exec output can be noisy and sensitive; default off.
include_exec_output: bool = Field(default=False)
# When enabled, bound output sizes.
max_stdout_chars: int = Field(default=8_000, ge=0)
max_stderr_chars: int = Field(default=8_000, ge=0)
# For write events, we only include a best-effort byte count (never file bytes).
include_write_len: bool = Field(default=True)
class SandboxSessionEventBase(BaseModel):
"""Shared fields for all sandbox audit events."""
version: int = Field(default=1)
event_id: uuid.UUID = Field(default_factory=uuid.uuid4)
ts: datetime = Field(default_factory=_utcnow)
session_id: uuid.UUID
seq: int
op: OpName
phase: EventPhase
# Correlates start/finish records for an operation.
# When SDK tracing is active, this is the SDK span id for the operation.
span_id: str
parent_span_id: str | None = None
trace_id: str | None = None
# Operation-specific metadata (paths, argv, timings, etc.)
data: dict[str, object] = Field(default_factory=dict)
class SandboxSessionStartEvent(SandboxSessionEventBase):
"""The start event for an operation."""
phase: Literal["start"] = Field(default="start")
class SandboxSessionFinishEvent(SandboxSessionEventBase):
"""The finish event for an operation."""
phase: Literal["finish"] = Field(default="finish")
ok: bool
duration_ms: float
error_code: ErrorCode | None = None
error_type: str | None = None
error_message: str | None = None
error_retryable: bool | None = None
# Optional exec outputs (truncated / opt-in via policy).
stdout: str | None = None
stderr: str | None = None
# Raw exec outputs (bytes) for per-sink/per-op policy application.
# These are excluded from serialization (JSONL / HTTP) by default.
stdout_bytes: bytes | None = Field(default=None, exclude=True)
stderr_bytes: bytes | None = Field(default=None, exclude=True)
# Discriminated union keyed by `phase`.
SandboxSessionEvent = Annotated[
SandboxSessionStartEvent | SandboxSessionFinishEvent,
Field(discriminator="phase"),
]
_SANDBOX_SESSION_EVENT_ADAPTER: TypeAdapter[SandboxSessionEvent] = TypeAdapter(SandboxSessionEvent)
def validate_sandbox_session_event(obj: object) -> SandboxSessionEvent:
"""Parse an event payload (e.g. from JSON) into the correct phase-specific model."""
return _SANDBOX_SESSION_EVENT_ADAPTER.validate_python(obj)