""" utils.py - 工具函数模块 提供日志配置、文本分块和表格转换等通用工具函数。 """ import logging import sys logger = logging.getLogger(__name__) def setup_logging(verbose: bool = False): """配置日志格式。 - verbose=True: DEBUG级别,显示文件名行号 - verbose=False: INFO级别,简洁格式 Args: verbose: 是否开启详细日志模式 """ level = logging.DEBUG if verbose else logging.INFO fmt = ( "%(asctime)s [%(levelname)s] %(name)s (%(filename)s:%(lineno)d): %(message)s" if verbose else "[%(levelname)s] %(message)s" ) logging.basicConfig( level=level, format=fmt, stream=sys.stderr, force=True, ) def chunk_text(text: str, max_chars: int = 4000) -> list[str]: """将长文本按最大字符数切分,尽量在段落边界处切分。 Args: text: 待切分的文本 max_chars: 每个块的最大字符数(默认 4000) Returns: 切分后的文本块列表 """ if not text: logger.debug("chunk_text 收到空文本,返回空列表") return [] if max_chars <= 0: logger.warning("max_chars=%d 无效,使用默认值 4000", max_chars) max_chars = 4000 if len(text) <= max_chars: logger.debug("文本长度 %d <= %d,无需切分", len(text), max_chars) return [text] chunks: list[str] = [] start = 0 while start < len(text): # 如果剩余文本不足 max_chars,直接取剩余全部 if start + max_chars >= len(text): chunks.append(text[start:]) break # 在当前块末尾附近寻找段落边界(换行符) end = start + max_chars # 在 [start+max_chars//2, start+max_chars] 范围内向前找最后一个换行符 search_start = max(start + max_chars // 2, start) boundary = text.rfind("\n", search_start, end) if boundary <= start: # 找不到合适换行符,在最大范围内向后找 boundary = text.find("\n", end) if boundary == -1 or boundary >= start + int(max_chars * 1.5): # 仍然找不到,直接在 max_chars 处切分 boundary = end chunk = text[start:boundary].strip() if chunk: chunks.append(chunk) start = boundary + 1 # 跳过换行符 logger.debug("文本已切分为 %d 个块", len(chunks)) return chunks def table_to_markdown(table_info) -> str: """将 pdf_table_to_docx.table_parser.TableInfo 转换为 Markdown 表格格式。 用于将表格内容序列化为文本,方便传给 AI。 Args: table_info: 来自 pdf_table_to_docx 的 TableInfo 对象 Returns: Markdown 格式的表格字符串 """ # 输入校验 if table_info is None: logger.warning("table_to_markdown 收到 None 输入") return "(空表格)" if not hasattr(table_info, "cells"): logger.warning("table_to_markdown 收到的对象没有 cells 属性") return "(空表格)" rows = table_info.cells # list[list[CellInfo]] if not rows: logger.debug("table_to_markdown: 空表格(无行)") return "(空表格)" lines: list[str] = [] for row_idx, row in enumerate(rows): # 提取该行每个单元格的文本,去除首尾空白 cell_texts = [cell.text.strip() if hasattr(cell, "text") else str(cell).strip() for cell in row] # 用 | 分隔单元格 lines.append("| " + " | ".join(cell_texts) + " |") # 在第一行之后添加分隔行(表头分隔符) if row_idx == 0: lines.append("|" + "|".join("---" for _ in row) + "|") return "\n".join(lines)