From 38aec8b506d4402cec5480225d1769cbe69c6066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=9E=E6=B3=95=E6=93=8D=E4=BD=9C?= Date: Wed, 15 Jul 2026 10:01:53 +0800 Subject: [PATCH] chore: chatflow api should check workflow_id and sandbox plan (#38892) --- api/controllers/service_api/app/completion.py | 14 ++++ api/openapi/markdown/service-openapi.md | 4 +- .../service_api/app/test_completion.py | 78 +++++++++++++++++++ .../components/develop/__tests__/md.spec.tsx | 14 ++++ ...rkflow-version-api-upgrade-notice.spec.tsx | 52 ++++++++++++- web/app/components/develop/md.tsx | 4 +- .../__tests__/template_advanced_chat.spec.ts | 35 +++++++++ .../__tests__/template_workflow.spec.ts | 26 +++++++ .../template/template_advanced_chat.en.mdx | 4 +- .../template/template_advanced_chat.ja.mdx | 4 +- .../template/template_advanced_chat.zh.mdx | 4 +- .../develop/template/template_workflow.en.mdx | 4 +- .../develop/template/template_workflow.ja.mdx | 4 +- .../develop/template/template_workflow.zh.mdx | 4 +- .../workflow-version-api-upgrade-notice.tsx | 13 +++- 15 files changed, 251 insertions(+), 13 deletions(-) create mode 100644 web/app/components/develop/template/__tests__/template_advanced_chat.spec.ts create mode 100644 web/app/components/develop/template/__tests__/template_workflow.spec.ts diff --git a/api/controllers/service_api/app/completion.py b/api/controllers/service_api/app/completion.py index 7bbe1ec5ffc..17ca4238395 100644 --- a/api/controllers/service_api/app/completion.py +++ b/api/controllers/service_api/app/completion.py @@ -10,6 +10,7 @@ from sqlalchemy.orm import Session from werkzeug.exceptions import BadRequest, InternalServerError, NotFound import services +from configs import dify_config from controllers.common.fields import SimpleResultResponse from controllers.common.schema import register_response_schema_models, register_schema_models from controllers.console.app.wraps import with_session @@ -23,6 +24,7 @@ from controllers.service_api.app.error import ( ProviderModelCurrentlyNotSupportError, ProviderNotInitializeError, ProviderQuotaExceededError, + WorkflowVersionExecutionNotAllowedError, ) from controllers.service_api.schema import ( InputFileList, @@ -40,6 +42,7 @@ from core.errors.error import ( QuotaExceededError, ) from core.helper.trace_id_helper import get_external_trace_id, get_trace_session_id, omit_trace_session_id_from_payload +from enums.cloud_plan import CloudPlan from extensions.ext_database import db from graphon.model_runtime.errors.invoke import InvokeError from libs import helper @@ -47,6 +50,7 @@ from libs.helper import UUIDStrOrEmpty from models.model import App, AppMode, EndUser from services.app_generate_service import AppGenerateService from services.app_task_service import AppTaskService +from services.billing_service import BillingService from services.conversation_service import ConversationService from services.errors.app import IsDraftWorkflowError, WorkflowIdFormatError, WorkflowNotFoundError from services.errors.llm import InvokeRateLimitError @@ -331,6 +335,10 @@ class ChatApi(Resource): "- `model_currently_not_support` : Current model unavailable.\n" "- `completion_request_error` : Text generation failed." ), + 403: ( + "`workflow_version_execution_not_allowed` : Workflow version execution is unavailable on the " + "current plan. Upgrade to a paid plan." + ), 404: "`not_found` : Conversation does not exist.", 429: ( "- `too_many_requests` : Too many concurrent requests for this app.\n" @@ -348,6 +356,7 @@ class ChatApi(Resource): 200: "Message sent successfully", 400: "Bad request - invalid parameters or workflow issues", 401: "Unauthorized - invalid API token", + 403: "Forbidden - upgrade to a paid plan to execute a specific workflow version", 404: "Conversation or workflow not found", 429: "Rate limit exceeded", 500: "Internal server error", @@ -368,6 +377,11 @@ class ChatApi(Resource): payload = ChatRequestPayload.model_validate(omit_trace_session_id_from_payload(service_api_ns.payload) or {}) + if app_mode == AppMode.ADVANCED_CHAT and payload.workflow_id and dify_config.BILLING_ENABLED: + billing_info = BillingService.get_info(app_model.tenant_id, exclude_vector_space=True) + if billing_info["enabled"] and billing_info["subscription"]["plan"] == CloudPlan.SANDBOX: + raise WorkflowVersionExecutionNotAllowedError() + external_trace_id = get_external_trace_id(request) args = payload.model_dump(exclude_none=True) trace_session_id = get_trace_session_id(request) diff --git a/api/openapi/markdown/service-openapi.md b/api/openapi/markdown/service-openapi.md index 1a96b1bcf37..3cc8e4c3410 100644 --- a/api/openapi/markdown/service-openapi.md +++ b/api/openapi/markdown/service-openapi.md @@ -327,7 +327,7 @@ Send a request to the chat application. | 200 | Successful response. The content type and structure depend on the `response_mode` parameter in the request. - If `response_mode` is `blocking`, returns `application/json` with a `ChatCompletionResponse` object. - If `response_mode` is `streaming`, returns `text/event-stream` with a stream of Server-Sent Events. | | 400 | - `app_unavailable` : App unavailable or misconfigured. - `not_chat_app` : App mode does not match the API route. - `conversation_completed` : The conversation has ended. - `provider_not_initialize` : No valid model provider credentials found. - `provider_quota_exceeded` : Model provider quota exhausted. - `model_currently_not_support` : Current model unavailable. - `completion_request_error` : Text generation failed. | | 401 | Unauthorized - invalid API token | -| 403 | Forbidden - token scope, app, dataset, or workspace access denied | +| 403 | `workflow_version_execution_not_allowed` : Workflow version execution is unavailable on the current plan. Upgrade to a paid plan. | | 404 | `not_found` : Conversation does not exist. | | 429 | - `too_many_requests` : Too many concurrent requests for this app. - `rate_limit_error` : The upstream model provider rate limit was exceeded. | | 500 | `internal_server_error` : Internal server error. | @@ -474,7 +474,7 @@ Send a request to the chat application. | 200 | Successful response. The content type and structure depend on the `response_mode` parameter in the request. - If `response_mode` is `blocking`, returns `application/json` with a `ChatCompletionResponse` object. - If `response_mode` is `streaming`, returns `text/event-stream` with a stream of Server-Sent Events. | | 400 | - `app_unavailable` : App unavailable or misconfigured. - `not_chat_app` : App mode does not match the API route. - `conversation_completed` : The conversation has ended. - `provider_not_initialize` : No valid model provider credentials found. - `provider_quota_exceeded` : Model provider quota exhausted. - `model_currently_not_support` : Current model unavailable. - `completion_request_error` : Text generation failed. | | 401 | Unauthorized - invalid API token | -| 403 | Forbidden - token scope, app, dataset, or workspace access denied | +| 403 | `workflow_version_execution_not_allowed` : Workflow version execution is unavailable on the current plan. Upgrade to a paid plan. | | 404 | `not_found` : Conversation does not exist. | | 429 | - `too_many_requests` : Too many concurrent requests for this app. - `rate_limit_error` : The upstream model provider rate limit was exceeded. | | 500 | `internal_server_error` : Internal server error. | diff --git a/api/tests/unit_tests/controllers/service_api/app/test_completion.py b/api/tests/unit_tests/controllers/service_api/app/test_completion.py index 79e17a65aae..0ac0dde0f5a 100644 --- a/api/tests/unit_tests/controllers/service_api/app/test_completion.py +++ b/api/tests/unit_tests/controllers/service_api/app/test_completion.py @@ -11,6 +11,7 @@ Focus on: - Error types and their mappings """ +import sys import uuid from decimal import Decimal from inspect import unwrap @@ -37,15 +38,18 @@ from controllers.service_api.app.error import ( AppUnavailableError, ConversationCompletedError, NotChatAppError, + WorkflowVersionExecutionNotAllowedError, ) from core.app.apps.agent_app.errors import AgentAppNotPublishedError from core.errors.error import QuotaExceededError +from enums.cloud_plan import CloudPlan from graphon.model_runtime.errors.invoke import InvokeError from models.base import TypeBase from models.enums import ConversationFromSource, EndUserType from models.model import App, AppMode, Conversation, EndUser, IconType, Message from services.app_generate_service import AppGenerateService from services.app_task_service import AppTaskService +from services.billing_service import BillingService from services.conversation_service import ConversationService from services.errors.app import IsDraftWorkflowError, WorkflowIdFormatError, WorkflowNotFoundError from services.errors.conversation import ConversationNotExistsError @@ -548,6 +552,80 @@ class TestCompletionStopApiController: class TestChatApiController: + def test_rejects_sandbox_plan_workflow_version( + self, app: Flask, monkeypatch: pytest.MonkeyPatch, orm_session: Session + ) -> None: + completion_module = sys.modules["controllers.service_api.app.completion"] + monkeypatch.setattr(completion_module.dify_config, "BILLING_ENABLED", True) + + billing_get_info = Mock(return_value={"enabled": True, "subscription": {"plan": CloudPlan.SANDBOX}}) + generate = Mock() + monkeypatch.setattr(BillingService, "get_info", billing_get_info) + monkeypatch.setattr(AppGenerateService, "generate", generate) + + api = ChatApi() + handler = unwrap(api.post) + app_model, end_user, _, _ = _persist_completion_state(orm_session, AppMode.ADVANCED_CHAT) + workflow_id = str(uuid.uuid4()) + + with app.test_request_context( + "/chat-messages", + method="POST", + json={"inputs": {}, "query": "hi", "workflow_id": workflow_id}, + ): + with pytest.raises(WorkflowVersionExecutionNotAllowedError) as exc_info: + handler(api, session=orm_session, app_model=app_model, end_user=end_user) + + billing_get_info.assert_called_once_with(app_model.tenant_id, exclude_vector_space=True) + generate.assert_not_called() + assert exc_info.value.code == 403 + assert exc_info.value.error_code == "workflow_version_execution_not_allowed" + + @pytest.mark.parametrize( + ("billing_config_enabled", "billing_enabled", "plan", "workflow_id"), + [ + (False, True, CloudPlan.SANDBOX, str(uuid.uuid4())), + (True, False, CloudPlan.SANDBOX, str(uuid.uuid4())), + (True, True, CloudPlan.PROFESSIONAL, str(uuid.uuid4())), + (True, True, CloudPlan.SANDBOX, None), + ], + ) + def test_allows_default_or_entitled_workflow_version_execution( + self, + app: Flask, + monkeypatch: pytest.MonkeyPatch, + orm_session: Session, + billing_config_enabled: bool, + billing_enabled: bool, + plan: CloudPlan, + workflow_id: str | None, + ) -> None: + completion_module = sys.modules["controllers.service_api.app.completion"] + monkeypatch.setattr(completion_module.dify_config, "BILLING_ENABLED", billing_config_enabled) + + billing_get_info = Mock(return_value={"enabled": billing_enabled, "subscription": {"plan": plan}}) + generate = Mock(return_value={"result": "ok"}) + monkeypatch.setattr(BillingService, "get_info", billing_get_info) + monkeypatch.setattr(AppGenerateService, "generate", generate) + monkeypatch.setattr(completion_module.helper, "compact_generate_response", lambda response: response) + + api = ChatApi() + handler = unwrap(api.post) + app_model, end_user, _, _ = _persist_completion_state(orm_session, AppMode.ADVANCED_CHAT) + request_json = {"inputs": {}, "query": "hi"} + if workflow_id: + request_json["workflow_id"] = workflow_id + + with app.test_request_context("/chat-messages", method="POST", json=request_json): + response = handler(api, session=orm_session, app_model=app_model, end_user=end_user) + + assert response == {"result": "ok"} + generate.assert_called_once() + if billing_config_enabled and workflow_id: + billing_get_info.assert_called_once_with(app_model.tenant_id, exclude_vector_space=True) + else: + billing_get_info.assert_not_called() + def test_wrong_mode(self, app: Flask, orm_session: Session) -> None: api = ChatApi() handler = unwrap(api.post) diff --git a/web/app/components/develop/__tests__/md.spec.tsx b/web/app/components/develop/__tests__/md.spec.tsx index 756ee34b56d..1fdd96e831f 100644 --- a/web/app/components/develop/__tests__/md.spec.tsx +++ b/web/app/components/develop/__tests__/md.spec.tsx @@ -346,6 +346,20 @@ describe('md.tsx components', () => { expect(screen.getByText('User identifier')).toBeInTheDocument() }) + it('should render a name action next to the property name', () => { + render( + Upgrade}> + User identifier + , + ) + + const nameContainer = screen.getByText('user_id').parentElement! + const actionContainer = screen.getByRole('button', { name: 'Upgrade' }).parentElement! + expect(nameContainer).not.toHaveClass('flex') + expect(actionContainer).toHaveClass('ml-2', 'inline-flex', 'align-middle') + expect(nameContainer.children[1]).toBe(actionContainer) + }) + it('should render as li element', () => { const { container } = render(Description) expect(container.querySelector('li')).toBeInTheDocument() diff --git a/web/app/components/develop/__tests__/workflow-version-api-upgrade-notice.spec.tsx b/web/app/components/develop/__tests__/workflow-version-api-upgrade-notice.spec.tsx index 161e01eb4b3..3aa8643c7bf 100644 --- a/web/app/components/develop/__tests__/workflow-version-api-upgrade-notice.spec.tsx +++ b/web/app/components/develop/__tests__/workflow-version-api-upgrade-notice.spec.tsx @@ -1,6 +1,9 @@ import { fireEvent, render, screen, within } from '@testing-library/react' import { Plan } from '@/app/components/billing/type' -import WorkflowVersionApiUpgradeNotice from '../workflow-version-api-upgrade-notice' +import { + WorkflowVersionApiContent, + WorkflowVersionApiUpgradeNotice, +} from '../workflow-version-api-upgrade-notice' let mockPlanType = Plan.professional let mockEnableBilling = true @@ -127,3 +130,50 @@ describe('WorkflowVersionApiUpgradeNotice', () => { ).toBeInTheDocument() }) }) + +describe('WorkflowVersionApiContent', () => { + beforeEach(() => { + mockPlanType = Plan.professional + mockEnableBilling = true + mockIsFetchedPlan = true + }) + + it('should hide content while the billing plan is loading', () => { + mockIsFetchedPlan = false + + render(paid API details) + + expect(screen.queryByText('paid API details')).not.toBeInTheDocument() + }) + + it('should hide content for sandbox plans', () => { + mockPlanType = Plan.sandbox + + const { container } = render( + <> +

paid API

+ paid API details +
+ , + ) + + expect(screen.queryByText('paid API details')).not.toBeInTheDocument() + expect(container.querySelector('h2 + [aria-hidden="true"] + hr')).toBeInTheDocument() + }) + + it('should show content for paid plans', () => { + render(paid API details) + + expect(screen.getByText('paid API details')).toBeInTheDocument() + }) + + it('should show content when billing is disabled', () => { + mockPlanType = Plan.sandbox + mockEnableBilling = false + mockIsFetchedPlan = false + + render(paid API details) + + expect(screen.getByText('paid API details')).toBeInTheDocument() + }) +}) diff --git a/web/app/components/develop/md.tsx b/web/app/components/develop/md.tsx index 4e3b3693ff7..e870cb98780 100644 --- a/web/app/components/develop/md.tsx +++ b/web/app/components/develop/md.tsx @@ -95,14 +95,16 @@ export function Properties({ children }: IChildrenProps) { type IProperty = IChildrenProps & { name: string type: string + nameAction?: React.ReactNode } -export function Property({ name, type, children }: IProperty) { +export function Property({ name, type, nameAction, children }: IProperty) { return (
  • Name
    {name} + {nameAction ? {nameAction} : null}
    Type
    {type}
    diff --git a/web/app/components/develop/template/__tests__/template_advanced_chat.spec.ts b/web/app/components/develop/template/__tests__/template_advanced_chat.spec.ts new file mode 100644 index 00000000000..e68eb0518b9 --- /dev/null +++ b/web/app/components/develop/template/__tests__/template_advanced_chat.spec.ts @@ -0,0 +1,35 @@ +import { readFileSync } from 'node:fs' +import { resolve } from 'node:path' + +const templates = [ + ['English', 'template_advanced_chat.en.mdx'], + ['Japanese', 'template_advanced_chat.ja.mdx'], + ['Chinese', 'template_advanced_chat.zh.mdx'], +] as const + +describe.each(templates)('%s advanced chat API template', (_language, templateFile) => { + it('should show the workflow version upgrade action next to workflow_id', () => { + const source = readFileSync( + resolve(process.cwd(), 'app/components/develop/template', templateFile), + 'utf8', + ) + + expect(source).toContain( + "import { WorkflowVersionApiUpgradeNotice } from '../workflow-version-api-upgrade-notice.tsx'", + ) + expect(source).toContain( + "}>", + ) + expect(source).not.toContain('titleAction={}') + }) + + it('should document the workflow version plan error', () => { + const source = readFileSync( + resolve(process.cwd(), 'app/components/develop/template', templateFile), + 'utf8', + ) + + expect(source).toContain('403') + expect(source).toContain('workflow_version_execution_not_allowed') + }) +}) diff --git a/web/app/components/develop/template/__tests__/template_workflow.spec.ts b/web/app/components/develop/template/__tests__/template_workflow.spec.ts new file mode 100644 index 00000000000..10151e5af43 --- /dev/null +++ b/web/app/components/develop/template/__tests__/template_workflow.spec.ts @@ -0,0 +1,26 @@ +import { readFileSync } from 'node:fs' +import { resolve } from 'node:path' + +const templates = [ + ['English', 'template_workflow.en.mdx'], + ['Japanese', 'template_workflow.ja.mdx'], + ['Chinese', 'template_workflow.zh.mdx'], +] as const + +describe.each(templates)('%s workflow API template', (_language, templateFile) => { + it('should hide only the paid API details behind the plan gate', () => { + const source = readFileSync( + resolve(process.cwd(), 'app/components/develop/template', templateFile), + 'utf8', + ) + const paidApiStart = source.indexOf("name='#Execute-Specific-Workflow'") + const nextApiStart = source.indexOf("url='/workflows/run/:workflow_run_id'", paidApiStart) + const paidApi = source.slice(paidApiStart, nextApiStart) + + expect(paidApiStart).toBeGreaterThanOrEqual(0) + expect(nextApiStart).toBeGreaterThan(paidApiStart) + expect(paidApi).toContain('titleAction={}') + expect(paidApi).toContain('\n') + expect(paidApi).toContain('\n') + }) +}) diff --git a/web/app/components/develop/template/template_advanced_chat.en.mdx b/web/app/components/develop/template/template_advanced_chat.en.mdx index 5bf401b3346..1f37660acd8 100644 --- a/web/app/components/develop/template/template_advanced_chat.en.mdx +++ b/web/app/components/develop/template/template_advanced_chat.en.mdx @@ -1,5 +1,6 @@ import { CodeGroup, Embed } from '../code.tsx' import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' +import { WorkflowVersionApiUpgradeNotice } from '../workflow-version-api-upgrade-notice.tsx' # Advanced Chat App API @@ -74,7 +75,7 @@ Chat applications support session persistence, allowing previous chat history to Auto-generate title, default is `true`. If set to `false`, can achieve async title generation by calling the conversation rename API and setting `auto_generate` to `true`. - + }> (Optional) Workflow ID to specify a specific version, if not provided, uses the default published version. @@ -250,6 +251,7 @@ Chat applications support session persistence, allowing previous chat history to - 400, `draft_workflow_error`, cannot use draft workflow version - 400, `workflow_id_format_error`, invalid workflow_id format, expected UUID format - 400, `completion_request_error`, text generation failed + - 403, `workflow_version_execution_not_allowed`, Workflow version execution is not available on your current plan. Please upgrade to a paid plan. - 500, internal server error diff --git a/web/app/components/develop/template/template_advanced_chat.ja.mdx b/web/app/components/develop/template/template_advanced_chat.ja.mdx index bc96e326f5c..5a722ef8380 100644 --- a/web/app/components/develop/template/template_advanced_chat.ja.mdx +++ b/web/app/components/develop/template/template_advanced_chat.ja.mdx @@ -1,5 +1,6 @@ import { CodeGroup } from '../code.tsx' import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' +import { WorkflowVersionApiUpgradeNotice } from '../workflow-version-api-upgrade-notice.tsx' # 高度なチャットアプリ API @@ -74,7 +75,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' タイトルを自動生成、デフォルトは`true`。 `false`に設定すると、会話のリネームAPIを呼び出し、`auto_generate`を`true`に設定することで非同期タイトル生成を実現できます。 - + }> (オプション)ワークフローID、特定のバージョンを指定するために使用、提供されない場合はデフォルトの公開バージョンを使用。 @@ -250,6 +251,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' - 400, `draft_workflow_error`, ドラフトワークフローバージョンは使用できません - 400, `workflow_id_format_error`, ワークフローID形式エラー、UUID形式が必要です - 400, `completion_request_error`, テキスト生成に失敗しました + - 403, `workflow_version_execution_not_allowed`, 現在のプランではワークフローバージョンを実行できません。有料プランにアップグレードしてください - 500, 内部サーバーエラー diff --git a/web/app/components/develop/template/template_advanced_chat.zh.mdx b/web/app/components/develop/template/template_advanced_chat.zh.mdx index c9a0ed2efe2..b37182435a9 100755 --- a/web/app/components/develop/template/template_advanced_chat.zh.mdx +++ b/web/app/components/develop/template/template_advanced_chat.zh.mdx @@ -1,5 +1,6 @@ import { CodeGroup } from '../code.tsx' import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' +import { WorkflowVersionApiUpgradeNotice } from '../workflow-version-api-upgrade-notice.tsx' # 工作流编排对话型应用 API @@ -71,7 +72,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' (选填)自动生成标题,默认 `true`。 若设置为 `false`,则可通过调用会话重命名接口并设置 `auto_generate` 为 `true` 实现异步生成标题。 - + }> (选填)工作流ID,用于指定特定版本,如果不提供则使用默认的已发布版本。 @@ -248,6 +249,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' - 400,`draft_workflow_error`,无法使用草稿工作流版本 - 400,`workflow_id_format_error`,工作流ID格式错误,需要UUID格式 - 400,`completion_request_error`,文本生成失败 + - 403,`workflow_version_execution_not_allowed`,当前套餐不支持执行工作流版本,请升级至付费套餐 - 500,服务内部异常 diff --git a/web/app/components/develop/template/template_workflow.en.mdx b/web/app/components/develop/template/template_workflow.en.mdx index daff5d5f955..dc56b1bee00 100644 --- a/web/app/components/develop/template/template_workflow.en.mdx +++ b/web/app/components/develop/template/template_workflow.en.mdx @@ -1,6 +1,6 @@ import { CodeGroup } from '../code.tsx' import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' -import WorkflowVersionApiUpgradeNotice from '../workflow-version-api-upgrade-notice.tsx' +import { WorkflowVersionApiContent, WorkflowVersionApiUpgradeNotice } from '../workflow-version-api-upgrade-notice.tsx' # Workflow App API @@ -645,6 +645,7 @@ Workflow applications offers non-session support and is ideal for translation, a name='#Execute-Specific-Workflow' titleAction={} /> + Execute a specific version of workflow by specifying the workflow ID in the path parameter. @@ -895,6 +896,7 @@ Workflow applications offers non-session support and is ideal for translation, a + --- diff --git a/web/app/components/develop/template/template_workflow.ja.mdx b/web/app/components/develop/template/template_workflow.ja.mdx index a9def5bb14e..4b104aa862a 100644 --- a/web/app/components/develop/template/template_workflow.ja.mdx +++ b/web/app/components/develop/template/template_workflow.ja.mdx @@ -1,6 +1,6 @@ import { CodeGroup } from '../code.tsx' import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' -import WorkflowVersionApiUpgradeNotice from '../workflow-version-api-upgrade-notice.tsx' +import { WorkflowVersionApiContent, WorkflowVersionApiUpgradeNotice } from '../workflow-version-api-upgrade-notice.tsx' # ワークフローアプリ API @@ -645,6 +645,7 @@ import WorkflowVersionApiUpgradeNotice from '../workflow-version-api-upgrade-not name='#Execute-Specific-Workflow' titleAction={} /> + パスパラメータでワークフローIDを指定して、特定バージョンのワークフローを実行します。 @@ -890,6 +891,7 @@ import WorkflowVersionApiUpgradeNotice from '../workflow-version-api-upgrade-not + --- diff --git a/web/app/components/develop/template/template_workflow.zh.mdx b/web/app/components/develop/template/template_workflow.zh.mdx index 3ed68af921d..b637a5f4ba6 100644 --- a/web/app/components/develop/template/template_workflow.zh.mdx +++ b/web/app/components/develop/template/template_workflow.zh.mdx @@ -1,6 +1,6 @@ import { CodeGroup } from '../code.tsx' import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' -import WorkflowVersionApiUpgradeNotice from '../workflow-version-api-upgrade-notice.tsx' +import { WorkflowVersionApiContent, WorkflowVersionApiUpgradeNotice } from '../workflow-version-api-upgrade-notice.tsx' # Workflow 应用 API @@ -635,6 +635,7 @@ Workflow 应用无会话支持,适合用于翻译/文章写作/总结 AI 等 name='#Execute-Specific-Workflow' titleAction={} /> + 执行指定版本的工作流,通过路径参数指定工作流ID。 @@ -883,6 +884,7 @@ Workflow 应用无会话支持,适合用于翻译/文章写作/总结 AI 等 + --- diff --git a/web/app/components/develop/workflow-version-api-upgrade-notice.tsx b/web/app/components/develop/workflow-version-api-upgrade-notice.tsx index bdb799f9c54..ee18b30a2ca 100644 --- a/web/app/components/develop/workflow-version-api-upgrade-notice.tsx +++ b/web/app/components/develop/workflow-version-api-upgrade-notice.tsx @@ -1,5 +1,6 @@ 'use client' +import type { PropsWithChildren } from 'react' import { useState } from 'react' import { useTranslation } from 'react-i18next' import { PlanUpgradeModal } from '@/app/components/billing/plan-upgrade-modal' @@ -7,7 +8,15 @@ import { Plan } from '@/app/components/billing/type' import UpgradeBtn from '@/app/components/billing/upgrade-btn' import { useProviderContext } from '@/context/provider-context' -const WorkflowVersionApiUpgradeNotice = () => { +export const WorkflowVersionApiContent = ({ children }: PropsWithChildren) => { + const { plan, enableBilling, isFetchedPlan } = useProviderContext() + + if (enableBilling && (!isFetchedPlan || plan.type === Plan.sandbox)) return
    + + return children +} + +export const WorkflowVersionApiUpgradeNotice = () => { const { t } = useTranslation('billing') const { plan, enableBilling, isFetchedPlan } = useProviderContext() const [isPlanUpgradeModalOpen, setIsPlanUpgradeModalOpen] = useState(false) @@ -39,5 +48,3 @@ const WorkflowVersionApiUpgradeNotice = () => { ) } - -export default WorkflowVersionApiUpgradeNotice