dify_plugin 0.3.4__tar.gz → 0.4.0__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 (144) hide show
  1. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/PKG-INFO +4 -4
  2. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/README.md +2 -2
  3. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/core/entities/invocation.py +1 -0
  4. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/core/entities/plugin/request.py +18 -1
  5. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/core/entities/plugin/setup.py +13 -2
  6. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/core/plugin_executor.py +31 -0
  7. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/core/runtime.py +2 -0
  8. dify_plugin-0.4.0/dify_plugin/entities/__init__.py +47 -0
  9. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/entities/model/llm.py +36 -0
  10. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/entities/provider_config.py +1 -0
  11. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/entities/tool.py +6 -14
  12. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/interfaces/agent/__init__.py +8 -4
  13. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/interfaces/model/openai_compatible/llm.py +14 -6
  14. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/interfaces/tool/__init__.py +30 -3
  15. dify_plugin-0.4.0/dify_plugin/interfaces/trigger/__init__.py +99 -0
  16. dify_plugin-0.4.0/dify_plugin/invocations/model/llm_structured_output.py +115 -0
  17. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/plugin.py +7 -0
  18. dify_plugin-0.4.0/dify_plugin/protocol/dynamic_select.py +21 -0
  19. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/pyproject.toml +2 -2
  20. dify_plugin-0.4.0/tests/interfaces/tool/test_costruct_tool.py +55 -0
  21. dify_plugin-0.3.4/dify_plugin/entities/__init__.py +0 -28
  22. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/LICENSE +0 -0
  23. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/__init__.py +0 -0
  24. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/cli.py +0 -0
  25. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/commands/__init__.py +0 -0
  26. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/commands/generate_docs.py +0 -0
  27. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/config/__init__.py +0 -0
  28. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/config/config.py +0 -0
  29. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/config/integration_config.py +0 -0
  30. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/config/logger_format.py +0 -0
  31. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/core/__init__.py +0 -0
  32. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/core/documentation/__init__.py +0 -0
  33. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/core/documentation/generator.py +0 -0
  34. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/core/documentation/schema_doc.py +0 -0
  35. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/core/entities/__init__.py +0 -0
  36. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/core/entities/message.py +0 -0
  37. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/core/entities/plugin/__init__.py +0 -0
  38. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/core/entities/plugin/io.py +0 -0
  39. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/core/entities/plugin/parameter_type.py +0 -0
  40. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/core/plugin_registration.py +0 -0
  41. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/core/server/__base/__init__.py +0 -0
  42. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/core/server/__base/filter_reader.py +0 -0
  43. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/core/server/__base/request_reader.py +0 -0
  44. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/core/server/__base/response_writer.py +0 -0
  45. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/core/server/__base/writer_entities.py +0 -0
  46. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/core/server/__init__.py +0 -0
  47. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/core/server/io_server.py +0 -0
  48. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/core/server/router.py +0 -0
  49. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/core/server/serverless/__init__.py +0 -0
  50. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/core/server/serverless/request_reader.py +0 -0
  51. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/core/server/serverless/response_writer.py +0 -0
  52. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/core/server/stdio/__init__.py +0 -0
  53. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/core/server/stdio/request_reader.py +0 -0
  54. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/core/server/stdio/response_writer.py +0 -0
  55. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/core/server/tcp/__init__.py +0 -0
  56. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/core/server/tcp/request_reader.py +0 -0
  57. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/core/utils/__init__.py +0 -0
  58. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/core/utils/class_loader.py +0 -0
  59. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/core/utils/http_parser.py +0 -0
  60. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/core/utils/position_helper.py +0 -0
  61. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/core/utils/yaml_loader.py +0 -0
  62. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/entities/agent.py +0 -0
  63. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/entities/endpoint.py +0 -0
  64. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/entities/model/__init__.py +0 -0
  65. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/entities/model/message.py +0 -0
  66. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/entities/model/moderation.py +0 -0
  67. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/entities/model/provider.py +0 -0
  68. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/entities/model/rerank.py +0 -0
  69. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/entities/model/speech2text.py +0 -0
  70. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/entities/model/text_embedding.py +0 -0
  71. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/entities/model/tts.py +0 -0
  72. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/entities/oauth.py +0 -0
  73. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/entities/workflow_node.py +0 -0
  74. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/errors/__init__.py +0 -0
  75. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/errors/model.py +0 -0
  76. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/errors/tool.py +0 -0
  77. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/file/__init__.py +0 -0
  78. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/file/constants.py +0 -0
  79. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/file/entities.py +0 -0
  80. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/file/file.py +0 -0
  81. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/integration/entities.py +0 -0
  82. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/integration/exc.py +0 -0
  83. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/integration/run.py +0 -0
  84. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/interfaces/__init__.py +0 -0
  85. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/interfaces/endpoint/__init__.py +0 -0
  86. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/interfaces/model/__init__.py +0 -0
  87. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/interfaces/model/ai_model.py +0 -0
  88. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/interfaces/model/audio.mp3 +0 -0
  89. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/interfaces/model/large_language_model.py +0 -0
  90. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/interfaces/model/moderation_model.py +0 -0
  91. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/interfaces/model/openai_compatible/__init__.py +0 -0
  92. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/interfaces/model/openai_compatible/common.py +0 -0
  93. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/interfaces/model/openai_compatible/provider.py +0 -0
  94. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/interfaces/model/openai_compatible/rerank.py +0 -0
  95. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/interfaces/model/openai_compatible/speech2text.py +0 -0
  96. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/interfaces/model/openai_compatible/text_embedding.py +0 -0
  97. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/interfaces/model/openai_compatible/tts.py +0 -0
  98. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/interfaces/model/rerank_model.py +0 -0
  99. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/interfaces/model/speech2text_model.py +0 -0
  100. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/interfaces/model/text_embedding_model.py +0 -0
  101. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/interfaces/model/tts_model.py +0 -0
  102. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/invocations/__init__.py +0 -0
  103. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/invocations/app/__init__.py +0 -0
  104. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/invocations/app/chat.py +0 -0
  105. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/invocations/app/completion.py +0 -0
  106. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/invocations/app/workflow.py +0 -0
  107. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/invocations/file.py +0 -0
  108. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/invocations/model/__init__.py +0 -0
  109. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/invocations/model/llm.py +0 -0
  110. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/invocations/model/moderation.py +0 -0
  111. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/invocations/model/rerank.py +0 -0
  112. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/invocations/model/speech2text.py +0 -0
  113. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/invocations/model/text_embedding.py +0 -0
  114. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/invocations/model/tts.py +0 -0
  115. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/invocations/storage.py +0 -0
  116. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/invocations/tool.py +0 -0
  117. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/invocations/workflow_node/__init__.py +0 -0
  118. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/invocations/workflow_node/parameter_extractor.py +0 -0
  119. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/invocations/workflow_node/question_classifier.py +0 -0
  120. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/protocol/__init__.py +0 -0
  121. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/protocol/oauth.py +0 -0
  122. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/tool/__init__.py +0 -0
  123. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/dify_plugin/tool/entities.py +0 -0
  124. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/tests/__init__.py +0 -0
  125. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/tests/__mock_server/__init__.py +0 -0
  126. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/tests/__mock_server/openai.py +0 -0
  127. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/tests/config/test_config.py +0 -0
  128. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/tests/consts/mockserver.py +0 -0
  129. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/tests/entities/endpoint/test_endpoint_group.py +0 -0
  130. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/tests/entities/models/test_llm.py +0 -0
  131. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/tests/entities/plugin/test_declaration.py +0 -0
  132. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/tests/integration/test_invoke_llm.py +0 -0
  133. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/tests/interfaces/agent/test_agent.py +0 -0
  134. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/tests/interfaces/model/__init__.py +0 -0
  135. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/tests/interfaces/model/openai_compatible/__init__.py +0 -0
  136. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/tests/interfaces/model/openai_compatible/test_increase_tool_call.py +0 -0
  137. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/tests/interfaces/tool/__init__.py +0 -0
  138. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/tests/interfaces/tool/test_construct_tool_provider.py +0 -0
  139. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/tests/invocations/test_storage.py +0 -0
  140. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/tests/servers/test_stdio.py +0 -0
  141. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/tests/test_llm_result.py +0 -0
  142. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/tests/test_prompt_message.py +0 -0
  143. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/tests/test_tool_call_model_init.py +0 -0
  144. {dify_plugin-0.3.4 → dify_plugin-0.4.0}/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.4
