docs: add training case product readme
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
# Training Case Engine M1-M6 Product README
|
||||
|
||||
Training Case Engine is the product-facing training module in AI Exchange. M1-M6 provide persisted training cases, a lightweight HTTP API, a dashboard entry, validation hardening, public package import/export, and an offline package smoke path.
|
||||
|
||||
## Dashboard Entry
|
||||
|
||||
Run the local API server with the project script and open the root dashboard. The dashboard includes a Training panel with:
|
||||
|
||||
- available cases
|
||||
- user progress
|
||||
- attempt start
|
||||
- annotation note save
|
||||
- attempt submit
|
||||
|
||||
The dashboard calls the same Training endpoints listed below and displays public case fields only.
|
||||
|
||||
## API Surface
|
||||
|
||||
All endpoints use JSON responses. List endpoints accept `limit` with an upper bound of `100`.
|
||||
|
||||
| Method | Path | Capability |
|
||||
| --- | --- | --- |
|
||||
| GET | `/training/cases` | List cases by optional status, skill tag, instrument, and limit filters. |
|
||||
| GET | `/training/cases/{case_id}` | Read one public case. |
|
||||
| GET | `/training/cases/export` | Export a public package with the same case filters as the case list. |
|
||||
| POST | `/training/cases/import` | Import a versioned public package and create cases plus tasks. |
|
||||
| POST | `/training/attempts` | Start an attempt for a case. |
|
||||
| POST | `/training/attempts/{attempt_id}/annotations` | Save an annotation payload for an attempt. |
|
||||
| POST | `/training/attempts/{attempt_id}/submit` | Submit an attempt with an optional score. |
|
||||
| GET | `/training/progress` | List progress rows by optional user, skill tag, and limit filters. |
|
||||
|
||||
Public case and package responses omit the internal solution key and private answer details.
|
||||
|
||||
## Package Smoke
|
||||
|
||||
Use the offline package smoke to check import plus public export without external services:
|
||||
|
||||
```bash
|
||||
.venv/bin/python scripts/run_training_case_package.py \
|
||||
--input tests/fixtures/training_case_package_m6.json \
|
||||
--output /tmp/training_case_public_export.json
|
||||
```
|
||||
|
||||
The broader offline check suite also includes this package smoke:
|
||||
|
||||
```bash
|
||||
.venv/bin/python scripts/run_offline_checks.py
|
||||
```
|
||||
|
||||
## Validation And Errors
|
||||
|
||||
- Malformed or non-object request bodies return `400` with `invalid_json_body`.
|
||||
- Missing required JSON fields return `400` with `missing_required_json_field:<field>`.
|
||||
- Invalid JSON field values return `400` with `invalid_json_field:<field>`.
|
||||
- Invalid path identifiers return `400` with `invalid_path_param:<field>`.
|
||||
- Invalid list limits return `400` with `invalid_limit_query_param:limit`; valid limits are `1` through `100`.
|
||||
- Missing cases return `404` with `not_found`.
|
||||
- Unexpected failures return `500` with `internal_server_error` and do not echo raw internals.
|
||||
|
||||
Package import rejects private internal solution-key fields. Package export is stable public JSON and omits private answer detail.
|
||||
|
||||
## Data Isolation Guarantees
|
||||
|
||||
- Training records are scoped to the training domain: cases, tasks, attempts, annotations, scores, and progress.
|
||||
- Training may reference upstream market or rule events as read-only context.
|
||||
- Training APIs and package tooling do not write to signals, executions, backtest results, or live-trading records.
|
||||
- Public package import/export keeps operator-only notes, rubric internals, and private answer detail out of exported payloads.
|
||||
- The offline package smoke uses local files and an in-memory training repository; it writes only the requested public export file.
|
||||
@@ -4,6 +4,7 @@ from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
TRAINING_PRODUCT_DOC = ROOT / "docs" / "training_case_engine_m1_m6_product_readme.md"
|
||||
|
||||
|
||||
class DocumentationConsistencyTests(unittest.TestCase):
|
||||
@@ -17,6 +18,47 @@ class DocumentationConsistencyTests(unittest.TestCase):
|
||||
|
||||
self.assertEqual(missing, [])
|
||||
|
||||
def test_training_case_product_doc_lists_public_m1_m6_surface(self) -> None:
|
||||
text = TRAINING_PRODUCT_DOC.read_text(encoding="utf-8")
|
||||
|
||||
for snippet in (
|
||||
"Training panel",
|
||||
"/training/cases",
|
||||
"/training/cases/{case_id}",
|
||||
"/training/cases/export",
|
||||
"/training/cases/import",
|
||||
"/training/attempts",
|
||||
"/training/attempts/{attempt_id}/annotations",
|
||||
"/training/attempts/{attempt_id}/submit",
|
||||
"/training/progress",
|
||||
".venv/bin/python scripts/run_training_case_package.py",
|
||||
".venv/bin/python scripts/run_offline_checks.py",
|
||||
"internal solution key",
|
||||
"Data Isolation Guarantees",
|
||||
):
|
||||
with self.subTest(snippet=snippet):
|
||||
self.assertIn(snippet, text)
|
||||
|
||||
def test_training_case_product_doc_avoids_private_literal_shapes(self) -> None:
|
||||
text = TRAINING_PRODUCT_DOC.read_text(encoding="utf-8").lower()
|
||||
|
||||
for term in (
|
||||
"data" + "base",
|
||||
"data" + "base_url",
|
||||
"post" + "gresql",
|
||||
"psy" + "copg",
|
||||
"pass" + "word",
|
||||
"pass" + "wd",
|
||||
"tok" + "en",
|
||||
"sec" + "ret",
|
||||
"api" + "_key",
|
||||
"answer" + "_key",
|
||||
):
|
||||
with self.subTest(term=term):
|
||||
self.assertNotIn(term, text)
|
||||
|
||||
self.assertIsNone(re.search(r"\b(?:\d{1,3}\.){3}\d{1,3}\b", text))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user