Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add server URL and token options #60

Merged
merged 1 commit into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 43 additions & 14 deletions gptscript.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"fmt"
"io"
"log/slog"
"net/url"
"os"
"os/exec"
"path/filepath"
Expand All @@ -28,7 +29,6 @@ var (
const relativeToBinaryPath = "<me>"

type GPTScript struct {
url string
globalOpts GlobalOptions
}

Expand All @@ -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 {
Expand All @@ -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
Expand Down Expand Up @@ -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) {
Expand All @@ -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()
Expand All @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
4 changes: 4 additions & 0 deletions opts.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand Down Expand Up @@ -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)
Expand Down
34 changes: 22 additions & 12 deletions run.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
}
}

Expand Down Expand Up @@ -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
Expand Down