Files
dify-plugin-sdks/python/dify_plugin/entities/invoke_message.py
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

150 lines
5.1 KiB
Python

import base64
import contextlib
import uuid
from collections.abc import Mapping
from enum import Enum
from typing import Any
from pydantic import (
BaseModel,
Field,
field_serializer,
field_validator,
model_validator,
)
from dify_plugin.entities.provider_config import LogMetadata
class InvokeMessage(BaseModel):
class TextMessage(BaseModel):
text: str
def to_dict(self):
return {"text": self.text}
class JsonMessage(BaseModel):
json_object: Mapping | list
def to_dict(self):
return {"json_object": self.json_object}
class BlobMessage(BaseModel):
blob: bytes
class BlobChunkMessage(BaseModel):
id: str = Field(..., description="The id of the blob")
sequence: int = Field(..., description="The sequence of the chunk")
total_length: int = Field(..., description="The total length of the blob")
blob: bytes = Field(..., description="The blob data of the chunk")
end: bool = Field(..., description="Whether the chunk is the last chunk")
class VariableMessage(BaseModel):
variable_name: str = Field(
...,
description="The name of the variable, only supports root-level variables",
)
variable_value: Any = Field(..., description="The value of the variable")
stream: bool = Field(default=False, description="Whether the variable is streamed")
@model_validator(mode="before")
@classmethod
def validate_variable_value_and_stream(cls, values):
# skip validation if values is not a dict
if not isinstance(values, dict):
return values
if values.get("stream") and not isinstance(values.get("variable_value"), str):
raise ValueError("When 'stream' is True, 'variable_value' must be a string.")
return values
class LogMessage(BaseModel):
class LogStatus(Enum):
START = "start"
ERROR = "error"
SUCCESS = "success"
id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="The id of the log")
label: str = Field(..., description="The label of the log")
parent_id: str | None = Field(default=None, description="Leave empty for root log")
error: str | None = Field(default=None, description="The error message")
status: LogStatus = Field(..., description="The status of the log")
data: Mapping[str, Any] = Field(..., description="Detailed log data")
metadata: Mapping[LogMetadata, Any] | None = Field(default=None, description="The metadata of the log")
class RetrieverResourceMessage(BaseModel):
class RetrieverResource(BaseModel):
"""
Model class for retriever resource.
"""
position: int | None = None
dataset_id: str | None = None
dataset_name: str | None = None
document_id: str | None = None
document_name: str | None = None
data_source_type: str | None = None
segment_id: str | None = None
retriever_from: str | None = None
score: float | None = None
hit_count: int | None = None
word_count: int | None = None
segment_position: int | None = None
index_node_hash: str | None = None
content: str | None = None
page: int | None = None
doc_metadata: dict | None = None
retriever_resources: list[RetrieverResource] = Field(..., description="retriever resources")
context: str = Field(..., description="context")
class MessageType(Enum):
TEXT = "text"
FILE = "file"
BLOB = "blob"
JSON = "json"
LINK = "link"
IMAGE = "image"
IMAGE_LINK = "image_link"
VARIABLE = "variable"
BLOB_CHUNK = "blob_chunk"
LOG = "log"
RETRIEVER_RESOURCES = "retriever_resources"
type: MessageType
# TODO: pydantic will validate and construct the message one by one, until it encounters a correct type
# we need to optimize the construction process
message: (
TextMessage
| JsonMessage
| VariableMessage
| BlobMessage
| BlobChunkMessage
| LogMessage
| RetrieverResourceMessage
| None
)
meta: dict | None = None
@field_validator("message", mode="before")
@classmethod
def decode_blob_message(cls, v):
if isinstance(v, dict) and "blob" in v:
with contextlib.suppress(Exception):
v["blob"] = base64.b64decode(v["blob"])
return v
@field_serializer("message")
def serialize_message(self, v):
if isinstance(v, self.BlobMessage):
return {"blob": base64.b64encode(v.blob).decode("utf-8")}
elif isinstance(v, self.BlobChunkMessage):
return {
"id": v.id,
"sequence": v.sequence,
"total_length": v.total_length,
"blob": base64.b64encode(v.blob).decode("utf-8"),
"end": v.end,
}
return v