- 传播 trace_id:BusToolCallEmitter/SubAgentEmitter/processor 全链路设置 - AgentEnd 配对:补发 5 个 cancel/defensive 路径,闭合 AgentStart 指标 - LLM 计时修正:attempt_start 移入 retry 循环,排除退避等待时间 - /metrics auth:非 loopback 部署时纳入 Bearer token 校验 - recorder 复用:OnceLock 缓存 PrometheusHandle,热重启后不再返回 503 - 结构化日志:新增 tracing_ctx + JSON 日志格式支持
123 lines
4.2 KiB
Rust
123 lines
4.2 KiB
Rust
pub mod message;
|
|
|
|
pub use crate::domain::messages::ContentBlock;
|
|
pub use message::{
|
|
ChatMessage, InboundMessage, MediaItem, OutboundMessage, SYSTEM_CONTEXT_AGENT_PROMPT,
|
|
SYSTEM_CONTEXT_HISTORY_COMPACTION, SYSTEM_CONTEXT_SCHEDULED_PROMPT,
|
|
};
|
|
|
|
use std::sync::Arc;
|
|
use tokio::sync::{Mutex, mpsc};
|
|
|
|
// ============================================================================
|
|
// MessageBus - async inbound/outbound queues
|
|
// ============================================================================
|
|
|
|
pub struct MessageBus {
|
|
inbound_tx: mpsc::Sender<InboundMessage>,
|
|
outbound_tx: mpsc::Sender<OutboundMessage>,
|
|
inbound_rx: Mutex<mpsc::Receiver<InboundMessage>>,
|
|
outbound_rx: Mutex<mpsc::Receiver<OutboundMessage>>,
|
|
}
|
|
|
|
impl MessageBus {
|
|
/// Create a new MessageBus with the given channel capacity
|
|
pub fn new(capacity: usize) -> Arc<Self> {
|
|
let (inbound_tx, inbound_rx) = mpsc::channel(capacity);
|
|
let (outbound_tx, outbound_rx) = mpsc::channel(capacity);
|
|
Arc::new(Self {
|
|
inbound_tx,
|
|
outbound_tx,
|
|
inbound_rx: Mutex::new(inbound_rx),
|
|
outbound_rx: Mutex::new(outbound_rx),
|
|
})
|
|
}
|
|
|
|
/// Publish a message to the inbound queue
|
|
pub async fn publish_inbound(&self, msg: InboundMessage) -> Result<(), BusError> {
|
|
tracing::debug!(
|
|
channel = %msg.channel,
|
|
sender = %msg.sender_id,
|
|
chat_id = %msg.chat_id,
|
|
trace_id = %msg.trace_id,
|
|
content_len = %msg.content.len(),
|
|
media_count = %msg.media.len(),
|
|
"Bus: publishing inbound message"
|
|
);
|
|
self.inbound_tx
|
|
.send(msg)
|
|
.await
|
|
.map_err(|_| BusError::Closed)
|
|
}
|
|
|
|
/// Consume a message from the inbound queue.
|
|
/// Returns `None` when the channel is closed (all senders dropped).
|
|
pub async fn consume_inbound(&self) -> Option<InboundMessage> {
|
|
let msg = self.inbound_rx.lock().await.recv().await?;
|
|
tracing::debug!(
|
|
channel = %msg.channel,
|
|
sender = %msg.sender_id,
|
|
chat_id = %msg.chat_id,
|
|
trace_id = %msg.trace_id,
|
|
"Bus: consuming inbound message"
|
|
);
|
|
Some(msg)
|
|
}
|
|
|
|
/// Publish a message to the outbound queue.
|
|
///
|
|
/// Uses `try_send` (non-blocking): if the queue is full, the message is
|
|
/// dropped immediately with a warning. This ensures the agent loop is never
|
|
/// blocked by slow or disconnected display consumers. Persistent state is
|
|
/// unaffected — messages are stored in SQLite independently.
|
|
pub async fn publish_outbound(&self, msg: OutboundMessage) -> Result<(), BusError> {
|
|
tracing::debug!(
|
|
channel = %msg.channel,
|
|
chat_id = %msg.chat_id,
|
|
trace_id = %msg.trace_id,
|
|
content_len = %msg.content.len(),
|
|
"Bus: publishing outbound message"
|
|
);
|
|
match self.outbound_tx.try_send(msg) {
|
|
Ok(()) => Ok(()),
|
|
Err(tokio::sync::mpsc::error::TrySendError::Full(msg)) => {
|
|
tracing::warn!(
|
|
channel = %msg.channel,
|
|
chat_id = %msg.chat_id,
|
|
trace_id = %msg.trace_id,
|
|
"Outbound bus full, dropping message"
|
|
);
|
|
Err(BusError::Dropped)
|
|
}
|
|
Err(tokio::sync::mpsc::error::TrySendError::Closed(_)) => Err(BusError::Closed),
|
|
}
|
|
}
|
|
|
|
/// Consume an outbound message from the outbound queue.
|
|
/// Returns `None` when the channel is closed (all senders dropped).
|
|
pub async fn consume_outbound(&self) -> Option<OutboundMessage> {
|
|
self.outbound_rx.lock().await.recv().await
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// BusError
|
|
// ============================================================================
|
|
|
|
#[derive(Debug)]
|
|
pub enum BusError {
|
|
Closed,
|
|
Dropped,
|
|
}
|
|
|
|
impl std::fmt::Display for BusError {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
BusError::Closed => write!(f, "Bus channel closed"),
|
|
BusError::Dropped => write!(f, "Bus full, message dropped"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::error::Error for BusError {}
|