Add local model asset catalog
This commit is contained in:
@@ -222,10 +222,13 @@ function App(): JSX.Element {
|
||||
})
|
||||
const {
|
||||
detectionModels,
|
||||
modelAssets,
|
||||
loadingDetectionModels,
|
||||
detectionModelError,
|
||||
modelAssetError,
|
||||
selectedDetectionDatasetId,
|
||||
selectedDetectionModelId,
|
||||
selectedModelAssetId,
|
||||
detectionTileManifestPath,
|
||||
detectionConfidenceThreshold,
|
||||
runningDetection,
|
||||
@@ -254,6 +257,7 @@ function App(): JSX.Element {
|
||||
resetDetectionForProject,
|
||||
setSelectedDetectionDatasetId,
|
||||
setSelectedDetectionModelId,
|
||||
setSelectedModelAssetId,
|
||||
setDetectionTileManifestPath,
|
||||
setDetectionConfidenceThreshold,
|
||||
setSelectedDetectionRunId,
|
||||
@@ -930,10 +934,13 @@ function App(): JSX.Element {
|
||||
<div className="workspace-grid workspace-grid-ai">
|
||||
<DetectionLab
|
||||
detectionModels={detectionModels}
|
||||
modelAssets={modelAssets}
|
||||
loadingDetectionModels={loadingDetectionModels}
|
||||
detectionModelError={detectionModelError}
|
||||
modelAssetError={modelAssetError}
|
||||
selectedDetectionDatasetId={selectedDetectionDatasetId}
|
||||
selectedDetectionModelId={selectedDetectionModelId}
|
||||
selectedModelAssetId={selectedModelAssetId}
|
||||
detectionTileManifestPath={detectionTileManifestPath}
|
||||
detectionConfidenceThreshold={detectionConfidenceThreshold}
|
||||
runningDetection={runningDetection}
|
||||
@@ -959,6 +966,7 @@ function App(): JSX.Element {
|
||||
onRefreshYoloPreflight={() => loadYoloPreflight()}
|
||||
onSelectDataset={setSelectedDetectionDatasetId}
|
||||
onSelectModel={setSelectedDetectionModelId}
|
||||
onSelectModelAsset={setSelectedModelAssetId}
|
||||
onSetConfidenceThreshold={setDetectionConfidenceThreshold}
|
||||
onSetTileManifestPath={setDetectionTileManifestPath}
|
||||
onRunDetection={runDetection}
|
||||
|
||||
@@ -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`
|
||||
}
|
||||
|
||||
@@ -48,6 +48,12 @@ export function ProviderPanel({
|
||||
</div>
|
||||
|
||||
<div className="system-provider-capability-surface" aria-label="Provider capability registry">
|
||||
<div className="provider-detail-stack">
|
||||
<strong>Official reference sources</strong>
|
||||
<div>
|
||||
GRB and OSM are reference-data provider capabilities, not AI model choices. Manual upload is the configured path for real reference datasets today; fixtures remain test/demo only.
|
||||
</div>
|
||||
</div>
|
||||
<ul className="system-provider-list">
|
||||
{providers.map((provider) => (
|
||||
<li className="system-provider-card" key={provider.provider_name}>
|
||||
|
||||
@@ -7,6 +7,7 @@ import type {
|
||||
DetectionRead,
|
||||
DetectionRunRead,
|
||||
DetectionRunResponse,
|
||||
ModelAssetRead,
|
||||
QualityCheckRead,
|
||||
YoloPreflightResponse,
|
||||
} from '../types'
|
||||
@@ -28,10 +29,13 @@ export function useDetectionWorkflow({
|
||||
loadQualityChecks,
|
||||
}: DetectionWorkflowOptions) {
|
||||
const [detectionModels, setDetectionModels] = useState<DetectionModelCapability[]>([])
|
||||
const [modelAssets, setModelAssets] = useState<ModelAssetRead[]>([])
|
||||
const [loadingDetectionModels, setLoadingDetectionModels] = useState(false)
|
||||
const [detectionModelError, setDetectionModelError] = useState<string | null>(null)
|
||||
const [modelAssetError, setModelAssetError] = useState<string | null>(null)
|
||||
const [selectedDetectionDatasetId, setSelectedDetectionDatasetId] = useState('')
|
||||
const [selectedDetectionModelId, setSelectedDetectionModelId] = useState('yolo-placeholder')
|
||||
const [selectedModelAssetId, setSelectedModelAssetId] = useState('')
|
||||
const [detectionTileManifestPath, setDetectionTileManifestPath] = useState('')
|
||||
const [detectionConfidenceThreshold, setDetectionConfidenceThreshold] = useState(0.5)
|
||||
const [runningDetection, setRunningDetection] = useState(false)
|
||||
@@ -55,6 +59,7 @@ export function useDetectionWorkflow({
|
||||
const loadDetectionModels = async () => {
|
||||
setLoadingDetectionModels(true)
|
||||
setDetectionModelError(null)
|
||||
setModelAssetError(null)
|
||||
try {
|
||||
const response = await detectionApi.listModels()
|
||||
setDetectionModels(response.models)
|
||||
@@ -63,6 +68,17 @@ export function useDetectionWorkflow({
|
||||
}
|
||||
} catch (error) {
|
||||
setDetectionModelError(formatError(error, 'Failed to load detection models'))
|
||||
}
|
||||
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 ?? '')
|
||||
}
|
||||
} catch (error) {
|
||||
setModelAssets([])
|
||||
setModelAssetError(formatError(error, 'Failed to load local model assets'))
|
||||
} finally {
|
||||
setLoadingDetectionModels(false)
|
||||
}
|
||||
@@ -74,6 +90,7 @@ export function useDetectionWorkflow({
|
||||
try {
|
||||
const response = await detectionApi.getYoloPreflight({
|
||||
tile_manifest_path: tileManifestPath.trim() || null,
|
||||
model_asset_id: selectedModelAssetId || null,
|
||||
})
|
||||
setYoloPreflight(response)
|
||||
} catch (error) {
|
||||
@@ -143,6 +160,7 @@ export function useDetectionWorkflow({
|
||||
project_id: selectedProjectId,
|
||||
dataset_id: datasetId,
|
||||
model_id: selectedDetectionModelId,
|
||||
model_asset_id: selectedModelAssetId || null,
|
||||
confidence_threshold: detectionConfidenceThreshold,
|
||||
tile_manifest_path: detectionTileManifestPath.trim() || null,
|
||||
parameters_json: {},
|
||||
@@ -198,10 +216,13 @@ export function useDetectionWorkflow({
|
||||
|
||||
return {
|
||||
detectionModels,
|
||||
modelAssets,
|
||||
loadingDetectionModels,
|
||||
detectionModelError,
|
||||
modelAssetError,
|
||||
selectedDetectionDatasetId,
|
||||
selectedDetectionModelId,
|
||||
selectedModelAssetId,
|
||||
detectionTileManifestPath,
|
||||
detectionConfidenceThreshold,
|
||||
runningDetection,
|
||||
@@ -230,6 +251,7 @@ export function useDetectionWorkflow({
|
||||
resetDetectionForProject,
|
||||
setSelectedDetectionDatasetId,
|
||||
setSelectedDetectionModelId,
|
||||
setSelectedModelAssetId,
|
||||
setDetectionTileManifestPath,
|
||||
setDetectionConfidenceThreshold,
|
||||
setSelectedDetectionRunId,
|
||||
|
||||
@@ -8,6 +8,7 @@ import type {
|
||||
DetectionRunRead,
|
||||
DetectionRunRequest,
|
||||
DetectionRunResponse,
|
||||
ModelAssetListResponse,
|
||||
YoloPreflightResponse,
|
||||
} from '../../types'
|
||||
|
||||
@@ -24,7 +25,8 @@ function queryString(params: Record<string, string | number | boolean | null | u
|
||||
|
||||
export const detectionApi = {
|
||||
listModels: (): Promise<DetectionModelsResponse> => apiGet<DetectionModelsResponse>('/api/v1/detection/models'),
|
||||
getYoloPreflight: (params: { tile_manifest_path?: string | null; check_model_load?: boolean | null } = {}): Promise<YoloPreflightResponse> =>
|
||||
listModelAssets: (): Promise<ModelAssetListResponse> => apiGet<ModelAssetListResponse>('/api/v1/detection/model-assets'),
|
||||
getYoloPreflight: (params: { tile_manifest_path?: string | null; check_model_load?: boolean | null; model_asset_id?: string | null } = {}): Promise<YoloPreflightResponse> =>
|
||||
apiGet<YoloPreflightResponse>(`/api/v1/detection/yolo/preflight${queryString(params)}`),
|
||||
run: (payload: DetectionRunRequest): Promise<DetectionRunResponse> =>
|
||||
apiPost<DetectionRunResponse>('/api/v1/detection/run', payload),
|
||||
|
||||
@@ -404,6 +404,28 @@ export interface DetectionModelsResponse {
|
||||
models: DetectionModelCapability[]
|
||||
}
|
||||
|
||||
export interface ModelAssetRead {
|
||||
model_asset_id: string
|
||||
filename: string
|
||||
display_name: string
|
||||
model_path: string
|
||||
suffix: string
|
||||
framework: string
|
||||
task_type: string
|
||||
size_bytes: number
|
||||
sha256: string
|
||||
active: boolean
|
||||
status: string
|
||||
limitation_message: string
|
||||
will_download_models: boolean
|
||||
}
|
||||
|
||||
export interface ModelAssetListResponse {
|
||||
items: ModelAssetRead[]
|
||||
total: number
|
||||
model_directory: string
|
||||
}
|
||||
|
||||
export interface YoloPreflightChecks {
|
||||
enabled: boolean
|
||||
dependencies_available?: boolean | null
|
||||
@@ -428,6 +450,7 @@ export interface YoloPreflightRuntime {
|
||||
|
||||
export interface YoloPreflightResponse {
|
||||
model_id: string
|
||||
model_asset_id?: string | null
|
||||
model_path?: string | null
|
||||
tile_manifest_path?: string | null
|
||||
status: string
|
||||
@@ -445,6 +468,7 @@ export interface DetectionRunRequest {
|
||||
project_id: string
|
||||
dataset_id: string
|
||||
model_id: string
|
||||
model_asset_id?: string | null
|
||||
confidence_threshold: number
|
||||
class_filter?: string[] | null
|
||||
tile_manifest_path?: string | null
|
||||
|
||||
Reference in New Issue
Block a user