Files
Genarrative/apps/ai-game-creator-shell/src-tauri/src/debug/debug_drafts.rs
T
AIGameCreator App 998e33ca3d 完善AI游戏创作App本地工作区与Agent协作
补齐工作区启动器、非空目录确认和主窗口切换入口

改为运行时配置文件并支持全局与单Agent LLM Provider

接入平台 External API 生成和同步美术资产

补齐本地对话记录、Agent私有记忆和项目黑板

完善Agent状态、历史Run、快照、文件和资产快捷入口

同步AI游戏创作App实施计划和共享决策记录
2026-07-03 01:32:51 +08:00

101 lines
3.9 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! 调试落盘:保存 LLM 原始输出 / 失败输入,便于排查截断、空返回等问题。
//!
//! 本模块用 `#[cfg(all(debug_assertions, not(test)))]` 声明——仅在开发(debug)
//! 且非测试构建中编入二进制;生产 release 与 cargo test 下整个模块与其调用处一并被剔除,
//! 不会往仓库写任何文件。
use crate::unix_millis;
use std::fs;
use std::path::PathBuf;
// 找到仓库根(包含 apps/ai-game-creator-shell/src-tauri/Cargo.toml 的目录),
// 以便把草案落到仓库内而非 tmp 项目目录。
fn repo_root() -> Option<PathBuf> {
let mut roots = Vec::new();
if let Ok(cwd) = std::env::current_dir() {
roots.push(cwd);
}
if let Ok(exe) = std::env::current_exe() {
if let Some(parent) = exe.parent() {
roots.push(parent.to_path_buf());
}
}
for root in roots {
for directory in root.ancestors().take(8) {
let marker = directory
.join("apps")
.join("ai-game-creator-shell")
.join("src-tauri")
.join("Cargo.toml");
if marker.is_file() {
return Some(directory.to_path_buf());
}
}
}
None
}
// 把生成器原始返回(含被截断、解析失败的内容)保存到仓库 .llm-drafts/,
// 便于排查 “EOF while parsing a string” 这类输出截断问题。尽力而为,不阻断主流程。
pub(crate) fn persist_snapshot(raw_content: &str) {
let Some(repo_root) = repo_root() else {
eprintln!("llm.draft.snapshot.skip: 未能定位仓库根目录");
return;
};
let dir = repo_root
.join("apps")
.join("ai-game-creator-shell")
.join(".llm-drafts");
if let Err(error) = fs::create_dir_all(&dir) {
eprintln!("llm.draft.snapshot.dir.failed: {}: {error}", dir.display());
return;
}
let path = dir.join(format!("draft-{}.txt", unix_millis()));
if let Err(error) = fs::write(&path, raw_content) {
eprintln!(
"llm.draft.snapshot.write.failed: {}: {error}",
path.display()
);
return;
}
// 同步更新 latest.txt,方便直接打开最近一次草案。
let latest = dir.join("latest.txt");
let _ = fs::write(&latest, raw_content);
eprintln!("llm.draft.snapshot.saved: {}", path.display());
}
// LLM 调用失败(例如返回内容为空)时,把本次发送给模型的输入(system + 完整 user prompt)
// 连同错误信息一起落盘到 .llm-drafts/,便于按同样输入复现与定位。尽力而为,不阻断主流程。
pub(crate) fn persist_error_input(system_prompt: &str, user_prompt: &str, error: &str) {
let Some(repo_root) = repo_root() else {
eprintln!("llm.draft.error-input.skip: 未能定位仓库根目录");
return;
};
let dir = repo_root
.join("apps")
.join("ai-game-creator-shell")
.join(".llm-drafts");
if let Err(io_error) = fs::create_dir_all(&dir) {
eprintln!(
"llm.draft.error-input.dir.failed: {}: {io_error}",
dir.display()
);
return;
}
let body = format!(
"# LLM 生成失败输入快照\n\n## 错误\n{error}\n\n## System Prompt\n{system_prompt}\n\n## User Prompt(含用户需求/短长期记忆/spec/agenda/组 brief/findings)\n{user_prompt}\n",
);
let path = dir.join(format!("error-input-{}.txt", unix_millis()));
if let Err(io_error) = fs::write(&path, &body) {
eprintln!(
"llm.draft.error-input.write.failed: {}: {io_error}",
path.display()
);
return;
}
// 同步更新 latest-error-input.txt,方便直接打开最近一次失败输入。
let latest = dir.join("latest-error-input.txt");
let _ = fs::write(&latest, &body);
eprintln!("llm.draft.error-input.saved: {}", path.display());
}