- api_core.py 5处、init.py 6处、pipeline.py 1处: uuid4().hex -> getID()
(uuid4 hex 32位+chunk后缀_cN 溢出 VARCHAR(32) → ingest 1406 DataError,
即 search 0 命中根因;getID() 21位放得下)
- upload_file.dspy: 去掉 replace('-','')[:16] 手工截断,直用沙箱 uuid()
- 规范:.py 必须 getID(),.dspy 必须 uuid()
64 lines
3.2 KiB
Plaintext
64 lines
3.2 KiB
Plaintext
import base64, os, subprocess
|
||
ns = params_kw.copy()
|
||
kb_id = ns.get('kb_id', '')
|
||
folder_id = ns.get('folder', '')
|
||
file_name = ns.get('file_name', 'upload.bin')
|
||
if not kb_id:
|
||
return json.dumps({"status": "error", "error": "kb_id required"}, ensure_ascii=False)
|
||
|
||
file_data = await request.read()
|
||
if not file_data:
|
||
return json.dumps({"status": "error", "error": "no file data"}, ensure_ascii=False)
|
||
|
||
env = request._run_ns
|
||
userorgid = await env.get_userorgid()
|
||
file_size = len(file_data)
|
||
|
||
# ---- ORG STORAGE QUOTA CHECK (per-org limit, not global) ----
|
||
def fmt_bytes(n):
|
||
if n < 1024: return str(n) + 'B'
|
||
if n < 1048576: return str(round(n/1024, 1)) + 'KB'
|
||
return str(round(n/1048576, 1)) + 'MB'
|
||
|
||
quota_limit = 104857600
|
||
used = 0
|
||
async with get_sor_context(env, 'rag') as sor:
|
||
rec = await sor.sqlExe("SELECT COALESCE(SUM(file_size),0) AS used FROM rag_documents WHERE org_id=${org_id}$", {"org_id": userorgid})
|
||
if rec: used = int(rec[0].used)
|
||
lim = await sor.sqlExe("SELECT limit_bytes FROM rag_org_storage_limits WHERE org_id=${org_id}$", {"org_id": userorgid})
|
||
if lim: quota_limit = int(lim[0].limit_bytes)
|
||
if used + file_size > quota_limit:
|
||
return json.dumps({"status": "error", "error": "storage_quota_exceeded",
|
||
"message": "存储配额超限:机构已用 " + fmt_bytes(used) + ",限额 " + fmt_bytes(quota_limit) + ",本文件 " + fmt_bytes(file_size)}, ensure_ascii=False)
|
||
|
||
# Save file via FileStorage (returns web path e.g. /idfile/191/193/197/97/xxx.txt)
|
||
web_path = await env.save_file(file_data, file_name)
|
||
real_path = env.realpath(web_path)
|
||
|
||
doc_id = uuid()
|
||
ext = '.' + file_name.rsplit('.', 1)[1] if '.' in file_name else '.bin'
|
||
ext_l = ext.lower()
|
||
|
||
# ── 后台入库:共享模块 rag.ingest(UI 上传与对外 API 上传同一实现)──
|
||
# background_reco 派发 asyncio 任务;ingest_one 只接原始类型参数、自建连接。
|
||
from rag.ingest import ingest_one
|
||
# SYNC PART — record document as 'pending', update KB counts,
|
||
# fire background ingestion, return immediately.
|
||
# ============================================================
|
||
async with get_sor_context(env, 'rag') as sor:
|
||
await sor.sqlExe(
|
||
"INSERT INTO rag_documents (id, kb_id, folder_id, file_name, file_type, file_size, file_path, mime_type, status, chunk_count, metadata, org_id, created_at, updated_at) "
|
||
"VALUES (${id}$, ${kb_id}$, ${folder_id}$, ${file_name}$, 'other', ${file_size}$, ${file_path}$, 'application/octet-stream', 'pending', 0, '{}', ${org_id}$, NOW(), NOW())",
|
||
{"id": doc_id, "kb_id": kb_id, "folder_id": folder_id, "file_name": file_name,
|
||
"file_size": file_size, "file_path": web_path, "org_id": userorgid})
|
||
await sor.sqlExe(
|
||
"UPDATE rag_knowledge_bases SET doc_count=doc_count+1, total_size=total_size+${size}$ WHERE id=${kb_id}$",
|
||
{"size": file_size, "kb_id": kb_id})
|
||
|
||
# Fire background ingestion — pass primitives only (no env/request/proxy objects)
|
||
background_reco(ingest_one, doc_id, kb_id, file_name, ext_l, real_path)
|
||
|
||
result = {"status": "SUCCEEDED", "doc_id": doc_id, "file_name": file_name,
|
||
"file_size": file_size, "folder_id": folder_id, "ingest": "pending"}
|
||
return json.dumps(result, ensure_ascii=False, default=str)
|