From 66dc3960908b0a09bc2979f1ea02e217fb8c0ef2 Mon Sep 17 00:00:00 2001 From: Donnie Adams Date: Tue, 27 Aug 2024 15:11:21 -0400 Subject: [PATCH] feat: add server URL and token options If the server URL has a path, then the SDK will implicitly disable the server since the local SDK server cannot have a path. Signed-off-by: Donnie Adams --- gptscript.go | 57 +++++++++++++++++++++++++++++++++++++++------------- opts.go | 4 ++++ run.go | 34 ++++++++++++++++++++----------- 3 files changed, 69 insertions(+), 26 deletions(-) diff --git a/gptscript.go b/gptscript.go index 1968144..c8b6e64 100644 --- a/gptscript.go +++ b/gptscript.go @@ -10,6 +10,7 @@ import ( "fmt" "io" "log/slog" + "net/url" "os" "os/exec" "path/filepath" @@ -28,7 +29,6 @@ var ( const relativeToBinaryPath = "" type GPTScript struct { - url string globalOpts GlobalOptions } @@ -38,10 +38,11 @@ func NewGPTScript(opts ...GlobalOptions) (*GPTScript, error) { defer lock.Unlock() gptscriptCount++ - disableServer := os.Getenv("GPTSCRIPT_DISABLE_SERVER") == "true" - if serverURL == "" { - serverURL = os.Getenv("GPTSCRIPT_URL") + serverURL = opt.URL + if serverURL == "" { + serverURL = os.Getenv("GPTSCRIPT_URL") + } } if opt.Env == nil { @@ -50,11 +51,31 @@ func NewGPTScript(opts ...GlobalOptions) (*GPTScript, error) { opt.Env = append(opt.Env, opt.toEnv()...) - if serverProcessCancel == nil && !disableServer { + if serverProcessCancel == nil && os.Getenv("GPTSCRIPT_DISABLE_SERVER") != "true" { + if serverURL != "" { + u, err := url.Parse(serverURL) + if err != nil { + return nil, fmt.Errorf("failed to parse server URL: %w", err) + } + + // If the server URL has a path, then this implies that the server is already running. + // In that case, we don't need to start the server. + if u.Path != "" && u.Path != "/" { + opt.URL = serverURL + if !strings.HasPrefix(opt.URL, "http://") && !strings.HasPrefix(opt.URL, "https://") { + opt.URL = "http://" + opt.URL + } + + return &GPTScript{ + globalOpts: opt, + }, nil + } + } + ctx, cancel := context.WithCancel(context.Background()) in, _ := io.Pipe() - serverProcess = exec.CommandContext(ctx, getCommand(), "sys.sdkserver", "--listen-address", serverURL) + serverProcess = exec.CommandContext(ctx, getCommand(), "sys.sdkserver", "--listen-address", strings.TrimPrefix(serverURL, "http://")) serverProcess.Env = opt.Env[:] serverProcess.Stdin = in @@ -95,12 +116,14 @@ func NewGPTScript(opts ...GlobalOptions) (*GPTScript, error) { serverURL = strings.TrimSpace(serverURL) } - g := &GPTScript{ - url: "http://" + serverURL, - globalOpts: opt, - } - return g, nil + opt.URL = serverURL + if !strings.HasPrefix(opt.URL, "http://") && !strings.HasPrefix(opt.URL, "https://") { + opt.URL = "http://" + opt.URL + } + return &GPTScript{ + globalOpts: opt, + }, nil } func readAddress(stdErr io.Reader) (string, error) { @@ -117,6 +140,10 @@ func readAddress(stdErr io.Reader) (string, error) { return addr, nil } +func (g *GPTScript) URL() string { + return g.globalOpts.URL +} + func (g *GPTScript) Close() { lock.Lock() defer lock.Unlock() @@ -131,7 +158,8 @@ func (g *GPTScript) Close() { func (g *GPTScript) Evaluate(ctx context.Context, opts Options, tools ...ToolDef) (*Run, error) { opts.GlobalOptions = completeGlobalOptions(g.globalOpts, opts.GlobalOptions) return (&Run{ - url: g.url, + url: opts.URL, + token: opts.Token, requestPath: "evaluate", state: Creating, opts: opts, @@ -142,7 +170,8 @@ func (g *GPTScript) Evaluate(ctx context.Context, opts Options, tools ...ToolDef func (g *GPTScript) Run(ctx context.Context, toolPath string, opts Options) (*Run, error) { opts.GlobalOptions = completeGlobalOptions(g.globalOpts, opts.GlobalOptions) return (&Run{ - url: g.url, + url: opts.URL, + token: opts.Token, requestPath: "run", state: Creating, opts: opts, @@ -309,7 +338,7 @@ func (g *GPTScript) PromptResponse(ctx context.Context, resp PromptResponse) err func (g *GPTScript) runBasicCommand(ctx context.Context, requestPath string, body any) (string, error) { run := &Run{ - url: g.url, + url: g.globalOpts.URL, requestPath: requestPath, state: Creating, basicCommand: true, diff --git a/opts.go b/opts.go index 191a8d0..125a16f 100644 --- a/opts.go +++ b/opts.go @@ -3,6 +3,8 @@ package gptscript // GlobalOptions allows specification of settings that are used for every call made. // These options can be overridden by the corresponding Options. type GlobalOptions struct { + URL string `json:"url"` + Token string `json:"token"` OpenAIAPIKey string `json:"APIKey"` OpenAIBaseURL string `json:"BaseURL"` DefaultModel string `json:"DefaultModel"` @@ -33,6 +35,8 @@ func completeGlobalOptions(opts ...GlobalOptions) GlobalOptions { var result GlobalOptions for _, opt := range opts { result.CacheDir = firstSet(opt.CacheDir, result.CacheDir) + result.URL = firstSet(opt.URL, result.URL) + result.Token = firstSet(opt.Token, result.Token) result.OpenAIAPIKey = firstSet(opt.OpenAIAPIKey, result.OpenAIAPIKey) result.OpenAIBaseURL = firstSet(opt.OpenAIBaseURL, result.OpenAIBaseURL) result.DefaultModel = firstSet(opt.DefaultModel, result.DefaultModel) diff --git a/run.go b/run.go index 5b716aa..ae5a4a0 100644 --- a/run.go +++ b/run.go @@ -18,15 +18,15 @@ import ( var errAbortRun = errors.New("run aborted") type Run struct { - url, requestPath, toolPath string - tools []ToolDef - opts Options - state RunState - chatState string - cancel context.CancelCauseFunc - err error - wait func() - basicCommand bool + url, token, 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 @@ -175,18 +175,24 @@ func (r *Run) NextChat(ctx context.Context, input string) (*Run, error) { run.opts.ChatState = r.chatState } - var payload any + var ( + payload any + options = run.opts + ) + // Remove the url and token because they shouldn't be sent with the payload. + options.URL = "" + options.Token = "" if len(r.tools) != 0 { payload = requestPayload{ ToolDefs: r.tools, Input: input, - Options: run.opts, + Options: options, } } else if run.toolPath != "" { payload = requestPayload{ File: run.toolPath, Input: input, - Options: run.opts, + Options: options, } } @@ -228,6 +234,10 @@ func (r *Run) request(ctx context.Context, payload any) (err error) { return r.err } + if r.opts.Token != "" { + req.Header.Set("Authorization", "Bearer "+r.opts.Token) + } + resp, err := http.DefaultClient.Do(req) if err != nil { r.state = Error