diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml index 4ce121ba60..c40ca5c1ea 100644 --- a/.github/workflows/style.yml +++ b/.github/workflows/style.yml @@ -47,6 +47,10 @@ jobs: if: steps.changed-files.outputs.any_changed == 'true' run: uv run --directory api --dev lint-imports + - name: Run Response Contract Linter + if: steps.changed-files.outputs.any_changed == 'true' + run: uv run --project api --dev python api/dev/lint_response_contracts.py --fail-on-mismatch + - name: Run Type Checks if: steps.changed-files.outputs.any_changed == 'true' run: make type-check-core diff --git a/api/controllers/console/apikey.py b/api/controllers/console/apikey.py index 6463b022b5..aca22d5c5a 100644 --- a/api/controllers/console/apikey.py +++ b/api/controllers/console/apikey.py @@ -146,7 +146,7 @@ class BaseApiKeyResource(Resource): db.session.execute(delete(ApiToken).where(ApiToken.id == api_key_id)) db.session.commit() - return {"result": "success"}, 204 + return "", 204 @console_ns.route("/apps//api-keys") diff --git a/api/controllers/console/app/annotation.py b/api/controllers/console/app/annotation.py index cfeaec4af9..bf8b57685f 100644 --- a/api/controllers/console/app/annotation.py +++ b/api/controllers/console/app/annotation.py @@ -269,12 +269,12 @@ class AnnotationApi(Resource): "message": "annotation_ids are required if the parameter is provided.", }, 400 - result = AppAnnotationService.delete_app_annotations_in_batch(str(app_id), annotation_ids) - return result, 204 + AppAnnotationService.delete_app_annotations_in_batch(str(app_id), annotation_ids) + return "", 204 # If no annotation_ids are provided, handle clearing all annotations else: AppAnnotationService.clear_all_annotations(str(app_id)) - return {"result": "success"}, 204 + return "", 204 @console_ns.route("/apps//annotations/export") @@ -335,7 +335,7 @@ class AnnotationUpdateDeleteApi(Resource): @edit_permission_required def delete(self, app_id: UUID, annotation_id: UUID): AppAnnotationService.delete_app_annotation(str(app_id), str(annotation_id)) - return {"result": "success"}, 204 + return "", 204 @console_ns.route("/apps//annotations/batch-import") diff --git a/api/controllers/console/app/app.py b/api/controllers/console/app/app.py index 28d16f852b..6026be9bf9 100644 --- a/api/controllers/console/app/app.py +++ b/api/controllers/console/app/app.py @@ -633,7 +633,7 @@ class AppApi(Resource): app_service = AppService() app_service.delete_app(app_model) - return {"result": "success"}, 204 + return "", 204 @console_ns.route("/apps//copy") diff --git a/api/controllers/console/app/conversation.py b/api/controllers/console/app/conversation.py index c7347933cb..1978018366 100644 --- a/api/controllers/console/app/conversation.py +++ b/api/controllers/console/app/conversation.py @@ -29,9 +29,6 @@ from fields.conversation_fields import ( from fields.conversation_fields import ( ConversationWithSummaryPagination as ConversationWithSummaryPaginationResponse, ) -from fields.conversation_fields import ( - ResultResponse, -) from libs.datetime_utils import naive_utc_now, parse_time_range from libs.login import current_account_with_tenant, login_required from models import Conversation, EndUser, Message, MessageAnnotation @@ -77,7 +74,6 @@ register_schema_models( ConversationMessageDetailResponse, ConversationWithSummaryPaginationResponse, ConversationDetailResponse, - ResultResponse, CompletionConversationQuery, ChatConversationQuery, ) @@ -194,7 +190,7 @@ class CompletionConversationDetailApi(Resource): except ConversationNotExistsError: raise NotFound("Conversation Not Exists.") - return ResultResponse(result="success").model_dump(mode="json"), 204 + return "", 204 @console_ns.route("/apps//chat-conversations") @@ -347,7 +343,7 @@ class ChatConversationDetailApi(Resource): except ConversationNotExistsError: raise NotFound("Conversation Not Exists.") - return ResultResponse(result="success").model_dump(mode="json"), 204 + return "", 204 def _get_conversation(app_model, conversation_id): diff --git a/api/controllers/console/app/ops_trace.py b/api/controllers/console/app/ops_trace.py index 41acf39541..a1123a580e 100644 --- a/api/controllers/console/app/ops_trace.py +++ b/api/controllers/console/app/ops_trace.py @@ -128,6 +128,6 @@ class TraceAppConfigApi(Resource): result = OpsService.delete_tracing_app_config(app_id=app_model.id, tracing_provider=args.tracing_provider) if not result: raise TracingConfigNotExist() - return {"result": "success"}, 204 + return "", 204 except Exception as e: raise BadRequest(str(e)) diff --git a/api/controllers/console/app/workflow_comment.py b/api/controllers/console/app/workflow_comment.py index f011f576fd..27009953b7 100644 --- a/api/controllers/console/app/workflow_comment.py +++ b/api/controllers/console/app/workflow_comment.py @@ -311,7 +311,7 @@ class WorkflowCommentDetailApi(Resource): user_id=current_user.id, ) - return {"result": "success"}, 204 + return "", 204 @console_ns.route("/apps//workflow/comments//resolve") @@ -431,7 +431,7 @@ class WorkflowCommentReplyDetailApi(Resource): user_id=current_user.id, ) - return {"result": "success"}, 204 + return "", 204 @console_ns.route("/apps//workflow/comments/mention-users") diff --git a/api/controllers/console/auth/data_source_bearer_auth.py b/api/controllers/console/auth/data_source_bearer_auth.py index 2e3b8d2295..3f0650389f 100644 --- a/api/controllers/console/auth/data_source_bearer_auth.py +++ b/api/controllers/console/auth/data_source_bearer_auth.py @@ -93,4 +93,4 @@ class ApiKeyAuthDataSourceBindingDelete(Resource): ApiKeyAuthService.delete_provider_auth(current_tenant_id, binding_id) - return {"result": "success"}, 204 + return "", 204 diff --git a/api/controllers/console/datasets/datasets.py b/api/controllers/console/datasets/datasets.py index 0c5bf88a5a..e1666d92d1 100644 --- a/api/controllers/console/datasets/datasets.py +++ b/api/controllers/console/datasets/datasets.py @@ -535,7 +535,7 @@ class DatasetApi(Resource): try: if DatasetService.delete_dataset(dataset_id_str, current_user): DatasetPermissionService.clear_partial_member_list(dataset_id_str) - return {"result": "success"}, 204 + return "", 204 else: raise NotFound("Dataset not found.") except services.errors.dataset.DatasetInUseError: @@ -873,7 +873,7 @@ class DatasetApiDeleteApi(Resource): db.session.delete(key) db.session.commit() - return {"result": "success"}, 204 + return "", 204 @console_ns.route("/datasets//api-keys/") diff --git a/api/controllers/console/datasets/datasets_document.py b/api/controllers/console/datasets/datasets_document.py index d98da48daa..fabd61e6b0 100644 --- a/api/controllers/console/datasets/datasets_document.py +++ b/api/controllers/console/datasets/datasets_document.py @@ -504,7 +504,7 @@ class DatasetDocumentListApi(Resource): except services.errors.document.DocumentIndexingError: raise DocumentIndexingError("Cannot delete document during indexing.") - return {"result": "success"}, 204 + return "", 204 @console_ns.route("/datasets/init") @@ -966,7 +966,7 @@ class DocumentApi(DocumentResource): except services.errors.document.DocumentIndexingError: raise DocumentIndexingError("Cannot delete document during indexing.") - return {"result": "success"}, 204 + return "", 204 @console_ns.route("/datasets//documents//download") @@ -1204,7 +1204,7 @@ class DocumentPauseApi(DocumentResource): except services.errors.document.DocumentIndexingError: raise DocumentIndexingError("Cannot pause completed document.") - return {"result": "success"}, 204 + return "", 204 @console_ns.route("/datasets//documents//processing/resume") @@ -1236,7 +1236,7 @@ class DocumentRecoverApi(DocumentResource): except services.errors.document.DocumentIndexingError: raise DocumentIndexingError("Document is not in paused status.") - return {"result": "success"}, 204 + return "", 204 @console_ns.route("/datasets//retry") @@ -1279,7 +1279,7 @@ class DocumentRetryApi(DocumentResource): # retry document DocumentService.retry_document(dataset_id, retry_documents) - return {"result": "success"}, 204 + return "", 204 @console_ns.route("/datasets//documents//rename") diff --git a/api/controllers/console/datasets/datasets_segments.py b/api/controllers/console/datasets/datasets_segments.py index 2eaa64994e..1d3bc96c1b 100644 --- a/api/controllers/console/datasets/datasets_segments.py +++ b/api/controllers/console/datasets/datasets_segments.py @@ -251,7 +251,7 @@ class DatasetDocumentSegmentListApi(Resource): except services.errors.account.NoPermissionError as e: raise Forbidden(str(e)) SegmentService.delete_segments(segment_ids, document, dataset) - return {"result": "success"}, 204 + return "", 204 @console_ns.route("/datasets//documents//segment/") @@ -467,7 +467,7 @@ class DatasetDocumentSegmentUpdateApi(Resource): except services.errors.account.NoPermissionError as e: raise Forbidden(str(e)) SegmentService.delete_segment(segment, document, dataset) - return {"result": "success"}, 204 + return "", 204 @console_ns.route( @@ -754,7 +754,7 @@ class ChildChunkUpdateApi(Resource): SegmentService.delete_child_chunk(child_chunk, dataset) except ChildChunkDeleteIndexServiceError as e: raise ChildChunkDeleteIndexError(str(e)) - return {"result": "success"}, 204 + return "", 204 @setup_required @login_required diff --git a/api/controllers/console/datasets/external.py b/api/controllers/console/datasets/external.py index 37cee1c17a..d1cdc15d0b 100644 --- a/api/controllers/console/datasets/external.py +++ b/api/controllers/console/datasets/external.py @@ -218,7 +218,7 @@ class ExternalApiTemplateApi(Resource): raise Forbidden() ExternalDatasetService.delete_external_knowledge_api(current_tenant_id, external_knowledge_api_id) - return {"result": "success"}, 204 + return "", 204 @console_ns.route("/datasets/external-knowledge-api//use-check") diff --git a/api/controllers/console/explore/conversation.py b/api/controllers/console/explore/conversation.py index 826610acda..ae32571219 100644 --- a/api/controllers/console/explore/conversation.py +++ b/api/controllers/console/explore/conversation.py @@ -105,7 +105,7 @@ class ConversationApi(InstalledAppResource): except ConversationNotExistsError: raise NotFound("Conversation Not Exists.") - return ResultResponse(result="success").model_dump(mode="json"), 204 + return "", 204 @console_ns.route( diff --git a/api/controllers/console/explore/installed_app.py b/api/controllers/console/explore/installed_app.py index fb8d35013f..4ad3dbc85f 100644 --- a/api/controllers/console/explore/installed_app.py +++ b/api/controllers/console/explore/installed_app.py @@ -270,7 +270,7 @@ class InstalledAppApi(InstalledAppResource): db.session.delete(installed_app) db.session.commit() - return {"result": "success", "message": "App uninstalled successfully"}, 204 + return "", 204 @console_ns.response(200, "Success", console_ns.models[SimpleResultMessageResponse.__name__]) def patch(self, installed_app): diff --git a/api/controllers/console/explore/saved_message.py b/api/controllers/console/explore/saved_message.py index 51e73049a4..09f214bd2b 100644 --- a/api/controllers/console/explore/saved_message.py +++ b/api/controllers/console/explore/saved_message.py @@ -76,4 +76,4 @@ class SavedMessageApi(InstalledAppResource): SavedMessageService.delete(app_model, current_user, message_id) - return ResultResponse(result="success").model_dump(mode="json"), 204 + return "", 204 diff --git a/api/controllers/console/extension.py b/api/controllers/console/extension.py index e53bb95c24..35e62e3c7e 100644 --- a/api/controllers/console/extension.py +++ b/api/controllers/console/extension.py @@ -204,4 +204,4 @@ class APIBasedExtensionDetailAPI(Resource): APIBasedExtensionService.delete(extension_data_from_db) - return {"result": "success"}, 204 + return "", 204 diff --git a/api/controllers/console/workspace/model_providers.py b/api/controllers/console/workspace/model_providers.py index f00a7e5c79..221cb3e406 100644 --- a/api/controllers/console/workspace/model_providers.py +++ b/api/controllers/console/workspace/model_providers.py @@ -194,7 +194,7 @@ class ModelProviderCredentialApi(Resource): tenant_id=current_tenant_id, provider=provider, credential_id=args.credential_id ) - return {"result": "success"}, 204 + return "", 204 @console_ns.route("/workspaces/current/model-providers//credentials/switch") diff --git a/api/controllers/console/workspace/models.py b/api/controllers/console/workspace/models.py index a24d6d0f7d..c23207e402 100644 --- a/api/controllers/console/workspace/models.py +++ b/api/controllers/console/workspace/models.py @@ -259,7 +259,7 @@ class ModelProviderModelApi(Resource): tenant_id=tenant_id, provider=provider, model=args.model, model_type=args.model_type ) - return {"result": "success"}, 204 + return "", 204 @console_ns.route("/workspaces/current/model-providers//models/credentials") @@ -395,7 +395,7 @@ class ModelProviderModelCredentialApi(Resource): credential_id=args.credential_id, ) - return {"result": "success"}, 204 + return "", 204 @console_ns.route("/workspaces/current/model-providers//models/credentials/switch") diff --git a/api/controllers/web/conversation.py b/api/controllers/web/conversation.py index 6afa00c727..a99adb391f 100644 --- a/api/controllers/web/conversation.py +++ b/api/controllers/web/conversation.py @@ -136,7 +136,7 @@ class ConversationApi(WebApiResource): ConversationService.delete(app_model, conversation_id, end_user) except ConversationNotExistsError: raise NotFound("Conversation Not Exists.") - return ResultResponse(result="success").model_dump(mode="json"), 204 + return "", 204 @web_ns.route("/conversations//name") diff --git a/api/controllers/web/saved_message.py b/api/controllers/web/saved_message.py index b1382efbd8..e307367b64 100644 --- a/api/controllers/web/saved_message.py +++ b/api/controllers/web/saved_message.py @@ -112,4 +112,4 @@ class SavedMessageApi(WebApiResource): SavedMessageService.delete(app_model, end_user, message_id) - return ResultResponse(result="success").model_dump(mode="json"), 204 + return "", 204 diff --git a/api/tests/test_containers_integration_tests/controllers/console/explore/test_conversation.py b/api/tests/test_containers_integration_tests/controllers/console/explore/test_conversation.py index 917aa35fe6..b5f5917ee9 100644 --- a/api/tests/test_containers_integration_tests/controllers/console/explore/test_conversation.py +++ b/api/tests/test_containers_integration_tests/controllers/console/explore/test_conversation.py @@ -128,7 +128,7 @@ class TestConversationApi: body, status = result assert status == 204 - assert body["result"] == "success" + assert body == "" def test_delete_not_found(self, app: Flask, chat_app, user): api = conversation_module.ConversationApi() diff --git a/api/tests/test_containers_integration_tests/controllers/web/test_conversation.py b/api/tests/test_containers_integration_tests/controllers/web/test_conversation.py index c34da27ebe..0ec399ba2b 100644 --- a/api/tests/test_containers_integration_tests/controllers/web/test_conversation.py +++ b/api/tests/test_containers_integration_tests/controllers/web/test_conversation.py @@ -81,7 +81,7 @@ class TestConversationApi: result, status = ConversationApi().delete(_chat_app(), _end_user(), c_id) assert status == 204 - assert result["result"] == "success" + assert result == "" @patch("controllers.web.conversation.ConversationService.delete", side_effect=ConversationNotExistsError()) def test_delete_not_found(self, mock_delete: MagicMock, app: Flask) -> None: diff --git a/api/tests/unit_tests/controllers/console/datasets/test_datasets.py b/api/tests/unit_tests/controllers/console/datasets/test_datasets.py index e28d68ee5a..4b0dff037f 100644 --- a/api/tests/unit_tests/controllers/console/datasets/test_datasets.py +++ b/api/tests/unit_tests/controllers/console/datasets/test_datasets.py @@ -873,7 +873,7 @@ class TestDatasetApiDelete: result, status = method(api, dataset_id) assert status == 204 - assert result == {"result": "success"} + assert result == "" def test_delete_forbidden_no_permission(self, app: Flask): api = DatasetApi() @@ -1687,7 +1687,7 @@ class TestDatasetApiDeleteApi: response, status = method(api, "api-key-id") assert status == 204 - assert response["result"] == "success" + assert response == "" def test_delete_key_not_found(self, app: Flask): api = DatasetApiDeleteApi() diff --git a/api/tests/unit_tests/controllers/console/datasets/test_datasets_segments.py b/api/tests/unit_tests/controllers/console/datasets/test_datasets_segments.py index 66d257ee66..eb99c4eab3 100644 --- a/api/tests/unit_tests/controllers/console/datasets/test_datasets_segments.py +++ b/api/tests/unit_tests/controllers/console/datasets/test_datasets_segments.py @@ -940,7 +940,7 @@ class TestChildChunkUpdateApi: response, status = method(api, "ds-1", "doc-1", "seg-1", "cc-1") assert status == 204 - assert response["result"] == "success" + assert response == "" def test_delete_child_chunk_index_error(self, app: Flask): api = ChildChunkUpdateApi() diff --git a/api/tests/unit_tests/controllers/console/explore/test_installed_app.py b/api/tests/unit_tests/controllers/console/explore/test_installed_app.py index ec82803be4..47ac8d8f3f 100644 --- a/api/tests/unit_tests/controllers/console/explore/test_installed_app.py +++ b/api/tests/unit_tests/controllers/console/explore/test_installed_app.py @@ -327,7 +327,7 @@ class TestInstalledAppApi: resp, status = method(installed_app) assert status == 204 - assert resp["result"] == "success" + assert resp == "" def test_delete_owned_by_current_tenant(self, tenant_id: str): api = module.InstalledAppApi() diff --git a/api/tests/unit_tests/controllers/console/explore/test_saved_message.py b/api/tests/unit_tests/controllers/console/explore/test_saved_message.py index 49e5695e60..00c0d91d1d 100644 --- a/api/tests/unit_tests/controllers/console/explore/test_saved_message.py +++ b/api/tests/unit_tests/controllers/console/explore/test_saved_message.py @@ -141,7 +141,7 @@ class TestSavedMessageApi: delete_mock.assert_called_once() assert status == 204 - assert result == {"result": "success"} + assert result == "" def test_delete_not_completion_app(self): api = module.SavedMessageApi() diff --git a/api/tests/unit_tests/controllers/console/test_extension.py b/api/tests/unit_tests/controllers/console/test_extension.py index 2a33ee12be..20fc62073b 100644 --- a/api/tests/unit_tests/controllers/console/test_extension.py +++ b/api/tests/unit_tests/controllers/console/test_extension.py @@ -242,5 +242,5 @@ def test_api_based_extension_detail_delete_removes_extension(app: Flask, monkeyp response, status = APIBasedExtensionDetailAPI().delete(extension_id) delete_mock.assert_called_once_with(existing_extension) - assert response == {"result": "success"} assert status == 204 + assert response == "" diff --git a/api/tests/unit_tests/controllers/console/workspace/test_model_providers.py b/api/tests/unit_tests/controllers/console/workspace/test_model_providers.py index e836a3cc55..a81a8e1b1a 100644 --- a/api/tests/unit_tests/controllers/console/workspace/test_model_providers.py +++ b/api/tests/unit_tests/controllers/console/workspace/test_model_providers.py @@ -179,8 +179,8 @@ class TestModelProviderCredentialApi: ): result, status = method(api, provider="openai") - assert result["result"] == "success" assert status == 204 + assert result == "" class TestModelProviderCredentialSwitchApi: diff --git a/api/tests/unit_tests/controllers/web/test_saved_message.py b/api/tests/unit_tests/controllers/web/test_saved_message.py index 3d55804912..5de740192f 100644 --- a/api/tests/unit_tests/controllers/web/test_saved_message.py +++ b/api/tests/unit_tests/controllers/web/test_saved_message.py @@ -94,4 +94,4 @@ class TestSavedMessageApi: result, status = SavedMessageApi().delete(_completion_app(), _end_user(), msg_id) assert status == 204 - assert result["result"] == "success" + assert result == ""