From 01e2be112f4d434b5aaa58431a374542391bd795 Mon Sep 17 00:00:00 2001 From: Andrey Nering Date: Fri, 29 Aug 2025 14:09:23 -0300 Subject: [PATCH 1/8] ci: initial setup for build --- .github/workflows/build.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..3511f7fe0 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,11 @@ +name: build +on: [push, pull_request] + +jobs: + build: + uses: charmbracelet/meta/.github/workflows/build.yml@main + with: + go-version: "" + go-version-file: ./go.mod + secrets: + gh_pat: "${{ secrets.PERSONAL_ACCESS_TOKEN }}" From fd53e64d84fd72c144967e1c4e0c174a1ac5311f Mon Sep 17 00:00:00 2001 From: Andrey Nering Date: Fri, 29 Aug 2025 14:09:31 -0300 Subject: [PATCH 2/8] ci: initial setup for lint --- .github/workflows/lint.yml | 11 ++++++++++ .golangci.yml | 44 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 .github/workflows/lint.yml create mode 100644 .golangci.yml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 000000000..1ffad5f2f --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,11 @@ +name: lint +on: + push: + pull_request: + +jobs: + lint: + uses: charmbracelet/meta/.github/workflows/lint.yml@main + with: + golangci_path: .golangci.yml + timeout: 10m diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 000000000..929cb0ac9 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,44 @@ +version: "2" +run: + tests: false +linters: + enable: + - bodyclose + - exhaustive + - goconst + - godot + - gomoddirectives + - goprintffuncname + - gosec + - misspell + - nakedret + - nestif + - nilerr + - noctx + - nolintlint + - prealloc + - revive + - rowserrcheck + - sqlclosecheck + - tparallel + - unconvert + - unparam + - whitespace + - wrapcheck + exclusions: + rules: + - text: '(slog|log)\.\w+' + linters: + - noctx + generated: lax + presets: + - common-false-positives +issues: + max-issues-per-linter: 0 + max-same-issues: 0 +formatters: + enable: + - gofumpt + - goimports + exclusions: + generated: lax From 9b269d00b7441e49089e8098776de1ccee4df37a Mon Sep 17 00:00:00 2001 From: Andrey Nering Date: Fri, 29 Aug 2025 14:12:54 -0300 Subject: [PATCH 3/8] chore: add basic taskfile --- Taskfile.yaml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 Taskfile.yaml diff --git a/Taskfile.yaml b/Taskfile.yaml new file mode 100644 index 000000000..82864bf8e --- /dev/null +++ b/Taskfile.yaml @@ -0,0 +1,23 @@ +version: '3' + + +tasks: + test: + desc: Run tests + cmds: + - go test ./... + + lint: + desc: Run linters + cmds: + - golangci-lint run --config .golangci.yml --timeout 10m + + lint:fix: + desc: Fix lint issues + cmds: + - golangci-lint run --config .golangci.yml --fix + + fmt: + desc: Format code + cmds: + - gofumpt -w . From c76d7b4a88519ca88b889cec90f7ec67880d940d Mon Sep 17 00:00:00 2001 From: Andrey Nering Date: Fri, 29 Aug 2025 14:13:14 -0300 Subject: [PATCH 4/8] lint: fix auto-fixable issues --- agent.go | 6 +++--- content.go | 4 ++-- providers/anthropic.go | 6 +++--- providers/openai.go | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/agent.go b/agent.go index 54a83a563..7fcf9cb2a 100644 --- a/agent.go +++ b/agent.go @@ -868,7 +868,7 @@ func (a *agent) prepareTools(tools []AgentTool, activeTools []string, disableAll return preparedTools } -// validateAndRepairToolCall validates a tool call and attempts repair if validation fails +// validateAndRepairToolCall validates a tool call and attempts repair if validation fails. func (a *agent) validateAndRepairToolCall(ctx context.Context, toolCall ToolCallContent, availableTools []AgentTool, systemPrompt string, messages []Message, repairFunc RepairToolCallFunction) ToolCallContent { if err := a.validateToolCall(toolCall, availableTools); err == nil { return toolCall @@ -896,7 +896,7 @@ func (a *agent) validateAndRepairToolCall(ctx context.Context, toolCall ToolCall } } -// validateToolCall validates a tool call against available tools and their schemas +// validateToolCall validates a tool call against available tools and their schemas. func (a *agent) validateToolCall(toolCall ToolCallContent, availableTools []AgentTool) error { var tool AgentTool for _, t := range availableTools { @@ -1009,7 +1009,7 @@ func WithRepairToolCall(fn RepairToolCallFunction) AgentOption { } } -// processStepStream processes a single step's stream and returns the step result +// processStepStream processes a single step's stream and returns the step result. func (a *agent) processStepStream(ctx context.Context, stream StreamResponse, opts AgentStreamCall, _ []StepResult) (StepResult, bool, error) { var stepContent []Content var stepToolCalls []ToolCallContent diff --git a/content.go b/content.go index f2f9aa93d..375f21697 100644 --- a/content.go +++ b/content.go @@ -45,7 +45,7 @@ type ProviderOptions map[string]map[string]any // - `tool-calls`: model triggered tool calls // - `error`: model stopped because of an error // - `other`: model stopped for other reasons -// - `unknown`: the model has not transmitted a finish reason +// - `unknown`: the model has not transmitted a finish reason. type FinishReason string const ( @@ -450,7 +450,7 @@ func (p ProviderDefinedTool) GetName() string { return p.Name } -// Helpers +// Helpers. func NewUserMessage(prompt string, files ...FilePart) Message { content := []MessagePart{ TextPart{ diff --git a/providers/anthropic.go b/providers/anthropic.go index b0ed3f36c..61ea65789 100644 --- a/providers/anthropic.go +++ b/providers/anthropic.go @@ -382,7 +382,7 @@ func toAnthropicTools(tools []ai.Tool, toolChoice *ai.ToolChoice, disableParalle }, } } - return + return anthropicTools, anthropicToolChoice, warnings } switch *toolChoice { @@ -401,7 +401,7 @@ func toAnthropicTools(tools []ai.Tool, toolChoice *ai.ToolChoice, disableParalle }, } case ai.ToolChoiceNone: - return + return anthropicTools, anthropicToolChoice, warnings default: anthropicToolChoice = &anthropic.ToolChoiceUnionParam{ OfTool: &anthropic.ToolChoiceToolParam{ @@ -411,7 +411,7 @@ func toAnthropicTools(tools []ai.Tool, toolChoice *ai.ToolChoice, disableParalle }, } } - return + return anthropicTools, anthropicToolChoice, warnings } func toAnthropicPrompt(prompt ai.Prompt, sendReasoningData bool) ([]anthropic.TextBlockParam, []anthropic.MessageParam, []ai.CallWarning) { diff --git a/providers/openai.go b/providers/openai.go index 4615a359b..3e1832c49 100644 --- a/providers/openai.go +++ b/providers/openai.go @@ -839,7 +839,7 @@ func toOpenAiTools(tools []ai.Tool, toolChoice *ai.ToolChoice) (openAiTools []op }) } if toolChoice == nil { - return + return openAiTools, openAiToolChoice, warnings } switch *toolChoice { @@ -861,7 +861,7 @@ func toOpenAiTools(tools []ai.Tool, toolChoice *ai.ToolChoice) (openAiTools []op }, } } - return + return openAiTools, openAiToolChoice, warnings } func toOpenAiPrompt(prompt ai.Prompt) ([]openai.ChatCompletionMessageParamUnion, []ai.CallWarning) { @@ -1129,7 +1129,7 @@ func toOpenAiPrompt(prompt ai.Prompt) ([]openai.ChatCompletionMessageParamUnion, return messages, warnings } -// parseAnnotationsFromDelta parses annotations from the raw JSON of a delta +// parseAnnotationsFromDelta parses annotations from the raw JSON of a delta. func parseAnnotationsFromDelta(delta openai.ChatCompletionChunkChoiceDelta) []openai.ChatCompletionMessageAnnotation { var annotations []openai.ChatCompletionMessageAnnotation From 381b9ea448238c0286d5a080fb55e51638d599ca Mon Sep 17 00:00:00 2001 From: Andrey Nering Date: Fri, 29 Aug 2025 14:33:50 -0300 Subject: [PATCH 5/8] lint: fix uneeded conversion --- agent.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/agent.go b/agent.go index 7fcf9cb2a..103a1345b 100644 --- a/agent.go +++ b/agent.go @@ -1065,7 +1065,7 @@ func (a *agent) processStepStream(ctx context.Context, stream StreamResponse, op if text, exists := activeTextContent[part.ID]; exists { stepContent = append(stepContent, TextContent{ Text: text, - ProviderMetadata: ProviderMetadata(part.ProviderMetadata), + ProviderMetadata: part.ProviderMetadata, }) delete(activeTextContent, part.ID) } @@ -1100,12 +1100,12 @@ func (a *agent) processStepStream(ctx context.Context, stream StreamResponse, op if text, exists := activeTextContent[part.ID]; exists { stepContent = append(stepContent, ReasoningContent{ Text: text, - ProviderMetadata: ProviderMetadata(part.ProviderMetadata), + ProviderMetadata: part.ProviderMetadata, }) if opts.OnReasoningEnd != nil { err := opts.OnReasoningEnd(part.ID, ReasoningContent{ Text: text, - ProviderMetadata: ProviderMetadata(part.ProviderMetadata), + ProviderMetadata: part.ProviderMetadata, }) if err != nil { return StepResult{}, false, err @@ -1153,7 +1153,7 @@ func (a *agent) processStepStream(ctx context.Context, stream StreamResponse, op ToolName: part.ToolCallName, Input: part.ToolCallInput, ProviderExecuted: part.ProviderExecuted, - ProviderMetadata: ProviderMetadata(part.ProviderMetadata), + ProviderMetadata: part.ProviderMetadata, } // Validate and potentially repair the tool call @@ -1177,7 +1177,7 @@ func (a *agent) processStepStream(ctx context.Context, stream StreamResponse, op ID: part.ID, URL: part.URL, Title: part.Title, - ProviderMetadata: ProviderMetadata(part.ProviderMetadata), + ProviderMetadata: part.ProviderMetadata, } stepContent = append(stepContent, sourceContent) if opts.OnSource != nil { @@ -1190,7 +1190,7 @@ func (a *agent) processStepStream(ctx context.Context, stream StreamResponse, op case StreamPartTypeFinish: stepUsage = part.Usage stepFinishReason = part.FinishReason - stepProviderMetadata = ProviderMetadata(part.ProviderMetadata) + stepProviderMetadata = part.ProviderMetadata if opts.OnStreamFinish != nil { err := opts.OnStreamFinish(part.Usage, part.FinishReason, part.ProviderMetadata) if err != nil { From e4618bc96c7341b5dce90220723a27c73cb9d5f0 Mon Sep 17 00:00:00 2001 From: Andrey Nering Date: Fri, 29 Aug 2025 14:37:06 -0300 Subject: [PATCH 6/8] lint: fix `errcheck` issues --- agent.go | 6 +++--- providers/anthropic.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/agent.go b/agent.go index 103a1345b..6f628b0a7 100644 --- a/agent.go +++ b/agent.go @@ -767,7 +767,7 @@ func (a *agent) Stream(ctx context.Context, opts AgentStreamCall) (*AgentResult, // Start step stream if opts.OnStepStart != nil { - opts.OnStepStart(stepNumber) + _ = opts.OnStepStart(stepNumber) } // Create streaming call @@ -808,7 +808,7 @@ func (a *agent) Stream(ctx context.Context, opts AgentStreamCall) (*AgentResult, // Call step finished callback if opts.OnStepFinish != nil { - opts.OnStepFinish(stepResult) + _ = opts.OnStepFinish(stepResult) } // Add step messages to response messages @@ -834,7 +834,7 @@ func (a *agent) Stream(ctx context.Context, opts AgentStreamCall) (*AgentResult, } if opts.OnAgentFinish != nil { - opts.OnAgentFinish(agentResult) + _ = opts.OnAgentFinish(agentResult) } return agentResult, nil diff --git a/providers/anthropic.go b/providers/anthropic.go index 61ea65789..a8924a09d 100644 --- a/providers/anthropic.go +++ b/providers/anthropic.go @@ -775,7 +775,7 @@ func (a anthropicLanguageModel) Stream(ctx context.Context, call ai.Call) (ai.St for stream.Next() { chunk := stream.Current() - acc.Accumulate(chunk) + _ = acc.Accumulate(chunk) switch chunk.Type { case "content_block_start": contentBlockType := chunk.ContentBlock.Type From 91e5cea0233bf31d803663638b1b9dd69da02614 Mon Sep 17 00:00:00 2001 From: Andrey Nering Date: Fri, 29 Aug 2025 14:40:19 -0300 Subject: [PATCH 7/8] lint: preallocate slices --- agent.go | 2 +- content.go | 2 +- providers/openai.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/agent.go b/agent.go index 6f628b0a7..875420322 100644 --- a/agent.go +++ b/agent.go @@ -841,7 +841,7 @@ func (a *agent) Stream(ctx context.Context, opts AgentStreamCall) (*AgentResult, } func (a *agent) prepareTools(tools []AgentTool, activeTools []string, disableAllTools bool) []Tool { - var preparedTools []Tool + preparedTools := make([]Tool, 0, len(tools)) // If explicitly disabling all tools, return no tools if disableAllTools { diff --git a/content.go b/content.go index 375f21697..5ff7f0805 100644 --- a/content.go +++ b/content.go @@ -469,7 +469,7 @@ func NewUserMessage(prompt string, files ...FilePart) Message { } func NewSystemMessage(prompt ...string) Message { - var content []MessagePart + content := make([]MessagePart, 0, len(prompt)) for _, p := range prompt { content = append(content, TextPart{Text: p}) } diff --git a/providers/openai.go b/providers/openai.go index 3e1832c49..c9d396e43 100644 --- a/providers/openai.go +++ b/providers/openai.go @@ -436,7 +436,7 @@ func (o openAiLanguageModel) Generate(ctx context.Context, call ai.Call) (*ai.Re return nil, errors.New("no response generated") } choice := response.Choices[0] - var content []ai.Content + content := make([]ai.Content, 0, 1+len(choice.Message.ToolCalls)+len(choice.Message.Annotations)) text := choice.Message.Content if text != "" { content = append(content, ai.TextContent{ From d30acf5531c2b72283793e54e82822347b4acc31 Mon Sep 17 00:00:00 2001 From: Andrey Nering Date: Fri, 29 Aug 2025 14:40:35 -0300 Subject: [PATCH 8/8] lint: temporarily disable some linters --- .golangci.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 929cb0ac9..136f67403 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -4,27 +4,27 @@ run: linters: enable: - bodyclose - - exhaustive - - goconst + # - exhaustive + # - goconst - godot - gomoddirectives - goprintffuncname - gosec - misspell - nakedret - - nestif + # - nestif - nilerr - noctx - nolintlint - prealloc - - revive + # - revive - rowserrcheck - sqlclosecheck - tparallel - unconvert - unparam - whitespace - - wrapcheck + # - wrapcheck exclusions: rules: - text: '(slog|log)\.\w+'