Spaces:
Running
Running
File size: 18,322 Bytes
23f1cab 5843ccb e6e9663 5843ccb 3f8b540 a00ca40 23f1cab 5843ccb 23f1cab 3f8b540 39370a3 5843ccb 8b56583 3f8b540 39370a3 7351225 6dba5aa 46b0f24 a00ca40 2f102ab 807cb08 e0f119f 8b56583 9a5cb45 3f8b540 39370a3 eb4c258 39370a3 cbe212d 6dba5aa b8c4bf7 eb4c258 39370a3 52e9be7 eb4c258 52e9be7 913593f 99590e3 6df4daa e0f119f bd759b7 e0f119f ef63667 e0f119f db8d990 fa0de40 52e9be7 8b56583 913593f fa0de40 2f102ab 8b56583 52e9be7 39370a3 52e9be7 39370a3 52e9be7 e51b856 f73a224 6b87288 e51b856 40859fe e51b856 6b87288 2f102ab 00dd968 6b87288 e51b856 3a715d2 00dd968 e51b856 6b87288 e51b856 6b87288 e51b856 6b87288 3a715d2 52e9be7 8b56583 6dba5aa 39370a3 8b56583 39370a3 6dba5aa 39370a3 c5911ea f73a224 39370a3 6dba5aa b7fc59e 6cdc714 39370a3 938cbdf 39370a3 6cdc714 39370a3 6cdc714 8b56583 6dba5aa 52e9be7 39370a3 6dba5aa 46b0f24 6dba5aa 4c54685 8b56583 4c54685 6cdc714 4c54685 52e9be7 d1a5593 2aea070 9a5cb45 9e62a69 9a5cb45 2aea070 9a5cb45 9e62a69 9a5cb45 cb20bb6 2aea070 9a5cb45 35d927a 2aea070 9a5cb45 cb20bb6 2aea070 9a5cb45 35d927a 9a5cb45 2aea070 034075e b454840 984fb6f 3a715d2 44189e1 403d081 3922857 1d0bf87 3922857 8b3a3d5 13ed972 9a5cb45 13ed972 4c54685 b8c4bf7 6b87288 9a5cb45 54756f4 938cbdf 4c54685 6cdc714 4c54685 6cdc714 4c54685 07956b9 23f1cab 07956b9 23f1cab 52e9be7 07956b9 23f1cab f24fc2c 23f1cab 39370a3 0fe5a37 6dba5aa 39370a3 8b58e08 0fe5a37 4c54685 8b58e08 4c54685 8b58e08 4c54685 8b58e08 39370a3 4c54685 8b58e08 4c54685 e51b856 8b58e08 ebae6f7 8b58e08 80ef227 8b58e08 d1a5593 8b58e08 4c54685 39370a3 8b56583 e56c61d 39370a3 8b56583 e56c61d 39370a3 8b58e08 39370a3 8b58e08 4c54685 39370a3 23f1cab 8b58e08 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 |
# Standard libraries
import os
import re
import time
import json
import asyncio
import requests
import numpy as np
import pandas as pd
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor, as_completed
from functools import lru_cache
from typing import Optional, List
# External libraries
import gradio as gr
from sqlalchemy import create_engine, inspect, text
import psycopg2
from groq import Groq
from dotenv import load_dotenv
#-----------------------
# Fetch variables
user = os.getenv("user")
password = os.getenv("password")
host = os.getenv("host")
port = os.getenv("port")
dbname = os.getenv("dbname")
GROQ_API_KEY = os.getenv("GROQ_API_KEY_PAID")
GROQ_API_KEY_PAID = os.getenv("GROQ_API_KEY_PAID")
# Define path for cache file in the current directory
CACHE_FILE = Path("/tmp/sql_prompt_cache1.json")
#-----------------------
# create db connection
engine = create_engine(f"postgresql+psycopg2://{user}:{password}@{host}:{port}/{dbname}")
# print(engine)
# -----------------------
# Groq API settings
GROQ_EMBED_URL = "https://api.groq.com/openai/v1/embeddings"
GROQ_CHAT_URL = "https://api.groq.com/openai/v1/chat/completions"
EMBEDDING_MODEL = "llama3-405b-8192-embed"
# LLM_MODEL = "llama-3.3-70b-versatile"
LLM_MODEL = "llama3-70b-8192"
cMODEL = "llama-3.1-8b-instant"
MODEL = "llama-3.3-70b-versatile"
#-----------------------
# Configure headers for Groq API requests
GROQ_HEADERS = {
"Authorization": f"Bearer {GROQ_API_KEY}",
"Content-Type": "application/json"}
#-----------------------
# # version 1 (tables)
# def get_db_schema(engine):
# inspector = inspect(engine)
# schema_dict = {}
# for table in inspector.get_table_names():
# columns = inspector.get_columns(table)
# column_names = [col['name'] for col in columns]
# schema_dict[table] = column_names
# return schema_dict
# schema = get_db_schema(engine)
schema = {
'rag_color_conversion_chart': ['brand_name','brand_color_code','brand_color','candle_shade_code','matching_type'],
'rag_order_detail': ['orderno','customername', 'phone', 'email', 'orderdate', 'producttype', 'colorcode', 'orderedqty',
'status','tracking_number','shippingaddress', 'shipping_type', 'estimateddeliverydate',
'delivery_date', 'total_amount'],
'rag_promo_code_data': ['promocode','username','firstname','lastname','companyname','phone','issuancedate','expirydate','user_id'],
'rag_color_conversion_charts_link': ['Conversion Chart Name', 'link'],
'rag_certificate_link': ['Certificates Name', 'link']
}
# print(schema)
# -----------------------
# # Bag of words from table
# def get_db_schema_words(engine):
# inspector = inspect(engine)
# words = []
# for table in inspector.get_table_names():
# words.extend(table.split("_")) # Split table names into words
# columns = inspector.get_columns(table)
# for col in columns:
# words.extend(col['name'].split("_")) # Split column names into words
# return list(set(words)) # Remove duplicates, if needed
# bag_of_words = get_db_schema_words(engine)
# bag_of_words = schema
bag_of_words = schema['rag_color_conversion_chart'],schema['rag_order_detail'],schema['rag_promo_code_data'],schema['rag_color_conversion_charts_link'],schema['rag_certificate_link']
# -----------------------
# Query formatter
def clean_sql_output(raw_content: str) -> str:
# Remove any markdown formatting and explanations
raw_content = raw_content.replace("```sql", "").replace("```", "").strip()
# Extract only the first valid SQL query using regex
queries = re.findall(r"(SELECT\s+.+?;)", raw_content,
flags=re.IGNORECASE | re.DOTALL)
return queries[0].strip() if queries else "-- Unable to extract valid SQL"
# -----------------------
def query_groq(
user_prompt: str,
schema: Optional[str] = None,
system_prompt: str = "You are a PostgreSQL expert. Generate the best possible SQL queries for FILTERING based on the user's question. **FILTERING** must be done using **IN** or **OR**. Also If user asks for colors and matches and provides color codes, query for **brand_code** or **brand_color_code** by default. SQL queries must be design for **returning all attributes**. Return only the correct SQL query. Note: attributes datatypes are **text**.",
model: str = LLM_MODEL,
temperature: float = 0.3,
# max_tokens: int = 8192
) -> str:
full_usr_prompt = f"User Question: {user_prompt}\n\nRefer to these target keywords for SQL Queries:{bag_of_words}" if bag_of_words else user_prompt # user
full_sys_prompt = f"{system_prompt}\n\nRefer to this Schema for SQL Queries:{schema}" if schema else system_prompt # system
response = requests.post(
GROQ_CHAT_URL,
headers=GROQ_HEADERS,
json={
"model": model,
"messages": [
{"role": "system", "content": full_sys_prompt},
{"role": "user", "content": full_usr_prompt}
],
"temperature": temperature,
# "max_tokens": max_tokens
}
)
if response.status_code != 200:
raise Exception(f"❌ Error querying Groq:\n{response.text}")
# Clean output: remove triple backticks
content = response.json()["choices"][0]["message"]["content"].strip()
return clean_sql_output(content)
# -----------------------
client = Groq(api_key=GROQ_API_KEY)
def correct_spelling_with_groq(text, context):
try:
chat_completion = client.chat.completions.create(
messages=[
# {"role": "system", "content": f"You are a helpful assistant that uses this context of words: {context}, to correct spellings in user queries, keeping their intent intact. Also if user asks for thread, he is refering to to find candle color code match. If user asks for colors search for brand colors"},
{"role": "system", "content": f"You are a helpful assistant that uses this context of words: {context}, to correct spellings in user queries, keeping their intent intact. Also if user asks for thread, he is refering to to find candle_shade_code match. Also If user asks for colors and matches and provides color codes, search for brand code or brand color code by default"},
{"role": "user", "content": f"Correct the following query: {text}"}
],
model=cMODEL, # or the correct model name like llama-3.3-70b-versatile
temperature=0.3,
max_completion_tokens=256,
top_p=1,
stream=False,
)
return chat_completion.choices[0].message.content.strip()
except Exception as e:
print("Groq correction failed:", e)
return text # fallback to original if Groq fails
# -----------------------
# summarize = Groq(api_key=GROQ_API_KEY)
summarize = Groq(api_key=GROQ_API_KEY_PAID)
def summarize_with_groq(text, context):
if not text.strip():
return "No content provided to summarize."
try:
response = summarize.chat.completions.create(
messages=[
{"role": "system", "content": f"""You are a helpful assistant that organizes and numerically lists and sub-lists closely related important text and shows and handles all unique variations and handles variations duplicates from the user inputs and its **context : {context}** in a professional manner and Always presents the output in a clean, professional format..
[specially When the input contains order-related information, follow this following **strict format** without changes or additions:
---
Order: 2677
Customer Name: Brenda Cole
Customer Email: brendacole66@gmail.com
Phone Number: 12316130502
Order Date: 2024-01-02
Shipping Address: Title: Default, Address: 2606 N Lakeshore Dr Suit/Apt # , Zip Code: 49431
Status: Delivered
Total Amount: 225.5
---
A. 3-5 Days Shipping:
- Tracking Link: https://wwwapps.ups.com/WebTracking/track?track=yes&trackNums=1ZB1F8280339024054
a. Material: Polyester
1. Color Code: 7834 - Qty: 1,
2. Color Code: 61060 - Qty: 5,
3. Color Code: 60300 - Qty: 7,
4. Color Code: 8717 - Qty: 4,
---
b. Material: Trial Order
1. Color Code: 7834 - Qty: 1,
2. Color Code: 6106 - Qty: 5,
---
B. 10-15 Days Shipping:
- Tracking Link: Not Available
a. Material: Polyester
1. Color Code: 07834 - Qty: 1,
2. Color Code: 61060 - Qty: 5,
3. Color Code: 6030 - Qty: 7,
4. Color Code: 8717 - Qty: 4,
---
b. Material: Trial Order
1. Color Code: 07834 - Qty: 1,
2. Color Code: 6106 - Qty: 5
---]
**Important Guidelines:**
- Use **only** the information provided by the user — **do not** assume or add any details.
- **Do not** omit or miss any information.
- Use **Given format** for order related queries, and use normal formats for other queries.
- Enter all remaining items without stopping until complete.
- **Do not** use tables.
- **Do not** display processing information.
- **Do not** display dictionary.
- Always present the output in a **clean**, **professional** and **Customer friendly** format.
"""},
# {"role": "user", "content": f"Please organize the following text and numerically lists and sub-lists closely related important text and show and handle all unique variations and handle duplicates variations professionally:\n\n{text} "}
{"role": "user", "content": f"{text}"}
],
model=MODEL,
temperature=0.25,
# max_completion_tokens=8192,
top_p=1,
stream=False,
)
return response.choices[0].message.content.strip()
except Exception as e:
print("Groq Summarization failed:", e)
return text
# -----------------------
def load_cache():
if os.path.exists(CACHE_FILE):
with open(CACHE_FILE, "r") as f:
return json.load(f)
return {}
def save_cache(cache: dict):
with open(CACHE_FILE, "w") as f:
json.dump(cache, f, indent=2)
# -----------------------
def try_sql_parallel_with_disk_cache(
prompt: str,
engine,
schema: Optional[str] = None,
max_prompts: int = 6,
max_retries: int = 2,
verbose: bool = False
):
SQL_CACHE = load_cache()
def generate_variants(base_prompt: str, error: str, count: int) -> List[str]:
return [
f"{base_prompt}\n\nTry variation #{i+1}.\nPrevious error:\n{error}" if error else f"{base_prompt}\n\nVariation #{i+1}"
for i in range(count)
]
def try_sql(sql: str):
try:
df = pd.read_sql(sql, engine)
return df if not df.empty else None
except Exception:
return None
attempts = 0
error_message = ""
last_sql = ""
# ✅ Check cache first
if prompt in SQL_CACHE:
if verbose:
print("[Disk cache hit] Using cached SQL.")
sql = SQL_CACHE[prompt]
df = try_sql(sql)
if df is not None:
return {"success": True, "dataframe": df}
while attempts < max_retries:
attempts += 1
prompt_variants = generate_variants(prompt, error_message, max_prompts)
with ThreadPoolExecutor(max_workers=max_prompts) as executor:
futures = {executor.submit(query_groq, p, schema=schema): p for p in prompt_variants}
new_sqls = []
for future in as_completed(futures):
try:
new_sql = future.result()
new_sqls.append((futures[future], new_sql))
except Exception:
continue
for p, sql in new_sqls:
last_sql = sql
if not sql or not sql.lower().strip().startswith("select"):
error_message = "-- Not a SELECT statement"
continue
df = try_sql(sql)
if df is not None:
# ✅ Cache only successful result
SQL_CACHE[prompt] = sql
save_cache(SQL_CACHE)
return {"success": True, "dataframe": df}
error_message = "-- Query failed or returned no rows"
return {"success": False, "error": error_message}
# -----------------------
with gr.Blocks() as interface:
gr.Markdown("<h1 style='text-align: center;'>🕯️ CANDLES A.I CHAT SUPPORT 🕯️</h1>")
gr.Markdown("<h3 style='text-align: center;'>Please type your query below. For better results, include your full name or username, order no, brand color or candle color, user ID.</h3>")
chat_history = gr.State([])
# Chatbot full-width, larger, scrollable, and copyable
chatbot = gr.Chatbot(label="Conversation", height=600, autoscroll=True, show_copy_button=True)
# Input and submit row
with gr.Row(equal_height=True):
user_input = gr.Textbox(
label="Your Question",
placeholder="e.g., John Smith, 1800, show matching candles etc.",
autofocus=True,
scale=4
)
submit_btn = gr.Button("Submit", variant="primary", scale=1)
# Undo and clear row
with gr.Row():
undo_btn = gr.Button("Undo Last")
clear_btn = gr.Button("Clear")
# Status feedback message
status_msg = gr.Markdown("")
# Main logic without SQL table
def handle_submit(user_input, history):
if not user_input.strip():
return gr.update(), history, "⚠️ Please enter a valid query."
# correct_user_input = correct_spelling_with_groq(user_input , f"{bag_of_words}")
# history.append(("🧑 User", user_input))
# sql_attempt = try_sql_parallel_with_disk_cache(user_input, engine, schema, max_prompts=7, max_retries=2)
sql_attempt = try_sql_parallel_with_disk_cache(user_input, engine, schema)
if sql_attempt["success"]:
# summary = summarize_with_groq(f"{correct_spelling_with_groq}\n\n{sql_attempt['dataframe'].to_dict()}" , f"{sql_attempt['dataframe'].columns.to_list()}")
summary = summarize_with_groq(f"{sql_attempt['dataframe'].to_dict()}" , f"{sql_attempt['dataframe'].columns.to_list()}")
answer = f"✅ Query executed successfully.\n\n{summary}"
status = "✅ Success"
else:
# summary = summarize_with_groq(f"User query: {user_input}", f"{correct_user_input}")
correct_user_input = correct_spelling_with_groq(user_input , f"{bag_of_words}")
answer = f"❌ Please try :\n\n{correct_user_input}"
status = "❌ Error: Invalid query or no matching data."
history.append(("🧑 User", user_input))
history.append(("🤖 Assistant", answer))
return "", history, status
# Button bindings
submit_btn.click(
handle_submit,
[user_input, chat_history],
[user_input, chat_history, status_msg]
).then(
lambda hist: hist,
[chat_history],
[chatbot]
)
user_input.submit(
handle_submit,
[user_input, chat_history],
[user_input, chat_history, status_msg]
).then(
lambda hist: hist,
[chat_history],
[chatbot]
)
undo_btn.click(
lambda history: history[:-2] if len(history) >= 2 else [],
[chat_history],
[chat_history]
).then(
lambda hist: hist,
[chat_history],
[chatbot]
)
clear_btn.click(
lambda: [],
None,
[chat_history]
).then(
lambda: ([], ""),
None,
[chatbot, status_msg]
)
interface.launch(ssr_mode=False,share=True)
# -----------------------
|