Welcome to your Electron application.
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /pc/app-auto-coder/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app-auto-coder", 3 | "productName": "app-auto-coder", 4 | "version": "1.0.0", 5 | "description": "My Electron application description", 6 | "main": ".vite/build/main.js", 7 | "scripts": { 8 | "start": "electron-forge start", 9 | "package": "electron-forge package", 10 | "make": "electron-forge make", 11 | "publish": "electron-forge publish", 12 | "lint": "eslint --ext .ts,.tsx ." 13 | }, 14 | "devDependencies": { 15 | "@electron-forge/cli": "^7.4.0", 16 | "@electron-forge/maker-deb": "^7.4.0", 17 | "@electron-forge/maker-rpm": "^7.4.0", 18 | "@electron-forge/maker-squirrel": "^7.4.0", 19 | "@electron-forge/maker-zip": "^7.4.0", 20 | "@electron-forge/plugin-auto-unpack-natives": "^7.4.0", 21 | "@electron-forge/plugin-fuses": "^7.4.0", 22 | "@electron-forge/plugin-vite": "^7.4.0", 23 | "@electron/fuses": "^1.8.0", 24 | "@typescript-eslint/eslint-plugin": "^5.62.0", 25 | "@typescript-eslint/parser": "^5.62.0", 26 | "electron": "31.0.1", 27 | "eslint": "^8.57.0", 28 | "eslint-plugin-import": "^2.29.1", 29 | "ts-node": "^10.9.2", 30 | "typescript": "~4.5.4", 31 | "vite": "^5.3.1" 32 | }, 33 | "keywords": [], 34 | "author": { 35 | "name": "WilliamZhu", 36 | "email": "allwefantasy@gmail.com" 37 | }, 38 | "license": "MIT", 39 | "dependencies": { 40 | "electron-squirrel-startup": "^1.0.1" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /pc/app-auto-coder/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, 3 | Arial, sans-serif; 4 | margin: auto; 5 | max-width: 38rem; 6 | padding: 2rem; 7 | } 8 | -------------------------------------------------------------------------------- /pc/app-auto-coder/src/preload.ts: -------------------------------------------------------------------------------- 1 | // See the Electron documentation for details on how to use preload scripts: 2 | // https://www.electronjs.org/docs/latest/tutorial/process-model#preload-scripts 3 | -------------------------------------------------------------------------------- /pc/app-auto-coder/src/renderer.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file will automatically be loaded by vite and run in the "renderer" context. 3 | * To learn more about the differences between the "main" and the "renderer" context in 4 | * Electron, visit: 5 | * 6 | * https://electronjs.org/docs/tutorial/application-architecture#main-and-renderer-processes 7 | * 8 | * By default, Node.js integration in this file is disabled. When enabling Node.js integration 9 | * in a renderer process, please be aware of potential security implications. You can read 10 | * more about security risks here: 11 | * 12 | * https://electronjs.org/docs/tutorial/security 13 | * 14 | * To enable Node.js integration in this file, open up `main.ts` and enable the `nodeIntegration` 15 | * flag: 16 | * 17 | * ``` 18 | * // Create the browser window. 19 | * mainWindow = new BrowserWindow({ 20 | * width: 800, 21 | * height: 600, 22 | * webPreferences: { 23 | * nodeIntegration: true 24 | * } 25 | * }); 26 | * ``` 27 | */ 28 | 29 | import './index.css'; 30 | 31 | console.log('👋 This message is being logged by "renderer.ts", included via Vite'); 32 | -------------------------------------------------------------------------------- /pc/app-auto-coder/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "commonjs", 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "noImplicitAny": true, 9 | "sourceMap": true, 10 | "baseUrl": ".", 11 | "outDir": "dist", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /pc/app-auto-coder/vite.main.config.ts: -------------------------------------------------------------------------------- 1 | import type { ConfigEnv, UserConfig } from 'vite'; 2 | import { defineConfig, mergeConfig } from 'vite'; 3 | import { getBuildConfig, getBuildDefine, external, pluginHotRestart } from './vite.base.config'; 4 | 5 | // https://vitejs.dev/config 6 | export default defineConfig((env) => { 7 | const forgeEnv = env as ConfigEnv<'build'>; 8 | const { forgeConfigSelf } = forgeEnv; 9 | const define = getBuildDefine(forgeEnv); 10 | const config: UserConfig = { 11 | build: { 12 | lib: { 13 | entry: forgeConfigSelf.entry!, 14 | fileName: () => '[name].js', 15 | formats: ['cjs'], 16 | }, 17 | rollupOptions: { 18 | external, 19 | }, 20 | }, 21 | plugins: [pluginHotRestart('restart')], 22 | define, 23 | resolve: { 24 | // Load the Node.js entry. 25 | mainFields: ['module', 'jsnext:main', 'jsnext'], 26 | }, 27 | }; 28 | 29 | return mergeConfig(getBuildConfig(forgeEnv), config); 30 | }); 31 | -------------------------------------------------------------------------------- /pc/app-auto-coder/vite.preload.config.ts: -------------------------------------------------------------------------------- 1 | import type { ConfigEnv, UserConfig } from 'vite'; 2 | import { defineConfig, mergeConfig } from 'vite'; 3 | import { getBuildConfig, external, pluginHotRestart } from './vite.base.config'; 4 | 5 | // https://vitejs.dev/config 6 | export default defineConfig((env) => { 7 | const forgeEnv = env as ConfigEnv<'build'>; 8 | const { forgeConfigSelf } = forgeEnv; 9 | const config: UserConfig = { 10 | build: { 11 | rollupOptions: { 12 | external, 13 | // Preload scripts may contain Web assets, so use the `build.rollupOptions.input` instead `build.lib.entry`. 14 | input: forgeConfigSelf.entry!, 15 | output: { 16 | format: 'cjs', 17 | // It should not be split chunks. 18 | inlineDynamicImports: true, 19 | entryFileNames: '[name].js', 20 | chunkFileNames: '[name].js', 21 | assetFileNames: '[name].[ext]', 22 | }, 23 | }, 24 | }, 25 | plugins: [pluginHotRestart('reload')], 26 | }; 27 | 28 | return mergeConfig(getBuildConfig(forgeEnv), config); 29 | }); 30 | -------------------------------------------------------------------------------- /pc/app-auto-coder/vite.renderer.config.ts: -------------------------------------------------------------------------------- 1 | import type { ConfigEnv, UserConfig } from 'vite'; 2 | import { defineConfig } from 'vite'; 3 | import { pluginExposeRenderer } from './vite.base.config'; 4 | 5 | // https://vitejs.dev/config 6 | export default defineConfig((env) => { 7 | const forgeEnv = env as ConfigEnv<'renderer'>; 8 | const { root, mode, forgeConfigSelf } = forgeEnv; 9 | const name = forgeConfigSelf.name ?? ''; 10 | 11 | return { 12 | root, 13 | mode, 14 | base: './', 15 | build: { 16 | outDir: `.vite/renderer/${name}`, 17 | }, 18 | plugins: [pluginExposeRenderer(name)], 19 | resolve: { 20 | preserveSymlinks: true, 21 | }, 22 | clearScreen: false, 23 | } as UserConfig; 24 | }); 25 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | filterwarnings = 3 | ignore::Warning 4 | addopts = -s 5 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | contextlib2 2 | ninja 3 | jinja2 4 | rich 5 | # netifaces 6 | # accelerate 7 | # bitsandbytes>=0.39.0 8 | # transformers>=4.35.0 9 | # torch>=2.1.2 10 | # ray[default]>=2.9.1 11 | # sentencepiece 12 | # sentence-transformers 13 | # transformers_stream_generator 14 | # optimum 15 | # einops 16 | # pyarrow 17 | # datasets 18 | # langchain 19 | 20 | paramiko 21 | 22 | tqdm 23 | loguru 24 | pyjava>=0.6.21 25 | fastapi 26 | uvicorn 27 | retrying 28 | zhipuai 29 | dashscope 30 | tiktoken 31 | tabulate 32 | jupyter_client 33 | prompt-toolkit 34 | tokenizers 35 | aiofiles 36 | readerwriterlock 37 | 38 | # dependencies only for stable_diffusion 39 | # safetensors 40 | # pydantic 41 | # nltk 42 | # pillow 43 | # diffusers 44 | # lycoris 45 | # lycoris-lora 46 | ## the package above will downgrade the tokenizers, so here we need to force lock the version 47 | # tokenizers==0.13.3 48 | 49 | # camelot-py 50 | # llama_index 51 | byzerllm[saas]>=0.1.190 52 | patch 53 | diff_match_patch 54 | GitPython 55 | openai>=1.14.3 56 | anthropic 57 | google-generativeai 58 | protobuf 59 | azure-cognitiveservices-speech 60 | real_agent 61 | 62 | duckdb 63 | python-docx 64 | docx2txt 65 | pdf2image 66 | # Spire.Doc 67 | docx2pdf 68 | pypdf 69 | pyperclip 70 | colorama 71 | pylint 72 | reportlab 73 | pathspec 74 | # simpleaudio 75 | # pandoc 76 | # pypandoc 77 | openpyxl 78 | python-pptx 79 | watchfiles 80 | cairosvg 81 | matplotlib 82 | mammoth 83 | markdownify 84 | pdfminer.six 85 | puremagic 86 | pydub 87 | youtube-transcript-api 88 | SpeechRecognition 89 | pathvalidate 90 | pexpect 91 | mcp ; python_version >= '3.10' 92 | setuptools 93 | filelock 94 | argcomplete 95 | -------------------------------------------------------------------------------- /scripts/run_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 确保脚本可以在任何目录下执行 4 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 5 | PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" 6 | cd "$PROJECT_ROOT" 7 | 8 | # 设置环境变量 9 | export autocoder_auto_init=${autocoder_auto_init:-false} 10 | export auto_coder_log_stdout=${auto_coder_log_stdout:-true} 11 | 12 | echo "环境变量设置:" 13 | echo " - autocoder_auto_init=${autocoder_auto_init}" 14 | echo " - auto_coder_log_stdout=${auto_coder_log_stdout}" 15 | 16 | # 检查是否提供了参数 17 | if [ $# -eq 0 ]; then 18 | echo "用法: $0 [测试路径] [pytest参数]" 19 | echo "例如: $0 src/autocoder/rag/ -v" 20 | echo "未提供参数,将运行所有测试" 21 | python -m pytest -s --log-cli-level=INFO tests/ 22 | else 23 | # 运行pytest命令并传递所有参数 24 | echo "正在运行测试: $@" 25 | python -m pytest -s --log-cli-level=INFO "$@" 26 | fi -------------------------------------------------------------------------------- /scripts/runtime_hook.py: -------------------------------------------------------------------------------- 1 | 2 | # -*- coding: utf-8 -*- 3 | # PyInstaller运行时钩子脚本 4 | import os 5 | import sys 6 | import site 7 | import importlib.util 8 | 9 | def ensure_package_imported(package_name): 10 | """确保包被正确导入""" 11 | try: 12 | importlib.import_module(package_name) 13 | return True 14 | except ImportError: 15 | return False 16 | 17 | # 添加打包后的目录到sys.path 18 | def setup_environment(): 19 | # 获取应用程序根目录 20 | if getattr(sys, 'frozen', False): 21 | # 运行在PyInstaller打包的环境中 22 | app_path = os.path.dirname(sys.executable) 23 | else: 24 | # 运行在普通Python环境中 25 | app_path = os.path.dirname(os.path.abspath(__file__)) 26 | 27 | # 添加关键路径 28 | paths_to_add = [ 29 | app_path, 30 | os.path.join(app_path, 'src'), 31 | os.path.join(app_path, 'autocoder'), 32 | ] 33 | 34 | for path in paths_to_add: 35 | if path not in sys.path and os.path.exists(path): 36 | sys.path.insert(0, path) 37 | 38 | # 设置环境变量 39 | os.environ['PYTHONPATH'] = os.pathsep.join(sys.path) 40 | 41 | # 初始化环境 42 | setup_environment() 43 | 44 | # 预加载关键模块 45 | key_packages = [ 46 | 'flask', 'jinja2', 'tiktoken', 'numpy', 'transformers' 47 | ] 48 | 49 | for package in key_packages: 50 | ensure_package_imported(package) 51 | -------------------------------------------------------------------------------- /scripts/to_cursor_rule.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 脚本功能:将.autocoderrules目录下的所有markdown文件复制到.cursor/rules目录 4 | # 如果有重名文件,强制替换 5 | 6 | # 获取当前工作目录 7 | CURRENT_DIR=$(pwd) 8 | 9 | # 源目录和目标目录 10 | SOURCE_DIR="${CURRENT_DIR}/.autocoderrules" 11 | TARGET_DIR="${CURRENT_DIR}/.cursor/rules" 12 | 13 | # 检查源目录是否存在 14 | if [ ! -d "$SOURCE_DIR" ]; then 15 | echo "错误: 源目录 $SOURCE_DIR 不存在!" 16 | exit 1 17 | fi 18 | 19 | # 检查目标目录是否存在,如果不存在则创建 20 | if [ ! -d "$TARGET_DIR" ]; then 21 | echo "目标目录 $TARGET_DIR 不存在,正在创建..." 22 | mkdir -p "$TARGET_DIR" 23 | if [ $? -ne 0 ]; then 24 | echo "错误: 无法创建目标目录 $TARGET_DIR!" 25 | exit 1 26 | fi 27 | fi 28 | 29 | echo "开始复制文件..." 30 | 31 | # 找到所有markdown文件并保存到临时数组 32 | files=($(find "$SOURCE_DIR" -type f -name "*.md")) 33 | TOTAL_FILES=${#files[@]} 34 | COPIED_FILES=0 35 | 36 | # 遍历所有找到的文件进行复制 37 | for file in "${files[@]}"; do 38 | # 获取文件名 39 | filename=$(basename "$file") 40 | 41 | # 复制文件到目标目录,强制覆盖已存在的文件 42 | cp -f "$file" "$TARGET_DIR/" 43 | 44 | if [ $? -eq 0 ]; then 45 | COPIED_FILES=$((COPIED_FILES + 1)) 46 | echo "已复制: $filename" 47 | else 48 | echo "复制失败: $filename" 49 | fi 50 | done 51 | 52 | echo "操作完成!" 53 | echo "总共找到 $TOTAL_FILES 个markdown文件" 54 | echo "成功复制 $COPIED_FILES 个文件到 $TARGET_DIR" 55 | 56 | # 给脚本执行权限 57 | chmod +x "$0" -------------------------------------------------------------------------------- /src/autocoder/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/autocoder/agent/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/autocoder/agent/base_agentic/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/autocoder/agent/base_agentic/tools/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | 基础工具包,包含基础工具解析器和注册工具 3 | """ 4 | from .base_tool_resolver import BaseToolResolver 5 | from .talk_to_tool_resolver import TalkToToolResolver 6 | from .talk_to_group_tool_resolver import TalkToGroupToolResolver 7 | 8 | __all__ = [ 9 | "BaseToolResolver", 10 | "TalkToToolResolver", 11 | "TalkToGroupToolResolver" 12 | ] -------------------------------------------------------------------------------- /src/autocoder/agent/base_agentic/tools/attempt_completion_tool_resolver.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Any, Optional 2 | from autocoder.agent.base_agentic.tools.base_tool_resolver import BaseToolResolver 3 | from autocoder.agent.base_agentic.types import AttemptCompletionTool, ToolResult # Import ToolResult from types 4 | from loguru import logger 5 | import typing 6 | from autocoder.common import AutoCoderArgs 7 | import os 8 | import tempfile 9 | 10 | if typing.TYPE_CHECKING: 11 | from ..base_agent import BaseAgent 12 | 13 | class AttemptCompletionToolResolver(BaseToolResolver): 14 | def __init__(self, agent: Optional['BaseAgent'], tool: AttemptCompletionTool, args: AutoCoderArgs): 15 | super().__init__(agent, tool, args) 16 | self.tool: AttemptCompletionTool = tool # For type hinting 17 | 18 | def resolve(self) -> ToolResult: 19 | """ 20 | Packages the completion result and optional command to signal task completion. 21 | """ 22 | result_text = self.tool.result 23 | command = self.tool.command 24 | 25 | logger.info(f"Resolving AttemptCompletionTool: Result='{result_text[:100]}...', Command='{command}'") 26 | 27 | if not result_text: 28 | return ToolResult(success=False, message="Error: Completion result cannot be empty.") 29 | 30 | # The actual presentation of the result happens outside the resolver. 31 | result_content = { 32 | "result": result_text, 33 | "command": command 34 | } 35 | 36 | # Indicate success in preparing the completion data 37 | return ToolResult(success=True, message="Task completion attempted.", content=result_content) 38 | -------------------------------------------------------------------------------- /src/autocoder/agent/base_agentic/tools/base_tool_resolver.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from typing import Any, Dict, Optional, TYPE_CHECKING 3 | from autocoder.common import AutoCoderArgs 4 | 5 | if TYPE_CHECKING: 6 | from autocoder.agent.base_agentic.base_agent import BaseAgent 7 | from autocoder.agent.base_agentic.types import BaseTool, ToolResult 8 | 9 | 10 | class BaseToolResolver(ABC): 11 | """ 12 | 工具解析器的基类,所有工具解析器都应该继承此类 13 | """ 14 | def __init__(self, agent: Optional['BaseAgent'], tool: 'BaseTool', args: AutoCoderArgs): 15 | """ 16 | 初始化解析器 17 | 18 | Args: 19 | agent: 代理实例,可能为None 20 | tool: 工具实例 21 | args: 其他需要的参数,如项目目录等 22 | """ 23 | self.agent = agent 24 | self.tool = tool 25 | self.args = args 26 | 27 | @abstractmethod 28 | def resolve(self) -> 'ToolResult': 29 | """ 30 | 执行工具逻辑,返回结果 31 | 32 | Returns: 33 | ToolResult对象,表示成功或失败以及消息 34 | """ 35 | pass -------------------------------------------------------------------------------- /src/autocoder/agent/base_agentic/tools/example_tool_resolver.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | from autocoder.common import AutoCoderArgs 3 | from autocoder.agent.base_agentic.tools.base_tool_resolver import BaseToolResolver 4 | from autocoder.agent.base_agentic.types import BaseTool, ToolResult 5 | from pydantic import BaseModel 6 | import typing 7 | 8 | if typing.TYPE_CHECKING: 9 | from ..base_agent import BaseAgent 10 | 11 | 12 | # 首先定义工具模型 13 | class ExampleTool(BaseTool): 14 | """示例工具,只是一个演示""" 15 | message: str 16 | times: int = 1 17 | 18 | 19 | # 然后定义对应的解析器 20 | class ExampleToolResolver(BaseToolResolver): 21 | """示例工具解析器""" 22 | def __init__(self, agent: Optional['BaseAgent'], tool: ExampleTool, args: AutoCoderArgs): 23 | super().__init__(agent, tool, args) 24 | self.tool: ExampleTool = tool # 类型提示 25 | 26 | def resolve(self) -> ToolResult: 27 | """ 28 | 执行工具逻辑 29 | 30 | Returns: 31 | 工具执行结果 32 | """ 33 | try: 34 | # 简单示例:重复消息N次 35 | result = self.tool.message * self.tool.times 36 | return ToolResult( 37 | success=True, 38 | message="成功执行示例工具", 39 | content=result 40 | ) 41 | except Exception as e: 42 | return ToolResult( 43 | success=False, 44 | message=f"执行示例工具时出错: {str(e)}", 45 | content=None 46 | ) -------------------------------------------------------------------------------- /src/autocoder/agent/base_agentic/tools/plan_mode_respond_tool_resolver.py: -------------------------------------------------------------------------------- 1 | import json 2 | from typing import Dict, Any, Optional 3 | import typing 4 | from autocoder.common import AutoCoderArgs 5 | from autocoder.agent.base_agentic.tools.base_tool_resolver import BaseToolResolver 6 | from autocoder.agent.base_agentic.types import PlanModeRespondTool, ToolResult # Import ToolResult from types 7 | from loguru import logger 8 | 9 | if typing.TYPE_CHECKING: 10 | from ..base_agent import BaseAgent 11 | 12 | class PlanModeRespondToolResolver(BaseToolResolver): 13 | def __init__(self, agent: Optional['BaseAgent'], tool: PlanModeRespondTool, args: AutoCoderArgs): 14 | super().__init__(agent, tool, args) 15 | self.tool: PlanModeRespondTool = tool # For type hinting 16 | 17 | def resolve(self) -> ToolResult: 18 | """ 19 | Packages the response and options for Plan Mode interaction. 20 | """ 21 | response_text = self.tool.response 22 | options = self.tool.options 23 | logger.info(f"Resolving PlanModeRespondTool: Response='{response_text[:100]}...', Options={options}") 24 | 25 | if not response_text: 26 | return ToolResult(success=False, message="Error: Plan mode response cannot be empty.") 27 | 28 | # The actual presentation happens outside the resolver. 29 | result_content = { 30 | "response": response_text, 31 | "options": options 32 | } 33 | 34 | # Indicate success in preparing the plan mode response data 35 | return ToolResult(success=True, message="Plan mode response prepared.", content=result_content) 36 | -------------------------------------------------------------------------------- /src/autocoder/agent/base_agentic/tools/use_mcp_tool_resolver.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Any, Optional 2 | import typing 3 | from autocoder.agent.base_agentic.tools.base_tool_resolver import BaseToolResolver 4 | from autocoder.agent.base_agentic.types import UseMcpTool, ToolResult # Import ToolResult from types 5 | from autocoder.common import AutoCoderArgs 6 | from loguru import logger 7 | 8 | if typing.TYPE_CHECKING: 9 | from ..base_agent import BaseAgent 10 | 11 | 12 | class UseMcpToolResolver(BaseToolResolver): 13 | def __init__(self, agent: Optional['BaseAgent'], tool: UseMcpTool, args: AutoCoderArgs): 14 | super().__init__(agent, tool, args) 15 | self.tool: UseMcpTool = tool # For type hinting 16 | 17 | def resolve(self) -> ToolResult: 18 | """ 19 | Executes a tool via the Model Context Protocol (MCP) server. 20 | """ 21 | final_query = "" 22 | server_name = self.tool.server_name 23 | tool_name = self.tool.tool_name 24 | 25 | if server_name: 26 | final_query += f"{server_name}\n" 27 | 28 | if tool_name: 29 | final_query += f"{tool_name} is recommended for the following query:\n" 30 | 31 | final_query += f"{self.tool.query}" 32 | 33 | logger.info(f"Resolving UseMcpTool: Server='{server_name}', Tool='{tool_name}', Query='{final_query}'") 34 | 35 | mcp_server = get_mcp_server() 36 | response = mcp_server.send_request( 37 | McpRequest( 38 | query=final_query, 39 | model=self.args.inference_model or self.args.model, 40 | product_mode=self.args.product_mode 41 | ) 42 | ) 43 | return ToolResult(success=True, message=response.result) 44 | 45 | -------------------------------------------------------------------------------- /src/autocoder/agent/entry_command_agent/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Entry Command Agent 模块 3 | 4 | 该模块包含各种入口命令的代理实现,用于处理不同类型的用户命令。 5 | 6 | 目前包含的代理: 7 | - ChatAgent: 处理聊天命令的代理 8 | - ProjectReaderAgent: 处理项目阅读命令的代理 9 | - Voice2TextAgent: 处理语音转文字命令的代理 10 | - GenerateCommandAgent: 处理命令生成的代理 11 | - AutoToolAgent: 处理自动工具命令的代理 12 | - DesignerAgent: 处理设计命令的代理 13 | """ 14 | 15 | from .chat import ChatAgent 16 | from .project_reader import ProjectReaderAgent 17 | from .voice2text import Voice2TextAgent 18 | from .generate_command import GenerateCommandAgent 19 | from .auto_tool import AutoToolAgent 20 | from .designer import DesignerAgent 21 | 22 | __all__ = [ 23 | 'ChatAgent', 24 | 'ProjectReaderAgent', 25 | 'Voice2TextAgent', 26 | 'GenerateCommandAgent', 27 | 'AutoToolAgent', 28 | 'DesignerAgent' 29 | ] 30 | -------------------------------------------------------------------------------- /src/autocoder/agent/entry_command_agent/auto_tool.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | from rich.console import Console 7 | from rich.panel import Panel 8 | from rich.markdown import Markdown 9 | from rich.live import Live 10 | 11 | from autocoder.utils.request_queue import ( 12 | request_queue, 13 | RequestValue, 14 | DefaultValue, 15 | RequestOption, 16 | ) 17 | 18 | 19 | class AutoToolAgent: 20 | def __init__(self, args, llm, raw_args): 21 | self.args = args 22 | self.llm = llm 23 | self.raw_args = raw_args 24 | self.console = Console() 25 | 26 | def run(self): 27 | """执行 auto_tool 命令的主要逻辑""" 28 | from autocoder.agent.auto_tool import AutoTool 29 | 30 | auto_tool = AutoTool(self.args, self.llm) 31 | v = auto_tool.run(self.args.query) 32 | 33 | if self.args.request_id: 34 | request_queue.add_request( 35 | self.args.request_id, 36 | RequestValue( 37 | value=DefaultValue(value=v), status=RequestOption.COMPLETED 38 | ), 39 | ) 40 | 41 | markdown_content = v 42 | 43 | with Live( 44 | Panel("", title="Response", border_style="green", expand=False), 45 | refresh_per_second=4, 46 | auto_refresh=True, 47 | vertical_overflow="visible", 48 | console=Console(force_terminal=True, color_system="auto", height=None) 49 | ) as live: 50 | live.update( 51 | Panel( 52 | Markdown(markdown_content), 53 | title="Response", 54 | border_style="green", 55 | expand=False, 56 | ) 57 | ) 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /src/autocoder/agent/entry_command_agent/designer.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | from autocoder.utils.request_queue import ( 9 | request_queue, 10 | RequestValue, 11 | DefaultValue, 12 | RequestOption, 13 | ) 14 | 15 | 16 | class DesignerAgent: 17 | def __init__(self, args, llm, raw_args): 18 | self.args = args 19 | self.llm = llm 20 | self.raw_args = raw_args 21 | 22 | def run(self): 23 | """执行 designer 命令的主要逻辑""" 24 | from autocoder.agent.designer import SVGDesigner, SDDesigner, LogoDesigner 25 | 26 | if self.args.agent_designer_mode == "svg": 27 | designer = SVGDesigner(self.args, self.llm) 28 | designer.run(self.args.query) 29 | print("Successfully generated image in output.png") 30 | elif self.args.agent_designer_mode == "sd": 31 | designer = SDDesigner(self.args, self.llm) 32 | designer.run(self.args.query) 33 | print("Successfully generated image in output.jpg") 34 | elif self.args.agent_designer_mode.startswith("logo"): 35 | designer = LogoDesigner(self.args, self.llm) 36 | designer.run(self.args.query) 37 | print("Successfully generated image in output.png") 38 | 39 | if self.args.request_id: 40 | request_queue.add_request( 41 | self.args.request_id, 42 | RequestValue( 43 | value=DefaultValue( 44 | value="Successfully generated image"), 45 | status=RequestOption.COMPLETED, 46 | ), 47 | ) 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/autocoder/agent/entry_command_agent/generate_command.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | import os 6 | from rich.console import Console 7 | from rich.panel import Panel 8 | 9 | from autocoder.utils.request_queue import ( 10 | request_queue, 11 | RequestValue, 12 | DefaultValue, 13 | RequestOption, 14 | ) 15 | 16 | 17 | class GenerateCommandAgent: 18 | def __init__(self, args, llm, raw_args): 19 | self.args = args 20 | self.llm = llm 21 | self.raw_args = raw_args 22 | self.console = Console() 23 | 24 | def run(self): 25 | """执行 generate_command 命令的主要逻辑""" 26 | from autocoder.common.command_generator import generate_shell_script 27 | 28 | shell_script = generate_shell_script(self.args, self.llm) 29 | 30 | self.console.print( 31 | Panel( 32 | shell_script, 33 | title="Shell Script", 34 | border_style="magenta", 35 | ) 36 | ) 37 | 38 | with open(os.path.join(".auto-coder", "exchange.txt"), "w", encoding="utf-8") as f: 39 | f.write(shell_script) 40 | 41 | request_queue.add_request( 42 | self.args.request_id, 43 | RequestValue( 44 | value=DefaultValue(value=shell_script), 45 | status=RequestOption.COMPLETED, 46 | ), 47 | ) 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/autocoder/chat/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/autocoder/commands/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/autocoder/common/ac_style_command_parser/__init__.py: -------------------------------------------------------------------------------- 1 | from .parser import ( 2 | CommandParser, 3 | parse_query, 4 | has_command, 5 | get_command_args, 6 | get_command_kwargs 7 | ) 8 | 9 | __all__ = [ 10 | "CommandParser", 11 | "parse_query", 12 | "has_command", 13 | "get_command_args", 14 | "get_command_kwargs" 15 | ] -------------------------------------------------------------------------------- /src/autocoder/common/buildin_tokenizer.py: -------------------------------------------------------------------------------- 1 | import pkg_resources 2 | from tokenizers import Tokenizer 3 | from typing import Optional 4 | 5 | class BuildinTokenizer: 6 | _instance: Optional['BuildinTokenizer'] = None 7 | _tokenizer: Optional[Tokenizer] = None 8 | 9 | def __new__(cls) -> 'BuildinTokenizer': 10 | if cls._instance is None: 11 | cls._instance = super(BuildinTokenizer, cls).__new__(cls) 12 | cls._instance._initialize() 13 | return cls._instance 14 | 15 | def _initialize(self) -> None: 16 | try: 17 | tokenizer_path = pkg_resources.resource_filename( 18 | "autocoder", "data/tokenizer.json" 19 | ) 20 | except FileNotFoundError: 21 | tokenizer_path = None 22 | 23 | if tokenizer_path: 24 | self._tokenizer = Tokenizer.from_file(tokenizer_path) 25 | else: 26 | raise ValueError("Cannot find tokenizer.json file in package data directory") 27 | 28 | def count_tokens(self, text: str) -> int: 29 | if not self._tokenizer: 30 | raise ValueError("Tokenizer is not initialized") 31 | 32 | encoded = self._tokenizer.encode('{"role":"user","content":"' + text + '"}') 33 | return len(encoded.ids) 34 | 35 | @property 36 | def tokenizer(self) -> Optional[Tokenizer]: 37 | return self._tokenizer -------------------------------------------------------------------------------- /src/autocoder/common/cleaner.py: -------------------------------------------------------------------------------- 1 | import re 2 | import os 3 | 4 | PYTHON_PREFIX = os.environ.get("CONDA_PREFIX", "/usr/local") 5 | 6 | SITE_PKG_ERROR_PREFIX = f'File {PYTHON_PREFIX}/lib/python3.10/' 7 | 8 | def get_error_header(traceback_str): 9 | lines = traceback_str.split('\n') 10 | for line in lines: 11 | if 'Error:' in line: 12 | return line 13 | return '' # Return None if no error message is found 14 | 15 | def clean_error_msg(error_str:str =''): 16 | filtered_error_msg = error_str.__str__().split('An error occurred while executing the following cell')[-1].split("\n------------------\n")[-1] 17 | raw_error_msg = "".join(filtered_error_msg) 18 | 19 | # Remove escape sequences for colored text 20 | ansi_escape = re.compile(r'\x1b\[[0-?]*[ -/]*[@-~]') 21 | error_msg = ansi_escape.sub('', raw_error_msg) 22 | 23 | error_str_out = '' 24 | error_msg_only_cell = error_msg.split(SITE_PKG_ERROR_PREFIX) 25 | 26 | error_str_out += f'{error_msg_only_cell[0]}\n' 27 | error_header = get_error_header(error_msg_only_cell[-1]) 28 | if error_header not in error_str_out: 29 | error_str_out += get_error_header(error_msg_only_cell[-1]) 30 | 31 | return error_str_out -------------------------------------------------------------------------------- /src/autocoder/common/command_file_manager/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | 命令管理模块 3 | 4 | 该模块提供了一种机制来管理和分析 .autocodercommands 目录中的命令文件。 5 | 主要功能包括: 6 | 1. 列出命令目录中的所有命令文件 7 | 2. 读取指定的命令文件内容 8 | 3. 分析命令文件,提取其中的Jinja2变量 9 | 4. 提供简单直观的API,便于集成到现有的项目中 10 | """ 11 | 12 | from autocoder.common.command_file_manager.models import ( 13 | CommandFile, JinjaVariable, CommandFileAnalysisResult, ListCommandsResult 14 | ) 15 | from autocoder.common.command_file_manager.manager import CommandManager 16 | from autocoder.common.command_file_manager.utils import ( 17 | extract_jinja2_variables, extract_jinja2_variables_with_metadata, 18 | analyze_command_file, is_command_file 19 | ) 20 | 21 | __all__ = [ 22 | 'CommandFile', 'JinjaVariable', 'CommandFileAnalysisResult', 'ListCommandsResult', 23 | 'CommandManager', 'extract_jinja2_variables', 'extract_jinja2_variables_with_metadata', 24 | 'analyze_command_file', 'is_command_file' 25 | ] 26 | -------------------------------------------------------------------------------- /src/autocoder/common/conf_utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import pkg_resources 4 | from autocoder.common import AutoCoderArgs 5 | 6 | ## 用于auto-coder 内部使用 7 | 8 | def load_tokenizer(): 9 | from autocoder.rag.variable_holder import VariableHolder 10 | from tokenizers import Tokenizer 11 | try: 12 | tokenizer_path = pkg_resources.resource_filename( 13 | "autocoder", "data/tokenizer.json" 14 | ) 15 | VariableHolder.TOKENIZER_PATH = tokenizer_path 16 | VariableHolder.TOKENIZER_MODEL = Tokenizer.from_file(tokenizer_path) 17 | except FileNotFoundError: 18 | tokenizer_path = None 19 | 20 | 21 | def save_memory(args: AutoCoderArgs,memory): 22 | with open(os.path.join(args.source_dir, ".auto-coder", "plugins", "chat-auto-coder", "memory.json"), "w",encoding="utf-8") as f: 23 | json.dump(memory, f, indent=2, ensure_ascii=False) 24 | 25 | def load_memory(args: AutoCoderArgs): 26 | memory_path = os.path.join(args.source_dir, ".auto-coder", "plugins", "chat-auto-coder", "memory.json") 27 | if os.path.exists(memory_path): 28 | with open(memory_path, "r", encoding="utf-8") as f: 29 | _memory = json.load(f) 30 | memory = _memory 31 | else: 32 | memory = {} 33 | return memory 34 | 35 | def get_memory(args: AutoCoderArgs): 36 | return load_memory(args) -------------------------------------------------------------------------------- /src/autocoder/common/conversations/backup/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Backup and restore functionality for conversation management. 3 | 4 | This package provides backup and restore capabilities for conversation data, 5 | including incremental and full backups, version management, and data recovery. 6 | """ 7 | 8 | from .backup_manager import BackupManager 9 | from .restore_manager import RestoreManager 10 | 11 | __all__ = [ 12 | 'BackupManager', 13 | 'RestoreManager', 14 | ] -------------------------------------------------------------------------------- /src/autocoder/common/conversations/cache/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Cache module for conversation management. 3 | 4 | This module provides caching functionality for conversations and messages, 5 | including memory-based caching with LRU eviction and TTL support. 6 | """ 7 | 8 | from .base_cache import BaseCache 9 | from .memory_cache import MemoryCache 10 | from .cache_manager import CacheManager 11 | 12 | __all__ = [ 13 | 'BaseCache', 14 | 'MemoryCache', 15 | 'CacheManager' 16 | ] -------------------------------------------------------------------------------- /src/autocoder/common/conversations/search/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Search and filtering module for conversations. 3 | 4 | This module provides text search and filtering capabilities for conversations 5 | and messages, supporting full-text search, keyword matching, and complex 6 | filtering operations. 7 | """ 8 | 9 | from .text_searcher import TextSearcher 10 | from .filter_manager import FilterManager 11 | 12 | __all__ = [ 13 | 'TextSearcher', 14 | 'FilterManager' 15 | ] -------------------------------------------------------------------------------- /src/autocoder/common/conversations/storage/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Storage module for conversation management. 3 | 4 | This module provides storage functionality for conversations and messages, 5 | including file-based storage and indexing capabilities. 6 | """ 7 | 8 | from .base_storage import BaseStorage 9 | from .file_storage import FileStorage 10 | from .index_manager import IndexManager 11 | 12 | __all__ = [ 13 | 'BaseStorage', 14 | 'FileStorage', 15 | 'IndexManager' 16 | ] -------------------------------------------------------------------------------- /src/autocoder/common/directory_cache/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/autocoder/common/file_checkpoint/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | 文件变更管理模块 3 | 4 | 该模块提供了一种可靠的机制来应用、记录和撤销对项目文件的修改。 5 | 主要功能包括: 6 | 1. 将影子系统中的文件变更安全地应用到用户的实际项目中 7 | 2. 记录每次变更的历史,支持多版本撤销功能 8 | 3. 提供简单直观的API,便于集成到现有的编辑流程中 9 | """ 10 | 11 | from autocoder.common.file_checkpoint.models import ( 12 | FileChange, ChangeRecord, ApplyResult, UndoResult, DiffResult 13 | ) 14 | from autocoder.common.file_checkpoint.manager import FileChangeManager 15 | from autocoder.common.file_checkpoint.store import FileChangeStore 16 | from autocoder.common.file_checkpoint.backup import FileBackupManager 17 | 18 | __all__ = [ 19 | 'FileChange', 'ChangeRecord', 'ApplyResult', 'UndoResult', 'DiffResult', 20 | 'FileChangeManager', 'FileChangeStore', 'FileBackupManager' 21 | ] 22 | -------------------------------------------------------------------------------- /src/autocoder/common/file_monitor/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | # -*- coding: utf-8 -*- 3 | from .monitor import FileMonitor 4 | 5 | __all__ = ["FileMonitor"] 6 | -------------------------------------------------------------------------------- /src/autocoder/common/ignorefiles/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | from .ignore_file_utils import should_ignore 3 | 4 | __all__ = ["should_ignore"] 5 | -------------------------------------------------------------------------------- /src/autocoder/common/mcp_servers/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/autocoder/common/pruner/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/autocoder/common/pull_requests/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/autocoder/common/pull_requests/config.py: -------------------------------------------------------------------------------- 1 | """ 2 | Pull Request 配置管理 3 | """ 4 | import os 5 | from typing import Dict, Any, Optional 6 | from .models import PRConfig, PlatformType 7 | from .exceptions import ConfigurationError 8 | 9 | 10 | def get_config(platform: str, **overrides) -> PRConfig: 11 | """ 12 | 获取平台配置 13 | 14 | Args: 15 | platform: 平台名称 16 | **overrides: 配置覆盖参数 17 | 18 | Returns: 19 | 配置对象 20 | """ 21 | # 从环境变量加载配置 22 | env_config = _load_from_env(platform) 23 | 24 | # 合并配置 25 | merged_config = {} 26 | if env_config: 27 | merged_config.update(env_config) 28 | merged_config.update(overrides) 29 | 30 | # 验证必需的配置 31 | if 'token' not in merged_config: 32 | raise ConfigurationError(f"平台 {platform} 缺少必需的 token 配置") 33 | 34 | return PRConfig(platform=PlatformType(platform), **merged_config) 35 | 36 | 37 | def _load_from_env(platform: str) -> Dict[str, Any]: 38 | """从环境变量加载配置""" 39 | env_mappings = { 40 | 'github': { 41 | 'token': 'GITHUB_TOKEN', 42 | 'base_url': 'GITHUB_BASE_URL' 43 | }, 44 | 'gitlab': { 45 | 'token': 'GITLAB_TOKEN', 46 | 'base_url': 'GITLAB_BASE_URL' 47 | }, 48 | 'gitee': { 49 | 'token': 'GITEE_TOKEN', 50 | 'base_url': 'GITEE_BASE_URL' 51 | }, 52 | 'gitcode': { 53 | 'token': 'GITCODE_TOKEN', 54 | 'base_url': 'GITCODE_BASE_URL' 55 | } 56 | } 57 | 58 | mapping = env_mappings.get(platform, {}) 59 | config = {} 60 | 61 | for key, env_var in mapping.items(): 62 | value = os.getenv(env_var) 63 | if value: 64 | config[key] = value 65 | 66 | return config -------------------------------------------------------------------------------- /src/autocoder/common/pull_requests/example.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/autocoder/common/pull_requests/exceptions.py: -------------------------------------------------------------------------------- 1 | """ 2 | Pull Request 模块自定义异常类 3 | """ 4 | from typing import Optional 5 | 6 | class PRError(Exception): 7 | """Pull Request 操作基础异常""" 8 | def __init__(self, message: str, error_code: Optional[str] = None, platform: Optional[str] = None): 9 | self.message = message 10 | self.error_code = error_code 11 | self.platform = platform 12 | super().__init__(message) 13 | 14 | class AuthenticationError(PRError): 15 | """认证失败异常""" 16 | pass 17 | 18 | class RepositoryNotFoundError(PRError): 19 | """仓库不存在异常""" 20 | pass 21 | 22 | class BranchNotFoundError(PRError): 23 | """分支不存在异常""" 24 | pass 25 | 26 | class NetworkError(PRError): 27 | """网络错误异常""" 28 | pass 29 | 30 | class RateLimitError(PRError): 31 | """API 限流异常""" 32 | def __init__(self, message: str, retry_after: int = 60, **kwargs): 33 | self.retry_after = retry_after 34 | super().__init__(message, **kwargs) 35 | 36 | class ValidationError(PRError): 37 | """参数验证错误异常""" 38 | pass 39 | 40 | class PlatformNotSupportedError(PRError): 41 | """平台不支持异常""" 42 | pass 43 | 44 | class ConfigurationError(PRError): 45 | """配置错误异常""" 46 | pass -------------------------------------------------------------------------------- /src/autocoder/common/pull_requests/providers/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Pull Request 平台提供者模块 3 | """ 4 | from .github_provider import GitHubProvider 5 | from .gitlab_provider import GitLabProvider 6 | from .gitee_provider import GiteeProvider 7 | from .gitcode_provider import GitCodeProvider 8 | 9 | # 提供者映射 10 | PROVIDERS = { 11 | 'github': GitHubProvider, 12 | 'gitlab': GitLabProvider, 13 | 'gitee': GiteeProvider, 14 | 'gitcode': GitCodeProvider 15 | } 16 | 17 | __all__ = [ 18 | 'GitHubProvider', 19 | 'GitLabProvider', 20 | 'GiteeProvider', 21 | 'GitCodeProvider', 22 | 'PROVIDERS' 23 | ] -------------------------------------------------------------------------------- /src/autocoder/common/pull_requests/providers/gitcode_provider.py: -------------------------------------------------------------------------------- 1 | """ 2 | GitCode API 提供者实现 3 | """ 4 | from typing import List 5 | from .github_provider import GitHubProvider 6 | from ..models import RepoInfo, PRData, PRResult, PRInfo 7 | 8 | 9 | class GitCodeProvider(GitHubProvider): 10 | """GitCode API 提供者(基于GitHub提供者)""" 11 | 12 | def _get_auth_header(self) -> str: 13 | """获取认证头""" 14 | return f"Bearer {self.config.token}" 15 | 16 | def create_pr(self, repo_info: RepoInfo, pr_data: PRData) -> PRResult: 17 | """创建 Merge Request""" 18 | # 简化实现,实际应该调用GitCode API 19 | return super().create_pr(repo_info, pr_data) -------------------------------------------------------------------------------- /src/autocoder/common/pull_requests/providers/gitee_provider.py: -------------------------------------------------------------------------------- 1 | """ 2 | Gitee API 提供者实现 3 | """ 4 | from typing import List 5 | from .github_provider import GitHubProvider 6 | from ..models import RepoInfo, PRData, PRResult, PRInfo 7 | 8 | 9 | class GiteeProvider(GitHubProvider): 10 | """Gitee API 提供者(基于GitHub提供者)""" 11 | 12 | def _get_auth_header(self) -> str: 13 | """获取认证头""" 14 | # Gitee 使用 token 参数而不是 Authorization 头 15 | return "" 16 | 17 | def create_pr(self, repo_info: RepoInfo, pr_data: PRData) -> PRResult: 18 | """创建 Pull Request""" 19 | # 简化实现,实际应该调用Gitee API 20 | return super().create_pr(repo_info, pr_data) -------------------------------------------------------------------------------- /src/autocoder/common/pull_requests/providers/gitlab_provider.py: -------------------------------------------------------------------------------- 1 | """ 2 | GitLab API 提供者实现 3 | """ 4 | from typing import List 5 | from .github_provider import GitHubProvider 6 | from ..models import RepoInfo, PRData, PRResult, PRInfo 7 | 8 | 9 | class GitLabProvider(GitHubProvider): 10 | """GitLab API 提供者(基于GitHub提供者)""" 11 | 12 | def _get_auth_header(self) -> str: 13 | """获取认证头""" 14 | return f"Bearer {self.config.token}" 15 | 16 | def create_pr(self, repo_info: RepoInfo, pr_data: PRData) -> PRResult: 17 | """创建 Merge Request (GitLab的PR称为MR)""" 18 | # 简化实现,实际应该调用GitLab API 19 | return super().create_pr(repo_info, pr_data) 20 | 21 | def list_prs( 22 | self, 23 | repo_info: RepoInfo, 24 | state: str = "opened", # GitLab使用"opened"而不是"open" 25 | per_page: int = 30, 26 | page: int = 1 27 | ) -> List[PRInfo]: 28 | """列出仓库的MR""" 29 | return super().list_prs(repo_info, state, per_page, page) -------------------------------------------------------------------------------- /src/autocoder/common/pull_requests/test_module.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/autocoder/common/rag_manager/__init__.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | from .rag_manager import RAGManager 3 | 4 | __all__ = ["RAGManager"] -------------------------------------------------------------------------------- /src/autocoder/common/rulefiles/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | # -*- coding: utf-8 -*- 3 | """ 4 | AutoCoder 规则文件管理模块 5 | 6 | 提供读取、解析和监控 AutoCoder 规则文件的功能。 7 | """ 8 | 9 | from .autocoderrules_utils import ( 10 | get_rules, 11 | ) 12 | 13 | __all__ = [ 14 | 'get_rules', 15 | ] 16 | -------------------------------------------------------------------------------- /src/autocoder/common/stream_out_type.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | class AutoCommandStreamOutType(Enum): 4 | COMMAND_SUGGESTION = "command_suggestion" 5 | class IndexFilterStreamOutType(Enum): 6 | FILE_NUMBER_LIST = "file_number_list" 7 | 8 | class AgenticFilterStreamOutType(Enum): 9 | AGENTIC_FILTER = "agentic_filter" 10 | 11 | 12 | class CodeGenerateStreamOutType(Enum): 13 | CODE_GENERATE = "code_generate" 14 | 15 | class CodeRankStreamOutType(Enum): 16 | CODE_RANK = "code_rank" 17 | 18 | class LintStreamOutType(Enum): 19 | LINT = "lint" 20 | 21 | class UnmergedBlocksStreamOutType(Enum): 22 | UNMERGED_BLOCKS = "unmerged_blocks" 23 | 24 | class CompileStreamOutType(Enum): 25 | COMPILE = "compile" 26 | 27 | class ContextMissingCheckStreamOutType(Enum): 28 | CONTEXT_MISSING_CHECK = "context_missing_check" 29 | 30 | class IndexStreamOutType(Enum): 31 | INDEX_BUILD = "index_build" -------------------------------------------------------------------------------- /src/autocoder/common/text.py: -------------------------------------------------------------------------------- 1 | from difflib import SequenceMatcher 2 | 3 | class TextSimilarity: 4 | def __init__(self, text_a, text_b): 5 | self.text_a = text_a 6 | self.text_b = text_b 7 | self.lines_a = self._split_into_lines(text_a) 8 | self.lines_b = self._split_into_lines(text_b) 9 | self.m = len(self.lines_a) 10 | self.n = len(self.lines_b) 11 | 12 | def _split_into_lines(self, text): 13 | return text.splitlines() 14 | 15 | def _levenshtein_ratio(self, s1, s2): 16 | return SequenceMatcher(None, s1, s2).ratio() 17 | 18 | def get_best_matching_window(self): 19 | best_similarity = 0 20 | best_window = [] 21 | 22 | for i in range(self.n - self.m + 1): # 滑动窗口 23 | window_b = self.lines_b[i:i + self.m] 24 | similarity = self._levenshtein_ratio("\n".join(self.lines_a), "\n".join(window_b)) 25 | 26 | if similarity > best_similarity: 27 | best_similarity = similarity 28 | best_window = window_b 29 | 30 | return best_similarity, "\n".join(best_window) 31 | -------------------------------------------------------------------------------- /src/autocoder/common/tokens/models.py: -------------------------------------------------------------------------------- 1 | from typing import List, Dict, Optional 2 | from dataclasses import dataclass 3 | 4 | 5 | @dataclass 6 | class TokenResult: 7 | """单个文件的 token 统计结果""" 8 | file_path: str 9 | token_count: int 10 | char_count: int 11 | line_count: int 12 | success: bool = True 13 | error: Optional[str] = None 14 | 15 | 16 | @dataclass 17 | class DirectoryTokenResult: 18 | """目录的 token 统计结果""" 19 | directory_path: str 20 | total_tokens: int 21 | file_count: int 22 | skipped_count: int 23 | files: List[TokenResult] 24 | errors: List[str] = None 25 | 26 | def __post_init__(self): 27 | if self.errors is None: 28 | self.errors = [] 29 | -------------------------------------------------------------------------------- /src/autocoder/common/types.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | import pydantic 3 | from typing import List, Dict, Tuple,Any,Optional 4 | class Mode(Enum): 5 | MULTI_ROUND = "multi_round" 6 | SINGLE_ROUND = "single_round" 7 | 8 | class StepNum(pydantic.BaseModel): 9 | step_num:int= pydantic.Field(1,description="总共步骤数") 10 | content:int= pydantic.Field(1,description="详细的执行步骤,每个步骤需要包含一个shell/python 代码块") 11 | 12 | class CodeGenerateResult(pydantic.BaseModel): 13 | contents:List[str] 14 | conversations:List[List[Dict[str, Any]]] 15 | metadata:Dict[str, Any] = {} 16 | 17 | class MergeCodeWithoutEffect(pydantic.BaseModel): 18 | success_blocks: List[Tuple[str, str]] 19 | failed_blocks: List[Any] 20 | merged_blocks: Optional[Any] = None -------------------------------------------------------------------------------- /src/autocoder/common/v2/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/autocoder/common/v2/agent/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/autocoder/common/v2/agent/agentic_edit_tools/attempt_completion_tool_resolver.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Any, Optional 2 | from autocoder.common.v2.agent.agentic_edit_tools.base_tool_resolver import BaseToolResolver 3 | from autocoder.common.v2.agent.agentic_edit_types import AttemptCompletionTool, ToolResult # Import ToolResult from types 4 | from loguru import logger 5 | import typing 6 | from autocoder.common import AutoCoderArgs 7 | 8 | if typing.TYPE_CHECKING: 9 | from autocoder.common.v2.agent.agentic_edit import AgenticEdit 10 | 11 | class AttemptCompletionToolResolver(BaseToolResolver): 12 | def __init__(self, agent: Optional['AgenticEdit'], tool: AttemptCompletionTool, args: AutoCoderArgs): 13 | super().__init__(agent, tool, args) 14 | self.tool: AttemptCompletionTool = tool # For type hinting 15 | 16 | def resolve(self) -> ToolResult: 17 | """ 18 | Packages the completion result and optional command to signal task completion. 19 | """ 20 | result_text = self.tool.result 21 | command = self.tool.command 22 | 23 | logger.info(f"Resolving AttemptCompletionTool: Result='{result_text[:100]}...', Command='{command}'") 24 | 25 | if not result_text: 26 | return ToolResult(success=False, message="Error: Completion result cannot be empty.") 27 | 28 | # The actual presentation of the result happens outside the resolver. 29 | result_content = { 30 | "result": result_text, 31 | "command": command 32 | } 33 | 34 | # Indicate success in preparing the completion data 35 | return ToolResult(success=True, message="Task completion attempted.", content=result_content) 36 | -------------------------------------------------------------------------------- /src/autocoder/common/v2/agent/agentic_edit_tools/base_tool_resolver.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from typing import Any, Dict, Optional 3 | from autocoder.common.v2.agent.agentic_edit_types import BaseTool, ToolResult # Import ToolResult from types 4 | from autocoder.common import AutoCoderArgs 5 | import typing 6 | 7 | if typing.TYPE_CHECKING: 8 | from autocoder.common.v2.agent.agentic_edit import AgenticEdit 9 | 10 | 11 | class BaseToolResolver(ABC): 12 | def __init__(self, agent: Optional['AgenticEdit'], tool: BaseTool, args: AutoCoderArgs): 13 | """ 14 | Initializes the resolver. 15 | 16 | Args: 17 | agent: The AutoCoder agent instance. 18 | tool: The Pydantic model instance representing the tool call. 19 | args: Additional arguments needed for execution (e.g., source_dir). 20 | """ 21 | self.agent = agent 22 | self.tool = tool 23 | self.args = args 24 | 25 | @abstractmethod 26 | def resolve(self) -> ToolResult: 27 | """ 28 | Executes the tool's logic. 29 | 30 | Returns: 31 | A ToolResult object indicating success or failure and a message. 32 | """ 33 | pass 34 | -------------------------------------------------------------------------------- /src/autocoder/common/v2/agent/agentic_edit_tools/plan_mode_respond_tool_resolver.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Any, Optional 2 | import typing 3 | from autocoder.common import AutoCoderArgs 4 | from autocoder.common.v2.agent.agentic_edit_tools.base_tool_resolver import BaseToolResolver 5 | from autocoder.common.v2.agent.agentic_edit_types import PlanModeRespondTool, ToolResult # Import ToolResult from types 6 | from loguru import logger 7 | 8 | if typing.TYPE_CHECKING: 9 | from autocoder.common.v2.agent.agentic_edit import AgenticEdit 10 | 11 | class PlanModeRespondToolResolver(BaseToolResolver): 12 | def __init__(self, agent: Optional['AgenticEdit'], tool: PlanModeRespondTool, args: AutoCoderArgs): 13 | super().__init__(agent, tool, args) 14 | self.tool: PlanModeRespondTool = tool # For type hinting 15 | 16 | def resolve(self) -> ToolResult: 17 | """ 18 | Packages the response and options for Plan Mode interaction. 19 | """ 20 | response_text = self.tool.response 21 | options = self.tool.options 22 | logger.info(f"Resolving PlanModeRespondTool: Response='{response_text[:100]}...', Options={options}") 23 | 24 | if not response_text: 25 | return ToolResult(success=False, message="Error: Plan mode response cannot be empty.") 26 | 27 | # The actual presentation happens outside the resolver. 28 | result_content = { 29 | "response": response_text, 30 | "options": options 31 | } 32 | 33 | # Indicate success in preparing the plan mode response data 34 | return ToolResult(success=True, message="Plan mode response prepared.", content=result_content) 35 | -------------------------------------------------------------------------------- /src/autocoder/common/v2/agent/runner/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Runner 模块提供了多种运行模式,用于在不同环境下执行 AgenticEdit 代理。 3 | 4 | 这个模块包含三种主要的运行器: 5 | 1. TerminalRunner: 在终端环境中运行代理,提供格式化输出 6 | 2. EventRunner: 将代理事件转换为标准事件系统格式 7 | 3. SdkRunner: 提供生成器接口,适用于SDK环境 8 | 9 | 使用示例: 10 | ```python 11 | from autocoder.common.v2.agent.runner import TerminalRunner 12 | from autocoder.common.v2.agent.agentic_edit_types import AgenticEditRequest 13 | 14 | runner = TerminalRunner(llm=llm, args=args, ...) 15 | runner.run(AgenticEditRequest(user_input="请帮我实现一个HTTP服务器")) 16 | ``` 17 | """ 18 | 19 | from .base_runner import BaseRunner 20 | from .terminal_runner import TerminalRunner 21 | from .event_runner import EventRunner 22 | from .sdk_runner import SdkRunner 23 | from .tool_display import get_tool_display_message 24 | 25 | __all__ = [ 26 | "BaseRunner", 27 | "TerminalRunner", 28 | "EventRunner", 29 | "SdkRunner", 30 | "get_tool_display_message" 31 | ] 32 | -------------------------------------------------------------------------------- /src/autocoder/common/v2/agent/runner/sdk_runner.py: -------------------------------------------------------------------------------- 1 | """ 2 | SdkRunner 提供生成器接口,适用于SDK环境下的代理运行。 3 | 4 | 这个模块提供了一个简单的生成器接口,允许外部代码迭代处理代理事件。 5 | 它是三种运行模式中最轻量级的一种,适合集成到其他应用程序中。 6 | """ 7 | 8 | import logging 9 | from typing import Generator, Any 10 | 11 | from autocoder.common.v2.agent.agentic_edit_types import ( 12 | AgenticEditRequest, AgentEvent, CompletionEvent 13 | ) 14 | from .base_runner import BaseRunner 15 | 16 | logger = logging.getLogger(__name__) 17 | 18 | class SdkRunner(BaseRunner): 19 | """ 20 | 提供生成器接口的代理运行器,适用于SDK环境。 21 | 22 | 这个运行器返回一个事件生成器,允许外部代码迭代处理代理事件。 23 | 它是三种运行模式中最轻量级的一种,适合集成到其他应用程序中。 24 | """ 25 | 26 | def run(self, request: AgenticEditRequest) -> Generator[AgentEvent, None, None]: 27 | """ 28 | Runs the agentic edit process and yields events for external processing. 29 | """ 30 | try: 31 | event_stream = self.analyze(request) 32 | for agent_event in event_stream: 33 | if isinstance(agent_event, CompletionEvent): 34 | self.apply_changes() 35 | yield agent_event 36 | 37 | except Exception as e: 38 | logger.exception( 39 | "An unexpected error occurred during agent execution: {e}") 40 | raise e 41 | -------------------------------------------------------------------------------- /src/autocoder/compilers/shadow_compiler.py: -------------------------------------------------------------------------------- 1 | import os 2 | from typing import Optional 3 | from autocoder.shadows.shadow_manager import ShadowManager 4 | from autocoder.compilers.compiler_factory import CompilerFactory 5 | from autocoder.compilers.models import ProjectCompilationResult,FileCompilationResult 6 | 7 | class ShadowCompiler: 8 | """ 9 | 用于对ShadowManager管理的文件进行代码检查并将结果转换回项目路径的类。 10 | """ 11 | 12 | def __init__(self, shadow_manager: ShadowManager,verbose:bool=False): 13 | """ 14 | 使用ShadowManager实例初始化。 15 | 16 | 参数: 17 | shadow_manager (ShadowManager): 用于管理文件路径映射的实例 18 | verbose (bool): 是否启用详细输出 19 | """ 20 | self.shadow_manager = shadow_manager 21 | self.compiler = CompilerFactory.create_compiler(language="provided",config_path=os.path.join(self.shadow_manager.source_dir,".auto-coder","projects","compiler.yml")) 22 | 23 | def compile_shadow_file(self, shadow_path: str) -> FileCompilationResult: 24 | return FileCompilationResult( 25 | file_path=shadow_path, 26 | success=True, 27 | language="provided", 28 | errors=[], 29 | error_message=None, 30 | warning_count=0, 31 | error_count=0, 32 | info_count=0, 33 | execution_time_ms=0, 34 | output_file=None 35 | ) 36 | 37 | def compile_all_shadow_files(self,target_compiler_name:Optional[str]=None) -> ProjectCompilationResult: 38 | link_projects_dir = self.shadow_manager.create_link_project() 39 | result = self.compiler.compile_project(link_projects_dir,target_compiler_name) 40 | result.project_path = self.shadow_manager.source_dir 41 | return result 42 | 43 | -------------------------------------------------------------------------------- /src/autocoder/db/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/autocoder/dispacher/__init__.py: -------------------------------------------------------------------------------- 1 | from autocoder.common import AutoCoderArgs 2 | from autocoder.dispacher.actions.copilot import ActionCopilot 3 | from autocoder.dispacher.actions.action import ( 4 | ActionTSProject, 5 | ActionPyProject, 6 | ActionSuffixProject, 7 | ) 8 | from autocoder.dispacher.actions.plugins.action_regex_project import ActionRegexProject 9 | from typing import Optional 10 | import byzerllm 11 | # from autocoder.common.conversations.get_conversation_manager import ( 12 | # get_conversation_manager, 13 | # get_conversation_manager_config, 14 | # reset_conversation_manager 15 | # ) 16 | 17 | 18 | class Dispacher: 19 | def __init__(self, args: AutoCoderArgs, llm: Optional[byzerllm.ByzerLLM] = None): 20 | self.args = args 21 | self.llm = llm 22 | 23 | def dispach(self): 24 | # manager = get_conversation_manager() 25 | # if not manager.get_current_conversation(): 26 | # manager.create_conversation(name="New Conversation", description="New Conversation") 27 | # manager.set_current_conversation(manager.get_current_conversation_id()) 28 | 29 | args = self.args 30 | actions = [ 31 | ActionTSProject(args=args, llm=self.llm), 32 | ActionPyProject(args=args, llm=self.llm), 33 | ActionCopilot(args=args, llm=self.llm), 34 | ActionRegexProject(args=args, llm=self.llm), 35 | ActionSuffixProject(args=args, llm=self.llm), 36 | ] 37 | for action in actions: 38 | if action.run(): 39 | return 40 | -------------------------------------------------------------------------------- /src/autocoder/dispacher/actions/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/autocoder/dispacher/actions/plugins/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/autocoder/events/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Event handling system for autocoder. 3 | This module provides a way for two systems to communicate through events. 4 | """ 5 | 6 | from .event_store import EventStore, JsonlEventStore 7 | from .event_types import Event, EventType, ResponseEvent 8 | from .event_manager import EventManager 9 | from .event_manager_singleton import EventManagerSingleton, get_event_manager 10 | from .event_content import ( 11 | BaseEventContent, StreamContent, ResultContent, 12 | AskUserContent, UserResponseContent, CodeContent, 13 | MarkdownContent, ErrorContent, CompletionContent, ContentType, StreamState, 14 | create_stream_thinking, create_stream_content, 15 | create_result, create_ask_user, create_user_response, 16 | create_completion, create_error 17 | ) 18 | 19 | __all__ = [ 20 | # Event store 21 | "EventStore", 22 | "JsonlEventStore", 23 | 24 | # Event types 25 | "Event", 26 | "EventType", 27 | "ResponseEvent", 28 | "EventManager", 29 | 30 | # Singleton 31 | "EventManagerSingleton", 32 | "get_event_manager", 33 | 34 | # Content models 35 | "BaseEventContent", 36 | "StreamContent", 37 | "ResultContent", 38 | "AskUserContent", 39 | "UserResponseContent", 40 | "CodeContent", 41 | "MarkdownContent", 42 | "ErrorContent", 43 | "CompletionContent", 44 | 45 | # Enums 46 | "ContentType", 47 | "StreamState", 48 | 49 | # Factory functions 50 | "create_stream_thinking", 51 | "create_stream_content", 52 | "create_result", 53 | "create_ask_user", 54 | "create_user_response", 55 | "create_completion", 56 | "create_error" 57 | ] -------------------------------------------------------------------------------- /src/autocoder/helper/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/autocoder/index/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/autocoder/index/filter/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/autocoder/index/types.py: -------------------------------------------------------------------------------- 1 | import pydantic 2 | from typing import List 3 | 4 | class IndexItem(pydantic.BaseModel): 5 | module_name: str 6 | symbols: str 7 | last_modified: float 8 | md5: str # 新增文件内容的MD5哈希值字段 9 | 10 | 11 | class TargetFile(pydantic.BaseModel): 12 | file_path: str 13 | reason: str = pydantic.Field( 14 | ..., description="The reason why the file is the target file" 15 | ) 16 | 17 | 18 | class VerifyFileRelevance(pydantic.BaseModel): 19 | relevant_score: int 20 | reason: str 21 | 22 | 23 | class FileList(pydantic.BaseModel): 24 | file_list: List[TargetFile] 25 | 26 | class FileNumberList(pydantic.BaseModel): 27 | file_list: List[int] -------------------------------------------------------------------------------- /src/autocoder/linters/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/autocoder/linters/test_samples/react_component.jsx: -------------------------------------------------------------------------------- 1 | 2 | import React from 'react'; 3 | 4 | function TestComponent(props) { 5 | const { name } = props; 6 | 7 | return ( 8 |This is a test React component.
11 |