File size: 4,536 Bytes
9f87ec0 db764ae 9f87ec0 db764ae 9f87ec0 db764ae 9f87ec0 db764ae 9f87ec0 db764ae 9f87ec0 db764ae 9f87ec0 db764ae 9f87ec0 db764ae 9f87ec0 db764ae 9f87ec0 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 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | import { useState, useEffect } from "react";
import { api, getErrorMessage } from "../api";
import type { W2VInitResponse } from "../types";
import StatusMessage from "./StatusMessage";
import LogViewer from "./LogViewer";
import MetricCard from "./MetricCard";
interface Props {
onReady: (ready: boolean, info?: { vocab_size: number; sentences: number; vector_size: number }) => void;
}
export default function Word2VecPanel({ onReady }: Props) {
const [statusChecked, setStatusChecked] = useState(false);
const [trainResult, setTrainResult] = useState<W2VInitResponse | null>(null);
const [vectorSize, setVectorSize] = useState(100);
const [windowSize, setWindowSize] = useState(5);
const [w2vEpochs, setW2vEpochs] = useState(50);
const [showAdvanced, setShowAdvanced] = useState(false);
const [initLoading, setInitLoading] = useState(false);
const [error, setError] = useState("");
useEffect(() => {
api.w2vStatus().then(res => {
if (res.ready) {
onReady(true, { vocab_size: res.vocab_size!, sentences: res.sentences!, vector_size: res.vector_size! });
}
setStatusChecked(true);
}).catch(() => setStatusChecked(true));
}, []);
async function handleTrainFromEngine() {
setInitLoading(true); setError(""); setTrainResult(null);
try {
const res = await api.w2vInitFromEngine({
vector_size: vectorSize,
window: windowSize,
epochs: w2vEpochs,
});
setTrainResult(res);
} catch (err) {
setError(getErrorMessage(err));
} finally {
setInitLoading(false);
}
}
if (!statusChecked) {
return <div className="panel"><p>Checking Word2Vec status...</p></div>;
}
// Training complete — show results + continue button
if (trainResult) {
return (
<div>
<div className="panel">
<h2>Training Complete</h2>
<div className="metric-grid">
<MetricCard value={trainResult.vocab_size} label="Vocabulary" />
<MetricCard value={trainResult.sentences} label="Sentences" />
<MetricCard value={trainResult.vector_size} label="Dimensions" />
<MetricCard value={`${trainResult.seconds}s`} label="Train Time" />
</div>
<StatusMessage type="ok" message="Word2Vec model trained and saved. It will persist across restarts." />
<button className="btn btn-primary" style={{ marginTop: 12 }}
onClick={() => onReady(true, { vocab_size: trainResult.vocab_size, sentences: trainResult.sentences, vector_size: trainResult.vector_size })}>
Continue to Analysis
</button>
</div>
<LogViewer active={false} />
</div>
);
}
// Training form
return (
<div>
<div className="panel">
<h2>Word2Vec Baseline (gensim)</h2>
<p className="panel-desc">
Static embeddings — one vector per word, no context awareness.
Train on all documents loaded in the engine to use as a baseline comparison.
</p>
<button className="advanced-toggle" onClick={() => setShowAdvanced(!showAdvanced)}>
{showAdvanced ? "\u25be" : "\u25b8"} Advanced Settings
</button>
{showAdvanced && (
<div className="advanced-section">
<div className="form-row">
<div className="form-group" style={{ maxWidth: 120 }}>
<label>Vector Size</label>
<input type="number" value={vectorSize} onChange={e => setVectorSize(+e.target.value)} min={50} max={300} />
</div>
<div className="form-group" style={{ maxWidth: 120 }}>
<label>Window</label>
<input type="number" value={windowSize} onChange={e => setWindowSize(+e.target.value)} min={2} max={15} />
</div>
<div className="form-group" style={{ maxWidth: 120 }}>
<label>Epochs</label>
<input type="number" value={w2vEpochs} onChange={e => setW2vEpochs(+e.target.value)} min={5} max={200} />
</div>
</div>
</div>
)}
<button className="btn btn-primary" onClick={handleTrainFromEngine}
disabled={initLoading} style={{ marginTop: 8 }}>
{initLoading ? <><span className="spinner" /> Training on all engine documents...</> : "Train Word2Vec"}
</button>
<LogViewer active={initLoading} />
</div>
{error && <StatusMessage type="err" message={error} />}
</div>
);
}
|