Harden detection model asset selection
This commit is contained in:
@@ -109,10 +109,20 @@ export function DetectionLab({
|
||||
const detectionHasModel = selectedDetectionModel !== null
|
||||
const detectionModelReady = Boolean(selectedDetectionModel?.configured)
|
||||
const detectionModelUiRunnable = detectionModelReady && selectedDetectionModelId !== 'manual-fixture-detector'
|
||||
const detectionHasExplicitModelAsset =
|
||||
selectedDetectionModelId !== 'yolo-configured' || modelAssets.length === 0 || selectedModelAssetId.length > 0
|
||||
const benchmarkCandidateAsset = modelAssets.find(
|
||||
(asset) => asset.model_asset_id === 'geointel-building-yolov8s-hardneg160r4e50-pt',
|
||||
)
|
||||
const detectionHasTileManifest =
|
||||
!detectionRequiresTileManifest || detectionTileManifestPath.trim().length > 0
|
||||
const detectionRunReady =
|
||||
Boolean(selectedProjectId) && detectionHasDataset && detectionHasModel && detectionModelUiRunnable && detectionHasTileManifest
|
||||
Boolean(selectedProjectId) &&
|
||||
detectionHasDataset &&
|
||||
detectionHasModel &&
|
||||
detectionModelUiRunnable &&
|
||||
detectionHasExplicitModelAsset &&
|
||||
detectionHasTileManifest
|
||||
const detectionRunBlockedReason = !selectedProjectId
|
||||
? 'Select or create a project first'
|
||||
: !detectionHasDataset
|
||||
@@ -123,6 +133,8 @@ export function DetectionLab({
|
||||
? 'Fixture model is explicit test/demo-only'
|
||||
: !detectionModelReady
|
||||
? selectedDetectionModel?.limitation_message ?? 'Selected model is not configured'
|
||||
: !detectionHasExplicitModelAsset
|
||||
? 'Select a local model asset deliberately'
|
||||
: !detectionHasTileManifest
|
||||
? 'Provide a raster tile manifest for configured YOLO'
|
||||
: null
|
||||
@@ -193,17 +205,26 @@ export function DetectionLab({
|
||||
<div className="ai-lab-model-surface" aria-label="Local model asset selection">
|
||||
<div className="ai-lab-section-header">
|
||||
<div>
|
||||
<h3>Local model assets</h3>
|
||||
<p>Select an existing model file mounted into the backend runtime. GeoIntel does not download model weights.</p>
|
||||
<h3>Explicit model asset</h3>
|
||||
<p>Local model assets are read-only. No model file is selected automatically. Choose an existing local model asset before submitting configured YOLO.</p>
|
||||
</div>
|
||||
<span className={selectedModelAsset ? 'status-badge status-badge-ready' : 'status-badge'}>
|
||||
{selectedModelAsset ? 'asset selected' : 'using configured path'}
|
||||
{selectedModelAsset ? 'asset selected' : 'no explicit asset'}
|
||||
</span>
|
||||
</div>
|
||||
{benchmarkCandidateAsset ? (
|
||||
<div className="model-asset-guidance">
|
||||
<strong>Current benchmark candidate</strong>
|
||||
<p>
|
||||
{benchmarkCandidateAsset.display_name} is available for deliberate evaluation. Recommended starting threshold: 0.25.
|
||||
Keep it operator-selected until hard-negative false positives are reduced.
|
||||
</p>
|
||||
</div>
|
||||
) : null}
|
||||
<label>
|
||||
Local model file
|
||||
<select value={selectedModelAssetId} onChange={(event) => onSelectModelAsset(event.target.value)}>
|
||||
<option value="">Use configured YOLO_MODEL_PATH</option>
|
||||
<option value="">Select explicit local model asset</option>
|
||||
{modelAssets.map((asset) => (
|
||||
<option key={asset.model_asset_id} value={asset.model_asset_id}>
|
||||
{asset.display_name} {asset.active ? '(active)' : ''}
|
||||
@@ -221,6 +242,8 @@ export function DetectionLab({
|
||||
<div className="result-summary-card">
|
||||
<p>File: {selectedModelAsset.filename}</p>
|
||||
<p>Status: {selectedModelAsset.status}</p>
|
||||
<p>Active runtime env model: {selectedModelAsset.active ? 'yes' : 'no'}</p>
|
||||
<p>will_download_models: {selectedModelAsset.will_download_models ? 'yes' : 'no'}</p>
|
||||
<p>Size: {formatModelAssetSize(selectedModelAsset.size_bytes)}</p>
|
||||
<p>SHA-256: {selectedModelAsset.sha256.slice(0, 12)}</p>
|
||||
<p>Path: {selectedModelAsset.model_path}</p>
|
||||
@@ -340,6 +363,18 @@ export function DetectionLab({
|
||||
: 'Not required for this model'}
|
||||
</strong>
|
||||
</div>
|
||||
<div className={detectionHasExplicitModelAsset ? 'lab-readiness-item lab-readiness-item-ready' : 'lab-readiness-item'}>
|
||||
<span>Local model asset</span>
|
||||
<strong>
|
||||
{selectedDetectionModelId === 'yolo-configured'
|
||||
? selectedModelAsset
|
||||
? selectedModelAsset.display_name
|
||||
: modelAssets.length > 0
|
||||
? 'Select a local model asset deliberately'
|
||||
: 'No local assets reported; backend configured path only'
|
||||
: 'Not required for this model'}
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={detectionRunReady ? 'lab-action-guardrail lab-action-guardrail-ready' : 'lab-action-guardrail'}>
|
||||
@@ -384,6 +419,11 @@ export function DetectionLab({
|
||||
value={detectionConfidenceThreshold}
|
||||
onChange={(event) => onSetConfidenceThreshold(Number(event.target.value))}
|
||||
/>
|
||||
{selectedDetectionModelId === 'yolo-configured' ? (
|
||||
<span className="field-guidance">
|
||||
Recommended starting threshold: 0.25 for the current local YOLOv8s benchmark candidate.
|
||||
</span>
|
||||
) : null}
|
||||
</label>
|
||||
</div>
|
||||
{selectedDetectionModelId === 'yolo-configured' ? (
|
||||
|
||||
@@ -72,9 +72,8 @@ export function useDetectionWorkflow({
|
||||
try {
|
||||
const assetResponse = await detectionApi.listModelAssets()
|
||||
setModelAssets(assetResponse.items)
|
||||
const activeAsset = assetResponse.items.find((asset) => asset.active) ?? assetResponse.items[0] ?? null
|
||||
if (!assetResponse.items.some((asset) => asset.model_asset_id === selectedModelAssetId)) {
|
||||
setSelectedModelAssetId(activeAsset?.model_asset_id ?? '')
|
||||
setSelectedModelAssetId('')
|
||||
}
|
||||
} catch (error) {
|
||||
setModelAssets([])
|
||||
|
||||
@@ -3369,6 +3369,34 @@ button.entity-card {
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.model-asset-guidance {
|
||||
display: grid;
|
||||
gap: 0.22rem;
|
||||
border: 1px solid #d8e3de;
|
||||
border-left: 4px solid #315a92;
|
||||
border-radius: 8px;
|
||||
padding: 0.58rem 0.68rem;
|
||||
background: #f8fbff;
|
||||
}
|
||||
|
||||
.model-asset-guidance strong {
|
||||
color: var(--text);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.model-asset-guidance p,
|
||||
.field-guidance {
|
||||
margin: 0;
|
||||
color: var(--muted);
|
||||
font-size: 0.78rem;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.field-guidance {
|
||||
display: block;
|
||||
margin-top: 0.24rem;
|
||||
}
|
||||
|
||||
.lab-action-guardrail {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
|
||||
Reference in New Issue
Block a user