v4.1.13: 언어앱 타일뷰 기본화 + 카테고리 추가 + 선생님 Q&A 패널

기본 보기를 타일 그리드로 전환하고 사용자가 카테고리를 직접 추가할 수 있게 함.
문법/단어/문화 질문에 답하는 "선생님" 채팅 패널 신설(/api/language/ask-teacher).
선생님 답변의 마크다운(표·볼드·목록·인용)이 그대로 노출되던 버그를 자체 렌더러로 수정.
This commit is contained in:
kim
2026-07-16 15:30:48 +09:00
parent edd7816048
commit f6f7f14541
3 changed files with 243 additions and 23 deletions
+41
View File
@@ -171,6 +171,47 @@ JSON으로만 응답하세요 (코드 블록 없이):
}
});
// POST /api/language/ask-teacher — general Q&A chat, NOT translation (that's /interpret).
// A student asks about grammar/usage/culture/vocabulary in Korean or Uzbek and gets an
// explanation + examples, with short conversation history for follow-up questions.
app.post('/api/language/ask-teacher', async (req: express.Request, res: express.Response) => {
try {
const message = String(req.body?.message || '').trim().slice(0, 1000);
if (!message) { res.status(400).json({ success: false, error: 'message required' }); return; }
const historyRaw = Array.isArray(req.body?.history) ? req.body.history : [];
// Cap history to the last 10 turns — this is a lightweight tutor chat, not a full
// session-backed conversation; the client resends what it wants remembered each time.
const history = historyRaw.slice(-10).filter((m: any) => m?.role && m?.content).map((m: any) => ({
role: m.role === 'assistant' ? 'assistant' as const : 'user' as const,
content: String(m.content).slice(0, 1000),
}));
const systemPrompt = `당신은 친절한 우즈베크어 선생님입니다. 한국인 학습자의 질문에 답합니다.
- 문법, 단어 용법, 발음, 우즈베크 문화·관습에 대한 질문에 답하세요.
- 설명은 한국어로 하고, 우즈베크어 예문은 항상 라틴 문자(1993년 이후 표준, oʻ/gʻ 포함)로 정확히 표기하세요.
- 예문에는 한글 발음 표기와 뜻을 함께 달아주세요.
- 학습자가 문장을 만들어서 봐달라고 하면 자연스러운 표현으로 고쳐주고 왜 그런지 짧게 설명하세요.
- 통역 요청(문장을 그냥 번역해달라는 요청)이 오면 번역은 해주되, 배울 점(문법 포인트 등)을 한 줄 덧붙이세요.
- 답변은 간결하게 — 장황한 설명보다 핵심 위주로, 이모지는 최소화.`;
const ollama = getOllamaClient();
const result = await ollama.chatWithThinking(
[
{ role: 'system' as const, content: systemPrompt },
...history,
{ role: 'user' as const, content: message },
],
'executor',
{ num_predict: 800, temperature: 0.4, think: false }
);
const reply = String(result.message?.content || '').trim();
if (!reply) { res.json({ success: false, error: '응답 생성 실패' }); return; }
res.json({ success: true, reply });
} catch (err: any) {
res.status(500).json({ success: false, error: String(err?.message || err) });
}
});
// POST /api/language/tts — Uzbek TTS via edge-tts
app.post('/api/language/tts', async (req: express.Request, res: express.Response) => {
try {