Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. import json
  2. from datetime import datetime
  3. from typing import Any, cast
  4. from urllib.parse import urlparse
  5. import sqlalchemy as sa
  6. from deprecated import deprecated
  7. from sqlalchemy import ForeignKey, func
  8. from sqlalchemy.orm import Mapped, mapped_column
  9. from core.file import helpers as file_helpers
  10. from core.helper import encrypter
  11. from core.mcp.types import Tool
  12. from core.tools.entities.common_entities import I18nObject
  13. from core.tools.entities.tool_bundle import ApiToolBundle
  14. from core.tools.entities.tool_entities import ApiProviderSchemaType, WorkflowToolParameterConfiguration
  15. from models.base import Base
  16. from .engine import db
  17. from .model import Account, App, Tenant
  18. from .types import StringUUID
  19. class BuiltinToolProvider(Base):
  20. """
  21. This table stores the tool provider information for built-in tools for each tenant.
  22. """
  23. __tablename__ = "tool_builtin_providers"
  24. __table_args__ = (
  25. db.PrimaryKeyConstraint("id", name="tool_builtin_provider_pkey"),
  26. # one tenant can only have one tool provider with the same name
  27. db.UniqueConstraint("tenant_id", "provider", name="unique_builtin_tool_provider"),
  28. )
  29. # id of the tool provider
  30. id: Mapped[str] = mapped_column(StringUUID, server_default=db.text("uuid_generate_v4()"))
  31. # id of the tenant
  32. tenant_id: Mapped[str] = mapped_column(StringUUID, nullable=True)
  33. # who created this tool provider
  34. user_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
  35. # name of the tool provider
  36. provider: Mapped[str] = mapped_column(db.String(256), nullable=False)
  37. # credential of the tool provider
  38. encrypted_credentials: Mapped[str] = mapped_column(db.Text, nullable=True)
  39. created_at: Mapped[datetime] = mapped_column(
  40. db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")
  41. )
  42. updated_at: Mapped[datetime] = mapped_column(
  43. db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")
  44. )
  45. @property
  46. def credentials(self) -> dict:
  47. return cast(dict, json.loads(self.encrypted_credentials))
  48. class ApiToolProvider(Base):
  49. """
  50. The table stores the api providers.
  51. """
  52. __tablename__ = "tool_api_providers"
  53. __table_args__ = (
  54. db.PrimaryKeyConstraint("id", name="tool_api_provider_pkey"),
  55. db.UniqueConstraint("name", "tenant_id", name="unique_api_tool_provider"),
  56. )
  57. id = db.Column(StringUUID, server_default=db.text("uuid_generate_v4()"))
  58. # name of the api provider
  59. name = db.Column(db.String(255), nullable=False)
  60. # icon
  61. icon = db.Column(db.String(255), nullable=False)
  62. # original schema
  63. schema = db.Column(db.Text, nullable=False)
  64. schema_type_str: Mapped[str] = db.Column(db.String(40), nullable=False)
  65. # who created this tool
  66. user_id = db.Column(StringUUID, nullable=False)
  67. # tenant id
  68. tenant_id = db.Column(StringUUID, nullable=False)
  69. # description of the provider
  70. description = db.Column(db.Text, nullable=False)
  71. # json format tools
  72. tools_str = db.Column(db.Text, nullable=False)
  73. # json format credentials
  74. credentials_str = db.Column(db.Text, nullable=False)
  75. # privacy policy
  76. privacy_policy = db.Column(db.String(255), nullable=True)
  77. # custom_disclaimer
  78. custom_disclaimer: Mapped[str] = mapped_column(sa.TEXT, default="")
  79. created_at: Mapped[datetime] = mapped_column(db.DateTime, nullable=False, server_default=func.current_timestamp())
  80. updated_at: Mapped[datetime] = mapped_column(db.DateTime, nullable=False, server_default=func.current_timestamp())
  81. @property
  82. def schema_type(self) -> ApiProviderSchemaType:
  83. return ApiProviderSchemaType.value_of(self.schema_type_str)
  84. @property
  85. def tools(self) -> list[ApiToolBundle]:
  86. return [ApiToolBundle(**tool) for tool in json.loads(self.tools_str)]
  87. @property
  88. def credentials(self) -> dict:
  89. return dict(json.loads(self.credentials_str))
  90. @property
  91. def user(self) -> Account | None:
  92. if not self.user_id:
  93. return None
  94. return db.session.query(Account).filter(Account.id == self.user_id).first()
  95. @property
  96. def tenant(self) -> Tenant | None:
  97. return db.session.query(Tenant).filter(Tenant.id == self.tenant_id).first()
  98. class ToolLabelBinding(Base):
  99. """
  100. The table stores the labels for tools.
  101. """
  102. __tablename__ = "tool_label_bindings"
  103. __table_args__ = (
  104. db.PrimaryKeyConstraint("id", name="tool_label_bind_pkey"),
  105. db.UniqueConstraint("tool_id", "label_name", name="unique_tool_label_bind"),
  106. )
  107. id: Mapped[str] = mapped_column(StringUUID, server_default=db.text("uuid_generate_v4()"))
  108. # tool id
  109. tool_id: Mapped[str] = mapped_column(db.String(64), nullable=False)
  110. # tool type
  111. tool_type: Mapped[str] = mapped_column(db.String(40), nullable=False)
  112. # label name
  113. label_name: Mapped[str] = mapped_column(db.String(40), nullable=False)
  114. class WorkflowToolProvider(Base):
  115. """
  116. The table stores the workflow providers.
  117. """
  118. __tablename__ = "tool_workflow_providers"
  119. __table_args__ = (
  120. db.PrimaryKeyConstraint("id", name="tool_workflow_provider_pkey"),
  121. db.UniqueConstraint("name", "tenant_id", name="unique_workflow_tool_provider"),
  122. db.UniqueConstraint("tenant_id", "app_id", name="unique_workflow_tool_provider_app_id"),
  123. )
  124. id: Mapped[str] = mapped_column(StringUUID, server_default=db.text("uuid_generate_v4()"))
  125. # name of the workflow provider
  126. name: Mapped[str] = mapped_column(db.String(255), nullable=False)
  127. # label of the workflow provider
  128. label: Mapped[str] = mapped_column(db.String(255), nullable=False, server_default="")
  129. # icon
  130. icon: Mapped[str] = mapped_column(db.String(255), nullable=False)
  131. # app id of the workflow provider
  132. app_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
  133. # version of the workflow provider
  134. version: Mapped[str] = mapped_column(db.String(255), nullable=False, server_default="")
  135. # who created this tool
  136. user_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
  137. # tenant id
  138. tenant_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
  139. # description of the provider
  140. description: Mapped[str] = mapped_column(db.Text, nullable=False)
  141. # parameter configuration
  142. parameter_configuration: Mapped[str] = mapped_column(db.Text, nullable=False, server_default="[]")
  143. # privacy policy
  144. privacy_policy: Mapped[str] = mapped_column(db.String(255), nullable=True, server_default="")
  145. created_at: Mapped[datetime] = mapped_column(
  146. db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")
  147. )
  148. updated_at: Mapped[datetime] = mapped_column(
  149. db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")
  150. )
  151. @property
  152. def user(self) -> Account | None:
  153. return db.session.query(Account).filter(Account.id == self.user_id).first()
  154. @property
  155. def tenant(self) -> Tenant | None:
  156. return db.session.query(Tenant).filter(Tenant.id == self.tenant_id).first()
  157. @property
  158. def parameter_configurations(self) -> list[WorkflowToolParameterConfiguration]:
  159. return [WorkflowToolParameterConfiguration(**config) for config in json.loads(self.parameter_configuration)]
  160. @property
  161. def app(self) -> App | None:
  162. return db.session.query(App).filter(App.id == self.app_id).first()
  163. class MCPToolProvider(Base):
  164. """
  165. The table stores the mcp providers.
  166. """
  167. __tablename__ = "tool_mcp_providers"
  168. __table_args__ = (
  169. db.PrimaryKeyConstraint("id", name="tool_mcp_provider_pkey"),
  170. db.UniqueConstraint("tenant_id", "server_url_hash", name="unique_mcp_provider_server_url"),
  171. db.UniqueConstraint("tenant_id", "name", name="unique_mcp_provider_name"),
  172. db.UniqueConstraint("tenant_id", "server_identifier", name="unique_mcp_provider_server_identifier"),
  173. )
  174. id: Mapped[str] = mapped_column(StringUUID, server_default=db.text("uuid_generate_v4()"))
  175. # name of the mcp provider
  176. name: Mapped[str] = mapped_column(db.String(40), nullable=False)
  177. # server identifier of the mcp provider
  178. server_identifier: Mapped[str] = mapped_column(db.String(24), nullable=False)
  179. # encrypted url of the mcp provider
  180. server_url: Mapped[str] = mapped_column(db.Text, nullable=False)
  181. # hash of server_url for uniqueness check
  182. server_url_hash: Mapped[str] = mapped_column(db.String(64), nullable=False)
  183. # icon of the mcp provider
  184. icon: Mapped[str] = mapped_column(db.String(255), nullable=True)
  185. # tenant id
  186. tenant_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
  187. # who created this tool
  188. user_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
  189. # encrypted credentials
  190. encrypted_credentials: Mapped[str] = mapped_column(db.Text, nullable=True)
  191. # authed
  192. authed: Mapped[bool] = mapped_column(db.Boolean, nullable=False, default=False)
  193. # tools
  194. tools: Mapped[str] = mapped_column(db.Text, nullable=False, default="[]")
  195. created_at: Mapped[datetime] = mapped_column(
  196. db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")
  197. )
  198. updated_at: Mapped[datetime] = mapped_column(
  199. db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")
  200. )
  201. def load_user(self) -> Account | None:
  202. return db.session.query(Account).filter(Account.id == self.user_id).first()
  203. @property
  204. def tenant(self) -> Tenant | None:
  205. return db.session.query(Tenant).filter(Tenant.id == self.tenant_id).first()
  206. @property
  207. def credentials(self) -> dict:
  208. try:
  209. return cast(dict, json.loads(self.encrypted_credentials)) or {}
  210. except Exception:
  211. return {}
  212. @property
  213. def mcp_tools(self) -> list[Tool]:
  214. return [Tool(**tool) for tool in json.loads(self.tools)]
  215. @property
  216. def provider_icon(self) -> dict[str, str] | str:
  217. try:
  218. return cast(dict[str, str], json.loads(self.icon))
  219. except json.JSONDecodeError:
  220. return file_helpers.get_signed_file_url(self.icon)
  221. @property
  222. def decrypted_server_url(self) -> str:
  223. return cast(str, encrypter.decrypt_token(self.tenant_id, self.server_url))
  224. @property
  225. def masked_server_url(self) -> str:
  226. def mask_url(url: str, mask_char: str = "*") -> str:
  227. """
  228. mask the url to a simple string
  229. """
  230. parsed = urlparse(url)
  231. base_url = f"{parsed.scheme}://{parsed.netloc}"
  232. if parsed.path and parsed.path != "/":
  233. return f"{base_url}/{mask_char * 6}"
  234. else:
  235. return base_url
  236. return mask_url(self.decrypted_server_url)
  237. @property
  238. def decrypted_credentials(self) -> dict:
  239. from core.tools.mcp_tool.provider import MCPToolProviderController
  240. from core.tools.utils.configuration import ProviderConfigEncrypter
  241. provider_controller = MCPToolProviderController._from_db(self)
  242. tool_configuration = ProviderConfigEncrypter(
  243. tenant_id=self.tenant_id,
  244. config=list(provider_controller.get_credentials_schema()),
  245. provider_type=provider_controller.provider_type.value,
  246. provider_identity=provider_controller.provider_id,
  247. )
  248. return tool_configuration.decrypt(self.credentials, use_cache=False)
  249. class ToolModelInvoke(Base):
  250. """
  251. store the invoke logs from tool invoke
  252. """
  253. __tablename__ = "tool_model_invokes"
  254. __table_args__ = (db.PrimaryKeyConstraint("id", name="tool_model_invoke_pkey"),)
  255. id = db.Column(StringUUID, server_default=db.text("uuid_generate_v4()"))
  256. # who invoke this tool
  257. user_id = db.Column(StringUUID, nullable=False)
  258. # tenant id
  259. tenant_id = db.Column(StringUUID, nullable=False)
  260. # provider
  261. provider = db.Column(db.String(255), nullable=False)
  262. # type
  263. tool_type = db.Column(db.String(40), nullable=False)
  264. # tool name
  265. tool_name = db.Column(db.String(40), nullable=False)
  266. # invoke parameters
  267. model_parameters = db.Column(db.Text, nullable=False)
  268. # prompt messages
  269. prompt_messages = db.Column(db.Text, nullable=False)
  270. # invoke response
  271. model_response = db.Column(db.Text, nullable=False)
  272. prompt_tokens = db.Column(db.Integer, nullable=False, server_default=db.text("0"))
  273. answer_tokens = db.Column(db.Integer, nullable=False, server_default=db.text("0"))
  274. answer_unit_price = db.Column(db.Numeric(10, 4), nullable=False)
  275. answer_price_unit = db.Column(db.Numeric(10, 7), nullable=False, server_default=db.text("0.001"))
  276. provider_response_latency = db.Column(db.Float, nullable=False, server_default=db.text("0"))
  277. total_price = db.Column(db.Numeric(10, 7))
  278. currency = db.Column(db.String(255), nullable=False)
  279. created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp())
  280. updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp())
  281. @deprecated
  282. class ToolConversationVariables(Base):
  283. """
  284. store the conversation variables from tool invoke
  285. """
  286. __tablename__ = "tool_conversation_variables"
  287. __table_args__ = (
  288. db.PrimaryKeyConstraint("id", name="tool_conversation_variables_pkey"),
  289. # add index for user_id and conversation_id
  290. db.Index("user_id_idx", "user_id"),
  291. db.Index("conversation_id_idx", "conversation_id"),
  292. )
  293. id = db.Column(StringUUID, server_default=db.text("uuid_generate_v4()"))
  294. # conversation user id
  295. user_id = db.Column(StringUUID, nullable=False)
  296. # tenant id
  297. tenant_id = db.Column(StringUUID, nullable=False)
  298. # conversation id
  299. conversation_id = db.Column(StringUUID, nullable=False)
  300. # variables pool
  301. variables_str = db.Column(db.Text, nullable=False)
  302. created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp())
  303. updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp())
  304. @property
  305. def variables(self) -> Any:
  306. return json.loads(self.variables_str)
  307. class ToolFile(Base):
  308. """This table stores file metadata generated in workflows,
  309. not only files created by agent.
  310. """
  311. __tablename__ = "tool_files"
  312. __table_args__ = (
  313. db.PrimaryKeyConstraint("id", name="tool_file_pkey"),
  314. db.Index("tool_file_conversation_id_idx", "conversation_id"),
  315. )
  316. id: Mapped[str] = mapped_column(StringUUID, server_default=db.text("uuid_generate_v4()"))
  317. # conversation user id
  318. user_id: Mapped[str] = mapped_column(StringUUID)
  319. # tenant id
  320. tenant_id: Mapped[str] = mapped_column(StringUUID)
  321. # conversation id
  322. conversation_id: Mapped[str] = mapped_column(StringUUID, nullable=True)
  323. # file key
  324. file_key: Mapped[str] = mapped_column(db.String(255), nullable=False)
  325. # mime type
  326. mimetype: Mapped[str] = mapped_column(db.String(255), nullable=False)
  327. # original url
  328. original_url: Mapped[str] = mapped_column(db.String(2048), nullable=True)
  329. # name
  330. name: Mapped[str] = mapped_column(default="")
  331. # size
  332. size: Mapped[int] = mapped_column(default=-1)
  333. @deprecated
  334. class DeprecatedPublishedAppTool(Base):
  335. """
  336. The table stores the apps published as a tool for each person.
  337. """
  338. __tablename__ = "tool_published_apps"
  339. __table_args__ = (
  340. db.PrimaryKeyConstraint("id", name="published_app_tool_pkey"),
  341. db.UniqueConstraint("app_id", "user_id", name="unique_published_app_tool"),
  342. )
  343. id = db.Column(StringUUID, server_default=db.text("uuid_generate_v4()"))
  344. # id of the app
  345. app_id = db.Column(StringUUID, ForeignKey("apps.id"), nullable=False)
  346. user_id: Mapped[str] = db.Column(StringUUID, nullable=False)
  347. # who published this tool
  348. description = db.Column(db.Text, nullable=False)
  349. # llm_description of the tool, for LLM
  350. llm_description = db.Column(db.Text, nullable=False)
  351. # query description, query will be seem as a parameter of the tool,
  352. # to describe this parameter to llm, we need this field
  353. query_description = db.Column(db.Text, nullable=False)
  354. # query name, the name of the query parameter
  355. query_name = db.Column(db.String(40), nullable=False)
  356. # name of the tool provider
  357. tool_name = db.Column(db.String(40), nullable=False)
  358. # author
  359. author = db.Column(db.String(40), nullable=False)
  360. created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)"))
  361. updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)"))
  362. @property
  363. def description_i18n(self) -> I18nObject:
  364. return I18nObject(**json.loads(self.description))