dify_plugin 0.4.1__tar.gz → 0.4.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 (147) hide show
  1. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/PKG-INFO +1 -1
  2. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/config/config.py +2 -3
  3. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/config/integration_config.py +1 -1
  4. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/core/documentation/schema_doc.py +7 -7
  5. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/core/entities/plugin/io.py +13 -10
  6. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/core/entities/plugin/request.py +23 -9
  7. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/core/entities/plugin/setup.py +23 -26
  8. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/core/plugin_executor.py +22 -7
  9. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/core/runtime.py +55 -17
  10. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/core/server/__base/filter_reader.py +4 -4
  11. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/core/server/__base/response_writer.py +6 -7
  12. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/core/server/__base/writer_entities.py +2 -3
  13. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/core/server/io_server.py +13 -10
  14. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/core/server/router.py +2 -2
  15. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/core/server/serverless/request_reader.py +1 -0
  16. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/core/server/stdio/request_reader.py +1 -0
  17. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/core/server/tcp/request_reader.py +3 -2
  18. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/entities/__init__.py +3 -5
  19. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/entities/agent.py +14 -16
  20. dify_plugin-0.4.2/dify_plugin/entities/invoke_message.py +149 -0
  21. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/entities/model/__init__.py +10 -10
  22. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/entities/model/llm.py +5 -6
  23. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/entities/model/message.py +5 -5
  24. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/entities/model/provider.py +22 -23
  25. dify_plugin-0.4.2/dify_plugin/entities/oauth.py +35 -0
  26. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/entities/provider_config.py +11 -6
  27. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/entities/tool.py +27 -156
  28. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/entities/workflow_node.py +2 -3
  29. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/errors/model.py +2 -5
  30. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/errors/tool.py +4 -0
  31. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/integration/run.py +17 -5
  32. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/interfaces/agent/__init__.py +14 -11
  33. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/interfaces/model/ai_model.py +6 -8
  34. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/interfaces/model/large_language_model.py +13 -13
  35. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/interfaces/model/moderation_model.py +2 -3
  36. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/interfaces/model/openai_compatible/llm.py +12 -12
  37. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/interfaces/model/openai_compatible/rerank.py +3 -4
  38. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/interfaces/model/openai_compatible/speech2text.py +2 -2
  39. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/interfaces/model/openai_compatible/text_embedding.py +1 -2
  40. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/interfaces/model/openai_compatible/tts.py +3 -4
  41. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/interfaces/model/rerank_model.py +6 -7
  42. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/interfaces/model/speech2text_model.py +3 -3
  43. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/interfaces/model/text_embedding_model.py +2 -3
  44. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/interfaces/model/tts_model.py +7 -7
  45. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/interfaces/tool/__init__.py +97 -44
  46. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/invocations/file.py +2 -3
  47. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/invocations/tool.py +18 -6
  48. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/plugin.py +29 -8
  49. dify_plugin-0.4.2/dify_plugin/protocol/oauth.py +47 -0
  50. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/pyproject.toml +1 -1
  51. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/tests/interfaces/tool/test_construct_tool_provider.py +1 -1
  52. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/tests/interfaces/tool/test_costruct_tool.py +23 -0
  53. dify_plugin-0.4.2/tests/servers/test_session.py +75 -0
  54. dify_plugin-0.4.1/dify_plugin/entities/oauth.py +0 -17
  55. dify_plugin-0.4.1/dify_plugin/protocol/oauth.py +0 -27
  56. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/LICENSE +0 -0
  57. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/README.md +0 -0
  58. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/__init__.py +0 -0
  59. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/cli.py +0 -0
  60. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/commands/__init__.py +0 -0
  61. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/commands/generate_docs.py +0 -0
  62. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/config/__init__.py +0 -0
  63. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/config/logger_format.py +0 -0
  64. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/core/__init__.py +0 -0
  65. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/core/documentation/__init__.py +0 -0
  66. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/core/documentation/generator.py +0 -0
  67. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/core/entities/__init__.py +0 -0
  68. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/core/entities/invocation.py +0 -0
  69. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/core/entities/message.py +0 -0
  70. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/core/entities/plugin/__init__.py +0 -0
  71. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/core/entities/plugin/parameter_type.py +0 -0
  72. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/core/plugin_registration.py +0 -0
  73. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/core/server/__base/__init__.py +0 -0
  74. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/core/server/__base/request_reader.py +0 -0
  75. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/core/server/__init__.py +0 -0
  76. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/core/server/serverless/__init__.py +0 -0
  77. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/core/server/serverless/response_writer.py +0 -0
  78. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/core/server/stdio/__init__.py +0 -0
  79. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/core/server/stdio/response_writer.py +0 -0
  80. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/core/server/tcp/__init__.py +0 -0
  81. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/core/utils/__init__.py +0 -0
  82. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/core/utils/class_loader.py +0 -0
  83. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/core/utils/http_parser.py +0 -0
  84. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/core/utils/position_helper.py +0 -0
  85. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/core/utils/yaml_loader.py +0 -0
  86. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/entities/endpoint.py +0 -0
  87. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/entities/model/moderation.py +0 -0
  88. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/entities/model/rerank.py +0 -0
  89. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/entities/model/speech2text.py +0 -0
  90. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/entities/model/text_embedding.py +0 -0
  91. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/entities/model/tts.py +0 -0
  92. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/errors/__init__.py +0 -0
  93. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/file/__init__.py +0 -0
  94. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/file/constants.py +0 -0
  95. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/file/entities.py +0 -0
  96. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/file/file.py +0 -0
  97. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/integration/entities.py +0 -0
  98. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/integration/exc.py +0 -0
  99. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/interfaces/__init__.py +0 -0
  100. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/interfaces/endpoint/__init__.py +0 -0
  101. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/interfaces/model/__init__.py +0 -0
  102. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/interfaces/model/audio.mp3 +0 -0
  103. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/interfaces/model/openai_compatible/__init__.py +0 -0
  104. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/interfaces/model/openai_compatible/common.py +0 -0
  105. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/interfaces/model/openai_compatible/provider.py +0 -0
  106. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/interfaces/trigger/__init__.py +0 -0
  107. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/invocations/__init__.py +0 -0
  108. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/invocations/app/__init__.py +0 -0
  109. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/invocations/app/chat.py +0 -0
  110. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/invocations/app/completion.py +0 -0
  111. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/invocations/app/workflow.py +0 -0
  112. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/invocations/model/__init__.py +0 -0
  113. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/invocations/model/llm.py +0 -0
  114. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/invocations/model/llm_structured_output.py +0 -0
  115. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/invocations/model/moderation.py +0 -0
  116. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/invocations/model/rerank.py +0 -0
  117. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/invocations/model/speech2text.py +0 -0
  118. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/invocations/model/text_embedding.py +0 -0
  119. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/invocations/model/tts.py +0 -0
  120. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/invocations/storage.py +0 -0
  121. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/invocations/workflow_node/__init__.py +0 -0
  122. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/invocations/workflow_node/parameter_extractor.py +0 -0
  123. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/invocations/workflow_node/question_classifier.py +0 -0
  124. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/protocol/__init__.py +0 -0
  125. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/protocol/dynamic_select.py +0 -0
  126. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/tool/__init__.py +0 -0
  127. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/dify_plugin/tool/entities.py +0 -0
  128. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/tests/__init__.py +0 -0
  129. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/tests/__mock_server/__init__.py +0 -0
  130. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/tests/__mock_server/openai.py +0 -0
  131. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/tests/config/test_config.py +0 -0
  132. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/tests/consts/mockserver.py +0 -0
  133. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/tests/entities/endpoint/test_endpoint_group.py +0 -0
  134. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/tests/entities/models/test_llm.py +0 -0
  135. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/tests/entities/plugin/test_declaration.py +0 -0
  136. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/tests/integration/test_invoke_llm.py +0 -0
  137. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/tests/interfaces/agent/test_agent.py +0 -0
  138. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/tests/interfaces/model/__init__.py +0 -0
  139. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/tests/interfaces/model/openai_compatible/__init__.py +0 -0
  140. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/tests/interfaces/model/openai_compatible/test_increase_tool_call.py +0 -0
  141. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/tests/interfaces/tool/__init__.py +0 -0
  142. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/tests/invocations/test_storage.py +0 -0
  143. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/tests/servers/test_stdio.py +0 -0
  144. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/tests/test_llm_result.py +0 -0
  145. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/tests/test_prompt_message.py +0 -0
  146. {dify_plugin-0.4.1 → dify_plugin-0.4.2}/tests/test_tool_call_model_init.py +0 -0
  147. {dify_plugin-0.4.1 → dify_plugin-0.4.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.4.1
3
+ Version: 0.4.2
4
4
  Summary: Dify Plugin SDK
5
5
  Keywords: dify,plugin,sdk
6
6
  Author-Email: langgenius <hello@dify.ai>
@@ -1,5 +1,4 @@
1
1
  from enum import Enum
2
- from typing import Optional
3
2
 
4
3
  from pydantic import Field
5
4
  from pydantic_settings import BaseSettings, SettingsConfigDict
@@ -24,10 +23,10 @@ class DifyPluginEnv(BaseSettings):
24
23
  description="Installation method, local or network",
25
24
  )
