Explorar o código

Feat: optimize error desc (#152)

John Wang hai 1 ano
pai
achega
9a5ae9f51f

+ 1 - 1
api/controllers/console/app/__init__.py

@@ -17,6 +17,6 @@ def _get_app(app_id, mode=None):
         raise NotFound("App not found")
 
     if mode and app.mode != mode:
-        raise AppUnavailableError()
+        raise NotFound("The {} app not found".format(mode))
 
     return app

+ 8 - 6
api/controllers/console/app/error.py

@@ -9,31 +9,33 @@ class AppNotFoundError(BaseHTTPException):
 
 class ProviderNotInitializeError(BaseHTTPException):
     error_code = 'provider_not_initialize'
-    description = "Provider Token not initialize."
+    description = "No valid model provider credentials found. " \
+                  "Please go to Settings -> Model Provider to complete your provider credentials."
     code = 400
 
 
 class ProviderQuotaExceededError(BaseHTTPException):
     error_code = 'provider_quota_exceeded'
-    description = "Provider quota exceeded."
+    description = "Your quota for Dify Hosted OpenAI has been exhausted. " \
+                  "Please go to Settings -> Model Provider to complete your own provider credentials."
     code = 400
 
 
 class ProviderModelCurrentlyNotSupportError(BaseHTTPException):
     error_code = 'model_currently_not_support'
-    description = "GPT-4 currently not support."
+    description = "Dify Hosted OpenAI trial currently not support the GPT-4 model."
     code = 400
 
 
 class ConversationCompletedError(BaseHTTPException):
     error_code = 'conversation_completed'
-    description = "Conversation was completed."
+    description = "The conversation has ended. Please start a new conversation."
     code = 400
 
 
 class AppUnavailableError(BaseHTTPException):
     error_code = 'app_unavailable'
-    description = "App unavailable."
+    description = "App unavailable, please check your app configurations."
     code = 400
 
 
@@ -45,5 +47,5 @@ class CompletionRequestError(BaseHTTPException):
 
 class AppMoreLikeThisDisabledError(BaseHTTPException):
     error_code = 'app_more_like_this_disabled'
-    description = "More like this disabled."
+    description = "The 'More like this' feature is disabled. Please refresh your page."
     code = 403

+ 11 - 2
api/controllers/console/datasets/datasets_document.py

@@ -10,13 +10,14 @@ from werkzeug.exceptions import NotFound, Forbidden
 
 import services
 from controllers.console import api
-from controllers.console.app.error import ProviderNotInitializeError
+from controllers.console.app.error import ProviderNotInitializeError, ProviderQuotaExceededError, \
+    ProviderModelCurrentlyNotSupportError
 from controllers.console.datasets.error import DocumentAlreadyFinishedError, InvalidActionError, DocumentIndexingError, \
     InvalidMetadataError, ArchivedDocumentImmutableError
 from controllers.console.setup import setup_required
 from controllers.console.wraps import account_initialization_required
 from core.indexing_runner import IndexingRunner
-from core.llm.error import ProviderTokenNotInitError
+from core.llm.error import ProviderTokenNotInitError, QuotaExceededError, ModelCurrentlyNotSupportError
 from extensions.ext_redis import redis_client
 from libs.helper import TimestampField
 from extensions.ext_database import db
@@ -222,6 +223,10 @@ class DatasetDocumentListApi(Resource):
             document = DocumentService.save_document_with_dataset_id(dataset, args, current_user)
         except ProviderTokenNotInitError:
             raise ProviderNotInitializeError()
+        except QuotaExceededError:
+            raise ProviderQuotaExceededError()
+        except ModelCurrentlyNotSupportError:
+            raise ProviderModelCurrentlyNotSupportError()
 
         return document
 
@@ -259,6 +264,10 @@ class DatasetInitApi(Resource):
             )
         except ProviderTokenNotInitError:
             raise ProviderNotInitializeError()
