Add AI lab action guardrails
GeoIntel CI / docs-smoke (push) Has been cancelled
GeoIntel CI / contract-smoke (push) Has been cancelled

This commit is contained in:
Codex
2026-06-24 23:59:25 +02:00
parent 12a7d22c8c
commit 0bc3b2f2e3
7 changed files with 147 additions and 4 deletions
@@ -89,10 +89,24 @@ export function DetectionLab({
const detectionHasDataset = selectedDetectionDatasetId.length > 0
const detectionHasModel = selectedDetectionModel !== null
const detectionModelReady = Boolean(selectedDetectionModel?.configured)
const detectionModelUiRunnable = detectionModelReady && selectedDetectionModelId !== 'manual-fixture-detector'
const detectionHasTileManifest =
!detectionRequiresTileManifest || detectionTileManifestPath.trim().length > 0
const detectionRunReady =
Boolean(selectedProjectId) && detectionHasDataset && detectionHasModel && detectionModelReady && detectionHasTileManifest
Boolean(selectedProjectId) && detectionHasDataset && detectionHasModel && detectionModelUiRunnable && detectionHasTileManifest
const detectionRunBlockedReason = !selectedProjectId
? 'Select or create a project first'
: !detectionHasDataset
? 'Select a raster dataset'
: !detectionHasModel
? 'Select a detection model'
: selectedDetectionModelId === 'manual-fixture-detector'
? 'Fixture model is explicit test/demo-only'
: !detectionModelReady
? selectedDetectionModel?.limitation_message ?? 'Selected model is not configured'
: !detectionHasTileManifest
? 'Provide a raster tile manifest for configured YOLO'
: null
return (
<section className="workspace-panel ai-lab-shell detection-lab-shell">
@@ -191,6 +205,10 @@ export function DetectionLab({
</div>
</div>
</div>
<div className={detectionRunReady ? 'lab-action-guardrail lab-action-guardrail-ready' : 'lab-action-guardrail'}>
<span>Run action</span>
<strong>{detectionRunReady ? 'Ready to submit a detection job' : detectionRunBlockedReason}</strong>
</div>
{rasterDatasets.length === 0 ? (
<div className="result-state result-state-empty">
<strong>No raster datasets available for detection.</strong>
@@ -242,7 +260,7 @@ export function DetectionLab({
/>
</label>
) : null}
<button className="primary-action" type="button" onClick={onRunDetection} disabled={runningDetection || !selectedProjectId || rasterDatasets.length === 0}>
<button className="primary-action" type="button" onClick={onRunDetection} disabled={runningDetection || !detectionRunReady}>
Run detection
</button>
</div>
@@ -90,8 +90,19 @@ export function SegmentationLab({
}: SegmentationLabProps): JSX.Element {
const segmentationHasDataset = selectedSegmentationDatasetId.length > 0
const segmentationHasTileManifest = segmentationTileManifestPath.trim().length > 0
const segmentationModelUiRunnable =
selectedSegmentationModelConfigured && selectedSegmentationModelId !== 'fixture-segmenter'
const segmentationRunReady =
Boolean(selectedProjectId) && segmentationHasDataset && selectedSegmentationModelConfigured
Boolean(selectedProjectId) && segmentationHasDataset && segmentationModelUiRunnable
const segmentationRunBlockedReason = !selectedProjectId
? 'Select or create a project first'
: !segmentationHasDataset
? 'Select a raster dataset'
: selectedSegmentationModelId === 'fixture-segmenter'
? 'Fixture segmenter is explicit test/demo-only'
: !selectedSegmentationModelConfigured
? selectedSegmentationModelLimitation ?? 'Selected segmentation model is not configured'
: null
return (
<section className="workspace-panel ai-lab-shell segmentation-lab-shell">
@@ -184,6 +195,10 @@ export function SegmentationLab({
</div>
</div>
</div>
<div className={segmentationRunReady ? 'lab-action-guardrail lab-action-guardrail-ready' : 'lab-action-guardrail'}>
<span>Run action</span>
<strong>{segmentationRunReady ? 'Ready to submit a segmentation job' : segmentationRunBlockedReason}</strong>
</div>
{rasterDatasets.length === 0 ? (
<div className="result-state result-state-empty">
<strong>No raster datasets available for segmentation.</strong>
@@ -237,7 +252,7 @@ export function SegmentationLab({
className="primary-action"
type="button"
onClick={onRunSegmentation}
disabled={runningSegmentation || !selectedProjectId || rasterDatasets.length === 0 || !selectedSegmentationModelConfigured}
disabled={runningSegmentation || !segmentationRunReady}
>
Run segmentation
</button>
+34
View File
@@ -2719,6 +2719,40 @@ button.entity-card {
overflow-wrap: anywhere;
}
.lab-action-guardrail {
display: flex;
min-width: 0;
align-items: center;
justify-content: space-between;
gap: 0.6rem;
border: 1px solid #d8e3de;
border-radius: 8px;
padding: 0.52rem 0.62rem;
background: #ffffff;
}
.lab-action-guardrail-ready {
border-color: #b8dcc9;
background: #f8fff9;
}
.lab-action-guardrail span {
flex: 0 0 auto;
color: var(--muted);
font-size: 0.72rem;
font-weight: 700;
text-transform: uppercase;
}
.lab-action-guardrail strong {
min-width: 0;
color: var(--text);
font-size: 0.88rem;
line-height: 1.28;
text-align: right;
overflow-wrap: anywhere;
}
.ai-lab-shell .model-list {
grid-template-columns: repeat(auto-fit, minmax(12.5rem, 1fr));
gap: 0.55rem;