3
+ Version: 0.4.0
4
4
  Summary: Dify Plugin SDK
5
5
  Keywords: dify,plugin,sdk
6
6
  Author-Email: langgenius <hello@dify.ai>
@@ -10,7 +10,7 @@ Requires-Python: >=3.11
10
10
  Requires-Dist: Flask~=3.0.3
11
11
  Requires-Dist: Werkzeug~=3.0.3
12
12
  Requires-Dist: dpkt~=1.9.8
13
- Requires-Dist: gevent~=24.11.1
13
+ Requires-Dist: gevent~=25.5.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
@@ -37,12 +37,12 @@ This SDK follows Semantic Versioning (a.b.c):
37
37
  ### For SDK Users
38
38
 
39
39
  When depending on this SDK, it's recommended to specify version constraints that:
40
+
40
41
  - Allow patch and minor updates for bug fixes and new features
41
42
  - Prevent major version updates to avoid breaking changes
42
43
 
43
44
  Example in your project's dependency management:
44
45
 
45
- ```
46
+ ```python
46
47
  dify_plugin>=0.3.0,<0.4.0
47
48
  ```
48
-
@@ -13,12 +13,12 @@ This SDK follows Semantic Versioning (a.b.c):
13
13
  ### For SDK Users
14
14
 
15
15
  When depending on this SDK, it's recommended to specify version constraints that:
16
+
16
17
  - Allow patch and minor updates for bug fixes and new features
17
18
  - Prevent major version updates to avoid breaking changes
18
19
 
19
20
  Example in your project's dependency management:
20
21
 
21
- ```
22
+ ```python
22
23
  dify_plugin>=0.3.0,<0.4.0
23
24
  ```
