refactor ai lookup

This commit is contained in:
an-lee
2024-05-15 17:06:10 +08:00
parent 8c18f2458d
commit 89faf35483
5 changed files with 91 additions and 52 deletions

View File

@@ -57,9 +57,8 @@ const DICITIONARY_PROMPT = `You are an {learning_language}-{native_language} dic
I will provide "word(it also maybe a phrase)" and "context" as input, you should return the "word", "lemma", "pronunciation", "pos", "definition", "translation" and "context_translation" as output.
If no context is provided, return the most common definition.
If you do not know the appropriate definition, return an empty string for "definition" and "translation".
Always return output in JSON format.
Always return the output in JSON format as following:
The output format:
{{
"word": "the original word or phrase",
"lemma": "lemma",

View File

@@ -436,7 +436,8 @@
"extracting": "Extracting",
"extractionFailed": "Extraction failed",
"extractedSuccessfully": "Extracted successfully",
"lookup": "Look up",
"lookup": "Lookup",
"reLookup": "Re-Lookup",
"lookupAll": "Look up all",
"lookingUp": "Looking up",
"pending": "Pending",

View File

@@ -437,6 +437,7 @@
"extractionFailed": "提取失败",
"extractedSuccessfully": "提取成功",
"lookup": "查询",
"reLookup": "重新查询",
"lookupAll": "全部查询",
"lookingUp": "正在查询",
"pending": "等待中",

View File

@@ -12,6 +12,7 @@ import {
import { useAiCommand, useCamdict } from "@renderer/hooks";
import { LoaderIcon, Volume2Icon } from "lucide-react";
import { t } from "i18next";
import { md5 } from "js-md5";
export const LookupWidget = () => {
const { EnjoyApp } = useContext(AppSettingsProviderContext);
@@ -104,15 +105,16 @@ export const AiLookupResult = (props: {
sourceId?: string;
}) => {
const { word, context = "", sourceType, sourceId } = props;
const { webApi } = useContext(AppSettingsProviderContext);
const { webApi, EnjoyApp } = useContext(AppSettingsProviderContext);
const [lookingUp, setLookingUp] = useState<boolean>(false);
const [result, setResult] = useState<LookupType>();
const { lookupWord } = useAiCommand();
const handleLookup = async () => {
const handleLookup = async (options?: { force: boolean }) => {
if (lookingUp) return;
if (!word) return;
const { force = false } = options;
setLookingUp(true);
lookupWord({
@@ -120,6 +122,8 @@ export const AiLookupResult = (props: {
context,
sourceId,
sourceType,
cacheKey: `lookup-${md5(`${word}-${context}`)}`,
force,
})
.then((lookup) => {
if (lookup?.meaning) {
@@ -134,26 +138,36 @@ export const AiLookupResult = (props: {
});
};
const fetchCachedLookup = async () => {
const remoteLookup = await webApi.lookup({
word,
context,
sourceId,
sourceType,
});
if (remoteLookup?.meaning) {
setResult(remoteLookup);
return;
}
const cached = await EnjoyApp.cacheObjects.get(
`lookup-${md5(`${word}-${context}`)}`
);
if (cached?.meaning) {
setResult(cached);
return;
}
setResult(undefined);
};
/*
* Fetch cached lookup result.
*/
useEffect(() => {
if (!word) return;
webApi
.lookup({
word,
context,
sourceId,
sourceType,
})
.then((res) => {
if (res?.meaning) {
setResult(res);
} else {
setResult(null);
}
});
fetchCachedLookup();
}, [word, context]);
if (!word) return null;
@@ -164,34 +178,53 @@ export const AiLookupResult = (props: {
{t("aiDictionary")}
</div>
{result ? (
<div className="mb-4 select-text">
<div className="mb-2 font-semibord font-serif">{word}</div>
<div className="mb-2">
{result.meaning?.pos && (
<span className="italic text-sm text-muted-foreground mr-2">
{result.meaning.pos}
</span>
)}
{result.meaning?.pronunciation && (
<span className="text-sm font-code mr-2">
/{result.meaning.pronunciation.replaceAll("/", "")}/
</span>
)}
{result.meaning?.lemma &&
result.meaning.lemma !== result.meaning.word && (
<span className="text-sm">({result.meaning.lemma})</span>
<>
<div className="mb-4 select-text">
<div className="mb-2 font-semibord font-serif">{word}</div>
<div className="mb-2">
{result.meaning?.pos && (
<span className="italic text-sm text-muted-foreground mr-2">
{result.meaning.pos}
</span>
)}
{result.meaning?.pronunciation && (
<span className="text-sm font-code mr-2">
/{result.meaning.pronunciation.replaceAll("/", "")}/
</span>
)}
{result.meaning?.lemma &&
result.meaning.lemma !== result.meaning.word && (
<span className="text-sm">({result.meaning.lemma})</span>
)}
</div>
<div className="text-serif">{result.meaning.translation}</div>
<div className="text-serif">{result.meaning.definition}</div>
</div>
<div className="text-serif">{result.meaning.translation}</div>
<div className="text-serif">{result.meaning.definition}</div>
</div>
<div className="flex items-center">
<Button
className="cursor-pointer"
variant="secondary"
size="sm"
disabled={lookingUp}
onClick={() => handleLookup({ force: true })}
asChild
>
<a>
{lookingUp && (
<LoaderIcon className="animate-spin w-4 h-4 mr-2" />
)}
{t("reLookup")}
</a>
</Button>
</div>
</>
) : (
<div className="flex items-center space-x-2 py-2">
<Button
className="cursor-pointer"
size="sm"
asChild
onClick={handleLookup}
onClick={() => handleLookup()}
>
<a>
{lookingUp && (

View File

@@ -21,8 +21,10 @@ export const useAiCommand = () => {
context: string;
sourceId?: string;
sourceType?: string;
cacheKey?: string;
force?: boolean;
}) => {
const { context, sourceId, sourceType } = params;
const { context, sourceId, sourceType, cacheKey, force = false } = params;
let { word } = params;
word = word.trim();
if (!word) return;
@@ -34,7 +36,7 @@ export const useAiCommand = () => {
sourceType,
});
if (lookup.meaning) {
if (lookup.meaning && !force) {
return lookup;
}
@@ -54,18 +56,21 @@ export const useAiCommand = () => {
}
);
// Accept result from gpt-3/4 models
if (modelName.match(/^gpt-(3|4)\S*/i) && res.context_translation?.trim()) {
return webApi.updateLookup(lookup.id, {
meaning: res,
sourceId,
sourceType,
});
} else {
return Object.assign(lookup, {
meaning: res,
});
webApi.updateLookup(lookup.id, {
meaning: res,
sourceId,
sourceType,
});
const result = Object.assign(lookup, {
meaning: res,
});
if (cacheKey) {
EnjoyApp.cacheObjects.set(cacheKey, result);
}
return result;
};
const extractStory = async (story: StoryType) => {