utils.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. import site
  2. from pathlib import Path
  3. import black
  4. import markdown
  5. from open_webui.config import DATA_DIR, ENABLE_ADMIN_EXPORT
  6. from open_webui.constants import ERROR_MESSAGES
  7. from fastapi import APIRouter, Depends, HTTPException, Response, status
  8. from fpdf import FPDF
  9. from pydantic import BaseModel
  10. from starlette.responses import FileResponse
  11. from open_webui.utils.misc import get_gravatar_url
  12. from open_webui.utils.utils import get_admin_user
  13. router = APIRouter()
  14. @router.get("/gravatar")
  15. async def get_gravatar(
  16. email: str,
  17. ):
  18. return get_gravatar_url(email)
  19. class CodeFormatRequest(BaseModel):
  20. code: str
  21. @router.post("/code/format")
  22. async def format_code(request: CodeFormatRequest):
  23. try:
  24. formatted_code = black.format_str(request.code, mode=black.Mode())
  25. return {"code": formatted_code}
  26. except black.NothingChanged:
  27. return {"code": request.code}
  28. except Exception as e:
  29. raise HTTPException(status_code=400, detail=str(e))
  30. class MarkdownForm(BaseModel):
  31. md: str
  32. @router.post("/markdown")
  33. async def get_html_from_markdown(
  34. form_data: MarkdownForm,
  35. ):
  36. return {"html": markdown.markdown(form_data.md)}
  37. class ChatForm(BaseModel):
  38. title: str
  39. messages: list[dict]
  40. @router.post("/pdf")
  41. async def download_chat_as_pdf(
  42. form_data: ChatForm,
  43. ):
  44. pdf = FPDF()
  45. pdf.add_page()
  46. # When running in docker, workdir is /app/backend, so fonts is in /app/backend/static/fonts
  47. FONTS_DIR = Path("./static/fonts")
  48. # Non Docker Installation
  49. # When running using `pip install` the static directory is in the site packages.
  50. if not FONTS_DIR.exists():
  51. FONTS_DIR = Path(site.getsitepackages()[0]) / "static/fonts"
  52. # When running using `pip install -e .` the static directory is in the site packages.
  53. # This path only works if `open-webui serve` is run from the root of this project.
  54. if not FONTS_DIR.exists():
  55. FONTS_DIR = Path("./backend/static/fonts")
  56. pdf.add_font("NotoSans", "", f"{FONTS_DIR}/NotoSans-Regular.ttf")
  57. pdf.add_font("NotoSans", "b", f"{FONTS_DIR}/NotoSans-Bold.ttf")
  58. pdf.add_font("NotoSans", "i", f"{FONTS_DIR}/NotoSans-Italic.ttf")
  59. pdf.add_font("NotoSansKR", "", f"{FONTS_DIR}/NotoSansKR-Regular.ttf")
  60. pdf.add_font("NotoSansJP", "", f"{FONTS_DIR}/NotoSansJP-Regular.ttf")
  61. pdf.add_font("NotoSansSC", "", f"{FONTS_DIR}/NotoSansSC-Regular.ttf")
  62. pdf.set_font("NotoSans", size=12)
  63. pdf.set_fallback_fonts(["NotoSansKR", "NotoSansJP", "NotoSansSC"])
  64. pdf.set_auto_page_break(auto=True, margin=15)
  65. # Adjust the effective page width for multi_cell
  66. effective_page_width = (
  67. pdf.w - 2 * pdf.l_margin - 10
  68. ) # Subtracted an additional 10 for extra padding
  69. # Add chat messages
  70. for message in form_data.messages:
  71. role = message["role"]
  72. content = message["content"]
  73. pdf.set_font("NotoSans", "B", size=14) # Bold for the role
  74. pdf.multi_cell(effective_page_width, 10, f"{role.upper()}", 0, "L")
  75. pdf.ln(1) # Extra space between messages
  76. pdf.set_font("NotoSans", size=10) # Regular for content
  77. pdf.multi_cell(effective_page_width, 6, content, 0, "L")
  78. pdf.ln(1.5) # Extra space between messages
  79. # Save the pdf with name .pdf
  80. pdf_bytes = pdf.output()
  81. return Response(
  82. content=bytes(pdf_bytes),
  83. media_type="application/pdf",
  84. headers={"Content-Disposition": "attachment;filename=chat.pdf"},
  85. )
  86. @router.get("/db/download")
  87. async def download_db(user=Depends(get_admin_user)):
  88. if not ENABLE_ADMIN_EXPORT:
  89. raise HTTPException(
  90. status_code=status.HTTP_401_UNAUTHORIZED,
  91. detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
  92. )
  93. from open_webui.apps.webui.internal.db import engine
  94. if engine.name != "sqlite":
  95. raise HTTPException(
  96. status_code=status.HTTP_400_BAD_REQUEST,
  97. detail=ERROR_MESSAGES.DB_NOT_SQLITE,
  98. )
  99. return FileResponse(
  100. engine.url.database,
  101. media_type="application/octet-stream",
  102. filename="webui.db",
  103. )
  104. @router.get("/litellm/config")
  105. async def download_litellm_config_yaml(user=Depends(get_admin_user)):
  106. return FileResponse(
  107. f"{DATA_DIR}/litellm/config.yaml",
  108. media_type="application/octet-stream",
  109. filename="config.yaml",
  110. )