dify_plugin 0.3.0__tar.gz → 0.3.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/PKG-INFO +1 -2
  2. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/core/entities/plugin/request.py +28 -6
  3. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/core/plugin_executor.py +30 -0
  4. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/core/plugin_registration.py +22 -0
  5. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/entities/tool.py +7 -27
  6. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/integration/run.py +32 -1
  7. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/interfaces/tool/__init__.py +16 -3
  8. dify_plugin-0.3.2/dify_plugin/protocol/oauth.py +27 -0
  9. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/pyproject.toml +1 -2
  10. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/tests/__mock_server/openai.py +3 -3
  11. dify_plugin-0.3.2/tests/consts/mockserver.py +1 -0
  12. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/tests/integration/test_invoke_llm.py +1 -1
  13. dify_plugin-0.3.2/tests/interfaces/model/openai_compatible/__init__.py +0 -0
  14. dify_plugin-0.3.2/tests/interfaces/tool/__init__.py +0 -0
  15. dify_plugin-0.3.2/tests/interfaces/tool/test_construct_tool_provider.py +20 -0
  16. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/LICENSE +0 -0
  17. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/README.md +0 -0
  18. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/__init__.py +0 -0
  19. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/cli.py +0 -0
  20. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/commands/__init__.py +0 -0
  21. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/commands/generate_docs.py +0 -0
  22. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/config/__init__.py +0 -0
  23. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/config/config.py +0 -0
  24. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/config/integration_config.py +0 -0
  25. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/config/logger_format.py +0 -0
  26. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/core/__init__.py +0 -0
  27. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/core/documentation/__init__.py +0 -0
  28. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/core/documentation/generator.py +0 -0
  29. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/core/documentation/schema_doc.py +0 -0
  30. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/core/entities/__init__.py +0 -0
  31. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/core/entities/invocation.py +0 -0
  32. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/core/entities/message.py +0 -0
  33. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/core/entities/plugin/__init__.py +0 -0
  34. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/core/entities/plugin/io.py +0 -0
  35. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/core/entities/plugin/parameter_type.py +0 -0
  36. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/core/entities/plugin/setup.py +0 -0
  37. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/core/runtime.py +0 -0
  38. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/core/server/__base/__init__.py +0 -0
  39. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/core/server/__base/filter_reader.py +0 -0
  40. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/core/server/__base/request_reader.py +0 -0
  41. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/core/server/__base/response_writer.py +0 -0
  42. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/core/server/__base/writer_entities.py +0 -0
  43. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/core/server/__init__.py +0 -0
  44. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/core/server/io_server.py +0 -0
  45. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/core/server/router.py +0 -0
  46. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/core/server/serverless/__init__.py +0 -0
  47. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/core/server/serverless/request_reader.py +0 -0
  48. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/core/server/serverless/response_writer.py +0 -0
  49. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/core/server/stdio/__init__.py +0 -0
  50. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/core/server/stdio/request_reader.py +0 -0
  51. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/core/server/stdio/response_writer.py +0 -0
  52. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/core/server/tcp/__init__.py +0 -0
  53. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/core/server/tcp/request_reader.py +0 -0
  54. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/core/utils/__init__.py +0 -0
  55. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/core/utils/class_loader.py +0 -0
  56. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/core/utils/http_parser.py +0 -0
  57. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/core/utils/position_helper.py +0 -0
  58. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/core/utils/yaml_loader.py +0 -0
  59. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/entities/__init__.py +0 -0
  60. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/entities/agent.py +0 -0
  61. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/entities/endpoint.py +0 -0
  62. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/entities/model/__init__.py +0 -0
  63. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/entities/model/llm.py +0 -0
  64. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/entities/model/message.py +0 -0
  65. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/entities/model/moderation.py +0 -0
  66. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/entities/model/provider.py +0 -0
  67. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/entities/model/rerank.py +0 -0
  68. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/entities/model/speech2text.py +0 -0
  69. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/entities/model/text_embedding.py +0 -0
  70. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/entities/model/tts.py +0 -0
  71. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/entities/oauth.py +0 -0
  72. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/entities/provider_config.py +0 -0
  73. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/entities/workflow_node.py +0 -0
  74. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/errors/__init__.py +0 -0
  75. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/errors/model.py +0 -0
  76. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/errors/tool.py +0 -0
  77. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/file/__init__.py +0 -0
  78. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/file/constants.py +0 -0
  79. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/file/entities.py +0 -0
  80. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/file/file.py +0 -0
  81. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/integration/entities.py +0 -0
  82. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/integration/exc.py +0 -0
  83. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/interfaces/__init__.py +0 -0
  84. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/interfaces/agent/__init__.py +0 -0
  85. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/interfaces/endpoint/__init__.py +0 -0
  86. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/interfaces/model/__init__.py +0 -0
  87. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/interfaces/model/ai_model.py +0 -0
  88. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/interfaces/model/audio.mp3 +0 -0
  89. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/interfaces/model/large_language_model.py +0 -0
  90. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/interfaces/model/moderation_model.py +0 -0
  91. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/interfaces/model/openai_compatible/__init__.py +0 -0
  92. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/interfaces/model/openai_compatible/common.py +0 -0
  93. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/interfaces/model/openai_compatible/llm.py +0 -0
  94. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/interfaces/model/openai_compatible/provider.py +0 -0
  95. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/interfaces/model/openai_compatible/rerank.py +0 -0
  96. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/interfaces/model/openai_compatible/speech2text.py +0 -0
  97. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/interfaces/model/openai_compatible/text_embedding.py +0 -0
  98. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/interfaces/model/openai_compatible/tts.py +0 -0
  99. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/interfaces/model/rerank_model.py +0 -0
  100. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/interfaces/model/speech2text_model.py +0 -0
  101. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/interfaces/model/text_embedding_model.py +0 -0
  102. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/interfaces/model/tts_model.py +0 -0
  103. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/invocations/__init__.py +0 -0
  104. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/invocations/app/__init__.py +0 -0
  105. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/invocations/app/chat.py +0 -0
  106. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/invocations/app/completion.py +0 -0
  107. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/invocations/app/workflow.py +0 -0
  108. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/invocations/file.py +0 -0
  109. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/invocations/model/__init__.py +0 -0
  110. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/invocations/model/llm.py +0 -0
  111. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/invocations/model/moderation.py +0 -0
  112. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/invocations/model/rerank.py +0 -0
  113. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/invocations/model/speech2text.py +0 -0
  114. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/invocations/model/text_embedding.py +0 -0
  115. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/invocations/model/tts.py +0 -0
  116. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/invocations/storage.py +0 -0
  117. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/invocations/tool.py +0 -0
  118. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/invocations/workflow_node/__init__.py +0 -0
  119. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/invocations/workflow_node/parameter_extractor.py +0 -0
  120. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/invocations/workflow_node/question_classifier.py +0 -0
  121. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/plugin.py +0 -0
  122. {dify_plugin-0.3.0/dify_plugin/tool → dify_plugin-0.3.2/dify_plugin/protocol}/__init__.py +0 -0
  123. {dify_plugin-0.3.0/tests → dify_plugin-0.3.2/dify_plugin/tool}/__init__.py +0 -0
  124. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/dify_plugin/tool/entities.py +0 -0
  125. {dify_plugin-0.3.0/tests/interfaces/model → dify_plugin-0.3.2/tests}/__init__.py +0 -0
  126. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/tests/__mock_server/__init__.py +0 -0
  127. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/tests/config/test_config.py +0 -0
  128. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/tests/entities/endpoint/test_endpoint_group.py +0 -0
  129. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/tests/entities/models/test_llm.py +0 -0
  130. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/tests/entities/plugin/test_declaration.py +0 -0
  131. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/tests/interfaces/agent/test_agent.py +0 -0
  132. {dify_plugin-0.3.0/tests/interfaces/model/openai_compatible → dify_plugin-0.3.2/tests/interfaces/model}/__init__.py +0 -0
  133. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/tests/interfaces/model/openai_compatible/test_increase_tool_call.py +0 -0
  134. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/tests/invocations/test_storage.py +0 -0
  135. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/tests/servers/test_stdio.py +0 -0
  136. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/tests/test_llm_result.py +0 -0
  137. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/tests/test_prompt_message.py +0 -0
  138. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/tests/test_tool_call_model_init.py +0 -0
  139. {dify_plugin-0.3.0 → dify_plugin-0.3.2}/tests/utils/test_http_parser.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dify_plugin
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Summary: Dify Plugin SDK
5
5
  Keywords: dify,plugin,sdk
