Преглед изворни кода

feat: implement invoke app args

Yeuoly пре 9 месеци
родитељ
комит
12ea085e22

+ 22 - 7
api/controllers/inner_api/plugin/plugin.py

@@ -6,10 +6,13 @@ from controllers.console.setup import setup_required
 from controllers.inner_api import api
 from controllers.inner_api.plugin.wraps import get_tenant, plugin_data
 from controllers.inner_api.wraps import plugin_inner_api_only
+from core.plugin.backwards_invocation.app import PluginAppBackwardsInvocation
 from core.plugin.backwards_invocation.model import PluginModelBackwardsInvocation
 from core.plugin.entities.request import (
+    RequestInvokeApp,
     RequestInvokeLLM,
     RequestInvokeModeration,
+    RequestInvokeNode,
     RequestInvokeRerank,
     RequestInvokeSpeech2Text,
     RequestInvokeTextEmbedding,
@@ -104,21 +107,33 @@ class PluginInvokeNodeApi(Resource):
     @setup_required
     @plugin_inner_api_only
     @get_tenant
-    def post(self, user_id: str, tenant_model: Tenant):
-        parser = reqparse.RequestParser()
-        args = parser.parse_args()
-
-        return {'message': 'success'}
+    @plugin_data(payload_type=RequestInvokeNode)
+    def post(self, user_id: str, tenant_model: Tenant, payload: RequestInvokeNode):
+        pass
 
 class PluginInvokeAppApi(Resource):
     @setup_required
     @plugin_inner_api_only
     @get_tenant
-    def post(self, user_id: str, tenant_model: Tenant):
+    @plugin_data(payload_type=RequestInvokeApp)
+    def post(self, user_id: str, tenant_model: Tenant, payload: RequestInvokeApp):
         parser = reqparse.RequestParser()
         args = parser.parse_args()
 
-        return {'message': 'success'}
+        response = PluginAppBackwardsInvocation.invoke_app(
+            app_id=payload.app_id,
+            user_id=user_id,
+            tenant_id=tenant_model.id,
+            conversation_id=payload.conversation_id,
+            query=payload.query,
+            stream=payload.stream,
+            inputs=payload.inputs,
+            files=payload.files
+        )
+        
+        return compact_generate_response(
+            PluginAppBackwardsInvocation.convert_to_event_stream(response)
+        )
 
 api.add_resource(PluginInvokeLLMApi, '/invoke/llm')
 api.add_resource(PluginInvokeTextEmbeddingApi, '/invoke/text-embedding')

+ 72 - 24
api/core/plugin/backwards_invocation/app.py

@@ -1,7 +1,10 @@
 from collections.abc import Generator, Mapping
-from typing import Literal, Union
+from typing import Optional, Union
 
 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
+from core.app.apps.completion.app_generator import CompletionAppGenerator
 from core.app.apps.workflow.app_generator import WorkflowAppGenerator
 from core.app.entities.app_invoke_entities import InvokeFrom
 from core.plugin.backwards_invocation.base import BaseBackwardsInvocation
@@ -16,20 +19,29 @@ class PluginAppBackwardsInvocation(BaseBackwardsInvocation):
         cls, app_id: str, 
         user_id: str, 
         tenant_id: str,
-        query: str,
+        conversation_id: Optional[str],
+        query: Optional[str],
+        stream: bool,
         inputs: Mapping,
         files: list[dict], 
-    ) -> Generator[dict, None, None] | dict:
+    ) -> Generator[dict | str, None, None] | dict:
         """
         invoke app
         """
         app = cls._get_app(app_id, tenant_id)
+        user = cls._get_user(user_id)
+
+        conversation_id = conversation_id or ""
+
         if app.mode in [AppMode.ADVANCED_CHAT.value, AppMode.AGENT_CHAT.value, AppMode.CHAT.value]:
-            return cls.invoke_chat_app(app, user_id, tenant_id, query, inputs, files)
+            if not query:
+                raise ValueError("missing query")
+            
+            return cls.invoke_chat_app(app, user, conversation_id, query, stream, inputs, files)
         elif app.mode in [AppMode.WORKFLOW.value]:
