File size: 2,680 Bytes
db764ae
 
 
 
 
 
9f87ec0
db764ae
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9f87ec0
 
 
 
 
 
 
 
 
db764ae
9f87ec0
db764ae
9f87ec0
db764ae
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
import { useState } from "react";
import { api } from "../api";
import type { QueryResultItem } from "../types";
import { useApiCall } from "../hooks/useApiCall";
import ScoreBar from "./ScoreBar";
import StatusMessage from "./StatusMessage";
import DocumentViewer from "./DocumentViewer";

export default function SemanticSearch() {
  const [query, setQuery] = useState("");
  const [topK, setTopK] = useState(10);
  const { data: results, loading, error, run } = useApiCall<QueryResultItem[]>();

  async function handleSearch() {
    if (!query.trim()) return;
    await run(() => api.query({ text: query, top_k: topK }).then((r) => r.results));
  }

  return (
    <div>
      <div className="panel">
        <h2>Semantic Search</h2>
        <p className="panel-desc">
          Find passages most semantically similar to your query across the entire corpus.
        </p>
        <div className="form-row">
          <div className="form-group">
            <label>Query</label>
            <input
              value={query}
              onChange={(e) => setQuery(e.target.value)}
              placeholder="e.g. a place where children learn and take tests"
              onKeyDown={(e) => e.key === "Enter" && handleSearch()}
            />
          </div>
          <div className="form-group form-group-sm">
            <label>Top K</label>
            <input type="number" value={topK} onChange={(e) => setTopK(+e.target.value)} min={1} max={50} />
          </div>
          <div className="form-group form-group-sm">
            <label>&nbsp;</label>
            <button className="btn btn-primary" onClick={handleSearch} disabled={loading || !query.trim()}>
              {loading ? "Searching..." : "Search"}
            </button>
          </div>
        </div>
      </div>

      {error && <StatusMessage type="err" message={error} />}

      {results && (
        <div className="panel">
          <h3>Results ({results.length})</h3>
          {results.map((r) => (
            <DocumentViewer key={`${r.doc_id}-${r.chunk_index}`} docId={r.doc_id}>
              <div className="result-card" style={{ cursor: "pointer" }}>
                <div className="result-header">
                  <div>
                    <span className="badge">#{r.rank}</span>{" "}
                    <span className="badge">{r.doc_id}</span>{" "}
                    <span className="tag">chunk {r.chunk_index}</span>
                  </div>
                  <ScoreBar score={r.score} />
                </div>
                <div className="result-text">{r.text}</div>
              </div>
            </DocumentViewer>
          ))}
        </div>
      )}
    </div>
  );
}