Skip to content

Commit 9fb5c09

Browse files
committed
fix: allow retrying of failed runs
Signed-off-by: Donnie Adams <[email protected]>
1 parent 39497c0 commit 9fb5c09

File tree

5 files changed

+71
-20
lines changed

5 files changed

+71
-20
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

+16-9
Original file line numberDiff line numberDiff line change
@@ -154,22 +154,24 @@ func (r *Run) ChatState() string {
154154
// NextChat will pass input and create the next run in a chat.
155155
// The new Run will be returned.
156156
func (r *Run) NextChat(ctx context.Context, input string) (*Run, error) {
157-
if r.state != Creating && r.state != Continue {
158-
return nil, fmt.Errorf("run must be in creating or continue state not %q", r.state)
157+
if r.state != Creating && r.state != Continue && r.state != Error {
158+
return nil, fmt.Errorf("run must be in creating, continue, or error state not %q", r.state)
159159
}
160160

161161
run := &Run{
162162
url: r.url,
163163
requestPath: r.requestPath,
164164
state: Creating,
165-
chatState: r.chatState,
166165
toolPath: r.toolPath,
167166
content: r.content,
168167
opts: r.opts,
169168
}
169+
170170
run.opts.Input = input
171-
if run.chatState != "" {
172-
run.opts.ChatState = run.chatState
171+
if r.chatState != "" && r.state != Error {
172+
// If the previous run errored, then don't update the chat state.
173+
// opts.ChatState will be the last chat state where an error did not occur.
174+
run.opts.ChatState = r.chatState
173175
}
174176

175177
var payload any
@@ -350,10 +352,15 @@ func (r *Run) request(ctx context.Context, payload any) (err error) {
350352
r.parentCallFrameID = event.Call.ID
351353
}
352354
r.callsLock.Unlock()
353-
} else if event.Run != nil && event.Run.Type == EventTypeRunStart {
354-
r.callsLock.Lock()
355-
r.program = &event.Run.Program
356-
r.callsLock.Unlock()
355+
} else if event.Run != nil {
356+
if event.Run.Type == EventTypeRunStart {
357+
r.callsLock.Lock()
358+
r.program = &event.Run.Program
359+
r.callsLock.Unlock()
360+
} else if event.Run.Type == EventTypeRunFinish && event.Run.Error != "" {
361+
r.state = Error
362+
r.err = fmt.Errorf(event.Run.Error)
363+
}
357364
}
358365

359366
if r.opts.IncludeEvents {

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+
"runtime"
6+
"testing"
7+
)
8+
9+
func TestRestartingErrorRun(t *testing.T) {
10+
instructions := "#!/bin/bash\nexit ${EXIT_CODE}"
11+
if runtime.GOOS == "windows" {
12+
instructions = "#!/usr/bin/env powershell.exe\n\n$e = $env:EXIT_CODE;\nif ($e) { Exit 1; }"
13+
}
14+
tool := &ToolDef{
15+
Context: []string{"my-context"},
16+
Instructions: "Say hello",
17+
}
18+
contextTool := &ToolDef{
19+
Name: "my-context",
20+
Instructions: instructions,
21+
}
22+
23+
run, err := c.Evaluate(context.Background(), Options{Env: []string{"EXIT_CODE=1"}, IncludeEvents: true}, 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 = nil
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)