feat(search): P3.1 filters and multi-query merge
Native include/exclude domains, time_range, topic, exact_match, min_score, and up to 4 queries fused with RRF. Honesty notes stay off the Warn/fail path. Tavily keeps include_answer false and sets auto_parameters false. Cache key is canonical JSON. CLI flags and skill recipe included.
This commit is contained in:
+54
-23
@@ -19,16 +19,7 @@ type Tavily struct {
|
||||
func (s *Tavily) Name() string { return NameTavily }
|
||||
func (s *Tavily) Ready() bool { return s != nil && s.Client.Ready() }
|
||||
|
||||
type tavilyReq struct {
|
||||
Query string `json:"query"`
|
||||
MaxResults int `json:"max_results"`
|
||||
IncludeAnswer bool `json:"include_answer"`
|
||||
IncludeRawContent bool `json:"include_raw_content"`
|
||||
IncludeImages bool `json:"include_images"`
|
||||
SearchDepth string `json:"search_depth"`
|
||||
}
|
||||
|
||||
func (s *Tavily) Search(ctx context.Context, query string, k int) SourceOut {
|
||||
func (s *Tavily) Search(ctx context.Context, query string, k int, f Filter) SourceOut {
|
||||
if s == nil || s.Client == nil {
|
||||
return SourceOut{Warn: NameTavily + ": error"}
|
||||
}
|
||||
@@ -41,14 +32,7 @@ func (s *Tavily) Search(ctx context.Context, query string, k int) SourceOut {
|
||||
if k > 20 {
|
||||
k = 20
|
||||
}
|
||||
payload, _ := json.Marshal(tavilyReq{
|
||||
Query: query,
|
||||
MaxResults: k,
|
||||
IncludeAnswer: false,
|
||||
IncludeRawContent: false,
|
||||
IncludeImages: false,
|
||||
SearchDepth: "basic",
|
||||
})
|
||||
payload, _ := json.Marshal(tavilyBody(query, k, f))
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, s.Client.URL.String(), bytes.NewReader(payload))
|
||||
if err != nil {
|
||||
return SourceOut{Warn: warnErr(NameTavily, err)}
|
||||
@@ -76,14 +60,58 @@ func (s *Tavily) Search(ctx context.Context, query string, k int) SourceOut {
|
||||
return SourceOut{Hits: hits, Credits: credits}
|
||||
}
|
||||
|
||||
func tavilyBody(query string, k int, f Filter) map[string]any {
|
||||
body := map[string]any{
|
||||
"query": query,
|
||||
"max_results": k,
|
||||
"include_answer": false,
|
||||
"include_raw_content": false,
|
||||
"include_images": false,
|
||||
"search_depth": "basic",
|
||||
"auto_parameters": false,
|
||||
}
|
||||
if len(f.IncludeDomains) > 0 {
|
||||
body["include_domains"] = f.IncludeDomains
|
||||
mode := f.IncludeDomainsMode
|
||||
if mode == "" {
|
||||
mode = "restrict"
|
||||
}
|
||||
body["include_domains_mode"] = mode
|
||||
}
|
||||
if len(f.ExcludeDomains) > 0 {
|
||||
body["exclude_domains"] = f.ExcludeDomains
|
||||
}
|
||||
if f.TimeRange != "" {
|
||||
body["time_range"] = f.TimeRange
|
||||
} else {
|
||||
if f.StartDate != "" {
|
||||
body["start_date"] = f.StartDate
|
||||
}
|
||||
if f.EndDate != "" {
|
||||
body["end_date"] = f.EndDate
|
||||
}
|
||||
}
|
||||
if f.Topic != "" && f.Topic != "general" {
|
||||
body["topic"] = f.Topic
|
||||
}
|
||||
if f.ExactMatch {
|
||||
body["exact_match"] = true
|
||||
}
|
||||
if f.Topic == "news" || f.TimeRange != "" || f.StartDate != "" || f.EndDate != "" {
|
||||
body["include_published_date"] = true
|
||||
}
|
||||
return body
|
||||
}
|
||||
|
||||
func mapTavily(raw []byte) ([]Hit, int, error) {
|
||||
var env struct {
|
||||
Answer string `json:"answer"`
|
||||
Results []struct {
|
||||
URL string `json:"url"`
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
Score float64 `json:"score"`
|
||||
URL string `json:"url"`
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
Score float64 `json:"score"`
|
||||
PublishedDate string `json:"published_date"`
|
||||
} `json:"results"`
|
||||
Usage struct {
|
||||
Credits int `json:"credits"`
|
||||
@@ -98,7 +126,10 @@ func mapTavily(raw []byte) ([]Hit, int, error) {
|
||||
if r.URL == "" {
|
||||
continue
|
||||
}
|
||||
hits = append(hits, Hit{URL: r.URL, Title: r.Title, Snippet: r.Content, Score: r.Score, Source: NameTavily})
|
||||
hits = append(hits, Hit{
|
||||
URL: r.URL, Title: r.Title, Snippet: r.Content,
|
||||
Score: r.Score, Source: NameTavily, PublishedDate: r.PublishedDate,
|
||||
})
|
||||
}
|
||||
credits := env.Usage.Credits
|
||||
if credits <= 0 {
|
||||
|
||||
Reference in New Issue
Block a user