fix(tools): improve error handling and search result formatting
- focus: log warnings on DeleteKV and corrupt knowledge block instead of silently swallowing errors - retrieval: include search method (keyword/semantic) in result messages for clearer agent context - subagent test: respect iterator yield protocol by checking return value
This commit is contained in:
parent
e6c81dd5b3
commit
e7c53ca5df
4 changed files with 16 additions and 11 deletions
|
|
@ -197,8 +197,9 @@ func (t *CompleteFocusTool) Execute(ctx context.Context, args map[string]interfa
|
|||
pruned := t.pruneHistory(history, state.CheckpointIndex)
|
||||
t.sessions.SetHistory(sk, pruned)
|
||||
|
||||
// Clean up focus state
|
||||
_ = t.delegate.DeleteKV(ctx, focusAgentID, focusKey)
|
||||
if err := t.delegate.DeleteKV(ctx, focusAgentID, focusKey); err != nil {
|
||||
logger.WarnCF("focus", "failed to clean up focus state", map[string]interface{}{"error": err, "session": sk})
|
||||
}
|
||||
|
||||
msgsBefore := len(history)
|
||||
msgsAfter := len(pruned)
|
||||
|
|
@ -252,7 +253,9 @@ func (t *CompleteFocusTool) appendKnowledge(ctx context.Context, sessionKey, top
|
|||
kb := &KnowledgeBlock{}
|
||||
raw, err := t.delegate.GetKV(ctx, focusAgentID, kvKey)
|
||||
if err == nil && raw != "" {
|
||||
_ = jsonv2.Unmarshal([]byte(raw), kb)
|
||||
if uerr := jsonv2.Unmarshal([]byte(raw), kb); uerr != nil {
|
||||
logger.WarnCF("focus", "corrupt knowledge block, resetting", map[string]interface{}{"error": uerr, "key": kvKey})
|
||||
}
|
||||
}
|
||||
|
||||
kb.Entries = append(kb.Entries, KnowledgeEntry{
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ func (t *KeywordSearchTool) Execute(ctx context.Context, args map[string]interfa
|
|||
return ErrorResult(fmt.Sprintf("keyword search failed: %v", err))
|
||||
}
|
||||
|
||||
return SilentResult(formatSearchResults("keyword_search", query, results))
|
||||
return SilentResult(formatSearchResults("keyword", query, results))
|
||||
}
|
||||
|
||||
// SemanticSearchTool performs vector ANN search using embeddings.
|
||||
|
|
@ -136,7 +136,7 @@ func (t *SemanticSearchTool) Execute(ctx context.Context, args map[string]interf
|
|||
return ErrorResult(fmt.Sprintf("semantic search failed: %v", err))
|
||||
}
|
||||
|
||||
return SilentResult(formatSearchResults("semantic_search", query, results))
|
||||
return SilentResult(formatSearchResults("semantic", query, results))
|
||||
}
|
||||
|
||||
// ChunkReadTool loads full document/chunk content by ID.
|
||||
|
|
@ -185,13 +185,13 @@ func (t *ChunkReadTool) Execute(ctx context.Context, args map[string]interface{}
|
|||
return SilentResult(content)
|
||||
}
|
||||
|
||||
func formatSearchResults(source, query string, results []memory.SearchResult) string {
|
||||
func formatSearchResults(method, query string, results []memory.SearchResult) string {
|
||||
if len(results) == 0 {
|
||||
return fmt.Sprintf("No results found for: %s", query)
|
||||
return fmt.Sprintf("No %s results found for: %s", method, query)
|
||||
}
|
||||
|
||||
var sb strings.Builder
|
||||
fmt.Fprintf(&sb, "Found %d results for '%s':\n\n", len(results), query)
|
||||
fmt.Fprintf(&sb, "Found %d %s results for '%s':\n\n", len(results), method, query)
|
||||
|
||||
for i, r := range results {
|
||||
fmt.Fprintf(&sb, "%d. [%s] (score: %.2f) id=%s\n", i+1, r.Source, r.Score, r.ID)
|
||||
|
|
|
|||
|
|
@ -55,8 +55,8 @@ func TestChunkReadTool_MissingID(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestFormatSearchResults_Empty(t *testing.T) {
|
||||
output := formatSearchResults("test", "query", nil)
|
||||
assert.Contains(t, output, "No results found")
|
||||
output := formatSearchResults("keyword", "query", nil)
|
||||
assert.Contains(t, output, "No keyword results found")
|
||||
}
|
||||
|
||||
func TestFormatSearchResults_WithResults(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -39,7 +39,9 @@ func (m *MockLanguageModel) Stream(_ context.Context, call fantasy.Call) (fantas
|
|||
return nil, err
|
||||
}
|
||||
return func(yield func(fantasy.StreamPart) bool) {
|
||||
yield(fantasy.StreamPart{Type: fantasy.StreamPartTypeTextDelta, Delta: resp.Content.Text()})
|
||||
if !yield(fantasy.StreamPart{Type: fantasy.StreamPartTypeTextDelta, Delta: resp.Content.Text()}) {
|
||||
return
|
||||
}
|
||||
yield(fantasy.StreamPart{Type: fantasy.StreamPartTypeFinish, FinishReason: fantasy.FinishReasonStop})
|
||||
}, nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue