@opennodes/registry 0.1.25 → 0.1.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/web/index.html +42 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opennodes/registry",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.26",
|
|
4
4
|
"description": "OpenNodes (ONP) reference registry: node registration with proof-of-resource qualification (Stage A/B/C), transparent ranking, enforceable estimates, catalog importers (OpenRouter, Hugging Face, models.dev, Ollama), MCP surface, and the web app (catalog, chat, advisor, Node Builder)",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
package/src/web/index.html
CHANGED
|
@@ -47,6 +47,13 @@
|
|
|
47
47
|
.mitem .t .nm { font-weight: 600; font-size: 0.86rem; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
48
48
|
.mitem .b { display: flex; gap: 0.8rem; margin-top: 0.25rem; font-size: 0.7rem; color: var(--muted); font-variant-numeric: tabular-nums; flex-wrap: wrap; }
|
|
49
49
|
.mdetail { min-height: 30rem; }
|
|
50
|
+
.scorebox { margin: 0.4rem 0 0.8rem; border: 1px solid var(--line); border-radius: 6px; background: var(--panel); padding: 0.5rem 0.75rem; font-size: 0.8rem; }
|
|
51
|
+
.scorebox summary { cursor: pointer; }
|
|
52
|
+
.scorebox table { width: 100%; border-collapse: collapse; margin-top: 0.4rem; }
|
|
53
|
+
.scorebox td { padding: 0.2rem 0.5rem 0.2rem 0; border-top: 1px solid var(--line-2); vertical-align: top; }
|
|
54
|
+
.scorebox td.num { text-align: right; white-space: nowrap; }
|
|
55
|
+
.scorebox td.sub { font-size: 0.74rem; }
|
|
56
|
+
.scorebox tr.tot td { border-top: 1px solid var(--line); }
|
|
50
57
|
.pager { display: flex; align-items: center; justify-content: center; gap: 0.3rem; margin-top: 0.5rem; font-size: 0.78rem; color: var(--muted); flex-wrap: wrap; }
|
|
51
58
|
.pager:empty { display: none; }
|
|
52
59
|
.pgbtn { font: inherit; min-width: 2rem; padding: 0.25rem 0.5rem; border-radius: 6px; border: 1px solid var(--line); background: var(--panel); color: var(--accent); cursor: pointer; }
|
|
@@ -267,6 +274,7 @@
|
|
|
267
274
|
</div>
|
|
268
275
|
<p class="desc" id="md-desc" hidden></p>
|
|
269
276
|
<div class="statstrip" id="md-stats"></div>
|
|
277
|
+
<div id="md-score"></div>
|
|
270
278
|
<div class="secnav" id="md-nav"></div>
|
|
271
279
|
<h2 id="sec-offerings">Offerings <span class="counts" id="md-count"></span></h2>
|
|
272
280
|
<div class="panel tablewrap" id="md-offerings"></div>
|
|
@@ -726,8 +734,10 @@ function renderAdvice(r) {
|
|
|
726
734
|
});
|
|
727
735
|
}
|
|
728
736
|
|
|
737
|
+
let listedOffering = null; // the list entry the detail was opened from: its score is explained there
|
|
729
738
|
function selectModel(el) {
|
|
730
739
|
document.querySelectorAll('.mitem').forEach((x) => x.classList.toggle('sel', x === el));
|
|
740
|
+
listedOffering = lastResults.find((o) => `${o.node_id}/${o.offering_id}` === el.dataset.key) ?? null;
|
|
731
741
|
const host = $('#m-detail');
|
|
732
742
|
if (!host.contains($('#model-card'))) { host.innerHTML = ''; host.appendChild($('#model-card')); }
|
|
733
743
|
loadModel(el.dataset.artifact);
|
|
@@ -736,7 +746,7 @@ function selectModel(el) {
|
|
|
736
746
|
let lastResults = [];
|
|
737
747
|
function renderItem(o) {
|
|
738
748
|
const obs = o.observed || {};
|
|
739
|
-
return `<div class="mitem" data-artifact="${esc(o.model.artifact)}">
|
|
749
|
+
return `<div class="mitem" data-artifact="${esc(o.model.artifact)}" data-key="${esc(o.node_id + '/' + o.offering_id)}">
|
|
740
750
|
<div class="t">
|
|
741
751
|
<span class="nm">${esc(o.model.name)}</span>
|
|
742
752
|
${tierChip(o.tier)}
|
|
@@ -839,6 +849,35 @@ function renderRow(o, idx) {
|
|
|
839
849
|
}
|
|
840
850
|
|
|
841
851
|
/* ---------- model detail ---------- */
|
|
852
|
+
// How the list's ranking score for the selected entry was computed: weight × component per part.
|
|
853
|
+
function renderScore(artifact) {
|
|
854
|
+
const host = $('#md-score');
|
|
855
|
+
const o = listedOffering;
|
|
856
|
+
if (!o?.rank_explanation || o.model.artifact !== artifact) { host.innerHTML = ''; return; }
|
|
857
|
+
const { weights, components } = o.rank_explanation;
|
|
858
|
+
const obs = o.observed || {};
|
|
859
|
+
const why = {
|
|
860
|
+
price: o.pricing?.input_per_mtok == null ? 'no price published'
|
|
861
|
+
: `${o.pricing.input_per_mtok === 0 ? 'free' : '$' + fmt(o.pricing.input_per_mtok) + '/MTok in'} — position among the listed results (cheapest 1.00, dearest 0.00)`,
|
|
862
|
+
tier: {
|
|
863
|
+
verified: 'verified: passed identity, context and capacity checks',
|
|
864
|
+
community: 'community: passed the registration checks',
|
|
865
|
+
attested: 'attested by a third party',
|
|
866
|
+
}[o.tier] ?? (o.source === 'import' ? 'unverified: imported from a public catalog, not checked by this registry' : 'unverified: not checked yet'),
|
|
867
|
+
probe_success: obs.probes > 0 ? `${Math.round((obs.probe_success ?? 0) * obs.probes)} of the last ${obs.probes} checks passed` : 'no checks yet',
|
|
868
|
+
};
|
|
869
|
+
const label = { price: 'Price', tier: 'Trust tier', probe_success: 'Check pass rate' };
|
|
870
|
+
const rows = Object.keys(weights).map((k) => `<tr>
|
|
871
|
+
<td>${label[k] ?? esc(k)}</td>
|
|
872
|
+
<td class="num mono">${fmt(components[k] ?? 0)} × ${fmt(weights[k])}</td>
|
|
873
|
+
<td class="num mono"><b>${fmt((components[k] ?? 0) * weights[k])}</b></td>
|
|
874
|
+
<td class="sub">${esc(why[k] ?? '')}</td></tr>`).join('');
|
|
875
|
+
host.innerHTML = `<details class="scorebox" open>
|
|
876
|
+
<summary>Ranking score <b class="mono">${fmt(o.rank)}</b> <span class="sub">for ${esc(o.node_id)} in the current list</span></summary>
|
|
877
|
+
<table>${rows}<tr class="tot"><td>Total</td><td></td><td class="num mono"><b>${fmt(o.rank)}</b></td><td class="sub">weighted sum; measured values, never paid placement</td></tr></table>
|
|
878
|
+
</details>`;
|
|
879
|
+
}
|
|
880
|
+
|
|
842
881
|
async function loadModel(artifact) {
|
|
843
882
|
$('#model-card').dataset.artifact = artifact;
|
|
844
883
|
$('#md-crumb').innerHTML = '<a href="#/">Models</a> / ' + esc(artifact);
|
|
@@ -892,6 +931,8 @@ async function loadModel(artifact) {
|
|
|
892
931
|
first.modalities ? stat(esc((first.modalities.input || []).join('+')) + ' → ' + esc((first.modalities.output || []).join('+')), 'MODALITIES') : '',
|
|
893
932
|
].filter(Boolean).join('');
|
|
894
933
|
|
|
934
|
+
renderScore(artifact);
|
|
935
|
+
|
|
895
936
|
// section nav (anchors appear only for sections that render)
|
|
896
937
|
const navBits = [['#sec-offerings', 'OFFERINGS'], ['#sec-play', 'PLAYGROUND']];
|
|
897
938
|
|