Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

tools.py 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. import json
  2. from datetime import datetime
  3. from typing import Any, Optional, cast
  4. from urllib.parse import urlparse
  5. import sqlalchemy as sa
  6. from deprecated import deprecated
  7. from sqlalchemy import ForeignKey, String, 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, TypeBase
  16. from .engine import db
  17. from .model import Account, App, Tenant
  18. from .types import StringUUID
  19. # system level tool oauth client params (client_id, client_secret, etc.)
  20. class ToolOAuthSystemClient(TypeBase):
  21. __tablename__ = "tool_oauth_system_clients"
  22. __table_args__ = (
  23. sa.PrimaryKeyConstraint("id", name="tool_oauth_system_client_pkey"),
  24. sa.UniqueConstraint("plugin_id", "provider", name="tool_oauth_system_client_plugin_id_provider_idx"),
  25. )
  26. id: Mapped[str] = mapped_column(StringUUID, server_default=sa.text("uuid_generate_v4()"), init=False)
  27. plugin_id: Mapped[str] = mapped_column(String(512), nullable=False)
  28. provider: Mapped[str] = mapped_column(String(255), nullable=False)
  29. # oauth params of the tool provider
  30. encrypted_oauth_params: Mapped[str] = mapped_column(sa.Text, nullable=False)
  31. # tenant level tool oauth client params (client_id, client_secret, etc.)
  32. class ToolOAuthTenantClient(Base):
  33. __tablename__ = "tool_oauth_tenant_clients"
  34. __table_args__ = (
  35. sa.PrimaryKeyConstraint("id", name="tool_oauth_tenant_client_pkey"),
  36. sa.UniqueConstraint("tenant_id", "plugin_id", "provider", name="unique_tool_oauth_tenant_client"),
  37. )
  38. id: Mapped[str] = mapped_column(StringUUID, server_default=sa.text("uuid_generate_v4()"))
  39. # tenant id
  40. tenant_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
  41. plugin_id: Mapped[str] = mapped_column(String(512), nullable=False)
  42. provider: Mapped[str] = mapped_column(String(255), nullable=False)
  43. enabled: Mapped[bool] = mapped_column(sa.Boolean, nullable=False, server_default=sa.text("true"))
  44. # oauth params of the tool provider
  45. encrypted_oauth_params: Mapped[str] = mapped_column(sa.Text, nullable=False)
  46. @property
  47. def oauth_params(self) -> dict[str, Any]:
  48. return cast(dict[str, Any], json.loads(self.encrypted_oauth_params or "{}"))
  49. class BuiltinToolProvider(Base):
  50. """
  51. This table stores the tool provider information for built-in tools for each tenant.
  52. """
  53. __tablename__ = "tool_builtin_providers"
  54. __table_args__ = (
  55. sa.PrimaryKeyConstraint("id", name="tool_builtin_provider_pkey"),
  56. sa.UniqueConstraint("tenant_id", "provider", "name", name="unique_builtin_tool_provider"),
  57. )
  58. # id of the tool provider
  59. id: Mapped[str] = mapped_column(StringUUID, server_default=sa.text("uuid_generate_v4()"))
  60. name: Mapped[str] = mapped_column(
  61. String(256), nullable=False, server_default=sa.text("'API KEY 1'::character varying")
  62. )
  63. # id of the tenant
  64. tenant_id: Mapped[str] = mapped_column(StringUUID, nullable=True)
  65. # who created this tool provider
  66. user_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
  67. # name of the tool provider
  68. provider: Mapped[str] = mapped_column(String(256), nullable=False)
  69. # credential of the tool provider
  70. encrypted_credentials: Mapped[str] = mapped_column(sa.Text, nullable=True)
  71. created_at: Mapped[datetime] = mapped_column(
  72. sa.DateTime, nullable=False, server_default=sa.text("CURRENT_TIMESTAMP(0)")
  73. )
  74. updated_at: Mapped[datetime] = mapped_column(
  75. sa.DateTime, nullable=False, server_default=sa.text("CURRENT_TIMESTAMP(0)")
  76. )
  77. is_default: Mapped[bool] = mapped_column(sa.Boolean, nullable=False, server_default=sa.text("false"))
  78. # credential type, e.g., "api-key", "oauth2"
  79. credential_type: Mapped[str] = mapped_column(
  80. String(32), nullable=False, server_default=sa.text("'api-key'::character varying")
  81. )
  82. expires_at: Mapped[int] = mapped_column(sa.BigInteger, nullable=False, server_default=sa.text("-1"))
  83. @property
  84. def credentials(self) -> dict[str, Any]:
  85. return cast(dict[str, Any], json.loads(self.encrypted_credentials))
  86. class ApiToolProvider(Base):
  87. """
  88. The table stores the api providers.
  89. """
  90. __tablename__ = "tool_api_providers"
  91. __table_args__ = (
  92. sa.PrimaryKeyConstraint("id", name="tool_api_provider_pkey"),
  93. sa.UniqueConstraint("name", "tenant_id", name="unique_api_tool_provider"),
  94. )
  95. id = mapped_column(StringUUID, server_default=sa.text("uuid_generate_v4()"))
  96. # name of the api provider
  97. name = mapped_column(String(255), nullable=False, server_default=sa.text("'API KEY 1'::character varying"))
  98. # icon
  99. icon: Mapped[str] = mapped_column(String(255), nullable=False)
  100. # original schema
  101. schema = mapped_column(sa.Text, nullable=False)
  102. schema_type_str: Mapped[str] = mapped_column(String(40), nullable=False)
  103. # who created this tool
  104. user_id = mapped_column(StringUUID, nullable=False)
  105. # tenant id
  106. tenant_id = mapped_column(StringUUID, nullable=False)
  107. # description of the provider
  108. description = mapped_column(sa.Text, nullable=False)
  109. # json format tools
  110. tools_str = mapped_column(sa.Text, nullable=False)
  111. # json format credentials
  112. credentials_str = mapped_column(sa.Text, nullable=False)
  113. # privacy policy
  114. privacy_policy = mapped_column(String(255), nullable=True)
  115. # custom_disclaimer
  116. custom_disclaimer: Mapped[str] = mapped_column(sa.TEXT, default="")
  117. created_at: Mapped[datetime] = mapped_column(sa.DateTime, nullable=False, server_default=func.current_timestamp())
  118. updated_at: Mapped[datetime] = mapped_column(sa.DateTime, nullable=False, server_default=func.current_timestamp())
  119. @property
  120. def schema_type(self) -> ApiProviderSchemaType:
  121. return ApiProviderSchemaType.value_of(self.schema_type_str)
  122. @property
  123. def tools(self) -> list[ApiToolBundle]:
  124. return [ApiToolBundle(**tool) for tool in json.loads(self.tools_str)]
  125. @property
  126. def credentials(self) -> dict[str, Any]:
  127. return dict[str, Any](json.loads(self.credentials_str))
  128. @property
  129. def user(self) -> Account | None:
  130. if not self.user_id:
  131. return None
  132. return db.session.query(Account).where(Account.id == self.user_id).first()
  133. @property
  134. def tenant(self) -> Tenant | None:
  135. return db.session.query(Tenant).where(Tenant.id == self.tenant_id).first()
  136. class ToolLabelBinding(TypeBase):
  137. """
  138. The table stores the labels for tools.
  139. """
  140. __tablename__ = "tool_label_bindings"
  141. __table_args__ = (
  142. sa.PrimaryKeyConstraint("id", name="tool_label_bind_pkey"),
  143. sa.UniqueConstraint("tool_id", "label_name", name="unique_tool_label_bind"),
  144. )
  145. id: Mapped[str] = mapped_column(StringUUID, server_default=sa.text("uuid_generate_v4()"), init=False)
  146. # tool id
  147. tool_id: Mapped[str] = mapped_column(String(64), nullable=False)
  148. # tool type
  149. tool_type: Mapped[str] = mapped_column(String(40), nullable=False)
  150. # label name
  151. label_name: Mapped[str] = mapped_column(String(40), nullable=False)
  152. class WorkflowToolProvider(Base):
  153. """
  154. The table stores the workflow providers.
  155. """
  156. __tablename__ = "tool_workflow_providers"
  157. __table_args__ = (
  158. sa.PrimaryKeyConstraint("id", name="tool_workflow_provider_pkey"),
  159. sa.UniqueConstraint("name", "tenant_id", name="unique_workflow_tool_provider"),
  160. sa.UniqueConstraint("tenant_id", "app_id", name="unique_workflow_tool_provider_app_id"),
  161. )
  162. id: Mapped[str] = mapped_column(StringUUID, server_default=sa.text("uuid_generate_v4()"))
  163. # name of the workflow provider
  164. name: Mapped[str] = mapped_column(String(255), nullable=False)
  165. # label of the workflow provider
  166. label: Mapped[str] = mapped_column(String(255), nullable=False, server_default="")
  167. # icon
  168. icon: Mapped[str] = mapped_column(String(255), nullable=False)
  169. # app id of the workflow provider
  170. app_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
  171. # version of the workflow provider
  172. version: Mapped[str] = mapped_column(String(255), nullable=False, server_default="")
  173. # who created this tool
  174. user_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
  175. # tenant id
  176. tenant_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
  177. # description of the provider
  178. description: Mapped[str] = mapped_column(sa.Text, nullable=False)
  179. # parameter configuration
  180. parameter_configuration: Mapped[str] = mapped_column(sa.Text, nullable=False, server_default="[]")
  181. # privacy policy
  182. privacy_policy: Mapped[str] = mapped_column(String(255), nullable=True, server_default="")
  183. created_at: Mapped[datetime] = mapped_column(
  184. sa.DateTime, nullable=False, server_default=sa.text("CURRENT_TIMESTAMP(0)")
  185. )
  186. updated_at: Mapped[datetime] = mapped_column(
  187. sa.DateTime, nullable=False, server_default=sa.text("CURRENT_TIMESTAMP(0)")
  188. )
  189. @property
  190. def user(self) -> Account | None:
  191. return db.session.query(Account).where(Account.id == self.user_id).first()
  192. @property
  193. def tenant(self) -> Tenant | None:
  194. return db.session.query(Tenant).where(Tenant.id == self.tenant_id).first()
  195. @property
  196. def parameter_configurations(self) -> list[WorkflowToolParameterConfiguration]:
  197. return [WorkflowToolParameterConfiguration(**config) for config in json.loads(self.parameter_configuration)]
  198. @property
  199. def app(self) -> App | None:
  200. return db.session.query(App).where(App.id == self.app_id).first()
  201. class MCPToolProvider(Base):
  202. """
  203. The table stores the mcp providers.
  204. """
  205. __tablename__ = "tool_mcp_providers"
  206. __table_args__ = (
  207. sa.PrimaryKeyConstraint("id", name="tool_mcp_provider_pkey"),
  208. sa.UniqueConstraint("tenant_id", "server_url_hash", name="unique_mcp_provider_server_url"),
  209. sa.UniqueConstraint("tenant_id", "name", name="unique_mcp_provider_name"),
  210. sa.UniqueConstraint("tenant_id", "server_identifier", name="unique_mcp_provider_server_identifier"),
  211. )
  212. id: Mapped[str] = mapped_column(StringUUID, server_default=sa.text("uuid_generate_v4()"))
  213. # name of the mcp provider
  214. name: Mapped[str] = mapped_column(String(40), nullable=False)
  215. # server identifier of the mcp provider
  216. server_identifier: Mapped[str] = mapped_column(String(64), nullable=False)
  217. # encrypted url of the mcp provider
  218. server_url: Mapped[str] = mapped_column(sa.Text, nullable=False)
  219. # hash of server_url for uniqueness check
  220. server_url_hash: Mapped[str] = mapped_column(String(64), nullable=False)
  221. # icon of the mcp provider
  222. icon: Mapped[str] = mapped_column(String(255), nullable=True)
  223. # tenant id
  224. tenant_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
  225. # who created this tool
  226. user_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
  227. # encrypted credentials
  228. encrypted_credentials: Mapped[str] = mapped_column(sa.Text, nullable=True)
  229. # authed
  230. authed: Mapped[bool] = mapped_column(sa.Boolean, nullable=False, default=False)
  231. # tools
  232. tools: Mapped[str] = mapped_column(sa.Text, nullable=False, default="[]")
  233. created_at: Mapped[datetime] = mapped_column(
  234. sa.DateTime, nullable=False, server_default=sa.text("CURRENT_TIMESTAMP(0)")
  235. )
  236. updated_at: Mapped[datetime] = mapped_column(
  237. sa.DateTime, nullable=False, server_default=sa.text("CURRENT_TIMESTAMP(0)")
  238. )
  239. timeout: Mapped[float] = mapped_column(sa.Float, nullable=False, server_default=sa.text("30"))
  240. sse_read_timeout: Mapped[float] = mapped_column(sa.Float, nullable=False, server_default=sa.text("300"))
  241. # encrypted headers for MCP server requests
  242. encrypted_headers: Mapped[str | None] = mapped_column(sa.Text, nullable=True)
  243. def load_user(self) -> Account | None:
  244. return db.session.query(Account).where(Account.id == self.user_id).first()
  245. @property
  246. def tenant(self) -> Tenant | None:
  247. return db.session.query(Tenant).where(Tenant.id == self.tenant_id).first()
  248. @property
  249. def credentials(self) -> dict[str, Any]:
  250. try:
  251. return cast(dict[str, Any], json.loads(self.encrypted_credentials)) or {}
  252. except Exception:
  253. return {}
  254. @property
  255. def mcp_tools(self) -> list[Tool]:
  256. return [Tool(**tool) for tool in json.loads(self.tools)]
  257. @property
  258. def provider_icon(self) -> dict[str, str] | str:
  259. try:
  260. return cast(dict[str, str], json.loads(self.icon))
  261. except json.JSONDecodeError:
  262. return file_helpers.get_signed_file_url(self.icon)
  263. @property
  264. def decrypted_server_url(self) -> str:
  265. return encrypter.decrypt_token(self.tenant_id, self.server_url)
  266. @property
  267. def decrypted_headers(self) -> dict[str, Any]:
  268. """Get decrypted headers for MCP server requests."""
  269. from core.entities.provider_entities import BasicProviderConfig
  270. from core.helper.provider_cache import NoOpProviderCredentialCache
  271. from core.tools.utils.encryption import create_provider_encrypter
  272. try:
  273. if not self.encrypted_headers:
  274. return {}
  275. headers_data = json.loads(self.encrypted_headers)
  276. # Create dynamic config for all headers as SECRET_INPUT
  277. config = [BasicProviderConfig(type=BasicProviderConfig.Type.SECRET_INPUT, name=key) for key in headers_data]
  278. encrypter_instance, _ = create_provider_encrypter(
  279. tenant_id=self.tenant_id,
  280. config=config,
  281. cache=NoOpProviderCredentialCache(),
  282. )
  283. result = encrypter_instance.decrypt(headers_data)
  284. return result
  285. except Exception:
  286. return {}
  287. @property
  288. def masked_headers(self) -> dict[str, Any]:
  289. """Get masked headers for frontend display."""
  290. from core.entities.provider_entities import BasicProviderConfig
  291. from core.helper.provider_cache import NoOpProviderCredentialCache
  292. from core.tools.utils.encryption import create_provider_encrypter
  293. try:
  294. if not self.encrypted_headers:
  295. return {}
  296. headers_data = json.loads(self.encrypted_headers)
  297. # Create dynamic config for all headers as SECRET_INPUT
  298. config = [BasicProviderConfig(type=BasicProviderConfig.Type.SECRET_INPUT, name=key) for key in headers_data]
  299. encrypter_instance, _ = create_provider_encrypter(
  300. tenant_id=self.tenant_id,
  301. config=config,
  302. cache=NoOpProviderCredentialCache(),
  303. )
  304. # First decrypt, then mask
  305. decrypted_headers = encrypter_instance.decrypt(headers_data)
  306. result = encrypter_instance.mask_tool_credentials(decrypted_headers)
  307. return result
  308. except Exception:
  309. return {}
  310. @property
  311. def masked_server_url(self) -> str:
  312. def mask_url(url: str, mask_char: str = "*") -> str:
  313. """
  314. mask the url to a simple string
  315. """
  316. parsed = urlparse(url)
  317. base_url = f"{parsed.scheme}://{parsed.netloc}"
  318. if parsed.path and parsed.path != "/":
  319. return f"{base_url}/{mask_char * 6}"
  320. else:
  321. return base_url
  322. return mask_url(self.decrypted_server_url)
  323. @property
  324. def decrypted_credentials(self) -> dict[str, Any]:
  325. from core.helper.provider_cache import NoOpProviderCredentialCache
  326. from core.tools.mcp_tool.provider import MCPToolProviderController
  327. from core.tools.utils.encryption import create_provider_encrypter
  328. provider_controller = MCPToolProviderController.from_db(self)
  329. encrypter, _ = create_provider_encrypter(
  330. tenant_id=self.tenant_id,
  331. config=[x.to_basic_provider_config() for x in provider_controller.get_credentials_schema()],
  332. cache=NoOpProviderCredentialCache(),
  333. )
  334. return encrypter.decrypt(self.credentials)
  335. class ToolModelInvoke(Base):
  336. """
  337. store the invoke logs from tool invoke
  338. """
  339. __tablename__ = "tool_model_invokes"
  340. __table_args__ = (sa.PrimaryKeyConstraint("id", name="tool_model_invoke_pkey"),)
  341. id = mapped_column(StringUUID, server_default=sa.text("uuid_generate_v4()"))
  342. # who invoke this tool
  343. user_id = mapped_column(StringUUID, nullable=False)
  344. # tenant id
  345. tenant_id = mapped_column(StringUUID, nullable=False)
  346. # provider
  347. provider: Mapped[str] = mapped_column(String(255), nullable=False)
  348. # type
  349. tool_type = mapped_column(String(40), nullable=False)
  350. # tool name
  351. tool_name = mapped_column(String(128), nullable=False)
  352. # invoke parameters
  353. model_parameters = mapped_column(sa.Text, nullable=False)
  354. # prompt messages
  355. prompt_messages = mapped_column(sa.Text, nullable=False)
  356. # invoke response
  357. model_response = mapped_column(sa.Text, nullable=False)
  358. prompt_tokens: Mapped[int] = mapped_column(sa.Integer, nullable=False, server_default=sa.text("0"))
  359. answer_tokens: Mapped[int] = mapped_column(sa.Integer, nullable=False, server_default=sa.text("0"))
  360. answer_unit_price = mapped_column(sa.Numeric(10, 4), nullable=False)
  361. answer_price_unit = mapped_column(sa.Numeric(10, 7), nullable=False, server_default=sa.text("0.001"))
  362. provider_response_latency = mapped_column(sa.Float, nullable=False, server_default=sa.text("0"))
  363. total_price = mapped_column(sa.Numeric(10, 7))
  364. currency: Mapped[str] = mapped_column(String(255), nullable=False)
  365. created_at = mapped_column(sa.DateTime, nullable=False, server_default=func.current_timestamp())
  366. updated_at = mapped_column(sa.DateTime, nullable=False, server_default=func.current_timestamp())
  367. @deprecated
  368. class ToolConversationVariables(Base):
  369. """
  370. store the conversation variables from tool invoke
  371. """
  372. __tablename__ = "tool_conversation_variables"
  373. __table_args__ = (
  374. sa.PrimaryKeyConstraint("id", name="tool_conversation_variables_pkey"),
  375. # add index for user_id and conversation_id
  376. sa.Index("user_id_idx", "user_id"),
  377. sa.Index("conversation_id_idx", "conversation_id"),
  378. )
  379. id = mapped_column(StringUUID, server_default=sa.text("uuid_generate_v4()"))
  380. # conversation user id
  381. user_id = mapped_column(StringUUID, nullable=False)
  382. # tenant id
  383. tenant_id = mapped_column(StringUUID, nullable=False)
  384. # conversation id
  385. conversation_id = mapped_column(StringUUID, nullable=False)
  386. # variables pool
  387. variables_str = mapped_column(sa.Text, nullable=False)
  388. created_at = mapped_column(sa.DateTime, nullable=False, server_default=func.current_timestamp())
  389. updated_at = mapped_column(sa.DateTime, nullable=False, server_default=func.current_timestamp())
  390. @property
  391. def variables(self):
  392. return json.loads(self.variables_str)
  393. class ToolFile(TypeBase):
  394. """This table stores file metadata generated in workflows,
  395. not only files created by agent.
  396. """
  397. __tablename__ = "tool_files"
  398. __table_args__ = (
  399. sa.PrimaryKeyConstraint("id", name="tool_file_pkey"),
  400. sa.Index("tool_file_conversation_id_idx", "conversation_id"),
  401. )
  402. id: Mapped[str] = mapped_column(StringUUID, server_default=sa.text("uuid_generate_v4()"), init=False)
  403. # conversation user id
  404. user_id: Mapped[str] = mapped_column(StringUUID)
  405. # tenant id
  406. tenant_id: Mapped[str] = mapped_column(StringUUID)
  407. # conversation id
  408. conversation_id: Mapped[Optional[str]] = mapped_column(StringUUID, nullable=True)
  409. # file key
  410. file_key: Mapped[str] = mapped_column(String(255), nullable=False)
  411. # mime type
  412. mimetype: Mapped[str] = mapped_column(String(255), nullable=False)
  413. # original url
  414. original_url: Mapped[Optional[str]] = mapped_column(String(2048), nullable=True, default=None)
  415. # name
  416. name: Mapped[str] = mapped_column(default="")
  417. # size
  418. size: Mapped[int] = mapped_column(default=-1)
  419. @deprecated
  420. class DeprecatedPublishedAppTool(Base):
  421. """
  422. The table stores the apps published as a tool for each person.
  423. """
  424. __tablename__ = "tool_published_apps"
  425. __table_args__ = (
  426. sa.PrimaryKeyConstraint("id", name="published_app_tool_pkey"),
  427. sa.UniqueConstraint("app_id", "user_id", name="unique_published_app_tool"),
  428. )
  429. id = mapped_column(StringUUID, server_default=sa.text("uuid_generate_v4()"))
  430. # id of the app
  431. app_id = mapped_column(StringUUID, ForeignKey("apps.id"), nullable=False)
  432. user_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
  433. # who published this tool
  434. description = mapped_column(sa.Text, nullable=False)
  435. # llm_description of the tool, for LLM
  436. llm_description = mapped_column(sa.Text, nullable=False)
  437. # query description, query will be seem as a parameter of the tool,
  438. # to describe this parameter to llm, we need this field
  439. query_description = mapped_column(sa.Text, nullable=False)
  440. # query name, the name of the query parameter
  441. query_name = mapped_column(String(40), nullable=False)
  442. # name of the tool provider
  443. tool_name = mapped_column(String(40), nullable=False)
  444. # author
  445. author = mapped_column(String(40), nullable=False)
  446. created_at = mapped_column(sa.DateTime, nullable=False, server_default=sa.text("CURRENT_TIMESTAMP(0)"))
  447. updated_at = mapped_column(sa.DateTime, nullable=False, server_default=sa.text("CURRENT_TIMESTAMP(0)"))
  448. @property
  449. def description_i18n(self) -> I18nObject:
  450. return I18nObject(**json.loads(self.description))