fix: resolve bugs 01 through 08 and set Ollama TTL to 1h
This commit is contained in:
+132
-8
@@ -171,11 +171,11 @@ async def fetch_wiktionary_definitions(word: str, client: Optional[httpx.AsyncCl
|
||||
return {"parts_of_speech": [], "definitions": [], "examples": []}
|
||||
|
||||
|
||||
async def fetch_mymemory(query: str, client: Optional[httpx.AsyncClient] = None) -> Dict[str, Any]:
|
||||
async def fetch_mymemory(query: str, langpair: str = "en|ru", client: Optional[httpx.AsyncClient] = None) -> Dict[str, Any]:
|
||||
url = "https://api.mymemory.translated.net/get"
|
||||
params = {
|
||||
"q": query,
|
||||
"langpair": "en|ru",
|
||||
"langpair": langpair,
|
||||
"de": config.MYMEMORY_EMAIL
|
||||
}
|
||||
try:
|
||||
@@ -294,11 +294,132 @@ async def lookup_term(query: str) -> Dict[str, Any]:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Check offline dictionary first (Zero Daemon, < 0.1ms via SQLite mmap)
|
||||
words = query_clean.split()
|
||||
is_sentence = len(words) > 4
|
||||
is_cyrillic = bool(re.search(r"[\u0400-\u04FF]", query_clean))
|
||||
words = query_clean.split()
|
||||
is_sentence = len(words) > 4
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Reverse Translation (Russian -> English)
|
||||
# -----------------------------------------------------------------------
|
||||
if is_cyrillic:
|
||||
matched_term = None
|
||||
phonetic = ""
|
||||
pos = ""
|
||||
clean_trans = query_clean
|
||||
tr_list = [query_clean]
|
||||
is_single = len(words) == 1
|
||||
|
||||
# 1. Search offline dictionary for exact reverse matches
|
||||
if not is_sentence:
|
||||
try:
|
||||
with get_db() as conn:
|
||||
cur = conn.execute(
|
||||
"SELECT term, translation, phonetic, pos FROM offline_dictionary WHERE translation LIKE ?",
|
||||
(f"%{query_clean}%",)
|
||||
)
|
||||
rows = cur.fetchall()
|
||||
exact_matches = []
|
||||
for r in rows:
|
||||
raw_t = r["translation"] or ""
|
||||
parts = [p.strip().lower() for p in raw_t.split(",")]
|
||||
if query_clean.lower() in parts:
|
||||
w_cnt = len(r["term"].split())
|
||||
is_multi = 1 if (is_single and w_cnt > 1) else 0
|
||||
is_inflection = 1 if " от " in raw_t else 0
|
||||
exact_matches.append((is_multi, is_inflection, w_cnt, len(r["term"]), r))
|
||||
if exact_matches:
|
||||
exact_matches.sort(key=lambda x: (x[0], x[1], x[2], x[3]))
|
||||
# Prefer single-word offline match if query was single word
|
||||
if exact_matches[0][0] == 0:
|
||||
best_row = exact_matches[0][4]
|
||||
matched_term = best_row["term"]
|
||||
phonetic = best_row["phonetic"] or ""
|
||||
pos = best_row["pos"] or ""
|
||||
clean_trans = clean_russian_translation(best_row["translation"] or "")
|
||||
tr_list = [t.strip() for t in clean_trans.split(",") if t.strip()]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 2. If no direct offline match, query MyMemory with langpair="ru|en"
|
||||
http_client = get_http_client()
|
||||
if not matched_term:
|
||||
try:
|
||||
mm_data = await fetch_mymemory(query_clean, langpair="ru|en", client=http_client)
|
||||
raw_cands = []
|
||||
if mm_data.get("translation"):
|
||||
raw_cands.append(mm_data["translation"])
|
||||
for m in mm_data.get("matches", []):
|
||||
if m and m not in raw_cands and m.lower() != query_clean.lower():
|
||||
raw_cands.append(m)
|
||||
|
||||
cleaned_cands = []
|
||||
for c in raw_cands:
|
||||
c_clean = re.sub(r"^(?:to|an?|the)\s+", "", c.strip(), flags=re.IGNORECASE).strip(" *.,!?\"'")
|
||||
if c_clean and c_clean not in cleaned_cands and not re.search(r"[\u0400-\u04FF]", c_clean):
|
||||
cleaned_cands.append(c_clean)
|
||||
|
||||
if cleaned_cands:
|
||||
matched_term = cleaned_cands[0]
|
||||
# Enrich from offline dictionary if candidate exists
|
||||
with get_db() as conn:
|
||||
for cand in cleaned_cands:
|
||||
off_row = conn.execute(
|
||||
"SELECT term, translation, phonetic, pos FROM offline_dictionary WHERE term = ? COLLATE NOCASE",
|
||||
(cand,)
|
||||
).fetchone()
|
||||
if off_row:
|
||||
matched_term = off_row["term"]
|
||||
phonetic = off_row["phonetic"] or ""
|
||||
pos = off_row["pos"] or ""
|
||||
if not clean_trans or clean_trans == query_clean:
|
||||
clean_trans = clean_russian_translation(off_row["translation"] or "") or query_clean
|
||||
tr_list = [t.strip() for t in clean_trans.split(",") if t.strip()]
|
||||
break
|
||||
|
||||
# If phonetic still missing and candidate is a single word, fetch from free dictionary API
|
||||
if not phonetic and len(matched_term.split()) == 1:
|
||||
fd = await fetch_free_dictionary_api(matched_term, client=http_client)
|
||||
if fd.get("phonetic"):
|
||||
phonetic = fd["phonetic"]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if not matched_term:
|
||||
matched_term = query_clean
|
||||
|
||||
res = {
|
||||
"query": query_clean,
|
||||
"term": matched_term,
|
||||
"english_term": matched_term,
|
||||
"russian_query": query_clean,
|
||||
"is_sentence": is_sentence,
|
||||
"is_reverse": True,
|
||||
"translation": clean_trans,
|
||||
"translations_list": tr_list if tr_list else [clean_trans],
|
||||
"translation_ru": clean_trans,
|
||||
"phonetic": phonetic,
|
||||
"audio_file": "",
|
||||
"audio_url": "",
|
||||
"part_of_speech": pos or ("Sentence" if is_sentence else "Word"),
|
||||
"definitions": [],
|
||||
"examples": [],
|
||||
"source": "reverse_dictionary",
|
||||
}
|
||||
|
||||
with get_db() as conn:
|
||||
conn.execute(
|
||||
"INSERT OR REPLACE INTO dictionary_cache (query, response_json) VALUES (?, ?)",
|
||||
(query_clean, json.dumps(res, ensure_ascii=False))
|
||||
)
|
||||
return res
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Forward Translation (English -> Russian)
|
||||
# -----------------------------------------------------------------------
|
||||
# Check offline dictionary first (Zero Daemon, < 0.1ms via SQLite mmap)
|
||||
if not is_sentence:
|
||||
try:
|
||||
with get_db() as conn:
|
||||
off_row = conn.execute(
|
||||
"SELECT term, translation, phonetic, pos FROM offline_dictionary WHERE term = ? COLLATE NOCASE",
|
||||
(query_clean,)
|
||||
@@ -329,7 +450,9 @@ async def lookup_term(query: str) -> Dict[str, Any]:
|
||||
tr_list = [t.strip() for t in clean_trans.split(",") if t.strip()]
|
||||
res = {
|
||||
"query": query_clean,
|
||||
"term": off_row["term"] or query_clean,
|
||||
"is_sentence": False,
|
||||
"is_reverse": False,
|
||||
"translation": clean_trans,
|
||||
"translations_list": tr_list,
|
||||
"translation_ru": clean_trans,
|
||||
@@ -346,13 +469,14 @@ async def lookup_term(query: str) -> Dict[str, Any]:
|
||||
(query_clean, json.dumps(res, ensure_ascii=False))
|
||||
)
|
||||
return res
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
result: Dict[str, Any] = {
|
||||
"query": query_clean,
|
||||
"term": query_clean,
|
||||
"is_sentence": is_sentence,
|
||||
"is_reverse": False,
|
||||
"translation": "",
|
||||
"translations_list": [],
|
||||
"phonetic": "",
|
||||
|
||||
Reference in New Issue
Block a user