24
-
@@ -4,6 +4,7 @@ from enum import Enum
4
4
  class InvokeType(Enum):
5
5
  Tool = "tool"
6
6
  LLM = "llm"
7
+ LLMStructuredOutput = "llm_structured_output"
7
8
  TextEmbedding = "text_embedding"
8
9
  Rerank = "rerank"
9
10
  TTS = "tts"
@@ -22,6 +22,7 @@ class PluginInvokeType(StrEnum):
22
22
  Endpoint = "endpoint"
23
23
  Agent = "agent_strategy"
24
24
  OAuth = "oauth"
25
+ DynamicParameter = "dynamic_parameter"
25
26
 
26
27
 
27
28
  class AgentActions(StrEnum):
@@ -58,8 +59,12 @@ class OAuthActions(StrEnum):
58
59
  GetCredentials = "get_credentials"
59
60
 
60
61
 
62
+ class DynamicParameterActions(StrEnum):
63
+ FetchParameterOptions = "fetch_parameter_options"
64
+
65
+
61
66
  # merge all the access actions
62
- PluginAccessAction = AgentActions | ToolActions | ModelActions | EndpointActions
67
+ PluginAccessAction = AgentActions | ToolActions | ModelActions | EndpointActions | DynamicParameterActions
63
68
 
64
69
 
65
70
  class PluginAccessRequest(BaseModel):
@@ -249,3 +254,15 @@ class OAuthGetCredentialsRequest(BaseModel):
249
254
  provider: str
250
255
  system_credentials: Mapping[str, Any]
251
256
  raw_http_request: str
257
+
258
+
259
+ class DynamicParameterFetchParameterOptionsRequest(BaseModel):
260
+ type: PluginInvokeType = PluginInvokeType.DynamicParameter
261
+ action: DynamicParameterActions = DynamicParameterActions.FetchParameterOptions
262
+ credentials: dict
263
+ provider: str
264
+ provider_action: str
265
+ user_id: str
266
+ parameter: str
267
+
268
+ model_config = ConfigDict(protected_namespaces=())
@@ -137,10 +137,21 @@ class PluginConfiguration(BaseModel):
137
137
  version: str
138
138
  entrypoint: str
139
139
 
140
- version: str
140
+ version: str = Field(
141
+ ...,
142
+ description="The version of the manifest specification, designed for backward compatibility,"
143
+ "when installing an older plugin to a newer Dify, it's hard to ensure breaking changes never happen,"
144
+ " but at least, Dify can detect it by this field, it knows which version of the manifest is supported.",
145
+ )
141
146
  arch: list[PluginArch]
142
147
  runner: PluginRunner
