"""司命台治理对象数据访问(api-spec §19.4)。 目标 / 感知通道 / 世界实体 / 计划 / 天工阁角色的 CRUD 数据访问; 均为本地单用户全局对象,不挂 character_id。 """ from sqlalchemy import func, select from sqlalchemy.ext.asyncio import AsyncSession from app.core.time import utc_now from app.models.governance import ( AgentRosterEntry, Goal, PerceptionChannel, Plan, ToolPolicy, WorldEntity, ) # —— 太一枢 · 目标 —— async def list_goals(session: AsyncSession) -> list[Goal]: result = await session.execute( select(Goal).order_by(Goal.priority.desc(), Goal.updated_at.desc()) ) return list(result.scalars().all()) async def get_goal(session: AsyncSession, goal_id: str) -> Goal | None: return await session.get(Goal, goal_id) async def add_goal(session: AsyncSession, goal: Goal) -> Goal: session.add(goal) await session.flush() return goal async def list_goals_by_character( session: AsyncSession, character_id: str, status_filter: str | None = None, ) -> list[Goal]: """按 character_id 获取目标列表;可选的 status 过滤。""" stmt = select(Goal).where(Goal.character_id == character_id) if status_filter: stmt = stmt.where(Goal.status == status_filter) stmt = stmt.order_by(Goal.priority.desc(), Goal.updated_at.desc()) result = await session.execute(stmt) return list(result.scalars().all()) async def update_goal_progress( session: AsyncSession, goal_id: str, progress: float, ) -> Goal | None: """更新目标的 progress 与 updated_at 字段。""" goal = await session.get(Goal, goal_id) if goal is None: return None goal.progress = progress goal.updated_at = utc_now() await session.flush() return goal async def list_active_session_goal(session: AsyncSession, character_id: str) -> Goal | None: """查找指定人物活跃的 session 级目标(各人物至多一个活跃 session 目标)。""" result = await session.execute( select(Goal) .where( Goal.character_id == character_id, Goal.level == "session", Goal.status == "active", ) .limit(1) ) return result.scalar_one_or_none() # —— 烛照枢 · 感知通道 —— async def list_perception_channels(session: AsyncSession) -> list[PerceptionChannel]: result = await session.execute(select(PerceptionChannel).order_by(PerceptionChannel.channel)) return list(result.scalars().all()) async def get_perception_channel(session: AsyncSession, channel: str) -> PerceptionChannel | None: return await session.get(PerceptionChannel, channel) async def add_perception_channel( session: AsyncSession, channel: PerceptionChannel ) -> PerceptionChannel: session.add(channel) await session.flush() return channel # —— 河图枢 · 世界实体 —— async def list_world_entities(session: AsyncSession) -> list[WorldEntity]: result = await session.execute(select(WorldEntity).order_by(WorldEntity.updated_at.desc())) return list(result.scalars().all()) async def get_world_entity(session: AsyncSession, entity_id: str) -> WorldEntity | None: return await session.get(WorldEntity, entity_id) async def add_world_entity(session: AsyncSession, entity: WorldEntity) -> WorldEntity: session.add(entity) await session.flush() return entity # —— 洛书枢 · 计划 —— async def list_plans(session: AsyncSession) -> list[Plan]: result = await session.execute(select(Plan).order_by(Plan.updated_at.desc())) return list(result.scalars().all()) async def get_plan(session: AsyncSession, plan_id: str) -> Plan | None: return await session.get(Plan, plan_id) async def add_plan(session: AsyncSession, plan: Plan) -> Plan: session.add(plan) await session.flush() return plan # —— 天工阁枢 · 角色在岗 —— async def list_agent_roster(session: AsyncSession) -> list[AgentRosterEntry]: result = await session.execute(select(AgentRosterEntry).order_by(AgentRosterEntry.role)) return list(result.scalars().all()) async def get_agent(session: AsyncSession, role: str) -> AgentRosterEntry | None: return await session.get(AgentRosterEntry, role) async def add_agent(session: AsyncSession, agent: AgentRosterEntry) -> AgentRosterEntry: session.add(agent) await session.flush() return agent # —— 百工枢 · 工具策略(§15/§16 共享)—— async def list_tool_policies(session: AsyncSession) -> list[ToolPolicy]: result = await session.execute(select(ToolPolicy).order_by(ToolPolicy.name)) return list(result.scalars().all()) async def get_tool_policy(session: AsyncSession, name: str) -> ToolPolicy | None: return await session.get(ToolPolicy, name) async def add_tool_policy(session: AsyncSession, policy: ToolPolicy) -> ToolPolicy: session.add(policy) await session.flush() return policy # —— 计数(供九枢健康度聚合)—— async def count(session: AsyncSession, model: type) -> int: result = await session.execute(select(func.count()).select_from(model)) return int(result.scalar_one())