您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

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