Major changes: - Add Go backend (backend/) with microservices architecture - Enhanced master-agents-svc: reranker, content-classifier, stealth-crawler, proxy-manager, media-search, fastClassifier, language detection - New web-svc widgets: KnowledgeCard, ProductCard, ProfileCard, VideoCard, UnifiedCard, CardGallery, InlineImageGallery, SourcesPanel, RelatedQuestions - Improved discover-svc with discover-db integration - Docker deployment improvements (Caddyfile, vendor.sh, BUILD.md) - Library-svc: project_id schema migration - Remove deprecated finance-svc and travel-svc - Localization improvements across services Made-with: Cursor
129 lines
2.6 KiB
Go
129 lines
2.6 KiB
Go
package agent
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/gooseek/backend/internal/llm"
|
|
"github.com/gooseek/backend/internal/search"
|
|
"github.com/gooseek/backend/internal/session"
|
|
"github.com/gooseek/backend/internal/types"
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
type ResearchInput struct {
|
|
ChatHistory []llm.Message
|
|
FollowUp string
|
|
Classification *ClassificationResult
|
|
Mode Mode
|
|
Sources []string
|
|
Locale string
|
|
DetectedLang string
|
|
IsArticleSummary bool
|
|
}
|
|
|
|
func research(
|
|
ctx context.Context,
|
|
sess *session.Session,
|
|
llmClient llm.Client,
|
|
searchClient *search.SearXNGClient,
|
|
input ResearchInput,
|
|
) ([]types.Chunk, error) {
|
|
maxIterations := 1
|
|
switch input.Mode {
|
|
case ModeBalanced:
|
|
maxIterations = 3
|
|
case ModeQuality:
|
|
maxIterations = 10
|
|
}
|
|
|
|
researchBlockID := uuid.New().String()
|
|
sess.EmitBlock(types.NewResearchBlock(researchBlockID))
|
|
|
|
allResults := make([]types.Chunk, 0)
|
|
seenURLs := make(map[string]bool)
|
|
|
|
searchQuery := input.Classification.StandaloneFollowUp
|
|
if searchQuery == "" {
|
|
searchQuery = input.FollowUp
|
|
}
|
|
|
|
for i := 0; i < maxIterations; i++ {
|
|
queries := generateSearchQueries(searchQuery)
|
|
|
|
sess.UpdateBlock(researchBlockID, []session.Patch{
|
|
{
|
|
Op: "replace",
|
|
Path: "/data/subSteps",
|
|
Value: []types.ResearchSubStep{
|
|
{
|
|
ID: uuid.New().String(),
|
|
Type: "searching",
|
|
Searching: queries,
|
|
},
|
|
},
|
|
},
|
|
})
|
|
|
|
for _, q := range queries {
|
|
resp, err := searchClient.Search(ctx, q, &search.SearchOptions{
|
|
Categories: categoriesToSearch(input.Sources),
|
|
PageNo: 1,
|
|
})
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
for _, r := range resp.Results {
|
|
if r.URL != "" && !seenURLs[r.URL] {
|
|
seenURLs[r.URL] = true
|
|
allResults = append(allResults, r.ToChunk())
|
|
}
|
|
}
|
|
}
|
|
|
|
if input.Mode == ModeSpeed {
|
|
break
|
|
}
|
|
|
|
if len(allResults) >= 20 && input.Mode == ModeBalanced {
|
|
break
|
|
}
|
|
|
|
if len(allResults) >= 50 {
|
|
break
|
|
}
|
|
}
|
|
|
|
return allResults, nil
|
|
}
|
|
|
|
func categoriesToSearch(sources []string) []string {
|
|
if len(sources) == 0 {
|
|
return []string{"general", "news"}
|
|
}
|
|
|
|
categories := make([]string, 0)
|
|
for _, s := range sources {
|
|
switch s {
|
|
case "web":
|
|
categories = append(categories, "general")
|
|
case "discussions":
|
|
categories = append(categories, "social media")
|
|
case "academic":
|
|
categories = append(categories, "science")
|
|
case "news":
|
|
categories = append(categories, "news")
|
|
case "images":
|
|
categories = append(categories, "images")
|
|
case "videos":
|
|
categories = append(categories, "videos")
|
|
}
|
|
}
|
|
|
|
if len(categories) == 0 {
|
|
return []string{"general"}
|
|
}
|
|
|
|
return categories
|
|
}
|