feat(ai-sdk): Advisor Reasoning Stack — Clarity+G1+Concept-Injector+Context-Scope+Term-Resolution+E4-Curation+Intent-Signal
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
package ucca
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// FetchByNormIDs loads one representative unit per norm_id from the KB slice
|
||||
// collection — the fetch side of the Concept->Norm recall injector. Returns
|
||||
// LegalSearchResult with the caller-provided concept-relevance score (there is no
|
||||
// similarity query; the injector places them by that score). Returns nil on any
|
||||
// error or when no KB slice is configured (graceful degradation).
|
||||
func (c *LegalRAGClient) FetchByNormIDs(ctx context.Context, normIDs []string, score float64) []LegalSearchResult {
|
||||
if c.kbSliceCollection == "" || len(normIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
should := make([]map[string]interface{}, 0, len(normIDs))
|
||||
for _, nid := range normIDs {
|
||||
should = append(should, map[string]interface{}{"key": "norm_id", "match": map[string]interface{}{"value": nid}})
|
||||
}
|
||||
reqBody := map[string]interface{}{
|
||||
"limit": len(normIDs) * 3,
|
||||
"with_payload": true,
|
||||
"with_vectors": false,
|
||||
"filter": map[string]interface{}{"should": should},
|
||||
}
|
||||
jsonBody, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
url := fmt.Sprintf("%s/collections/%s/points/scroll", c.qdrantURL, c.kbSliceCollection)
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewReader(jsonBody))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
if c.qdrantAPIKey != "" {
|
||||
req.Header.Set("api-key", c.qdrantAPIKey)
|
||||
}
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil
|
||||
}
|
||||
var scrollResp qdrantScrollResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&scrollResp); err != nil {
|
||||
return nil
|
||||
}
|
||||
seen := map[string]bool{}
|
||||
out := make([]LegalSearchResult, 0, len(normIDs))
|
||||
for _, pt := range scrollResp.Result.Points {
|
||||
nid := getString(pt.Payload, "norm_id")
|
||||
if nid == "" || seen[nid] {
|
||||
continue
|
||||
}
|
||||
seen[nid] = true
|
||||
out = append(out, scrollPointToResult(pt.Payload, score))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// scrollPointToResult maps a scroll-point payload to a LegalSearchResult. Mirrors
|
||||
// hitsToResults' payload keys; the score is assigned by the caller (concept rank).
|
||||
func scrollPointToResult(payload map[string]interface{}, score float64) LegalSearchResult {
|
||||
regCode := getString(payload, "regulation_code")
|
||||
if regCode == "" {
|
||||
regCode = getString(payload, "regulation_id")
|
||||
}
|
||||
return LegalSearchResult{
|
||||
Text: getString(payload, "chunk_text"),
|
||||
RegulationCode: regCode,
|
||||
RegulationName: getString(payload, "regulation_name_de"),
|
||||
RegulationShort: getString(payload, "regulation_short"),
|
||||
Category: getString(payload, "category"),
|
||||
Article: getString(payload, "article"),
|
||||
ArticleLabel: getString(payload, "article_label"),
|
||||
Paragraph: getString(payload, "paragraph"),
|
||||
SourceURL: getString(payload, "source_url"),
|
||||
CitationUnit: getString(payload, "citation_unit"),
|
||||
Score: score,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user