fix: 修复time未引包问题&状态工作台,文件列表递归调用接口的情况,递归放在后端

This commit is contained in:
supreme0597 2026-03-14 23:54:52 +08:00
parent 9c1e5e2154
commit c5622fdef4
7 changed files with 51 additions and 29 deletions

View File

@ -141,6 +141,8 @@ services:
- ./saves:/app/saves
- ./docker/sandbox_provisioner/app.py:/app/app.py:ro
- /var/run/docker.sock:/var/run/docker.sock
ports:
- "8002:8002"
networks:
- app-network
extra_hosts:

View File

@ -11,6 +11,7 @@ if sys.platform == "win32":
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
import time
from collections import defaultdict, deque
import uvicorn

View File

@ -516,6 +516,7 @@ async def resume_agent_chat(
db: AsyncSession = Depends(get_db),
):
"""恢复被人工审批中断的对话(需要登录)"""
def normalize_resume_input(raw_answer: Any, raw_approved: bool | None) -> Any:
if raw_answer is not None:
if isinstance(raw_answer, str):
@ -874,6 +875,7 @@ async def delete_thread_attachment(
async def list_thread_files(
thread_id: str,
path: str = Query("/mnt/user-data"),
recursive: bool = Query(False),
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_required_user),
):
@ -883,6 +885,7 @@ async def list_thread_files(
current_user_id=str(current_user.id),
db=db,
path=path,
recursive=recursive,
)
@ -923,11 +926,7 @@ async def get_thread_artifact(
)
media_type = guess_type(file_path.name)[0] or "application/octet-stream"
headers = (
{"Content-Disposition": f'attachment; filename="{file_path.name}"'}
if download
else None
)
headers = {"Content-Disposition": f'attachment; filename="{file_path.name}"'} if download else None
return FileResponse(path=file_path, media_type=media_type, headers=headers)

View File

@ -34,6 +34,7 @@ async def list_thread_files_view(
current_user_id: str,
db,
path: str | None = None,
recursive: bool = False,
) -> dict:
conv_repo = ConversationRepository(db)
await require_user_conversation(conv_repo, thread_id, str(current_user_id))
@ -50,6 +51,9 @@ async def list_thread_files_view(
if not actual_path.is_dir():
raise HTTPException(status_code=400, detail="path must be a directory")
if recursive:
return _list_files_recursive(thread_id, actual_path, virtual_path)
entries: list[dict[str, Any]] = []
for child in sorted(actual_path.iterdir(), key=lambda item: (not item.is_dir(), item.name.lower())):
stat = child.stat()
@ -70,6 +74,35 @@ async def list_thread_files_view(
return {"path": virtual_path, "files": entries}
def _list_files_recursive(thread_id: str, actual_path: Path, virtual_path: str) -> dict:
entries: list[dict[str, Any]] = []
def _scan_dir(base_actual_path: Path, base_virtual_path: str):
try:
for child in sorted(base_actual_path.iterdir(), key=lambda item: (not item.is_dir(), item.name.lower())):
stat = child.stat()
child_virtual_path = virtual_path_for_thread_file(thread_id, child)
entries.append(
{
"path": child_virtual_path,
"name": child.name,
"is_dir": child.is_dir(),
"size": stat.st_size if child.is_file() else 0,
"modified_at": _to_iso8601(stat.st_mtime),
"artifact_url": None
if child.is_dir()
else f"/api/chat/thread/{thread_id}/artifacts/{child_virtual_path.lstrip('/')}",
}
)
if child.is_dir():
_scan_dir(child, child_virtual_path)
except PermissionError:
pass
_scan_dir(actual_path, virtual_path)
return {"path": virtual_path, "files": entries}
async def read_thread_file_content_view(
*,
thread_id: str,

View File

@ -3402,6 +3402,9 @@ wheels = [
binary = [
{ name = "psycopg-binary", marker = "implementation_name != 'pypy'" },
]
pool = [
{ name = "psycopg-pool" },
]
[[package]]
name = "psycopg-binary"
@ -5653,7 +5656,7 @@ wheels = [
[[package]]
name = "yuxi-know"
version = "0.5.1"
version = "0.5.2"
source = { virtual = "." }
dependencies = [
{ name = "agent-sandbox" },
@ -5697,7 +5700,7 @@ dependencies = [
{ name = "openai" },
{ name = "opencv-python-headless" },
{ name = "pillow" },
{ name = "psycopg", extra = ["binary"] },
{ name = "psycopg", extra = ["binary", "pool"] },
{ name = "pyjwt" },
{ name = "pymilvus" },
{ name = "pymupdf" },
@ -5782,7 +5785,7 @@ requires-dist = [
{ name = "openai", specifier = ">=1.109" },
{ name = "opencv-python-headless", specifier = ">=4.11.0.86" },
{ name = "pillow", specifier = ">=10.5.0" },
{ name = "psycopg", extras = ["binary"], specifier = ">=3.2.0" },
{ name = "psycopg", extras = ["binary", "pool"], specifier = ">=3.3.3" },
{ name = "pyjwt", specifier = ">=2.8.0" },
{ name = "pymilvus", specifier = ">=2.5.8" },
{ name = "pymupdf", specifier = ">=1.25.5" },

View File

@ -340,10 +340,11 @@ export const threadApi = {
* 列出线程文件目录
* @param {string} threadId
* @param {string} path
* @param {boolean} recursive
* @returns {Promise}
*/
listThreadFiles: (threadId, path = '/mnt/user-data') =>
apiGet(`/api/chat/thread/${threadId}/files?path=${encodeURIComponent(path)}`),
listThreadFiles: (threadId, path = '/mnt/user-data', recursive = false) =>
apiGet(`/api/chat/thread/${threadId}/files?path=${encodeURIComponent(path)}&recursive=${recursive}`),
/**
* 读取线程文本文件内容分页

View File

@ -758,26 +758,9 @@ const fetchThreadMessages = async ({ agentId, threadId, delay = 0 }) => {
const fetchThreadFiles = async (threadId) => {
if (!threadId) return
const queue = ['/mnt/user-data']
const visited = new Set()
const entries = []
try {
while (queue.length > 0) {
const currentPath = queue.shift()
if (!currentPath || visited.has(currentPath)) continue
visited.add(currentPath)
const response = await threadApi.listThreadFiles(threadId, currentPath)
const files = Array.isArray(response?.files) ? response.files : []
files.forEach((item) => {
if (!item || typeof item.path !== 'string') return
entries.push(item)
if (item.is_dir === true) {
queue.push(item.path)
}
})
}
const response = await threadApi.listThreadFiles(threadId, '/mnt/user-data', true)
const entries = Array.isArray(response?.files) ? response.files : []
threadFilesMap.value[threadId] = entries
} catch (error) {
console.warn('Failed to fetch thread files:', error)