auto-coder-web 0.1.92__py3-none-any.whl → 0.1.94__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- auto_coder_web/common_router/chat_list_manager.py +194 -0
- auto_coder_web/common_router/chat_list_router.py +55 -115
- auto_coder_web/common_router/chat_session_manager.py +111 -0
- auto_coder_web/common_router/file_group_router.py +55 -2
- auto_coder_web/proxy.py +3 -2
- auto_coder_web/routers/auto_router.py +44 -29
- auto_coder_web/routers/chat_panels_router.py +159 -0
- auto_coder_web/routers/chat_router.py +120 -1
- auto_coder_web/routers/code_editor_tabs_router.py +191 -0
- auto_coder_web/routers/coding_router.py +28 -26
- auto_coder_web/version.py +1 -1
- auto_coder_web/web/assets/{main-edBorJ-r.css → main-D8cC7hK1.css} +1 -1
- auto_coder_web/web/assets/main.js +382 -382
- auto_coder_web/web/index.html +1 -1
- {auto_coder_web-0.1.92.dist-info → auto_coder_web-0.1.94.dist-info}/METADATA +2 -2
- {auto_coder_web-0.1.92.dist-info → auto_coder_web-0.1.94.dist-info}/RECORD +19 -18
- auto_coder_web/file_cacher/__init__.py +0 -0
- auto_coder_web/file_cacher/filecacher.py +0 -195
- auto_coder_web/file_group.py +0 -69
- {auto_coder_web-0.1.92.dist-info → auto_coder_web-0.1.94.dist-info}/WHEEL +0 -0
- {auto_coder_web-0.1.92.dist-info → auto_coder_web-0.1.94.dist-info}/entry_points.txt +0 -0
- {auto_coder_web-0.1.92.dist-info → auto_coder_web-0.1.94.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,194 @@
|
|
1
|
+
import os
|
2
|
+
import json
|
3
|
+
import asyncio
|
4
|
+
import aiofiles
|
5
|
+
from typing import List, Dict, Any, Tuple
|
6
|
+
from loguru import logger
|
7
|
+
|
8
|
+
def _get_chat_lists_dir(project_path: str) -> str:
|
9
|
+
"""获取聊天列表目录的路径,并确保目录存在"""
|
10
|
+
chat_lists_dir = os.path.join(project_path, ".auto-coder", "auto-coder.web", "chat-lists")
|
11
|
+
os.makedirs(chat_lists_dir, exist_ok=True)
|
12
|
+
return chat_lists_dir
|
13
|
+
|
14
|
+
def _get_chat_list_file_path(project_path: str, name: str) -> str:
|
15
|
+
"""获取特定聊天列表文件的完整路径"""
|
16
|
+
chat_lists_dir = _get_chat_lists_dir(project_path)
|
17
|
+
return os.path.join(chat_lists_dir, f"{name}.json")
|
18
|
+
|
19
|
+
async def save_chat_list(project_path: str, name: str, messages: List[Dict[str, Any]]) -> None:
|
20
|
+
"""
|
21
|
+
保存聊天列表到文件
|
22
|
+
|
23
|
+
Args:
|
24
|
+
project_path: 项目路径
|
25
|
+
name: 聊天列表名称
|
26
|
+
messages: 聊天消息列表
|
27
|
+
|
28
|
+
Raises:
|
29
|
+
Exception: 如果保存失败
|
30
|
+
"""
|
31
|
+
file_path = _get_chat_list_file_path(project_path, name)
|
32
|
+
try:
|
33
|
+
async with aiofiles.open(file_path, 'w') as f:
|
34
|
+
await f.write(json.dumps({"messages": messages}, indent=2, ensure_ascii=False))
|
35
|
+
except Exception as e:
|
36
|
+
logger.error(f"Error saving chat list {name}: {str(e)}")
|
37
|
+
raise e
|
38
|
+
|
39
|
+
async def get_chat_lists(project_path: str) -> List[str]:
|
40
|
+
"""
|
41
|
+
获取所有聊天列表的名称,按修改时间倒序排列(最新的在前)
|
42
|
+
|
43
|
+
Args:
|
44
|
+
project_path: 项目路径
|
45
|
+
|
46
|
+
Returns:
|
47
|
+
聊天列表名称列表
|
48
|
+
|
49
|
+
Raises:
|
50
|
+
Exception: 如果获取列表失败
|
51
|
+
"""
|
52
|
+
chat_lists_dir = _get_chat_lists_dir(project_path)
|
53
|
+
|
54
|
+
try:
|
55
|
+
# 获取文件及其修改时间
|
56
|
+
chat_lists = []
|
57
|
+
files = await asyncio.to_thread(os.listdir, chat_lists_dir)
|
58
|
+
for file in files:
|
59
|
+
if file.endswith('.json'):
|
60
|
+
file_path = os.path.join(chat_lists_dir, file)
|
61
|
+
mod_time = os.path.getmtime(file_path)
|
62
|
+
# 存储(名称, 修改时间)的元组
|
63
|
+
chat_lists.append((file[:-5], mod_time))
|
64
|
+
|
65
|
+
# 按修改时间倒序排序(最新的在前)
|
66
|
+
chat_lists.sort(key=lambda x: x[1], reverse=True)
|
67
|
+
|
68
|
+
# 只返回聊天列表名称
|
69
|
+
return [name for name, _ in chat_lists]
|
70
|
+
except Exception as e:
|
71
|
+
logger.error(f"Error getting chat lists: {str(e)}")
|
72
|
+
raise e
|
73
|
+
|
74
|
+
async def get_chat_list(project_path: str, name: str) -> Dict[str, Any]:
|
75
|
+
"""
|
76
|
+
获取特定聊天列表的内容
|
77
|
+
|
78
|
+
Args:
|
79
|
+
project_path: 项目路径
|
80
|
+
name: 聊天列表名称
|
81
|
+
|
82
|
+
Returns:
|
83
|
+
聊天列表内容
|
84
|
+
|
85
|
+
Raises:
|
86
|
+
FileNotFoundError: 如果聊天列表不存在
|
87
|
+
Exception: 如果读取失败
|
88
|
+
"""
|
89
|
+
file_path = _get_chat_list_file_path(project_path, name)
|
90
|
+
if not os.path.exists(file_path):
|
91
|
+
raise FileNotFoundError(f"Chat list {name} not found")
|
92
|
+
|
93
|
+
try:
|
94
|
+
async with aiofiles.open(file_path, 'r') as f:
|
95
|
+
content = await f.read()
|
96
|
+
return json.loads(content)
|
97
|
+
except json.JSONDecodeError as e:
|
98
|
+
logger.error(f"Invalid JSON in chat list {name}: {str(e)}")
|
99
|
+
raise Exception(f"Invalid JSON in chat list file: {str(e)}")
|
100
|
+
except Exception as e:
|
101
|
+
logger.error(f"Error reading chat list {name}: {str(e)}")
|
102
|
+
raise e
|
103
|
+
|
104
|
+
def get_chat_list_sync(project_path: str, name: str) -> Dict[str, Any]:
|
105
|
+
"""
|
106
|
+
获取特定聊天列表的内容(同步版本)
|
107
|
+
|
108
|
+
Args:
|
109
|
+
project_path: 项目路径
|
110
|
+
name: 聊天列表名称
|
111
|
+
|
112
|
+
Returns:
|
113
|
+
聊天列表内容
|
114
|
+
|
115
|
+
Raises:
|
116
|
+
FileNotFoundError: 如果聊天列表不存在
|
117
|
+
Exception: 如果读取失败
|
118
|
+
"""
|
119
|
+
file_path = _get_chat_list_file_path(project_path, name)
|
120
|
+
if not os.path.exists(file_path):
|
121
|
+
raise FileNotFoundError(f"Chat list {name} not found")
|
122
|
+
|
123
|
+
try:
|
124
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
125
|
+
content = f.read()
|
126
|
+
return json.loads(content)
|
127
|
+
except json.JSONDecodeError as e:
|
128
|
+
logger.error(f"Invalid JSON in chat list {name}: {str(e)}")
|
129
|
+
raise Exception(f"Invalid JSON in chat list file: {str(e)}")
|
130
|
+
except Exception as e:
|
131
|
+
logger.error(f"Error reading chat list {name}: {str(e)}")
|
132
|
+
raise e
|
133
|
+
|
134
|
+
async def delete_chat_list(project_path: str, name: str) -> None:
|
135
|
+
"""
|
136
|
+
删除聊天列表
|
137
|
+
|
138
|
+
Args:
|
139
|
+
project_path: 项目路径
|
140
|
+
name: 聊天列表名称
|
141
|
+
|
142
|
+
Raises:
|
143
|
+
FileNotFoundError: 如果聊天列表不存在
|
144
|
+
Exception: 如果删除失败
|
145
|
+
"""
|
146
|
+
file_path = _get_chat_list_file_path(project_path, name)
|
147
|
+
if not os.path.exists(file_path):
|
148
|
+
raise FileNotFoundError(f"Chat list {name} not found")
|
149
|
+
|
150
|
+
try:
|
151
|
+
os.remove(file_path)
|
152
|
+
except Exception as e:
|
153
|
+
logger.error(f"Error deleting chat list {name}: {str(e)}")
|
154
|
+
raise e
|
155
|
+
|
156
|
+
async def rename_chat_list(project_path: str, old_name: str, new_name: str) -> None:
|
157
|
+
"""
|
158
|
+
重命名聊天列表
|
159
|
+
|
160
|
+
Args:
|
161
|
+
project_path: 项目路径
|
162
|
+
old_name: 旧的聊天列表名称
|
163
|
+
new_name: 新的聊天列表名称
|
164
|
+
|
165
|
+
Raises:
|
166
|
+
FileNotFoundError: 如果原聊天列表不存在
|
167
|
+
FileExistsError: 如果新名称的聊天列表已存在
|
168
|
+
Exception: 如果重命名失败
|
169
|
+
"""
|
170
|
+
old_file_path = _get_chat_list_file_path(project_path, old_name)
|
171
|
+
new_file_path = _get_chat_list_file_path(project_path, new_name)
|
172
|
+
|
173
|
+
# 检查旧文件是否存在
|
174
|
+
if not os.path.exists(old_file_path):
|
175
|
+
raise FileNotFoundError(f"Chat list {old_name} not found")
|
176
|
+
|
177
|
+
# 检查新文件名是否已存在
|
178
|
+
if os.path.exists(new_file_path):
|
179
|
+
raise FileExistsError(f"Chat list with name {new_name} already exists")
|
180
|
+
|
181
|
+
try:
|
182
|
+
# 读取旧文件内容
|
183
|
+
async with aiofiles.open(old_file_path, 'r') as f:
|
184
|
+
content = await f.read()
|
185
|
+
|
186
|
+
# 写入新文件
|
187
|
+
async with aiofiles.open(new_file_path, 'w') as f:
|
188
|
+
await f.write(content)
|
189
|
+
|
190
|
+
# 删除旧文件
|
191
|
+
os.remove(old_file_path)
|
192
|
+
except Exception as e:
|
193
|
+
logger.error(f"Error renaming chat list from {old_name} to {new_name}: {str(e)}")
|
194
|
+
raise e
|
@@ -5,9 +5,15 @@ import aiofiles
|
|
5
5
|
from auto_coder_web.types import ChatList
|
6
6
|
from pydantic import BaseModel
|
7
7
|
import asyncio
|
8
|
+
from loguru import logger
|
9
|
+
# 导入会话管理函数
|
10
|
+
from .chat_session_manager import read_session_name, write_session_name
|
11
|
+
# 导入聊天列表管理函数
|
12
|
+
from .chat_list_manager import save_chat_list, get_chat_lists, get_chat_list, delete_chat_list, rename_chat_list
|
8
13
|
|
9
14
|
class SessionNameRequest(BaseModel):
|
10
15
|
session_name: str
|
16
|
+
panel_id: str = "" # 添加panel_id字段,默认为空字符串
|
11
17
|
|
12
18
|
|
13
19
|
class RenameChatListRequest(BaseModel):
|
@@ -25,103 +31,65 @@ router = APIRouter()
|
|
25
31
|
|
26
32
|
|
27
33
|
@router.post("/api/chat-lists/save")
|
28
|
-
async def
|
34
|
+
async def save_chat_list_endpoint(chat_list: ChatList, project_path: str = Depends(get_project_path)):
|
29
35
|
try:
|
30
|
-
|
31
|
-
|
32
|
-
os.makedirs(chat_lists_dir, exist_ok=True)
|
33
|
-
|
34
|
-
file_path = os.path.join(chat_lists_dir, f"{chat_list.name}.json")
|
35
|
-
async with aiofiles.open(file_path, 'w') as f:
|
36
|
-
await f.write(json.dumps({"messages": chat_list.messages}, indent=2, ensure_ascii=False))
|
36
|
+
# 调用管理模块保存聊天列表
|
37
|
+
await save_chat_list(project_path, chat_list.name, chat_list.messages)
|
37
38
|
return {"status": "success", "message": f"Chat list {chat_list.name} saved successfully"}
|
38
39
|
except Exception as e:
|
39
40
|
raise HTTPException(status_code=500, detail=str(e))
|
40
41
|
|
41
42
|
|
42
43
|
@router.get("/api/chat-lists")
|
43
|
-
async def
|
44
|
+
async def get_chat_lists_endpoint(project_path: str = Depends(get_project_path)):
|
44
45
|
try:
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
# Get files with their modification times
|
50
|
-
chat_lists = []
|
51
|
-
files = await asyncio.to_thread(os.listdir, chat_lists_dir)
|
52
|
-
for file in files:
|
53
|
-
if file.endswith('.json'):
|
54
|
-
file_path = os.path.join(chat_lists_dir, file)
|
55
|
-
mod_time = os.path.getmtime(file_path)
|
56
|
-
# Store tuple of (name, mod_time)
|
57
|
-
chat_lists.append((file[:-5], mod_time))
|
58
|
-
|
59
|
-
# Sort by modification time (newest first)
|
60
|
-
chat_lists.sort(key=lambda x: x[1], reverse=True)
|
61
|
-
|
62
|
-
# Return only the chat list names
|
63
|
-
return {"chat_lists": [name for name, _ in chat_lists]}
|
46
|
+
# 调用管理模块获取聊天列表
|
47
|
+
chat_lists = await get_chat_lists(project_path)
|
48
|
+
return {"chat_lists": chat_lists}
|
64
49
|
except Exception as e:
|
65
50
|
raise HTTPException(status_code=500, detail=str(e))
|
66
51
|
|
67
52
|
|
68
53
|
@router.get("/api/chat-lists/{name}")
|
69
|
-
async def
|
54
|
+
async def get_chat_list_endpoint(name: str, project_path: str = Depends(get_project_path)):
|
70
55
|
try:
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
status_code=404, detail=f"Chat list {name} not found")
|
76
|
-
|
77
|
-
async with aiofiles.open(file_path, 'r') as f:
|
78
|
-
content = await f.read()
|
79
|
-
return json.loads(content)
|
56
|
+
# 调用管理模块获取特定聊天列表
|
57
|
+
return await get_chat_list(project_path, name)
|
58
|
+
except FileNotFoundError:
|
59
|
+
raise HTTPException(status_code=404, detail=f"Chat list {name} not found")
|
80
60
|
except Exception as e:
|
81
61
|
raise HTTPException(status_code=500, detail=str(e))
|
82
62
|
|
83
63
|
|
84
64
|
@router.delete("/api/chat-lists/{name}")
|
85
|
-
async def
|
65
|
+
async def delete_chat_list_endpoint(name: str, project_path: str = Depends(get_project_path)):
|
86
66
|
try:
|
87
|
-
|
88
|
-
|
89
|
-
if not os.path.exists(file_path):
|
90
|
-
raise HTTPException(
|
91
|
-
status_code=404, detail=f"Chat list {name} not found")
|
92
|
-
|
93
|
-
os.remove(file_path)
|
67
|
+
# 调用管理模块删除聊天列表
|
68
|
+
await delete_chat_list(project_path, name)
|
94
69
|
return {"status": "success", "message": f"Chat list {name} deleted successfully"}
|
70
|
+
except FileNotFoundError:
|
71
|
+
raise HTTPException(status_code=404, detail=f"Chat list {name} not found")
|
95
72
|
except Exception as e:
|
96
73
|
raise HTTPException(status_code=500, detail=str(e))
|
97
74
|
|
98
75
|
|
99
76
|
@router.get("/api/chat-session/name")
|
100
|
-
async def get_current_session_name(project_path: str = Depends(get_project_path)):
|
77
|
+
async def get_current_session_name(panel_id: str = "", project_path: str = Depends(get_project_path)):
|
101
78
|
"""
|
102
79
|
获取当前会话名称
|
80
|
+
|
81
|
+
Args:
|
82
|
+
panel_id: 可选的面板ID,用于区分不同的聊天面板
|
83
|
+
project_path: 项目路径
|
103
84
|
"""
|
104
85
|
try:
|
105
|
-
#
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
# 会话信息文件路径
|
110
|
-
session_file = os.path.join(session_dir, "current-session.json")
|
111
|
-
|
112
|
-
# 如果文件不存在,返回空会话名称
|
113
|
-
if not os.path.exists(session_file):
|
114
|
-
return {"session_name": ""}
|
115
|
-
|
116
|
-
# 读取当前会话信息
|
117
|
-
async with aiofiles.open(session_file, 'r') as f:
|
118
|
-
content = await f.read()
|
119
|
-
session_data = json.loads(content)
|
120
|
-
return {"session_name": session_data.get("session_name", "")}
|
86
|
+
# 调用新的函数读取会话名称
|
87
|
+
session_name = await read_session_name(project_path, panel_id)
|
88
|
+
return {"session_name": session_name}
|
121
89
|
|
122
90
|
except Exception as e:
|
123
|
-
# 如果发生错误,记录错误但返回空会话名
|
124
|
-
|
91
|
+
# 如果发生错误,记录错误但返回空会话名 (read_session_name内部已处理部分错误)
|
92
|
+
logger.error(f"Error in get_current_session_name endpoint: {str(e)}")
|
125
93
|
return {"session_name": ""}
|
126
94
|
|
127
95
|
|
@@ -129,20 +97,14 @@ async def get_current_session_name(project_path: str = Depends(get_project_path)
|
|
129
97
|
async def set_current_session_name(request: SessionNameRequest, project_path: str = Depends(get_project_path)):
|
130
98
|
"""
|
131
99
|
设置当前会话名称
|
100
|
+
|
101
|
+
Args:
|
102
|
+
request: 包含会话名称和可选面板ID的请求
|
103
|
+
project_path: 项目路径
|
132
104
|
"""
|
133
105
|
try:
|
134
|
-
#
|
135
|
-
|
136
|
-
os.makedirs(session_dir, exist_ok=True)
|
137
|
-
|
138
|
-
# 会话信息文件路径
|
139
|
-
session_file = os.path.join(session_dir, "current-session.json")
|
140
|
-
|
141
|
-
# 保存当前会话信息
|
142
|
-
session_data = {"session_name": request.session_name}
|
143
|
-
async with aiofiles.open(session_file, 'w') as f:
|
144
|
-
await f.write(json.dumps(session_data, indent=2, ensure_ascii=False))
|
145
|
-
|
106
|
+
# 调用新的函数写入会话名称
|
107
|
+
await write_session_name(project_path, request.session_name, request.panel_id)
|
146
108
|
return {"status": "success", "message": "Current session name updated"}
|
147
109
|
|
148
110
|
except Exception as e:
|
@@ -150,53 +112,31 @@ async def set_current_session_name(request: SessionNameRequest, project_path: st
|
|
150
112
|
|
151
113
|
|
152
114
|
@router.post("/api/chat-lists/rename")
|
153
|
-
async def
|
115
|
+
async def rename_chat_list_endpoint(request: RenameChatListRequest, project_path: str = Depends(get_project_path)):
|
154
116
|
"""
|
155
117
|
重命名聊天列表
|
156
118
|
|
157
119
|
将现有聊天列表从旧名称重命名为新名称
|
158
120
|
"""
|
159
121
|
try:
|
160
|
-
|
161
|
-
|
162
|
-
new_file_path = os.path.join(chat_lists_dir, f"{request.new_name}.json")
|
163
|
-
|
164
|
-
# 检查旧文件是否存在
|
165
|
-
if not os.path.exists(old_file_path):
|
166
|
-
raise HTTPException(status_code=404, detail=f"Chat list {request.old_name} not found")
|
167
|
-
|
168
|
-
# 检查新文件名是否已存在
|
169
|
-
if os.path.exists(new_file_path):
|
170
|
-
raise HTTPException(status_code=409, detail=f"Chat list with name {request.new_name} already exists")
|
171
|
-
|
172
|
-
# 读取旧文件内容
|
173
|
-
async with aiofiles.open(old_file_path, 'r') as f:
|
174
|
-
content = await f.read()
|
175
|
-
|
176
|
-
# 写入新文件
|
177
|
-
async with aiofiles.open(new_file_path, 'w') as f:
|
178
|
-
await f.write(content)
|
179
|
-
|
180
|
-
# 删除旧文件
|
181
|
-
os.remove(old_file_path)
|
122
|
+
# 调用管理模块重命名聊天列表
|
123
|
+
await rename_chat_list(project_path, request.old_name, request.new_name)
|
182
124
|
|
183
125
|
# 如果当前会话名称是旧名称,则更新为新名称
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
async with aiofiles.open(session_file, 'w') as f:
|
194
|
-
await f.write(json.dumps(session_data, indent=2, ensure_ascii=False))
|
195
|
-
except Exception as e:
|
196
|
-
print(f"Error updating current session name: {str(e)}")
|
126
|
+
# 注意:这里只更新默认的会话文件(panel_id="")
|
127
|
+
# 如果需要根据panel_id更新所有相关会话文件,逻辑会更复杂
|
128
|
+
try:
|
129
|
+
current_session_name = await read_session_name(project_path, panel_id="") # P读取默认会话
|
130
|
+
if current_session_name == request.old_name:
|
131
|
+
await write_session_name(project_path, request.new_name, panel_id="") # 更新默认会话
|
132
|
+
except Exception as e:
|
133
|
+
# 记录错误,但不影响重命名操作的主要流程
|
134
|
+
logger.error(f"Error updating default current session name during rename: {str(e)}")
|
197
135
|
|
198
136
|
return {"status": "success", "message": f"Chat list renamed from {request.old_name} to {request.new_name}"}
|
199
|
-
except
|
200
|
-
raise
|
137
|
+
except FileNotFoundError:
|
138
|
+
raise HTTPException(status_code=404, detail=f"Chat list {request.old_name} not found")
|
139
|
+
except FileExistsError:
|
140
|
+
raise HTTPException(status_code=409, detail=f"Chat list with name {request.new_name} already exists")
|
201
141
|
except Exception as e:
|
202
142
|
raise HTTPException(status_code=500, detail=f"Failed to rename chat list: {str(e)}")
|
@@ -0,0 +1,111 @@
|
|
1
|
+
import os
|
2
|
+
import json
|
3
|
+
import aiofiles
|
4
|
+
from loguru import logger
|
5
|
+
|
6
|
+
def _get_session_file_path(project_path: str, panel_id: str = "") -> str:
|
7
|
+
"""获取会话信息文件的完整路径"""
|
8
|
+
session_dir = os.path.join(project_path, ".auto-coder", "auto-coder.web")
|
9
|
+
os.makedirs(session_dir, exist_ok=True)
|
10
|
+
file_name = "current-session.json" if not panel_id else f"current-session-{panel_id}.json"
|
11
|
+
return os.path.join(session_dir, file_name)
|
12
|
+
|
13
|
+
async def read_session_name(project_path: str, panel_id: str = "") -> str:
|
14
|
+
"""
|
15
|
+
从文件读取当前会话名称
|
16
|
+
|
17
|
+
Args:
|
18
|
+
project_path: 项目路径
|
19
|
+
panel_id: 可选的面板ID
|
20
|
+
|
21
|
+
Returns:
|
22
|
+
当前会话名称,如果文件不存在或出错则返回空字符串
|
23
|
+
"""
|
24
|
+
session_file = _get_session_file_path(project_path, panel_id)
|
25
|
+
|
26
|
+
if not os.path.exists(session_file):
|
27
|
+
return ""
|
28
|
+
|
29
|
+
try:
|
30
|
+
async with aiofiles.open(session_file, 'r') as f:
|
31
|
+
content = await f.read()
|
32
|
+
session_data = json.loads(content)
|
33
|
+
return session_data.get("session_name", "")
|
34
|
+
except Exception as e:
|
35
|
+
logger.error(f"Error reading session name from {session_file}: {str(e)}")
|
36
|
+
return ""
|
37
|
+
|
38
|
+
def read_session_name_sync(project_path: str, panel_id: str = "") -> str:
|
39
|
+
"""
|
40
|
+
从文件读取当前会话名称(同步版本)
|
41
|
+
|
42
|
+
Args:
|
43
|
+
project_path: 项目路径
|
44
|
+
panel_id: 可选的面板ID
|
45
|
+
|
46
|
+
Returns:
|
47
|
+
当前会话名称,如果文件不存在或出错则返回空字符串
|
48
|
+
"""
|
49
|
+
session_file = _get_session_file_path(project_path, panel_id)
|
50
|
+
|
51
|
+
if not os.path.exists(session_file):
|
52
|
+
return ""
|
53
|
+
|
54
|
+
try:
|
55
|
+
with open(session_file, 'r', encoding='utf-8') as f:
|
56
|
+
content = f.read()
|
57
|
+
session_data = json.loads(content)
|
58
|
+
return session_data.get("session_name", "")
|
59
|
+
except Exception as e:
|
60
|
+
logger.error(f"Error reading session name from {session_file}: {str(e)}")
|
61
|
+
return ""
|
62
|
+
|
63
|
+
async def write_session_name(project_path: str, session_name: str, panel_id: str = ""):
|
64
|
+
"""
|
65
|
+
将当前会话名称写入文件
|
66
|
+
|
67
|
+
Args:
|
68
|
+
project_path: 项目路径
|
69
|
+
session_name: 要写入的会话名称
|
70
|
+
panel_id: 可选的面板ID
|
71
|
+
|
72
|
+
Raises:
|
73
|
+
Exception: 如果写入文件失败
|
74
|
+
"""
|
75
|
+
session_file = _get_session_file_path(project_path, panel_id)
|
76
|
+
|
77
|
+
session_data = {
|
78
|
+
"session_name": session_name,
|
79
|
+
"panel_id": panel_id
|
80
|
+
}
|
81
|
+
try:
|
82
|
+
async with aiofiles.open(session_file, 'w') as f:
|
83
|
+
await f.write(json.dumps(session_data, indent=2, ensure_ascii=False))
|
84
|
+
except Exception as e:
|
85
|
+
logger.error(f"Error writing session name to {session_file}: {str(e)}")
|
86
|
+
raise e
|
87
|
+
|
88
|
+
def write_session_name_sync(project_path: str, session_name: str, panel_id: str = ""):
|
89
|
+
"""
|
90
|
+
将当前会话名称写入文件(同步版本)
|
91
|
+
|
92
|
+
Args:
|
93
|
+
project_path: 项目路径
|
94
|
+
session_name: 要写入的会话名称
|
95
|
+
panel_id: 可选的面板ID
|
96
|
+
|
97
|
+
Raises:
|
98
|
+
Exception: 如果写入文件失败
|
99
|
+
"""
|
100
|
+
session_file = _get_session_file_path(project_path, panel_id)
|
101
|
+
|
102
|
+
session_data = {
|
103
|
+
"session_name": session_name,
|
104
|
+
"panel_id": panel_id
|
105
|
+
}
|
106
|
+
try:
|
107
|
+
with open(session_file, 'w', encoding='utf-8') as f:
|
108
|
+
f.write(json.dumps(session_data, indent=2, ensure_ascii=False))
|
109
|
+
except Exception as e:
|
110
|
+
logger.error(f"Error writing session name to {session_file}: {str(e)}")
|
111
|
+
raise e
|
@@ -4,8 +4,10 @@ from fastapi import APIRouter, Request, HTTPException, Depends
|
|
4
4
|
from autocoder.agent.auto_filegroup import AutoFileGroup
|
5
5
|
from autocoder.utils import operate_config_api
|
6
6
|
from autocoder.auto_coder_runner import get_memory,save_memory, load_memory
|
7
|
-
import json
|
8
7
|
import os
|
8
|
+
from autocoder.rag.token_counter import count_tokens
|
9
|
+
import aiofiles
|
10
|
+
from loguru import logger
|
9
11
|
|
10
12
|
router = APIRouter()
|
11
13
|
|
@@ -145,6 +147,31 @@ async def auto_create_groups(
|
|
145
147
|
raise HTTPException(status_code=500, detail=str(e))
|
146
148
|
|
147
149
|
|
150
|
+
async def count_tokens_from_file(file_path: str) -> int:
|
151
|
+
"""异步计算文件的token数
|
152
|
+
|
153
|
+
Args:
|
154
|
+
file_path: 文件的绝对路径
|
155
|
+
|
156
|
+
Returns:
|
157
|
+
int: token数量,出错时返回0
|
158
|
+
"""
|
159
|
+
try:
|
160
|
+
if not os.path.exists(file_path):
|
161
|
+
logger.warning(f"文件不存在: {file_path}")
|
162
|
+
return 0
|
163
|
+
|
164
|
+
logger.info(f"计算文件token: {file_path}")
|
165
|
+
async with aiofiles.open(file_path, 'r', encoding='utf-8') as f:
|
166
|
+
content = await f.read()
|
167
|
+
|
168
|
+
file_tokens = count_tokens(content)
|
169
|
+
return file_tokens if file_tokens > 0 else 0
|
170
|
+
except Exception as e:
|
171
|
+
logger.error(f"读取或计算文件token出错: {file_path}, 错误: {str(e)}")
|
172
|
+
return 0
|
173
|
+
|
174
|
+
|
148
175
|
@router.post("/api/file-groups/switch")
|
149
176
|
async def switch_file_groups(
|
150
177
|
request: Request,
|
@@ -156,12 +183,38 @@ async def switch_file_groups(
|
|
156
183
|
|
157
184
|
# Convert relative file paths to absolute paths
|
158
185
|
absolute_file_paths = []
|
186
|
+
total_tokens = 0
|
187
|
+
|
159
188
|
for file_path in file_paths:
|
160
189
|
absolute_path = os.path.join(project_path, file_path)
|
161
190
|
absolute_file_paths.append(absolute_path)
|
162
191
|
|
192
|
+
# 计算所有文件的tokens
|
193
|
+
token_tasks = []
|
194
|
+
|
195
|
+
# 收集组里的文件
|
196
|
+
for group_name in group_names:
|
197
|
+
memory = get_memory()
|
198
|
+
files = memory["current_files"]["groups"].get(group_name, [])
|
199
|
+
for file_path in files:
|
200
|
+
token_tasks.append(count_tokens_from_file(file_path))
|
201
|
+
|
202
|
+
# 收集额外的文件
|
203
|
+
for file_path in absolute_file_paths:
|
204
|
+
token_tasks.append(count_tokens_from_file(file_path))
|
205
|
+
|
206
|
+
# 异步等待所有token计算任务完成
|
207
|
+
if token_tasks:
|
208
|
+
token_results = await asyncio.gather(*token_tasks)
|
209
|
+
total_tokens = sum(token_results)
|
210
|
+
|
163
211
|
await asyncio.to_thread(_switch_groups, group_names, absolute_file_paths)
|
164
|
-
return {
|
212
|
+
return {
|
213
|
+
"status": "success",
|
214
|
+
"message": f"Switched to groups: {group_names} and additional files",
|
215
|
+
"total_tokens": total_tokens,
|
216
|
+
"absolute_file_paths": absolute_file_paths
|
217
|
+
}
|
165
218
|
|
166
219
|
|
167
220
|
@router.delete("/api/file-groups/{name}")
|
auto_coder_web/proxy.py
CHANGED
@@ -19,14 +19,13 @@ import sys
|
|
19
19
|
from auto_coder_web.terminal import terminal_manager
|
20
20
|
from autocoder.common import AutoCoderArgs
|
21
21
|
from auto_coder_web.auto_coder_runner_wrapper import AutoCoderRunnerWrapper
|
22
|
-
from auto_coder_web.routers import todo_router, settings_router, auto_router, commit_router, chat_router, coding_router, index_router, config_router, upload_router, rag_router, editable_preview_router, mcp_router, direct_chat_router, rules_router
|
22
|
+
from auto_coder_web.routers import todo_router, settings_router, auto_router, commit_router, chat_router, coding_router, index_router, config_router, upload_router, rag_router, editable_preview_router, mcp_router, direct_chat_router, rules_router, chat_panels_router, code_editor_tabs_router
|
23
23
|
from auto_coder_web.expert_routers import history_router
|
24
24
|
from auto_coder_web.common_router import completions_router, file_router, auto_coder_conf_router, chat_list_router, file_group_router, model_router, compiler_router
|
25
25
|
from auto_coder_web.common_router import active_context_router
|
26
26
|
from rich.console import Console
|
27
27
|
from loguru import logger
|
28
28
|
from auto_coder_web.lang import get_message
|
29
|
-
from auto_coder_web.file_cacher.filecacher import FileCacher
|
30
29
|
|
31
30
|
class ProxyServer:
|
32
31
|
def __init__(self, project_path: str, quick: bool = False, product_mode: str = "pro"):
|
@@ -111,6 +110,8 @@ class ProxyServer:
|
|
111
110
|
self.app.include_router(active_context_router.router)
|
112
111
|
self.app.include_router(direct_chat_router.router)
|
113
112
|
self.app.include_router(rules_router.router)
|
113
|
+
self.app.include_router(chat_panels_router.router)
|
114
|
+
self.app.include_router(code_editor_tabs_router.router)
|
114
115
|
|
115
116
|
@self.app.on_event("shutdown")
|
116
117
|
async def shutdown_event():
|