mirror of
https://github.com/langgenius/dify.git
synced 2026-09-09 05:41:00 +08:00
fix(api): correct service api rate limit descriptions (#41975)
This commit is contained in:
parent
f3e5f41a3c
commit
2f00e647ef
@ -353,7 +353,7 @@ class ChatApi(Resource):
|
||||
404: "`not_found` : Conversation does not exist.",
|
||||
429: (
|
||||
"- `too_many_requests` : Too many concurrent requests for this app.\n"
|
||||
"- `rate_limit_error` : The upstream model provider rate limit was exceeded."
|
||||
"- `rate_limit_error` : The Dify Cloud workflow execution quota for this workspace has been reached."
|
||||
),
|
||||
500: "`internal_server_error` : Internal server error.",
|
||||
},
|
||||
|
||||
@ -315,8 +315,7 @@ class WorkflowRunApi(Resource):
|
||||
),
|
||||
429: (
|
||||
"- `too_many_requests` : Too many concurrent requests for this app.\n"
|
||||
"- `rate_limit_error` : The upstream model provider rate limit or the Dify Cloud workflow execution "
|
||||
"quota was exceeded."
|
||||
"- `rate_limit_error` : The Dify Cloud workflow execution quota for this workspace has been reached."
|
||||
),
|
||||
500: "`internal_server_error` : Internal server error.",
|
||||
},
|
||||
@ -430,8 +429,7 @@ class WorkflowRunByIdApi(Resource):
|
||||
404: "`not_found` : Workflow not found.",
|
||||
429: (
|
||||
"- `too_many_requests` : Too many concurrent requests for this app.\n"
|
||||
"- `rate_limit_error` : The upstream model provider rate limit or the Dify Cloud workflow execution "
|
||||
"quota was exceeded."
|
||||
"- `rate_limit_error` : The Dify Cloud workflow execution quota for this workspace has been reached."
|
||||
),
|
||||
500: "`internal_server_error` : Internal server error.",
|
||||
},
|
||||
|
||||
@ -338,7 +338,7 @@ Send a request to the chat application.
|
||||
| 401 | Unauthorized - invalid API token | |
|
||||
| 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. | |
|
||||
| 429 | - `too_many_requests` : Too many concurrent requests for this app. - `rate_limit_error` : The Dify Cloud workflow execution quota for this workspace has been reached. | |
|
||||
| 500 | `internal_server_error` : Internal server error. | |
|
||||
|
||||
### [POST] /chat-messages/{task_id}/stop
|
||||
@ -485,7 +485,7 @@ Send a request to the chat application.
|
||||
| 401 | Unauthorized - invalid API token | |
|
||||
| 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. | |
|
||||
| 429 | - `too_many_requests` : Too many concurrent requests for this app. - `rate_limit_error` : The Dify Cloud workflow execution quota for this workspace has been reached. | |
|
||||
| 500 | `internal_server_error` : Internal server error. | |
|
||||
|
||||
### [POST] /chat-messages/{task_id}/stop
|
||||
@ -2207,7 +2207,7 @@ Execute a workflow. Cannot be executed without a published workflow.
|
||||
| 400 | - `not_workflow_app` : App mode does not match the API route. - `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` : Workflow execution request failed. - `invalid_param` : Invalid parameter value. | |
|
||||
| 401 | Unauthorized - invalid API token | |
|
||||
| 403 | - `forbidden` : Token scope, app, or workspace access denied. - `trigger_workflow_service_mode_unavailable` : Trigger-entry workflows cannot be invoked through Web App, Service API, OpenAPI, or MCP. | |
|
||||
| 429 | - `too_many_requests` : Too many concurrent requests for this app. - `rate_limit_error` : The upstream model provider rate limit or the Dify Cloud workflow execution quota was exceeded. | |
|
||||
| 429 | - `too_many_requests` : Too many concurrent requests for this app. - `rate_limit_error` : The Dify Cloud workflow execution quota for this workspace has been reached. | |
|
||||
| 500 | `internal_server_error` : Internal server error. | |
|
||||
|
||||
### [GET] /workflows/run/{workflow_run_id}
|
||||
@ -2283,7 +2283,7 @@ Execute a specific workflow version identified by its ID. Useful for running a p
|
||||
| 401 | Unauthorized - invalid API token | |
|
||||
| 403 | - `forbidden` : Token scope, app, or workspace access denied. - `workflow_version_execution_not_allowed` : Workflow version execution is unavailable on the current plan. Upgrade to a paid plan. - `trigger_workflow_service_mode_unavailable` : The selected workflow version uses a trigger entry and cannot be invoked through Web App, Service API, OpenAPI, or MCP. | |
|
||||
| 404 | `not_found` : Workflow not found. | |
|
||||
| 429 | - `too_many_requests` : Too many concurrent requests for this app. - `rate_limit_error` : The upstream model provider rate limit or the Dify Cloud workflow execution quota was exceeded. | |
|
||||
| 429 | - `too_many_requests` : Too many concurrent requests for this app. - `rate_limit_error` : The Dify Cloud workflow execution quota for this workspace has been reached. | |
|
||||
| 500 | `internal_server_error` : Internal server error. | |
|
||||
|
||||
---
|
||||
|
||||
@ -36,14 +36,17 @@ from controllers.service_api.app.completion import (
|
||||
from controllers.service_api.app.error import (
|
||||
AgentNotPublishedError,
|
||||
AppUnavailableError,
|
||||
CompletionRequestError,
|
||||
ConversationCompletedError,
|
||||
NotChatAppError,
|
||||
WorkflowVersionExecutionNotAllowedError,
|
||||
)
|
||||
from controllers.web.error import InvokeRateLimitError as InvokeRateLimitHttpError
|
||||
from core.app.apps.agent_app.errors import AgentAppNotPublishedError
|
||||
from core.errors.error import QuotaExceededError
|
||||
from enums import CloudPlan, DeploymentEdition
|
||||
from graphon.model_runtime.errors.invoke import InvokeError
|
||||
from graphon.model_runtime.errors.invoke import InvokeRateLimitError as ProviderInvokeRateLimitError
|
||||
from models.base import TypeBase
|
||||
from models.enums import ConversationFromSource, EndUserType
|
||||
from models.model import App, AppMode, Conversation, EndUser, IconType, Message
|
||||
@ -553,6 +556,43 @@ class TestCompletionStopApiController:
|
||||
|
||||
|
||||
class TestChatApiController:
|
||||
@pytest.mark.parametrize(
|
||||
("source_error", "http_error", "status_code", "error_code"),
|
||||
[
|
||||
pytest.param(InvokeRateLimitError, InvokeRateLimitHttpError, 429, "rate_limit_error", id="cloud-quota"),
|
||||
pytest.param(
|
||||
ProviderInvokeRateLimitError, CompletionRequestError, 400, "completion_request_error", id="provider"
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_maps_rate_limits_by_source(
|
||||
self,
|
||||
app: Flask,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
orm_session: Session,
|
||||
source_error: type[InvokeRateLimitError | ProviderInvokeRateLimitError],
|
||||
http_error: type[InvokeRateLimitHttpError | CompletionRequestError],
|
||||
status_code: int,
|
||||
error_code: str,
|
||||
) -> None:
|
||||
generate = Mock(side_effect=source_error("rate limit reached"))
|
||||
monkeypatch.setattr(AppGenerateService, "generate", generate)
|
||||
app_model, end_user, _, _ = _persist_completion_state(orm_session, AppMode.ADVANCED_CHAT)
|
||||
|
||||
api = ChatApi()
|
||||
handler = unwrap(api.post)
|
||||
|
||||
with app.test_request_context(
|
||||
"/chat-messages", method="POST", json={"inputs": {}, "query": "hi", "response_mode": "blocking"}
|
||||
):
|
||||
with pytest.raises(http_error) as exc_info:
|
||||
handler(api, session=orm_session, app_model=app_model, end_user=end_user)
|
||||
|
||||
generate.assert_called_once()
|
||||
assert exc_info.value.code == status_code
|
||||
assert exc_info.value.error_code == error_code
|
||||
assert exc_info.value.description == "rate limit reached"
|
||||
|
||||
def test_rejects_sandbox_plan_workflow_version(
|
||||
self, app: Flask, monkeypatch: pytest.MonkeyPatch, orm_session: Session
|
||||
) -> None:
|
||||
|
||||
@ -29,6 +29,7 @@ from sqlalchemy.orm import Session, sessionmaker
|
||||
from werkzeug.exceptions import BadRequest, NotFound
|
||||
|
||||
from controllers.service_api.app.error import (
|
||||
CompletionRequestError,
|
||||
NotWorkflowAppError,
|
||||
TriggerWorkflowServiceModeUnavailableError,
|
||||
WorkflowVersionExecutionNotAllowedError,
|
||||
@ -49,6 +50,7 @@ from controllers.web.error import InvokeRateLimitError as InvokeRateLimitHttpErr
|
||||
from core.app.entities.app_invoke_entities import InvokeFrom
|
||||
from enums import CloudPlan, DeploymentEdition
|
||||
from graphon.enums import WorkflowExecutionStatus
|
||||
from graphon.model_runtime.errors.invoke import InvokeRateLimitError as ProviderInvokeRateLimitError
|
||||
from models import Account
|
||||
from models.enums import CreatorUserRole, WorkflowRunTriggeredFrom
|
||||
from models.model import App, AppMode, EndUser
|
||||
@ -556,21 +558,51 @@ class TestWorkflowRunApi:
|
||||
handler(api, session=sqlite_session, app_model=app_model, end_user=end_user)
|
||||
|
||||
@pytest.mark.parametrize("sqlite_session", [()], indirect=True)
|
||||
def test_rate_limit(self, app: Flask, monkeypatch: pytest.MonkeyPatch, sqlite_session: Session) -> None:
|
||||
@pytest.mark.parametrize("api_class", [WorkflowRunApi, WorkflowRunByIdApi])
|
||||
@pytest.mark.parametrize(
|
||||
("source_error", "http_error", "status_code", "error_code"),
|
||||
[
|
||||
pytest.param(InvokeRateLimitError, InvokeRateLimitHttpError, 429, "rate_limit_error", id="cloud-quota"),
|
||||
pytest.param(
|
||||
ProviderInvokeRateLimitError, CompletionRequestError, 400, "completion_request_error", id="provider"
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_maps_rate_limits_by_source(
|
||||
self,
|
||||
app: Flask,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
sqlite_session: Session,
|
||||
config_overrides: Callable[..., None],
|
||||
api_class: type[WorkflowRunApi | WorkflowRunByIdApi],
|
||||
source_error: type[InvokeRateLimitError | ProviderInvokeRateLimitError],
|
||||
http_error: type[InvokeRateLimitHttpError | CompletionRequestError],
|
||||
status_code: int,
|
||||
error_code: str,
|
||||
) -> None:
|
||||
config_overrides(DEPLOYMENT_EDITION=DeploymentEdition.CLOUD)
|
||||
monkeypatch.setattr(
|
||||
AppGenerateService,
|
||||
"generate",
|
||||
lambda *_args, **_kwargs: (_ for _ in ()).throw(InvokeRateLimitError("slow")),
|
||||
BillingService, "get_info", Mock(return_value={"subscription": {"plan": CloudPlan.PROFESSIONAL}})
|
||||
)
|
||||
generate = Mock(side_effect=source_error("rate limit reached"))
|
||||
monkeypatch.setattr(AppGenerateService, "generate", generate)
|
||||
|
||||
api = WorkflowRunApi()
|
||||
api = api_class()
|
||||
handler = unwrap(api.post)
|
||||
app_model = _make_app_model()
|
||||
end_user = _make_end_user()
|
||||
kwargs: dict[str, str] = {"workflow_id": str(uuid.uuid4())} if isinstance(api, WorkflowRunByIdApi) else {}
|
||||
|
||||
with app.test_request_context("/workflows/run", method="POST", json={"inputs": {}}):
|
||||
with pytest.raises(InvokeRateLimitHttpError):
|
||||
handler(api, session=sqlite_session, app_model=app_model, end_user=end_user)
|
||||
with app.test_request_context(
|
||||
"/workflows/run", method="POST", json={"inputs": {}, "response_mode": "blocking"}
|
||||
):
|
||||
with pytest.raises(http_error) as exc_info:
|
||||
handler(api, session=sqlite_session, app_model=app_model, end_user=end_user, **kwargs)
|
||||
|
||||
generate.assert_called_once()
|
||||
assert exc_info.value.code == status_code
|
||||
assert exc_info.value.error_code == error_code
|
||||
assert exc_info.value.description == "rate limit reached"
|
||||
|
||||
def test_trigger_workflow_returns_stable_unavailable_error(
|
||||
self,
|
||||
|
||||
@ -298,9 +298,10 @@ def test_service_openapi_documents_decorator_user_contracts():
|
||||
assert schema["properties"]["user"] == USER_PROPERTY_SCHEMA
|
||||
assert "user" in schema["required"]
|
||||
|
||||
for path in ("/workflows/run", "/workflows/{workflow_id}/run"):
|
||||
for path in ("/chat-messages", "/workflows/run", "/workflows/{workflow_id}/run"):
|
||||
rate_limit_description = paths[path]["post"]["responses"]["429"]["description"]
|
||||
assert "upstream model provider rate limit" in rate_limit_description
|
||||
assert "upstream model provider rate limit" not in rate_limit_description
|
||||
assert "too_many_requests" in rate_limit_description
|
||||
assert "Dify Cloud workflow execution quota" in rate_limit_description
|
||||
|
||||
task_stop_user_descriptions = {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user