MCPcopy Create free account
hub / github.com/NatsUIJM/autoContents / fetch_toc_from_image

Function fetch_toc_from_image

mainprogress/pdf_metadata_extractor.py:119–173  ·  view source on GitHub ↗

调用 LLM 识别拼接图片中的目录范围,并保存原始响应

(client: AsyncOpenAI, model: str, b64_img: str, start_p: int, end_p: int, raw_save_path: str = None)

Source from the content-addressed store, hash-verified

117【目录的严格定义】:一页中必须存在多个“标题 - 页码”对。如果某一页没有这个特征(例如纯文本正文、封面、版权页、序言),则它绝对不是目录。只要不存在页码,那这页绝对不是目录。相对应地,如果一页有这个特征,那么它必然是目录。
118
119【注意】
1201. 应以下方的 PDFNumber 作为页码。例如 PDFNumber 为 2-3 的图片是目录,那么起始页码就是 2,结束页码就是 3。
1212. 注意空白页不可以算作目录的一部分,空白页一定不是目录!
1223. 例如 PDFNumber 为 16 的图片是空白,为 17-18 的图片是目录,那么起始页码就是 17(切记,不是 16!),结束页码就是 18。请切记不要把空白页面识别为目录的一部分,这会导致后续程序出现严重错误!!!
123
124【输出要求】:
1251. 仅输出 JSON 格式,不要包含任何 markdown 标记(如 ```json )、解释或额外文本。
1262. 如果这几页中存在目录,输出格式为:{{"toc_start": 起始页码,"toc_end": 结束页码}}
1273. 如果这几页中没有任何一页是目录,输出格式为:{{"toc_start": null, "toc_end": null}}"""
128
129 try:
130 completion = await client.chat.completions.create(
131 model=model,
132 messages=[
133 {
134 "role": "user",
135 "content": [
136 {"type": "text", "text": prompt},
137 {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{b64_img}"}},
138 {"type": "text", "text": prompt},
139 ]
140 }
141 ],
142 extra_body={"enable_thinking": False},
143 temperature=0,
144 )
145 raw_content = completion.choices[0].message.content.strip()
146
147 if raw_save_path:
148 response_data = {
149 "page_range": f"{start_p}-{end_p}",
150 "raw_response": raw_content,
151 "model": model
152 }
153 with open(raw_save_path, 'w', encoding='utf-8') as f:
154 json.dump(response_data, f, ensure_ascii=False, indent=2)
155 logger.debug(f"原始响应已保存至:{raw_save_path}")
156 write_log(f"原始响应已保存至:{raw_save_path}")
157
158 return raw_content
159 except Exception as e:
160 error_msg = f"获取目录范围失败 ({start_p}-{end_p}): {e}"
161 logger.error(error_msg)
162 write_log(error_msg)
163 if raw_save_path:
164 with open(raw_save_path, 'w', encoding='utf-8') as f:
165 json.dump({"error": str(e), "page_range": f"{start_p}-{end_p}"}, f, ensure_ascii=False, indent=2)
166 return ""
167
168def parse_toc_json(text: str) -> tuple:
169 """安全解析 LLM 输出的 JSON,剥离 Markdown 标记"""
170 if not text:
171 return None, None
172 try:
173 clean_text = re.sub(r'^```(?:json)?\s*', '', text.strip(), flags=re.MULTILINE)
174 clean_text = re.sub(r'\s*```$', '', clean_text, flags=re.MULTILINE)
175 data = json.loads(clean_text)
176 return data.get("toc_start"), data.get("toc_end")

Callers 1

process_windowFunction · 0.85

Calls 1

write_logFunction · 0.70

Tested by

no test coverage detected