26
25
 
27
- REMOTE_INSTALL_URL: Optional[str] = Field(default=None, description="Remote installation URL")
26
+ REMOTE_INSTALL_URL: str | None = Field(default=None, description="Remote installation URL")
28
27
  REMOTE_INSTALL_HOST: str = Field(default="localhost", description="Remote installation host")
29
28
  REMOTE_INSTALL_PORT: int = Field(default=5003, description="Remote installation port")
30
- REMOTE_INSTALL_KEY: Optional[str] = Field(default=None, description="Remote installation key")
29
+ REMOTE_INSTALL_KEY: str | None = Field(default=None, description="Remote installation key")
31
30
 
32
31
  SERVERLESS_HOST: str = Field(default="0.0.0.0", description="Serverless host")
33
32
  SERVERLESS_PORT: int = Field(default=8080, description="Serverless port")
@@ -52,4 +52,4 @@ class IntegrationConfig(BaseSettings):
52
52
 
53
53
  return v
54
54
 
55
- model_config = SettingsConfigDict(env_file=".env")
55
+ model_config = SettingsConfigDict(env_file=".env", extra="allow")
@@ -1,5 +1,5 @@
1
1
  from collections.abc import Callable, Mapping
2
- from typing import Any, Optional
2
+ from typing import Any
3
3
 
