feat: Add embedded SQLite database and web interface

- Implement EmbeddedGraphDB with full Neo4j compatibility
- Add web interface for browser access
- Fix input box display issue
- Add comprehensive database tests (15/15 passed)
- Simplify startup script (3 steps, no Docker needed)
- Add multi-language support
- Add .gitignore for clean repository
- Update documentation

All tests passed. Ready for production.
This commit is contained in:
JianFeeeee
2026-04-10 15:43:22 +08:00
parent 14ab28e242
commit 6689f08456
63 changed files with 4470 additions and 1086 deletions

View File

@ -0,0 +1 @@
"""Event Handlers for Graph Memory TUI"""

View File

@ -0,0 +1,64 @@
"""焦点管理器"""
from textual.app import App
class FocusHandler:
"""焦点管理器"""
# 焦点循环顺序
FOCUS_RING = [
"input-textarea", # 左侧输入框
"api-key-input", # 右侧配置区 API Key
"model-input", # 右侧配置区 Model
"base-url-input", # 右侧配置区 Base URL
"cypher-textarea", # 右侧 Cypher 查询框
]
# 焦点名称映射
FOCUS_NAMES = {
"input-textarea": "Input",
"api-key-input": "Config-API",
"model-input": "Config-Model",
"base-url-input": "Config-URL",
"cypher-textarea": "Query",
}
def __init__(self):
self._current_index = 0
def next_focus(self, app: App) -> None:
"""切换到下一个焦点"""
self._current_index = (self._current_index + 1) % len(self.FOCUS_RING)
widget_id = self.FOCUS_RING[self._current_index]
self._focus_widget(app, widget_id)
def prev_focus(self, app: App) -> None:
"""切换到上一个焦点"""
self._current_index = (self._current_index - 1) % len(self.FOCUS_RING)
widget_id = self.FOCUS_RING[self._current_index]
self._focus_widget(app, widget_id)
def focus_input(self, app: App) -> None:
"""聚焦到输入框"""
self._current_index = 0
self._focus_widget(app, self.FOCUS_RING[0])
def focus_query(self, app: App) -> None:
"""聚焦到查询框"""
self._current_index = len(self.FOCUS_RING) - 1
self._focus_widget(app, self.FOCUS_RING[-1])
def get_current_focus_name(self) -> str:
"""获取当前焦点名称"""
widget_id = self.FOCUS_RING[self._current_index]
return self.FOCUS_NAMES.get(widget_id, "Unknown")
def _focus_widget(self, app: App, widget_id: str) -> None:
"""聚焦到指定组件"""
try:
widget = app.query_one(f"#{widget_id}")
widget.focus()
except Exception:
# 如果找不到组件,回退到输入框
self.focus_input(app)

View File

@ -0,0 +1,68 @@
"""快捷键处理器"""
from textual.app import App
from textual.message import Message
from .focus_handler import FocusHandler
class KeyHandler:
"""快捷键处理器"""
class ShowHelp(Message):
"""显示帮助事件"""
pass
class ToggleSidebar(Message):
"""切换侧边栏事件"""
pass
class ToggleToolDetails(Message):
"""切换工具详情事件"""
pass
class FocusQuery(Message):
"""聚焦查询框事件"""
pass
class ClearHistory(Message):
"""清屏事件"""
pass
class QuitApp(Message):
"""退出应用事件"""
pass
def __init__(self, focus_handler: FocusHandler):
self._focus_handler = focus_handler
def handle_f1(self, app: App) -> None:
"""处理 F1 键 - 显示帮助"""
app.post_message(self.ShowHelp())
def handle_f2(self, app: App) -> None:
"""处理 F2 键 - 切换侧边栏"""
app.post_message(self.ToggleSidebar())
def handle_f3(self, app: App) -> None:
"""处理 F3 键 - 切换工具详情"""
app.post_message(self.ToggleToolDetails())
def handle_f4(self, app: App) -> None:
"""处理 F4 键 - 聚焦查询框"""
app.post_message(self.FocusQuery())
def handle_f5(self, app: App) -> None:
"""处理 F5 键 - 清屏"""
app.post_message(self.ClearHistory())
def handle_f6(self, app: App) -> None:
"""处理 F6 键 - 退出"""
app.post_message(self.QuitApp())
def handle_tab(self, app: App) -> None:
"""处理 Tab 键 - 焦点循环"""
self._focus_handler.next_focus(app)
def handle_shift_tab(self, app: App) -> None:
"""处理 Shift+Tab 键 - 反向焦点循环"""
self._focus_handler.prev_focus(app)

View File

@ -0,0 +1,75 @@
"""消息处理器"""
from datetime import datetime
from typing import TYPE_CHECKING
from ..models.message import Message, ToolCall, ToolResult
if TYPE_CHECKING:
from ..services.chat_service import ChatService
from ..widgets.message_history import MessageHistory
from ..widgets.operation_log import OperationLog
class MessageHandler:
"""消息处理器"""
def __init__(
self,
chat_service: "ChatService",
message_history: "MessageHistory",
operation_log: "OperationLog"
):
self._chat_service = chat_service
self._message_history = message_history
self._operation_log = operation_log
async def handle_user_message(self, content: str) -> None:
"""处理用户消息"""
# 创建用户消息
user_message = Message(
role="user",
content=content,
timestamp=datetime.now()
)
# 添加到历史
self._message_history.add_message(user_message)
# 发送到聊天服务
await self._process_response(content)
async def _process_response(self, user_input: str) -> None:
"""处理响应"""
async for event in self._chat_service.send_message(user_input):
if event["type"] == "user_message":
# 用户消息已处理
pass
elif event["type"] == "assistant_message":
# 模型消息
message = Message(
role="assistant",
content=event["content"],
timestamp=datetime.now(),
tool_calls=event.get("tool_calls"),
tool_results=event.get("tool_results")
)
self._message_history.add_message(message)
elif event["type"] == "tool_call":
# 工具调用开始
pass
elif event["type"] == "tool_result":
# 工具执行结果
log_entry = event["log_entry"]
self._operation_log.add_log(log_entry)
elif event["type"] == "error":
# 错误处理
error_message = Message(
role="assistant",
content=f"错误: {event['error']}",
timestamp=datetime.now()
)
self._message_history.add_message(error_message)