From 440c5389ef6394dee30258cd120097106d6e407d Mon Sep 17 00:00:00 2001 From: Nimrod Teich Date: Mon, 9 Mar 2026 16:08:59 +0200 Subject: [PATCH 1/6] feat: expose WRS normalized scores in optimizer metrics API Add selection_availability, selection_latency, selection_sync, selection_stake, and selection_composite to the Full variants of provider and spec optimizer metrics endpoints. Values are stored as sums in the DB and averaged (sum / metrics_count) on read. Affected endpoints: - /providerConsumerOptimizerMetricsFull/:addr - /specConsumerOptimizerMetricsFull/:specId Co-Authored-By: Claude Opus 4.6 --- .../ProviderConsumerOptimizerMetricsFull.ts | 23 ++++++++++++++++++- .../spec/SpecConsumerOptimizerMetricsFull.ts | 23 ++++++++++++++++++- src/schemas/relaysSchema.ts | 9 +++++++- 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/src/redis/resources/ProviderConsumerOptimizerMetrics/ProviderConsumerOptimizerMetricsFull.ts b/src/redis/resources/ProviderConsumerOptimizerMetrics/ProviderConsumerOptimizerMetricsFull.ts index 7add77f7..3958cafc 100644 --- a/src/redis/resources/ProviderConsumerOptimizerMetrics/ProviderConsumerOptimizerMetricsFull.ts +++ b/src/redis/resources/ProviderConsumerOptimizerMetrics/ProviderConsumerOptimizerMetricsFull.ts @@ -35,6 +35,12 @@ export interface ConsumerOptimizerMetricsFullByProviderItem { tier2: number; tier3: number; }; + // WRS normalized scores (0-1, higher is better) + selection_availability: number; + selection_latency: number; + selection_sync: number; + selection_stake: number; + selection_composite: number; } export interface ConsumerOptimizerMetricsFullByProviderResponse { @@ -115,6 +121,11 @@ export class ConsumerOptimizerMetricsFullByProviderResource extends RedisResourc tier_chance_1_sum: aggregatedConsumerOptimizerMetrics.tier_chance_1_sum, tier_chance_2_sum: aggregatedConsumerOptimizerMetrics.tier_chance_2_sum, tier_chance_3_sum: aggregatedConsumerOptimizerMetrics.tier_chance_3_sum, + selection_availability_sum: aggregatedConsumerOptimizerMetrics.selection_availability_sum, + selection_latency_sum: aggregatedConsumerOptimizerMetrics.selection_latency_sum, + selection_sync_sum: aggregatedConsumerOptimizerMetrics.selection_sync_sum, + selection_stake_sum: aggregatedConsumerOptimizerMetrics.selection_stake_sum, + selection_composite_sum: aggregatedConsumerOptimizerMetrics.selection_composite_sum, }) .from(aggregatedConsumerOptimizerMetrics) .where(and( @@ -187,7 +198,17 @@ export class ConsumerOptimizerMetricsFullByProviderResource extends RedisResourc Number(m.tier_chance_2_sum) / Number(m.tier_metrics_count) : 0, tier3: (m.tier_metrics_count ?? 0) > 0 && m.tier_chance_3_sum != null ? Number(m.tier_chance_3_sum) / Number(m.tier_metrics_count) : 0, - } + }, + selection_availability: m.selection_availability_sum != null ? + Number(m.selection_availability_sum) / Number(m.metrics_count) : 0, + selection_latency: m.selection_latency_sum != null ? + Number(m.selection_latency_sum) / Number(m.metrics_count) : 0, + selection_sync: m.selection_sync_sum != null ? + Number(m.selection_sync_sum) / Number(m.metrics_count) : 0, + selection_stake: m.selection_stake_sum != null ? + Number(m.selection_stake_sum) / Number(m.metrics_count) : 0, + selection_composite: m.selection_composite_sum != null ? + Number(m.selection_composite_sum) / Number(m.metrics_count) : 0, }); } diff --git a/src/redis/resources/spec/SpecConsumerOptimizerMetricsFull.ts b/src/redis/resources/spec/SpecConsumerOptimizerMetricsFull.ts index 48016901..f5478bd1 100644 --- a/src/redis/resources/spec/SpecConsumerOptimizerMetricsFull.ts +++ b/src/redis/resources/spec/SpecConsumerOptimizerMetricsFull.ts @@ -37,6 +37,12 @@ export interface ConsumerOptimizerMetricsFullBySpecItem { tier2: number; tier3: number; }; + // WRS normalized scores (0-1, higher is better) + selection_availability: number; + selection_latency: number; + selection_sync: number; + selection_stake: number; + selection_composite: number; } export interface ConsumerOptimizerMetricsFullBySpecResponse { @@ -116,6 +122,11 @@ export class ConsumerOptimizerMetricsFullBySpecResource extends RedisResourceBas tier_chance_1_sum: aggregatedConsumerOptimizerMetrics.tier_chance_1_sum, tier_chance_2_sum: aggregatedConsumerOptimizerMetrics.tier_chance_2_sum, tier_chance_3_sum: aggregatedConsumerOptimizerMetrics.tier_chance_3_sum, + selection_availability_sum: aggregatedConsumerOptimizerMetrics.selection_availability_sum, + selection_latency_sum: aggregatedConsumerOptimizerMetrics.selection_latency_sum, + selection_sync_sum: aggregatedConsumerOptimizerMetrics.selection_sync_sum, + selection_stake_sum: aggregatedConsumerOptimizerMetrics.selection_stake_sum, + selection_composite_sum: aggregatedConsumerOptimizerMetrics.selection_composite_sum, }) .from(aggregatedConsumerOptimizerMetrics) .where(and( @@ -194,7 +205,17 @@ export class ConsumerOptimizerMetricsFullBySpecResource extends RedisResourceBas Number(m.tier_chance_2_sum) / Number(m.tier_metrics_count) : 0, tier3: (m.tier_metrics_count ?? 0) > 0 && m.tier_chance_3_sum != null ? Number(m.tier_chance_3_sum) / Number(m.tier_metrics_count) : 0, - } + }, + selection_availability: m.selection_availability_sum != null ? + Number(m.selection_availability_sum) / Number(m.metrics_count) : 0, + selection_latency: m.selection_latency_sum != null ? + Number(m.selection_latency_sum) / Number(m.metrics_count) : 0, + selection_sync: m.selection_sync_sum != null ? + Number(m.selection_sync_sum) / Number(m.metrics_count) : 0, + selection_stake: m.selection_stake_sum != null ? + Number(m.selection_stake_sum) / Number(m.metrics_count) : 0, + selection_composite: m.selection_composite_sum != null ? + Number(m.selection_composite_sum) / Number(m.metrics_count) : 0, }); } diff --git a/src/schemas/relaysSchema.ts b/src/schemas/relaysSchema.ts index 5722bc42..d0f1b9b6 100644 --- a/src/schemas/relaysSchema.ts +++ b/src/schemas/relaysSchema.ts @@ -61,7 +61,14 @@ export const aggregatedConsumerOptimizerMetrics = pgTable('aggregated_consumer_o tier_chance_1_sum: numeric('tier_chance_1_sum'), tier_chance_2_sum: numeric('tier_chance_2_sum'), tier_chance_3_sum: numeric('tier_chance_3_sum'), - tier_metrics_count: integer('tier_metrics_count') + tier_metrics_count: integer('tier_metrics_count'), + + // WRS normalized scores + selection_availability_sum: numeric('selection_availability_sum'), + selection_latency_sum: numeric('selection_latency_sum'), + selection_sync_sum: numeric('selection_sync_sum'), + selection_stake_sum: numeric('selection_stake_sum'), + selection_composite_sum: numeric('selection_composite_sum'), }); export type AggregatedConsumerOptimizerMetrics = typeof aggregatedConsumerOptimizerMetrics.$inferSelect From 8210a9245b0f9f4d6ecfeb79ac89f0685d27e0d9 Mon Sep 17 00:00:00 2001 From: Nimrod Teich Date: Mon, 9 Mar 2026 16:17:31 +0200 Subject: [PATCH 2/6] fix: add missing provider fields to ProviderConsumerOptimizerMetricsFull Add provider and provider_moniker to ConsumerOptimizerMetricsFullByProviderItem to satisfy MetricsItem interface, fixing the TS build error. Co-Authored-By: Claude Opus 4.6 --- .../ProviderConsumerOptimizerMetricsFull.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/redis/resources/ProviderConsumerOptimizerMetrics/ProviderConsumerOptimizerMetricsFull.ts b/src/redis/resources/ProviderConsumerOptimizerMetrics/ProviderConsumerOptimizerMetricsFull.ts index 3958cafc..34b23edc 100644 --- a/src/redis/resources/ProviderConsumerOptimizerMetrics/ProviderConsumerOptimizerMetricsFull.ts +++ b/src/redis/resources/ProviderConsumerOptimizerMetrics/ProviderConsumerOptimizerMetricsFull.ts @@ -20,6 +20,8 @@ export interface ConsumerOptimizerMetricsFullByProviderItem { consumer_hostname: string; metrics_count: number; provider_stake: number; + provider: string; + provider_moniker: string; latency_score: number; availability_score: number; sync_score: number; @@ -102,6 +104,7 @@ export class ConsumerOptimizerMetricsFullByProviderResource extends RedisResourc const metrics = await queryRelays(db => db.select({ + provider: aggregatedConsumerOptimizerMetrics.provider, chain: aggregatedConsumerOptimizerMetrics.chain, hourly_timestamp: aggregatedConsumerOptimizerMetrics.hourly_timestamp, consumer: aggregatedConsumerOptimizerMetrics.consumer, @@ -143,6 +146,7 @@ export class ConsumerOptimizerMetricsFullByProviderResource extends RedisResourc // }); const validMetrics: ConsumerOptimizerMetricsFullByProviderItem[] = []; + const providerMoniker = await ProviderMonikerService.GetMonikerForProvider(provider) || provider; for (const m of metrics) { if (m.latency_score_sum === null || @@ -180,6 +184,8 @@ export class ConsumerOptimizerMetricsFullByProviderResource extends RedisResourc consumer_hostname: m.consumer_hostname, metrics_count: m.metrics_count, provider_stake: m.provider_stake, + provider: m.provider || provider, + provider_moniker: providerMoniker, latency_score: Number(m.latency_score_sum) / Number(m.metrics_count), availability_score: Number(m.availability_score_sum) / Number(m.metrics_count), sync_score: Number(m.sync_score_sum) / Number(m.metrics_count), From 673532914a834c1c6c06275b5580a51998934afd Mon Sep 17 00:00:00 2001 From: Nimrod Teich Date: Mon, 9 Mar 2026 17:30:29 +0200 Subject: [PATCH 3/6] fix: add WRS scores, tier data, and missing fields to basic optimizer metrics endpoints Add selection_availability/latency/sync/stake/composite WRS fields and tier_average/tier_chances to basic (non-Full) provider and spec endpoints to match MetricsItem interface requirements and expose WRS data consistently. Co-Authored-By: Claude Opus 4.6 --- .../ProviderConsumerOptimizerMetrics.ts | 52 ++++++++++++++++++- .../spec/SpecConsumerOptimizerMetrics.ts | 22 +++++++- 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/src/redis/resources/ProviderConsumerOptimizerMetrics/ProviderConsumerOptimizerMetrics.ts b/src/redis/resources/ProviderConsumerOptimizerMetrics/ProviderConsumerOptimizerMetrics.ts index 2a14e93b..cb871e13 100644 --- a/src/redis/resources/ProviderConsumerOptimizerMetrics/ProviderConsumerOptimizerMetrics.ts +++ b/src/redis/resources/ProviderConsumerOptimizerMetrics/ProviderConsumerOptimizerMetrics.ts @@ -20,6 +20,8 @@ export interface ConsumerOptimizerMetricsByProviderItem { consumer_hostname: string; metrics_count: number; provider_stake: number; + provider: string; + provider_moniker: string; latency_score: number; availability_score: number; sync_score: number; @@ -28,6 +30,18 @@ export interface ConsumerOptimizerMetricsByProviderItem { entry_index: number; chain: string; epoch: number; + tier_average: number; + tier_chances: { + tier0: number; + tier1: number; + tier2: number; + tier3: number; + }; + selection_availability: number; + selection_latency: number; + selection_sync: number; + selection_stake: number; + selection_composite: number; } export interface ConsumerOptimizerMetricsByProviderResponse { @@ -100,7 +114,18 @@ export class ConsumerOptimizerMetricsByProviderResource extends RedisResourceBas node_error_rate_sum: aggregatedConsumerOptimizerMetrics.node_error_rate_sum, entry_index_sum: aggregatedConsumerOptimizerMetrics.entry_index_sum, provider_stake: aggregatedConsumerOptimizerMetrics.max_provider_stake, - max_epoch: aggregatedConsumerOptimizerMetrics.max_epoch + max_epoch: aggregatedConsumerOptimizerMetrics.max_epoch, + tier_sum: aggregatedConsumerOptimizerMetrics.tier_sum, + tier_metrics_count: aggregatedConsumerOptimizerMetrics.tier_metrics_count, + tier_chance_0_sum: aggregatedConsumerOptimizerMetrics.tier_chance_0_sum, + tier_chance_1_sum: aggregatedConsumerOptimizerMetrics.tier_chance_1_sum, + tier_chance_2_sum: aggregatedConsumerOptimizerMetrics.tier_chance_2_sum, + tier_chance_3_sum: aggregatedConsumerOptimizerMetrics.tier_chance_3_sum, + selection_availability_sum: aggregatedConsumerOptimizerMetrics.selection_availability_sum, + selection_latency_sum: aggregatedConsumerOptimizerMetrics.selection_latency_sum, + selection_sync_sum: aggregatedConsumerOptimizerMetrics.selection_sync_sum, + selection_stake_sum: aggregatedConsumerOptimizerMetrics.selection_stake_sum, + selection_composite_sum: aggregatedConsumerOptimizerMetrics.selection_composite_sum, }) .from(aggregatedConsumerOptimizerMetrics) .where(and( @@ -112,6 +137,7 @@ export class ConsumerOptimizerMetricsByProviderResource extends RedisResourceBas , `ConsumerOptimizerMetricsByProviderResource::getAggregatedMetrics_${provider}_${from}_${to}`); const validMetrics: ConsumerOptimizerMetricsByProviderItem[] = []; + const providerMoniker = await ProviderMonikerService.GetMonikerForProvider(provider) || provider; for (const m of metrics) { if (m.latency_score_sum === null || @@ -149,6 +175,8 @@ export class ConsumerOptimizerMetricsByProviderResource extends RedisResourceBas consumer_hostname: m.consumer_hostname, metrics_count: m.metrics_count, provider_stake: m.provider_stake, + provider: provider, + provider_moniker: providerMoniker, latency_score: Number(m.latency_score_sum) / Number(m.metrics_count), availability_score: Number(m.availability_score_sum) / Number(m.metrics_count), sync_score: Number(m.sync_score_sum) / Number(m.metrics_count), @@ -156,6 +184,28 @@ export class ConsumerOptimizerMetricsByProviderResource extends RedisResourceBas node_error_rate: Number(m.node_error_rate_sum) / Number(m.metrics_count), entry_index: Number(m.entry_index_sum) / Number(m.metrics_count), epoch: Number(m.max_epoch), + tier_average: (m.tier_metrics_count ?? 0) > 0 && m.tier_sum != null ? + Number(m.tier_sum) / Number(m.tier_metrics_count) : 0, + tier_chances: { + tier0: (m.tier_metrics_count ?? 0) > 0 && m.tier_chance_0_sum != null ? + Number(m.tier_chance_0_sum) / Number(m.tier_metrics_count) : 0, + tier1: (m.tier_metrics_count ?? 0) > 0 && m.tier_chance_1_sum != null ? + Number(m.tier_chance_1_sum) / Number(m.tier_metrics_count) : 0, + tier2: (m.tier_metrics_count ?? 0) > 0 && m.tier_chance_2_sum != null ? + Number(m.tier_chance_2_sum) / Number(m.tier_metrics_count) : 0, + tier3: (m.tier_metrics_count ?? 0) > 0 && m.tier_chance_3_sum != null ? + Number(m.tier_chance_3_sum) / Number(m.tier_metrics_count) : 0, + }, + selection_availability: m.selection_availability_sum != null ? + Number(m.selection_availability_sum) / Number(m.metrics_count) : 0, + selection_latency: m.selection_latency_sum != null ? + Number(m.selection_latency_sum) / Number(m.metrics_count) : 0, + selection_sync: m.selection_sync_sum != null ? + Number(m.selection_sync_sum) / Number(m.metrics_count) : 0, + selection_stake: m.selection_stake_sum != null ? + Number(m.selection_stake_sum) / Number(m.metrics_count) : 0, + selection_composite: m.selection_composite_sum != null ? + Number(m.selection_composite_sum) / Number(m.metrics_count) : 0, }); } diff --git a/src/redis/resources/spec/SpecConsumerOptimizerMetrics.ts b/src/redis/resources/spec/SpecConsumerOptimizerMetrics.ts index f4c40df9..9418a802 100644 --- a/src/redis/resources/spec/SpecConsumerOptimizerMetrics.ts +++ b/src/redis/resources/spec/SpecConsumerOptimizerMetrics.ts @@ -30,6 +30,11 @@ export interface ConsumerOptimizerMetricsBySpecItem { node_error_rate: number; entry_index: number; epoch: number; + selection_availability: number; + selection_latency: number; + selection_sync: number; + selection_stake: number; + selection_composite: number; } export interface ConsumerOptimizerMetricsBySpecResponse { @@ -107,7 +112,12 @@ export class ConsumerOptimizerMetricsBySpecResource extends RedisResourceBase Date: Mon, 9 Mar 2026 17:41:59 +0200 Subject: [PATCH 4/6] fix: add WRS scores to aggregation layer in handler utils The aggregateMetrics() functions were stripping WRS fields from the response. Add selection_availability/latency/sync/stake/composite to accumulator, BaseAggregatedMetrics interface, and averaged output in both provider and spec handler utils. Co-Authored-By: Claude Opus 4.6 --- ...eryProviderOptimizerMetricsHandlerUtils.ts | 38 +++++++++++++++++-- .../querySpecOptimizerMetricsHandlerUtils.ts | 38 +++++++++++++++++-- 2 files changed, 70 insertions(+), 6 deletions(-) diff --git a/src/query/utils/queryProviderOptimizerMetricsHandlerUtils.ts b/src/query/utils/queryProviderOptimizerMetricsHandlerUtils.ts index ffa9d666..e6cc0d37 100644 --- a/src/query/utils/queryProviderOptimizerMetricsHandlerUtils.ts +++ b/src/query/utils/queryProviderOptimizerMetricsHandlerUtils.ts @@ -34,6 +34,11 @@ export interface MetricsItem { tier2: number; tier3: number; }; + selection_availability: number; + selection_latency: number; + selection_sync: number; + selection_stake: number; + selection_composite: number; } export interface BaseAggregatedMetrics { @@ -48,6 +53,11 @@ export interface BaseAggregatedMetrics { generic_score: number; provider_stake: number; epoch: number; + selection_availability: number; + selection_latency: number; + selection_sync: number; + selection_stake: number; + selection_composite: number; } export interface AggregatedMetricsWithTiers extends BaseAggregatedMetrics { @@ -136,7 +146,12 @@ export function aggregateMetrics( tier1: number[], tier2: number[], tier3: number[] - } + }, + selection_availability: number[], + selection_latency: number[], + selection_sync: number[], + selection_stake: number[], + selection_composite: number[] }>(); for (const metric of metrics) { @@ -167,7 +182,12 @@ export function aggregateMetrics( tier1: [], tier2: [], tier3: [] - } + }, + selection_availability: [], + selection_latency: [], + selection_sync: [], + selection_stake: [], + selection_composite: [] }); } @@ -221,6 +241,13 @@ export function aggregateMetrics( if (metric.tier_chances.tier2 != null) agg.tier_chances.tier2.push(parseFloat(String(metric.tier_chances.tier2))); if (metric.tier_chances.tier3 != null) agg.tier_chances.tier3.push(parseFloat(String(metric.tier_chances.tier3))); } + + // Aggregate WRS selection scores + if (metric.selection_availability != null) agg.selection_availability.push(parseFloat(String(metric.selection_availability))); + if (metric.selection_latency != null) agg.selection_latency.push(parseFloat(String(metric.selection_latency))); + if (metric.selection_sync != null) agg.selection_sync.push(parseFloat(String(metric.selection_sync))); + if (metric.selection_stake != null) agg.selection_stake.push(parseFloat(String(metric.selection_stake))); + if (metric.selection_composite != null) agg.selection_composite.push(parseFloat(String(metric.selection_composite))); } return Array.from(aggregations.entries()).map(([key, agg]) => { @@ -236,7 +263,12 @@ export function aggregateMetrics( entry_index: parseFloat(avg(agg.entry_indices).toFixed(9)), generic_score: parseFloat(avg(agg.generic_scores).toFixed(9)), provider_stake: parseFloat(String(agg.provider_stake)), - epoch: parseFloat(String(agg.epoch)) + epoch: parseFloat(String(agg.epoch)), + selection_availability: parseFloat(avg(agg.selection_availability).toFixed(9)), + selection_latency: parseFloat(avg(agg.selection_latency).toFixed(9)), + selection_sync: parseFloat(avg(agg.selection_sync).toFixed(9)), + selection_stake: parseFloat(avg(agg.selection_stake).toFixed(9)), + selection_composite: parseFloat(avg(agg.selection_composite).toFixed(9)), }; if (!includeTiers) { diff --git a/src/query/utils/querySpecOptimizerMetricsHandlerUtils.ts b/src/query/utils/querySpecOptimizerMetricsHandlerUtils.ts index a1479328..3c15751d 100644 --- a/src/query/utils/querySpecOptimizerMetricsHandlerUtils.ts +++ b/src/query/utils/querySpecOptimizerMetricsHandlerUtils.ts @@ -36,6 +36,11 @@ export interface MetricsItem { tier2: number; tier3: number; }; + selection_availability?: number; + selection_latency?: number; + selection_sync?: number; + selection_stake?: number; + selection_composite?: number; } export interface BaseAggregatedMetrics { @@ -49,6 +54,11 @@ export interface BaseAggregatedMetrics { generic_score: number; provider_stake: number; epoch: number; + selection_availability: number; + selection_latency: number; + selection_sync: number; + selection_stake: number; + selection_composite: number; } export interface AggregatedMetricsWithTiers extends BaseAggregatedMetrics { @@ -163,7 +173,12 @@ export function aggregateMetrics( tier1: number[], tier2: number[], tier3: number[] - } + }, + selection_availability: number[], + selection_latency: number[], + selection_sync: number[], + selection_stake: number[], + selection_composite: number[] }>(); for (const metric of metrics) { @@ -191,7 +206,12 @@ export function aggregateMetrics( tier1: [], tier2: [], tier3: [] - } + }, + selection_availability: [], + selection_latency: [], + selection_sync: [], + selection_stake: [], + selection_composite: [] }); } @@ -245,6 +265,13 @@ export function aggregateMetrics( if (metric.tier_chances.tier2 != null) agg.tier_chances.tier2.push(parseFloat(String(metric.tier_chances.tier2))); if (metric.tier_chances.tier3 != null) agg.tier_chances.tier3.push(parseFloat(String(metric.tier_chances.tier3))); } + + // Aggregate WRS selection scores + if (metric.selection_availability != null) agg.selection_availability.push(parseFloat(String(metric.selection_availability))); + if (metric.selection_latency != null) agg.selection_latency.push(parseFloat(String(metric.selection_latency))); + if (metric.selection_sync != null) agg.selection_sync.push(parseFloat(String(metric.selection_sync))); + if (metric.selection_stake != null) agg.selection_stake.push(parseFloat(String(metric.selection_stake))); + if (metric.selection_composite != null) agg.selection_composite.push(parseFloat(String(metric.selection_composite))); } return Array.from(aggregations.entries()).map(([key, agg]) => { @@ -259,7 +286,12 @@ export function aggregateMetrics( entry_index: parseFloat(avg(agg.entry_indices).toFixed(9)), generic_score: parseFloat(avg(agg.generic_scores).toFixed(9)), provider_stake: parseFloat(String(agg.provider_stake)), - epoch: parseFloat(String(agg.epoch)) + epoch: parseFloat(String(agg.epoch)), + selection_availability: parseFloat(avg(agg.selection_availability).toFixed(9)), + selection_latency: parseFloat(avg(agg.selection_latency).toFixed(9)), + selection_sync: parseFloat(avg(agg.selection_sync).toFixed(9)), + selection_stake: parseFloat(avg(agg.selection_stake).toFixed(9)), + selection_composite: parseFloat(avg(agg.selection_composite).toFixed(9)), }; if (!includeTiers) { From 8ffa9f945554ea7c0a4185cd5250c6317544fadb Mon Sep 17 00:00:00 2001 From: Nimrod Teich Date: Mon, 9 Mar 2026 17:43:59 +0200 Subject: [PATCH 5/6] feat: add WRS scores to AVAILABLE_METRICS_FULL for spec endpoints Makes selection_availability/latency/sync/stake/composite selectable as metric types on the specConsumerOptimizerMetricsFull endpoint. Co-Authored-By: Claude Opus 4.6 --- src/query/utils/querySpecOptimizerMetricsHandlerUtils.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/query/utils/querySpecOptimizerMetricsHandlerUtils.ts b/src/query/utils/querySpecOptimizerMetricsHandlerUtils.ts index 3c15751d..71d4d753 100644 --- a/src/query/utils/querySpecOptimizerMetricsHandlerUtils.ts +++ b/src/query/utils/querySpecOptimizerMetricsHandlerUtils.ts @@ -490,7 +490,12 @@ export const AVAILABLE_METRICS_FULL = { tier_chances_tier3: "Tier 3 Chance", provider_stake: "Provider Stake", metrics_count: "Metrics Count", - epoch: "Epoch" + epoch: "Epoch", + selection_availability: "Selection Availability", + selection_latency: "Selection Latency", + selection_sync: "Selection Sync", + selection_stake: "Selection Stake", + selection_composite: "Selection Composite" } as const; export interface MetricsResponse { From c1da85f1c6602dc44f3e07d6621f14d5450386a7 Mon Sep 17 00:00:00 2001 From: Nimrod Teich Date: Tue, 10 Mar 2026 15:46:34 +0200 Subject: [PATCH 6/6] feat: add WRS selection metrics to standard spec endpoint Add selection_latency, selection_availability, selection_sync, selection_stake, and selection_composite to AVAILABLE_METRICS so the standard (non-key) spec endpoint accepts them as valid metric choices. Also update AVAILABLE_METRICS_FULL labels to match existing terminology (e.g. "Latency Score" instead of "Selection Latency"). Co-Authored-By: Claude Opus 4.6 --- .../querySpecOptimizerMetricsHandlerUtils.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/query/utils/querySpecOptimizerMetricsHandlerUtils.ts b/src/query/utils/querySpecOptimizerMetricsHandlerUtils.ts index 71d4d753..6d519b51 100644 --- a/src/query/utils/querySpecOptimizerMetricsHandlerUtils.ts +++ b/src/query/utils/querySpecOptimizerMetricsHandlerUtils.ts @@ -478,7 +478,12 @@ export const AVAILABLE_METRICS = { sync_score: "Sync Score", generic_score: "Reputation Score", node_error_rate: "Error Rate", - entry_index: "Entry Index" + entry_index: "Entry Index", + selection_latency: "Latency Score", + selection_availability: "Availability Score", + selection_sync: "Sync Score", + selection_stake: "Stake Score", + selection_composite: "Composite Score" } as const; export const AVAILABLE_METRICS_FULL = { @@ -491,11 +496,11 @@ export const AVAILABLE_METRICS_FULL = { provider_stake: "Provider Stake", metrics_count: "Metrics Count", epoch: "Epoch", - selection_availability: "Selection Availability", - selection_latency: "Selection Latency", - selection_sync: "Selection Sync", - selection_stake: "Selection Stake", - selection_composite: "Selection Composite" + selection_availability: "Availability Score", + selection_latency: "Latency Score", + selection_sync: "Sync Score", + selection_stake: "Stake Score", + selection_composite: "Composite Score" } as const; export interface MetricsResponse {