fix: colab notebook — CUDA 13 runtime + raw key capture

Colab now ships CUDA 13.0 (driver 580); CTranslate2 wheels are CUDA 12
so GPU init fails with 'unsupported device cuda:0'. Install
nvidia-cublas-cu12/nvidia-cudnn-cu12 and set LD_LIBRARY_PATH in the
install cell; transcription cell reports failures cleanly.

API smoke: api_keys.json stores only digests (raw key shown once), so
the notebook now captures RAW_KEY at creation and uses it for upload
instead of reading the digest file (fixes 401).
This commit is contained in:
2026-08-12 17:07:23 +09:00
parent f6e5ae662f
commit fb936e1953
2 changed files with 62 additions and 27 deletions
+5 -5
View File
@@ -59,7 +59,7 @@
"execution_count": null,
"metadata": {},
"outputs": [],
"source": "# 3) 설치 — Colab은 시스템 pip가 표준 (venv는 Colab에서 ensurepip 오류로 실패할 수 있음)\n!pip install -q --upgrade pip\n!pip install -q -e '.[engine,api]'\n!pip install -q edge-tts # 샘플 음성 생성용\nprint('설치 완료')\n!which luke-scribe && luke-scribe --help 2>&1 | head -8\n"
"source": "# 3) 설치 — Colab은 시스템 pip가 표준 (venv는 Colab에서 ensurepip 오류로 실패할 수 있음)\n!pip install -q --upgrade pip\n!pip install -q -e '.[engine,api]'\n!pip install -q edge-tts # 샘플 음성 생성용\n\n# CUDA 13(Colab)에서는 CTranslate2 wheel(CUDA 12용)이 런타임 라이브러리를 못 찾음.\n# CUDA 12 런타임(cuBLAS/cuDNN)을 pip로 설치하고 LD_LIBRARY_PATH에 추가한다.\n!pip install -q nvidia-cublas-cu12 nvidia-cudnn-cu12\nimport os, nvidia.cublas.lib, nvidia.cudnn.lib\nos.environ['LD_LIBRARY_PATH'] = (\n os.path.dirname(nvidia.cublas.lib.__file__) + ':' +\n os.path.dirname(nvidia.cudnn.lib.__file__) + ':' +\n os.environ.get('LD_LIBRARY_PATH', '')\n)\nprint('설치 완료')\n!which luke-scribe && luke-scribe --help 2>&1 | head -8\n"
},
{
"cell_type": "markdown",
@@ -102,7 +102,7 @@
"execution_count": null,
"metadata": {},
"outputs": [],
"source": "# 7) 실전 전사 — GPU 자동 감지 + 모델 다운로드 (large-v3-turbo)\n# 첫 실행 시 Hugging Face에서 모델을 다운로드합니다 (turbo ≈ 1.6GB, 1~2분)\n!time luke-scribe transcribe samples/colab-ko-en.mp3 --language ko --device auto\n"
"source": "# 7) 실전 전사 — GPU 자동 감지 + 모델 다운로드 (large-v3-turbo)\n# 첫 실행 시 Hugging Face에서 모델을 다운로드합니다 (turbo ≈ 1.6GB, 1~2분)\n# CUDA 13 환경에서 CTranslate2가 GPU를 못 열면 자동으로 CPU로 폴백합니다.\nimport subprocess, json\nr = subprocess.run(\n ['luke-scribe', 'transcribe', 'samples/colab-ko-en.mp3', '--language', 'ko', '--device', 'auto'],\n capture_output=True, text=True,\n)\nprint(r.stdout[-2500:] if r.stdout else '')\nprint(r.stderr[-800:] if r.stderr else '')\n"
},
{
"cell_type": "markdown",
@@ -126,7 +126,7 @@
"execution_count": null,
"metadata": {},
"outputs": [],
"source": "# 9) API 키 생성 (1회 출력)\n!luke-scribe key --create --scopes transcribe,admin --file /content/api_keys.json\n"
"source": "# 9) API 키 생성 — raw 키는 1회 출력되므로 여기서 캡처한다\nimport subprocess, json, os\nr = subprocess.run(\n ['luke-scribe', 'key', '--create', '--scopes', 'transcribe,admin', '--file', '/content/api_keys.json'],\n capture_output=True, text=True,\n)\ncreated = json.loads(r.stdout)\nRAW_KEY = created['key'] # 이후 셀에서 사용\nprint('key_id:', created['key_id'], '| scopes:', created['scopes'])\nprint('raw 키 캡처 완료 (표시 안 함)')\n"
},
{
"cell_type": "code",
@@ -140,7 +140,7 @@
"execution_count": null,
"metadata": {},
"outputs": [],
"source": "# 11) 업로드 → poll → 결과\nimport json, time, urllib.request, urllib.error\n\nKEY = json.load(open('/content/api_keys.json'))['keys'][0]\n\n# multipart 업로드 (curl 사용 — 간단)\nimport subprocess\nr = subprocess.run(\n ['curl', '-s', '-X', 'POST', 'http://localhost:8000/v1/jobs',\n '-H', f'X-API-Key: {KEY}',\n '-F', 'file=@samples/colab-ko-en.mp3',\n '-F', 'options={\"language\":\"ko\",\"formats\":[\"json\",\"srt\"]}'],\n capture_output=True, text=True,\n)\njob = json.loads(r.stdout)\nprint('생성:', job)\njob_id = job['job_id']\n\n# 완료까지 poll (워커 없이 in-proc이라 즉시 완료되지는 않음 → 여기선 생성까지만 확인)\nprint('job_id =', job_id)\n"
"source": "# 11) 업로드 → poll → 결과 (RAW_KEY는 셀 9에서 캡처된 값)\nimport json, time, urllib.request, urllib.error, subprocess\n\nassert 'RAW_KEY' in globals(), '셀 9(키 생성)를 먼저 실행하세요'\n\n# multipart 업로드 (curl 사용 — 간단)\nr = subprocess.run(\n ['curl', '-s', '-X', 'POST', 'http://localhost:8000/v1/jobs',\n '-H', f'X-API-Key: {RAW_KEY}',\n '-F', 'file=@samples/colab-ko-en.mp3',\n '-F', 'options={\"language\":\"ko\",\"formats\":[\"json\",\"srt\"]}'],\n capture_output=True, text=True,\n)\njob = json.loads(r.stdout)\nprint('생성:', job)\njob_id = job.get('job_id')\n\n# 완료까지 poll (in-proc 큐는 워커가 소비해야 하므로 셀 12에서 drain 처리)\nif job_id:\n print('job_id =', job_id)\n for _ in range(60):\n st = json.loads(urllib.request.urlopen(\n urllib.request.Request(f'http://localhost:8000/v1/jobs/{job_id}',\n headers={'X-API-Key': RAW_KEY})\n ).read())\n if st['status'] in ('completed', 'failed', 'cancelled'):\n print('최종 상태:', st['status'])\n break\n time.sleep(2)\n else:\n print('60초 내 미완료 — 셀 12(워커 drain) 실행 후 다시 확인')\nelse:\n print('업로드 실패 — 서버 로그 확인: /content/logs/server.log')\n"
},
{
"cell_type": "markdown",
@@ -169,7 +169,7 @@
{
"cell_type": "markdown",
"metadata": {},
"source": "## 8) 문제 해결\n\n### CUDA 라이브러리 오류 (faster-whisper/CTranslate2)\n\nColab T4에는 CUDA 런타임이 있지만, CTranslate2가 pip wheel의 CUDA 라이브러리를\n찾지 못하면 아래를 실행하세요:\n\n```python\nimport os\nos.environ['LD_LIBRARY_PATH'] = (\n '/usr/local/cuda/lib64:' + os.environ.get('LD_LIBRARY_PATH', '')\n)\n```\n\n### 모델 다운로드 실패\n\n- Hugging Face 연결 필요. 재시도: `LUKESCRIBE_MODEL_DOWNLOAD_RETRIES=3`\n- 특정 모델만: `--model large-v3-turbo` (기본) / `--model large-v3`\n\n### OOM (16GB VRAM)\n\n- EngineOwner가 자동으로 정밀도를 강등합니다 (float16 → int8_float16 → int8 → CPU)\n- 수동 지정: `--compute-type int8_float16`\n\n### 저장소가 private인데 클론 안 됨\n\n1번 셀의 `GITEA_TOKEN`에 토큰을 입력하고 런타임 → **Restart session** 후 다시 실행하세요.\n"
"source": "## 8) 문제 해결\n\n### CUDA 13 환경에서 `unsupported device cuda:0` (faster-whisper/CTranslate2)\n\nColab CUDA 13.0(드라이버 580)으로 올라가면서, CUDA 12용으로 빌드된\nCTranslate2 wheel이 런타임 라이브러리(cuBLAS/cuDNN)를 찾지 못하는 이슈가 발생합니다.\n설치 셀 3에서 CUDA 12 런타임을 pip로 설치하고 `LD_LIBRARY_PATH`를 잡아 해결합니다.\n그래도 안 되면 CPU로 폴백: `--device cpu --compute-type int8` (느리지만 확실).\n\n### 모델 다운로드 실패\n\n- Hugging Face 연결 필요. 재시도: `LUKESCRIBE_MODEL_DOWNLOAD_RETRIES=3`\n- 특정 모델만: `--model large-v3-turbo` (기본) / `--model large-v3`\n\n### API 키 인증 실패 (401)\n\n`api_keys.json`에는 **다이제스트만** 저장되고 raw 키는 생성 시 1회만 출력됩니다\n(보안 설계). 셀 9에서 `RAW_KEY`를 캡처했는지 확인하세요 — 파일에서 키를 읽으면 401이 납니다.\n\n### OOM (16GB VRAM)\n\n- EngineOwner가 자동으로 정밀도를 강등합니다 (float16 → int8_float16 → int8 → CPU)\n- 수동 지정: `--compute-type int8_float16`\n\n### 저장소가 private인데 클론 안 됨\n\n1번 셀의 `GITEA_TOKEN`에 토큰을 입력하고 런타임 → **Restart session** 후 다시 실행하세요.\n"
}
]
}
+56 -21
View File
@@ -106,6 +106,16 @@ def cells() -> list[dict]:
"!pip install -q --upgrade pip\n"
"!pip install -q -e '.[engine,api]'\n"
"!pip install -q edge-tts # 샘플 음성 생성용\n"
"\n"
"# CUDA 13(Colab)에서는 CTranslate2 wheel(CUDA 12용)이 런타임 라이브러리를 못 찾음.\n"
"# CUDA 12 런타임(cuBLAS/cuDNN)을 pip로 설치하고 LD_LIBRARY_PATH에 추가한다.\n"
"!pip install -q nvidia-cublas-cu12 nvidia-cudnn-cu12\n"
"import os, nvidia.cublas.lib, nvidia.cudnn.lib\n"
"os.environ['LD_LIBRARY_PATH'] = (\n"
" os.path.dirname(nvidia.cublas.lib.__file__) + ':' +\n"
" os.path.dirname(nvidia.cudnn.lib.__file__) + ':' +\n"
" os.environ.get('LD_LIBRARY_PATH', '')\n"
")\n"
"print('설치 완료')\n"
"!which luke-scribe && luke-scribe --help 2>&1 | head -8\n"
),
@@ -139,7 +149,14 @@ def cells() -> list[dict]:
code(
"# 7) 실전 전사 — GPU 자동 감지 + 모델 다운로드 (large-v3-turbo)\n"
"# 첫 실행 시 Hugging Face에서 모델을 다운로드합니다 (turbo ≈ 1.6GB, 1~2분)\n"
"!time luke-scribe transcribe samples/colab-ko-en.mp3 --language ko --device auto\n"
"# CUDA 13 환경에서 CTranslate2가 GPU를 못 열면 자동으로 CPU로 폴백합니다.\n"
"import subprocess, json\n"
"r = subprocess.run(\n"
" ['luke-scribe', 'transcribe', 'samples/colab-ko-en.mp3', '--language', 'ko', '--device', 'auto'],\n"
" capture_output=True, text=True,\n"
")\n"
"print(r.stdout[-2500:] if r.stdout else '')\n"
"print(r.stderr[-800:] if r.stderr else '')\n"
),
md(
"### 5-2) 후처리 검증\n\n"
@@ -155,8 +172,16 @@ def cells() -> list[dict]:
"서버를 백그라운드로 띄우고 **업로드 → poll → 결과(SRT)** 흐름을 검증합니다."
),
code(
"# 9) API 키 생성 (1회 출력)\n"
"!luke-scribe key --create --scopes transcribe,admin --file /content/api_keys.json\n"
"# 9) API 키 생성 — raw 키는 1회 출력되므로 여기서 캡처한다\n"
"import subprocess, json, os\n"
"r = subprocess.run(\n"
" ['luke-scribe', 'key', '--create', '--scopes', 'transcribe,admin', '--file', '/content/api_keys.json'],\n"
" capture_output=True, text=True,\n"
")\n"
"created = json.loads(r.stdout)\n"
"RAW_KEY = created['key'] # 이후 셀에서 사용\n"
"print('key_id:', created['key_id'], '| scopes:', created['scopes'])\n"
"print('raw 키 캡처 완료 (표시 안 함)')\n"
),
code(
"# 10) 서버 기동 (in-proc 큐, 백그라운드 — Colab에서는 %%bash --bg 또는 nohup 사용)\n"
@@ -176,26 +201,39 @@ def cells() -> list[dict]:
" print(open('/content/logs/server.log').read()[-1500:])\n"
),
code(
"# 11) 업로드 → poll → 결과\n"
"import json, time, urllib.request, urllib.error\n"
"# 11) 업로드 → poll → 결과 (RAW_KEY는 셀 9에서 캡처된 값)\n"
"import json, time, urllib.request, urllib.error, subprocess\n"
"\n"
"KEY = json.load(open('/content/api_keys.json'))['keys'][0]\n"
"assert 'RAW_KEY' in globals(), '셀 9(키 생성)를 먼저 실행하세요'\n"
"\n"
"# multipart 업로드 (curl 사용 — 간단)\n"
"import subprocess\n"
"r = subprocess.run(\n"
" ['curl', '-s', '-X', 'POST', 'http://localhost:8000/v1/jobs',\n"
" '-H', f'X-API-Key: {KEY}',\n"
" '-H', f'X-API-Key: {RAW_KEY}',\n"
" '-F', 'file=@samples/colab-ko-en.mp3',\n"
" '-F', 'options={\"language\":\"ko\",\"formats\":[\"json\",\"srt\"]}'],\n"
" capture_output=True, text=True,\n"
")\n"
"job = json.loads(r.stdout)\n"
"print('생성:', job)\n"
"job_id = job['job_id']\n"
"job_id = job.get('job_id')\n"
"\n"
"# 완료까지 poll (워커 없이 in-proc이라 즉시 완료되지는 않음 → 여기선 생성까지만 확인)\n"
"# 완료까지 poll (in-proc 큐는 워커가 소비해야 하므로 셀 12에서 drain 처리)\n"
"if job_id:\n"
" print('job_id =', job_id)\n"
" for _ in range(60):\n"
" st = json.loads(urllib.request.urlopen(\n"
" urllib.request.Request(f'http://localhost:8000/v1/jobs/{job_id}',\n"
" headers={'X-API-Key': RAW_KEY})\n"
" ).read())\n"
" if st['status'] in ('completed', 'failed', 'cancelled'):\n"
" print('최종 상태:', st['status'])\n"
" break\n"
" time.sleep(2)\n"
" else:\n"
" print('60초 내 미완료 — 셀 12(워커 drain) 실행 후 다시 확인')\n"
"else:\n"
" print('업로드 실패 — 서버 로그 확인: /content/logs/server.log')\n"
),
md(
"### 참고 — 서버에서 실제 전사까지 실행하려면\n\n"
@@ -254,21 +292,18 @@ def cells() -> list[dict]:
),
md(
"## 8) 문제 해결\n\n"
"### CUDA 라이브러리 오류 (faster-whisper/CTranslate2)\n\n"
"Colab T4에는 CUDA 런타임이 있지만, CTranslate2가 pip wheel의 CUDA 라이브러리를\n"
"찾지 못하면 아래를 실행하세요:\n"
"\n"
"```python\n"
"import os\n"
"os.environ['LD_LIBRARY_PATH'] = (\n"
" '/usr/local/cuda/lib64:' + os.environ.get('LD_LIBRARY_PATH', '')\n"
")\n"
"```\n"
"\n"
"### CUDA 13 환경에서 `unsupported device cuda:0` (faster-whisper/CTranslate2)\n\n"
"Colab CUDA 13.0(드라이버 580)으로 올라가면서, CUDA 12용으로 빌드된\n"
"CTranslate2 wheel이 런타임 라이브러리(cuBLAS/cuDNN)를 찾지 못하는 이슈가 발생합니다.\n"
"설치 셀 3에서 CUDA 12 런타임을 pip로 설치하고 `LD_LIBRARY_PATH`를 잡아 해결합니다.\n"
"그래도 안 되면 CPU로 폴백: `--device cpu --compute-type int8` (느리지만 확실).\n\n"
"### 모델 다운로드 실패\n\n"
"- Hugging Face 연결 필요. 재시도: `LUKESCRIBE_MODEL_DOWNLOAD_RETRIES=3`\n"
"- 특정 모델만: `--model large-v3-turbo` (기본) / `--model large-v3`\n"
"\n"
"### API 키 인증 실패 (401)\n\n"
"`api_keys.json`에는 **다이제스트만** 저장되고 raw 키는 생성 시 1회만 출력됩니다\n"
"(보안 설계). 셀 9에서 `RAW_KEY`를 캡처했는지 확인하세요 — 파일에서 키를 읽으면 401이 납니다.\n\n"
"### OOM (16GB VRAM)\n\n"
"- EngineOwner가 자동으로 정밀도를 강등합니다 (float16 → int8_float16 → int8 → CPU)\n"
"- 수동 지정: `--compute-type int8_float16`\n"