diff --git a/api/controllers/console/workspace/plugin.py b/api/controllers/console/workspace/plugin.py index ebe706d65d..e73ac6e1ca 100644 --- a/api/controllers/console/workspace/plugin.py +++ b/api/controllers/console/workspace/plugin.py @@ -133,9 +133,7 @@ class PluginInstallFromPkgApi(Resource): response = PluginService.install_from_local_pkg(tenant_id, args["plugin_unique_identifier"]) - return { - "task_id": response, - } + return response.model_dump() class PluginInstallFromGithubApi(Resource): @@ -160,9 +158,7 @@ class PluginInstallFromGithubApi(Resource): tenant_id, args["repo"], args["version"], args["package"], args["plugin_unique_identifier"] ) - return { - "task_id": response, - } + return response.model_dump() class PluginInstallFromMarketplaceApi(Resource): @@ -182,9 +178,7 @@ class PluginInstallFromMarketplaceApi(Resource): response = PluginService.install_from_marketplace_pkg(tenant_id, args["plugin_unique_identifier"]) - return { - "task_id": response, - } + return response.model_dump() class PluginFetchManifestApi(Resource): @@ -200,7 +194,9 @@ class PluginFetchManifestApi(Resource): tenant_id = user.current_tenant_id - return {"manifest": PluginService.fetch_plugin_manifest(tenant_id, args["plugin_unique_identifier"])} + return jsonable_encoder( + {"manifest": PluginService.fetch_plugin_manifest(tenant_id, args["plugin_unique_identifier"]).model_dump()} + ) class PluginFetchInstallTasksApi(Resource): diff --git a/api/core/plugin/entities/plugin.py b/api/core/plugin/entities/plugin.py index 0d56510551..129176ae41 100644 --- a/api/core/plugin/entities/plugin.py +++ b/api/core/plugin/entities/plugin.py @@ -3,7 +3,7 @@ from collections.abc import Mapping from enum import Enum from typing import Any, Optional -from pydantic import BaseModel, Field +from pydantic import BaseModel, Field, model_validator from core.model_runtime.entities.provider_entities import ProviderEntity from core.plugin.entities.base import BasePluginEntity @@ -54,6 +54,12 @@ class PluginResourceRequirements(BaseModel): permission: Optional[Permission] +class PluginCategory(str, Enum): + Tool = "tool" + Model = "model" + Extension = "extension" + + class PluginDeclaration(BaseModel): class Plugins(BaseModel): tools: Optional[list[str]] = Field(default_factory=list) @@ -65,6 +71,7 @@ class PluginDeclaration(BaseModel): name: str = Field(..., pattern=r"^[a-z0-9_-]{1,128}$") icon: str label: I18nObject + category: PluginCategory created_at: datetime.datetime resource: PluginResourceRequirements plugins: Plugins @@ -72,6 +79,18 @@ class PluginDeclaration(BaseModel): model: Optional[ProviderEntity] = None endpoint: Optional[EndpointProviderDeclaration] = None + @model_validator(mode="before") + @classmethod + def validate_category(cls, values: dict) -> dict: + # auto detect category + if values.get("tool"): + values["category"] = PluginCategory.Tool + elif values.get("model"): + values["category"] = PluginCategory.Model + else: + values["category"] = PluginCategory.Extension + return values + class PluginEntity(BasePluginEntity): name: str diff --git a/api/core/plugin/entities/plugin_daemon.py b/api/core/plugin/entities/plugin_daemon.py index d32dcf1adc..03cd069d6e 100644 --- a/api/core/plugin/entities/plugin_daemon.py +++ b/api/core/plugin/entities/plugin_daemon.py @@ -128,3 +128,8 @@ class PluginInstallTask(BasePluginEntity): total_plugins: int = Field(description="The total number of plugins to be installed.") completed_plugins: int = Field(description="The number of plugins that have been installed.") plugins: list[PluginInstallTaskPluginStatus] = Field(description="The status of the plugins.") + + +class PluginInstallTaskStartResponse(BasePluginEntity): + all_installed: bool = Field(description="Whether all plugins are installed.") + task_id: str = Field(description="The ID of the install task.") diff --git a/api/core/plugin/manager/plugin.py b/api/core/plugin/manager/plugin.py index 0912d473a5..3766bf33c0 100644 --- a/api/core/plugin/manager/plugin.py +++ b/api/core/plugin/manager/plugin.py @@ -1,7 +1,9 @@ from collections.abc import Sequence +from pydantic import BaseModel + from core.plugin.entities.plugin import PluginDeclaration, PluginEntity, PluginInstallationSource -from core.plugin.entities.plugin_daemon import PluginInstallTask +from core.plugin.entities.plugin_daemon import PluginInstallTask, PluginInstallTaskStartResponse from core.plugin.manager.base import BasePluginManager @@ -53,15 +55,15 @@ class PluginInstallationManager(BasePluginManager): def install_from_identifiers( self, tenant_id: str, identifiers: Sequence[str], source: PluginInstallationSource, meta: dict - ) -> str: + ) -> PluginInstallTaskStartResponse: """ Install a plugin from an identifier. """ # exception will be raised if the request failed return self._request_with_plugin_daemon_response( "POST", - f"plugin/{tenant_id}/management/install/identifier", - str, + f"plugin/{tenant_id}/management/install/identifiers", + PluginInstallTaskStartResponse, data={ "plugin_unique_identifiers": identifiers, "source": source, @@ -94,11 +96,16 @@ class PluginInstallationManager(BasePluginManager): """ Fetch a plugin manifest. """ + + class PluginDeclarationResponse(BaseModel): + declaration: PluginDeclaration + return self._request_with_plugin_daemon_response( "GET", - f"plugin/{tenant_id}/management/fetch/identifier", - PluginDeclaration, - ) + f"plugin/{tenant_id}/management/fetch/manifest", + PluginDeclarationResponse, + params={"plugin_unique_identifier": plugin_unique_identifier}, + ).declaration def uninstall(self, tenant_id: str, plugin_installation_id: str) -> bool: """ diff --git a/api/services/plugin/plugin_service.py b/api/services/plugin/plugin_service.py index 3b7d19aa0b..2ecb8f19a1 100644 --- a/api/services/plugin/plugin_service.py +++ b/api/services/plugin/plugin_service.py @@ -87,7 +87,7 @@ class PluginService: ) @staticmethod - def install_from_local_pkg(tenant_id: str, plugin_unique_identifier: str) -> str: + def install_from_local_pkg(tenant_id: str, plugin_unique_identifier: str): manager = PluginInstallationManager() return manager.install_from_identifiers( tenant_id, @@ -97,9 +97,7 @@ class PluginService: ) @staticmethod - def install_from_github( - tenant_id: str, plugin_unique_identifier: str, repo: str, version: str, package: str - ) -> str: + def install_from_github(tenant_id: str, plugin_unique_identifier: str, repo: str, version: str, package: str): """ Install plugin from github release package files, returns plugin_unique_identifier @@ -117,7 +115,7 @@ class PluginService: ) @staticmethod - def install_from_marketplace_pkg(tenant_id: str, plugin_unique_identifier: str) -> str: + def install_from_marketplace_pkg(tenant_id: str, plugin_unique_identifier: str): """ Install plugin from marketplace package files, returns installation task id