fix: resolve lint errors from upstream merge
- Remove unused activeRequests field, selectCandidates, findNearestUserMessage, and buildCommandsRuntime from loop.go - Remove duplicate createHTTPClient (moved to pkg/utils by upstream) - Fix golines violation (long line in helpers.go) - Fix misspell: serialises -> serializes - Fix golines: wrap long json.RawMessage literal in test Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
dbd9599913
commit
c65d45dae3
5 changed files with 6 additions and 144 deletions
|
|
@ -295,7 +295,8 @@ func setupAndStartServices(
|
||||||
if strings.HasPrefix(webAppURL, "https://") {
|
if strings.HasPrefix(webAppURL, "https://") {
|
||||||
hostname, tsErr := tailscale.DetectHostname()
|
hostname, tsErr := tailscale.DetectHostname()
|
||||||
if tsErr != nil {
|
if tsErr != nil {
|
||||||
logger.ErrorCF("miniapp", "HTTPS URL configured but Tailscale not available", map[string]any{"error": tsErr.Error()})
|
logger.ErrorCF("miniapp", "HTTPS URL configured but Tailscale not available",
|
||||||
|
map[string]any{"error": tsErr.Error()})
|
||||||
} else {
|
} else {
|
||||||
certDir := filepath.Join(cfg.WorkspacePath(), "state", "certs")
|
certDir := filepath.Join(cfg.WorkspacePath(), "state", "certs")
|
||||||
certFile, keyFile, certErr := tailscale.FetchCert(hostname, certDir)
|
certFile, keyFile, certErr := tailscale.FetchCert(hostname, certDir)
|
||||||
|
|
|
||||||
|
|
@ -66,8 +66,6 @@ type AgentLoop struct {
|
||||||
mcp mcpRuntime
|
mcp mcpRuntime
|
||||||
|
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
// Track active requests for safe provider cleanup
|
|
||||||
activeRequests sync.WaitGroup
|
|
||||||
|
|
||||||
providerCache map[string]providers.LLMProvider
|
providerCache map[string]providers.LLMProvider
|
||||||
|
|
||||||
|
|
@ -2263,69 +2261,6 @@ func (al *AgentLoop) runLLMIteration(
|
||||||
return finalContent, iteration, nil
|
return finalContent, iteration, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// selectCandidates returns the model candidates and resolved model name to use
|
|
||||||
// for a conversation turn. When model routing is configured and the incoming
|
|
||||||
// message scores below the complexity threshold, it returns the light model
|
|
||||||
// candidates instead of the primary ones.
|
|
||||||
//
|
|
||||||
// The returned (candidates, model) pair is used for all LLM calls within one
|
|
||||||
// turn — tool follow-up iterations use the same tier as the initial call so
|
|
||||||
// that a multi-step tool chain doesn't switch models mid-way.
|
|
||||||
func (al *AgentLoop) selectCandidates(
|
|
||||||
agent *AgentInstance,
|
|
||||||
userMsg string,
|
|
||||||
history []providers.Message,
|
|
||||||
) (candidates []providers.FallbackCandidate, model string) {
|
|
||||||
if agent.Router == nil || len(agent.LightCandidates) == 0 {
|
|
||||||
return agent.Candidates, agent.Model
|
|
||||||
}
|
|
||||||
|
|
||||||
_, usedLight, score := agent.Router.SelectModel(userMsg, history, agent.Model)
|
|
||||||
if !usedLight {
|
|
||||||
logger.DebugCF("agent", "Model routing: primary model selected",
|
|
||||||
map[string]any{
|
|
||||||
"agent_id": agent.ID,
|
|
||||||
"score": score,
|
|
||||||
"threshold": agent.Router.Threshold(),
|
|
||||||
})
|
|
||||||
return agent.Candidates, agent.Model
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.InfoCF("agent", "Model routing: light model selected",
|
|
||||||
map[string]any{
|
|
||||||
"agent_id": agent.ID,
|
|
||||||
"light_model": agent.Router.LightModel(),
|
|
||||||
"score": score,
|
|
||||||
"threshold": agent.Router.Threshold(),
|
|
||||||
})
|
|
||||||
return agent.LightCandidates, agent.Router.LightModel()
|
|
||||||
}
|
|
||||||
|
|
||||||
// findNearestUserMessage finds the nearest user message to the given index.
|
|
||||||
// It searches backward first, then forward if no user message is found.
|
|
||||||
func (al *AgentLoop) findNearestUserMessage(messages []providers.Message, mid int) int {
|
|
||||||
originalMid := mid
|
|
||||||
|
|
||||||
for mid > 0 && messages[mid].Role != "user" {
|
|
||||||
mid--
|
|
||||||
}
|
|
||||||
|
|
||||||
if messages[mid].Role == "user" {
|
|
||||||
return mid
|
|
||||||
}
|
|
||||||
|
|
||||||
mid = originalMid
|
|
||||||
for mid < len(messages) && messages[mid].Role != "user" {
|
|
||||||
mid++
|
|
||||||
}
|
|
||||||
|
|
||||||
if mid < len(messages) {
|
|
||||||
return mid
|
|
||||||
}
|
|
||||||
|
|
||||||
return originalMid
|
|
||||||
}
|
|
||||||
|
|
||||||
// callLLMWithRetry calls the LLM with streaming support, fallback chain,
|
// callLLMWithRetry calls the LLM with streaming support, fallback chain,
|
||||||
// and retry logic for timeout and context window errors.
|
// and retry logic for timeout and context window errors.
|
||||||
func (al *AgentLoop) callLLMWithRetry(
|
func (al *AgentLoop) callLLMWithRetry(
|
||||||
|
|
@ -2657,42 +2592,6 @@ func (al *AgentLoop) publishToolMedia(ctx context.Context, result *tools.ToolRes
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (al *AgentLoop) buildCommandsRuntime(agent *AgentInstance, opts *processOptions) *commands.Runtime {
|
|
||||||
registry := al.GetRegistry()
|
|
||||||
cfg := al.GetConfig()
|
|
||||||
rt := &commands.Runtime{
|
|
||||||
Config: cfg,
|
|
||||||
ListAgentIDs: registry.ListAgentIDs,
|
|
||||||
ListDefinitions: al.cmdRegistry.Definitions,
|
|
||||||
GetEnabledChannels: func() []string {
|
|
||||||
if al.channelManager == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return al.channelManager.GetEnabledChannels()
|
|
||||||
},
|
|
||||||
SwitchChannel: func(value string) error {
|
|
||||||
if al.channelManager == nil {
|
|
||||||
return fmt.Errorf("channel manager not initialized")
|
|
||||||
}
|
|
||||||
if _, exists := al.channelManager.GetChannel(value); !exists && value != "cli" {
|
|
||||||
return fmt.Errorf("channel '%s' not found or not enabled", value)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if agent != nil {
|
|
||||||
rt.GetModelInfo = func() (string, string) {
|
|
||||||
return agent.Model, cfg.Agents.Defaults.Provider
|
|
||||||
}
|
|
||||||
rt.SwitchModel = func(value string) (string, error) {
|
|
||||||
oldModel := agent.Model
|
|
||||||
agent.Model = value
|
|
||||||
return oldModel, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rt
|
|
||||||
}
|
|
||||||
|
|
||||||
// forceTextResponse makes a final LLM call without tools when max iterations
|
// forceTextResponse makes a final LLM call without tools when max iterations
|
||||||
// are exhausted, forcing a text response.
|
// are exhausted, forcing a text response.
|
||||||
func (al *AgentLoop) forceTextResponse(ctx context.Context, agent *AgentInstance, messages []providers.Message) string {
|
func (al *AgentLoop) forceTextResponse(ctx context.Context, agent *AgentInstance, messages []providers.Message) string {
|
||||||
|
|
|
||||||
|
|
@ -270,7 +270,7 @@ func buildRequestBody(
|
||||||
func buildTools(tools []ToolDefinition) []any {
|
func buildTools(tools []ToolDefinition) []any {
|
||||||
result := make([]any, len(tools))
|
result := make([]any, len(tools))
|
||||||
for i, tool := range tools {
|
for i, tool := range tools {
|
||||||
// Unmarshal Parameters (json.RawMessage) into map so it serialises
|
// Unmarshal Parameters (json.RawMessage) into map so it serializes
|
||||||
// correctly as a nested JSON object rather than a raw byte string.
|
// correctly as a nested JSON object rather than a raw byte string.
|
||||||
var schema any
|
var schema any
|
||||||
if len(tool.Function.Parameters) > 0 {
|
if len(tool.Function.Parameters) > 0 {
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,9 @@ func TestBuildRequestBody(t *testing.T) {
|
||||||
Function: ToolFunctionDefinition{
|
Function: ToolFunctionDefinition{
|
||||||
Name: "get_weather",
|
Name: "get_weather",
|
||||||
Description: "Get current weather",
|
Description: "Get current weather",
|
||||||
Parameters: json.RawMessage(`{"type":"object","properties":{"location":{"type":"string","description":"City name"}}}`),
|
Parameters: json.RawMessage(
|
||||||
|
`{"type":"object","properties":{"location":{"type":"string","description":"City name"}}}`,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -89,46 +89,6 @@ func (it *APIKeyIterator) Next() (string, bool) {
|
||||||
return key, true
|
return key, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// createHTTPClient creates an HTTP client with optional proxy support
|
|
||||||
func createHTTPClient(proxyURL string, timeout time.Duration) (*http.Client, error) {
|
|
||||||
client := &http.Client{
|
|
||||||
Timeout: timeout,
|
|
||||||
Transport: &http.Transport{
|
|
||||||
MaxIdleConns: 10,
|
|
||||||
|
|
||||||
IdleConnTimeout: 30 * time.Second,
|
|
||||||
|
|
||||||
DisableCompression: false,
|
|
||||||
|
|
||||||
TLSHandshakeTimeout: 15 * time.Second,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if proxyURL != "" {
|
|
||||||
proxy, err := url.Parse(proxyURL)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("invalid proxy URL: %w", err)
|
|
||||||
}
|
|
||||||
scheme := strings.ToLower(proxy.Scheme)
|
|
||||||
switch scheme {
|
|
||||||
case "http", "https", "socks5", "socks5h":
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf(
|
|
||||||
"unsupported proxy scheme %q (supported: http, https, socks5, socks5h)",
|
|
||||||
proxy.Scheme,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if proxy.Host == "" {
|
|
||||||
return nil, fmt.Errorf("invalid proxy URL: missing host")
|
|
||||||
}
|
|
||||||
client.Transport.(*http.Transport).Proxy = http.ProxyURL(proxy)
|
|
||||||
} else {
|
|
||||||
client.Transport.(*http.Transport).Proxy = http.ProxyFromEnvironment
|
|
||||||
}
|
|
||||||
|
|
||||||
return client, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type SearchProvider interface {
|
type SearchProvider interface {
|
||||||
Search(ctx context.Context, query string, count int) (string, error)
|
Search(ctx context.Context, query string, count int) (string, error)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue