prasai-ap commited on
Commit
759f456
·
verified ·
1 Parent(s): 9f09438

Upload 3 files

Browse files
Files changed (2) hide show
  1. README.md +3 -0
  2. app.py +296 -22
README.md CHANGED
@@ -28,4 +28,7 @@ Without `BACKEND_URL`, the Space can still run the same style of workflow locall
28
 
29
  - `LLM_BASE_URL`, `LLM_API_KEY`, `LLM_MODEL` for the AMD/vLLM tutor
30
  - `TRANSLATION_PROVIDER=gemini`, `GEMINI_API_KEY`, `GEMINI_MODEL` for Nepali adaptation and romanized question normalization
 
31
  - `OCR_PROVIDER=gemini`, `OCR_MAX_PAGES=5` for scanned or custom-font PDFs
 
 
 
28
 
29
  - `LLM_BASE_URL`, `LLM_API_KEY`, `LLM_MODEL` for the AMD/vLLM tutor
30
  - `TRANSLATION_PROVIDER=gemini`, `GEMINI_API_KEY`, `GEMINI_MODEL` for Nepali adaptation and romanized question normalization
31
+ - `TRANSLATION_PROVIDER=openai`, `OPENAI_API_KEY`, `OPENAI_MODEL` if you want to use OpenAI for Nepali adaptation instead
32
  - `OCR_PROVIDER=gemini`, `OCR_MAX_PAGES=5` for scanned or custom-font PDFs
33
+
34
+ Use `LLM_MODEL=Qwen/Qwen2.5-7B-Instruct` to match the project default unless your vLLM endpoint exposes a different model name.
app.py CHANGED
@@ -15,10 +15,12 @@ APP_NAME = os.getenv("APP_NAME", "Pathshala AI")
15
  BACKEND_URL = os.getenv("BACKEND_URL", "").rstrip("/")
16
  LLM_BASE_URL = os.getenv("LLM_BASE_URL", "").strip().rstrip("/")
17
  LLM_API_KEY = os.getenv("LLM_API_KEY", "")
18
- LLM_MODEL = os.getenv("LLM_MODEL", "Qwen/Qwen2.5-1.5B-Instruct")
19
  TRANSLATION_PROVIDER = os.getenv("TRANSLATION_PROVIDER", "mock").strip().lower()
20
  GEMINI_API_KEY = os.getenv("GEMINI_API_KEY", "")
21
  GEMINI_MODEL = os.getenv("GEMINI_MODEL", "gemini-2.5-flash")
 
 
22
  OCR_PROVIDER = os.getenv("OCR_PROVIDER", "off").strip().lower()
23
  OCR_MAX_PAGES = int(os.getenv("OCR_MAX_PAGES", "5") or "5")
