predictor/api/rest/predictor.swagger.yml

691 lines
22 KiB
YAML

openapi: 3.0.3
info:
title: stratoflights-predictor API
version: "1.0.0"
description: |
Balloon trajectory prediction and wind-dataset management.
Three prediction surfaces are exposed:
* **`GET /api/v1/prediction`** — Tawhiri-compatible, drop-in for the
Cambridge University Spaceflight predictor.
* **`POST /api/v2/prediction`** — profile-driven synchronous prediction
(arbitrary chains of propagators with constraints).
* **`POST /api/v1/predictions`** — the same profile API run asynchronously
via a worker pool, polled by job id.
Dataset management (download, list, delete, job status) lives under
`/api/v1/admin/`, and wind-field visualization data (leaflet-velocity /
wind-layer format) under `/api/v1/wind/`.
servers:
- url: /
description: This server.
tags:
- name: Prediction
- name: Datasets
- name: Wind
- name: Health
paths:
/ready:
get:
tags: [Health]
summary: Readiness check
operationId: readinessCheck
responses:
"200":
description: Readiness status
content:
application/json:
schema:
$ref: "#/components/schemas/ReadinessResponse"
default:
$ref: "#/components/responses/DefaultError"
/api/v1/prediction:
get:
tags: [Prediction]
summary: Tawhiri-compatible prediction
operationId: performPrediction
parameters:
- { in: query, name: launch_latitude, required: true, schema: { type: number } }
- { in: query, name: launch_longitude, required: true, schema: { type: number } }
- { in: query, name: launch_datetime, required: true, schema: { type: string, format: date-time } }
- { in: query, name: launch_altitude, schema: { type: number } }
- { in: query, name: profile, schema: { type: string, enum: [standard_profile, float_profile], default: standard_profile } }
- { in: query, name: ascent_rate, schema: { type: number } }
- { in: query, name: burst_altitude, schema: { type: number } }
- { in: query, name: descent_rate, schema: { type: number } }
- { in: query, name: float_altitude, schema: { type: number } }
- { in: query, name: stop_datetime, schema: { type: string, format: date-time } }
- { in: query, name: dataset, schema: { type: string, format: date-time } }
responses:
"200":
description: Prediction response
content:
application/json:
schema:
$ref: "#/components/schemas/PredictionResponse"
default:
$ref: "#/components/responses/DefaultError"
/api/v2/prediction:
post:
tags: [Prediction]
summary: Profile-driven prediction (synchronous)
operationId: performPredictionV2
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/PredictionV2Request"
responses:
"200":
description: Prediction result
content:
application/json:
schema:
$ref: "#/components/schemas/PredictionV2Response"
default:
$ref: "#/components/responses/DefaultError"
/api/v1/predictions:
post:
tags: [Prediction]
summary: Enqueue an asynchronous prediction
operationId: createPredictionJob
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/PredictionV2Request"
responses:
"202":
description: Job accepted
content:
application/json:
schema:
$ref: "#/components/schemas/PredictionJob"
default:
$ref: "#/components/responses/DefaultError"
/api/v1/predictions/{id}:
get:
tags: [Prediction]
summary: Poll an asynchronous prediction job
operationId: getPredictionJob
parameters:
- { in: path, name: id, required: true, schema: { type: string } }
responses:
"200":
description: Job status (with result when complete)
content:
application/json:
schema:
$ref: "#/components/schemas/PredictionJob"
default:
$ref: "#/components/responses/DefaultError"
delete:
tags: [Prediction]
summary: Cancel a queued prediction job
operationId: cancelPredictionJob
parameters:
- { in: path, name: id, required: true, schema: { type: string } }
responses:
"204":
description: Cancelled
default:
$ref: "#/components/responses/DefaultError"
/api/v1/admin/datasets:
get:
tags: [Datasets]
summary: List stored datasets
operationId: listDatasets
responses:
"200":
description: Stored datasets
content:
application/json:
schema:
$ref: "#/components/schemas/DatasetList"
default:
$ref: "#/components/responses/DefaultError"
post:
tags: [Datasets]
summary: Trigger a dataset download
operationId: triggerDatasetDownload
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/DownloadRequest"
responses:
"202":
description: Download accepted
content:
application/json:
schema:
$ref: "#/components/schemas/DownloadAccepted"
default:
$ref: "#/components/responses/DefaultError"
/api/v1/admin/datasets/{name}:
delete:
tags: [Datasets]
summary: Delete a stored dataset by filename
operationId: deleteDataset
parameters:
- { in: path, name: name, required: true, schema: { type: string } }
responses:
"204":
description: Deleted
default:
$ref: "#/components/responses/DefaultError"
/api/v1/admin/jobs:
get:
tags: [Datasets]
summary: List dataset download jobs
operationId: listDatasetJobs
responses:
"200":
description: Download jobs
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/DownloadJob"
default:
$ref: "#/components/responses/DefaultError"
/api/v1/admin/jobs/{id}:
get:
tags: [Datasets]
summary: Get a dataset download job
operationId: getDatasetJob
parameters:
- { in: path, name: id, required: true, schema: { type: string } }
responses:
"200":
description: Download job
content:
application/json:
schema:
$ref: "#/components/schemas/DownloadJob"
default:
$ref: "#/components/responses/DefaultError"
delete:
tags: [Datasets]
summary: Cancel a running download job
operationId: cancelDatasetJob
parameters:
- { in: path, name: id, required: true, schema: { type: string } }
responses:
"204":
description: Cancelled
default:
$ref: "#/components/responses/DefaultError"
/api/v1/admin/status:
get:
tags: [Datasets]
summary: Service status summary
operationId: getServiceStatus
responses:
"200":
description: Status
content:
application/json:
schema:
$ref: "#/components/schemas/StatusResponse"
default:
$ref: "#/components/responses/DefaultError"
/api/v1/wind/meta:
get:
tags: [Wind]
summary: Wind-field visualization metadata
operationId: getWindMeta
responses:
"200":
description: Metadata describing the active dataset for visualization
content:
application/json:
schema:
$ref: "#/components/schemas/WindMeta"
default:
$ref: "#/components/responses/DefaultError"
/api/v1/wind/field:
get:
tags: [Wind]
summary: Wind-field velocity grid (leaflet-velocity / wind-layer format)
operationId: getWindField
parameters:
- { in: query, name: time, schema: { type: string, format: date-time } }
- { in: query, name: altitude, schema: { type: number } }
- { in: query, name: min_lat, schema: { type: number } }
- { in: query, name: max_lat, schema: { type: number } }
- { in: query, name: min_lng, schema: { type: number } }
- { in: query, name: max_lng, schema: { type: number } }
- { in: query, name: step, schema: { type: number } }
responses:
"200":
description: Two-component (U, V) velocity grid
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/WindComponent"
default:
$ref: "#/components/responses/DefaultError"
components:
responses:
DefaultError:
description: Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
schemas:
Error:
type: object
required: [error]
properties:
error:
type: object
required: [type, description]
properties:
type: { type: string }
description: { type: string }
ReadinessResponse:
type: object
required: [status]
properties:
status: { type: string, enum: [ok, not_ready, error] }
dataset_time: { type: string, format: date-time }
error_message: { type: string }
# --- Tawhiri v1 ---------------------------------------------------------
PredictionResponse:
type: object
required: [prediction, metadata]
properties:
request:
type: object
properties:
dataset: { type: string }
launch_latitude: { type: number }
launch_longitude: { type: number }
launch_datetime: { type: string }
launch_altitude: { type: number }
profile: { type: string }
ascent_rate: { type: number }
burst_altitude: { type: number }
descent_rate: { type: number }
prediction:
type: array
items:
type: object
required: [stage, trajectory]
properties:
stage: { type: string, enum: [ascent, descent, float] }
trajectory:
type: array
items:
$ref: "#/components/schemas/TawhiriPoint"
metadata:
type: object
required: [start_datetime, complete_datetime]
properties:
start_datetime: { type: string, format: date-time }
complete_datetime: { type: string, format: date-time }
warnings:
type: object
additionalProperties: true
TawhiriPoint:
type: object
required: [datetime, latitude, longitude, altitude]
properties:
datetime: { type: string, format: date-time }
latitude: { type: number }
longitude: { type: number }
altitude: { type: number }
# --- v2 profile-driven --------------------------------------------------
PredictionV2Request:
type: object
required: [launch, profile]
description: |
A profile-driven prediction. `profile` is an ordered chain of
propagators; each integrates from where the previous ended. A stage's
`constraints` decide when it ends and what happens next: stop the
profile, hand off to `fallback_index`, or clip to the boundary.
properties:
launch: { $ref: "#/components/schemas/Launch" }
direction:
type: string
enum: [forward, reverse]
default: forward
description: forward integrates launch→landing; reverse integrates backward in time.
profile:
type: array
items: { $ref: "#/components/schemas/StageSpec" }
globals:
type: array
description: constraints evaluated on every stage in addition to its own.
items: { $ref: "#/components/schemas/ConstraintSpec" }
options: { $ref: "#/components/schemas/Options" }
example:
launch: { time: "2026-03-28T12:00:00Z", latitude: 52.2, longitude: 0.1, altitude: 0 }
profile:
- name: ascent
model: { type: constant_rate, rate: 5, include_wind: true }
constraints: [{ type: altitude, op: ">=", limit: 30000 }]
- name: descent
model: { type: parachute_descent, sea_level_rate: 5, include_wind: true }
constraints: [{ type: terrain_contact }]
Launch:
type: object
required: [time, latitude, longitude]
properties:
time: { type: string, format: date-time }
latitude: { type: number }
longitude: { type: number }
altitude: { type: number }
StageSpec:
type: object
required: [name, model]
properties:
name: { type: string }
model: { $ref: "#/components/schemas/ModelSpec" }
constraints:
type: array
items: { $ref: "#/components/schemas/ConstraintSpec" }
fallback_index: { type: integer }
ModelSpec:
type: object
required: [type]
properties:
type: { type: string, enum: [constant_rate, parachute_descent, piecewise, wind] }
rate: { type: number }
sea_level_rate: { type: number }
include_wind: { type: boolean }
segments:
type: array
items: { $ref: "#/components/schemas/PiecewiseSegment" }
PiecewiseSegment:
type: object
required: [until, rate]
properties:
until: { type: number }
rate: { type: number }
reference: { type: string, enum: [absolute, profile_start, propagator_start], default: absolute }
ConstraintSpec:
type: object
required: [type]
properties:
type: { type: string, enum: [altitude, time, terrain_contact, polygon] }
op: { type: string, enum: ["<", "<=", ">", ">=", "=="] }
limit: { type: number }
action: { type: string, enum: [stop, fallback, clip], default: stop }
mode: { type: string, enum: [inside, outside] }
label: { type: string }
vertices:
type: array
items: { $ref: "#/components/schemas/PolygonVertex" }
PolygonVertex:
type: object
required: [lat, lng]
properties:
lat: { type: number }
lng: { type: number }
Options:
type: object
properties:
step_seconds: { type: number }
tolerance: { type: number }
PredictionV2Response:
type: object
required: [stages, dataset, started_at, completed_at]
properties:
stages:
type: array
items: { $ref: "#/components/schemas/StageResult" }
events:
type: array
items: { $ref: "#/components/schemas/EventSummary" }
dataset: { $ref: "#/components/schemas/DatasetInfo" }
started_at: { type: string, format: date-time }
completed_at: { type: string, format: date-time }
StageResult:
type: object
required: [name, outcome, trajectory]
properties:
name: { type: string }
outcome: { type: string, enum: [stopped, fallback, continued] }
constraint: { type: string }
termination: { $ref: "#/components/schemas/TerminationInfo" }
events:
type: array
items: { $ref: "#/components/schemas/EventSummary" }
trajectory:
type: array
items: { $ref: "#/components/schemas/TrajectoryPoint" }
TrajectoryPoint:
type: object
required: [time, latitude, longitude, altitude]
properties:
time: { type: string, format: date-time }
latitude: { type: number }
longitude: { type: number }
altitude: { type: number }
GeoState:
type: object
required: [lat, lng, altitude]
properties:
lat: { type: number }
lng: { type: number }
altitude: { type: number }
TerminationInfo:
type: object
required: [violation_time, violation_state, refined_time, refined_state]
properties:
violation_time: { type: string, format: date-time }
violation_state: { $ref: "#/components/schemas/GeoState" }
refined_time: { type: string, format: date-time }
refined_state: { $ref: "#/components/schemas/GeoState" }
EventSummary:
type: object
required: [type, count]
properties:
type: { type: string }
count: { type: integer, format: int64 }
first_time: { type: number }
last_time: { type: number }
first_state: { $ref: "#/components/schemas/GeoState" }
last_state: { $ref: "#/components/schemas/GeoState" }
message: { type: string }
DatasetInfo:
type: object
required: [source, epoch]
properties:
source: { type: string }
epoch: { type: string, format: date-time }
# --- async jobs ---------------------------------------------------------
PredictionJob:
type: object
required: [id, status, created_at]
properties:
id: { type: string }
status: { type: string, enum: [pending, running, complete, failed, cancelled] }
created_at: { type: string, format: date-time }
started_at: { type: string, format: date-time }
completed_at: { type: string, format: date-time }
error: { type: string }
result: { $ref: "#/components/schemas/PredictionV2Response" }
# --- dataset admin ------------------------------------------------------
Region:
type: object
required: [min_lat, max_lat, min_lng, max_lng]
properties:
min_lat: { type: number }
max_lat: { type: number }
min_lng: { type: number }
max_lng: { type: number }
HourRange:
type: object
required: [min_hour, max_hour]
properties:
min_hour: { type: integer }
max_hour: { type: integer }
SubsetSpec:
type: object
properties:
region: { $ref: "#/components/schemas/Region" }
hour_range: { $ref: "#/components/schemas/HourRange" }
members:
type: array
items: { type: integer }
Coverage:
type: object
required: [region, start_time, end_time]
properties:
region: { $ref: "#/components/schemas/Region" }
start_time: { type: string, format: date-time }
end_time: { type: string, format: date-time }
DownloadRequest:
type: object
properties:
epoch: { type: string, format: date-time }
latest: { type: boolean }
subset: { $ref: "#/components/schemas/SubsetSpec" }
DownloadAccepted:
type: object
required: [job_id]
properties:
job_id: { type: string }
DatasetEntry:
type: object
required: [filename, epoch, loaded]
properties:
filename: { type: string }
epoch: { type: string, format: date-time }
subset: { $ref: "#/components/schemas/SubsetSpec" }
coverage: { $ref: "#/components/schemas/Coverage" }
loaded: { type: boolean }
DatasetList:
type: object
required: [source, datasets]
properties:
source: { type: string }
datasets:
type: array
items: { $ref: "#/components/schemas/DatasetEntry" }
DownloadJob:
type: object
required: [id, source, dataset, epoch, status, started_at, total_units, done_units, bytes]
properties:
id: { type: string }
source: { type: string }
dataset: { type: string }
epoch: { type: string, format: date-time }
status: { type: string, enum: [pending, running, complete, failed, cancelled] }
started_at: { type: string, format: date-time }
ended_at: { type: string, format: date-time }
error: { type: string }
total_units: { type: integer }
done_units: { type: integer }
bytes: { type: integer, format: int64 }
StatusResponse:
type: object
required: [source, uptime, goroutines, memory_mb, jobs_by_status, stored_datasets, loaded_datasets]
properties:
source: { type: string }
uptime: { type: string }
goroutines: { type: integer }
memory_mb: { type: integer, format: int64 }
jobs_by_status:
type: object
additionalProperties: { type: integer }
stored_datasets: { type: integer }
loaded_datasets: { type: integer }
# --- wind visualization -------------------------------------------------
WindMeta:
type: object
required: [source, epoch, default_step, min_step, suggested_altitudes, bbox]
properties:
source: { type: string }
epoch: { type: string, format: date-time }
default_step: { type: number }
min_step: { type: number }
suggested_altitudes:
type: array
items: { type: integer }
bbox: { $ref: "#/components/schemas/Region" }
WindComponent:
type: object
required: [header, data]
properties:
header: { $ref: "#/components/schemas/WindHeader" }
data:
type: array
items: { type: number }
WindHeader:
type: object
required: [parameterCategory, parameterNumber, nx, ny, lo1, la1, lo2, la2, dx, dy, refTime, forecastTime]
properties:
parameterCategory: { type: integer }
parameterNumber: { type: integer }
parameterNumberName: { type: string }
parameterUnit: { type: string }
nx: { type: integer }
ny: { type: integer }
lo1: { type: number }
la1: { type: number }
lo2: { type: number }
la2: { type: number }
dx: { type: number }
dy: { type: number }
refTime: { type: string }
forecastTime: { type: integer }