Download app.js from mnemoverse/agent-memory-evaluation-map: direct link, hf CLI and curl.
- Browser
- Download file 19.2 kB
-
https://huggingface.co/spaces/mnemoverse/agent-memory-evaluation-map/resolve/main/app.js
- Command line
-
hf download hf://spaces/mnemoverse/agent-memory-evaluation-map/app.js
-
curl -L -o app.js https://huggingface.co/spaces/mnemoverse/agent-memory-evaluation-map/resolve/main/app.js
19.2 kB
| ; | |
| const TARGET_PROMPTS = { | |
| factual_recall: [ | |
| "Does the task separate exact extraction from semantically related but unsupported recall?", | |
| "How will the evaluator score a correct fact recovered from the wrong source or time?" | |
| ], | |
| multi_session_reasoning: [ | |
| "Which answers require joining evidence across sessions rather than retrieving one passage?", | |
| "Are session boundaries, identities, and ordering fixed across systems?" | |
| ], | |
| temporal_reasoning: [ | |
| "Can the evaluator reconstruct what the agent knew at the moment it acted?", | |
| "Are corrections, superseded facts, and valid-time versus ingestion-time represented?" | |
| ], | |
| contradiction_preservation: [ | |
| "Do conflicting claims remain separately attributable, or are they silently overwritten?", | |
| "What must the agent do when sources disagree and no resolution is available?" | |
| ], | |
| abstraction: [ | |
| "Which details may be consolidated, and which evidence boundaries must remain reversible?", | |
| "How will the evaluation detect a useful abstraction that nevertheless loses a decision-critical fact?" | |
| ], | |
| episodic_planning: [ | |
| "Does recalled experience improve the next action rather than merely resemble the current state?", | |
| "How are exploration history, goals, and action costs controlled across runs?" | |
| ], | |
| end_task_behavior: [ | |
| "Does the task score successful downstream behavior separately from retrieval recall?", | |
| "Which model, prompt, tool policy, and stopping rule stay fixed across memory systems?" | |
| ], | |
| abstention: [ | |
| "Can the agent expose missing evidence and decline to answer without being scored as a retrieval failure?", | |
| "Are unsupported confident answers penalized separately from explicit abstention?" | |
| ] | |
| }; | |
| const BASE_PROMPTS = [ | |
| "What is the unit of memory, and which principal is allowed to write or revise it?", | |
| "What provenance is preserved from source evidence to the final answer or action?", | |
| "Are retrieval quality, downstream behavior, latency, and cost measured separately?", | |
| "Which model, prompt, chunking, index configuration, and tool policy are fixed across systems?", | |
| "What stopping condition prevents expensive search loops or repeated use of stale evidence?" | |
| ]; | |
| const state = { | |
| manifest: null, | |
| filtered: [], | |
| selected: new Set(), | |
| view: "cards" | |
| }; | |
| const elements = {}; | |
| document.addEventListener("DOMContentLoaded", init); | |
| async function init() { | |
| cacheElements(); | |
| bindEvents(); | |
| try { | |
| const response = await fetch("data/artifacts.json"); | |
| if (!response.ok) throw new Error(`Manifest request failed: ${response.status}`); | |
| state.manifest = await response.json(); | |
| populateTargets(); | |
| updateSummaryMetrics(); | |
| applyFilters(); | |
| renderRecipe(); | |
| } catch (error) { | |
| elements.resultSummary.textContent = "The public evidence manifest could not be loaded."; | |
| elements.cardView.innerHTML = `<div class="empty-state"><p>${escapeHtml(error.message)}</p><p>Open this directory through an HTTP server rather than as a local file.</p></div>`; | |
| } | |
| } | |
| function cacheElements() { | |
| Object.assign(elements, { | |
| search: document.querySelector("#search"), | |
| targetFilter: document.querySelector("#target-filter"), | |
| outcomeFilter: document.querySelector("#outcome-filter"), | |
| resetFilters: document.querySelector("#reset-filters"), | |
| emptyReset: document.querySelector("#empty-reset"), | |
| resultSummary: document.querySelector("#result-summary"), | |
| cardView: document.querySelector("#card-view"), | |
| matrixView: document.querySelector("#matrix-view"), | |
| matrixBody: document.querySelector("#matrix-body"), | |
| emptyState: document.querySelector("#empty-state"), | |
| compareGrid: document.querySelector("#compare-grid"), | |
| compareStatus: document.querySelector("#compare-status"), | |
| recipeTargets: document.querySelector("#recipe-targets"), | |
| recipeName: document.querySelector("#recipe-name"), | |
| recipeQuestions: document.querySelector("#recipe-questions"), | |
| questionCount: document.querySelector("#question-count"), | |
| downloadMarkdown: document.querySelector("#download-markdown"), | |
| downloadJson: document.querySelector("#download-json"), | |
| artifactCount: document.querySelector("#artifact-count"), | |
| targetCount: document.querySelector("#target-count"), | |
| verifiedDate: document.querySelector("#verified-date"), | |
| evidence: document.querySelector("#evidence") | |
| }); | |
| } | |
| function bindEvents() { | |
| elements.search.addEventListener("input", applyFilters); | |
| elements.targetFilter.addEventListener("change", applyFilters); | |
| elements.outcomeFilter.addEventListener("change", applyFilters); | |
| elements.resetFilters.addEventListener("click", resetFilters); | |
| elements.emptyReset.addEventListener("click", resetFilters); | |
| document.querySelectorAll(".view-button").forEach(button => { | |
| button.addEventListener("click", () => setView(button.dataset.view)); | |
| }); | |
| elements.cardView.addEventListener("change", event => { | |
| const checkbox = event.target.closest("[data-compare-id]"); | |
| if (!checkbox) return; | |
| toggleCompare(checkbox.dataset.compareId, checkbox.checked, checkbox); | |
| }); | |
| elements.compareGrid.addEventListener("click", event => { | |
| const button = event.target.closest("[data-remove-compare-id]"); | |
| if (!button) return; | |
| const index = [...state.selected].indexOf(button.dataset.removeCompareId); | |
| state.selected.delete(button.dataset.removeCompareId); | |
| renderCompare(); | |
| refreshCompareCheckboxes(); | |
| const remainingButtons = elements.compareGrid.querySelectorAll("[data-remove-compare-id]"); | |
| (remainingButtons[Math.min(index, remainingButtons.length - 1)] || elements.search).focus(); | |
| }); | |
| elements.recipeTargets.addEventListener("change", renderRecipe); | |
| elements.recipeName.addEventListener("input", renderRecipe); | |
| document.querySelector(".skip-link").addEventListener("click", event => { | |
| event.preventDefault(); | |
| history.replaceState(null, "", "#evidence"); | |
| elements.evidence.scrollIntoView({ block: "start" }); | |
| elements.evidence.focus(); | |
| }); | |
| } | |
| function populateTargets() { | |
| const options = state.manifest.targets.map(target => `<option value="${escapeHtml(target.id)}">${escapeHtml(target.label)}</option>`).join(""); | |
| elements.targetFilter.insertAdjacentHTML("beforeend", options); | |
| elements.recipeTargets.innerHTML = state.manifest.targets.map(target => ` | |
| <label class="check-option" title="${escapeHtml(target.description)}"> | |
| <input type="checkbox" value="${escapeHtml(target.id)}"> | |
| <span>${escapeHtml(target.label)}</span> | |
| </label> | |
| `).join(""); | |
| } | |
| function updateSummaryMetrics() { | |
| elements.artifactCount.textContent = state.manifest.artifacts.length; | |
| elements.targetCount.textContent = state.manifest.targets.length; | |
| elements.verifiedDate.textContent = state.manifest.verified_at; | |
| } | |
| function applyFilters() { | |
| if (!state.manifest) return; | |
| const query = elements.search.value.trim().toLocaleLowerCase(); | |
| const target = elements.targetFilter.value; | |
| const outcome = elements.outcomeFilter.value; | |
| state.filtered = state.manifest.artifacts.filter(artifact => { | |
| const haystack = [ | |
| artifact.title, | |
| artifact.summary, | |
| artifact.memory_unit, | |
| artifact.time_model, | |
| artifact.conflict_update_model, | |
| ...artifact.memory_scope, | |
| ...artifact.evaluation_targets.map(targetLabel), | |
| ...artifact.authors | |
| ].join(" ").toLocaleLowerCase(); | |
| const queryMatch = !query || haystack.includes(query); | |
| const targetMatch = target === "all" || artifact.evaluation_targets.includes(target); | |
| const outcomeMatch = outcome === "all" || artifact.outcome_level === outcome; | |
| return queryMatch && targetMatch && outcomeMatch; | |
| }); | |
| renderEvidence(); | |
| } | |
| function renderEvidence() { | |
| const total = state.manifest.artifacts.length; | |
| const count = state.filtered.length; | |
| elements.resultSummary.textContent = `Showing ${count} of ${total} public artifacts`; | |
| elements.emptyState.hidden = count !== 0; | |
| elements.cardView.hidden = state.view !== "cards" || count === 0; | |
| elements.matrixView.hidden = state.view !== "matrix" || count === 0; | |
| elements.cardView.innerHTML = state.filtered.map(renderCard).join(""); | |
| elements.matrixBody.innerHTML = state.filtered.map(renderMatrixRow).join(""); | |
| refreshCompareCheckboxes(); | |
| } | |
| function renderCard(artifact) { | |
| const targetLabels = artifact.evaluation_targets.map(targetLabel); | |
| const statusLabel = formatLabel(artifact.evidence_status); | |
| const links = sourceLinks(artifact); | |
| return ` | |
| <article class="evidence-card" data-artifact-id="${escapeHtml(artifact.id)}"> | |
| <div class="card-topline"> | |
| <span class="type-badge">${escapeHtml(formatLabel(artifact.artifact_type))}</span> | |
| ${artifact.mnemoverse_authored ? '<span class="author-badge">Mnemoverse-authored</span>' : ''} | |
| </div> | |
| <h3><a href="${safeUrl(artifact.primary_url)}" target="_blank" rel="noopener">${escapeHtml(artifact.title)}</a></h3> | |
| <p class="artifact-summary">${escapeHtml(artifact.summary)}</p> | |
| <ul class="target-list" aria-label="Evaluation targets">${targetLabels.map(label => `<li>${escapeHtml(label)}</li>`).join("") || "<li>Context only</li>"}</ul> | |
| <dl class="evidence-facts"> | |
| <div><dt>Memory unit</dt><dd>${formatEvidence(artifact.memory_unit)}</dd></div> | |
| <div><dt>Outcome</dt><dd>${escapeHtml(formatLabel(artifact.outcome_level))}</dd></div> | |
| <div><dt>Time model</dt><dd>${formatEvidence(artifact.time_model)}</dd></div> | |
| <div><dt>Evidence status</dt><dd><span class="status-badge ${artifact.evidence_status === "not_evaluated" || artifact.evidence_status === "unknown" ? "unknown" : ""}">${escapeHtml(statusLabel)}</span></dd></div> | |
| <div><dt>Source check</dt><dd>${escapeHtml(artifact.verified_at)}</dd></div> | |
| </dl> | |
| <div class="card-footer"> | |
| <div class="source-links">${links}</div> | |
| <label class="compare-toggle"> | |
| <input type="checkbox" data-compare-id="${escapeHtml(artifact.id)}" aria-label="Compare ${escapeHtml(artifact.title)}" aria-describedby="compare-status" ${state.selected.has(artifact.id) ? "checked" : ""}> | |
| <span>Compare</span> | |
| </label> | |
| </div> | |
| </article> | |
| `; | |
| } | |
| function renderMatrixRow(artifact) { | |
| return ` | |
| <tr> | |
| <th scope="row"><a href="${safeUrl(artifact.primary_url)}" target="_blank" rel="noopener">${escapeHtml(artifact.title)}</a></th> | |
| <td>${formatEvidence(artifact.memory_unit)}</td> | |
| <td>${formatEvidence(artifact.time_model)}</td> | |
| <td>${formatEvidence(artifact.conflict_update_model)}</td> | |
| <td>${escapeHtml(formatLabel(artifact.outcome_level))}</td> | |
| <td>${artifact.code_url ? `<a href="${safeUrl(artifact.code_url)}" target="_blank" rel="noopener">Available</a>` : "Unknown"}</td> | |
| <td>${artifact.dataset_url ? `<a href="${safeUrl(artifact.dataset_url)}" target="_blank" rel="noopener">Available</a>` : "Unknown"}</td> | |
| <td>${escapeHtml(artifact.license)}</td> | |
| </tr> | |
| `; | |
| } | |
| function setView(view) { | |
| state.view = view; | |
| document.querySelectorAll(".view-button").forEach(button => { | |
| const active = button.dataset.view === view; | |
| button.classList.toggle("active", active); | |
| button.setAttribute("aria-pressed", String(active)); | |
| }); | |
| renderEvidence(); | |
| } | |
| function resetFilters() { | |
| elements.search.value = ""; | |
| elements.targetFilter.value = "all"; | |
| elements.outcomeFilter.value = "all"; | |
| applyFilters(); | |
| elements.search.focus(); | |
| } | |
| function toggleCompare(id, checked, checkbox) { | |
| if (checked && state.selected.size >= 2 && !state.selected.has(id)) { | |
| checkbox.checked = false; | |
| elements.compareStatus.textContent = "Two artifacts are already selected. Remove one before adding another."; | |
| return; | |
| } | |
| if (checked) state.selected.add(id); | |
| else state.selected.delete(id); | |
| renderCompare(); | |
| refreshCompareCheckboxes(); | |
| } | |
| function refreshCompareCheckboxes() { | |
| document.querySelectorAll("[data-compare-id]").forEach(checkbox => { | |
| const selected = state.selected.has(checkbox.dataset.compareId); | |
| checkbox.checked = selected; | |
| checkbox.disabled = !selected && state.selected.size >= 2; | |
| }); | |
| } | |
| function renderCompare() { | |
| const selectedArtifacts = [...state.selected].map(id => state.manifest.artifacts.find(artifact => artifact.id === id)); | |
| const slots = selectedArtifacts.map(renderCompareCard); | |
| while (slots.length < 2) slots.push(`<div class="compare-placeholder">${slots.length === 0 ? "Select an artifact" : "Select a second artifact"}</div>`); | |
| elements.compareGrid.innerHTML = slots.join(""); | |
| elements.compareStatus.textContent = selectedArtifacts.length === 2 | |
| ? "Two evidence boundaries selected. Remove either below to change the pair." | |
| : selectedArtifacts.length === 1 | |
| ? "One selected. Choose one more artifact." | |
| : "Choose up to two artifacts from the evidence cards."; | |
| } | |
| function renderCompareCard(artifact) { | |
| return ` | |
| <article class="compare-card"> | |
| <div class="card-topline"> | |
| <span class="type-badge">${escapeHtml(formatLabel(artifact.artifact_type))}</span> | |
| <button class="button quiet compare-remove" type="button" data-remove-compare-id="${escapeHtml(artifact.id)}" aria-label="Remove ${escapeHtml(artifact.title)} from comparison">Remove</button> | |
| </div> | |
| <h3>${escapeHtml(artifact.title)}</h3> | |
| <p class="artifact-summary">${escapeHtml(artifact.summary)}</p> | |
| <dl> | |
| <div><dt>Targets</dt><dd>${artifact.evaluation_targets.map(targetLabel).map(escapeHtml).join(", ") || "Context only"}</dd></div> | |
| <div><dt>Memory unit</dt><dd>${formatEvidence(artifact.memory_unit)}</dd></div> | |
| <div><dt>Time model</dt><dd>${formatEvidence(artifact.time_model)}</dd></div> | |
| <div><dt>Update / conflict model</dt><dd>${formatEvidence(artifact.conflict_update_model)}</dd></div> | |
| <div><dt>Outcome</dt><dd>${escapeHtml(formatLabel(artifact.outcome_level))} · ${escapeHtml(formatLabel(artifact.evidence_status))}</dd></div> | |
| <div><dt>Public code</dt><dd>${artifact.code_url ? `<a href="${safeUrl(artifact.code_url)}" target="_blank" rel="noopener">Open repository</a>` : "Unknown"}</dd></div> | |
| <div><dt>Public dataset</dt><dd>${artifact.dataset_url ? `<a href="${safeUrl(artifact.dataset_url)}" target="_blank" rel="noopener">Open dataset</a>` : "Unknown"}</dd></div> | |
| <div><dt>License</dt><dd>${escapeHtml(artifact.license)}</dd></div> | |
| <div><dt>Limitations</dt><dd>${artifact.limitations.map(escapeHtml).join(" ")}</dd></div> | |
| <div><dt>Source check</dt><dd>${escapeHtml(artifact.verified_at)}</dd></div> | |
| </dl> | |
| </article> | |
| `; | |
| } | |
| function renderRecipe() { | |
| if (!state.manifest) return; | |
| const selectedTargets = [...elements.recipeTargets.querySelectorAll("input:checked")].map(input => input.value); | |
| const questions = buildRecipe(selectedTargets); | |
| elements.recipeQuestions.innerHTML = questions.map(question => `<li>${escapeHtml(question)}</li>`).join(""); | |
| elements.questionCount.textContent = `${questions.length} ${questions.length === 1 ? "question" : "questions"}`; | |
| updateRecipeDownloads(); | |
| } | |
| function buildRecipe(targets) { | |
| const prompts = [...BASE_PROMPTS]; | |
| targets.forEach(target => prompts.push(...(TARGET_PROMPTS[target] || []))); | |
| return [...new Set(prompts)]; | |
| } | |
| function recipePayload() { | |
| const targetIds = [...elements.recipeTargets.querySelectorAll("input:checked")].map(input => input.value); | |
| return { | |
| title: elements.recipeName.value.trim() || "Agent memory evaluation", | |
| generated_from: `Agent Memory Evaluation Map v${state.manifest.manifest_version}`, | |
| evidence_verified_at: state.manifest.verified_at, | |
| artifact_verification_dates: Object.fromEntries(state.manifest.artifacts.map(artifact => [artifact.id, artifact.verified_at])), | |
| targets: targetIds.map(id => ({ id, label: targetLabel(id) })), | |
| questions: buildRecipe(targetIds), | |
| guardrails: [ | |
| "No composite ranking is implied.", | |
| "Unknown and not evaluated are not failures.", | |
| "Follow primary sources before citing reported outcomes." | |
| ] | |
| }; | |
| } | |
| function recipeMarkdown(payload) { | |
| return [ | |
| `# ${payload.title}`, | |
| "", | |
| `Generated from ${payload.generated_from}`, | |
| `Latest source update: ${payload.evidence_verified_at}`, | |
| "Per-paper source checks:", | |
| ...Object.entries(payload.artifact_verification_dates).map(([id, date]) => `- ${id}: ${date}`), | |
| "", | |
| "## Capabilities in scope", | |
| "", | |
| ...(payload.targets.length ? payload.targets.map(target => `- ${target.label}`) : ["- None selected; base reproducibility questions only"]), | |
| "", | |
| "## Evaluation questions", | |
| "", | |
| ...payload.questions.map(question => `- [ ] ${question}`), | |
| "", | |
| "## Interpretation guardrails", | |
| "", | |
| ...payload.guardrails.map(guardrail => `- ${guardrail}`), | |
| "" | |
| ].join("\n"); | |
| } | |
| function updateRecipeDownloads() { | |
| const payload = recipePayload(); | |
| const safeName = slugify(payload.title) || "agent-memory-evaluation"; | |
| elements.downloadMarkdown.href = `data:text/markdown;charset=utf-8,${encodeURIComponent(recipeMarkdown(payload))}`; | |
| elements.downloadMarkdown.download = `${safeName}.md`; | |
| elements.downloadJson.href = `data:application/json;charset=utf-8,${encodeURIComponent(JSON.stringify(payload, null, 2))}`; | |
| elements.downloadJson.download = `${safeName}.json`; | |
| } | |
| function sourceLinks(artifact) { | |
| const title = escapeHtml(artifact.title); | |
| const links = [`<a href="${safeUrl(artifact.primary_url)}" target="_blank" rel="noopener" aria-label="Paper: ${title} (opens in a new tab)">Paper</a>`]; | |
| if (artifact.code_url) links.push(`<a href="${safeUrl(artifact.code_url)}" target="_blank" rel="noopener" aria-label="Code for ${title} (opens in a new tab)">Code</a>`); | |
| if (artifact.dataset_url) links.push(`<a href="${safeUrl(artifact.dataset_url)}" target="_blank" rel="noopener" aria-label="Data for ${title} (opens in a new tab)">Data</a>`); | |
| if (artifact.project_url) links.push(`<a href="${safeUrl(artifact.project_url)}" target="_blank" rel="noopener" aria-label="Project for ${title} (opens in a new tab)">Project</a>`); | |
| return links.join(""); | |
| } | |
| function targetLabel(id) { | |
| return state.manifest.targets.find(target => target.id === id)?.label || formatLabel(id); | |
| } | |
| function formatEvidence(value) { | |
| const unknown = String(value).trim().toLowerCase() === "unknown"; | |
| return unknown ? '<span class="status-badge unknown">Unknown</span>' : escapeHtml(value); | |
| } | |
| function formatLabel(value) { | |
| return String(value).replaceAll("_", " ").replace(/\b\w/g, letter => letter.toUpperCase()); | |
| } | |
| function slugify(value) { | |
| return value.toLocaleLowerCase().normalize("NFKD").replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 60); | |
| } | |
| function safeUrl(value) { | |
| try { | |
| const url = new URL(value, window.location.href); | |
| return url.protocol === "https:" ? escapeHtml(url.href) : "#"; | |
| } catch { | |
| return "#"; | |
| } | |
| } | |
| function escapeHtml(value) { | |
| return String(value) | |
| .replaceAll("&", "&") | |
| .replaceAll("<", "<") | |
| .replaceAll(">", ">") | |
| .replaceAll('"', """) | |
| .replaceAll("'", "'"); | |
| } | |