Skip to content

Commit e000e01

Browse files
Merge remote-tracking branch 'origin/codex/create-engaging-wiki-for-project-documentation'
2 parents 03aead0 + 62ef150 commit e000e01

8 files changed

Lines changed: 1417 additions & 13 deletions

File tree

apps/embedding-explorer/app.js

Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,314 @@
4949
return;
5050
}
5151

52+
const summaryCells = summaryGrid ? Array.from(summaryGrid.querySelectorAll('dd')) : [];
53+
const valueTableBody = valueTable;
54+
let activeSampleId = '';
55+
56+
function formatDecimal(value) {
57+
if (!Number.isFinite(value)) {
58+
return '0.000';
59+
}
60+
const rounded = Math.round(value * 1000) / 1000;
61+
return rounded.toFixed(3);
62+
}
63+
64+
function formatRange(min, max) {
65+
if (!Number.isFinite(min) || !Number.isFinite(max)) {
66+
return '— / —';
67+
}
68+
return `${formatDecimal(min)} / ${formatDecimal(max)}`;
69+
}
70+
71+
function calculateStats(vector) {
72+
const values = Array.isArray(vector) ? vector.map((entry) => Number(entry)).filter((entry) => Number.isFinite(entry)) : [];
73+
const length = values.length;
74+
75+
if (!length) {
76+
return {
77+
values: [],
78+
dimensions: 0,
79+
magnitude: 0,
80+
mean: 0,
81+
deviation: 0,
82+
min: null,
83+
max: null,
84+
zeroShare: 0,
85+
};
86+
}
87+
88+
let sum = 0;
89+
let sumSquares = 0;
90+
let min = values[0];
91+
let max = values[0];
92+
let zeroCount = 0;
93+
94+
values.forEach((value) => {
95+
sum += value;
96+
sumSquares += value * value;
97+
if (value < min) {
98+
min = value;
99+
}
100+
if (value > max) {
101+
max = value;
102+
}
103+
if (Math.abs(value) < 1e-9) {
104+
zeroCount += 1;
105+
}
106+
});
107+
108+
const mean = sum / length;
109+
let variance = 0;
110+
values.forEach((value) => {
111+
const delta = value - mean;
112+
variance += delta * delta;
113+
});
114+
variance /= length;
115+
116+
return {
117+
values,
118+
dimensions: length,
119+
magnitude: Math.sqrt(sumSquares),
120+
mean,
121+
deviation: Math.sqrt(variance),
122+
min,
123+
max,
124+
zeroShare: length ? zeroCount / length : 0,
125+
};
126+
}
127+
128+
function updateSummary(stats) {
129+
if (!summaryCells.length) {
130+
return;
131+
}
132+
133+
const entries = [
134+
stats.dimensions.toLocaleString(),
135+
formatDecimal(stats.magnitude),
136+
formatDecimal(stats.mean),
137+
formatDecimal(stats.deviation),
138+
formatRange(stats.min, stats.max),
139+
`${Math.round(stats.zeroShare * 100)}%`,
140+
];
141+
142+
entries.forEach((value, index) => {
143+
if (summaryCells[index]) {
144+
summaryCells[index].textContent = value;
145+
}
146+
});
147+
}
148+
149+
function renderExtrema(listElement, entries, placeholder) {
150+
if (!listElement) {
151+
return;
152+
}
153+
154+
listElement.innerHTML = '';
155+
156+
if (!entries.length) {
157+
const item = document.createElement('li');
158+
const text = document.createElement('span');
159+
text.className = 'placeholder';
160+
text.textContent = placeholder;
161+
item.appendChild(text);
162+
listElement.appendChild(item);
163+
return;
164+
}
165+
166+
entries.forEach((entry) => {
167+
const item = document.createElement('li');
168+
item.textContent = `#${entry.index}${formatDecimal(entry.value)}`;
169+
listElement.appendChild(item);
170+
});
171+
}
172+
173+
function renderValueRows(values) {
174+
if (!valueTableBody) {
175+
return;
176+
}
177+
178+
valueTableBody.innerHTML = '';
179+
180+
if (!values.length) {
181+
const row = document.createElement('tr');
182+
const cell = document.createElement('td');
183+
cell.colSpan = 3;
184+
cell.innerHTML = '<p class="placeholder">Enter an embedding vector to populate this table.</p>';
185+
row.appendChild(cell);
186+
valueTableBody.appendChild(row);
187+
return;
188+
}
189+
190+
const maxAbs = values.reduce((max, value) => {
191+
const magnitude = Math.abs(value);
192+
return magnitude > max ? magnitude : max;
193+
}, 0);
194+
195+
values.forEach((value, index) => {
196+
const row = document.createElement('tr');
197+
const indexCell = document.createElement('td');
198+
indexCell.textContent = `#${index + 1}`;
199+
200+
const valueCell = document.createElement('td');
201+
const valueSpan = document.createElement('span');
202+
valueSpan.textContent = formatDecimal(value);
203+
valueCell.appendChild(valueSpan);
204+
205+
const normalizedCell = document.createElement('td');
206+
const normalizedSpan = document.createElement('span');
207+
const normalized = maxAbs > 0 ? Math.abs(value) / maxAbs : 0;
208+
normalizedSpan.textContent = formatDecimal(normalized);
209+
normalizedCell.appendChild(normalizedSpan);
210+
211+
row.append(indexCell, valueCell, normalizedCell);
212+
valueTableBody.appendChild(row);
213+
});
214+
}
215+
216+
function updateInsights(vector) {
217+
const stats = calculateStats(vector);
218+
updateSummary(stats);
219+
220+
const entries = stats.values.map((value, index) => ({ index: index + 1, value }));
221+
const positive = entries
222+
.filter((entry) => entry.value > 0)
223+
.sort((a, b) => b.value - a.value)
224+
.slice(0, 3);
225+
const negative = entries
226+
.filter((entry) => entry.value < 0)
227+
.sort((a, b) => a.value - b.value)
228+
.slice(0, 3);
229+
230+
renderExtrema(positiveList, positive, 'Positive values appear here.');
231+
renderExtrema(negativeList, negative, 'Negative values appear here.');
232+
renderValueRows(stats.values);
233+
}
234+
235+
function formatVectorForInput(values) {
236+
return values.map((value) => formatDecimal(value)).join(', ');
237+
}
238+
239+
function setSampleMeta(description) {
240+
if (!sampleMeta) {
241+
return;
242+
}
243+
244+
sampleMeta.innerHTML = '';
245+
const paragraph = document.createElement('p');
246+
paragraph.textContent = description || 'Choose a sample to populate its description.';
247+
sampleMeta.appendChild(paragraph);
248+
}
249+
250+
function highlightSampleButtons() {
251+
if (!sampleList) {
252+
return;
253+
}
254+
255+
const buttons = sampleList.querySelectorAll('.sample-button');
256+
buttons.forEach((button) => {
257+
const isActive = button.dataset.sampleId === activeSampleId;
258+
button.classList.toggle('is-active', isActive);
259+
button.setAttribute('aria-pressed', isActive ? 'true' : 'false');
260+
});
261+
}
262+
263+
function parseVectorInput(input) {
264+
if (typeof input !== 'string') {
265+
return [];
266+
}
267+
268+
return input
269+
.split(/[,\s]+/)
270+
.map((chunk) => Number.parseFloat(chunk))
271+
.filter((value) => Number.isFinite(value));
272+
}
273+
274+
function selectSample(sample) {
275+
if (!sample) {
276+
activeSampleId = '';
277+
highlightSampleButtons();
278+
updateInsights([]);
279+
setSampleMeta('Choose a sample to populate its description.');
280+
return;
281+
}
282+
283+
activeSampleId = sample.id;
284+
highlightSampleButtons();
285+
const vector = Array.isArray(sample.vector) ? sample.vector : [];
286+
updateInsights(vector);
287+
setSampleMeta(sample.description || '');
288+
289+
if (embeddingInput) {
290+
embeddingInput.value = formatVectorForInput(vector);
291+
}
292+
}
293+
294+
function renderSampleToolbar() {
295+
if (!sampleList) {
296+
return;
297+
}
298+
299+
sampleList.innerHTML = '';
300+
301+
if (!samples.length) {
302+
const placeholder = document.createElement('p');
303+
placeholder.className = 'placeholder';
304+
placeholder.textContent = 'No curated samples available.';
305+
sampleList.appendChild(placeholder);
306+
return;
307+
}
308+
309+
const fragment = document.createDocumentFragment();
310+
samples.slice(0, 3).forEach((sample, index) => {
311+
const button = document.createElement('button');
312+
button.type = 'button';
313+
button.className = 'sample-button';
314+
button.dataset.sampleId = sample.id;
315+
button.textContent = sample.label || `Sample ${index + 1}`;
316+
button.addEventListener('click', () => {
317+
selectSample(sample);
318+
});
319+
fragment.appendChild(button);
320+
});
321+
322+
sampleList.appendChild(fragment);
323+
}
324+
325+
function setupSamples() {
326+
if (!sampleList || !sampleMeta || !summaryGrid || !valueTableBody) {
327+
return;
328+
}
329+
330+
renderSampleToolbar();
331+
if (samples.length) {
332+
selectSample(samples[0]);
333+
} else {
334+
updateInsights([]);
335+
}
336+
}
337+
338+
function setupManualInput() {
339+
if (!updateButton || !embeddingInput) {
340+
return;
341+
}
342+
343+
updateButton.addEventListener('click', () => {
344+
const values = parseVectorInput(embeddingInput.value);
345+
if (!values.length) {
346+
activeSampleId = '';
347+
highlightSampleButtons();
348+
updateInsights([]);
349+
setSampleMeta('Enter numbers separated by commas or spaces to update the insights.');
350+
return;
351+
}
352+
353+
activeSampleId = '';
354+
highlightSampleButtons();
355+
updateInsights(values);
356+
setSampleMeta('Custom embedding applied.');
357+
});
358+
}
359+
52360
const datasetCache = new Map();
53361
let activeDatasetId = '';
54362
let activeRecordId = '';
@@ -1136,6 +1444,8 @@
11361444
applyFilter();
11371445
});
11381446

1447+
setupSamples();
1448+
setupManualInput();
11391449
populateDatasetSelect();
11401450

11411451
if (datasetSelect.value) {

0 commit comments

Comments
 (0)