-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
185 additions
and
109 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
167 changes: 158 additions & 9 deletions
167
internal/core/plugin_manager/local_manager/environment_python.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,168 @@ | ||
package local_manager | ||
|
||
import "os/exec" | ||
import ( | ||
"context" | ||
"fmt" | ||
"os" | ||
"os/exec" | ||
"path" | ||
"path/filepath" | ||
"strings" | ||
"sync" | ||
"time" | ||
|
||
func (p *LocalPluginRuntime) InitPythonEnvironment(requirements_txt string) error { | ||
// create virtual env | ||
identity, err := p.Identity() | ||
"github.com/langgenius/dify-plugin-daemon/internal/utils/log" | ||
"github.com/langgenius/dify-plugin-daemon/internal/utils/routine" | ||
) | ||
|
||
func (p *LocalPluginRuntime) InitPythonEnvironment() error { | ||
// execute init command, create a virtual environment | ||
success := false | ||
|
||
cmd := exec.Command("bash", "-c", "python3 -m venv .venv") | ||
cmd.Dir = p.State.WorkingPath | ||
if err := cmd.Run(); err != nil { | ||
return fmt.Errorf("failed to create virtual environment: %s", err) | ||
} | ||
defer func() { | ||
// if init failed, remove the .venv directory | ||
if !success { | ||
os.RemoveAll(path.Join(p.State.WorkingPath, ".venv")) | ||
} | ||
}() | ||
|
||
// wait for the virtual environment to be created | ||
if err := cmd.Wait(); err != nil { | ||
return fmt.Errorf("failed to create virtual environment: %s", err) | ||
} | ||
|
||
// try find python interpreter and pip | ||
pip_path, err := filepath.Abs(path.Join(p.State.WorkingPath, ".venv/bin/pip")) | ||
if err != nil { | ||
return err | ||
return fmt.Errorf("failed to find pip: %s", err) | ||
} | ||
|
||
cmd := exec.Command("python", "-m", "venv", identity.String()) | ||
python_path, err := filepath.Abs(path.Join(p.State.WorkingPath, ".venv/bin/python")) | ||
if err != nil { | ||
return fmt.Errorf("failed to find python: %s", err) | ||
} | ||
|
||
if _, err := os.Stat(pip_path); err != nil { | ||
return fmt.Errorf("failed to find pip: %s", err) | ||
} | ||
|
||
// set working directory | ||
cmd.Dir = p.WorkingPath | ||
if _, err := os.Stat(python_path); err != nil { | ||
return fmt.Errorf("failed to find python: %s", err) | ||
} | ||
|
||
p.python_interpreter_path = python_path | ||
|
||
// try find requirements.txt | ||
requirements_path := path.Join(p.State.WorkingPath, "requirements.txt") | ||
if _, err := os.Stat(requirements_path); err != nil { | ||
return fmt.Errorf("failed to find requirements.txt: %s", err) | ||
} | ||
|
||
// install dependencies | ||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) | ||
defer cancel() | ||
|
||
cmd = exec.CommandContext(ctx, pip_path, "install", "-r", requirements_path) | ||
cmd.Dir = p.State.WorkingPath | ||
if err := cmd.Run(); err != nil { | ||
return fmt.Errorf("failed to install dependencies: %s", err) | ||
} | ||
|
||
// get stdout and stderr | ||
stdout, err := cmd.StdoutPipe() | ||
if err != nil { | ||
return fmt.Errorf("failed to get stdout: %s", err) | ||
} | ||
defer stdout.Close() | ||
|
||
stderr, err := cmd.StderrPipe() | ||
if err != nil { | ||
return fmt.Errorf("failed to get stderr: %s", err) | ||
} | ||
defer stderr.Close() | ||
|
||
// start command | ||
if err := cmd.Start(); err != nil { | ||
return fmt.Errorf("failed to start command: %s", err) | ||
} | ||
defer func() { | ||
if cmd.Process != nil { | ||
cmd.Process.Kill() | ||
} | ||
}() | ||
|
||
var err_msg strings.Builder | ||
var wg sync.WaitGroup | ||
wg.Add(2) | ||
|
||
last_active_at := time.Now() | ||
|
||
routine.Submit(func() { | ||
defer wg.Done() | ||
// read stdout | ||
buf := make([]byte, 1024) | ||
for { | ||
n, err := stdout.Read(buf) | ||
if err != nil { | ||
break | ||
} | ||
log.Info("installing %s - %s", p.Config.Identity(), string(buf[:n])) | ||
last_active_at = time.Now() | ||
} | ||
}) | ||
|
||
routine.Submit(func() { | ||
defer wg.Done() | ||
// read stderr | ||
buf := make([]byte, 1024) | ||
for { | ||
n, err := stderr.Read(buf) | ||
if err != nil && err != os.ErrClosed { | ||
last_active_at = time.Now() | ||
err_msg.WriteString(string(buf[:n])) | ||
break | ||
} else if err == os.ErrClosed { | ||
break | ||
} | ||
|
||
if n > 0 { | ||
err_msg.WriteString(string(buf[:n])) | ||
last_active_at = time.Now() | ||
} | ||
} | ||
}) | ||
|
||
routine.Submit(func() { | ||
ticker := time.NewTicker(5 * time.Second) | ||
defer ticker.Stop() | ||
for range ticker.C { | ||
if cmd.ProcessState != nil && cmd.ProcessState.Exited() { | ||
break | ||
} | ||
|
||
if time.Since(last_active_at) > 60*time.Second { | ||
cmd.Process.Kill() | ||
err_msg.WriteString("init process exited due to long time no activity") | ||
break | ||
} | ||
} | ||
}) | ||
|
||
wg.Wait() | ||
|
||
if err_msg.Len() > 0 { | ||
return fmt.Errorf("install failed: %s", err_msg.String()) | ||
} | ||
|
||
if err := cmd.Wait(); err != nil { | ||
return fmt.Errorf("failed to install dependencies: %s", err) | ||
} | ||
|
||
// TODO | ||
success = true | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters