Commit Graph

5 Commits

Author SHA1 Message Date
EJClaw
8d91087f85 ci: GPU 없는 서버 Windows VM 지원 + 전 파이프라인 자동 검증
Some checks failed
Windows / verify (push) Has been cancelled
RTX 5050 은 PCIe 패스스루로 이 VM(.9)이 독점하므로 Proxmox 에 Windows VM 을
띄워도 GPU 를 못 준다. 그래서 "Windows 검증에 GPU 가 정말 필요한가"를 실제로
재봤다 — GPU 를 완전히 가린 채 점검기를 돌린 결과, 실패한 건 'GPU 인식' 하나뿐이고
그건 이미 경고 처리라 빌드를 막지 않는다. 나머지(단축키·모니터·오디오 열거·
exe 빌드·기동·오버레이 렌더링)는 전부 통과한다. 즉 GPU 없는 서버 VM 으로 충분하고,
CUDA/VRAM 관련은 GPU 가 물려 있는 .9 에서 이미 검증 중이다.

- tests/fixtures/en_callout_16k.wav (139KB)
  MeloTTS 로 만든 4.4초 영어 콜아웃. CI 가 실제 음성으로 검증할 수 있게 커밋.

- windows_smoke.py --pipeline
  오디오 파일 -> 음성인식 -> 번역 -> 한국어 자막까지 끝까지 확인한다.
  부품이 다 통과해도 이어 붙이면 안 되는 경우를 잡기 위한 것.
  CPU + 가장 가벼운 티어로 돌려 GPU 없는 VM 에서도 그대로 된다.
  실측: 27초, 'Enemy coming from the left, fall back now.'
        -> '적 왼쪽에서 오는, 지금 뒤로 물러서.'
  음성인식 결과에 핵심 단어가 있는지, 자막에 한글이 있는지까지 검사한다.

- 워크플로에 해당 단계 추가 (30분 타임아웃, 첫 회만 모델 다운로드)

- docs/WINDOWS-TESTING.md 에 서버 VM 구축 절차
  Proxmox qm create 예시와 함께, 빠뜨리면 조용히 실패하는 두 가지를 명시:
  (1) --audio0 없으면 오디오 장치가 안 잡혀 WASAPI 점검이 실패
  (2) 자동 로그온이 없으면 데스크톱 세션이 없어 GUI/스크린샷이 전부 실패
      (러너를 서비스로 돌리면 안 되는 실질적 이유이기도 하다)

검증: pytest 189개 통과, ruff clean, 워크플로 11스텝 파싱 확인(bash 0개),
      GPU 숨긴 조건에서 점검기 실행, --pipeline 실제 통과

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-09-23 03:32:35 +09:00
EJClaw
4af97a68f2 fix: Windows 점검기 SIGABRT 수정 + 러너 안내 정확하게
Some checks failed
Windows / verify (push) Has been cancelled
리뷰 지적을 확인해보니 두 건은 실제 결함이었다.

1) 점검기가 화면 없는 환경에서 그냥 죽음 (SIGABRT, 종료코드 134)
   QT_QPA_PLATFORM 을 "windows" 로 하드코딩해 둔 탓에, 표시할 화면이 없으면
   첫 QApplication 생성에서 플러그인 로드에 실패하며 아무 결과도 못 남기고
   abort 했다. 점검기가 그렇게 죽으면 존재 이유가 없다.
   - 화면이 없으면 offscreen 으로 떨어지게 하고, 이 판단을 Qt import 전에 한다
   - QApplication 생성을 _qt_app() 한 곳으로 모음
   - 실행 플랫폼을 출력해 리눅스에서 돌아간 경우가 바로 보이게 함
   이제 환경변수 없이 돌려도 항목별 판정과 종료코드가 정상으로 나온다.

2) 보안 설명이 과장돼 있었음 — 가장 중요한 수정
   "커밋된 워크플로만 실행하니 임의 명령은 불가"라고 썼는데, 나는 이 저장소
   main 에 push 할 수 있으므로 워크플로를 고쳐 커밋하면 그 PC에서 원하는
   명령을 돌릴 수 있다. 개인 PC를 내주는 판단에 쓰일 설명을 틀리게 준 것이라
   그대로 바로잡았다. 실제로 보장되는 것(숨길 수 없음·언제든 끌 수 있음·
   계정 권한으로만 동작)만 적고, VM 또는 전용 계정을 권하도록 바꿨다.
   서비스 등록(SYSTEM 권한) 안내도 로그온 작업(계정 권한)으로 교체 —
   "관리자로 띄우지 말라"는 권고와 모순됐다.

3) 라벨 오타 안전장치 (공식 문서 확인 중 발견)
   Gitea 는 runs-on 이 어떤 라벨과도 안 맞으면 실패시키지 않고 기본 우분투
   컨테이너에서 돌린다. Windows 검증이 조용히 리눅스에서 돌아가 "통과"로
   보일 수 있어, 워크플로 첫 단계에서 RUNNER_OS 를 확인해 막는다.
   문서의 등록 라벨도 windows:host 로 명시 (생략 시 기본값이 host 이지만
   명시가 낫다 — docs.gitea.com/runner/labels).

4) 트리거 설명 정정: "아무거나 push" -> main push 또는 수동 실행.

검증: pytest 189개 통과, ruff clean, 워크플로 트리거/스텝 파싱 확인,
      QT_QPA_PLATFORM 없이 점검기 실행해 abort 없이 종료코드 1 확인

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-09-23 03:17:11 +09:00
EJClaw
2fcd961f8f ci: Windows 자동 검증 추가 (Gitea Actions + 스모크 스크립트)
Some checks failed
Windows / verify (push) Has been cancelled
개발이 리눅스에서 이뤄지다 보니 Windows 전용 경로는 한 번도 실행해 본 적이
없다. 오디오 캡처, 전역 단축키, 모니터 열거, exe 빌드가 전부 여기 해당하고,
"리눅스에서 다 통과했는데 윈도우에서 안 되더라"가 나올 수 있는 지점이다.

- .gitea/workflows/windows.yml
  push 때마다 Windows 러너에서 테스트 -> 스모크 -> 포터블 exe 빌드 ->
  빌드된 exe 기동 확인까지 하고 exe·스크린샷을 artifact 로 올린다.
  의존성을 packaging/requirements-portable.txt 로 깔아 torch 없는 포터블
  조건을 그대로 재현하고, torch 가 섞이면 바로 실패하게 했다.

- packaging/windows_smoke.py
  리눅스에서 확인 불가능한 항목만 모은 점검기. WASAPI 장치 열거, 소리 내는
  프로그램 열거, 전역 단축키 등록/해제, 실제 모니터 열거, GPU 인식,
  사용 가능 티어, 자막 오버레이 실제 렌더링(스크린샷 저장).
  한 항목이 실패해도 나머지를 계속 돌려 전체 그림을 한 번에 보여준다.
  러너 환경에 따라 없을 수 있는 항목(오디오 장치·보조 exe·GPU)은 경고로만
  두고, 코드 문제인 항목만 빌드를 실패시킨다.

- docs/WINDOWS-TESTING.md
  러너 등록 절차와, 러너 없이 직접 돌려보는 방법.

러너가 없으면 워크플로는 대기만 하므로 지금 상태에 영향이 없다.

검증: pytest 189개 통과, ruff clean, 워크플로 YAML 파싱 확인,
      스모크 스크립트를 리눅스에서 실행해 항목별 판정과 종료코드 확인

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-09-23 03:11:31 +09:00
EJClaw
bede94ee48 fix: 포터블에 transformers 포함 + 실제 모델로 드러난 용어집 결함 수정
리뷰 지적(포터블에서 transformers 제외 -> NLLB 로드 실패)을 고치려고 실제
NLLB 를 받아 돌려봤고, 그 과정에서 용어집이 사실상 동작하지 않고 있었다는
것을 발견했다. 단위 테스트는 "모델이 자리표시자를 통과시킨다"는 틀린 전제
위에 서 있었다.

1) 포터블 패키징 (리뷰 지적)
   transformers 를 제외 목록에서 빼고 hiddenimports 에 넣었다. NLLB
   토크나이저가 AutoTokenizer 를 쓰기 때문이다. transformers 는 torch 가
   없으면 토크나이저 전용 모드로 뜨며 그게 우리 용도와 정확히 맞는다.
   torch 없는 환경에서 ctranslate2/transformers/faster-whisper import 와
   앱 기동을 검증하는 test_portable.py 를 추가했다.

