修复: DeepSeek 400 全量扫描 + 队列 per-conv 隔离
- openai_compat: 扫描所有 assistant 消息剥离 orphan tool_calls(原仅查末条) - queue 加 conversationId 字段,按会话精准 drain - regenerate/editMessage 只清本会话排队消息 - newConversation 保留旧会话排队消息 - AiError 只清出错会话的队列项
This commit is contained in:
@@ -352,17 +352,15 @@ pub async fn list_branches(
|
||||
return Ok(serde_json::json!({ "current": "", "branches": [] }));
|
||||
}
|
||||
let dir = module.path.clone();
|
||||
let result = std::thread::spawn(move || -> (String, Vec<serde_json::Value>) {
|
||||
// git 命令在 spawn_blocking 中执行(阻塞 IO 不污染 async runtime),10s 超时
|
||||
let result = tokio::task::spawn_blocking(move || -> (String, Vec<serde_json::Value>) {
|
||||
// git branch --format="%(HEAD)%00%(refname:short)"
|
||||
let out = std::process::Command::new("git")
|
||||
.args(["branch", "--format=%(HEAD)%00%(refname:short)"])
|
||||
.current_dir(&dir)
|
||||
.env("LANG", "en_US.UTF-8")
|
||||
.env("LC_ALL", "en_US.UTF-8")
|
||||
.output()
|
||||
.ok()
|
||||
.and_then(|o| if o.status.success() { Some(String::from_utf8_lossy(&o.stdout).to_string()) } else { None })
|
||||
.unwrap_or_default();
|
||||
let out = run_git_cmd(
|
||||
std::path::Path::new(&dir),
|
||||
&["branch", "--format=%(HEAD)%00%(refname:short)"],
|
||||
std::time::Duration::from_secs(10),
|
||||
)
|
||||
.unwrap_or_default();
|
||||
let mut current = String::new();
|
||||
let mut branches = Vec::new();
|
||||
for line in out.lines() {
|
||||
@@ -378,8 +376,8 @@ pub async fn list_branches(
|
||||
}
|
||||
(current, branches)
|
||||
})
|
||||
.join()
|
||||
.map_err(|_| "分支列表查询线程 Join 失败".to_string())?;
|
||||
.await
|
||||
.map_err(|e| format!("分支列表查询任务失败: {e}"))?;
|
||||
|
||||
Ok(serde_json::json!({
|
||||
"current": result.0,
|
||||
@@ -801,26 +799,18 @@ pub async fn get_module_file_diff(
|
||||
return Ok(serde_json::json!({"path": file_path, "diff": ""}));
|
||||
}
|
||||
// 跑 git diff <path>(工作树 vs 索引的 unstaged 变更+staged 变更)
|
||||
// 设置环境变量强制 UTF-8 输出,防中文乱码。
|
||||
// spawn_blocking + run_git_cmd(10s 超时,防 git 卡死阻塞 runtime)。
|
||||
let cmd_dir = root.clone();
|
||||
let path_arg = file_path.replace('\\', "/");
|
||||
let diff = std::thread::spawn(move || -> Option<String> {
|
||||
let out = std::process::Command::new("git")
|
||||
.args(["diff", "--", &path_arg])
|
||||
.current_dir(&cmd_dir)
|
||||
.env("LANG", "en_US.UTF-8")
|
||||
.env("LC_ALL", "en_US.UTF-8")
|
||||
.env("GIT_PAGER", "cat")
|
||||
.output()
|
||||
.ok()?;
|
||||
if out.status.success() && !out.stdout.is_empty() {
|
||||
Some(String::from_utf8_lossy(&out.stdout).to_string())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
let diff = tokio::task::spawn_blocking(move || -> Option<String> {
|
||||
run_git_cmd(
|
||||
&cmd_dir,
|
||||
&["diff", "--", &path_arg],
|
||||
std::time::Duration::from_secs(10),
|
||||
)
|
||||
})
|
||||
.join()
|
||||
.map_err(|_| "git diff 线程 Join 失败".to_string())?
|
||||
.await
|
||||
.map_err(|e| format!("git diff 任务失败: {e}"))?
|
||||
.unwrap_or_default();
|
||||
Ok(serde_json::json!({
|
||||
"path": file_path.replace('\\', "/"),
|
||||
@@ -879,15 +869,19 @@ pub async fn scan_project_modules(
|
||||
if !child_path.join(".git").exists() { continue; }
|
||||
let child_path_str = child_path.to_string_lossy().replace("\\", "/");
|
||||
if existing_paths.contains(&child_path_str.to_lowercase()) { continue; }
|
||||
// 获取远程地址(失败忽略)
|
||||
let git_url = std::process::Command::new("git")
|
||||
.args(["remote", "get-url", "origin"])
|
||||
.current_dir(&child_path)
|
||||
.output()
|
||||
.ok()
|
||||
.filter(|o| o.status.success())
|
||||
.map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string())
|
||||
.filter(|s| !s.is_empty());
|
||||
// 获取远程地址(失败忽略);spawn_blocking + run_git_cmd(10s 超时,防 git 卡死阻塞 runtime)
|
||||
let url_dir = child_path.clone();
|
||||
let git_url = tokio::task::spawn_blocking(move || {
|
||||
run_git_cmd(
|
||||
&url_dir,
|
||||
&["remote", "get-url", "origin"],
|
||||
std::time::Duration::from_secs(10),
|
||||
)
|
||||
.map(|s| s.trim().to_string())
|
||||
.filter(|s| !s.is_empty())
|
||||
})
|
||||
.await
|
||||
.unwrap_or(None);
|
||||
let now_str = now_millis();
|
||||
let record = ProjectModuleRecord {
|
||||
id: new_id(),
|
||||
@@ -976,22 +970,19 @@ pub async fn get_module_commits(
|
||||
let fetch_plus = fetch + 1;
|
||||
let dir = module.path.clone();
|
||||
let dir_for_git = dir.clone();
|
||||
let commits: Vec<serde_json::Value> = std::thread::spawn(move || -> Vec<serde_json::Value> {
|
||||
let out = std::process::Command::new("git")
|
||||
.args([
|
||||
// git 命令在 spawn_blocking 中执行(阻塞 IO 不污染 async runtime),10s 超时
|
||||
let commits: Vec<serde_json::Value> = tokio::task::spawn_blocking(move || -> Vec<serde_json::Value> {
|
||||
let out = run_git_cmd(
|
||||
std::path::Path::new(&dir_for_git),
|
||||
&[
|
||||
"log",
|
||||
&format!("--skip={}", skip),
|
||||
&format!("-{}", fetch_plus),
|
||||
"--format=%h %ct %an %s",
|
||||
])
|
||||
.current_dir(&dir_for_git)
|
||||
.env("LANG", "en_US.UTF-8")
|
||||
.env("LC_ALL", "en_US.UTF-8")
|
||||
.output()
|
||||
.ok()
|
||||
.and_then(|o| if o.status.success() { Some(o.stdout) } else { None })
|
||||
.map(|b| String::from_utf8_lossy(&b).to_string())
|
||||
.unwrap_or_default();
|
||||
],
|
||||
std::time::Duration::from_secs(10),
|
||||
)
|
||||
.unwrap_or_default();
|
||||
let mut commits: Vec<serde_json::Value> = Vec::new();
|
||||
for line in out.lines() {
|
||||
let line = line.trim();
|
||||
@@ -1014,8 +1005,8 @@ pub async fn get_module_commits(
|
||||
}
|
||||
commits
|
||||
})
|
||||
.join()
|
||||
.map_err(|_| "提交历史查询线程 Join 失败".to_string())?;
|
||||
.await
|
||||
.map_err(|e| format!("提交历史查询任务失败: {e}"))?;
|
||||
|
||||
// 判断 has_more:取了 N+1 条但只返回 N 条,说明有更多
|
||||
let total_fetched = commits.len();
|
||||
@@ -1055,17 +1046,17 @@ pub async fn get_commit_detail(
|
||||
return Ok(serde_json::json!({ "files": [], "diff": "" }));
|
||||
}
|
||||
let dir = module.path.clone();
|
||||
let (files, diff, parents, author, date, full_message) = std::thread::spawn(move || -> (Vec<serde_json::Value>, String, Vec<String>, String, String, String) {
|
||||
// git 命令在 spawn_blocking 中执行(阻塞 IO 不污染 async runtime),10s 超时
|
||||
let (files, diff, parents, author, date, full_message) = tokio::task::spawn_blocking(move || -> (Vec<serde_json::Value>, String, Vec<String>, String, String, String) {
|
||||
let dir_path = std::path::Path::new(&dir);
|
||||
let timeout = std::time::Duration::from_secs(10);
|
||||
// 1) 获取变更文件列表:`git diff-tree --no-commit-id -r --name-status <hash>`
|
||||
let files_out = std::process::Command::new("git")
|
||||
.args(["diff-tree", "--no-commit-id", "-r", "--name-status", &commit_hash])
|
||||
.current_dir(&dir)
|
||||
.env("LANG", "en_US.UTF-8")
|
||||
.env("LC_ALL", "en_US.UTF-8")
|
||||
.output()
|
||||
.ok()
|
||||
.and_then(|o| if o.status.success() { Some(String::from_utf8_lossy(&o.stdout).to_string()) } else { None })
|
||||
.unwrap_or_default();
|
||||
let files_out = run_git_cmd(
|
||||
dir_path,
|
||||
&["diff-tree", "--no-commit-id", "-r", "--name-status", &commit_hash],
|
||||
timeout,
|
||||
)
|
||||
.unwrap_or_default();
|
||||
let mut files: Vec<serde_json::Value> = Vec::new();
|
||||
for line in files_out.lines() {
|
||||
let line = line.trim();
|
||||
@@ -1079,26 +1070,20 @@ pub async fn get_commit_detail(
|
||||
}
|
||||
}
|
||||
// 2) 获取全量 diff:`git show <hash>`(仅 diff 部分)
|
||||
let diff = std::process::Command::new("git")
|
||||
.args(["show", "--format=", &commit_hash])
|
||||
.current_dir(&dir)
|
||||
.env("LANG", "en_US.UTF-8")
|
||||
.env("LC_ALL", "en_US.UTF-8")
|
||||
.output()
|
||||
.ok()
|
||||
.and_then(|o| if o.status.success() { Some(String::from_utf8_lossy(&o.stdout).to_string()) } else { None })
|
||||
.unwrap_or_default();
|
||||
let diff = run_git_cmd(
|
||||
dir_path,
|
||||
&["show", "--format=", &commit_hash],
|
||||
timeout,
|
||||
)
|
||||
.unwrap_or_default();
|
||||
// 3) 获取提交元信息(父提交/作者/日期/完整消息):
|
||||
// 用 printf 自定义格式,%P=父哈希(空格分隔多个)\t%an=作者\t%ad=日期\t%B=完整消息
|
||||
let meta = std::process::Command::new("git")
|
||||
.args(["show", "-s", "--format=%P\t%an\t%ad\t%B", &commit_hash])
|
||||
.current_dir(&dir)
|
||||
.env("LANG", "en_US.UTF-8")
|
||||
.env("LC_ALL", "en_US.UTF-8")
|
||||
.output()
|
||||
.ok()
|
||||
.and_then(|o| if o.status.success() { Some(String::from_utf8_lossy(&o.stdout).to_string()) } else { None })
|
||||
.unwrap_or_default();
|
||||
let meta = run_git_cmd(
|
||||
dir_path,
|
||||
&["show", "-s", "--format=%P\t%an\t%ad\t%B", &commit_hash],
|
||||
timeout,
|
||||
)
|
||||
.unwrap_or_default();
|
||||
let mut parents = Vec::new();
|
||||
let mut author = String::new();
|
||||
let mut date = String::new();
|
||||
@@ -1121,8 +1106,8 @@ pub async fn get_commit_detail(
|
||||
}
|
||||
(files, diff, parents, author, date, full_message)
|
||||
})
|
||||
.join()
|
||||
.map_err(|_| "提交详情查询线程 Join 失败".to_string())?;
|
||||
.await
|
||||
.map_err(|e| format!("提交详情查询任务失败: {e}"))?;
|
||||
|
||||
Ok(serde_json::json!({
|
||||
"files": files,
|
||||
|
||||
Reference in New Issue
Block a user