+        except QuotaExceededError:
+            raise ProviderQuotaExceededError()
+        except ModelCurrentlyNotSupportError:
+            raise ProviderModelCurrentlyNotSupportError()
 
         response = {
             'dataset': dataset,

+ 8 - 8
api/controllers/console/datasets/error.py

@@ -3,7 +3,7 @@ from libs.exception import BaseHTTPException
 
 class NoFileUploadedError(BaseHTTPException):
     error_code = 'no_file_uploaded'
-    description = "No file uploaded."
+    description = "Please upload your file."
     code = 400
 
 
@@ -27,25 +27,25 @@ class UnsupportedFileTypeError(BaseHTTPException):
 
 class HighQualityDatasetOnlyError(BaseHTTPException):
     error_code = 'high_quality_dataset_only'
-    description = "High quality dataset only."
+    description = "Current operation only supports 'high-quality' datasets."
     code = 400
 
 
 class DatasetNotInitializedError(BaseHTTPException):
     error_code = 'dataset_not_initialized'
-    description = "Dataset not initialized."
+    description = "The dataset is still being initialized or indexing. Please wait a moment."
     code = 400
 
 
 class ArchivedDocumentImmutableError(BaseHTTPException):
     error_code = 'archived_document_immutable'
-    description = "Cannot process an archived document."
+    description = "The archived document is not editable."
     code = 403
 
 
 class DatasetNameDuplicateError(BaseHTTPException):
     error_code = 'dataset_name_duplicate'
-    description = "Dataset name already exists."
+    description = "The dataset name already exists. Please modify your dataset name."
     code = 409
 
 
@@ -57,17 +57,17 @@ class InvalidActionError(BaseHTTPException):
 
 class DocumentAlreadyFinishedError(BaseHTTPException):
     error_code = 'document_already_finished'
-    description = "Document already finished."
+    description = "The document has been processed. Please refresh the page or go to the document details."
     code = 400
 
 
 class DocumentIndexingError(BaseHTTPException):
     error_code = 'document_indexing'
-    description = "Document indexing."
+    description = "The document is being processed and cannot be edited."
     code = 400
 
 
 class InvalidMetadataError(BaseHTTPException):
     error_code = 'invalid_metadata'
-    description = "Invalid metadata."
+    description = "The metadata content is incorrect. Please check and verify."
     code = 400

+ 9 - 0
api/controllers/console/datasets/hit_testing.py

@@ -6,9 +6,12 @@ from werkzeug.exceptions import InternalServerError, NotFound, Forbidden
 
 import services
 from controllers.console import api
+from controllers.console.app.error import ProviderNotInitializeError, ProviderQuotaExceededError, \
+    ProviderModelCurrentlyNotSupportError
 from controllers.console.datasets.error import HighQualityDatasetOnlyError, DatasetNotInitializedError
 from controllers.console.setup import setup_required
 from controllers.console.wraps import account_initialization_required
+from core.llm.error import ProviderTokenNotInitError, QuotaExceededError, ModelCurrentlyNotSupportError
 from libs.helper import TimestampField
 from services.dataset_service import DatasetService
 from services.hit_testing_service import HitTestingService
@@ -92,6 +95,12 @@ class HitTestingApi(Resource):
             return {"query": response['query'], 'records': marshal(response['records'], hit_testing_record_fields)}
         except services.errors.index.IndexNotInitializedError:
             raise DatasetNotInitializedError()
+        except ProviderTokenNotInitError:
+            raise ProviderNotInitializeError()
+        except QuotaExceededError:
+            raise ProviderQuotaExceededError()
+        except ModelCurrentlyNotSupportError:
+            raise ProviderModelCurrentlyNotSupportError()
         except Exception as e:
             logging.exception("Hit testing failed.")
             raise InternalServerError(str(e))

+ 3 - 2
api/controllers/console/error.py

@@ -3,13 +3,14 @@ from libs.exception import BaseHTTPException
 
 class AlreadySetupError(BaseHTTPException):
     error_code = 'already_setup'
-    description = "Application already setup."
+    description = "Dify has been successfully installed. Please refresh the page or return to the dashboard homepage."
     code = 403
 
 
 class NotSetupError(BaseHTTPException):
     error_code = 'not_setup'
-    description = "Application not setup."
+    description = "Dify has not been initialized and installed yet. " \
+                  "Please proceed with the initialization and installation process first."
     code = 401
 
 

+ 2 - 2
api/controllers/console/workspace/error.py

@@ -21,11 +21,11 @@ class InvalidInvitationCodeError(BaseHTTPException):
 
 class AccountAlreadyInitedError(BaseHTTPException):
     error_code = 'account_already_inited'
-    description = "Account already inited."
+    description = "The account has been initialized. Please refresh the page."
     code = 400
 
 
 class AccountNotInitializedError(BaseHTTPException):
     error_code = 'account_not_initialized'
-    description = "Account not initialized."
+    description = "The account has not been initialized yet. Please proceed with the initialization process first."
     code = 400

+ 2 - 2
api/controllers/console/workspace/providers.py

@@ -90,8 +90,8 @@ class ProviderTokenApi(Resource):
                     configs=args['token']
                 )
                 token_is_valid = True
