| import gradio as gr |
| import os |
| import json |
| import requests |
| import xml.etree.ElementTree as ET |
| import schedule |
| import time |
| import threading |
| from huggingface_hub import HfApi, create_repo, hf_hub_download |
| import warnings |
| import pandas as pd |
| from docx import Document |
| import spaces |
| from google.oauth2.service_account import Credentials |
| from googleapiclient.discovery import build |
| from googleapiclient.http import MediaIoBaseDownload |
| import io |
| import warnings |
| from requests.packages.urllib3.exceptions import InsecureRequestWarning |
| warnings.simplefilter('ignore', InsecureRequestWarning) |
|
|
| |
| from prompts import get_active_prompts |
|
|
| |
| from enhanced_features import ( |
| initialize_enhanced_features, process_image_message, |
| handle_comparison_request |
| ) |
| from image_renderer import extract_product_info_for_gallery, format_message_with_images |
|
|
| |
| from conversation_tracker import add_conversation |
|
|
| |
|
|
| |
| try: |
| from smart_warehouse_with_price import get_warehouse_stock_smart_with_price |
| get_warehouse_stock_smart = get_warehouse_stock_smart_with_price |
| except ImportError: |
| try: |
| from smart_warehouse import get_warehouse_stock_smart |
| except ImportError: |
| get_warehouse_stock_smart = None |
|
|
| def get_warehouse_stock(product_name): |
| """Use GPT intelligence to find warehouse stock""" |
| |
| if get_warehouse_stock_smart: |
| result = get_warehouse_stock_smart(product_name) |
| if result: |
| return result |
| |
| |
| return get_warehouse_stock_old(product_name) |
|
|
| |
| def get_warehouse_stock_old(product_name): |
| """Smart warehouse stock finder with general algorithm""" |
| try: |
| import re |
| import requests |
| |
| |
| xml_text = None |
| for attempt in range(3): |
| try: |
| url = 'https://video.trek-turkey.com/bizimhesap-warehouse-xml-b2b-api-v2.php' |
| timeout_val = 10 + (attempt * 5) |
| response = requests.get(url, verify=False, timeout=timeout_val) |
| xml_text = response.text |
| break |
| except requests.exceptions.Timeout: |
| if attempt == 2: |
| return None |
| except Exception: |
| return None |
| |
| |
| def normalize(text): |
| tr_map = {'ı': 'i', 'ğ': 'g', 'ü': 'u', 'ş': 's', 'ö': 'o', 'ç': 'c', 'İ': 'i', 'I': 'i'} |
| text = text.lower() |
| for tr, en in tr_map.items(): |
| text = text.replace(tr, en) |
| return text |
| |
| |
| query = normalize(product_name.strip()).replace('(2026)', '').replace('(2025)', '').strip() |
| words = query.split() |
| |
| |
| sizes = ['s', 'm', 'l', 'xl', 'xs', 'xxl', 'ml'] |
| size = next((w for w in words if w in sizes), None) |
| |
| |
| product_words = [] |
| |
| |
| if len(words) <= 2 and not any(w.isdigit() for w in words): |
| |
| pass |
| else: |
| |
| for word in words: |
| |
| if word in sizes: |
| continue |
| |
| |
| if word.isdigit(): |
| product_words.append(word) |
| |
| |
| elif any(c.isdigit() for c in word) and any(c.isalpha() for c in word): |
| product_words.append(word) |
| |
| |
| elif len(word) in [2, 3] and word.isalpha(): |
| |
| if word in ['mi', 'mı', 'mu', 'mü', 'var', 'yok', 've', 'de', 'da']: |
| continue |
| |
| if any(c not in 'aeiouı' for c in word): |
| product_words.append(word) |
| |
| |
| elif len(word) > 3: |
| |
| if any(word.endswith(suffix) for suffix in ['mi', 'mı', 'mu', 'mü']): |
| continue |
| |
| consonants = sum(1 for c in word if c not in 'aeiouı') |
| if consonants <= 2: |
| continue |
| |
| product_words.append(word) |
| |
| print(f"DEBUG - Searching: {' '.join(product_words)}, Size: {size}") |
| |
| |
| product_pattern = r'<Product>(.*?)</Product>' |
| all_products = re.findall(product_pattern, xml_text, re.DOTALL) |
| |
| print(f"DEBUG - Total products in XML: {len(all_products)}") |
| |
| |
| best_match = None |
| |
| for product_block in all_products: |
| |
| name_match = re.search(r'<ProductName><!\[CDATA\[(.*?)\]\]></ProductName>', product_block) |
| if not name_match: |
| continue |
| |
| product_name_in_xml = name_match.group(1) |
| normalized_xml_name = normalize(product_name_in_xml) |
| |
| |
| |
| match = True |
| for word in product_words: |
| |
| if word not in normalized_xml_name: |
| |
| if not (word.isdigit() and any(f"{prev}{word}" in normalized_xml_name or f"{prev} {word}" in normalized_xml_name for prev in product_words if not prev.isdigit())): |
| match = False |
| break |
| |
| if match: |
| |
| |
| if size: |
| |
| variant_match = re.search(r'<ProductVariant><!\[CDATA\[(.*?)\]\]></ProductVariant>', product_block) |
| if variant_match: |
| variant = variant_match.group(1) |
| |
| if variant.upper().startswith(f'{size.upper()}-'): |
| print(f"DEBUG - Found match: {product_name_in_xml} - {variant}") |
| best_match = product_block |
| break |
| else: |
| |
| best_match = product_block |
| break |
| |
| if best_match: |
| |
| warehouse_info = [] |
| warehouse_regex = r'<Warehouse>.*?<Name><!\[CDATA\[(.*?)\]\]></Name>.*?<Stock>(.*?)</Stock>.*?</Warehouse>' |
| warehouses = re.findall(warehouse_regex, best_match, re.DOTALL) |
| |
| for wh_name, wh_stock in warehouses: |
| try: |
| stock = int(wh_stock.strip()) |
| if stock > 0: |
| |
| if "CADDEBOSTAN" in wh_name: |
| display = "Caddebostan mağazası" |
| elif "ORTAKÖY" in wh_name: |
| display = "Ortaköy mağazası" |
| elif "ALSANCAK" in wh_name: |
| display = "İzmir Alsancak mağazası" |
| elif "BAHCEKOY" in wh_name or "BAHÇEKÖY" in wh_name: |
| display = "Bahçeköy mağazası" |
| else: |
| display = wh_name |
| |
| warehouse_info.append(f"{display}: Mevcut") |
| except: |
| pass |
| |
| return warehouse_info if warehouse_info else ["Hiçbir mağazada mevcut değil"] |
| else: |
| print(f"DEBUG - No match found for {' '.join(product_words)}") |
| return ["Hiçbir mağazada mevcut değil"] |
| |
| except Exception as e: |
| print(f"Warehouse error: {e}") |
| return None |
|
|
| |
| def get_warehouse_stock_old_slow(product_name): |
| """B2B API'den mağaza stok bilgilerini çek - Optimize edilmiş versiyon""" |
| try: |
| import re |
| |
| |
| use_signal = False |
| try: |
| import signal |
| import threading |
| |
| if threading.current_thread() is threading.main_thread(): |
| def timeout_handler(signum, frame): |
| raise TimeoutError("Warehouse API timeout") |
| signal.signal(signal.SIGALRM, timeout_handler) |
| signal.alarm(8) |
| use_signal = True |
| except Exception as e: |
| print(f"Signal not available: {e}") |
| use_signal = False |
| |
| try: |
| warehouse_url = 'https://video.trek-turkey.com/bizimhesap-warehouse-xml-b2b-api-v2.php' |
| response = requests.get(warehouse_url, verify=False, timeout=7) |
| |
| if response.status_code != 200: |
| return None |
| |
| |
| xml_text = response.text |
| |
| |
| turkish_map = {'ı': 'i', 'ğ': 'g', 'ü': 'u', 'ş': 's', 'ö': 'o', 'ç': 'c', 'İ': 'i', 'I': 'i'} |
| |
| def normalize_turkish(text): |
| import unicodedata |
| |
| text = unicodedata.normalize('NFKD', text) |
| text = ''.join(char for char in text if unicodedata.category(char) != 'Mn') |
| |
| for tr_char, en_char in turkish_map.items(): |
| text = text.replace(tr_char, en_char) |
| |
| text = text.replace('İ', 'i').replace('I', 'i') |
| return text.lower() |
| |
| |
| search_name = normalize_turkish(product_name.strip()) |
| |
| search_name = search_name.replace('(2026)', '').replace('(2025)', '').strip() |
| search_words = search_name.split() |
| |
| print(f"DEBUG - Searching for: {product_name}") |
| print(f"DEBUG - Normalized (gen kept): {search_name}") |
| |
| |
| |
| product_index = {} |
| |
| |
| for product in root.findall('Product'): |
| name_elem = product.find('ProductName') |
| if name_elem is not None and name_elem.text: |
| product_name_key = normalize_turkish(name_elem.text.strip()) |
| |
| |
| if product_name_key not in product_index: |
| product_index[product_name_key] = [] |
| |
| variant_elem = product.find('ProductVariant') |
| variant_text = "" |
| if variant_elem is not None and variant_elem.text: |
| variant_text = normalize_turkish(variant_elem.text.strip()) |
| |
| product_index[product_name_key].append({ |
| 'element': product, |
| 'original_name': name_elem.text.strip(), |
| 'variant': variant_text |
| }) |
| |
| |
| size_color_words = ['s', 'm', 'l', 'xl', 'xs', 'xxl', 'ml'] |
| size_indicators = ['beden', 'size', 'boy'] |
| |
| variant_words = [word for word in search_words if word in size_color_words] |
| product_words = [word for word in search_words if word not in size_color_words and word not in size_indicators] |
| |
| |
| product_key = ' '.join(product_words) |
| |
| print(f"DEBUG - Looking for key: {product_key}") |
| print(f"DEBUG - Variant filter: {variant_words}") |
| |
| |
| candidates = [] |
| |
| |
| if product_key in product_index: |
| print(f"DEBUG - Exact match found!") |
| for item in product_index[product_key]: |
| |
| if variant_words: |
| |
| if len(variant_words) == 1 and len(variant_words[0]) <= 2: |
| if item['variant'].startswith(variant_words[0] + '-') or item['variant'].startswith(variant_words[0] + ' '): |
| candidates.append((item['element'], item['original_name'], item['variant'])) |
| else: |
| if all(word in item['variant'] for word in variant_words): |
| candidates.append((item['element'], item['original_name'], item['variant'])) |
| else: |
| |
| candidates.append((item['element'], item['original_name'], item['variant'])) |
| else: |
| |
| print(f"DEBUG - No exact match, searching partial matches...") |
| for key, items in product_index.items(): |
| if all(word in key for word in product_words): |
| for item in items: |
| if variant_words: |
| if len(variant_words) == 1 and len(variant_words[0]) <= 2: |
| if item['variant'].startswith(variant_words[0] + '-'): |
| candidates.append((item['element'], item['original_name'], item['variant'])) |
| else: |
| candidates.append((item['element'], item['original_name'], item['variant'])) |
| |
| if len(candidates) >= 5: |
| break |
| |
| print(f"DEBUG - Found {len(candidates)} candidates instantly!") |
| |
| |
| warehouse_stock_map = {} |
| |
| for product, xml_name, variant in candidates: |
| |
| for warehouse in product.findall('Warehouse'): |
| name_elem = warehouse.find('Name') |
| stock_elem = warehouse.find('Stock') |
| |
| if name_elem is not None and stock_elem is not None: |
| warehouse_name = name_elem.text if name_elem.text else "Bilinmeyen" |
| try: |
| stock_count = int(stock_elem.text) if stock_elem.text else 0 |
| if stock_count > 0: |
| if warehouse_name in warehouse_stock_map: |
| warehouse_stock_map[warehouse_name] += stock_count |
| else: |
| warehouse_stock_map[warehouse_name] = stock_count |
| except (ValueError, TypeError): |
| pass |
| |
| |
| signal.alarm(0) |
| |
| print(f"DEBUG - Found {len(candidates)} candidates") |
| if candidates: |
| for product, name, variant in candidates[:3]: |
| print(f"DEBUG - Candidate: {name} - {variant}") |
| |
| if warehouse_stock_map: |
| all_warehouse_info = [] |
| for warehouse_name, total_stock in warehouse_stock_map.items(): |
| |
| if "Caddebostan" in warehouse_name: |
| display_name = "Caddebostan mağazası" |
| elif "Ortaköy" in warehouse_name: |
| display_name = "Ortaköy mağazası" |
| elif "Sarıyer" in warehouse_name: |
| display_name = "Sarıyer mağazası" |
| elif "Alsancak" in warehouse_name or "İzmir" in warehouse_name: |
| display_name = "İzmir Alsancak mağazası" |
| else: |
| display_name = warehouse_name |
| |
| all_warehouse_info.append(f"{display_name}: Mevcut") |
| return all_warehouse_info |
| else: |
| return ["Hiçbir mağazada mevcut değil"] |
| |
| except TimeoutError: |
| if use_signal: |
| try: |
| signal.alarm(0) |
| except: |
| pass |
| print("Warehouse API timeout - skipping") |
| return None |
| finally: |
| if use_signal: |
| try: |
| signal.alarm(0) |
| except: |
| pass |
| |
| except Exception as e: |
| print(f"Mağaza stok bilgisi çekme hatası: {e}") |
| return None |
|
|
| |
| try: |
| from improved_chatbot import ImprovedChatbot |
| USE_IMPROVED_SEARCH = True |
| print("DEBUG - Improved chatbot loaded successfully") |
| except ImportError as e: |
| print(f"DEBUG - Improved chatbot not available: {e}, using basic search") |
| USE_IMPROVED_SEARCH = False |
|
|
| |
| warnings.filterwarnings("ignore", category=UserWarning, module="gradio.components.chatbot") |
|
|
| |
| LOG_FILE = '/data/chat_logs.txt' if os.path.exists('/data') else 'chat_logs.txt' |
| print(f"Dosya yolu: {os.path.abspath(LOG_FILE)}") |
|
|
| |
| API_URL = "https://api.openai.com/v1/chat/completions" |
| OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") |
| if not OPENAI_API_KEY: |
| print("Hata: OPENAI_API_KEY çevre değişkeni ayarlanmamış!") |
|
|
| |
| url = 'https://www.trekbisiklet.com.tr/output/8582384479' |
| response = requests.get(url, verify=False, timeout=30) |
| if response.status_code == 200 and response.content: |
| root = ET.fromstring(response.content) |
| else: |
| print(f"HTTP hatası: {response.status_code}") |
| root = None |
|
|
| products = [] |
| if root is not None: |
| for item in root.findall('item'): |
| |
| rootlabel_elem = item.find('rootlabel') |
| stock_elem = item.find('stockAmount') |
| |
| if rootlabel_elem is None or stock_elem is None: |
| continue |
| |
| name_words = rootlabel_elem.text.lower().split() |
| name = name_words[0] |
| full_name = ' '.join(name_words) |
| |
| stock_amount = "stokta" if stock_elem.text and stock_elem.text > '0' else "stokta değil" |
| |
| |
| if stock_amount == "stokta": |
| |
| price_elem = item.find('priceTaxWithCur') |
| price_str = price_elem.text if price_elem is not None and price_elem.text else "Fiyat bilgisi yok" |
| |
| |
| price_eft_elem = item.find('priceEft') |
| price_eft_str = price_eft_elem.text if price_eft_elem is not None and price_eft_elem.text else "" |
| |
| |
| price_rebate_elem = item.find('priceRebateWithTax') |
| price_rebate_str = price_rebate_elem.text if price_rebate_elem is not None and price_rebate_elem.text else "" |
| |
| |
| price_rebate_money_order_elem = item.find('priceRebateWithMoneyOrderWithTax') |
| price_rebate_money_order_str = price_rebate_money_order_elem.text if price_rebate_money_order_elem is not None and price_rebate_money_order_elem.text else "" |
| |
| |
| try: |
| price_float = float(price_str) |
| |
| if price_float > 200000: |
| price = str(round(price_float / 5000) * 5000) |
| |
| elif price_float > 30000: |
| price = str(round(price_float / 1000) * 1000) |
| |
| elif price_float > 10000: |
| price = str(round(price_float / 100) * 100) |
| |
| else: |
| price = str(round(price_float / 10) * 10) |
| except (ValueError, TypeError): |
| price = price_str |
| |
| |
| if price_eft_str: |
| try: |
| price_eft_float = float(price_eft_str) |
| |
| if price_eft_float > 200000: |
| price_eft = str(round(price_eft_float / 5000) * 5000) |
| |
| elif price_eft_float > 30000: |
| price_eft = str(round(price_eft_float / 1000) * 1000) |
| |
| elif price_eft_float > 10000: |
| price_eft = str(round(price_eft_float / 100) * 100) |
| |
| else: |
| price_eft = str(round(price_eft_float / 10) * 10) |
| except (ValueError, TypeError): |
| price_eft = price_eft_str |
| else: |
| price_eft = "" |
| |
| |
| if price_rebate_str: |
| try: |
| price_rebate_float = float(price_rebate_str) |
| |
| if price_rebate_float > 200000: |
| price_rebate = str(round(price_rebate_float / 5000) * 5000) |
| |
| elif price_rebate_float > 30000: |
| price_rebate = str(round(price_rebate_float / 1000) * 1000) |
| |
| elif price_rebate_float > 10000: |
| price_rebate = str(round(price_rebate_float / 100) * 100) |
| |
| else: |
| price_rebate = str(round(price_rebate_float / 10) * 10) |
| except (ValueError, TypeError): |
| price_rebate = price_rebate_str |
| else: |
| price_rebate = "" |
| |
| |
| if price_rebate_money_order_str: |
| try: |
| price_rebate_money_order_float = float(price_rebate_money_order_str) |
| |
| if price_rebate_money_order_float > 200000: |
| price_rebate_money_order = str(round(price_rebate_money_order_float / 5000) * 5000) |
| |
| elif price_rebate_money_order_float > 30000: |
| price_rebate_money_order = str(round(price_rebate_money_order_float / 1000) * 1000) |
| |
| elif price_rebate_money_order_float > 10000: |
| price_rebate_money_order = str(round(price_rebate_money_order_float / 100) * 100) |
| |
| else: |
| price_rebate_money_order = str(round(price_rebate_money_order_float / 10) * 10) |
| except (ValueError, TypeError): |
| price_rebate_money_order = price_rebate_money_order_str |
| else: |
| price_rebate_money_order = "" |
| |
| |
| product_url_elem = item.find('productLink') |
| product_url = product_url_elem.text if product_url_elem is not None and product_url_elem.text else "" |
| product_info = [stock_amount, price, product_url, price_eft, price_rebate, price_rebate_money_order] |
| |
| |
| image_elem = item.find('picture1Path') |
| if image_elem is not None and image_elem.text: |
| product_info.append(image_elem.text) |
| else: |
| product_info.append("") |
| |
| else: |
| |
| product_info = [stock_amount] |
| |
| products.append((name, product_info, full_name)) |
|
|
| print(f"Toplam {len(products)} ürün yüklendi.") |
|
|
| |
| initialize_enhanced_features(OPENAI_API_KEY, products) |
|
|
| |
| improved_bot = None |
| if USE_IMPROVED_SEARCH: |
| try: |
| improved_bot = ImprovedChatbot(products) |
| print("Improved product search initialized successfully") |
| except Exception as e: |
| print(f"Failed to initialize improved search: {e}") |
| USE_IMPROVED_SEARCH = False |
|
|
| |
| GOOGLE_CREDENTIALS_PATH = os.getenv("GOOGLE_CREDENTIALS_PATH") |
| GOOGLE_FOLDER_ID = "1bE8aMj8-eFGftjMPOF8bKQJAhfHa0BN8" |
|
|
| |
| file_lock = threading.Lock() |
| history_lock = threading.Lock() |
| global_chat_history = [] |
| document_content = "" |
|
|
| |
| def download_documents_from_drive(): |
| global document_content |
| |
| if not GOOGLE_CREDENTIALS_PATH: |
| print("Google credentials dosyası bulunamadı.") |
| return |
| |
| try: |
| credentials = Credentials.from_service_account_file(GOOGLE_CREDENTIALS_PATH) |
| service = build('drive', 'v3', credentials=credentials) |
| |
| |
| results = service.files().list( |
| q=f"'{GOOGLE_FOLDER_ID}' in parents", |
| fields="files(id, name, mimeType)" |
| ).execute() |
| |
| files = results.get('files', []) |
| all_content = [] |
| |
| for file in files: |
| print(f"İndiriliyor: {file['name']}") |
| |
| |
| if file['mimeType'] == 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': |
| request = service.files().get_media(fileId=file['id']) |
| file_io = io.BytesIO() |
| downloader = MediaIoBaseDownload(file_io, request) |
| |
| done = False |
| while done is False: |
| status, done = downloader.next_chunk() |
| |
| file_io.seek(0) |
| doc = Document(file_io) |
| |
| content = f"\\n=== {file['name']} ===\\n" |
| for paragraph in doc.paragraphs: |
| if paragraph.text.strip(): |
| content += paragraph.text + "\\n" |
| |
| all_content.append(content) |
| |
| document_content = "\\n".join(all_content) |
| print(f"Toplam {len(files)} döküman yüklendi.") |
| |
| except Exception as e: |
| print(f"Google Drive'dan döküman indirme hatası: {e}") |
|
|
| |
| document_thread = threading.Thread(target=download_documents_from_drive, daemon=True) |
| document_thread.start() |
|
|
| |
| def clear_log_file(): |
| try: |
| if os.path.exists(LOG_FILE): |
| with file_lock: |
| with open(LOG_FILE, 'w', encoding='utf-8') as f: |
| f.write("Log dosyası temizlendi.\\n") |
| print("Log dosyası temizlendi.") |
| except Exception as e: |
| print(f"Log dosyası temizleme hatası: {e}") |
|
|
| |
| def run_scheduler(chat_history): |
| schedule.every().day.at("03:00").do(clear_log_file) |
| |
| while True: |
| schedule.run_pending() |
| time.sleep(60) |
|
|
| |
| def chatbot_fn(user_message, history, image=None): |
| if history is None: |
| history = [] |
| |
| |
| warehouse_stock_data = None |
| print(f"DEBUG - Getting warehouse stock FIRST for: {user_message}") |
| try: |
| warehouse_stock_data = get_warehouse_stock(user_message) |
| if warehouse_stock_data: |
| print(f"DEBUG - Warehouse stock found: {warehouse_stock_data[:2]}...") |
| else: |
| print(f"DEBUG - No warehouse stock data returned") |
| except Exception as e: |
| print(f"DEBUG - Warehouse stock error at start: {e}") |
|
|
| try: |
| |
| if image is not None: |
| user_message = process_image_message(image, user_message) |
| |
| |
| comparison_result = handle_comparison_request(user_message) |
| if comparison_result: |
| yield comparison_result |
| return |
| |
| |
| |
|
|
| except Exception as e: |
| print(f"Enhanced features error: {e}") |
| |
|
|
| |
| try: |
| with file_lock: |
| with open(LOG_FILE, 'a', encoding='utf-8') as f: |
| f.write(f"User: {user_message}\\n") |
| except Exception as e: |
| print(f"Dosya yazma hatası (Kullanıcı): {e}") |
|
|
| |
| system_messages = get_active_prompts() |
|
|
| |
| if document_content: |
| system_messages.append({"role": "system", "content": f"Dökümanlardan gelen bilgiler: {document_content}"}) |
| |
| |
| |
| |
| |
| product_found_improved = False |
| |
| |
| if not product_found_improved: |
| print(f"DEBUG chatbot_fn - Using warehouse stock data for: {user_message}") |
| |
| |
| if warehouse_stock_data and warehouse_stock_data != ["Hiçbir mağazada mevcut değil"]: |
| warehouse_info = f"🏪 MAĞAZA STOK BİLGİLERİ:\n" |
| for store_info in warehouse_stock_data: |
| warehouse_info += f"• {store_info}\n" |
| system_messages.append({ |
| "role": "system", |
| "content": f"KRITIK - GERCEK STOK VERISI (XML VERITABANINDAN):\n{warehouse_info}\n\nCOK ONEMLI: Bu veri gercek veritabanindan gelmektedir ve KESINLIKLE DOGRUDUR. Kullanicinin sordugu urun STOKTA MEVCUTTUR. ASLA 'boyle bir model yok', 'bulunamadi' veya 'Trek gaminda bulunmuyor' DEME. Yukardaki stok ve fiyat bilgisini kullanarak dogru cevap ver." |
| }) |
| print(f"DEBUG - Using warehouse stock data") |
| elif warehouse_stock_data == ["Hiçbir mağazada mevcut değil"]: |
| system_messages.append({ |
| "role": "system", |
| "content": "🏪 MAĞAZA STOK BİLGİLERİ: Sorduğunuz ürün hiçbir mağazada mevcut değil." |
| }) |
| print(f"DEBUG - Product not available in any store") |
| else: |
| print(f"DEBUG - No warehouse stock data available") |
|
|
| messages = system_messages + history + [{"role": "user", "content": user_message}] |
|
|
| payload = { |
| "model": "gpt-5.3-chat-latest", |
| "messages": messages, |
| "stream": True, |
| "top_p": 1, |
| "presence_penalty": 0, |
| "frequency_penalty": 0, |
| "n": 1, |
| } |
|
|
| headers = { |
| "Content-Type": "application/json", |
| "Authorization": f"Bearer {OPENAI_API_KEY}" |
| } |
|
|
| response = requests.post(API_URL, headers=headers, json=payload, stream=True) |
|
|
| |
| if response.status_code != 200: |
| try: |
| print("\n🔴 OPENAI API ERROR") |
| print("Status Code:", response.status_code) |
| print("Response Body:", response.text, "\n") |
| except Exception as e: |
| print("Error printing OpenAI error:", e) |
|
|
| yield f"API hatası: {response.status_code}\n{response.text}" |
| return |
|
|
| |
| partial_response = "" |
|
|
|
|
|
|
| partial_response = "" |
| |
| for chunk in response.iter_lines(): |
| if not chunk: |
| continue |
| chunk_str = chunk.decode('utf-8') |
| if chunk_str.startswith("data: ") and chunk_str != "data: [DONE]": |
| try: |
| chunk_data = json.loads(chunk_str[6:]) |
| delta = chunk_data['choices'][0]['delta'] |
| if 'content' in delta: |
| partial_response += delta['content'] |
| |
| formatted_response = extract_product_info_for_gallery(partial_response) |
| yield formatted_response |
| except json.JSONDecodeError as e: |
| print(f"JSON parse hatası: {e} - Chunk: {chunk_str}") |
| elif chunk_str == "data: [DONE]": |
| break |
| |
| |
| final_response = extract_product_info_for_gallery(partial_response) |
| yield final_response |
|
|
| |
|
|
| |
| try: |
| with file_lock: |
| with open(LOG_FILE, 'a', encoding='utf-8') as f: |
| f.write(f"Bot: {partial_response}\\n") |
| except Exception as e: |
| print(f"Dosya yazma hatası (Bot): {e}") |
|
|
| |
| with history_lock: |
| global_chat_history.append({"role": "user", "content": user_message}) |
| global_chat_history.append({"role": "assistant", "content": partial_response}) |
|
|
| |
| def slow_echo(message, history): |
| for i in range(len(message)): |
| time.sleep(0.05) |
| yield "You typed: " + message[: i + 1] |
|
|
| |
| USE_SLOW_ECHO = False |
| chat_fn = slow_echo if USE_SLOW_ECHO else chatbot_fn |
|
|
| if not USE_SLOW_ECHO: |
| scheduler_thread = threading.Thread(target=run_scheduler, args=(global_chat_history,), daemon=True) |
| scheduler_thread.start() |
| |
| |
| trek_theme = gr.themes.Base( |
| primary_hue="red", |
| secondary_hue="slate", |
| neutral_hue="slate", |
| radius_size=gr.themes.sizes.radius_sm, |
| spacing_size=gr.themes.sizes.spacing_md, |
| text_size=gr.themes.sizes.text_sm |
| ) |
| |
| custom_css = """ |
| /* Genel font ayarları */ |
| .gradio-container, .gradio-container *, .message-wrap, .message-wrap p, .message-wrap div, |
| button, input, select, textarea { |
| font-family: 'Segoe UI', 'SF Pro Text', 'Roboto', -apple-system, BlinkMacSystemFont, |
| 'Helvetica Neue', Arial, sans-serif !important; |
| font-size: 0.6rem !important; |
| } |
| |
| /* Mobil responsive başlangıç */ |
| @media (max-width: 768px) { |
| .gradio-container, .gradio-container *, .message-wrap, .message-wrap p, .message-wrap div { |
| font-size: 0.8rem !important; |
| } |
| |
| h1 { |
| font-size: 1.8rem !important; |
| text-align: center; |
| margin: 10px 0; |
| } |
| |
| h2 { |
| font-size: 1.4rem !important; |
| } |
| } |
| |
| /* Input alanı için de aynı boyut */ |
| .message-textbox textarea { |
| font-size: 0.6rem !important; |
| } |
| |
| /* Başlıklar için özel boyutlar */ |
| h1 { |
| font-size: 1.4rem !important; |
| font-weight: 800 !important; |
| } |
| |
| h2 { |
| font-size: 1.2rem !important; |
| font-weight: 600 !important; |
| } |
| |
| h3 { |
| font-size: 1rem !important; |
| font-weight: 600 !important; |
| } |
| |
| /* Kart arka plan renkleri - görseldeki gibi */ |
| /* Kullanıcı mesajları için mavi tonda arka plan */ |
| .user-message, .user-message-highlighted { |
| background-color: #e9f5fe !important; |
| border-bottom-right-radius: 0 !important; |
| box-shadow: 0 1px 2px rgba(0,0,0,0.1) !important; |
| } |
| |
| /* Bot mesajları için beyaz arka plan ve hafif kenarlık */ |
| .bot-message, .bot-message-highlighted { |
| background-color: white !important; |
| border: 1px solid #e0e0e0 !important; |
| border-bottom-left-radius: 0 !important; |
| box-shadow: 0 1px 2px rgba(0,0,0,0.05) !important; |
| } |
| |
| /* Mesaj baloncuklarının köşe yuvarlatma değerleri */ |
| .message-wrap { |
| border-radius: 12px !important; |
| margin: 0.5rem 0 !important; |
| max-width: 90% !important; |
| } |
| |
| /* Sohbet alanının genel arka planı */ |
| .chat-container, .gradio-container { |
| background-color: #f7f7f7 !important; |
| } |
| |
| /* Daha net yazılar için text rendering */ |
| * { |
| -webkit-font-smoothing: antialiased; |
| -moz-osx-font-smoothing: grayscale; |
| text-rendering: optimizeLegibility; |
| } |
| |
| /* Restore butonu stilleri kaldırıldı */ |
| |
| /* Responsive mobil tasarım - iOS benzeri */ |
| @media (max-width: 768px) { |
| /* Daha büyük ve dokunmatik dostu boyutlar */ |
| .gradio-container { |
| padding: 0 !important; |
| margin: 0 !important; |
| } |
| |
| /* Mesaj baloncukları iOS tarzı */ |
| .message-wrap { |
| margin: 0.3rem 0.5rem !important; |
| max-width: 85% !important; |
| border-radius: 18px !important; |
| padding: 10px 15px !important; |
| font-size: 0.9rem !important; |
| } |
| |
| /* Kullanıcı mesajları */ |
| .user-message, .user-message-highlighted { |
| background-color: #007AFF !important; |
| color: white !important; |
| margin-left: auto !important; |
| margin-right: 8px !important; |
| } |
| |
| /* Bot mesajları */ |
| .bot-message, .bot-message-highlighted { |
| background-color: #f1f1f1 !important; |
| color: #333 !important; |
| margin-left: 8px !important; |
| margin-right: auto !important; |
| } |
| } |
| |
| /* Input alanına uçan kağıt ikonu ekle */ |
| #msg-input { |
| position: relative; |
| } |
| |
| #msg-input textarea { |
| padding-right: 40px !important; |
| } |
| |
| .input-icon { |
| position: absolute; |
| right: 12px; |
| top: 50%; |
| transform: translateY(-50%); |
| font-size: 16px; |
| pointer-events: none; |
| z-index: 10; |
| color: #666; |
| } |
| |
| /* Mobil responsive - Input alanı */ |
| @media (max-width: 768px) { |
| #msg-input { |
| margin: 10px 0; |
| } |
| |
| #msg-input textarea { |
| padding: 12px 45px 12px 15px !important; |
| font-size: 1rem !important; |
| border-radius: 20px !important; |
| border: 1px solid #ddd !important; |
| box-shadow: 0 1px 3px rgba(0,0,0,0.1) !important; |
| } |
| |
| .input-icon { |
| right: 15px; |
| font-size: 18px; |
| } |
| } |
| |
| /* Genel mobil iyileştirmeler */ |
| @media (max-width: 768px) { |
| /* Chatbot alanı tam ekran */ |
| .gradio-container .main { |
| padding: 0 !important; |
| } |
| |
| /* Başlık alanı küçült */ |
| .gradio-container header { |
| padding: 8px !important; |
| } |
| |
| /* Tab alanlarını küçült */ |
| .tab-nav { |
| padding: 5px !important; |
| } |
| |
| /* Scroll bar'ı gizle */ |
| .scroll-hide { |
| scrollbar-width: none; |
| -ms-overflow-style: none; |
| } |
| |
| .scroll-hide::-webkit-scrollbar { |
| display: none; |
| } |
| } |
| |
| /* CSS Bitişi */ |
| """ |
|
|
| |
| storage_js = "" |
|
|
| |
| def enhanced_chatbot_fn(message, history, image): |
| return chatbot_fn(message, history, image) |
|
|
| |
|
|
| |
| def get_conversations_api(): |
| """Get all conversations as JSON for dashboard""" |
| try: |
| from conversation_tracker import load_conversations |
| conversations = load_conversations() |
| return conversations |
| except Exception as e: |
| return [{"error": str(e)}] |
|
|
| with gr.Blocks(css=custom_css, theme="soft", title="Trek Asistanı", head=storage_js) as demo: |
| gr.Markdown("# 🚲 Trek Asistanı AI") |
| gr.Markdown("**Akıllı özellikler:** Ürün karşılaştırması ve detaylı ürün bilgileri sunuyorum.") |
| |
| |
| |
| chatbot = gr.Chatbot(height=600, elem_id="chatbot", show_label=False, type="messages") |
| |
| msg = gr.Textbox( |
| placeholder="Trek bisikletleri hakkında soru sorun...", |
| show_label=False, |
| elem_id="msg-input" |
| ) |
| |
| |
| |
| |
| def respond(message, chat_history): |
| if not message.strip(): |
| return "", chat_history |
| |
| |
| if chat_history is None: |
| chat_history = [] |
| |
| |
| chat_history.append({"role": "user", "content": message}) |
| yield "", chat_history |
| |
| |
| formatted_history = [] |
| for msg in chat_history[:-1]: |
| formatted_history.append(msg) |
| |
| try: |
| |
| response_generator = chatbot_fn(message, formatted_history, None) |
| |
| |
| response = "" |
| for partial in response_generator: |
| response = partial |
| |
| |
| if chat_history[-1]["role"] == "user": |
| chat_history.append({"role": "assistant", "content": response}) |
| else: |
| |
| chat_history[-1]["content"] = response |
| yield "", chat_history |
| |
| |
| try: |
| add_conversation(message, response) |
| except Exception as e: |
| print(f"Error saving conversation: {e}") |
| |
| except Exception as e: |
| error_msg = f"Üzgünüm, bir hata oluştu: {str(e)}" |
| print(f"Chat error: {e}") |
| |
| if chat_history[-1]["role"] == "user": |
| chat_history.append({"role": "assistant", "content": error_msg}) |
| else: |
| chat_history[-1]["content"] = error_msg |
| yield "", chat_history |
| |
| |
| |
| with gr.Row(visible=False): |
| api_btn = gr.Button("Get Conversations", elem_id="api_btn") |
| api_output = gr.JSON(label="Conversations") |
| |
| api_btn.click(fn=get_conversations_api, outputs=api_output, api_name="get_conversations") |
| |
| msg.submit(respond, [msg, chatbot], [msg, chatbot], show_progress=True) |
| |
| |
|
|
| |
| |
|
|
| |
| |
| |
| |
|
|
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
|
|
|
|
| |
| if __name__ == "__main__": |
| demo.launch(allowed_paths=["/app/conversations.json", "/app/public"], |
| server_name="0.0.0.0", |
| server_port=7860, |
| share=False, |
| show_api=True |
| ) |
|
|