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