fix: 修复TUI界面多个问题,添加记忆模块模式选择功能

- 修复ToolResult对象的get方法错误
- 修复TUI版本号显示错误(v5.4.1 -> v6.0)
- 修复Shift+Enter换行功能
- 修复AI回复颜色显示问题
- 添加记忆模块模式选择功能(陪伴模式/工作模式)
- 清理项目文件,准备上传到Gitee
This commit is contained in:
shuiyu1123 committed 2026-06-26 22:26:13 +08:00
1 parent 329854866b
commit b5f638cffc
6 files changed
+153 -76

No files matched your search

+19 -21
View File
@@ -1,27 +1,25 @@
# 小狸插件配置
# ── 必备生产力 ──
# 小狸插件配置 - 由启动器自动生成
ENABLE_AI_SEARCH = True
ENABLE_CMD_EXECUTOR = True
ENABLE_FILE_MANAGER = True
ENABLE_CODE_EDITOR = True
ENABLE_GIT_TOOLS = True
ENABLE_FRONTEND_TESTER = True
ENABLE_NETWORK_TOOLS = True
ENABLE_SCHEDULER = True
ENABLE_SPEECH_RECOGNITION = True
ENABLE_TASK_MANAGER = True
ENABLE_TOOL_SEARCH = True
ENABLE_AUTO_ENGINEER = True
ENABLE_TERMINAL = True
ENABLE_BROWSER_AUTO = True
ENABLE_CMD_EXECUTOR = True
ENABLE_CODE_EDITOR = True
ENABLE_FILE_MANAGER = True
ENABLE_FRONTEND_TESTER = True
ENABLE_GIT_TOOLS = True
ENABLE_GUI_AUTO = True
ENABLE_SUB_AGENT = True
ENABLE_WORKFLOW = True
ENABLE_MEMORY_PLUGIN = True
ENABLE_JSON_TOOLKIT = True
ENABLE_REGEX_TESTER = True
ENABLE_SNIPPET_MANAGER = True
ENABLE_MARKDOWN_TOOL = True
ENABLE_ID_GENERATOR = True
ENABLE_IMAGE_READER = True
ENABLE_JSON_TOOLKIT = True
ENABLE_MARKDOWN_TOOL = True
ENABLE_MEMORY_PLUGIN = True
ENABLE_NETWORK_TOOLS = True
ENABLE_REGEX_TESTER = True
ENABLE_SCHEDULER = True
ENABLE_SNIPPET_MANAGER = True
ENABLE_SPEECH_RECOGNITION = True
ENABLE_SUB_AGENT = True
ENABLE_TASK_MANAGER = True
ENABLE_TERMINAL = True
ENABLE_TOOL_SEARCH = True
ENABLE_WORKFLOW = True
+11 -29
View File
@@ -1,30 +1,12 @@
# 小狸 Pro-CLI 依赖库
# 修复后的包名(使用 pip 安装名)
# 小狸 Pro-CLI 依赖
# 生成于 2026-06-26 20:58
# 核心依赖
colorama>=0.4.6
openai>=1.0.0
requests>=2.28.0
ollama>=0.1.0
# 图像处理
Pillow>=9.0.0
opencv-python>=4.5.0
numpy>=1.21.0
ascii-magic>=2.0.0
# 数据处理
pandas>=1.3.0
scipy>=1.7.0
# 音频
pygame>=2.0.0
# TUI 模式
textual>=0.40.0
# WebSocket 远程
websockets>=11.0
# 类型提示
typing-extensions>=4.0.0
PIL
ascii_magic
colorama
cv2
numpy
ollama
openai
requests
tkinter
+4 -5
View File
@@ -45,13 +45,12 @@ class DisplayMixin:
response_lines = content.split('\n')
if self.tui_output_callback:
for i, line in enumerate(response_lines):
if i == 0:
prefix = "[yellow]>[/] " if is_continue else "[cyan]>[/] "
line = prefix + line
# 为每一行都添加前缀,确保整个AI回复都使用正确的样式
prefix = "[yellow]>[/] " if is_continue else "[cyan]>[/] "
if i == len(response_lines) - 1:
self._output(f"{line} {user_id_display}")
self._output(f"{prefix}{line} {user_id_display}")
else:
self._output(line)
self._output(f"{prefix}{line}")
self._output("")
else:
for i, line in enumerate(response_lines):
+7 -2
View File
@@ -105,6 +105,9 @@ class ToolMixin:
result = self.liugin_manager.execute(tool_name, args, **arguments)
else:
result = self.liugin_manager.execute(tool_name, args)
# 将 ToolResult 对象转换为字典格式,兼容旧接口
if hasattr(result, 'to_dict'):
return result.to_dict()
return result
# 回退:直接调用 handler(旧逻辑)
@@ -212,8 +215,10 @@ class ToolMixin:
else:
tool_args_display = tool_args
# TUI 模式下不做延迟和 ESC 检测
if not self.tui_output_callback:
# TUI 模式下显示工具执行状态
if self.tui_output_callback:
self._output(f" 正在执行工具: {tool_name} {tool_args_display}")
else:
try:
import msvcrt
canceled = False
+100
View File
@@ -17,13 +17,71 @@ from typing import List, Dict, Optional, Tuple
class MemoryManager:
"""持久化记忆管理器"""
# 记忆模式常量
MODE_COMPANION = "companion" # 陪伴模式(长期记忆)
MODE_WORK = "work" # 工作模式(短期记忆)
def __init__(self, project_dir: str):
self.project_dir = project_dir
self.memory_dir = os.path.join(project_dir, "memory")
self.memory_file = os.path.join(project_dir, "MEMORY.md")
self.chat_dir = os.path.join(project_dir, "chat_history")
self.config_file = os.path.join(project_dir, "config.json")
self.compress_threshold = 50 # 超过 50 条消息触发压缩
self._ensure_dirs()
self._memory_mode = self._load_memory_mode()
def _load_memory_mode(self) -> str:
"""加载记忆模式配置"""
try:
if os.path.exists(self.config_file):
with open(self.config_file, 'r', encoding='utf-8') as f:
config = json.load(f)
return config.get("system", {}).get("memory_mode", "")
except Exception:
pass
return ""
def _save_memory_mode(self, mode: str):
"""保存记忆模式配置"""
try:
config = {}
if os.path.exists(self.config_file):
with open(self.config_file, 'r', encoding='utf-8') as f:
config = json.load(f)
if "system" not in config:
config["system"] = {}
config["system"]["memory_mode"] = mode
with open(self.config_file, 'w', encoding='utf-8') as f:
json.dump(config, f, ensure_ascii=False, indent=2)
self._memory_mode = mode
except Exception as e:
print(f"保存记忆模式配置失败: {e}")
def get_memory_mode(self) -> str:
"""获取当前记忆模式"""
return self._memory_mode
def is_mode_selected(self) -> bool:
"""检查是否已选择记忆模式"""
return self._memory_mode in [self.MODE_COMPANION, self.MODE_WORK]
def select_mode(self, mode: str) -> bool:
"""选择记忆模式(只能选择一次)"""
if self.is_mode_selected():
return False # 已经选择过,不能更改
if mode not in [self.MODE_COMPANION, self.MODE_WORK]:
return False # 无效的模式
self._save_memory_mode(mode)
return True
def get_mode_description(self) -> str:
"""获取当前模式的描述"""
if self._memory_mode == self.MODE_COMPANION:
return "陪伴模式(长期记忆):拥有成熟的记忆功能,记住用户的偏好、习惯、历史对话等"
elif self._memory_mode == self.MODE_WORK:
return "工作模式(短期记忆):每次对话是独立的短期记忆,可通过指令手动存储聊天记录"
return "未选择模式"
def _ensure_dirs(self):
"""确保目录存在"""
@@ -522,6 +580,44 @@ class MemoryManager:
# 记忆插件(注册到插件系统)
# ══════════════════════════════════════════════
def save_conversation_by_mode(self, conversation: list, label: str = ""):
"""根据模式保存对话"""
if self._memory_mode == self.MODE_COMPANION:
# 陪伴模式:自动保存到长期记忆
self.auto_save_chat(conversation, label)
# 提取关键信息保存到MEMORY.md
self._extract_and_save_key_info(conversation)
elif self._memory_mode == self.MODE_WORK:
# 工作模式:自动保存聊天记录
self.auto_save_chat(conversation, label)
def _extract_and_save_key_info(self, conversation: list):
"""从对话中提取关键信息保存到MEMORY.md(陪伴模式专用)"""
# 这里可以添加AI提取关键信息的逻辑
# 暂时简单实现:保存最后一条助手消息
for msg in reversed(conversation):
if msg.get("role") == "assistant":
content = msg.get("content", "")
if content and len(content) > 20:
self.append_memory("对话记录", f"- {content[:200]}")
break
def load_context_by_mode(self, conversation: list) -> list:
"""根据模式加载上下文"""
if self._memory_mode == self.MODE_COMPANION:
# 陪伴模式:加载长期记忆作为上下文
memory_content = self.load_memory()
if memory_content:
context_msg = {"role": "system", "content": f"长期记忆:{memory_content}"}
return [context_msg] + conversation
return conversation
elif self._memory_mode == self.MODE_WORK:
# 工作模式:只返回当前对话
return conversation
return conversation
class Liugin:
"""记忆系统插件 — AI 可读写记忆、搜索历史"""
@@ -596,3 +692,7 @@ JSON格式示例:
"required": ["action"]
}
}
# ══════════════════════════════════════════════
# 模式特定的方法
# ══════════════════════════════════════════════
+12 -19
View File
@@ -379,7 +379,8 @@ if TEXTUAL_AVAILABLE:
self.styles.opacity = 0
def on_mount(self):
self.animate("opacity", value=1.0, duration=0.3, easing="out_cubic")
# 使用 styles.animate 来动画化样式属性
self.styles.animate("opacity", value=1.0, duration=0.3, easing="out_cubic")
class TypingIndicator(Static):
@@ -411,17 +412,7 @@ if TEXTUAL_AVAILABLE:
# ═══════════════════════════════════════════════════
class SendTextArea(TextArea):
"""回车发送消息,Shift+回车换行的输入框"""
async def _on_key(self, event):
if event.key in ("enter", "\r", "\n"):
# prevent_default 阻止父类 TextArea._on_key 执行
event.prevent_default()
try:
self.app.action_send_message()
except Exception:
pass
return
# 其他键正常处理
"""多行输入框,Ctrl+Enter发送消息"""
# ═══════════════════════════════════════════════════
# TUI 主应用
@@ -460,9 +451,8 @@ if TEXTUAL_AVAILABLE:
yield VerticalScroll(id="chat-scroll")
with Vertical(id="input-area"):
with Container(id="input-wrapper"):
yield SendTextArea(
placeholder="输入消息... (回车发送, Shift+回车换行)",
id="user-input",
yield SendTextArea(
placeholder="输入消息... (Ctrl+Enter 发送, Enter 换行)", id="user-input",
soft_wrap=True,
tab_behavior="indent",
)
@@ -498,7 +488,7 @@ if TEXTUAL_AVAILABLE:
"",
" ╔══════════════════════════════════════════════╗",
" ║ ║",
" ║ 小狸 Pro-CLI v5.4.1 ║",
" ║ 小狸 Pro-CLI v6.0 ║",
" ║ 智能编程助手 · 动画增强版 ║",
" ║ ║",
" ╚══════════════════════════════════════════════╝",
@@ -542,7 +532,7 @@ if TEXTUAL_AVAILABLE:
("", "msg-dim"),
(" ╔══════════════════════════════════════════════╗", "msg-welcome"),
(" ║ ║", "msg-welcome"),
(" ║ 小狸 Pro-CLI v5.4.1 ║", "msg-welcome"),
(" ║ 小狸 Pro-CLI v6.0 ║", "msg-welcome"),
(" ║ 智能编程助手 · 动画增强版 ║", "msg-welcome"),
(" ║ ║", "msg-welcome"),
(" ╚══════════════════════════════════════════════╝", "msg-welcome"),
@@ -743,7 +733,7 @@ if TEXTUAL_AVAILABLE:
'cls': lambda: self.action_clear(),
'model': lambda: self._switch_model(args),
'engine': lambda: self._switch_model(args),
'about': lambda: self._system(" 小狸 Pro-CLI v5.4.1 - 智能编程助手"),
'about': lambda: self._system(" 小狸 Pro-CLI v6.0 - 智能编程助手"),
'status': lambda: self._show_status(),
'tools': lambda: self._show_tools(),
'engines': lambda: self._show_engines(),
@@ -878,7 +868,10 @@ if TEXTUAL_AVAILABLE:
self.call_after_refresh(self._update_sidebar)
def _write_raw(self, msg):
if '✅' in msg or 'OK 工具' in msg:
# 检查是否是AI回复(包含Rich标记前缀)
if msg.startswith('[cyan]>[/]') or msg.startswith('[yellow]>[/]'):
self._add(f" {msg}", "msg-ai")
elif '✅' in msg or 'OK 工具' in msg:
self._add(f" {msg}", "msg-tool-ok")
elif '❌' in msg or 'X 工具' in msg or '错误' in msg:
self._add(f" {msg}", "msg-tool-err")