feat: add optional native mqtt and udp transport

This commit is contained in:
caixypromise
2026-07-27 02:10:55 +08:00
parent 0c582ed3b6
commit eac573706d
35 changed files with 6325 additions and 200 deletions
@@ -23,8 +23,20 @@ class GoodbyeProcessor(MessageProcessor):
if isinstance(msg_json, dict) and msg_json.get("type") == "goodbye":
logger.info(f"收到goodbye: session_id={msg_json.get('session_id')}")
# 长连接传输仅结束逻辑会话,短连接传输关闭连接
# WebSocket 直接关闭连接;MQTT/UDP 仅结束音频会话,保持连接
if transport.keeps_connection_between_sessions:
end_call = getattr(
getattr(context, "server", None),
"end_native_mqtt_call",
None,
)
if callable(end_call):
await end_call(
context.device_id,
"设备结束通话",
notify_device=False,
expected_session_id=msg_json.get("session_id"),
)
end_conversation = getattr(context, "end_conversation", None)
if callable(end_conversation):
await end_conversation(msg_json.get("session_id"))
@@ -15,6 +15,7 @@ from core.processors.abort_processor import AbortProcessor
from core.processors.goodbye_processor import GoodbyeProcessor
from core.processors.text_processor import TextProcessor
from core.processors.ping_processor import PingProcessor
from core.processors.udp_timeout_processor import UdpTimeoutProcessor
from config.logger import setup_logging
logger = setup_logging()
@@ -40,6 +41,7 @@ class MessageRouter(MessageProcessor):
self.audio_receive_processor = AudioReceiveProcessor()
self.text_processor = TextProcessor()
self.ping_processor = PingProcessor()
self.udp_timeout_processor = UdpTimeoutProcessor()
# 按优先级排序的processor列表
self.processors: List[MessageProcessor] = [
@@ -48,6 +50,7 @@ class MessageRouter(MessageProcessor):
self.server_processor, # 服务器消息(管理端下发动作)
self.abort_processor, # 中断消息
self.goodbye_processor, # goodbye消息
self.udp_timeout_processor,
self.hello_processor, # hello消息
self.ping_processor, # 可选JSON ping/pong心跳
self.listen_processor, # listen消息
@@ -19,7 +19,7 @@ class TimeoutProcessor(MessageProcessor):
context: SessionContext,
transport: TransportInterface,
) -> bool:
"""End an expired logical conversation without closing a long connection."""
"""End an expired logical conversation without conflating it with MQTT."""
if not getattr(context, "conversation_active", False):
return False
@@ -29,7 +29,7 @@ class TimeoutProcessor(MessageProcessor):
try:
if transport.keeps_connection_between_sessions:
logger.info(f"会话超时,结束长连接逻辑会话: {context.session_id}")
logger.info(f"会话超时,结束MQTT逻辑会话: {context.session_id}")
from core.processors.audio_receive_processor import (
AudioReceiveProcessor,
)
@@ -0,0 +1,52 @@
import json
from typing import Any
from core.context.session_context import SessionContext
from core.pipeline.message_pipeline import MessageProcessor
from core.transport.transport_interface import TransportInterface
from config.logger import setup_logging
logger = setup_logging()
class UdpTimeoutProcessor(MessageProcessor):
async def process(
self,
context: SessionContext,
transport: TransportInterface,
message: Any,
) -> bool:
if isinstance(message, str):
try:
message = json.loads(message)
except json.JSONDecodeError:
return False
if not isinstance(message, dict) or message.get("type") != "udp_timeout":
return False
if not transport.keeps_connection_between_sessions:
return False
logger.info(
"收到udp_timeout: session_id={}", message.get("session_id")
)
end_call = getattr(
getattr(context, "server", None),
"end_native_mqtt_call",
None,
)
call_ended = False
if callable(end_call):
call_ended = await end_call(
context.device_id,
"设备UDP接收超时",
notify_device=True,
expected_session_id=message.get("session_id"),
)
if not call_ended:
end_conversation = getattr(context, "end_conversation", None)
if callable(end_conversation):
await end_conversation(message.get("session_id"))
await transport.end_session(
message.get("session_id") or context.session_id
)
return True