codeBOKER commited on
Commit
511ba56
·
1 Parent(s): 114c332

connect to supabase

Browse files
=3.0.0 ADDED
File without changes
Dockerfile CHANGED
@@ -4,14 +4,17 @@ FROM python:3.11-slim
4
  # Set working directory
5
  WORKDIR /app
6
 
7
- # Set environment variables
8
  ENV PYTHONDONTWRITEBYTECODE=1 \
9
  PYTHONUNBUFFERED=1 \
10
- PORT=7860
 
 
11
 
12
- # Install system dependencies
13
  RUN apt-get update && apt-get install -y \
14
  gcc \
 
15
  && rm -rf /var/lib/apt/lists/*
16
 
17
  # Copy requirements first for better caching
@@ -21,20 +24,18 @@ COPY requirements.txt .
21
  RUN pip install --no-cache-dir --upgrade pip && \
22
  pip install --no-cache-dir -r requirements.txt
23
 
24
- # Copy application code
25
- COPY app.py .
26
 
27
- # Create non-root user for security
28
- RUN useradd --create-home --shell /bin/bash app && \
29
- chown -R app:app /app
30
- USER app
31
 
32
- # Expose port
33
- EXPOSE 7860
34
 
35
  # Health check
36
  HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \
37
- CMD curl -f http://localhost:7860/ || exit 1
38
 
39
- # Run the application
40
- CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
 
4
  # Set working directory
5
  WORKDIR /app
6
 
7
+ # Set environment variables for Hugging Face Spaces (can be overridden for local development)
8
  ENV PYTHONDONTWRITEBYTECODE=1 \
9
  PYTHONUNBUFFERED=1 \
10
+ PORT=8000 \
11
+ HF_HOME=/app/cache \
12
+ TRANSFORMERS_CACHE=/app/cache
13
 
14
+ # Install system dependencies including curl for health check
15
  RUN apt-get update && apt-get install -y \
16
  gcc \
17
+ curl \
18
  && rm -rf /var/lib/apt/lists/*
19
 
20
  # Copy requirements first for better caching
 
24
  RUN pip install --no-cache-dir --upgrade pip && \
25
  pip install --no-cache-dir -r requirements.txt
26
 
27
+ # Copy all application code
28
+ COPY . .
29
 
30
+ # Create cache directory for Hugging Face models
31
+ RUN mkdir -p /app/cache && chmod 777 /app/cache
 
 
32
 
33
+ # Expose port (default 8000 for local, can be set to 7860 for Hugging Face Spaces)
34
+ EXPOSE ${PORT}
35
 
36
  # Health check
37
  HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \
38
+ CMD curl -f http://localhost:${PORT}/ || exit 1
39
 
40
+ # Run the application (uses PORT environment variable)
41
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "${PORT}"]
README.md CHANGED
@@ -1,14 +1 @@
1
- ---
2
- title: customer_service
3
- sdk: docker
4
- emoji: 🚀
5
- colorFrom: blue
6
- colorTo: blue
7
- app_file: app.py
8
- ---
9
  # customer_service
10
-
11
-
12
- ---
13
-
14
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
1
  # customer_service
 
 
 
 
 
__pycache__/ai_service.cpython-313.pyc ADDED
Binary file (3.15 kB). View file
 
__pycache__/app.cpython-313.pyc ADDED
Binary file (3.29 kB). View file
 
__pycache__/config.cpython-313.pyc ADDED
Binary file (1.93 kB). View file
 
__pycache__/database.cpython-313.pyc ADDED
Binary file (10.2 kB). View file
 
__pycache__/main.cpython-313.pyc ADDED
Binary file (1.6 kB). View file
 
__pycache__/telegram_handlers.cpython-313.pyc ADDED
Binary file (2.74 kB). View file
 
__pycache__/utils.cpython-313.pyc ADDED
Binary file (1.08 kB). View file
 
ai_service.py ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+ from config import pc, index, groq_client, EMBED_MODEL, GROQ_MODEL, PROMPT
3
+ from database import db_manager
4
+
5
+ def clean_ai_response(text: str):
6
+ cleaned_text = re.sub(r'<think>.*?</think>', '', text, flags=re.DOTALL)
7
+ return cleaned_text.strip()
8
+
9
+ async def get_ai_response(user_query: str, telegram_id: int = None):
10
+
11
+ if not pc or not index or not groq_client:
12
+ return "Ai service is not available at the moment. Please try again later."
13
+
14
+ # Save user message if database is available and telegram_id is provided
15
+ if telegram_id and db_manager:
16
+ db_manager.save_message(telegram_id, user_query, "user")
17
+
18
+ conversation_history = ""
19
+ if telegram_id and db_manager:
20
+ conversation_history = db_manager.get_formatted_history(telegram_id, limit=6)
21
+
22
+
23
+ query_embedding = pc.inference.embed(
24
+ model=EMBED_MODEL,
25
+ inputs=[user_query],
26
+ parameters={"input_type": "query"}
27
+ )
28
+
29
+ # Search Pinecone for Bank Context
30
+ search_results = index.query(
31
+ vector=query_embedding[0].values,
32
+ top_k=3,
33
+ include_metadata=True
34
+ )
35
+
36
+ retrieved_context = "\n".join([res.metadata['original_text'] for res in search_results.matches])
37
+
38
+
39
+ user_content = f"""
40
+ ### Historical Conversation:
41
+ {conversation_history}
42
+
43
+ ### Retrieved Context from Bank Documents:
44
+ {retrieved_context}
45
+
46
+ ### Current User Message:
47
+ {user_query}
48
+
49
+ بناءً على ما سبق، قدم إجابة دقيقة ومفيدة للعميل:
50
+ """
51
+ completion = groq_client.chat.completions.create(
52
+ messages=[
53
+ {"role": "system", "content": PROMPT},
54
+ {"role": "user", "content": f"{conversation_history}\n\nRetrieved Context: {retrieved_context}\n\nCurrent User Message: {user_query}"}
55
+ ],
56
+ model=GROQ_MODEL,
57
+ temperature=0.1,
58
+ max_completion_tokens=800,
59
+ top_p=0.9,
60
+ )
61
+ ai_response = completion.choices[0].message.content
62
+ cleaned_response = clean_ai_response(ai_response)
63
+
64
+ # Save assistant response if database is available and telegram_id is provided
65
+ if telegram_id and db_manager:
66
+ db_manager.save_message(telegram_id, cleaned_response, "assistant")
67
+
68
+ return cleaned_response
69
+
app.py DELETED
@@ -1,113 +0,0 @@
1
- import os
2
- from fastapi import FastAPI, Request
3
- from pinecone import Pinecone
4
- from groq import Groq
5
- import httpx
6
- import re
7
-
8
- # 1. Configuration & Clients
9
- # Use Hugging Face Secrets for these!
10
- PINECONE_API_KEY = os.environ.get("PINECONE_API_KEY")
11
- GROQ_API_KEY = os.environ.get("GROQ_API_KEY")
12
- TELEGRAM_TOKEN = os.environ.get("TELEGRAM_TOKEN")
13
- TELEGRAM_URL = f"https://149.154.167.220/bot{TELEGRAM_TOKEN}/sendMessage"
14
-
15
- EMBED_MODEL= os.environ.get("EMBED_MODEL")
16
- GROQ_MODEL = os.environ.get("GROQ_MODEL")
17
- PROMPT = os.environ.get("PROMPT")
18
-
19
- pc = Pinecone(api_key=PINECONE_API_KEY)
20
- index = pc.Index("customerserviceindex")
21
- groq_client = Groq(api_key=GROQ_API_KEY)
22
-
23
- app = FastAPI()
24
-
25
- def clean_ai_response(text: str):
26
- # إزالة كل ما بين وسمي <think> و </think> بما في ذلك الوسوم نفسها
27
- cleaned_text = re.sub(r'<think>.*?</think>', '', text, flags=re.DOTALL)
28
- return cleaned_text.strip()
29
-
30
- # 2. The Core AI Logic
31
- async def get_ai_response(user_query: str):
32
- # Vectorize query using Pinecone Inference
33
- query_embedding = pc.inference.embed(
34
- model=EMBED_MODEL,
35
- inputs=[user_query],
36
- parameters={"input_type": "query"}
37
- )
38
-
39
- # Search Pinecone for Bank Context
40
- search_results = index.query(
41
- vector=query_embedding[0].values,
42
- top_k=3,
43
- include_metadata=True
44
- )
45
-
46
- retrieved_context = "\n".join([res.metadata['original_text'] for res in search_results.matches])
47
-
48
-
49
- prompt = f"""
50
- {PROMPT}
51
-
52
- Message:{user_query}
53
-
54
- Retrieved Context:{retrieved_context}
55
-
56
- Final Answer:
57
- """
58
-
59
- completion = groq_client.chat.completions.create(
60
- messages=[{"role": "user", "content": prompt}],
61
- model=GROQ_MODEL,
62
- temperature=0.1,
63
- max_completion_tokens=800,
64
- top_p=0.9,
65
- )
66
- ai_response = completion.choices[0].message.content
67
- return clean_ai_response(ai_response)
68
-
69
- # 3. The Webhook Endpoint
70
- @app.post("/webhook")
71
- async def telegram_webhook(request: Request):
72
- data = await request.json()
73
-
74
- if "message" in data:
75
- chat_id = data["message"]["chat"]["id"]
76
- user_text = data["message"].get("text", "")
77
-
78
- if user_text:
79
- # Get the intelligent response
80
- ai_answer = await get_ai_response(user_text)
81
- # Send back to Telegram
82
- async with httpx.AsyncClient(verify=False) as client:
83
- await client.post(
84
- TELEGRAM_URL,
85
- headers={"Host": "api.telegram.org"},
86
- json={
87
- "chat_id": chat_id,
88
- "text": ai_answer,
89
- "parse_mode": "Markdown"
90
- }
91
- )
92
-
93
- return {"status": "ok"}
94
-
95
- @app.get("/")
96
- async def root():
97
- return {"message": "Hadhramout Bank AI Backend is Live"}
98
-
99
- @app.post("/test")
100
- async def test_webhook(request: Request):
101
- data = await request.json()
102
- response = await get_ai_response(data["message"]["text"])
103
- return {"response": response}
104
-
105
- import socket
106
-
107
- @app.get("/dns-test")
108
- async def dns_test():
109
- try:
110
- ip = socket.gethostbyname("api.telegram.org")
111
- return {"resolved_ip": ip}
112
- except Exception as e:
113
- return {"error": str(e)}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
config.py ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from pinecone import Pinecone
3
+ from groq import Groq
4
+ from dotenv import load_dotenv
5
+
6
+ # Load environment variables from .env file
7
+ load_dotenv()
8
+
9
+ # Environment Variables
10
+ PINECONE_API_KEY = os.environ.get("PINECONE_API_KEY")
11
+ GROQ_API_KEY = os.environ.get("GROQ_API_KEY")
12
+ TELEGRAM_TOKEN = os.environ.get("TELEGRAM_TOKEN")
13
+ SUPABASE_URL = os.environ.get("SUPABASE_URL")
14
+ SUPABASE_KEY = os.environ.get("SUPABASE_KEY")
15
+
16
+
17
+ # Only create TELEGRAM_URL if token exists
18
+ TELEGRAM_URL = f"https://149.154.167.220/bot{TELEGRAM_TOKEN}/sendMessage" if TELEGRAM_TOKEN else None
19
+
20
+ EMBED_MODEL = os.environ.get("EMBED_MODEL", "multilingual-e5-large")
21
+ GROQ_MODEL = os.environ.get("GROQ_MODEL", "llama-3.1-8b-instant")
22
+ PROMPT = os.environ.get("PROMPT", "You are a helpful customer service assistant for Hadhramout Bank. Answer the user's question based on the provided context. If the context doesn't contain the answer, politely say you don't have enough information to help with that specific query.")
23
+
24
+ # Initialize clients only if API keys are available
25
+ pc = None
26
+ if PINECONE_API_KEY:
27
+ pc = Pinecone(api_key=PINECONE_API_KEY)
28
+
29
+ groq_client = None
30
+ if GROQ_API_KEY:
31
+ try:
32
+ groq_client = Groq(api_key=GROQ_API_KEY)
33
+ except Exception as e:
34
+ print(f"Warning: Failed to initialize Groq client: {e}")
35
+ groq_client = None
36
+
37
+ # Initialize index only if Pinecone client is available
38
+ index = None
39
+ if pc:
40
+ index = pc.Index("customerserviceindex")
database.py ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from datetime import datetime
3
+ from typing import List, Dict, Optional
4
+ from supabase import create_client, Client
5
+ import logging
6
+ from config import SUPABASE_URL, SUPABASE_KEY
7
+
8
+
9
+ class DatabaseManager:
10
+ def __init__(self, supabase_url: str = SUPABASE_URL, supabase_key: str = SUPABASE_KEY):
11
+ if not supabase_url or not supabase_key:
12
+ raise ValueError("SUPABASE_URL and SUPABASE_KEY must be set in environment variables")
13
+
14
+ self.supabase: Client = create_client(supabase_url, supabase_key)
15
+ self.logger = logging.getLogger(__name__)
16
+
17
+ def create_or_update_user(self, telegram_id: int, username: str = None,
18
+ first_name: str = None, last_name: str = None):
19
+ """Create or update user information"""
20
+ try:
21
+ # Check if user exists
22
+ existing_user = self.supabase.table("users").select("id").eq("telegram_id", telegram_id).execute()
23
+
24
+ user_data = {
25
+ "telegram_id": telegram_id,
26
+ "username": username,
27
+ "first_name": first_name,
28
+ "last_name": last_name,
29
+ "updated_at": datetime.utcnow().isoformat()
30
+ }
31
+
32
+ if existing_user.data:
33
+ # Update existing user
34
+ result = self.supabase.table("users").update(user_data).eq("telegram_id", telegram_id).execute()
35
+ else:
36
+ # Create new user
37
+ user_data["created_at"] = datetime.utcnow().isoformat()
38
+ result = self.supabase.table("users").insert(user_data).execute()
39
+
40
+ return result.data[0] if result.data else None
41
+
42
+ except Exception as e:
43
+ self.logger.error(f"Error creating/updating user: {e}")
44
+ return None
45
+
46
+ def save_message(self, telegram_id: int, message_text: str, message_type: str):
47
+ """Save a message to the database"""
48
+ try:
49
+ # Ensure user exists
50
+ self.create_or_update_user(telegram_id)
51
+
52
+ # Save message
53
+ message_data = {
54
+ "telegram_id": telegram_id,
55
+ "message_text": message_text,
56
+ "message_type": message_type,
57
+ "created_at": datetime.utcnow().isoformat()
58
+ }
59
+
60
+ result = self.supabase.table("messages").insert(message_data).execute()
61
+
62
+ # Ensure active session exists
63
+ self._ensure_active_session(telegram_id)
64
+
65
+ return result.data[0] if result.data else None
66
+
67
+ except Exception as e:
68
+ self.logger.error(f"Error saving message: {e}")
69
+ return None
70
+
71
+ def get_conversation_history(self, telegram_id: int, limit: int = 10) -> List[Dict]:
72
+ """Get conversation history for a user"""
73
+ try:
74
+ result = (self.supabase.table("messages")
75
+ .select("message_text, message_type, created_at")
76
+ .eq("telegram_id", telegram_id)
77
+ .order("created_at", desc=True)
78
+ .limit(limit)
79
+ .execute())
80
+
81
+ return result.data if result.data else []
82
+
83
+ except Exception as e:
84
+ self.logger.error(f"Error getting conversation history: {e}")
85
+ return []
86
+
87
+ def get_formatted_history(self, telegram_id: int, limit: int = 10) -> str:
88
+ """Get formatted conversation history for Groq"""
89
+ history = self.get_conversation_history(telegram_id, limit)
90
+
91
+ if not history:
92
+ return ""
93
+
94
+ # Reverse to get chronological order
95
+ history.reverse()
96
+
97
+ formatted_history = "Previous conversation:\n"
98
+ for msg in history:
99
+ role = "User" if msg['message_type'] == 'user' else "Assistant"
100
+ formatted_history += f"{role}: {msg['message_text']}\n"
101
+
102
+ return formatted_history
103
+
104
+ def _ensure_active_session(self, telegram_id: int):
105
+ """Ensure an active session exists for the user"""
106
+ try:
107
+ # Check for active session
108
+ active_session = (self.supabase.table("conversation_sessions")
109
+ .select("id")
110
+ .eq("telegram_id", telegram_id)
111
+ .is_("session_end", "null")
112
+ .execute())
113
+
114
+ if not active_session.data:
115
+ # Create new session
116
+ session_data = {
117
+ "telegram_id": telegram_id,
118
+ "session_start": datetime.utcnow().isoformat(),
119
+ "created_at": datetime.utcnow().isoformat()
120
+ }
121
+ self.supabase.table("conversation_sessions").insert(session_data).execute()
122
+
123
+ except Exception as e:
124
+ self.logger.error(f"Error ensuring active session: {e}")
125
+
126
+ def start_new_session(self, telegram_id: int):
127
+ """Start a new conversation session"""
128
+ try:
129
+ # End previous sessions
130
+ self.supabase.table("conversation_sessions").update({
131
+ "session_end": datetime.utcnow().isoformat()
132
+ }).eq("telegram_id", telegram_id).is_("session_end", "null").execute()
133
+
134
+ # Start new session
135
+ session_data = {
136
+ "telegram_id": telegram_id,
137
+ "session_start": datetime.utcnow().isoformat(),
138
+ "created_at": datetime.utcnow().isoformat()
139
+ }
140
+ result = self.supabase.table("conversation_sessions").insert(session_data).execute()
141
+
142
+ return result.data[0] if result.data else None
143
+
144
+ except Exception as e:
145
+ self.logger.error(f"Error starting new session: {e}")
146
+ return None
147
+
148
+ def get_user_stats(self, telegram_id: int) -> Dict:
149
+ """Get user conversation statistics"""
150
+ try:
151
+ # Get message counts
152
+ message_stats = (self.supabase.table("messages")
153
+ .select("message_type")
154
+ .eq("telegram_id", telegram_id)
155
+ .execute())
156
+
157
+ if not message_stats.data:
158
+ return {
159
+ "total_messages": 0,
160
+ "user_messages": 0,
161
+ "assistant_messages": 0,
162
+ "first_message": None,
163
+ "last_message": None
164
+ }
165
+
166
+ total_messages = len(message_stats.data)
167
+ user_messages = len([m for m in message_stats.data if m['message_type'] == 'user'])
168
+ assistant_messages = len([m for m in message_stats.data if m['message_type'] == 'assistant'])
169
+
170
+ # Get first and last message timestamps
171
+ timestamps = [m['created_at'] for m in message_stats.data]
172
+ first_message = min(timestamps) if timestamps else None
173
+ last_message = max(timestamps) if timestamps else None
174
+
175
+ return {
176
+ "total_messages": total_messages,
177
+ "user_messages": user_messages,
178
+ "assistant_messages": assistant_messages,
179
+ "first_message": first_message,
180
+ "last_message": last_message
181
+ }
182
+
183
+ except Exception as e:
184
+ self.logger.error(f"Error getting user stats: {e}")
185
+ return {
186
+ "total_messages": 0,
187
+ "user_messages": 0,
188
+ "assistant_messages": 0,
189
+ "first_message": None,
190
+ "last_message": None
191
+ }
192
+
193
+ # Global database instance
194
+ try:
195
+ db_manager = DatabaseManager()
196
+ except ValueError as e:
197
+ print(f"Database initialization failed: {e}")
198
+ print("Please set SUPABASE_URL and SUPABASE_KEY environment variables")
199
+ db_manager = None
200
+ except Exception as e:
201
+ print(f"Unexpected database error: {e}")
202
+ db_manager = None
database_schema.sql ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -- Conversation History Database Schema
2
+ -- SQLite Database Structure
3
+
4
+ -- Users table to store user information
5
+ CREATE TABLE IF NOT EXISTS users (
6
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
7
+ chat_id BIGINT UNIQUE NOT NULL,
8
+ username VARCHAR(255),
9
+ first_name VARCHAR(255),
10
+ last_name VARCHAR(255),
11
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
12
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
13
+ );
14
+
15
+ -- Messages table to store conversation history
16
+ CREATE TABLE IF NOT EXISTS messages (
17
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
18
+ chat_id BIGINT NOT NULL,
19
+ message_text TEXT NOT NULL,
20
+ message_type VARCHAR(20) NOT NULL CHECK (message_type IN ('user', 'assistant')),
21
+ timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
22
+ FOREIGN KEY (chat_id) REFERENCES users(chat_id) ON DELETE CASCADE
23
+ );
24
+
25
+ -- Conversation sessions to group messages by session
26
+ CREATE TABLE IF NOT EXISTS conversation_sessions (
27
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
28
+ chat_id BIGINT NOT NULL,
29
+ session_start TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
30
+ session_end TIMESTAMP NULL,
31
+ message_count INTEGER DEFAULT 0,
32
+ FOREIGN KEY (chat_id) REFERENCES users(chat_id) ON DELETE CASCADE
33
+ );
34
+
35
+ -- Indexes for better performance
36
+ CREATE INDEX IF NOT EXISTS idx_messages_chat_id_timestamp ON messages(chat_id, timestamp);
37
+ CREATE INDEX IF NOT EXISTS idx_users_chat_id ON users(chat_id);
38
+ CREATE INDEX IF NOT EXISTS idx_sessions_chat_id ON conversation_sessions(chat_id);
39
+
40
+ -- Trigger to update user's updated_at timestamp
41
+ CREATE TRIGGER IF NOT EXISTS update_user_timestamp
42
+ AFTER INSERT ON messages
43
+ FOR EACH ROW
44
+ BEGIN
45
+ UPDATE users SET updated_at = CURRENT_TIMESTAMP WHERE chat_id = NEW.chat_id;
46
+ END;
47
+
48
+ -- Trigger to update session message count
49
+ CREATE TRIGGER IF NOT EXISTS update_session_count
50
+ AFTER INSERT ON messages
51
+ FOR EACH ROW
52
+ BEGIN
53
+ UPDATE conversation_sessions
54
+ SET message_count = message_count + 1
55
+ WHERE chat_id = NEW.chat_id AND session_end IS NULL;
56
+ END;
main.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI
2
+ from telegram_handlers import telegram_webhook, test_webhook
3
+ from utils import dns_test, test_ai_response
4
+
5
+ app = FastAPI()
6
+
7
+ @app.get("/")
8
+ async def root():
9
+ return {"message": "Hadhramout Bank AI Backend is Live"}
10
+
11
+ @app.post("/webhook")
12
+ async def webhook(request):
13
+ return await telegram_webhook(request)
14
+
15
+ @app.post("/test")
16
+ async def test(request):
17
+ return await test_webhook(request)
18
+
19
+ @app.get("/dns-test")
20
+ async def dns():
21
+ return await dns_test()
22
+
23
+ @app.get("/ai-test")
24
+ async def ai():
25
+ return await test_ai_response()
requirements.txt CHANGED
@@ -4,3 +4,4 @@ pinecone
4
  groq
5
  httpx
6
  python-dotenv
 
 
4
  groq
5
  httpx
6
  python-dotenv
7
+ supabase
rest.http ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ POST https://codeboker-customer-service.hf.space/ai-test HTTP/1.1
2
+ Content-Type: application/json
3
+
4
+ {
5
+ "message": {
6
+ "chat": {"id": 123},
7
+ "text": "هاي"
8
+ }
9
+ }
10
+ ###
11
+ GET https://codeboker-customer-service.hf.space/ai-test HTTP/1.1
12
+
13
+ ###
14
+ POST https://codeboker-customer-service.hf.space/webhook HTTP/1.1
15
+ Content-Type: application/json
16
+ {
17
+ "message": {
18
+ "chat": {"id": 123},
19
+ "text": "السلام عليكم وؤحمة الله اريد ان استعلم عنلاالخدمات الالكترونية"
20
+ }
21
+ }
supabase_schema.sql ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -- Supabase Database Schema for Conversation History
2
+ -- Run this in your Supabase SQL Editor
3
+
4
+ -- Enable UUID extension
5
+ CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
6
+
7
+ -- Users table to store user information
8
+ CREATE TABLE IF NOT EXISTS users (
9
+ id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
10
+ telegram_id BIGINT UNIQUE NOT NULL,
11
+ username VARCHAR(255),
12
+ first_name VARCHAR(255),
13
+ last_name VARCHAR(255),
14
+ created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
15
+ updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
16
+ );
17
+
18
+ -- Messages table to store conversation history
19
+ CREATE TABLE IF NOT EXISTS messages (
20
+ id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
21
+ telegram_id BIGINT NOT NULL,
22
+ message_text TEXT NOT NULL,
23
+ message_type VARCHAR(20) NOT NULL CHECK (message_type IN ('user', 'assistant')),
24
+ created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
25
+ FOREIGN KEY (telegram_id) REFERENCES users(telegram_id) ON DELETE CASCADE
26
+ );
27
+
28
+ -- Conversation sessions to group messages by session
29
+ CREATE TABLE IF NOT EXISTS conversation_sessions (
30
+ id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
31
+ telegram_id BIGINT NOT NULL,
32
+ session_start TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
33
+ session_end TIMESTAMP WITH TIME ZONE,
34
+ message_count INTEGER DEFAULT 0,
35
+ created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
36
+ FOREIGN KEY (telegram_id) REFERENCES users(telegram_id) ON DELETE CASCADE
37
+ );
38
+
39
+ -- Indexes for better performance
40
+ CREATE INDEX IF NOT EXISTS idx_messages_telegram_id_created_at ON messages(telegram_id, created_at);
41
+ CREATE INDEX IF NOT EXISTS idx_users_telegram_id ON users(telegram_id);
42
+ CREATE INDEX IF NOT EXISTS idx_sessions_telegram_id ON conversation_sessions(telegram_id);
43
+
44
+ -- Function to update updated_at timestamp
45
+ CREATE OR REPLACE FUNCTION update_updated_at_column()
46
+ RETURNS TRIGGER AS $$
47
+ BEGIN
48
+ NEW.updated_at = NOW();
49
+ RETURN NEW;
50
+ END;
51
+ $$ language 'plpgsql';
52
+
53
+ -- Trigger to update user's updated_at timestamp
54
+ CREATE TRIGGER update_user_updated_at
55
+ BEFORE UPDATE ON users
56
+ FOR EACH ROW
57
+ EXECUTE FUNCTION update_updated_at_column();
58
+
59
+ -- Trigger to update session message count
60
+ CREATE OR REPLACE FUNCTION update_session_count()
61
+ RETURNS TRIGGER AS $$
62
+ BEGIN
63
+ UPDATE conversation_sessions
64
+ SET message_count = message_count + 1
65
+ WHERE telegram_id = NEW.telegram_id AND session_end IS NULL;
66
+ RETURN NEW;
67
+ END;
68
+ $$ language 'plpgsql';
69
+
70
+ CREATE TRIGGER update_session_message_count
71
+ AFTER INSERT ON messages
72
+ FOR EACH ROW
73
+ EXECUTE FUNCTION update_session_count();
74
+
75
+ -- Row Level Security (RLS) policies
76
+ ALTER TABLE users ENABLE ROW LEVEL SECURITY;
77
+ ALTER TABLE messages ENABLE ROW LEVEL SECURITY;
78
+ ALTER TABLE conversation_sessions ENABLE ROW LEVEL SECURITY;
79
+
80
+ -- Policy for users table (allow all operations for now)
81
+ CREATE POLICY "Enable all operations for users" ON users
82
+ FOR ALL USING (true);
83
+
84
+ -- Policy for messages table (allow all operations for now)
85
+ CREATE POLICY "Enable all operations for messages" ON messages
86
+ FOR ALL USING (true);
87
+
88
+ -- Policy for conversation_sessions table (allow all operations for now)
89
+ CREATE POLICY "Enable all operations for conversation_sessions" ON conversation_sessions
90
+ FOR ALL USING (true);
telegram_handlers.py ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import Request
2
+ import httpx
3
+ from config import TELEGRAM_URL
4
+ from ai_service import get_ai_response
5
+ from database import db_manager
6
+
7
+ async def telegram_webhook(request: Request):
8
+ data = await request.json()
9
+
10
+ if "message" in data:
11
+ telegram_id = data["message"]["chat"]["id"]
12
+ user_text = data["message"].get("text", "")
13
+
14
+ # Extract user info
15
+ user_info = data["message"]["chat"]
16
+ username = user_info.get("username")
17
+ first_name = user_info.get("first_name")
18
+ last_name = user_info.get("last_name")
19
+
20
+ if user_text:
21
+ # Save user message to database if available
22
+ if db_manager:
23
+ db_manager.save_message(telegram_id, user_text, "user")
24
+ db_manager.create_or_update_user(telegram_id, username, first_name, last_name)
25
+
26
+ # Get the intelligent response with conversation history
27
+ ai_answer = await get_ai_response(user_text, telegram_id)
28
+
29
+ # Save assistant response to database if available
30
+ if db_manager:
31
+ db_manager.save_message(telegram_id, ai_answer, "assistant")
32
+
33
+ # Send back to Telegram if TELEGRAM_URL is available
34
+ if TELEGRAM_URL:
35
+ async with httpx.AsyncClient(verify=False) as client:
36
+ await client.post(
37
+ TELEGRAM_URL,
38
+ headers={"Host": "api.telegram.org"},
39
+ json={
40
+ "chat_id": telegram_id,
41
+ "text": ai_answer,
42
+ "parse_mode": "Markdown"
43
+ }
44
+ )
45
+
46
+ return {"status": "ok"}
47
+
48
+ async def test_webhook(request: Request):
49
+ data = await request.json()
50
+ telegram_id = data["message"]["chat"]["id"]
51
+ user_text = data["message"]["text"]
52
+
53
+ # Save user message to database if available
54
+ if db_manager:
55
+ db_manager.save_message(telegram_id, user_text, "user")
56
+
57
+ # Get response with conversation history
58
+ response = await get_ai_response(user_text, telegram_id)
59
+
60
+ # Save assistant response to database if available
61
+ if db_manager:
62
+ db_manager.save_message(telegram_id, response, "assistant")
63
+
64
+ return {"response": response}
utils.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import socket
2
+
3
+ async def dns_test():
4
+ try:
5
+ ip = socket.gethostbyname("api.telegram.org")
6
+ return {"resolved_ip": ip}
7
+ except Exception as e:
8
+ return {"error": str(e)}
9
+
10
+ async def test_ai_response():
11
+ try:
12
+ from ai_service import get_ai_response
13
+ response = await get_ai_response("عن ماذا كنت اسال", 12)
14
+ return {"response": response}
15
+ except Exception as e:
16
+ return {"error": str(e)}