| | |
| | |
| | |
| | |
| | import os |
| | import logging |
| | import sys |
| | import re |
| | import json |
| | from lingua import Language, LanguageDetectorBuilder |
| | import gradio as gr |
| | from openai import OpenAI as OpenAIOG |
| | from llama_index.llms.openai import OpenAI |
| | from llama_index.core import VectorStoreIndex, StorageContext, load_index_from_storage |
| | from deep_translator import GoogleTranslator |
| |
|
| | |
| | |
| | os.environ.get("OPENAI_API_KEY") |
| |
|
| | |
| | llm = OpenAI(temperature=0.0, model="gpt-4o") |
| | client = OpenAIOG() |
| |
|
| | |
| | storage_context = StorageContext.from_defaults(persist_dir="arv_metadata") |
| | index = load_index_from_storage(storage_context) |
| | retriever = index.as_retriever(similarity_top_k=3) |
| |
|
| | |
| | acknowledgment_keywords_sw = ["sawa", "ndiyo", "naam", "hakika", "asante", "nimeelewa", "nimekupata", "ni kweli", "kwa hakika", "nimesikia", "ahsante"] |
| | acknowledgment_keywords_en = ["thanks", "thank you", "thx", "ok", "okay", "great", "got it", "appreciate", "good", "makes sense"] |
| | follow_up_keywords = ["but", "also", "and", "what", "how", "why", "when", "is", "?", "lakini", "pia", "na", "nini", "vipi", "kwanini", "wakati"] |
| | greeting_keywords_sw = ["sasa", "niaje", "habari", "mambo", "jambo", "shikamoo", "marahaba", "hujambo", "hamjambo", "salama", "vipi"] |
| | greeting_keywords_en = ["hi", "hello", "hey", "how's it", "what's up", "yo", "howdy"] |
| | |
| | |
| |
|
| | def contains_exact_word_or_phrase(text, keywords): |
| | """Check if the given text contains any exact keyword from the list.""" |
| | text = text.lower() |
| | return any(re.search(r'\b' + re.escape(keyword) + r'\b', text) for keyword in keywords) |
| |
|
| | def contains_greeting_sw(text): |
| | return contains_exact_word_or_phrase(text, greeting_keywords_sw) |
| |
|
| | def contains_greeting_en(text): |
| | return contains_exact_word_or_phrase(text, greeting_keywords_en) |
| |
|
| | def contains_acknowledgment_sw(text): |
| | return contains_exact_word_or_phrase(text, acknowledgment_keywords_sw) |
| |
|
| | def contains_acknowledgment_en(text): |
| | return contains_exact_word_or_phrase(text, acknowledgment_keywords_en) |
| |
|
| | def contains_follow_up(text): |
| | return contains_exact_word_or_phrase(text, follow_up_keywords) |
| |
|
| | def convert_to_date(date_str): |
| | """Convert date string in YYYYMMDD format to YYYY-MM-DD.""" |
| | try: |
| | return datetime.strptime(date_str, "%Y%m%d").strftime("%Y-%m-%d") |
| | except ValueError: |
| | return "Unknown Date" |
| |
|
| | def detect_language(text): |
| | """Detect language of a given text using Lingua for short texts and langdetect for longer ones.""" |
| | if len(text.split()) < 5: |
| | languages = [Language.ENGLISH, Language.SWAHILI] |
| | detector = LanguageDetectorBuilder.from_languages(*languages).build() |
| | detected_language = detector.detect_language_of(text) |
| | return "sw" if detected_language == Language.SWAHILI else "en" |
| | try: |
| | return detect(text) |
| | except Exception as e: |
| | logging.warning(f"Language detection error: {e}") |
| | return "unknown" |
| | |
| | |
| | def nishauri(question, conversation_history: list[str]): |
| |
|
| | """Process user query, detect language, handle greetings, acknowledgments, and retrieve relevant information.""" |
| | context = " ".join([item["user"] + " " + item["chatbot"] for item in conversation_history]) |
| | |
| | |
| | for lang, contains_greeting, contains_acknowledgment in [("en", contains_greeting_en, contains_acknowledgment_en), ("sw", contains_greeting_sw, contains_acknowledgment_sw)]: |
| | if contains_greeting(question) and not contains_follow_up(question): |
| | prompt = f"The user said: {question}. Respond accordingly in {lang}." |
| | elif contains_acknowledgment(question) and not contains_follow_up(question): |
| | prompt = f"The user acknowledged: {question}. Respond accordingly in {lang}." |
| | else: |
| | continue |
| | completion = client.chat.completions.create( |
| | model="gpt-4o", |
| | messages=[{"role": "user", "content": prompt}] |
| | ) |
| | reply_to_user = completion.choices[0].message.content |
| | conversation_history.append({"user": question, "chatbot": reply_to_user}) |
| | return reply_to_user, conversation_history |
| |
|
| | |
| | lang_question = detect_language(question) |
| | if lang_question == "sw": |
| | question = GoogleTranslator(source='sw', target='en').translate(question) |
| | |
| | |
| | sources = retriever.retrieve(question) |
| | retrieved_text = "\n\n".join([f"Source {i+1}: {source.text}" for i, source in enumerate(sources[:3])]) |
| |
|
| | |
| | question_final = ( |
| | f"The user asked the following question: \"{question}\"\n\n" |
| | f"Use only the content below to answer the question:\n\n{retrieved_text}\n\n" |
| | "Guidelines:\n" |
| | "- Only answer the question that was asked.\n" |
| | "- Do not change the subject or include unrelated information.\n" |
| | "- Only discuss HIV. If the question is not about HIV, say that you can only answer HIV-related questions.\n" |
| | ) |
| |
|
| | |
| | system_prompt = ( |
| | "You are a helpful assistant who only answers questions about HIV.\n" |
| | "- Do not answer questions about other topics (e.g., malaria or tuberculosis).\n" |
| | "- If a question is unrelated to HIV, politely respond that you can only answer HIV-related questions.\n\n" |
| | |
| | "The person asking the question is living with HIV.\n" |
| | "- Do not suggest they get tested for HIV or take post-exposure prophylaxis (PEP).\n" |
| | "- You may mention that their partners might benefit from testing or PEP, if relevant.\n" |
| | "- Do not mention in your response that the person is living with HIV.\n" |
| | "- Only suggest things relevant to someone who already has HIV.\n\n" |
| | "- Keep the answer under 50 words.\n" |
| | "- Use simple, easy-to-understand language. Avoid medical jargon.\n" |
| | |
| | "Use the following authoritative information about viral loads:\n" |
| | "- A high or non-suppressed viral load is above 200 copies/ml.\n" |
| | "- A viral load above 1000 copies/ml suggests treatment failure.\n" |
| | "- A suppressed viral load is one below 200 copies/ml.\n\n" |
| | ) |
| | |
| | |
| | messages = [{"role": "system", "content": system_prompt}] |
| |
|
| | |
| | for turn in conversation_history: |
| | messages.append({"role": "user", "content": turn["user"]}) |
| | messages.append({"role": "assistant", "content": turn["chatbot"]}) |
| | |
| | |
| | messages.append({"role": "user", "content": question_final}) |
| |
|
| | |
| | completion = client.chat.completions.create( |
| | model="gpt-4o", |
| | messages=messages |
| | ) |
| |
|
| | |
| | reply_to_user = completion.choices[0].message.content |
| |
|
| | |
| | conversation_history.append({"user": question, "chatbot": reply_to_user}) |
| |
|
| | |
| | if lang_question=="sw": |
| | reply_to_user = GoogleTranslator(source='auto', target='sw').translate(reply_to_user) |
| |
|
| | |
| | return reply_to_user, conversation_history |
| |
|
| | |
| | demo = gr.Interface( |
| | title = "Nishauri Chatbot Demo", |
| | fn=nishauri, |
| | inputs=["text", gr.State(value=[])], |
| | outputs=["text", gr.State()], |
| | ) |
| |
|
| | demo.launch() |