6
6
  Author-Email: langgenius <hello@dify.ai>
@@ -14,7 +14,6 @@ Requires-Dist: gevent~=24.11.1
14
14
  Requires-Dist: httpx~=0.28.1
15
15
  Requires-Dist: pydantic_settings<3.0.0,>=2.5.0
16
16
  Requires-Dist: pydantic>=2.8.2
17
- Requires-Dist: pydub~=0.25.1
18
17
  Requires-Dist: pyyaml~=6.0.1
19
18
  Requires-Dist: requests~=2.32.3
20
19
  Requires-Dist: socksio==1.0.0
@@ -1,4 +1,5 @@
1
- from enum import Enum
1
+ from collections.abc import Mapping
2
+ from enum import StrEnum
2
3
  from typing import Any, Optional
3
4
 
4
5
  from pydantic import BaseModel, ConfigDict, field_validator
@@ -15,24 +16,25 @@ from dify_plugin.entities.model.message import (
15
16
  )
16
17
 
17
18
 
18
- class PluginInvokeType(Enum):
19
+ class PluginInvokeType(StrEnum):
19
20
  Tool = "tool"
20
21
  Model = "model"
21
22
  Endpoint = "endpoint"
22
23
  Agent = "agent_strategy"
24
+ OAuth = "oauth"
23
25
 
