chore: 增强日志追踪与代码格式化
- daemon: 添加 LLM 回合开始/结束标记日志,工具执行结果追踪 - daemon: 发送消费者日志增强(显示完整文本、截断用户 ID) - conversation: 移除日志截断,输出完整工具调用参数和结果 - deepseek: 代码格式化,预留请求/响应摘要日志 - 删除 systemd/ias.service
This commit is contained in:
+141
-99
@@ -109,9 +109,9 @@ pub async fn run(database: &Arc<Database>, memory_store: &Arc<MemoryStore>) {
|
|||||||
info!("Daemon 已启动");
|
info!("Daemon 已启动");
|
||||||
|
|
||||||
// 4-6. 审批管理器、工具列表、模型配置
|
// 4-6. 审批管理器、工具列表、模型配置
|
||||||
let approval_manager = Arc::new(ApprovalManager::new(
|
let approval_manager = Arc::new(ApprovalManager::new(Some(Arc::new(
|
||||||
Some(Arc::new(database.pool().clone())),
|
database.pool().clone(),
|
||||||
));
|
))));
|
||||||
let tools_list = build_tools_list();
|
let tools_list = build_tools_list();
|
||||||
let system_prompt = std::env::var("WEIXIN_LLM_SYSTEM_PROMPT").unwrap_or_else(|_| String::new());
|
let system_prompt = std::env::var("WEIXIN_LLM_SYSTEM_PROMPT").unwrap_or_else(|_| String::new());
|
||||||
let model = std::env::var("DEEPSEEK_MODEL").unwrap_or_else(|_| "deepseek-v4-flash".to_string());
|
let model = std::env::var("DEEPSEEK_MODEL").unwrap_or_else(|_| "deepseek-v4-flash".to_string());
|
||||||
@@ -158,28 +158,28 @@ pub async fn run(database: &Arc<Database>, memory_store: &Arc<MemoryStore>) {
|
|||||||
let sched_enqueue = enqueue_handle.clone();
|
let sched_enqueue = enqueue_handle.clone();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let scheduler = crate::scheduler::Scheduler::new(Some(Arc::new(sched_db)));
|
let scheduler = crate::scheduler::Scheduler::new(Some(Arc::new(sched_db)));
|
||||||
scheduler
|
scheduler
|
||||||
.run(move |to: &str, name: &str, msg: &str| {
|
.run(move |to: &str, name: &str, msg: &str| {
|
||||||
let eq = sched_enqueue.clone();
|
let eq = sched_enqueue.clone();
|
||||||
let uid = to.to_string();
|
let uid = to.to_string();
|
||||||
let text = msg.to_string();
|
let text = msg.to_string();
|
||||||
let tn = name.to_string();
|
let tn = name.to_string();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
eq.enqueue(PipelineMessage {
|
eq.enqueue(PipelineMessage {
|
||||||
channel: ChannelId::wechat(uid),
|
channel: ChannelId::wechat(uid),
|
||||||
correlation_id: Uuid::new_v4(),
|
correlation_id: Uuid::new_v4(),
|
||||||
kind: MessageKind::ScheduledTask {
|
kind: MessageKind::ScheduledTask {
|
||||||
text,
|
text,
|
||||||
task_name: tn,
|
task_name: tn,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
});
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
});
|
});
|
||||||
info!("定时任务调度器已启动");
|
info!("定时任务调度器已启动");
|
||||||
|
|
||||||
// 12. QueueRunner
|
// 12. QueueRunner
|
||||||
let runner_handle = tokio::spawn(async move {
|
let runner_handle = tokio::spawn(async move {
|
||||||
@@ -349,8 +349,6 @@ async fn llm_consumer_loop(
|
|||||||
context_token,
|
context_token,
|
||||||
approved_tool,
|
approved_tool,
|
||||||
} => {
|
} => {
|
||||||
info!("LLM Consumer: user={} text={:.60}", user_id, text);
|
|
||||||
|
|
||||||
// 加载数据
|
// 加载数据
|
||||||
ctx.memory_store.load_if_needed(&user_id).await;
|
ctx.memory_store.load_if_needed(&user_id).await;
|
||||||
let history = load_history(&ctx.db, &user_id).await;
|
let history = load_history(&ctx.db, &user_id).await;
|
||||||
@@ -399,45 +397,46 @@ async fn llm_consumer_loop(
|
|||||||
let session = conv.session();
|
let session = conv.session();
|
||||||
let approved_tool = approved_tool.clone();
|
let approved_tool = approved_tool.clone();
|
||||||
|
|
||||||
let executor: ToolExecutor = Arc::new(move |name: &str, args_json: &str, tc_id: &str| {
|
let executor: ToolExecutor =
|
||||||
let eq = eq.clone();
|
Arc::new(move |name: &str, args_json: &str, tc_id: &str| {
|
||||||
let uid = uid.clone();
|
let eq = eq.clone();
|
||||||
let corr = corr;
|
let uid = uid.clone();
|
||||||
let session = session.clone();
|
let corr = corr;
|
||||||
let n = name.to_string();
|
let session = session.clone();
|
||||||
let args = args_json.to_string();
|
let n = name.to_string();
|
||||||
let is_approved = approved_tool.as_ref() == Some(&n);
|
let args = args_json.to_string();
|
||||||
let tool_call_id = tc_id.to_string();
|
let is_approved = approved_tool.as_ref() == Some(&n);
|
||||||
|
let tool_call_id = tc_id.to_string();
|
||||||
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let ch = ChannelId::wechat(&uid);
|
let ch = ChannelId::wechat(&uid);
|
||||||
let sid = { session.lock().await.session_id };
|
let sid = { session.lock().await.session_id };
|
||||||
if is_approved {
|
if is_approved {
|
||||||
eq.enqueue(PipelineMessage::approved_tool_call(
|
eq.enqueue(PipelineMessage::approved_tool_call(
|
||||||
ch,
|
ch,
|
||||||
corr,
|
corr,
|
||||||
sid,
|
sid,
|
||||||
&tool_call_id,
|
&tool_call_id,
|
||||||
&n,
|
&n,
|
||||||
&args,
|
&args,
|
||||||
None,
|
None,
|
||||||
))
|
))
|
||||||
.await;
|
.await;
|
||||||
} else {
|
} else {
|
||||||
eq.enqueue(PipelineMessage::tool_call(
|
eq.enqueue(PipelineMessage::tool_call(
|
||||||
ch,
|
ch,
|
||||||
corr,
|
corr,
|
||||||
sid,
|
sid,
|
||||||
&tool_call_id,
|
&tool_call_id,
|
||||||
&n,
|
&n,
|
||||||
&args,
|
&args,
|
||||||
None,
|
None,
|
||||||
))
|
))
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
Ok(format!("{}{}", ASYNC_MARKER, n))
|
Ok(format!("{}{}", ASYNC_MARKER, n))
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
conv.set_tool_executor(executor);
|
conv.set_tool_executor(executor);
|
||||||
|
|
||||||
// 缓存 Conversation(记录创建时间用于 TTL)
|
// 缓存 Conversation(记录创建时间用于 TTL)
|
||||||
@@ -465,11 +464,6 @@ async fn llm_consumer_loop(
|
|||||||
result,
|
result,
|
||||||
context_token,
|
context_token,
|
||||||
} => {
|
} => {
|
||||||
info!(
|
|
||||||
"LLM Consumer: 工具结果回喂 user={} tool={}",
|
|
||||||
user_id, tool_name
|
|
||||||
);
|
|
||||||
|
|
||||||
// 从缓存恢复会话,注入工具结果并恢复 LLM 循环
|
// 从缓存恢复会话,注入工具结果并恢复 LLM 循环
|
||||||
let should_resume = {
|
let should_resume = {
|
||||||
let mut map = sessions.lock().await;
|
let mut map = sessions.lock().await;
|
||||||
@@ -532,6 +526,8 @@ async fn run_llm_round(
|
|||||||
sessions: &Arc<Mutex<HashMap<Uuid, (Conversation, Instant)>>>,
|
sessions: &Arc<Mutex<HashMap<Uuid, (Conversation, Instant)>>>,
|
||||||
enqueue: &EnqueueHandle,
|
enqueue: &EnqueueHandle,
|
||||||
) {
|
) {
|
||||||
|
info!("══════════════════════════════════════════════════");
|
||||||
|
info!("🔄 LLM 回合开始 user={} text={}", user_id, text);
|
||||||
let result = {
|
let result = {
|
||||||
let map = sessions.lock().await;
|
let map = sessions.lock().await;
|
||||||
let conv = match map.get(&correlation_id) {
|
let conv = match map.get(&correlation_id) {
|
||||||
@@ -575,6 +571,8 @@ async fn resume_llm_round(
|
|||||||
sessions: &Arc<Mutex<HashMap<Uuid, (Conversation, Instant)>>>,
|
sessions: &Arc<Mutex<HashMap<Uuid, (Conversation, Instant)>>>,
|
||||||
enqueue: &EnqueueHandle,
|
enqueue: &EnqueueHandle,
|
||||||
) {
|
) {
|
||||||
|
info!("══════════════════════════════════════════════════");
|
||||||
|
info!("🔄 LLM 恢复 user={}", user_id);
|
||||||
let result = {
|
let result = {
|
||||||
let map = sessions.lock().await;
|
let map = sessions.lock().await;
|
||||||
let conv = match map.get(&correlation_id) {
|
let conv = match map.get(&correlation_id) {
|
||||||
@@ -636,6 +634,7 @@ async fn handle_chat_result(
|
|||||||
enqueue.enqueue(msg).await;
|
enqueue.enqueue(msg).await;
|
||||||
}
|
}
|
||||||
sessions.lock().await.remove(&correlation_id);
|
sessions.lock().await.remove(&correlation_id);
|
||||||
|
info!("✅ LLM 回合结束 user={}", user_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Tool Consumer ───
|
// ─── Tool Consumer ───
|
||||||
@@ -660,7 +659,10 @@ async fn tool_consumer_loop(
|
|||||||
context_token,
|
context_token,
|
||||||
approved,
|
approved,
|
||||||
} => {
|
} => {
|
||||||
info!("Tool Consumer: user={} tool={}", user_id, tool_name);
|
info!(
|
||||||
|
"Tool Consumer: user={} tool={} args={}",
|
||||||
|
user_id, tool_name, arguments
|
||||||
|
);
|
||||||
|
|
||||||
// 高风险工具 → 审批(除非已被用户批准)
|
// 高风险工具 → 审批(除非已被用户批准)
|
||||||
if !approved && crate::tools::is_high_risk(&tool_name) {
|
if !approved && crate::tools::is_high_risk(&tool_name) {
|
||||||
@@ -734,7 +736,9 @@ fn execute_tool<'a>(
|
|||||||
// 上下文工具
|
// 上下文工具
|
||||||
match name {
|
match name {
|
||||||
"read_memories" => {
|
"read_memories" => {
|
||||||
return ctx.memory_store.read_for(user_id, None).await;
|
let result = ctx.memory_store.read_for(user_id, None).await;
|
||||||
|
tracing::info!("🔧 execute_tool: read_memories → {}", result);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
"write_memory" => {
|
"write_memory" => {
|
||||||
let params: serde_json::Value = serde_json::from_str(arguments).unwrap_or_default();
|
let params: serde_json::Value = serde_json::from_str(arguments).unwrap_or_default();
|
||||||
@@ -743,7 +747,9 @@ fn execute_tool<'a>(
|
|||||||
return "请提供 content 参数".to_string();
|
return "请提供 content 参数".to_string();
|
||||||
}
|
}
|
||||||
ctx.memory_store.write_for(user_id, content).await;
|
ctx.memory_store.write_for(user_id, content).await;
|
||||||
return format!("已添加长期记忆: {}", content);
|
let result = format!("已添加长期记忆: {}", content);
|
||||||
|
tracing::info!("🔧 execute_tool: write_memory → {}", result);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
"delete_memory" => {
|
"delete_memory" => {
|
||||||
let params: serde_json::Value = serde_json::from_str(arguments).unwrap_or_default();
|
let params: serde_json::Value = serde_json::from_str(arguments).unwrap_or_default();
|
||||||
@@ -754,7 +760,9 @@ fn execute_tool<'a>(
|
|||||||
if memory_id <= 0 {
|
if memory_id <= 0 {
|
||||||
return "请提供有效的 memory_id 参数".to_string();
|
return "请提供有效的 memory_id 参数".to_string();
|
||||||
}
|
}
|
||||||
return ctx.memory_store.delete_for(user_id, memory_id).await;
|
let result = ctx.memory_store.delete_for(user_id, memory_id).await;
|
||||||
|
tracing::info!("🔧 execute_tool: delete_memory({}) → {}", memory_id, result);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
"update_memory" => {
|
"update_memory" => {
|
||||||
let params: serde_json::Value = serde_json::from_str(arguments).unwrap_or_default();
|
let params: serde_json::Value = serde_json::from_str(arguments).unwrap_or_default();
|
||||||
@@ -769,26 +777,35 @@ fn execute_tool<'a>(
|
|||||||
if content.is_empty() {
|
if content.is_empty() {
|
||||||
return "请提供 content 参数".to_string();
|
return "请提供 content 参数".to_string();
|
||||||
}
|
}
|
||||||
return ctx
|
let result = ctx
|
||||||
.memory_store
|
.memory_store
|
||||||
.update_for(user_id, memory_id, content)
|
.update_for(user_id, memory_id, content)
|
||||||
.await;
|
.await;
|
||||||
|
tracing::info!("🔧 execute_tool: update_memory({}) → {}", memory_id, result);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
"read_summaries" => {
|
"read_summaries" => {
|
||||||
let sums = load_summaries(&ctx.db, user_id).await;
|
let sums = load_summaries(&ctx.db, user_id).await;
|
||||||
if sums.is_empty() {
|
let result = if sums.is_empty() {
|
||||||
return "暂无历史摘要".to_string();
|
"暂无历史摘要".to_string()
|
||||||
}
|
} else {
|
||||||
return sums
|
sums.iter()
|
||||||
.iter()
|
.enumerate()
|
||||||
.enumerate()
|
.map(|(i, s)| format!("{}. {}", i + 1, s))
|
||||||
.map(|(i, s)| format!("{}. {}", i + 1, s))
|
.collect::<Vec<_>>()
|
||||||
.collect::<Vec<_>>()
|
.join("\n---\n")
|
||||||
.join("\n---\n");
|
};
|
||||||
|
tracing::info!("🔧 execute_tool: read_summaries → {}条", sums.len());
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
"query_capabilities" => {
|
"query_capabilities" => {
|
||||||
let specs = crate::tools::specs();
|
let specs = crate::tools::specs();
|
||||||
return crate::tools::build_capability_guide(&specs);
|
let result = crate::tools::build_capability_guide(&specs);
|
||||||
|
tracing::info!(
|
||||||
|
"🔧 execute_tool: query_capabilities → {}个工具",
|
||||||
|
specs.len()
|
||||||
|
);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
"call_capability" => {
|
"call_capability" => {
|
||||||
// 解包 {name, prompt} → 提取目标工具名和参数,递归派发
|
// 解包 {name, prompt} → 提取目标工具名和参数,递归派发
|
||||||
@@ -807,6 +824,11 @@ fn execute_tool<'a>(
|
|||||||
}
|
}
|
||||||
let target_args = serde_json::to_string(&crate::tools::unpack_call_params(&cp))
|
let target_args = serde_json::to_string(&crate::tools::unpack_call_params(&cp))
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
tracing::info!(
|
||||||
|
"🔧 execute_tool: call_capability → {} args={}",
|
||||||
|
target_name,
|
||||||
|
target_args
|
||||||
|
);
|
||||||
// 递归派发
|
// 递归派发
|
||||||
return execute_tool(&target_name, &target_args, ctx, user_id).await;
|
return execute_tool(&target_name, &target_args, ctx, user_id).await;
|
||||||
}
|
}
|
||||||
@@ -815,8 +837,19 @@ fn execute_tool<'a>(
|
|||||||
|
|
||||||
// 内置工具注册表
|
// 内置工具注册表
|
||||||
match crate::tools::execute(name, arguments).await {
|
match crate::tools::execute(name, arguments).await {
|
||||||
Some(r) => r.output,
|
Some(r) => {
|
||||||
None => format!("未知工具: {}", name),
|
tracing::info!(
|
||||||
|
"🔧 execute_tool: {} → success={} output={}",
|
||||||
|
name,
|
||||||
|
r.success,
|
||||||
|
r.output
|
||||||
|
);
|
||||||
|
r.output
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
tracing::warn!("🔧 execute_tool: 未知工具 {}", name);
|
||||||
|
format!("未知工具: {}", name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -836,7 +869,12 @@ async fn send_consumer_loop(
|
|||||||
context_token,
|
context_token,
|
||||||
} => {
|
} => {
|
||||||
let user_id = &msg.channel.user_id;
|
let user_id = &msg.channel.user_id;
|
||||||
info!("Send Consumer: user={} text_len={}", user_id, text.len());
|
info!(
|
||||||
|
"Send Consumer: user={} text_len={} text={}",
|
||||||
|
user_id,
|
||||||
|
text.len(),
|
||||||
|
text
|
||||||
|
);
|
||||||
|
|
||||||
match ctx
|
match ctx
|
||||||
.client
|
.client
|
||||||
@@ -844,18 +882,22 @@ async fn send_consumer_loop(
|
|||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(sent_id) => {
|
Ok(sent_id) => {
|
||||||
info!("回复成功 user={} msg_id={}", user_id, sent_id);
|
info!(
|
||||||
|
"回复成功 user={} msg_id={}",
|
||||||
|
user_id.chars().take(5).collect::<String>(),
|
||||||
|
sent_id
|
||||||
|
);
|
||||||
let _ = crate::db::models::insert_chat_record(
|
let _ = crate::db::models::insert_chat_record(
|
||||||
ctx.db.pool(),
|
ctx.db.pool(),
|
||||||
"outbound",
|
"outbound",
|
||||||
user_id,
|
user_id,
|
||||||
&ctx.account_id,
|
&ctx.account_id,
|
||||||
&text,
|
&text,
|
||||||
"llm",
|
"llm",
|
||||||
context_token.as_deref(),
|
context_token.as_deref(),
|
||||||
&sent_id,
|
&sent_id,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
Err(e) => error!("发送回复失败: {}", e),
|
Err(e) => error!("发送回复失败: {}", e),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -313,7 +313,7 @@ impl Conversation {
|
|||||||
calls.len(),
|
calls.len(),
|
||||||
calls
|
calls
|
||||||
.iter()
|
.iter()
|
||||||
.map(|c| format!("{}({:.80})", c.function.name, c.function.arguments))
|
.map(|c| format!("{}({})", c.function.name, c.function.arguments))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(", ")
|
.join(", ")
|
||||||
);
|
);
|
||||||
@@ -344,7 +344,7 @@ impl Conversation {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
tracing::info!("📦 {} → {:.150}", tc.function.name, result);
|
tracing::info!("📦 {} → {}", tc.function.name, result);
|
||||||
self.session.lock().await.add_tool_result(
|
self.session.lock().await.add_tool_result(
|
||||||
&tc.id,
|
&tc.id,
|
||||||
&tc.function.name,
|
&tc.function.name,
|
||||||
|
|||||||
+38
-23
@@ -154,6 +154,13 @@ async fn stream_deepseek(
|
|||||||
return Err(format!("HTTP {}: {}", status, text));
|
return Err(format!("HTTP {}: {}", status, text));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 记录请求摘要
|
||||||
|
// tracing::info!(
|
||||||
|
// "🌐 LLM 请求: model={} messages_count={}",
|
||||||
|
// body.get("model").and_then(|v| v.as_str()).unwrap_or("?"),
|
||||||
|
// body.get("messages").and_then(|v| v.as_array()).map(|a| a.len()).unwrap_or(0),
|
||||||
|
// );
|
||||||
|
|
||||||
let mut full_text = String::new();
|
let mut full_text = String::new();
|
||||||
let mut full_reasoning = String::new();
|
let mut full_reasoning = String::new();
|
||||||
let mut usage: Option<Usage> = None;
|
let mut usage: Option<Usage> = None;
|
||||||
@@ -216,33 +223,33 @@ async fn stream_deepseek(
|
|||||||
|
|
||||||
// 检查最后的 buffer
|
// 检查最后的 buffer
|
||||||
if !buf.trim().is_empty()
|
if !buf.trim().is_empty()
|
||||||
&& let Some(parsed) = parse_chat_chunk(buf.trim()) {
|
&& let Some(parsed) = parse_chat_chunk(buf.trim())
|
||||||
match parsed {
|
{
|
||||||
ParsedChunk::Text(t) => full_text.push_str(&t),
|
match parsed {
|
||||||
ParsedChunk::ToolCallDelta {
|
ParsedChunk::Text(t) => full_text.push_str(&t),
|
||||||
index,
|
ParsedChunk::ToolCallDelta {
|
||||||
id,
|
index,
|
||||||
name,
|
id,
|
||||||
arguments,
|
name,
|
||||||
} => {
|
arguments,
|
||||||
let entry =
|
} => {
|
||||||
tool_call_builders
|
let entry = tool_call_builders
|
||||||
.entry(index)
|
.entry(index)
|
||||||
.or_insert((None, None, String::new()));
|
.or_insert((None, None, String::new()));
|
||||||
if let Some(call_id) = id {
|
if let Some(call_id) = id {
|
||||||
entry.0 = Some(call_id);
|
entry.0 = Some(call_id);
|
||||||
}
|
|
||||||
if let Some(call_name) = name {
|
|
||||||
entry.1 = Some(call_name);
|
|
||||||
}
|
|
||||||
entry.2.push_str(&arguments);
|
|
||||||
}
|
}
|
||||||
ParsedChunk::Usage(u) => {
|
if let Some(call_name) = name {
|
||||||
usage = Some(u);
|
entry.1 = Some(call_name);
|
||||||
}
|
}
|
||||||
_ => {}
|
entry.2.push_str(&arguments);
|
||||||
}
|
}
|
||||||
|
ParsedChunk::Usage(u) => {
|
||||||
|
usage = Some(u);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 构建 tool_calls
|
// 构建 tool_calls
|
||||||
let tool_calls: Option<Vec<ToolCall>> = if tool_call_builders.is_empty() {
|
let tool_calls: Option<Vec<ToolCall>> = if tool_call_builders.is_empty() {
|
||||||
@@ -267,6 +274,14 @@ async fn stream_deepseek(
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 记录完整的响应输出
|
||||||
|
// tracing::info!(
|
||||||
|
// "🌐 LLM 响应: text_len={} tool_calls={} usage={:?}",
|
||||||
|
// full_text.len(),
|
||||||
|
// tool_calls.as_ref().map(|tc| tc.len()).unwrap_or(0),
|
||||||
|
// usage
|
||||||
|
// );
|
||||||
|
|
||||||
let _ = tx
|
let _ = tx
|
||||||
.send(StreamChunk::Done {
|
.send(StreamChunk::Done {
|
||||||
text: full_text,
|
text: full_text,
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
[Unit]
|
|
||||||
Description=iAs - 微信 AI 智能助手
|
|
||||||
After=network.target postgresql.service
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=simple
|
|
||||||
User=xiao
|
|
||||||
WorkingDirectory=/home/xiao/project/iAs
|
|
||||||
EnvironmentFile=/home/xiao/project/iAs/.env
|
|
||||||
ExecStart=/home/xiao/project/iAs/target/release/iAs service
|
|
||||||
Restart=on-failure
|
|
||||||
RestartSec=10
|
|
||||||
StandardOutput=null
|
|
||||||
StandardError=null
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
Reference in New Issue
Block a user