feat: 增强编码能力 - 新增代码编辑器、代码搜索、Git集成

- 新增 plugins/code_editor.py: 精准代码编辑(搜索替换、批量编辑、diff对比)
- 新增 plugins/code_search.py: 代码搜索与理解(跨文件搜索、符号提取、依赖分析)
- 新增 plugins/git_tools.py: Git 版本控制(status/diff/log/commit/branch)
- 新增 core/ 模块: 配置管理、引擎管理、对话处理、显示输出、代码沙箱
- 修复 requirements.txt: 使用正确的 pip 包名(Pillow、opencv-python 等)
- 更新 unified_tool_manager.py: 兼容 Plugin 和 Liugin 两种类名
- 更新系统提示词: 增强编程工作流指导

本次更新由 mimo 自动化执行,与开发者无关。
This commit is contained in:
Xiaoli Dev committed 2026-04-25 10:55:27 +08:00
commit e22652fbbb
53 files changed
+20067

No files matched your search

+209
View File
@@ -0,0 +1,209 @@
"""
图像识别引擎模块
支持多种图像识别引擎的切换和管理
"""
import os
import sys
import importlib
import json
from colorama import Fore, Style
# 添加项目根目录到sys.path
current_dir = os.path.dirname(os.path.abspath(__file__))
project_root = os.path.dirname(current_dir)
sys.path.insert(0, project_root)
class BaseImageEngine:
"""图像识别引擎基类"""
name = "base"
description = "基础图像识别引擎"
requires_api_key = False
def __init__(self):
self.config = {}
def set_config(self, config):
"""设置引擎配置"""
self.config = config
def is_available(self):
"""检查引擎是否可用(子类实现)"""
return False
def analyze(self, image_path, prompt="请描述这张图片"):
"""分析图片(子类实现)"""
raise NotImplementedError("子类必须实现 analyze 方法")
def stream_analyze(self, image_path, prompt="请描述这张图片"):
"""流式分析图片(子类实现,可选)"""
# 默认实现:调用 analyze 并一次性返回
result = self.analyze(image_path, prompt)
yield result
def get_model_list(self):
"""获取可用模型列表(子类实现,可选)"""
return []
def set_model(self, model_name):
"""设置当前使用的模型(子类实现,可选)"""
pass
def get_help(self):
"""获取引擎帮助信息"""
return f"{self.name}: {self.description}"
class ImageEngineManager:
"""图像识别引擎管理器"""
def __init__(self):
self.engines = {}
self.current_engine = None
self.current_engine_name = None
self.config = self._load_config()
# 自动加载所有引擎
self._load_engines()
# 设置默认引擎
self._set_default_engine()
def _load_config(self):
"""加载配置文件"""
config_file = os.path.join(project_root, "config.json")
try:
with open(config_file, 'r', encoding='utf-8') as f:
return json.load(f)
except:
return {"image": {"engines": {}, "default_engine": "ollama"}}
def _save_config(self):
"""保存配置文件"""
config_file = os.path.join(project_root, "config.json")
try:
with open(config_file, 'r', encoding='utf-8') as f:
full_config = json.load(f)
except:
full_config = {"api": {"engines": {}}, "system": {}, "image": {}}
# 确保image配置存在
if "image" not in full_config:
full_config["image"] = {}
full_config["image"]["default_engine"] = self.current_engine_name
try:
with open(config_file, 'w', encoding='utf-8') as f:
json.dump(full_config, f, ensure_ascii=False, indent=2)
except Exception as e:
print(f"{Fore.RED}保存配置失败: {e}{Style.RESET_ALL}")
def _load_engines(self):
"""加载所有引擎"""
engine_dir = os.path.dirname(__file__)
for filename in os.listdir(engine_dir):
if filename.endswith('_engine.py') and filename != '__init__.py':
engine_name = filename[:-11] # 移除 _engine.py
try:
# 动态导入模块
module_path = os.path.join(engine_dir, filename)
spec = importlib.util.spec_from_file_location(f"image_engine.{engine_name}", module_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
# 获取引擎类
for attr_name in dir(module):
attr = getattr(module, attr_name)
if (isinstance(attr, type) and
issubclass(attr, BaseImageEngine) and
attr != BaseImageEngine):
engine_instance = attr()
# 加载引擎配置
engine_config = self.config.get("image", {}).get("engines", {}).get(engine_name, {})
engine_instance.set_config(engine_config)
self.engines[engine_name] = engine_instance
print(f"{Fore.GREEN}已加载图像引擎: {engine_name}{Style.RESET_ALL}")
break
except Exception as e:
print(f"{Fore.RED}加载图像引擎 {engine_name} 失败: {e}{Style.RESET_ALL}")
def _set_default_engine(self):
"""设置默认引擎"""
# 从配置读取默认引擎
default = self.config.get("image", {}).get("default_engine", "ollama")
if default in self.engines:
engine = self.engines[default]
if engine.is_available():
self.current_engine = engine
self.current_engine_name = default
print(f"{Fore.GREEN}当前图像引擎: {default}{Style.RESET_ALL}")
return
# 如果配置的默认引擎不可用,找第一个可用的
for name, engine in self.engines.items():
if engine.is_available():
self.current_engine = engine
self.current_engine_name = name
print(f"{Fore.GREEN}当前图像引擎: {name}{Style.RESET_ALL}")
return
print(f"{Fore.YELLOW}警告: 没有可用的图像引擎{Style.RESET_ALL}")
def get_engine(self, name=None):
"""获取指定引擎或当前引擎"""
if name:
return self.engines.get(name)
return self.current_engine
def switch_engine(self, name):
"""切换引擎"""
if name not in self.engines:
available = list(self.engines.keys())
return False, f"引擎 '{name}' 不存在。可用引擎: {', '.join(available)}"
engine = self.engines[name]
if not engine.is_available():
return False, f"引擎 '{name}' 不可用,请检查配置"
self.current_engine = engine
self.current_engine_name = name
self._save_config()
return True, f"已切换到图像引擎: {name}"
def list_engines(self):
"""列出所有引擎"""
result = f"{Fore.CYAN}图像识别引擎列表:{Style.RESET_ALL}\n"
for name, engine in self.engines.items():
status = f"{Fore.GREEN}✓ 可用{Style.RESET_ALL}" if engine.is_available() else f"{Fore.RED}✗ 不可用{Style.RESET_ALL}"
current = f" {Fore.YELLOW}[当前]{Style.RESET_ALL}" if name == self.current_engine_name else ""
result += f" - {name}: {engine.description} {status}{current}\n"
return result
def analyze(self, image_path, prompt="请描述这张图片", stream=True):
"""使用当前引擎分析图片"""
if not self.current_engine:
raise Exception("没有可用的图像引擎")
if stream and hasattr(self.current_engine, 'stream_analyze'):
return self.current_engine.stream_analyze(image_path, prompt)
else:
return self.current_engine.analyze(image_path, prompt)
def get_current_engine_name(self):
"""获取当前引擎名称"""
return self.current_engine_name
# 全局引擎管理器实例
_engine_manager = None
def get_engine_manager():
"""获取全局引擎管理器实例"""
global _engine_manager
if _engine_manager is None:
_engine_manager = ImageEngineManager()
return _engine_manager
+204
View File
@@ -0,0 +1,204 @@
"""
Ollama 视觉引擎
使用本地 Ollama 服务进行图像识别
"""
import os
from colorama import Fore, Style
from . import BaseImageEngine
try:
import ollama
OLLAMA_AVAILABLE = True
except ImportError:
OLLAMA_AVAILABLE = False
class OllamaImageEngine(BaseImageEngine):
"""Ollama 视觉引擎"""
name = "ollama"
description = "Ollama本地视觉模型(支持多种视觉模型)"
requires_api_key = False
# 预设提示词
PROMPTS = {
"analyze": """请详细分析这张图片:
1. 图像类型:照片/截图/插画/图表/文档等
2. 主要内容:描述核心元素和场景
3. 文字内容:如有文字,请完整转录
4. 视觉细节:颜色、构图、风格等
5. 总结:一句话概括
请用中文简洁回答。""",
"ocr": "请识别图片中的所有文字内容,按原文格式输出。如果没有文字,请描述图片内容。",
"quick": "请用中文简要描述这张图片的内容。",
"describe": "请详细描述这张图片的内容,包括所有可见的元素、颜色、文字等。"
}
# 推荐的视觉模型列表
VISION_MODELS = [
'qwen3-vl:4b', 'qwen3-vl:latest',
'llava:7b', 'llava:13b', 'llava:34b',
'llava-llama3:latest',
'minicpm-v:latest',
'deepseek-ocr:latest',
'moondream:latest',
'bakllava:latest',
]
def __init__(self):
super().__init__()
self.model = "qwen3-vl:4b"
self.installed_models = []
self._refresh_models()
def set_config(self, config):
"""设置引擎配置"""
super().set_config(config)
if 'model' in config:
self.model = config['model']
def _refresh_models(self):
"""刷新已安装模型列表"""
if not OLLAMA_AVAILABLE:
self.installed_models = []
return
try:
result = ollama.list()
self.installed_models = [m['model'] for m in result.get('models', [])]
except:
self.installed_models = []
def is_available(self):
"""检查引擎是否可用"""
if not OLLAMA_AVAILABLE:
return False
try:
ollama.list()
return True
except:
return False
def _find_model(self, model_name):
"""查找模型(处理版本标签)"""
for m in self.installed_models:
if m == model_name or m.startswith(model_name + ':') or model_name in m:
return m
return None
def get_model_list(self):
"""获取可用模型列表"""
self._refresh_models()
result = f"{Fore.CYAN}已安装的模型:{Style.RESET_ALL}\n"
for m in self.installed_models:
marker = f"{Fore.GREEN}*{Style.RESET_ALL}" if any(vm.split(':')[0] in m for vm in ['vl', 'llava', 'vision', 'ocr', 'minicpm', 'moondream', 'qwen3-vl']) else " "
result += f" {marker} {m}\n"
result += f"\n{Fore.CYAN}推荐的视觉模型:{Style.RESET_ALL}\n"
for vm in self.VISION_MODELS:
status = f"{Fore.GREEN}[已安装]{Style.RESET_ALL}" if self._find_model(vm) else f"{Fore.YELLOW}[未安装]{Style.RESET_ALL}"
result += f" - {vm} {status}\n"
return result
def set_model(self, model_name):
"""设置当前使用的模型"""
actual = self._find_model(model_name)
if actual:
self.model = actual
return True, f"已切换到模型: {actual}"
# 如果没找到,直接使用用户指定的名称(可能还没安装)
self.model = model_name
return True, f"已设置模型: {model_name} (未安装? 运行 ollama pull {model_name})"
def analyze(self, image_path, prompt="请描述这张图片"):
"""分析图片(非流式)"""
if not OLLAMA_AVAILABLE:
raise Exception("ollama 库未安装,请运行: pip install ollama")
if not os.path.exists(image_path):
raise Exception(f"文件不存在: {image_path}")
# 使用预设提示词或自定义提示词
actual_prompt = self.PROMPTS.get(prompt, prompt)
try:
response = ollama.chat(
model=self.model,
messages=[{
'role': 'user',
'content': actual_prompt,
'images': [image_path]
}]
)
return response.get('message', {}).get('content', '')
except Exception as e:
raise Exception(f"Ollama识别失败: {str(e)}")
def stream_analyze(self, image_path, prompt="请描述这张图片"):
"""流式分析图片"""
if not OLLAMA_AVAILABLE:
raise Exception("ollama 库未安装,请运行: pip install ollama")
if not os.path.exists(image_path):
raise Exception(f"文件不存在: {image_path}")
# 使用预设提示词或自定义提示词
actual_prompt = self.PROMPTS.get(prompt, prompt)
# 检查模型是否已安装
actual_model = self._find_model(self.model)
if not actual_model:
raise Exception(f"模型 {self.model} 未安装。请运行: ollama pull {self.model}")
try:
response = ollama.chat(
model=actual_model,
messages=[{
'role': 'user',
'content': actual_prompt,
'images': [image_path]
}],
stream=False
)
content = response.get('message', {}).get('content', '')
if content:
yield content
else:
raise Exception("模型返回空内容")
except Exception as e:
error_msg = str(e)
if "connection" in error_msg.lower():
raise Exception("无法连接到 Ollama 服务,请确保服务正在运行 (ollama serve)")
raise Exception(f"Ollama识别失败: {error_msg}")
def get_help(self):
"""获取引擎帮助信息"""
return f"""
Ollama 视觉引擎帮助
==================
当前模型: {self.model}
使用说明:
1. 确保 Ollama 服务正在运行
2. 安装视觉模型: ollama pull qwen3-vl:4b
3. 使用 /image model <模型名> 切换模型
支持的操作:
- analyze 详细分析
- ocr 文字识别
- quick 快速描述
- describe 完整描述
模型管理:
- /image models 列出模型
- /image model <名> 切换模型
"""