llm.py 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. import io
  2. import os
  3. import time
  4. import base64
  5. import logging
  6. # 首先导入日志配置模块,确保日志系统在导入其他模块之前就配置好
  7. # 导入logger_config以自动配置日志系统(如果还没有配置)
  8. try:
  9. import logger_config
  10. except ImportError:
  11. pass
  12. # 使用标准logging
  13. # 使用__name__会得到"llm",确保日志能正确输出
  14. logger = logging.getLogger(__name__)
  15. # 确保logger正确配置 - 强制添加handler,确保日志能输出
  16. logger.setLevel(logging.INFO)
  17. # 为llm logger强制添加handler(直接输出,不依赖传播)
  18. # 这样即使gunicorn重置了根logger,llm的logger仍能输出
  19. # 清除已有handler,重新添加(应对gunicorn worker重启的情况)
  20. for h in logger.handlers[:]:
  21. logger.removeHandler(h)
  22. import sys
  23. handler = logging.StreamHandler(sys.stderr)
  24. formatter = logging.Formatter(
  25. '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
  26. datefmt='%Y-%m-%d %H:%M:%S'
  27. )
  28. handler.setFormatter(formatter)
  29. handler.setLevel(logging.INFO)
  30. logger.addHandler(handler)
  31. logger.propagate = False # 不传播,直接使用自己的handler
  32. # 现在导入其他模块
  33. from PIL import Image
  34. import numpy as np
  35. from openai import OpenAI
  36. from conf import *
  37. from tos import HttpMethodType
  38. import requests
  39. from requests.adapters import HTTPAdapter
  40. from urllib3.util.retry import Retry
  41. def image_to_base64(image):
  42. # 将Image对象转换为BytesIO对象
  43. image_io = io.BytesIO()
  44. image.save(image_io, format='JPEG', quality=95)
  45. image_io.seek(0)
  46. # 使用base64编码
  47. image_base64 = base64.b64encode(image_io.read()).decode('utf-8')
  48. return image_base64
  49. def download_image_with_retry(url, max_retries=3, timeout=30):
  50. """下载图片并重试机制"""
  51. session = requests.Session()
  52. retry_strategy = Retry(
  53. total=max_retries,
  54. backoff_factor=1,
  55. status_forcelist=[429, 500, 502, 503, 504],
  56. )
  57. adapter = HTTPAdapter(max_retries=retry_strategy)
  58. session.mount("http://", adapter)
  59. session.mount("https://", adapter)
  60. try:
  61. logger.info(f"正在下载图片: {url}")
  62. response = session.get(url, timeout=timeout)
  63. response.raise_for_status()
  64. logger.info("图片下载成功")
  65. return Image.open(io.BytesIO(response.content))
  66. except Exception as e:
  67. logger.error(f"下载图片失败: {e}")
  68. return None
  69. def image_reader(image):
  70. """图片读取器,输出PIL.Image格式的图片"""
  71. if isinstance(image,str):
  72. if image.startswith("http"):
  73. # 先尝试下载图片到本地处理
  74. out_image = download_image_with_retry(image)
  75. if out_image is None:
  76. # 如果下载失败,抛出异常
  77. raise Exception(f"无法下载图片: {image}")
  78. else:
  79. image_path = image
  80. out_image = Image.open(image_path)
  81. elif isinstance(image,np.ndarray):
  82. out_image = Image.fromarray(image)
  83. else:
  84. out_image = image
  85. out_image=out_image.convert('RGB')
  86. base64_img=image_to_base64(out_image)
  87. # 返回完整的data URI格式
  88. return f"data:image/jpeg;base64,{base64_img}"
  89. def get_lm_text(sys_prompt,user_prompt):
  90. completion = LMConfig.lm_client.chat.completions.create(
  91. messages = [
  92. {"role": "system", "content": sys_prompt},
  93. {"role": "user", "content": user_prompt},
  94. ],
  95. model=LMConfig.model,
  96. )
  97. return completion.choices[0].message.content
  98. ## 多模态的输入
  99. def compress_image(input_path, output_path):
  100. img = Image.open(input_path)
  101. current_size = os.path.getsize(input_path)
  102. # 粗略的估计压缩质量,也可以从常量开始,逐步减小压缩质量,直到文件大小小于目标大小
  103. image_quality = int(float(MMMConfig.target_size / current_size) * 100)
  104. img.save(output_path, optimize=True, quality=int(float(MMMConfig.target_size / current_size) * 100))
  105. # 如果压缩后文件大小仍然大于目标大小,则继续压缩
  106. # 压缩质量递减,直到文件大小小于目标大小
  107. while os.path.getsize(output_path) > MMMConfig.target_size:
  108. img = Image.open(output_path)
  109. image_quality -= 10
  110. if image_quality <= 0:
  111. break
  112. img.save(output_path, optimize=True, quality=image_quality)
  113. return image_quality
  114. def upload_tos(filename, tos_object_key):
  115. tos_client, inner_tos_client = MMMConfig.tos_client, MMMConfig.inner_tos_client
  116. try:
  117. # 将本地文件上传到目标桶中, filename为本地压缩后图片的完整路径
  118. tos_client.put_object_from_file(MMMConfig.tos_bucket_name, tos_object_key, filename)
  119. # 获取上传后预签名的 url
  120. return inner_tos_client.pre_signed_url(HttpMethodType.Http_Method_Get, MMMConfig.tos_bucket_name, tos_object_key)
  121. except Exception as e:
  122. if isinstance(e, tos.exceptions.TosClientError):
  123. # 操作失败,捕获客户端异常,一般情况为非法请求参数或网络异常
  124. logger.error('TOS客户端错误, message:{}, cause: {}'.format(e.message, e.cause))
  125. elif isinstance(e, tos.exceptions.TosServerError):
  126. # 操作失败,捕获服务端异常,可从返回信息中获取详细错误信息
  127. logger.error('TOS服务端错误, code: {}'.format(e.code))
  128. # request id 可定位具体问题,强烈建议日志中保存
  129. logger.error('error with request id: {}'.format(e.request_id))
  130. logger.error('error with message: {}'.format(e.message))
  131. logger.error('error with http code: {}'.format(e.status_code))
  132. else:
  133. logger.error('TOS上传失败,未知错误: {}'.format(e))
  134. raise e
  135. def doubao_MMM_request(pre_signed_url_output, prompt):
  136. client = MMMConfig.client
  137. response = client.chat.completions.create(
  138. model=MMMConfig.model,
  139. messages=[{"role": "user","content": [
  140. {"type": "text", "text": prompt},
  141. {"type": "image_url", "image_url": {"url": pre_signed_url_output.signed_url}}
  142. ],
  143. }],
  144. temperature=0.8,
  145. extra_headers={"x-ark-beta-vision": "true"}
  146. )
  147. result = response.choices[0].message.content
  148. return result
  149. class llm_request:
  150. def __init__(self,api_key,base_url,model) -> None:
  151. self.api_key=api_key
  152. self.base_url=base_url
  153. self.model=model
  154. def llm_mm_request(self,usr_text,img,sys_text="You are a helpful assistant."):
  155. client = OpenAI(
  156. # 若没有配置环境变量,请用百炼API Key将下行替换为:api_key="sk-xxx"
  157. api_key=self.api_key,
  158. base_url=self.base_url
  159. )
  160. completion = client.chat.completions.create(
  161. model=self.model,#
  162. messages=[
  163. {
  164. "role": "system",
  165. "content": [{"type":"text","text": sys_text}]},
  166. {
  167. "role": "user",
  168. "content": [
  169. {
  170. "type": "image_url",
  171. # 需要注意,传入Base64,图像格式(即image/{format})需要与支持的图片列表中的Content Type保持一致。"f"是字符串格式化的方法。
  172. # PNG图像: f"data:image/png;base64,{base64_image}"
  173. # JPEG图像: f"data:image/jpeg;base64,{base64_image}"
  174. # WEBP图像: f"data:image/webp;base64,{base64_image}"
  175. "image_url": {"url": image_reader(img)},
  176. },
  177. {"type": "text", "text": usr_text},
  178. ],
  179. }
  180. ],
  181. temperature=1.5,
  182. top_p=0.85,
  183. presence_penalty=1.5,
  184. frequency_penalty=1.5,
  185. timeout=120.0
  186. )
  187. return completion.choices[0].message.content
  188. def llm_text_request(self,text,sys_text="You are a helpful assistant."):
  189. client = OpenAI(
  190. # 若没有配置环境变量,请用百炼API Key将下行替换为:api_key="sk-xxx"
  191. api_key=self.api_key,
  192. base_url=self.base_url
  193. )
  194. completion = client.chat.completions.create(
  195. model=self.model,#
  196. messages=[
  197. {
  198. "role": "system",
  199. "content": sys_text},
  200. {
  201. "role": "user",
  202. "content": text,
  203. }
  204. ],
  205. temperature=0.9,
  206. timeout=120.0
  207. )
  208. return completion.choices[0].message.content
  209. if __name__=="__main__":
  210. ##ali
  211. ky="sk-04b63960983445f980d85ff185a17876"
  212. baseurl="https://dashscope.aliyuncs.com/compatible-mode/v1"
  213. model="qwen-vl-max-latest"
  214. ##doubao
  215. # ky='817dff39-5586-4f9b-acba-55004167c0b1'
  216. # baseurl="https://ark.cn-beijing.volces.com/api/v3"
  217. # model="doubao-1-5-vision-pro-32k-250115"
  218. llm=llm_request(ky,baseurl,model)
  219. res1=llm.llm_mm_request("描述一下图片中的衣服","/data/data/Mia/product_env_project/gen_sellpoint/企业微信截图_17372766091671.png")
  220. print(res1)
  221. res2=llm.llm_text_request("你好!你是谁")
  222. print(res2)