ci: fix linter errors in CI pipeline

- Fixed `musttag` errors in `web/backend/api/config.go` by explicitly decoding JSON into an anonymous struct.
- Fixed a `govet` shadow error in `pkg/providers/common/http_logger.go`.
- Fixed formatting errors reported by `golines` and `gci` in `pkg/config/config.go`, `pkg/config/security.go`, and `pkg/providers/anthropic_messages/provider.go`.

Co-authored-by: TanLuong <28281768+TanLuong@users.noreply.github.com>
This commit is contained in:
google-labs-jules[bot] 2026-03-28 08:55:07 +00:00
parent 06ca00403b
commit 44e84585d6
5 changed files with 58 additions and 20 deletions

View file

@ -1048,10 +1048,10 @@ type SearXNGConfig struct {
} }
type GLMSearchConfig struct { type GLMSearchConfig struct {
Enabled bool `json:"enabled" env:"PICOCLAW_TOOLS_WEB_GLM_ENABLED"` Enabled bool `json:"enabled" env:"PICOCLAW_TOOLS_WEB_GLM_ENABLED"`
apiKey string apiKey string
secDirty bool secDirty bool
BaseURL string `json:"base_url" env:"PICOCLAW_TOOLS_WEB_GLM_BASE_URL"` BaseURL string `json:"base_url" env:"PICOCLAW_TOOLS_WEB_GLM_BASE_URL"`
// SearchEngine specifies the search backend: "search_std" (default), // SearchEngine specifies the search backend: "search_std" (default),
// "search_pro", "search_pro_sogou", or "search_pro_quark". // "search_pro", "search_pro_sogou", or "search_pro_quark".
SearchEngine string `json:"search_engine" env:"PICOCLAW_TOOLS_WEB_GLM_SEARCH_ENGINE"` SearchEngine string `json:"search_engine" env:"PICOCLAW_TOOLS_WEB_GLM_SEARCH_ENGINE"`
@ -1766,7 +1766,7 @@ func SaveConfig(path string, cfg *Config) error {
m.secModelName = newName m.secModelName = newName
} }
cfg.security.ModelList = newModelList cfg.security.ModelList = newModelList
if cfg.Channels.Pico.secDirty { if cfg.Channels.Pico.secDirty {
cfg.security.Channels.Pico = &PicoSecurity{ cfg.security.Channels.Pico = &PicoSecurity{

View file

@ -239,6 +239,7 @@ func (c *Config) MergeAndApplySecurity(newer *SecurityConfig) error {
c.security = mergeSecurityConfig(c.security, newer) c.security = mergeSecurityConfig(c.security, newer)
return applySecurityConfig(c, c.security) return applySecurityConfig(c, c.security)
} }
// mergeSecurityConfig merges two SecurityConfig instances, preferring non-empty values from 'newer'. // mergeSecurityConfig merges two SecurityConfig instances, preferring non-empty values from 'newer'.
// This is used during config migration to preserve existing security data while adding new entries. // This is used during config migration to preserve existing security data while adding new entries.
func mergeSecurityConfig(existing, newer *SecurityConfig) *SecurityConfig { func mergeSecurityConfig(existing, newer *SecurityConfig) *SecurityConfig {
@ -335,7 +336,8 @@ func mergeChannelsSecurity(dst, src *ChannelsSecurity) {
if src.Pico != nil && src.Pico.Token != "" { if src.Pico != nil && src.Pico.Token != "" {
dst.Pico = src.Pico dst.Pico = src.Pico
} }
if src.IRC != nil && (src.IRC.Password != "" || src.IRC.NickServPassword != "" || src.IRC.SASLPassword != "") { if src.IRC != nil &&
(src.IRC.Password != "" || src.IRC.NickServPassword != "" || src.IRC.SASLPassword != "") {
dst.IRC = src.IRC dst.IRC = src.IRC
} }
} }

View file

@ -58,8 +58,8 @@ func NewProviderWithTimeout(apiKey, apiBase string, timeoutSeconds int) *Provide
} }
return &Provider{ return &Provider{
apiKey: apiKey, apiKey: apiKey,
apiBase: baseURL, apiBase: baseURL,
httpClient: common.NewHTTPClientWithTimeout("", timeout), httpClient: common.NewHTTPClientWithTimeout("", timeout),
} }
} }
@ -102,7 +102,10 @@ func (p *Provider) Chat(
// Set headers // Set headers
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-API-Key", p.apiKey) //nolint:canonicalheader // Anthropic API requires exact header name req.Header.Set(
"X-API-Key",
p.apiKey,
) //nolint:canonicalheader // Anthropic API requires exact header name
req.Header.Set("Anthropic-Version", defaultAPIVersion) req.Header.Set("Anthropic-Version", defaultAPIVersion)
// Execute request // Execute request
@ -134,7 +137,11 @@ func (p *Provider) Chat(
return nil, fmt.Errorf("service unavailable (503): %s", string(body)) return nil, fmt.Errorf("service unavailable (503): %s", string(body))
default: default:
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("API request failed with status %d: %s", resp.StatusCode, string(body)) return nil, fmt.Errorf(
"API request failed with status %d: %s",
resp.StatusCode,
string(body),
)
} }
} }
@ -194,7 +201,8 @@ func buildRequestBody(
"content": msg.Content, "content": msg.Content,
} }
if len(apiMessages) > 0 { if len(apiMessages) > 0 {
if prev, ok := apiMessages[len(apiMessages)-1].(map[string]any); ok && prev["role"] == "user" { if prev, ok := apiMessages[len(apiMessages)-1].(map[string]any); ok &&
prev["role"] == "user" {
if content, ok := prev["content"].([]map[string]any); ok { if content, ok := prev["content"].([]map[string]any); ok {
prev["content"] = append(content, toolResultBlock) prev["content"] = append(content, toolResultBlock)
continue continue
@ -258,7 +266,8 @@ func buildRequestBody(
"content": msg.Content, "content": msg.Content,
} }
if len(apiMessages) > 0 { if len(apiMessages) > 0 {
if prev, ok := apiMessages[len(apiMessages)-1].(map[string]any); ok && prev["role"] == "user" { if prev, ok := apiMessages[len(apiMessages)-1].(map[string]any); ok &&
prev["role"] == "user" {
if content, ok := prev["content"].([]map[string]any); ok { if content, ok := prev["content"].([]map[string]any); ok {
prev["content"] = append(content, toolResultBlock) prev["content"] = append(content, toolResultBlock)
continue continue
@ -310,7 +319,10 @@ func parseResponseBody(body []byte) (*LLMResponse, error) {
// Extract content and tool calls // Extract content and tool calls
var content strings.Builder var content strings.Builder
toolCalls := make([]ToolCall, 0) // Initialize as empty slice (not nil) for consistent JSON serialization toolCalls := make(
[]ToolCall,
0,
) // Initialize as empty slice (not nil) for consistent JSON serialization
for _, block := range resp.Content { for _, block := range resp.Content {
switch block.Type { switch block.Type {

View file

@ -48,8 +48,8 @@ func (lrt *LoggingRoundTripper) RoundTrip(req *http.Request) (*http.Response, er
// Optional: Log Response Body // Optional: Log Response Body
if res.Body != nil { if res.Body != nil {
bodyBytes, err := io.ReadAll(res.Body) bodyBytes, readErr := io.ReadAll(res.Body)
if err == nil { if readErr == nil {
res.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) res.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
// Truncate response body if too long to avoid flooding logs // Truncate response body if too long to avoid flooding logs
respStr := string(bodyBytes) respStr := string(bodyBytes)

View file

@ -66,9 +66,22 @@ func (h *Handler) handleUpdateConfig(w http.ResponseWriter, r *http.Request) {
cfg.SecurityCopyFrom(oldCfg) cfg.SecurityCopyFrom(oldCfg)
// Intercept explicitly provided security tokens from JSON payload that json.Unmarshal drops. // Intercept explicitly provided security tokens from JSON payload that json.Unmarshal drops.
var incomingSec config.SecurityConfig // We need to decode into an anonymous struct with json tags because SecurityConfig
// doesn't have json tags for its fields (it uses yaml tags).
var incomingSec struct {
ModelList map[string]config.ModelSecurityEntry `json:"model_list"`
Channels *config.ChannelsSecurity `json:"channels,omitempty"`
Web *config.WebToolsSecurity `json:"web,omitempty"`
Skills *config.SkillsSecurity `json:"skills,omitempty"`
}
if err := json.Unmarshal(body, &incomingSec); err == nil { if err := json.Unmarshal(body, &incomingSec); err == nil {
cfg.MergeAndApplySecurity(&incomingSec) secConfig := config.SecurityConfig{
ModelList: incomingSec.ModelList,
Channels: incomingSec.Channels,
Web: incomingSec.Web,
Skills: incomingSec.Skills,
}
cfg.MergeAndApplySecurity(&secConfig)
} else { } else {
cfg.ApplySecurity() cfg.ApplySecurity()
} }
@ -164,9 +177,20 @@ func (h *Handler) handlePatchConfig(w http.ResponseWriter, r *http.Request) {
// Restore security fields from existing config and merge explicitly provided overrides. // Restore security fields from existing config and merge explicitly provided overrides.
newCfg.SecurityCopyFrom(cfg) newCfg.SecurityCopyFrom(cfg)
var incomingSec config.SecurityConfig var patchSec struct {
if err := json.Unmarshal(patchBody, &incomingSec); err == nil { ModelList map[string]config.ModelSecurityEntry `json:"model_list"`
newCfg.MergeAndApplySecurity(&incomingSec) Channels *config.ChannelsSecurity `json:"channels,omitempty"`
Web *config.WebToolsSecurity `json:"web,omitempty"`
Skills *config.SkillsSecurity `json:"skills,omitempty"`
}
if err := json.Unmarshal(patchBody, &patchSec); err == nil {
secConfig := config.SecurityConfig{
ModelList: patchSec.ModelList,
Channels: patchSec.Channels,
Web: patchSec.Web,
Skills: patchSec.Skills,
}
newCfg.MergeAndApplySecurity(&secConfig)
} else { } else {
newCfg.ApplySecurity() newCfg.ApplySecurity()
} }