fix(ci): resolve golangci-lint formatting and unused variable errors
The `golangci-lint` step in the CI check suite failed due to `golines` and `gci` formatting issues, as well as `unused` variable warnings for `defaultRequestTimeout` in `pkg/providers/vertex/provider.go` and `defaultThreshold` in `pkg/routing/router.go`. This commit addresses the errors by removing the unused variables and running the required formatters (`gci` with the repository prefix and `golines` with a 120 character width limit) on the affected files. Co-authored-by: TanLuong <28281768+TanLuong@users.noreply.github.com>
This commit is contained in:
parent
b05489bff4
commit
4c6c4ae3c6
9 changed files with 148 additions and 80 deletions
|
|
@ -1101,13 +1101,13 @@ type WebToolsConfig struct {
|
|||
// the client-side web_search tool is hidden to avoid duplicate search surfaces,
|
||||
// and the provider's built-in search is used instead. Falls back to client-side
|
||||
// search when the provider does not support native search.
|
||||
PreferNative bool `json:"prefer_native" env:"PICOCLAW_TOOLS_WEB_PREFER_NATIVE"`
|
||||
PreferNative bool ` json:"prefer_native" env:"PICOCLAW_TOOLS_WEB_PREFER_NATIVE"`
|
||||
// Proxy is an optional proxy URL for web tools (http/https/socks5/socks5h).
|
||||
// For authenticated proxies, prefer HTTP_PROXY/HTTPS_PROXY env vars instead of embedding credentials in config.
|
||||
Proxy string `json:"proxy,omitempty" env:"PICOCLAW_TOOLS_WEB_PROXY"`
|
||||
FetchLimitBytes int64 `json:"fetch_limit_bytes,omitempty" env:"PICOCLAW_TOOLS_WEB_FETCH_LIMIT_BYTES"`
|
||||
Format string `json:"format,omitempty" env:"PICOCLAW_TOOLS_WEB_FORMAT"`
|
||||
PrivateHostWhitelist FlexibleStringSlice `json:"private_host_whitelist,omitempty" env:"PICOCLAW_TOOLS_WEB_PRIVATE_HOST_WHITELIST"`
|
||||
Proxy string ` json:"proxy,omitempty" env:"PICOCLAW_TOOLS_WEB_PROXY"`
|
||||
FetchLimitBytes int64 ` json:"fetch_limit_bytes,omitempty" env:"PICOCLAW_TOOLS_WEB_FETCH_LIMIT_BYTES"`
|
||||
Format string ` json:"format,omitempty" env:"PICOCLAW_TOOLS_WEB_FORMAT"`
|
||||
PrivateHostWhitelist FlexibleStringSlice ` json:"private_host_whitelist,omitempty" env:"PICOCLAW_TOOLS_WEB_PRIVATE_HOST_WHITELIST"`
|
||||
}
|
||||
|
||||
type CronToolsConfig struct {
|
||||
|
|
@ -1271,7 +1271,7 @@ type MCPConfig struct {
|
|||
ToolConfig ` envPrefix:"PICOCLAW_TOOLS_MCP_"`
|
||||
Discovery ToolDiscoveryConfig ` json:"discovery"`
|
||||
// Servers is a map of server name to server configuration
|
||||
Servers map[string]MCPServerConfig `json:"servers,omitempty"`
|
||||
Servers map[string]MCPServerConfig ` json:"servers,omitempty"`
|
||||
}
|
||||
|
||||
func LoadConfig(path string) (*Config, error) {
|
||||
|
|
|
|||
|
|
@ -28,10 +28,6 @@ type (
|
|||
FunctionCall = protocoltypes.FunctionCall
|
||||
)
|
||||
|
||||
const (
|
||||
defaultRequestTimeout = common.DefaultRequestTimeout
|
||||
)
|
||||
|
||||
// Provider implements the LLM provider interface for Google Vertex AI.
|
||||
// It uses the standard Vertex AI REST API for Gemini models.
|
||||
type Provider struct {
|
||||
|
|
@ -114,7 +110,6 @@ func (p *Provider) buildURL(model string, action string) string {
|
|||
return baseURL
|
||||
}
|
||||
|
||||
|
||||
// parseMediaData converts base64 media data into the Vertex AI inlineData format.
|
||||
// It tries to detect mime type from the data URI scheme if present.
|
||||
func parseMediaData(mediaData string) map[string]any {
|
||||
|
|
@ -138,7 +133,11 @@ func parseMediaData(mediaData string) map[string]any {
|
|||
}
|
||||
|
||||
// buildRequestBody formats the standard messages and tools into the Vertex AI (Gemini) REST payload format.
|
||||
func (p *Provider) buildRequestBody(messages []Message, tools []ToolDefinition, options map[string]any) (map[string]any, error) {
|
||||
func (p *Provider) buildRequestBody(
|
||||
messages []Message,
|
||||
tools []ToolDefinition,
|
||||
options map[string]any,
|
||||
) (map[string]any, error) {
|
||||
req := make(map[string]any)
|
||||
|
||||
var contents []map[string]any
|
||||
|
|
@ -291,7 +290,6 @@ func (p *Provider) buildRequestBody(messages []Message, tools []ToolDefinition,
|
|||
return req, nil
|
||||
}
|
||||
|
||||
|
||||
func (p *Provider) Chat(
|
||||
ctx context.Context,
|
||||
messages []Message,
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ import (
|
|||
"net/url"
|
||||
"testing"
|
||||
|
||||
|
||||
"github.com/sipeed/picoclaw/pkg/providers/protocoltypes"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/sipeed/picoclaw/pkg/providers/protocoltypes"
|
||||
)
|
||||
|
||||
func TestProvider_buildURL(t *testing.T) {
|
||||
|
|
@ -59,16 +59,21 @@ func TestProvider_buildURL(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
func TestProvider_buildRequestBody(t *testing.T) {
|
||||
p := NewProvider("key", "", "", "proj", "us-central1")
|
||||
|
||||
messages := []protocoltypes.Message{
|
||||
{Role: "system", Content: "You are a helpful assistant."},
|
||||
{Role: "user", Content: "Hello!", Media: []string{"data:image/png;base64,iVBORw0KGgo"}},
|
||||
{Role: "assistant", ToolCalls: []protocoltypes.ToolCall{{Name: "get_weather", Arguments: map[string]any{"location": "Tokyo"}}}},
|
||||
{
|
||||
Role: "assistant",
|
||||
ToolCalls: []protocoltypes.ToolCall{{Name: "get_weather", Arguments: map[string]any{"location": "Tokyo"}}},
|
||||
},
|
||||
{Role: "tool", ToolCallID: "get_weather", Content: "Sunny"},
|
||||
{Role: "assistant", ToolCalls: []protocoltypes.ToolCall{{Name: "get_time", Arguments: map[string]any{"location": "Tokyo"}}}},
|
||||
{
|
||||
Role: "assistant",
|
||||
ToolCalls: []protocoltypes.ToolCall{{Name: "get_time", Arguments: map[string]any{"location": "Tokyo"}}},
|
||||
},
|
||||
{Role: "tool", ToolCallID: "get_time", Content: "12:00 PM"},
|
||||
}
|
||||
|
||||
|
|
@ -128,7 +133,6 @@ func TestProvider_buildRequestBody(t *testing.T) {
|
|||
assert.Equal(t, "model", contents[3]["role"])
|
||||
}
|
||||
|
||||
|
||||
func TestProvider_Chat(t *testing.T) {
|
||||
// Create a mock server
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
|
|
@ -194,7 +198,11 @@ func TestProvider_ChatStream(t *testing.T) {
|
|||
w.Header().Set("Content-Type", "text/event-stream")
|
||||
// Write mock chunks
|
||||
w.Write([]byte(`data: {"candidates":[{"content":{"parts":[{"text":"Hello"}]}}]}` + "\n\n"))
|
||||
w.Write([]byte(`data: {"candidates":[{"content":{"parts":[{"text":", world!"}]},"finishReason":"STOP"}],"usageMetadata":{"promptTokenCount":10,"candidatesTokenCount":5,"totalTokenCount":15}}` + "\n\n"))
|
||||
w.Write(
|
||||
[]byte(
|
||||
`data: {"candidates":[{"content":{"parts":[{"text":", world!"}]},"finishReason":"STOP"}],"usageMetadata":{"promptTokenCount":10,"candidatesTokenCount":5,"totalTokenCount":15}}` + "\n\n",
|
||||
),
|
||||
)
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
|
|
|
|||
|
|
@ -4,11 +4,6 @@ import (
|
|||
"github.com/sipeed/picoclaw/pkg/providers"
|
||||
)
|
||||
|
||||
// defaultThreshold is used when the config threshold is zero or negative.
|
||||
// At 0.35 a message needs at least one strong signal (code block, long text,
|
||||
// or an attachment) before the heavy model is chosen.
|
||||
const defaultThreshold = 0.35
|
||||
|
||||
// RoutingTier defines a single tier for model routing.
|
||||
type RoutingTier struct {
|
||||
Model string
|
||||
|
|
|
|||
|
|
@ -241,9 +241,15 @@ func TestRuleClassifier_ScoreDoesNotExceedOne(t *testing.T) {
|
|||
|
||||
// ── Router ───────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
func TestRouter_SelectModel_SimpleMessageUsesLight(t *testing.T) {
|
||||
r := New(RouterConfig{Tiers: []RoutingTier{{Model: "gemini-flash", Threshold: 0.0}, {Model: "claude-sonnet-4-6", Threshold: 0.35}}})
|
||||
r := New(
|
||||
RouterConfig{
|
||||
Tiers: []RoutingTier{
|
||||
{Model: "gemini-flash", Threshold: 0.0},
|
||||
{Model: "claude-sonnet-4-6", Threshold: 0.35},
|
||||
},
|
||||
},
|
||||
)
|
||||
msg := "hi"
|
||||
model, usedLight, _ := r.SelectModel(msg, nil, "claude-sonnet-4-6")
|
||||
if !usedLight {
|
||||
|
|
@ -255,7 +261,14 @@ func TestRouter_SelectModel_SimpleMessageUsesLight(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestRouter_SelectModel_CodeBlockUsesPrimary(t *testing.T) {
|
||||
r := New(RouterConfig{Tiers: []RoutingTier{{Model: "gemini-flash", Threshold: 0.0}, {Model: "claude-sonnet-4-6", Threshold: 0.35}}})
|
||||
r := New(
|
||||
RouterConfig{
|
||||
Tiers: []RoutingTier{
|
||||
{Model: "gemini-flash", Threshold: 0.0},
|
||||
{Model: "claude-sonnet-4-6", Threshold: 0.35},
|
||||
},
|
||||
},
|
||||
)
|
||||
msg := "```go\nfmt.Println(\"hello\")\n```"
|
||||
model, usedLight, _ := r.SelectModel(msg, nil, "claude-sonnet-4-6")
|
||||
if usedLight {
|
||||
|
|
@ -267,7 +280,14 @@ func TestRouter_SelectModel_CodeBlockUsesPrimary(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestRouter_SelectModel_AttachmentUsesPrimary(t *testing.T) {
|
||||
r := New(RouterConfig{Tiers: []RoutingTier{{Model: "gemini-flash", Threshold: 0.0}, {Model: "claude-sonnet-4-6", Threshold: 0.35}}})
|
||||
r := New(
|
||||
RouterConfig{
|
||||
Tiers: []RoutingTier{
|
||||
{Model: "gemini-flash", Threshold: 0.0},
|
||||
{Model: "claude-sonnet-4-6", Threshold: 0.35},
|
||||
},
|
||||
},
|
||||
)
|
||||
msg := "can you analyze this? data:image/png;base64,abc123"
|
||||
model, usedLight, _ := r.SelectModel(msg, nil, "claude-sonnet-4-6")
|
||||
if usedLight {
|
||||
|
|
@ -279,7 +299,14 @@ func TestRouter_SelectModel_AttachmentUsesPrimary(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestRouter_SelectModel_LongMessageUsesPrimary(t *testing.T) {
|
||||
r := New(RouterConfig{Tiers: []RoutingTier{{Model: "gemini-flash", Threshold: 0.0}, {Model: "claude-sonnet-4-6", Threshold: 0.35}}})
|
||||
r := New(
|
||||
RouterConfig{
|
||||
Tiers: []RoutingTier{
|
||||
{Model: "gemini-flash", Threshold: 0.0},
|
||||
{Model: "claude-sonnet-4-6", Threshold: 0.35},
|
||||
},
|
||||
},
|
||||
)
|
||||
// >200 token estimate: 210 * 3 = 630 chars
|
||||
msg := strings.Repeat("word ", 210)
|
||||
model, usedLight, _ := r.SelectModel(msg, nil, "claude-sonnet-4-6")
|
||||
|
|
@ -294,7 +321,14 @@ func TestRouter_SelectModel_LongMessageUsesPrimary(t *testing.T) {
|
|||
func TestRouter_SelectModel_DeepToolChainUsesLight(t *testing.T) {
|
||||
// Tool calls alone (0.25) don't cross the 0.35 threshold — acceptable behavior.
|
||||
// Routing is conservative: only promote to heavy when the signal is unambiguous.
|
||||
r := New(RouterConfig{Tiers: []RoutingTier{{Model: "gemini-flash", Threshold: 0.0}, {Model: "claude-sonnet-4-6", Threshold: 0.35}}})
|
||||
r := New(
|
||||
RouterConfig{
|
||||
Tiers: []RoutingTier{
|
||||
{Model: "gemini-flash", Threshold: 0.0},
|
||||
{Model: "claude-sonnet-4-6", Threshold: 0.35},
|
||||
},
|
||||
},
|
||||
)
|
||||
history := []providers.Message{
|
||||
{Role: "assistant", ToolCalls: []providers.ToolCall{{Name: "read_file"}, {Name: "write_file"}}},
|
||||
{Role: "assistant", ToolCalls: []providers.ToolCall{{Name: "exec"}, {Name: "search"}}},
|
||||
|
|
@ -308,7 +342,14 @@ func TestRouter_SelectModel_DeepToolChainUsesLight(t *testing.T) {
|
|||
|
||||
func TestRouter_SelectModel_ToolChainPlusMediumUsesHeavy(t *testing.T) {
|
||||
// Tool calls (0.25) + medium message (0.15) = 0.40 >= 0.35 → heavy
|
||||
r := New(RouterConfig{Tiers: []RoutingTier{{Model: "gemini-flash", Threshold: 0.0}, {Model: "claude-sonnet-4-6", Threshold: 0.35}}})
|
||||
r := New(
|
||||
RouterConfig{
|
||||
Tiers: []RoutingTier{
|
||||
{Model: "gemini-flash", Threshold: 0.0},
|
||||
{Model: "claude-sonnet-4-6", Threshold: 0.35},
|
||||
},
|
||||
},
|
||||
)
|
||||
history := []providers.Message{
|
||||
{Role: "assistant", ToolCalls: []providers.ToolCall{
|
||||
{Name: "a"}, {Name: "b"}, {Name: "c"}, {Name: "d"},
|
||||
|
|
@ -324,7 +365,14 @@ func TestRouter_SelectModel_ToolChainPlusMediumUsesHeavy(t *testing.T) {
|
|||
|
||||
func TestRouter_SelectModel_CustomThreshold(t *testing.T) {
|
||||
// Very low threshold: even a short message triggers heavy model
|
||||
r := New(RouterConfig{Tiers: []RoutingTier{{Model: "gemini-flash", Threshold: 0.0}, {Model: "claude-sonnet-4-6", Threshold: 0.05}}})
|
||||
r := New(
|
||||
RouterConfig{
|
||||
Tiers: []RoutingTier{
|
||||
{Model: "gemini-flash", Threshold: 0.0},
|
||||
{Model: "claude-sonnet-4-6", Threshold: 0.05},
|
||||
},
|
||||
},
|
||||
)
|
||||
msg := strings.Repeat("word ", 55) // medium message → 0.15 >= 0.05
|
||||
_, usedLight, _ := r.SelectModel(msg, nil, "claude-sonnet-4-6")
|
||||
if usedLight {
|
||||
|
|
@ -334,7 +382,14 @@ func TestRouter_SelectModel_CustomThreshold(t *testing.T) {
|
|||
|
||||
func TestRouter_SelectModel_HighThreshold(t *testing.T) {
|
||||
// Very high threshold: even code blocks route to light
|
||||
r := New(RouterConfig{Tiers: []RoutingTier{{Model: "gemini-flash", Threshold: 0.0}, {Model: "claude-sonnet-4-6", Threshold: 0.99}}})
|
||||
r := New(
|
||||
RouterConfig{
|
||||
Tiers: []RoutingTier{
|
||||
{Model: "gemini-flash", Threshold: 0.0},
|
||||
{Model: "claude-sonnet-4-6", Threshold: 0.99},
|
||||
},
|
||||
},
|
||||
)
|
||||
msg := "```go\nfmt.Println()\n```"
|
||||
_, usedLight, _ := r.SelectModel(msg, nil, "claude-sonnet-4-6")
|
||||
if !usedLight {
|
||||
|
|
@ -343,7 +398,11 @@ func TestRouter_SelectModel_HighThreshold(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestRouter_Tiers(t *testing.T) {
|
||||
r := New(RouterConfig{Tiers: []RoutingTier{{Model: "my-fast-model", Threshold: 0.0}, {Model: "heavy-model", Threshold: 0.35}}})
|
||||
r := New(
|
||||
RouterConfig{
|
||||
Tiers: []RoutingTier{{Model: "my-fast-model", Threshold: 0.0}, {Model: "heavy-model", Threshold: 0.35}},
|
||||
},
|
||||
)
|
||||
if r.Tiers()[0].Model != "my-fast-model" {
|
||||
t.Errorf("LightModel: got %q, want %q", "my-fast-model", "my-fast-model")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,11 +92,19 @@ func (t *UpdateSkillTool) Execute(ctx context.Context, args map[string]any) *Too
|
|||
return ErrorResult(fmt.Sprintf("failed to append to SKILL.md: %v", err))
|
||||
}
|
||||
|
||||
output := fmt.Sprintf("Successfully learned and updated SKILL.md.\n\nAnalysis: %s\nSkills Improved: %s\n", analysis, skillsToImprove)
|
||||
output := fmt.Sprintf(
|
||||
"Successfully learned and updated SKILL.md.\n\nAnalysis: %s\nSkills Improved: %s\n",
|
||||
analysis,
|
||||
skillsToImprove,
|
||||
)
|
||||
|
||||
// The response is passed back to the LLM.
|
||||
// We also populate the ForUser field to notify the user.
|
||||
res := SilentResult(output)
|
||||
res.ForUser = fmt.Sprintf("I have analyzed our conversation and improved my skills.\n\n**My Analysis**:\n%s\n\n**Skills I've Improved/Added**:\n%s\n\nI have saved these learnings to `SKILL.md`.", analysis, skillsToImprove)
|
||||
res.ForUser = fmt.Sprintf(
|
||||
"I have analyzed our conversation and improved my skills.\n\n**My Analysis**:\n%s\n\n**Skills I've Improved/Added**:\n%s\n\nI have saved these learnings to `SKILL.md`.",
|
||||
analysis,
|
||||
skillsToImprove,
|
||||
)
|
||||
return res
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue