From b2e9d33164258c8744e80a0f4dbb76efdc8246ff Mon Sep 17 00:00:00 2001 From: dashitongzhi Date: Thu, 30 Apr 2026 11:46:49 +0800 Subject: [PATCH] feat: add multimodal vision expert for image analysis and code generation - VisionExpert class: image analysis + code generation from images - Anthropic Messages API integration (mimo-v2-omni model) - Gate classifier: new 'vision' expert type - Orchestrator: vision pipeline with image_paths support - CLI: /paste (clipboard) and /image (file) commands - Optional deps: pip install kwcode[multimodal] (Pillow + pyperclip) - Architecture diagram in docs/ --- .gitignore | 1 + README.md | 13 +- docs/kwcode_architecture.png | Bin 0 -> 92040 bytes kaiwu/cli/main.py | 65 ++++- kaiwu/core/gate.py | 8 +- kaiwu/core/orchestrator.py | 33 +++ kaiwu/experts/vision_expert.py | 425 +++++++++++++++++++++++++++++++++ pyproject.toml | 1 + 8 files changed, 540 insertions(+), 6 deletions(-) create mode 100644 docs/kwcode_architecture.png create mode 100644 kaiwu/experts/vision_expert.py diff --git a/.gitignore b/.gitignore index 60862a4..6ff0960 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ build/ .pytest_cache/ test_project/ .eggs/ +.DS_Store diff --git a/README.md b/README.md index 9ac3c1f..e9d8034 100644 --- a/README.md +++ b/README.md @@ -211,6 +211,14 @@ Prompt Optimizer(可选,需 Anthropic API key): ### Office 文档 - Excel / PPT / Word 生成 +### 多模态图片处理 +- `/paste` 命令:从剪贴板粘贴图片 +- `/image ` 命令:添加图片文件 +- 图片分析:代码截图、UI设计图、文档表格 +- 基于图片的代码生成:UI截图→HTML/CSS、错误截图→修复代码 +- 支持格式:PNG、JPG、JPEG、GIF、WebP、BMP +- 安装:`pip install kwcode[multimodal]` + ### 价值可见 - `kwcode stats`:完成任务数、节省时间估算 - 飞轮通知:专家投产时弹出 @@ -355,6 +363,8 @@ kwcode --plan "重构数据库连接层" /memory 查看项目记忆 /init 初始化项目规则文件 /cd <路径> 切换项目目录 +/paste 从剪贴板粘贴图片 +/image <路径> 添加图片文件 /help 显示帮助 ``` @@ -418,7 +428,8 @@ kaiwu/ │ ├── verifier.py # [元专家] 语法检查 + pytest │ ├── debug_subagent.py # [元专家] 运行时调试(sys.settrace) │ ├── reviewer.py # [元专家] 需求对齐审查 -│ └── search_augmentor.py # 搜索增强 + BM25 + CE 重排 +│ ├── search_augmentor.py # 搜索增强 + BM25 + CE 重排 +│ └── vision_expert.py # [元专家] 多模态图片处理 ├── search/ │ ├── reranker.py # Cross-Encoder 可选重排 │ ├── duckduckgo.py # SearXNG + DDG 并行搜索 diff --git a/docs/kwcode_architecture.png b/docs/kwcode_architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..7283f5d07c99c2698fa55b06e2bd74607f1eba13 GIT binary patch literal 92040 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

mx4D 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%0DOj(7AqKCX(k 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 K&t;ucLK6VQ|8gM! literal 0 HcmV?d00001 diff --git a/kaiwu/cli/main.py b/kaiwu/cli/main.py index 962f83a..3e77d4f 100644 --- a/kaiwu/cli/main.py +++ b/kaiwu/cli/main.py @@ -161,6 +161,10 @@ def _build_pipeline(model_path, ollama_url, ollama_model, project_root, verbose) from kaiwu.experts.chat_expert import ChatExpert chat_expert = ChatExpert(llm=llm, search_augmentor=search) + # Vision Expert (多模态图片处理) + from kaiwu.experts.vision_expert import VisionExpert + vision_expert = VisionExpert(llm=llm, tool_executor=tools) + # Debug Subagent (问题1修复:实例化并注入) from kaiwu.experts.debug_subagent import DebugSubagent debug_subagent = DebugSubagent(llm, tools) @@ -183,6 +187,7 @@ def _build_pipeline(model_path, ollama_url, ollama_model, project_root, verbose) ab_tester=ab_tester, chat_expert=chat_expert, debug_subagent=debug_subagent, + vision_expert=vision_expert, ) # Wire circular reference: ABTester needs orchestrator for backtest ab_tester.orchestrator = orchestrator @@ -192,17 +197,26 @@ def _build_pipeline(model_path, ollama_url, ollama_model, project_root, verbose) # ── Single task execution ───────────────────────────────────── -def _run_task(task, gate, orchestrator, memory, project_root, verbose, plan=False, no_search=False): +def _run_task(task, gate, orchestrator, memory, project_root, verbose, plan=False, no_search=False, image_paths=None): """Execute a single task through the pipeline. Returns success bool.""" from kaiwu.core.orchestrator import EXPERT_SEQUENCES from rich.progress import Progress, SpinnerColumn, TextColumn + # 处理图片上下文 + if image_paths: + logger.info(f"[main] 任务包含 {len(image_paths)} 张图片") + # 将图片路径添加到任务描述中 + image_context = "\n".join([f"[图片: {img}]" for img in image_paths]) + task_with_images = f"{task}\n\n{image_context}" + else: + task_with_images = task + # Gate (with spinner) with Progress(SpinnerColumn(), TextColumn("{task.description}"), transient=True, console=console) as progress: spin = progress.add_task("分析任务...", total=None) try: - gate_result = gate.classify(task, memory_context=memory.load(project_root)) + gate_result = gate.classify(task_with_images, memory_context=memory.load(project_root)) except Exception as e: progress.stop() console.print(f"\n [red]模型调用失败:{e}[/red]") @@ -298,12 +312,13 @@ def _run_task(task, gate, orchestrator, memory, project_root, verbose, plan=Fals logger.debug("[main] 预搜索失败: %s", e) result = orchestrator.run( - user_input=task, + user_input=task_with_images, gate_result=gate_result, project_root=project_root, on_status=_status_fn, no_search=no_search, pre_search_results=pre_search, + image_paths=image_paths, ) # 保存最后结果供conversation_history使用(问题7) orchestrator._last_result = result @@ -393,6 +408,8 @@ REPL_COMMANDS = { "/plan": "计划模式 (用法: /plan <任务> 或 /plan 后输入任务)", "/multi": "多任务模式 (用法: /multi 后按提示输入多个任务)", "/api": "API配置 (用法: /api show | /api temp | /api default )", + "/paste": "从剪贴板粘贴图片", + "/image": "添加图片文件 (用法: /image )", "/exit": "退出", } @@ -642,6 +659,35 @@ def _repl(model_path, ollama_url, ollama_model, project_root, verbose): elif cmd == "/multi": _handle_multi_command(arg, gate, orchestrator, project_root, console) + elif cmd == "/paste": + from kaiwu.experts.vision_expert import save_clipboard_image + image_path = save_clipboard_image() + if image_path: + console.print(f" [green]图片已从剪贴板保存: {image_path}[/green]") + console.print(" [dim]现在可以输入任务描述,图片将作为上下文[/dim]") + # 存储图片路径供后续任务使用 + if not hasattr(session, '_pending_images'): + session._pending_images = [] + session._pending_images.append(image_path) + else: + console.print(" [yellow]剪贴板中没有图片[/yellow]") + + elif cmd == "/image": + if not arg: + console.print(" [yellow]用法: /image <图片路径>[/yellow]") + else: + from kaiwu.experts.vision_expert import validate_image_path + image_path = arg.strip() + if validate_image_path(image_path): + console.print(f" [green]图片已添加: {image_path}[/green]") + console.print(" [dim]现在可以输入任务描述,图片将作为上下文[/dim]") + # 存储图片路径供后续任务使用 + if not hasattr(session, '_pending_images'): + session._pending_images = [] + session._pending_images.append(image_path) + else: + console.print(f" [red]图片文件不存在或格式不支持: {image_path}[/red]") + elif cmd == "/api": api_parts = user_input.split() result = _handle_api_command(api_parts, ollama_url, ollama_model) @@ -677,6 +723,13 @@ def _repl(model_path, ollama_url, ollama_model, project_root, verbose): f"耗时{pruner._last_compress_ms:.1f}ms)[/dim]" ) + # 处理待处理的图片 + pending_images = getattr(session, '_pending_images', []) + if pending_images: + console.print(f" [cyan]图片上下文: {len(pending_images)} 张图片[/cyan]") + for img_path in pending_images: + console.print(f" - {img_path}") + t0 = time.perf_counter() # P2: Small model forces plan mode (问题6修复:用户可通过 no_search 间接控制) effective_plan = plan_next or (model_strategy.force_plan_mode and not no_search) @@ -689,7 +742,13 @@ def _repl(model_path, ollama_url, ollama_model, project_root, verbose): verbose=verbose, plan=effective_plan, no_search=False, + image_paths=pending_images if pending_images else None, ) + + # 清除已使用的图片 + if pending_images: + session._pending_images = [] + elapsed = time.perf_counter() - t0 plan_next = False # Reset plan flag diff --git a/kaiwu/core/gate.py b/kaiwu/core/gate.py index 5c11609..976fa72 100644 --- a/kaiwu/core/gate.py +++ b/kaiwu/core/gate.py @@ -27,6 +27,7 @@ expert_type选项: - doc:写注释、文档、README(仅限代码相关文档,用户明确提到已有文件+docstring/注释) - office:仅限生成Excel(.xlsx)/Word(.docx)/PPT(.pptx)办公文档,不包括代码文件 - chat:问候、闲聊、非编码问题、询问天气、询问知识 +- vision:图片分析、图片代码生成、UI截图分析、设计图实现(用户上传了图片或提到图片相关内容) difficulty选项:easy | hard hard条件(满足任意一条): @@ -48,6 +49,7 @@ subtask_hint:仅difficulty=hard时填写,逗号分隔的子任务简述。 - "修复src/xxx.py" → locator_repair - "重构src/xxx.py" → refactor - 不确定时优先选codegen或locator_repair,不要选office +- 图片相关任务(上传图片、分析截图、根据设计图生成代码) → vision 示例: - "你好" → {{"expert_type":"chat","task_summary":"问候","difficulty":"easy","needs_search":false,"subtask_hint":""}} @@ -57,6 +59,8 @@ subtask_hint:仅difficulty=hard时填写,逗号分隔的子任务简述。 - "生成一个Excel报表" → {{"expert_type":"office","task_summary":"Excel报表","difficulty":"easy","needs_search":false,"subtask_hint":""}} - "根据内容生成项目介绍PPT" → {{"expert_type":"office","task_summary":"项目PPT","difficulty":"easy","needs_search":false,"subtask_hint":""}} - "做个幻灯片汇报" → {{"expert_type":"office","task_summary":"汇报PPT","difficulty":"easy","needs_search":false,"subtask_hint":""}} +- "分析这张截图" → {{"expert_type":"vision","task_summary":"图片分析","difficulty":"easy","needs_search":false,"subtask_hint":""}} +- "根据UI设计图生成HTML代码" → {{"expert_type":"vision","task_summary":"UI代码生成","difficulty":"easy","needs_search":false,"subtask_hint":""}} 格式:{{"expert_type":"...","task_summary":"10字内","difficulty":"...","needs_search":false,"subtask_hint":""}} @@ -66,14 +70,14 @@ subtask_hint:仅difficulty=hard时填写,逗号分隔的子任务简述。 GATE_GRAMMAR = r''' root ::= "{" ws expert-type "," ws task-summary "," ws difficulty "}" ws expert-type ::= "\"expert_type\"" ws ":" ws "\"" expert-val "\"" -expert-val ::= "locator_repair" | "codegen" | "refactor" | "doc" | "office" | "chat" +expert-val ::= "locator_repair" | "codegen" | "refactor" | "doc" | "office" | "chat" | "vision" task-summary ::= "\"task_summary\"" ws ":" ws string difficulty ::= "\"difficulty\"" ws ":" ws ("\"easy\"" | "\"hard\"") string ::= "\"" [^"]* "\"" ws ::= [ \t\n]* ''' -VALID_EXPERT_TYPES = {"locator_repair", "codegen", "refactor", "doc", "office", "chat"} +VALID_EXPERT_TYPES = {"locator_repair", "codegen", "refactor", "doc", "office", "chat", "vision"} VALID_DIFFICULTIES = {"easy", "hard"} diff --git a/kaiwu/core/orchestrator.py b/kaiwu/core/orchestrator.py index 6672286..8ac4bbe 100644 --- a/kaiwu/core/orchestrator.py +++ b/kaiwu/core/orchestrator.py @@ -36,6 +36,7 @@ EXPERT_SEQUENCES = { "doc": ["locator", "generator"], "office": ["office"], "chat": ["chat"], + "vision": ["vision"], } @@ -59,6 +60,7 @@ class PipelineOrchestrator: ab_tester: ABTester | None = None, chat_expert: ChatExpert | None = None, debug_subagent=None, + vision_expert=None, ): self.locator = locator self.generator = generator @@ -66,6 +68,7 @@ class PipelineOrchestrator: self.search_augmentor = search_augmentor self.office_handler = office_handler self.chat_expert = chat_expert + self.vision_expert = vision_expert self.tools = tool_executor self.memory = memory self.registry = registry @@ -85,6 +88,7 @@ class PipelineOrchestrator: no_search: bool = False, skip_checkpoint: bool = False, pre_search_results: str = "", + image_paths: list = None, ) -> dict: """ Execute the expert pipeline. @@ -104,6 +108,11 @@ class PipelineOrchestrator: expert_system_prompt=gate_result.get("system_prompt", ""), ) + # 处理图片路径 + if image_paths: + ctx.image_paths = image_paths + logger.info(f"[orchestrator] 任务包含 {len(image_paths)} 张图片") + expert_type = gate_result.get("expert_type", "locator_repair") # ── Pre-search results injection (P1-B) ── @@ -140,6 +149,30 @@ class PipelineOrchestrator: "elapsed": elapsed, } + # vision类型:图片处理任务 + if expert_type == "vision": + self._emit(on_status, "vision", "图片处理模式") + if self.vision_expert and ctx.image_paths: + # 使用第一个图片进行处理 + ctx.image_path = ctx.image_paths[0] + result = self.vision_expert.run(ctx) + elapsed = time.time() - start_time + return { + "success": result.get("success", False), + "context": ctx, + "error": result.get("error"), + "elapsed": elapsed, + } + else: + ctx.generator_output = {"explanation": "图片处理功能需要配置Vision专家", "patches": []} + elapsed = time.time() - start_time + return { + "success": False, + "context": ctx, + "error": "Vision专家未配置或未提供图片", + "elapsed": elapsed, + } + # Gate 3: AB test — check if a candidate expert should be used for this task ab_candidate_name = None ab_used_new = False diff --git a/kaiwu/experts/vision_expert.py b/kaiwu/experts/vision_expert.py new file mode 100644 index 0000000..ea52462 --- /dev/null +++ b/kaiwu/experts/vision_expert.py @@ -0,0 +1,425 @@ +""" +VisionExpert: 多模态图片处理专家 +支持图片上传、剪贴板粘贴、图片分析和基于图片的代码生成。 + +Pipeline: 图片输入 → 图片分析 → 任务路由 → 代码生成/图片描述 +""" + +import base64 +import logging +import os +import tempfile +from pathlib import Path +from typing import Optional, Union + +from kaiwu.core.context import TaskContext + +logger = logging.getLogger(__name__) + +# ── Vision system prompts ────────────────────────────────────────── + +VISION_ANALYSIS_SYSTEM = """\ +你是KWCode的多模态视觉专家。用户上传了一张图片,请根据图片内容提供有用的分析。 + +## 分析类型 +1. **代码截图分析**:如果图片包含代码、错误信息、终端输出 + - 识别代码语言和功能 + - 分析错误信息并提供修复建议 + - 如果用户有相关任务,提供代码修改建议 + +2. **UI/设计图分析**:如果图片是UI设计、网页截图、界面原型 + - 描述布局和设计元素 + - 提供实现建议(HTML/CSS/框架) + - 识别可复用的组件 + +3. **文档/表格分析**:如果图片是文档、表格、图表 + - 提取关键信息 + - 结构化数据 + - 提供数据处理建议 + +4. **通用图片分析**:其他类型图片 + - 详细描述图片内容 + - 识别关键元素 + - 提供相关建议 + +## 输出格式 +- 简洁明了,2-5句话 +- 如果是代码相关,提供具体的修改建议 +- 如果是UI设计,提供实现方向 +- 使用中文回复""" + +VISION_CODEGEN_SYSTEM = """\ +你是KWCode的多模态代码生成专家。用户上传了图片并要求生成代码。 + +## 代码生成规则 +1. **UI截图 → HTML/CSS** + - 使用Tailwind CSS + - 响应式设计 + - 保持视觉一致性 + +2. **错误截图 → 修复代码** + - 分析错误信息 + - 定位问题根源 + - 提供最小化修复 + +3. **设计图 → 组件代码** + - 选择合适的框架(React/Vue/原生) + - 模块化组件设计 + - 可复用性优先 + +## 输出要求 +- 只输出完整可执行的代码 +- 不要解释,不要markdown代码块标记 +- 代码末尾添加使用说明注释""" + + +class VisionExpert: + """多模态图片处理专家:分析图片内容,生成代码或提供分析""" + + def __init__(self, llm, tool_executor=None): + self.llm = llm + self.tools = tool_executor + self._temp_dir = Path(tempfile.mkdtemp(prefix="kwcode_vision_")) + + def run(self, ctx: TaskContext) -> dict: + """ + 处理图片输入任务 + + Args: + ctx: 任务上下文,包含 user_input 和 image_path + + Returns: + dict: 包含 success, output, metadata + """ + image_path = getattr(ctx, 'image_path', None) + + if not image_path: + return { + "success": False, + "output": "错误:未提供图片路径", + "metadata": {"error": "no_image_path"} + } + + # 验证图片文件 + if not self._validate_image(image_path): + return { + "success": False, + "output": f"错误:图片文件不存在或格式不支持: {image_path}", + "metadata": {"error": "invalid_image"} + } + + # 分析用户意图 + user_input = ctx.user_input.strip() + is_codegen_task = self._is_codegen_task(user_input) + + try: + # 编码图片 + image_base64 = self._encode_image(image_path) + + if is_codegen_task: + return self._run_codegen(ctx, image_base64, image_path) + else: + return self._run_analysis(ctx, image_base64, image_path) + + except Exception as e: + logger.error(f"VisionExpert error: {e}") + return { + "success": False, + "output": f"图片处理失败: {str(e)}", + "metadata": {"error": str(e)} + } + + def _validate_image(self, image_path: str) -> bool: + """验证图片文件是否存在且格式支持""" + path = Path(image_path) + if not path.exists(): + return False + + # 支持的图片格式 + supported_formats = {'.png', '.jpg', '.jpeg', '.gif', '.webp', '.bmp'} + return path.suffix.lower() in supported_formats + + def _encode_image(self, image_path: str) -> str: + """将图片编码为base64""" + with open(image_path, "rb") as f: + return base64.b64encode(f.read()).decode('utf-8') + + def _is_codegen_task(self, user_input: str) -> bool: + """判断是否为代码生成任务""" + codegen_keywords = [ + "生成代码", "写代码", "实现", "创建", "生成", + "代码", "函数", "类", "组件", "页面", + "generate", "create", "implement", "code", + "html", "css", "javascript", "python", "react", "vue" + ] + + # 检查是否包含代码相关关键词 + user_input_lower = user_input.lower() + return any(kw in user_input_lower for kw in codegen_keywords) + + def _run_analysis(self, ctx: TaskContext, image_base64: str, image_path: str) -> dict: + """运行图片分析""" + logger.info(f"[vision] 分析图片: {image_path}") + + # 构建提示词 + user_input = ctx.user_input.strip() + if user_input: + prompt = f"用户上传了一张图片并说:{user_input}\n\n请分析这张图片。" + else: + prompt = "用户上传了一张图片,请分析其内容。" + + # 调用Vision LLM + response = self._call_vision_llm( + system_prompt=VISION_ANALYSIS_SYSTEM, + user_prompt=prompt, + image_base64=image_base64 + ) + + return { + "success": True, + "output": response, + "metadata": { + "type": "vision_analysis", + "image_path": image_path, + "has_user_task": bool(user_input) + } + } + + def _run_codegen(self, ctx: TaskContext, image_base64: str, image_path: str) -> dict: + """运行基于图片的代码生成""" + logger.info(f"[vision] 基于图片生成代码: {image_path}") + + user_input = ctx.user_input.strip() + + # 构建提示词 + prompt = f"用户上传了一张图片并要求:{user_input}\n\n请根据图片内容生成代码。" + + # 调用Vision LLM + response = self._call_vision_llm( + system_prompt=VISION_CODEGEN_SYSTEM, + user_prompt=prompt, + image_base64=image_base64 + ) + + # 尝试执行生成的代码(如果用户要求) + if self.tools and self._should_execute_code(user_input): + execution_result = self._execute_generated_code(response) + if execution_result: + response += f"\n\n--- 执行结果 ---\n{execution_result}" + + return { + "success": True, + "output": response, + "metadata": { + "type": "vision_codegen", + "image_path": image_path, + "task": user_input + } + } + + def _call_vision_llm(self, system_prompt: str, user_prompt: str, image_base64: str) -> str: + """调用支持Vision的LLM (Anthropic Messages API 格式) + + 优先使用 self.llm (如果支持 vision),否则回退到环境变量配置的 API: + KWCODE_VISION_API_URL - API endpoint (默认 Anthropic 格式) + KWCODE_VISION_API_KEY - API key + KWCODE_VISION_MODEL - 模型名 (默认 mimo-v2-omni) + """ + # 尝试通过 self.llm 直接调用(如果后端支持多模态) + if self.llm is not None: + try: + return self._try_llm_vision(system_prompt, user_prompt, image_base64) + except (AttributeError, TypeError, Exception) as e: + logger.debug(f"[vision] self.llm 不支持 vision,回退到 API: {e}") + + # 回退:直接调用 Anthropic Messages API + return self._call_anthropic_vision(system_prompt, user_prompt, image_base64) + + def _try_llm_vision(self, system_prompt: str, user_prompt: str, image_base64: str) -> str: + """尝试通过 self.llm 的 chat 接口发送多模态请求""" + messages = [ + {"role": "system", "content": system_prompt}, + { + "role": "user", + "content": [ + {"type": "text", "text": user_prompt}, + { + "type": "image_url", + "image_url": {"url": f"data:image/png;base64,{image_base64}"}, + }, + ], + }, + ] + return self.llm.chat(messages, max_tokens=2048) + + def _call_anthropic_vision(self, system_prompt: str, user_prompt: str, image_base64: str) -> str: + """直接调用 Anthropic Messages API(兼容 xiaomimimo 等代理)""" + import json as _json + import urllib.request + import urllib.error + + api_url = os.environ.get( + "KWCODE_VISION_API_URL", + "https://token-plan-cn.xiaomimimo.com/anthropic/v1/messages", + ) + api_key = os.environ.get("KWCODE_VISION_API_KEY", "") + model = os.environ.get("KWCODE_VISION_MODEL", "mimo-v2-omni") + + # 检测图片格式(从 base64 开头字节判断) + media_type = "image/png" + raw_sample = image_base64[:20] + if raw_sample.startswith("/9j"): + media_type = "image/jpeg" + elif raw_sample.startswith("R0lGOD"): + media_type = "image/gif" + elif raw_sample.startswith("UklGR"): + media_type = "image/webp" + + payload = { + "model": model, + "max_tokens": 2048, + "system": system_prompt, + "messages": [ + { + "role": "user", + "content": [ + { + "type": "image", + "source": { + "type": "base64", + "media_type": media_type, + "data": image_base64, + }, + }, + {"type": "text", "text": user_prompt}, + ], + } + ], + } + + headers = { + "Content-Type": "application/json", + "anthropic-version": "2023-06-01", + } + if api_key: + headers["x-api-key"] = api_key + + logger.info(f"[vision] 调用 {api_url} model={model}") + data_bytes = _json.dumps(payload).encode("utf-8") + req = urllib.request.Request(api_url, data=data_bytes, headers=headers, method="POST") + + try: + with urllib.request.urlopen(req, timeout=90) as resp: + result = _json.loads(resp.read().decode("utf-8")) + except urllib.error.HTTPError as e: + body = e.read().decode("utf-8", errors="replace")[:500] + raise RuntimeError(f"Vision API HTTP {e.code}: {body}") from e + + # 提取文本 + # 检查 API 级别错误 + if result.get("type") == "error" or result.get("isError"): + error_msg = result.get("error", {}).get("message", str(result)) + raise RuntimeError(f"Vision API error: {error_msg}") + + stop_reason = result.get("stop_reason", "") + if stop_reason == "max_tokens": + logger.warning("[vision] 输出被截断 (max_tokens reached)") + + text_parts = [] + for block in result.get("content", []): + if block.get("type") == "text": + text_parts.append(block["text"]) + + usage = result.get("usage", {}) + logger.info( + f"[vision] 完成: {usage.get('input_tokens', '?')} in / " + f"{usage.get('output_tokens', '?')} out tokens" + ) + return "\n".join(text_parts) if text_parts else "[VisionExpert] 模型未返回文本内容" + + def _should_execute_code(self, user_input: str) -> bool: + """判断是否应该执行生成的代码""" + execute_keywords = ["执行", "运行", "测试", "run", "execute", "test"] + return any(kw in user_input.lower() for kw in execute_keywords) + + def _execute_generated_code(self, code: str) -> Optional[str]: + """执行生成的代码""" + if not self.tools: + return None + + try: + # 保存代码到临时文件 + temp_file = self._temp_dir / "generated_code.py" + temp_file.write_text(code, encoding='utf-8') + + # 执行代码 + result = self.tools.run_bash(f"python {temp_file}") + return result.get("output", "") + + except Exception as e: + return f"执行失败: {str(e)}" + + def cleanup(self): + """清理临时文件""" + import shutil + if self._temp_dir.exists(): + shutil.rmtree(self._temp_dir) + + +def save_clipboard_image() -> Optional[str]: + """ + 从剪贴板保存图片 + + Returns: + str: 保存的图片路径,如果剪贴板没有图片则返回None + """ + try: + from PIL import Image, ImageGrab + + image = ImageGrab.grabclipboard() + if isinstance(image, Image.Image): + # 生成临时文件路径 + temp_dir = Path(tempfile.mkdtemp(prefix="kwcode_clipboard_")) + temp_path = temp_dir / "clipboard_image.png" + + # 保存图片 + image.save(temp_path, "PNG") + logger.info(f"[vision] 剪贴板图片已保存: {temp_path}") + + return str(temp_path) + else: + logger.debug("[vision] 剪贴板中没有图片") + return None + + except ImportError: + logger.warning("[vision] Pillow未安装,无法处理剪贴板图片") + return None + except Exception as e: + logger.error(f"[vision] 处理剪贴板图片失败: {e}") + return None + + +def validate_image_path(path: str) -> bool: + """验证图片路径是否有效""" + path_obj = Path(path) + if not path_obj.exists(): + return False + + supported_formats = {'.png', '.jpg', '.jpeg', '.gif', '.webp', '.bmp'} + return path_obj.suffix.lower() in supported_formats + + +def get_image_info(image_path: str) -> dict: + """获取图片基本信息""" + try: + from PIL import Image + + with Image.open(image_path) as img: + return { + "format": img.format, + "size": img.size, + "mode": img.mode, + "file_size": os.path.getsize(image_path) + } + except Exception as e: + return {"error": str(e)} diff --git a/pyproject.toml b/pyproject.toml index 1ffa3a8..fb9d764 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,6 +30,7 @@ dependencies = [ [project.optional-dependencies] rerank = ["sentence-transformers>=2.7.0"] +multimodal = ["Pillow>=10.0", "pyperclip>=1.8"] [project.scripts] kwcode = "kaiwu.cli.main:app"