Merge remote-tracking branch 'origin/main' into feat/trigger

This commit is contained in:
yessenia
2025-09-25 17:14:24 +08:00
3013 changed files with 148826 additions and 44294 deletions

View File

@ -1,5 +1,4 @@
from datetime import datetime
from typing import Optional
from pydantic import BaseModel, Field, model_validator
@ -24,7 +23,7 @@ class EndpointProviderDeclaration(BaseModel):
"""
settings: list[ProviderConfig] = Field(default_factory=list)
endpoints: Optional[list[EndpointDeclaration]] = Field(default_factory=list[EndpointDeclaration])
endpoints: list[EndpointDeclaration] | None = Field(default_factory=list[EndpointDeclaration])
class EndpointEntity(BasePluginEntity):

View File

@ -1,5 +1,3 @@
from typing import Optional
from pydantic import BaseModel, Field, model_validator
from core.model_runtime.entities.provider_entities import ProviderEntity
@ -19,11 +17,11 @@ class MarketplacePluginDeclaration(BaseModel):
resource: PluginResourceRequirements = Field(
..., description="Specification of computational resources needed to run the plugin"
)
endpoint: Optional[EndpointProviderDeclaration] = Field(
endpoint: EndpointProviderDeclaration | None = Field(
None, description="Configuration for the plugin's API endpoint, if applicable"
)
model: Optional[ProviderEntity] = Field(None, description="Details of the AI model used by the plugin, if any")
tool: Optional[ToolProviderEntity] = Field(
model: ProviderEntity | None = Field(None, description="Details of the AI model used by the plugin, if any")
tool: ToolProviderEntity | None = Field(
None, description="Information about the tool functionality provided by the plugin, if any"
)
latest_version: str = Field(

View File

@ -0,0 +1,21 @@
from collections.abc import Sequence
from pydantic import BaseModel, Field
from core.entities.provider_entities import ProviderConfig
class OAuthSchema(BaseModel):
"""
OAuth schema
"""
client_schema: Sequence[ProviderConfig] = Field(
default_factory=list,
description="client schema like client_id, client_secret, etc.",
)
credentials_schema: Sequence[ProviderConfig] = Field(
default_factory=list,
description="credentials schema like access_token, refresh_token, etc.",
)

View File

@ -1,19 +1,17 @@
import enum
from typing import Any, Optional, Union
import json
from enum import StrEnum, auto
from typing import Any, Union
from pydantic import BaseModel, Field, field_validator
from core.entities.parameter_entities import CommonParameterType
from core.tools.entities.common_entities import I18nObject
from core.workflow.nodes.base.entities import NumberType
class PluginParameterOption(BaseModel):
value: str = Field(..., description="The value of the option")
label: I18nObject = Field(..., description="The label of the option")
icon: Optional[str] = Field(
default=None, description="The icon of the option, can be a url or a base64 encoded image"
)
icon: str | None = Field(default=None, description="The icon of the option, can be a url or a base64 encoded image")
@field_validator("value", mode="before")
@classmethod
@ -24,44 +22,44 @@ class PluginParameterOption(BaseModel):
return value
class PluginParameterType(enum.StrEnum):
class PluginParameterType(StrEnum):
"""
all available parameter types
"""
STRING = CommonParameterType.STRING.value
NUMBER = CommonParameterType.NUMBER.value
BOOLEAN = CommonParameterType.BOOLEAN.value
SELECT = CommonParameterType.SELECT.value
SECRET_INPUT = CommonParameterType.SECRET_INPUT.value
FILE = CommonParameterType.FILE.value
FILES = CommonParameterType.FILES.value
APP_SELECTOR = CommonParameterType.APP_SELECTOR.value
MODEL_SELECTOR = CommonParameterType.MODEL_SELECTOR.value
TOOLS_SELECTOR = CommonParameterType.TOOLS_SELECTOR.value
ANY = CommonParameterType.ANY.value
DYNAMIC_SELECT = CommonParameterType.DYNAMIC_SELECT.value
STRING = CommonParameterType.STRING
NUMBER = CommonParameterType.NUMBER
BOOLEAN = CommonParameterType.BOOLEAN
SELECT = CommonParameterType.SELECT
SECRET_INPUT = CommonParameterType.SECRET_INPUT
FILE = CommonParameterType.FILE
FILES = CommonParameterType.FILES
APP_SELECTOR = CommonParameterType.APP_SELECTOR
MODEL_SELECTOR = CommonParameterType.MODEL_SELECTOR
TOOLS_SELECTOR = CommonParameterType.TOOLS_SELECTOR
ANY = CommonParameterType.ANY
DYNAMIC_SELECT = CommonParameterType.DYNAMIC_SELECT
# deprecated, should not use.
SYSTEM_FILES = CommonParameterType.SYSTEM_FILES.value
SYSTEM_FILES = CommonParameterType.SYSTEM_FILES
# MCP object and array type parameters
ARRAY = CommonParameterType.ARRAY.value
OBJECT = CommonParameterType.OBJECT.value
ARRAY = CommonParameterType.ARRAY
OBJECT = CommonParameterType.OBJECT
class MCPServerParameterType(enum.StrEnum):
class MCPServerParameterType(StrEnum):
"""
MCP server got complex parameter types
"""
ARRAY = "array"
OBJECT = "object"
ARRAY = auto()
OBJECT = auto()
class PluginParameterAutoGenerate(BaseModel):
class Type(enum.StrEnum):
PROMPT_INSTRUCTION = "prompt_instruction"
class Type(StrEnum):
PROMPT_INSTRUCTION = auto()
type: Type
@ -73,15 +71,15 @@ class PluginParameterTemplate(BaseModel):
class PluginParameter(BaseModel):
name: str = Field(..., description="The name of the parameter")
label: I18nObject = Field(..., description="The label presented to the user")
placeholder: Optional[I18nObject] = Field(default=None, description="The placeholder presented to the user")
placeholder: I18nObject | None = Field(default=None, description="The placeholder presented to the user")
scope: str | None = None
auto_generate: Optional[PluginParameterAutoGenerate] = None
template: Optional[PluginParameterTemplate] = None
auto_generate: PluginParameterAutoGenerate | None = None
template: PluginParameterTemplate | None = None
required: bool = False
default: Optional[Union[float, int, str]] = None
min: Optional[Union[float, int]] = None
max: Optional[Union[float, int]] = None
precision: Optional[int] = None
default: Union[float, int, str] | None = None
min: Union[float, int] | None = None
max: Union[float, int] | None = None
precision: int | None = None
options: list[PluginParameterOption] = Field(default_factory=list)
@field_validator("options", mode="before")
@ -92,7 +90,7 @@ class PluginParameter(BaseModel):
return v
def as_normal_type(typ: enum.StrEnum):
def as_normal_type(typ: StrEnum):
if typ.value in {
PluginParameterType.SECRET_INPUT,
PluginParameterType.SELECT,
@ -101,7 +99,7 @@ def as_normal_type(typ: enum.StrEnum):
return typ.value
def cast_parameter_value(typ: enum.StrEnum, value: Any, /):
def cast_parameter_value(typ: StrEnum, value: Any, /):
try:
match typ.value:
case PluginParameterType.STRING | PluginParameterType.SECRET_INPUT | PluginParameterType.SELECT:
@ -154,7 +152,7 @@ def cast_parameter_value(typ: enum.StrEnum, value: Any, /):
raise ValueError("The tools selector must be a list.")
return value
case PluginParameterType.ANY:
if value and not isinstance(value, str | dict | list | NumberType):
if value and not isinstance(value, str | dict | list | int | float):
raise ValueError("The var selector must be a string, dictionary, list or number.")
return value
case PluginParameterType.ARRAY:
@ -162,8 +160,6 @@ def cast_parameter_value(typ: enum.StrEnum, value: Any, /):
# Try to parse JSON string for arrays
if isinstance(value, str):
try:
import json
parsed_value = json.loads(value)
if isinstance(parsed_value, list):
return parsed_value
@ -176,8 +172,6 @@ def cast_parameter_value(typ: enum.StrEnum, value: Any, /):
# Try to parse JSON string for objects
if isinstance(value, str):
try:
import json
parsed_value = json.loads(value)
if isinstance(parsed_value, dict):
return parsed_value
@ -193,7 +187,7 @@ def cast_parameter_value(typ: enum.StrEnum, value: Any, /):
raise ValueError(f"The tool parameter value {value} is not in correct type of {as_normal_type(typ)}.")
def init_frontend_parameter(rule: PluginParameter, type: enum.StrEnum, value: Any):
def init_frontend_parameter(rule: PluginParameter, type: StrEnum, value: Any):
"""
init frontend parameter by rule
"""

View File

@ -1,13 +1,13 @@
import datetime
import enum
import re
from collections.abc import Mapping
from typing import Any, Optional
from enum import StrEnum, auto
from typing import Any
from pydantic import BaseModel, Field, model_validator
from werkzeug.exceptions import NotFound
from packaging.version import InvalidVersion, Version
from pydantic import BaseModel, Field, field_validator, model_validator
from core.agent.plugin_entities import AgentStrategyProviderEntity
from core.datasource.entities.datasource_entities import DatasourceProviderEntity
from core.model_runtime.entities.provider_entities import ProviderEntity
from core.plugin.entities.base import BasePluginEntity
from core.plugin.entities.endpoint import EndpointProviderDeclaration
@ -16,11 +16,11 @@ from core.tools.entities.tool_entities import ToolProviderEntity
from core.trigger.entities.entities import TriggerProviderEntity
class PluginInstallationSource(enum.StrEnum):
Github = "github"
Marketplace = "marketplace"
Package = "package"
Remote = "remote"
class PluginInstallationSource(StrEnum):
Github = auto()
Marketplace = auto()
Package = auto()
Remote = auto()
class PluginResourceRequirements(BaseModel):
@ -28,84 +28,109 @@ class PluginResourceRequirements(BaseModel):
class Permission(BaseModel):
class Tool(BaseModel):
enabled: Optional[bool] = Field(default=False)
enabled: bool | None = Field(default=False)
class Model(BaseModel):
enabled: Optional[bool] = Field(default=False)
llm: Optional[bool] = Field(default=False)
text_embedding: Optional[bool] = Field(default=False)
rerank: Optional[bool] = Field(default=False)
tts: Optional[bool] = Field(default=False)
speech2text: Optional[bool] = Field(default=False)
moderation: Optional[bool] = Field(default=False)
enabled: bool | None = Field(default=False)
llm: bool | None = Field(default=False)
text_embedding: bool | None = Field(default=False)
rerank: bool | None = Field(default=False)
tts: bool | None = Field(default=False)
speech2text: bool | None = Field(default=False)
moderation: bool | None = Field(default=False)
class Node(BaseModel):
enabled: Optional[bool] = Field(default=False)
enabled: bool | None = Field(default=False)
class Endpoint(BaseModel):
enabled: Optional[bool] = Field(default=False)
enabled: bool | None = Field(default=False)
class Storage(BaseModel):
enabled: Optional[bool] = Field(default=False)
enabled: bool | None = Field(default=False)
size: int = Field(ge=1024, le=1073741824, default=1048576)
tool: Optional[Tool] = Field(default=None)
model: Optional[Model] = Field(default=None)
node: Optional[Node] = Field(default=None)
endpoint: Optional[Endpoint] = Field(default=None)
storage: Optional[Storage] = Field(default=None)
tool: Tool | None = Field(default=None)
model: Model | None = Field(default=None)
node: Node | None = Field(default=None)
endpoint: Endpoint | None = Field(default=None)
storage: Storage | None = Field(default=None)
permission: Optional[Permission] = Field(default=None)
permission: Permission | None = Field(default=None)
class PluginCategory(enum.StrEnum):
Tool = "tool"
Model = "model"
Extension = "extension"
class PluginCategory(StrEnum):
Tool = auto()
Model = auto()
Extension = auto()
AgentStrategy = "agent-strategy"
Datasource = "datasource"
Trigger = "trigger"
class PluginDeclaration(BaseModel):
class Plugins(BaseModel):
tools: Optional[list[str]] = Field(default_factory=list[str])
models: Optional[list[str]] = Field(default_factory=list[str])
endpoints: Optional[list[str]] = Field(default_factory=list[str])
tools: list[str] | None = Field(default_factory=list[str])
models: list[str] | None = Field(default_factory=list[str])
endpoints: list[str] | None = Field(default_factory=list[str])
datasources: list[str] | None = Field(default_factory=list[str])
triggers: Optional[list[str]] = Field(default_factory=list[str])
class Meta(BaseModel):
minimum_dify_version: Optional[str] = Field(default=None, pattern=r"^\d{1,4}(\.\d{1,4}){1,3}(-\w{1,16})?$")
version: Optional[str] = Field(default=None)
minimum_dify_version: str | None = Field(default=None)
version: str | None = Field(default=None)
version: str = Field(..., pattern=r"^\d{1,4}(\.\d{1,4}){1,3}(-\w{1,16})?$")
author: Optional[str] = Field(..., pattern=r"^[a-zA-Z0-9_-]{1,64}$")
@field_validator("minimum_dify_version")
@classmethod
def validate_minimum_dify_version(cls, v: str | None) -> str | None:
if v is None:
return v
try:
Version(v)
return v
except InvalidVersion as e:
raise ValueError(f"Invalid version format: {v}") from e
version: str = Field(...)
author: str | None = Field(..., pattern=r"^[a-zA-Z0-9_-]{1,64}$")
name: str = Field(..., pattern=r"^[a-z0-9_-]{1,128}$")
description: I18nObject
icon: str
icon_dark: Optional[str] = Field(default=None)
icon_dark: str | None = Field(default=None)
label: I18nObject
category: PluginCategory
created_at: datetime.datetime
resource: PluginResourceRequirements
plugins: Plugins
tags: list[str] = Field(default_factory=list)
repo: Optional[str] = Field(default=None)
repo: str | None = Field(default=None)
verified: bool = Field(default=False)
tool: Optional[ToolProviderEntity] = None
tool: ToolProviderEntity | None = None
model: ProviderEntity | None = None
endpoint: EndpointProviderDeclaration | None = None
agent_strategy: AgentStrategyProviderEntity | None = None
datasource: DatasourceProviderEntity | None = None
trigger: Optional[TriggerProviderEntity] = None
model: Optional[ProviderEntity] = None
endpoint: Optional[EndpointProviderDeclaration] = None
agent_strategy: Optional[AgentStrategyProviderEntity] = None
meta: Meta
@field_validator("version")
@classmethod
def validate_version(cls, v: str) -> str:
try:
Version(v)
return v
except InvalidVersion as e:
raise ValueError(f"Invalid version format: {v}") from e
@model_validator(mode="before")
@classmethod
def validate_category(cls, values: dict) -> dict:
def validate_category(cls, values: dict):
# auto detect category
if values.get("tool"):
values["category"] = PluginCategory.Tool
elif values.get("model"):
values["category"] = PluginCategory.Model
elif values.get("datasource"):
values["category"] = PluginCategory.Datasource
elif values.get("agent_strategy"):
values["category"] = PluginCategory.AgentStrategy
elif values.get("trigger"):
@ -141,64 +166,11 @@ class PluginEntity(PluginInstallation):
return self
class GenericProviderID:
organization: str
plugin_name: str
provider_name: str
is_hardcoded: bool
def to_string(self) -> str:
return str(self)
def __str__(self) -> str:
return f"{self.organization}/{self.plugin_name}/{self.provider_name}"
def __init__(self, value: str, is_hardcoded: bool = False) -> None:
if not value:
raise NotFound("plugin not found, please add plugin")
# check if the value is a valid plugin id with format: $organization/$plugin_name/$provider_name
if not re.match(r"^[a-z0-9_-]+\/[a-z0-9_-]+\/[a-z0-9_-]+$", value):
# check if matches [a-z0-9_-]+, if yes, append with langgenius/$value/$value
if re.match(r"^[a-z0-9_-]+$", value):
value = f"langgenius/{value}/{value}"
else:
raise ValueError(f"Invalid plugin id {value}")
self.organization, self.plugin_name, self.provider_name = value.split("/")
self.is_hardcoded = is_hardcoded
def is_langgenius(self) -> bool:
return self.organization == "langgenius"
@property
def plugin_id(self) -> str:
return f"{self.organization}/{self.plugin_name}"
class ModelProviderID(GenericProviderID):
def __init__(self, value: str, is_hardcoded: bool = False) -> None:
super().__init__(value, is_hardcoded)
if self.organization == "langgenius" and self.provider_name == "google":
self.plugin_name = "gemini"
class ToolProviderID(GenericProviderID):
def __init__(self, value: str, is_hardcoded: bool = False) -> None:
super().__init__(value, is_hardcoded)
if self.organization == "langgenius":
if self.provider_name in ["jina", "siliconflow", "stepfun", "gitee_ai"]:
self.plugin_name = f"{self.provider_name}_tool"
class TriggerProviderID(GenericProviderID):
pass
class PluginDependency(BaseModel):
class Type(enum.StrEnum):
Github = PluginInstallationSource.Github.value
Marketplace = PluginInstallationSource.Marketplace.value
Package = PluginInstallationSource.Package.value
class Type(StrEnum):
Github = PluginInstallationSource.Github
Marketplace = PluginInstallationSource.Marketplace
Package = PluginInstallationSource.Package
class Github(BaseModel):
repo: str
@ -212,6 +184,7 @@ class PluginDependency(BaseModel):
class Marketplace(BaseModel):
marketplace_plugin_unique_identifier: str
version: str | None = None
@property
def plugin_unique_identifier(self) -> str:
@ -219,12 +192,13 @@ class PluginDependency(BaseModel):
class Package(BaseModel):
plugin_unique_identifier: str
version: str | None = None
type: Type
value: Github | Marketplace | Package
current_identifier: Optional[str] = None
current_identifier: str | None = None
class MissingPluginDependency(BaseModel):
plugin_unique_identifier: str
current_identifier: Optional[str] = None
current_identifier: str | None = None

View File

@ -2,11 +2,12 @@ import enum
from collections.abc import Mapping, Sequence
from datetime import datetime
from enum import StrEnum
from typing import Any, Generic, Optional, TypeVar
from typing import Any, Generic, TypeVar
from pydantic import BaseModel, ConfigDict, Field
from core.agent.plugin_entities import AgentProviderEntityWithPlugin
from core.datasource.entities.datasource_entities import DatasourceProviderEntityWithPlugin
from core.model_runtime.entities.model_entities import AIModelEntity
from core.model_runtime.entities.provider_entities import ProviderEntity
from core.plugin.entities.base import BasePluginEntity
@ -26,7 +27,7 @@ class PluginDaemonBasicResponse(BaseModel, Generic[T]):
code: int
message: str
data: Optional[T]
data: T | None = None
class InstallPluginMessage(BaseModel):
@ -50,6 +51,14 @@ class PluginToolProviderEntity(BaseModel):
declaration: ToolProviderEntityWithPlugin
class PluginDatasourceProviderEntity(BaseModel):
provider: str
plugin_unique_identifier: str
plugin_id: str
is_authorized: bool = False
declaration: DatasourceProviderEntityWithPlugin
class PluginAgentProviderEntity(BaseModel):
provider: str
plugin_unique_identifier: str
@ -176,7 +185,7 @@ class PluginVerification(BaseModel):
class PluginDecodeResponse(BaseModel):
unique_identifier: str = Field(description="The unique identifier of the plugin.")
manifest: PluginDeclaration
verification: Optional[PluginVerification] = Field(default=None, description="Basic verification information")
verification: PluginVerification | None = Field(default=None, description="Basic verification information")
class PluginOAuthAuthorizationUrlResponse(BaseModel):
@ -235,9 +244,9 @@ class CredentialType(enum.StrEnum):
@classmethod
def of(cls, credential_type: str) -> "CredentialType":
type_name = credential_type.lower()
if type_name == "api-key":
if type_name in {"api-key", "api_key"}:
return cls.API_KEY
elif type_name == "oauth2":
elif type_name in {"oauth2", "oauth"}:
return cls.OAUTH2
elif type_name == "unauthorized":
return cls.UNAUTHORIZED

View File

@ -37,7 +37,7 @@ class InvokeCredentials(BaseModel):
class PluginInvokeContext(BaseModel):
credentials: Optional[InvokeCredentials] = Field(
credentials: InvokeCredentials | None = Field(
default_factory=InvokeCredentials,
description="Credentials context for the plugin invocation or backward invocation.",
)
@ -52,7 +52,7 @@ class RequestInvokeTool(BaseModel):
provider: str
tool: str
tool_parameters: dict
credential_id: Optional[str] = None
credential_id: str | None = None
class BaseRequestInvokeModel(BaseModel):
@ -72,9 +72,9 @@ class RequestInvokeLLM(BaseRequestInvokeModel):
mode: str
completion_params: dict[str, Any] = Field(default_factory=dict)
prompt_messages: list[PromptMessage] = Field(default_factory=list)
tools: Optional[list[PromptMessageTool]] = Field(default_factory=list[PromptMessageTool])
stop: Optional[list[str]] = Field(default_factory=list[str])
stream: Optional[bool] = False
tools: list[PromptMessageTool] | None = Field(default_factory=list[PromptMessageTool])
stop: list[str] | None = Field(default_factory=list[str])
stream: bool | None = False
model_config = ConfigDict(protected_namespaces=())
@ -196,10 +196,10 @@ class RequestInvokeApp(BaseModel):
app_id: str
inputs: dict[str, Any]
query: Optional[str] = None
query: str | None = None
response_mode: Literal["blocking", "streaming"]
conversation_id: Optional[str] = None
user: Optional[str] = None
conversation_id: str | None = None
user: str | None = None
files: list[dict] = Field(default_factory=list)