| @@ -4,14 +4,10 @@ import platform | |||
| import re | |||
| import urllib.parse | |||
| import warnings | |||
| from collections.abc import Mapping | |||
| from typing import Any | |||
| from uuid import uuid4 | |||
| import httpx | |||
| from constants import DEFAULT_FILE_NUMBER_LIMITS | |||
| try: | |||
| import magic | |||
| except ImportError: | |||
| @@ -31,8 +27,6 @@ except ImportError: | |||
| from pydantic import BaseModel | |||
| from configs import dify_config | |||
| class FileInfo(BaseModel): | |||
| filename: str | |||
| @@ -89,38 +83,3 @@ def guess_file_info_from_response(response: httpx.Response): | |||
| mimetype=mimetype, | |||
| 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, | |||
| }, | |||
| } | |||
| @@ -1,10 +1,10 @@ | |||
| from flask_restful import marshal_with # type: ignore | |||
| from controllers.common import fields | |||
| from controllers.common import helpers as controller_helpers | |||
| from controllers.console import api | |||
| from controllers.console.app.error import AppUnavailableError | |||
| 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 services.app_service import AppService | |||
| @@ -36,9 +36,7 @@ class AppParameterApi(InstalledAppResource): | |||
| 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): | |||
| @@ -13,6 +13,7 @@ from core.plugin.backwards_invocation.model import PluginModelBackwardsInvocatio | |||
| from core.plugin.backwards_invocation.node import PluginNodeBackwardsInvocation | |||
| from core.plugin.backwards_invocation.tool import PluginToolBackwardsInvocation | |||
| from core.plugin.entities.request import ( | |||
| RequestFetchAppInfo, | |||
| RequestInvokeApp, | |||
| RequestInvokeEncrypt, | |||
| RequestInvokeLLM, | |||
| @@ -278,6 +279,17 @@ class PluginUploadFileRequestApi(Resource): | |||
| 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(PluginInvokeTextEmbeddingApi, "/invoke/text-embedding") | |||
| api.add_resource(PluginInvokeRerankApi, "/invoke/rerank") | |||
| @@ -291,3 +303,4 @@ api.add_resource(PluginInvokeAppApi, "/invoke/app") | |||
| api.add_resource(PluginInvokeEncryptApi, "/invoke/encrypt") | |||
| api.add_resource(PluginInvokeSummaryApi, "/invoke/summary") | |||
| api.add_resource(PluginUploadFileRequestApi, "/upload/file/request") | |||
| api.add_resource(PluginFetchAppInfoApi, "/fetch/app/info") | |||
| @@ -1,10 +1,10 @@ | |||
| from flask_restful import Resource, marshal_with # type: ignore | |||
| from controllers.common import fields | |||
| from controllers.common import helpers as controller_helpers | |||
| from controllers.service_api import api | |||
| from controllers.service_api.app.error import AppUnavailableError | |||
| 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 services.app_service import AppService | |||
| @@ -32,9 +32,7 @@ class AppParameterApi(Resource): | |||
| 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): | |||
| @@ -1,10 +1,10 @@ | |||
| from flask_restful import marshal_with # type: ignore | |||
| from controllers.common import fields | |||
| from controllers.common import helpers as controller_helpers | |||
| from controllers.web import api | |||
| from controllers.web.error import AppUnavailableError | |||
| 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 services.app_service import AppService | |||
| @@ -31,9 +31,7 @@ class AppParameterApi(WebApiResource): | |||
| 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): | |||
| @@ -0,0 +1,45 @@ | |||
| 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, | |||
| }, | |||
| } | |||
| @@ -2,6 +2,7 @@ from collections.abc import Generator, Mapping | |||
| from typing import Optional, Union | |||
| 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.agent_chat.app_generator import AgentChatAppGenerator | |||
| from core.app.apps.chat.app_generator import ChatAppGenerator | |||
| @@ -15,6 +16,34 @@ from models.model import App, AppMode, EndUser | |||
| 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 | |||
| def invoke_app( | |||
| cls, | |||
| @@ -204,3 +204,11 @@ class RequestRequestUploadFile(BaseModel): | |||
| filename: str | |||
| mimetype: str | |||
| class RequestFetchAppInfo(BaseModel): | |||
| """ | |||
| Request to fetch app info | |||
| """ | |||
| app_id: str | |||