Dataset (학습 데이터셋)
LLM/VLM 파인튜닝용 학습 데이터셋을 관리하는 API입니다 (Epic #2527). 데이터셋은 워크스페이스 격리 하에 Iceberg 테이블(iceberg.ws_<슬러그>.ds_* — 워크스페이스 스키마)로 저장되며, 버전은 Iceberg snapshot ID 고정 방식입니다 — 설계 배경은 ADR-0027 참조. prefix: /api/v1/datasets.
- modality:
image/image_text/text/video(v1.2+) — audio 는 스키마 예약 - UI 가이드: 학습 데이터셋 (
/datasets)
데이터셋 CRUD
| Method | Path | 권한 | 요청/응답 요지 |
|---|---|---|---|
| POST | /api/v1/datasets | analyst | 생성 (201) — body: name/description/modality. PG 카탈로그 행 + Iceberg 테이블 DDL + 생성자에게 table-scope DataGrant 자동 발급. 이름 중복 409 |
| GET | /api/v1/datasets | viewer | 목록 — limit(≤500)/offset, 워크스페이스 가시성 적용. items[]/total |
| GET | /api/v1/datasets/{dataset_id} | viewer | 단건 상세. 교차 워크스페이스 접근은 404 (IDOR-safe) |
| DELETE | /api/v1/datasets/{dataset_id} | analyst | 동기 DROP + partial failure 표면화 — deleted/target_dropped/drop_error. DROP 실패 시 행은 status='deleting' 으로 보존되며 동일 DELETE 재호출로 재시도 |
적재 (파일 / JSONL)
| Method | Path | 권한 | 요청/응답 요지 |
|---|---|---|---|
| POST | /api/v1/datasets/{dataset_id}/files?split=&label= | analyst | 이미지·비디오 다중 업로드 (split/label 은 image·video 공통 기본값 — zip 폴더/metadata.jsonl 이 우선, v1.2+) (multipart/form-data, image·video modality — zip 은 image 전용, video 는 .mp4/.webm 개별 파일 단건 ≤100MB, v1.2+) → S3 + Iceberg 적재. 응답: accepted[]/duplicates[]/rejected[]/rows_added/resulting_snapshot_id |
| POST | /api/v1/datasets/{dataset_id}/jsonl | analyst | JSONL 승격 (text modality 전용, .jsonl/.ndjson). 쿼리: dry_run(쓰기 없는 미리보기) / mask_pii(PII 마스킹 적재) / split(레코드에 값이 없을 때의 기본값, v1.2+). 지원 키: text/messages/prompt+response(별칭 completion, v1.2+)/chosen+rejected. 응답: subformat(plain/messages/prompt_response/dpo)/rows_added/raw_uri(Bronze 원본 보존)/resulting_snapshot_id |
| POST | /api/v1/datasets/{dataset_id}/uploads | analyst | 청크 업로드 세션 개시 (v1.2+, ADR-0042) — body: filename(.parquet)/declared_bytes/declared_sha256. 쿼터 선판정 413·상한 기본 5GB |
| PUT | /api/v1/datasets/{dataset_id}/uploads/{session_id}/parts/{n} | analyst | part 릴레이 (raw body ≤45MB, 순차) — 선언 초과 413 |
| POST | /api/v1/datasets/{dataset_id}/uploads/{session_id}/complete | analyst | MPU 조립 + sha256 재해시 검증 + parquet 승격 (불일치 409) |
| DELETE | /api/v1/datasets/{dataset_id}/uploads/{session_id} | analyst | 세션 중단 (204) — 만료 세션은 GC 가 24h TTL 로 정리 |
| GET | /api/v1/datasets/{dataset_id}/stats | viewer | 통계 — row_count/split_stats/label_stats |
resulting_snapshot_id는 이번 적재가 만든 Iceberg snapshot 의 10진 문자열입니다 (JS Number 정밀도 보호). 버전 고정 시 이 값을 그대로 사용합니다.
행 조회 / 삭제
| Method | Path | 권한 | 요청/응답 요지 |
|---|---|---|---|
| GET | /api/v1/datasets/{dataset_id}/rows | viewer | 행 조회 — keyset pagination (limit/after/split/label). 응답: columns/items/next_cursor |
| GET | /api/v1/datasets/{dataset_id}/versions/{version}/rows | viewer | 버전 고정 행 조회 — FOR VERSION AS OF snapshot 재현 |
| GET | /api/v1/datasets/{dataset_id}/blob/{content_hash} | viewer | 이미지·비디오 바이트 프록시 (`variant=original |
| DELETE | /api/v1/datasets/{dataset_id}/rows | analyst | 행 삭제 — body: record_ids[] (1~1000). PII 삭제권/오염 데이터 제거. 응답: requested/resulting_snapshot_id |
버전 (snapshot 고정)
| Method | Path | 권한 | 요청/응답 요지 |
|---|---|---|---|
| POST | /api/v1/datasets/{dataset_id}/versions | analyst | 버전 생성 (201) — body: snapshot_id(적재 응답의 resulting_snapshot_id)/note/pii_masked. snapshot 시점 row_count/split_stats 실측 기록. 무효 snapshot 은 422 |
| GET | /api/v1/datasets/{dataset_id}/versions | viewer | 버전 목록 (최신 우선) |
Export (Gold 산출)
| Method | Path | 권한 | 요청/응답 요지 |
|---|---|---|---|
| POST | /api/v1/datasets/{dataset_id}/exports | analyst | 버전 고정 JSONL export 요청 (202) — body: version. 백그라운드 실행 후 상태 폴링. 응답의 dataset_version_uri(gend://datasets/{id}/versions/{v}) 를 학습 잡이 MLflow run tag gend.dataset_version 으로 기록하면 dataset→model 계보가 남습니다 |
| GET | /api/v1/datasets/{dataset_id}/exports | viewer | export 목록 |
| GET | /api/v1/datasets/{dataset_id}/exports/{export_id} | viewer | export 상태/결과 — status/export_uri/row_count/error_message |
| GET | /api/v1/datasets/{dataset_id}/exports/{export_id}/download | viewer | export 산출물 바이트 프록시 (v1.2+) — 인가 후 스트리밍, S3 비노출. ?part=N(총수 X-Export-Part-Count 헤더)·Range 단일 범위(206/416)·미완료 export 409 |
사용량 · 스토리지 쿼터 (v1.2+)
워크스페이스 단위 스토리지 쿼터(#2989 M0). 사용량은 적재 원장(bytes_added) 합계 — files 는 실제 저장된 blob+썸네일 바이트, JSONL/Parquet 는 Bronze 원본 바이트 기준입니다.
| Method | Path | 권한 | 요청/응답 요지 |
|---|---|---|---|
| GET | /api/v1/datasets/usage | viewer | 활성 워크스페이스 사용량 — used_bytes/soft_limit_bytes/hard_limit_bytes(0=무제한)/remaining_bytes(무제한이면 null)/soft_exceeded/hard_exceeded/per_user[](created_by 별 breakdown) |
| POST | /api/v1/datasets/quota-increase | analyst | 쿼터 증설 승인 신청 (201) — body: soft_limit_bytes/hard_limit_bytes(절대값)/reason. 같은 ws 대기 중 신청 존재 시 409. 승인은 POST /api/v1/approvals/{id}/decide (admin) — 승인 시 쿼터 자동 upsert |
| GET | /api/v1/admin/dataset-quotas | admin | 전체 워크스페이스 쿼터 매트릭스 |
| PUT | /api/v1/admin/dataset-quotas/{workspace_id} | admin | 쿼터 upsert — body: soft_limit_bytes/hard_limit_bytes (0=무제한, 둘 다 >0 이면 soft≤hard 강제) |
| DELETE | /api/v1/admin/dataset-quotas/{workspace_id} | admin | 쿼터 해제 (204) — 이후 GEND_DATASET_DEFAULT_QUOTA_BYTES 기본값 적용 (기본 0=무제한) |
- hard 초과: 업로드(
/files·/jsonl)가 413 으로 거부됩니다 — detail 에 "쿼터" 문구가 있어 파일 크기 상한 413 과 구분됩니다. - soft 초과: 업로드는 수행되고 응답의
quota_warning필드에 경고가 실립니다. dry_run=trueJSONL 미리보기는 쓰기가 없어 쿼터 판정을 하지 않습니다.- 운영 절차는 스토리지 쿼터 관리 참조.
인증 · 권한
JWT Bearer 토큰이 필요합니다. 조회는 viewer, 생성/적재/삭제/버전/export 는 analyst 이상이 필요합니다. 모든 경로에 워크스페이스 격리(scope)가 적용되며, 다른 워크스페이스의 데이터셋 ID 로 접근하면 존재 여부를 노출하지 않고 404 를 반환합니다.
에러 코드
| 코드 | 설명 |
|---|---|
| 400 | 잘못된 입력 — modality 불일치, 파일 수 초과, 잘못된 content_hash/variant, 행 삭제 검증 실패 |
| 403 | 워크스페이스에 해당 네임스페이스의 write 바인딩이 없음(v1.2+) — 데이터셋 생성·적재·행 삭제·데이터셋 삭제에 적용됩니다. 정리 경로(청크 세션 abort)는 예외입니다. 관리자에게 바인딩 발급을 요청하세요 |
| 404 | 데이터셋/버전/blob/export 없음 (교차 워크스페이스 포함) |
| 409 | 같은 이름의 데이터셋 존재, 삭제 진행 중(deleting) 데이터셋에 쓰기, 또는 미완료 export 다운로드 |
| 413 | 파일/요청 합산 크기 상한 초과, 또는 워크스페이스 스토리지 쿼터(hard) 초과 (v1.2+ — detail 의 "쿼터" 문구로 구분) |
| 416 | Range 불만족 — blob·export download (Content-Range: bytes */N 반환) |
| 422 | JSONL 파싱 실패, 무효 snapshot_id, 무효 split/label(공백·제어문자·64자 초과, v1.2+) |
| 502 | Trino/Iceberg 적재·조회 실패 |
| 503 | 워크스페이스 해석 불가 (기본 테넌트 부재) |