125 lines
4.5 KiB
Python

"""框架端口对象。
这些对象只描述一次 Agent 执行所需的通用材料,不加载 Muse 角色合同、数据库或业务状态。
"""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Any, Mapping
@dataclass(frozen=True)
class FrameworkExecutionRequest:
"""已由业务侧解析完成的框架执行请求。"""
system_prompt: str
user_content: str
tool_allowlist: tuple[str, ...] = ()
timeout_seconds: float = 600.0
request_id: str | None = None
session_mode: str = "fresh"
def __post_init__(self) -> None:
if not isinstance(self.system_prompt, str) or not self.system_prompt.strip():
raise ValueError("system_prompt 必须是非空字符串")
if not isinstance(self.user_content, str) or not self.user_content.strip():
raise ValueError("user_content 必须是非空字符串")
if self.session_mode not in {"fresh", "continue"}:
raise ValueError("session_mode 必须是 fresh 或 continue")
if self.timeout_seconds <= 0:
raise ValueError("timeout_seconds 必须大于 0")
if any(not isinstance(name, str) or not name.strip() for name in self.tool_allowlist):
raise ValueError("tool_allowlist 必须是非空工具名数组")
def as_dict(self) -> dict[str, Any]:
return {
"systemPrompt": self.system_prompt,
"userContent": self.user_content,
"toolAllowlist": list(self.tool_allowlist),
"timeoutSeconds": self.timeout_seconds,
"requestId": self.request_id,
"sessionMode": self.session_mode,
}
@dataclass(frozen=True)
class FrameworkEvent:
"""框架原生事件的通用观察材料。"""
framework: str
framework_version: str
session_id: str | None
source_seq: int
kind: str
phase: str
safe_details: Mapping[str, Any] = field(default_factory=dict)
payload_sha256: str | None = None
source_event_id: str | None = None
run_id: str | None = None
observed_at: str = ""
def __post_init__(self) -> None:
if not self.framework or not self.framework_version:
raise ValueError("FrameworkEvent 缺少 framework/framework_version")
if self.source_seq < 1:
raise ValueError("FrameworkEvent source_seq 必须从 1 开始")
if self.kind not in {"session", "turn", "step", "model", "tool", "agent", "transport", "unknown"}:
raise ValueError(f"未知 FrameworkEvent kind: {self.kind}")
if self.phase not in {"started", "progress", "completed", "failed"}:
raise ValueError(f"未知 FrameworkEvent phase: {self.phase}")
if not self.observed_at:
raise ValueError("FrameworkEvent observed_at 不能为空")
def as_dict(self) -> dict[str, Any]:
return {
"framework": self.framework,
"frameworkVersion": self.framework_version,
"sessionId": self.session_id,
"sourceSeq": self.source_seq,
"sourceEventId": self.source_event_id,
"runId": self.run_id,
"kind": self.kind,
"phase": self.phase,
"safeDetails": dict(self.safe_details),
"payloadSha256": self.payload_sha256,
"observedAt": self.observed_at,
}
@dataclass(frozen=True)
class FrameworkExecutionResult:
"""框架执行结果;业务层仍需运行自己的 Schema 与主权门。"""
status: str
final_text: str | None
requested_model: str
actual_models: tuple[str, ...] = ()
session_id: str | None = None
artifact_locator: str | None = None
trace_digest: str | None = None
def __post_init__(self) -> None:
if self.status not in {"completed", "failed", "cancelled", "timed_out", "invalid_output"}:
raise ValueError(f"未知框架结果状态: {self.status}")
if not self.requested_model:
raise ValueError("FrameworkExecutionResult requested_model 不能为空")
def as_dict(self) -> dict[str, Any]:
return {
"status": self.status,
"finalText": self.final_text,
"requestedModel": self.requested_model,
"actualModels": list(self.actual_models),
"sessionId": self.session_id,
"artifactLocator": self.artifact_locator,
"traceDigest": self.trace_digest,
}
__all__ = [
"FrameworkEvent",
"FrameworkExecutionRequest",
"FrameworkExecutionResult",
]