24
  EMBEDDING_MODEL = os.getenv(
@@ -256,7 +258,19 @@ def adapt_nepali_answer(question, english_answer, sources):
256
  except (requests.RequestException, KeyError, IndexError, TypeError, ValueError):
257
  pass
258
 
259
- return nepali_answer(question, " ".join(str(source.get("text", "")) for source in sources))
 
 
 
 
 
 
 
 
 
 
 
 
260
 
261
 
262
  def translate_with_gemini(question, english_answer):
@@ -271,6 +285,46 @@ def translate_with_gemini(question, english_answer):
271
  return gemini_generate_text(prompt, temperature=0.1, max_output_tokens=450)
272
 
273
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
274
  def normalize_with_gemini(question):
275
  prompt = (
276
  "Convert this student question into one clear, simple English question for "
@@ -320,23 +374,53 @@ def fallback_english_answer(sources):
320
  return "I do not have enough textbook context to answer this question."
321
 
322
  topic_text = " ".join(str(source.get("text", "")) for source in sources[:3]).lower()
323
- if "soil erosion" in topic_text or "erosion" in topic_text:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
324
  return (
325
  "Soil erosion means the top fertile layer of soil is carried away by "
326
  "water, wind, or other causes. It makes land less useful for growing "
327
  "plants, so protecting soil with plants and controlled water flow is important."
328
  )
329
- if "photosynthesis" in topic_text or "chlorophyll" in topic_text:
 
330
  return (
331
  "Photosynthesis is the process by which green plants make their own food "
332
  "using sunlight, water, and carbon dioxide. Chlorophyll in leaves helps "
333
  "plants capture sunlight, and oxygen is released during the process."
334
  )
335
 
336
- return "Based on the textbook context, here is the simple explanation: " + truncate(
337
- " ".join(context.split()),
338
- 500,
339
- )
340
 
341
 
342
  def format_sources_for_prompt(sources):
@@ -673,17 +757,122 @@ def normalize_question(question):
673
  pass
674
 
675
  text = cleaned.lower()
676
- if "mato" in text and "katan" in text:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
677
  return "What is soil erosion?"
678
- if "prakash" in text and "sansleshan" in text:
 
 
 
 
 
 
 
 
 
 
 
679
  return "What is photosynthesis?"
680
- if "bhinn" in text or "fraction" in text:
 
681
  return "What is a fraction?"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
682
  return cleaned
683
 
684
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
685
  def display_topic(question):
686
  normalized = str(question).lower()
 
 
 
 
687
  if "photosynthesis" in normalized or "prakash" in normalized:
688
  return "प्रकाश संश्लेषण"
689
  if "soil erosion" in normalized or ("mato" in normalized and "katan" in normalized):
@@ -692,22 +881,23 @@ def display_topic(question):
692
  return "भिन्न"
693
  if "oxygen" in normalized:
694
  return "अक्सिजन"
 
 
 
 
 
 
 
 
695
  return str(question).strip() or "आजको पाठ"
696
 
697
 
698
  def nepali_answer(question, context):
699
  text = f"{question} {context}".lower()
700
- if "soil erosion" in text or "माटो कटान" in context:
701
- return (
702
- "माटो कटान भनेको पानी, हावा वा अरू कारणले माटोको माथिल्लो मलिलो भाग "
703
- "बग्नु वा हट्नु हो। यसले जमिनको उर्वर शक्ति घटाउँछ। रूख, घाँस र बिरुवा "
704
- "रोप्दा माटो जोगाउन मद्दत हुन्छ।"
705
- )
706
- if "photosynthesis" in text or "प्रकाश संश्लेषण" in context:
707
- return (
708
- "प्रकाश संश्लेषण भनेको हरिया बिरुवाले घामको प्रकाश, पानी र कार्बन "
709
- "डाइअक्साइड प्रयोग गरेर खाना बनाउने प्रक्रिया हो। यस क्रममा अक्सिजन पनि निस्कन्छ।"
710
- )
711
  if has_devanagari(context):
712
  return "अपलोड गरिएको पाठ्यपुस्तकको सन्दर्भअनुसार मुख्य कुरा यस्तो छ:\n\n" + truncate(context, 700)
713
  return (
@@ -716,6 +906,88 @@ def nepali_answer(question, context):
716
  )
717
 
718
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
719
  def nepali_quiz_questions(context):
720
  short_context = truncate(first_sentence(context), 140)
721
  return [
@@ -824,6 +1096,8 @@ def startup_status():
824
  nepali_status = (
825
  "Gemini Nepali adaptation enabled."
826
  if TRANSLATION_PROVIDER == "gemini" and GEMINI_API_KEY
 
 
827
  else "Mock Nepali adaptation enabled."
828
  )
829
  ocr_status = (
 
15
  BACKEND_URL = os.getenv("BACKEND_URL", "").rstrip("/")
16
  LLM_BASE_URL = os.getenv("LLM_BASE_URL", "").strip().rstrip("/")
17
  LLM_API_KEY = os.getenv("LLM_API_KEY", "")
18
+ LLM_MODEL = os.getenv("LLM_MODEL", "Qwen/Qwen2.5-7B-Instruct")
19
  TRANSLATION_PROVIDER = os.getenv("TRANSLATION_PROVIDER", "mock").strip().lower()
20
  GEMINI_API_KEY = os.getenv("GEMINI_API_KEY", "")
21
  GEMINI_MODEL = os.getenv("GEMINI_MODEL", "gemini-2.5-flash")
22
+ OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "")
23
+ OPENAI_MODEL = os.getenv("OPENAI_MODEL", "gpt-4o")
24
  OCR_PROVIDER = os.getenv("OCR_PROVIDER", "off").strip().lower()
25
  OCR_MAX_PAGES = int(os.getenv("OCR_MAX_PAGES", "5") or "5")
26
  EMBEDDING_MODEL = os.getenv(
 
258
  except (requests.RequestException, KeyError, IndexError, TypeError, ValueError):
259
  pass
260
 
261
+ if TRANSLATION_PROVIDER == "openai" and OPENAI_API_KEY:
262
+ try:
263
+ translated = translate_with_openai(question, english_answer)
264
+ translated = remove_source_lines(translated)
265
+ if is_valid_nepali(translated):
266
+ return translated
267
+ except (requests.RequestException, KeyError, IndexError, TypeError, ValueError):
268
+ pass
269
+
270
+ return nepali_answer(
271
+ question,
272
+ " ".join(str(source.get("text", "")) for source in sources),
273
+ )
274
 
275
 
276
  def translate_with_gemini(question, english_answer):
 
285
  return gemini_generate_text(prompt, temperature=0.1, max_output_tokens=450)
286
 
287
 
288
+ def translate_with_openai(question, english_answer):
289
+ response = requests.post(
290
+ "https://api.openai.com/v1/chat/completions",
291
+ json={
292
+ "model": OPENAI_MODEL,
293
+ "messages": [
294
+ {
295
+ "role": "system",
296
+ "content": (
297
+ "You adapt English tutoring answers into natural Nepali for "
298
+ "primary-school students. Write only Nepali Devanagari. Do not "
299
+ "add source labels, markdown, or English sentences."
300
+ ),
301
+ },
302
+ {
303
+ "role": "user",
304
+ "content": (
305
+ "Translate and simplify this grounded English tutoring answer "
306
+ "into natural Nepali for a primary-school student in Nepal. "
307
+ "Keep the same meaning. Use Nepali Devanagari only. Do not add "
308
+ "new facts. Do not include source citations or headings.\n\n"
309
+ f"Student question:\n{question}\n\n"
310
+ f"English answer:\n{english_answer}"
311
+ ),
312
+ },
313
+ ],
314
+ "temperature": 0.1,
315
+ "max_tokens": 450,
316
+ },
317
+ headers={
318
+ "Authorization": f"Bearer {OPENAI_API_KEY}",
319
+ "Content-Type": "application/json",
320
+ },
321
+ timeout=45,
322
+ )
323
+ response.raise_for_status()
324
+ data = response.json()
325
+ return data["choices"][0]["message"]["content"]
326
+
327
+
328
  def normalize_with_gemini(question):
329
  prompt = (
330
  "Convert this student question into one clear, simple English question for "
 
374
  return "I do not have enough textbook context to answer this question."
375
 
376
  topic_text = " ".join(str(source.get("text", "")) for source in sources[:3]).lower()
377
+ concept_answer = known_english_concept_answer(topic_text)
378
+ if concept_answer:
379
+ return concept_answer
380
+
381
+ return "Based on the textbook context, here is the simple explanation: " + truncate(
382
+ " ".join(context.split()),
383
+ 500,
384
+ )
385
+
386
+
387
+ def known_english_concept_answer(text):
388
+ if (
389
+ "living thing" in text
390
+ or "living things" in text
391
+ or "organism" in text
392
+ or "organisms" in text
393
+ ):
394
+ return (
395
+ "Living things are organisms that show the signs of life. They need food "
396
+ "or energy, breathe or exchange gases, grow, respond to their surroundings, "
397
+ "and can reproduce. Plants, animals, fungi, and microorganisms are "
398
+ "examples of living things."
399
+ )
400
+
401
+ if "reflection" in text or "mirror" in text or "image of that object" in text:
402
+ return (
403
+ "Reflection of light means light bounces back after hitting a surface. "
404
+ "A mirror reflects light in an orderly way, so we can see a clear image "
405
+ "of an object in it. Smooth, flat surfaces make clearer reflections, while "
406
+ "rough surfaces scatter light and do not show a clear image."
407
+ )
408
+
409
+ if "soil erosion" in text or "erosion" in text:
410
  return (
411
  "Soil erosion means the top fertile layer of soil is carried away by "
412
  "water, wind, or other causes. It makes land less useful for growing "
413
  "plants, so protecting soil with plants and controlled water flow is important."
414
  )
415
+
416
+ if "photosynthesis" in text or "chlorophyll" in text:
417
  return (
418
  "Photosynthesis is the process by which green plants make their own food "
419
  "using sunlight, water, and carbon dioxide. Chlorophyll in leaves helps "
420
  "plants capture sunlight, and oxygen is released during the process."
421
  )
422
 
423
+ return None
 
 
 
424
 
425
 
426
  def format_sources_for_prompt(sources):
 
757
  pass
758
 
759
  text = cleaned.lower()
760
+ if has_any(
761
+ text,
762
+ [
763
+ "living thing",
764
+ "living things",
765
+ "organism",
766
+ "organisms",
767
+ "sajeev",
768
+ "sajiv",
769
+ "जीवित",
770
+ "सजीव",
771
+ ],
772
+ ):
773
+ return "What are living things?"
774
+
775
+ if (
776
+ "soil erosion" in text
777
+ or "erosion" in text
778
+ or "माटो कटान" in cleaned
779
+ or (
780
+ has_any(text, ["mati", "mato", "matto", "maato"])
781
+ and has_any(text, ["katan", "katne", "katnu", "bagcha", "bagdai"])
782
+ )
783
+ ):
784
  return "What is soil erosion?"
785
+
786
+ if has_any(text, ["oxygen", "aksijan", "akshijan", "अक्सिजन"]):
787
+ return "What is oxygen?"
788
+
789
+ if (
790
+ "photosynthesis" in text
791
+ or "प्रकाश संश्लेषण" in cleaned
792
+ or (
793
+ has_any(text, ["prakash", "prakaash"])
794
+ and has_any(text, ["sansleshan", "samsleshan", "sanshleshan"])
795
+ )
796
+ ):
797
  return "What is photosynthesis?"
798
+
799
+ if has_any(text, ["fraction", "bhinn", "vag", "bhaag", "भाग", "भिन्न"]):
800
  return "What is a fraction?"
801
+
802
+ if has_any(text, ["mitochondria", "mitochondrion", "mitokondria"]):
803
+ return "What is mitochondria?"
804
+
805
+ if has_any(text, ["chloroplast", "kloroplast", "chlorophyll"]):
806
+ return "What is chloroplast?"
807
+
808
+ if has_any(text, ["cell", "koshika", "kosika", "कोषिका"]):
809
+ return "What is a cell?"
810
+
811
+ if has_any(text, ["energy", "urja", "oorja", "ऊर्जा"]):
812
+ return "What is energy?"
813
+
814
+ mixed_topic = extract_mixed_language_topic(text)
815
+ if mixed_topic:
816
+ return f"What is {mixed_topic}?"
817
+
818
  return cleaned
819
 
820
 
821
+ def has_any(text, keywords):
822
+ return any(keyword in text for keyword in keywords)
823
+
824
+
825
+ def extract_mixed_language_topic(text):
826
+ markers = [
827
+ " vaneko ",
828
+ " bhaneko ",
829
+ " vanya ",
830
+ " bhanya ",
831
+ " vanne ",
832
+ " bhanne ",
833
+ ]
834
+
835
+ if not any(marker in f" {text} " for marker in markers):
836
+ return ""
837
+
838
+ topic = f" {text} "
839
+ removable_phrases = [
840
+ " vaneko ",
841
+ " bhaneko ",
842
+ " vanya ",
843
+ " bhanya ",
844
+ " vanne ",
845
+ " bhanne ",
846
+ " ke ho ",
847
+ " k ho ",
848
+ " kya ho ",
849
+ " ho ",
850
+ " ? ",
851
+ ]
852
+
853
+ for phrase in removable_phrases:
854
+ topic = topic.replace(phrase, " ")
855
+
856
+ topic = " ".join(topic.split()).strip(" ?.,")
857
+ if not topic:
858
+ return ""
859
+
860
+ blocked_words = {"malai", "please", "explain", "bujhau", "bujhaunu", "sir", "mam"}
861
+ topic_words = [word for word in topic.split() if word not in blocked_words]
862
+ topic = " ".join(topic_words)
863
+
864
+ if not topic or len(topic) > 80:
865
+ return ""
866
+
867
+ return topic
868
+
869
+
870
  def display_topic(question):
871
  normalized = str(question).lower()
872
+ if "living thing" in normalized or "organism" in normalized:
873
+ return "सजीव वस्तु"
874
+ if "reflection" in normalized:
875
+ return "प्रकाशको परावर्तन"
876
  if "photosynthesis" in normalized or "prakash" in normalized:
877
  return "प्रकाश संश्लेषण"
878
  if "soil erosion" in normalized or ("mato" in normalized and "katan" in normalized):
 
881
  return "भिन्न"
882
  if "oxygen" in normalized:
883
  return "अक्सिजन"
884
+ if "mitochondria" in normalized or "mitochondrion" in normalized:
885
+ return "माइटोकन्ड्रिया"
886
+ if "chloroplast" in normalized:
887
+ return "क्लोरोप्लास्ट"
888
+ if "cell" in normalized:
889
+ return "कोषिका"
890
+ if "energy" in normalized:
891
+ return "ऊर्जा"
892
  return str(question).strip() or "आजको पाठ"
893
 
894
 
895
  def nepali_answer(question, context):
896
  text = f"{question} {context}".lower()
897
+ known_answer = known_nepali_concept_answer(text)
898
+ if known_answer:
899
+ return known_answer
900
+
 
 
 
 
 
 
 
901
  if has_devanagari(context):
902
  return "अपलोड गरिएको पाठ्यपुस्तकको सन्दर्भअनुसार मुख्य कुरा यस्तो छ:\n\n" + truncate(context, 700)
903
  return (
 
906
  )
907
 
908
 
909
+ def known_nepali_concept_answer(text):
910
+ if (
911
+ "living thing" in text
912
+ or "living things" in text
913
+ or "organism" in text
914
+ or "organisms" in text
915
+ or "sajeev" in text
916
+ or "sajiv" in text
917
+ or "सजीव" in text
918
+ or "जीवित वस्तु" in text
919
+ ):
920
+ return (
921
+ "सजीव वा जीवित वस्तु भनेको जीवनका लक्षण देखाउने वस्तु हो। सजीवले "
922
+ "खाना वा ऊर्जा लिन्छ, सास फेर्छ, बढ्छ, वातावरणको परिवर्तनमा प्रतिक्रिया "
923
+ "दिन्छ, र प्रजनन गर्न सक्छ। बिरुवा, जनावर, ढुसी र सूक्ष्म जीवहरू "
924
+ "सजीवका उदाहरण हुन्।"
925
+ )
926
+
927
+ if "reflection" in text or "mirror" in text or "ऐना" in text or "प्रतिबिम्ब" in text:
928
+ return (
929
+ "प्रकाशको परावर्तन भनेको प्रकाश कुनै सतहमा ठोक्किएर फर्कनु हो। ऐनाले "
930
+ "प्रकाशलाई राम्रोसँग फर्काउँछ, त्यसैले त्यसमा वस्तुको प्रतिबिम्ब देखिन्छ। "
931
+ "समथर र चिल्लो सतहमा प्रतिबिम्ब प्रस्ट देखिन्छ, तर खस्रो सतहमा प्रकाश धेरै "
932
+ "दिशामा छरिने भएकाले प्रतिबिम्ब प्रस्ट देखिँदैन।"
933
+ )
934
+
935
+ if "soil erosion" in text or "erosion" in text or "माटो कटान" in text:
936
+ return (
937
+ "माटो कटान भनेको हावा, पानी वा अन्य कारणले माटोको माथिल्लो मलिलो भाग "
938
+ "बिस्तारै बगेर वा उडेर जानु हो। यसले खेतबारीको उर्वर शक्ति घटाउँछ। "
939
+ "त्यसैले बिरुवा रोप्ने, घाँस जोगाउने र पानीको बहाव नियन्त्रण गर्ने काम "
940
+ "माटो जोगाउन उपयोगी हुन्छ।"
941
+ )
942
+
943
+ if "oxygen" in text or "अक्सिजन" in text:
944
+ return (
945
+ "अक्सिजन एउटा ग्यास हो। जीवित प्राणीले सास फेर्दा अक्सिजन प्रयोग गर्छन्। "
946
+ "कोषिकाले खाना तोडेर ऊर्जा बनाउन पनि अक्सिजनको मद्दत लिन्छ। "
947
+ "त्यसैले अक्सिजन जीवनका लागि धेरै महत्त्वपूर्ण हु��्छ।"
948
+ )
949
+
950
+ if "photosynthesis" in text or "chlorophyll" in text or "प्रकाश संश्लेषण" in text:
951
+ return (
952
+ "प्रकाश संश्लेषण भनेको हरिया बिरुवाले घामको प्रकाश, पानी र कार्बन डाइअक्साइड "
953
+ "प्रयोग गरेर आफ्नो खाना बनाउने प्रक्रिया हो। यो काम पातमा हुने हरियो पदार्थ "
954
+ "क्लोरोफिलको मद्दतले हुन्छ। यस प्रक्रियामा अक्सिजन पनि निस्कन्छ।"
955
+ )
956
+
957
+ if "fraction" in text or "भिन्न" in text:
958
+ return (
959
+ "भिन्न भनेको कुनै पूर्ण वस्तुको भाग देखाउने संख्या हो। माथिको संख्या अंश हो, "
960
+ "जसले कति भाग लिइयो भनेर देखाउँछ। तलको संख्या हर हो, जसले पूर्ण वस्तु कति "
961
+ "बराबर भागमा बाँडिएको छ भनेर देखाउँछ।"
962
+ )
963
+
964
+ if "mitochondria" in text or "mitochondrion" in text:
965
+ return (
966
+ "माइटोकन्ड्रिया कोषिकाभित्र हुने सानो अंगक हो। यसको मुख्य काम खानाबाट ऊर्जा "
967
+ "बनाउनु हो। त्यसैले यसलाई कोषिकाको ऊर्जा घर पनि भनिन्छ।"
968
+ )
969
+
970
+ if "chloroplast" in text or "plastid" in text:
971
+ return (
972
+ "क्लोरोप्लास्ट बिरुवाको कोषिकामा पाइने हरियो अंगक हो। यसमा क्लोरोफिल हुन्छ। "
973
+ "क्लोरोफिलले घामको प्रकाश लिन मद्दत गर्छ र बिरुवाले खाना बनाउन सक्छ।"
974
+ )
975
+
976
+ if "cell" in text or "कोषिका" in text:
977
+ return (
978
+ "कोषिका जीवित वस्तुको सबैभन्दा सानो आधारभूत एकाइ हो। हाम्रो शरीर, बिरुवा "
979
+ "र धेरै जीवहरू कोषिकाबाट बनेका हुन्छन्। कोषिकाले जीवनका आवश्यक कामहरू गर्छ।"
980
+ )
981
+
982
+ if "energy" in text or "ऊर्जा" in text:
983
+ return (
984
+ "ऊर्जा भनेको काम गर्न चाहिने शक्ति हो। जीवित प्राणीले खाना र सास फेर्ने "
985
+ "प्रक्रियाबाट ऊर्जा पाउँछन्। कोषिकाले यही ऊर्जा प्रयोग गरेर जीवनका काम गर्छ।"
986
+ )
987
+
988
+ return None
989
+
990
+
991
  def nepali_quiz_questions(context):
992
  short_context = truncate(first_sentence(context), 140)
993
  return [
 
1096
  nepali_status = (
1097
  "Gemini Nepali adaptation enabled."
1098
  if TRANSLATION_PROVIDER == "gemini" and GEMINI_API_KEY
1099
+ else "OpenAI Nepali adaptation enabled."
1100
+ if TRANSLATION_PROVIDER == "openai" and OPENAI_API_KEY
1101
  else "Mock Nepali adaptation enabled."
1102
  )
1103
  ocr_status = (