diff --git a/.gitignore b/.gitignore index 759e3286..707d130f 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,6 @@ /binaries /checksums.txt /.env* +ui/ +gptscript.code-workspace +examples/ diff --git a/Makefile b/Makefile index e0d93a1d..3bb7e7e4 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,12 @@ all: build build-exe: GOOS=windows go build -o bin/gptscript.exe -tags "${GO_TAGS}" . +build-linux-amd64: + GOOS=linux GOARCH=amd64 go build -o bin/gptscript_linux_amd64 -tags "${GO_TAGS}" . + +build-linux-arm64: + GOOS=linux GOARCH=arm64 go build -o bin/gptscript_linux_arm64 -tags "${GO_TAGS}" . + build: CGO_ENABLED=0 go build -o bin/gptscript -tags "${GO_TAGS}" -ldflags "-s -w" . @@ -23,6 +29,12 @@ smoke: go test -v -tags='smoke' ./pkg/tests/smoke/... GOLANGCI_LINT_VERSION ?= v1.59.0 + +cp: build-linux-amd64 build-linux-arm64 + cp bin/gptscript_linux_amd64 ~/Workspace/streamlit-gptscript/gptscript/bin/gptscript + cp bin/gptscript_linux_amd64 ~/Workspace/streamlit-gptscript/gptscript/bin/gptscript_linux_amd64 + cp bin/gptscript_linux_arm64 ~/Workspace/streamlit-gptscript/gptscript/bin/gptscript_linux_arm64 + lint: if ! command -v golangci-lint &> /dev/null; then \ echo "Could not find golangci-lint, installing version $(GOLANGCI_LINT_VERSION)."; \ diff --git a/go.mod b/go.mod index f0213f7e..f5f2ce9a 100644 --- a/go.mod +++ b/go.mod @@ -48,6 +48,7 @@ require ( github.com/bodgit/plumbing v1.2.0 // indirect github.com/bodgit/sevenzip v1.3.0 // indirect github.com/bodgit/windows v1.0.0 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/charmbracelet/glamour v0.7.0 // indirect github.com/charmbracelet/lipgloss v0.11.0 // indirect github.com/charmbracelet/x/ansi v0.1.1 // indirect @@ -55,6 +56,7 @@ require ( github.com/containerd/console v1.0.4 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dlclark/regexp2 v1.4.0 // indirect github.com/dsnet/compress v0.0.1 // indirect github.com/go-openapi/jsonpointer v0.20.2 // indirect diff --git a/pkg/engine/control.go b/pkg/engine/control.go new file mode 100644 index 00000000..7bb45c0a --- /dev/null +++ b/pkg/engine/control.go @@ -0,0 +1,20 @@ +package engine + +import ( + "encoding/json" + "fmt" + + "github.com/gptscript-ai/gptscript/pkg/types" +) + +func (e *Engine) runBreak(tool types.Tool, input string) (cmdOut *Return, cmdErr error) { + info, err := json.Marshal(tool) + if err != nil { + return nil, err + } + var dict map[string]interface{} + json.Unmarshal(info, &dict) + dict["input"] = input + info, err = json.Marshal(dict) + return nil, fmt.Errorf("TOOL_BREAK: %s", info) +} diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index 250e9578..a62db5cd 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -1,9 +1,12 @@ package engine import ( + "bufio" "context" "encoding/json" "fmt" + "log/slog" + "os" "strings" "sync" @@ -256,6 +259,39 @@ func (c *Context) WrappedContext() context.Context { return context.WithValue(c.Ctx, engineContext{}, c) } +func putHistory(messages []types.CompletionMessage) []types.CompletionMessage { + prevHistoryFile := strings.TrimSpace(os.Getenv("GPTSCRIPT_PREVIOUS_HISTORY_FILE")) + + if prevHistoryFile == "" { + return messages + } + fp, err := os.Open(prevHistoryFile) + if err != nil { + slog.Error("Open Error", err) + return messages + } + defer fp.Close() + + scanner := bufio.NewScanner(fp) + + prevMessages := []types.CompletionMessage{} + for scanner.Scan() { + var message types.CompletionMessage + line := scanner.Text() + err := json.Unmarshal([]byte(line), &message) + if err != nil { + slog.Error("Unmarshal Error", err) + return messages + } + if message.Role == "system" { + continue + } + prevMessages = append(prevMessages, message) + } + + return append(messages, prevMessages...) +} + func (e *Engine) Start(ctx Context, input string) (ret *Return, _ error) { tool := ctx.Tool @@ -274,6 +310,8 @@ func (e *Engine) Start(ctx Context, input string) (ret *Return, _ error) { return e.runOpenAPI(tool, input) } else if tool.IsEcho() { return e.runEcho(tool) + } else if tool.IsBreak() { + return e.runBreak(tool, input) } s, err := e.runCommand(ctx, tool, input, ctx.ToolCategory) if err != nil { @@ -314,6 +352,10 @@ func (e *Engine) Start(ctx Context, input string) (ret *Return, _ error) { input = "" } + if ctx.Parent == nil { + completion.Messages = putHistory(completion.Messages) + } + if input != "" { completion.Messages = append(completion.Messages, types.CompletionMessage{ Role: types.CompletionMessageRoleTypeUser, diff --git a/pkg/openai/client.go b/pkg/openai/client.go index 53252895..d82b8e1e 100644 --- a/pkg/openai/client.go +++ b/pkg/openai/client.go @@ -361,7 +361,7 @@ func (c *Client) Call(ctx context.Context, messageRequest types.CompletionReques id := counter.Next() status <- types.CompletionStatus{ CompletionID: id, - Request: request, + Request: messageRequest.Messages, } var cacheResponse bool diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index cc5a3927..610673c7 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -5,7 +5,9 @@ import ( "encoding/json" "errors" "fmt" + "os" "sort" + "strconv" "strings" "sync" "time" @@ -672,17 +674,29 @@ func streamProgress(callCtx *engine.Context, monitor Monitor) (chan<- types.Comp wg := sync.WaitGroup{} wg.Add(1) + progressTimeStepMs, err := strconv.Atoi(os.Getenv("GPTSCRIPT_PROGRESS_TIME_STEP_MS")) + if err != nil { + // 기본값 250ms를 사용하거나 오류를 처리합니다. + progressTimeStepMs = 250 + } + progressTimeStep := time.Duration(progressTimeStepMs) * time.Millisecond go func() { defer wg.Done() + lastSentTimeMap := make(map[string]time.Time) for status := range progress { if message := status.PartialResponse; message != nil { - monitor.Event(Event{ - Time: time.Now(), - CallContext: callCtx.GetCallContext(), - Type: EventTypeCallProgress, - ChatCompletionID: status.CompletionID, - Content: getEventContent(message.String(), *callCtx), - }) + now := time.Now() + lastSentTime, ok := lastSentTimeMap[status.CompletionID] + if !ok || now.Sub(lastSentTime) > progressTimeStep { + lastSentTimeMap[status.CompletionID] = now + monitor.Event(Event{ + Time: time.Now(), + CallContext: callCtx.GetCallContext(), + Type: EventTypeCallProgress, + ChatCompletionID: status.CompletionID, + Content: getEventContent(message.String(), *callCtx), + }) + } } else { monitor.Event(Event{ Time: time.Now(), @@ -694,6 +708,7 @@ func streamProgress(callCtx *engine.Context, monitor Monitor) (chan<- types.Comp Usage: status.Usage, ChatResponseCached: status.Cached, }) + delete(lastSentTimeMap, status.CompletionID) } } }() diff --git a/pkg/types/tool.go b/pkg/types/tool.go index e4b3424a..de5d368c 100644 --- a/pkg/types/tool.go +++ b/pkg/types/tool.go @@ -19,6 +19,7 @@ const ( DaemonPrefix = "#!sys.daemon" OpenAPIPrefix = "#!sys.openapi" EchoPrefix = "#!sys.echo" + BreakPrefix = "#!sys.break" CommandPrefix = "#!" ) @@ -771,6 +772,10 @@ func (t Tool) IsEcho() bool { return strings.HasPrefix(t.Instructions, EchoPrefix) } +func (t Tool) IsBreak() bool { + return strings.HasPrefix(t.Instructions, BreakPrefix) +} + func (t Tool) IsHTTP() bool { return strings.HasPrefix(t.Instructions, "#!http://") || strings.HasPrefix(t.Instructions, "#!https://")