diff --git a/go.mod b/go.mod index f3b7eb22..f903e27e 100644 --- a/go.mod +++ b/go.mod @@ -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-20240614062633-985091711b0a + 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 diff --git a/go.sum b/go.sum index bbaa5899..1ab5607d 100644 --- a/go.sum +++ b/go.sum @@ -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-20240614062633-985091711b0a h1:LFsEDiIAx0Rq0V6aOMlRjXMMIfkA3uEhqqqjoggLlDQ= -github.com/gptscript-ai/tui v0.0.0-20240614062633-985091711b0a/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= diff --git a/pkg/builtin/builtin.go b/pkg/builtin/builtin.go index a4aeb74e..989f523c 100644 --- a/pkg/builtin/builtin.go +++ b/pkg/builtin/builtin.go @@ -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) } @@ -640,11 +632,11 @@ func SysChatFinish(_ context.Context, _ []string, input string) (string, error) Message string `json:"return,omitempty"` } if err := json.Unmarshal([]byte(input), ¶ms); err != nil { - return "", &ErrChatFinish{ + return "", &engine.ErrChatFinish{ Message: input, } } - return "", &ErrChatFinish{ + return "", &engine.ErrChatFinish{ Message: params.Message, } } diff --git a/pkg/engine/cmd.go b/pkg/engine/cmd.go index 48b2d2b8..cb88c1d6 100644 --- a/pkg/engine/cmd.go +++ b/pkg/engine/cmd.go @@ -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() @@ -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 { @@ -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) { @@ -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 } diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index c94b236a..2dd17b1f 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -21,6 +21,7 @@ type Model interface { type RuntimeManager interface { GetContext(ctx context.Context, tool types.Tool, cmd, env []string) (string, []string, error) + EnsureCredentialHelpers(ctx context.Context) error SetUpCredentialHelpers(ctx context.Context, cliCfg *config.CLIConfig, env []string) error } @@ -105,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 "" diff --git a/pkg/input/input.go b/pkg/input/input.go index 80bb49ee..0037fa5e 100644 --- a/pkg/input/input.go +++ b/pkg/input/input.go @@ -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 { @@ -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 { diff --git a/pkg/monitor/display.go b/pkg/monitor/display.go index 36c7a35d..167498c7 100644 --- a/pkg/monitor/display.go +++ b/pkg/monitor/display.go @@ -20,25 +20,22 @@ import ( ) type Options struct { - DisplayProgress bool `usage:"-"` - DumpState string `usage:"Dump the internal execution state to a file"` - DebugMessages bool `usage:"Enable logging of chat completion calls"` + DumpState string `usage:"Dump the internal execution state to a file"` + DebugMessages bool `usage:"Enable logging of chat completion calls"` } func Complete(opts ...Options) (result Options) { for _, opt := range opts { result.DumpState = types.FirstSet(opt.DumpState, result.DumpState) - result.DisplayProgress = types.FirstSet(opt.DisplayProgress, result.DisplayProgress) result.DebugMessages = types.FirstSet(opt.DebugMessages, result.DebugMessages) } return } type Console struct { - dumpState string - displayProgress bool - printMessages bool - callLock sync.Mutex + dumpState string + printMessages bool + callLock sync.Mutex } var ( @@ -47,7 +44,7 @@ var ( func (c *Console) Start(_ context.Context, prg *types.Program, _ []string, input string) (runner.Monitor, error) { id := counter.Next() - mon := newDisplay(c.dumpState, c.displayProgress, c.printMessages) + mon := newDisplay(c.dumpState, c.printMessages) mon.callLock = &c.callLock mon.dump.ID = fmt.Sprint(id) mon.dump.Program = prg @@ -315,23 +312,20 @@ func (d *display) Stop(output string, err error) { func NewConsole(opts ...Options) *Console { opt := Complete(opts...) return &Console{ - dumpState: opt.DumpState, - displayProgress: opt.DisplayProgress, - printMessages: opt.DebugMessages, + dumpState: opt.DumpState, + printMessages: opt.DebugMessages, } } -func newDisplay(dumpState string, progress, printMessages bool) *display { +func newDisplay(dumpState string, printMessages bool) *display { display := &display{ dumpState: dumpState, callIDMap: make(map[string]string), printMessages: printMessages, } - if progress { - display.livePrinter = &livePrinter{ - lastContent: map[string]string{}, - callIDMap: display.callIDMap, - } + display.livePrinter = &livePrinter{ + lastContent: map[string]string{}, + callIDMap: display.callIDMap, } return display } diff --git a/pkg/mvl/log.go b/pkg/mvl/log.go index 6aa7c491..015fa199 100644 --- a/pkg/mvl/log.go +++ b/pkg/mvl/log.go @@ -1,6 +1,7 @@ package mvl import ( + "context" "encoding/json" "fmt" "io" @@ -156,6 +157,25 @@ func (l *Logger) Fields(kv ...any) *Logger { } } +type InfoLogger interface { + Infof(msg string, args ...any) +} + +type infoKey struct{} + +func WithInfo(ctx context.Context, logger InfoLogger) context.Context { + return context.WithValue(ctx, infoKey{}, logger) +} + +func (l *Logger) InfofCtx(ctx context.Context, msg string, args ...any) { + il, ok := ctx.Value(infoKey{}).(InfoLogger) + if ok { + il.Infof(msg, args...) + return + } + l.log.WithFields(l.fields).Infof(msg, args...) +} + func (l *Logger) Infof(msg string, args ...any) { l.log.WithFields(l.fields).Infof(msg, args...) } diff --git a/pkg/openai/client.go b/pkg/openai/client.go index 27a6317c..8ed4014b 100644 --- a/pkg/openai/client.go +++ b/pkg/openai/client.go @@ -437,6 +437,8 @@ func appendMessage(msg types.CompletionMessage, response openai.ChatCompletionSt if tc.ToolCall.Function.Name != tool.Function.Name { tc.ToolCall.Function.Name += tool.Function.Name } + // OpenAI like to sometimes add this prefix for no good reason + tc.ToolCall.Function.Name = strings.TrimPrefix(tc.ToolCall.Function.Name, "namespace.") tc.ToolCall.Function.Arguments += tool.Function.Arguments msg.Content[idx] = tc diff --git a/pkg/repos/get.go b/pkg/repos/get.go index 291ec038..68b2e427 100644 --- a/pkg/repos/get.go +++ b/pkg/repos/get.go @@ -9,6 +9,7 @@ import ( "os" "path/filepath" "strings" + "sync" "github.com/BurntSushi/locker" "github.com/gptscript-ai/gptscript/pkg/config" @@ -43,11 +44,19 @@ func (n noopRuntime) Setup(_ context.Context, _, _ string, _ []string) ([]string } type Manager struct { - storageDir string - gitDir string - runtimeDir string - credHelperDirs credentials.CredentialHelperDirs - runtimes []Runtime + storageDir string + gitDir string + runtimeDir string + credHelperDirs credentials.CredentialHelperDirs + runtimes []Runtime + credHelperConfig *credHelperConfig +} + +type credHelperConfig struct { + lock sync.Mutex + initialized bool + cliCfg *config.CLIConfig + env []string } func New(cacheDir string, runtimes ...Runtime) *Manager { @@ -61,7 +70,32 @@ func New(cacheDir string, runtimes ...Runtime) *Manager { } } -func (m *Manager) SetUpCredentialHelpers(ctx context.Context, cliCfg *config.CLIConfig, env []string) error { +func (m *Manager) EnsureCredentialHelpers(ctx context.Context) error { + if m.credHelperConfig == nil { + return nil + } + m.credHelperConfig.lock.Lock() + defer m.credHelperConfig.lock.Unlock() + + if !m.credHelperConfig.initialized { + if err := m.deferredSetUpCredentialHelpers(ctx, m.credHelperConfig.cliCfg, m.credHelperConfig.env); err != nil { + return err + } + m.credHelperConfig.initialized = true + } + + return nil +} + +func (m *Manager) SetUpCredentialHelpers(_ context.Context, cliCfg *config.CLIConfig, env []string) error { + m.credHelperConfig = &credHelperConfig{ + cliCfg: cliCfg, + env: env, + } + return nil +} + +func (m *Manager) deferredSetUpCredentialHelpers(ctx context.Context, cliCfg *config.CLIConfig, env []string) error { var ( helperName = cliCfg.CredentialsStore suffix string diff --git a/pkg/repos/git/git.go b/pkg/repos/git/git.go index bf0f1341..978f3a6d 100644 --- a/pkg/repos/git/git.go +++ b/pkg/repos/git/git.go @@ -33,7 +33,7 @@ func Checkout(ctx context.Context, base, repo, commit, toDir string) error { return err } - log.Infof("Checking out %s to %s", commit, toDir) + log.InfofCtx(ctx, "Checking out %s to %s", commit, toDir) return gitWorktreeAdd(ctx, gitDir(base, repo), toDir, commit) } @@ -46,11 +46,11 @@ func Fetch(ctx context.Context, base, repo, commit string) error { if found, err := exists(gitDir); err != nil { return err } else if !found { - log.Infof("Cloning %s", repo) + log.InfofCtx(ctx, "Cloning %s", repo) if err := cloneBare(ctx, repo, gitDir); err != nil { return err } } - log.Infof("Fetching %s at %s", commit, repo) + log.InfofCtx(ctx, "Fetching %s at %s", commit, repo) return fetchCommit(ctx, gitDir, commit) } diff --git a/pkg/repos/runtimes/golang/golang.go b/pkg/repos/runtimes/golang/golang.go index acf66ab1..28300439 100644 --- a/pkg/repos/runtimes/golang/golang.go +++ b/pkg/repos/runtimes/golang/golang.go @@ -68,7 +68,7 @@ func (r *Runtime) BuildCredentialHelper(ctx context.Context, helperName string, } newEnv := runtimeEnv.AppendPath(env, binPath) - log.Infof("Building credential helper %s", helperName) + log.InfofCtx(ctx, "Building credential helper %s", helperName) cmd := debugcmd.New(ctx, filepath.Join(binPath, "go"), "build", "-buildvcs=false", "-o", filepath.Join(credHelperDirs.BinDir, "gptscript-credential-"+helperName+suffix), @@ -103,7 +103,7 @@ func stripGo(env []string) (result []string) { } func (r *Runtime) runBuild(ctx context.Context, toolSource, binDir string, env []string) error { - log.Infof("Running go build in %s", toolSource) + log.InfofCtx(ctx, "Running go build in %s", toolSource) cmd := debugcmd.New(ctx, filepath.Join(binDir, "go"), "build", "-buildvcs=false", "-o", artifactName()) cmd.Env = stripGo(env) cmd.Dir = toolSource @@ -134,7 +134,7 @@ func (r *Runtime) getRuntime(ctx context.Context, cwd string) (string, error) { return "", err } - log.Infof("Downloading Go %s", r.Version) + log.InfofCtx(ctx, "Downloading Go %s", r.Version) tmp := target + ".download" defer os.RemoveAll(tmp) diff --git a/pkg/repos/runtimes/node/node.go b/pkg/repos/runtimes/node/node.go index 9280d3b0..575e3b23 100644 --- a/pkg/repos/runtimes/node/node.go +++ b/pkg/repos/runtimes/node/node.go @@ -101,7 +101,7 @@ func (r *Runtime) getReleaseAndDigest() (string, string, error) { } func (r *Runtime) runNPM(ctx context.Context, toolSource, binDir string, env []string) error { - log.Infof("Running npm in %s", toolSource) + log.InfofCtx(ctx, "Running npm in %s", toolSource) cmd := debugcmd.New(ctx, filepath.Join(binDir, "npm"), "install") cmd.Env = env cmd.Dir = toolSource @@ -141,7 +141,7 @@ func (r *Runtime) getRuntime(ctx context.Context, cwd string) (string, error) { return "", err } - log.Infof("Downloading Node %s.x", r.Version) + log.InfofCtx(ctx, "Downloading Node %s.x", r.Version) tmp := target + ".download" defer os.RemoveAll(tmp) diff --git a/pkg/repos/runtimes/python/python.go b/pkg/repos/runtimes/python/python.go index 72de5457..a5268f31 100644 --- a/pkg/repos/runtimes/python/python.go +++ b/pkg/repos/runtimes/python/python.go @@ -76,7 +76,7 @@ func uvBin(binDir string) string { } func (r *Runtime) installVenv(ctx context.Context, binDir, venvPath string) error { - log.Infof("Creating virtualenv in %s", venvPath) + log.InfofCtx(ctx, "Creating virtualenv in %s", venvPath) cmd := debugcmd.New(ctx, uvBin(binDir), "venv", "-p", pythonCmd(binDir), venvPath) return cmd.Run() } @@ -171,7 +171,7 @@ func (r *Runtime) getReleaseAndDigest() (string, string, error) { } func (r *Runtime) runPip(ctx context.Context, toolSource, binDir string, env []string) error { - log.Infof("Running pip in %s", toolSource) + log.InfofCtx(ctx, "Running pip in %s", toolSource) for _, req := range []string{"requirements-gptscript.txt", "requirements.txt"} { reqFile := filepath.Join(toolSource, req) if s, err := os.Stat(reqFile); err == nil && !s.IsDir() { @@ -203,7 +203,7 @@ func (r *Runtime) getRuntime(ctx context.Context, cwd string) (string, error) { return "", err } - log.Infof("Downloading Python %s.x", r.Version) + log.InfofCtx(ctx, "Downloading Python %s.x", r.Version) tmp := target + ".download" defer os.RemoveAll(tmp) diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index 06ccf941..fbb4622c 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -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: @@ -421,7 +431,7 @@ func (r *Runner) start(callCtx engine.Context, state *State, monitor Monitor, en e := engine.Engine{ Model: r.c, - RuntimeManager: r.runtimeManager, + RuntimeManager: runtimeWithLogger(callCtx, monitor, r.runtimeManager), Progress: progress, Env: env, } @@ -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 @@ -602,7 +612,7 @@ func (r *Runner) resume(callCtx engine.Context, monitor Monitor, env []string, s e := engine.Engine{ Model: r.c, - RuntimeManager: r.runtimeManager, + RuntimeManager: runtimeWithLogger(callCtx, monitor, r.runtimeManager), Progress: progress, Env: env, } @@ -836,6 +846,11 @@ func (r *Runner) handleCredentials(callCtx engine.Context, monitor Monitor, env exists bool ) + rm := runtimeWithLogger(callCtx, monitor, r.runtimeManager) + if err := rm.EnsureCredentialHelpers(callCtx.Ctx); err != nil { + return nil, fmt.Errorf("failed to setup credential helpers: %w", err) + } + // Only try to look up the cred if the tool is on GitHub or has an alias. // If it is a GitHub tool and has an alias, the alias overrides the tool name, so we use it as the credential name. if isGitHubTool(toolName) && credentialAlias == "" { diff --git a/pkg/runner/runtimemanager.go b/pkg/runner/runtimemanager.go new file mode 100644 index 00000000..e1c5a4c6 --- /dev/null +++ b/pkg/runner/runtimemanager.go @@ -0,0 +1,50 @@ +package runner + +import ( + "context" + "fmt" + "time" + + "github.com/gptscript-ai/gptscript/pkg/config" + "github.com/gptscript-ai/gptscript/pkg/engine" + "github.com/gptscript-ai/gptscript/pkg/mvl" + "github.com/gptscript-ai/gptscript/pkg/types" +) + +func runtimeWithLogger(callCtx engine.Context, monitor Monitor, rm engine.RuntimeManager) engine.RuntimeManager { + if rm == nil { + return nil + } + return runtimeManagerLogger{ + callCtx: callCtx, + monitor: monitor, + rm: rm, + } +} + +type runtimeManagerLogger struct { + callCtx engine.Context + monitor Monitor + rm engine.RuntimeManager +} + +func (r runtimeManagerLogger) Infof(msg string, args ...any) { + r.monitor.Event(Event{ + Time: time.Now(), + Type: EventTypeCallProgress, + CallContext: r.callCtx.GetCallContext(), + Content: fmt.Sprintf(msg, args...), + }) +} + +func (r runtimeManagerLogger) GetContext(ctx context.Context, tool types.Tool, cmd, env []string) (string, []string, error) { + return r.rm.GetContext(mvl.WithInfo(ctx, r), tool, cmd, env) +} + +func (r runtimeManagerLogger) EnsureCredentialHelpers(ctx context.Context) error { + return r.rm.EnsureCredentialHelpers(mvl.WithInfo(ctx, r)) +} + +func (r runtimeManagerLogger) SetUpCredentialHelpers(_ context.Context, _ *config.CLIConfig, _ []string) error { + panic("not implemented") +} diff --git a/pkg/system/prompt.go b/pkg/system/prompt.go index 72858c93..98b2535a 100644 --- a/pkg/system/prompt.go +++ b/pkg/system/prompt.go @@ -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() { diff --git a/pkg/tests/runner_test.go b/pkg/tests/runner_test.go index 9470662c..fb698a0e 100644 --- a/pkg/tests/runner_test.go +++ b/pkg/tests/runner_test.go @@ -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" + } } } ], @@ -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" + } } } ], diff --git a/pkg/tests/testdata/TestAgents/call1.golden b/pkg/tests/testdata/TestAgents/call1.golden index a4c9d565..c0465ac7 100644 --- a/pkg/tests/testdata/TestAgents/call1.golden +++ b/pkg/tests/testdata/TestAgents/call1.golden @@ -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" + } } } ], diff --git a/pkg/tests/testdata/TestAgents/call2.golden b/pkg/tests/testdata/TestAgents/call2.golden index b7f53512..a4b53537 100644 --- a/pkg/tests/testdata/TestAgents/call2.golden +++ b/pkg/tests/testdata/TestAgents/call2.golden @@ -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" + } } } ], diff --git a/pkg/tests/testdata/TestAgents/call3.golden b/pkg/tests/testdata/TestAgents/call3.golden index 5b6b3b87..4a001215 100644 --- a/pkg/tests/testdata/TestAgents/call3.golden +++ b/pkg/tests/testdata/TestAgents/call3.golden @@ -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" + } } } ], diff --git a/pkg/tests/testdata/TestAgents/step1.golden b/pkg/tests/testdata/TestAgents/step1.golden index 59f11e22..423069b5 100644 --- a/pkg/tests/testdata/TestAgents/step1.golden +++ b/pkg/tests/testdata/TestAgents/step1.golden @@ -14,14 +14,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" + } } } ], @@ -92,7 +108,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" + } } } ], @@ -154,14 +178,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" + } } } ], diff --git a/pkg/tests/testdata/TestAsterick/call1.golden b/pkg/tests/testdata/TestAsterick/call1.golden index c530c3ba..d6d56df9 100644 --- a/pkg/tests/testdata/TestAsterick/call1.golden +++ b/pkg/tests/testdata/TestAsterick/call1.golden @@ -7,14 +7,11 @@ "name": "afoo", "parameters": { "properties": { - "defaultPromptParameter": { - "description": "Prompt to send to the tool or assistant. This may be instructions or question.", + "prompt": { + "description": "Prompt to send to the tool. This may be an instruction or question.", "type": "string" } }, - "required": [ - "defaultPromptParameter" - ], "type": "object" } } @@ -25,14 +22,11 @@ "name": "a", "parameters": { "properties": { - "defaultPromptParameter": { - "description": "Prompt to send to the tool or assistant. This may be instructions or question.", + "prompt": { + "description": "Prompt to send to the tool. This may be an instruction or question.", "type": "string" } }, - "required": [ - "defaultPromptParameter" - ], "type": "object" } } diff --git a/pkg/tests/testdata/TestContextSubChat/call1.golden b/pkg/tests/testdata/TestContextSubChat/call1.golden index f976d1c8..09da7a95 100644 --- a/pkg/tests/testdata/TestContextSubChat/call1.golden +++ b/pkg/tests/testdata/TestContextSubChat/call1.golden @@ -5,7 +5,15 @@ "function": { "toolID": "testdata/TestContextSubChat/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" + } } } ], diff --git a/pkg/tests/testdata/TestContextSubChat/call4.golden b/pkg/tests/testdata/TestContextSubChat/call4.golden index 4fb1d2bb..abe24599 100644 --- a/pkg/tests/testdata/TestContextSubChat/call4.golden +++ b/pkg/tests/testdata/TestContextSubChat/call4.golden @@ -5,7 +5,15 @@ "function": { "toolID": "testdata/TestContextSubChat/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" + } } } ], diff --git a/pkg/tests/testdata/TestContextSubChat/call6.golden b/pkg/tests/testdata/TestContextSubChat/call6.golden index f976d1c8..09da7a95 100644 --- a/pkg/tests/testdata/TestContextSubChat/call6.golden +++ b/pkg/tests/testdata/TestContextSubChat/call6.golden @@ -5,7 +5,15 @@ "function": { "toolID": "testdata/TestContextSubChat/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" + } } } ], diff --git a/pkg/tests/testdata/TestContextSubChat/call9.golden b/pkg/tests/testdata/TestContextSubChat/call9.golden index 0cb1f56a..37f67e56 100644 --- a/pkg/tests/testdata/TestContextSubChat/call9.golden +++ b/pkg/tests/testdata/TestContextSubChat/call9.golden @@ -5,7 +5,15 @@ "function": { "toolID": "testdata/TestContextSubChat/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" + } } } ], diff --git a/pkg/tests/testdata/TestContextSubChat/step1.golden b/pkg/tests/testdata/TestContextSubChat/step1.golden index 1a675b28..464efb36 100644 --- a/pkg/tests/testdata/TestContextSubChat/step1.golden +++ b/pkg/tests/testdata/TestContextSubChat/step1.golden @@ -13,7 +13,15 @@ "function": { "toolID": "testdata/TestContextSubChat/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" + } } } ], diff --git a/pkg/tests/testdata/TestContextSubChat/step3.golden b/pkg/tests/testdata/TestContextSubChat/step3.golden index d57035d5..3c365c54 100644 --- a/pkg/tests/testdata/TestContextSubChat/step3.golden +++ b/pkg/tests/testdata/TestContextSubChat/step3.golden @@ -54,7 +54,15 @@ "function": { "toolID": "testdata/TestContextSubChat/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" + } } } ], diff --git a/pkg/tests/testdata/TestDualSubChat/call1.golden b/pkg/tests/testdata/TestDualSubChat/call1.golden index a0c21ccc..267690fe 100644 --- a/pkg/tests/testdata/TestDualSubChat/call1.golden +++ b/pkg/tests/testdata/TestDualSubChat/call1.golden @@ -5,14 +5,30 @@ "function": { "toolID": "testdata/TestDualSubChat/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" + } } }, { "function": { "toolID": "testdata/TestDualSubChat/test.gpt:chatbot2", "name": "chatbot2", - "parameters": null + "parameters": { + "properties": { + "prompt": { + "description": "Prompt to send to the assistant. This may be an instruction or question.", + "type": "string" + } + }, + "type": "object" + } } } ], diff --git a/pkg/tests/testdata/TestDualSubChat/call7.golden b/pkg/tests/testdata/TestDualSubChat/call7.golden index 60842c68..5f1d227a 100644 --- a/pkg/tests/testdata/TestDualSubChat/call7.golden +++ b/pkg/tests/testdata/TestDualSubChat/call7.golden @@ -5,14 +5,30 @@ "function": { "toolID": "testdata/TestDualSubChat/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" + } } }, { "function": { "toolID": "testdata/TestDualSubChat/test.gpt:chatbot2", "name": "chatbot2", - "parameters": null + "parameters": { + "properties": { + "prompt": { + "description": "Prompt to send to the assistant. This may be an instruction or question.", + "type": "string" + } + }, + "type": "object" + } } } ], diff --git a/pkg/tests/testdata/TestDualSubChat/step1.golden b/pkg/tests/testdata/TestDualSubChat/step1.golden index 421bb069..46932939 100644 --- a/pkg/tests/testdata/TestDualSubChat/step1.golden +++ b/pkg/tests/testdata/TestDualSubChat/step1.golden @@ -13,14 +13,30 @@ "function": { "toolID": "testdata/TestDualSubChat/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" + } } }, { "function": { "toolID": "testdata/TestDualSubChat/test.gpt:chatbot2", "name": "chatbot2", - "parameters": null + "parameters": { + "properties": { + "prompt": { + "description": "Prompt to send to the assistant. This may be an instruction or question.", + "type": "string" + } + }, + "type": "object" + } } } ], diff --git a/pkg/tests/testdata/TestDualSubChat/step2.golden b/pkg/tests/testdata/TestDualSubChat/step2.golden index 82f1a7cd..2e1511c7 100644 --- a/pkg/tests/testdata/TestDualSubChat/step2.golden +++ b/pkg/tests/testdata/TestDualSubChat/step2.golden @@ -13,14 +13,30 @@ "function": { "toolID": "testdata/TestDualSubChat/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" + } } }, { "function": { "toolID": "testdata/TestDualSubChat/test.gpt:chatbot2", "name": "chatbot2", - "parameters": null + "parameters": { + "properties": { + "prompt": { + "description": "Prompt to send to the assistant. This may be an instruction or question.", + "type": "string" + } + }, + "type": "object" + } } } ], diff --git a/pkg/tests/testdata/TestDualSubChat/step3.golden b/pkg/tests/testdata/TestDualSubChat/step3.golden index ab2dc7c3..d21249c3 100644 --- a/pkg/tests/testdata/TestDualSubChat/step3.golden +++ b/pkg/tests/testdata/TestDualSubChat/step3.golden @@ -13,14 +13,30 @@ "function": { "toolID": "testdata/TestDualSubChat/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" + } } }, { "function": { "toolID": "testdata/TestDualSubChat/test.gpt:chatbot2", "name": "chatbot2", - "parameters": null + "parameters": { + "properties": { + "prompt": { + "description": "Prompt to send to the assistant. This may be an instruction or question.", + "type": "string" + } + }, + "type": "object" + } } } ], diff --git a/pkg/tests/testdata/TestExport/call1.golden b/pkg/tests/testdata/TestExport/call1.golden index bc35465f..8b360d8a 100644 --- a/pkg/tests/testdata/TestExport/call1.golden +++ b/pkg/tests/testdata/TestExport/call1.golden @@ -7,14 +7,11 @@ "name": "frommain", "parameters": { "properties": { - "defaultPromptParameter": { - "description": "Prompt to send to the tool or assistant. This may be instructions or question.", + "prompt": { + "description": "Prompt to send to the tool. This may be an instruction or question.", "type": "string" } }, - "required": [ - "defaultPromptParameter" - ], "type": "object" } } @@ -25,14 +22,11 @@ "name": "parentLocal", "parameters": { "properties": { - "defaultPromptParameter": { - "description": "Prompt to send to the tool or assistant. This may be instructions or question.", + "prompt": { + "description": "Prompt to send to the tool. This may be an instruction or question.", "type": "string" } }, - "required": [ - "defaultPromptParameter" - ], "type": "object" } } @@ -43,14 +37,11 @@ "name": "transient", "parameters": { "properties": { - "defaultPromptParameter": { - "description": "Prompt to send to the tool or assistant. This may be instructions or question.", + "prompt": { + "description": "Prompt to send to the tool. This may be an instruction or question.", "type": "string" } }, - "required": [ - "defaultPromptParameter" - ], "type": "object" } } diff --git a/pkg/tests/testdata/TestExport/call3.golden b/pkg/tests/testdata/TestExport/call3.golden index 3b76c8af..a653821a 100644 --- a/pkg/tests/testdata/TestExport/call3.golden +++ b/pkg/tests/testdata/TestExport/call3.golden @@ -7,14 +7,11 @@ "name": "frommain", "parameters": { "properties": { - "defaultPromptParameter": { - "description": "Prompt to send to the tool or assistant. This may be instructions or question.", + "prompt": { + "description": "Prompt to send to the tool. This may be an instruction or question.", "type": "string" } }, - "required": [ - "defaultPromptParameter" - ], "type": "object" } } @@ -25,14 +22,11 @@ "name": "parentLocal", "parameters": { "properties": { - "defaultPromptParameter": { - "description": "Prompt to send to the tool or assistant. This may be instructions or question.", + "prompt": { + "description": "Prompt to send to the tool. This may be an instruction or question.", "type": "string" } }, - "required": [ - "defaultPromptParameter" - ], "type": "object" } } @@ -43,14 +37,11 @@ "name": "transient", "parameters": { "properties": { - "defaultPromptParameter": { - "description": "Prompt to send to the tool or assistant. This may be instructions or question.", + "prompt": { + "description": "Prompt to send to the tool. This may be an instruction or question.", "type": "string" } }, - "required": [ - "defaultPromptParameter" - ], "type": "object" } } diff --git a/pkg/tests/testdata/TestExportContext/call1.golden b/pkg/tests/testdata/TestExportContext/call1.golden index 5476a745..2e1d2421 100644 --- a/pkg/tests/testdata/TestExportContext/call1.golden +++ b/pkg/tests/testdata/TestExportContext/call1.golden @@ -7,14 +7,11 @@ "name": "subtool", "parameters": { "properties": { - "defaultPromptParameter": { - "description": "Prompt to send to the tool or assistant. This may be instructions or question.", + "prompt": { + "description": "Prompt to send to the tool. This may be an instruction or question.", "type": "string" } }, - "required": [ - "defaultPromptParameter" - ], "type": "object" } } diff --git a/pkg/tests/testdata/TestSubChat/call1.golden b/pkg/tests/testdata/TestSubChat/call1.golden index dc161663..53104a4e 100644 --- a/pkg/tests/testdata/TestSubChat/call1.golden +++ b/pkg/tests/testdata/TestSubChat/call1.golden @@ -5,7 +5,15 @@ "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" + } } } ], diff --git a/pkg/tests/testdata/TestToolAs/call1.golden b/pkg/tests/testdata/TestToolAs/call1.golden index e7ec18b5..519f07b8 100644 --- a/pkg/tests/testdata/TestToolAs/call1.golden +++ b/pkg/tests/testdata/TestToolAs/call1.golden @@ -7,14 +7,11 @@ "name": "local", "parameters": { "properties": { - "defaultPromptParameter": { - "description": "Prompt to send to the tool or assistant. This may be instructions or question.", + "prompt": { + "description": "Prompt to send to the tool. This may be an instruction or question.", "type": "string" } }, - "required": [ - "defaultPromptParameter" - ], "type": "object" } } @@ -25,14 +22,11 @@ "name": "remote", "parameters": { "properties": { - "defaultPromptParameter": { - "description": "Prompt to send to the tool or assistant. This may be instructions or question.", + "prompt": { + "description": "Prompt to send to the tool. This may be an instruction or question.", "type": "string" } }, - "required": [ - "defaultPromptParameter" - ], "type": "object" } } diff --git a/pkg/types/tool.go b/pkg/types/tool.go index fe137e6a..6c016d82 100644 --- a/pkg/types/tool.go +++ b/pkg/types/tool.go @@ -601,6 +601,8 @@ func toolRefsToCompletionTools(completionTools []ToolReference, prg Program) (re args := subTool.Parameters.Arguments if args == nil && !subTool.IsCommand() && !subTool.Chat { args = &system.DefaultToolSchema + } else if args == nil && !subTool.IsCommand() { + args = &system.DefaultChatSchema } if subTool.Instructions == "" {