24
26
 
25
- class AgentActions(Enum):
27
+ class AgentActions(StrEnum):
26
28
  InvokeAgentStrategy = "invoke_agent_strategy"
27
29
 
28
30
 
29
- class ToolActions(Enum):
31
+ class ToolActions(StrEnum):
30
32
  ValidateCredentials = "validate_tool_credentials"
31
33
  InvokeTool = "invoke_tool"
32
34
  GetToolRuntimeParameters = "get_tool_runtime_parameters"
33
35
 
34
36
 
35
- class ModelActions(Enum):
37
+ class ModelActions(StrEnum):
36
38
  ValidateProviderCredentials = "validate_provider_credentials"
37
39
  ValidateModelCredentials = "validate_model_credentials"
38
40
  InvokeLLM = "invoke_llm"
@@ -47,10 +49,15 @@ class ModelActions(Enum):
47
49
  GetAIModelSchemas = "get_ai_model_schemas"
48
50
 
49
51
 
50
- class EndpointActions(Enum):
52
+ class EndpointActions(StrEnum):
51
53
  InvokeEndpoint = "invoke_endpoint"
52
54
 
53
55
 
56
+ class OAuthActions(StrEnum):
57
+ GetAuthorizationUrl = "get_authorization_url"
58
+ GetCredentials = "get_credentials"
59
+
60
+
54
61
  # merge all the access actions
55
62
  PluginAccessAction = AgentActions | ToolActions | ModelActions | EndpointActions
56
63
 
@@ -227,3 +234,18 @@ class EndpointInvokeRequest(BaseModel):
227
234
  action: EndpointActions = EndpointActions.InvokeEndpoint
228
235
  settings: dict
229
236
  raw_http_request: str
