Files
glpi-neural-brain/internal/engine/research_diagnostics.go
2026-08-07 08:33:41 +02:00

110 lines
4.1 KiB
Go

package engine
import (
"context"
"errors"
"fmt"
"log/slog"
"strings"
"time"
"github.com/local/glpi-neural-brain/internal/model"
"github.com/local/glpi-neural-brain/internal/research"
)
type ResearchTestResult struct {
OK bool `json:"ok"`
Query string `json:"query"`
Diagnostic research.Diagnostic `json:"diagnostic"`
Results []model.ResearchResult `json:"results"`
}
func (e *Engine) ResearchStatus() research.Diagnostic {
if e == nil || e.Research == nil {
return research.Diagnostic{Configured: false, OK: false, ErrorKind: "not_configured", Error: "SearXNG ist nicht konfiguriert"}
}
status := e.Research.Status()
if !e.ResearchEnabledForRuntime() {
status.OK = false
status.ErrorKind = "disabled"
status.Error = "BRAIN_RESEARCH_ENABLED und Autonomous Research sind deaktiviert"
}
return status
}
func (e *Engine) TestResearch(ctx context.Context, query string, limit int) (ResearchTestResult, error) {
query = sanitizeSearchQuerySiteFilters(strings.TrimSpace(query))
if query == "" {
query = "Btrfs Snapshots und ZFS History Unterschiede Timeline"
}
if limit < 1 {
limit = 4
}
if limit > 10 {
limit = 10
}
if !e.ResearchEnabledForRuntime() {
err := errors.New("SearXNG-Recherche ist deaktiviert")
return ResearchTestResult{Query: query, Diagnostic: e.ResearchStatus()}, err
}
if e.Research == nil {
err := errors.New("SearXNG-Client ist nicht konfiguriert")
return ResearchTestResult{Query: query, Diagnostic: e.ResearchStatus()}, err
}
researchID := newResearchRunID("research-test", query)
started := time.Now()
startMetadata := map[string]any{
"trigger": "diagnostic",
"research_id": researchID,
"research_query": query,
"animation_min_ms": 2000,
"test_only": true,
"searxng_base_url": e.Research.Status().BaseURL,
}
e.Broker.Publish(model.Activity{Type: "research.test.started", Source: "searxng", Phase: "diagnostic", Message: "SearXNG-Verbindung und JSON-Suche werden direkt getestet", Strength: .92, Metadata: startMetadata})
var results []model.ResearchResult
var diagnostic research.Diagnostic
err := e.withSharedResearchWork(ctx, "searxng.diagnostic", func() error {
var searchErr error
results, diagnostic, searchErr = e.Research.SearchDetailed(ctx, query, limit)
return searchErr
})
if err != nil {
metadata := mergeResearchMetadata(startMetadata, researchDiagnosticMetadata(diagnostic))
metadata["duration_ms"] = time.Since(started).Milliseconds()
metadata["error"] = err.Error()
slog.Warn("SearXNG diagnostic search failed", "base_url", diagnostic.BaseURL, "kind", diagnostic.ErrorKind, "duration_ms", diagnostic.DurationMS, "error", err)
e.Broker.Publish(model.Activity{Type: "research.test.failed", Source: "searxng", Phase: "diagnostic", Message: "SearXNG-Test fehlgeschlagen", Strength: .4, Metadata: metadata})
return ResearchTestResult{OK: false, Query: query, Diagnostic: diagnostic}, err
}
metadata := mergeResearchMetadata(researchEventMetadata("diagnostic", researchID, query, results, time.Since(started)), researchDiagnosticMetadata(diagnostic))
metadata["test_only"] = true
message := fmt.Sprintf("SearXNG-Test erfolgreich · %d verwertbare Treffer", len(results))
if len(results) == 0 {
message = "SearXNG-Test erfolgreich · JSON-Antwort erhalten, aber keine verwertbaren Treffer"
}
e.Broker.Publish(model.Activity{Type: "research.test.results", Source: "searxng", Phase: "diagnostic", Message: message, Strength: 1, Metadata: metadata})
return ResearchTestResult{OK: true, Query: query, Diagnostic: diagnostic, Results: results}, nil
}
func researchDiagnosticMetadata(diagnostic research.Diagnostic) map[string]any {
metadata := map[string]any{
"searxng_configured": diagnostic.Configured,
"searxng_ok": diagnostic.OK,
"searxng_base_url": diagnostic.BaseURL,
"searxng_http_status": diagnostic.HTTPStatus,
"searxng_content_type": diagnostic.ContentType,
"error_kind": diagnostic.ErrorKind,
}
if diagnostic.DurationMS > 0 {
metadata["duration_ms"] = diagnostic.DurationMS
}
if diagnostic.Error != "" {
metadata["error"] = diagnostic.Error
}
return metadata
}