package engine import ( "fmt" "net/url" "sort" "strings" "time" "github.com/local/glpi-neural-brain/internal/graph" "github.com/local/glpi-neural-brain/internal/model" ) type researchGraphRefs struct { NodeIDs []string EdgeIDs []string } func newResearchRunID(prefix, query string) string { return graph.ID(prefix, strings.TrimSpace(query), fmt.Sprintf("%d", time.Now().UnixNano())) } func researchEventMetadata(trigger, researchID, query string, results []model.ResearchResult, elapsed time.Duration) map[string]any { titles := make([]string, 0, min(5, len(results))) domains := make([]string, 0, min(5, len(results))) urls := make([]string, 0, min(5, len(results))) seenDomains := map[string]bool{} for _, result := range results { if title := strings.TrimSpace(result.Title); title != "" && len(titles) < 5 { titles = append(titles, title) } if rawURL := strings.TrimSpace(result.URL); rawURL != "" { if len(urls) < 5 { urls = append(urls, rawURL) } if parsed, err := url.Parse(rawURL); err == nil { domain := strings.TrimPrefix(strings.ToLower(parsed.Hostname()), "www.") if domain != "" && !seenDomains[domain] && len(domains) < 5 { seenDomains[domain] = true domains = append(domains, domain) } } } } sort.Strings(domains) return map[string]any{ "trigger": trigger, "research_id": researchID, "research_query": query, "result_count": len(results), "result_titles": titles, "result_domains": domains, "result_urls": urls, "duration_ms": elapsed.Milliseconds(), "animation_min_ms": 2000, } } func mergeResearchMetadata(base map[string]any, extra map[string]any) map[string]any { out := make(map[string]any, len(base)+len(extra)) for key, value := range base { out[key] = value } for key, value := range extra { out[key] = value } return out } func uniqueResearchRefs(refs researchGraphRefs) researchGraphRefs { refs.NodeIDs = unique(refs.NodeIDs) refs.EdgeIDs = unique(refs.EdgeIDs) return refs }