237
+
238
+
239
+ class OAuthGetAuthorizationUrlRequest(BaseModel):
240
+ type: PluginInvokeType = PluginInvokeType.OAuth
241
+ action: OAuthActions = OAuthActions.GetAuthorizationUrl
242
+ provider: str
243
+ system_credentials: Mapping[str, Any]
244
+
245
+
246
+ class OAuthGetCredentialsRequest(BaseModel):
247
+ type: PluginInvokeType = PluginInvokeType.OAuth
248
+ action: OAuthActions = OAuthActions.GetCredentials
249
+ provider: str
250
+ system_credentials: Mapping[str, Any]
251
+ raw_http_request: str
@@ -20,6 +20,8 @@ from dify_plugin.core.entities.plugin.request import (
20
20
  ModelInvokeTTSRequest,
21
21
  ModelValidateModelCredentialsRequest,
22
22
  ModelValidateProviderCredentialsRequest,
23
+ OAuthGetAuthorizationUrlRequest,
24
+ OAuthGetCredentialsRequest,
23
25
  ToolGetRuntimeParametersRequest,
24
26
  ToolInvokeRequest,
25
27
  ToolValidateCredentialsRequest,
@@ -37,6 +39,8 @@ from dify_plugin.interfaces.model.rerank_model import RerankModel
37
39
  from dify_plugin.interfaces.model.speech2text_model import Speech2TextModel
38
40
  from dify_plugin.interfaces.model.text_embedding_model import TextEmbeddingModel
39
41
  from dify_plugin.interfaces.model.tts_model import TTSModel
42
+ from dify_plugin.interfaces.tool import ToolProvider
43
+ from dify_plugin.protocol.oauth import OAuthProviderProtocol
40
44
 
41
45
 
42
46
  class PluginExecutor:
@@ -321,3 +325,29 @@ class PluginExecutor:
321
325
  result["result"] += binascii.hexlify(chunk.encode("utf-8")).decode()
322
326
 
323
327
  yield result
328
+
329
+ def _get_oauth_provider_instance(self, provider: str) -> OAuthProviderProtocol:
330
+ provider_cls = self.registration.get_supported_oauth_provider_cls(provider)
331
+ if provider_cls is None:
332
+ raise ValueError(f"Provider `{provider}` does not support OAuth")
333
+
334
+ if provider_cls == ToolProvider:
335
+ return provider_cls()
336
+
337
+ raise ValueError(f"Provider `{provider}` does not support OAuth")
338
+
339
+ def get_oauth_authorization_url(self, session: Session, data: OAuthGetAuthorizationUrlRequest):
340
+ provider_instance = self._get_oauth_provider_instance(data.provider)
341
+
342
+ return {
343
+ "authorization_url": provider_instance.oauth_get_authorization_url(data.system_credentials),
344
+ }
345
+
346
+ def get_oauth_credentials(self, session: Session, data: OAuthGetCredentialsRequest):
347
+ provider_instance = self._get_oauth_provider_instance(data.provider)
348
+ bytes_data = binascii.unhexlify(data.raw_http_request)
349
+ request = parse_raw_request(bytes_data)
350
+
351
+ return {
352
+ "credentials": provider_instance.oauth_get_credentials(data.system_credentials, request),
353
+ }
@@ -28,6 +28,7 @@ from dify_plugin.interfaces.model.speech2text_model import Speech2TextModel
28
28
  from dify_plugin.interfaces.model.text_embedding_model import TextEmbeddingModel
29
29
  from dify_plugin.interfaces.model.tts_model import TTSModel
30
30
  from dify_plugin.interfaces.tool import Tool, ToolProvider
31
+ from dify_plugin.protocol.oauth import OAuthProviderProtocol
31
32
 
32
33
  T = TypeVar("T")
33
34
 
@@ -63,6 +64,15 @@ class PluginRegistration:
63
64
  ]
64
65
  endpoints_configuration: list[EndpointProviderConfiguration]
65
66
  endpoints: Map
67
+ datasource_configuration: list[None] # TBD
68
+ datasource_mapping: dict[ # provider -> (provider_cls, datasource_mapping)
69
+ str,
70
+ tuple[
71
+ None,
72
+ dict[str, tuple[None, type[None]]], # datasource_name -> (datasource_configuration, datasource_cls)
73
+ ],
74
+ ] # TBD
75
+
66
76
  files: list[PluginAsset]
