Skip to content

Commit 6187704

Browse files
authored
Merge pull request #18 from LinJHS/dev
2 parents bbd4cd2 + f3d9b8d commit 6187704

8 files changed

Lines changed: 893 additions & 11 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
- 🖼️ **自定义图标**:轻松设置文件夹图标(支持绝对路径和相对路径)。
2727
- 🏷️ **文件夹别名**:在资源管理器中直观地重命名文件夹显示,而不改变实际目录名称。
2828
- ℹ️ **提示信息**:为文件夹添加自定义悬停文本描述 (InfoTip)。
29+
- 🤖 **AI 自动命名**:集成多种 AI 模型(OpenAI, DeepSeek 等),根据文件夹名称自动生成 Emoji 图标和中文别名。
2930
- 🔄 **批量操作**:批量将绝对图标路径转换为相对路径,便于移动设备使用。
3031
- 📂 **快速操作**:直接从 UI 在资源管理器或 CMD 中打开文件夹。
3132
- 🚀 **Web 界面**:基于 Flask 的简单 UI,通过浏览器即可访问。

README_EN.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
- 🖼️ **Custom Icons**: Easily set folder icons (supports absolute and relative paths).
2727
- 🏷️ **Folder Aliases**: Rename folders visually in Explorer without changing the actual directory name.
2828
- ℹ️ **Info Tips**: Add custom hover text descriptions to folders.
29+
- 🤖 **AI Auto-naming**: Integrate with various AI models (e.g., OpenAI, DeepSeek) to automatically generate Emoji icons and aliases based on folder names.
2930
- 🔄 **Batch Operations**: Convert absolute icon paths to relative paths in batch for portability.
3031
- 📂 **Quick Actions**: Open folders in Explorer or CMD directly from the UI.
3132
- 🚀 **Web Interface**: Simple Flask-based UI accessible from your browser.

manager/ai_service.py

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
AI 命名服务 - 兼容 OpenAI API 格式
4+
支持官方 OpenAI API 及第三方兼容接口(如 DeepSeek、OneAPI 等)
5+
"""
6+
7+
import requests
8+
import json
9+
import re
10+
11+
12+
class AINamingService:
13+
"""AI 命名服务"""
14+
15+
def __init__(self, ai_config):
16+
"""
17+
初始化 AI 服务
18+
19+
Args:
20+
ai_config: 包含 api_base, api_key, model 的字典
21+
"""
22+
self.api_base = ai_config.get('api_base', 'https://api.openai.com/v1')
23+
self.api_key = ai_config.get('api_key', '')
24+
self.model = ai_config.get('model', 'gpt-3.5-turbo')
25+
26+
def _build_prompt(self, folder_name):
27+
"""构造 AI Prompt"""
28+
return f"""你是一个文件夹命名专家。请根据以下文件夹名称,生成一个中文别名和一个合适的 Emoji 图标。
29+
30+
文件夹名称: {folder_name}
31+
32+
请严格按以下 JSON 格式返回(不要有任何其他文字):
33+
{{
34+
"alias": "中文名称(2-6个字)",
35+
"infotip": "简短备注(10-20字,可选填)",
36+
"emoji": "一个相关的Emoji图标"
37+
}}
38+
39+
命名规则:
40+
1. alias: 简洁易懂的中文名称,例如:
41+
- "MyProject" → "我的项目"
42+
- "Downloads" → "下载目录"
43+
- "230214_Meeting" → "230214_会议记录"
44+
45+
2. infotip: 可选,描述文件夹用途
46+
47+
3. emoji: 选择最相关的 Emoji,例如:
48+
- 项目文件夹: 📁
49+
- 代码: 💻
50+
- 文档: 📄
51+
- 图片: 🖼️
52+
- 音乐: 🎵
53+
- 下载: ⬇️
54+
- 工作: 💼
55+
- 学习: 📚
56+
57+
只返回 JSON,不要任何解释。"""
58+
59+
def generate(self, folder_name):
60+
"""
61+
调用 AI API 生成命名
62+
63+
Args:
64+
folder_name: 文件夹名称
65+
66+
Returns:
67+
包含 status, alias, infotip, emoji 的字典
68+
"""
69+
if not self.api_key:
70+
raise Exception("API Key 未配置")
71+
72+
url = f"{self.api_base.rstrip('/')}/chat/completions"
73+
headers = {
74+
"Content-Type": "application/json",
75+
"Authorization": f"Bearer {self.api_key}"
76+
}
77+
78+
payload = {
79+
"model": self.model,
80+
"messages": [
81+
{"role": "user", "content": self._build_prompt(folder_name)}
82+
],
83+
"temperature": 0.3,
84+
"max_tokens": 2000
85+
}
86+
87+
try:
88+
response = requests.post(url, headers=headers, json=payload, timeout=30)
89+
response.raise_for_status()
90+
91+
result = response.json()
92+
93+
if 'choices' not in result or len(result['choices']) == 0:
94+
raise Exception("API 返回了空的选择列表 (choices is empty)")
95+
96+
content = result['choices'][0]['message']['content'].strip()
97+
98+
if not content:
99+
raise Exception("AI 返回的内容为空 (content is empty)")
100+
101+
# 更加强健的 JSON 提取逻辑
102+
json_str = content
103+
104+
# 1. 优先尝试提取 markdown 代码块中的 JSON
105+
code_block_match = re.search(r"```(?:json)?\s*(\{[\s\S]*?\})\s*```", content, re.IGNORECASE)
106+
if code_block_match:
107+
json_str = code_block_match.group(1)
108+
else:
109+
# 2. 如果没有代码块,尝试提取最外层的 {} 包裹的内容
110+
brace_match = re.search(r"\{[\s\S]*\}", content)
111+
if brace_match:
112+
json_str = brace_match.group(0)
113+
114+
# 3. 尝试解析
115+
try:
116+
ai_result = json.loads(json_str)
117+
except json.JSONDecodeError:
118+
# 4. 容错处理:尝试清理常见的 JSON 格式错误(如尾部逗号)
119+
try:
120+
# 去除对象末尾多余的逗号
121+
fixed_json = re.sub(r",\s*\}", "}", json_str)
122+
fixed_json = re.sub(r",\s*\]", "]", fixed_json)
123+
ai_result = json.loads(fixed_json)
124+
except Exception:
125+
# 记录原始返回内容以便调试
126+
print(f"JSON Parse Error. Raw content: {content}")
127+
raise Exception("AI 返回的不是有效 JSON,请检查日志或重试")
128+
129+
return {
130+
"status": "success",
131+
"alias": ai_result.get('alias', ''),
132+
"infotip": ai_result.get('infotip', ''),
133+
"emoji": ai_result.get('emoji', '📁')
134+
}
135+
136+
except requests.exceptions.RequestException as e:
137+
raise Exception(f"API 请求失败: {str(e)}")
138+
except KeyError as e:
139+
raise Exception(f"AI 响应格式异常: {str(e)}")

0 commit comments

Comments
 (0)