新增rag功能,支持用户上传、聊天提供文件夹或文件地址上传rag知识库,删除、查询个人知识库及暴露source给agent以获取所有知识来源;新增获取文件tree . /f工具
This commit is contained in:
1 parent
95fefe96dd
commit
14788cad6a
36 files changed
+1238
-404
No files matched your search
+33
-6
@@ -32,6 +32,16 @@ _WEB_DIR = Path(__file__).resolve().parent
|
||||
_IMAGE_DIR = _WEB_DIR / "image"
|
||||
|
||||
_AUDIO_EXT = frozenset({".wav", ".mp3", ".m4a", ".flac", ".ogg", ".webm"})
|
||||
_IMAGE_EXT = frozenset(IMAGE_FILE_EXTENSIONS)
|
||||
|
||||
|
||||
def _infer_upload_kind(fp: str) -> str | None:
|
||||
ext = os.path.splitext(fp)[1].lower()
|
||||
if ext in _IMAGE_EXT:
|
||||
return "image"
|
||||
if ext in _AUDIO_EXT:
|
||||
return "audio"
|
||||
return "file"
|
||||
|
||||
|
||||
def _data_url_for_file(fp: str) -> str | None:
|
||||
@@ -82,6 +92,7 @@ def _serialize_content(content: Any, *, meta: dict[str, Any] | None = None, role
|
||||
parts.append({"type": "text", "text": piece.get("text") or ""})
|
||||
continue
|
||||
fp = piece.get("path") or (piece.get("file") or {}).get("path")
|
||||
display_name = piece.get("name")
|
||||
if piece.get("type") == "image_url":
|
||||
fp = fp or (piece.get("image_url") or {}).get("url")
|
||||
if piece.get("type") == "audio":
|
||||
@@ -93,19 +104,19 @@ def _serialize_content(content: Any, *, meta: dict[str, Any] | None = None, role
|
||||
continue
|
||||
ext = os.path.splitext(fp)[1].lower()
|
||||
if ext in IMAGE_FILE_EXTENSIONS:
|
||||
parts.append({"type": "image", "url": data_url, "path": fp, "name": os.path.basename(fp)})
|
||||
parts.append({"type": "image", "url": data_url, "path": fp, "name": display_name or os.path.basename(fp)})
|
||||
elif ext in _AUDIO_EXT:
|
||||
audio_meta = meta.get("audio") if isinstance(meta.get("audio"), dict) else {}
|
||||
parts.append({
|
||||
"type": "audio",
|
||||
"url": data_url,
|
||||
"path": fp,
|
||||
"name": os.path.basename(fp),
|
||||
"name": display_name or os.path.basename(fp),
|
||||
"autoplay": bool(meta.get("autoplay") or audio_meta.get("autoplay")) if role == "assistant" else False,
|
||||
"duration_seconds": audio_meta.get("duration_seconds"),
|
||||
})
|
||||
else:
|
||||
parts.append({"type": "file", "url": data_url, "path": fp, "name": os.path.basename(fp)})
|
||||
parts.append({"type": "file", "url": data_url, "path": fp, "name": display_name or os.path.basename(fp)})
|
||||
return parts
|
||||
|
||||
|
||||
@@ -235,12 +246,14 @@ def api_delete_session(session_id: str = Form(...)) -> dict[str, Any]:
|
||||
|
||||
def _materialize_uploads_to_temp(
|
||||
images: list[UploadFile] | None,
|
||||
files: list[UploadFile] | None,
|
||||
audio: UploadFile | None,
|
||||
text: str,
|
||||
) -> tuple[dict[str, Any], str]:
|
||||
"""将上传文件落盘到临时目录,返回 ``(query_dict, tmp_root)``;调用方须在 finally 中删除 ``tmp_root``。"""
|
||||
tmp_root = tempfile.mkdtemp(prefix="web_mm_")
|
||||
file_paths: list[str] = []
|
||||
file_names: list[str] = []
|
||||
for uf in images or []:
|
||||
if uf is None or not uf.filename:
|
||||
continue
|
||||
@@ -249,6 +262,17 @@ def _materialize_uploads_to_temp(
|
||||
with open(dest, "wb") as f:
|
||||
shutil.copyfileobj(uf.file, f)
|
||||
file_paths.append(dest)
|
||||
file_names.append(uf.filename)
|
||||
|
||||
for uf in files or []:
|
||||
if uf is None or not uf.filename:
|
||||
continue
|
||||
ext = Path(uf.filename).suffix or ""
|
||||
dest = os.path.join(tmp_root, f"{uuid.uuid4().hex}{ext}")
|
||||
with open(dest, "wb") as f:
|
||||
shutil.copyfileobj(uf.file, f)
|
||||
file_paths.append(dest)
|
||||
file_names.append(uf.filename)
|
||||
|
||||
if audio is not None and audio.filename:
|
||||
ext = Path(audio.filename).suffix or ".bin"
|
||||
@@ -256,8 +280,9 @@ def _materialize_uploads_to_temp(
|
||||
with open(dest, "wb") as f:
|
||||
shutil.copyfileobj(audio.file, f)
|
||||
file_paths.append(dest)
|
||||
file_names.append(audio.filename)
|
||||
|
||||
query = {"text": (text or "").strip(), "files": file_paths}
|
||||
query = {"text": (text or "").strip(), "files": file_paths, "file_names": file_names}
|
||||
return query, tmp_root
|
||||
|
||||
|
||||
@@ -309,6 +334,7 @@ async def api_chat_start(
|
||||
session_id: str = Form(...),
|
||||
text: str = Form(""),
|
||||
images: list[UploadFile] | None = File(None),
|
||||
files: list[UploadFile] | None = File(None),
|
||||
audio: UploadFile | None = File(None),
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
@@ -318,7 +344,7 @@ async def api_chat_start(
|
||||
if not session_id:
|
||||
raise HTTPException(status_code=400, detail="缺少 session_id")
|
||||
|
||||
query, tmp_root = _materialize_uploads_to_temp(images, audio, text)
|
||||
query, tmp_root = _materialize_uploads_to_temp(images, files, audio, text)
|
||||
try:
|
||||
return _chat_start_core(session_id, query)
|
||||
finally:
|
||||
@@ -341,6 +367,7 @@ async def api_chat(
|
||||
session_id: str = Form(...),
|
||||
text: str = Form(""),
|
||||
images: list[UploadFile] | None = File(None),
|
||||
files: list[UploadFile] | None = File(None),
|
||||
audio: UploadFile | None = File(None),
|
||||
output_type: str = Form("text"),
|
||||
) -> dict[str, Any]:
|
||||
@@ -350,7 +377,7 @@ async def api_chat(
|
||||
if not session_id:
|
||||
raise HTTPException(status_code=400, detail="缺少 session_id")
|
||||
|
||||
query, tmp_root = _materialize_uploads_to_temp(images, audio, text)
|
||||
query, tmp_root = _materialize_uploads_to_temp(images, files, audio, text)
|
||||
try:
|
||||
_chat_start_core(session_id, query)
|
||||
return _chat_complete_core(session_id, output_type=output_type)
|
||||
|
||||
Reference in new issue
Block a user