persist LLM sentence explainer with supabase

This commit is contained in:
polv committed 2026-08-25 16:33:05 +07:00
1 parent e734d09039
commit 82b51c0703
2 files changed
+58 -12

No files matched your search

+57 -11
View File
@@ -67,13 +67,23 @@
<div v-else v-html="v.notes"></div> <div v-else v-html="v.notes"></div>
</blockquote> </blockquote>
<div v-if="example"> <div v-if="example">
<blockquote <div v-if="exNotes">
v-if="exNotes" <button
v-html="markdownIt.render(exNotes)" type="button"
></blockquote> @click="
<button type="button" v-else @click="explainSentence(example.ko)"> exNotes = '';
Explain sentence explainSentence(example.ko);
</button> "
>
New explanation
</button>
<blockquote v-html="markdownIt.render(exNotes)" />
</div>
<div v-else>
<button type="button" @click="explainSentence(example.ko)">
Explain sentence
</button>
</div>
</div> </div>
</details> </details>
</nav> </nav>
@@ -168,7 +178,7 @@ const vaud = useTemplateRef<InstanceType<typeof SentenceAudio>>("vaud");
const v = computed(() => vList.value[vIndex.value]); const v = computed(() => vList.value[vIndex.value]);
const example = computed(() => { const example = computed(() => {
if (!v.value.examples?.length) return null; if (!v.value?.examples?.length) return null;
const ex = v.value.examples; const ex = v.value.examples;
return ex[exIndex.value]; return ex[exIndex.value];
}); });
@@ -200,6 +210,25 @@ watch(quizState, () => {
saveQuizState(); saveQuizState();
}); });
watch(example, () => {
exNotes.value = "";
if (!example.value) return;
if (supabase) {
let { ko } = example.value;
ko = ko.replaceAll("**", "");
supabase
.from("sentence-explain")
.select<any, { explain: string }>("explain")
.eq("ko", ko)
.then((r) => {
if (!r.data?.[0]) return;
exNotes.value = r.data[0].explain;
});
}
});
const vocabulary: Record<string, IVoc[]> = {}; const vocabulary: Record<string, IVoc[]> = {};
for (const [k, vs] of Object.entries( for (const [k, vs] of Object.entries(
@@ -400,7 +429,9 @@ const markdownIt = new MarkdownIt({
typographer: true, typographer: true,
}); });
async function explainSentence(s: string) { async function explainSentence(ko: string) {
ko = ko.replaceAll("**", "");
for await (const data of LLMstream({ for await (const data of LLMstream({
model: "deepseek-v4-flash", model: "deepseek-v4-flash",
messages: [ messages: [
@@ -409,14 +440,29 @@ async function explainSentence(s: string) {
content: content:
"Explain how this sentence works in 500 characters. Give key vocabularies.", "Explain how this sentence works in 500 characters. Give key vocabularies.",
}, },
{ role: "user", content: s }, { role: "user", content: ko },
], ],
})) { })) {
if (data.done) return; if (data.done) break;
if (data.content) { if (data.content) {
exNotes.value += data.content; exNotes.value += data.content;
} }
} }
if (supabase) {
console.log(supabase);
const user_id = await supabase.auth.getUser().then((r) => r.data.user?.id);
console.log(user_id);
await supabase.from("sentence-explain").upsert(
{
user_id,
ko,
explain: exNotes.value,
},
{ onConflict: "ko,user_id" },
);
}
} }
function onModalClickOutside(ev: MouseEvent) { function onModalClickOutside(ev: MouseEvent) {
@@ -71,7 +71,7 @@ vocabulary:
audio: https://www.howtostudykorean.com/wp-content/uploads/2015/11/Lesson20v6.mp3 audio: https://www.howtostudykorean.com/wp-content/uploads/2015/11/Lesson20v6.mp3
en: powder en: powder
examples: examples:
- ko: 케이크 반죽에 **밀가루**를 넣으세요 - ko: 케이크 반죽에 밀**가루**를 넣으세요
en: Put some **flour** into the cake batter, please en: Put some **flour** into the cake batter, please
- ko: 봄에는 꽃**가루** 알레르기를 조심해 야 해요 - ko: 봄에는 꽃**가루** 알레르기를 조심해 야 해요
en: You need to be careful about **pollen** allergies in the spring en: You need to be careful about **pollen** allergies in the spring