plugin.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. import datetime
  2. import enum
  3. import re
  4. from collections.abc import Mapping
  5. from typing import Any, Optional
  6. from pydantic import BaseModel, Field, model_validator
  7. from core.agent.plugin_entities import AgentStrategyProviderEntity
  8. from core.model_runtime.entities.provider_entities import ProviderEntity
  9. from core.plugin.entities.base import BasePluginEntity
  10. from core.plugin.entities.endpoint import EndpointProviderDeclaration
  11. from core.tools.entities.common_entities import I18nObject
  12. from core.tools.entities.tool_entities import ToolProviderEntity
  13. class PluginInstallationSource(enum.StrEnum):
  14. Github = "github"
  15. Marketplace = "marketplace"
  16. Package = "package"
  17. Remote = "remote"
  18. class PluginResourceRequirements(BaseModel):
  19. memory: int
  20. class Permission(BaseModel):
  21. class Tool(BaseModel):
  22. enabled: Optional[bool] = Field(default=False)
  23. class Model(BaseModel):
  24. enabled: Optional[bool] = Field(default=False)
  25. llm: Optional[bool] = Field(default=False)
  26. text_embedding: Optional[bool] = Field(default=False)
  27. rerank: Optional[bool] = Field(default=False)
  28. tts: Optional[bool] = Field(default=False)
  29. speech2text: Optional[bool] = Field(default=False)
  30. moderation: Optional[bool] = Field(default=False)
  31. class Node(BaseModel):
  32. enabled: Optional[bool] = Field(default=False)
  33. class Endpoint(BaseModel):
  34. enabled: Optional[bool] = Field(default=False)
  35. class Storage(BaseModel):
  36. enabled: Optional[bool] = Field(default=False)
  37. size: int = Field(ge=1024, le=1073741824, default=1048576)
  38. tool: Optional[Tool] = Field(default=None)
  39. model: Optional[Model] = Field(default=None)
  40. node: Optional[Node] = Field(default=None)
  41. endpoint: Optional[Endpoint] = Field(default=None)
  42. storage: Storage = Field(default=None)
  43. permission: Optional[Permission] = Field(default=None)
  44. class PluginCategory(enum.StrEnum):
  45. Tool = "tool"
  46. Model = "model"
  47. Extension = "extension"
  48. AgentStrategy = "agent-strategy"
  49. class PluginDeclaration(BaseModel):
  50. class Plugins(BaseModel):
  51. tools: Optional[list[str]] = Field(default_factory=list)
  52. models: Optional[list[str]] = Field(default_factory=list)
  53. endpoints: Optional[list[str]] = Field(default_factory=list)
  54. version: str = Field(..., pattern=r"^\d{1,4}(\.\d{1,4}){1,3}(-\w{1,16})?$")
  55. author: Optional[str] = Field(..., pattern=r"^[a-zA-Z0-9_-]{1,64}$")
  56. name: str = Field(..., pattern=r"^[a-z0-9_-]{1,128}$")
  57. description: I18nObject
  58. icon: str
  59. label: I18nObject
  60. category: PluginCategory
  61. created_at: datetime.datetime
  62. resource: PluginResourceRequirements
  63. plugins: Plugins
  64. tags: list[str] = Field(default_factory=list)
  65. verified: bool = Field(default=False)
  66. tool: Optional[ToolProviderEntity] = None
  67. model: Optional[ProviderEntity] = None
  68. endpoint: Optional[EndpointProviderDeclaration] = None
  69. agent_strategy: Optional[AgentStrategyProviderEntity] = None
  70. @model_validator(mode="before")
  71. @classmethod
  72. def validate_category(cls, values: dict) -> dict:
  73. # auto detect category
  74. if values.get("tool"):
  75. values["category"] = PluginCategory.Tool
  76. elif values.get("model"):
  77. values["category"] = PluginCategory.Model
  78. elif values.get("agent_strategy"):
  79. values["category"] = PluginCategory.AgentStrategy
  80. else:
  81. values["category"] = PluginCategory.Extension
  82. return values
  83. class PluginInstallation(BasePluginEntity):
  84. tenant_id: str
  85. endpoints_setups: int
  86. endpoints_active: int
  87. runtime_type: str
  88. source: PluginInstallationSource
  89. meta: Mapping[str, Any]
  90. plugin_id: str
  91. plugin_unique_identifier: str
  92. version: str
  93. checksum: str
  94. declaration: PluginDeclaration
  95. class PluginEntity(PluginInstallation):
  96. name: str
  97. installation_id: str
  98. version: str
  99. latest_version: Optional[str] = None
  100. latest_unique_identifier: Optional[str] = None
  101. @model_validator(mode="after")
  102. def set_plugin_id(self):
  103. if self.declaration.tool:
  104. self.declaration.tool.plugin_id = self.plugin_id
  105. return self
  106. class GithubPackage(BaseModel):
  107. repo: str
  108. version: str
  109. package: str
  110. class GithubVersion(BaseModel):
  111. repo: str
  112. version: str
  113. class GenericProviderID:
  114. organization: str
  115. plugin_name: str
  116. provider_name: str
  117. is_hardcoded: bool
  118. def to_string(self) -> str:
  119. return str(self)
  120. def __str__(self) -> str:
  121. return f"{self.organization}/{self.plugin_name}/{self.provider_name}"
  122. def __init__(self, value: str, is_hardcoded: bool = False) -> None:
  123. # check if the value is a valid plugin id with format: $organization/$plugin_name/$provider_name
  124. if not re.match(r"^[a-z0-9_-]+\/[a-z0-9_-]+\/[a-z0-9_-]+$", value):
  125. # check if matches [a-z0-9_-]+, if yes, append with langgenius/$value/$value
  126. if re.match(r"^[a-z0-9_-]+$", value):
  127. value = f"langgenius/{value}/{value}"
  128. else:
  129. raise ValueError(f"Invalid plugin id {value}")
  130. self.organization, self.plugin_name, self.provider_name = value.split("/")
  131. self.is_hardcoded = is_hardcoded
  132. @property
  133. def plugin_id(self) -> str:
  134. return f"{self.organization}/{self.plugin_name}"
  135. class ModelProviderID(GenericProviderID):
  136. def __init__(self, value: str, is_hardcoded: bool = False) -> None:
  137. super().__init__(value, is_hardcoded)
  138. if self.organization == "langgenius" and self.provider_name == "google":
  139. self.plugin_name = "gemini"
  140. class ToolProviderID(GenericProviderID):
  141. def __init__(self, value: str, is_hardcoded: bool = False) -> None:
  142. super().__init__(value, is_hardcoded)
  143. if self.organization == "langgenius":
  144. if self.provider_name in ["jina", "siliconflow", "stepfun"]:
  145. self.plugin_name = f"{self.provider_name}_tool"
  146. class PluginDependency(BaseModel):
  147. class Type(enum.StrEnum):
  148. Github = PluginInstallationSource.Github.value
  149. Marketplace = PluginInstallationSource.Marketplace.value
  150. Package = PluginInstallationSource.Package.value
  151. class Github(BaseModel):
  152. repo: str
  153. version: str
  154. package: str
  155. github_plugin_unique_identifier: str
  156. @property
  157. def plugin_unique_identifier(self) -> str:
  158. return self.github_plugin_unique_identifier
  159. class Marketplace(BaseModel):
  160. marketplace_plugin_unique_identifier: str
  161. @property
  162. def plugin_unique_identifier(self) -> str:
  163. return self.marketplace_plugin_unique_identifier
  164. class Package(BaseModel):
  165. plugin_unique_identifier: str
  166. type: Type
  167. value: Github | Marketplace | Package
  168. current_identifier: Optional[str] = None
  169. class MissingPluginDependency(BaseModel):
  170. plugin_unique_identifier: str
  171. current_identifier: Optional[str] = None