tools.py 5.3 KB

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