-            return cls.invoke_workflow_app(app, user_id, tenant_id, inputs, files)
+            return cls.invoke_workflow_app(app, user, stream, inputs, files)
         elif app.mode in [AppMode.COMPLETION]:
-            return cls.invoke_completion_app(app, user_id, tenant_id, inputs, files)
+            return cls.invoke_completion_app(app, user, stream, inputs, files)
 
         raise ValueError("unexpected app type")
 
@@ -38,13 +50,12 @@ class PluginAppBackwardsInvocation(BaseBackwardsInvocation):
         cls, 
         app: App,
         user: Account | EndUser, 
-        tenant_id: str,
         conversation_id: str,
         query: str,
         stream: bool,
         inputs: Mapping,
         files: list[dict], 
-    ) -> Generator[dict, None, None] | dict:
+    ) -> Generator[dict | str, None, None] | dict:
         """
         invoke chat app
         """
@@ -53,25 +64,54 @@ class PluginAppBackwardsInvocation(BaseBackwardsInvocation):
             if not workflow:
                 raise ValueError("unexpected app type")
             
-            generator = AdvancedChatAppGenerator()
-            response = generator.generate(
+            return AdvancedChatAppGenerator().generate(
                 app_model=app, 
                 workflow=workflow, 
                 user=user, 
                 args={
+                    "inputs": inputs,
+                    "query": query,
+                    "files": files,
+                    "conversation_id": conversation_id,
+                },
+                invoke_from=InvokeFrom.SERVICE_API,
+                stream=stream
+            )
+        elif app.mode == AppMode.AGENT_CHAT.value:
+            return AgentChatAppGenerator().generate(
+                app_model=app, 
+                user=user, 
+                args={
+                    "inputs": inputs,
+                    "query": query,
+                    "files": files,
+                    "conversation_id": conversation_id,
+                },
+                invoke_from=InvokeFrom.SERVICE_API,
+                stream=stream
+            )
+        elif app.mode == AppMode.CHAT.value:
+            return ChatAppGenerator().generate(
+                app_model=app, 
+                user=user, 
+                args={
+                    "inputs": inputs,
+                    "query": query,
+                    "files": files,
+                    "conversation_id": conversation_id,
                 },
                 invoke_from=InvokeFrom.SERVICE_API,
                 stream=stream
             )
+        else:
+            raise ValueError("unexpected app type")
 
-            
-    
     @classmethod
     def invoke_workflow_app(
         cls, 
         app: App,
-        user_id: str, 
-        tenant_id: str,
+        user: EndUser | Account, 
+        stream: bool,
         inputs: Mapping,
         files: list[dict], 
     ):
@@ -82,33 +122,41 @@ class PluginAppBackwardsInvocation(BaseBackwardsInvocation):
         if not workflow:
             raise ValueError("")
 
-        generator = WorkflowAppGenerator()
-
-        result = generator.generate(
+        return WorkflowAppGenerator().generate(
             app_model=app, 
             workflow=workflow, 
-            user=cls._get_user(user_id), 
+            user=user, 
             args={
-                'inputs': tool_parameters,
+                'inputs': inputs,
                 'files': files
             }, 
-            invoke_from=self.runtime.invoke_from,
-            stream=False,
-            call_depth=self.workflow_call_depth + 1,
+            invoke_from=InvokeFrom.SERVICE_API,
+            stream=stream,
+            call_depth=1,
         )
 
     @classmethod
     def invoke_completion_app(
         cls, 
         app: App,
-        user_id: str, 
-        tenant_id: str,
+        user: EndUser | Account, 
+        stream: bool,
         inputs: Mapping,
         files: list[dict], 
     ):
         """
         invoke completion app
         """
+        return CompletionAppGenerator().generate(
+            app_model=app, 
+            user=user, 
+            args={
+                'inputs': inputs,
+                'files': files
+            }, 
+            invoke_from=InvokeFrom.SERVICE_API,
+            stream=stream,
+        )
 
     @classmethod
     def _get_user(cls, user_id: str) -> Union[EndUser, Account]:

+ 4 - 2
api/core/plugin/backwards_invocation/base.py

@@ -6,15 +6,17 @@ from pydantic import BaseModel
 
 class BaseBackwardsInvocation:
     @classmethod
-    def convert_to_event_stream(cls, response: Generator[BaseModel | dict, None, None] | BaseModel | dict):
+    def convert_to_event_stream(cls, response: Generator[BaseModel | dict | str, None, None] | BaseModel | dict):
         if isinstance(response, Generator):
             for chunk in response:
                 if isinstance(chunk, BaseModel):
                     yield chunk.model_dump_json().encode() + b'\n\n'
+                if isinstance(chunk, str):
+                    yield f"event: {chunk}\n\n".encode()
                 else:
                     yield json.dumps(chunk).encode() + b'\n\n'
         else:
             if isinstance(response, BaseModel):
                 yield response.model_dump_json().encode() + b'\n\n'
             else:
-                yield json.dumps(response).encode() + b'\n\n'
+                yield json.dumps(response).encode() + b'\n\n'

+ 1 - 0
api/core/plugin/entities/request.py

@@ -105,3 +105,4 @@ class RequestInvokeApp(BaseModel):
     conversation_id: Optional[str] = None
     user: Optional[str] = None
     files: list[dict] = Field(default_factory=list)
+    stream: bool = Field(default=False)

+ 0 - 49
api/services/plugin/plugin_invoke_service.py

@@ -1,49 +0,0 @@
-from collections.abc import Generator
-from typing import Any, Union
-
-from core.app.entities.app_invoke_entities import InvokeFrom
-from core.callback_handler.plugin_tool_callback_handler import DifyPluginCallbackHandler
-from core.model_runtime.entities.model_entities import ModelType
-from core.tools.entities.tool_entities import ToolInvokeMessage, ToolProviderType
-from core.tools.tool_engine import ToolEngine
-from core.tools.tool_manager import ToolManager
-from core.tools.utils.message_transformer import ToolFileMessageTransformer
-from core.workflow.entities.node_entities import NodeType
-from models.account import Tenant
-from services.tools.tools_transform_service import ToolTransformService
-
-
-class PluginInvokeService:
-    @classmethod
-    def invoke_tool(cls, user_id: str, invoke_from: InvokeFrom, tenant: Tenant, 
-                    tool_provider_type: ToolProviderType, tool_provider: str, tool_name: str,
-                    tool_parameters: dict[str, Any]) -> Generator[ToolInvokeMessage]:
-        """
-        Invokes a tool with the given user ID and tool parameters.
-        """
-        tool_runtime = ToolManager.get_tool_runtime(tool_provider_type, provider_id=tool_provider, 
-                                                    tool_name=tool_name, tenant_id=tenant.id, 
-                                                    invoke_from=invoke_from)
-        
-        response = ToolEngine.plugin_invoke(tool_runtime, 
-                                            tool_parameters, 
-                                            user_id, 
-                                            callback=DifyPluginCallbackHandler())
-        response = ToolFileMessageTransformer.transform_tool_invoke_messages(response)
-        return ToolTransformService.transform_messages_to_dict(response)
-        
-    @classmethod
-    def invoke_model(cls, user_id: str, tenant: Tenant, 
-                     model_provider: str, model_name: str, model_type: ModelType,
-                     model_parameters: dict[str, Any]) -> Union[dict, Generator[ToolInvokeMessage]]:
-        """
-        Invokes a model with the given user ID and model parameters.
-        """
-
-    @classmethod
-    def invoke_workflow_node(cls, user_id: str, tenant: Tenant, 
-                              node_type: NodeType, node_data: dict[str, Any],
-                              inputs: dict[str, Any]) -> Generator[ToolInvokeMessage]:
-        """
-        Invokes a workflow node with the given user ID and node parameters.
-        """