a2bbeb79ee
Agent 聊天在用户消息已保存后将回复失败原因落盘为 assistant 消息 新增 --agent-chat 开发诊断入口验证单 Agent LLM 调用链 补充开发 Agent 聊天失败回执和 CLI 解析测试
201 lines
7.1 KiB
Rust
201 lines
7.1 KiB
Rust
use super::*;
|
|
|
|
#[derive(Debug, Eq, PartialEq)]
|
|
pub(crate) enum CliCommand {
|
|
LlmStatus,
|
|
AgentChat {
|
|
project_path: PathBuf,
|
|
agent_id: String,
|
|
prompt: String,
|
|
},
|
|
AgentRun {
|
|
project_path: PathBuf,
|
|
prompt: String,
|
|
wait_for_enter: bool,
|
|
},
|
|
}
|
|
|
|
pub(crate) fn game_creator_llm_status_lines(status: &GameCreatorLlmConfigStatus) -> Vec<String> {
|
|
let mut lines = vec![
|
|
format!("llm.configured={}", status.configured),
|
|
format!("llm.apiKeyPresent={}", status.api_key_present),
|
|
format!(
|
|
"llm.baseUrl={}",
|
|
status.base_url.as_deref().unwrap_or_default()
|
|
),
|
|
format!("llm.model={}", status.model.as_deref().unwrap_or_default()),
|
|
format!("llm.apiKind={}", status.api_kind),
|
|
format!("llm.stream={}", status.stream),
|
|
];
|
|
for agent in &status.agents {
|
|
lines.push(format!(
|
|
"llm.agent.{}.configured={}",
|
|
agent.agent_id, agent.configured
|
|
));
|
|
lines.push(format!(
|
|
"llm.agent.{}.apiKeyPresent={}",
|
|
agent.agent_id, agent.api_key_present
|
|
));
|
|
lines.push(format!(
|
|
"llm.agent.{}.baseUrl={}",
|
|
agent.agent_id,
|
|
agent.base_url.as_deref().unwrap_or_default()
|
|
));
|
|
lines.push(format!(
|
|
"llm.agent.{}.model={}",
|
|
agent.agent_id,
|
|
agent.model.as_deref().unwrap_or_default()
|
|
));
|
|
lines.push(format!(
|
|
"llm.agent.{}.apiKind={}",
|
|
agent.agent_id, agent.api_kind
|
|
));
|
|
lines.push(format!(
|
|
"llm.agent.{}.stream={}",
|
|
agent.agent_id, agent.stream
|
|
));
|
|
if let Some(error) = agent.error.as_deref() {
|
|
lines.push(format!("llm.agent.{}.error={error}", agent.agent_id));
|
|
}
|
|
}
|
|
if let Some(error) = status.error.as_deref() {
|
|
lines.push(format!("llm.error={error}"));
|
|
}
|
|
lines
|
|
}
|
|
|
|
pub(crate) fn parse_cli_command(args: &[String]) -> Result<Option<CliCommand>, String> {
|
|
if args.first().map(String::as_str) == Some("--llm-status") {
|
|
return Ok(Some(CliCommand::LlmStatus));
|
|
}
|
|
if args.first().map(String::as_str) == Some("--agent-chat") {
|
|
let project_path = args.get(1).map(String::as_str).ok_or_else(|| {
|
|
"用法:--agent-chat <本地项目绝对路径> <agentId> <聊天内容>".to_string()
|
|
})?;
|
|
let agent_id = args.get(2).map(String::as_str).ok_or_else(|| {
|
|
"用法:--agent-chat <本地项目绝对路径> <agentId> <聊天内容>".to_string()
|
|
})?;
|
|
if args.len() < 4 {
|
|
return Err("用法:--agent-chat <本地项目绝对路径> <agentId> <聊天内容>".to_string());
|
|
}
|
|
let prompt = args[3..].join(" ");
|
|
let prompt = prompt.trim();
|
|
if prompt.is_empty() {
|
|
return Err("聊天内容不能为空".to_string());
|
|
}
|
|
return Ok(Some(CliCommand::AgentChat {
|
|
project_path: PathBuf::from(project_path),
|
|
agent_id: agent_id.trim().to_string(),
|
|
prompt: prompt.to_string(),
|
|
}));
|
|
}
|
|
if args.first().map(String::as_str) != Some("--agent-run") {
|
|
return Ok(None);
|
|
}
|
|
let mut rest = args[1..].to_vec();
|
|
let wait_for_enter = if let Some(index) = rest.iter().position(|arg| arg == "--no-wait") {
|
|
rest.remove(index);
|
|
false
|
|
} else {
|
|
true
|
|
};
|
|
let project_path = rest
|
|
.first()
|
|
.map(String::as_str)
|
|
.ok_or_else(|| "用法:--agent-run [--no-wait] <本地项目绝对路径> <创作需求>".to_string())?;
|
|
if rest.len() < 2 {
|
|
return Err("用法:--agent-run [--no-wait] <本地项目绝对路径> <创作需求>".to_string());
|
|
}
|
|
let prompt = rest[1..].join(" ");
|
|
let prompt = prompt.trim();
|
|
if prompt.is_empty() {
|
|
return Err("创作需求不能为空".to_string());
|
|
}
|
|
Ok(Some(CliCommand::AgentRun {
|
|
project_path: PathBuf::from(project_path),
|
|
prompt: prompt.to_string(),
|
|
wait_for_enter,
|
|
}))
|
|
}
|
|
|
|
pub(crate) fn run_cli_command(command: CliCommand) -> Result<(), String> {
|
|
match command {
|
|
CliCommand::LlmStatus => {
|
|
let status = check_game_creator_llm_config_from_config();
|
|
for line in game_creator_llm_status_lines(&status) {
|
|
println!("{line}");
|
|
}
|
|
if status.configured {
|
|
Ok(())
|
|
} else {
|
|
Err("LLM 配置未就绪".to_string())
|
|
}
|
|
}
|
|
CliCommand::AgentChat {
|
|
project_path,
|
|
agent_id,
|
|
prompt,
|
|
} => {
|
|
let runtime = tokio::runtime::Builder::new_multi_thread()
|
|
.enable_all()
|
|
.build()
|
|
.map_err(|error| format!("创建 CLI runtime 失败:{error}"))?;
|
|
let reply = runtime.block_on(chat_with_game_creator_role_agent_at(
|
|
&project_path,
|
|
&agent_id,
|
|
&prompt,
|
|
))?;
|
|
println!("agent.chat.completed");
|
|
println!("projectPath={}", project_path.display());
|
|
println!("agentId={agent_id}");
|
|
println!("replyText={}", reply.reply_text);
|
|
Ok(())
|
|
}
|
|
CliCommand::AgentRun {
|
|
project_path,
|
|
prompt,
|
|
wait_for_enter,
|
|
} => {
|
|
let runtime = tokio::runtime::Builder::new_multi_thread()
|
|
.enable_all()
|
|
.build()
|
|
.map_err(|error| format!("创建 CLI runtime 失败:{error}"))?;
|
|
let result =
|
|
runtime.block_on(generate_local_game_draft_at(&project_path, &prompt, None))?;
|
|
let (preview, stop) = start_local_game_preview_for_project(&project_path)?;
|
|
record_preview_state(
|
|
&project_path,
|
|
GameCreationAppPreviewStatus::Running,
|
|
Some(preview.url.clone()),
|
|
Some(preview.port),
|
|
)?;
|
|
append_preview_log(&project_path, "running", Some(&preview.url))?;
|
|
append_preview_start_trace_step(&project_path, &preview)?;
|
|
println!("agent.run.completed");
|
|
println!("projectPath={}", result.project_path);
|
|
println!("gameIndexPath={}", result.game_index_path);
|
|
println!("designPath={}", result.design_path);
|
|
println!(
|
|
"tracePath={}",
|
|
project_path.join(".agent/run.latest.json").display()
|
|
);
|
|
println!("previewUrl={}", preview.url);
|
|
if wait_for_enter {
|
|
println!("按 Enter 停止本地预览。");
|
|
let mut line = String::new();
|
|
let _ = std::io::stdin().read_line(&mut line);
|
|
}
|
|
let _ = stop.send(());
|
|
let _ = record_preview_state(
|
|
&project_path,
|
|
GameCreationAppPreviewStatus::Stopped,
|
|
None,
|
|
None,
|
|
);
|
|
let _ = append_preview_log(&project_path, "stopped", None);
|
|
let _ = append_preview_stop_trace_step(&project_path);
|
|
Ok(())
|
|
}
|
|
}
|
|
}
|