mirror of
https://github.com/nad4-su/meeting-minutes.git
synced 2026-10-11 15:50:13 +09:00
feat: 템플릿(6종) + 강도 조절(3단계) + 커스텀 프롬프트
회의록 외 다양한 용도로 쓸 수 있도록 출력 구조와 요약 깊이를 분리. 템플릿: - 🗂️ 회의록 (표준, 라이브 O) - 🎓 강의·세미나 노트 (상세, 라이브 O) - 🤝 1:1 미팅 (표준, 라이브 O) - 💡 브레인스토밍 (상세, 라이브 O) - 🎤 인터뷰 (표준, 라이브 X — Q&A 포맷) - 📝 원문 정리 (상세 고정, 라이브 X — 요약 없이 문단화만) - ⚙️ 커스텀 (자유 프롬프트) 강도: 간결 / 표준 / 상세 — 템플릿별 기본값 프리셋, 언제든 override. 변경: - src/lib/templates.ts 신규 — 프리셋 + 프롬프트 빌더 - minutes-generator / live-summary 가 buildPrompt 공유 - /api/summarize{-live} 가 template/depth/customPrompt 파라미터 수용 - page.tsx UI: 템플릿 픽커, 강도 라디오, 커스텀 textarea - LiveRecorder 가 설정을 useLiveSummary 로 전달 (ref 기반 최신값) - 단순 변환 모드는 기존 동작 유지 (템플릿 미적용) - 프롬프트 빌더 단위 테스트 19개 (총 57 tests)
This commit is contained in:
1 parent
6e104cbca1
commit
0101e4b0d2
9 files changed
+675
-64
No files matched your search
@@ -0,0 +1,166 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import {
|
||||
TEMPLATES,
|
||||
buildPrompt,
|
||||
resolveDepth,
|
||||
liveEnabledFor,
|
||||
depthAdjustableFor,
|
||||
DEFAULT_TEMPLATE_ID,
|
||||
DEFAULT_DEPTH,
|
||||
} from '@/lib/templates'
|
||||
|
||||
describe('TEMPLATES registry', () => {
|
||||
it('6개의 프리셋 템플릿을 제공한다', () => {
|
||||
expect(Object.keys(TEMPLATES)).toHaveLength(6)
|
||||
expect(TEMPLATES).toHaveProperty('meeting')
|
||||
expect(TEMPLATES).toHaveProperty('lecture')
|
||||
expect(TEMPLATES).toHaveProperty('one_on_one')
|
||||
expect(TEMPLATES).toHaveProperty('brainstorm')
|
||||
expect(TEMPLATES).toHaveProperty('interview')
|
||||
expect(TEMPLATES).toHaveProperty('raw')
|
||||
})
|
||||
|
||||
it('기본 템플릿은 meeting, 기본 강도는 standard', () => {
|
||||
expect(DEFAULT_TEMPLATE_ID).toBe('meeting')
|
||||
expect(DEFAULT_DEPTH).toBe('standard')
|
||||
})
|
||||
|
||||
it('raw 템플릿은 라이브 요약과 강도 조절이 불가', () => {
|
||||
expect(TEMPLATES.raw.liveSupported).toBe(false)
|
||||
expect(TEMPLATES.raw.depthAdjustable).toBe(false)
|
||||
})
|
||||
|
||||
it('interview 템플릿은 라이브 요약 불가', () => {
|
||||
expect(TEMPLATES.interview.liveSupported).toBe(false)
|
||||
})
|
||||
|
||||
it('lecture는 detailed, meeting은 standard 기본 강도', () => {
|
||||
expect(TEMPLATES.lecture.defaultDepth).toBe('detailed')
|
||||
expect(TEMPLATES.meeting.defaultDepth).toBe('standard')
|
||||
})
|
||||
})
|
||||
|
||||
describe('buildPrompt', () => {
|
||||
const transcript = '안녕하세요 테스트입니다'
|
||||
|
||||
it('meeting + standard + non-live 조합의 프롬프트를 생성한다', () => {
|
||||
const prompt = buildPrompt({
|
||||
templateId: 'meeting',
|
||||
depth: 'standard',
|
||||
transcript,
|
||||
})
|
||||
expect(prompt).toContain('회의록 작성 전문가')
|
||||
expect(prompt).toContain('액션 아이템')
|
||||
expect(prompt).toContain('표준')
|
||||
expect(prompt).toContain(transcript)
|
||||
expect(prompt).not.toContain('회의가 아직 진행 중')
|
||||
})
|
||||
|
||||
it('live=true이면 진행 중 모디파이어를 포함한다', () => {
|
||||
const prompt = buildPrompt({
|
||||
templateId: 'meeting',
|
||||
depth: 'standard',
|
||||
transcript,
|
||||
live: true,
|
||||
})
|
||||
expect(prompt).toContain('회의가 아직 진행 중')
|
||||
expect(prompt).toContain('논의 중')
|
||||
})
|
||||
|
||||
it('depthAdjustable=false인 raw 템플릿은 강도 모디파이어를 적용하지 않는다', () => {
|
||||
const prompt = buildPrompt({
|
||||
templateId: 'raw',
|
||||
depth: 'concise',
|
||||
transcript,
|
||||
})
|
||||
expect(prompt).toContain('편집자')
|
||||
expect(prompt).not.toContain('작성 강도')
|
||||
})
|
||||
|
||||
it('liveSupported=false 템플릿은 live=true여도 라이브 모디파이어를 넣지 않는다', () => {
|
||||
const prompt = buildPrompt({
|
||||
templateId: 'interview',
|
||||
depth: 'standard',
|
||||
transcript,
|
||||
live: true,
|
||||
})
|
||||
expect(prompt).not.toContain('회의가 아직 진행 중')
|
||||
})
|
||||
|
||||
it('강도별로 다른 모디파이어가 포함된다', () => {
|
||||
const concise = buildPrompt({
|
||||
templateId: 'meeting',
|
||||
depth: 'concise',
|
||||
transcript,
|
||||
})
|
||||
const detailed = buildPrompt({
|
||||
templateId: 'meeting',
|
||||
depth: 'detailed',
|
||||
transcript,
|
||||
})
|
||||
expect(concise).toContain('간결')
|
||||
expect(detailed).toContain('상세')
|
||||
expect(concise).not.toContain('상세')
|
||||
})
|
||||
|
||||
it('custom 템플릿은 customPrompt를 그대로 사용한다', () => {
|
||||
const prompt = buildPrompt({
|
||||
templateId: 'custom',
|
||||
depth: 'standard',
|
||||
transcript,
|
||||
customPrompt: '당신은 시인입니다. 시 형식으로 정리하세요.',
|
||||
})
|
||||
expect(prompt).toContain('당신은 시인입니다')
|
||||
expect(prompt).toContain(transcript)
|
||||
expect(prompt).not.toContain('액션 아이템')
|
||||
})
|
||||
|
||||
it('custom인데 customPrompt가 비어있으면 meeting 기본 프롬프트로 폴백', () => {
|
||||
const prompt = buildPrompt({
|
||||
templateId: 'custom',
|
||||
depth: 'standard',
|
||||
transcript,
|
||||
customPrompt: ' ',
|
||||
})
|
||||
expect(prompt).toContain('회의록 작성 전문가')
|
||||
})
|
||||
})
|
||||
|
||||
describe('resolveDepth', () => {
|
||||
it('depth 미지정 시 템플릿 기본값을 반환한다', () => {
|
||||
expect(resolveDepth('lecture')).toBe('detailed')
|
||||
expect(resolveDepth('meeting')).toBe('standard')
|
||||
})
|
||||
|
||||
it('depthAdjustable=false 템플릿은 전달된 depth를 무시', () => {
|
||||
expect(resolveDepth('raw', 'concise')).toBe('detailed')
|
||||
})
|
||||
|
||||
it('지정된 depth가 있고 조절 가능하면 그대로 반환', () => {
|
||||
expect(resolveDepth('meeting', 'concise')).toBe('concise')
|
||||
})
|
||||
})
|
||||
|
||||
describe('liveEnabledFor / depthAdjustableFor', () => {
|
||||
it('raw는 라이브/강도 둘 다 false', () => {
|
||||
expect(liveEnabledFor('raw')).toBe(false)
|
||||
expect(depthAdjustableFor('raw')).toBe(false)
|
||||
})
|
||||
|
||||
it('interview는 라이브 false, 강도 true', () => {
|
||||
expect(liveEnabledFor('interview')).toBe(false)
|
||||
expect(depthAdjustableFor('interview')).toBe(true)
|
||||
})
|
||||
|
||||
it('meeting / lecture / brainstorm / one_on_one은 둘 다 true', () => {
|
||||
expect(liveEnabledFor('meeting')).toBe(true)
|
||||
expect(liveEnabledFor('lecture')).toBe(true)
|
||||
expect(liveEnabledFor('brainstorm')).toBe(true)
|
||||
expect(liveEnabledFor('one_on_one')).toBe(true)
|
||||
})
|
||||
|
||||
it('custom은 라이브 가능, 강도 조절은 불가(사용자 프롬프트가 강도까지 지정)', () => {
|
||||
expect(liveEnabledFor('custom')).toBe(true)
|
||||
expect(depthAdjustableFor('custom')).toBe(false)
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user