chat.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597
  1. import sys
  2. import time
  3. import os
  4. import logging
  5. # 首先导入日志配置模块,确保日志系统在导入其他模块之前就配置好
  6. # 这样当导入 llm、conf 等模块时,它们的日志也能正常工作
  7. import logger_config
  8. # 使用标准logging,与app_v2.py共用日志配置
  9. # 使用__name__会得到"chat",确保日志能正确输出
  10. logger = logging.getLogger(__name__)
  11. # 确保logger正确配置 - 强制添加handler,确保日志能输出
  12. logger.setLevel(logging.INFO)
  13. # 为chat logger强制添加handler(直接输出,不依赖传播)
  14. # 这样即使gunicorn重置了根logger,chat的logger仍能输出
  15. # 清除已有handler,重新添加(应对gunicorn worker重启的情况)
  16. for h in logger.handlers[:]:
  17. logger.removeHandler(h)
  18. handler = logging.StreamHandler(sys.stderr)
  19. formatter = logging.Formatter(
  20. '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
  21. datefmt='%Y-%m-%d %H:%M:%S'
  22. )
  23. handler.setFormatter(formatter)
  24. handler.setLevel(logging.INFO)
  25. logger.addHandler(handler)
  26. logger.propagate = False # 不传播,直接使用自己的handler
  27. # 现在导入其他模块(它们会在导入时使用已配置好的日志系统)
  28. from PIL import Image
  29. import requests
  30. from prompt import *
  31. from llm import *
  32. import json
  33. from conf import *
  34. import re
  35. MAX_RETRIES = 5
  36. MAX_HISTORY = 20
  37. MAX_CHAR_LIMIT = 400
  38. MIN_CHAR_LIMIT = 100 # 假设的最小长度限制
  39. history_list=[]
  40. plugins = {
  41. # "ch_en_selling_points":get_ch_en_selling_points,
  42. # "en_ch_selling_points":get_en_ch_selling_points,
  43. "ch_en_selling_title":get_ch_en_selling_title,
  44. # "en_ch_selling_points_his":get_en_ch_selling_points_his,
  45. # "TextControl_his":TextControl_his,
  46. # "TextControl":TextControl
  47. }
  48. def contains_chinese(text):
  49. pattern = re.compile(r'[\u4e00-\u9fa5]')
  50. return bool(pattern.search(text))
  51. def check_image_url(url):
  52. """检查图片URL是否有效
  53. Args:
  54. url: 图片URL字符串
  55. Returns:
  56. tuple: (is_valid, message)
  57. is_valid: bool,表示URL是否有效
  58. message: str,错误信息(如果无效)或空字符串(如果有效)
  59. """
  60. logger.info(f"开始检查图片URL: {url}")
  61. if not url or not isinstance(url, str):
  62. logger.warning(f"图片URL格式无效: {url}")
  63. return False, "Image URL is required and must be a string."
  64. # 检查是否是有效的URL格式
  65. if not url.startswith(('http://', 'https://')):
  66. logger.warning(f"图片URL必须以http://或https://开头: {url}")
  67. return False, "Image URL must start with http:// or https://"
  68. try:
  69. # 使用HEAD请求检查URL是否可访问,设置超时避免长时间等待
  70. logger.debug(f"发送HEAD请求检查URL: {url}")
  71. response = requests.head(url, timeout=10, allow_redirects=True)
  72. logger.debug(f"HEAD请求响应状态码: {response.status_code}")
  73. # 检查状态码
  74. if response.status_code == 200:
  75. # 检查Content-Type是否是图片类型
  76. content_type = response.headers.get('Content-Type', '').lower()
  77. logger.debug(f"Content-Type: {content_type}")
  78. if 'image' in content_type:
  79. logger.info(f"图片URL验证成功: {url}")
  80. return True, ""
  81. else:
  82. logger.warning(f"URL不是图片类型,Content-Type: {content_type}")
  83. return False, f"URL does not point to an image. Content-Type: {content_type}"
  84. elif response.status_code == 405:
  85. # 如果HEAD不支持,尝试GET方法(但只获取头信息)
  86. logger.info(f"HEAD方法不支持,尝试GET方法: {url}")
  87. try:
  88. response = requests.get(url, timeout=10, stream=True)
  89. response.raise_for_status()
  90. content_type = response.headers.get('Content-Type', '').lower()
  91. logger.debug(f"GET请求Content-Type: {content_type}")
  92. if 'image' in content_type:
  93. logger.info(f"图片URL验证成功(通过GET方法): {url}")
  94. return True, ""
  95. else:
  96. logger.warning(f"URL不是图片类型,Content-Type: {content_type}")
  97. return False, f"URL does not point to an image. Content-Type: {content_type}"
  98. except requests.exceptions.RequestException as e:
  99. logger.error(f"GET请求失败: {str(e)}")
  100. return False, f"Cannot access image URL: {str(e)}"
  101. else:
  102. logger.warning(f"无法访问图片URL,状态码: {response.status_code}")
  103. return False, f"Cannot access image URL. Status code: {response.status_code}"
  104. except requests.exceptions.Timeout:
  105. logger.error(f"检查图片URL超时: {url}")
  106. return False, "Timeout while checking image URL. Please check if the URL is accessible."
  107. except requests.exceptions.ConnectionError:
  108. logger.error(f"无法连接到图片URL: {url}")
  109. return False, "Cannot connect to the image URL. Please check your network connection."
  110. except requests.exceptions.RequestException as e:
  111. logger.error(f"检查图片URL时发生请求异常: {str(e)}")
  112. return False, f"Error checking image URL: {str(e)}"
  113. except Exception as e:
  114. logger.error(f"检查图片URL时发生未知错误: {str(e)}")
  115. return False, f"Unexpected error while checking image URL: {str(e)}"
  116. def format_history(strings, indent=" "):
  117. result = ""
  118. for i, string in enumerate(strings, start=1):
  119. # 拼接序号、缩进和字符串,并添加换行符
  120. result += f"{indent}{i}. {string}\n"
  121. return result
  122. def get_history():
  123. """获取格式化的历史记录(用于原始prompt)"""
  124. global history_list
  125. if len(history_list)==0:
  126. history=''
  127. else:
  128. history=format_history(history_list)
  129. return history
  130. def add_history(input,max_num=20):
  131. global history_list
  132. text = re.split(r'[,\.\!\?\;\:]+', input)
  133. text=text[0].strip()
  134. logger.debug(f"添加历史记录: {text[:50]}..." if len(text) > 50 else f"添加历史记录: {text}")
  135. history_list.insert(0, text)
  136. if len(history_list)>max_num:
  137. history_list=history_list[:max_num]
  138. def generate_text(plm_info,img,graphic_label=None,plat="ali",model_name="mm_qwen"):
  139. logger.info(f"开始生成文本,platform={plat}, model={model_name}")
  140. logger.debug(f"plm_info长度: {len(plm_info) if plm_info else 0}, graphic_label: {graphic_label}")
  141. history_string=get_history()
  142. logger.debug(f"历史记录数量: {len(history_list)}")
  143. if graphic_label:
  144. tags_sen=",".join(graphic_label)
  145. plm_info+="\n' '以下是该衣服的标签信息:"+tags_sen
  146. logger.debug(f"添加标签信息: {tags_sen}")
  147. if plat=="ali":
  148. key=ali_ky
  149. model=ali_model[model_name]
  150. logger.debug(f"使用阿里云平台,模型: {model}")
  151. else:
  152. key=doubao_ky
  153. model=doubao_model[model_name]
  154. logger.debug(f"使用豆包平台,模型: {model}")
  155. llm=llm_request(*key,model)
  156. en,kw='',['']
  157. result_json = None
  158. for attempt in range(MAX_RETRIES):
  159. # --- 构造Prompt ---
  160. if attempt == 0:
  161. # 第一次尝试:使用您的主Prompt
  162. usrp = user_prompt.format(basic_info_string=plm_info,history_string=history_string)
  163. logger.info(f"第 {attempt + 1} 次尝试:使用原始Prompt")
  164. else:
  165. # 后续尝试:使用"修正Prompt"
  166. usrp = get_refinement_prompt(plm_info, history_string, result_json)
  167. logger.info(f"第 {attempt + 1} 次尝试:使用修正Prompt,失败原因: {result_json.get('error', 'UNKNOWN')}")
  168. logger.debug(f"Prompt长度: {len(usrp)}")
  169. try:
  170. response_text = llm.llm_mm_request(usrp,img,sys_text=system_prompt)
  171. logger.debug(f"API响应长度: {len(response_text) if response_text else 0}")
  172. except Exception as e:
  173. logger.error(f"API调用失败(第{attempt + 1}次尝试): {str(e)}")
  174. result_json = {"error": "API_FAILURE", "raw_response": str(e)}
  175. continue
  176. try:
  177. is_valid, validation_error, result_json = validate_response(response_text)
  178. if is_valid:
  179. # 成功!
  180. en,kw=result_json['en'],result_json['kw']
  181. logger.info(f"文本生成成功(第{attempt + 1}次尝试),描述长度: {len(en)}, 关键词数量: {len(kw) if isinstance(kw, list) else 1}")
  182. logger.debug(f"生成的描述: {en[:100]}..." if len(en) > 100 else f"生成的描述: {en}")
  183. add_history(en)
  184. break
  185. else:
  186. # 失败,记录错误,循环将继续
  187. logger.warning(f"第 {attempt + 1} 次尝试验证失败: {validation_error}")
  188. # result_json 已经包含了失败的文本和错误信息,将用于下一次修正
  189. continue
  190. except Exception as e:
  191. logger.error(f"验证响应时发生异常: {str(e)}")
  192. result_json = {"error": "VALIDATION_ERROR", "raw_response": str(e)}
  193. continue
  194. if result_json and result_json.get("error") == "EN_TOO_LONG":
  195. # 如果是因为超长而失败,且 raw_response 有效
  196. logger.info("检测到文本超长,尝试智能截断")
  197. try:
  198. failed_data = json.loads(result_json.get("raw_response", "{}"))
  199. long_en_text = failed_data.get("en")
  200. if long_en_text and len(long_en_text) > MAX_CHAR_LIMIT+100:
  201. logger.info(f"原始文本长度: {len(long_en_text)},开始截断到 {MAX_CHAR_LIMIT+100} 字符")
  202. en = smart_truncate_by_sentence(long_en_text, max_chars=MAX_CHAR_LIMIT+100)
  203. kw = failed_data.get("kw", '')
  204. logger.info(f"截断后文本长度: {len(en)}")
  205. add_history(en)
  206. except (json.JSONDecodeError, KeyError, TypeError) as e:
  207. logger.error(f"截断文本时发生错误: {str(e)}")
  208. pass
  209. if isinstance(kw,str):
  210. kw = [item.strip() for item in kw.split('.') if item.strip()]
  211. logger.debug(f"关键词从字符串转换为列表,数量: {len(kw)}")
  212. if not en:
  213. logger.warning("最终生成的描述为空")
  214. return en,kw
  215. def validate_response(response_text):
  216. """验证模型的输出是否符合所有规则"""
  217. logger.debug(f"开始验证响应,响应文本长度: {len(response_text) if response_text else 0}")
  218. try:
  219. # 规则1: 是否是有效JSON?
  220. data = json.loads(response_text.strip())
  221. logger.debug("JSON解析成功")
  222. except json.JSONDecodeError as e:
  223. logger.warning(f"JSON解析失败: {str(e)}")
  224. return False, "INVALID_JSON", {"error": "INVALID_JSON", "raw_response": response_text}
  225. # 规则2: 键是否齐全?
  226. required_keys = ["en", "ch", "kw"]
  227. missing_keys = [k for k in required_keys if k not in data]
  228. if missing_keys:
  229. logger.warning(f"缺少必需的键: {missing_keys}")
  230. return False, "MISSING_KEYS", {"error": "MISSING_KEYS", "raw_response": json.dumps(data)}
  231. en_text = data.get("en", "")
  232. logger.debug(f"英文文本长度: {len(en_text)}")
  233. # 规则3: 长度是否超标?
  234. if len(en_text) > MAX_CHAR_LIMIT+100:
  235. logger.warning(f"文本长度超标: {len(en_text)} > {MAX_CHAR_LIMIT+100}")
  236. return False, "EN_TOO_LONG", {"error": "EN_TOO_LONG", "raw_response": json.dumps(data)}
  237. # 规则4: 长度是否太短?
  238. if len(en_text) < MIN_CHAR_LIMIT:
  239. logger.warning(f"文本长度太短: {len(en_text)} < {MIN_CHAR_LIMIT}")
  240. return False, "EN_TOO_SHORT", {"error": "EN_TOO_SHORT", "raw_response": json.dumps(data)}
  241. # 规则5: 是否包含中文
  242. if contains_chinese(en_text):
  243. logger.warning(f"文本包含中文字符")
  244. return False, "EN_CONTAINS_CHINESE", {"error": "EN_CONTAINS_CHINESE", "raw_response": json.dumps(data)}
  245. logger.debug("响应验证成功")
  246. return True, "SUCCESS", data
  247. def smart_truncate_by_sentence(text, max_chars=MAX_CHAR_LIMIT):
  248. logger.info(f"开始智能截断文本,原始长度: {len(text)}, 最大字符数: {max_chars}")
  249. if len(text) <= max_chars:
  250. logger.debug("文本长度未超过限制,无需截断")
  251. return text
  252. # 按句子分隔符分割文本,保留分隔符
  253. sentence_pattern = re.compile(r'([^.!?]+[.!?])')
  254. sentences = sentence_pattern.findall(text)
  255. logger.debug(f"分割出 {len(sentences)} 个句子")
  256. # 如果没有找到完整句子,直接截断
  257. if not sentences:
  258. logger.warning("未找到完整句子,使用直接截断")
  259. truncated = text[:max_chars-3].strip() + '...'
  260. logger.info(f"直接截断后长度: {len(truncated)}")
  261. return truncated
  262. # 遍历每个句子,累加长度
  263. result_sentences = []
  264. total_length = 0
  265. for i, sentence in enumerate(sentences):
  266. sentence_length = len(sentence)
  267. # 如果加上当前句子后超过限制,则停止添加
  268. if total_length + sentence_length > max_chars:
  269. logger.debug(f"第 {i+1} 个句子(长度: {sentence_length})会导致超出限制,停止添加")
  270. break
  271. # 累加句子
  272. result_sentences.append(sentence)
  273. total_length += sentence_length
  274. logger.debug(f"添加第 {i+1} 个句子(长度: {sentence_length}),累计长度: {total_length}")
  275. # 如果至少有一个句子被添加
  276. if result_sentences:
  277. truncated_text = ''.join(result_sentences).strip()
  278. # 确保以句子结尾符号结尾
  279. if truncated_text and not truncated_text.endswith(('.', '!', '?')):
  280. truncated_text += '.'
  281. logger.info(f"截断完成,使用了 {len(result_sentences)} 个句子,最终长度: {len(truncated_text)}")
  282. return truncated_text.strip()
  283. else:
  284. # 如果第一个句子就超过限制,直接截断到 max_chars
  285. logger.warning("第一个句子就超过限制,使用直接截断")
  286. truncated = text[:max_chars-3].strip() + '...'
  287. logger.info(f"直接截断后长度: {len(truncated)}")
  288. return truncated
  289. def get_refinement_prompt(basic_info_string, history_string, failed_result):
  290. """
  291. 根据上一次的失败原因,生成一个“引导式修正”的Prompt
  292. """
  293. failure_reason = failed_result.get("error", "UNKNOWN")
  294. raw_response = failed_result.get("raw_response", "")
  295. feedback = ""
  296. # 尝试提取上次失败的文案
  297. last_text_en = ""
  298. try:
  299. if raw_response:
  300. last_text_en = json.loads(raw_response).get("en", "")
  301. except json.JSONDecodeError:
  302. pass # 无法解析,last_text_en 保持空
  303. if failure_reason == "INVALID_JSON":
  304. feedback = f"你上次的输出不是一个有效的JSON。请【严格】按照JSON格式输出。你上次的错误输出是:\n{raw_response}"
  305. elif failure_reason == "EN_TOO_LONG":
  306. feedback = f"""
  307. 你上次生成的 "en" 描述【超过了{MAX_CHAR_LIMIT}个字符】!
  308. 【你生成的超长原文】:\n{last_text_en}
  309. 【修正任务】: 请【大幅精简】上述原文,保留核心卖点,使其长度【绝对】在{MIN_CHAR_LIMIT}-{MAX_CHAR_LIMIT}字符以内。
  310. """
  311. elif failure_reason == "EN_TOO_SHORT":
  312. feedback = f"""
  313. 你上次生成的 "en" 描述太短了(小于{MIN_CHAR_LIMIT}字符)。
  314. 【你生成的原文】:\n{last_text_en}
  315. 【修正任务】: 请在原文案基础上,围绕核心卖点再丰富一些细节,使其达到{MIN_CHAR_LIMIT}-{MAX_CHAR_LIMIT}字符。
  316. """
  317. elif failure_reason == "MISSING_KEYS":
  318. feedback = f"你上次输出的JSON缺少 'en', 'ch' 或 'kw' 键。请确保三者齐全。"
  319. elif failure_reason == "TOO_SIMILAR":
  320. feedback = "你上次生成的文案与历史记录太相似了。请换一个角度(比如从'材质'或'穿搭场景')重新构思,字数保持在要求的范围内。"
  321. elif failure_reason == "EN_CONTAINS_CHINESE":
  322. feedback = f"""
  323. 你上次生成的 "en" 描述中包含了中文汉字(例如:{last_text_en})。
  324. 【修正任务】: "en" 字段【必须是纯英文】,【绝对禁止】出现任何中文字符。请严格修正并重新输出。
  325. """
  326. else:
  327. feedback = "你上次的生成失败了。请重新严格按照所有规则生成一次。"
  328. # 修正Prompt模板
  329. refinement_prompt = f"""## 角色
  330. 你是一个文案修正专家。
  331. ## 原始任务
  332. 根据以下信息和随消息传入的图片生成文案:{basic_info_string}
  333. ## 上次失败的反馈 (你必须修正!)
  334. {feedback}
  335. ## 核心规则 (必须再次遵守)
  336. 1. 【必须】输出严格的JSON格式。
  337. 2. "en" 描述【必须严格在{MAX_CHAR_LIMIT}字符以内】。
  338. 3. 【不要】使用历史开篇:\n{history_string}
  339. ## 最终输出
  340. 请直接输出修正后的、严格符合要求的JSON字典。
  341. """
  342. return refinement_prompt
  343. def gen_title(info,tags=None,referencr_title=None,method="ch_en_selling_title",plat="ali",model_name="text_dsv3"):
  344. logger.info(f"开始生成标题,platform={plat}, model={model_name}, method={method}")
  345. logger.debug(f"info长度: {len(info) if info else 0}, tags: {tags}, reference_title: {referencr_title}")
  346. if tags:
  347. tags_sen=",".join(tags)
  348. info="\n' '以下是该衣服的关键点:"+tags_sen
  349. logger.debug(f"添加标签信息: {tags_sen}")
  350. if referencr_title:
  351. info="\n' '请以这条标题样例的结构作为借鉴来写这条标题:"+referencr_title
  352. logger.debug(f"添加参考标题: {referencr_title}")
  353. try:
  354. sysp,usrp = plugins[method](info)
  355. logger.debug(f"Prompt生成成功,system prompt长度: {len(sysp) if sysp else 0}, user prompt长度: {len(usrp) if usrp else 0}")
  356. except KeyError as e:
  357. logger.error(f"未知的方法: {method}, 可用方法: {list(plugins.keys())}")
  358. raise
  359. except Exception as e:
  360. logger.error(f"生成Prompt时发生错误: {str(e)}")
  361. raise
  362. if plat=="ali":
  363. key=ali_ky
  364. model=ali_model[model_name]
  365. logger.debug(f"使用阿里云平台,模型: {model}")
  366. else:
  367. key=doubao_ky
  368. model=doubao_model[model_name]
  369. logger.debug(f"使用豆包平台,模型: {model}")
  370. llm=llm_request(*key,model)
  371. try:
  372. res=llm.llm_text_request(usrp,sysp)
  373. logger.debug(f"API响应长度: {len(res) if res else 0}")
  374. except Exception as e:
  375. logger.error(f"API调用失败: {str(e)}")
  376. raise
  377. try:
  378. res_dict = json.loads(res)
  379. logger.debug(f"JSON解析成功,keys: {list(res_dict.keys())}")
  380. except json.JSONDecodeError as e:
  381. logger.error(f"JSON解析失败: {str(e)}, 响应内容: {res[:200] if res else 'None'}...")
  382. raise
  383. title = res_dict.get("en_tile") # 注意:这里可能是拼写错误,但保持原样
  384. if not title:
  385. logger.warning(f"响应中未找到 'en_tile' 字段,可用字段: {list(res_dict.keys())}")
  386. logger.info(f"标题生成成功,标题长度: {len(title) if title else 0}")
  387. return {"title": title}
  388. if __name__ == "__main__":
  389. # inf="'Meet your new best friend in fashion—this unisex sweater that whispers comfort and style. Crafted from premium cotton, it feels like a gentle hug on your skin. The heart embroidery adds a touch of whimsy, making you the star of any casual outing. Perfect for layering or wearing solo, this soft companion keeps you cozy all season long."
  390. # print(gen_title(inf))
  391. # id_image,id_price, id_color, id_ingredient, id_selling_point, id_details=search_json_files("1A6H4K7V0")
  392. # id_image=id_image[2:]
  393. # id_image=os.path.join("/data/data/luosy/project/sku_search",id_image)
  394. id_image="https://img2.goelia.com.au/prod/product/1ENC6E220/material/main/Shopify/-1/72736752b0ad405382d5ed277dabc660.jpg"
  395. graphic_label=['-100% Merino wool', '-With pockets', '-H-line fit']
  396. plm_info='1、手工流苏边设计 \xa0 2、贴袋设计 \xa0 3、金属纽扣'
  397. # print(id_details,id_image)
  398. for _ in range(3):
  399. result=generate_text(plm_info,id_image,graphic_label)
  400. # result=gen_title("This maxi dress features unparalleled comfort and a unique texture with its <b>tencel blend fabric</b>. The square neckline and smocked bodice create a flattering silhouette, while the layered skirt adds romantic flair. <b>Side pockets and an included scarf scrunchie</b> enhance both style and functionality, elevating its versatility for everyday wear and beyond.")
  401. print(result)
  402. # from tqdm import tqdm
  403. # def image_to_base64(image):
  404. # # 将Image对象转换为BytesIO对象
  405. # image_io = io.BytesIO()
  406. # image.save(image_io, format='PNG')
  407. # image_io.seek(0)
  408. # # 使用base64编码
  409. # image_base64 = base64.b64encode(image_io.read()).decode('utf-8')
  410. # return image_base64
  411. # def create_html_with_base64_images(root, output_html):
  412. # with open(output_html, 'w', encoding='utf-8') as html_file:
  413. # html_file.write('<!DOCTYPE html>\n<html>\n<head>\n<title>Images in Table</title>\n')
  414. # html_file.write('<meta charset="UTF-8">\n') # 添加字符编码声明
  415. # html_file.write('<style>\n')
  416. # html_file.write('table {\nborder-collapse: collapse;\nwidth: 100%;\n}\n')
  417. # html_file.write('table, th, td {\nborder: 1px solid black;\n}\n')
  418. # html_file.write('img {\nmax-width: 100%;\nheight: auto;\ndisplay: block;\nmargin-left: auto;\nmargin-right: auto;\n}\n')
  419. # html_file.write('</style>\n')
  420. # html_file.write('</head>\n<body>\n')
  421. # html_file.write('<table>\n')
  422. # html_file.write('<tr>\n')
  423. # html_file.write('<th>输入的图片</th>\n') # 第一列:索引
  424. # html_file.write('<th>输入的描述</th>\n') # 第二列:标题
  425. # html_file.write('<th>输出的商品详情</th>\n') # 第二列:标题
  426. # html_file.write('<th>输出的商品详情(翻译)</th>\n') # 第三列:图表
  427. # html_file.write('<th>输出的卖点</th>\n') # 第三列:图表
  428. # # for i in range(1, 100): # 添加序号列1到13
  429. # # html_file.write(f'<th>{i}</th>\n')
  430. # html_file.write('</tr>\n')
  431. # for file in tqdm(os.listdir(root)[:100], desc="Processing", unit="iter"):
  432. # if '.ipynb_checkpoints' in file:
  433. # continue
  434. # file_path = os.path.join(root, file)
  435. # with open(file_path, 'r') as f:
  436. # data = json.load(f)
  437. # if data and "商品图像" in data.keys():
  438. # id_image,id_details=data["商品图像"][2:], data["商品细节"]
  439. # else:
  440. # continue
  441. # id_image=os.path.join("/data/data/luosy/project/sku_search",id_image)
  442. # img_base64 = image_to_base64(Image.open(id_image))
  443. # ch,en,kw=generate_text_new(id_details,id_image)
  444. # html_file.write('<tr>\n')
  445. # # html_file.write(f'<td>{index+1}</td>\n') # 添加序号
  446. # # html_file.write('<td>\n')
  447. # # html_file.write(f'<img src="data:image/png;base64,{frame_title_img}" alt="Image">\n')
  448. # # html_file.write('</td>\n')
  449. # html_file.write('<td>\n')
  450. # html_file.write(f'<img src="data:image/png;base64,{img_base64}" alt="Image">\n')
  451. # html_file.write('</td>\n')
  452. # html_file.write(f'<td>{id_details}</td>\n') # 添加序号
  453. # html_file.write(f'<td>{en}</td>\n') # 添加序号
  454. # html_file.write(f'<td>{ch}</td>\n') # 添加序号
  455. # html_file.write(f'<td>{kw}</td>\n') # 添加序号
  456. # # html_file.write('</td>\n')
  457. # # for img in image_data:
  458. # # html_file.write('<td>\n')
  459. # # html_file.write(f'<img src="data:image/jpeg;base64,{img}" alt="Image" style="max-width: 100px; max-height: 100px; margin: 5px;">\n')
  460. # # html_file.write('</td>\n')
  461. # # html_file.write('</td>\n')
  462. # html_file.write('</tr>\n')
  463. # html_file.write('</table>\n')
  464. # html_file.write('</body>\n</html>')
  465. # root='/data/data/luosy/project/sku_search/database/meta'
  466. # create_html_with_base64_images(root, "out——qw_v6.html")
  467. # app.run(host="0.0.0.0",port=2222,debug=True)
  468. # print(gen_title(info= "This sweatshirt is a wardrobe essential with its simple yet stylish design and 3D heart pattern that adds a fun visual pop. <b>The unisex design is perfect for couples</b>, and it pairs effortlessly with jeans, cargo pants, or a pleated skirt. Ideal for school, work, or casual outings, it's comfortable and trendy all day long!"))
  469. # from PIL import Image
  470. # img1=Image.open("/data/data/luosy/project/sku_search/temp_img/企业微信截图_17372766091671.png")
  471. # ch_sen,en_sen,key_point,id_image,id_price, id_color, id_ingredient, id_selling_point, id_details=generate_text("",img1,"""-With elastic waistband
  472. # -With hairband
  473. # -X-line fit
  474. # 1.腰部橡筋 2.袖子橡
  475. # 筋 3.前中绳子可调
  476. # 节大小""")
  477. # print(len(en_sen),end=" ")
  478. # print(ch_sen,en_sen,key_point)
  479. # ###############################
  480. # img2=Image.open("/data/data/luosy/project/sku_search/temp_img/企业微信截图_17389065463149[1](1).png")
  481. # ch_sen,en_sen,key_point,id_image,id_price, id_color, id_ingredient, id_selling_point, id_details=generate_text("",img2,"""-Washable wool
  482. # -Unisex
  483. # -With silver threads
  484. # 1.后中开衩;2.双扣可调节袖袢;3.暗门筒设计,天然果实扣;4.可水洗羊毛含银葱人字纹面料;5.里面左右两侧均有内袋,左侧最外层内袋是手机袋,防丢失""")
  485. # print(len(en_sen),end=" ")
  486. # print(ch_sen,en_sen,key_point)
  487. # ###############################
  488. # img3=Image.open("/data/data/luosy/project/sku_search/temp_img/企业微信截图_17392379937637.png")
  489. # ch_sen,en_sen,key_point,id_image,id_price, id_color, id_ingredient, id_selling_point, id_details=generate_text("",img3,"""-Acetate
  490. # -With pockets
  491. # -Workwear
  492. # 1.描述二醋酸面料:2.扣子为镶钻布包扣;3.半裙后腰包橡筋;4.半裙有
  493. # 侧插袋;5.半裙有侧开隐形拉链,这是两件套套装""")
  494. # print(len(en_sen),end=" ")
  495. # print(ch_sen,en_sen,key_point)