utils.py 4.3 KB

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