@opennodes/registry 0.1.0 → 0.1.2
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/README.md +21 -21
- package/bin.js +25 -24
- package/package.json +49 -18
- package/src/importers.js +346 -346
- package/src/mcp.js +182 -182
- package/src/reimport.js +55 -55
- package/src/search.js +123 -118
- package/src/server.js +392 -391
- package/src/stageb.js +19 -8
- package/src/store-pg.js +127 -127
- package/src/store.js +126 -126
- package/src/web/index.html +1794 -1778
package/src/search.js
CHANGED
|
@@ -1,118 +1,123 @@
|
|
|
1
|
-
// Offering search + transparent ranking (ONP-3 §5–6).
|
|
2
|
-
|
|
3
|
-
export const RANK_COMPONENTS = { price: 0.35, tier: 0.35, probe_success: 0.30 };
|
|
4
|
-
const TIER_SCORE = { unverified: 0, community: 0.4, verified: 0.8, attested: 1.0 };
|
|
5
|
-
|
|
6
|
-
/** Flatten indexed nodes into searchable offering summaries. */
|
|
7
|
-
export async function collectOfferings(store) {
|
|
8
|
-
const out = [];
|
|
9
|
-
for (const node of await store.allNodes()) {
|
|
10
|
-
if (['delisted', 'suspended', 'submitted', 'challenged', 'disputed'].includes(node.state)) continue;
|
|
11
|
-
const card = await store.latestCard(node.id);
|
|
12
|
-
if (!card) continue;
|
|
13
|
-
const probes = await store.probesFor(node.id);
|
|
14
|
-
const stageA = probes.filter((p) => p.stage === 'A');
|
|
15
|
-
const probeSuccess = stageA.length ? stageA.filter((p) => p.ok).length / stageA.length : 0;
|
|
16
|
-
const tier = node.state === 'indexed' ? 'unverified' : node.state;
|
|
17
|
-
const availability = (await store.getObservations(node.id, '_node')).availability ?? null;
|
|
18
|
-
for (const offering of card.offerings) {
|
|
19
|
-
const obs = await store.getObservations(node.id, offering.offering_id);
|
|
20
|
-
// Measured values override claims (DS-ADM-03/04): cap the advertised context to
|
|
21
|
-
// the verified size, and surface registry-measured latency/throughput.
|
|
22
|
-
const serving = { ...offering.serving };
|
|
23
|
-
if (obs.context_cap && obs.context_cap < (serving.context_window ?? Infinity)) {
|
|
24
|
-
serving.context_window = obs.context_cap;
|
|
25
|
-
serving.context_capped_from = offering.serving.context_window;
|
|
26
|
-
}
|
|
27
|
-
out.push({
|
|
28
|
-
node_id: node.id,
|
|
29
|
-
offering_id: offering.offering_id,
|
|
30
|
-
tier,
|
|
31
|
-
source: node.source ?? 'registration',
|
|
32
|
-
institutional: Boolean(node.institutional),
|
|
33
|
-
card_revision: card.revision,
|
|
34
|
-
modality: offering.modality,
|
|
35
|
-
modalities: offering.modalities ?? null,
|
|
36
|
-
local: offering.local ?? null,
|
|
37
|
-
model: offering.model,
|
|
38
|
-
serving,
|
|
39
|
-
binding: offering.binding,
|
|
40
|
-
pricing: offering.pricing,
|
|
41
|
-
data_policy: offering.data_policy ?? null,
|
|
42
|
-
// Allocated hardware for THIS offering (offering-level override wins over the
|
|
43
|
-
// node-level description) — same artifact on different vendors runs on different
|
|
44
|
-
// silicon, and clients deserve to see which. Always basis-labeled, never measured.
|
|
45
|
-
hardware: offering.hardware ?? card.hardware ?? null,
|
|
46
|
-
benchmarks: offering.benchmarks ?? null,
|
|
47
|
-
observed: {
|
|
48
|
-
probe_success: round2(probeSuccess),
|
|
49
|
-
probes: stageA.length,
|
|
50
|
-
ttft_ms: obs.ttft_ms ?? null,
|
|
51
|
-
tps: obs.tps ?? null,
|
|
52
|
-
availability,
|
|
53
|
-
},
|
|
54
|
-
endpoints: card.endpoints,
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
return out;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (q('
|
|
67
|
-
if (q('
|
|
68
|
-
if (q('
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (q('
|
|
74
|
-
if (q('
|
|
75
|
-
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
o.
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
//
|
|
104
|
-
|
|
105
|
-
const
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if (sort === '
|
|
110
|
-
if (sort === '
|
|
111
|
-
return b
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
|
|
1
|
+
// Offering search + transparent ranking (ONP-3 §5–6).
|
|
2
|
+
|
|
3
|
+
export const RANK_COMPONENTS = { price: 0.35, tier: 0.35, probe_success: 0.30 };
|
|
4
|
+
const TIER_SCORE = { unverified: 0, community: 0.4, verified: 0.8, attested: 1.0 };
|
|
5
|
+
|
|
6
|
+
/** Flatten indexed nodes into searchable offering summaries. */
|
|
7
|
+
export async function collectOfferings(store) {
|
|
8
|
+
const out = [];
|
|
9
|
+
for (const node of await store.allNodes()) {
|
|
10
|
+
if (['delisted', 'suspended', 'submitted', 'challenged', 'disputed'].includes(node.state)) continue;
|
|
11
|
+
const card = await store.latestCard(node.id);
|
|
12
|
+
if (!card) continue;
|
|
13
|
+
const probes = await store.probesFor(node.id);
|
|
14
|
+
const stageA = probes.filter((p) => p.stage === 'A');
|
|
15
|
+
const probeSuccess = stageA.length ? stageA.filter((p) => p.ok).length / stageA.length : 0;
|
|
16
|
+
const tier = node.state === 'indexed' ? 'unverified' : node.state;
|
|
17
|
+
const availability = (await store.getObservations(node.id, '_node')).availability ?? null;
|
|
18
|
+
for (const offering of card.offerings) {
|
|
19
|
+
const obs = await store.getObservations(node.id, offering.offering_id);
|
|
20
|
+
// Measured values override claims (DS-ADM-03/04): cap the advertised context to
|
|
21
|
+
// the verified size, and surface registry-measured latency/throughput.
|
|
22
|
+
const serving = { ...offering.serving };
|
|
23
|
+
if (obs.context_cap != null && obs.context_cap < (serving.context_window ?? Infinity)) {
|
|
24
|
+
serving.context_window = obs.context_cap;
|
|
25
|
+
serving.context_capped_from = offering.serving.context_window;
|
|
26
|
+
}
|
|
27
|
+
out.push({
|
|
28
|
+
node_id: node.id,
|
|
29
|
+
offering_id: offering.offering_id,
|
|
30
|
+
tier,
|
|
31
|
+
source: node.source ?? 'registration',
|
|
32
|
+
institutional: Boolean(node.institutional),
|
|
33
|
+
card_revision: card.revision,
|
|
34
|
+
modality: offering.modality,
|
|
35
|
+
modalities: offering.modalities ?? null,
|
|
36
|
+
local: offering.local ?? null,
|
|
37
|
+
model: offering.model,
|
|
38
|
+
serving,
|
|
39
|
+
binding: offering.binding,
|
|
40
|
+
pricing: offering.pricing,
|
|
41
|
+
data_policy: offering.data_policy ?? null,
|
|
42
|
+
// Allocated hardware for THIS offering (offering-level override wins over the
|
|
43
|
+
// node-level description) — same artifact on different vendors runs on different
|
|
44
|
+
// silicon, and clients deserve to see which. Always basis-labeled, never measured.
|
|
45
|
+
hardware: offering.hardware ?? card.hardware ?? null,
|
|
46
|
+
benchmarks: offering.benchmarks ?? null,
|
|
47
|
+
observed: {
|
|
48
|
+
probe_success: round2(probeSuccess),
|
|
49
|
+
probes: stageA.length,
|
|
50
|
+
ttft_ms: obs.ttft_ms ?? null,
|
|
51
|
+
tps: obs.tps ?? null,
|
|
52
|
+
availability,
|
|
53
|
+
},
|
|
54
|
+
endpoints: card.endpoints,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return out;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Search returning the page plus the total number of matches (before `limit`). */
|
|
62
|
+
export async function searchOfferingsPaged(store, query) {
|
|
63
|
+
let items = await collectOfferings(store);
|
|
64
|
+
|
|
65
|
+
const q = (k) => query.get(k);
|
|
66
|
+
if (q('modality')) items = items.filter((o) => o.modality === q('modality'));
|
|
67
|
+
if (q('family')) items = items.filter((o) => o.model.family === q('family') || o.model.artifact === q('family'));
|
|
68
|
+
if (q('min_context')) items = items.filter((o) => (o.serving?.context_window ?? 0) >= Number(q('min_context')));
|
|
69
|
+
if (q('supports')) {
|
|
70
|
+
const wanted = q('supports').split(',');
|
|
71
|
+
items = items.filter((o) => wanted.every((s) => o.serving?.supports?.includes(s)));
|
|
72
|
+
}
|
|
73
|
+
if (q('max_input_price')) items = items.filter((o) => (o.pricing.input_per_mtok ?? Infinity) <= Number(q('max_input_price')));
|
|
74
|
+
if (q('scheme')) items = items.filter((o) => o.pricing.schemes.includes(q('scheme')));
|
|
75
|
+
if (q('tier')) {
|
|
76
|
+
const order = ['unverified', 'community', 'verified', 'attested'];
|
|
77
|
+
const min = order.indexOf(q('tier'));
|
|
78
|
+
items = items.filter((o) => order.indexOf(o.tier) >= min);
|
|
79
|
+
}
|
|
80
|
+
if (q('lang')) {
|
|
81
|
+
const [lang, minGrade = 'basic'] = q('lang').split(':');
|
|
82
|
+
const grades = ['basic', 'strong', 'native'];
|
|
83
|
+
items = items.filter((o) => (o.serving?.languages ?? []).some(
|
|
84
|
+
(l) => l.lang === lang && grades.indexOf(l.grade) >= grades.indexOf(minGrade)));
|
|
85
|
+
}
|
|
86
|
+
if (q('q')) {
|
|
87
|
+
const needle = q('q').toLowerCase();
|
|
88
|
+
items = items.filter((o) => JSON.stringify(o.model).toLowerCase().includes(needle)
|
|
89
|
+
|| o.offering_id.includes(needle) || o.node_id.includes(needle));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const maxPrice = Math.max(...items.map((o) => o.pricing.input_per_mtok ?? 0), 0.000001);
|
|
93
|
+
for (const o of items) {
|
|
94
|
+
const components = {
|
|
95
|
+
price: 1 - (o.pricing.input_per_mtok ?? maxPrice) / maxPrice,
|
|
96
|
+
tier: TIER_SCORE[o.tier] ?? 0,
|
|
97
|
+
probe_success: o.observed.probe_success,
|
|
98
|
+
};
|
|
99
|
+
o.rank = round2(Object.entries(RANK_COMPONENTS).reduce((sum, [k, w]) => sum + w * components[k], 0));
|
|
100
|
+
o.rank_explanation = { weights: RANK_COMPONENTS, components };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Performance sorts prefer registry-measured values; claimed expected_* is the
|
|
104
|
+
// fallback, and offerings with neither sink to the bottom (ONP-3 §4).
|
|
105
|
+
const ttftOf = (o) => o.observed.ttft_ms?.p50 ?? o.serving?.expected_ttft_ms?.p50 ?? Infinity;
|
|
106
|
+
const tpsOf = (o) => (o.observed.tps > 0 ? o.observed.tps : null) ?? o.serving?.expected_tps?.p50 ?? -Infinity;
|
|
107
|
+
const sort = q('sort') ?? 'rank';
|
|
108
|
+
items.sort((a, b) => {
|
|
109
|
+
if (sort === 'price') return (a.pricing.input_per_mtok ?? Infinity) - (b.pricing.input_per_mtok ?? Infinity);
|
|
110
|
+
if (sort === 'ttft') return ttftOf(a) - ttftOf(b);
|
|
111
|
+
if (sort === 'tps') return tpsOf(b) - tpsOf(a);
|
|
112
|
+
return b.rank - a.rank;
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const limit = Math.min(Number(q('limit') ?? 50), 200);
|
|
116
|
+
return { offerings: items.slice(0, limit), total: items.length };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export async function searchOfferings(store, query) {
|
|
120
|
+
return (await searchOfferingsPaged(store, query)).offerings;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const round2 = (n) => Math.round(n * 100) / 100;
|