mirror of
https://github.com/langgenius/dify.git
synced 2026-07-21 02:28:30 +08:00
chore: chatflow api should check workflow_id and sandbox plan (#38892)
This commit is contained in:
parent
302d7b1e1b
commit
38aec8b506
@ -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)
|
||||
|
||||
@ -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. |
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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(
|
||||
<Property {...defaultProps} nameAction={<button type="button">Upgrade</button>}>
|
||||
User identifier
|
||||
</Property>,
|
||||
)
|
||||
|
||||
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(<Property {...defaultProps}>Description</Property>)
|
||||
expect(container.querySelector('li')).toBeInTheDocument()
|
||||
|
||||
@ -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(<WorkflowVersionApiContent>paid API details</WorkflowVersionApiContent>)
|
||||
|
||||
expect(screen.queryByText('paid API details')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should hide content for sandbox plans', () => {
|
||||
mockPlanType = Plan.sandbox
|
||||
|
||||
const { container } = render(
|
||||
<>
|
||||
<h2>paid API</h2>
|
||||
<WorkflowVersionApiContent>paid API details</WorkflowVersionApiContent>
|
||||
<hr />
|
||||
</>,
|
||||
)
|
||||
|
||||
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(<WorkflowVersionApiContent>paid API details</WorkflowVersionApiContent>)
|
||||
|
||||
expect(screen.getByText('paid API details')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should show content when billing is disabled', () => {
|
||||
mockPlanType = Plan.sandbox
|
||||
mockEnableBilling = false
|
||||
mockIsFetchedPlan = false
|
||||
|
||||
render(<WorkflowVersionApiContent>paid API details</WorkflowVersionApiContent>)
|
||||
|
||||
expect(screen.getByText('paid API details')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
@ -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 (
|
||||
<li className="m-0 px-0 py-4 first:pt-0 last:pb-0">
|
||||
<dl className="m-0 flex flex-wrap items-center gap-x-3 gap-y-2">
|
||||
<dt className="sr-only">Name</dt>
|
||||
<dd>
|
||||
<code>{name}</code>
|
||||
{nameAction ? <span className="ml-2 inline-flex align-middle">{nameAction}</span> : null}
|
||||
</dd>
|
||||
<dt className="sr-only">Type</dt>
|
||||
<dd className="font-mono text-xs text-zinc-400 dark:text-zinc-500">{type}</dd>
|
||||
|
||||
@ -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(
|
||||
"<Property name='workflow_id' type='string' key='workflow_id' nameAction={<WorkflowVersionApiUpgradeNotice />}>",
|
||||
)
|
||||
expect(source).not.toContain('titleAction={<WorkflowVersionApiUpgradeNotice />}')
|
||||
})
|
||||
|
||||
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')
|
||||
})
|
||||
})
|
||||
@ -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={<WorkflowVersionApiUpgradeNotice />}')
|
||||
expect(paidApi).toContain('<WorkflowVersionApiContent>\n<Row>')
|
||||
expect(paidApi).toContain('</Row>\n</WorkflowVersionApiContent>')
|
||||
})
|
||||
})
|
||||
@ -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`.
|
||||
</Property>
|
||||
<Property name='workflow_id' type='string' key='workflow_id'>
|
||||
<Property name='workflow_id' type='string' key='workflow_id' nameAction={<WorkflowVersionApiUpgradeNotice />}>
|
||||
(Optional) Workflow ID to specify a specific version, if not provided, uses the default published version.
|
||||
</Property>
|
||||
<Property name='trace_id' type='string' key='trace_id'>
|
||||
@ -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
|
||||
|
||||
</Col>
|
||||
|
||||
@ -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`に設定することで非同期タイトル生成を実現できます。
|
||||
</Property>
|
||||
<Property name='workflow_id' type='string' key='workflow_id'>
|
||||
<Property name='workflow_id' type='string' key='workflow_id' nameAction={<WorkflowVersionApiUpgradeNotice />}>
|
||||
(オプション)ワークフローID、特定のバージョンを指定するために使用、提供されない場合はデフォルトの公開バージョンを使用。
|
||||
</Property>
|
||||
<Property name='trace_id' type='string' key='trace_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, 内部サーバーエラー
|
||||
|
||||
</Col>
|
||||
|
||||
@ -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'
|
||||
<Property name='auto_generate_name' type='bool' key='auto_generate_name'>
|
||||
(选填)自动生成标题,默认 `true`。 若设置为 `false`,则可通过调用会话重命名接口并设置 `auto_generate` 为 `true` 实现异步生成标题。
|
||||
</Property>
|
||||
<Property name='workflow_id' type='string' key='workflow_id'>
|
||||
<Property name='workflow_id' type='string' key='workflow_id' nameAction={<WorkflowVersionApiUpgradeNotice />}>
|
||||
(选填)工作流ID,用于指定特定版本,如果不提供则使用默认的已发布版本。
|
||||
</Property>
|
||||
<Property name='trace_id' type='string' key='trace_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,服务内部异常
|
||||
|
||||
</Properties>
|
||||
|
||||
@ -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={<WorkflowVersionApiUpgradeNotice />}
|
||||
/>
|
||||
<WorkflowVersionApiContent>
|
||||
<Row>
|
||||
<Col>
|
||||
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
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
</WorkflowVersionApiContent>
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -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={<WorkflowVersionApiUpgradeNotice />}
|
||||
/>
|
||||
<WorkflowVersionApiContent>
|
||||
<Row>
|
||||
<Col>
|
||||
パスパラメータでワークフローIDを指定して、特定バージョンのワークフローを実行します。
|
||||
@ -890,6 +891,7 @@ import WorkflowVersionApiUpgradeNotice from '../workflow-version-api-upgrade-not
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
</WorkflowVersionApiContent>
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -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={<WorkflowVersionApiUpgradeNotice />}
|
||||
/>
|
||||
<WorkflowVersionApiContent>
|
||||
<Row>
|
||||
<Col>
|
||||
执行指定版本的工作流,通过路径参数指定工作流ID。
|
||||
@ -883,6 +884,7 @@ Workflow 应用无会话支持,适合用于翻译/文章写作/总结 AI 等
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
</WorkflowVersionApiContent>
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -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 <div aria-hidden />
|
||||
|
||||
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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user