From 769b4df1c393c6eb95e57df5b04ab7ebe037578c Mon Sep 17 00:00:00 2001 From: GareArc Date: Sat, 10 Oct 2026 15:38:27 -0700 Subject: [PATCH] fix(api): keep plugin install statuses strictly the enum, and type openapi's task status with it --- api/controllers/openapi/_models.py | 5 ++-- api/controllers/openapi/plugins.py | 4 +-- api/core/plugin/entities/plugin_daemon.py | 4 +-- api/openapi/markdown/console-openapi.md | 4 +-- api/openapi/markdown/openapi-openapi.md | 10 +++++-- .../core/plugin/test_plugin_entities.py | 30 +------------------ .../api/console/workspaces/types.gen.ts | 4 +-- .../api/console/workspaces/zod.gen.ts | 4 +-- .../generated/api/openapi/types.gen.ts | 6 ++-- .../generated/api/openapi/zod.gen.ts | 9 ++++-- 10 files changed, 33 insertions(+), 47 deletions(-) diff --git a/api/controllers/openapi/_models.py b/api/controllers/openapi/_models.py index e030c9bcfe4..3ad506a5079 100644 --- a/api/controllers/openapi/_models.py +++ b/api/controllers/openapi/_models.py @@ -15,6 +15,7 @@ from controllers.common.human_input import HumanInputFormSubmitPayload from controllers.openapi._i18n import localized from controllers.openapi._upload import UploadPart, UploadParts from core.plugin.entities.plugin import PluginCategory +from core.plugin.entities.plugin_daemon import PluginInstallTaskStatus from core.tools.entities.tool_entities import ToolProviderType from enums import DeploymentEdition, WebAppAccessMode from fields.workflow_run_fields import WorkflowRunPaginationResponse @@ -571,13 +572,13 @@ class PluginTaskStartResponse(Hinted): class PluginTaskItem(BaseModel): plugin_id: str identifier: str - status: str + status: PluginInstallTaskStatus message: str class PluginTaskResponse(Hinted): task_id: str - status: str = Field(description="pending, running, success or failed") + status: PluginInstallTaskStatus plugins: list[PluginTaskItem] diff --git a/api/controllers/openapi/plugins.py b/api/controllers/openapi/plugins.py index 5c5933a3760..4a1dcc6bfae 100644 --- a/api/controllers/openapi/plugins.py +++ b/api/controllers/openapi/plugins.py @@ -268,12 +268,12 @@ class PluginTaskApi(Resource): hints = [] return PluginTaskResponse( task_id=task_id, - status=str(task.status), + status=task.status, plugins=[ PluginTaskItem( plugin_id=plugin.plugin_id, identifier=plugin.plugin_unique_identifier, - status=str(plugin.status), + status=plugin.status, message=plugin.message, ) for plugin in task.plugins diff --git a/api/core/plugin/entities/plugin_daemon.py b/api/core/plugin/entities/plugin_daemon.py index f050341511c..94b011a9ae4 100644 --- a/api/core/plugin/entities/plugin_daemon.py +++ b/api/core/plugin/entities/plugin_daemon.py @@ -194,7 +194,7 @@ class PluginInstallTaskStatus(StrEnum): class PluginInstallTaskPluginStatus(BaseModel): plugin_unique_identifier: str = Field(description="The plugin unique identifier of the install task.") plugin_id: str = Field(description="The plugin ID of the install task.") - status: PluginInstallTaskStatus | str = Field(description="The status of the install task.") + status: PluginInstallTaskStatus = Field(description="The status of the install task.") message: str = Field(description="The message of the install task.") icon: str = Field(description="The icon of the plugin.") labels: I18nObject = Field(description="The labels of the plugin.") @@ -202,7 +202,7 @@ class PluginInstallTaskPluginStatus(BaseModel): class PluginInstallTask(BasePluginEntity): - status: PluginInstallTaskStatus | str = Field(description="The status of the install task.") + status: PluginInstallTaskStatus = Field(description="The status of the install task.") 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.") diff --git a/api/openapi/markdown/console-openapi.md b/api/openapi/markdown/console-openapi.md index 48177c60e7b..50264af47c2 100644 --- a/api/openapi/markdown/console-openapi.md +++ b/api/openapi/markdown/console-openapi.md @@ -22021,7 +22021,7 @@ Shared permission levels for resources (datasets, credentials, etc.) | created_at | dateTime | | Yes | | id | string | | Yes | | plugins | [ [PluginInstallTaskPluginStatus](#plugininstalltaskpluginstatus) ] | The status of the plugins. | Yes | -| status | [PluginInstallTaskStatus](#plugininstalltaskstatus)
string | The status of the install task. | Yes | +| status | [PluginInstallTaskStatus](#plugininstalltaskstatus) | The status of the install task. | Yes | | total_plugins | integer | The total number of plugins to be installed. | Yes | | updated_at | dateTime | | Yes | @@ -22035,7 +22035,7 @@ Shared permission levels for resources (datasets, credentials, etc.) | plugin_id | string | The plugin ID of the install task. | Yes | | plugin_unique_identifier | string | The plugin unique identifier of the install task. | Yes | | source | string | The installation source of the plugin | No | -| status | [PluginInstallTaskStatus](#plugininstalltaskstatus)
string | The status of the install task. | Yes | +| status | [PluginInstallTaskStatus](#plugininstalltaskstatus) | The status of the install task. | Yes | #### PluginInstallTaskStartResponse diff --git a/api/openapi/markdown/openapi-openapi.md b/api/openapi/markdown/openapi-openapi.md index 7eb9174e36b..0de1b568ca6 100644 --- a/api/openapi/markdown/openapi-openapi.md +++ b/api/openapi/markdown/openapi-openapi.md @@ -2935,6 +2935,12 @@ Strict (extra='forbid'). | ---- | ---- | ----------- | -------- | | identifiers | [ string ] | Versioned plugin ids such as langgenius/openai:0.2.1@sha256…, from get.marketplace.plugin (identifier) or check.console_app.dependency | Yes | +#### PluginInstallTaskStatus + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| PluginInstallTaskStatus | string | | | + #### PluginListQuery | Name | Type | Description | Required | @@ -2975,7 +2981,7 @@ Strict (extra='forbid'). | identifier | string | | Yes | | message | string | | Yes | | plugin_id | string | | Yes | -| status | string | | Yes | +| status | [PluginInstallTaskStatus](#plugininstalltaskstatus) | | Yes | #### PluginTaskResponse @@ -2983,7 +2989,7 @@ Strict (extra='forbid'). | ---- | ---- | ----------- | -------- | | hints | [ [Hint](#hint) ] | Next steps the caller can take | No | | plugins | [ [PluginTaskItem](#plugintaskitem) ] | | Yes | -| status | string | pending, running, success or failed | Yes | +| status | [PluginInstallTaskStatus](#plugininstalltaskstatus) | | Yes | | task_id | string | | Yes | #### PluginTaskStartResponse diff --git a/api/tests/unit_tests/core/plugin/test_plugin_entities.py b/api/tests/unit_tests/core/plugin/test_plugin_entities.py index afff1608b84..ca85d75f8c5 100644 --- a/api/tests/unit_tests/core/plugin/test_plugin_entities.py +++ b/api/tests/unit_tests/core/plugin/test_plugin_entities.py @@ -17,7 +17,7 @@ from core.plugin.entities.parameters import ( cast_parameter_value, init_frontend_parameter, ) -from core.plugin.entities.plugin_daemon import CredentialType, PluginInstallTask, PluginInstallTaskStatus +from core.plugin.entities.plugin_daemon import CredentialType from core.plugin.entities.request import ( RequestInvokeLLM, RequestInvokeSpeech2Text, @@ -337,34 +337,6 @@ class TestPluginDaemonEntities: with pytest.raises(ValueError, match="Invalid credential type"): CredentialType.of("invalid") - @pytest.mark.parametrize( - ("raw", "expected"), - [("success", PluginInstallTaskStatus.Success), ("a-status-added-later", "a-status-added-later")], - ) - def test_install_task_parses_known_and_unknown_statuses(self, raw, expected): - plugin = { - "plugin_unique_identifier": "langgenius/openai:0.1.0@abc", - "plugin_id": "langgenius/openai", - "status": raw, - "message": "", - "icon": "", - "labels": {"en_US": "OpenAI"}, - } - task = PluginInstallTask.model_validate( - { - "id": "task-1", - "created_at": "2026-01-01T00:00:00Z", - "updated_at": "2026-01-01T00:00:00Z", - "status": raw, - "total_plugins": 1, - "completed_plugins": 0, - "plugins": [plugin], - } - ) - - assert task.status == expected - assert task.plugins[0].status == expected - class TestPluginRequestEntities: def test_request_invoke_llm_converts_prompt_messages(self): diff --git a/packages/contracts/generated/api/console/workspaces/types.gen.ts b/packages/contracts/generated/api/console/workspaces/types.gen.ts index 5c76bdec4df..3b0ea2b6d3e 100644 --- a/packages/contracts/generated/api/console/workspaces/types.gen.ts +++ b/packages/contracts/generated/api/console/workspaces/types.gen.ts @@ -1596,7 +1596,7 @@ export type PluginInstallTask = { created_at: string id: string plugins: Array - status: PluginInstallTaskStatus | string + status: PluginInstallTaskStatus total_plugins: number updated_at: string } @@ -2331,7 +2331,7 @@ export type PluginInstallTaskPluginStatus = { plugin_id: string plugin_unique_identifier: string source?: string | null - status: PluginInstallTaskStatus | string + status: PluginInstallTaskStatus } export type PluginInstallTaskStatus = 'failed' | 'pending' | 'running' | 'success' diff --git a/packages/contracts/generated/api/console/workspaces/zod.gen.ts b/packages/contracts/generated/api/console/workspaces/zod.gen.ts index 61689067771..e66b1d499b4 100644 --- a/packages/contracts/generated/api/console/workspaces/zod.gen.ts +++ b/packages/contracts/generated/api/console/workspaces/zod.gen.ts @@ -2423,7 +2423,7 @@ export const zPluginInstallTaskPluginStatus = z.object({ plugin_id: z.string(), plugin_unique_identifier: z.string(), source: z.string().nullish(), - status: z.union([zPluginInstallTaskStatus, z.string()]), + status: zPluginInstallTaskStatus, }) /** @@ -2434,7 +2434,7 @@ export const zPluginInstallTask = z.object({ created_at: z.iso.datetime(), id: z.string(), plugins: z.array(zPluginInstallTaskPluginStatus), - status: z.union([zPluginInstallTaskStatus, z.string()]), + status: zPluginInstallTaskStatus, total_plugins: z.int(), updated_at: z.iso.datetime(), }) diff --git a/packages/contracts/generated/api/openapi/types.gen.ts b/packages/contracts/generated/api/openapi/types.gen.ts index 774464de120..5e232a70c76 100644 --- a/packages/contracts/generated/api/openapi/types.gen.ts +++ b/packages/contracts/generated/api/openapi/types.gen.ts @@ -956,6 +956,8 @@ export type PluginInstallPayload = { identifiers: Array } +export type PluginInstallTaskStatus = 'failed' | 'pending' | 'running' | 'success' + export type PluginListQuery = { category?: PluginCategory | null } @@ -985,13 +987,13 @@ export type PluginTaskItem = { identifier: string message: string plugin_id: string - status: string + status: PluginInstallTaskStatus } export type PluginTaskResponse = { hints?: Array plugins: Array - status: string + status: PluginInstallTaskStatus task_id: string } diff --git a/packages/contracts/generated/api/openapi/zod.gen.ts b/packages/contracts/generated/api/openapi/zod.gen.ts index 9ad2ad9ed17..f155b5ce5ca 100644 --- a/packages/contracts/generated/api/openapi/zod.gen.ts +++ b/packages/contracts/generated/api/openapi/zod.gen.ts @@ -1287,6 +1287,11 @@ export const zPluginInstallPayload = z.object({ identifiers: z.array(z.string()).min(1), }) +/** + * PluginInstallTaskStatus + */ +export const zPluginInstallTaskStatus = z.enum(['failed', 'pending', 'running', 'success']) + /** * PluginListQuery */ @@ -1331,7 +1336,7 @@ export const zPluginTaskItem = z.object({ identifier: z.string(), message: z.string(), plugin_id: z.string(), - status: z.string(), + status: zPluginInstallTaskStatus, }) /** @@ -1340,7 +1345,7 @@ export const zPluginTaskItem = z.object({ export const zPluginTaskResponse = z.object({ hints: z.array(zHint).optional(), plugins: z.array(zPluginTaskItem), - status: z.string(), + status: zPluginInstallTaskStatus, task_id: z.string(), })