fix: format code with golines to pass linter checks
Fixed formatting issues in three files to comply with golines requirements: - pkg/agent/loop.go: Split long function calls and logger statements - pkg/config/config.go: Align struct field tags - pkg/tools/toolloop.go: Split long function calls Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
aca03f5df7
commit
2cb05975bf
3 changed files with 153 additions and 41 deletions
|
|
@ -163,7 +163,10 @@ func registerSharedTools(
|
||||||
|
|
||||||
if cfg.Tools.IsToolEnabled("web") {
|
if cfg.Tools.IsToolEnabled("web") {
|
||||||
searchTool, err := tools.NewWebSearchTool(tools.WebSearchToolOptions{
|
searchTool, err := tools.NewWebSearchTool(tools.WebSearchToolOptions{
|
||||||
BraveAPIKeys: config.MergeAPIKeys(cfg.Tools.Web.Brave.APIKey(), cfg.Tools.Web.Brave.APIKeys()),
|
BraveAPIKeys: config.MergeAPIKeys(
|
||||||
|
cfg.Tools.Web.Brave.APIKey(),
|
||||||
|
cfg.Tools.Web.Brave.APIKeys(),
|
||||||
|
),
|
||||||
BraveMaxResults: cfg.Tools.Web.Brave.MaxResults,
|
BraveMaxResults: cfg.Tools.Web.Brave.MaxResults,
|
||||||
BraveEnabled: cfg.Tools.Web.Brave.Enabled,
|
BraveEnabled: cfg.Tools.Web.Brave.Enabled,
|
||||||
TavilyAPIKeys: config.MergeAPIKeys(
|
TavilyAPIKeys: config.MergeAPIKeys(
|
||||||
|
|
@ -196,7 +199,11 @@ func registerSharedTools(
|
||||||
Proxy: cfg.Tools.Web.Proxy,
|
Proxy: cfg.Tools.Web.Proxy,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.ErrorCF("agent", "Failed to create web search tool", map[string]any{"error": err.Error()})
|
logger.ErrorCF(
|
||||||
|
"agent",
|
||||||
|
"Failed to create web search tool",
|
||||||
|
map[string]any{"error": err.Error()},
|
||||||
|
)
|
||||||
} else if searchTool != nil {
|
} else if searchTool != nil {
|
||||||
agent.Tools.Register(searchTool)
|
agent.Tools.Register(searchTool)
|
||||||
}
|
}
|
||||||
|
|
@ -209,7 +216,11 @@ func registerSharedTools(
|
||||||
cfg.Tools.Web.FetchLimitBytes,
|
cfg.Tools.Web.FetchLimitBytes,
|
||||||
cfg.Tools.Web.PrivateHostWhitelist)
|
cfg.Tools.Web.PrivateHostWhitelist)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.ErrorCF("agent", "Failed to create web fetch tool", map[string]any{"error": err.Error()})
|
logger.ErrorCF(
|
||||||
|
"agent",
|
||||||
|
"Failed to create web fetch tool",
|
||||||
|
map[string]any{"error": err.Error()},
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
agent.Tools.Register(fetchTool)
|
agent.Tools.Register(fetchTool)
|
||||||
}
|
}
|
||||||
|
|
@ -285,7 +296,14 @@ func registerSharedTools(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Team tool
|
// Team tool
|
||||||
teamSubagentManager := tools.NewSubagentManager(provider, agent.Model, agent.Candidates, agent.Workspace, cfg.Tools.Team, msgBus)
|
teamSubagentManager := tools.NewSubagentManager(
|
||||||
|
provider,
|
||||||
|
agent.Model,
|
||||||
|
agent.Candidates,
|
||||||
|
agent.Workspace,
|
||||||
|
cfg.Tools.Team,
|
||||||
|
msgBus,
|
||||||
|
)
|
||||||
teamSubagentManager.SetLLMOptions(agent.MaxTokens, agent.Temperature)
|
teamSubagentManager.SetLLMOptions(agent.MaxTokens, agent.Temperature)
|
||||||
|
|
||||||
teamTool := tools.NewTeamTool(teamSubagentManager, cfg)
|
teamTool := tools.NewTeamTool(teamSubagentManager, cfg)
|
||||||
|
|
@ -492,7 +510,12 @@ func (al *AgentLoop) Run(ctx context.Context) error {
|
||||||
"queue_depth": al.pendingSteeringCountForScope(target.SessionKey),
|
"queue_depth": al.pendingSteeringCountForScope(target.SessionKey),
|
||||||
})
|
})
|
||||||
|
|
||||||
continued, continueErr := al.Continue(ctx, target.SessionKey, target.Channel, target.ChatID)
|
continued, continueErr := al.Continue(
|
||||||
|
ctx,
|
||||||
|
target.SessionKey,
|
||||||
|
target.Channel,
|
||||||
|
target.ChatID,
|
||||||
|
)
|
||||||
if continueErr != nil {
|
if continueErr != nil {
|
||||||
logger.WarnCF("agent", "Failed to continue queued steering",
|
logger.WarnCF("agent", "Failed to continue queued steering",
|
||||||
map[string]any{
|
map[string]any{
|
||||||
|
|
@ -520,14 +543,22 @@ func (al *AgentLoop) Run(ctx context.Context) error {
|
||||||
"queue_depth": al.pendingSteeringCountForScope(target.SessionKey),
|
"queue_depth": al.pendingSteeringCountForScope(target.SessionKey),
|
||||||
})
|
})
|
||||||
|
|
||||||
continued, continueErr := al.Continue(ctx, target.SessionKey, target.Channel, target.ChatID)
|
continued, continueErr := al.Continue(
|
||||||
|
ctx,
|
||||||
|
target.SessionKey,
|
||||||
|
target.Channel,
|
||||||
|
target.ChatID,
|
||||||
|
)
|
||||||
if continueErr != nil {
|
if continueErr != nil {
|
||||||
logger.WarnCF("agent", "Failed to continue queued steering after shutdown drain",
|
logger.WarnCF(
|
||||||
|
"agent",
|
||||||
|
"Failed to continue queued steering after shutdown drain",
|
||||||
map[string]any{
|
map[string]any{
|
||||||
"channel": target.Channel,
|
"channel": target.Channel,
|
||||||
"chat_id": target.ChatID,
|
"chat_id": target.ChatID,
|
||||||
"error": continueErr.Error(),
|
"error": continueErr.Error(),
|
||||||
})
|
},
|
||||||
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if continued == "" {
|
if continued == "" {
|
||||||
|
|
@ -586,11 +617,15 @@ func (al *AgentLoop) drainBusToSteering(ctx context.Context, activeScope, active
|
||||||
msgScope, _, scopeOK := al.resolveSteeringTarget(msg)
|
msgScope, _, scopeOK := al.resolveSteeringTarget(msg)
|
||||||
if !scopeOK || msgScope != activeScope {
|
if !scopeOK || msgScope != activeScope {
|
||||||
if err := al.requeueInboundMessage(msg); err != nil {
|
if err := al.requeueInboundMessage(msg); err != nil {
|
||||||
logger.WarnCF("agent", "Failed to requeue non-steering inbound message", map[string]any{
|
logger.WarnCF(
|
||||||
|
"agent",
|
||||||
|
"Failed to requeue non-steering inbound message",
|
||||||
|
map[string]any{
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
"channel": msg.Channel,
|
"channel": msg.Channel,
|
||||||
"sender_id": msg.SenderID,
|
"sender_id": msg.SenderID,
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
@ -624,7 +659,10 @@ func (al *AgentLoop) Stop() {
|
||||||
al.running.Store(false)
|
al.running.Store(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (al *AgentLoop) publishResponseIfNeeded(ctx context.Context, channel, chatID, response string) {
|
func (al *AgentLoop) publishResponseIfNeeded(
|
||||||
|
ctx context.Context,
|
||||||
|
channel, chatID, response string,
|
||||||
|
) {
|
||||||
if response == "" {
|
if response == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -1074,7 +1112,10 @@ var audioAnnotationRe = regexp.MustCompile(`\[(voice|audio)(?::[^\]]*)?\]`)
|
||||||
// transcribeAudioInMessage resolves audio media refs, transcribes them, and
|
// transcribeAudioInMessage resolves audio media refs, transcribes them, and
|
||||||
// replaces audio annotations in msg.Content with the transcribed text.
|
// replaces audio annotations in msg.Content with the transcribed text.
|
||||||
// Returns the (possibly modified) message and true if audio was transcribed.
|
// Returns the (possibly modified) message and true if audio was transcribed.
|
||||||
func (al *AgentLoop) transcribeAudioInMessage(ctx context.Context, msg bus.InboundMessage) (bus.InboundMessage, bool) {
|
func (al *AgentLoop) transcribeAudioInMessage(
|
||||||
|
ctx context.Context,
|
||||||
|
msg bus.InboundMessage,
|
||||||
|
) (bus.InboundMessage, bool) {
|
||||||
if al.transcriber == nil || al.mediaStore == nil || len(msg.Media) == 0 {
|
if al.transcriber == nil || al.mediaStore == nil || len(msg.Media) == 0 {
|
||||||
return msg, false
|
return msg, false
|
||||||
}
|
}
|
||||||
|
|
@ -1084,7 +1125,11 @@ func (al *AgentLoop) transcribeAudioInMessage(ctx context.Context, msg bus.Inbou
|
||||||
for _, ref := range msg.Media {
|
for _, ref := range msg.Media {
|
||||||
path, meta, err := al.mediaStore.ResolveWithMeta(ref)
|
path, meta, err := al.mediaStore.ResolveWithMeta(ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.WarnCF("voice", "Failed to resolve media ref", map[string]any{"ref": ref, "error": err})
|
logger.WarnCF(
|
||||||
|
"voice",
|
||||||
|
"Failed to resolve media ref",
|
||||||
|
map[string]any{"ref": ref, "error": err},
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if !utils.IsAudioFile(meta.Filename, meta.ContentType) {
|
if !utils.IsAudioFile(meta.Filename, meta.ContentType) {
|
||||||
|
|
@ -1162,7 +1207,11 @@ func (al *AgentLoop) sendTranscriptionFeedback(
|
||||||
ReplyToMessageID: messageID,
|
ReplyToMessageID: messageID,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.WarnCF("voice", "Failed to send transcription feedback", map[string]any{"error": err.Error()})
|
logger.WarnCF(
|
||||||
|
"voice",
|
||||||
|
"Failed to send transcription feedback",
|
||||||
|
map[string]any{"error": err.Error()},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1362,7 +1411,9 @@ func (al *AgentLoop) processMessage(ctx context.Context, msg bus.InboundMessage)
|
||||||
return al.runAgentLoop(ctx, agent, opts)
|
return al.runAgentLoop(ctx, agent, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (al *AgentLoop) resolveMessageRoute(msg bus.InboundMessage) (routing.ResolvedRoute, *AgentInstance, error) {
|
func (al *AgentLoop) resolveMessageRoute(
|
||||||
|
msg bus.InboundMessage,
|
||||||
|
) (routing.ResolvedRoute, *AgentInstance, error) {
|
||||||
registry := al.GetRegistry()
|
registry := al.GetRegistry()
|
||||||
route := registry.ResolveRoute(routing.RouteInput{
|
route := registry.ResolveRoute(routing.RouteInput{
|
||||||
Channel: msg.Channel,
|
Channel: msg.Channel,
|
||||||
|
|
@ -1378,7 +1429,10 @@ func (al *AgentLoop) resolveMessageRoute(msg bus.InboundMessage) (routing.Resolv
|
||||||
agent = registry.GetDefaultAgent()
|
agent = registry.GetDefaultAgent()
|
||||||
}
|
}
|
||||||
if agent == nil {
|
if agent == nil {
|
||||||
return routing.ResolvedRoute{}, nil, fmt.Errorf("no agent available for route (agent_id=%s)", route.AgentID)
|
return routing.ResolvedRoute{}, nil, fmt.Errorf(
|
||||||
|
"no agent available for route (agent_id=%s)",
|
||||||
|
route.AgentID,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return route, agent, nil
|
return route, agent, nil
|
||||||
|
|
@ -2579,12 +2633,15 @@ turnLoop:
|
||||||
}
|
}
|
||||||
|
|
||||||
if steerMsgs := al.dequeueSteeringMessagesForScope(ts.sessionKey); len(steerMsgs) > 0 {
|
if steerMsgs := al.dequeueSteeringMessagesForScope(ts.sessionKey); len(steerMsgs) > 0 {
|
||||||
logger.InfoCF("agent", "Steering arrived after turn completion; continuing turn before finalizing",
|
logger.InfoCF(
|
||||||
|
"agent",
|
||||||
|
"Steering arrived after turn completion; continuing turn before finalizing",
|
||||||
map[string]any{
|
map[string]any{
|
||||||
"agent_id": ts.agent.ID,
|
"agent_id": ts.agent.ID,
|
||||||
"steering_count": len(steerMsgs),
|
"steering_count": len(steerMsgs),
|
||||||
"session_key": ts.sessionKey,
|
"session_key": ts.sessionKey,
|
||||||
})
|
},
|
||||||
|
)
|
||||||
pendingMessages = append(pendingMessages, steerMsgs...)
|
pendingMessages = append(pendingMessages, steerMsgs...)
|
||||||
finalContent = ""
|
finalContent = ""
|
||||||
goto turnLoop
|
goto turnLoop
|
||||||
|
|
@ -2701,11 +2758,18 @@ func (al *AgentLoop) selectCandidates(
|
||||||
"score": score,
|
"score": score,
|
||||||
"threshold": agent.Router.Threshold(),
|
"threshold": agent.Router.Threshold(),
|
||||||
})
|
})
|
||||||
return agent.LightCandidates, resolvedCandidateModel(agent.LightCandidates, agent.Router.LightModel())
|
return agent.LightCandidates, resolvedCandidateModel(
|
||||||
|
agent.LightCandidates,
|
||||||
|
agent.Router.LightModel(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// maybeSummarize triggers summarization if the session history exceeds thresholds.
|
// maybeSummarize triggers summarization if the session history exceeds thresholds.
|
||||||
func (al *AgentLoop) maybeSummarize(agent *AgentInstance, sessionKey string, turnScope turnEventScope) {
|
func (al *AgentLoop) maybeSummarize(
|
||||||
|
agent *AgentInstance,
|
||||||
|
sessionKey string,
|
||||||
|
turnScope turnEventScope,
|
||||||
|
) {
|
||||||
newHistory := agent.Sessions.GetHistory(sessionKey)
|
newHistory := agent.Sessions.GetHistory(sessionKey)
|
||||||
tokenEstimate := al.estimateTokens(newHistory)
|
tokenEstimate := al.estimateTokens(newHistory)
|
||||||
threshold := agent.ContextWindow * agent.SummarizeTokenPercent / 100
|
threshold := agent.ContextWindow * agent.SummarizeTokenPercent / 100
|
||||||
|
|
@ -2739,7 +2803,10 @@ type compressionResult struct {
|
||||||
// prompt is built dynamically by BuildMessages and is NOT stored here.
|
// prompt is built dynamically by BuildMessages and is NOT stored here.
|
||||||
// The compression note is recorded in the session summary so that
|
// The compression note is recorded in the session summary so that
|
||||||
// BuildMessages can include it in the next system prompt.
|
// BuildMessages can include it in the next system prompt.
|
||||||
func (al *AgentLoop) forceCompression(agent *AgentInstance, sessionKey string) (compressionResult, bool) {
|
func (al *AgentLoop) forceCompression(
|
||||||
|
agent *AgentInstance,
|
||||||
|
sessionKey string,
|
||||||
|
) (compressionResult, bool) {
|
||||||
history := agent.Sessions.GetHistory(sessionKey)
|
history := agent.Sessions.GetHistory(sessionKey)
|
||||||
if len(history) <= 2 {
|
if len(history) <= 2 {
|
||||||
return compressionResult{}, false
|
return compressionResult{}, false
|
||||||
|
|
@ -2892,7 +2959,11 @@ func formatToolsForLog(toolDefs []providers.ToolDefinition) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// summarizeSession summarizes the conversation history for a session.
|
// summarizeSession summarizes the conversation history for a session.
|
||||||
func (al *AgentLoop) summarizeSession(agent *AgentInstance, sessionKey string, turnScope turnEventScope) {
|
func (al *AgentLoop) summarizeSession(
|
||||||
|
agent *AgentInstance,
|
||||||
|
sessionKey string,
|
||||||
|
turnScope turnEventScope,
|
||||||
|
) {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
|
|
@ -3180,7 +3251,10 @@ func (al *AgentLoop) handleCommand(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (al *AgentLoop) buildCommandsRuntime(agent *AgentInstance, opts *processOptions) *commands.Runtime {
|
func (al *AgentLoop) buildCommandsRuntime(
|
||||||
|
agent *AgentInstance,
|
||||||
|
opts *processOptions,
|
||||||
|
) *commands.Runtime {
|
||||||
registry := al.GetRegistry()
|
registry := al.GetRegistry()
|
||||||
cfg := al.GetConfig()
|
cfg := al.GetConfig()
|
||||||
rt := &commands.Runtime{
|
rt := &commands.Runtime{
|
||||||
|
|
@ -3221,7 +3295,10 @@ func (al *AgentLoop) buildCommandsRuntime(agent *AgentInstance, opts *processOpt
|
||||||
}
|
}
|
||||||
if agent != nil {
|
if agent != nil {
|
||||||
rt.GetModelInfo = func() (string, string) {
|
rt.GetModelInfo = func() (string, string) {
|
||||||
return agent.Model, resolvedCandidateProvider(agent.Candidates, cfg.Agents.Defaults.Provider)
|
return agent.Model, resolvedCandidateProvider(
|
||||||
|
agent.Candidates,
|
||||||
|
cfg.Agents.Defaults.Provider,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
rt.SwitchModel = func(value string) (string, error) {
|
rt.SwitchModel = func(value string) (string, error) {
|
||||||
value = strings.TrimSpace(value)
|
value = strings.TrimSpace(value)
|
||||||
|
|
@ -3235,7 +3312,12 @@ func (al *AgentLoop) buildCommandsRuntime(agent *AgentInstance, opts *processOpt
|
||||||
return "", fmt.Errorf("failed to initialize model %q: %w", value, err)
|
return "", fmt.Errorf("failed to initialize model %q: %w", value, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
nextCandidates := resolveModelCandidates(cfg, cfg.Agents.Defaults.Provider, modelCfg.Model, agent.Fallbacks)
|
nextCandidates := resolveModelCandidates(
|
||||||
|
cfg,
|
||||||
|
cfg.Agents.Defaults.Provider,
|
||||||
|
modelCfg.Model,
|
||||||
|
agent.Fallbacks,
|
||||||
|
)
|
||||||
if len(nextCandidates) == 0 {
|
if len(nextCandidates) == 0 {
|
||||||
return "", fmt.Errorf("model %q did not resolve to any provider candidates", value)
|
return "", fmt.Errorf("model %q did not resolve to any provider candidates", value)
|
||||||
}
|
}
|
||||||
|
|
@ -3324,7 +3406,10 @@ func (al *AgentLoop) applyExplicitSkillCommand(
|
||||||
|
|
||||||
canonicalSkill, ok := agent.ContextBuilder.ResolveSkillName(fields[1])
|
canonicalSkill, ok := agent.ContextBuilder.ResolveSkillName(fields[1])
|
||||||
if !ok {
|
if !ok {
|
||||||
return true, true, fmt.Sprintf("Unknown skill: %s\nUse /list skills to see installed skills.", fields[1])
|
return true, true, fmt.Sprintf(
|
||||||
|
"Unknown skill: %s\nUse /list skills to see installed skills.",
|
||||||
|
fields[1],
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(fields) == 2 {
|
if len(fields) == 2 {
|
||||||
|
|
|
||||||
|
|
@ -1395,7 +1395,10 @@ func LoadConfig(path string) (*Config, error) {
|
||||||
var cfg *Config
|
var cfg *Config
|
||||||
switch versionInfo.Version {
|
switch versionInfo.Version {
|
||||||
case 0:
|
case 0:
|
||||||
logger.InfoF("config migrate start", map[string]any{"from": versionInfo.Version, "to": CurrentVersion})
|
logger.InfoF(
|
||||||
|
"config migrate start",
|
||||||
|
map[string]any{"from": versionInfo.Version, "to": CurrentVersion},
|
||||||
|
)
|
||||||
// Legacy config (no version field)
|
// Legacy config (no version field)
|
||||||
v, e := loadConfigV0(data)
|
v, e := loadConfigV0(data)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
|
|
@ -1403,10 +1406,16 @@ func LoadConfig(path string) (*Config, error) {
|
||||||
}
|
}
|
||||||
cfg, e = v.Migrate()
|
cfg, e = v.Migrate()
|
||||||
if e != nil {
|
if e != nil {
|
||||||
logger.DebugF("config migrate fail", map[string]any{"from": versionInfo.Version, "to": CurrentVersion})
|
logger.DebugF(
|
||||||
|
"config migrate fail",
|
||||||
|
map[string]any{"from": versionInfo.Version, "to": CurrentVersion},
|
||||||
|
)
|
||||||
return nil, e
|
return nil, e
|
||||||
}
|
}
|
||||||
logger.DebugF("config migrate success", map[string]any{"from": versionInfo.Version, "to": CurrentVersion})
|
logger.DebugF(
|
||||||
|
"config migrate success",
|
||||||
|
map[string]any{"from": versionInfo.Version, "to": CurrentVersion},
|
||||||
|
)
|
||||||
defer func() {
|
defer func() {
|
||||||
_ = SaveConfig(path, cfg)
|
_ = SaveConfig(path, cfg)
|
||||||
}()
|
}()
|
||||||
|
|
@ -1437,9 +1446,11 @@ func LoadConfig(path string) (*Config, error) {
|
||||||
for _, m := range cfg.ModelList {
|
for _, m := range cfg.ModelList {
|
||||||
for _, k := range m.apiKeys {
|
for _, k := range m.apiKeys {
|
||||||
if k != "" && !strings.HasPrefix(k, "enc://") && !strings.HasPrefix(k, "file://") {
|
if k != "" && !strings.HasPrefix(k, "enc://") && !strings.HasPrefix(k, "file://") {
|
||||||
fmt.Fprintf(os.Stderr,
|
fmt.Fprintf(
|
||||||
|
os.Stderr,
|
||||||
"picoclaw: warning: model %q has a plaintext api_key; call SaveConfig to encrypt it\n",
|
"picoclaw: warning: model %q has a plaintext api_key; call SaveConfig to encrypt it\n",
|
||||||
m.ModelName)
|
m.ModelName,
|
||||||
|
)
|
||||||
break // Only warn once per model
|
break // Only warn once per model
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,13 @@ func RunToolLoop(
|
||||||
llmOpts = map[string]any{}
|
llmOpts = map[string]any{}
|
||||||
}
|
}
|
||||||
// 3. Call LLM
|
// 3. Call LLM
|
||||||
response, err := config.Provider.Chat(ctx, messages, providerToolDefs, config.Model, llmOpts)
|
response, err := config.Provider.Chat(
|
||||||
|
ctx,
|
||||||
|
messages,
|
||||||
|
providerToolDefs,
|
||||||
|
config.Model,
|
||||||
|
llmOpts,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.ErrorCF("toolloop", "LLM call failed",
|
logger.ErrorCF("toolloop", "LLM call failed",
|
||||||
map[string]any{
|
map[string]any{
|
||||||
|
|
@ -119,8 +125,11 @@ func RunToolLoop(
|
||||||
// 3.6 Truncation Recovery: LLM response was cut off (max_tokens hit or malformed JSON).
|
// 3.6 Truncation Recovery: LLM response was cut off (max_tokens hit or malformed JSON).
|
||||||
// Inject a recovery message so the LLM knows to retry with a shorter, complete response.
|
// Inject a recovery message so the LLM knows to retry with a shorter, complete response.
|
||||||
if response.FinishReason == "truncated" {
|
if response.FinishReason == "truncated" {
|
||||||
logger.WarnCF("toolloop", "LLM response was truncated (max_tokens hit), injecting recovery message",
|
logger.WarnCF(
|
||||||
map[string]any{"iteration": iteration})
|
"toolloop",
|
||||||
|
"LLM response was truncated (max_tokens hit), injecting recovery message",
|
||||||
|
map[string]any{"iteration": iteration},
|
||||||
|
)
|
||||||
messages = append(messages, providers.Message{
|
messages = append(messages, providers.Message{
|
||||||
Role: "assistant",
|
Role: "assistant",
|
||||||
Content: response.Content,
|
Content: response.Content,
|
||||||
|
|
@ -233,7 +242,14 @@ func RunToolLoop(
|
||||||
|
|
||||||
var toolResult *ToolResult
|
var toolResult *ToolResult
|
||||||
if config.Tools != nil {
|
if config.Tools != nil {
|
||||||
toolResult = config.Tools.ExecuteWithContext(ctx, tc.Name, tc.Arguments, channel, chatID, nil)
|
toolResult = config.Tools.ExecuteWithContext(
|
||||||
|
ctx,
|
||||||
|
tc.Name,
|
||||||
|
tc.Arguments,
|
||||||
|
channel,
|
||||||
|
chatID,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
toolResult = ErrorResult("No tools available")
|
toolResult = ErrorResult("No tools available")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue