ItsCxdy commited on
Commit
e123a74
Β·
verified Β·
1 Parent(s): 4098289

Upload 12 files

Browse files
.gitattributes CHANGED
@@ -33,3 +33,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ homeopathy_book.pdf filter=lfs diff=lfs merge=lfs -text
37
+ vector_db/chroma.sqlite3 filter=lfs diff=lfs merge=lfs -text
Dockerfile ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Read the doc: https://huggingface.co/docs/hub/spaces-sdks-docker# you will also find guides on how best to write your Dockerfile
2
+ FROM python:3.10-slim
3
+
4
+ # Set the working directory in the container
5
+ WORKDIR /app
6
+
7
+ # Copy the dependency file and install dependencies
8
+ COPY requirements.txt .
9
+ RUN pip install --no-cache-dir -r requirements.txt
10
+
11
+ # Copy the rest of the application files (including vector_db)
12
+ COPY . .
13
+
14
+ # Grant execution permissions to the run script
15
+ RUN chmod +x run.sh
16
+
17
+ # This tells the container what command to run when it starts
18
+ ENTRYPOINT ["./run.sh"]
doctor_bot.py ADDED
@@ -0,0 +1,168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import requests
3
+ import json
4
+ from dotenv import load_dotenv
5
+ import logging
6
+
7
+ # Set up basic logging
8
+ logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s')
9
+ logger = logging.getLogger(__name__)
10
+
11
+ # Reverting to community package imports as in the original code
12
+ from langchain_community.embeddings import HuggingFaceEmbeddings
13
+ from langchain_community.vectorstores import Chroma
14
+
15
+ # Load environment variables
16
+ load_dotenv()
17
+
18
+ class HomeopathyDoctorBot:
19
+ def __init__(self):
20
+ # Initialize embeddings and vector store
21
+ self.embeddings = HuggingFaceEmbeddings(
22
+ model_name="sentence-transformers/all-MiniLM-L6-v2"
23
+ )
24
+ self.vector_store = Chroma(
25
+ persist_directory="./vector_db",
26
+ embedding_function=self.embeddings
27
+ )
28
+ self.retriever = self.vector_store.as_retriever(search_kwargs={"k": 4})
29
+
30
+ # Initialize chat history for state management
31
+ self.chat_history = []
32
+
33
+ def get_relevant_context(self, query):
34
+ """Retrieve relevant context from the vector database"""
35
+ # Using the modern .invoke() method remains correct for the retriever object
36
+ docs = self.retriever.invoke(query)
37
+ context = "\n".join([doc.page_content for doc in docs])
38
+ return context
39
+
40
+ def query_ai(self, user_message, context, history):
41
+ """Query the AI model via Chute.ai"""
42
+ api_key = os.getenv("CHUTEAI_API_KEY")
43
+
44
+ if not api_key:
45
+ logger.error("CHUTEAI_API_KEY is missing or empty in the environment.")
46
+ return "Error: Configuration missing CHUTEAI_API_KEY."
47
+
48
+ headers = {
49
+ "Authorization": f"Bearer {api_key}",
50
+ "Content-Type": "application/json"
51
+ }
52
+
53
+ # System prompt remains the same
54
+ system_prompt = """You are an expert Homeopathy Doctor. Your goal is to find the best possible remedy from the provided Context.
55
+
56
+ **DIAGNOSTIC PROCESS & CONSTRAINTS:**
57
+ 1. **Focus:** Analyze the patient's full symptom set, including the **Chat History**. Only analyze the symptoms explicitly mentioned by the patient. IGNORE any symptoms found *only* in the Context that the patient has not mentioned.
58
+ 2. **Clarification:** You MUST conclude the diagnosis and suggest a remedy within the first **two or three turns** of the conversation. Ask a MAXIMUM of 3 concise clarifying questions in the first turn only, if needed. In subsequent turns, prioritize diagnosing based on the accumulated history.
59
+ 3. **Prescription Rule:** **MUST** prescribe the single best-matching remedy when:
60
+ a) You have clear matching symptoms from the Context.
61
+ b) The patient explicitly asks for the medicine, or indicates they cannot answer more questions. In this case, use the best available information from the Chat History to prescribe.
62
+ 4. **Safety:** If unsure or the context doesn't contain a relevant remedy, admit it honestly.
63
+ 5. **Tone:** Always be professional, caring, and responsible.
64
+ """
65
+ messages = [
66
+ {"role": "system", "content": system_prompt},
67
+ *history,
68
+ {"role": "user", "content": f"Context from homeopathy book:\n{context}\n\nPatient Complaint: {user_message}"}
69
+ ]
70
+
71
+ data = {
72
+ # FIX: Replaced the likely incorrect model name ("Gemma 3 4b It")
73
+ # with a known working model from the user's Chute.ai example,
74
+ # as the 404 error is usually caused by an invalid model identifier.
75
+ "model": "meituan-longcat/LongCat-Flash-Chat-FP8",
76
+ "messages": messages,
77
+ "temperature": 0.2,
78
+ "max_tokens": 500
79
+ }
80
+
81
+ # --- FIX: Corrected hostname from llm.chute.ai to llm.chutes.ai ---
82
+ api_url = "https://llm.chutes.ai/v1/chat/completions"
83
+
84
+ try:
85
+ response = requests.post(
86
+ api_url,
87
+ headers=headers,
88
+ json=data,
89
+ timeout=30
90
+ )
91
+
92
+ if response.status_code == 200:
93
+ return response.json()["choices"][0]["message"]["content"]
94
+ else:
95
+ error_data = response.json()
96
+ error_message = error_data.get("error", {}).get("message", "No detailed error message.")
97
+ logger.error(f"Chute.ai API Error Status: {response.status_code}. Message: {error_message}")
98
+ return f"Error: Unable to get response (Status: {response.status_code}). Chute.ai Message: {error_message}"
99
+
100
+ except requests.exceptions.RequestException as e:
101
+ logger.error(f"Connection/Request Error: {e}")
102
+ return f"Error: Failed to connect to Chute.ai. Check your connection or API endpoint. Details: {str(e)}"
103
+
104
+ def start_consultation(self):
105
+
106
+ print("πŸ€– Homeopathy AI Doctor is ready!")
107
+ print("=" * 50)
108
+ print("Please describe your symptoms... (Type 'quit' to exit)")
109
+ print()
110
+
111
+ while True:
112
+ try:
113
+ user_input = input("Patient: ").strip()
114
+
115
+ if user_input.lower() in ['quit', 'exit', 'bye']:
116
+ print("\nHomeopathy Doctor: Thank you for consulting! Wishing you good health! 🌿")
117
+ break
118
+
119
+ if not user_input:
120
+ print("Homeopathy Doctor: Please describe your symptoms.")
121
+ continue
122
+
123
+ print("Homeopathy Doctor: Analyzing your symptoms...")
124
+
125
+ # --- Get relevant context ---
126
+ context = self.get_relevant_context(user_input)
127
+
128
+ # --- Get AI response ---
129
+ # Pass the history to the query function
130
+ response = self.query_ai(user_input, context, self.chat_history)
131
+
132
+ print(f"Homeopathy Doctor: {response}\n")
133
+ print("-" * 50)
134
+
135
+ # --- Update chat history with patient input and bot response ---
136
+ self.chat_history.append({"role": "user", "content": user_input})
137
+ # Check if the response contains an error message before appending as assistant's content
138
+ if not response.startswith("Error:"):
139
+ self.chat_history.append({"role": "assistant", "content": response})
140
+
141
+ except KeyboardInterrupt:
142
+ print("\n\nHomeopathy Doctor: Consultation ended. Take care!")
143
+ break
144
+ except Exception as e:
145
+ print(f"Homeopathy Doctor: I encountered an issue. Please try again. Error: {str(e)}")
146
+
147
+ def main():
148
+ print("πŸš€ Starting Homeopathy AI Doctor Setup...")
149
+
150
+ # Check if vector database exists
151
+ if not os.path.exists("./vector_db"):
152
+ print("❌ Vector database not found.")
153
+ print("πŸ’‘ Please run 'python ingest_book.py' first to create the knowledge base.")
154
+ return
155
+
156
+ # Check API key
157
+ if not os.getenv("CHUTEAI_API_KEY"):
158
+ print("❌ CHUTEAI_API_KEY not found in .env file.")
159
+ return
160
+
161
+ try:
162
+ bot = HomeopathyDoctorBot()
163
+ bot.start_consultation()
164
+ except Exception as e:
165
+ print(f"❌ Failed to start bot: {str(e)}")
166
+
167
+ if __name__ == "__main__":
168
+ main()
homeopathy_book.pdf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:290679bb4a824b003e0d79d7d4ded27e27dc36d229a0ff41e1c3c8071d452138
3
+ size 2472154
ingest_book.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from langchain_community.document_loaders import PyPDFLoader
3
+ from langchain_text_splitters import RecursiveCharacterTextSplitter
4
+ from langchain_community.vectorstores import Chroma
5
+ from langchain_community.embeddings import HuggingFaceEmbeddings
6
+
7
+ def main():
8
+ print("πŸ“– Loading homeopathy_book.pdf...")
9
+ loader = PyPDFLoader("homeopathy_book.pdf")
10
+ documents = loader.load()
11
+
12
+ print("βœ‚οΈ Splitting text into chunks...")
13
+ text_splitter = RecursiveCharacterTextSplitter(
14
+ chunk_size=1000,
15
+ chunk_overlap=200
16
+ )
17
+ chunks = text_splitter.split_documents(documents)
18
+
19
+ print(f"🧠 Creating memory database with {len(chunks)} chunks...")
20
+ embeddings = HuggingFaceEmbeddings(
21
+ model_name="sentence-transformers/all-MiniLM-L6-v2"
22
+ )
23
+
24
+ vector_store = Chroma.from_documents(
25
+ documents=chunks,
26
+ embedding=embeddings,
27
+ persist_directory="./vector_db"
28
+ )
29
+
30
+ print("βœ… Ingestion complete! Memory saved to 'vector_db' folder.")
31
+
32
+ if __name__ == "__main__":
33
+ main()
requirements.txt ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ aiohappyeyeballs==2.6.1
2
+ aiohttp==3.13.2
3
+ aiosignal==1.4.0
4
+ annotated-types==0.7.0
5
+ anyio==4.12.0
6
+ attrs==25.4.0
7
+ backoff==2.2.1
8
+ bcrypt==5.0.0
9
+ build==1.3.0
10
+ cachetools==6.2.2
11
+ certifi==2025.11.12
12
+ charset-normalizer==3.4.4
13
+ chromadb==1.3.5
14
+ click==8.3.1
15
+ colorama==0.4.6
16
+ coloredlogs==15.0.1
17
+ dataclasses-json==0.6.7
18
+ distro==1.9.0
19
+ durationpy==0.10
20
+ filelock==3.20.0
21
+ flatbuffers==25.9.23
22
+ frozenlist==1.8.0
23
+ fsspec==2025.10.0
24
+ google-auth==2.43.0
25
+ googleapis-common-protos==1.72.0
26
+ greenlet==3.2.4
27
+ grpcio==1.76.0
28
+ h11==0.16.0
29
+ httpcore==1.0.9
30
+ httptools==0.7.1
31
+ httpx==0.28.1
32
+ httpx-sse==0.4.3
33
+ huggingface-hub==0.36.0
34
+ humanfriendly==10.0
35
+ idna==3.11
36
+ importlib_metadata==8.7.0
37
+ importlib_resources==6.5.2
38
+ Jinja2==3.1.6
39
+ joblib==1.5.2
40
+ jsonpatch==1.33
41
+ jsonpointer==3.0.0
42
+ jsonschema==4.25.1
43
+ jsonschema-specifications==2025.9.1
44
+ kubernetes==34.1.0
45
+ langchain==1.1.0
46
+ langchain-chroma==1.0.0
47
+ langchain-classic==1.0.0
48
+ langchain-community==0.4.1
49
+ langchain-core==1.1.0
50
+ langchain-huggingface==1.1.0
51
+ langchain-text-splitters==1.0.0
52
+ langgraph==1.0.4
53
+ langgraph-checkpoint==3.0.1
54
+ langgraph-prebuilt==1.0.5
55
+ langgraph-sdk==0.2.10
56
+ langsmith==0.4.49
57
+ markdown-it-py==4.0.0
58
+ MarkupSafe==3.0.3
59
+ marshmallow==3.26.1
60
+ mdurl==0.1.2
61
+ mmh3==5.2.0
62
+ mpmath==1.3.0
63
+ multidict==6.7.0
64
+ mypy_extensions==1.1.0
65
+ networkx==3.6
66
+ numpy==2.3.5
67
+ oauthlib==3.3.1
68
+ onnxruntime==1.23.2
69
+ opentelemetry-api==1.38.0
70
+ opentelemetry-exporter-otlp-proto-common==1.38.0
71
+ opentelemetry-exporter-otlp-proto-grpc==1.38.0
72
+ opentelemetry-proto==1.38.0
73
+ opentelemetry-sdk==1.38.0
74
+ opentelemetry-semantic-conventions==0.59b0
75
+ orjson==3.11.4
76
+ ormsgpack==1.12.0
77
+ overrides==7.7.0
78
+ packaging==25.0
79
+ pillow==12.0.0
80
+ posthog==5.4.0
81
+ propcache==0.4.1
82
+ protobuf==6.33.1
83
+ pyasn1==0.6.1
84
+ pyasn1_modules==0.4.2
85
+ pybase64==1.4.2
86
+ pydantic==2.12.5
87
+ pydantic-settings==2.12.0
88
+ pydantic_core==2.41.5
89
+ Pygments==2.19.2
90
+ pypdf==6.4.0
91
+ PyPika==0.48.9
92
+ pyproject_hooks==1.2.0
93
+ pyreadline3==3.5.4
94
+ python-dateutil==2.9.0.post0
95
+ python-dotenv==1.2.1
96
+ python-telegram-bot==22.5
97
+ PyYAML==6.0.3
98
+ referencing==0.37.0
99
+ regex==2025.11.3
100
+ requests==2.32.5
101
+ requests-oauthlib==2.0.0
102
+ requests-toolbelt==1.0.0
103
+ rich==14.2.0
104
+ rpds-py==0.29.0
105
+ rsa==4.9.1
106
+ safetensors==0.7.0
107
+ scikit-learn==1.7.2
108
+ scipy==1.16.3
109
+ sentence-transformers==5.1.2
110
+ setuptools==80.9.0
111
+ shellingham==1.5.4
112
+ six==1.17.0
113
+ sniffio==1.3.1
114
+ SQLAlchemy==2.0.44
115
+ sympy==1.14.0
116
+ tenacity==9.1.2
117
+ threadpoolctl==3.6.0
118
+ tokenizers==0.22.1
119
+ torch==2.9.1
120
+ tqdm==4.67.1
121
+ transformers==4.57.3
122
+ typer==0.20.0
123
+ typing-inspect==0.9.0
124
+ typing-inspection==0.4.2
125
+ typing_extensions==4.15.0
126
+ urllib3==2.3.0
127
+ uvicorn==0.38.0
128
+ watchfiles==1.1.1
129
+ websocket-client==1.9.0
130
+ websockets==15.0.1
131
+ xxhash==3.6.0
132
+ yarl==1.22.0
133
+ zipp==3.23.0
134
+ zstandard==0.25.0
telegram_bot.py ADDED
@@ -0,0 +1,395 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import requests
3
+ import json
4
+ from dotenv import load_dotenv
5
+ from telegram import Update, ReplyKeyboardMarkup, ReplyKeyboardRemove
6
+ import datetime
7
+ from telegram.ext import Application, CommandHandler, MessageHandler, filters, ContextTypes, ConversationHandler
8
+ import logging
9
+
10
+ # FIX 1: Using the langchain_community package imports for stability
11
+ from langchain_community.embeddings import HuggingFaceEmbeddings
12
+ from langchain_community.vectorstores import Chroma
13
+
14
+ # Load environment variables
15
+ load_dotenv()
16
+
17
+ # Enable logging
18
+ logging.basicConfig(
19
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
20
+ level=logging.INFO
21
+ )
22
+ logger = logging.getLogger(__name__)
23
+
24
+ # Conversation states
25
+ DESCRIBING_SYMPTOMS = 1
26
+
27
+ class TelegramHomeopathyBot:
28
+ def __init__(self):
29
+ # Create logs directory if not exists
30
+ self.logs_dir = "UserChatLogs"
31
+ os.makedirs(self.logs_dir, exist_ok=True)
32
+
33
+ # Initialize embeddings and vector store
34
+ self.embeddings = HuggingFaceEmbeddings(
35
+ model_name="sentence-transformers/all-MiniLM-L6-v2"
36
+ )
37
+ self.vector_store = Chroma(
38
+ persist_directory="./vector_db",
39
+ embedding_function=self.embeddings
40
+ )
41
+ self.retriever = self.vector_store.as_retriever(search_kwargs={"k": 4})
42
+
43
+ # Store user sessions: includes chat_history
44
+ self.user_sessions = {}
45
+
46
+ # FIX 4: Removed duplicate log_chat definition
47
+ def log_chat(self, user_id: int, username: str, message: str, is_bot: bool = False):
48
+ """Log user/bot messages to individual files"""
49
+ try:
50
+ log_file = os.path.join(self.logs_dir, f"user_{user_id}.log")
51
+ timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
52
+ direction = "BOT" if is_bot else "USER"
53
+
54
+ with open(log_file, "a", encoding="utf-8") as f:
55
+ f.write(f"[{timestamp}] {direction} ({username}): {message}\n")
56
+ except Exception as e:
57
+ logger.error(f"Failed to log chat: {e}")
58
+
59
+ def get_user_session(self, user_id):
60
+ """Get or create user session, initializing history."""
61
+ if user_id not in self.user_sessions:
62
+ self.user_sessions[user_id] = {
63
+ 'chat_history': [],
64
+ 'consultation_count': 0,
65
+ 'last_query': None # Track query context
66
+ }
67
+ return self.user_sessions[user_id]
68
+
69
+ def get_relevant_context(self, query, history):
70
+ """
71
+ Retrieve relevant context from the vector database using the latest query
72
+ and the accumulated user history for better RAG results.
73
+ """
74
+ # Create a composite query string from previous user inputs and the latest query
75
+ user_history = [msg['content'] for msg in history if msg['role'] == 'user']
76
+ history_summary = " ".join(user_history)
77
+
78
+ # Combine the user's history and current message for a comprehensive retrieval query
79
+ composite_query = f"Patient's case summary: {history_summary} {query}" if history_summary else query
80
+
81
+ docs = self.retriever.invoke(composite_query)
82
+ context = "\n".join([doc.page_content for doc in docs])
83
+ return context
84
+
85
+ def query_ai(self, user_message, context, history):
86
+ """Query the AI model via Chute.ai"""
87
+ api_key = os.getenv("CHUTEAI_API_KEY")
88
+
89
+ if not api_key:
90
+ logger.error("🚨 CRITICAL API ERROR: CHUTEAI_API_KEY is missing or empty during function call.")
91
+ return "❌ Configuration Error: The AI service API key (CHUTEAI_API_KEY) is missing. Please check your setup."
92
+
93
+ headers = {
94
+ "Authorization": f"Bearer {api_key}",
95
+ "Content-Type": "application/json"
96
+ }
97
+
98
+ # System prompt remains the same
99
+ system_prompt = """You are an expert Homeopathy Doctor. Your goal is to find the best possible remedy from the provided Context.
100
+
101
+ **DIAGNOSTIC PROCESS & CONSTRAINTS:**
102
+ 1. **Focus:** Analyze the patient's full symptom set, including the **Chat History**. Only analyze the symptoms explicitly mentioned by the patient. IGNORE any symptoms found *only* in the Context that the patient has not mentioned.
103
+ 2. **Clarification:** You MUST conclude the diagnosis and suggest a remedy within the first **two or three turns** of the conversation. Ask a MAXIMUM of 3 concise clarifying questions in the first turn only, if needed. In subsequent turns, prioritize diagnosing based on the accumulated history.
104
+ 3. **Prescription Rule:** **MUST** prescribe the single best-matching remedy when:
105
+ a) You have clear matching symptoms from the Context.
106
+ b) The patient explicitly asks for the medicine, or indicates they cannot answer more questions. In this case, use the best available information from the Chat History to prescribe.
107
+ 4. **Safety:** If unsure or the context doesn't contain a relevant remedy, admit it honestly.
108
+ 5. **Tone:** Always be professional, caring, and responsible.
109
+ """
110
+ messages = [
111
+ {"role": "system", "content": system_prompt},
112
+ *history,
113
+ {"role": "user", "content": f"Context from homeopathy book:\n{context}\n\nPatient Complaint: {user_message}"}
114
+ ]
115
+
116
+ data = {
117
+ # FIX 3: Changed to the working model that resolved the 404 error in the console bot
118
+ "model": "meituan-longcat/LongCat-Flash-Chat-FP8",
119
+ "messages": messages,
120
+ "temperature": 0.2,
121
+ "max_tokens": 500
122
+ }
123
+
124
+ # FIX 2: Corrected API URL to the one that successfully connected
125
+ api_url = "https://llm.chutes.ai/v1/chat/completions"
126
+
127
+ try:
128
+ response = requests.post(
129
+ api_url,
130
+ headers=headers,
131
+ json=data,
132
+ timeout=30
133
+ )
134
+
135
+ if response.status_code == 200:
136
+ return response.json()["choices"][0]["message"]["content"]
137
+ else:
138
+ error_data = response.json()
139
+ error_message = error_data.get("error", {}).get("message", "No detailed error message.")
140
+
141
+ if response.status_code == 401:
142
+ logger.error("🚨 CRITICAL API ERROR (401 Unauthorized) 🚨: The CHUTEAI_API_KEY is likely invalid or missing. Please check your .env file.")
143
+
144
+ logger.error(f"Chute.ai Error {response.status_code}: {error_message}")
145
+ return f"❌ I'm having technical difficulties. Please try again later. (Error: {response.status_code})"
146
+
147
+ except Exception as e:
148
+ logger.error(f"Connection Error: {e}")
149
+ return f"❌ Connection error. Please try again. Error: {str(e)}"
150
+
151
+ # Create bot instance
152
+ homeopathy_bot = TelegramHomeopathyBot()
153
+
154
+ # Telegram Bot Handlers
155
+ async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
156
+ """Send welcome message when the command /start is issued."""
157
+ user = update.effective_user
158
+ welcome_text = f"""
159
+ πŸ‘‹ Hello *{user.first_name}*! I'm your AI Homeopathy Doctor πŸ€–
160
+
161
+ I can help you analyze symptoms and suggest potential homeopathic remedies based on medical knowledge.
162
+
163
+ πŸ’‘ *How to use:*
164
+ 1. Describe your symptoms in detail
165
+ 2. I'll ask clarifying questions (max 3 in the first turn)
166
+ 3. I'll suggest potential homeopathic remedies within 3 turns
167
+
168
+ ⚠️ *Important Disclaimer:*
169
+ This is for educational purposes only. Always consult a qualified homeopath or medical professional for proper diagnosis and treatment.
170
+
171
+ Type your symptoms below to begin...
172
+ """
173
+
174
+ # Reset session for a clean start
175
+ user_id = update.effective_user.id
176
+ homeopathy_bot.user_sessions[user_id] = {'chat_history': [], 'consultation_count': 0}
177
+
178
+ await update.message.reply_text(welcome_text, parse_mode='Markdown')
179
+ # Log start command
180
+ homeopathy_bot.log_chat(
181
+ user.id,
182
+ user.username or f"{user.first_name} {user.last_name or ''}".strip(),
183
+ "/start command received",
184
+ is_bot=False
185
+ )
186
+ return DESCRIBING_SYMPTOMS
187
+
188
+ async def handle_symptoms(update: Update, context: ContextTypes.DEFAULT_TYPE):
189
+ """Handle user's symptom description and continue the diagnosis."""
190
+ user_id = update.effective_user.id
191
+ user_input = update.message.text
192
+
193
+ # Get user session
194
+ session = homeopathy_bot.get_user_session(user_id)
195
+ session['consultation_count'] += 1
196
+
197
+ # Send typing action
198
+ await update.message.reply_chat_action(action="typing")
199
+
200
+ # Send processing message
201
+ processing_msg = await update.message.reply_text("πŸ” Analyzing your symptoms...")
202
+
203
+ try:
204
+ # Get relevant context (using the history to enrich the retrieval query)
205
+ context_text = homeopathy_bot.get_relevant_context(user_input, session['chat_history'])
206
+
207
+ # Get AI response
208
+ response = homeopathy_bot.query_ai(user_input, context_text, session['chat_history'])
209
+
210
+ # Update chat history
211
+ if not response.startswith("❌"): # Only update history if not an error message
212
+ session['chat_history'].append({"role": "user", "content": user_input})
213
+ session['chat_history'].append({"role": "assistant", "content": response})
214
+
215
+ # Log user input and bot response
216
+ user = update.effective_user
217
+ homeopathy_bot.log_chat(
218
+ user.id,
219
+ user.username or f"{user.first_name} {user.last_name or ''}".strip(),
220
+ user_input,
221
+ is_bot=False
222
+ )
223
+ homeopathy_bot.log_chat(
224
+ user.id,
225
+ user.username or f"{user.first_name} {user.last_name or ''}".strip(),
226
+ response,
227
+ is_bot=True
228
+ )
229
+
230
+ # Keep only last 6 messages (3 user + 3 assistant) to prevent context overflow
231
+ if len(session['chat_history']) > 6:
232
+ session['chat_history'] = session['chat_history'][-6:]
233
+
234
+ # Send response using Markdown for clear formatting
235
+ try:
236
+ await processing_msg.delete() # Remove processing message
237
+ except Exception as e:
238
+ logger.warning(f"Could not delete processing message: {e}")
239
+ await update.message.reply_text(
240
+ f"🩺 *Homeopathy Doctor:*\n\n{response}",
241
+ parse_mode='Markdown' # Use Markdown for formatting
242
+ )
243
+
244
+ # Add quick actions
245
+ quick_actions = [["πŸ” Describe more symptoms or answer questions"], ["πŸ”„ Start a new consultation"]]
246
+ reply_markup = ReplyKeyboardMarkup(quick_actions, one_time_keyboard=True, resize_keyboard=True)
247
+ await update.message.reply_text("What would you like to do next?", reply_markup=reply_markup)
248
+
249
+ except Exception as e:
250
+ logger.error(f"Error processing message: {e}")
251
+ try:
252
+ await processing_msg.delete()
253
+ except:
254
+ pass
255
+ await update.message.reply_text("❌ Sorry, I encountered an error. Please try again.")
256
+
257
+ return DESCRIBING_SYMPTOMS
258
+
259
+ async def handle_quick_actions(update: Update, context: ContextTypes.DEFAULT_TYPE):
260
+ """Handle quick action buttons."""
261
+ user_input = update.message.text
262
+
263
+ if user_input in ["πŸ” Describe more symptoms or answer questions", "Reset please"]:
264
+ # If the bot has prescribed a remedy, the user might be ready to start fresh,
265
+ # so this is a good opportunity to offer the reset.
266
+ if len(homeopathy_bot.get_user_session(update.effective_user.id)['chat_history']) >= 4: # If 2 turns have passed
267
+ await update.message.reply_text("Please provide any additional details or clarify the Doctor's previous questions...")
268
+ else:
269
+ await update.message.reply_text("Please provide any additional details or clarify the Doctor's previous questions...")
270
+
271
+ elif user_input in ["πŸ”„ Start a new consultation", "Reset please"]:
272
+ user_id = update.effective_user.id
273
+ if user_id in homeopathy_bot.user_sessions:
274
+ # Clear history and reset embeddings for a completely fresh start
275
+ homeopathy_bot.user_sessions[user_id] = {
276
+ 'chat_history': [],
277
+ 'consultation_count': 0,
278
+ 'last_query': None # Reset any cached query context
279
+ }
280
+ # Reinitialize the retriever to clear any cached context
281
+ homeopathy_bot.retriever = homeopathy_bot.vector_store.as_retriever(search_kwargs={"k": 4})
282
+ await update.message.reply_text("πŸ”„ Starting new consultation. Please describe your symptoms...", reply_markup=ReplyKeyboardRemove())
283
+
284
+ return DESCRIBING_SYMPTOMS
285
+
286
+ async def cancel(update: Update, context: ContextTypes.DEFAULT_TYPE):
287
+ """Cancel the conversation."""
288
+ user_id = update.effective_user.id
289
+ if user_id in homeopathy_bot.user_sessions:
290
+ del homeopathy_bot.user_sessions[user_id]
291
+
292
+ await update.message.reply_text(
293
+ "πŸ‘‹ Consultation ended. Thank you for using Homeopathy AI Doctor!\n\n"
294
+ "Remember to consult a qualified homeopath for proper treatment. 🌿",
295
+ reply_markup=ReplyKeyboardRemove()
296
+ )
297
+ return ConversationHandler.END
298
+
299
+ async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
300
+ """Send help message."""
301
+ help_text = """
302
+ πŸ€– *Homeopathy AI Doctor Bot Help*
303
+
304
+ *Available Commands:*
305
+ /start - Begin a new consultation (resets history)
306
+ /help - Show this help message
307
+ /cancel - End current consultation
308
+
309
+ *How to get the best results:*
310
+ β€’ Describe symptoms in detail
311
+ β€’ Mention location, intensity, and timing
312
+ β€’ Share what makes symptoms better/worse
313
+ β€’ Be specific about associated feelings
314
+
315
+ *Disclaimer:* This bot provides educational information only. Always consult qualified medical professionals.
316
+ """
317
+ await update.message.reply_text(help_text, parse_mode='Markdown')
318
+
319
+ # FIX: Add a new handler to catch unhandled text messages and guide the user.
320
+ async def generic_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
321
+ """Handles any plain text message received outside the ConversationHandler."""
322
+ await update.message.reply_text(
323
+ "πŸ‘‹ Welcome! To begin a new consultation and describe your symptoms, please use the */start* command. Thank you!",
324
+ parse_mode='Markdown'
325
+ )
326
+
327
+ async def error_handler(update: Update, context: ContextTypes.DEFAULT_TYPE):
328
+ """Log errors and send a friendly message."""
329
+ logger.error(f"Update {update} caused error {context.error}")
330
+ if update and update.message:
331
+ await update.message.reply_text(
332
+ "❌ Sorry, I encountered an unexpected error. Please try again or use /start to begin a new consultation."
333
+ )
334
+
335
+ def check_dependencies():
336
+ """Checks for required environment variables and the vector database."""
337
+ if not os.getenv("TELEGRAM_BOT_TOKEN"):
338
+ print("❌ TELEGRAM_BOT_TOKEN not found in .env file. Please set it to run the bot.")
339
+ return False
340
+
341
+ if not os.path.exists("./vector_db"):
342
+ print("❌ Vector database not found. Please run 'python ingest_book.py' first.")
343
+ return False
344
+
345
+ # FIX 5: Changed dependency check to look for CHUTEAI_API_KEY
346
+ if not os.getenv("CHUTEAI_API_KEY"):
347
+ print("❌ CHUTEAI_API_KEY not found in .env file. Please set it to run the bot.")
348
+ return False
349
+
350
+ return True
351
+
352
+ def main():
353
+ """Start the bot."""
354
+
355
+ if not check_dependencies():
356
+ return
357
+
358
+ print("πŸš€ Starting Telegram Homeopathy Bot...")
359
+
360
+ # Get Telegram bot token from environment variable
361
+ TELEGRAM_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN")
362
+
363
+ # Create Application
364
+ application = Application.builder().token(TELEGRAM_TOKEN).build()
365
+
366
+ # Add conversation handler
367
+ conv_handler = ConversationHandler(
368
+ entry_points=[CommandHandler('start', start)],
369
+ states={
370
+ DESCRIBING_SYMPTOMS: [
371
+ MessageHandler(filters.Regex(r'^(πŸ”|πŸ”„)'), handle_quick_actions),
372
+ MessageHandler(filters.TEXT & ~filters.COMMAND, handle_symptoms),
373
+ ],
374
+ },
375
+ fallbacks=[CommandHandler('cancel', cancel)],
376
+ )
377
+
378
+ # Add handlers
379
+ application.add_handler(conv_handler)
380
+ application.add_handler(CommandHandler('help', help_command))
381
+ application.add_handler(CommandHandler('cancel', cancel))
382
+
383
+ # Handler for general text outside the conversation flow
384
+ application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, generic_message))
385
+
386
+ # Add error handler
387
+ application.add_error_handler(error_handler)
388
+
389
+ # Start the Bot
390
+ print("βœ… Bot is running... Press Ctrl+C to stop")
391
+ application.run_polling(allowed_updates=Update.ALL_TYPES)
392
+
393
+ if __name__ == '__main__':
394
+ # Ensure all initialization warnings are visible before starting the main loop
395
+ main()
vector_db/160d4ccf-36f6-4e8b-bb3b-9945cfc169c4/data_level0.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:bf9bc05dc6181caab1ff5fb86c61ec6967dbd30760d781c104f9fa4402580967
3
+ size 1717900
vector_db/160d4ccf-36f6-4e8b-bb3b-9945cfc169c4/header.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ee4ab08e6fb139f5b0d19abbdd26b64432c4d68347e6b86cb888481912424126
3
+ size 100
vector_db/160d4ccf-36f6-4e8b-bb3b-9945cfc169c4/index_metadata.pickle ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8702fa124ba709c0669f27d01bdfb4d3aabfc45b1ca1a0dc91224eec3a4c67a1
3
+ size 94432
vector_db/160d4ccf-36f6-4e8b-bb3b-9945cfc169c4/length.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f3eb7cd5b86cf81e02ddca312545b406bea36b60184d3e83ef8b843d755eaf7a
3
+ size 4100
vector_db/160d4ccf-36f6-4e8b-bb3b-9945cfc169c4/link_lists.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a4ad77cb35c50720bfc70f08aba4efafa066782ee4b430db66d4fb66a2441eb2
3
+ size 8860
vector_db/chroma.sqlite3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ef01ae6d84095769d55c7d5cdcbd5bbf758781104a22e7e7e8c8ae5d7bfb2843
3
+ size 12500992