-            except ValidateFailedError:
-                token_is_valid = False
+            except ValidateFailedError as ex:
+                raise ValueError(str(ex))
 
             base64_encrypted_token = ProviderService.get_encrypted_token(
                 tenant=current_user.current_tenant,

+ 9 - 7
api/controllers/service_api/app/error.py

@@ -4,43 +4,45 @@ from libs.exception import BaseHTTPException
 
 class AppUnavailableError(BaseHTTPException):
     error_code = 'app_unavailable'
-    description = "App unavailable."
+    description = "App unavailable, please check your app configurations."
     code = 400
 
 
 class NotCompletionAppError(BaseHTTPException):
     error_code = 'not_completion_app'
-    description = "Not Completion App"
+    description = "Please check if your Completion app mode matches the right API route."
     code = 400
 
 
 class NotChatAppError(BaseHTTPException):
     error_code = 'not_chat_app'
-    description = "Not Chat App"
+    description = "Please check if your Chat app mode matches the right API route."
     code = 400
 
 
 class ConversationCompletedError(BaseHTTPException):
     error_code = 'conversation_completed'
-    description = "Conversation Completed."
+    description = "The conversation has ended. Please start a new conversation."
     code = 400
 
 
 class ProviderNotInitializeError(BaseHTTPException):
     error_code = 'provider_not_initialize'
-    description = "Provider Token not initialize."
+    description = "No valid model provider credentials found. " \
+                  "Please go to Settings -> Model Provider to complete your provider credentials."
     code = 400
 
 
 class ProviderQuotaExceededError(BaseHTTPException):
     error_code = 'provider_quota_exceeded'
-    description = "Provider quota exceeded."
+    description = "Your quota for Dify Hosted OpenAI has been exhausted. " \
+                  "Please go to Settings -> Model Provider to complete your own provider credentials."
     code = 400
 
 
 class ProviderModelCurrentlyNotSupportError(BaseHTTPException):
     error_code = 'model_currently_not_support'
-    description = "GPT-4 currently not support."
+    description = "Dify Hosted OpenAI trial currently not support the GPT-4 model."
     code = 400
 
 

+ 1 - 1
api/controllers/service_api/dataset/error.py

@@ -16,5 +16,5 @@ class DocumentIndexingError(BaseHTTPException):
 
 class DatasetNotInitedError(BaseHTTPException):
     error_code = 'dataset_not_inited'
-    description = "Dataset not inited."
+    description = "The dataset is still being initialized or indexing. Please wait a moment."
     code = 403

+ 11 - 9
api/controllers/web/error.py

@@ -4,43 +4,45 @@ from libs.exception import BaseHTTPException
 
 class AppUnavailableError(BaseHTTPException):
     error_code = 'app_unavailable'
-    description = "App unavailable."
+    description = "App unavailable, please check your app configurations."
     code = 400
 
 
 class NotCompletionAppError(BaseHTTPException):
     error_code = 'not_completion_app'
-    description = "Not Completion App"
+    description = "Please check if your Completion app mode matches the right API route."
     code = 400
 
 
 class NotChatAppError(BaseHTTPException):
     error_code = 'not_chat_app'
-    description = "Not Chat App"
+    description = "Please check if your Chat app mode matches the right API route."
     code = 400
 
 
 class ConversationCompletedError(BaseHTTPException):
     error_code = 'conversation_completed'
-    description = "Conversation Completed."
+    description = "The conversation has ended. Please start a new conversation."
     code = 400
 
 
 class ProviderNotInitializeError(BaseHTTPException):
     error_code = 'provider_not_initialize'
-    description = "Provider Token not initialize."
+    description = "No valid model provider credentials found. " \
+                  "Please go to Settings -> Model Provider to complete your provider credentials."
     code = 400
 
 
 class ProviderQuotaExceededError(BaseHTTPException):
     error_code = 'provider_quota_exceeded'
-    description = "Provider quota exceeded."
+    description = "Your quota for Dify Hosted OpenAI has been exhausted. " \
+                  "Please go to Settings -> Model Provider to complete your own provider credentials."
     code = 400
 
 
 class ProviderModelCurrentlyNotSupportError(BaseHTTPException):
     error_code = 'model_currently_not_support'
-    description = "GPT-4 currently not support."
+    description = "Dify Hosted OpenAI trial currently not support the GPT-4 model."
     code = 400
 
 
@@ -52,11 +54,11 @@ class CompletionRequestError(BaseHTTPException):
 
 class AppMoreLikeThisDisabledError(BaseHTTPException):
     error_code = 'app_more_like_this_disabled'
-    description = "More like this disabled."
+    description = "The 'More like this' feature is disabled. Please refresh your page."
     code = 403
 
 
 class AppSuggestedQuestionsAfterAnswerDisabledError(BaseHTTPException):
     error_code = 'app_suggested_questions_after_answer_disabled'
-    description = "Function Suggested questions after answer disabled."
+    description = "The 'Suggested Questions After Answer' feature is disabled. Please refresh your page."
     code = 403

+ 12 - 0
api/core/index/index_builder.py

@@ -46,3 +46,15 @@ class IndexBuilder:
             prompt_helper=prompt_helper,
             embed_model=OpenAIEmbedding(**model_credentials),
         )
