| import re | import re | ||||
| import urllib.parse | import urllib.parse | ||||
| import warnings | import warnings | ||||
| from collections.abc import Mapping | |||||
| from typing import Any | |||||
| from uuid import uuid4 | from uuid import uuid4 | ||||
| import httpx | import httpx | ||||
| from constants import DEFAULT_FILE_NUMBER_LIMITS | |||||
| try: | try: | ||||
| import magic | import magic | ||||
| except ImportError: | except ImportError: | ||||
| from pydantic import BaseModel | from pydantic import BaseModel | ||||
| from configs import dify_config | |||||
| class FileInfo(BaseModel): | class FileInfo(BaseModel): | ||||
| filename: str | filename: str | ||||
| mimetype=mimetype, | mimetype=mimetype, | ||||
| size=int(response.headers.get("Content-Length", -1)), | size=int(response.headers.get("Content-Length", -1)), | ||||
| ) | ) | ||||
| def get_parameters_from_feature_dict(*, features_dict: Mapping[str, Any], user_input_form: list[dict[str, Any]]): | |||||
| return { | |||||
| "opening_statement": features_dict.get("opening_statement"), | |||||
| "suggested_questions": features_dict.get("suggested_questions", []), | |||||
| "suggested_questions_after_answer": features_dict.get("suggested_questions_after_answer", {"enabled": False}), | |||||
| "speech_to_text": features_dict.get("speech_to_text", {"enabled": False}), | |||||
| "text_to_speech": features_dict.get("text_to_speech", {"enabled": False}), | |||||
| "retriever_resource": features_dict.get("retriever_resource", {"enabled": False}), | |||||
| "annotation_reply": features_dict.get("annotation_reply", {"enabled": False}), | |||||
| "more_like_this": features_dict.get("more_like_this", {"enabled": False}), | |||||
| "user_input_form": user_input_form, | |||||
| "sensitive_word_avoidance": features_dict.get( | |||||
| "sensitive_word_avoidance", {"enabled": False, "type": "", "configs": []} | |||||
| ), | |||||
| "file_upload": features_dict.get( | |||||
| "file_upload", | |||||
| { | |||||
| "image": { | |||||
| "enabled": False, | |||||
| "number_limits": DEFAULT_FILE_NUMBER_LIMITS, | |||||
| "detail": "high", | |||||
| "transfer_methods": ["remote_url", "local_file"], | |||||
| } | |||||
| }, | |||||
| ), | |||||
| "system_parameters": { | |||||
| "image_file_size_limit": dify_config.UPLOAD_IMAGE_FILE_SIZE_LIMIT, | |||||
| "video_file_size_limit": dify_config.UPLOAD_VIDEO_FILE_SIZE_LIMIT, | |||||
| "audio_file_size_limit": dify_config.UPLOAD_AUDIO_FILE_SIZE_LIMIT, | |||||
| "file_size_limit": dify_config.UPLOAD_FILE_SIZE_LIMIT, | |||||
| "workflow_file_upload_limit": dify_config.WORKFLOW_FILE_UPLOAD_LIMIT, | |||||
| }, | |||||
| } |
| from flask_restful import marshal_with # type: ignore | from flask_restful import marshal_with # type: ignore | ||||
| from controllers.common import fields | from controllers.common import fields | ||||
| from controllers.common import helpers as controller_helpers | |||||
| from controllers.console import api | from controllers.console import api | ||||
| from controllers.console.app.error import AppUnavailableError | from controllers.console.app.error import AppUnavailableError | ||||
| from controllers.console.explore.wraps import InstalledAppResource | from controllers.console.explore.wraps import InstalledAppResource | ||||
| from core.app.app_config.common.parameters_mapping import get_parameters_from_feature_dict | |||||
| from models.model import AppMode, InstalledApp | from models.model import AppMode, InstalledApp | ||||
| from services.app_service import AppService | from services.app_service import AppService | ||||
| user_input_form = features_dict.get("user_input_form", []) | user_input_form = features_dict.get("user_input_form", []) | ||||
| return controller_helpers.get_parameters_from_feature_dict( | |||||
| features_dict=features_dict, user_input_form=user_input_form | |||||
| ) | |||||
| return get_parameters_from_feature_dict(features_dict=features_dict, user_input_form=user_input_form) | |||||
| class ExploreAppMetaApi(InstalledAppResource): | class ExploreAppMetaApi(InstalledAppResource): |
| from core.plugin.backwards_invocation.node import PluginNodeBackwardsInvocation | from core.plugin.backwards_invocation.node import PluginNodeBackwardsInvocation | ||||
| from core.plugin.backwards_invocation.tool import PluginToolBackwardsInvocation | from core.plugin.backwards_invocation.tool import PluginToolBackwardsInvocation | ||||
| from core.plugin.entities.request import ( | from core.plugin.entities.request import ( | ||||
| RequestFetchAppInfo, | |||||
| RequestInvokeApp, | RequestInvokeApp, | ||||
| RequestInvokeEncrypt, | RequestInvokeEncrypt, | ||||
| RequestInvokeLLM, | RequestInvokeLLM, | ||||
| return BaseBackwardsInvocationResponse(data={"url": url}).model_dump() | return BaseBackwardsInvocationResponse(data={"url": url}).model_dump() | ||||
| class PluginFetchAppInfoApi(Resource): | |||||
| @setup_required | |||||
| @plugin_inner_api_only | |||||
| @get_user_tenant | |||||
| @plugin_data(payload_type=RequestFetchAppInfo) | |||||
| def post(self, user_model: Account | EndUser, tenant_model: Tenant, payload: RequestFetchAppInfo): | |||||
| return BaseBackwardsInvocationResponse( | |||||
| data=PluginAppBackwardsInvocation.fetch_app_info(payload.app_id, tenant_model.id) | |||||
| ).model_dump() | |||||
| api.add_resource(PluginInvokeLLMApi, "/invoke/llm") | api.add_resource(PluginInvokeLLMApi, "/invoke/llm") | ||||
| api.add_resource(PluginInvokeTextEmbeddingApi, "/invoke/text-embedding") | api.add_resource(PluginInvokeTextEmbeddingApi, "/invoke/text-embedding") | ||||
| api.add_resource(PluginInvokeRerankApi, "/invoke/rerank") | api.add_resource(PluginInvokeRerankApi, "/invoke/rerank") | ||||
| api.add_resource(PluginInvokeEncryptApi, "/invoke/encrypt") | api.add_resource(PluginInvokeEncryptApi, "/invoke/encrypt") | ||||
| api.add_resource(PluginInvokeSummaryApi, "/invoke/summary") | api.add_resource(PluginInvokeSummaryApi, "/invoke/summary") | ||||
| api.add_resource(PluginUploadFileRequestApi, "/upload/file/request") | api.add_resource(PluginUploadFileRequestApi, "/upload/file/request") | ||||
| api.add_resource(PluginFetchAppInfoApi, "/fetch/app/info") |
| from flask_restful import Resource, marshal_with # type: ignore | from flask_restful import Resource, marshal_with # type: ignore | ||||
| from controllers.common import fields | from controllers.common import fields | ||||
| from controllers.common import helpers as controller_helpers | |||||
| from controllers.service_api import api | from controllers.service_api import api | ||||
| from controllers.service_api.app.error import AppUnavailableError | from controllers.service_api.app.error import AppUnavailableError | ||||
| from controllers.service_api.wraps import validate_app_token | from controllers.service_api.wraps import validate_app_token | ||||
| from core.app.app_config.common.parameters_mapping import get_parameters_from_feature_dict | |||||
| from models.model import App, AppMode | from models.model import App, AppMode | ||||
| from services.app_service import AppService | from services.app_service import AppService | ||||
| user_input_form = features_dict.get("user_input_form", []) | user_input_form = features_dict.get("user_input_form", []) | ||||
| return controller_helpers.get_parameters_from_feature_dict( | |||||
| features_dict=features_dict, user_input_form=user_input_form | |||||
| ) | |||||
| return get_parameters_from_feature_dict(features_dict=features_dict, user_input_form=user_input_form) | |||||
| class AppMetaApi(Resource): | class AppMetaApi(Resource): |
| from flask_restful import marshal_with # type: ignore | from flask_restful import marshal_with # type: ignore | ||||
| from controllers.common import fields | from controllers.common import fields | ||||
| from controllers.common import helpers as controller_helpers | |||||
| from controllers.web import api | from controllers.web import api | ||||
| from controllers.web.error import AppUnavailableError | from controllers.web.error import AppUnavailableError | ||||
| from controllers.web.wraps import WebApiResource | from controllers.web.wraps import WebApiResource | ||||
| from core.app.app_config.common.parameters_mapping import get_parameters_from_feature_dict | |||||
| from models.model import App, AppMode | from models.model import App, AppMode | ||||
| from services.app_service import AppService | from services.app_service import AppService | ||||
| user_input_form = features_dict.get("user_input_form", []) | user_input_form = features_dict.get("user_input_form", []) | ||||
| return controller_helpers.get_parameters_from_feature_dict( | |||||
| features_dict=features_dict, user_input_form=user_input_form | |||||
| ) | |||||
| return get_parameters_from_feature_dict(features_dict=features_dict, user_input_form=user_input_form) | |||||
| class AppMeta(WebApiResource): | class AppMeta(WebApiResource): |
| from collections.abc import Mapping | |||||
| from typing import Any | |||||
| from configs import dify_config | |||||
| from constants import DEFAULT_FILE_NUMBER_LIMITS | |||||
| def get_parameters_from_feature_dict( | |||||
| *, features_dict: Mapping[str, Any], user_input_form: list[dict[str, Any]] | |||||
| ) -> Mapping[str, Any]: | |||||
| """ | |||||
| Mapping from feature dict to webapp parameters | |||||
| """ | |||||
| return { | |||||
| "opening_statement": features_dict.get("opening_statement"), | |||||
| "suggested_questions": features_dict.get("suggested_questions", []), | |||||
| "suggested_questions_after_answer": features_dict.get("suggested_questions_after_answer", {"enabled": False}), | |||||
| "speech_to_text": features_dict.get("speech_to_text", {"enabled": False}), | |||||
| "text_to_speech": features_dict.get("text_to_speech", {"enabled": False}), | |||||
| "retriever_resource": features_dict.get("retriever_resource", {"enabled": False}), | |||||
| "annotation_reply": features_dict.get("annotation_reply", {"enabled": False}), | |||||
| "more_like_this": features_dict.get("more_like_this", {"enabled": False}), | |||||
| "user_input_form": user_input_form, | |||||
| "sensitive_word_avoidance": features_dict.get( | |||||
| "sensitive_word_avoidance", {"enabled": False, "type": "", "configs": []} | |||||
| ), | |||||
| "file_upload": features_dict.get( | |||||
| "file_upload", | |||||
| { | |||||
| "image": { | |||||
| "enabled": False, | |||||
| "number_limits": DEFAULT_FILE_NUMBER_LIMITS, | |||||
| "detail": "high", | |||||
| "transfer_methods": ["remote_url", "local_file"], | |||||
| } | |||||
| }, | |||||
| ), | |||||
| "system_parameters": { | |||||
| "image_file_size_limit": dify_config.UPLOAD_IMAGE_FILE_SIZE_LIMIT, | |||||
| "video_file_size_limit": dify_config.UPLOAD_VIDEO_FILE_SIZE_LIMIT, | |||||
| "audio_file_size_limit": dify_config.UPLOAD_AUDIO_FILE_SIZE_LIMIT, | |||||
| "file_size_limit": dify_config.UPLOAD_FILE_SIZE_LIMIT, | |||||
| "workflow_file_upload_limit": dify_config.WORKFLOW_FILE_UPLOAD_LIMIT, | |||||
| }, | |||||
| } |
| from typing import Optional, Union | from typing import Optional, Union | ||||
| from controllers.service_api.wraps import create_or_update_end_user_for_user_id | from controllers.service_api.wraps import create_or_update_end_user_for_user_id | ||||
| from core.app.app_config.common.parameters_mapping import get_parameters_from_feature_dict | |||||
| from core.app.apps.advanced_chat.app_generator import AdvancedChatAppGenerator | from core.app.apps.advanced_chat.app_generator import AdvancedChatAppGenerator | ||||
| from core.app.apps.agent_chat.app_generator import AgentChatAppGenerator | from core.app.apps.agent_chat.app_generator import AgentChatAppGenerator | ||||
| from core.app.apps.chat.app_generator import ChatAppGenerator | from core.app.apps.chat.app_generator import ChatAppGenerator | ||||
| class PluginAppBackwardsInvocation(BaseBackwardsInvocation): | class PluginAppBackwardsInvocation(BaseBackwardsInvocation): | ||||
| @classmethod | |||||
| def fetch_app_info(cls, app_id: str, tenant_id: str) -> Mapping: | |||||
| """ | |||||
| Fetch app info | |||||
| """ | |||||
| app = cls._get_app(app_id, tenant_id) | |||||
| """Retrieve app parameters.""" | |||||
| if app.mode in {AppMode.ADVANCED_CHAT.value, AppMode.WORKFLOW.value}: | |||||
| workflow = app.workflow | |||||
| if workflow is None: | |||||
| raise ValueError("unexpected app type") | |||||
| features_dict = workflow.features_dict | |||||
| user_input_form = workflow.user_input_form(to_old_structure=True) | |||||
| else: | |||||
| app_model_config = app.app_model_config | |||||
| if app_model_config is None: | |||||
| raise ValueError("unexpected app type") | |||||
| features_dict = app_model_config.to_dict() | |||||
| user_input_form = features_dict.get("user_input_form", []) | |||||
| return { | |||||
| "data": get_parameters_from_feature_dict(features_dict=features_dict, user_input_form=user_input_form), | |||||
| } | |||||
| @classmethod | @classmethod | ||||
| def invoke_app( | def invoke_app( | ||||
| cls, | cls, |
| filename: str | filename: str | ||||
| mimetype: str | mimetype: str | ||||
| class RequestFetchAppInfo(BaseModel): | |||||
| """ | |||||
| Request to fetch app info | |||||
| """ | |||||
| app_id: str |