dify_plugin 0.1.0__tar.gz → 0.2.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.
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/LICENSE +1 -1
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/PKG-INFO +2 -2
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/README.md +1 -1
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/core/entities/invocation.py +1 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/core/entities/plugin/setup.py +1 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/core/plugin_executor.py +1 -1
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/core/plugin_registration.py +2 -2
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/core/runtime.py +10 -6
- dify_plugin-0.2.0/dify_plugin/core/server/__base/request_reader.py +114 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/core/server/serverless/request_reader.py +4 -2
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/core/server/stdio/request_reader.py +10 -2
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/core/server/tcp/request_reader.py +14 -8
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/entities/agent.py +2 -1
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/entities/endpoint.py +1 -1
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/entities/model/__init__.py +1 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/entities/model/llm.py +33 -4
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/entities/model/message.py +2 -1
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/entities/tool.py +2 -2
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/interfaces/agent/__init__.py +11 -1
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/interfaces/model/ai_model.py +5 -6
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/interfaces/model/large_language_model.py +0 -2
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/interfaces/model/openai_compatible/llm.py +9 -13
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/interfaces/tool/__init__.py +3 -3
- dify_plugin-0.2.0/dify_plugin/invocations/app/__init__.py +26 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/invocations/model/llm.py +0 -1
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/invocations/model/text_embedding.py +1 -1
- dify_plugin-0.2.0/dify_plugin/invocations/storage.py +89 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/pyproject.toml +1 -1
- dify_plugin-0.2.0/tests/entities/models/test_llm.py +54 -0
- dify_plugin-0.2.0/tests/interfaces/agent/test_agent.py +22 -0
- dify_plugin-0.2.0/tests/invocations/test_storage.py +78 -0
- dify_plugin-0.2.0/tests/test_llm_result.py +43 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/tests/test_tool_call_model_init.py +5 -9
- dify_plugin-0.1.0/dify_plugin/core/server/__base/request_reader.py +0 -66
- dify_plugin-0.1.0/dify_plugin/invocations/storage.py +0 -51
- dify_plugin-0.1.0/tests/__init__.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/__init__.py +14 -14
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/config/__init__.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/config/config.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/config/logger_format.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/core/__init__.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/core/entities/__init__.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/core/entities/message.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/core/entities/plugin/__init__.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/core/entities/plugin/io.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/core/entities/plugin/parameter_type.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/core/entities/plugin/request.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/core/server/__base/__init__.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/core/server/__base/filter_reader.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/core/server/__base/response_writer.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/core/server/__base/writer_entities.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/core/server/__init__.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/core/server/io_server.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/core/server/router.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/core/server/serverless/__init__.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/core/server/serverless/response_writer.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/core/server/stdio/__init__.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/core/server/stdio/response_writer.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/core/server/tcp/__init__.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/core/utils/__init__.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/core/utils/class_loader.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/core/utils/http_parser.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/core/utils/position_helper.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/core/utils/yaml_loader.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/entities/__init__.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/entities/model/moderation.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/entities/model/provider.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/entities/model/rerank.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/entities/model/speech2text.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/entities/model/text_embedding.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/entities/model/tts.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/entities/workflow_node.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/errors/__init__.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/errors/model.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/errors/tool.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/file/__init__.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/file/constants.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/file/entities.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/file/file.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/interfaces/__init__.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/interfaces/endpoint/__init__.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/interfaces/model/__init__.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/interfaces/model/audio.mp3 +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/interfaces/model/moderation_model.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/interfaces/model/openai_compatible/__init__.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/interfaces/model/openai_compatible/common.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/interfaces/model/openai_compatible/provider.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/interfaces/model/openai_compatible/rerank.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/interfaces/model/openai_compatible/speech2text.py +1 -1
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/interfaces/model/openai_compatible/text_embedding.py +1 -1
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/interfaces/model/openai_compatible/tts.py +1 -1
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/interfaces/model/rerank_model.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/interfaces/model/speech2text_model.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/interfaces/model/text_embedding_model.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/interfaces/model/tts_model.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/invocations/__init__.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/invocations/app/chat.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/invocations/app/completion.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/invocations/app/workflow.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/invocations/file.py +0 -0
- {dify_plugin-0.1.0/dify_plugin/invocations/app → dify_plugin-0.2.0/dify_plugin/invocations/model}/__init__.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/invocations/model/moderation.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/invocations/model/rerank.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/invocations/model/speech2text.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/invocations/model/tts.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/invocations/tool.py +0 -0
- {dify_plugin-0.1.0/dify_plugin/invocations/model → dify_plugin-0.2.0/dify_plugin/invocations/workflow_node}/__init__.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/invocations/workflow_node/parameter_extractor.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/invocations/workflow_node/question_classifier.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/plugin.py +1 -1
- {dify_plugin-0.1.0/dify_plugin/invocations/workflow_node → dify_plugin-0.2.0/dify_plugin/tool}/__init__.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/tool/entities.py +0 -0
- {dify_plugin-0.1.0/dify_plugin/tool → dify_plugin-0.2.0/tests}/__init__.py +0 -0
- {dify_plugin-0.1.0 → dify_plugin-0.2.0}/tests/test_prompt_message.py +0 -0
|
@@ -199,4 +199,4 @@
|
|
|
199
199
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
200
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
201
|
See the License for the specific language governing permissions and
|
|
202
|
-
limitations under the License.
|
|
202
|
+
limitations under the License.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: dify_plugin
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Dify Plugin SDK
|
|
5
5
|
Keywords: dify,plugin,sdk
|
|
6
6
|
Author-Email: langgenius <hello@dify.ai>
|
|
@@ -43,6 +43,6 @@ When depending on this SDK, it's recommended to specify version constraints that
|
|
|
43
43
|
Example in your project's dependency management:
|
|
44
44
|
|
|
45
45
|
```
|
|
46
|
-
dify_plugin
|
|
46
|
+
dify_plugin>=0.1.0,<0.2.0
|
|
47
47
|
```
|
|
48
48
|
|
|
@@ -75,6 +75,7 @@ class PluginConfiguration(BaseModel):
|
|
|
75
75
|
version: str
|
|
76
76
|
arch: list[PluginArch]
|
|
77
77
|
runner: PluginRunner
|
|
78
|
+
minimum_dify_version: Optional[str] = Field(..., pattern=r"^\d{1,4}(\.\d{1,4}){1,3}(-\w{1,16})?$")
|
|
78
79
|
|
|
79
80
|
version: str = Field(..., pattern=r"^\d{1,4}(\.\d{1,4}){1,3}(-\w{1,16})?$")
|
|
80
81
|
type: PluginType
|
|
@@ -281,7 +281,7 @@ class PluginExecutor:
|
|
|
281
281
|
except ValueError as e:
|
|
282
282
|
response = Response(str(e), status=404)
|
|
283
283
|
except Exception as e:
|
|
284
|
-
response = Response(f"Internal Server Error: {
|
|
284
|
+
response = Response(f"Internal Server Error: {e!s}", status=500)
|
|
285
285
|
|
|
286
286
|
# check if response is a generator
|
|
287
287
|
if isinstance(response.response, Generator):
|
|
@@ -123,7 +123,7 @@ class PluginRegistration:
|
|
|
123
123
|
self.agent_strategies_configuration.append(agent_provider_configuration)
|
|
124
124
|
|
|
125
125
|
except Exception as e:
|
|
126
|
-
raise ValueError(f"Error loading plugin configuration: {
|
|
126
|
+
raise ValueError(f"Error loading plugin configuration: {e!s}") from e
|
|
127
127
|
|
|
128
128
|
def _resolve_tool_providers(self):
|
|
129
129
|
"""
|
|
@@ -349,4 +349,4 @@ class PluginRegistration:
|
|
|
349
349
|
endpoint, values = adapter.match()
|
|
350
350
|
return endpoint, values
|
|
351
351
|
except werkzeug.exceptions.HTTPException as e:
|
|
352
|
-
raise ValueError(f"Failed to dispatch endpoint request: {
|
|
352
|
+
raise ValueError(f"Failed to dispatch endpoint request: {e!s}") from e
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import json
|
|
2
1
|
import uuid
|
|
3
2
|
from abc import ABC
|
|
4
|
-
from collections.abc import Generator
|
|
3
|
+
from collections.abc import Generator, Mapping
|
|
5
4
|
from concurrent.futures import ThreadPoolExecutor
|
|
6
5
|
from enum import Enum
|
|
7
|
-
from typing import Generic, Optional, TypeVar, Union
|
|
6
|
+
from typing import Any, Generic, Optional, TypeVar, Union
|
|
8
7
|
|
|
9
8
|
import httpx
|
|
10
|
-
from pydantic import BaseModel
|
|
9
|
+
from pydantic import BaseModel, TypeAdapter
|
|
11
10
|
from yarl import URL
|
|
12
11
|
|
|
13
12
|
from dify_plugin.config.config import InstallMethod
|
|
@@ -46,6 +45,7 @@ class ModelInvocations:
|
|
|
46
45
|
|
|
47
46
|
class AppInvocations:
|
|
48
47
|
def __init__(self, session: "Session"):
|
|
48
|
+
from dify_plugin.invocations.app import FetchAppInvocation
|
|
49
49
|
from dify_plugin.invocations.app.chat import ChatAppInvocation
|
|
50
50
|
from dify_plugin.invocations.app.completion import CompletionAppInvocation
|
|
51
51
|
from dify_plugin.invocations.app.workflow import WorkflowAppInvocation
|
|
@@ -53,6 +53,10 @@ class AppInvocations:
|
|
|
53
53
|
self.chat = ChatAppInvocation(session)
|
|
54
54
|
self.completion = CompletionAppInvocation(session)
|
|
55
55
|
self.workflow = WorkflowAppInvocation(session)
|
|
56
|
+
self.fetch_app_invocation = FetchAppInvocation(session)
|
|
57
|
+
|
|
58
|
+
def fetch_app(self, app_id: str) -> Mapping:
|
|
59
|
+
return self.fetch_app_invocation.get(app_id)
|
|
56
60
|
|
|
57
61
|
|
|
58
62
|
class WorkflowNodeInvocations:
|
|
@@ -219,7 +223,7 @@ class BackwardsInvocation(Generic[T], ABC):
|
|
|
219
223
|
try:
|
|
220
224
|
yield data_type(**event.data)
|
|
221
225
|
except Exception as e:
|
|
222
|
-
raise Exception(f"Failed to parse response: {
|
|
226
|
+
raise Exception(f"Failed to parse response: {e!s}") from e
|
|
223
227
|
|
|
224
228
|
def _http_backwards_invoke(
|
|
225
229
|
self,
|
|
@@ -271,7 +275,7 @@ class BackwardsInvocation(Generic[T], ABC):
|
|
|
271
275
|
if not line:
|
|
272
276
|
continue
|
|
273
277
|
|
|
274
|
-
data =
|
|
278
|
+
data = TypeAdapter(dict[str, Any]).validate_json(line)
|
|
275
279
|
yield PluginInStreamBase(
|
|
276
280
|
session_id=data["session_id"],
|
|
277
281
|
event=PluginInStreamEvent.value_of(data["event"]),
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import threading
|
|
3
|
+
import time
|
|
4
|
+
from abc import ABC, abstractmethod
|
|
5
|
+
from collections.abc import Callable, Generator
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from dify_plugin.core.entities.plugin.io import PluginInStream
|
|
10
|
+
|
|
11
|
+
from dify_plugin.core.server.__base.filter_reader import (
|
|
12
|
+
FilterReader,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class RequestReader(ABC):
|
|
19
|
+
def __init__(self):
|
|
20
|
+
# Convert class variables to instance variables to avoid global lock contention
|
|
21
|
+
self.lock = threading.Lock()
|
|
22
|
+
self.readers = []
|
|
23
|
+
|
|
24
|
+
@abstractmethod
|
|
25
|
+
def _read_stream(self) -> Generator["PluginInStream", None, None]:
|
|
26
|
+
"""
|
|
27
|
+
Read stream from stdin
|
|
28
|
+
"""
|
|
29
|
+
raise NotImplementedError
|
|
30
|
+
|
|
31
|
+
def event_loop(self):
|
|
32
|
+
# read line by line
|
|
33
|
+
while True:
|
|
34
|
+
try:
|
|
35
|
+
for line in self._read_stream():
|
|
36
|
+
self._process_line(line)
|
|
37
|
+
except Exception:
|
|
38
|
+
logger.exception("Error in event loop")
|
|
39
|
+
time.sleep(0.01) # Prevent high CPU usage
|
|
40
|
+
|
|
41
|
+
def _process_line(self, data: "PluginInStream"):
|
|
42
|
+
try:
|
|
43
|
+
session_id = data.session_id
|
|
44
|
+
readers_to_process = []
|
|
45
|
+
|
|
46
|
+
# Acquire lock to safely access readers list
|
|
47
|
+
self.lock.acquire()
|
|
48
|
+
try:
|
|
49
|
+
# Safely copy the reader list under lock protection
|
|
50
|
+
readers_to_process = self.readers.copy()
|
|
51
|
+
finally:
|
|
52
|
+
self.lock.release()
|
|
53
|
+
|
|
54
|
+
# Execute filter operations outside of lock
|
|
55
|
+
matched_readers = []
|
|
56
|
+
for reader in readers_to_process:
|
|
57
|
+
try:
|
|
58
|
+
result = reader.filter(data)
|
|
59
|
+
if result:
|
|
60
|
+
matched_readers.append(reader)
|
|
61
|
+
except Exception:
|
|
62
|
+
logger.exception("Error in filter")
|
|
63
|
+
|
|
64
|
+
# Process readers in batches to avoid blocking
|
|
65
|
+
for reader in matched_readers:
|
|
66
|
+
try:
|
|
67
|
+
reader.write(data)
|
|
68
|
+
except Exception:
|
|
69
|
+
logger.exception("Error writing to reader")
|
|
70
|
+
|
|
71
|
+
except Exception as e:
|
|
72
|
+
data.writer.error(
|
|
73
|
+
session_id=session_id,
|
|
74
|
+
data={"error": f"Failed to process request ({type(e).__name__}): {e!s}"},
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
def read(self, filter: Callable[["PluginInStream"], bool]) -> FilterReader: # noqa: A002
|
|
78
|
+
def close(reader: FilterReader):
|
|
79
|
+
self.lock.acquire()
|
|
80
|
+
try:
|
|
81
|
+
if reader in self.readers:
|
|
82
|
+
self.readers.remove(reader)
|
|
83
|
+
finally:
|
|
84
|
+
self.lock.release()
|
|
85
|
+
|
|
86
|
+
reader = FilterReader(filter, close_callback=lambda: close(reader))
|
|
87
|
+
|
|
88
|
+
self.lock.acquire()
|
|
89
|
+
try:
|
|
90
|
+
self.readers.append(reader)
|
|
91
|
+
finally:
|
|
92
|
+
self.lock.release()
|
|
93
|
+
|
|
94
|
+
return reader
|
|
95
|
+
|
|
96
|
+
def close(self):
|
|
97
|
+
"""
|
|
98
|
+
close stdin processing
|
|
99
|
+
"""
|
|
100
|
+
readers_to_close = []
|
|
101
|
+
|
|
102
|
+
self.lock.acquire()
|
|
103
|
+
try:
|
|
104
|
+
readers_to_close = self.readers.copy()
|
|
105
|
+
self.readers.clear()
|
|
106
|
+
finally:
|
|
107
|
+
self.lock.release()
|
|
108
|
+
|
|
109
|
+
# Close readers outside the lock
|
|
110
|
+
for reader in readers_to_close:
|
|
111
|
+
try:
|
|
112
|
+
reader.close()
|
|
113
|
+
except Exception:
|
|
114
|
+
logger.exception("Error closing reader")
|
{dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/core/server/serverless/request_reader.py
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import threading
|
|
2
|
+
import time
|
|
2
3
|
from collections.abc import Generator
|
|
3
4
|
from queue import Empty, Queue
|
|
4
|
-
import time
|
|
5
5
|
|
|
6
6
|
from flask import Flask, request
|
|
7
7
|
|
|
@@ -27,6 +27,7 @@ class ServerlessRequestReader(RequestReader):
|
|
|
27
27
|
"""
|
|
28
28
|
Initialize the ServerlessStream and wait for jobs
|
|
29
29
|
"""
|
|
30
|
+
super().__init__()
|
|
30
31
|
self.app = Flask(__name__)
|
|
31
32
|
self.host = host
|
|
32
33
|
self.port = port
|
|
@@ -94,6 +95,7 @@ class ServerlessRequestReader(RequestReader):
|
|
|
94
95
|
self.app.route("/health", methods=["GET"])(self.health)
|
|
95
96
|
|
|
96
97
|
import socket
|
|
98
|
+
|
|
97
99
|
import gevent.socket
|
|
98
100
|
|
|
99
101
|
if socket.socket is gevent.socket.socket:
|
|
@@ -101,7 +103,7 @@ class ServerlessRequestReader(RequestReader):
|
|
|
101
103
|
|
|
102
104
|
server = WSGIServer((self.host, self.port), self.app)
|
|
103
105
|
print("* Serving Flask app 'dify_plugin.core.server.serverless.request_reader'")
|
|
104
|
-
print("* Running on http
|
|
106
|
+
print(f"* Running on http://{self.host}:{self.port} (Press CTRL+C to quit)")
|
|
105
107
|
print("* Server Worker: gevent.wsgi.WSGIServer", flush=True)
|
|
106
108
|
server.serve_forever()
|
|
107
109
|
else:
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import sys
|
|
2
2
|
from collections.abc import Generator
|
|
3
|
-
from
|
|
3
|
+
from typing import Any
|
|
4
4
|
|
|
5
5
|
from gevent.os import tp_read
|
|
6
|
+
from pydantic import TypeAdapter
|
|
6
7
|
|
|
7
8
|
from dify_plugin.core.entities.plugin.io import (
|
|
8
9
|
PluginInStream,
|
|
@@ -13,6 +14,9 @@ from dify_plugin.core.server.stdio.response_writer import StdioResponseWriter
|
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
class StdioRequestReader(RequestReader):
|
|
17
|
+
def __init__(self):
|
|
18
|
+
super().__init__()
|
|
19
|
+
|
|
16
20
|
def _read_stream(self) -> Generator[PluginInStream, None, None]:
|
|
17
21
|
buffer = b""
|
|
18
22
|
while True:
|
|
@@ -34,9 +38,13 @@ class StdioRequestReader(RequestReader):
|
|
|
34
38
|
lines = lines[:-1]
|
|
35
39
|
for line in lines:
|
|
36
40
|
try:
|
|
37
|
-
data =
|
|
41
|
+
data = TypeAdapter(dict[str, Any]).validate_json(line)
|
|
38
42
|
yield PluginInStream(
|
|
39
43
|
session_id=data["session_id"],
|
|
44
|
+
conversation_id=data.get("conversation_id"),
|
|
45
|
+
message_id=data.get("message_id"),
|
|
46
|
+
app_id=data.get("app_id"),
|
|
47
|
+
endpoint_id=data.get("endpoint_id"),
|
|
40
48
|
event=PluginInStreamEvent.value_of(data["event"]),
|
|
41
49
|
data=data["data"],
|
|
42
50
|
reader=self,
|
|
@@ -2,18 +2,18 @@ import errno
|
|
|
2
2
|
import logging
|
|
3
3
|
import os
|
|
4
4
|
import signal
|
|
5
|
-
from threading import Lock
|
|
6
|
-
import time
|
|
7
5
|
import socket as native_socket
|
|
8
|
-
|
|
9
|
-
from
|
|
10
|
-
from
|
|
6
|
+
import time
|
|
7
|
+
from collections.abc import Callable, Generator
|
|
8
|
+
from threading import Lock
|
|
9
|
+
from typing import Any, Optional
|
|
11
10
|
|
|
11
|
+
from gevent import sleep
|
|
12
|
+
from gevent import socket as gevent_socket
|
|
12
13
|
from gevent.select import select
|
|
13
|
-
from
|
|
14
|
+
from pydantic import TypeAdapter
|
|
14
15
|
|
|
15
16
|
from dify_plugin.core.entities.message import InitializeMessage
|
|
16
|
-
|
|
17
17
|
from dify_plugin.core.entities.plugin.io import (
|
|
18
18
|
PluginInStream,
|
|
19
19
|
PluginInStreamEvent,
|
|
@@ -37,6 +37,8 @@ class TCPReaderWriter(RequestReader, ResponseWriter):
|
|
|
37
37
|
"""
|
|
38
38
|
Initialize the TCPStream and connect to the target, raise exception if connection failed
|
|
39
39
|
"""
|
|
40
|
+
super().__init__()
|
|
41
|
+
|
|
40
42
|
self.host = host
|
|
41
43
|
self.port = port
|
|
42
44
|
self.key = key
|
|
@@ -188,9 +190,13 @@ class TCPReaderWriter(RequestReader, ResponseWriter):
|
|
|
188
190
|
lines = lines[:-1]
|
|
189
191
|
for line in lines:
|
|
190
192
|
try:
|
|
191
|
-
data =
|
|
193
|
+
data = TypeAdapter(dict[str, Any]).validate_json(line)
|
|
192
194
|
chunk = PluginInStream(
|
|
193
195
|
session_id=data["session_id"],
|
|
196
|
+
conversation_id=data.get("conversation_id"),
|
|
197
|
+
message_id=data.get("message_id"),
|
|
198
|
+
app_id=data.get("app_id"),
|
|
199
|
+
endpoint_id=data.get("endpoint_id"),
|
|
194
200
|
event=PluginInStreamEvent.value_of(data["event"]),
|
|
195
201
|
data=data["data"],
|
|
196
202
|
reader=self,
|
|
@@ -45,6 +45,7 @@ class AgentStrategyParameter(BaseModel):
|
|
|
45
45
|
|
|
46
46
|
name: str = Field(..., description="The name of the parameter")
|
|
47
47
|
label: I18nObject = Field(..., description="The label presented to the user")
|
|
48
|
+
help: Optional[I18nObject] = None
|
|
48
49
|
type: ToolParameterType = Field(..., description="The type of the parameter")
|
|
49
50
|
auto_generate: Optional[ParameterAutoGenerate] = Field(
|
|
50
51
|
default=None, description="The auto generate of the parameter"
|
|
@@ -115,7 +116,7 @@ class AgentStrategyProviderConfiguration(BaseModel):
|
|
|
115
116
|
)
|
|
116
117
|
)
|
|
117
118
|
except Exception as e:
|
|
118
|
-
raise ValueError(f"Error loading agent strategy configuration: {
|
|
119
|
+
raise ValueError(f"Error loading agent strategy configuration: {e!s}") from e
|
|
119
120
|
|
|
120
121
|
return strategies
|
|
121
122
|
|
|
@@ -38,6 +38,6 @@ class EndpointProviderConfiguration(BaseModel):
|
|
|
38
38
|
file = load_yaml_file(endpoint)
|
|
39
39
|
endpoints.append(EndpointConfiguration(**file))
|
|
40
40
|
except Exception as e:
|
|
41
|
-
raise ValueError(f"Error loading endpoint configuration: {
|
|
41
|
+
raise ValueError(f"Error loading endpoint configuration: {e!s}") from e
|
|
42
42
|
|
|
43
43
|
return endpoints
|
|
@@ -2,7 +2,7 @@ from decimal import Decimal
|
|
|
2
2
|
from enum import Enum
|
|
3
3
|
from typing import Optional
|
|
4
4
|
|
|
5
|
-
from pydantic import BaseModel, ConfigDict, Field
|
|
5
|
+
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
6
6
|
|
|
7
7
|
from dify_plugin.entities.model import BaseModelConfig, ModelType, ModelUsage, PriceInfo
|
|
8
8
|
from dify_plugin.entities.model.message import (
|
|
@@ -86,10 +86,25 @@ class LLMResultChunk(BaseModel):
|
|
|
86
86
|
"""
|
|
87
87
|
|
|
88
88
|
model: str
|
|
89
|
-
prompt_messages: list[PromptMessage]
|
|
89
|
+
prompt_messages: list[PromptMessage] = Field(default_factory=list)
|
|
90
90
|
system_fingerprint: Optional[str] = None
|
|
91
91
|
delta: LLMResultChunkDelta
|
|
92
92
|
|
|
93
|
+
@field_validator("prompt_messages", mode="before")
|
|
94
|
+
@classmethod
|
|
95
|
+
def transform_prompt_messages(cls, value):
|
|
96
|
+
"""
|
|
97
|
+
ISSUE:
|
|
98
|
+
- https://github.com/langgenius/dify/issues/17799
|
|
99
|
+
- https://github.com/langgenius/dify-official-plugins/issues/648
|
|
100
|
+
|
|
101
|
+
The `prompt_messages` field is deprecated, but to keep backward compatibility
|
|
102
|
+
we need to always set it to an empty list.
|
|
103
|
+
|
|
104
|
+
NOTE: just do not use it anymore, it will be removed in the future.
|
|
105
|
+
"""
|
|
106
|
+
return []
|
|
107
|
+
|
|
93
108
|
|
|
94
109
|
class LLMResult(BaseModel):
|
|
95
110
|
"""
|
|
@@ -97,15 +112,29 @@ class LLMResult(BaseModel):
|
|
|
97
112
|
"""
|
|
98
113
|
|
|
99
114
|
model: str
|
|
100
|
-
prompt_messages: list[PromptMessage]
|
|
115
|
+
prompt_messages: list[PromptMessage] = Field(default_factory=list)
|
|
101
116
|
message: AssistantPromptMessage
|
|
102
117
|
usage: LLMUsage
|
|
103
118
|
system_fingerprint: Optional[str] = None
|
|
104
119
|
|
|
120
|
+
@field_validator("prompt_messages", mode="before")
|
|
121
|
+
@classmethod
|
|
122
|
+
def transform_prompt_messages(cls, value):
|
|
123
|
+
"""
|
|
124
|
+
ISSUE:
|
|
125
|
+
- https://github.com/langgenius/dify/issues/17799
|
|
126
|
+
- https://github.com/langgenius/dify-official-plugins/issues/648
|
|
127
|
+
|
|
128
|
+
The `prompt_messages` field is deprecated, but to keep backward compatibility
|
|
129
|
+
we need to always set it to an empty list.
|
|
130
|
+
|
|
131
|
+
NOTE: just do not use it anymore, it will be removed in the future.
|
|
132
|
+
"""
|
|
133
|
+
return []
|
|
134
|
+
|
|
105
135
|
def to_llm_result_chunk(self) -> "LLMResultChunk":
|
|
106
136
|
return LLMResultChunk(
|
|
107
137
|
model=self.model,
|
|
108
|
-
prompt_messages=self.prompt_messages,
|
|
109
138
|
system_fingerprint=self.system_fingerprint,
|
|
110
139
|
delta=LLMResultChunkDelta(
|
|
111
140
|
index=0,
|
|
@@ -2,7 +2,7 @@ from collections.abc import Sequence
|
|
|
2
2
|
from enum import Enum, StrEnum
|
|
3
3
|
from typing import Annotated, Literal, Optional, Union
|
|
4
4
|
|
|
5
|
-
from pydantic import BaseModel, Field, field_validator
|
|
5
|
+
from pydantic import BaseModel, BeforeValidator, Field, field_validator
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class PromptMessageRole(Enum):
|
|
@@ -188,6 +188,7 @@ def _ensure_field_empty_str(value: Optional[str]) -> str:
|
|
|
188
188
|
return ""
|
|
189
189
|
return value
|
|
190
190
|
|
|
191
|
+
|
|
191
192
|
class AssistantPromptMessage(PromptMessage):
|
|
192
193
|
"""
|
|
193
194
|
Model class for assistant prompt message.
|
|
@@ -81,7 +81,7 @@ class ToolInvokeMessage(BaseModel):
|
|
|
81
81
|
return {"text": self.text}
|
|
82
82
|
|
|
83
83
|
class JsonMessage(BaseModel):
|
|
84
|
-
json_object:
|
|
84
|
+
json_object: Mapping
|
|
85
85
|
|
|
86
86
|
def to_dict(self):
|
|
87
87
|
return {"json_object": self.json_object}
|
|
@@ -384,7 +384,7 @@ class ToolProviderConfiguration(BaseModel):
|
|
|
384
384
|
)
|
|
385
385
|
)
|
|
386
386
|
except Exception as e:
|
|
387
|
-
raise ValueError(f"Error loading tool configuration: {
|
|
387
|
+
raise ValueError(f"Error loading tool configuration: {e!s}") from e
|
|
388
388
|
|
|
389
389
|
return tools
|
|
390
390
|
|
|
@@ -28,7 +28,7 @@ class AgentToolIdentity(ToolIdentity):
|
|
|
28
28
|
|
|
29
29
|
class AgentModelConfig(LLMModelConfig):
|
|
30
30
|
entity: AIModelEntity | None = None
|
|
31
|
-
history_prompt_messages: list[PromptMessage] =
|
|
31
|
+
history_prompt_messages: list[PromptMessage] = Field(default_factory=list)
|
|
32
32
|
|
|
33
33
|
@field_validator("history_prompt_messages", mode="before")
|
|
34
34
|
@classmethod
|
|
@@ -277,6 +277,11 @@ class AgentStrategy(ToolLike[AgentInvokeMessage]):
|
|
|
277
277
|
ToolParameter.ToolParameterType.FILES,
|
|
278
278
|
}:
|
|
279
279
|
continue
|
|
280
|
+
if parameter.type in {
|
|
281
|
+
ToolParameter.ToolParameterType.SELECT,
|
|
282
|
+
ToolParameter.ToolParameterType.SECRET_INPUT,
|
|
283
|
+
}:
|
|
284
|
+
parameter_type = ToolParameter.ToolParameterType.STRING
|
|
280
285
|
enum = []
|
|
281
286
|
if parameter.type == ToolParameter.ToolParameterType.SELECT:
|
|
282
287
|
enum = [option.value for option in parameter.options] if parameter.options else []
|
|
@@ -311,6 +316,11 @@ class AgentStrategy(ToolLike[AgentInvokeMessage]):
|
|
|
311
316
|
ToolParameter.ToolParameterType.FILES,
|
|
312
317
|
}:
|
|
313
318
|
continue
|
|
319
|
+
if parameter.type in {
|
|
320
|
+
ToolParameter.ToolParameterType.SELECT,
|
|
321
|
+
ToolParameter.ToolParameterType.SECRET_INPUT,
|
|
322
|
+
}:
|
|
323
|
+
parameter_type = ToolParameter.ToolParameterType.STRING
|
|
314
324
|
enum = []
|
|
315
325
|
if parameter.type == ToolParameter.ToolParameterType.SELECT:
|
|
316
326
|
enum = [option.value for option in parameter.options] if parameter.options else []
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import decimal
|
|
2
|
+
import socket
|
|
2
3
|
from abc import ABC, abstractmethod
|
|
3
4
|
from collections.abc import Mapping
|
|
4
5
|
from typing import Optional
|
|
@@ -18,8 +19,6 @@ from dify_plugin.entities.model import (
|
|
|
18
19
|
)
|
|
19
20
|
from dify_plugin.errors.model import InvokeAuthorizationError, InvokeError
|
|
20
21
|
|
|
21
|
-
import socket
|
|
22
|
-
|
|
23
22
|
if socket.socket is gevent.socket.socket:
|
|
24
23
|
import gevent.threadpool
|
|
25
24
|
|
|
@@ -84,9 +83,9 @@ class AIModel(ABC):
|
|
|
84
83
|
"please check and try again. "
|
|
85
84
|
)
|
|
86
85
|
|
|
87
|
-
return invoke_error(description=f"[{provider_name}] {invoke_error.description}, {
|
|
86
|
+
return invoke_error(description=f"[{provider_name}] {invoke_error.description}, {error!s}")
|
|
88
87
|
|
|
89
|
-
return InvokeError(description=f"[{provider_name}] Error: {
|
|
88
|
+
return InvokeError(description=f"[{provider_name}] Error: {error!s}")
|
|
90
89
|
|
|
91
90
|
def get_price(self, model: str, credentials: dict, price_type: PriceType, tokens: int) -> PriceInfo:
|
|
92
91
|
"""
|
|
@@ -271,11 +270,11 @@ class AIModel(ABC):
|
|
|
271
270
|
if len(text) >= 100000:
|
|
272
271
|
return len(text)
|
|
273
272
|
|
|
274
|
-
import tiktoken
|
|
275
|
-
|
|
276
273
|
# check if gevent is patched to main thread
|
|
277
274
|
import socket
|
|
278
275
|
|
|
276
|
+
import tiktoken
|
|
277
|
+
|
|
279
278
|
if socket.socket is gevent.socket.socket:
|
|
280
279
|
# using gevent real thread to avoid blocking main thread
|
|
281
280
|
result = threadpool.spawn(lambda: len(tiktoken.encoding_for_model("gpt2").encode(text)))
|
{dify_plugin-0.1.0 → dify_plugin-0.2.0}/dify_plugin/interfaces/model/large_language_model.py
RENAMED
|
@@ -440,7 +440,6 @@ if you are not sure about the structure.
|
|
|
440
440
|
if new_piece:
|
|
441
441
|
yield LLMResultChunk(
|
|
442
442
|
model=model,
|
|
443
|
-
prompt_messages=prompt_messages,
|
|
444
443
|
delta=LLMResultChunkDelta(
|
|
445
444
|
index=0,
|
|
446
445
|
message=AssistantPromptMessage(content=new_piece, tool_calls=[]),
|
|
@@ -515,7 +514,6 @@ if you are not sure about the structure.
|
|
|
515
514
|
# Only yield content collected within the code block
|
|
516
515
|
yield LLMResultChunk(
|
|
517
516
|
model=model,
|
|
518
|
-
prompt_messages=prompt_messages,
|
|
519
517
|
delta=LLMResultChunkDelta(
|
|
520
518
|
index=0,
|
|
521
519
|
message=AssistantPromptMessage(content=new_piece, tool_calls=[]),
|