mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-16 19:59:56 +02:00
Les outils web des tours de la façade (web_search, web_images, web_open,
web_read, web_grep) ne passent plus par /tool/exec du Go : src/webtools.rs
porte chat_internet_tools.go, chat_internet.go et chat_web_images.go. Le
cache des pages (10 min, clé MD5 des options comme le Go) est dans la
façade ; client Crawl4AI au corps près, réponses lues comme json.Unmarshal ;
résultats DuckDuckGo tirés du markdown (uddg, entités, extraits) ;
recherche d'images avec le jeton vqd et les cookies gardés par hôte ;
web_grep sur les regex du Go, erreurs au texte de regexp.
Le moteur intégré lit le HTML avec go-readability et html-to-markdown, que
rien en Rust ne reproduit au caractère près : la conversion d'une page et la
lecture des résultats restent au Go, derrière /internal/v1/web/fetch et
/internal/v1/web/search (sans état). web_screenshot reste au Go.
Le traducteur go_regex refuse la répétition d'une répétition et les comptes
au-delà de 1000, et rend littéral un « { » qui n'ouvre pas de répétition,
comme le Go (motifs retry_patterns compris).
Parité : journal de 74 appels tiré du Go (parity_web_tools_test.go →
tests/parity_web/outils.json : résultats, requêtes reçues par les faux
Crawl4AI et DuckDuckGo, pages demandées au moteur intégré), rejoué par
tests/webtools.rs ; un tour de la façade qui ouvre une page (tests/lot6.rs) ;
TestInternalMoteurWebIntegre côté Go.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
59 lines
2.0 KiB
Go
59 lines
2.0 KiB
Go
package loki
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// L'API interne du moteur web intégré (lot 42) : la conversion d'une page et
|
|
// la recherche DuckDuckGo, telles que la façade les demande.
|
|
func TestInternalMoteurWebIntegre(t *testing.T) {
|
|
site := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
switch r.URL.Path {
|
|
case "/page.txt":
|
|
w.Header().Set("Content-Type", "text/plain")
|
|
_, _ = io.WriteString(w, "bonjour\n")
|
|
case "/html/":
|
|
w.Header().Set("Content-Type", "text/html")
|
|
_, _ = io.WriteString(w, `<div class="result"><a class="result__a" href="https://r.example/">R</a>`+
|
|
`<a class="result__snippet">résumé</a></div>`)
|
|
default:
|
|
http.NotFound(w, r)
|
|
}
|
|
}))
|
|
defer site.Close()
|
|
old := ddgHTMLBase
|
|
ddgHTMLBase = site.URL
|
|
defer func() { ddgHTMLBase = old }()
|
|
|
|
call := func(h http.HandlerFunc, method, body string) (int, map[string]any) {
|
|
w := httptest.NewRecorder()
|
|
h(w, httptest.NewRequest(method, "/x", strings.NewReader(body)))
|
|
var out map[string]any
|
|
_ = json.Unmarshal(w.Body.Bytes(), &out)
|
|
return w.Code, out
|
|
}
|
|
if code, out := call(handleInternalWebFetch, "POST", `{"url":"`+site.URL+`/page.txt"}`); code != 200 || out["markdown"] != "bonjour\n" {
|
|
t.Fatalf("fetch : %d %v", code, out)
|
|
}
|
|
if code, out := call(handleInternalWebFetch, "POST", `{"url":"`+site.URL+`/absente"}`); code != 200 ||
|
|
out["error"] != "HTTP 404 sur "+site.URL+"/absente" {
|
|
t.Fatalf("fetch absente : %d %v", code, out)
|
|
}
|
|
code, out := call(handleInternalWebSearch, "POST", `{"query":"q","limit":5}`)
|
|
res, _ := json.Marshal(out["results"])
|
|
if code != 200 || string(res) != `[{"snippet":"résumé","title":"R","url":"https://r.example/"}]` {
|
|
t.Fatalf("search : %d %s", code, res)
|
|
}
|
|
if code, _ := call(handleInternalWebSearch, "GET", ""); code != http.StatusMethodNotAllowed {
|
|
t.Fatalf("GET : %d", code)
|
|
}
|
|
if code, _ := call(handleInternalWebFetch, "POST", "pas du json"); code != http.StatusBadRequest {
|
|
t.Fatalf("corps illisible : %d", code)
|
|
}
|
|
}
|