Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions python/dify_plugin/core/entities/plugin/request.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from collections.abc import Mapping
from enum import Enum
from typing import Any, Optional

from pydantic import BaseModel, ConfigDict, field_validator
from pydantic import BaseModel, ConfigDict, Field, field_validator

from dify_plugin.entities.model import ModelType
from dify_plugin.entities.model.message import (
Expand Down Expand Up @@ -48,6 +49,7 @@ class ModelActions(Enum):


class EndpointActions(Enum):
SetupEndpoint = "setup_endpoint"
InvokeEndpoint = "invoke_endpoint"


Expand Down Expand Up @@ -215,8 +217,15 @@ class ModelGetAIModelSchemas(PluginAccessModelRequest):
action: ModelActions = ModelActions.GetAIModelSchemas


class EndpointSetupRequest(BaseModel):
type: PluginInvokeType = PluginInvokeType.Endpoint
action: EndpointActions = EndpointActions.SetupEndpoint
endpoint_group: str = Field(default="default", description="The endpoint group name")
settings: Mapping


class EndpointInvokeRequest(BaseModel):
type: PluginInvokeType = PluginInvokeType.Endpoint
action: EndpointActions = EndpointActions.InvokeEndpoint
settings: dict
settings: Mapping
raw_http_request: str
8 changes: 8 additions & 0 deletions python/dify_plugin/core/plugin_executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from dify_plugin.core.entities.plugin.request import (
AgentInvokeRequest,
EndpointInvokeRequest,
EndpointSetupRequest,
ModelGetAIModelSchemas,
ModelGetLLMNumTokens,
ModelGetTextEmbeddingNumTokens,
Expand Down Expand Up @@ -274,6 +275,13 @@ def invoke_moderation(self, session: Session, data: ModelInvokeModerationRequest
else:
raise ValueError(f"Model `{data.model_type}` not found for provider `{data.provider}`")

def setup_endpoint_group(self, session: Session, data: EndpointSetupRequest):
endpoint_group_cls = self.registration.get_endpoint_group_cls(data.endpoint_group)
if endpoint_group_cls is None:
raise ValueError(f"Endpoint group `{data.endpoint_group}` not found")

endpoint_group_cls().setup(data.settings)

def invoke_endpoint(self, session: Session, data: EndpointInvokeRequest):
bytes_data = binascii.unhexlify(data.raw_http_request)
request = parse_raw_request(bytes_data)
Expand Down
29 changes: 28 additions & 1 deletion python/dify_plugin/core/plugin_registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from dify_plugin.entities.model.provider import ModelProviderConfiguration
from dify_plugin.entities.tool import ToolConfiguration, ToolProviderConfiguration
from dify_plugin.interfaces.agent import AgentStrategy
from dify_plugin.interfaces.endpoint import Endpoint
from dify_plugin.interfaces.endpoint import Endpoint, EndpointGroup
from dify_plugin.interfaces.model import ModelProvider
from dify_plugin.interfaces.model.ai_model import AIModel
from dify_plugin.interfaces.model.large_language_model import LargeLanguageModel
Expand Down Expand Up @@ -62,6 +62,7 @@ class PluginRegistration:
],
]
endpoints_configuration: list[EndpointProviderConfiguration]
endpoint_groups: list[type[EndpointGroup]] # only support one group by now
endpoints: Map
files: list[PluginAsset]

Expand All @@ -74,6 +75,7 @@ def __init__(self, config: DifyPluginEnv) -> None:
self.tools_mapping = {}
self.models_mapping = {}
self.endpoints_configuration = []
self.endpoint_groups = []
self.endpoints = Map()
self.files = []
self.agent_strategies_configuration = []
Expand Down Expand Up @@ -239,6 +241,18 @@ def _resolve_endpoints(self):
load endpoints
"""
for endpoint_provider in self.endpoints_configuration:
# load endpoint group if exists
if endpoint_provider.extra and endpoint_provider.custom_initialize_process_enabled:
module_source = os.path.splitext(endpoint_provider.extra.python.source)[0]
# replace / with .
module_source = module_source.replace("/", ".")
endpoint_group_cls = load_single_subclass_from_source(
module_name=module_source,
script_path=os.path.join(os.getcwd(), endpoint_provider.extra.python.source),
parent_type=EndpointGroup,
)
self.endpoint_groups.append(endpoint_group_cls)

# load endpoints
for endpoint in endpoint_provider.endpoints:
# remove extension
Expand Down Expand Up @@ -338,6 +352,19 @@ def get_model_instance(self, provider: str, model_type: ModelType):
if registration:
return registration

def get_endpoint_group_cls(self, endpoint_group: str):
"""
get the endpoint group class by endpoint group name

NOTE: only support one group by now
:param endpoint_group: endpoint group name
:return: endpoint group class
"""
if endpoint_group == "default":
return self.endpoint_groups[0]
else:
raise ValueError(f"Endpoint group `{endpoint_group}` not found")

def dispatch_endpoint_request(self, request: Request) -> tuple[type[Endpoint], Mapping]:
"""
dispatch endpoint request, match the request to the registered endpoints
Expand Down
6 changes: 4 additions & 2 deletions python/dify_plugin/core/server/serverless/request_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
from collections.abc import Generator
from queue import Empty, Queue

from flask import Flask, request

from dify_plugin.core.entities.plugin.io import (
PluginInStream,
PluginInStreamEvent,
Expand All @@ -27,6 +25,8 @@ def __init__(
"""
Initialize the ServerlessStream and wait for jobs
"""
from flask import Flask

super().__init__()
self.app = Flask(__name__)
self.host = host
Expand All @@ -46,6 +46,8 @@ def _read_stream(self) -> Generator[PluginInStream, None, None]:
yield self.request_queue.get()

def handler(self):
from flask import request

try:
queue = Queue[str]()
data = request.get_json()
Expand Down
22 changes: 22 additions & 0 deletions python/dify_plugin/entities/endpoint.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Optional

from pydantic import BaseModel, Field, field_validator

from dify_plugin.core.documentation.schema_doc import docs
Expand Down Expand Up @@ -27,6 +29,17 @@ class EndpointConfiguration(BaseModel):
extra: EndpointConfigurationExtra


@docs(
name="EndpointGroupExtra",
description="The extra of the endpoint group",
)
class EndpointGroupConfigurationExtra(BaseModel):
class Python(BaseModel):
source: str

python: Python


@docs(
name="EndpointGroup",
description="The Manifest of the endpoint group",
Expand All @@ -35,6 +48,15 @@ class EndpointConfiguration(BaseModel):
class EndpointProviderConfiguration(BaseModel):
settings: list[ProviderConfig] = Field(default_factory=list)
endpoints: list[EndpointConfiguration] = Field(default_factory=list)
custom_initialize_process_enabled: bool = Field(
default=False,
description="Whether enable custom initialize process, "
"please ensure `extra` is not None and `python.source` is set",
)
extra: Optional[EndpointGroupConfigurationExtra] = Field(
default=None,
description="The extra of the endpoint group",
)

@classmethod
def _load_yaml_file(cls, path: str) -> dict:
Expand Down
16 changes: 16 additions & 0 deletions python/dify_plugin/errors/endpoint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from typing import Optional


class EndpointSetupFailedError(Exception):
"""
The error that occurs when the endpoint setup failed
"""

description: Optional[str] = None

def __init__(self, description: Optional[str] = None) -> None:
if description:
self.description = description

def __str__(self):
return self.description or self.__class__.__name__
9 changes: 9 additions & 0 deletions python/dify_plugin/interfaces/endpoint/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@
from dify_plugin.core.runtime import Session


class EndpointGroup(ABC):
def setup(self, settings: Mapping):
return self._setup(settings)

@abstractmethod
def _setup(self, settings: Mapping):
pass


class Endpoint(ABC):
@final
def __init__(self, session: Session) -> None:
Expand Down
6 changes: 6 additions & 0 deletions python/dify_plugin/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,12 @@ def _register_request_routes(self):
and data.get("action") == EndpointActions.InvokeEndpoint.value,
)

self.register_route(
self.plugin_executer.setup_endpoint_group,
lambda data: data.get("type") == PluginInvokeType.Endpoint.value
and data.get("action") == EndpointActions.SetupEndpoint.value,
)

self.register_route(
self.plugin_executer.get_ai_model_schemas,
lambda data: data.get("type") == PluginInvokeType.Model.value
Expand Down