1 line
18 KiB
JSON
1 line
18 KiB
JSON
{"repo": "allwefantasy/william-toolbox", "files": {"src/williamtoolbox/storage/json_file.py": {"d2a2144964cfcd4761d11583f2b69012a7b20222": {"t": 1785674272.0119743, "n": 11068, "h": "051e98fe18c9f563", "c": "import os\r\nimport json\r\nimport aiofiles\r\nimport aiofiles.os\r\nimport asyncio\r\nfrom contextlib import asynccontextmanager\r\nimport asyncio\r\nimport aiofiles\r\nfrom contextlib import asynccontextmanager\r\nfrom pathlib import Path\r\nfrom typing import Dict, Any, Optional\r\nfrom datetime import datetime, timedelta\r\nimport uuid\r\n\r\nclass AsyncFileLock:\r\n def __init__(self, lock_file: str):\r\n self.lock_file = Path(lock_file+\".lock\")\r\n self._lock = asyncio.Lock()\r\n self._lock_handle = None # 初始化为 None\r\n\r\n async def acquire(self, timeout: int = 30):\r\n async with self._lock: # 确保线程安全\r\n start_time = asyncio.get_running_loop().time()\r\n while True:\r\n try:\r\n # 尝试创建锁文件\r\n self._lock_handle = await aiofiles.open(self.lock_file, mode=\"x\")\r\n break\r\n except FileExistsError:\r\n # 如果锁文件存在,等待一小段时间重试\r\n if asyncio.get_running_loop().time() - start_time > timeout:\r\n raise TimeoutError(f\"Could not acquire lock within {timeout} seconds for {self.lock_file}\")\r\n await asyncio.sleep(0.1)\r\n\r\n async def release(self):\r\n async with self._lock: # 确保线程安全\r\n if self._lock_handle:\r\n await self._lock_handle.close() # 关闭锁文件\r\n self.lock_file.unlink() # 删除锁文件\r\n self._lock_handle = None # 重置\r\n\r\n@asynccontextmanager\r\nasync def with_file_lock(file_path: str, timeout: int = 30):\r\n lock = AsyncFileLock(file_path)\r\n try:\r\n await lock.acquire(timeout=timeout)\r\n yield\r\n finally:\r\n await lock.release()\r\n\r\n\r\n# Path to the models.json file\r\nMODELS_JSON_PATH = \"models.json\"\r\nRAGS_JSON_PATH = \"rags.json\"\r\nSUPER_ANALYSIS_JSON_PATH = \"super_analysis.json\"\r\n\r\n# Path to the chat.json file\r\nCHAT_JSON_PATH = \"chat.json\"\r\n\r\n\r\n# Function to load chat data from JSON file for a specific user\r\nasync def load_chat_data(username: str):\r\n chat_dir = os.path.join(\"chat_data\", username)\r\n chat_file = os.path.join(chat_dir, \"chat.json\")\r\n os.makedirs(chat_dir, exist_ok=True) \r\n if os.path.exists(chat_file):\r\n async with aiofiles.open(chat_file, \"r\") as f:\r\n content = await f.read()\r\n return json.loads(content)\r\n return {\"conversations\": []}\r\n\r\n\r\n# Function to save chat data to JSON file for a specific user\r\nasync def save_chat_data(username: str, data):\r\n chat_dir = os.path.join(\"chat_data\", username)\r\n chat_file = os.path.join(chat_dir, \"chat.json\")\r\n os.makedirs(chat_dir, exist_ok=True)\r\n \r\n async with with_file_lock(chat_file):\r\n async with aiofiles.open(chat_file, \"w\") as f:\r\n content = json.dumps(data, ensure_ascii=False)\r\n await f.write(content)\r\n\r\n\r\n# Add this function to load the config\r\nasync def load_config():\r\n config_path = \"config.json\"\r\n default_config = {\r\n \"saasBaseUrls\": [\r\n {\"value\": \"https://ark.cn-beijing.volces.com/api/v3\", \"label\": \"火山方舟\"},\r\n {\"value\": \"https://api.siliconflow.cn/v1\", \"label\": \"硅基流动\"},\r\n {\"value\": \"https://api.deepseek.com/beta\", \"label\": \"DeepSeek\"},\r\n {\"value\": \"https://dashscope.aliyuncs.com/compatible-mode/v1\", \"label\": \"通义千问\"},\r\n {\"value\": \"https://api.moonshot.cn/v1\", \"label\": \"Kimi\"}\r\n ],\r\n \"pretrainedModelTypes\": [\r\n {\"value\": \"saas/openai\", \"label\": \"OpenAI 兼容模型\"},\r\n {\"value\": \"saas/qianwen\", \"label\": \"通义千问\"},\r\n {\"value\": \"saas/qianwen_vl\", \"label\": \"通义千问视觉\"},\r\n {\"value\": \"saas/claude\", \"label\": \"Claude\"}\r\n ],\r\n \"openaiServerList\": [],\r\n \"commons\": [ \r\n ]\r\n }\r\n\r\n async with with_file_lock(config_path):\r\n if os.path.exists(config_path):\r\n async with aiofiles.open(config_path, \"r\") as f:\r\n content = await f.read()\r\n user_config = json.loads(content)\r\n \r\n # Merge user config with default config\r\n for key in default_config:\r\n if key not in user_config:\r\n user_config[key] = default_config[key]\r\n \r\n return user_config\r\n \r\n return default_config\r\n\r\n\r\nasync def save_config(config):\r\n \"\"\"Save the configuration to file.\"\"\"\r\n config_path = \"config.json\"\r\n async with with_file_lock(config_path):\r\n async with aiofiles.open(config_path, \"w\") as f:\r\n content = json.dumps(config, ensure_ascii=False)\r\n await f.write(content)\r\n\r\n\r\n# Path to the models.json file\r\nMODELS_JSON_PATH = \"models.json\"\r\nRAGS_JSON_PATH = \"rags.json\"\r\n\r\n\r\n# Function to load models from JSON file\r\nasync def load_models_from_json():\r\n async with with_file_lock(MODELS_JSON_PATH):\r\n if os.path.exists(MODELS_JSON_PATH):\r\n async with aiofiles.open(MODELS_JSON_PATH, \"r\") as f:\r\n content = await f.read()\r\n return json.loads(content)\r\n return {}\r\n\r\n\r\n# Function to save models to JSON file\r\nasync def save_models_to_json(models):\r\n async with with_file_lock(MODELS_JSON_PATH):\r\n async with aiofiles.open(MODELS_JSON_PATH, \"w\") as f:\r\n content = json.dumps(models, ensure_ascii=False)\r\n await f.write(content)\r\n\r\n\r\ndef b_load_models_from_json(): \r\n if os.path.exists(MODELS_JSON_PATH):\r\n with open(MODELS_JSON_PATH, \"r\") as f:\r\n content = f.read()\r\n return json.loads(content)\r\n return {}\r\n\r\n\r\ndef b_save_models_to_json(models): \r\n with open(MODELS_JSON_PATH, \"w\") as f:\r\n content = json.dumps(models, ensure_ascii=False)\r\n f.write(content)\r\n\r\n\r\n# Function to load RAGs from JSON file\r\nasync def load_rags_from_json():\r\n async with with_file_lock(RAGS_JSON_PATH):\r\n if os.path.exists(RAGS_JSON_PATH):\r\n async with aiofiles.open(RAGS_JSON_PATH, \"r\") as f:\r\n content = await f.read()\r\n return json.loads(content)\r\n return {}\r\n\r\n\r\n# Function to save RAGs to JSON file\r\nasync def save_rags_to_json(rags):\r\n async with with_file_lock(RAGS_JSON_PATH):\r\n async with aiofiles.open(RAGS_JSON_PATH, \"w\") as f:\r\n content = json.dumps(rags, ensure_ascii=False)\r\n await f.write(content)\r\n\r\n# Function to load Super Analysis from JSON file\r\nasync def load_super_analysis_from_json():\r\n async with with_file_lock(SUPER_ANALYSIS_JSON_PATH):\r\n if os.path.exists(SUPER_ANALYSIS_JSON_PATH):\r\n async with aiofiles.open(SUPER_ANALYSIS_JSON_PATH, \"r\") as f:\r\n content = await f.read()\r\n return json.loads(content)\r\n return {}\r\n\r\n# Function to save Super Analysis to JSON file\r\nasync def save_super_analysis_to_json(analyses):\r\n async with with_file_lock(SUPER_ANALYSIS_JSON_PATH):\r\n async with aiofiles.open(SUPER_ANALYSIS_JSON_PATH, \"w\") as f:\r\n content = json.dumps(analyses, ensure_ascii=False)\r\n await f.write(content)\r\n\r\nasync def get_event_file_path(request_id: str) -> str:\r\n os.makedirs(\"chat_events\", exist_ok=True)\r\n return f\"chat_events/{request_id}.json\"\r\n\r\n\r\nasync def load_byzer_sql_from_json():\r\n byzer_sql_path = \"byzer_sql.json\"\r\n async with with_file_lock(byzer_sql_path):\r\n if os.path.exists(byzer_sql_path):\r\n async with aiofiles.open(byzer_sql_path, \"r\") as f:\r\n content = await f.read()\r\n return json.loads(content) \r\n return {} \r\n\r\nasync def save_byzer_sql_to_json(services) -> None:\r\n byzer_sql_path = \"byzer_sql.json\"\r\n async with with_file_lock(byzer_sql_path):\r\n async with aiofiles.open(byzer_sql_path, \"w\") as f:\r\n content = json.dumps(services, ensure_ascii=False)\r\n await f.write(content)\r\n\r\n# File resources related functions\r\nFILE_RESOURCES_JSON_PATH = \"file_resources.json\"\r\n\r\nasync def load_file_resources() -> Dict[str, Any]:\r\n \"\"\"Load file resources from JSON file\"\"\"\r\n async with with_file_lock(FILE_RESOURCES_JSON_PATH):\r\n if os.path.exists(FILE_RESOURCES_JSON_PATH):\r\n async with aiofiles.open(FILE_RESOURCES_JSON_PATH, \"r\") as f:\r\n content = await f.read()\r\n return json.loads(content)\r\n return {}\r\n\r\nasync def save_file_resources(resources: Dict[str, Any]) -> None:\r\n \"\"\"Save file resources to JSON file\"\"\"\r\n async with with_file_lock(FILE_RESOURCES_JSON_PATH):\r\n async with aiofiles.open(FILE_RESOURCES_JSON_PATH, \"w\") as f:\r\n content = json.dumps(resources, ensure_ascii=False)\r\n await f.write(content)\r\n\r\n\r\n# API Key related functions\r\nAPI_KEYS_JSON_PATH = \"api_keys.json\"\r\n\r\nasync def load_api_keys() -> Dict[str, Any]:\r\n \"\"\"Load API keys from JSON file\"\"\"\r\n async with with_file_lock(API_KEYS_JSON_PATH):\r\n if os.path.exists(API_KEYS_JSON_PATH):\r\n async with aiofiles.open(API_KEYS_JSON_PATH, \"r\") as f:\r\n content = await f.read()\r\n return json.loads(content)\r\n return {}\r\n\r\nasync def save_api_keys(api_keys: Dict[str, Any]) -> None:\r\n \"\"\"Save API keys to JSON file\"\"\"\r\n async with with_file_lock(API_KEYS_JSON_PATH):\r\n async with aiofiles.open(API_KEYS_JSON_PATH, \"w\") as f:\r\n content = json.dumps(api_keys, ensure_ascii=False)\r\n await f.write(content)\r\n\r\nasync def create_api_key(name: str, description: Optional[str] = None, expires_in_days: int = 30) -> Dict[str, Any]:\r\n \"\"\"Create a new API key\"\"\"\r\n api_keys = await load_api_keys()\r\n \r\n # Generate a unique API key\r\n api_key = f\"sk-{uuid.uuid4().hex}\"\r\n \r\n # Create key info\r\n created_at = datetime.now().isoformat()\r\n expires_at = -1 if expires_in_days == -1 else (datetime.now() + timedelta(days=expires_in_days)).isoformat()\r\n api_key_info = {\r\n \"key\": api_key,\r\n \"name\": name,\r\n \"description\": description,\r\n \"created_at\": created_at,\r\n \"expires_at\": expires_at,\r\n \"is_active\": True\r\n } \r\n \r\n api_keys[api_key] = api_key_info\r\n await save_api_keys(api_keys)\r\n return api_key_info\r\n\r\nasync def revoke_api_key(key: str) -> None:\r\n \"\"\"Revoke an API key\"\"\"\r\n api_keys = await load_api_keys()\r\n if key in api_keys:\r\n api_keys[key][\"is_active\"] = False\r\n await save_api_keys(api_keys)\r\n\r\nasync def verify_api_key(api_key: str) -> bool:\r\n \"\"\"Verify if an API key is valid and not expired\"\"\"\r\n api_keys = await load_api_keys()\r\n if api_key not in api_keys:\r\n return False\r\n \r\n key_info = api_keys[api_key]\r\n if not key_info[\"is_active\"]:\r\n return False\r\n \r\n # -1 means never expire\r\n if key_info[\"expires_at\"] == -1:\r\n return True\r\n \r\n expires_at = datetime.fromisoformat(key_info[\"expires_at\"])\r\n if expires_at < datetime.now():\r\n return False\r\n \r\n return True\r\n\r\n\r\n\r\n"}}, "auto-coder-actions/153_chat_action.yml": {"d2a2144964cfcd4761d11583f2b69012a7b20222": {"t": 1785746331.0872421, "n": 627, "h": "beb7d17fc75d2928", "c": "auto_merge: editblock\r\nchat_model: qwen_vl\r\ncode_model: qwen_vl\r\ncontext: '下面是对上面文件按分组给到的一些描述,当用户的需求正好匹配描述的时候,参考描述来做修改:\r\n\r\n\r\n '\r\nhuman_as_model: false\r\ninclude_file:\r\n- ./base/base.yml\r\ninclude_project_structure: true\r\nproject_type: py\r\nquery: saas.base_url 支持下拉框,有硅基流动/https://api.siliconflow.cn/v1 DeepSeek/https://api.deepseek.com/beta\r\n , 也可以自己输入\r\nsilence: true\r\nskip_build_index: false\r\nskip_confirm: true\r\nskip_filter_index: true\r\nurls:\r\n- /Users/allwefantasy/projects/william-toolbox/src/williamtoolbox/server/proxy_server.py\r\n- /Users/allwefantasy/projects/william-toolbox/frontend/src/components/CreateModel.tsx\r\n"}, "2ca3d228d9bd5e4c73400c1e7c7237dc60cbce6e": {"t": 1785761986.6781473, "n": 627, "h": "beb7d17fc75d2928", "c": "auto_merge: editblock\r\nchat_model: qwen_vl\r\ncode_model: qwen_vl\r\ncontext: '下面是对上面文件按分组给到的一些描述,当用户的需求正好匹配描述的时候,参考描述来做修改:\r\n\r\n\r\n '\r\nhuman_as_model: false\r\ninclude_file:\r\n- ./base/base.yml\r\ninclude_project_structure: true\r\nproject_type: py\r\nquery: saas.base_url 支持下拉框,有硅基流动/https://api.siliconflow.cn/v1 DeepSeek/https://api.deepseek.com/beta\r\n , 也可以自己输入\r\nsilence: true\r\nskip_build_index: false\r\nskip_confirm: true\r\nskip_filter_index: true\r\nurls:\r\n- /Users/allwefantasy/projects/william-toolbox/src/williamtoolbox/server/proxy_server.py\r\n- /Users/allwefantasy/projects/william-toolbox/frontend/src/components/CreateModel.tsx\r\n"}, "4b9166a8efdbaa02b0a44e1ed411e051462c5e4f": {"t": 1785761987.8092895, "n": 606, "h": "601e458c528e3e32", "c": "auto_merge: editblock\nchat_model: qwen_vl\ncode_model: qwen_vl\ncontext: '下面是对上面文件按分组给到的一些描述,当用户的需求正好匹配描述的时候,参考描述来做修改:\n\n\n '\nhuman_as_model: false\ninclude_file:\n- ./base/base.yml\ninclude_project_structure: true\nproject_type: py\nquery: saas.base_url 支持下拉框,有硅基流动/https://api.siliconflow.cn/v1 DeepSeek/https://api.deepseek.com/beta\n , 也可以自己输入\nsilence: true\nskip_build_index: false\nskip_confirm: true\nskip_filter_index: true\nurls:\n- /Users/allwefantasy/projects/william-toolbox/src/williamtoolbox/server/proxy_server.py\n- /Users/allwefantasy/projects/william-toolbox/frontend/src/components/CreateModel.tsx\n"}}, "auto-coder-actions/154_chat_action.yml": {"d2a2144964cfcd4761d11583f2b69012a7b20222": {"t": 1785746331.0962968, "n": 678, "h": "c14a64c460acb0e1", "c": "auto_merge: editblock\r\nchat_model: qwen_vl\r\ncode_model: qwen_vl\r\ncontext: '下面是对上面文件按分组给到的一些描述,当用户的需求正好匹配描述的时候,参考描述来做修改:\r\n\r\n\r\n '\r\nhuman_as_model: true\r\ninclude_file:\r\n- ./base/base.yml\r\ninclude_project_structure: true\r\nproject_type: py\r\nquery: saas.base_url 支持下拉框,有硅基流动/https://api.siliconflow.cn/v1 DeepSeek/https://api.deepseek.com/beta\r\n , 也可以自己输入. 如果用户选择了 DeepSeek, 那么 saas.model 默认填入 deepseek-chat\r\nsilence: true\r\nskip_build_index: false\r\nskip_confirm: true\r\nskip_filter_index: true\r\nurls:\r\n- /Users/allwefantasy/projects/william-toolbox/src/williamtoolbox/server/proxy_server.py\r\n- /Users/allwefantasy/projects/william-toolbox/frontend/src/components/CreateModel.tsx\r\n"}, "2ca3d228d9bd5e4c73400c1e7c7237dc60cbce6e": {"t": 1785761982.4980953, "n": 678, "h": "c14a64c460acb0e1", "c": "auto_merge: editblock\r\nchat_model: qwen_vl\r\ncode_model: qwen_vl\r\ncontext: '下面是对上面文件按分组给到的一些描述,当用户的需求正好匹配描述的时候,参考描述来做修改:\r\n\r\n\r\n '\r\nhuman_as_model: true\r\ninclude_file:\r\n- ./base/base.yml\r\ninclude_project_structure: true\r\nproject_type: py\r\nquery: saas.base_url 支持下拉框,有硅基流动/https://api.siliconflow.cn/v1 DeepSeek/https://api.deepseek.com/beta\r\n , 也可以自己输入. 如果用户选择了 DeepSeek, 那么 saas.model 默认填入 deepseek-chat\r\nsilence: true\r\nskip_build_index: false\r\nskip_confirm: true\r\nskip_filter_index: true\r\nurls:\r\n- /Users/allwefantasy/projects/william-toolbox/src/williamtoolbox/server/proxy_server.py\r\n- /Users/allwefantasy/projects/william-toolbox/frontend/src/components/CreateModel.tsx\r\n"}, "4b9166a8efdbaa02b0a44e1ed411e051462c5e4f": {"t": 1785761984.791235, "n": 657, "h": "7aa29b1e62427ece", "c": "auto_merge: editblock\nchat_model: qwen_vl\ncode_model: qwen_vl\ncontext: '下面是对上面文件按分组给到的一些描述,当用户的需求正好匹配描述的时候,参考描述来做修改:\n\n\n '\nhuman_as_model: true\ninclude_file:\n- ./base/base.yml\ninclude_project_structure: true\nproject_type: py\nquery: saas.base_url 支持下拉框,有硅基流动/https://api.siliconflow.cn/v1 DeepSeek/https://api.deepseek.com/beta\n , 也可以自己输入. 如果用户选择了 DeepSeek, 那么 saas.model 默认填入 deepseek-chat\nsilence: true\nskip_build_index: false\nskip_confirm: true\nskip_filter_index: true\nurls:\n- /Users/allwefantasy/projects/william-toolbox/src/williamtoolbox/server/proxy_server.py\n- /Users/allwefantasy/projects/william-toolbox/frontend/src/components/CreateModel.tsx\n"}}}} |