tools.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. from fastapi import Depends, FastAPI, HTTPException, status
  2. from datetime import datetime, timedelta
  3. from typing import List, Union, Optional
  4. from fastapi import APIRouter
  5. from pydantic import BaseModel
  6. import json
  7. from apps.webui.models.tools import Tools, ToolForm, ToolModel, ToolResponse
  8. from utils.utils import get_current_user, get_admin_user
  9. from utils.tools import get_tools_specs
  10. from constants import ERROR_MESSAGES
  11. from importlib import util
  12. import os
  13. from config import DATA_DIR
  14. TOOLS_DIR = f"{DATA_DIR}/tools"
  15. os.makedirs(TOOLS_DIR, exist_ok=True)
  16. TOOLS = {}
  17. router = APIRouter()
  18. def load_toolkit_module_from_path(tools_id, tools_path):
  19. spec = util.spec_from_file_location(tools_id, tools_path)
  20. module = util.module_from_spec(spec)
  21. try:
  22. spec.loader.exec_module(module)
  23. print(f"Loaded module: {module.__name__}")
  24. if hasattr(module, "Tools"):
  25. return module.Tools()
  26. else:
  27. raise Exception("No Tools class found")
  28. except Exception as e:
  29. print(f"Error loading module: {tools_id}")
  30. # Move the file to the error folder
  31. os.rename(tools_path, f"{tools_path}.error")
  32. raise e
  33. ############################
  34. # GetToolkits
  35. ############################
  36. @router.get("/", response_model=List[ToolResponse])
  37. async def get_toolkits(user=Depends(get_current_user)):
  38. toolkits = [toolkit for toolkit in Tools.get_tools()]
  39. return toolkits
  40. ############################
  41. # ExportToolKits
  42. ############################
  43. @router.get("/export", response_model=List[ToolModel])
  44. async def get_toolkits(user=Depends(get_admin_user)):
  45. toolkits = [toolkit for toolkit in Tools.get_tools()]
  46. return toolkits
  47. ############################
  48. # CreateNewToolKit
  49. ############################
  50. @router.post("/create", response_model=Optional[ToolResponse])
  51. async def create_new_toolkit(form_data: ToolForm, user=Depends(get_admin_user)):
  52. if not form_data.id.isidentifier():
  53. raise HTTPException(
  54. status_code=status.HTTP_400_BAD_REQUEST,
  55. detail="Only alphanumeric characters and underscores are allowed in the id",
  56. )
  57. form_data.id = form_data.id.lower()
  58. toolkit = Tools.get_tool_by_id(form_data.id)
  59. if toolkit == None:
  60. toolkit_path = os.path.join(TOOLS_DIR, f"{form_data.id}.py")
  61. try:
  62. with open(toolkit_path, "w") as tool_file:
  63. tool_file.write(form_data.content)
  64. toolkit_module = load_toolkit_module_from_path(form_data.id, toolkit_path)
  65. TOOLS[form_data.id] = toolkit_module
  66. specs = get_tools_specs(TOOLS[form_data.id])
  67. toolkit = Tools.insert_new_tool(user.id, form_data, specs)
  68. if toolkit:
  69. return toolkit
  70. else:
  71. raise HTTPException(
  72. status_code=status.HTTP_400_BAD_REQUEST,
  73. detail=ERROR_MESSAGES.FILE_EXISTS,
  74. )
  75. except Exception as e:
  76. raise HTTPException(
  77. status_code=status.HTTP_400_BAD_REQUEST,
  78. detail=ERROR_MESSAGES.DEFAULT(e),
  79. )
  80. else:
  81. raise HTTPException(
  82. status_code=status.HTTP_400_BAD_REQUEST,
  83. detail=ERROR_MESSAGES.ID_TAKEN,
  84. )
  85. ############################
  86. # GetToolkitById
  87. ############################
  88. @router.get("/id/{id}", response_model=Optional[ToolModel])
  89. async def get_toolkit_by_id(id: str, user=Depends(get_admin_user)):
  90. toolkit = Tools.get_tool_by_id(id)
  91. if toolkit:
  92. return toolkit
  93. else:
  94. raise HTTPException(
  95. status_code=status.HTTP_401_UNAUTHORIZED,
  96. detail=ERROR_MESSAGES.NOT_FOUND,
  97. )
  98. ############################
  99. # UpdateToolkitById
  100. ############################
  101. @router.post("/id/{id}/update", response_model=Optional[ToolModel])
  102. async def update_toolkit_by_id(
  103. id: str, form_data: ToolForm, user=Depends(get_admin_user)
  104. ):
  105. toolkit_path = os.path.join(TOOLS_DIR, f"{id}.py")
  106. try:
  107. with open(toolkit_path, "w") as tool_file:
  108. tool_file.write(form_data.content)
  109. toolkit_module = load_toolkit_module_from_path(id, toolkit_path)
  110. TOOLS[id] = toolkit_module
  111. specs = get_tools_specs(TOOLS[id])
  112. updated = {
  113. **form_data.model_dump(exclude={"id"}),
  114. "specs": specs,
  115. }
  116. print(updated)
  117. toolkit = Tools.update_tool_by_id(id, updated)
  118. if toolkit:
  119. return toolkit
  120. else:
  121. raise HTTPException(
  122. status_code=status.HTTP_400_BAD_REQUEST,
  123. detail=ERROR_MESSAGES.DEFAULT("Error updating toolkit"),
  124. )
  125. except Exception as e:
  126. raise HTTPException(
  127. status_code=status.HTTP_400_BAD_REQUEST,
  128. detail=ERROR_MESSAGES.DEFAULT(e),
  129. )
  130. ############################
  131. # DeleteToolkitById
  132. ############################
  133. @router.delete("/id/{id}/delete", response_model=bool)
  134. async def delete_toolkit_by_id(id: str, user=Depends(get_admin_user)):
  135. result = Tools.delete_tool_by_id(id)
  136. return result