67
77
 
68
78
  def __init__(self, config: DifyPluginEnv) -> None:
@@ -338,6 +348,18 @@ class PluginRegistration:
338
348
  if registration:
339
349
  return registration
340
350
 
351
+ def get_supported_oauth_provider_cls(self, provider: str) -> type[OAuthProviderProtocol] | None:
352
+ """
353
+ get provider which supports oauth
354
+ :param provider: provider name
355
+ :return: supported oauth providers
356
+ """
357
+ for provider_registration in self.tools_mapping:
358
+ if provider_registration == provider and self.tools_mapping[provider_registration][0].oauth_schema:
359
+ return self.tools_mapping[provider_registration][1]
360
+
361
+ return None
362
+
341
363
  def dispatch_endpoint_request(self, request: Request) -> tuple[type[Endpoint], Mapping]:
342
364
  """
343
365
  dispatch endpoint request, match the request to the registered endpoints
@@ -17,33 +17,8 @@ from dify_plugin.core.documentation.schema_doc import docs
17
17
  from dify_plugin.core.utils.yaml_loader import load_yaml_file
18
18
  from dify_plugin.entities import I18nObject
19
19
  from dify_plugin.entities.model.message import PromptMessageTool
20
- from dify_plugin.entities.provider_config import ProviderConfig
21
-
22
-
23
- class LogMetadata(str, Enum):
24
- STARTED_AT = "started_at"
25
- FINISHED_AT = "finished_at"
26
- ELAPSED_TIME = "elapsed_time"
27
- TOTAL_PRICE = "total_price"
28
- TOTAL_TOKENS = "total_tokens"
29
- PROVIDER = "provider"
30
- CURRENCY = "currency"
31
-
32
-
33
- class CommonParameterType(Enum):
34
- SECRET_INPUT = "secret-input"
35
- TEXT_INPUT = "text-input"
36
- SELECT = "select"
37
- STRING = "string"
38
- NUMBER = "number"
39
- FILE = "file"
40
- FILES = "files"
41
- BOOLEAN = "boolean"
42
- APP_SELECTOR = "app-selector"
43
- MODEL_SELECTOR = "model-selector"
44
- # TOOL_SELECTOR = "tool-selector"
45
- TOOLS_SELECTOR = "array[tools]"
46
- ANY = "any"
20
+ from dify_plugin.entities.oauth import OAuthSchema
21
+ from dify_plugin.entities.provider_config import CommonParameterType, LogMetadata, ProviderConfig
47
22
 
48
23
 
49
24
  class ToolRuntime(BaseModel):
@@ -360,6 +335,10 @@ class ToolProviderConfiguration(BaseModel):
360
335
  alias="credentials_for_provider",
361
336
  description="The credentials schema of the tool provider",
362
337
  )
338
+ oauth_schema: Optional[OAuthSchema] = Field(
339
+ default=None,
340
+ description="The OAuth schema of the tool provider if OAuth is supported",
341
+ )
363
342
  tools: list[ToolConfiguration] = Field(default=[], description="The tools of the tool provider")
364
343
  extra: ToolProviderConfigurationExtra
365
344
 
@@ -415,6 +394,7 @@ class ToolProviderType(Enum):
415
394
  API = "api"
416
395
  APP = "app"
417
396
  DATASET_RETRIEVAL = "dataset-retrieval"
397
+ MCP = "mcp"
418
398
 
419
399
  @classmethod
420
400
  def value_of(cls, value: str) -> "ToolProviderType":
@@ -1,7 +1,9 @@
1
1
  import logging
2
2
  import os
3
+ import shutil
3
4
  import signal
4
5
  import subprocess
6
+ import tempfile
5
7
  import threading
6
8
  import uuid
7
9
  from collections.abc import Generator
@@ -56,6 +58,7 @@ class PluginRunner:
56
58
  self.config = config
57
59
  self.plugin_package_path = plugin_package_path
58
60
  self.extra_args = extra_args or []
61
+ self.resources_need_to_be_cleaned = []
59
62
 
60
63
  # create pipe to communicate with the plugin
61
64
  self.stdout_pipe_read, self.stdout_pipe_write = os.pipe()
@@ -71,8 +74,26 @@ class PluginRunner:
71
74
 
72
75
  logger.info(f"Running plugin from {plugin_package_path}")
73
76
 
77
+ # check if plugin is a directory
78
+ if os.path.isdir(plugin_package_path):
79
+ logger.info("plugin source directory detected, building plugin")
80
+ with tempfile.TemporaryDirectory(delete=False) as temp_dir:
81
+ output_path = os.path.join(temp_dir, "plugin.difypkg")
82
+ self._build_plugin(plugin_package_path, output_path)
83
+ self.plugin_package_path = output_path
84
+ logger.info(f"Plugin built in {self.plugin_package_path}")
85
+ self.resources_need_to_be_cleaned.append(temp_dir)
86
+
74
87
  self.process = subprocess.Popen( # noqa: S603
75
- [config.dify_cli_path, "plugin", "run", plugin_package_path, "--response-format", "json", *self.extra_args],
88
+ [
89
+ self.config.dify_cli_path,
90
+ "plugin",
91
+ "run",
92
+ self.plugin_package_path,
93
+ "--response-format",
94
+ "json",
95
+ *self.extra_args,
96
+ ],
76
97
  stdout=self.stdout_pipe_write,
77
98
  stderr=self.stderr_pipe_write,
78
99
  stdin=self.stdin_pipe_read,
@@ -98,6 +119,13 @@ class PluginRunner:
98
119
 
99
120
  logger.info("Plugin ready")
100
121
 
122
+ def _build_plugin(self, package_path: str, output_path: str):
123
+ # build plugin
124
+ output = subprocess.check_output( # noqa: S603
125
+ [self.config.dify_cli_path, "plugin", "package", package_path, "-o", output_path],
126
+ )
127
+ logger.info(output.decode("utf-8"))
128
+
101
129
  def _close(self):
102
130
  with self.stop_flag_lock:
103
131
  if self.stop_flag:
@@ -240,3 +268,6 @@ class PluginRunner:
240
268
 
241
269
  def __exit__(self, exc_type, exc_value, traceback):
242
270
  self._close()
271
+
272
+ for resource in self.resources_need_to_be_cleaned:
273
+ shutil.rmtree(resource)
@@ -2,6 +2,8 @@ from abc import ABC, abstractmethod
2
2
  from collections.abc import Generator, Mapping
3
3
  from typing import Any, Generic, Optional, TypeVar, final
4
4
 
5
+ from werkzeug import Request
6
+
5
7
  from dify_plugin.core.runtime import Session
6
8
  from dify_plugin.entities.agent import AgentInvokeMessage
7
9
  from dify_plugin.entities.tool import LogMetadata, ToolInvokeMessage, ToolParameter, ToolRuntime, ToolSelector
@@ -222,13 +224,24 @@ class ToolLike(ABC, Generic[T]):
222
224
  return tool_parameters
223
225
 
224
226
 
225
- class ToolProvider(ABC):
227
+ class ToolProvider:
226
228
  def validate_credentials(self, credentials: dict):
227
229
  return self._validate_credentials(credentials)
228
230
 
229
- @abstractmethod
230
231
  def _validate_credentials(self, credentials: dict):
231
- pass
232
+ raise NotImplementedError("This method should be implemented by a subclass")
233
+
234
+ def oauth_get_authorization_url(self, system_credentials: Mapping[str, Any]) -> str:
235
+ return self._oauth_get_authorization_url(system_credentials)
236
+
237
+ def _oauth_get_authorization_url(self, system_credentials: Mapping[str, Any]) -> str:
238
+ raise NotImplementedError("This method should be implemented by a subclass")
239
+
240
+ def oauth_get_credentials(self, system_credentials: Mapping[str, Any], request: Request) -> Mapping[str, Any]:
241
+ return self._oauth_get_credentials(system_credentials, request)
242
+
243
+ def _oauth_get_credentials(self, system_credentials: Mapping[str, Any], request: Request) -> Mapping[str, Any]:
244
+ raise NotImplementedError("This method should be implemented by a subclass")
232
245
 
233
246
 
234
247
  class Tool(ToolLike[ToolInvokeMessage]):
@@ -0,0 +1,27 @@
1
+ from collections.abc import Mapping
2
+ from typing import Any, Protocol
3
+
4
+ from werkzeug import Request
5
+
6
+
7
+ class OAuthProviderProtocol(Protocol):
8
+ def oauth_get_authorization_url(self, system_credentials: Mapping[str, Any]) -> str:
9
+ """
10
+ Get the authorization url
11
+ :param system_credentials: system credentials
12
+ :return: authorization url
13
+ """
14
+ ...
15
+
16
+ def oauth_get_credentials(
17
+ self,
18
+ system_credentials: Mapping[str, Any],
19
+ request: Request,
20
+ ) -> Mapping[str, Any]:
21
+ """
22
+ Get the credentials
23
+ :param request: request
24
+ :param system_credentials: system credentials
25
+ :return: credentials
26
+ """
27
+ ...
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "dify_plugin"
3
- version = "0.3.0"
3
+ version = "0.3.2"
4
4
  description = "Dify Plugin SDK"
5
5
  authors = [
6
6
  { name = "langgenius", email = "hello@dify.ai" },
@@ -13,7 +13,6 @@ dependencies = [
13
13
  "httpx~=0.28.1",
14
14
  "pydantic_settings>=2.5.0,<3.0.0",
15
15
  "pydantic>=2.8.2",
16
- "pydub~=0.25.1",
17
16
  "pyyaml~=6.0.1",
18
17
  "requests~=2.32.3",
19
18
  "socksio==1.0.0",
@@ -3,9 +3,9 @@ import json
3
3
  import flask.cli
4
4
  from flask import Flask, Response, jsonify, request
5
5
 
6
- flask.cli.show_server_banner = lambda *args: None
6
+ from tests.consts.mockserver import OPENAI_MOCK_SERVER_PORT
7
7
 
8
- OPENAI_MOCK_SERVER_PORT = 11451
8
+ flask.cli.show_server_banner = lambda *args: None
9
9
 
10
10
 
11
11
  def openai_server_mock():
@@ -40,4 +40,4 @@ def openai_server_mock():
40
40
  }
41
41
  )
42
42
 
43
- app.run(port=11451)
43
+ app.run(port=OPENAI_MOCK_SERVER_PORT)
@@ -0,0 +1 @@
1
+ OPENAI_MOCK_SERVER_PORT = 11451
@@ -6,7 +6,7 @@ from dify_plugin.entities.model import ModelType
6
6
  from dify_plugin.entities.model.llm import LLMResultChunk
7
7
  from dify_plugin.entities.model.message import UserPromptMessage
8
8
  from dify_plugin.integration.run import PluginRunner
9
- from tests.__mock_server.openai import OPENAI_MOCK_SERVER_PORT
9
+ from tests.consts.mockserver import OPENAI_MOCK_SERVER_PORT
10
10
 
11
11
  _MARKETPLACE_API_URL = "https://marketplace.dify.ai"
12
12
 
File without changes
@@ -0,0 +1,20 @@
1
+ import pytest
2
+
3
+ from dify_plugin.interfaces.tool import ToolProvider
4
+
5
+
6
+ def test_construct_tool_provider():
7
+ """
8
+ Test that the ToolProvider can be constructed without implementing any methods
9
+ """
10
+ provider = ToolProvider()
11
+ assert provider is not None
12
+
13
+
14
+ def test_oauth_get_authorization_url():
15
+ """
16
+ Test that the ToolProvider can get the authorization url
17
+ """
18
+ provider = ToolProvider()
19
+ with pytest.raises(NotImplementedError):
20
+ provider.oauth_get_authorization_url({})
File without changes
File without changes