4
4
  from pydantic import BaseModel
5
5
 
@@ -9,10 +9,10 @@ class SchemaDoc:
9
9
  self,
10
10
  cls: type[BaseModel],
11
11
  description: str,
12
- name: Optional[str] = None,
12
+ name: str | None = None,
13
13
  top: bool = False,
14
- ignore_fields: Optional[list[str]] = None,
15
- outside_reference_fields: Optional[Mapping[str, type[BaseModel]]] = None,
14
+ ignore_fields: list[str] | None = None,
15
+ outside_reference_fields: Mapping[str, type[BaseModel]] | None = None,
16
16
  ):
17
17
  self.cls = cls
18
18
  self.description = description
@@ -27,10 +27,10 @@ __cls_mapping__: dict[type[BaseModel], SchemaDoc] = {}
27
27
 
28
28
  def docs(
29
29
  description: str,
30
- name: Optional[str] = None,
30
+ name: str | None = None,
31
31
  top: bool = False,
32
- ignore_fields: Optional[list[str]] = None,
33
- outside_reference_fields: Optional[Mapping[str, type[BaseModel]]] = None,
32
+ ignore_fields: list[str] | None = None,
33
+ outside_reference_fields: Mapping[str, type[BaseModel]] | None = None,
34
34
  ) -> Callable:
