Skip to content

Commit 7faff00

Browse files
committed
fix: allow retrying of failed runs
Signed-off-by: Donnie Adams <[email protected]>
1 parent 8452bda commit 7faff00

File tree

5 files changed

+67
-16
lines changed

5 files changed

+67
-16
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ None of the options is required, and the defaults will reduce the number of call
3434
- `chatState`: The chat state to continue, or null to start a new chat and return the state
3535
- `confirm`: Prompt before running potentially dangerous commands
3636
- `prompt`: Allow prompting of the user
37+
- `env`: Extra environment variables to pass to the script in the form `KEY=VAL`
3738

3839
## Functions
3940

client.go

-2
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,6 @@ func (c *client) Evaluate(ctx context.Context, opts Options, tools ...fmt.String
119119
state: Creating,
120120
opts: opts,
121121
content: concatTools(tools),
122-
chatState: opts.ChatState,
123122
}).NextChat(ctx, opts.Input)
124123
}
125124

@@ -130,7 +129,6 @@ func (c *client) Run(ctx context.Context, toolPath string, opts Options) (*Run,
130129
state: Creating,
131130
opts: opts,
132131
toolPath: toolPath,
133-
chatState: opts.ChatState,
134132
}).NextChat(ctx, opts.Input)
135133
}
136134

opts.go

+10-9
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ package gptscript
22

33
// Options represents options for the gptscript tool or file.
44
type Options struct {
5-
Confirm bool `json:"confirm"`
6-
Input string `json:"input"`
7-
DisableCache bool `json:"disableCache"`
8-
CacheDir string `json:"cacheDir"`
9-
SubTool string `json:"subTool"`
10-
Workspace string `json:"workspace"`
11-
ChatState string `json:"chatState"`
12-
IncludeEvents bool `json:"includeEvents"`
13-
Prompt bool `json:"prompt"`
5+
Confirm bool `json:"confirm"`
6+
Input string `json:"input"`
7+
DisableCache bool `json:"disableCache"`
8+
CacheDir string `json:"cacheDir"`
9+
SubTool string `json:"subTool"`
10+
Workspace string `json:"workspace"`
11+
ChatState string `json:"chatState"`
12+
IncludeEvents bool `json:"includeEvents"`
13+
Prompt bool `json:"prompt"`
14+
Env []string `json:"env"`
1415
}

run.go

+12-5
Original file line numberDiff line numberDiff line change
@@ -106,22 +106,24 @@ func (r *Run) ChatState() string {
106106
// NextChat will pass input and create the next run in a chat.
107107
// The new Run will be returned.
108108
func (r *Run) NextChat(ctx context.Context, input string) (*Run, error) {
109-
if r.state != Creating && r.state != Continue {
110-
return nil, fmt.Errorf("run must be in creating or continue state not %q", r.state)
109+
if r.state != Creating && r.state != Continue && r.state != Error {
110+
return nil, fmt.Errorf("run must be in creating, continue, or error state not %q", r.state)
111111
}
112112

113113
run := &Run{
114114
url: r.url,
115115
requestPath: r.requestPath,
116116
state: Creating,
117-
chatState: r.chatState,
118117
toolPath: r.toolPath,
119118
content: r.content,
120119
opts: r.opts,
121120
}
121+
122122
run.opts.Input = input
123-
if run.chatState != "" {
124-
run.opts.ChatState = run.chatState
123+
if r.chatState != "" && r.state != Error {
124+
// If the previous run errored, then don't update the chat state.
125+
// opts.ChatState will be the last chat state where an error did not occur.
126+
run.opts.ChatState = r.chatState
125127
}
126128

127129
var payload any
@@ -287,6 +289,11 @@ func (r *Run) request(ctx context.Context, payload any) (err error) {
287289
return
288290
}
289291

292+
if event.Run != nil && event.Run.Type == EventTypeRunFinish && event.Run.Error != "" {
293+
r.state = Error
294+
r.err = fmt.Errorf(event.Run.Error)
295+
}
296+
290297
if r.opts.IncludeEvents {
291298
r.events <- event
292299
}

run_test.go

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package gptscript
2+
3+
import (
4+
"context"
5+
"testing"
6+
)
7+
8+
func TestRestartingErrorRun(t *testing.T) {
9+
tool := &ToolDef{
10+
Context: []string{"my-context"},
11+
Instructions: "Say hello",
12+
}
13+
contextTool := &ToolDef{
14+
Name: "my-context",
15+
Instructions: `#!/usr/bin/env python3
16+
17+
import os, sys
18+
19+
sys.exit(int(os.environ['EXIT_CODE']))
20+
`,
21+
}
22+
23+
run, err := c.Evaluate(context.Background(), Options{Env: []string{"EXIT_CODE=1"}}, tool, contextTool)
24+
if err != nil {
25+
t.Errorf("Error executing tool: %v", err)
26+
}
27+
28+
// Wait for the run to complete
29+
_, err = run.Text()
30+
if err == nil {
31+
t.Fatalf("no error returned from run")
32+
}
33+
34+
run.opts.Env = []string{"EXIT_CODE=0"}
35+
run, err = run.NextChat(context.Background(), "")
36+
if err != nil {
37+
t.Errorf("Error executing next run: %v", err)
38+
}
39+
40+
_, err = run.Text()
41+
if err != nil {
42+
t.Errorf("executing run with input of 0 should not fail: %v", err)
43+
}
44+
}

0 commit comments

Comments
 (0)