落地并存测服全端:智能体 Graph/路由、后台切 8180、主账与部署脚本齐备,便于线上重发与验收。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
"""
|
||||
AIBOT 入站入口:桥进程把长连接消息转到本路由。
|
||||
|
||||
本文件职责:校验 bridge token、归一 InboundMessage、入 inbox 快回。
|
||||
禁止:在本请求线程调 LLM / Graph / 出站企微。
|
||||
路径:/internal/aibot/inbound(对齐 prompt/13)。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any, Optional
|
||||
|
||||
from fastapi import APIRouter, Header, HTTPException
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from agent.channel.queue import get_message_store
|
||||
from agent.channel.wecom.models import InboundMessage
|
||||
from agent.config import get_settings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
router = APIRouter(prefix="/internal/aibot", tags=["aibot"])
|
||||
|
||||
|
||||
class AibotInboundBody(BaseModel):
|
||||
"""桥转发的归一字段(桥侧已解析,智能体不再调企微换 ID)。"""
|
||||
|
||||
sender_id: str = Field(description="企微 userid,必须来自 AIBOT 报文")
|
||||
message_id: str
|
||||
content: str = ""
|
||||
msg_type: str = "text"
|
||||
chat_id: str = ""
|
||||
chat_type: str = "group"
|
||||
agent_id: str = ""
|
||||
create_time: int = 0
|
||||
media: dict[str, Any] = Field(default_factory=dict)
|
||||
raw: dict[str, Any] = Field(default_factory=dict)
|
||||
|
||||
|
||||
def _check_bridge_token(x_token: Optional[str]) -> None:
|
||||
s = get_settings()
|
||||
expected = (getattr(s, "wecom_aibot_bridge_token", None) or "").strip()
|
||||
if not expected:
|
||||
if (s.ytd_env or "").lower() == "prod":
|
||||
raise HTTPException(status_code=503, detail="AIBOT bridge token 未配置")
|
||||
logger.warning("aibot inbound:未配置 bridge token,test 跳过校验")
|
||||
return
|
||||
if (x_token or "").strip() != expected:
|
||||
raise HTTPException(status_code=401, detail="unauthorized")
|
||||
|
||||
|
||||
@router.get("/ping")
|
||||
def aibot_ping() -> dict[str, str]:
|
||||
return {"status": "ok", "channel": "aibot"}
|
||||
|
||||
|
||||
@router.post("/inbound")
|
||||
def aibot_inbound(
|
||||
body: AibotInboundBody,
|
||||
x_aibot_bridge_token: Optional[str] = Header(
|
||||
default=None, alias="X-Aibot-Bridge-Token"
|
||||
),
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
接收桥转发入站:无 sender_id 拒绝;其余入 inbox。
|
||||
"""
|
||||
_check_bridge_token(x_aibot_bridge_token)
|
||||
msg = InboundMessage(
|
||||
sender_id=body.sender_id.strip(),
|
||||
message_id=body.message_id.strip(),
|
||||
content=body.content or "",
|
||||
msg_type=body.msg_type or "text",
|
||||
chat_id=body.chat_id or "",
|
||||
chat_type=body.chat_type or "group",
|
||||
agent_id=body.agent_id or "",
|
||||
create_time=int(body.create_time or 0),
|
||||
media=body.media or {},
|
||||
raw=body.raw or {},
|
||||
)
|
||||
if not msg.has_sender():
|
||||
raise HTTPException(status_code=400, detail="missing_sender_id")
|
||||
if not msg.message_id:
|
||||
raise HTTPException(status_code=400, detail="missing_message_id")
|
||||
|
||||
ok, info = get_message_store().enqueue_inbound(msg)
|
||||
logger.info(
|
||||
"aibot.inbound ok=%s info=%s sender=%s msg_id=%s",
|
||||
ok,
|
||||
info,
|
||||
msg.sender_id,
|
||||
msg.message_id,
|
||||
)
|
||||
return {"status": "accepted" if ok else "duplicate", "detail": info}
|
||||
Reference in New Issue
Block a user