35
35
  """
36
36
  Decorator to add schema documentation to a class
@@ -1,5 +1,5 @@
1
1
  from enum import Enum
2
- from typing import TYPE_CHECKING, Optional
2
+ from typing import TYPE_CHECKING
3
3
 
4
4
  if TYPE_CHECKING:
5
5
  from dify_plugin.core.server.__base.request_reader import RequestReader
@@ -25,10 +25,11 @@ class PluginInStreamBase:
25
25
  session_id: str,
26
26
  event: PluginInStreamEvent,
27
27
  data: dict,
28
- conversation_id: Optional[str] = None,
29
- message_id: Optional[str] = None,
30
- app_id: Optional[str] = None,
31
- endpoint_id: Optional[str] = None,
28
+ conversation_id: str | None = None,
29
+ message_id: str | None = None,
30
+ app_id: str | None = None,
31
+ endpoint_id: str | None = None,
32
+ context: dict | None = None,
32
33
  ) -> None:
33
34
  self.session_id = session_id
34
35
  self.event = event
@@ -37,6 +38,7 @@ class PluginInStreamBase:
37
38
  self.message_id = message_id
38
39
  self.app_id = app_id
39
40
  self.endpoint_id = endpoint_id
41
+ self.context = context
40
42
 
41
43
 
42
44
  class PluginInStream(PluginInStreamBase):
@@ -47,11 +49,12 @@ class PluginInStream(PluginInStreamBase):
47
49
  data: dict,
48
50
  reader: "RequestReader",
49
51
  writer: "ResponseWriter",
50
- conversation_id: Optional[str] = None,
51
- message_id: Optional[str] = None,
52
- app_id: Optional[str] = None,
53
- endpoint_id: Optional[str] = None,
52
+ conversation_id: str | None = None,
53
+ message_id: str | None = None,
54
+ app_id: str | None = None,
55
+ endpoint_id: str | None = None,
56
+ context: dict | None = None,
54
57
  ):
55
58
  self.reader = reader
56
59
  self.writer = writer
57
- super().__init__(session_id, event, data, conversation_id, message_id, app_id, endpoint_id)
60
+ super().__init__(session_id, event, data, conversation_id, message_id, app_id, endpoint_id, context)
@@ -1,6 +1,6 @@
1
1
  from collections.abc import Mapping
2
2
  from enum import StrEnum
3
- from typing import Any, Optional
3
+ from typing import Any
4
4
 
5
5
  from pydantic import BaseModel, ConfigDict, field_validator
6
6
 
@@ -14,6 +14,7 @@ from dify_plugin.entities.model.message import (
14
14
  ToolPromptMessage,
15
15
  UserPromptMessage,
16
16
  )
17
+ from dify_plugin.entities.provider_config import CredentialType
17
18
 
18
19
 
19
20
  class PluginInvokeType(StrEnum):
@@ -57,6 +58,7 @@ class EndpointActions(StrEnum):
57
58
  class OAuthActions(StrEnum):
58
59
  GetAuthorizationUrl = "get_authorization_url"
59
60
  GetCredentials = "get_credentials"
61
+ RefreshCredentials = "refresh_credentials"
60
62
 
61
63
 
62
64
  class DynamicParameterActions(StrEnum):
@@ -78,6 +80,7 @@ class ToolInvokeRequest(PluginAccessRequest):
78
80
  provider: str
79
81
  tool: str
80
82
  credentials: dict
83
+ credential_type: CredentialType = CredentialType.API_KEY
81
84
  tool_parameters: dict[str, Any]
82
85
 
83
86
 
@@ -146,8 +149,8 @@ class ModelInvokeLLMRequest(PluginAccessModelRequest, PromptMessageMixin):
146
149
  action: ModelActions = ModelActions.InvokeLLM
147
150
 
148
151
  model_parameters: dict[str, Any]
149
- stop: Optional[list[str]]
150
- tools: Optional[list[PromptMessageTool]]
152
+ stop: list[str] | None
153
+ tools: list[PromptMessageTool] | None
151
154
  stream: bool = True
152
155
 
153
156
  model_config = ConfigDict(protected_namespaces=())
@@ -156,7 +159,7 @@ class ModelInvokeLLMRequest(PluginAccessModelRequest, PromptMessageMixin):
156
159
  class ModelGetLLMNumTokens(PluginAccessModelRequest, PromptMessageMixin):
157
160
  action: ModelActions = ModelActions.GetLLMNumTokens
158
161
 
159
- tools: Optional[list[PromptMessageTool]]
162
+ tools: list[PromptMessageTool] | None
160
163
 
161
164
 
162
165
  class ModelInvokeTextEmbeddingRequest(PluginAccessModelRequest):
@@ -176,8 +179,8 @@ class ModelInvokeRerankRequest(PluginAccessModelRequest):
176
179
 
177
180
  query: str
178
181
  docs: list[str]
179
- score_threshold: Optional[float]
180
- top_n: Optional[int]
182
+ score_threshold: float | None
183
+ top_n: int | None
181
184
 
182
185
 
183
186
  class ModelInvokeTTSRequest(PluginAccessModelRequest):
@@ -191,7 +194,7 @@ class ModelInvokeTTSRequest(PluginAccessModelRequest):
191
194
  class ModelGetTTSVoices(PluginAccessModelRequest):
192
195
  action: ModelActions = ModelActions.GetTTSVoices
193
196
 
194
- language: Optional[str]
197
+ language: str | None
195
198
 
196
199
 
197
200
  class ModelInvokeSpeech2TextRequest(PluginAccessModelRequest):
@@ -241,21 +244,32 @@ class EndpointInvokeRequest(BaseModel):
241
244
  raw_http_request: str
242
245
 
243
246
 
244
- class OAuthGetAuthorizationUrlRequest(BaseModel):
247
+ class OAuthGetAuthorizationUrlRequest(PluginAccessRequest):
245
248
  type: PluginInvokeType = PluginInvokeType.OAuth
246
249
  action: OAuthActions = OAuthActions.GetAuthorizationUrl
247
250
  provider: str
251
+ redirect_uri: str
248
252
  system_credentials: Mapping[str, Any]
249
253
 
250
254
 
251
- class OAuthGetCredentialsRequest(BaseModel):
255
+ class OAuthGetCredentialsRequest(PluginAccessRequest):
252
256
  type: PluginInvokeType = PluginInvokeType.OAuth
253
257
  action: OAuthActions = OAuthActions.GetCredentials
254
258
  provider: str
259
+ redirect_uri: str
255
260
  system_credentials: Mapping[str, Any]
256
261
  raw_http_request: str
257
262
 
258
263
 
264
+ class OAuthRefreshCredentialsRequest(PluginAccessRequest):
265
+ type: PluginInvokeType = PluginInvokeType.OAuth
266
+ action: OAuthActions = OAuthActions.RefreshCredentials
267
+ provider: str
268
+ redirect_uri: str
269
+ system_credentials: Mapping[str, Any]
270
+ credentials: Mapping[str, Any]
271
+
272
+
259
273
  class DynamicParameterFetchParameterOptionsRequest(BaseModel):
260
274
  type: PluginInvokeType = PluginInvokeType.DynamicParameter
261
275
  action: DynamicParameterActions = DynamicParameterActions.FetchParameterOptions
@@ -1,6 +1,5 @@
1
1
  import datetime
2
2
  from enum import Enum
3
- from typing import Optional
4
3
 
5
4
  from pydantic import BaseModel, Field
6
5
 
@@ -44,57 +43,55 @@ class PluginResourceRequirements(BaseModel):
44
43
  description="Permission of tool",
45
44
  )
46
45
  class Tool(BaseModel):
47
- enabled: Optional[bool] = Field(default=False)
46
+ enabled: bool | None = Field(default=False)
48
47
 
49
48
  @docs(
50
49
  description="Permission of model",
51
50
  )
52
51
  class Model(BaseModel):
53
- enabled: Optional[bool] = Field(default=False, description="Whether to enable invocation of model")
54
- llm: Optional[bool] = Field(default=False, description="Whether to enable invocation of llm")
55
- text_embedding: Optional[bool] = Field(
52
+ enabled: bool | None = Field(default=False, description="Whether to enable invocation of model")
53
+ llm: bool | None = Field(default=False, description="Whether to enable invocation of llm")
54
+ text_embedding: bool | None = Field(
56
55
  default=False, description="Whether to enable invocation of text embedding"
57
56
  )
58
- rerank: Optional[bool] = Field(default=False, description="Whether to enable invocation of rerank")
59
- tts: Optional[bool] = Field(default=False, description="Whether to enable invocation of tts")
60
- speech2text: Optional[bool] = Field(
61
- default=False, description="Whether to enable invocation of speech2text"
62
- )
63
- moderation: Optional[bool] = Field(default=False, description="Whether to enable invocation of moderation")
57
+ rerank: bool | None = Field(default=False, description="Whether to enable invocation of rerank")
58
+ tts: bool | None = Field(default=False, description="Whether to enable invocation of tts")
59
+ speech2text: bool | None = Field(default=False, description="Whether to enable invocation of speech2text")
60
+ moderation: bool | None = Field(default=False, description="Whether to enable invocation of moderation")
64
61
 
65
62
  @docs(
66
63
  description="Permission of node",
67
64
  )
68
65
  class Node(BaseModel):
69
- enabled: Optional[bool] = Field(default=False, description="Whether to enable invocation of node")
66
+ enabled: bool | None = Field(default=False, description="Whether to enable invocation of node")
70
67
 
71
68
  @docs(
72
69
  description="Permission of endpoint",
73
70
  )
74
71
  class Endpoint(BaseModel):
75
- enabled: Optional[bool] = Field(default=False, description="Whether to enable registration of endpoint")
72
+ enabled: bool | None = Field(default=False, description="Whether to enable registration of endpoint")
76
73
 
77
74
  @docs(
78
75
  description="Permission of app",
79
76
  )
80
77
  class App(BaseModel):
81
- enabled: Optional[bool] = Field(default=False, description="Whether to enable invocation of app")
78
+ enabled: bool | None = Field(default=False, description="Whether to enable invocation of app")
82
79
 
83
80
  @docs(
84
81
  description="Permission of storage",
85
82
  )
86
83
  class Storage(BaseModel):
87
- enabled: Optional[bool] = Field(default=False, description="Whether to enable uses of storage")
84
+ enabled: bool | None = Field(default=False, description="Whether to enable uses of storage")
88
85
  size: int = Field(ge=1024, le=1073741824, default=1048576, description="Size of storage")
89
86
 
90
- tool: Optional[Tool] = Field(default=None, description="Permission of tool")
91
- model: Optional[Model] = Field(default=None, description="Permission of model")
92
- node: Optional[Node] = Field(default=None, description="Permission of node")
93
- endpoint: Optional[Endpoint] = Field(default=None, description="Permission of endpoint")
94
- app: Optional[App] = Field(default=None, description="Permission of app")
95
- storage: Optional[Storage] = Field(default=None, description="Permission of storage")
87
+ tool: Tool | None = Field(default=None, description="Permission of tool")
88
+ model: Model | None = Field(default=None, description="Permission of model")
89
+ node: Node | None = Field(default=None, description="Permission of node")
90
+ endpoint: Endpoint | None = Field(default=None, description="Permission of endpoint")
91
+ app: App | None = Field(default=None, description="Permission of app")
92
+ storage: Storage | None = Field(default=None, description="Permission of storage")
96
93
 
97
- permission: Optional[Permission] = Field(default=None, description="Permission of plugin")
94
+ permission: Permission | None = Field(default=None, description="Permission of plugin")
98
95
 
99
96
 
100
97
  @docs(
@@ -145,7 +142,7 @@ class PluginConfiguration(BaseModel):
145
142
  )
146
143
  arch: list[PluginArch]
147
144
  runner: PluginRunner
148
- minimum_dify_version: Optional[str] = Field(
145
+ minimum_dify_version: str | None = Field(
149
146
  None,
150
147
  description="The minimum version of Dify, designed for forward compatibility."
151
148
  "When installing a newer plugin to an older Dify, many new features may not be available,"
@@ -155,12 +152,12 @@ class PluginConfiguration(BaseModel):
155
152
 
156
153
  version: str = Field(..., pattern=r"^\d{1,4}(\.\d{1,4}){1,3}(-\w{1,16})?$")
157
154
  type: PluginType
158
- author: Optional[str] = Field(..., pattern=r"^[a-zA-Z0-9_-]{1,64}$")
155
+ author: str | None = Field(..., pattern=r"^[a-zA-Z0-9_-]{1,64}$")
159
156
  name: str = Field(..., pattern=r"^[a-z0-9_-]{1,128}$")
160
- repo: Optional[str] = Field(None, description="The repository URL of the plugin")
157
+ repo: str | None = Field(None, description="The repository URL of the plugin")
161
158
  description: I18nObject
162
159
  icon: str
163
- icon_dark: Optional[str] = Field(None, description="The dark mode icon of the plugin")
160
+ icon_dark: str | None = Field(None, description="The dark mode icon of the plugin")
164
161
  label: I18nObject
165
162
  created_at: datetime.datetime
166
163
  resource: PluginResourceRequirements
@@ -23,6 +23,7 @@ from dify_plugin.core.entities.plugin.request import (
23
23
  ModelValidateProviderCredentialsRequest,
24
24
  OAuthGetAuthorizationUrlRequest,
25
25
  OAuthGetCredentialsRequest,
26
+ OAuthRefreshCredentialsRequest,
26
27
  ToolGetRuntimeParametersRequest,
27
28
  ToolInvokeRequest,
28
29
  ToolValidateCredentialsRequest,
@@ -40,7 +41,6 @@ from dify_plugin.interfaces.model.rerank_model import RerankModel
40
41
  from dify_plugin.interfaces.model.speech2text_model import Speech2TextModel
41
42
  from dify_plugin.interfaces.model.text_embedding_model import TextEmbeddingModel
42
43
  from dify_plugin.interfaces.model.tts_model import TTSModel
43
- from dify_plugin.interfaces.tool import ToolProvider
44
44
  from dify_plugin.protocol.dynamic_select import DynamicSelectProtocol
45
45
  from dify_plugin.protocol.oauth import OAuthProviderProtocol
46
46
 
@@ -73,6 +73,7 @@ class PluginExecutor:
73
73
  tool = tool_cls(
74
74
  runtime=ToolRuntime(
75
75
  credentials=request.credentials,
76
+ credential_type=request.credential_type,
76
77
  user_id=request.user_id,
77
78
  session_id=session.session_id,
78
79
  ),
@@ -333,16 +334,15 @@ class PluginExecutor:
333
334
  if provider_cls is None:
334
335
  raise ValueError(f"Provider `{provider}` does not support OAuth")
335
336
 
336
- if provider_cls == ToolProvider:
337
- return provider_cls()
338
-
339
- raise ValueError(f"Provider `{provider}` does not support OAuth")
337
+ return provider_cls()
340
338
 
341
339
  def get_oauth_authorization_url(self, session: Session, data: OAuthGetAuthorizationUrlRequest):
342
340
  provider_instance = self._get_oauth_provider_instance(data.provider)
343
341
 
344
342
  return {
345
- "authorization_url": provider_instance.oauth_get_authorization_url(data.system_credentials),
343
+ "authorization_url": provider_instance.oauth_get_authorization_url(
344
+ data.redirect_uri, data.system_credentials
345
+ ),
346
346
  }
347
347
 
348
348
  def get_oauth_credentials(self, session: Session, data: OAuthGetCredentialsRequest):
@@ -350,8 +350,23 @@ class PluginExecutor:
350
350
  bytes_data = binascii.unhexlify(data.raw_http_request)
351
351
  request = parse_raw_request(bytes_data)
352
352
 
353
+ credentials = provider_instance.oauth_get_credentials(data.redirect_uri, data.system_credentials, request)
354
+
355
+ return {
356
+ "metadata": credentials.metadata or {},
357
+ "credentials": credentials.credentials,
358
+ "expires_at": credentials.expires_at,
359
+ }
360
+
361
+ def refresh_oauth_credentials(self, session: Session, data: OAuthRefreshCredentialsRequest):
362
+ provider_instance = self._get_oauth_provider_instance(data.provider)
363
+ credentials = provider_instance.oauth_refresh_credentials(
364
+ data.redirect_uri, data.system_credentials, data.credentials
365
+ )
366
+
353
367
  return {
354
- "credentials": provider_instance.oauth_get_credentials(data.system_credentials, request),
368
+ "credentials": credentials.credentials,
369
+ "expires_at": credentials.expires_at,
355
370
  }
356
371
 
357
372
  def _get_dynamic_parameter_action(
@@ -3,10 +3,10 @@ from abc import ABC
3
3
  from collections.abc import Generator, Mapping
4
4
  from concurrent.futures import ThreadPoolExecutor
5
5
  from enum import Enum
6
- from typing import Any, Generic, Optional, TypeVar, Union
6
+ from typing import Any, Generic, TypeVar, Union
7
7
 
8
8
  import httpx
9
- from pydantic import BaseModel, TypeAdapter
9
+ from pydantic import BaseModel, Field, TypeAdapter
10
10
  from yarl import URL
11
11
 
12
12
  from dify_plugin.config.config import InstallMethod
@@ -28,7 +28,9 @@ 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
+ from dify_plugin.invocations.model.llm_structured_output import (
32
+ LLMStructuredOutputInvocation,
33
+ )
32
34
  from dify_plugin.invocations.model.moderation import ModerationInvocation
33
35
  from dify_plugin.invocations.model.rerank import RerankInvocation
34
36
  from dify_plugin.invocations.model.speech2text import Speech2TextInvocation
@@ -74,6 +76,35 @@ class WorkflowNodeInvocations:
74
76
  self.parameter_extractor = ParameterExtractorNodeInvocation(session)
75
77
 
76
78
 
79
+ class InvokeCredentials(BaseModel):
80
+ """
81
+ Invoke credentials
82
+
83
+ Session Level Credentials, used to store session level credentials, such as tool call credentials.
84
+ Especially for the backwards invoke, when invoker is not specified
85
+ We need to use the credential id from the session context.
86
+ """
87
+
88
+ tool_credentials: dict[str, str] = Field(
89
+ default_factory=dict,
90
+ description="This is a map of tool provider to credential id.",
91
+ )
92
+
93
+ def get_credential_id(self, provider: str) -> str | None:
94
+ return self.tool_credentials.get(provider)
95
+
96
+
97
+ class SessionContext(BaseModel):
98
+ """
99
+ Session Context
100
+
101
+ Session Context is used to store the session level context, such as credentials.
102
+ In the future, we will refactor message_id and conversation_id and the others to be part of the session context.
103
+ """
104
+
105
+ credentials: InvokeCredentials = Field(default_factory=InvokeCredentials)
106
+
107
+
77
108
  class Session:
78
109
  def __init__(
79
110
  self,
@@ -81,12 +112,13 @@ class Session:
81
112
  executor: ThreadPoolExecutor,
82
113
  reader: RequestReader,
83
114
  writer: ResponseWriter,
84
- install_method: Optional[InstallMethod] = None,
85
- dify_plugin_daemon_url: Optional[str] = None,
86
- conversation_id: Optional[str] = None,
87
- message_id: Optional[str] = None,
88
- app_id: Optional[str] = None,
89
- endpoint_id: Optional[str] = None,
115
+ install_method: InstallMethod | None = None,
116
+ dify_plugin_daemon_url: str | None = None,
117
+ conversation_id: str | None = None,
118
+ message_id: str | None = None,
119
+ app_id: str | None = None,
120
+ endpoint_id: str | None = None,
121
+ context: SessionContext | dict | None = None,
90
122
  ) -> None:
91
123
  # current session id
92
124
  self.session_id: str = session_id
@@ -99,22 +131,27 @@ class Session:
99
131
  self.writer: ResponseWriter = writer
100
132
 
101
133
  # conversation id
102
- self.conversation_id: Optional[str] = conversation_id
134
+ self.conversation_id: str | None = conversation_id
103
135
 
104
136
  # message id
105
- self.message_id: Optional[str] = message_id
137
+ self.message_id: str | None = message_id
106
138
 
107
139
  # app id
108
- self.app_id: Optional[str] = app_id
140
+ self.app_id: str | None = app_id
109
141
 
110
142
  # endpoint id
111
- self.endpoint_id: Optional[str] = endpoint_id
143
+ self.endpoint_id: str | None = endpoint_id
112
144
 
113
145
  # install method
114
- self.install_method: Optional[InstallMethod] = install_method
146
+ self.install_method: InstallMethod | None = install_method
147
+
148
+ # context
149
+ self.context: SessionContext = (
150
+ SessionContext.model_validate(context) if isinstance(context, dict) else context or SessionContext()
151
+ )
115
152
 
116
153
  # dify plugin daemon url
117
- self.dify_plugin_daemon_url: Optional[str] = dify_plugin_daemon_url
154
+ self.dify_plugin_daemon_url: str | None = dify_plugin_daemon_url
118
155
 
119
156
  # register invocations
120
157
  self._register_invocations()
@@ -140,6 +177,7 @@ class Session:
140
177
  writer=TCPReaderWriter(host="", port=0, key=""),
141
178
  install_method=None,
142
179
  dify_plugin_daemon_url=None,
180
+ context=None,
143
181
  )
144
182
 
145
183
 
@@ -157,7 +195,7 @@ class BackwardsInvocationResponseEvent(BaseModel):
157
195
  backwards_request_id: str
158
196
  event: Event
159
197
  message: str
160
- data: Optional[dict]
198
+ data: dict | None
161
199
 
162
200
 
163
201
  T = TypeVar("T", bound=Union[BaseModel, dict, str])
@@ -166,7 +204,7 @@ T = TypeVar("T", bound=Union[BaseModel, dict, str])
166
204
  class BackwardsInvocation(Generic[T], ABC):
167
205
  def __init__(
168
206
  self,
169
- session: Optional[Session] = None,
207
+ session: Session | None = None,
170
208
  ) -> None:
171
209
  self.session = session
172
210
 
@@ -1,7 +1,7 @@
1
1
  import queue
2
2
  from collections.abc import Callable, Generator
3
3
  from queue import Queue
4
- from typing import Optional, overload
4
+ from typing import overload
5
5
 
6
6
  from dify_plugin.core.entities.plugin.io import PluginInStream
7
7
 
@@ -9,12 +9,12 @@ from dify_plugin.core.entities.plugin.io import PluginInStream
9
9
  class FilterReader:
10
10
  filter: Callable[[PluginInStream], bool]
11
11
  queue: Queue[PluginInStream | None]
12
- close_callback: Optional[Callable]
12
+ close_callback: Callable | None
13
13
 
14
14
  def __init__(
15
15
  self,
16
16
  filter: Callable[[PluginInStream], bool], # noqa: A002
17
- close_callback: Optional[Callable] = None,
17
+ close_callback: Callable | None = None,
18
18
  ) -> None:
19
19
  self.filter = filter
20
20
  self.queue = Queue()
@@ -26,7 +26,7 @@ class FilterReader:
26
26
  @overload
27
27
  def read(self) -> Generator[PluginInStream, None, None]: ...
28
28
 
29
- def read(self, timeout_for_round: Optional[float] = None) -> Generator[PluginInStream | None, None, None]:
29
+ def read(self, timeout_for_round: float | None = None) -> Generator[PluginInStream | None, None, None]:
30
30
  while True:
31
31
  try:
32
32
  data = self.queue.get(timeout=timeout_for_round)
@@ -1,5 +1,4 @@
1
1
  from abc import ABC, abstractmethod
2
- from typing import Optional
3
2
 
4
3
  from pydantic import BaseModel
5
4
 
@@ -27,8 +26,8 @@ class ResponseWriter(ABC):
27
26
  def put(
28
27
  self,
29
28
  event: Event,
30
- session_id: Optional[str] = None,
31
- data: Optional[dict | BaseModel] = None,
29
+ session_id: str | None = None,
30
+ data: dict | BaseModel | None = None,
32
31
  ):
33
32
  """
