images.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618
  1. import asyncio
  2. import base64
  3. import io
  4. import json
  5. import logging
  6. import mimetypes
  7. import re
  8. import uuid
  9. from pathlib import Path
  10. from typing import Optional
  11. import requests
  12. from fastapi import APIRouter, Depends, HTTPException, Request, UploadFile
  13. from open_webui.config import CACHE_DIR
  14. from open_webui.constants import ERROR_MESSAGES
  15. from open_webui.env import ENABLE_FORWARD_USER_INFO_HEADERS, SRC_LOG_LEVELS
  16. from open_webui.routers.files import upload_file
  17. from open_webui.utils.auth import get_admin_user, get_verified_user
  18. from open_webui.utils.images.comfyui import (
  19. ComfyUIGenerateImageForm,
  20. ComfyUIWorkflow,
  21. comfyui_generate_image,
  22. )
  23. from pydantic import BaseModel
  24. log = logging.getLogger(__name__)
  25. log.setLevel(SRC_LOG_LEVELS["IMAGES"])
  26. IMAGE_CACHE_DIR = Path(CACHE_DIR).joinpath("./image/generations/")
  27. IMAGE_CACHE_DIR.mkdir(parents=True, exist_ok=True)
  28. router = APIRouter()
  29. @router.get("/config")
  30. async def get_config(request: Request, user=Depends(get_admin_user)):
  31. return {
  32. "enabled": request.app.state.config.ENABLE_IMAGE_GENERATION,
  33. "engine": request.app.state.config.IMAGE_GENERATION_ENGINE,
  34. "prompt_generation": request.app.state.config.ENABLE_IMAGE_PROMPT_GENERATION,
  35. "openai": {
  36. "OPENAI_API_BASE_URL": request.app.state.config.IMAGES_OPENAI_API_BASE_URL,
  37. "OPENAI_API_KEY": request.app.state.config.IMAGES_OPENAI_API_KEY,
  38. },
  39. "automatic1111": {
  40. "AUTOMATIC1111_BASE_URL": request.app.state.config.AUTOMATIC1111_BASE_URL,
  41. "AUTOMATIC1111_API_AUTH": request.app.state.config.AUTOMATIC1111_API_AUTH,
  42. "AUTOMATIC1111_CFG_SCALE": request.app.state.config.AUTOMATIC1111_CFG_SCALE,
  43. "AUTOMATIC1111_SAMPLER": request.app.state.config.AUTOMATIC1111_SAMPLER,
  44. "AUTOMATIC1111_SCHEDULER": request.app.state.config.AUTOMATIC1111_SCHEDULER,
  45. },
  46. "comfyui": {
  47. "COMFYUI_BASE_URL": request.app.state.config.COMFYUI_BASE_URL,
  48. "COMFYUI_API_KEY": request.app.state.config.COMFYUI_API_KEY,
  49. "COMFYUI_WORKFLOW": request.app.state.config.COMFYUI_WORKFLOW,
  50. "COMFYUI_WORKFLOW_NODES": request.app.state.config.COMFYUI_WORKFLOW_NODES,
  51. },
  52. }
  53. class OpenAIConfigForm(BaseModel):
  54. OPENAI_API_BASE_URL: str
  55. OPENAI_API_KEY: str
  56. class Automatic1111ConfigForm(BaseModel):
  57. AUTOMATIC1111_BASE_URL: str
  58. AUTOMATIC1111_API_AUTH: str
  59. AUTOMATIC1111_CFG_SCALE: Optional[str | float | int]
  60. AUTOMATIC1111_SAMPLER: Optional[str]
  61. AUTOMATIC1111_SCHEDULER: Optional[str]
  62. class ComfyUIConfigForm(BaseModel):
  63. COMFYUI_BASE_URL: str
  64. COMFYUI_API_KEY: str
  65. COMFYUI_WORKFLOW: str
  66. COMFYUI_WORKFLOW_NODES: list[dict]
  67. class ConfigForm(BaseModel):
  68. enabled: bool
  69. engine: str
  70. prompt_generation: bool
  71. openai: OpenAIConfigForm
  72. automatic1111: Automatic1111ConfigForm
  73. comfyui: ComfyUIConfigForm
  74. @router.post("/config/update")
  75. async def update_config(
  76. request: Request, form_data: ConfigForm, user=Depends(get_admin_user)
  77. ):
  78. request.app.state.config.IMAGE_GENERATION_ENGINE = form_data.engine
  79. request.app.state.config.ENABLE_IMAGE_GENERATION = form_data.enabled
  80. request.app.state.config.ENABLE_IMAGE_PROMPT_GENERATION = (
  81. form_data.prompt_generation
  82. )
  83. request.app.state.config.IMAGES_OPENAI_API_BASE_URL = (
  84. form_data.openai.OPENAI_API_BASE_URL
  85. )
  86. request.app.state.config.IMAGES_OPENAI_API_KEY = form_data.openai.OPENAI_API_KEY
  87. request.app.state.config.AUTOMATIC1111_BASE_URL = (
  88. form_data.automatic1111.AUTOMATIC1111_BASE_URL
  89. )
  90. request.app.state.config.AUTOMATIC1111_API_AUTH = (
  91. form_data.automatic1111.AUTOMATIC1111_API_AUTH
  92. )
  93. request.app.state.config.AUTOMATIC1111_CFG_SCALE = (
  94. float(form_data.automatic1111.AUTOMATIC1111_CFG_SCALE)
  95. if form_data.automatic1111.AUTOMATIC1111_CFG_SCALE
  96. else None
  97. )
  98. request.app.state.config.AUTOMATIC1111_SAMPLER = (
  99. form_data.automatic1111.AUTOMATIC1111_SAMPLER
  100. if form_data.automatic1111.AUTOMATIC1111_SAMPLER
  101. else None
  102. )
  103. request.app.state.config.AUTOMATIC1111_SCHEDULER = (
  104. form_data.automatic1111.AUTOMATIC1111_SCHEDULER
  105. if form_data.automatic1111.AUTOMATIC1111_SCHEDULER
  106. else None
  107. )
  108. request.app.state.config.COMFYUI_BASE_URL = (
  109. form_data.comfyui.COMFYUI_BASE_URL.strip("/")
  110. )
  111. request.app.state.config.COMFYUI_WORKFLOW = form_data.comfyui.COMFYUI_WORKFLOW
  112. request.app.state.config.COMFYUI_WORKFLOW_NODES = (
  113. form_data.comfyui.COMFYUI_WORKFLOW_NODES
  114. )
  115. return {
  116. "enabled": request.app.state.config.ENABLE_IMAGE_GENERATION,
  117. "engine": request.app.state.config.IMAGE_GENERATION_ENGINE,
  118. "prompt_generation": request.app.state.config.ENABLE_IMAGE_PROMPT_GENERATION,
  119. "openai": {
  120. "OPENAI_API_BASE_URL": request.app.state.config.IMAGES_OPENAI_API_BASE_URL,
  121. "OPENAI_API_KEY": request.app.state.config.IMAGES_OPENAI_API_KEY,
  122. },
  123. "automatic1111": {
  124. "AUTOMATIC1111_BASE_URL": request.app.state.config.AUTOMATIC1111_BASE_URL,
  125. "AUTOMATIC1111_API_AUTH": request.app.state.config.AUTOMATIC1111_API_AUTH,
  126. "AUTOMATIC1111_CFG_SCALE": request.app.state.config.AUTOMATIC1111_CFG_SCALE,
  127. "AUTOMATIC1111_SAMPLER": request.app.state.config.AUTOMATIC1111_SAMPLER,
  128. "AUTOMATIC1111_SCHEDULER": request.app.state.config.AUTOMATIC1111_SCHEDULER,
  129. },
  130. "comfyui": {
  131. "COMFYUI_BASE_URL": request.app.state.config.COMFYUI_BASE_URL,
  132. "COMFYUI_API_KEY": request.app.state.config.COMFYUI_API_KEY,
  133. "COMFYUI_WORKFLOW": request.app.state.config.COMFYUI_WORKFLOW,
  134. "COMFYUI_WORKFLOW_NODES": request.app.state.config.COMFYUI_WORKFLOW_NODES,
  135. },
  136. }
  137. def get_automatic1111_api_auth(request: Request):
  138. if request.app.state.config.AUTOMATIC1111_API_AUTH is None:
  139. return ""
  140. else:
  141. auth1111_byte_string = request.app.state.config.AUTOMATIC1111_API_AUTH.encode(
  142. "utf-8"
  143. )
  144. auth1111_base64_encoded_bytes = base64.b64encode(auth1111_byte_string)
  145. auth1111_base64_encoded_string = auth1111_base64_encoded_bytes.decode("utf-8")
  146. return f"Basic {auth1111_base64_encoded_string}"
  147. @router.get("/config/url/verify")
  148. async def verify_url(request: Request, user=Depends(get_admin_user)):
  149. if request.app.state.config.IMAGE_GENERATION_ENGINE == "automatic1111":
  150. try:
  151. r = requests.get(
  152. url=f"{request.app.state.config.AUTOMATIC1111_BASE_URL}/sdapi/v1/options",
  153. headers={"authorization": get_automatic1111_api_auth(request)},
  154. )
  155. r.raise_for_status()
  156. return True
  157. except Exception:
  158. request.app.state.config.ENABLE_IMAGE_GENERATION = False
  159. raise HTTPException(status_code=400, detail=ERROR_MESSAGES.INVALID_URL)
  160. elif request.app.state.config.IMAGE_GENERATION_ENGINE == "comfyui":
  161. try:
  162. r = requests.get(
  163. url=f"{request.app.state.config.COMFYUI_BASE_URL}/object_info"
  164. )
  165. r.raise_for_status()
  166. return True
  167. except Exception:
  168. request.app.state.config.ENABLE_IMAGE_GENERATION = False
  169. raise HTTPException(status_code=400, detail=ERROR_MESSAGES.INVALID_URL)
  170. else:
  171. return True
  172. def set_image_model(request: Request, model: str):
  173. log.info(f"Setting image model to {model}")
  174. request.app.state.config.IMAGE_GENERATION_MODEL = model
  175. if request.app.state.config.IMAGE_GENERATION_ENGINE in ["", "automatic1111"]:
  176. api_auth = get_automatic1111_api_auth(request)
  177. r = requests.get(
  178. url=f"{request.app.state.config.AUTOMATIC1111_BASE_URL}/sdapi/v1/options",
  179. headers={"authorization": api_auth},
  180. )
  181. options = r.json()
  182. if model != options["sd_model_checkpoint"]:
  183. options["sd_model_checkpoint"] = model
  184. r = requests.post(
  185. url=f"{request.app.state.config.AUTOMATIC1111_BASE_URL}/sdapi/v1/options",
  186. json=options,
  187. headers={"authorization": api_auth},
  188. )
  189. return request.app.state.config.IMAGE_GENERATION_MODEL
  190. def get_image_model(request):
  191. if request.app.state.config.IMAGE_GENERATION_ENGINE == "openai":
  192. return (
  193. request.app.state.config.IMAGE_GENERATION_MODEL
  194. if request.app.state.config.IMAGE_GENERATION_MODEL
  195. else "dall-e-2"
  196. )
  197. elif request.app.state.config.IMAGE_GENERATION_ENGINE == "comfyui":
  198. return (
  199. request.app.state.config.IMAGE_GENERATION_MODEL
  200. if request.app.state.config.IMAGE_GENERATION_MODEL
  201. else ""
  202. )
  203. elif (
  204. request.app.state.config.IMAGE_GENERATION_ENGINE == "automatic1111"
  205. or request.app.state.config.IMAGE_GENERATION_ENGINE == ""
  206. ):
  207. try:
  208. r = requests.get(
  209. url=f"{request.app.state.config.AUTOMATIC1111_BASE_URL}/sdapi/v1/options",
  210. headers={"authorization": get_automatic1111_api_auth(request)},
  211. )
  212. options = r.json()
  213. return options["sd_model_checkpoint"]
  214. except Exception as e:
  215. request.app.state.config.ENABLE_IMAGE_GENERATION = False
  216. raise HTTPException(status_code=400, detail=ERROR_MESSAGES.DEFAULT(e))
  217. class ImageConfigForm(BaseModel):
  218. MODEL: str
  219. IMAGE_SIZE: str
  220. IMAGE_STEPS: int
  221. @router.get("/image/config")
  222. async def get_image_config(request: Request, user=Depends(get_admin_user)):
  223. return {
  224. "MODEL": request.app.state.config.IMAGE_GENERATION_MODEL,
  225. "IMAGE_SIZE": request.app.state.config.IMAGE_SIZE,
  226. "IMAGE_STEPS": request.app.state.config.IMAGE_STEPS,
  227. }
  228. @router.post("/image/config/update")
  229. async def update_image_config(
  230. request: Request, form_data: ImageConfigForm, user=Depends(get_admin_user)
  231. ):
  232. set_image_model(request, form_data.MODEL)
  233. pattern = r"^\d+x\d+$"
  234. if re.match(pattern, form_data.IMAGE_SIZE):
  235. request.app.state.config.IMAGE_SIZE = form_data.IMAGE_SIZE
  236. else:
  237. raise HTTPException(
  238. status_code=400,
  239. detail=ERROR_MESSAGES.INCORRECT_FORMAT(" (e.g., 512x512)."),
  240. )
  241. if form_data.IMAGE_STEPS >= 0:
  242. request.app.state.config.IMAGE_STEPS = form_data.IMAGE_STEPS
  243. else:
  244. raise HTTPException(
  245. status_code=400,
  246. detail=ERROR_MESSAGES.INCORRECT_FORMAT(" (e.g., 50)."),
  247. )
  248. return {
  249. "MODEL": request.app.state.config.IMAGE_GENERATION_MODEL,
  250. "IMAGE_SIZE": request.app.state.config.IMAGE_SIZE,
  251. "IMAGE_STEPS": request.app.state.config.IMAGE_STEPS,
  252. }
  253. @router.get("/models")
  254. def get_models(request: Request, user=Depends(get_verified_user)):
  255. try:
  256. if request.app.state.config.IMAGE_GENERATION_ENGINE == "openai":
  257. return [
  258. {"id": "dall-e-2", "name": "DALL·E 2"},
  259. {"id": "dall-e-3", "name": "DALL·E 3"},
  260. ]
  261. elif request.app.state.config.IMAGE_GENERATION_ENGINE == "comfyui":
  262. # TODO - get models from comfyui
  263. headers = {
  264. "Authorization": f"Bearer {request.app.state.config.COMFYUI_API_KEY}"
  265. }
  266. r = requests.get(
  267. url=f"{request.app.state.config.COMFYUI_BASE_URL}/object_info",
  268. headers=headers,
  269. )
  270. info = r.json()
  271. workflow = json.loads(request.app.state.config.COMFYUI_WORKFLOW)
  272. model_node_id = None
  273. for node in request.app.state.config.COMFYUI_WORKFLOW_NODES:
  274. if node["type"] == "model":
  275. if node["node_ids"]:
  276. model_node_id = node["node_ids"][0]
  277. break
  278. if model_node_id:
  279. model_list_key = None
  280. print(workflow[model_node_id]["class_type"])
  281. for key in info[workflow[model_node_id]["class_type"]]["input"][
  282. "required"
  283. ]:
  284. if "_name" in key:
  285. model_list_key = key
  286. break
  287. if model_list_key:
  288. return list(
  289. map(
  290. lambda model: {"id": model, "name": model},
  291. info[workflow[model_node_id]["class_type"]]["input"][
  292. "required"
  293. ][model_list_key][0],
  294. )
  295. )
  296. else:
  297. return list(
  298. map(
  299. lambda model: {"id": model, "name": model},
  300. info["CheckpointLoaderSimple"]["input"]["required"][
  301. "ckpt_name"
  302. ][0],
  303. )
  304. )
  305. elif (
  306. request.app.state.config.IMAGE_GENERATION_ENGINE == "automatic1111"
  307. or request.app.state.config.IMAGE_GENERATION_ENGINE == ""
  308. ):
  309. r = requests.get(
  310. url=f"{request.app.state.config.AUTOMATIC1111_BASE_URL}/sdapi/v1/sd-models",
  311. headers={"authorization": get_automatic1111_api_auth(request)},
  312. )
  313. models = r.json()
  314. return list(
  315. map(
  316. lambda model: {"id": model["title"], "name": model["model_name"]},
  317. models,
  318. )
  319. )
  320. except Exception as e:
  321. request.app.state.config.ENABLE_IMAGE_GENERATION = False
  322. raise HTTPException(status_code=400, detail=ERROR_MESSAGES.DEFAULT(e))
  323. class GenerateImageForm(BaseModel):
  324. model: Optional[str] = None
  325. prompt: str
  326. size: Optional[str] = None
  327. n: int = 1
  328. negative_prompt: Optional[str] = None
  329. def load_b64_image_data(b64_str):
  330. try:
  331. if "," in b64_str:
  332. header, encoded = b64_str.split(",", 1)
  333. mime_type = header.split(";")[0]
  334. img_data = base64.b64decode(encoded)
  335. else:
  336. mime_type = "image/png"
  337. img_data = base64.b64decode(b64_str)
  338. return img_data, mime_type
  339. except Exception as e:
  340. log.exception(f"Error loading image data: {e}")
  341. return None
  342. def load_url_image_data(url, headers=None):
  343. try:
  344. if headers:
  345. r = requests.get(url, headers=headers)
  346. else:
  347. r = requests.get(url)
  348. r.raise_for_status()
  349. if r.headers["content-type"].split("/")[0] == "image":
  350. mime_type = r.headers["content-type"]
  351. return r.content, mime_type
  352. else:
  353. log.error("Url does not point to an image.")
  354. return None
  355. except Exception as e:
  356. log.exception(f"Error saving image: {e}")
  357. return None
  358. @router.post("/generations")
  359. async def image_generations(
  360. request: Request,
  361. form_data: GenerateImageForm,
  362. user=Depends(get_verified_user),
  363. ):
  364. width, height = tuple(map(int, request.app.state.config.IMAGE_SIZE.split("x")))
  365. r = None
  366. try:
  367. if request.app.state.config.IMAGE_GENERATION_ENGINE == "openai":
  368. headers = {}
  369. headers["Authorization"] = (
  370. f"Bearer {request.app.state.config.IMAGES_OPENAI_API_KEY}"
  371. )
  372. headers["Content-Type"] = "application/json"
  373. if ENABLE_FORWARD_USER_INFO_HEADERS:
  374. headers["X-OpenWebUI-User-Name"] = user.name
  375. headers["X-OpenWebUI-User-Id"] = user.id
  376. headers["X-OpenWebUI-User-Email"] = user.email
  377. headers["X-OpenWebUI-User-Role"] = user.role
  378. data = {
  379. "model": (
  380. request.app.state.config.IMAGE_GENERATION_MODEL
  381. if request.app.state.config.IMAGE_GENERATION_MODEL != ""
  382. else "dall-e-2"
  383. ),
  384. "prompt": form_data.prompt,
  385. "n": form_data.n,
  386. "size": (
  387. form_data.size
  388. if form_data.size
  389. else request.app.state.config.IMAGE_SIZE
  390. ),
  391. "response_format": "b64_json",
  392. }
  393. # Use asyncio.to_thread for the requests.post call
  394. r = await asyncio.to_thread(
  395. requests.post,
  396. url=f"{request.app.state.config.IMAGES_OPENAI_API_BASE_URL}/images/generations",
  397. json=data,
  398. headers=headers,
  399. )
  400. r.raise_for_status()
  401. res = r.json()
  402. images = []
  403. for image in res["data"]:
  404. image_data, content_type = load_b64_image_data(image["b64_json"])
  405. file = UploadFile(
  406. file=io.BytesIO(image_data),
  407. filename="image", # will be converted to a unique ID on upload_file
  408. headers={
  409. "content-type": content_type,
  410. },
  411. )
  412. file_item = upload_file(request, file, user)
  413. url = request.app.url_path_for(
  414. "get_file_content_by_id", id=file_item.id
  415. )
  416. images.append({"url": url})
  417. file_body_path = IMAGE_CACHE_DIR.joinpath(f"{file_item.id}.json")
  418. with open(file_body_path, "w") as f:
  419. json.dump(data, f)
  420. return images
  421. elif request.app.state.config.IMAGE_GENERATION_ENGINE == "comfyui":
  422. data = {
  423. "prompt": form_data.prompt,
  424. "width": width,
  425. "height": height,
  426. "n": form_data.n,
  427. }
  428. if request.app.state.config.IMAGE_STEPS is not None:
  429. data["steps"] = request.app.state.config.IMAGE_STEPS
  430. if form_data.negative_prompt is not None:
  431. data["negative_prompt"] = form_data.negative_prompt
  432. form_data = ComfyUIGenerateImageForm(
  433. **{
  434. "workflow": ComfyUIWorkflow(
  435. **{
  436. "workflow": request.app.state.config.COMFYUI_WORKFLOW,
  437. "nodes": request.app.state.config.COMFYUI_WORKFLOW_NODES,
  438. }
  439. ),
  440. **data,
  441. }
  442. )
  443. res = await comfyui_generate_image(
  444. request.app.state.config.IMAGE_GENERATION_MODEL,
  445. form_data,
  446. user.id,
  447. request.app.state.config.COMFYUI_BASE_URL,
  448. request.app.state.config.COMFYUI_API_KEY,
  449. )
  450. log.debug(f"res: {res}")
  451. images = []
  452. for image in res["data"]:
  453. headers = None
  454. if request.app.state.config.COMFYUI_API_KEY:
  455. headers = {
  456. "Authorization": f"Bearer {request.app.state.config.COMFYUI_API_KEY}"
  457. }
  458. image_data, content_type = load_url_image_data(image["url"], headers)
  459. file = UploadFile(
  460. file=io.BytesIO(image_data),
  461. filename="image", # will be converted to a unique ID on upload_file
  462. headers={
  463. "content-type": content_type,
  464. },
  465. )
  466. file_item = upload_file(request, file, user)
  467. url = request.app.url_path_for(
  468. "get_file_content_by_id", id=file_item.id
  469. )
  470. images.append({"url": url})
  471. file_body_path = IMAGE_CACHE_DIR.joinpath(f"{file_item.id}.json")
  472. with open(file_body_path, "w") as f:
  473. json.dump(form_data.model_dump(exclude_none=True), f)
  474. log.debug(f"images: {images}")
  475. return images
  476. elif (
  477. request.app.state.config.IMAGE_GENERATION_ENGINE == "automatic1111"
  478. or request.app.state.config.IMAGE_GENERATION_ENGINE == ""
  479. ):
  480. if form_data.model:
  481. set_image_model(form_data.model)
  482. data = {
  483. "prompt": form_data.prompt,
  484. "batch_size": form_data.n,
  485. "width": width,
  486. "height": height,
  487. }
  488. if request.app.state.config.IMAGE_STEPS is not None:
  489. data["steps"] = request.app.state.config.IMAGE_STEPS
  490. if form_data.negative_prompt is not None:
  491. data["negative_prompt"] = form_data.negative_prompt
  492. if request.app.state.config.AUTOMATIC1111_CFG_SCALE:
  493. data["cfg_scale"] = request.app.state.config.AUTOMATIC1111_CFG_SCALE
  494. if request.app.state.config.AUTOMATIC1111_SAMPLER:
  495. data["sampler_name"] = request.app.state.config.AUTOMATIC1111_SAMPLER
  496. if request.app.state.config.AUTOMATIC1111_SCHEDULER:
  497. data["scheduler"] = request.app.state.config.AUTOMATIC1111_SCHEDULER
  498. # Use asyncio.to_thread for the requests.post call
  499. r = await asyncio.to_thread(
  500. requests.post,
  501. url=f"{request.app.state.config.AUTOMATIC1111_BASE_URL}/sdapi/v1/txt2img",
  502. json=data,
  503. headers={"authorization": get_automatic1111_api_auth(request)},
  504. )
  505. res = r.json()
  506. log.debug(f"res: {res}")
  507. images = []
  508. for image in res["images"]:
  509. image_data, content_type = load_b64_image_data(image)
  510. file = UploadFile(
  511. file=io.BytesIO(image_data),
  512. filename="image", # will be converted to a unique ID on upload_file
  513. headers={
  514. "content-type": content_type,
  515. },
  516. )
  517. file_item = upload_file(request, file, user)
  518. url = request.app.url_path_for(
  519. "get_file_content_by_id", id=file_item.id
  520. )
  521. images.append({"url": url})
  522. file_body_path = IMAGE_CACHE_DIR.joinpath(f"{file_item.id}.json")
  523. with open(file_body_path, "w") as f:
  524. json.dump({**data, "info": res["info"]}, f)
  525. return images
  526. except Exception as e:
  527. error = e
  528. if r != None:
  529. data = r.json()
  530. if "error" in data:
  531. error = data["error"]["message"]
  532. raise HTTPException(status_code=400, detail=ERROR_MESSAGES.DEFAULT(error))