- MemoService.organize() → List<MemoDraft> 반환. 프롬프트를 JSON 배열 출력으로
변경, 서로 다른 주제는 분리·관련 내용은 묶도록 지시. extractJson 배열 대응.
- POST /api/notes/organize → { items: [...] } (save=true면 전부 저장).
- 프론트: 정리 결과를 다중 카드로 표시, 항목별 수정·삭제 + '모두 저장'(카드마다 /memo).
실측: 두서없는 5주제 입력 → 5개 항목(각 PARA 카테고리+GTD 태그)으로 정확히 분리.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- OciGenAiService.chat(): 모델 인지 분기. openai.* 모델은 maxTokens/
temperature(0.3)를 거부하므로 maxCompletionTokens 사용 + temperature 생략.
gemini/cohere 등 기존 모델은 maxTokens+temperature 유지(하위호환).
- NoteController: 노트 최종 결과 하단에 사용 모델 푸터 표기
(음성변환 모델 + 교정·요약 모델). 모델명은 설정값에서 조회해 자동 반영.
- STT(음성→텍스트)는 OpenRouter Gemini 유지, 변경 없음.
- OCI_GENAI_MODEL은 .env에서 openai.gpt-5.6-terra로 전환(커밋 제외).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
대용량 오디오가 OpenRouter(Gemini) 단일 요청 한도(~20MB)를 초과해 502로
실패하고, 깨진 Gemma fallback(ollama runner crash)으로 넘어가 전체 실패하던
문제 수정.
- 공통 청크 분할기 transcribeChunked(전략 패턴)로 OpenRouter/Gemma 통일
- CHUNK_SECONDS=180s, OVERLAP_SECONDS=10s 오버랩 분할(경계 단어 손실 방지)
- mergeWithOverlapDedup: 인접 청크 토큰 위치정렬 80% 일치로 중복 제거,
미검출 시 줄바꿈 안전 연결(누락 방지)
- 청크 실패는 기록·로그 후 [변환 실패한 구간 번호] 명시, 전부 실패 시 예외
- transcribeAsync의 Gemma fallback도 단발→transcribeWithGemma(청크) 사용
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- generate_checklist.js: 본문에 '비서울 지역 + 거주/소재/관내/재학' 정방향 패턴이면 제외
- 서울/수도권/전국 포함 시 유지(서울 거주자 가능), 서울 기관 사업도 유지
- 역방향(주소+지역)은 기관 연락처 푸터 오탐이라 미검사
- apply-checklist.md: 지역(제목+주관+본문)+연령+성별/대상 → 109건
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- generate_checklist.js: 남성 기준 여성 전용 제외, 특수대상(장애인/보훈/다문화/북한이탈) 전용 제외
- 제목+주관기관 기준(본문 '우대' 가점 언급은 미검사로 오제거 방지)
- 지역 보완: 달구벌(=대구) 추가
- apply-checklist.md: 지역+연령+성별/대상 누적 적용 → 117건
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- generate_checklist.js: 서울 거주 기준 타 지역 한정 공고 제외(접두/주관기관 + 안전한 도·권역은 제목 본문까지)
- apply-checklist.md: 252→137건(타지역 115건 제외), 서울+전국 공고만 유지
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Chrome 136+가 기본 프로필 디렉토리에서 원격 디버깅(CDP)을 거부하여
4월 13일 이후 웹크롤링 3차 폴백/유튜브 자막 추출이 전부 실패하던 문제 해결.
- 프로필을 non-default 디렉토리(~/.config/google-chrome-cdp)로 이동해
로그인 세션 유지한 채 CDP 허용
- start-chrome.sh 신규: 기존 Chrome 정리 + stale lock 제거 후
--remote-debugging-port=9222 --remote-debugging-address=127.0.0.1 로 기동
- ecosystem.config.cjs: sundol-chrome PM2 앱 추가 (수동 실행 금지, PM2 통일)
※ frontend script의 /usr/local/bin/node 변경은 이전 작업분이 함께 포함됨
- PlaywrightBrowserService: CDP_URL을 127.0.0.1로 고정 (IPv6 ::1 해석 함정 제거)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Use 1.7B model (0.6B had tensor mismatch with cached prompts)
- Speak endpoint uses ref_audio directly (not cached pkl) as fallback
- Cache voice clone prompts in memory on startup
- Add SpeakableText component: 🔊 icon on each p and li element
- Remove old TTSReader sequential approach
- Add global exception handler to TTS server
- Fix profile localStorage caching
- inference_mode + bf16 optimization
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Notes:
- notes table with TEXT/AUDIO types, category support
- Audio upload → OpenRouter Gemini STT → OCI GenAI polish/summary
- Raw STT saved separately in raw_content column
- Polish/summary button for manual re-processing
- Async processing with real-time polling
Voice Clone TTS:
- Qwen3-TTS 1.7B model on A10 GPU via FastAPI server
- Voice profile registration (record/upload → save embedding)
- Profile-based TTS generation API
- TTS web page with recording, profile management, generation
Auth fixes:
- Store both access + refresh tokens in localStorage
- Initialize state from localStorage synchronously (no flash)
- Request interceptor reads token from localStorage every request
- Refresh via body (not just cookie)
Other fixes:
- maxTokens 4096 → 65536 (OCI GenAI Gemini supports up to 65536)
- Fix broken Korean chars in source files
- OpenRouter config for STT
- ffmpeg installed for audio conversion
- Ollama + Gemma 4 E4B installed (STT fallback)
- nginx proxy for TTS server (/api/tts/)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add 2-panel category view: sidebar tree + filtered item list
- Category counts use DISTINCT with descendant inclusion
- Hide empty categories, show category badges on item cards
- Add client-side pagination (10 items/page) for both views
- Persist access token in localStorage to survive page refresh
- Fix token refresh retry on backend restart
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace Playwright standalone browser with CDP connection to user Chrome
(bypasses YouTube bot detection by using logged-in Chrome session)
- Add video playback, ad detection/skip, and play confirmation before transcript extraction
- Extract transcript JS to separate resource files (fix SyntaxError in evaluate)
- Add ytInitialPlayerResponse-based transcript extraction as primary method
- Fix token refresh: retry on network error during backend restart
- Fix null userId logout, CLOB type hint for structured_content
- Disable XFCE screen lock/screensaver
- Add troubleshooting entries (#10-12) and YouTube transcript guide
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Load cookies.txt (Netscape format) into Playwright browser context
before navigating to YouTube, enabling authenticated access to bypass
bot detection that blocks transcript retrieval.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace Jsoup-based approach with io.github.thoroldvix:youtube-transcript-api
as primary method (supports manual/generated captions, language priority).
Playwright head mode kept as fallback when API fails.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Jsoup was blocked by YouTube bot detection. Now uses Playwright with
headed Chromium via Xvfb virtual display to bypass restrictions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- YouTubeTranscriptService: fetches captions from YouTube page (ko > en > first available)
- GET /api/knowledge/youtube-transcript endpoint
- Frontend: "트랜스크립트 자동 가져오기" button appears when valid YouTube URL entered
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Jsoup fails on bot-blocked sites (403). Now tries Jsoup first,
then falls back to Jina Reader (r.jina.ai) for better coverage.
Supports optional API key via JINA_READER_API_KEY env var.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Google OAuth authentication with callback flow
- Knowledge ingest pipeline (TEXT/WEB/YOUTUBE → chunking → categorization → embedding)
- OCI GenAI integration (chat, embeddings) with multi-model support
- Semantic search via Oracle VECTOR_DISTANCE
- RAG-based AI chat with source attribution
- Todos with subtasks, filters, and priority levels
- Habits with daily check-in, streak tracking, and color customization
- Study Cards with SM-2 spaced repetition and LLM auto-generation
- Tags system with knowledge item mapping
- Dashboard with live data from all modules
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>