dify_plugin 0.6.2__tar.gz → 0.7.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 (177) hide show
  1. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/PKG-INFO +2 -2
  2. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/entities/invocation.py +2 -0
  3. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/entities/plugin/request.py +23 -2
  4. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/plugin_executor.py +28 -0
  5. dify_plugin-0.7.0/dify_plugin/entities/model/rerank.py +57 -0
  6. dify_plugin-0.7.0/dify_plugin/entities/model/text_embedding.py +71 -0
  7. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/entities/tool.py +0 -9
  8. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/interfaces/model/openai_compatible/llm.py +3 -1
  9. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/interfaces/model/rerank_model.py +48 -1
  10. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/interfaces/model/text_embedding_model.py +44 -1
  11. dify_plugin-0.7.0/dify_plugin/invocations/model/rerank.py +49 -0
  12. dify_plugin-0.7.0/dify_plugin/invocations/model/text_embedding.py +57 -0
  13. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/plugin.py +12 -0
  14. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/pyproject.toml +2 -2
  15. dify_plugin-0.7.0/tests/interfaces/model/openai_compatible/test_plugin_config.py +70 -0
  16. dify_plugin-0.7.0/tests/interfaces/model/test_rerank.py +104 -0
  17. dify_plugin-0.7.0/tests/interfaces/model/test_text_embedding.py +117 -0
  18. dify_plugin-0.6.2/dify_plugin/entities/model/rerank.py +0 -34
  19. dify_plugin-0.6.2/dify_plugin/entities/model/text_embedding.py +0 -39
  20. dify_plugin-0.6.2/dify_plugin/invocations/model/rerank.py +0 -22
  21. dify_plugin-0.6.2/dify_plugin/invocations/model/text_embedding.py +0 -28
  22. dify_plugin-0.6.2/tests/interfaces/model/test_text_embedding.py +0 -50
  23. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/LICENSE +0 -0
  24. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/README.md +0 -0
  25. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/__init__.py +0 -0
  26. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/cli.py +0 -0
  27. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/commands/__init__.py +0 -0
  28. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/commands/generate_docs.py +0 -0
  29. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/config/__init__.py +0 -0
  30. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/config/config.py +0 -0
  31. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/config/integration_config.py +0 -0
  32. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/config/logger_format.py +0 -0
  33. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/__init__.py +0 -0
  34. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/documentation/__init__.py +0 -0
  35. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/documentation/generator.py +0 -0
  36. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/documentation/schema_doc.py +0 -0
  37. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/entities/__init__.py +0 -0
  38. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/entities/message.py +0 -0
  39. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/entities/plugin/__init__.py +0 -0
  40. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/entities/plugin/io.py +0 -0
  41. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/entities/plugin/parameter_type.py +0 -0
  42. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/entities/plugin/setup.py +0 -0
  43. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/entities/providers.py +0 -0
  44. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/model_factory.py +0 -0
  45. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/plugin_registration.py +0 -0
  46. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/runtime.py +0 -0
  47. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/server/__base/__init__.py +0 -0
  48. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/server/__base/filter_reader.py +0 -0
  49. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/server/__base/request_reader.py +0 -0
  50. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/server/__base/response_writer.py +0 -0
  51. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/server/__base/writer_entities.py +0 -0
  52. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/server/__init__.py +0 -0
  53. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/server/io_server.py +0 -0
  54. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/server/router.py +0 -0
  55. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/server/serverless/__init__.py +0 -0
  56. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/server/serverless/request_reader.py +0 -0
  57. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/server/serverless/response_writer.py +0 -0
  58. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/server/stdio/__init__.py +0 -0
  59. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/server/stdio/request_reader.py +0 -0
  60. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/server/stdio/response_writer.py +0 -0
  61. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/server/tcp/__init__.py +0 -0
  62. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/server/tcp/request_reader.py +0 -0
  63. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/trigger_factory.py +0 -0
  64. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/utils/__init__.py +0 -0
  65. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/utils/class_loader.py +0 -0
  66. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/utils/http_parser.py +0 -0
  67. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/utils/position_helper.py +0 -0
  68. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/core/utils/yaml_loader.py +0 -0
  69. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/entities/__init__.py +0 -0
  70. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/entities/agent.py +0 -0
  71. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/entities/datasource.py +0 -0
  72. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/entities/datasource_manifest.py +0 -0
  73. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/entities/endpoint.py +0 -0
  74. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/entities/invoke_message.py +0 -0
  75. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/entities/model/__init__.py +0 -0
  76. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/entities/model/llm.py +0 -0
  77. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/entities/model/message.py +0 -0
  78. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/entities/model/moderation.py +0 -0
  79. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/entities/model/provider.py +0 -0
  80. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/entities/model/speech2text.py +0 -0
  81. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/entities/model/tts.py +0 -0
  82. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/entities/oauth.py +0 -0
  83. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/entities/provider_config.py +0 -0
  84. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/entities/trigger.py +0 -0
  85. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/entities/workflow_node.py +0 -0
  86. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/errors/__init__.py +0 -0
  87. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/errors/model.py +0 -0
  88. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/errors/tool.py +0 -0
  89. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/errors/trigger.py +0 -0
  90. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/file/__init__.py +0 -0
  91. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/file/constants.py +0 -0
  92. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/file/entities.py +0 -0
  93. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/file/file.py +0 -0
  94. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/integration/entities.py +0 -0
  95. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/integration/exc.py +0 -0
  96. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/integration/run.py +0 -0
  97. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/interfaces/__init__.py +0 -0
  98. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/interfaces/agent/__init__.py +0 -0
  99. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/interfaces/datasource/__init__.py +0 -0
  100. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/interfaces/datasource/online_document.py +0 -0
  101. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/interfaces/datasource/online_drive.py +0 -0
  102. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/interfaces/datasource/website.py +0 -0
  103. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/interfaces/endpoint/__init__.py +0 -0
  104. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/interfaces/exec/ai_model.py +0 -0
  105. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/interfaces/model/__init__.py +0 -0
  106. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/interfaces/model/ai_model.py +0 -0
  107. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/interfaces/model/audio.mp3 +0 -0
  108. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/interfaces/model/large_language_model.py +0 -0
  109. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/interfaces/model/moderation_model.py +0 -0
  110. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/interfaces/model/openai_compatible/__init__.py +0 -0
  111. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/interfaces/model/openai_compatible/common.py +0 -0
  112. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/interfaces/model/openai_compatible/provider.py +0 -0
  113. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/interfaces/model/openai_compatible/rerank.py +0 -0
  114. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/interfaces/model/openai_compatible/speech2text.py +0 -0
  115. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/interfaces/model/openai_compatible/text_embedding.py +0 -0
  116. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/interfaces/model/openai_compatible/tts.py +0 -0
  117. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/interfaces/model/speech2text_model.py +0 -0
  118. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/interfaces/model/tts_model.py +0 -0
  119. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/interfaces/tool/__init__.py +0 -0
  120. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/interfaces/trigger/__init__.py +0 -0
  121. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/invocations/__init__.py +0 -0
  122. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/invocations/app/__init__.py +0 -0
  123. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/invocations/app/chat.py +0 -0
  124. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/invocations/app/completion.py +0 -0
  125. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/invocations/app/workflow.py +0 -0
  126. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/invocations/file.py +0 -0
  127. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/invocations/model/__init__.py +0 -0
  128. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/invocations/model/llm.py +0 -0
  129. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/invocations/model/llm_structured_output.py +0 -0
  130. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/invocations/model/moderation.py +0 -0
  131. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/invocations/model/speech2text.py +0 -0
  132. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/invocations/model/tts.py +0 -0
  133. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/invocations/storage.py +0 -0
  134. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/invocations/tool.py +0 -0
  135. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/invocations/workflow_node/__init__.py +0 -0
  136. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/invocations/workflow_node/parameter_extractor.py +0 -0
  137. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/invocations/workflow_node/question_classifier.py +0 -0
  138. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/protocol/__init__.py +0 -0
  139. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/protocol/dynamic_select.py +0 -0
  140. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/protocol/oauth.py +0 -0
  141. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/tool/__init__.py +0 -0
  142. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/dify_plugin/tool/entities.py +0 -0
  143. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/tests/__init__.py +0 -0
  144. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/tests/__mock_server/__init__.py +0 -0
  145. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/tests/__mock_server/openai.py +0 -0
  146. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/tests/config/test_config.py +0 -0
  147. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/tests/consts/mockserver.py +0 -0
  148. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/tests/entities/endpoint/test_endpoint_group.py +0 -0
  149. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/tests/entities/models/test_llm.py +0 -0
  150. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/tests/entities/plugin/test_declaration.py +0 -0
  151. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/tests/integration/test_invoke_llm.py +0 -0
  152. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/tests/interfaces/agent/test_agent.py +0 -0
  153. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/tests/interfaces/datasource/test_construct_datasource.py +0 -0
  154. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/tests/interfaces/datasource/test_construct_datasource_provider.py +0 -0
  155. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/tests/interfaces/model/__init__.py +0 -0
  156. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/tests/interfaces/model/openai_compatible/__init__.py +0 -0
  157. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/tests/interfaces/model/openai_compatible/test_increase_tool_call.py +0 -0
  158. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/tests/interfaces/model/test_ai_model_timing_context.py +0 -0
  159. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/tests/interfaces/model/test_construct_model_provider.py +0 -0
  160. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/tests/interfaces/model/test_llm_timing_context.py +0 -0
  161. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/tests/interfaces/model/test_model_registry_get_model.py +0 -0
  162. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/tests/interfaces/model/utils.py +0 -0
  163. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/tests/interfaces/tool/__init__.py +0 -0
  164. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/tests/interfaces/tool/test_construct_tool_provider.py +0 -0
  165. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/tests/interfaces/tool/test_costruct_tool.py +0 -0
  166. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/tests/interfaces/trigger/__init__.py +0 -0
  167. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/tests/interfaces/trigger/test_construct_trigger.py +0 -0
  168. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/tests/interfaces/trigger/test_construct_trigger_provider.py +0 -0
  169. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/tests/invocations/test_storage.py +0 -0
  170. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/tests/servers/test_session.py +0 -0
  171. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/tests/servers/test_stdio.py +0 -0
  172. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/tests/test_llm_result.py +0 -0
  173. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/tests/test_model_registry_get_model.py +0 -0
  174. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/tests/test_prompt_message.py +0 -0
  175. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/tests/test_tool_call_model_init.py +0 -0
  176. {dify_plugin-0.6.2 → dify_plugin-0.7.0}/tests/test_trigger_factory.py +0 -0
  177. {dify_plugin-0.6.2 → dify_plugin-0.7.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.6.2
3
+ Version: 0.7.0
4
4
  Summary: Dify Plugin SDK
5
5
  Keywords: dify,plugin,sdk
6
6
  Author-Email: langgenius <hello@dify.ai>
@@ -8,7 +8,7 @@ License: Apache2.0
8
8
  Project-URL: Homepage, https://github.com/langgenius/dify-plugin-sdks.git
9
9
  Requires-Python: >=3.11
10
10
  Requires-Dist: Flask~=3.0.3
11
- Requires-Dist: Werkzeug~=3.0.3
11
+ Requires-Dist: Werkzeug~=3.1.4
12
12
  Requires-Dist: dpkt~=1.9.8
13
13
  Requires-Dist: gevent~=25.5.1
14
14
  Requires-Dist: httpx~=0.28.1
@@ -6,7 +6,9 @@ class InvokeType(Enum):
6
6
  LLM = "llm"
7
7
  LLMStructuredOutput = "llm_structured_output"
8
8
  TextEmbedding = "text_embedding"
9
+ MultimodalEmbedding = "multimodal_embedding"
9
10
  Rerank = "rerank"
11
+ MultimodalRerank = "multimodal_rerank"
10
12
  TTS = "tts"
11
13
  Speech2Text = "speech2text"
12
14
  Moderation = "moderation"
@@ -1,4 +1,4 @@
1
- from collections.abc import Mapping
1
+ from collections.abc import Mapping, Sequence
2
2
  from enum import StrEnum
3
3
  from typing import Any
4
4
 
@@ -9,7 +9,7 @@ from dify_plugin.entities.datasource import (
9
9
  OnlineDriveBrowseFilesRequest,
10
10
  OnlineDriveDownloadFileRequest,
11
11
  )
12
- from dify_plugin.entities.model import ModelType
12
+ from dify_plugin.entities.model import EmbeddingInputType, ModelType
13
13
  from dify_plugin.entities.model.message import (
14
14
  AssistantPromptMessage,
15
15
  PromptMessage,
@@ -19,6 +19,7 @@ from dify_plugin.entities.model.message import (
19
19
  ToolPromptMessage,
20
20
  UserPromptMessage,
21
21
  )
22
+ from dify_plugin.entities.model.text_embedding import MultiModalContent
22
23
  from dify_plugin.entities.provider_config import CredentialType
23
24
  from dify_plugin.entities.trigger import Subscription
24
25
 
@@ -59,8 +60,10 @@ class ModelActions(StrEnum):
59
60
  InvokeLLM = "invoke_llm"
60
61
  GetLLMNumTokens = "get_llm_num_tokens"
61
62
  InvokeTextEmbedding = "invoke_text_embedding"
63
+ InvokeMultimodalEmbedding = "invoke_multimodal_embedding"
62
64
  GetTextEmbeddingNumTokens = "get_text_embedding_num_tokens"
63
65
  InvokeRerank = "invoke_rerank"
66
+ InvokeMultimodalRerank = "invoke_multimodal_rerank"
64
67
  InvokeTTS = "invoke_tts"
65
68
  GetTTSVoices = "get_tts_model_voices"
66
69
  InvokeSpeech2Text = "invoke_speech2text"
@@ -202,6 +205,14 @@ class ModelInvokeTextEmbeddingRequest(PluginAccessModelRequest):
202
205
  texts: list[str]
203
206
 
204
207
 
208
+ class ModelInvokeMultimodalEmbeddingRequest(PluginAccessModelRequest):
209
+ action: ModelActions = ModelActions.InvokeMultimodalEmbedding
210
+ model_type: ModelType = ModelType.TEXT_EMBEDDING
211
+
212
+ documents: list[MultiModalContent]
213
+ input_type: EmbeddingInputType = EmbeddingInputType.DOCUMENT
214
+
215
+
205
216
  class ModelGetTextEmbeddingNumTokens(PluginAccessModelRequest):
206
217
  action: ModelActions = ModelActions.GetTextEmbeddingNumTokens
207
218
 
@@ -217,6 +228,16 @@ class ModelInvokeRerankRequest(PluginAccessModelRequest):
217
228
  top_n: int | None
218
229
 
219
230
 
231
+ class ModelInvokeMultimodalRerankRequest(PluginAccessModelRequest):
232
+ action: ModelActions = ModelActions.InvokeMultimodalRerank
233
+ model_type: ModelType = ModelType.RERANK
234
+
235
+ query: MultiModalContent
236
+ docs: Sequence[MultiModalContent]
237
+ score_threshold: float | None
238
+ top_n: int | None
239
+
240
+
220
241
  class ModelInvokeTTSRequest(PluginAccessModelRequest):
221
242
  action: ModelActions = ModelActions.InvokeTTS
222
243
 
@@ -22,6 +22,8 @@ from dify_plugin.core.entities.plugin.request import (
22
22
  ModelGetTTSVoices,
23
23
  ModelInvokeLLMRequest,
24
24
  ModelInvokeModerationRequest,
25
+ ModelInvokeMultimodalEmbeddingRequest,
26
+ ModelInvokeMultimodalRerankRequest,
25
27
  ModelInvokeRerankRequest,
26
28
  ModelInvokeSpeech2TextRequest,
27
29
  ModelInvokeTextEmbeddingRequest,
@@ -219,6 +221,18 @@ class PluginExecutor:
219
221
  else:
220
222
  raise ValueError(f"Model `{data.model_type}` not found for provider `{data.provider}`")
221
223
 
224
+ def invoke_multimodal_embedding(self, session: Session, data: ModelInvokeMultimodalEmbeddingRequest):
225
+ model_instance = self.registration.get_model_instance(data.provider, data.model_type)
226
+ if isinstance(model_instance, TextEmbeddingModel):
227
+ return model_instance.invoke_multimodal(
228
+ data.model,
229
+ data.credentials,
230
+ data.documents,
231
+ user=data.user_id,
232
+ input_type=data.input_type,
233
+ )
234
+ raise ValueError(f"Model `{data.model_type}` not found for provider `{data.provider}`")
235
+
222
236
  def get_text_embedding_num_tokens(self, session: Session, data: ModelGetTextEmbeddingNumTokens):
223
237
  model_instance = self.registration.get_model_instance(data.provider, data.model_type)
224
238
  if isinstance(model_instance, TextEmbeddingModel):
@@ -247,6 +261,20 @@ class PluginExecutor:
247
261
  else:
248
262
  raise ValueError(f"Model `{data.model_type}` not found for provider `{data.provider}`")
249
263
 
264
+ def invoke_multimodal_rerank(self, session: Session, data: ModelInvokeMultimodalRerankRequest):
265
+ model_instance = self.registration.get_model_instance(data.provider, data.model_type)
266
+ if isinstance(model_instance, RerankModel):
267
+ return model_instance.invoke_multimodal(
268
+ data.model,
269
+ data.credentials,
270
+ data.query,
271
+ data.docs,
272
+ score_threshold=data.score_threshold,
273
+ top_n=data.top_n,
274
+ user=data.user_id,
275
+ )
276
+ raise ValueError(f"Model `{data.model_type}` not found for provider `{data.provider}`")
277
+
250
278
  def invoke_tts(self, session: Session, data: ModelInvokeTTSRequest):
251
279
  model_instance = self.registration.get_model_instance(data.provider, data.model_type)
252
280
  if isinstance(model_instance, TTSModel):
@@ -0,0 +1,57 @@
1
+ from pydantic import BaseModel, ConfigDict, Field
2
+
3
+ from dify_plugin.entities.model import BaseModelConfig, ModelType
4
+
5
+
6
+ class RerankDocument(BaseModel):
7
+ """
8
+ Model class for rerank document.
9
+ """
10
+
11
+ index: int
12
+ text: str
13
+ score: float
14
+
15
+
16
+ class RerankResult(BaseModel):
17
+ """
18
+ Model class for rerank result.
19
+ """
20
+
21
+ model: str
22
+ docs: list[RerankDocument]
23
+
24
+
25
+ class RerankModelConfig(BaseModelConfig):
26
+ """
27
+ Model class for rerank model config.
28
+ """
29
+
30
+ model_type: ModelType = ModelType.RERANK
31
+ score_threshold: float
32
+ top_n: int
33
+
34
+ model_config = ConfigDict(protected_namespaces=())
35
+
36
+
37
+ class MultiModalRerankResult(BaseModel):
38
+ """Rerank response produced by a multimodal rerank model."""
39
+
40
+ model: str = Field(..., description="Identifier of the model producing the reranked documents.")
41
+ docs: list[RerankDocument] = Field(..., description="Reranked documents with scores.")
42
+
43
+
44
+ class MultiModalRerankModelConfig(BaseModelConfig):
45
+ """Configuration payload for invoking a multimodal rerank model."""
46
+
47
+ model_type: ModelType = ModelType.RERANK
48
+ score_threshold: float | None = Field(
49
+ default=None,
50
+ description="Optional threshold for filtering documents based on score.",
51
+ )
52
+ top_n: int | None = Field(
53
+ default=None,
54
+ description="Optional limit on the number of documents returned.",
55
+ )
56
+
57
+ model_config = ConfigDict(protected_namespaces=())
@@ -0,0 +1,71 @@
1
+ from decimal import Decimal
2
+ from enum import StrEnum
3
+
4
+ from pydantic import BaseModel, ConfigDict, Field
5
+
6
+ from dify_plugin.entities.model import BaseModelConfig, ModelType, ModelUsage
7
+
8
+
9
+ class EmbeddingUsage(ModelUsage):
10
+ """
11
+ Model class for embedding usage.
12
+ """
13
+
14
+ tokens: int
15
+ total_tokens: int
16
+ unit_price: Decimal
17
+ price_unit: Decimal
18
+ total_price: Decimal
19
+ currency: str
20
+ latency: float
21
+
22
+
23
+ class TextEmbeddingResult(BaseModel):
24
+ """
25
+ Model class for text embedding result.
26
+ """
27
+
28
+ model: str
29
+ embeddings: list[list[float]]
30
+ usage: EmbeddingUsage
31
+
32
+
33
+ class TextEmbeddingModelConfig(BaseModelConfig):
34
+ """
35
+ Model class for text embedding model config.
36
+ """
37
+
38
+ model_type: ModelType = ModelType.TEXT_EMBEDDING
39
+
40
+ model_config = ConfigDict(protected_namespaces=())
41
+
42
+
43
+ class MultiModalContentType(StrEnum):
44
+ """Supported content types for multimodal inputs."""
45
+
46
+ TEXT = "text"
47
+ IMAGE = "image"
48
+
49
+
50
+ class MultiModalContent(BaseModel):
51
+ """A multimodal content payload provided by the caller."""
52
+
53
+ content: str = Field(..., description="The payload content, plain text or base64 encoded file data.")
54
+ content_type: MultiModalContentType = Field(..., description="The modality of the provided content.")
55
+
56
+
57
+ class MultiModalEmbeddingResult(BaseModel):
58
+ """Embedding response produced by a multimodal embedding model."""
59
+
60
+ model: str = Field(..., description="Identifier of the model generating embeddings.")
61
+ embeddings: list[list[float]] = Field(..., description="Embedding vectors for provided contents.")
62
+ usage: EmbeddingUsage = Field(..., description="Usage metrics associated with the inference.")
63
+
64
+
65
+ class MultiModalEmbeddingModelConfig(BaseModelConfig):
66
+ """Configuration payload for invoking a multimodal embedding model."""
67
+
68
+ model_type: ModelType = ModelType.TEXT_EMBEDDING
69
+ tenant_id: str = Field(..., description="Vendor tenant identifier associated with the dataset.")
70
+
71
+ model_config = ConfigDict(protected_namespaces=())
@@ -20,15 +20,6 @@ from dify_plugin.entities.provider_config import (
20
20
  CredentialType,
21
21
  ProviderConfig,
22
22
  )
23
- from dify_plugin.entities.provider_config import (
24
- LogMetadata as CommonLogMetadata,
25
- )
26
-
27
- # TODO: this is a temporary solution to avoid breaking changes from agent imports
28
- # ImportError: cannot import name "LogMetadata" from "dify_plugin.entities.tool"
29
- # which will be removed in 0.5.0
30
- # ISSUE: https://github.com/langgenius/dify-plugin-sdks/issues/181
31
- LogMetadata = CommonLogMetadata
32
23
 
33
24
 
34
25
  class ToolRuntime(BaseModel):
@@ -47,6 +47,8 @@ from dify_plugin.interfaces.model.openai_compatible.common import _CommonOaiApiC
47
47
 
48
48
  logger = logging.getLogger(__name__)
49
49
 
50
+ _plugin_config = DifyPluginEnv()
51
+
50
52
 
51
53
  def _gen_tool_call_id() -> str:
52
54
  return f"chatcmpl-tool-{uuid.uuid4().hex!s}"
@@ -473,7 +475,7 @@ class OAICompatLargeLanguageModel(_CommonOaiApiCompat, LargeLanguageModel):
473
475
  endpoint_url,
474
476
  headers=headers,
475
477
  json=data,
476
- timeout=(10, DifyPluginEnv.MAX_REQUEST_TIMEOUT),
478
+ timeout=(10, _plugin_config.MAX_REQUEST_TIMEOUT),
477
479
  stream=stream,
478
480
  )
479
481
 
@@ -1,7 +1,9 @@
1
1
  from abc import abstractmethod
2
+ from collections.abc import Sequence
2
3
 
3
4
  from dify_plugin.entities.model import ModelType
4
- from dify_plugin.entities.model.rerank import RerankResult
5
+ from dify_plugin.entities.model.rerank import MultiModalRerankResult, RerankResult
6
+ from dify_plugin.entities.model.text_embedding import MultiModalContent
5
7
  from dify_plugin.interfaces.model.ai_model import AIModel
6
8
 
7
9
 
@@ -41,6 +43,23 @@ class RerankModel(AIModel):
41
43
  """
42
44
  raise NotImplementedError
43
45
 
46
+ def _invoke_multimodal(
47
+ self,
48
+ model: str,
49
+ credentials: dict,
50
+ query: MultiModalContent,
51
+ docs: Sequence[MultiModalContent],
52
+ score_threshold: float | None = None,
53
+ top_n: int | None = None,
54
+ user: str | None = None,
55
+ ) -> MultiModalRerankResult:
56
+ """Invoke a multimodal rerank model."""
57
+
58
+ raise NotImplementedError(
59
+ f"{self.__class__.__name__} does not implement `_invoke_multimodal`. "
60
+ "Implement this method to support multimodal rerank invocations."
61
+ )
62
+
44
63
  ############################################################
45
64
  # For executor use only #
46
65
  ############################################################
@@ -73,3 +92,31 @@ class RerankModel(AIModel):
73
92
  return self._invoke(model, credentials, query, docs, score_threshold, top_n, user)
74
93
  except Exception as e:
75
94
  raise self._transform_invoke_error(e) from e
95
+
96
+ def invoke_multimodal(
97
+ self,
98
+ model: str,
99
+ credentials: dict,
100
+ query: MultiModalContent,
101
+ docs: Sequence[MultiModalContent],
102
+ score_threshold: float | None = None,
103
+ top_n: int | None = None,
104
+ user: str | None = None,
105
+ ) -> MultiModalRerankResult:
106
+ """Invoke a multimodal rerank model."""
107
+
108
+ with self.timing_context():
109
+ try:
110
+ return self._invoke_multimodal(
111
+ model,
112
+ credentials,
113
+ query,
114
+ docs,
115
+ score_threshold,
116
+ top_n,
117
+ user,
118
+ )
119
+ except NotImplementedError:
120
+ raise
121
+ except Exception as e:
122
+ raise self._transform_invoke_error(e) from e
@@ -3,7 +3,11 @@ from abc import abstractmethod
3
3
  from pydantic import ConfigDict
4
4
 
5
5
  from dify_plugin.entities.model import EmbeddingInputType, ModelPropertyKey, ModelType
6
- from dify_plugin.entities.model.text_embedding import TextEmbeddingResult
6
+ from dify_plugin.entities.model.text_embedding import (
7
+ MultiModalContent,
8
+ MultiModalEmbeddingResult,
9
+ TextEmbeddingResult,
10
+ )
7
11
  from dify_plugin.interfaces.model.ai_model import AIModel
8
12
 
9
13
 
@@ -42,6 +46,21 @@ class TextEmbeddingModel(AIModel):
42
46
  """
43
47
  raise NotImplementedError
44
48
 
49
+ def _invoke_multimodal(
50
+ self,
51
+ model: str,
52
+ credentials: dict,
53
+ documents: list[MultiModalContent],
54
+ user: str | None = None,
55
+ input_type: EmbeddingInputType = EmbeddingInputType.DOCUMENT,
56
+ ) -> MultiModalEmbeddingResult:
57
+ """Invoke a multimodal embedding model."""
58
+
59
+ raise NotImplementedError(
60
+ f"{self.__class__.__name__} does not implement `_invoke_multimodal`. "
61
+ "Implement this method to support multimodal embeddings."
62
+ )
63
+
45
64
  @abstractmethod
46
65
  def get_num_tokens(self, model: str, credentials: dict, texts: list[str]) -> list[int]:
47
66
  """
@@ -115,3 +134,27 @@ class TextEmbeddingModel(AIModel):
115
134
  return self._invoke(model, credentials, texts, user, input_type)
116
135
  except Exception as e:
117
136
  raise self._transform_invoke_error(e) from e
137
+
138
+ def invoke_multimodal(
139
+ self,
140
+ model: str,
141
+ credentials: dict,
142
+ documents: list[MultiModalContent],
143
+ user: str | None = None,
144
+ input_type: EmbeddingInputType = EmbeddingInputType.DOCUMENT,
145
+ ) -> MultiModalEmbeddingResult:
146
+ """Invoke a multimodal embedding model."""
147
+
148
+ with self.timing_context():
149
+ try:
150
+ return self._invoke_multimodal(
151
+ model,
152
+ credentials,
153
+ documents,
154
+ user,
155
+ input_type,
156
+ )
157
+ except NotImplementedError:
158
+ raise
159
+ except Exception as e:
160
+ raise self._transform_invoke_error(e) from e
@@ -0,0 +1,49 @@
1
+ from dify_plugin.core.entities.invocation import InvokeType
2
+ from dify_plugin.core.runtime import BackwardsInvocation
3
+ from dify_plugin.entities.model.rerank import (
4
+ MultiModalRerankModelConfig,
5
+ MultiModalRerankResult,
6
+ RerankModelConfig,
7
+ RerankResult,
8
+ )
9
+ from dify_plugin.entities.model.text_embedding import MultiModalContent
10
+
11
+
12
+ class RerankInvocation(BackwardsInvocation[RerankResult]):
13
+ def invoke(self, model_config: RerankModelConfig, docs: list[str], query: str) -> RerankResult:
14
+ """
15
+ Invoke rerank
16
+ """
17
+ for data in self._backwards_invoke(
18
+ InvokeType.Rerank,
19
+ RerankResult,
20
+ {
21
+ **model_config.model_dump(),
22
+ "docs": docs,
23
+ "query": query,
24
+ },
25
+ ):
26
+ return data
27
+
28
+ raise Exception("No response from rerank")
29
+
30
+ def invoke_multimodal(
31
+ self,
32
+ model_config: MultiModalRerankModelConfig,
33
+ query: MultiModalContent,
34
+ docs: list[MultiModalContent],
35
+ ) -> MultiModalRerankResult:
36
+ payload = {
37
+ **model_config.model_dump(),
38
+ "query": query.model_dump() if isinstance(query, MultiModalContent) else query,
39
+ "docs": [doc.model_dump() if isinstance(doc, MultiModalContent) else doc for doc in docs],
40
+ }
41
+
42
+ for data in self._backwards_invoke(
43
+ InvokeType.MultimodalRerank,
44
+ MultiModalRerankResult,
45
+ payload,
46
+ ):
47
+ return data
48
+
49
+ raise Exception("No response from multimodal rerank")
@@ -0,0 +1,57 @@
1
+ from dify_plugin.core.entities.invocation import InvokeType
2
+ from dify_plugin.core.runtime import BackwardsInvocation
3
+ from dify_plugin.entities.model import EmbeddingInputType
4
+ from dify_plugin.entities.model.text_embedding import (
5
+ MultiModalContent,
6
+ MultiModalEmbeddingModelConfig,
7
+ MultiModalEmbeddingResult,
8
+ TextEmbeddingModelConfig,
9
+ TextEmbeddingResult,
10
+ )
11
+
12
+
13
+ class TextEmbeddingInvocation(BackwardsInvocation[TextEmbeddingResult]):
14
+ def invoke(
15
+ self,
16
+ model_config: TextEmbeddingModelConfig,
17
+ texts: list[str],
18
+ input_type: EmbeddingInputType = EmbeddingInputType.QUERY,
19
+ ) -> TextEmbeddingResult:
20
+ """
21
+ Invoke text embedding
22
+ """
23
+ for data in self._backwards_invoke(
24
+ InvokeType.TextEmbedding,
25
+ TextEmbeddingResult,
26
+ {
27
+ **model_config.model_dump(),
28
+ "texts": texts,
29
+ "input_type": input_type.value,
30
+ },
31
+ ):
32
+ return data
33
+
34
+ raise Exception("No response from text embedding")
35
+
36
+ def invoke_multimodal(
37
+ self,
38
+ model_config: MultiModalEmbeddingModelConfig,
39
+ documents: list[MultiModalContent],
40
+ input_type: EmbeddingInputType = EmbeddingInputType.QUERY,
41
+ ) -> MultiModalEmbeddingResult:
42
+ payload = {
43
+ **model_config.model_dump(),
44
+ "documents": [
45
+ document.model_dump() if isinstance(document, MultiModalContent) else document for document in documents
46
+ ],
47
+ "input_type": input_type.value,
48
+ }
49
+
50
+ for data in self._backwards_invoke(
51
+ InvokeType.MultimodalEmbedding,
52
+ MultiModalEmbeddingResult,
53
+ payload,
54
+ ):
55
+ return data
56
+
57
+ raise Exception("No response from multimodal embedding")
@@ -266,6 +266,12 @@ class Plugin(IOServer, Router):
266
266
  and data.get("action") == ModelActions.InvokeTextEmbedding.value,
267
267
  )
268
268
 
269
+ self.register_route(
270
+ self.plugin_executer.invoke_multimodal_embedding,
271
+ lambda data: data.get("type") == PluginInvokeType.Model.value
272
+ and data.get("action") == ModelActions.InvokeMultimodalEmbedding.value,
273
+ )
274
+
269
275
  self.register_route(
270
276
  self.plugin_executer.get_text_embedding_num_tokens,
271
277
  lambda data: data.get("type") == PluginInvokeType.Model.value
@@ -278,6 +284,12 @@ class Plugin(IOServer, Router):
278
284
  and data.get("action") == ModelActions.InvokeRerank.value,
279
285
  )
280
286
 
287
+ self.register_route(
288
+ self.plugin_executer.invoke_multimodal_rerank,
289
+ lambda data: data.get("type") == PluginInvokeType.Model.value
290
+ and data.get("action") == ModelActions.InvokeMultimodalRerank.value,
291
+ )
292
+
281
293
  self.register_route(
282
294
  self.plugin_executer.invoke_tts,
283
295
  lambda data: data.get("type") == PluginInvokeType.Model.value
@@ -1,13 +1,13 @@
1
1
  [project]
2
2
  name = "dify_plugin"
3
- version = "0.6.2"
3
+ version = "0.7.0"
4
4
  description = "Dify Plugin SDK"
5
5
  authors = [
6
6
  { name = "langgenius", email = "hello@dify.ai" },
7
7
  ]
8
8
  dependencies = [
9
9
  "Flask~=3.0.3",
10
- "Werkzeug~=3.0.3",
10
+ "Werkzeug~=3.1.4",
11
11
  "dpkt~=1.9.8",
12
12
  "gevent~=25.5.1",
13
13
  "httpx~=0.28.1",
@@ -0,0 +1,70 @@
1
+ """
2
+ Tests for _plugin_config configuration object in openai_compatible/llm.py
3
+
4
+ These tests verify:
5
+ 1. Configuration object is properly instantiated at module level
6
+ 2. Configuration object is a singleton (only one instance at module level)
7
+ 3. Configuration object has correct default values
8
+ 4. Configuration object has correct types
9
+ """
10
+
11
+ from dify_plugin.config.config import DifyPluginEnv
12
+ from dify_plugin.interfaces.model.openai_compatible import llm
13
+
14
+
15
+ def test_plugin_config_exists():
16
+ """Test that _plugin_config object exists"""
17
+ assert hasattr(llm, "_plugin_config")
18
+ assert llm._plugin_config is not None
19
+
20
+
21
+ def test_plugin_config_type():
22
+ """Test that _plugin_config is an instance of DifyPluginEnv"""
23
+ assert isinstance(llm._plugin_config, DifyPluginEnv)
24
+
25
+
26
+ def test_plugin_config_has_max_request_timeout():
27
+ """Test that _plugin_config has MAX_REQUEST_TIMEOUT attribute"""
28
+ assert hasattr(llm._plugin_config, "MAX_REQUEST_TIMEOUT")
29
+ assert isinstance(llm._plugin_config.MAX_REQUEST_TIMEOUT, int)
30
+ # Default value should be 300 seconds (unless overridden by environment variable)
31
+ assert llm._plugin_config.MAX_REQUEST_TIMEOUT > 0
32
+
33
+
34
+ def test_plugin_config_has_max_invocation_timeout():
35
+ """Test that _plugin_config has MAX_INVOCATION_TIMEOUT attribute"""
36
+ assert hasattr(llm._plugin_config, "MAX_INVOCATION_TIMEOUT")
37
+ assert isinstance(llm._plugin_config.MAX_INVOCATION_TIMEOUT, int)
38
+ # Default value should be 250 seconds (unless overridden by environment variable)
39
+ assert llm._plugin_config.MAX_INVOCATION_TIMEOUT > 0
40
+
41
+
42
+ def test_plugin_config_singleton():
43
+ """Test that configuration object is instantiated only once at module level"""
44
+ # Get the id of the config object
45
+ config_id_1 = id(llm._plugin_config)
46
+
47
+ # Import again, should be the same object
48
+ from dify_plugin.interfaces.model.openai_compatible import llm as llm2
49
+
50
+ config_id_2 = id(llm2._plugin_config)
51
+
52
+ # Verify it's the same object
53
+ assert config_id_1 == config_id_2
54
+
55
+
56
+ def test_plugin_config_timeout_values_reasonable():
57
+ """Test that timeout configuration values are within reasonable range"""
58
+ # MAX_REQUEST_TIMEOUT should be between 1 and 3600 seconds
59
+ assert 1 <= llm._plugin_config.MAX_REQUEST_TIMEOUT <= 3600
60
+
61
+ # MAX_INVOCATION_TIMEOUT should be between 1 and 3600 seconds
62
+ assert 1 <= llm._plugin_config.MAX_INVOCATION_TIMEOUT <= 3600
63
+
64
+
65
+ def test_plugin_config_can_create_new_instance():
66
+ """Test that new DifyPluginEnv instances can be created (verify the config class itself works)"""
67
+ new_config = DifyPluginEnv()
68
+ assert isinstance(new_config, DifyPluginEnv)
69
+ assert hasattr(new_config, "MAX_REQUEST_TIMEOUT")
70
+ assert hasattr(new_config, "MAX_INVOCATION_TIMEOUT")