Files
Maries c6f83a63e1 feat[0.4.2]: Tool OAuth (#179)
* chore: fix ruff issue

* feat(oauth): implement OAuth

* feat(invoke-message): refactor message handling and introduce InvokeMessage class

* feat(plugin-oauth): add credential_id and credential_type to tool parameters

* feat(plugin-oauth): add credential_id and credential_type to tool parameters

* chore: update dify_plugin version to 0.5.0b4 and clean up github.yaml

* chore: update plugin version to 0.1.2 in manifest.yaml

* feat(session): session context and tool backwards invocation credential support

* feat(oauth): session context and tool backwards invocation credential support

* feat: update README and requirements for OAuth support in version 0.4.2

* feat: add .gitignore to exclude IDE files and secret keys

* chore: apply ruff

* feat: bump version to 0.4.2b1

* feat: update GitHub plugin configuration for OAuth support and improve credential handling

* feat: update .gitignore to exclude dify plugin files and public keys

* feat: fix credential validation for GitHub API and bump version to 0.2.1

* feat: update GitHub plugin to support multiple access tokens and bump version to 0.2.5

* chore: apply ruff

* feat: add ToolProviderOAuthError for improved OAuth error handling in GitHub plugin

* chore: apply ruff

* chore: bump version to 0.4.2

* chore: update examples sdk version to 0.4.2

* fix: thread deadlock in PluginRunner when running tests without gevent monkey patching

* feat: add support for refreshing OAuth credentials in Plugin and GitHub provider

* feat: refactor OAuth credential handling to return structured OAuthCredentials object

* apply ruff

* feat: refactor OAuth credential handling to use ToolOAuthCredentials for improved structure

* feat: reorganize imports in __init__.py for improved clarity and structure

* feat: add Microsoft To Do plugin for refresh token example

* chore: apply ruff

* fix: update author in GitHub configuration and clean up Microsoft To Do schema

* chore: bump version to 0.4.2b2 in pyproject.toml

* feat: update Microsoft To Do plugin to handle OAuth token encoding and version bump

* feat:remove inelegant example

* chore: update dify_plugin version to 0.4.2

* chore: bump version to 0.4.2 in pyproject.toml

---------

Co-authored-by: Yeuoly <admin@srmxy.cn>
2025-07-23 13:49:01 +08:00

171 lines
5.7 KiB
Python

from collections.abc import Mapping
from enum import Enum
from typing import Any, Union
from pydantic import BaseModel, Field, field_validator
from dify_plugin.core.documentation.schema_doc import docs
from dify_plugin.core.utils.yaml_loader import load_yaml_file
from dify_plugin.entities import I18nObject
from dify_plugin.entities.invoke_message import InvokeMessage
from dify_plugin.entities.tool import (
CommonParameterType,
ParameterAutoGenerate,
ParameterTemplate,
ToolIdentity,
ToolParameterOption,
ToolProviderIdentity,
)
@docs(
description="The identity of the agent strategy provider",
)
class AgentStrategyProviderIdentity(ToolProviderIdentity):
pass
class AgentRuntime(BaseModel):
user_id: str | None
@docs(
description="The feature of the agent strategy",
)
class AgentStrategyFeature(str, Enum):
HISTORY_MESSAGES = "history-messages"
@docs(
description="The identity of the agent strategy",
)
class AgentStrategyIdentity(ToolIdentity):
pass
@docs(
description="The parameter of the agent strategy",
)
class AgentStrategyParameter(BaseModel):
class ToolParameterType(str, Enum):
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
MODEL_SELECTOR = CommonParameterType.MODEL_SELECTOR.value
APP_SELECTOR = CommonParameterType.APP_SELECTOR.value
TOOLS_SELECTOR = CommonParameterType.TOOLS_SELECTOR.value
# TOOL_SELECTOR = CommonParameterType.TOOL_SELECTOR.value
ANY = CommonParameterType.ANY.value
# MCP object and array type parameters
OBJECT = CommonParameterType.OBJECT.value
ARRAY = CommonParameterType.ARRAY.value
name: str = Field(..., description="The name of the parameter")
label: I18nObject = Field(..., description="The label presented to the user")
help: I18nObject | None = None
type: ToolParameterType = Field(..., description="The type of the parameter")
auto_generate: ParameterAutoGenerate | None = Field(default=None, description="The auto generate of the parameter")
template: ParameterTemplate | None = Field(default=None, description="The template of the parameter")
scope: str | None = None
required: bool | None = False
default: Union[int, float, str] | None = None
min: Union[float, int] | None = None
max: Union[float, int] | None = None
precision: int | None = None
options: list[ToolParameterOption] | None = None
@docs(
name="Python",
description="The extra of the agent strategy",
)
class Python(BaseModel):
source: str
@docs(
name="AgentStrategyExtra",
description="The extra of the agent strategy",
)
class AgentStrategyConfigurationExtra(BaseModel):
python: Python
@docs(
name="AgentStrategy",
description="The Manifest of the agent strategy",
)
class AgentStrategyConfiguration(BaseModel):
identity: AgentStrategyIdentity
parameters: list[AgentStrategyParameter] = Field(default=[], description="The parameters of the agent")
description: I18nObject
extra: AgentStrategyConfigurationExtra
has_runtime_parameters: bool = Field(default=False, description="Whether the tool has runtime parameters")
output_schema: Mapping[str, Any] | None = None
features: list[AgentStrategyFeature] = Field(default=[], description="The features of the agent")
@docs(
name="AgentStrategyProviderExtra",
description="The extra of the agent provider",
)
class AgentProviderConfigurationExtra(BaseModel):
@docs(
name="Python",
description="The extra of the agent provider",
)
class Python(BaseModel):
source: str
python: Python
@docs(
name="AgentStrategyProvider",
description="The Manifest of the agent strategy provider",
outside_reference_fields={"strategies": AgentStrategyConfiguration},
)
class AgentStrategyProviderConfiguration(BaseModel):
identity: AgentStrategyProviderIdentity
strategies: list[AgentStrategyConfiguration] = Field(default=[], description="The strategies of the agent provider")
@field_validator("strategies", mode="before")
@classmethod
def validate_strategies(cls, value) -> list[AgentStrategyConfiguration]:
if not isinstance(value, list):
raise ValueError("strategies should be a list")
strategies: list[AgentStrategyConfiguration] = []
for strategy in value:
# read from yaml
if not isinstance(strategy, str):
raise ValueError("strategy path should be a string")
try:
file = load_yaml_file(strategy)
strategies.append(
AgentStrategyConfiguration(
**{
"identity": AgentStrategyIdentity(**file["identity"]),
"parameters": [
AgentStrategyParameter(**param) for param in file.get("parameters", []) or []
],
"description": I18nObject(**file["description"]),
"extra": AgentStrategyConfigurationExtra(**file.get("extra", {})),
"features": file.get("features", []),
}
)
)
except Exception as e:
raise ValueError(f"Error loading agent strategy configuration: {e!s}") from e
return strategies
class AgentInvokeMessage(InvokeMessage):
pass