34
33
  serialize the output to the daemon
@@ -39,19 +38,19 @@ class ResponseWriter(ABC):
39
38
  self.write(StreamOutputMessage(event=event, session_id=session_id, data=data).model_dump_json())
40
39
  self.write("\n\n")
41
40
 
42
- def error(self, session_id: Optional[str] = None, data: Optional[dict | BaseModel] = None):
41
+ def error(self, session_id: str | None = None, data: dict | BaseModel | None = None):
43
42
  return self.put(Event.ERROR, session_id, data)
44
43
 
45
- def log(self, data: Optional[dict] = None):
44
+ def log(self, data: dict | None = None):
46
45
  return self.put(Event.LOG, None, data)
47
46
 
48
47
  def heartbeat(self):
49
48
  return self.put(Event.HEARTBEAT, None, {})
50
49
 
51
- def session_message(self, session_id: Optional[str] = None, data: Optional[dict | BaseModel] = None):
50
+ def session_message(self, session_id: str | None = None, data: dict | BaseModel | None = None):
52
51
  return self.put(Event.SESSION, session_id, data)
53
52
 
54
- def session_message_text(self, session_id: Optional[str] = None, data: Optional[dict | BaseModel] = None) -> str:
53
+ def session_message_text(self, session_id: str | None = None, data: dict | BaseModel | None = None) -> str:
55
54
  if isinstance(data, BaseModel):
56
55
  data = data.model_dump()
57
56
 
@@ -1,5 +1,4 @@
1
1
  from enum import Enum
2
- from typing import Optional
3
2
 
4
3
  from pydantic import BaseModel
5
4
 
@@ -13,5 +12,5 @@ class Event(Enum):
13
12
 
14
13
  class StreamOutputMessage(BaseModel):
15
14
  event: Event
16
- session_id: Optional[str]
17
- data: Optional[dict | BaseModel]
15
+ session_id: str | None
16
+ data: dict | BaseModel | None