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 4a52694a..20362360 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.60.1 + +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/examples/sh.gpt b/examples/sh.gpt new file mode 100644 index 00000000..7494cb1e --- /dev/null +++ b/examples/sh.gpt @@ -0,0 +1,26 @@ +name: main +tools: answer, extract_hashtag +Chat: True + +당신은 주어진 툴을 이용해서 사용자의 질문에 대답하는 agent입니다. +처음에는 "안녕"이라 말해주세요. + +--- +name: answer +description: 사용자의 질문에 대해서 답해줍니다. +args: question : 사용자의 질문 +args: hashtags : example) ["postgres", "mysql"]. 이 인자를 얻기위해서는 반드시 extract_hashtag tool을 사용한 결과로 해야합니다. +tools: extract_hashtag + +#!/usr/bin/env python + +print("melong") + +--- +name: extract_hashtag +description: 사용자의 질문에서 해시태그를 추출합니다. +args: question : 사용자의 질문 + +#!/usr/bin/env python + +print("hash1, hash2") diff --git a/go.mod b/go.mod index 83146eeb..386d7fc4 100644 --- a/go.mod +++ b/go.mod @@ -53,6 +53,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 @@ -62,6 +63,7 @@ require ( github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // 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/emirpasic/gods v1.18.1 // indirect diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index d028d50b..59766dcf 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" @@ -281,6 +284,39 @@ func populateMessageParams(ctx Context, completion *types.CompletionRequest, too return nil } +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 @@ -322,6 +358,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/loader/openapi.go b/pkg/loader/openapi.go index e62fc5ef..66adce31 100644 --- a/pkg/loader/openapi.go +++ b/pkg/loader/openapi.go @@ -298,17 +298,6 @@ func getOpenAPITools(t *openapi3.T, defaultHost, source, targetToolName string) return nil, err } - if len(infos) > 0 { - // Set up credential tools for the first set of infos. - for _, info := range infos[0] { - operationServerURL, err := url.Parse(operationServer) - if err != nil { - return nil, fmt.Errorf("failed to parse operation server URL: %w", err) - } - tool.Credentials = append(tool.Credentials, info.GetCredentialToolStrings(operationServerURL.Hostname())...) - } - } - // Register toolNames = append(toolNames, tool.Parameters.Name) tools = append(tools, tool) diff --git a/pkg/openai/client.go b/pkg/openai/client.go index 61a7ec77..5200ed7c 100644 --- a/pkg/openai/client.go +++ b/pkg/openai/client.go @@ -378,7 +378,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 c843b6b5..02c5224e 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" @@ -636,17 +638,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(), @@ -658,6 +672,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 d9d59837..b6e0b77f 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 = "#!" )