diff --git a/frame.go b/frame.go index 4350f29..01756dc 100644 --- a/frame.go +++ b/frame.go @@ -84,13 +84,14 @@ type Call struct { } type CallContext struct { - ID string `json:"id"` - Tool Tool `json:"tool"` - DisplayText string `json:"displayText"` - InputContext []InputContext `json:"inputContext"` - ToolCategory ToolCategory `json:"toolCategory,omitempty"` - ToolName string `json:"toolName,omitempty"` - ParentID string `json:"parentID,omitempty"` + ID string `json:"id"` + Tool Tool `json:"tool"` + AgentGroup []ToolReference `json:"agentGroup,omitempty"` + DisplayText string `json:"displayText"` + InputContext []InputContext `json:"inputContext"` + ToolCategory ToolCategory `json:"toolCategory,omitempty"` + ToolName string `json:"toolName,omitempty"` + ParentID string `json:"parentID,omitempty"` } type InputContext struct { diff --git a/gptscript.go b/gptscript.go index 9c8f118..23e703c 100644 --- a/gptscript.go +++ b/gptscript.go @@ -28,7 +28,7 @@ const relativeToBinaryPath = "" type GPTScript interface { Run(context.Context, string, Options) (*Run, error) - Evaluate(context.Context, Options, ...fmt.Stringer) (*Run, error) + Evaluate(context.Context, Options, ...ToolDef) (*Run, error) Parse(context.Context, string) ([]Node, error) ParseTool(context.Context, string) ([]Node, error) Version(context.Context) (string, error) @@ -124,13 +124,13 @@ func (g *gptscript) Close() { } } -func (g *gptscript) Evaluate(ctx context.Context, opts Options, tools ...fmt.Stringer) (*Run, error) { +func (g *gptscript) Evaluate(ctx context.Context, opts Options, tools ...ToolDef) (*Run, error) { return (&Run{ url: g.url, requestPath: "evaluate", state: Creating, opts: opts, - content: concatTools(tools), + tools: tools, }).NextChat(ctx, opts.Input) } diff --git a/gptscript_test.go b/gptscript_test.go index 65e7c6c..423d6b0 100644 --- a/gptscript_test.go +++ b/gptscript_test.go @@ -81,7 +81,7 @@ func TestListModels(t *testing.T) { } func TestAbortRun(t *testing.T) { - tool := &ToolDef{Instructions: "What is the capital of the united states?"} + tool := ToolDef{Instructions: "What is the capital of the united states?"} run, err := g.Evaluate(context.Background(), Options{DisableCache: true, IncludeEvents: true}, tool) if err != nil { @@ -105,7 +105,7 @@ func TestAbortRun(t *testing.T) { } func TestSimpleEvaluate(t *testing.T) { - tool := &ToolDef{Instructions: "What is the capital of the united states?"} + tool := ToolDef{Instructions: "What is the capital of the united states?"} run, err := g.Evaluate(context.Background(), Options{}, tool) if err != nil { @@ -142,7 +142,7 @@ func TestEvaluateWithContext(t *testing.T) { t.Fatalf("Error getting current working directory: %v", err) } - tool := &ToolDef{ + tool := ToolDef{ Instructions: "What is the capital of the united states?", Context: []string{ wd + "/test/acorn-labs-context.gpt", @@ -165,7 +165,7 @@ func TestEvaluateWithContext(t *testing.T) { } func TestEvaluateComplexTool(t *testing.T) { - tool := &ToolDef{ + tool := ToolDef{ JSONResponse: true, Instructions: ` Create three short graphic artist descriptions and their muses. @@ -202,18 +202,16 @@ func TestEvaluateWithToolList(t *testing.T) { if runtime.GOOS == "windows" { shebang = "#!/usr/bin/env powershell.exe" } - tools := []fmt.Stringer{ - &ToolDef{ + tools := []ToolDef{ + { Tools: []string{"echo"}, Instructions: "echo hello there", }, - &ToolDef{ - Name: "echo", - Tools: []string{"sys.exec"}, - Description: "Echoes the input", - Args: map[string]string{ - "input": "The string input to echo", - }, + { + Name: "echo", + Tools: []string{"sys.exec"}, + Description: "Echoes the input", + Arguments: ObjectSchema("input", "The string input to echo"), Instructions: shebang + "\n echo ${input}", }, } @@ -238,23 +236,21 @@ func TestEvaluateWithToolListAndSubTool(t *testing.T) { if runtime.GOOS == "windows" { shebang = "#!/usr/bin/env powershell.exe" } - tools := []fmt.Stringer{ - &ToolDef{ + tools := []ToolDef{ + { Tools: []string{"echo"}, Instructions: "echo 'hello there'", }, - &ToolDef{ + { Name: "other", Tools: []string{"echo"}, Instructions: "echo 'hello somewhere else'", }, - &ToolDef{ - Name: "echo", - Tools: []string{"sys.exec"}, - Description: "Echoes the input", - Args: map[string]string{ - "input": "The string input to echo", - }, + { + Name: "echo", + Tools: []string{"sys.exec"}, + Description: "Echoes the input", + Arguments: ObjectSchema("input", "The string input to echo"), Instructions: shebang + "\n echo ${input}", }, } @@ -276,7 +272,7 @@ func TestEvaluateWithToolListAndSubTool(t *testing.T) { func TestStreamEvaluate(t *testing.T) { var eventContent string - tool := &ToolDef{Instructions: "What is the capital of the united states?"} + tool := ToolDef{Instructions: "What is the capital of the united states?"} run, err := g.Evaluate(context.Background(), Options{IncludeEvents: true}, tool) if err != nil { @@ -536,7 +532,7 @@ echo hello there } func TestToolChat(t *testing.T) { - tool := &ToolDef{ + tool := ToolDef{ Chat: true, Instructions: "You are a chat bot. Don't finish the conversation until I say 'bye'.", Tools: []string{"sys.chat.finish"}, @@ -688,8 +684,8 @@ func TestToolWithGlobalTools(t *testing.T) { func TestConfirm(t *testing.T) { var eventContent string - tools := []fmt.Stringer{ - &ToolDef{ + tools := []ToolDef{ + { Instructions: "List the files in the current directory", Tools: []string{"sys.exec"}, }, @@ -759,8 +755,8 @@ func TestConfirm(t *testing.T) { func TestConfirmDeny(t *testing.T) { var eventContent string - tools := []fmt.Stringer{ - &ToolDef{ + tools := []ToolDef{ + { Instructions: "List the files in the current directory", Tools: []string{"sys.exec"}, }, @@ -831,8 +827,8 @@ func TestConfirmDeny(t *testing.T) { func TestPrompt(t *testing.T) { var eventContent string - tools := []fmt.Stringer{ - &ToolDef{ + tools := []ToolDef{ + { Instructions: "Use the sys.prompt user to ask the user for 'first name' which is not sensitive. After you get their first name, say hello.", Tools: []string{"sys.prompt"}, }, @@ -914,8 +910,8 @@ func TestPrompt(t *testing.T) { } func TestPromptWithoutPromptAllowed(t *testing.T) { - tools := []fmt.Stringer{ - &ToolDef{ + tools := []ToolDef{ + { Instructions: "Use the sys.prompt user to ask the user for 'first name' which is not sensitive. After you get their first name, say hello.", Tools: []string{"sys.prompt"}, }, diff --git a/run.go b/run.go index 8631603..4b4650f 100644 --- a/run.go +++ b/run.go @@ -18,14 +18,15 @@ import ( var errAbortRun = errors.New("run aborted") type Run struct { - url, requestPath, toolPath, content string - opts Options - state RunState - chatState string - cancel context.CancelCauseFunc - err error - wait func() - basicCommand bool + url, requestPath, toolPath string + tools []ToolDef + opts Options + state RunState + chatState string + cancel context.CancelCauseFunc + err error + wait func() + basicCommand bool program *Program callsLock sync.RWMutex @@ -163,7 +164,7 @@ func (r *Run) NextChat(ctx context.Context, input string) (*Run, error) { requestPath: r.requestPath, state: Creating, toolPath: r.toolPath, - content: r.content, + tools: r.tools, opts: r.opts, } @@ -175,11 +176,11 @@ func (r *Run) NextChat(ctx context.Context, input string) (*Run, error) { } var payload any - if r.content != "" { + if len(r.tools) != 0 { payload = requestPayload{ - Content: run.content, - Input: input, - Options: run.opts, + ToolDefs: r.tools, + Input: input, + Options: run.opts, } } else if run.toolPath != "" { payload = requestPayload{ @@ -412,8 +413,8 @@ const ( ) type requestPayload struct { - Content string `json:"content"` - File string `json:"file"` - Input string `json:"input"` - Options `json:",inline"` + Options `json:",inline"` + File string `json:"file"` + Input string `json:"input"` + ToolDefs []ToolDef `json:"toolDefs,inline"` } diff --git a/run_test.go b/run_test.go index 3ebc47c..041cc4e 100644 --- a/run_test.go +++ b/run_test.go @@ -11,11 +11,11 @@ func TestRestartingErrorRun(t *testing.T) { if runtime.GOOS == "windows" { instructions = "#!/usr/bin/env powershell.exe\n\n$e = $env:EXIT_CODE;\nif ($e) { Exit 1; }" } - tool := &ToolDef{ + tool := ToolDef{ Context: []string{"my-context"}, Instructions: "Say hello", } - contextTool := &ToolDef{ + contextTool := ToolDef{ Name: "my-context", Instructions: instructions, } diff --git a/tool.go b/tool.go index c51da93..526af9f 100644 --- a/tool.go +++ b/tool.go @@ -1,101 +1,49 @@ package gptscript import ( - "fmt" - "strings" - "github.com/getkin/kin-openapi/openapi3" ) // ToolDef struct represents a tool with various configurations. type ToolDef struct { - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` - MaxTokens int `json:"maxTokens,omitempty"` - ModelName string `json:"modelName,omitempty"` - ModelProvider bool `json:"modelProvider,omitempty"` - JSONResponse bool `json:"jsonResponse,omitempty"` - Chat bool `json:"chat,omitempty"` - Temperature *float32 `json:"temperature,omitempty"` - Cache *bool `json:"cache,omitempty"` - InternalPrompt *bool `json:"internalPrompt"` - Args map[string]string `json:"args,omitempty"` - Tools []string `json:"tools,omitempty"` - GlobalTools []string `json:"globalTools,omitempty"` - GlobalModelName string `json:"globalModelName,omitempty"` - Context []string `json:"context,omitempty"` - ExportContext []string `json:"exportContext,omitempty"` - Export []string `json:"export,omitempty"` - Credentials []string `json:"credentials,omitempty"` - Instructions string `json:"instructions,omitempty"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + MaxTokens int `json:"maxTokens,omitempty"` + ModelName string `json:"modelName,omitempty"` + ModelProvider bool `json:"modelProvider,omitempty"` + JSONResponse bool `json:"jsonResponse,omitempty"` + Chat bool `json:"chat,omitempty"` + Temperature *float32 `json:"temperature,omitempty"` + Cache *bool `json:"cache,omitempty"` + InternalPrompt *bool `json:"internalPrompt"` + Arguments *openapi3.Schema `json:"arguments,omitempty"` + Tools []string `json:"tools,omitempty"` + GlobalTools []string `json:"globalTools,omitempty"` + GlobalModelName string `json:"globalModelName,omitempty"` + Context []string `json:"context,omitempty"` + ExportContext []string `json:"exportContext,omitempty"` + Export []string `json:"export,omitempty"` + Agents []string `json:"agents,omitempty"` + Credentials []string `json:"credentials,omitempty"` + Instructions string `json:"instructions,omitempty"` } -// String method returns the string representation of ToolDef. -func (t *ToolDef) String() string { - var sb strings.Builder - - if t.Name != "" { - sb.WriteString(fmt.Sprintf("Name: %s\n", t.Name)) - } - if t.Description != "" { - sb.WriteString(fmt.Sprintf("Description: %s\n", t.Description)) - } - if len(t.Tools) > 0 { - sb.WriteString(fmt.Sprintf("Tools: %s\n", strings.Join(t.Tools, ", "))) - } - if t.MaxTokens != 0 { - sb.WriteString(fmt.Sprintf("Max tokens: %d\n", t.MaxTokens)) - } - if t.ModelName != "" { - sb.WriteString(fmt.Sprintf("Model: %s\n", t.ModelName)) - } - if t.Cache != nil && !*t.Cache { - sb.WriteString("Cache: false\n") - } - if len(t.Context) > 0 { - sb.WriteString(fmt.Sprintf("Context: %s\n", strings.Join(t.Context, ", "))) - } - if len(t.ExportContext) > 0 { - sb.WriteString(fmt.Sprintf("Export Context: %s\n", strings.Join(t.ExportContext, ", "))) - } - if len(t.Export) > 0 { - sb.WriteString(fmt.Sprintf("Export: %s\n", strings.Join(t.Export, ", "))) - } - if len(t.GlobalTools) > 0 { - sb.WriteString(fmt.Sprintf("Global Tools: %s\n", strings.Join(t.GlobalTools, ", "))) - } - if t.Temperature != nil { - sb.WriteString(fmt.Sprintf("Temperature: %f\n", *t.Temperature)) - } - if t.JSONResponse { - sb.WriteString("JSON Response: true\n") - } - if len(t.Args) > 0 { - for arg, desc := range t.Args { - sb.WriteString(fmt.Sprintf("Args: %s: %s\n", arg, desc)) +func ObjectSchema(kv ...string) *openapi3.Schema { + s := &openapi3.Schema{ + Type: "object", + Properties: openapi3.Schemas{}, + } + for i, v := range kv { + if i%2 == 1 { + s.Properties[kv[i-1]] = &openapi3.SchemaRef{ + Value: &openapi3.Schema{ + Description: v, + Type: "string", + }, + } } } - if t.Chat { - sb.WriteString("Chat: true\n") - } - if t.InternalPrompt != nil && *t.InternalPrompt { - sb.WriteString("Internal prompt: true\n") - } - if t.Instructions != "" { - sb.WriteString(t.Instructions) - } - - return sb.String() -} - -type ToolDefs []ToolDef - -func (t ToolDefs) String() string { - resp := make([]string, 0, len(t)) - for _, tool := range t { - resp = append(resp, tool.String()) - } - return strings.Join(resp, "\n---\n") + return s } type Document struct { @@ -145,14 +93,3 @@ type Repo struct { Name string Revision string } - -func concatTools(tools []fmt.Stringer) string { - var sb strings.Builder - for i, tool := range tools { - sb.WriteString(tool.String()) - if i < len(tools)-1 { - sb.WriteString("\n---\n") - } - } - return sb.String() -}