Files
beaver_project/app-instance/backend/beaver/tasks/models.py
steven_li ebfa242862 feat(outlook): 添加Outlook集成功能支持
添加完整的Outlook MCP集成,包括邮件和日历功能,通过AuthZ模式进行认证和权限管理,
支持邮箱连接、断开、状态检查和数据同步等功能。

fix(config): 统一配置文件路径从.nanobot到.beaver

将配置文件路径从/root/.nanobot统一更改为/root/.beaver,更新Dockerfile中的环境变量定义,
确保所有组件使用一致的配置目录结构。

feat(agent): 添加代理删除功能和助手身份提示

为代理注册表添加delete_agent方法,实现代理的动态删除功能;同时添加海狸助手身份提示,
确保AI助手在交互中保持一致的身份认知。

feat(engine): 增强引擎循环并添加意图决策快照

扩展AgentLoop类,添加intent_agent_decision参数用于意图驱动的代理决策,并在会话中记录
决策快照,便于后续分析和调试。

feat(authz): 扩展认证客户端功能

为AuthzClient添加设置权限、用户注册、后端注册和Outlook设置管理等新方法,增强系统
的认证和授权能力。
2026-05-14 16:01:46 +08:00

183 lines
6.3 KiB
Python

"""Models for internal task tracking and validation."""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Any
TASK_OPEN_STATUSES = {"open", "running", "validating", "awaiting_feedback", "needs_revision"}
@dataclass(slots=True)
class ValidationResult:
passed: bool
score: float
issues: list[str] = field(default_factory=list)
missing_requirements: list[str] = field(default_factory=list)
recommended_revision_prompt: str = ""
validator: str = "heuristic"
@property
def accepted(self) -> bool:
return self.passed and self.score >= 0.75
def to_dict(self) -> dict[str, Any]:
return {
"passed": self.passed,
"score": self.score,
"issues": list(self.issues),
"missing_requirements": list(self.missing_requirements),
"recommended_revision_prompt": self.recommended_revision_prompt,
"validator": self.validator,
"accepted": self.accepted,
}
@classmethod
def from_dict(cls, payload: dict[str, Any] | None) -> "ValidationResult | None":
if not isinstance(payload, dict):
return None
return cls(
passed=bool(payload.get("passed")),
score=float(payload.get("score", 0.0) or 0.0),
issues=[str(item) for item in payload.get("issues") or []],
missing_requirements=[str(item) for item in payload.get("missing_requirements") or []],
recommended_revision_prompt=str(payload.get("recommended_revision_prompt") or ""),
validator=str(payload.get("validator") or "unknown"),
)
@dataclass(slots=True)
class TaskRecord:
task_id: str
session_id: str
description: str
goal: str
constraints: list[str]
priority: int
status: str
creator: str
created_at: str
updated_at: str
parent_task_id: str | None = None
closed_at: str | None = None
close_reason: str | None = None
satisfaction: float | None = None
run_ids: list[str] = field(default_factory=list)
skill_names: list[str] = field(default_factory=list)
feedback: list[dict[str, Any]] = field(default_factory=list)
validation_result: dict[str, Any] | None = None
metadata: dict[str, Any] = field(default_factory=dict)
@property
def is_open(self) -> bool:
return self.status in TASK_OPEN_STATUSES
def to_dict(self) -> dict[str, Any]:
return {
"task_id": self.task_id,
"session_id": self.session_id,
"parent_task_id": self.parent_task_id,
"description": self.description,
"goal": self.goal,
"constraints": list(self.constraints),
"priority": self.priority,
"status": self.status,
"creator": self.creator,
"created_at": self.created_at,
"updated_at": self.updated_at,
"closed_at": self.closed_at,
"close_reason": self.close_reason,
"satisfaction": self.satisfaction,
"run_ids": list(self.run_ids),
"skill_names": list(self.skill_names),
"feedback": list(self.feedback),
"validation_result": self.validation_result,
"metadata": dict(self.metadata),
}
@classmethod
def from_dict(cls, payload: dict[str, Any]) -> "TaskRecord":
return cls(
task_id=str(payload["task_id"]),
session_id=str(payload["session_id"]),
parent_task_id=_optional_str(payload.get("parent_task_id")),
description=str(payload.get("description") or ""),
goal=str(payload.get("goal") or payload.get("description") or ""),
constraints=[str(item) for item in payload.get("constraints") or []],
priority=int(payload.get("priority", 0) or 0),
status=str(payload.get("status") or "open"),
creator=str(payload.get("creator") or "main-agent"),
created_at=str(payload.get("created_at") or ""),
updated_at=str(payload.get("updated_at") or ""),
closed_at=_optional_str(payload.get("closed_at")),
close_reason=_optional_str(payload.get("close_reason")),
satisfaction=_optional_float(payload.get("satisfaction")),
run_ids=[str(item) for item in payload.get("run_ids") or []],
skill_names=[str(item) for item in payload.get("skill_names") or []],
feedback=[dict(item) for item in payload.get("feedback") or [] if isinstance(item, dict)],
validation_result=dict(payload["validation_result"]) if isinstance(payload.get("validation_result"), dict) else None,
metadata=dict(payload.get("metadata") or {}),
)
@dataclass(slots=True)
class TaskEvent:
event_id: str
task_id: str
session_id: str
event_type: str
created_at: str
run_id: str | None = None
payload: dict[str, Any] = field(default_factory=dict)
def to_dict(self) -> dict[str, Any]:
return {
"event_id": self.event_id,
"task_id": self.task_id,
"session_id": self.session_id,
"run_id": self.run_id,
"event_type": self.event_type,
"created_at": self.created_at,
"payload": dict(self.payload),
}
@classmethod
def from_dict(cls, payload: dict[str, Any]) -> "TaskEvent":
return cls(
event_id=str(payload["event_id"]),
task_id=str(payload["task_id"]),
session_id=str(payload["session_id"]),
run_id=_optional_str(payload.get("run_id")),
event_type=str(payload.get("event_type") or ""),
created_at=str(payload.get("created_at") or ""),
payload=dict(payload.get("payload") or {}),
)
@dataclass(slots=True)
class MainAgentDecision:
mode: str
reason: str
starts_new_task: bool = False
closes_task: bool = False
abandons_task: bool = False
short_title: str | None = None
action: str = ""
@property
def is_task(self) -> bool:
return self.mode == "task"
def _optional_str(value: Any) -> str | None:
if value in (None, ""):
return None
return str(value)
def _optional_float(value: Any) -> float | None:
if value in (None, ""):
return None
return float(value)