from __future__ import annotations from .shared import * def _resolve_tenant_id(user: User, fallback_tenant_id: int) -> int: try: return int(getattr(user, "tenant_id") or fallback_tenant_id or 1) except Exception: return int(fallback_tenant_id or 1) def _safe_decode_bytes(raw: bytes) -> str: for encoding in ("utf-8", "utf-8-sig", "gb18030", "latin-1"): try: return raw.decode(encoding) except Exception: continue return raw.decode("utf-8", errors="ignore") def _extract_text_from_upload(filename: str, raw: bytes) -> tuple[str, str]: name = str(filename or "uploaded.txt") suffix = name.rsplit(".", 1)[-1].lower() if "." in name else "txt" if suffix in {"txt", "md", "markdown", "csv", "json", "py", "ts", "tsx", "js", "jsx"}: return ("md" if suffix in {"md", "markdown"} else "txt", _safe_decode_bytes(raw)) if suffix == "pdf": try: from pypdf import PdfReader # type: ignore except Exception as exc: raise HTTPException(status_code=400, detail=f"PDF 解析依赖缺失: {exc}") from exc reader = PdfReader(io.BytesIO(raw)) parts: list[str] = [] for idx, page in enumerate(reader.pages, start=1): page_text = (page.extract_text() or "").strip() if not page_text: continue # 写入页码标记,向量分块时会保留 page_number 元数据,便于引用来源页码。 parts.append(f"[[PAGE:{idx}]]\n{page_text}") text = "\n\n".join(parts) return ("pdf", text) if suffix in {"docx", "doc"}: try: from docx import Document # type: ignore except Exception as exc: raise HTTPException(status_code=400, detail=f"Word 解析依赖缺失: {exc}") from exc doc = Document(io.BytesIO(raw)) text = "\n".join([p.text for p in doc.paragraphs if p.text]) return ("word", text) raise HTTPException(status_code=400, detail=f"不支持的文件类型: .{suffix}")