1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164package local_runtime
import (
_ "embed"
"fmt"
"os"
"os/exec"
"path"
"regexp"
"strings"
version "github.com/hashicorp/go-version"
"github.com/langgenius/dify-plugin-daemon/pkg/utils/log"
)
//go:embed patches/0.0.1b70.ai_model.py.patch
var python001b70aiModelsPatches []byte
//go:embed patches/0.1.1.llm.py.patch
var python011llmPatches []byte
//go:embed patches/0.1.1.request_reader.py.patch
var python011requestReaderPatches []byte
func (p *LocalPluginRuntime) patchPluginSdk(
dependencyFilePath string,
pythonInterpreterPath string,
) error {
// get the version of the plugin sdk from dependency file
dependencyContent, err := os.ReadFile(dependencyFilePath)
if err != nil {
return fmt.Errorf("failed to read dependency file %s: %s", dependencyFilePath, err)
}
pluginSdkVersion, err := p.getPluginSdkVersion(string(dependencyContent))
if err != nil {
log.Error("failed to get the version of the plugin sdk", "error", err)
return nil
}
pluginSdkVersionObj, err := version.NewVersion(pluginSdkVersion)
if err != nil {
log.Error("failed to create the version", "error", err)
return nil
}
if pluginSdkVersionObj.LessThan(version.Must(version.NewVersion("0.0.1b70"))) {
// get dify-plugin path
command := exec.Command(pythonInterpreterPath, "-c", "import importlib.util;print(importlib.util.find_spec('dify_plugin').origin)")
command.Dir = p.State.WorkingPath
output, err := command.Output()
if err != nil {
return fmt.Errorf("failed to get the path of the plugin sdk: %s", err)
}
pluginSdkPath := path.Dir(strings.TrimSpace(string(output)))
patchPath := path.Join(pluginSdkPath, "interfaces/model/ai_model.py")
// apply the patch
if _, err := os.Stat(patchPath); err != nil {
return fmt.Errorf("failed to find the patch file: %s", err)
}
if err := os.WriteFile(patchPath, python001b70aiModelsPatches, 0644); err != nil {
return fmt.Errorf("failed to write the patch file: %s", err)
}
}
if pluginSdkVersionObj.LessThan(version.Must(version.NewVersion("0.1.1"))) {
// get dify-plugin path
command := exec.Command(pythonInterpreterPath, "-c", "import importlib.util;print(importlib.util.find_spec('dify_plugin').origin)")
command.Dir = p.State.WorkingPath
output, err := command.Output()
if err != nil {
return fmt.Errorf("failed to get the path of the plugin sdk: %s", err)
}
pluginSdkPath := path.Dir(strings.TrimSpace(string(output)))
patchPath := path.Join(pluginSdkPath, "entities/model/llm.py")
// apply the patch
if _, err := os.Stat(patchPath); err != nil {
return fmt.Errorf("failed to find the patch file: %s", err)
}
if err := os.WriteFile(patchPath, python011llmPatches, 0644); err != nil {
return fmt.Errorf("failed to write the patch file: %s", err)
}
patchPath = path.Join(pluginSdkPath, "core/server/stdio/request_reader.py")
if _, err := os.Stat(patchPath); err != nil {
return fmt.Errorf("failed to find the patch file: %s", err)
}
if err := os.WriteFile(patchPath, python011requestReaderPatches, 0644); err != nil {
return fmt.Errorf("failed to write the patch file: %s", err)
}
}
return nil
}
// getPluginSdkVersion extracts the dify-plugin SDK version from dependency file content.
// Works with both requirements.txt and pyproject.toml formats.
func (p *LocalPluginRuntime) getPluginSdkVersion(dependencyFileContent string) (string, error) {
// using regex to find the version of the plugin sdk
// First try to match exact version or compatible version
re := regexp.MustCompile(`(?:dify[_-]plugin)(?:~=|==)([0-9.a-z]+)`)
matches := re.FindStringSubmatch(dependencyFileContent)
if len(matches) >= 2 {
return matches[1], nil
}
// Try to match version ranges with multiple constraints
// Extract all version constraints for dify-plugin
// Try to match version ranges with multiple constraints
// For example: dify-plugin>=0.1.0,<0.2.0
reAllConstraints := regexp.MustCompile(`(?:dify[_-]plugin)([><]=?|==)([0-9.a-z]+)(?:,([><]=?|==)([0-9.a-z]+))?`)
allMatches := reAllConstraints.FindAllStringSubmatch(dependencyFileContent, -1)
if len(allMatches) > 0 {
// Always return the highest version among all constraints
var highestVersion *version.Version
var versionStr string
for _, match := range allMatches {
// Check for the second version constraint if it exists
if len(match) >= 5 {
currentVersionStr := match[4]
currentVersion, err := version.NewVersion(currentVersionStr)
if err != nil {
continue
}
if highestVersion == nil || currentVersion.GreaterThan(highestVersion) {
highestVersion = currentVersion
versionStr = currentVersionStr
}
} else if len(match) >= 3 {
currentVersionStr := match[2]
currentVersion, err := version.NewVersion(currentVersionStr)
if err != nil {
continue
}
if highestVersion == nil || currentVersion.GreaterThan(highestVersion) {
highestVersion = currentVersion
versionStr = currentVersionStr
}
}
}
if versionStr != "" {
return versionStr, nil
}
// If we couldn't parse any versions but have matches, return the first one
if len(allMatches[0]) >= 3 {
return allMatches[0][2], nil
}
}
return "", fmt.Errorf("failed to find the version of the plugin sdk")
}