refactor(deep_compression): 改造深度压缩消息结构、提示词加载与前端展示
- 删除 compact 文件和 inject guide 中的最近工具记录 - 新结构:用户所有输入(按压缩轮次分段)+ 历次压缩总结 + 最近一次输入 - 用户输入分段标记改为 <第N次压缩> 和 <当前触发的第X次压缩> - 压缩总结提示词迁移到 prompts/deep_compression_summary.txt - deep_compression_records 增加 user_inputs_before 和 summary 字段 - 压缩后清空全部 frozen prompt 缓存 - 修复 wait 模式 in-place 压缩前端不刷新问题 - 更新手动压缩确认弹窗文案 - 修复 _apply_workspace_personalization_preferences 测试 mock 签名 - 清理 context.py 中误导性的主提示词构建参数
This commit is contained in:
parent
c5dde27faa
commit
f75e2f07a3
@ -1,6 +1,5 @@
|
||||
import asyncio
|
||||
import json
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Set
|
||||
|
||||
@ -380,18 +379,8 @@ class MainTerminalContextMixin:
|
||||
|
||||
def _build_main_system_prompt() -> str:
|
||||
system_prompt_template = self.load_prompt(prompt_name)
|
||||
container_path = self.container_mount_path or "/workspace"
|
||||
container_cpus = self.container_cpu_limit
|
||||
container_memory = self.container_memory_limit
|
||||
project_storage = self.project_storage_limit
|
||||
# main_system.txt / main_system_qwenvl.txt 仅使用 {model_description}
|
||||
return system_prompt_template.format(
|
||||
project_path=container_path,
|
||||
container_path=container_path,
|
||||
container_cpus=container_cpus,
|
||||
container_memory=container_memory,
|
||||
project_storage=project_storage,
|
||||
file_tree=context["project_info"]["file_tree"],
|
||||
current_time=datetime.now().strftime("%Y-%m-%d %H"),
|
||||
model_description=prompt_replacements.get("model_description", "")
|
||||
)
|
||||
|
||||
|
||||
17
prompts/deep_compression_summary.txt
Normal file
17
prompts/deep_compression_summary.txt
Normal file
@ -0,0 +1,17 @@
|
||||
由于当前对话过长,系统正在自动压缩。请你基于已有上下文输出一份可继续执行的工作总结,要求:
|
||||
1) 任务目标与用户真实诉求:用户到底想让你完成什么。
|
||||
2) 已完成工作:按时间顺序列出关键步骤,包含涉及文件和核心结果。
|
||||
3) 关键决策与原因:为什么这样选,而不是别的方案。
|
||||
4) 修改过的文件与核心变更点:具体到文件路径和改动概要。
|
||||
5) 工具调用中的重要结果/错误与修复:哪些尝试失败过,最终怎么解决的。
|
||||
6) 风险与注意事项:继续工作时需要规避的问题。
|
||||
7) 当前正在执行的任务与进度:正在做什么、做到什么程度、卡在哪里。
|
||||
8) 下一步具体行动:必须足够具体,至少包含以下信息:
|
||||
- 如果要读取文件,列出具体文件路径。
|
||||
- 如果要搜索,列出搜索关键词和范围。
|
||||
- 如果要修改文件,说明文件路径和预期改动。
|
||||
- 如果要运行命令,列出具体命令。
|
||||
- 如果要验证,说明验证方式和预期结果。
|
||||
请使用中文,结构清晰,尽量具体,不要省略关键上下文。
|
||||
不要考虑过往对话,只考虑当前对话任务。
|
||||
禁止调用任何工具,必须直接输出总结内容。
|
||||
@ -1253,6 +1253,29 @@ def compress_conversation(conversation_id, terminal: WebTerminal, workspace: Use
|
||||
)
|
||||
response_payload["auto_task_started"] = False
|
||||
response_payload["guide_inserted"] = True
|
||||
# 通知前端实时显示这条 compact 消息(覆盖 socket 与 in-place 未刷新场景)
|
||||
try:
|
||||
emit(
|
||||
"user_message",
|
||||
{
|
||||
"message": guide_message,
|
||||
"images": [],
|
||||
"videos": [],
|
||||
"media_refs": [],
|
||||
"message_source": "compression_handoff",
|
||||
"visibility": "compact",
|
||||
"starts_work": True,
|
||||
"metadata": {
|
||||
"message_source": "compression_handoff",
|
||||
"visibility": "compact",
|
||||
"starts_work": True,
|
||||
},
|
||||
"conversation_id": normalized_id,
|
||||
},
|
||||
room=f"user_{username}",
|
||||
)
|
||||
except Exception as emit_exc:
|
||||
debug_log(f"[Compression] 发送 user_message 事件失败: {emit_exc}")
|
||||
except Exception as exc:
|
||||
debug_log(f"[Compression] 追加引导语消息失败: {exc}")
|
||||
response_payload["auto_task_started"] = False
|
||||
|
||||
@ -7,22 +7,14 @@ from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
|
||||
SUMMARY_PROMPT = (
|
||||
"由于当前对话过长,系统正在自动压缩。请你基于已有上下文输出一份可继续执行的工作总结,要求:\n"
|
||||
"1) 任务目标与用户真实诉求\n"
|
||||
"2) 已完成工作(按时间顺序)\n"
|
||||
"3) 关键决策与原因\n"
|
||||
"4) 修改过的文件与核心变更点\n"
|
||||
"5) 工具调用中的重要结果/错误与修复\n"
|
||||
"6) 风险与注意事项\n"
|
||||
"7) 当前正在执行的任务与进度(正在做什么、做到什么程度、卡在哪里)\n"
|
||||
"8) 下一步具体行动(可直接执行的第一步,尽量具体)\n"
|
||||
"请使用中文,结构清晰,尽量具体,不要省略关键上下文。\n"
|
||||
"不要考虑过往对话,只考虑当前对话任务。\n"
|
||||
"禁止调用任何工具,必须直接输出总结内容。"
|
||||
)
|
||||
|
||||
GUIDE_USER_MESSAGE_TEMPLATE = "当前对话已经被自动压缩(第{count}次)。请阅读{path}并继续工作"
|
||||
def _load_summary_prompt(web_terminal) -> str:
|
||||
"""从 prompts/deep_compression_summary.txt 加载压缩总结提示词。"""
|
||||
try:
|
||||
return web_terminal.load_prompt("deep_compression_summary").strip()
|
||||
except Exception:
|
||||
return (
|
||||
"由于当前对话过长,系统正在自动压缩。请你基于已有上下文输出一份可继续执行的工作总结。"
|
||||
)
|
||||
|
||||
|
||||
def _emit(sender, event_type: str, payload: Dict[str, Any]):
|
||||
@ -49,12 +41,18 @@ def _normalize_deep_compression_records(metadata: Dict[str, Any]) -> List[Dict[s
|
||||
path = str(item.get("compact_file") or "").strip()
|
||||
if count <= 0 or not path:
|
||||
continue
|
||||
try:
|
||||
user_inputs_before = int(item.get("user_inputs_before", 0) or 0)
|
||||
except Exception:
|
||||
user_inputs_before = 0
|
||||
normalized.append({
|
||||
"count": count,
|
||||
"compact_file": path,
|
||||
"created_at": item.get("created_at"),
|
||||
"source_conversation_id": item.get("source_conversation_id"),
|
||||
"compressed_conversation_id": item.get("compressed_conversation_id"),
|
||||
"user_inputs_before": user_inputs_before,
|
||||
"summary": str(item.get("summary") or ""),
|
||||
})
|
||||
normalized.sort(key=lambda x: (int(x.get("count") or 0), str(x.get("created_at") or "")))
|
||||
deduped: List[Dict[str, Any]] = []
|
||||
@ -68,19 +66,9 @@ def _normalize_deep_compression_records(metadata: Dict[str, Any]) -> List[Dict[s
|
||||
return deduped
|
||||
|
||||
|
||||
def _build_guide_message(*, compression_index: int, compact_file: str, previous_records: List[Dict[str, Any]]) -> str:
|
||||
def _build_guide_message(*, compression_index: int, compact_file: str) -> str:
|
||||
"""生成文件模式的引导语:仅提示压缩文件位置,由模型自行阅读。"""
|
||||
base = GUIDE_USER_MESSAGE_TEMPLATE.format(count=compression_index, path=compact_file)
|
||||
if not previous_records:
|
||||
return base
|
||||
lines = [base, "", "此前压缩摘要文件位置:"]
|
||||
for rec in previous_records:
|
||||
count = int(rec.get("count") or 0)
|
||||
path = str(rec.get("compact_file") or "").strip()
|
||||
if count <= 0 or not path:
|
||||
continue
|
||||
lines.append(f"- 第{count}次:{path}")
|
||||
return "\n".join(lines).strip()
|
||||
return f"当前对话已经被第{compression_index}次压缩。请阅读 {compact_file} 并继续工作。"
|
||||
|
||||
|
||||
def _read_compact_file_content(project_path: Path, relative_path: str) -> str:
|
||||
@ -95,29 +83,96 @@ def _read_compact_file_content(project_path: Path, relative_path: str) -> str:
|
||||
return ""
|
||||
|
||||
|
||||
def _read_summary_from_record(record: Dict[str, Any]) -> str:
|
||||
"""从 deep_compression_records 条目中读取保存的总结内容。"""
|
||||
summary = record.get("summary")
|
||||
return str(summary).strip() if isinstance(summary, str) else ""
|
||||
|
||||
|
||||
def _build_user_inputs_section(
|
||||
user_inputs: List[str],
|
||||
previous_records: List[Dict[str, Any]],
|
||||
current_count: int,
|
||||
) -> str:
|
||||
"""构建'用户的所有输入'区块,按历史压缩轮次插入分段标记。"""
|
||||
if not user_inputs:
|
||||
return "(无)"
|
||||
breakpoints: Dict[int, int] = {}
|
||||
for rec in previous_records:
|
||||
count = int(rec.get("count") or 0)
|
||||
before = int(rec.get("user_inputs_before") or 0)
|
||||
if count > 0 and before > 0:
|
||||
breakpoints[before] = count
|
||||
sorted_breaks = sorted(breakpoints.items(), key=lambda x: x[0])
|
||||
break_iter = iter(sorted_breaks)
|
||||
next_break = next(break_iter, None)
|
||||
lines: List[str] = []
|
||||
last_break_index = 0
|
||||
for idx, text in enumerate(user_inputs, start=1):
|
||||
lines.append(f"{idx}. {text}")
|
||||
if next_break and idx == next_break[0]:
|
||||
lines.append(f"<第{next_break[1]}次压缩>")
|
||||
last_break_index = idx
|
||||
next_break = next(break_iter, None)
|
||||
# 当前压缩之后还有新增输入时,追加当前压缩标记
|
||||
if len(user_inputs) > last_break_index:
|
||||
lines.append(f"<当前触发的第{current_count}次压缩>")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def _build_summaries_section(
|
||||
previous_records: List[Dict[str, Any]],
|
||||
current_summary: str,
|
||||
current_count: int,
|
||||
) -> List[str]:
|
||||
"""构建'历次压缩总结'区块,按顺序列出每次压缩的总结。"""
|
||||
lines: List[str] = []
|
||||
for rec in previous_records:
|
||||
count = int(rec.get("count") or 0)
|
||||
if count <= 0:
|
||||
continue
|
||||
summary = _read_summary_from_record(rec)
|
||||
lines.append(f"### 第{count}次的总结")
|
||||
lines.append(summary or "(读取失败)")
|
||||
lines.append("")
|
||||
lines.append(f"### 第{current_count}次的总结")
|
||||
lines.append(current_summary or "(生成失败)")
|
||||
return lines
|
||||
|
||||
|
||||
def _build_inject_guide_message(
|
||||
*,
|
||||
project_path: Path,
|
||||
compression_index: int,
|
||||
current_record: Dict[str, Any],
|
||||
previous_records: List[Dict[str, Any]],
|
||||
user_inputs: List[str],
|
||||
latest_user_input: str,
|
||||
) -> str:
|
||||
"""生成直接注入模式的引导语:把历次压缩文件全文按顺序拼入正文,不提及文件位置。"""
|
||||
lines: List[str] = [f"当前对话已被第{compression_index}次压缩,以下为历次压缩的完整工作总结,请据此继续工作。"]
|
||||
all_records = list(previous_records) + [current_record]
|
||||
for rec in all_records:
|
||||
count = int(rec.get("count") or 0)
|
||||
rel_path = str(rec.get("compact_file") or "").strip()
|
||||
if count <= 0 or not rel_path:
|
||||
continue
|
||||
content = _read_compact_file_content(project_path, rel_path)
|
||||
lines.append("")
|
||||
lines.append(f"第{count}次压缩:")
|
||||
lines.append(content or "(压缩文件内容读取失败)")
|
||||
"""生成直接注入模式的引导语:把历次压缩总结、用户输入按顺序拼入正文。"""
|
||||
lines: List[str] = [
|
||||
f"当前对话已被第{compression_index}次压缩。以下为按时间顺序汇总的用户输入、历次压缩总结以及最近一次输入,请据此继续工作。",
|
||||
"",
|
||||
"用户的所有输入",
|
||||
]
|
||||
lines.append(_build_user_inputs_section(user_inputs, previous_records, compression_index))
|
||||
lines.append("")
|
||||
lines.append("历次压缩总结")
|
||||
lines.append("")
|
||||
summary_lines = _build_summaries_section(
|
||||
previous_records,
|
||||
current_record.get("summary", ""),
|
||||
compression_index,
|
||||
)
|
||||
lines.extend(summary_lines)
|
||||
lines.append("")
|
||||
lines.append("用户的最近一次输入:")
|
||||
if (latest_user_input or "").strip():
|
||||
lines.append(latest_user_input.strip())
|
||||
else:
|
||||
lines.append("(无)")
|
||||
return "\n".join(lines).strip()
|
||||
|
||||
|
||||
|
||||
def _extract_text_only(content: Any) -> str:
|
||||
if content is None:
|
||||
return ""
|
||||
@ -136,48 +191,6 @@ def _extract_text_only(content: Any) -> str:
|
||||
return str(content)
|
||||
|
||||
|
||||
def _extract_tool_arg_map(messages: List[Dict[str, Any]]) -> Dict[str, Dict[str, Any]]:
|
||||
tool_map: Dict[str, Dict[str, Any]] = {}
|
||||
for msg in messages:
|
||||
if msg.get("role") != "assistant":
|
||||
continue
|
||||
for tc in msg.get("tool_calls") or []:
|
||||
tc_id = tc.get("id") or tc.get("tool_call_id")
|
||||
if not tc_id:
|
||||
continue
|
||||
func = tc.get("function") or {}
|
||||
name = func.get("name") or tc.get("name") or "unknown_tool"
|
||||
args_raw = func.get("arguments")
|
||||
args_obj: Any = args_raw
|
||||
if isinstance(args_raw, str):
|
||||
try:
|
||||
args_obj = json.loads(args_raw)
|
||||
except Exception:
|
||||
args_obj = args_raw
|
||||
tool_map[tc_id] = {"name": name, "arguments": args_obj}
|
||||
return tool_map
|
||||
|
||||
|
||||
def _collect_last_tool_entries(messages: List[Dict[str, Any]], limit: int = 5, max_content_chars: int = 3000) -> List[Dict[str, Any]]:
|
||||
tool_arg_map = _extract_tool_arg_map(messages)
|
||||
entries: List[Dict[str, Any]] = []
|
||||
for msg in messages:
|
||||
if msg.get("role") != "tool":
|
||||
continue
|
||||
tc_id = msg.get("tool_call_id") or msg.get("id")
|
||||
mapping = tool_arg_map.get(tc_id, {})
|
||||
content_text = _extract_text_only(msg.get("content"))
|
||||
if len(content_text) > max_content_chars:
|
||||
content_text = content_text[:max_content_chars] + "\n...(已截断)"
|
||||
entries.append({
|
||||
"tool_call_id": tc_id,
|
||||
"tool_name": msg.get("name") or mapping.get("name") or "unknown_tool",
|
||||
"arguments": mapping.get("arguments"),
|
||||
"content": content_text,
|
||||
})
|
||||
return entries[-max(1, limit):]
|
||||
|
||||
|
||||
def _collect_user_texts(messages: List[Dict[str, Any]]) -> List[str]:
|
||||
result: List[str] = []
|
||||
for msg in messages:
|
||||
@ -230,45 +243,31 @@ def _write_compact_file(
|
||||
compression_index: int,
|
||||
summary_text: str,
|
||||
user_inputs: List[str],
|
||||
last_tools: List[Dict[str, Any]],
|
||||
latest_user_input: str,
|
||||
previous_records: List[Dict[str, Any]],
|
||||
) -> str:
|
||||
compact_dir = project_path / ".agents" / "compact_result"
|
||||
compact_dir.mkdir(parents=True, exist_ok=True)
|
||||
filename = f"compact_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{compression_index:03d}.md"
|
||||
file_path = compact_dir / filename
|
||||
|
||||
lines: List[str] = [
|
||||
f"# 对话已被第{compression_index}次压缩",
|
||||
"",
|
||||
"## 工作总结",
|
||||
summary_text or "生成总结失败",
|
||||
"",
|
||||
"## 用户的所有输入(仅文字)",
|
||||
"## 用户的所有输入",
|
||||
]
|
||||
if user_inputs:
|
||||
for idx, text in enumerate(user_inputs, start=1):
|
||||
lines.append(f"{idx}. {text}")
|
||||
else:
|
||||
lines.append("- (无)")
|
||||
lines.extend(["", "## 最近5条工具调用(参数 + 结果)"])
|
||||
if last_tools:
|
||||
for idx, item in enumerate(last_tools, start=1):
|
||||
lines.extend([
|
||||
f"### {idx}. {item.get('tool_name')}",
|
||||
f"- tool_call_id: {item.get('tool_call_id')}",
|
||||
"- 参数:",
|
||||
"```json",
|
||||
json.dumps(item.get("arguments"), ensure_ascii=False, indent=2),
|
||||
"```",
|
||||
"- 结果:",
|
||||
"```text",
|
||||
str(item.get("content") or ""),
|
||||
"```",
|
||||
"",
|
||||
])
|
||||
else:
|
||||
lines.append("- (无)")
|
||||
lines.extend(["", "## 用户最新的一次输入"])
|
||||
lines.append(_build_user_inputs_section(user_inputs, previous_records, compression_index))
|
||||
lines.append("")
|
||||
lines.append("## 历次压缩总结")
|
||||
lines.append("")
|
||||
summary_lines = _build_summaries_section(
|
||||
previous_records,
|
||||
summary_text,
|
||||
compression_index,
|
||||
)
|
||||
lines.extend(summary_lines)
|
||||
lines.append("")
|
||||
lines.append("## 用户最新的一次输入")
|
||||
if (latest_user_input or "").strip():
|
||||
lines.append(latest_user_input.strip())
|
||||
else:
|
||||
@ -322,7 +321,7 @@ async def run_deep_compression(
|
||||
return {"success": False, "error": "对话正在压缩中", "in_progress": True}
|
||||
|
||||
# 读取个性化压缩设置:
|
||||
# - compress_form: file(生成文件,引导语提示位置) / inject(把历次压缩文件全文注入引导语)
|
||||
# - compress_form: file(生成文件,引导语提示位置) / inject(把历次压缩内容注入引导语)
|
||||
# - compress_behavior: continue(注入引导语并触发请求) / wait(仅注入引导语,等待用户)
|
||||
# compress_behavior 仅作用于手动压缩;自动深压缩永远继续工作。
|
||||
try:
|
||||
@ -368,7 +367,7 @@ async def run_deep_compression(
|
||||
"job_id": job_id,
|
||||
})
|
||||
|
||||
summary_text, summary_fail_reason = await _generate_summary(web_terminal, SUMMARY_PROMPT, retries=5)
|
||||
summary_text, summary_fail_reason = await _generate_summary(web_terminal, _load_summary_prompt(web_terminal), retries=5)
|
||||
if summary_fail_reason:
|
||||
_emit(sender, "system_message", {"content": f"自动压缩总结失败,将使用失败占位文本:{summary_fail_reason}"})
|
||||
|
||||
@ -389,14 +388,15 @@ async def run_deep_compression(
|
||||
messages = conv_data.get("messages") or []
|
||||
user_inputs = _collect_user_texts(messages)
|
||||
latest_user_input = user_inputs[-1] if user_inputs else ""
|
||||
last_tools = _collect_last_tool_entries(messages, limit=5, max_content_chars=3000)
|
||||
user_inputs_before = len(user_inputs)
|
||||
|
||||
relative_compact_path = _write_compact_file(
|
||||
Path(workspace.project_path),
|
||||
compression_index=target_count,
|
||||
summary_text=summary_text,
|
||||
user_inputs=user_inputs,
|
||||
last_tools=last_tools,
|
||||
latest_user_input=latest_user_input,
|
||||
previous_records=previous_records,
|
||||
)
|
||||
|
||||
cm.set_compression_state(
|
||||
@ -407,7 +407,6 @@ async def run_deep_compression(
|
||||
)
|
||||
|
||||
# === in-place 压缩:不创建/切换新对话,只把当前对话历史前缀打上 deep_compacted 标记 ===
|
||||
# 当前对话已在函数开头对齐为目标对话,这里直接对内存历史打标。
|
||||
now_iso = datetime.now().isoformat()
|
||||
marked_count = _mark_history_compacted(
|
||||
cm.conversation_history or [],
|
||||
@ -439,31 +438,40 @@ async def run_deep_compression(
|
||||
"created_at": now_iso,
|
||||
"source_conversation_id": conversation_id,
|
||||
"compressed_conversation_id": conversation_id,
|
||||
"user_inputs_before": user_inputs_before,
|
||||
"summary": summary_text,
|
||||
}
|
||||
all_records = previous_records + [current_record]
|
||||
|
||||
# 构建引导语(按压缩形式)。inject 模式读取历次压缩文件全文,文件仍会生成,只是不提及位置。
|
||||
# 构建引导语(按压缩形式)。
|
||||
if compress_form == "inject":
|
||||
guide_message = _build_inject_guide_message(
|
||||
project_path=Path(workspace.project_path),
|
||||
compression_index=target_count,
|
||||
current_record=current_record,
|
||||
previous_records=previous_records,
|
||||
user_inputs=user_inputs,
|
||||
latest_user_input=latest_user_input,
|
||||
)
|
||||
else:
|
||||
guide_message = _build_guide_message(
|
||||
compression_index=target_count,
|
||||
compact_file=relative_compact_path,
|
||||
previous_records=previous_records,
|
||||
)
|
||||
|
||||
# 更新对话 metadata:压缩记录 + 清理压缩状态标记(同一对话,无切换)。
|
||||
# 同时清除需要重建的 frozen prompt 缓存,使压缩后下一次请求自动重新加载动态内容。
|
||||
# 同时清除 frozen prompt 缓存,使压缩后下一次请求自动重新加载动态内容。
|
||||
REBUILD_FROZEN_KEYS = (
|
||||
"frozen_skills_prompt",
|
||||
"frozen_workspace_prompt",
|
||||
"frozen_main_system_prompt",
|
||||
"frozen_permission_prompt",
|
||||
"frozen_execution_prompt",
|
||||
"frozen_recent_conversations_prompt",
|
||||
"frozen_personalization_prompt",
|
||||
"frozen_workspace_prompt",
|
||||
"frozen_agents_md_prompt",
|
||||
"frozen_skills_prompt",
|
||||
"frozen_memory_prompt",
|
||||
"frozen_custom_system_prompt",
|
||||
"frozen_disabled_tools_prompt",
|
||||
)
|
||||
meta_updates = {
|
||||
"compression_count": target_count,
|
||||
@ -511,4 +519,3 @@ async def run_deep_compression(
|
||||
"summary_failed": bool(summary_fail_reason),
|
||||
"guide_message": guide_message,
|
||||
}
|
||||
|
||||
|
||||
@ -1041,7 +1041,7 @@ export const messageMethods = {
|
||||
|
||||
const confirmed = await this.confirmAction({
|
||||
title: '压缩对话',
|
||||
message: '确定要压缩当前对话记录吗?压缩后会生成新的对话副本。',
|
||||
message: '确定要压缩当前对话记录吗?较早的消息会被折叠并生成压缩摘要,当前对话 ID 保持不变。',
|
||||
confirmText: '压缩',
|
||||
cancelText: '取消'
|
||||
});
|
||||
@ -1102,9 +1102,11 @@ export const messageMethods = {
|
||||
this.monitorShowPendingReply();
|
||||
}
|
||||
} else if (compressBehavior === 'wait') {
|
||||
// 等待用户:后端已把引导语作为 user 消息追加进历史,这里仅刷新展示,不触发请求。
|
||||
// 等待用户:后端已把引导语作为 user 消息追加进历史,这里刷新展示,不触发请求。
|
||||
if (newId && !isInPlace) {
|
||||
await this.loadConversation(newId, { force: true });
|
||||
} else if (isInPlace) {
|
||||
await this.loadConversation(this.currentConversationId, { force: true });
|
||||
}
|
||||
} else if (guideMessage) {
|
||||
await this.sendAutoUserMessage(guideMessage);
|
||||
|
||||
@ -105,7 +105,7 @@ class ServerRefactorSmokeTest(unittest.TestCase):
|
||||
thinking_mode = False
|
||||
model_key = None
|
||||
|
||||
def apply_personalization_preferences(self, config):
|
||||
def apply_personalization_preferences(self, config, **kwargs):
|
||||
calls.append(config)
|
||||
|
||||
workspace = SimpleNamespace(data_dir="/tmp/workspace-data")
|
||||
|
||||
Loading…
Reference in New Issue
Block a user