python单文件实现类似curosr等ai编程工具ide的编程开发工具bfwide代码

代码语言:python

所属分类:其他

代码描述:python单文件实现类似curosr等ai编程工具ide的编程开发工具bfwide代码,包括多项目目录管理,文件树,多tab代码编辑器、ai问答和build自动修改文件和运行cmd命令行等ai聊天模块,包含了基本的ai编辑器功能,还能设置多个不同的ai大模型api,并选择不同的ai大模型切换问答。

代码标签: python 单文件 实现 类似 curosr ai 编程 工具 ide 编程 开发 工具 代码 b

下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开

import os
import shutil
import subprocess
import threading
import uvicorn
import webview
import json
import uuid
import xml.etree.ElementTree as ET
from typing import List, Optional, Dict, Any
from fastapi.responses import HTMLResponse, StreamingResponse
from fastapi import FastAPI, Query, Response, HTTPException
from pydantic import BaseModel

# --- FastAPI App Definition ---
app = FastAPI()

# --- Configuration ---
BASE_DIR = os.path.join(os.getcwd(), 'projects')
PROJECTS_XML = os.path.join(BASE_DIR, 'projects.xml')
MODELS_JSON = os.path.join(os.getcwd(), 'models.json') # 模型配置文件

# --- pywebview API Class for JS Interop ---
class Api:
    def select_folder(self):
        window = webview.windows[0]
        result = window.create_file_dialog(webview.FOLDER_DIALOG)
        return result

    def open_file_dialog(self):
        window = webview.windows[0]
        result = window.create_file_dialog(webview.OPEN_DIALOG)
        return result

    def save_file_dialog(self):
        window = webview.windows[0]
        result = window.create_file_dialog(webview.SAVE_DIALOG)
        return result

    def read_file_content(self, path: str) -> Optional[str]:
        try:
            with open(path, 'r', encoding='utf-8') as f:
                return f.read()
        except Exception as e:
            return None

    def save_file_content(self, path: str, content: str) -> bool:
        try:
            with open(path, 'w', encoding='utf-8') as f:
                f.write(content)
            return True
        except Exception as e:
            return False

api = Api()

# --- Pydantic Models ---
class Project(BaseModel):
    id: str
    name: str
    serverType: str
    localFolder: Optional[str] = None

class CommandExecution(BaseModel):
    command: str
    project: str

class FileData(BaseModel):
    path: str
    content: str
    project: Optional[str] = None

class NewItemData(BaseModel):
    path: str
    name: str
    project: str

class RenameData(BaseModel):
    path: str
    newName: str
    project: str

class DeleteData(BaseModel):
    path: str
    project: str

class AIModelBase(BaseModel):
    model_config = {'protected_namespaces': ()}
    name: str
    base_url: str
    api_key: str
    model_name: str

class AIModel(AIModelBase):
    id: str

class Msg(BaseModel):
    role: str
    content: str

class ChatMessage(BaseModel):
    model_config = {'protected_namespaces': ()}
    message: str
    history: List[Msg] = []
    mode: str = 'ask'  # 'ask' or 'build'
    project: str
    model_id: Optional[str] = None
    files: List[Dict[str, Any]] = []

# --- Helper Functions ---
if not os.path.exists(BASE_DIR): os.makedirs(BASE_DIR)

if not os.path.exists(PROJECTS_XML):
    root = ET.Element("Projects")
    tree = ET.ElementTree(root)
    tree.write(PROJECTS_XML, encoding='utf-8', xml_declaration=True)

def find_project_by_id(project_id: str) -> Optional[Dict[str, Any]]:
    if not os.path.exists(PROJECTS_XML) or project_id is None: return None
    try:
        tree = ET.parse(PROJECTS_XML)
        root = tree.getroot()
        for project_elem in root.findall('Project'):
            id_elem = project_elem.find('id')
            if id_elem is not None and id_elem.text == project_id:
                project_info = {}
                for child in project_elem:
                    project_info[child.tag] = child.text
                return project_info
    except Exception: pass
    return None

def load_projects() -> List[Dict[str, Any]]:
    if not os.path.exists(PROJECTS_XML): return []
    tree = ET.parse(PROJECTS_XML)
    projects = []
    for project_elem in tree.getroot().findall('Project'):
        project = {child.tag: child.text for child in project_elem}
        projects.append(project)
    return projects

def save_projects(projects: List[Dict[str, Any]]):
    root = ET.Element("Projects")
    for project in projects:
        project_elem = ET.SubElement(root, "Project")
        for key, value in project.items():
            if value is not None:
                ET.SubElement(project_elem, key).text = str(value)
    ET.ElementTree(root).write(PROJECTS_XML, encoding='utf-8', xml_declaration=True)

def load_models() -> List[AIModel]:
    if not os.path.exists(MODELS_JSON): return []
    try:
        with open(MODELS_JSON, 'r', encoding='utf-8') as f:
            return [AIModel(**data) for data in json.load(f)]
    except Exception: return []

def save_models(models: List[AIModel]):
    with open(MODELS_JSON, 'w', encoding='utf-8') as f:
        json.dump([model.dict() for model in models], f, indent=4, ensure_ascii=False)

def execute_local_command(command, working_dir):
    try:
        result = subprocess.run(command, shell=True, capture_output=True, cwd=working_dir)
        try: out = result.stdout.decode('utf-8')
        except UnicodeDecodeError: out = result.stdout.decode('gbk', errors='ignore')
        try: err = result.stderr.decode('utf-8')
        except UnicodeDecodeError: err = result.stderr.decode('gbk', errors='ignore')
        return out + err, None
    except Exception as e: return None, str(e)

def read_local_file(path):
    if not os.path.exists(path) or os.path.isdir(path): return None
    try:
        with open(path, 'r', encoding='utf-8', errors='ignore') as f: return f.read()
    except Exception: return None

# 增强的上下文读取(支持递归文件夹)
def _get_file_context(project_base_path, files, projectinfo):
    context = []
    ignore_dirs = {'.git', 'node_modules', '__pycache__', '.venv', 'venv', 'dist', 'build'}
    valid_exts = {'.js', '.ts', '.py', '.html', '.css', '.vue', '.json', '.xml', '.md', '.java', '.c', '.cpp', '.txt', '.yaml', '.yml'}
    
    total_chars = 0
    max_chars = 100000 # 限制上下文大小,防止Token溢出
    
    for file_info in files:
        if total_chars > max_chars: break
        
        path = file_info.get('path')
        if not path: continue
        
        full_path = os.path.normpath(os.path.join(project_base_path, path))
        if not full_path.startswith(os.path.normpath(project_base_path)): continue
        
        if os.path.isfile(full_path):
            content = read_local_file(full_path)
            if content:
                context.append(f"====== File: {path} ======\n{content}\n")
                total_chars += len(content)
        elif os.path.isdir(full_path):
            for root, dirs, filenames in os.walk(full_path):
                dirs[:] = [d for d in dirs if d not in ignore_dirs and not d.startswith('.')]
                for name in filenames:
                    if total_chars > max_chars:.........完整代码请登录后点击上方下载按钮下载查看

网友评论0