2) 자리표시자 형식 (실측으로 발견)
   `⟦0⟧` 는 NLLB 가 괄호를 날려 생존률 0/3 이었다. 용어가 자막에서 그냥
   사라지고 있었다 ("Third party incoming" -> "0 들어오는"). 후보 8종을
   실제 모델로 비교해 `#0#` 로 교체 (3/3, 다중 4/5).

3) 소실 대비
   모델이 문장 일부를 누락하면 자리표시자도 사라진다. 그대로 복원하면
   용어가 증발하므로, 하나라도 없으면 보호 없이 재번역한다.

4) 서술어는 문장 전체일 때만 (whole_only)
   절/서술어를 문장 중간에서 치환하면 문법이 무너진다.
     before: "탄 필요해와 구급상자"
     after : "탄약과 구급상자가 필요합니다"
   해당 56개 항목을 whole_only 로 지정해 단독 발화일 때만 적용한다.
   ("Cover me!" -> "엄호해줘" 는 그대로 유지)

5) 조사 교정
   역어 받침이 달라 "자기장를" 이 남던 것을 fix_particles() 로 고친다.
   을/를, 이/가, 은/는, 과/와, (으)로 — 한글 코드에서 받침을 읽어 판정.

검증: pytest 189개 통과, ruff clean
      실제 NLLB-600M(torch 없이 CPU)로 번역 품질 직접 확인

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-09-23 01:20:42 +09:00
EJClaw
71436843d8 feat: 존댓말/반말 모드, 포터블 exe 빌드, AI 로고/배너
말투 (존댓말 기본, 반말 선택)
- 원문이 실제로 존댓말이면 반말 모드여도 존댓말을 지킨다. 상대가 정중하게
  말했는데 자막이 반말이면 뉘앙스가 뒤집히기 때문.
- 영어·중국어는 문법적 높임이 없으므로 항상 고른 모드를 따른다.
  "please" 를 존댓말 근거로 삼으면 오탐이 많아 쓰지 않았다.
- 낮춤 변환은 한글 자모를 분해해 실제 활용 규칙(모음조화 + 축약)을 구현했다.
  오+아->와, 지+어->져, 하+어->해, 았/었 뒤는 항상 어, 치겠습니다->칠게.
  어미를 나열하는 방식보다 훨씬 넓게 맞는다.
- 변환 방향은 존댓말->반말 한쪽만 한다. 번역 모델의 한국어 출력이 이미
  격식체라 존댓말 모드는 손댈 필요가 없고, 반대 방향은 훨씬 자주 틀린다.
- 지시문을 이해하는 Qwen3 에는 프롬프트로도 전달한다. Seed-X 는 지시문을
  못 알아듣는 모델이라 후처리로만 맞춘다.

포터블 exe
- PyInstaller 명세 + 빌드 스크립트. torch 를 의도적으로 제외했다.
  torch+CUDA 만 2.5GB 라 onefile 로 묶으면 실행할 때마다 그걸 임시폴더에
  푸느라 1분 넘게 걸려 쓸 수 없다.
- 음성인식(faster-whisper)도 번역(NLLB)도 CTranslate2 위에서 돌아 torch 가
  필요 없다. 덕분에 1~3티어는 그대로 다 되고 exe 는 3GB -> 500MB 가 된다.
- 4~5티어는 못 쓰므로 tier_availability() 로 판정해 모델 화면에 '사용 불가'와
  이유를 표시한다. torch 없는 환경에서 앱 전체가 뜨는 것을 확인했다.

AI 이미지
- SDXL-turbo 로 아이콘/배경 생성 (로컬 GPU, 피크 VRAM 1.9GB).
- 글자는 AI 가 제대로 못 쓰므로 아트만 AI 로 만들고 타이포그래피는
  정확히 렌더링해 합성했다. icon.ico 는 16~256px 멀티해상도.

검증: pytest 163개 통과 (말투 53개 신규), ruff clean

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-09-23 01:06:23 +09:00