+
+    @classmethod
+    def get_fake_llm_service_context(cls, tenant_id: str) -> ServiceContext:
+        llm = LLMBuilder.to_llm(
+            tenant_id=tenant_id,
+            model_name='fake'
+        )
+
+        return ServiceContext.from_defaults(
+            llm_predictor=LLMPredictor(llm=llm),
+            embed_model=OpenAIEmbedding()
+        )

+ 2 - 2
api/core/index/vector_index.py

@@ -83,7 +83,7 @@ class VectorIndex:
         if not self._dataset.index_struct_dict:
             return
 
-        service_context = IndexBuilder.get_default_service_context(tenant_id=self._dataset.tenant_id)
+        service_context = IndexBuilder.get_fake_llm_service_context(tenant_id=self._dataset.tenant_id)
 
         index = vector_store.get_index(
             service_context=service_context,
@@ -101,7 +101,7 @@ class VectorIndex:
         if not self._dataset.index_struct_dict:
             return
 
-        service_context = IndexBuilder.get_default_service_context(tenant_id=self._dataset.tenant_id)
+        service_context = IndexBuilder.get_fake_llm_service_context(tenant_id=self._dataset.tenant_id)
 
         index = vector_store.get_index(
             service_context=service_context,

+ 39 - 12
api/core/llm/provider/azure_provider.py

@@ -1,22 +1,24 @@
 import json
+import logging
 from typing import Optional, Union
 
 import requests
 
 from core.llm.provider.base import BaseProvider
+from core.llm.provider.errors import ValidateFailedError
 from models.provider import ProviderName
 
 
 class AzureProvider(BaseProvider):
-    def get_models(self, model_id: Optional[str] = None) -> list[dict]:
-        credentials = self.get_credentials(model_id)
+    def get_models(self, model_id: Optional[str] = None, credentials: Optional[dict] = None) -> list[dict]:
+        credentials = self.get_credentials(model_id) if not credentials else credentials
         url = "{}/openai/deployments?api-version={}".format(
-            credentials.get('openai_api_base'),
-            credentials.get('openai_api_version')
+            str(credentials.get('openai_api_base')),
+            str(credentials.get('openai_api_version'))
         )
 
         headers = {
-            "api-key": credentials.get('openai_api_key'),
+            "api-key": str(credentials.get('openai_api_key')),
             "content-type": "application/json; charset=utf-8"
         }
 
@@ -29,8 +31,10 @@ class AzureProvider(BaseProvider):
                 'name': '{} ({})'.format(deployment['id'], deployment['model'])
             } for deployment in result['data'] if deployment['status'] == 'succeeded']
         else:
-            # TODO: optimize in future
-            raise Exception('Failed to get deployments from Azure OpenAI. Status code: {}'.format(response.status_code))
+            if response.status_code == 401:
+                raise AzureAuthenticationError()
+            else:
+                raise AzureRequestFailedError('Failed to request Azure OpenAI. Status code: {}'.format(response.status_code))
 
     def get_credentials(self, model_id: Optional[str] = None) -> dict:
         """
@@ -38,7 +42,7 @@ class AzureProvider(BaseProvider):
         """
         config = self.get_provider_api_key(model_id=model_id)
         config['openai_api_type'] = 'azure'
-        config['deployment_name'] = model_id.replace('.', '')
+        config['deployment_name'] = model_id.replace('.', '') if model_id else None
         return config
 
     def get_provider_name(self):
@@ -54,7 +58,7 @@ class AzureProvider(BaseProvider):
             config = {
                 'openai_api_type': 'azure',
                 'openai_api_version': '2023-03-15-preview',
-                'openai_api_base': 'https://<your-domain-prefix>.openai.azure.com/',
+                'openai_api_base': '',
                 'openai_api_key': ''
             }
 
@@ -63,7 +67,7 @@ class AzureProvider(BaseProvider):
                 config = {
                     'openai_api_type': 'azure',
                     'openai_api_version': '2023-03-15-preview',
-                    'openai_api_base': 'https://<your-domain-prefix>.openai.azure.com/',
+                    'openai_api_base': '',
                     'openai_api_key': ''
                 }
 
@@ -80,8 +84,23 @@ class AzureProvider(BaseProvider):
         """
         Validates the given config.
         """
-        # TODO: implement
-        pass
+        try:
+            if not isinstance(config, dict):
+                raise ValueError('Config must be a object.')
+
+            if 'openai_api_version' not in config:
+                config['openai_api_version'] = '2023-03-15-preview'
+
+            self.get_models(credentials=config)
+        except AzureAuthenticationError:
+            raise ValidateFailedError('Azure OpenAI Credentials validation failed, please check your API Key.')
+        except requests.ConnectionError:
+            raise ValidateFailedError('Azure OpenAI Credentials validation failed, please check your API Base Endpoint.')
+        except AzureRequestFailedError as ex:
+            raise ValidateFailedError('Azure OpenAI Credentials validation failed, error: {}.'.format(str(ex)))
+        except Exception as ex:
+            logging.exception('Azure OpenAI Credentials validation failed')
+            raise ex
 
     def get_encrypted_token(self, config: Union[dict | str]):
         """
@@ -101,3 +120,11 @@ class AzureProvider(BaseProvider):
         config = json.loads(token)
         config['openai_api_key'] = self.decrypt_token(config['openai_api_key'])
         return config
+
+
+class AzureAuthenticationError(Exception):
+    pass
+
+
+class AzureRequestFailedError(Exception):
+    pass

+ 1 - 1
web/i18n/lang/common.en.ts

@@ -149,7 +149,7 @@ const translation = {
     invalidApiKey: 'Invalid API key',
     azure: {
       apiBase: 'API Base',
-      apiBasePlaceholder: 'The API Base URL of your Azure OpenAI Resource.',
+      apiBasePlaceholder: 'The API Base URL of your Azure OpenAI Endpoint.',
       apiKey: 'API Key',
       apiKeyPlaceholder: 'Enter your API key here',
       helpTip: 'Learn Azure OpenAI Service',