diff --git a/chat.go b/chat.go index 5d8b77b..9ce93f2 100644 --- a/chat.go +++ b/chat.go @@ -357,11 +357,13 @@ func RunCompletion(ctx context.Context, response *Stream, request *openrouter.Ch defer stream.Close() var ( - id string - result strings.Builder - tool *ToolCall + id string + tool *ToolCall ) + buf := GetFreeBuffer() + defer pool.Put(buf) + for { chunk, err := stream.Recv() if err != nil { @@ -409,7 +411,7 @@ func RunCompletion(ctx context.Context, response *Stream, request *openrouter.Ch content := choice.Delta.Content if content != "" { - result.WriteString(content) + buf.WriteString(content) response.Send(TextChunk(content)) } else if choice.Delta.Reasoning != nil { @@ -417,7 +419,7 @@ func RunCompletion(ctx context.Context, response *Stream, request *openrouter.Ch } } - return tool, result.String(), nil + return tool, buf.String(), nil } func SplitImagePairs(text string) []openrouter.ChatMessagePart { diff --git a/exa.go b/exa.go index a95789d..aeaa7fc 100644 --- a/exa.go +++ b/exa.go @@ -6,7 +6,6 @@ import ( "encoding/json" "fmt" "net/http" - "strings" "time" ) @@ -32,13 +31,14 @@ type ExaResults struct { } func (e *ExaResults) String() string { - var builder strings.Builder + buf := GetFreeBuffer() + defer pool.Put(buf) - json.NewEncoder(&builder).Encode(map[string]any{ + json.NewEncoder(buf).Encode(map[string]any{ "results": e.Results, }) - return builder.String() + return buf.String() } func NewExaRequest(ctx context.Context, path string, data any) (*http.Request, error) { diff --git a/github.go b/github.go index eb10db2..b7473e6 100644 --- a/github.go +++ b/github.go @@ -220,35 +220,36 @@ func RepoOverview(ctx context.Context, arguments GitHubRepositoryArguments) (str // wait and combine results wg.Wait() - var builder strings.Builder + buf := GetFreeBuffer() + defer pool.Put(buf) - fmt.Fprintf(&builder, "### %s (%s)\n", repository.Name, repository.Visibility) - fmt.Fprintf(&builder, "- URL: %s\n", repository.HtmlURL) - fmt.Fprintf(&builder, "- Description: %s\n", strings.ReplaceAll(repository.Description, "\n", " ")) - fmt.Fprintf(&builder, "- Default branch: %s\n", repository.DefaultBranch) - fmt.Fprintf(&builder, "- Stars: %d | Forks: %d\n", repository.Stargazers, repository.Forks) + fmt.Fprintf(buf, "### %s (%s)\n", repository.Name, repository.Visibility) + fmt.Fprintf(buf, "- URL: %s\n", repository.HtmlURL) + fmt.Fprintf(buf, "- Description: %s\n", strings.ReplaceAll(repository.Description, "\n", " ")) + fmt.Fprintf(buf, "- Default branch: %s\n", repository.DefaultBranch) + fmt.Fprintf(buf, "- Stars: %d | Forks: %d\n", repository.Stargazers, repository.Forks) - builder.WriteString("\n### Top-level files and directories\n") + buf.WriteString("\n### Top-level files and directories\n") if len(directories) == 0 && len(files) == 0 { - builder.WriteString("*No entries or insufficient permissions.*\n") + buf.WriteString("*No entries or insufficient permissions.*\n") } else { for _, directory := range directories { - fmt.Fprintf(&builder, "- [D] %s\n", directory) + fmt.Fprintf(buf, "- [D] %s\n", directory) } for _, file := range files { - fmt.Fprintf(&builder, "- [F] %s\n", file) + fmt.Fprintf(buf, "- [F] %s\n", file) } } - builder.WriteString("\n### README\n") + buf.WriteString("\n### README\n") if readmeMarkdown == "" { - builder.WriteString("*No README found or could not load.*\n") + buf.WriteString("*No README found or could not load.*\n") } else { - builder.WriteString(readmeMarkdown) + buf.WriteString(readmeMarkdown) } - return builder.String(), nil + return buf.String(), nil } diff --git a/prompts.go b/prompts.go index e39722c..9a0cda1 100644 --- a/prompts.go +++ b/prompts.go @@ -134,9 +134,10 @@ func BuildPrompt(name string, metadata Metadata, model *Model) (string, error) { metadata.Platform = "Unknown" } - var buf bytes.Buffer + buf := GetFreeBuffer() + defer pool.Put(buf) - err := tmpl.Execute(&buf, PromptData{ + err := tmpl.Execute(buf, PromptData{ Name: model.Name, Slug: model.ID, Date: time.Now().In(tz).Format(time.RFC1123), diff --git a/search.go b/search.go index 6b198eb..523f004 100644 --- a/search.go +++ b/search.go @@ -210,12 +210,18 @@ func ParseAndUpdateArgs(tool *ToolCall, arguments any) error { return err } - b, err := json.Marshal(arguments) + buf := GetFreeBuffer() + defer pool.Put(buf) + + enc := json.NewEncoder(buf) + enc.SetEscapeHTML(false) + + err = enc.Encode(arguments) if err != nil { return err } - tool.Args = string(b) + tool.Args = buf.String() return nil } diff --git a/stream.go b/stream.go index 493fe0c..c83f723 100644 --- a/stream.go +++ b/stream.go @@ -27,6 +27,14 @@ var pool = sync.Pool{ }, } +func GetFreeBuffer() *bytes.Buffer { + buf := pool.Get().(*bytes.Buffer) + + buf.Reset() + + return buf +} + func NewStream(w http.ResponseWriter, ctx context.Context) (*Stream, error) { w.Header().Set("Content-Type", "text/event-stream") w.Header().Set("Cache-Control", "no-cache") @@ -104,10 +112,7 @@ func WriteChunk(w http.ResponseWriter, ctx context.Context, chunk any) error { return err } - buf := pool.Get().(*bytes.Buffer) - - buf.Reset() - + buf := GetFreeBuffer() defer pool.Put(buf) if err := json.NewEncoder(buf).Encode(chunk); err != nil { diff --git a/title.go b/title.go index 6e41ebd..198ebe6 100644 --- a/title.go +++ b/title.go @@ -82,9 +82,10 @@ func HandleTitle(w http.ResponseWriter, r *http.Request) { return } - var prompt strings.Builder + buf := GetFreeBuffer() + defer pool.Put(buf) - if err := InternalTitleTmpl.Execute(&prompt, raw); err != nil { + if err := InternalTitleTmpl.Execute(buf, raw); err != nil { RespondJson(w, http.StatusInternalServerError, map[string]any{ "error": err.Error(), }) @@ -95,7 +96,7 @@ func HandleTitle(w http.ResponseWriter, r *http.Request) { request := openrouter.ChatCompletionRequest{ Model: env.Settings.TitleModel, Messages: []openrouter.ChatCompletionMessage{ - openrouter.SystemMessage(prompt.String()), + openrouter.SystemMessage(buf.String()), openrouter.UserMessage(strings.Join(messages, "\n")), }, Temperature: 0.25,