persistent auth in SQLite, API chat/logs, agent completion via channel
- Auth: move from state.json to SQLite authed_chats table, with memory cache - Remove Persistent/state.json, all state now in noc.db - HTTP API: POST /api/chat (end-to-end LLM), GET /api/logs (failed API requests) - API logging: store raw request/response for 400 errors in api_log table - Agent completion: spawn_agent sends LifeEvent::AgentDone via channel, life loop picks up with full conversation context and responds - Config structs: derive Clone for HTTP server - System prompt: instruct LLM not to add timestamps - Makefile: rsync without --delete to preserve VPS-only tools
This commit is contained in:
49
src/life.rs
49
src/life.rs
@@ -19,6 +19,15 @@ const DIARY_SCHEDULE: &str = "cron:0 55 22 * * *";
|
||||
pub enum LifeEvent {
|
||||
/// Force-fire a specific timer by ID.
|
||||
FireTimer(i64),
|
||||
/// A sub-agent completed — feed result back through LLM.
|
||||
AgentDone {
|
||||
id: String,
|
||||
chat_id: i64,
|
||||
session_id: String,
|
||||
task: String,
|
||||
output: String,
|
||||
exit_code: Option<i32>,
|
||||
},
|
||||
}
|
||||
|
||||
pub async fn life_loop(
|
||||
@@ -54,6 +63,46 @@ pub async fn life_loop(
|
||||
warn!(timer_id = id, "force-fire: timer not found");
|
||||
}
|
||||
}
|
||||
LifeEvent::AgentDone { id, chat_id: cid, session_id, task, output, exit_code } => {
|
||||
info!(agent = %id, session = %session_id, "agent done, notifying");
|
||||
let preview = crate::display::truncate_at_char_boundary(&output, 3000);
|
||||
let notification = format!(
|
||||
"[子代理 '{id}' 完成 (exit={exit_code:?})]\n任务: {task}\n输出:\n{preview}"
|
||||
);
|
||||
|
||||
// load conversation context so LLM knows what was discussed
|
||||
let conv = state.load_conv(&session_id).await;
|
||||
let persona = state.get_config("persona").await.unwrap_or_default();
|
||||
let memory_slots = state.get_memory_slots().await;
|
||||
let inner = state.get_inner_state().await;
|
||||
|
||||
let system = crate::stream::build_system_prompt(
|
||||
&conv.summary, &persona, &memory_slots, &inner,
|
||||
);
|
||||
|
||||
let mut messages = vec![system];
|
||||
// include recent conversation history
|
||||
messages.extend(conv.messages.iter().cloned());
|
||||
// append the agent completion as a new user message
|
||||
messages.push(serde_json::json!({"role": "user", "content": notification}));
|
||||
|
||||
if let BackendConfig::OpenAI { ref endpoint, ref model, ref api_key } = config.backend {
|
||||
let chat_id_tg = ChatId(cid);
|
||||
let sid = format!("agent-{id}");
|
||||
let mut tg_output;
|
||||
let mut buf_output;
|
||||
let out: &mut dyn crate::output::Output = if cid == 0 {
|
||||
buf_output = BufferOutput::new();
|
||||
&mut buf_output
|
||||
} else {
|
||||
tg_output = TelegramOutput::new(bot.clone(), chat_id_tg, true);
|
||||
&mut tg_output
|
||||
};
|
||||
let _ = run_openai_with_tools(
|
||||
endpoint, model, api_key, messages, out, &state, &sid, &config, cid,
|
||||
).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user