run.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. package local_manager
  2. import (
  3. "errors"
  4. "fmt"
  5. "os"
  6. "os/exec"
  7. "sync"
  8. "github.com/langgenius/dify-plugin-daemon/internal/process"
  9. "github.com/langgenius/dify-plugin-daemon/internal/types/entities/constants"
  10. "github.com/langgenius/dify-plugin-daemon/internal/types/entities/plugin_entities"
  11. "github.com/langgenius/dify-plugin-daemon/internal/utils/log"
  12. "github.com/langgenius/dify-plugin-daemon/internal/utils/routine"
  13. )
  14. func (r *LocalPluginRuntime) gc() {
  15. if r.io_identity != "" {
  16. RemoveStdio(r.io_identity)
  17. }
  18. if r.wait_chan != nil {
  19. close(r.wait_chan)
  20. r.wait_chan = nil
  21. }
  22. }
  23. func (r *LocalPluginRuntime) init() {
  24. r.wait_chan = make(chan bool)
  25. r.SetLaunching()
  26. }
  27. func (r *LocalPluginRuntime) Type() plugin_entities.PluginRuntimeType {
  28. return plugin_entities.PLUGIN_RUNTIME_TYPE_LOCAL
  29. }
  30. func (r *LocalPluginRuntime) getCmd() (*exec.Cmd, error) {
  31. if r.Config.Meta.Runner.Language == constants.Python {
  32. cmd := exec.Command(r.python_interpreter_path, "-m", r.Config.Meta.Runner.Entrypoint)
  33. cmd.Dir = r.State.WorkingPath
  34. return cmd, nil
  35. }
  36. return nil, fmt.Errorf("unsupported language: %s", r.Config.Meta.Runner.Language)
  37. }
  38. func (r *LocalPluginRuntime) StartPlugin() error {
  39. defer log.Info("plugin %s stopped", r.Config.Identity())
  40. defer func() {
  41. r.wait_chan_lock.Lock()
  42. for _, c := range r.wait_stopped_chan {
  43. select {
  44. case c <- true:
  45. default:
  46. }
  47. }
  48. r.wait_chan_lock.Unlock()
  49. }()
  50. r.init()
  51. // start plugin
  52. e, err := r.getCmd()
  53. if err != nil {
  54. return err
  55. }
  56. e.Dir = r.State.WorkingPath
  57. // add env INSTALL_METHOD=local
  58. e.Env = append(e.Env, "INSTALL_METHOD=local", "PATH="+os.Getenv("PATH"))
  59. // NOTE: subprocess will be taken care of by subprocess manager
  60. // ensure all subprocess are killed when parent process exits, especially on Golang debugger
  61. process.WrapProcess(e)
  62. // get writer
  63. stdin, err := e.StdinPipe()
  64. if err != nil {
  65. r.SetRestarting()
  66. return fmt.Errorf("get stdin pipe failed: %s", err.Error())
  67. }
  68. defer stdin.Close()
  69. stdout, err := e.StdoutPipe()
  70. if err != nil {
  71. r.SetRestarting()
  72. return fmt.Errorf("get stdout pipe failed: %s", err.Error())
  73. }
  74. defer stdout.Close()
  75. stderr, err := e.StderrPipe()
  76. if err != nil {
  77. r.SetRestarting()
  78. return fmt.Errorf("get stderr pipe failed: %s", err.Error())
  79. }
  80. defer stderr.Close()
  81. if err := e.Start(); err != nil {
  82. r.SetRestarting()
  83. return err
  84. }
  85. // add to subprocess manager
  86. process.NewProcess(e)
  87. defer process.RemoveProcess(e)
  88. defer func() {
  89. // wait for plugin to exit
  90. err = e.Wait()
  91. if err != nil {
  92. r.SetRestarting()
  93. log.Error("plugin %s exited with error: %s", r.Config.Identity(), err.Error())
  94. }
  95. r.gc()
  96. }()
  97. defer e.Process.Kill()
  98. log.Info("plugin %s started", r.Config.Identity())
  99. // setup stdio
  100. stdio := PutStdioIo(r.Config.Identity(), stdin, stdout, stderr)
  101. r.io_identity = stdio.GetID()
  102. defer stdio.Stop()
  103. wg := sync.WaitGroup{}
  104. wg.Add(2)
  105. // listen to plugin stdout
  106. routine.Submit(func() {
  107. defer wg.Done()
  108. stdio.StartStdout()
  109. })
  110. // listen to plugin stderr
  111. routine.Submit(func() {
  112. defer wg.Done()
  113. stdio.StartStderr()
  114. })
  115. // send started event
  116. r.wait_chan_lock.Lock()
  117. for _, c := range r.wait_started_chan {
  118. select {
  119. case c <- true:
  120. default:
  121. }
  122. }
  123. r.wait_chan_lock.Unlock()
  124. // wait for plugin to exit
  125. err = stdio.Wait()
  126. if err != nil {
  127. return err
  128. }
  129. wg.Wait()
  130. // plugin has exited
  131. r.SetPending()
  132. return nil
  133. }
  134. func (r *LocalPluginRuntime) Wait() (<-chan bool, error) {
  135. if r.wait_chan == nil {
  136. return nil, errors.New("plugin not started")
  137. }
  138. return r.wait_chan, nil
  139. }
  140. func (r *LocalPluginRuntime) WaitStarted() <-chan bool {
  141. c := make(chan bool)
  142. r.wait_chan_lock.Lock()
  143. r.wait_started_chan = append(r.wait_started_chan, c)
  144. r.wait_chan_lock.Unlock()
  145. return c
  146. }
  147. func (r *LocalPluginRuntime) WaitStopped() <-chan bool {
  148. c := make(chan bool)
  149. r.wait_chan_lock.Lock()
  150. r.wait_stopped_chan = append(r.wait_stopped_chan, c)
  151. r.wait_chan_lock.Unlock()
  152. return c
  153. }