add test module
This commit is contained in:
@@ -1,92 +1,82 @@
|
||||
# Sentence Boundary API
|
||||
# Oral Trainer Video Service
|
||||
|
||||
This service looks up pre-generated sentence boundaries by the SHA-256 hash of
|
||||
the exact video bytes. It does not analyze media during an API request.
|
||||
FastAPI service for the Android oral-training SDK. It provides:
|
||||
|
||||
## Install
|
||||
- chunked video upload with SHA-256 calculation;
|
||||
- files stored under `data/v/` and metadata stored in SQLite;
|
||||
- background sentence transcription through MOSS-Transcribe-Diarize;
|
||||
- a browser-based administration page at `/admin`;
|
||||
- video catalog and HTTP Range playback;
|
||||
- sentence-boundary lookup compatible with the existing Android SDK;
|
||||
- student recording upload and `asr-fluency-v1` assessment;
|
||||
- the legacy JSON boundary index as a read-only fallback.
|
||||
|
||||
From the repository root:
|
||||
## Local Run
|
||||
|
||||
```bash
|
||||
python3 -m venv .venv-sentence-api
|
||||
python3.12 -m venv .venv-sentence-api
|
||||
. .venv-sentence-api/bin/activate
|
||||
python -m pip install -r sentence_api/requirements.txt
|
||||
```
|
||||
|
||||
## Generate The Index
|
||||
|
||||
Generate boundaries with the same silence detector used by the desktop player:
|
||||
|
||||
```bash
|
||||
python -m sentence_api.generate_boundaries \
|
||||
"/path/to/lesson.mp4" \
|
||||
--index sentence_api/data/sentence_boundaries.json
|
||||
```
|
||||
|
||||
The command calculates the SHA-256 hash, detects boundaries, converts seconds
|
||||
to milliseconds, infers each `end_ms` from the next sentence start, and writes
|
||||
the result atomically into the JSON index. The last sentence ends at the media
|
||||
duration.
|
||||
|
||||
For a large course library, run this command in an ingestion worker and store
|
||||
the same document in a database or object storage instead of committing the
|
||||
JSON file to the application image.
|
||||
|
||||
## Run
|
||||
|
||||
```bash
|
||||
SENTENCE_BOUNDARIES_FILE=sentence_api/data/sentence_boundaries.json \
|
||||
export ADMIN_API_KEY=local-development-key
|
||||
export MOSS_TRANSCRIBE_URL=http://127.0.0.1:8001/v1/audio/transcriptions
|
||||
python -m uvicorn sentence_api.main:app --host 0.0.0.0 --port 8000
|
||||
```
|
||||
|
||||
The interactive API documentation is available at `/docs`.
|
||||
Open:
|
||||
|
||||
## Request
|
||||
|
||||
```http
|
||||
GET /api/v1/videos/{sha256}/sentence-boundaries
|
||||
```text
|
||||
http://127.0.0.1:8000/admin
|
||||
http://127.0.0.1:8000/docs
|
||||
```
|
||||
|
||||
Example using the demo record in the checked-in index:
|
||||
When `MOSS_TRANSCRIBE_URL` is empty, uploaded videos still receive silence-based
|
||||
boundaries, but no reference transcript is produced and assessment is disabled.
|
||||
|
||||
## Main Endpoints
|
||||
|
||||
```text
|
||||
GET /healthz
|
||||
GET /api/v1/videos
|
||||
GET /api/v1/videos/{sha256}
|
||||
GET /api/v1/videos/{sha256}/content
|
||||
GET /api/v1/videos/{sha256}/sentence-boundaries
|
||||
POST /api/v1/videos/{sha256}/sentences/{index}/assessments
|
||||
|
||||
POST /api/v1/admin/videos
|
||||
PUT /api/v1/admin/videos/raw
|
||||
POST /api/v1/admin/videos/{sha256}/process
|
||||
PUT /api/v1/admin/videos/{sha256}/sentences/{index}
|
||||
DELETE /api/v1/admin/videos/{sha256}
|
||||
```
|
||||
|
||||
Admin endpoints use `X-Admin-Key` when `ADMIN_API_KEY` is configured. Assessment
|
||||
requests use `X-Client-Key` when `CLIENT_API_KEY` is configured.
|
||||
|
||||
## Assessment
|
||||
|
||||
```bash
|
||||
curl http://127.0.0.1:8000/api/v1/videos/468a4d064f6ec49942b45e25ab93c500d31870f978c4f28ff8b3b408852326e0/sentence-boundaries
|
||||
curl -X POST \
|
||||
http://127.0.0.1:8000/api/v1/videos/{sha256}/sentences/0/assessments \
|
||||
-F audio=@student.wav \
|
||||
-F language=en
|
||||
```
|
||||
|
||||
The MP4 used during development is also indexed. Its hash is
|
||||
`b6631d5cf48f37fed0ecc623563dd48b7ed660689b4d25d2ebac7fecab807ddc`, and its
|
||||
generated index contains 244 boundaries.
|
||||
The current score intentionally measures reading content and fluency:
|
||||
|
||||
The response is:
|
||||
|
||||
```json
|
||||
{
|
||||
"video_hash": "...",
|
||||
"duration_ms": 16000,
|
||||
"algorithm_version": "silence-rms-v1",
|
||||
"sentences": [
|
||||
{
|
||||
"index": 0,
|
||||
"start_ms": 0,
|
||||
"end_ms": 4230,
|
||||
"text": null
|
||||
}
|
||||
]
|
||||
}
|
||||
```text
|
||||
overall = content * 80% + fluency * 20%
|
||||
fluency = duration * 35% + pauses * 40% + speech rate * 25%
|
||||
```
|
||||
|
||||
Unknown hashes return `404`. A hash must be a 64-character hexadecimal
|
||||
SHA-256 digest; malformed values return `422`.
|
||||
`pronunciation_score` and `prosody_score` remain `null` until a phoneme/GOP
|
||||
model is connected.
|
||||
|
||||
When testing from a physical Android phone, replace `127.0.0.1` with the
|
||||
computer's LAN IP address. `127.0.0.1` on the phone refers to the phone itself.
|
||||
For production, expose the API over HTTPS.
|
||||
## Tests
|
||||
|
||||
## Android Request Flow
|
||||
```bash
|
||||
python -m pytest sentence_api/tests -q
|
||||
```
|
||||
|
||||
The mobile app should calculate the hash from the selected `content://` URI in
|
||||
streaming chunks, request the endpoint, map the returned `sentences` to
|
||||
`SentenceBoundary`, and then call `controller.loadItem`. The hash must be
|
||||
calculated from the exact bytes of the same video served to the player. For
|
||||
HTTPS course videos, the course manifest can carry the hash and avoid hashing
|
||||
the entire remote file on every device.
|
||||
See [DEPLOYMENT.md](DEPLOYMENT.md) for Docker, MOSS, Nginx, HTTPS, large-file
|
||||
upload, backup, and production operation instructions.
|
||||
|
||||
Reference in New Issue
Block a user