100 lines
2.9 KiB
Python
100 lines
2.9 KiB
Python
"""叩心枢会话/消息创建与一炁事件推送。
|
||
|
||
设计 docs/domain/core-domains.md§7。
|
||
|
||
主动会话优先复用已有活跃会话,避免每次触发都新建会话。
|
||
只有在角色无活跃会话时才会创建新会话。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from datetime import UTC, datetime
|
||
from typing import Any
|
||
|
||
from sqlalchemy.ext.asyncio import AsyncSession
|
||
|
||
from app.core.logging import logger
|
||
from app.models.chat import ChatMessage, ChatSession
|
||
from app.modules.runtime.events import Event
|
||
from app.services.event_store import DbEventStore
|
||
|
||
|
||
async def create_proactive_message(
|
||
session: AsyncSession,
|
||
event_store: DbEventStore,
|
||
character_id: str,
|
||
opener_text: str,
|
||
condition_name: str,
|
||
*,
|
||
session_id: str | None = None,
|
||
now: datetime | None = None,
|
||
) -> dict[str, Any]:
|
||
"""创建叩心枢主动发起的消息,可追加到现有会话或新建会话。
|
||
|
||
策略:
|
||
- 若 ``session_id`` 不为空,追加消息到该会话(不创建新会话)
|
||
- 若 ``session_id`` 为空,新建会话 + 消息
|
||
|
||
Returns:
|
||
{"session_id": str, "message_id": str}: 实际写入的会话 ID 和消息 ID。
|
||
"""
|
||
now = now or datetime.now(UTC)
|
||
is_new = session_id is None
|
||
|
||
if is_new:
|
||
# 创建新会话(status 默认 'active')
|
||
chat_session = ChatSession(
|
||
character_id=character_id,
|
||
title=opener_text[:60],
|
||
)
|
||
session.add(chat_session)
|
||
await session.flush()
|
||
session_id = chat_session.id
|
||
event_type = "proactive.session"
|
||
else:
|
||
event_type = "proactive.message"
|
||
|
||
# 创建开场消息(无 run_id——非流式、无推理过程)
|
||
message = ChatMessage(
|
||
character_id=character_id,
|
||
session_id=session_id,
|
||
role="assistant",
|
||
modality="text",
|
||
content=opener_text,
|
||
)
|
||
session.add(message)
|
||
await session.flush()
|
||
|
||
# 推一炁事件
|
||
payload: dict[str, Any] = {
|
||
"session_id": session_id,
|
||
"message_id": message.id,
|
||
"character_id": character_id,
|
||
"preview": opener_text[:100],
|
||
"trigger": condition_name,
|
||
"is_new": is_new,
|
||
}
|
||
if is_new:
|
||
payload["title_preview"] = opener_text[:60]
|
||
|
||
event = Event(
|
||
type=event_type,
|
||
source="knocking_pivot",
|
||
character_id=character_id,
|
||
payload=payload,
|
||
visibility="public",
|
||
created_at=now.isoformat(),
|
||
)
|
||
# 复用调用方的 session——同一事务写 events 表,避免 session A 写 chat 表、
|
||
# session B 写 events 表时两条连接争写导致锁冲突
|
||
await event_store.append(event, session=session)
|
||
|
||
logger.info(
|
||
f"knocking_pivot_{'session_created' if is_new else 'message_appended'}",
|
||
character_id=character_id,
|
||
session_id=session_id,
|
||
message_id=message.id,
|
||
trigger=condition_name,
|
||
)
|
||
return {"session_id": session_id, "message_id": message.id}
|