143
- minimum_dify_version: Optional[str] = Field(None, pattern=r"^\d{1,4}(\.\d{1,4}){1,3}(-\w{1,16})?$")
148
+ minimum_dify_version: Optional[str] = Field(
149
+ None,
150
+ description="The minimum version of Dify, designed for forward compatibility."
151
+ "When installing a newer plugin to an older Dify, many new features may not be available,"
152
+ "but showing the minimum Dify version helps users understand how to upgrade.",
153
+ pattern=r"^\d{1,4}(\.\d{1,4}){1,3}(-\w{1,16})?$",
154
+ )
144
155
 
145
156
  version: str = Field(..., pattern=r"^\d{1,4}(\.\d{1,4}){1,3}(-\w{1,16})?$")
146
157
  type: PluginType
@@ -7,6 +7,7 @@ from werkzeug import Response
7
7
  from dify_plugin.config.config import DifyPluginEnv
8
8
  from dify_plugin.core.entities.plugin.request import (
9
9
  AgentInvokeRequest,
10
+ DynamicParameterFetchParameterOptionsRequest,
10
11
  EndpointInvokeRequest,
11
12
  ModelGetAIModelSchemas,
12
13
  ModelGetLLMNumTokens,
@@ -40,6 +41,7 @@ from dify_plugin.interfaces.model.speech2text_model import Speech2TextModel
40
41
  from dify_plugin.interfaces.model.text_embedding_model import TextEmbeddingModel
41
42
  from dify_plugin.interfaces.model.tts_model import TTSModel
42
43
  from dify_plugin.interfaces.tool import ToolProvider
44
+ from dify_plugin.protocol.dynamic_select import DynamicSelectProtocol
43
45
  from dify_plugin.protocol.oauth import OAuthProviderProtocol
44
46
 
45
47
 
@@ -351,3 +353,32 @@ class PluginExecutor:
351
353
  return {
352
354
  "credentials": provider_instance.oauth_get_credentials(data.system_credentials, request),
353
355
  }
356
+
357
+ def _get_dynamic_parameter_action(
358
+ self, session: Session, data: DynamicParameterFetchParameterOptionsRequest
359
+ ) -> DynamicSelectProtocol | None:
360
+ """
361
+ get the dynamic parameter provider class by provider name
362
+
363
+ :param session: session
364
+ :param data: data
365
+ :return: dynamic parameter provider class
366
+ """
367
+ # get tool
368
+ tool_cls = self.registration.get_tool_cls(data.provider, data.provider_action)
369
+ if tool_cls is not None:
370
+ return tool_cls(
371
+ runtime=ToolRuntime(credentials=data.credentials, user_id=data.user_id, session_id=session.session_id),
372
+ session=session,
373
+ )
374
+
375
+ # TODO: trigger
376
+
377
+ def fetch_parameter_options(self, session: Session, data: DynamicParameterFetchParameterOptionsRequest):
378
+ action_instance = self._get_dynamic_parameter_action(session, data)
379
+ if action_instance is None:
380
+ raise ValueError(f"Provider `{data.provider}` not found")
381
+
382
+ return {
383
+ "options": action_instance.fetch_parameter_options(data.parameter),
384
+ }
@@ -28,6 +28,7 @@ from dify_plugin.core.server.tcp.request_reader import TCPReaderWriter
28
28
  class ModelInvocations:
29
29
  def __init__(self, session: "Session") -> None:
30
30
  from dify_plugin.invocations.model.llm import LLMInvocation, SummaryInvocation
31
+ from dify_plugin.invocations.model.llm_structured_output import LLMStructuredOutputInvocation
31
32
  from dify_plugin.invocations.model.moderation import ModerationInvocation
32
33
  from dify_plugin.invocations.model.rerank import RerankInvocation
33
34
  from dify_plugin.invocations.model.speech2text import Speech2TextInvocation
@@ -35,6 +36,7 @@ class ModelInvocations:
35
36
  from dify_plugin.invocations.model.tts import TTSInvocation
36
37
 
37
38
  self.llm = LLMInvocation(session)
39
+ self.llm_structured_output = LLMStructuredOutputInvocation(session)
38
40
  self.text_embedding = TextEmbeddingInvocation(session)
39
41
  self.rerank = RerankInvocation(session)
40
42
  self.speech2text = Speech2TextInvocation(session)
@@ -0,0 +1,47 @@
1
+ from typing import Optional
2
+
3
+ from pydantic import BaseModel, Field, field_validator
4
+
5
+ from dify_plugin.core.documentation.schema_doc import docs
6
+
7
+
8
+ @docs(
9
+ description="Common i18n object",
10
+ )
11
+ class I18nObject(BaseModel):
12
+ """
13
+ Model class for i18n object.
14
+ """
15
+
16
+ zh_Hans: Optional[str] = None
17
+ pt_BR: Optional[str] = None
18
+ en_US: str
19
+
20
+ def __init__(self, **data):
21
+ super().__init__(**data)
22
+ if not self.zh_Hans:
23
+ self.zh_Hans = self.en_US
24
+ if not self.pt_BR:
25
+ self.pt_BR = self.en_US
26
+
27
+ def to_dict(self) -> dict:
28
+ return {"zh_Hans": self.zh_Hans, "en_US": self.en_US, "pt_BR": self.pt_BR}
29
+
30
+
31
+ @docs(
32
+ description="The option of the parameter",
33
+ )
34
+ class ParameterOption(BaseModel):
35
+ value: str = Field(..., description="The value of the option")
36
+ label: I18nObject = Field(..., description="The label of the option")
37
+ icon: Optional[str] = Field(
38
+ default=None, description="The icon of the option, can be a URL or a base64 encoded string"
39
+ )
40
+
41
+ @field_validator("value", mode="before")
42
+ @classmethod
43
+ def transform_id_to_str(cls, value) -> str:
44
+ if not isinstance(value, str):
45
+ return str(value)
46
+ else:
47
+ return value
@@ -1,3 +1,4 @@
1
+ from collections.abc import Mapping
1
2
  from decimal import Decimal
2
3
  from enum import Enum
3
4
  from typing import Optional
@@ -106,6 +107,22 @@ class LLMResultChunk(BaseModel):
106
107
  return []
107
108
 
108
109
 
110
+ class LLMStructuredOutput(BaseModel):
111
+ """
112
+ Model class for llm structured output.
113
+ """
114
+
115
+ structured_output: Optional[Mapping] = None
116
+
117
+
118
+ class LLMResultChunkWithStructuredOutput(LLMResultChunk, LLMStructuredOutput):
119
+ """
120
+ Model class for llm result chunk with structured output.
121
+ """
122
+
123
+ pass
124
+
125
+
109
126
  class LLMResult(BaseModel):
110
127
  """
111
128
  Model class for llm result.
@@ -145,6 +162,25 @@ class LLMResult(BaseModel):
145
162
  )
146
163
 
147
164
 
165
+ class LLMResultWithStructuredOutput(LLMResult, LLMStructuredOutput):
166
+ """
167
+ Model class for llm result with structured output.
168
+ """
169
+
170
+ def to_llm_result_chunk_with_structured_output(self) -> "LLMResultChunkWithStructuredOutput":
171
+ return LLMResultChunkWithStructuredOutput(
172
+ model=self.model,
173
+ system_fingerprint=self.system_fingerprint,
174
+ delta=LLMResultChunkDelta(
175
+ index=0,
176
+ message=self.message,
177
+ usage=self.usage,
178
+ finish_reason=None,
179
+ ),
180
+ structured_output=self.structured_output,
181
+ )
182
+
183
+
148
184
  class SummaryResult(BaseModel):
149
185
  """
150
186
  Model class for summary result.
@@ -37,6 +37,7 @@ class CommonParameterType(Enum):
37
37
  # MCP object and array type parameters
38
38
  OBJECT = "object"
39
39
  ARRAY = "array"
40
+ DYNAMIC_SELECT = "dynamic-select"
40
41
 
41
42
 
42
43
  @docs(
@@ -15,7 +15,7 @@ from pydantic import (
15
15
 
16
16
  from dify_plugin.core.documentation.schema_doc import docs
17
17
  from dify_plugin.core.utils.yaml_loader import load_yaml_file
18
- from dify_plugin.entities import I18nObject
18
+ from dify_plugin.entities import I18nObject, ParameterOption
19
19
  from dify_plugin.entities.model.message import PromptMessageTool
20
20
  from dify_plugin.entities.oauth import OAuthSchema
21
21
  from dify_plugin.entities.provider_config import CommonParameterType, LogMetadata, ProviderConfig
@@ -172,17 +172,8 @@ class ToolIdentity(BaseModel):
172
172
  @docs(
173
173
  description="The option of the tool parameter",
174
174
  )
175
- class ToolParameterOption(BaseModel):
176
- value: str = Field(..., description="The value of the option")
177
- label: I18nObject = Field(..., description="The label of the option")
178
-
179
- @field_validator("value", mode="before")
180
- @classmethod
181
- def transform_id_to_str(cls, value) -> str:
182
- if not isinstance(value, str):
183
- return str(value)
184
- else:
185
- return value
175
+ class ToolParameterOption(ParameterOption):
176
+ pass
186
177
 
187
178
 
188
179
  @docs(
@@ -219,8 +210,9 @@ class ToolParameter(BaseModel):
219
210
  # TOOL_SELECTOR = CommonParameterType.TOOL_SELECTOR.value
220
211
  ANY = CommonParameterType.ANY.value
221
212
  # MCP object and array type parameters
222
- OBJECT = "object"
223
- ARRAY = "array"
213
+ OBJECT = CommonParameterType.OBJECT.value
214
+ ARRAY = CommonParameterType.ARRAY.value
215
+ DYNAMIC_SELECT = CommonParameterType.DYNAMIC_SELECT.value
224
216
 
225
217
  class ToolParameterForm(Enum):
226
218
  SCHEMA = "schema" # should be set while adding tool
@@ -338,10 +338,14 @@ class AgentStrategy(ToolLike[AgentInvokeMessage]):
338
338
  if parameter.type == ToolParameter.ToolParameterType.SELECT:
339
339
  enum = [option.value for option in parameter.options] if parameter.options else []
340
340
 
341
- prompt_tool.parameters["properties"][parameter.name] = {
342
- "type": parameter_type,
343
- "description": parameter.llm_description or "",
344
- }
341
+ prompt_tool.parameters["properties"][parameter.name] = (
342
+ {
343
+ "type": parameter_type,
344
+ "description": parameter.llm_description or "",
345
+ }
346
+ if parameter.input_schema is None
347
+ else parameter.input_schema
348
+ )
345
349
 
346
350
  if len(enum) > 0:
347
351
  prompt_tool.parameters["properties"][parameter.name]["enum"] = enum
@@ -203,7 +203,8 @@ class OAICompatLargeLanguageModel(_CommonOaiApiCompat, LargeLanguageModel):
203
203
  response = requests.post(endpoint_url, headers=headers, json=data, timeout=(10, 300), stream=True)
204
204
  if response.status_code != 200:
205
205
  raise CredentialsValidateFailedError(
206
- f"Credentials validation failed with status code {response.status_code}"
206
+ f"Credentials validation failed with status code {response.status_code} "
207
+ f"and response body {response.text}"
207
208
  )
208
209
  return
209
210
 
@@ -212,13 +213,16 @@ class OAICompatLargeLanguageModel(_CommonOaiApiCompat, LargeLanguageModel):
212
213
 
213
214
  if response.status_code != 200:
214
215
  raise CredentialsValidateFailedError(
215
- f"Credentials validation failed with status code {response.status_code}"
216
+ f"Credentials validation failed with status code {response.status_code} "
217
+ f"and response body {response.text}"
216
218
  )
217
219
 
218
220
  try:
219
221
  json_result = response.json()
220
222
  except json.JSONDecodeError:
221
- raise CredentialsValidateFailedError("Credentials validation failed: JSON decode error") from None
223
+ raise CredentialsValidateFailedError(
224
+ f"Credentials validation failed: JSON decode error, response body {response.text}"
225
+ ) from None
222
226
 
223
227
  if completion_type is LLMMode.CHAT and json_result.get("object", "") == "":
224
228
  json_result["object"] = "chat.completion"
@@ -229,18 +233,22 @@ class OAICompatLargeLanguageModel(_CommonOaiApiCompat, LargeLanguageModel):
229
233
  "object" not in json_result or json_result["object"] != "chat.completion"
230
234
  ):
231
235
  raise CredentialsValidateFailedError(
232
- "Credentials validation failed: invalid response object, must be 'chat.completion'"
236
+ f"Credentials validation failed: invalid response object, "
237
+ f"must be 'chat.completion', response body {response.text}"
233
238
  )
234
239
  elif completion_type is LLMMode.COMPLETION and (
235
240
  "object" not in json_result or json_result["object"] != "text_completion"
236
241
  ):
237
242
  raise CredentialsValidateFailedError(
238
- "Credentials validation failed: invalid response object, must be 'text_completion'"
243
+ f"Credentials validation failed: invalid response object, "
244
+ f"must be 'text_completion', response body {response.text}"
239
245
  )
240
246
  except CredentialsValidateFailedError:
241
247
  raise
242
248
  except Exception as ex:
243
- raise CredentialsValidateFailedError(f"An error occurred during credentials validation: {ex!s}") from ex
249
+ raise CredentialsValidateFailedError(
250
+ f"An error occurred during credentials validation: {ex!s}, response body {response.text}"
251
+ ) from ex
244
252
 
245
253
  def get_customizable_model_schema(self, model: str, credentials: dict) -> AIModelEntity:
246
254
  """
@@ -2,9 +2,11 @@ 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 typing_extensions import deprecated
5
6
  from werkzeug import Request
6
7
 
7
8
  from dify_plugin.core.runtime import Session
9
+ from dify_plugin.entities import ParameterOption
8
10
  from dify_plugin.entities.agent import AgentInvokeMessage
9
11
  from dify_plugin.entities.tool import LogMetadata, ToolInvokeMessage, ToolParameter, ToolRuntime, ToolSelector
10
12
  from dify_plugin.file.constants import DIFY_FILE_IDENTITY, DIFY_TOOL_SELECTOR_IDENTITY
@@ -167,6 +169,7 @@ class ToolLike(ABC, Generic[T]):
167
169
  ),
168
170
  )
169
171
 
172
+ @deprecated("This feature is deprecated, will soon be replaced by dynamic select parameter")
170
173
  def _get_runtime_parameters(self) -> list[ToolParameter]:
171
174
  """
172
175
  get the runtime parameters of the tool
@@ -229,19 +232,21 @@ class ToolProvider:
229
232
  return self._validate_credentials(credentials)
230
233
 
231
234
  def _validate_credentials(self, credentials: dict):
232
- raise NotImplementedError("This method should be implemented by a subclass")
235
+ raise NotImplementedError(
236
+ "This plugin should implement `_validate_credentials` method to enable credentials validation"
237
+ )
233
238
 
234
239
  def oauth_get_authorization_url(self, system_credentials: Mapping[str, Any]) -> str:
235
240
  return self._oauth_get_authorization_url(system_credentials)
236
241
 
237
242
  def _oauth_get_authorization_url(self, system_credentials: Mapping[str, Any]) -> str:
238
- raise NotImplementedError("This method should be implemented by a subclass")
243
+ raise NotImplementedError("This plugin should implement `_oauth_get_authorization_url` method to enable oauth")
239
244
 
240
245
  def oauth_get_credentials(self, system_credentials: Mapping[str, Any], request: Request) -> Mapping[str, Any]:
241
246
  return self._oauth_get_credentials(system_credentials, request)
242
247
 
243
248
  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")
249
+ raise NotImplementedError("This plugin should implement `_oauth_get_credentials` method to enable oauth")
245
250
 
246
251
 
247
252
  class Tool(ToolLike[ToolInvokeMessage]):
@@ -283,6 +288,18 @@ class Tool(ToolLike[ToolInvokeMessage]):
283
288
  def _invoke(self, tool_parameters: dict) -> Generator[ToolInvokeMessage, None, None]:
284
289
  pass
285
290
 
291
+ def _fetch_parameter_options(self, parameter: str) -> list[ParameterOption]:
292
+ """
293
+ Fetch the parameter options of the tool.
294
+
295
+ To be implemented by subclasses.
296
+
297
+ Also, it's optional to implement, that's why it's not an abstract method.
298
+ """
299
+ raise NotImplementedError(
300
+ "This plugin should implement `_fetch_parameter_options` method to enable dynamic select parameter"
301
+ )
302
+
286
303
  ############################################################
287
304
  # For executor use only #
288
305
  ############################################################
@@ -292,5 +309,15 @@ class Tool(ToolLike[ToolInvokeMessage]):
292
309
  tool_parameters = self._convert_parameters(tool_parameters)
293
310
  return self._invoke(tool_parameters)
294
311
 
312
+ @deprecated("This feature is deprecated, will soon be replaced by dynamic select parameter")
295
313
  def get_runtime_parameters(self) -> list[ToolParameter]:
296
314
  return self._get_runtime_parameters()
315
+
316
+ def fetch_parameter_options(self, parameter: str) -> list[ParameterOption]:
317
+ """
318
+ Fetch the parameter options of the tool.
319
+
320
+ To be implemented by subclasses.
321
+
322
+ """
323
+ return self._fetch_parameter_options(parameter)
@@ -0,0 +1,99 @@
1
+ from abc import ABC, abstractmethod
2
+ from collections.abc import Mapping
3
+ from typing import Any, final
4
+
5
+ from werkzeug import Request
6
+
7
+ from dify_plugin.core.runtime import Session
8
+ from dify_plugin.entities import ParameterOption
9
+ from dify_plugin.entities.trigger import TriggerResponse, TriggerRuntime
10
+
11
+
12
+ class TriggerProvider:
13
+ """
14
+ The provider of a trigger
15
+ """
16
+
17
+ def validate_credentials(self, credentials: dict):
18
+ return self._validate_credentials(credentials)
19
+
20
+ def _validate_credentials(self, credentials: dict):
21
+ raise NotImplementedError(
22
+ "This plugin should implement `_validate_credentials` method to enable credentials validation"
23
+ )
24
+
25
+ def oauth_get_authorization_url(self, system_credentials: Mapping[str, Any]) -> str:
26
+ return self._oauth_get_authorization_url(system_credentials)
27
+
28
+ def _oauth_get_authorization_url(self, system_credentials: Mapping[str, Any]) -> str:
29
+ raise NotImplementedError("This plugin should implement `_oauth_get_authorization_url` method to enable oauth")
30
+
31
+ def oauth_get_credentials(self, system_credentials: Mapping[str, Any], request: Request) -> Mapping[str, Any]:
32
+ return self._oauth_get_credentials(system_credentials, request)
33
+
34
+ def _oauth_get_credentials(self, system_credentials: Mapping[str, Any], request: Request) -> Mapping[str, Any]:
35
+ raise NotImplementedError("This plugin should implement `_oauth_get_credentials` method to enable oauth")
36
+
37
+
38
+ class Trigger(ABC):
39
+ """
40
+ The trigger interface
41
+ """
42
+
43
+ runtime: TriggerRuntime
44
+ session: Session
45
+
46
+ @final
47
+ def __init__(
48
+ self,
49
+ runtime: TriggerRuntime,
50
+ session: Session,
51
+ ):
52
+ """
53
+ Initialize the trigger
54
+
55
+ NOTE:
56
+ - This method has been marked as final, DO NOT OVERRIDE IT.
57
+ """
58
+ self.runtime = runtime
59
+ self.session = session
60
+
61
+ ############################################################
62
+ # Methods that can be implemented by plugin #
63
+ ############################################################
64
+
65
+ @abstractmethod
66
+ def _trigger(self, request: Request, values: Mapping, parameters: Mapping) -> TriggerResponse:
67
+ """
68
+ Trigger the trigger with the given request.
69
+
70
+ To be implemented by subclasses.
71
+ """
72
+
73
+ def _fetch_parameter_options(self, parameter: str) -> list[ParameterOption]:
74
+ """
75
+ Fetch the parameter options of the trigger.
76
+
77
+ To be implemented by subclasses.
78
+
79
+ Also, it's optional to implement, that's why it's not an abstract method.
80
+ """
81
+ raise NotImplementedError(
82
+ "This plugin should implement `_fetch_parameter_options` method to enable dynamic select parameter"
83
+ )
84
+
85
+ ############################################################
86
+ # For executor use only #
87
+ ############################################################
88
+
89
+ def trigger(self, request: Request, values: Mapping, parameters: Mapping) -> TriggerResponse:
90
+ """
91
+ Trigger the trigger with the given request.
92
+ """
93
+ return self._trigger(request, values, parameters)
94
+
95
+ def fetch_parameter_options(self, parameter: str) -> list[ParameterOption]:
96
+ """
97
+ Fetch the parameter options of the trigger.
98
+ """
99
+ return self._fetch_parameter_options(parameter)