Skip to content

Commit 45205ff

Browse files
committed
Preserve mixed Boltz token confidence through native viewer transport
1 parent 3a9187f commit 45205ff

8 files changed

Lines changed: 25 additions & 13 deletions

File tree

‎platform/api/services/boltz_scientific_consumer.py‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,9 @@ def retained_cp_metric(design, job, metric, params=None):
238238
values = z['plddt']
239239
if values.shape != (n,) or not np.isfinite(values).all() or np.any(values < 0) or np.any(values > 1):
240240
raise ValueError('invalid_native_confidence_vector')
241-
return ScientificResidueMetric.model_validate(dict(payload, axis=axis, native_positions=list(range(n)),
241+
return ScientificResidueMetric.model_validate(dict(payload,
242+
metric='token_plddt' if any('label_atom_id' in r for r in axis['residues']) else 'residue_plddt',
243+
axis=axis, native_positions=list(range(n)),
242244
artifact_sha256=selected['artifacts']['plddt']['sha256'], units='fraction', values=values.tolist()))
243245
if metric == 'chain_metrics':
244246
confidence = json.loads(selected['snapshots']['metrics'])
@@ -309,6 +311,7 @@ async def compute_persisted_native_metric(design, metric, session, *, selected=N
309311
with np.load(BytesIO(selected['snapshots']['plddt']), allow_pickle=False) as data:
310312
values = data['plddt'].tolist()
311313
payload.update(artifact_sha256=native['vectors'][0]['artifact_sha256'], units='fraction', values=values)
314+
payload['metric'] = 'token_plddt' if any('label_atom_id' in r for r in payload['axis']['residues']) else 'residue_plddt'
312315
result = ScientificResidueMetric.model_validate(payload)
313316
elif metric == 'chain_metrics':
314317
confidence = json.loads(selected['snapshots']['metrics'])

‎platform/api/services/scientific_viewer_contract.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,8 @@ def source_binding(self):
119119

120120

121121
class ScientificResidueMetric(NativeMetricBase):
122-
metric: Literal['residue_plddt']
122+
# Boltz tokens may bind polymer residues or exact non-polymer atoms.
123+
metric: Literal['residue_plddt', 'token_plddt']
123124
units: Literal['fraction']
124125
values: list[float]
125126

‎platform/frontend/src/components/StructurePredictionResults.tsx‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ export function confidenceProfile(raw: unknown, design: Design): { chains: Recor
6767
if (native.status !== 'ok') return { chains: {}, description: 'Native confidence', reason: native.reason };
6868
const chains: Record<string, ConfidenceChainMetric> = {};
6969
native.residues.forEach((ref, i) => {
70+
if (native.metric === 'token_plddt' && (ref.labelAtomId || ref.authAtomId)) return;
7071
if (native.metric === 'atom_plddt' && ((ref.labelAtomId ?? ref.authAtomId) !== 'CA' || !protein.has(ref.componentId ?? ''))) return;
7172
const id = chainId(ref), position = ref.authSeqId ?? ref.labelSeqId;
7273
if (position == null) return;
@@ -78,7 +79,7 @@ export function confidenceProfile(raw: unknown, design: Design): { chains: Recor
7879
chain.length++;
7980
});
8081
for (const chain of Object.values(chains)) chain.avg_plddt = chain.plddt.reduce((a, b) => a + b, 0) / chain.length;
81-
return { chains, description: native.metric === 'atom_plddt' ? 'Protein Cα-only confidence profile (0–100); no atom averaging. Native atom and non-protein evidence is available in Structure → Metrics & tools.' : 'Confidence by residue. Higher is better.' };
82+
return { chains, description: native.metric === 'token_plddt' ? 'Native polymer-residue token confidence (0–100). Exact atom-token confidence remains separately available in Structure → Metrics & tools; no atom averaging.' : native.metric === 'atom_plddt' ? 'Protein Cα-only confidence profile (0–100); no atom averaging. Native atom and non-protein evidence is available in Structure → Metrics & tools.' : 'Confidence by residue. Higher is better.' };
8283
}
8384
return { chains: {}, description: 'Legacy per-chain confidence' };
8485
}

‎platform/frontend/src/components/StructureViewerPane.tsx‎

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -592,7 +592,7 @@ export default function StructureViewerPane({
592592
const nativeResidue = useMemo(()=>parseScientificNativeMetric(residueQuery.data,nativeDocument,'residue_plddt',selectedDesignId ?? undefined),[residueQuery.data,nativeDocument,selectedDesignId]);
593593
const nativeChains = useMemo(()=>parseScientificNativeMetric(chainQuery.data,nativeDocument,'chain_metrics',selectedDesignId ?? undefined),[chainQuery.data,nativeDocument,selectedDesignId]);
594594
const nativeResidueLayer = useMemo<MetricLayer | null>(()=>nativeResidue.status !== 'ok' ? null : ({
595-
descriptor:{id:'native-plddt',label:nativeResidue.metric === 'atom_plddt' ? 'Native atom pLDDT' : 'Native residue pLDDT',dimension:nativeResidue.metric === 'atom_plddt' ? 'atom-scalar' : 'residue-scalar',units:'fraction',direction:'higher_is_better',valueRange:[0,1],projectionPolicy:'direct',normalization:'none',
595+
descriptor:{id:'native-plddt',label:nativeResidue.metric === 'atom_plddt' ? 'Native atom pLDDT' : nativeResidue.metric === 'token_plddt' ? 'Native polymer-residue pLDDT' : 'Native residue pLDDT',dimension:nativeResidue.metric === 'atom_plddt' ? 'atom-scalar' : 'residue-scalar',units:'fraction',direction:'higher_is_better',valueRange:[0,1],projectionPolicy:'direct',normalization:'none',
596596
categories: {
597597
veryHigh: {label: '≥90', color: '#3b82f6'},
598598
confident: {label: '70–<90', color: '#22d3ee'},
@@ -601,8 +601,14 @@ export default function StructureViewerPane({
601601
},
602602
provenance:{source:'Verified native confidence vector',artifactSha256:nativeResidue.artifactSha256}},
603603
// Reuse the established confidence bands for display only; retain native fractions.
604-
values:nativeResidue.residues.map((identity,index)=>({identity,value:nativeResidue.values[index],displayColor:plddtColor(nativeResidue.values[index]! * 100)})),
604+
values:nativeResidue.residues.map((identity,index)=>({identity,value:nativeResidue.values[index],displayColor:plddtColor(nativeResidue.values[index]! * 100)})).filter(entry => nativeResidue.metric !== 'token_plddt' || !(entry.identity.labelAtomId || entry.identity.authAtomId)),
605605
} as MetricLayer),[nativeResidue]);
606+
const nativeTokenAtomLayer = useMemo<MetricLayer | null>(() => {
607+
if (nativeResidue.status !== 'ok' || nativeResidue.metric !== 'token_plddt' || !nativeResidueLayer) return null;
608+
return { descriptor: { ...nativeResidueLayer.descriptor, id: 'native-plddt-atoms', label: 'Native atom-token pLDDT', dimension: 'atom-scalar' },
609+
values: nativeResidue.residues.flatMap((identity, index) => identity.labelAtomId || identity.authAtomId
610+
? [{ identity, value: nativeResidue.values[index], displayColor: plddtColor(nativeResidue.values[index]! * 100) }] : []) };
611+
}, [nativeResidue, nativeResidueLayer]);
606612
const chainMetrics = useMemo(() => requiresBoundMetrics ? {} : viewerAnalyses?.chainMetrics ?? {}, [requiresBoundMetrics, viewerAnalyses?.chainMetrics]);
607613
const chainMetricsBusy = viewerAnalyses?.chainMetricsBusy ?? false;
608614
const onRunChainMetrics = viewerAnalyses?.onRunChainMetrics;
@@ -1469,12 +1475,13 @@ export default function StructureViewerPane({
14691475
const allMetricLayers = useMemo<readonly MetricLayer[]>(
14701476
() => [
14711477
...(nativeResidueLayer ? [nativeResidueLayer] : []),
1478+
...(nativeTokenAtomLayer ? [nativeTokenAtomLayer] : []),
14721479
...structureScalarMetricLayers,
14731480
...(subunitMeanPlddtLayer ? [subunitMeanPlddtLayer] : []),
14741481
...pairMetricLayers,
14751482
...interfaceMetricLayers,
14761483
],
1477-
[nativeResidueLayer, interfaceMetricLayers, pairMetricLayers, structureScalarMetricLayers, subunitMeanPlddtLayer],
1484+
[nativeResidueLayer, nativeTokenAtomLayer, interfaceMetricLayers, pairMetricLayers, structureScalarMetricLayers, subunitMeanPlddtLayer],
14781485
);
14791486

14801487

@@ -3080,7 +3087,7 @@ export default function StructureViewerPane({
30803087
structureDocumentId={nativeDocument?.documentId ?? governedWorkbenchContext?.structureDocumentId}
30813088
structureContentSha256={nativeDocument?.contentSha256}
30823089
derivedComponents={derivedComponents}
3083-
activeMetricId={overlayView === 'pae' ? 'pae' : residueMetricLayer?.descriptor.id}
3090+
activeMetricId={overlayView === 'pae' ? 'pae' : nativeResidueLayer?.descriptor.id ?? residueMetricLayer?.descriptor.id}
30843091
showMetricWorkbench={!shapeMetrics && metricWorkbenchOpen}
30853092
metricDetails={selectedDesign && nativeChains.status === 'ok' ? (
30863093
<details className="rounded border border-slate-700 bg-slate-900/95 p-2 text-xs">

‎platform/frontend/src/lib/scientificViewerIdentity.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ function sampled(value: unknown, count: number): number[] {
9696
}
9797

9898
export type ScientificNativeMetric = { status: 'unavailable'; reason: string } | {
99-
status: 'ok'; reason: null; metric: 'residue_plddt' | 'atom_plddt' | 'chain_metrics';
99+
status: 'ok'; reason: null; metric: 'residue_plddt' | 'atom_plddt' | 'token_plddt' | 'chain_metrics';
100100
document: StructureDocumentRef; artifactSha256: string; residues: AtomRef[];
101101
confidenceSource?: { producerVersion: string; scope: 'collapsed_residue'; storedUnits: 'percent' };
102102
values: number[]; chains: { providerIndex: string; chainId: string; residues: AtomRef[]; ptm: number }[];
@@ -112,7 +112,7 @@ export function parseScientificNativeMetric(raw: unknown, expected: StructureDoc
112112
try {
113113
if (raw == null) return {status:'unavailable',reason:'Native metric not loaded'};
114114
const initial = record(raw);
115-
const actualMetric: 'residue_plddt' | 'atom_plddt' | 'chain_metrics' = metric === 'residue_plddt' && initial.metric === 'atom_plddt' ? 'atom_plddt' : metric;
115+
const actualMetric: 'residue_plddt' | 'atom_plddt' | 'token_plddt' | 'chain_metrics' = metric === 'residue_plddt' && (initial.metric === 'atom_plddt' || initial.metric === 'token_plddt') ? initial.metric : metric;
116116
const p = exact(raw, initial.status === 'unavailable' ? envelopeKeys : [...nativeBaseKeys,
117117
...(metric === 'residue_plddt' ? ['units','values'] : ['chain_index_map','chains_ptm','pair_chains_iptm','role_assignment','role_reason'])]);
118118
if (p.schema_name !== 'core_protein_viewer_metric' || p.schema_version !== 1 || p.contract_revision !== 1 || p.metric !== actualMetric) throw Error('unsupported native metric schema');

‎platform/frontend/src/structureViewer/StructureViewerHost.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,7 @@ export default function StructureViewerHost({
490490
</details>
491491
)}
492492
{showMetricWorkbench && activeLayer && <MetricLegendPanel layer={activeLayer} visible={layerVisible} opacity={layerOpacity} onVisibilityChange={setLayerVisible} onOpacityChange={setLayerOpacity} onReset={() => { setLayerVisible(true); setLayerOpacity(1); setFilters(DEFAULT_FILTERS); }} />}
493-
{showMetricWorkbench && !(pairLayer && 'dataset' in pairLayer && pairLayer.dataset?.matrixDirection === 'directed') && <FilterPanel value={filters} availableChains={chains} metricRange={activeLayer?.descriptor.valueRange} metricUnits={activeLayer?.descriptor.units ?? undefined} metricDisplayScale={activeLayer?.descriptor.id === 'native-plddt' && ['fraction', 'fractional'].includes(activeLayer.descriptor.units ?? '') ? 100 : 1} onChange={setFilters} />}
493+
{showMetricWorkbench && !(pairLayer && 'dataset' in pairLayer && pairLayer.dataset?.matrixDirection === 'directed') && <FilterPanel value={filters} availableChains={chains} metricRange={activeLayer?.descriptor.valueRange} metricUnits={activeLayer?.descriptor.units ?? undefined} metricDisplayScale={['native-plddt', 'native-plddt-atoms'].includes(activeLayer?.descriptor.id ?? '') && ['fraction', 'fractional'].includes(activeLayer.descriptor.units ?? '') ? 100 : 1} onChange={setFilters} />}
494494
{showLinkedSequence && residueLayer && <SequenceTrackExtension
495495
key={residueLayer.descriptor.id}
496496
metricId={residueLayer.descriptor.id}
@@ -501,7 +501,7 @@ export default function StructureViewerHost({
501501
value: typeof entry.value === 'number' ? entry.value : null,
502502
missingness: entry.missingness,
503503
// Display only: the registry, palette and export keep native fractions.
504-
displayValue: residueLayer.descriptor.id === 'native-plddt' && ['fraction', 'fractional'].includes(residueLayer.descriptor.units ?? '') && typeof entry.value === 'number'
504+
displayValue: ['native-plddt', 'native-plddt-atoms'].includes(residueLayer.descriptor.id ?? '') && ['fraction', 'fractional'].includes(residueLayer.descriptor.units ?? '') && typeof entry.value === 'number'
505505
? `${(entry.value * 100).toFixed(1)} / 100` : undefined,
506506
}))}
507507
selectedKeys={selectedResidueKeys}

‎platform/frontend/src/structureViewer/extensions/metrics/MetricLegendPanel.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { MetricDescriptor, MetricLayer } from '../../metrics/metricContracts.js';
22

3-
const formatRange = (descriptor: MetricDescriptor): string => descriptor.id === 'native-plddt' && ['fraction', 'fractional'].includes(descriptor.units ?? '') ? '0–100 display (native fraction × 100)' : descriptor.valueRange
3+
const formatRange = (descriptor: MetricDescriptor): string => ['native-plddt', 'native-plddt-atoms'].includes(descriptor.id ?? '') && ['fraction', 'fractional'].includes(descriptor.units ?? '') ? '0–100 display (native fraction × 100)' : descriptor.valueRange
44
? `${descriptor.valueRange[0]}–${descriptor.valueRange[1]}${descriptor.units ? ` ${descriptor.units}` : ''}`
55
: descriptor.units ?? 'unitless';
66

‎platform/frontend/src/structureViewer/metrics/metricProjection.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ const finiteValues = (values: readonly MetricValue<ResidueRef>[]): number[] => v
3232

3333
/** Display precision only: native values and provenance remain in the metric layer. */
3434
const metricTooltip = (layer: MetricLayer, value: number, identity: ResidueRef | AtomRef): string => {
35-
const nativePlddt = layer.descriptor.id === 'native-plddt' && layer.descriptor.units === 'fraction';
35+
const nativePlddt = ['native-plddt', 'native-plddt-atoms'].includes(layer.descriptor.id ?? '') && layer.descriptor.units === 'fraction';
3636
const rendered = nativePlddt ? `${(value * 100).toFixed(1)} /100`
3737
: `${Number(value.toPrecision(4))}${layer.descriptor.units ? ` ${layer.descriptor.units}` : ''}`;
3838
const author = identity.authAsymId !== undefined && identity.authSeqId !== undefined;

0 commit comments

Comments
 (0)