Add local model asset catalog
GeoIntel CI / docs-smoke (push) Has been cancelled
GeoIntel CI / contract-smoke (push) Has been cancelled

This commit is contained in:
Codex
2026-07-06 20:59:03 +02:00
parent 9e20cc82ae
commit 6e2a8cbdc4
33 changed files with 660 additions and 7 deletions
@@ -5,15 +5,19 @@ import type {
DetectionRead,
DetectionRunRead,
DetectionRunResponse,
ModelAssetRead,
YoloPreflightResponse,
} from '../../types'
interface DetectionLabProps {
detectionModels: DetectionModelCapability[]
modelAssets: ModelAssetRead[]
loadingDetectionModels: boolean
detectionModelError: string | null
modelAssetError: string | null
selectedDetectionDatasetId: string
selectedDetectionModelId: string
selectedModelAssetId: string
detectionTileManifestPath: string
detectionConfidenceThreshold: number
runningDetection: boolean
@@ -39,6 +43,7 @@ interface DetectionLabProps {
onRefreshYoloPreflight: () => void
onSelectDataset: (datasetId: string) => void
onSelectModel: (modelId: string) => void
onSelectModelAsset: (modelAssetId: string) => void
onSetConfidenceThreshold: (value: number) => void
onSetTileManifestPath: (value: string) => void
onRunDetection: () => void
@@ -53,10 +58,13 @@ interface DetectionLabProps {
export function DetectionLab({
detectionModels,
modelAssets,
loadingDetectionModels,
detectionModelError,
modelAssetError,
selectedDetectionDatasetId,
selectedDetectionModelId,
selectedModelAssetId,
detectionTileManifestPath,
detectionConfidenceThreshold,
runningDetection,
@@ -82,6 +90,7 @@ export function DetectionLab({
onRefreshYoloPreflight,
onSelectDataset,
onSelectModel,
onSelectModelAsset,
onSetConfidenceThreshold,
onSetTileManifestPath,
onRunDetection,
@@ -94,6 +103,7 @@ export function DetectionLab({
onRunQa,
}: DetectionLabProps): JSX.Element {
const selectedDetectionModel = detectionModels.find((model) => model.model_id === selectedDetectionModelId) ?? null
const selectedModelAsset = modelAssets.find((asset) => asset.model_asset_id === selectedModelAssetId) ?? null
const detectionRequiresTileManifest = selectedDetectionModelId === 'yolo-configured'
const detectionHasDataset = selectedDetectionDatasetId.length > 0
const detectionHasModel = selectedDetectionModel !== null
@@ -149,6 +159,12 @@ export function DetectionLab({
<p>{detectionModelError}</p>
</div>
) : null}
{modelAssetError ? (
<div className="result-state result-state-error">
<strong>Local model assets unavailable.</strong>
<p>{modelAssetError}</p>
</div>
) : null}
{detectionModels.length === 0 && !loadingDetectionModels ? (
<div className="result-state result-state-empty">
<strong>No detection models reported by backend.</strong>
@@ -173,6 +189,47 @@ export function DetectionLab({
</ul>
</div>
{selectedDetectionModelId === 'yolo-configured' ? (
<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>
</div>
<span className={selectedModelAsset ? 'status-badge status-badge-ready' : 'status-badge'}>
{selectedModelAsset ? 'asset selected' : 'using configured path'}
</span>
</div>
<label>
Local model file
<select value={selectedModelAssetId} onChange={(event) => onSelectModelAsset(event.target.value)}>
<option value="">Use configured YOLO_MODEL_PATH</option>
{modelAssets.map((asset) => (
<option key={asset.model_asset_id} value={asset.model_asset_id}>
{asset.display_name} {asset.active ? '(active)' : ''}
</option>
))}
</select>
</label>
{modelAssets.length === 0 && !loadingDetectionModels ? (
<div className="result-state result-state-empty">
<strong>No local model assets found.</strong>
<p>Mount model files into the backend model directory or continue with the configured YOLO_MODEL_PATH.</p>
</div>
) : null}
{selectedModelAsset ? (
<div className="result-summary-card">
<p>File: {selectedModelAsset.filename}</p>
<p>Status: {selectedModelAsset.status}</p>
<p>Size: {formatModelAssetSize(selectedModelAsset.size_bytes)}</p>
<p>SHA-256: {selectedModelAsset.sha256.slice(0, 12)}</p>
<p>Path: {selectedModelAsset.model_path}</p>
<p>{selectedModelAsset.limitation_message}</p>
</div>
) : null}
</div>
) : null}
<div className="ai-lab-model-surface" aria-label="YOLO runtime preflight">
<div className="ai-lab-section-header">
<div>
@@ -238,6 +295,7 @@ export function DetectionLab({
<span>cuda_available: {String(yoloPreflight.runtime.cuda_available ?? 'unknown')}</span>
<span>YOLO_CONFIG_DIR: {yoloPreflight.runtime.yolo_config_dir ?? 'n/a'}</span>
<span>model directory: {yoloPreflight.runtime.model_directory ?? 'n/a'}</span>
<span>model_asset_id: {yoloPreflight.model_asset_id ?? 'n/a'}</span>
</div>
</div>
) : null}
@@ -486,3 +544,13 @@ export function DetectionLab({
</section>
)
}
function formatModelAssetSize(sizeBytes: number): string {
if (sizeBytes >= 1024 * 1024) {
return `${(sizeBytes / (1024 * 1024)).toFixed(1)} MB`
}
if (sizeBytes >= 1024) {
return `${(sizeBytes / 1024).toFixed(1)} KB`
}
return `${sizeBytes} B`
}