@@ -17,12 +17,14 @@ import (
17
17
"sync"
18
18
)
19
19
20
+ var abortRunError = errors .New ("run aborted" )
21
+
20
22
type Run struct {
21
23
url , binPath , requestPath , toolPath , content string
22
24
opts Opts
23
25
state RunState
24
26
chatState string
25
- cmd * exec. Cmd
27
+ cancel context. CancelCauseFunc
26
28
err error
27
29
stdout , stderr io.Reader
28
30
wait func () error
@@ -61,6 +63,11 @@ func (r *Run) State() RunState {
61
63
return r .state
62
64
}
63
65
66
+ // Err returns the error that caused the gptscript to fail, if any.
67
+ func (r * Run ) Err () error {
68
+ return r .err
69
+ }
70
+
64
71
// ErrorOutput returns the stderr output of the gptscript.
65
72
// Should only be called after Bytes or Text has returned an error.
66
73
func (r * Run ) ErrorOutput () string {
@@ -75,20 +82,20 @@ func (r *Run) Events() <-chan Event {
75
82
// Close will stop the gptscript run, if it is running.
76
83
func (r * Run ) Close () error {
77
84
// If the command was not started, then report error.
78
- if r .cmd == nil || r . cmd . Process == nil {
85
+ if r .cancel == nil {
79
86
return fmt .Errorf ("run not started" )
80
87
}
81
88
82
- // If the command has already exited, then nothing to do.
83
- if r .cmd . ProcessState ! = nil {
89
+ r . cancel ( abortRunError )
90
+ if r .wait = = nil {
84
91
return nil
85
92
}
86
93
87
- if err := r .cmd . Process . Signal ( os . Kill ); err != nil {
94
+ if err := r .wait (); ! errors . Is ( err , abortRunError ) && ! errors . Is ( err , context . Canceled ) && ! errors . As ( err , new ( * exec. ExitError )) {
88
95
return err
89
96
}
90
97
91
- return r . wait ()
98
+ return nil
92
99
}
93
100
94
101
// RawOutput returns the raw output of the gptscript. Most users should use Text or Bytes instead.
@@ -169,26 +176,26 @@ func (r *Run) exec(ctx context.Context, extraArgs ...string) error {
169
176
args = append (args , r .toolPath )
170
177
}
171
178
172
- cancelCtx , cancel := context .WithCancel (ctx )
179
+ cancelCtx , cancel := context .WithCancelCause (ctx )
180
+ r .cancel = cancel
173
181
c , stdout , stderr , err := setupForkCommand (cancelCtx , r .binPath , r .content , r .opts .Input , args , eventsWrite )
174
182
if err != nil {
175
- cancel ()
183
+ r .err = fmt .Errorf ("failed to setup gptscript: %w" , err )
184
+ r .cancel (r .err )
176
185
_ = eventsRead .Close ()
177
186
r .state = Error
178
- r .err = fmt .Errorf ("failed to setup gptscript: %w" , err )
179
187
return r .err
180
188
}
181
189
182
190
if err = c .Start (); err != nil {
183
- cancel ()
191
+ r .err = fmt .Errorf ("failed to start gptscript: %w" , err )
192
+ r .cancel (r .err )
184
193
_ = eventsRead .Close ()
185
194
r .state = Error
186
- r .err = fmt .Errorf ("failed to start gptscript: %w" , err )
187
195
return r .err
188
196
}
189
197
190
198
r .state = Running
191
- r .cmd = c
192
199
r .stdout = stdout
193
200
r .stderr = stderr
194
201
r .events = make (chan Event , 100 )
@@ -197,14 +204,15 @@ func (r *Run) exec(ctx context.Context, extraArgs ...string) error {
197
204
r .wait = func () error {
198
205
err := c .Wait ()
199
206
_ = eventsRead .Close ()
200
- cancel ()
201
207
if err != nil {
202
208
r .state = Error
203
- r .err = fmt .Errorf ("failed to wait for gptscript: %w" , err )
209
+ r .err = fmt .Errorf ("failed to wait for gptscript: error: %w, stderr: %s" , err , string (r .errput ))
210
+ r .cancel (r .err )
204
211
} else {
205
212
if r .state == Running {
206
213
r .state = Finished
207
214
}
215
+ r .cancel (nil )
208
216
}
209
217
return r .err
210
218
}
@@ -255,7 +263,7 @@ func (r *Run) readEvents(ctx context.Context, events io.Reader) {
255
263
256
264
if err != nil && ! errors .Is (err , io .EOF ) {
257
265
slog .Debug ("failed to read events" , "error" , err )
258
- r .err = fmt .Errorf ("failed to read events: %w " , err )
266
+ r .err = fmt .Errorf ("failed to read events: error: %w, stderr: %s " , err , string ( r . errput ) )
259
267
}
260
268
}
261
269
@@ -332,6 +340,7 @@ func (r *Run) request(ctx context.Context, payload any) (err error) {
332
340
cancelCtx , cancel = context .WithCancelCause (ctx )
333
341
)
334
342
343
+ r .cancel = cancel
335
344
defer func () {
336
345
if err != nil {
337
346
cancel (err )
@@ -465,6 +474,8 @@ func (r *Run) request(ctx context.Context, payload any) (err error) {
465
474
if err := context .Cause (cancelCtx ); ! errors .Is (err , context .Canceled ) && r .err == nil {
466
475
r .state = Error
467
476
r .err = err
477
+ } else if r .state != Continue {
478
+ r .state = Finished
468
479
}
469
480
return r .err
470
481
}
0 commit comments