Skip to content

Commit

Permalink
chore: allow nested gptscript to run sys.chat.finish
Browse files Browse the repository at this point in the history
  • Loading branch information
ibuildthecloud committed Jun 18, 2024
1 parent 7181c20 commit 0fd1a4d
Show file tree
Hide file tree
Showing 34 changed files with 368 additions and 108 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ require (
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/google/uuid v1.6.0
github.com/gptscript-ai/chat-completion-client v0.0.0-20240531200700-af8e7ecf0379
github.com/gptscript-ai/tui v0.0.0-20240618175050-a1d627a00cff
github.com/gptscript-ai/tui v0.0.0-20240618230843-2b5961f3341b
github.com/hexops/autogold/v2 v2.2.1
github.com/hexops/valast v1.4.4
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,8 @@ github.com/gptscript-ai/chat-completion-client v0.0.0-20240531200700-af8e7ecf037
github.com/gptscript-ai/chat-completion-client v0.0.0-20240531200700-af8e7ecf0379/go.mod h1:7P/o6/IWa1KqsntVf68hSnLKuu3+xuqm6lYhch1w4jo=
github.com/gptscript-ai/go-gptscript v0.0.0-20240613214812-8111c2b02d71 h1:WehkkausLuXI91ePpIVrzZ6eBmfFIU/HfNsSA1CHiwo=
github.com/gptscript-ai/go-gptscript v0.0.0-20240613214812-8111c2b02d71/go.mod h1:Dh6vYRAiVcyC3ElZIGzTvNF1FxtYwA07BHfSiFKQY7s=
github.com/gptscript-ai/tui v0.0.0-20240618175050-a1d627a00cff h1:mjcUKZ4hHVpT8EkyIsxGpU608BTNko0EovpsY0fGfvU=
github.com/gptscript-ai/tui v0.0.0-20240618175050-a1d627a00cff/go.mod h1:ZlyM+BRiD6mV04w+Xw2mXP1VKGEUbn8BvwrosWlplUo=
github.com/gptscript-ai/tui v0.0.0-20240618230843-2b5961f3341b h1:OJfmpDQ/6ffz5P4UdJJEd5xeqo2dfWnsg1YZLDqJWYo=
github.com/gptscript-ai/tui v0.0.0-20240618230843-2b5961f3341b/go.mod h1:ZlyM+BRiD6mV04w+Xw2mXP1VKGEUbn8BvwrosWlplUo=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
Expand Down
12 changes: 2 additions & 10 deletions pkg/builtin/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -593,14 +593,6 @@ func SysGetenv(_ context.Context, env []string, input string) (string, error) {
return value, nil
}

type ErrChatFinish struct {
Message string
}

func (e *ErrChatFinish) Error() string {
return fmt.Sprintf("CHAT FINISH: %s", e.Message)
}

func invalidArgument(input string, err error) string {
return fmt.Sprintf("Failed to parse arguments %s: %v", input, err)
}
Expand Down Expand Up @@ -640,11 +632,11 @@ func SysChatFinish(_ context.Context, _ []string, input string) (string, error)
Message string `json:"return,omitempty"`
}
if err := json.Unmarshal([]byte(input), &params); err != nil {
return "", &ErrChatFinish{
return "", &engine.ErrChatFinish{
Message: input,
}
}
return "", &ErrChatFinish{
return "", &engine.ErrChatFinish{
Message: params.Message,
}
}
Expand Down
27 changes: 24 additions & 3 deletions pkg/engine/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,24 @@ var requiredFileExtensions = map[string]string{
"powershell": "*.ps1",
}

type outputWriter struct {
id string
progress chan<- types.CompletionStatus
buf bytes.Buffer
}

func (o *outputWriter) Write(p []byte) (n int, err error) {
o.buf.Write(p)
o.progress <- types.CompletionStatus{
CompletionID: o.id,
PartialResponse: &types.CompletionMessage{
Role: types.CompletionMessageRoleTypeAssistant,
Content: types.Text(o.buf.String()),
},
}
return len(p), nil
}

func (e *Engine) runCommand(ctx Context, tool types.Tool, input string, toolCategory ToolCategory) (cmdOut string, cmdErr error) {
id := counter.Next()

Expand Down Expand Up @@ -74,7 +92,10 @@ func (e *Engine) runCommand(ctx Context, tool types.Tool, input string, toolCate
output := &bytes.Buffer{}
all := &bytes.Buffer{}
cmd.Stderr = io.MultiWriter(all, os.Stderr)
cmd.Stdout = io.MultiWriter(all, output)
cmd.Stdout = io.MultiWriter(all, output, &outputWriter{
id: id,
progress: e.Progress,
})

if err := cmd.Run(); err != nil {
if toolCategory == NoCategory {
Expand All @@ -85,7 +106,7 @@ func (e *Engine) runCommand(ctx Context, tool types.Tool, input string, toolCate
return "", fmt.Errorf("ERROR: %s: %w", all, err)
}

return output.String(), nil
return output.String(), IsChatFinishMessage(output.String())
}

func (e *Engine) getRuntimeEnv(ctx context.Context, tool types.Tool, cmd, env []string) ([]string, error) {
Expand Down Expand Up @@ -161,7 +182,7 @@ func appendInputAsEnv(env []string, input string) []string {
}
}

env = appendEnv(env, "GPTSCRIPT_INPUT", input)
env = appendEnv(env, "GPTSCRIPT_INPUT_CONTENT", input)
return env
}

Expand Down
15 changes: 15 additions & 0 deletions pkg/engine/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,21 @@ type InputContext struct {
Content string `json:"content,omitempty"`
}

type ErrChatFinish struct {
Message string
}

func (e *ErrChatFinish) Error() string {
return fmt.Sprintf("CHAT FINISH: %s", e.Message)
}

func IsChatFinishMessage(msg string) error {
if msg, ok := strings.CutPrefix(msg, "CHAT FINISH: "); ok {
return &ErrChatFinish{Message: msg}
}
return nil
}

func (c *Context) ParentID() string {
if c.Parent == nil {
return ""
Expand Down
10 changes: 10 additions & 0 deletions pkg/input/input.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import (
"fmt"
"io"
"os"
"path/filepath"
"strings"

"github.com/gptscript-ai/gptscript/pkg/loader"
"github.com/gptscript-ai/gptscript/pkg/types"
)

func FromArgs(args []string) string {
Expand All @@ -31,6 +33,14 @@ func FromFile(file string) (string, error) {
}
return string(data), nil
} else if file != "" {
if s, err := os.Stat(file); err == nil && s.IsDir() {
for _, ext := range types.DefaultFiles {
if _, err := os.Stat(filepath.Join(file, ext)); err == nil {
file = filepath.Join(file, ext)
break
}
}
}
log.Debugf("reading file %s", file)
data, err := os.ReadFile(file)
if err != nil {
Expand Down
12 changes: 11 additions & 1 deletion pkg/runner/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,16 @@ type ChatState interface{}
func (r *Runner) Chat(ctx context.Context, prevState ChatState, prg types.Program, env []string, input string) (resp ChatResponse, err error) {
var state *State

defer func() {
if finish := (*engine.ErrChatFinish)(nil); errors.As(err, &finish) {
resp = ChatResponse{
Done: true,
Content: err.Error(),
}
err = nil
}
}()

if prevState != nil {
switch v := prevState.(type) {
case *State:
Expand Down Expand Up @@ -568,7 +578,7 @@ func (r *Runner) resume(callCtx engine.Context, monitor Monitor, env []string, s
)

state, callResults, err = r.subCalls(callCtx, monitor, env, state, callCtx.ToolCategory)
if errMessage := (*builtin.ErrChatFinish)(nil); errors.As(err, &errMessage) && callCtx.Tool.Chat {
if errMessage := (*engine.ErrChatFinish)(nil); errors.As(err, &errMessage) && callCtx.Tool.Chat {
return &State{
Result: &errMessage.Message,
}, nil
Expand Down
17 changes: 14 additions & 3 deletions pkg/system/prompt.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,30 @@ You don't move to the next step until you have a result.

// DefaultPromptParameter is used as the key in a json map to indication that we really wanted
// to just send pure text but the interface required JSON (as that is the fundamental interface of tools in OpenAI)
var DefaultPromptParameter = "defaultPromptParameter"
var DefaultPromptParameter = "prompt"

var DefaultToolSchema = openapi3.Schema{
Type: &openapi3.Types{"object"},
Properties: openapi3.Schemas{
DefaultPromptParameter: &openapi3.SchemaRef{
Value: &openapi3.Schema{
Description: "Prompt to send to the tool or assistant. This may be instructions or question.",
Description: "Prompt to send to the tool. This may be an instruction or question.",
Type: &openapi3.Types{"string"},
},
},
},
}

var DefaultChatSchema = openapi3.Schema{
Type: &openapi3.Types{"object"},
Properties: openapi3.Schemas{
DefaultPromptParameter: &openapi3.SchemaRef{
Value: &openapi3.Schema{
Description: "Prompt to send to the assistant. This may be an instruction or question.",
Type: &openapi3.Types{"string"},
},
},
},
Required: []string{DefaultPromptParameter},
}

func init() {
Expand Down
20 changes: 18 additions & 2 deletions pkg/tests/runner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,15 @@ func TestSubChat(t *testing.T) {
"function": {
"toolID": "testdata/TestSubChat/test.gpt:chatbot",
"name": "chatbot",
"parameters": null
"parameters": {
"properties": {
"prompt": {
"description": "Prompt to send to the assistant. This may be an instruction or question.",
"type": "string"
}
},
"type": "object"
}
}
}
],
Expand Down Expand Up @@ -435,7 +443,15 @@ func TestSubChat(t *testing.T) {
"function": {
"toolID": "testdata/TestSubChat/test.gpt:chatbot",
"name": "chatbot",
"parameters": null
"parameters": {
"properties": {
"prompt": {
"description": "Prompt to send to the assistant. This may be an instruction or question.",
"type": "string"
}
},
"type": "object"
}
}
}
],
Expand Down
20 changes: 18 additions & 2 deletions pkg/tests/testdata/TestAgents/call1.golden
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,30 @@
"function": {
"toolID": "testdata/TestAgents/test.gpt:agent1",
"name": "agent1",
"parameters": null
"parameters": {
"properties": {
"prompt": {
"description": "Prompt to send to the assistant. This may be an instruction or question.",
"type": "string"
}
},
"type": "object"
}
}
},
{
"function": {
"toolID": "testdata/TestAgents/test.gpt:agent2",
"name": "agent2",
"parameters": null
"parameters": {
"properties": {
"prompt": {
"description": "Prompt to send to the assistant. This may be an instruction or question.",
"type": "string"
}
},
"type": "object"
}
}
}
],
Expand Down
10 changes: 9 additions & 1 deletion pkg/tests/testdata/TestAgents/call2.golden
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,15 @@
"function": {
"toolID": "testdata/TestAgents/test.gpt:agent2",
"name": "agent2",
"parameters": null
"parameters": {
"properties": {
"prompt": {
"description": "Prompt to send to the assistant. This may be an instruction or question.",
"type": "string"
}
},
"type": "object"
}
}
}
],
Expand Down
20 changes: 18 additions & 2 deletions pkg/tests/testdata/TestAgents/call3.golden
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,30 @@
"function": {
"toolID": "testdata/TestAgents/test.gpt:agent1",
"name": "agent1",
"parameters": null
"parameters": {
"properties": {
"prompt": {
"description": "Prompt to send to the assistant. This may be an instruction or question.",
"type": "string"
}
},
"type": "object"
}
}
},
{
"function": {
"toolID": "testdata/TestAgents/test.gpt:agent3",
"name": "agent3",
"parameters": null
"parameters": {
"properties": {
"prompt": {
"description": "Prompt to send to the assistant. This may be an instruction or question.",
"type": "string"
}
},
"type": "object"
}
}
}
],
Expand Down
Loading

0 comments on commit 0fd1a4d

Please sign in to comment.