96 lines
3.4 KiB
Python
96 lines
3.4 KiB
Python
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
from typing import Any
|
|
|
|
from core.quote_state_machine import QuoteStateMachine
|
|
|
|
|
|
def refresh_quote_phase(state: Any, phase_hint: str = "") -> None:
|
|
"""统一维护收图报价状态机。"""
|
|
QuoteStateMachine().refresh(state, phase_hint=phase_hint)
|
|
|
|
|
|
def sync_pending_quote_state(agent: Any, customer_id: str, state: Any) -> None:
|
|
"""把待报价队列同步到客户库,避免重启丢失。"""
|
|
try:
|
|
refresh_quote_phase(state)
|
|
from db.customer_db import db
|
|
|
|
db.update_pending_quote_state(
|
|
customer_id,
|
|
state.pending_image_urls,
|
|
state.pending_requirements,
|
|
)
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
def restore_pending_quote_state(customer_id: str, state: Any) -> None:
|
|
"""从客户库恢复待报价队列。"""
|
|
try:
|
|
from db.customer_db import db
|
|
|
|
profile = db.get_customer(customer_id)
|
|
state.pending_image_urls = list(getattr(profile, "pending_quote_images", []) or [])
|
|
state.pending_requirements = list(getattr(profile, "pending_quote_requirements", []) or [])
|
|
state.image_count = len(state.pending_image_urls)
|
|
refresh_quote_phase(state)
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
def cleanup_inactive(conversations: dict, message_histories: dict, now: datetime) -> None:
|
|
"""清理超过 7 天没有消息的对话状态,释放内存。"""
|
|
if len(conversations) % 100 != 0:
|
|
return
|
|
expired = [
|
|
cid
|
|
for cid, state in conversations.items()
|
|
if state.last_update and (now - datetime.fromisoformat(state.last_update)).days > 7
|
|
]
|
|
for cid in expired:
|
|
conversations.pop(cid, None)
|
|
message_histories.pop(cid, None)
|
|
|
|
|
|
def get_conversation_state(agent: Any, customer_id: str) -> Any:
|
|
"""获取或创建对话状态,超时自动重置。"""
|
|
now = datetime.now()
|
|
|
|
if customer_id in agent.conversations:
|
|
state = agent.conversations[customer_id]
|
|
if state.last_update:
|
|
try:
|
|
last = datetime.fromisoformat(state.last_update)
|
|
hours = (now - last).total_seconds() / 3600
|
|
if hours > agent.CONVERSATION_TIMEOUT_HOURS:
|
|
state.stage = "售前"
|
|
state.discount_count = 0
|
|
agent.message_histories.pop(customer_id, None)
|
|
except Exception:
|
|
pass
|
|
if not state.pending_image_urls and not state.pending_requirements:
|
|
restore_pending_quote_state(customer_id, state)
|
|
else:
|
|
agent.conversations[customer_id] = agent.ConversationStateClass(
|
|
customer_id=customer_id,
|
|
last_update=now.isoformat(),
|
|
)
|
|
restore_pending_quote_state(customer_id, agent.conversations[customer_id])
|
|
|
|
cleanup_inactive(agent.conversations, agent.message_histories, now)
|
|
return agent.conversations[customer_id]
|
|
|
|
|
|
def should_defer_batch_quote(agent: Any, state: Any, mark_ready: bool = False) -> bool:
|
|
"""批量报价延后控制。"""
|
|
agent.quote_state_machine.delay_turns = max(0, int(agent.batch_quote_delay_turns))
|
|
return agent.quote_state_machine.should_defer_batch_quote(state, mark_ready=mark_ready)
|
|
|
|
|
|
def mark_quote_ready(agent: Any, state: Any) -> None:
|
|
"""仅标记 ready 状态,不消费等待轮次。"""
|
|
agent.quote_state_machine.delay_turns = max(0, int(agent.batch_quote_delay_turns))
|
|
agent.quote_state_machine.mark_ready(state)
|