sync: migrate delta specs to main openspec/specs
- Added 5 new capabilities: feature-engineering, annotation-ingestion, ml-training, ml-inference, prediction-ui - Updated 2 existing capabilities: backend-api, span-annotation - All specs synced from openspec/changes/candle-backend/specs/
This commit is contained in:
parent
65f00e6ce7
commit
7e0579f65d
7 changed files with 525 additions and 252 deletions
|
|
@ -1,91 +1,38 @@
|
|||
## ADDED Requirements
|
||||
|
||||
### Requirement: Upload endpoint
|
||||
The system SHALL provide a `POST /api/upload` endpoint that accepts a CSV file via multipart form data. The endpoint SHALL create a new chart (named from the uploaded filename without extension), parse the CSV using papaparse, validate the format, and insert all candle records into the `candles` table with the new chart's `chart_id` within a single database transaction. On success, the endpoint SHALL return a JSON response with the chart `id`, chart `name`, and the count of inserted records. On failure, it SHALL return an appropriate error status and message without creating a chart.
|
||||
### Requirement: Predict proxy endpoint
|
||||
The system SHALL provide a `POST /api/predict` Next.js API route that proxies requests to the Python inference service at `${INFERENCE_API_URL}/predict`. The route SHALL forward the request body (pair, timeframe, candles array) and return the Python service's response. If the inference service is unreachable, the route SHALL return HTTP 503 with `{ "error": "Inference service unavailable" }`.
|
||||
|
||||
#### Scenario: Successful upload
|
||||
- **WHEN** a valid CSV file named "BTC-1H.csv" is sent to POST /api/upload
|
||||
- **THEN** endpoint creates a chart named "BTC-1H", inserts candles with that chart_id, and returns `{ "success": true, "count": <number>, "chart": { "id": <number>, "name": "BTC-1H" } }` with HTTP 200
|
||||
#### Scenario: Successful prediction proxy
|
||||
- **WHEN** POST /api/predict is called with valid candle data and the Python service is running
|
||||
- **THEN** the route forwards the request to the inference service and returns the prediction response with HTTP 200
|
||||
|
||||
#### Scenario: Invalid CSV upload
|
||||
- **WHEN** a CSV with missing or invalid headers is sent to POST /api/upload
|
||||
- **THEN** endpoint returns `{ "error": "<description>" }` with HTTP 400 and does not create a chart
|
||||
#### Scenario: Inference service down
|
||||
- **WHEN** POST /api/predict is called but the Python inference service is unreachable
|
||||
- **THEN** the route returns HTTP 503 with `{ "error": "Inference service unavailable" }`
|
||||
|
||||
#### Scenario: No file provided
|
||||
- **WHEN** POST /api/upload is called without a file
|
||||
- **THEN** endpoint returns `{ "error": "No file provided" }` with HTTP 400
|
||||
#### Scenario: Inference service error
|
||||
- **WHEN** the Python inference service returns an error status (4xx or 5xx)
|
||||
- **THEN** the route forwards the error status and message to the client
|
||||
|
||||
#### Scenario: Duplicate filename
|
||||
- **WHEN** a CSV named "BTC-1H.csv" is uploaded and a chart named "BTC-1H" already exists
|
||||
- **THEN** endpoint creates a chart named "BTC-1H-2" (or next available suffix) and inserts candles under that chart
|
||||
### Requirement: Batch predict proxy endpoint
|
||||
The system SHALL provide a `POST /api/predict/batch` Next.js API route that proxies batch prediction requests to `${INFERENCE_API_URL}/predict/batch`. The route SHALL forward pair, timeframe, start_date, and end_date.
|
||||
|
||||
### Requirement: Get annotations endpoint
|
||||
The system SHALL provide a `GET /api/annotations` endpoint that accepts an optional `chartId` query parameter. When `chartId` is provided, the endpoint SHALL return only annotations belonging to that chart. When `chartId` is omitted, the endpoint SHALL return annotations for the most recently created chart. Each annotation object SHALL include: `id`, `chart_id`, `timestamp`, `label_type`, `geometry` (parsed from JSON string or null), and `created_at`.
|
||||
#### Scenario: Successful batch prediction
|
||||
- **WHEN** POST /api/predict/batch is called with valid parameters
|
||||
- **THEN** the route forwards to the inference service and returns the batch prediction response
|
||||
|
||||
#### Scenario: Fetch annotations for specific chart
|
||||
- **WHEN** GET /api/annotations?chartId=3 is called
|
||||
- **THEN** endpoint returns a JSON array of annotations where chart_id equals 3, with HTTP 200
|
||||
#### Scenario: Timeout on large batch
|
||||
- **WHEN** the batch prediction takes longer than INFERENCE_BATCH_TIMEOUT
|
||||
- **THEN** the route returns HTTP 504 with `{ "error": "Batch prediction timed out" }`
|
||||
|
||||
#### Scenario: Fetch annotations without chartId
|
||||
- **WHEN** GET /api/annotations is called without a chartId parameter
|
||||
- **THEN** endpoint returns annotations for the most recently created chart with HTTP 200
|
||||
### Requirement: Model info proxy endpoint
|
||||
The system SHALL provide a `GET /api/model/info` Next.js API route that proxies to `${INFERENCE_API_URL}/model/info`. This endpoint returns model metadata and per-class metrics.
|
||||
|
||||
#### Scenario: No annotations exist for chart
|
||||
- **WHEN** GET /api/annotations?chartId=3 is called and no annotations exist for chart 3
|
||||
- **THEN** endpoint returns an empty JSON array `[]` with HTTP 200
|
||||
#### Scenario: Successful model info
|
||||
- **WHEN** GET /api/model/info is called and the inference service is running
|
||||
- **THEN** the route returns the model metadata JSON
|
||||
|
||||
### Requirement: Create annotation endpoint
|
||||
The system SHALL provide a `POST /api/annotations` endpoint that accepts a JSON body with fields: `timestamp` (required, integer), `label_type` (required, string), `chart_id` (required, integer), and `geometry` (optional, object). The endpoint SHALL validate the input, verify the chart exists, serialize geometry to JSON string if present, and insert the record into the `annotations` table. On success, it SHALL return the created annotation object with its assigned `id`.
|
||||
|
||||
#### Scenario: Create a marker annotation
|
||||
- **WHEN** POST /api/annotations is called with `{ "timestamp": 1700000000, "label_type": "break_up", "chart_id": 3 }`
|
||||
- **THEN** endpoint saves the annotation with chart_id 3 and returns the created object with `id` and HTTP 201
|
||||
|
||||
#### Scenario: Create a line annotation
|
||||
- **WHEN** POST /api/annotations is called with `{ "timestamp": 1700000000, "label_type": "line", "chart_id": 3, "geometry": { "startTime": 1700000000, "startPrice": 1.05, "endTime": 1700100000, "endPrice": 1.06 } }`
|
||||
- **THEN** endpoint saves the annotation with chart_id 3 and serialized geometry JSON, returns the created object with HTTP 201
|
||||
|
||||
#### Scenario: Invalid annotation data
|
||||
- **WHEN** POST /api/annotations is called with missing required fields (timestamp, label_type, or chart_id)
|
||||
- **THEN** endpoint returns `{ "error": "<description>" }` with HTTP 400
|
||||
|
||||
#### Scenario: Annotation for non-existent chart
|
||||
- **WHEN** POST /api/annotations is called with a chart_id that does not exist
|
||||
- **THEN** endpoint returns `{ "error": "Chart not found" }` with HTTP 404
|
||||
|
||||
### Requirement: Delete annotation endpoint
|
||||
The system SHALL provide a `DELETE /api/annotations/[id]` endpoint that removes an annotation by its ID. On success, it SHALL return HTTP 200. If the annotation does not exist, it SHALL return HTTP 404.
|
||||
|
||||
#### Scenario: Delete existing annotation
|
||||
- **WHEN** DELETE /api/annotations/5 is called and annotation with id 5 exists
|
||||
- **THEN** endpoint removes the annotation and returns HTTP 200
|
||||
|
||||
#### Scenario: Delete non-existent annotation
|
||||
- **WHEN** DELETE /api/annotations/999 is called and no annotation with that id exists
|
||||
- **THEN** endpoint returns HTTP 404
|
||||
|
||||
### Requirement: Export annotations endpoint
|
||||
The system SHALL provide a `GET /api/export` endpoint that accepts an optional `chartId` query parameter. When `chartId` is provided, the endpoint SHALL export only annotations for that chart. When `chartId` is omitted, the endpoint SHALL export annotations for the most recently created chart. The CSV SHALL have columns: `timestamp`, `label_type`, `price`. The response SHALL set `Content-Type: text/csv` and `Content-Disposition: attachment; filename="annotations.csv"` headers.
|
||||
|
||||
#### Scenario: Export for specific chart
|
||||
- **WHEN** GET /api/export?chartId=3 is called and annotations exist for chart 3
|
||||
- **THEN** endpoint returns a CSV file download with only annotations belonging to chart 3
|
||||
|
||||
#### Scenario: Export without chartId
|
||||
- **WHEN** GET /api/export is called without a chartId parameter
|
||||
- **THEN** endpoint exports annotations for the most recently created chart
|
||||
|
||||
### Requirement: Get candles endpoint
|
||||
The system SHALL provide a `GET /api/candles` endpoint that accepts an optional `chartId` query parameter. When `chartId` is provided, the endpoint SHALL return only candles belonging to that chart. When `chartId` is omitted, the endpoint SHALL return candles for the most recently created chart. Results SHALL be ordered by time ascending. Each object SHALL include: `time`, `open`, `high`, `low`, `close`.
|
||||
|
||||
#### Scenario: Fetch candles for specific chart
|
||||
- **WHEN** GET /api/candles?chartId=3 is called
|
||||
- **THEN** endpoint returns a JSON array of candle objects where chart_id equals 3, ordered by time ascending with HTTP 200
|
||||
|
||||
#### Scenario: Fetch candles without chartId
|
||||
- **WHEN** GET /api/candles is called without a chartId parameter
|
||||
- **THEN** endpoint returns candles for the most recently created chart with HTTP 200
|
||||
|
||||
#### Scenario: No candles exist for chart
|
||||
- **WHEN** GET /api/candles?chartId=3 is called and no candles exist for chart 3
|
||||
- **THEN** endpoint returns an empty JSON array `[]` with HTTP 200
|
||||
#### Scenario: No model available
|
||||
- **WHEN** GET /api/model/info is called and the inference service returns 503
|
||||
- **THEN** the route returns HTTP 503 with `{ "error": "No model available" }`
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue