fix: 修复 TUI 界面 API Key 配置和消息处理问题
1. 修复配置保存问题:config_section.py 现在正确发送 API 配置和工具限制两个消息
2. 修复代理环境变量问题:graph_client.py 自动修正缺少 scheme 前缀的代理 URL
3. 修复响应解析问题:app.py 正确从 data 字段获取 content 和 tool_calls
4. 添加操作日志更新:app.py 处理工具调用信息并更新操作日志
🤖 Generated with CodeArts Agent
This commit is contained in:
@ -1,83 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "===== Building TrulyMEM for Linux ====="
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
echo "Project root: $PROJECT_ROOT"
|
||||
|
||||
if ! command -v python3 &> /dev/null; then
|
||||
echo "Error: python3 not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 创建并激活虚拟环境
|
||||
VENV_DIR="$PROJECT_ROOT/.venv_build"
|
||||
|
||||
echo "Creating virtual environment: $VENV_DIR"
|
||||
python3 -m venv "$VENV_DIR"
|
||||
|
||||
echo "Activating virtual environment..."
|
||||
source "$VENV_DIR/bin/activate"
|
||||
|
||||
echo "Upgrading pip in virtual environment..."
|
||||
pip install --upgrade pip
|
||||
|
||||
echo "Installing dependencies in virtual environment..."
|
||||
pip install -r requirements.txt
|
||||
|
||||
echo "Cleaning previous builds..."
|
||||
rm -rf build/dist build/__pycache__ 2>/dev/null || true
|
||||
|
||||
echo "Running PyInstaller..."
|
||||
python -m PyInstaller trulymem_entry.py \
|
||||
--clean \
|
||||
--onefile \
|
||||
--console \
|
||||
--name TrulyMEM \
|
||||
--add-data "ui/styles:ui/styles" \
|
||||
--add-data "core/prompts/templates:core/prompts/templates" \
|
||||
--hidden-import textual \
|
||||
--hidden-import textual.app \
|
||||
--hidden-import textual.widgets \
|
||||
--hidden-import textual.css \
|
||||
--hidden-import openai \
|
||||
--hidden-import openai._client \
|
||||
--hidden-import neo4j \
|
||||
--hidden-import sqlite3 \
|
||||
--hidden-import core \
|
||||
--hidden-import core.embedded_db \
|
||||
--hidden-import core.graph_client \
|
||||
--hidden-import core.tool_executor \
|
||||
--hidden-import core.tool_limiter \
|
||||
--hidden-import core.tools \
|
||||
--hidden-import core.tools.memory_tools \
|
||||
--hidden-import core.prompts \
|
||||
--hidden-import core.prompts.prompt_manager \
|
||||
--hidden-import ui \
|
||||
--hidden-import ui.app \
|
||||
--hidden-import ui.models \
|
||||
--hidden-import ui.models.message \
|
||||
--hidden-import ui.models.config \
|
||||
--hidden-import ui.models.log_entry \
|
||||
--hidden-import ui.widgets \
|
||||
--hidden-import ui.handlers \
|
||||
--hidden-import ui.services \
|
||||
--hidden-import ui.services.config_manager \
|
||||
--hidden-import ui.services.config_service \
|
||||
--collect-all textual \
|
||||
--noconfirm
|
||||
|
||||
echo "===== Build Complete ====="
|
||||
echo "Binary: dist/TrulyMEM"
|
||||
ls -la dist/
|
||||
|
||||
# 清理虚拟环境
|
||||
echo "Cleaning up virtual environment..."
|
||||
deactivate
|
||||
rm -rf "$VENV_DIR"
|
||||
|
||||
echo "Build finished successfully!"
|
||||
@ -1,90 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "===== Building TrulyMEM for macOS ====="
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
echo "Project root: $PROJECT_ROOT"
|
||||
|
||||
if ! command -v python3 &> /dev/null; then
|
||||
echo "Error: python3 not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 创建并激活虚拟环境
|
||||
VENV_DIR="$PROJECT_ROOT/.venv_build"
|
||||
|
||||
echo "Creating virtual environment: $VENV_DIR"
|
||||
python3 -m venv "$VENV_DIR"
|
||||
|
||||
echo "Activating virtual environment..."
|
||||
source "$VENV_DIR/bin/activate"
|
||||
|
||||
echo "Upgrading pip in virtual environment..."
|
||||
pip install --upgrade pip
|
||||
|
||||
echo "Installing dependencies in virtual environment..."
|
||||
pip install -r requirements.txt
|
||||
|
||||
echo "Generating ICNS icon..."
|
||||
if [ -d "pic/TrulyMEM.iconset" ]; then
|
||||
iconutil -c icns pic/TrulyMEM.iconset -o pic/TrulyMEM.icns
|
||||
echo "ICNS icon generated: pic/TrulyMEM.icns"
|
||||
fi
|
||||
|
||||
echo "Cleaning previous builds..."
|
||||
rm -rf build/dist build/__pycache__ 2>/dev/null || true
|
||||
|
||||
echo "Running PyInstaller..."
|
||||
python -m PyInstaller trulymem_entry.py \
|
||||
--clean \
|
||||
--onefile \
|
||||
--console \
|
||||
--name TrulyMEM \
|
||||
--icon "pic/TrulyMEM.icns" \
|
||||
--add-data "ui/styles:ui/styles" \
|
||||
--add-data "core/prompts/templates:core/prompts/templates" \
|
||||
--hidden-import textual \
|
||||
--hidden-import textual.app \
|
||||
--hidden-import textual.widgets \
|
||||
--hidden-import textual.css \
|
||||
--hidden-import openai \
|
||||
--hidden-import openai._client \
|
||||
--hidden-import neo4j \
|
||||
--hidden-import sqlite3 \
|
||||
--hidden-import core \
|
||||
--hidden-import core.embedded_db \
|
||||
--hidden-import core.graph_client \
|
||||
--hidden-import core.tool_executor \
|
||||
--hidden-import core.tool_limiter \
|
||||
--hidden-import core.tools \
|
||||
--hidden-import core.tools.memory_tools \
|
||||
--hidden-import core.prompts \
|
||||
--hidden-import core.prompts.prompt_manager \
|
||||
--hidden-import ui \
|
||||
--hidden-import ui.app \
|
||||
--hidden-import ui.models \
|
||||
--hidden-import ui.models.message \
|
||||
--hidden-import ui.models.config \
|
||||
--hidden-import ui.models.log_entry \
|
||||
--hidden-import ui.widgets \
|
||||
--hidden-import ui.handlers \
|
||||
--hidden-import ui.services \
|
||||
--hidden-import ui.services.config_manager \
|
||||
--hidden-import ui.services.config_service \
|
||||
--collect-all textual \
|
||||
--noconfirm
|
||||
|
||||
echo "===== Build Complete ====="
|
||||
echo "Binary: dist/TrulyMEM"
|
||||
ls -la dist/
|
||||
|
||||
# 清理虚拟环境
|
||||
echo "Cleaning up virtual environment..."
|
||||
deactivate
|
||||
rm -rf "$VENV_DIR"
|
||||
|
||||
echo "Build finished successfully!"
|
||||
@ -1,60 +0,0 @@
|
||||
@echo off
|
||||
echo ===== Building TrulyMEM for Windows =====
|
||||
|
||||
REM 切换到脚本所在目录的上一级目录(项目根目录)
|
||||
cd /d "%~dp0.."
|
||||
echo Project root: %CD%
|
||||
|
||||
python --version >nul 2>&1
|
||||
if errorlevel 1 (
|
||||
echo Error: python not found
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo Installing dependencies...
|
||||
pip install -r requirements.txt
|
||||
|
||||
echo Running PyInstaller...
|
||||
python -m PyInstaller trulymem_entry.py ^
|
||||
--clean ^
|
||||
--onefile ^
|
||||
--console ^
|
||||
--name TrulyMEM ^
|
||||
--icon "pic/TrulyMEM.ico" ^
|
||||
--add-data "ui/styles;ui/styles" ^
|
||||
--add-data "core/prompts/templates;core/prompts/templates" ^
|
||||
--hidden-import textual ^
|
||||
--hidden-import textual.app ^
|
||||
--hidden-import textual.widgets ^
|
||||
--hidden-import textual.css ^
|
||||
--hidden-import openai ^
|
||||
--hidden-import openai._client ^
|
||||
--hidden-import neo4j ^
|
||||
--hidden-import sqlite3 ^
|
||||
--hidden-import core ^
|
||||
--hidden-import core.embedded_db ^
|
||||
--hidden-import core.graph_client ^
|
||||
--hidden-import core.tool_executor ^
|
||||
--hidden-import core.tool_limiter ^
|
||||
--hidden-import core.tools ^
|
||||
--hidden-import core.tools.memory_tools ^
|
||||
--hidden-import core.prompts ^
|
||||
--hidden-import core.prompts.prompt_manager ^
|
||||
--hidden-import ui ^
|
||||
--hidden-import ui.app ^
|
||||
--hidden-import ui.models ^
|
||||
--hidden-import ui.models.message ^
|
||||
--hidden-import ui.models.config ^
|
||||
--hidden-import ui.models.log_entry ^
|
||||
--hidden-import ui.widgets ^
|
||||
--hidden-import ui.handlers ^
|
||||
--hidden-import ui.services ^
|
||||
--hidden-import ui.services.config_manager ^
|
||||
--hidden-import ui.services.config_service ^
|
||||
--collect-all textual ^
|
||||
--noconfirm
|
||||
|
||||
echo ===== Build Complete =====
|
||||
echo Binary: dist\TrulyMEM.exe
|
||||
dir dist\TrulyMEM.exe
|
||||
pause
|
||||
@ -1,92 +0,0 @@
|
||||
# -*- mode: python ; coding: utf-8 -*-
|
||||
import os
|
||||
import sys
|
||||
|
||||
block_cipher = None
|
||||
|
||||
project_root = os.path.dirname(os.path.abspath(SPEC))
|
||||
sys.path.insert(0, project_root)
|
||||
|
||||
datas = []
|
||||
if os.path.exists(os.path.join(project_root, 'ui', 'styles')):
|
||||
for root, dirs, files in os.walk(os.path.join(project_root, 'ui', 'styles')):
|
||||
for f in files:
|
||||
src = os.path.join(root, f)
|
||||
dst = os.path.join('ui', 'styles', os.path.relpath(src, os.path.join(project_root, 'ui', 'styles')))
|
||||
datas.append((src, dst))
|
||||
|
||||
if os.path.exists(os.path.join(project_root, 'core', 'prompts', 'templates')):
|
||||
for root, dirs, files in os.walk(os.path.join(project_root, 'core', 'prompts', 'templates')):
|
||||
for f in files:
|
||||
src = os.path.join(root, f)
|
||||
dst = os.path.join('core', 'prompts', 'templates', os.path.relpath(src, os.path.join(project_root, 'core', 'prompts', 'templates')))
|
||||
datas.append((src, dst))
|
||||
|
||||
a = Analysis(
|
||||
['trulymem_entry.py'],
|
||||
pathex=[],
|
||||
binaries=[],
|
||||
datas=datas,
|
||||
hiddenimports=[
|
||||
'textual',
|
||||
'textual.app',
|
||||
'textual.widgets',
|
||||
'textual.css',
|
||||
'openai',
|
||||
'openai._client',
|
||||
'neo4j',
|
||||
'sqlite3',
|
||||
'graph_memory_tui',
|
||||
'core',
|
||||
'core.embedded_db',
|
||||
'core.graph_client',
|
||||
'core.tool_executor',
|
||||
'core.tool_limiter',
|
||||
'core.tools',
|
||||
'core.tools.memory_tools',
|
||||
'core.prompts',
|
||||
'core.prompts.prompt_manager',
|
||||
'ui',
|
||||
'ui.app',
|
||||
'ui.models',
|
||||
'ui.models.message',
|
||||
'ui.models.config',
|
||||
'ui.models.log_entry',
|
||||
'ui.widgets',
|
||||
'ui.widgets.left_panel',
|
||||
'ui.widgets.right_panel',
|
||||
'ui.widgets.input_box',
|
||||
'ui.widgets.message_history',
|
||||
'ui.widgets.status_bar',
|
||||
'ui.handlers',
|
||||
'ui.services',
|
||||
'ui.services.config_manager',
|
||||
],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
noarchive=False,
|
||||
optimize=0,
|
||||
)
|
||||
pyz = PYZ(a.pure, block_cipher)
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
a.binaries,
|
||||
a.datas,
|
||||
[],
|
||||
name='TrulyMEM',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
runtime_tmpdir=None,
|
||||
console=True,
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=False,
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
)
|
||||
@ -304,6 +304,16 @@ class GraphMemoryClient:
|
||||
"""图记忆客户端"""
|
||||
|
||||
def __init__(self, api_key: str, base_url: str, graph, model: str = "deepseek-chat"):
|
||||
# 清理可能存在的错误代理环境变量
|
||||
import os
|
||||
proxy_vars = ['http_proxy', 'https_proxy', 'HTTP_PROXY', 'HTTPS_PROXY', 'all_proxy', 'ALL_PROXY']
|
||||
for var in proxy_vars:
|
||||
if var in os.environ:
|
||||
value = os.environ[var]
|
||||
# 如果代理URL没有scheme前缀,添加http://
|
||||
if value and not value.startswith(('http://', 'https://', 'socks5://', 'socks4://')):
|
||||
os.environ[var] = f'http://{value}'
|
||||
|
||||
self.client = OpenAI(api_key=api_key, base_url=base_url)
|
||||
self.graph = graph
|
||||
self.tools = TOOLS
|
||||
|
||||
26
ui/app.py
26
ui/app.py
@ -128,6 +128,9 @@ class GraphMemoryApp(App):
|
||||
async def _process(self, user_input: str) -> None:
|
||||
from .widgets.message_history import MessageHistory
|
||||
from .widgets.status_bar import StatusBar
|
||||
from .widgets.right_panel import RightPanel
|
||||
from .models.log_entry import LogEntry
|
||||
from datetime import datetime
|
||||
|
||||
history = self.query_one(MessageHistory)
|
||||
status_bar = self.query_one(StatusBar)
|
||||
@ -138,8 +141,29 @@ class GraphMemoryApp(App):
|
||||
)
|
||||
|
||||
if result.get("success"):
|
||||
content = result.get("content", "(无回复)")
|
||||
# 响应结构: {"success": True, "data": {"content": "...", "tool_calls": [...], ...}, "error": None}
|
||||
data = result.get("data", {})
|
||||
content = data.get("content", "(无回复)")
|
||||
history.update_latest_message(content)
|
||||
|
||||
# 处理工具调用信息,更新操作日志
|
||||
tool_calls = data.get("tool_calls", [])
|
||||
if tool_calls:
|
||||
try:
|
||||
right_panel = self.query_one(RightPanel)
|
||||
operation_log = right_panel.get_operation_log()
|
||||
|
||||
for tool_call in tool_calls:
|
||||
entry = LogEntry(
|
||||
timestamp=datetime.now(),
|
||||
tool_name=tool_call.get("name", "unknown"),
|
||||
arguments=tool_call.get("arguments", {}),
|
||||
result=str(tool_call.get("result", "")),
|
||||
duration=0.0 # 后端没有返回耗时信息
|
||||
)
|
||||
operation_log.add_log(entry)
|
||||
except Exception:
|
||||
pass # 忽略操作日志更新失败
|
||||
else:
|
||||
error = result.get("error", "未知错误")
|
||||
history.update_latest_message(f"❌ 错误: {error}")
|
||||
|
||||
@ -141,6 +141,9 @@ class ConfigSection(Vertical):
|
||||
memory_update_max=int(memory_update.value or 10),
|
||||
)
|
||||
|
||||
# 先发送 API 配置更新(is_tool_limits=False)
|
||||
self.post_message(self.ConfigChanged(self._config, is_tool_limits=False))
|
||||
# 再发送工具限制更新(is_tool_limits=True)
|
||||
self.post_message(self.ConfigChanged(self._config, is_tool_limits=True))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
Reference in New Issue
Block a user