SamiKoen commited on
Commit
3696242
·
1 Parent(s): 3f83279

BF space'teki tam çalışan genel algoritma entegrasyonu - spesifik isim yok

Browse files
Files changed (2) hide show
  1. app.py +23 -17
  2. smart_warehouse_complete.py +366 -0
app.py CHANGED
@@ -30,11 +30,11 @@ except ImportError:
30
  print("Improved WhatsApp chatbot not available, using basic search")
31
  USE_IMPROVED_SEARCH = False
32
 
33
- # Import GPT-5 powered smart warehouse search
34
  try:
35
- from smart_warehouse_whatsapp import get_warehouse_stock_gpt5
36
  USE_GPT5_SEARCH = True
37
- logger.info("✅ GPT-5 smart warehouse search loaded")
38
  except ImportError:
39
  USE_GPT5_SEARCH = False
40
  logger.info("❌ GPT-5 search not available")
@@ -70,22 +70,28 @@ else:
70
  # Mağaza stok bilgilerini çekme fonksiyonu
71
  def get_warehouse_stock(product_name):
72
  """B2B API'den mağaza stok bilgilerini çek - GPT-5 enhanced"""
73
- # Try GPT-5 smart search first
74
  if USE_GPT5_SEARCH:
75
- gpt5_result = get_warehouse_stock_gpt5(product_name)
76
- if gpt5_result:
77
- # Format for WhatsApp display
 
 
 
78
  warehouse_info = []
79
- for product in gpt5_result: # Show all found products
80
- info = f"📦 {product['name']}"
81
- if product['variant']:
82
- info += f" ({product['variant']})"
83
- if product['warehouses']:
84
- info += f"\n📍 Mevcut: {', '.join(product['warehouses'])}"
85
- if product['price']:
86
- info += f"\n💰 {product['price']}"
87
- warehouse_info.append(info)
88
- return warehouse_info
 
 
 
89
 
90
  # Fallback to original search
91
  try:
 
30
  print("Improved WhatsApp chatbot not available, using basic search")
31
  USE_IMPROVED_SEARCH = False
32
 
33
+ # Import GPT-5 powered smart warehouse search - complete BF algorithm
34
  try:
35
+ from smart_warehouse_complete import get_warehouse_stock_smart_complete
36
  USE_GPT5_SEARCH = True
37
+ logger.info("✅ GPT-5 complete smart warehouse (BF algorithm) loaded")
38
  except ImportError:
39
  USE_GPT5_SEARCH = False
40
  logger.info("❌ GPT-5 search not available")
 
70
  # Mağaza stok bilgilerini çekme fonksiyonu
71
  def get_warehouse_stock(product_name):
72
  """B2B API'den mağaza stok bilgilerini çek - GPT-5 enhanced"""
73
+ # Try GPT-5 complete smart search (BF algorithm)
74
  if USE_GPT5_SEARCH:
75
+ gpt5_result = get_warehouse_stock_smart_complete(product_name)
76
+ if gpt5_result and isinstance(gpt5_result, list):
77
+ # Return directly if it's already formatted strings
78
+ if all(isinstance(item, str) for item in gpt5_result):
79
+ return gpt5_result
80
+ # Format for WhatsApp if dict format
81
  warehouse_info = []
82
+ for item in gpt5_result:
83
+ if isinstance(item, dict):
84
+ info = f"📦 {item.get('name', '')}"
85
+ if item.get('variant'):
86
+ info += f" ({item['variant']})"
87
+ if item.get('warehouses'):
88
+ info += f"\n📍 Mevcut: {', '.join(item['warehouses'])}"
89
+ if item.get('price'):
90
+ info += f"\n💰 {item['price']}"
91
+ warehouse_info.append(info)
92
+ else:
93
+ warehouse_info.append(str(item))
94
+ return warehouse_info if warehouse_info else None
95
 
96
  # Fallback to original search
97
  try:
smart_warehouse_complete.py ADDED
@@ -0,0 +1,366 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Smart warehouse stock finder with price and link information"""
2
+
3
+ import requests
4
+ import re
5
+ import os
6
+ import json
7
+ import xml.etree.ElementTree as ET
8
+
9
+ def get_product_price_and_link(product_name, variant=None):
10
+ """Get price and link from Trek website XML"""
11
+ try:
12
+ # Get Trek product catalog
13
+ url = 'https://www.trekbisiklet.com.tr/output/8582384479'
14
+ response = requests.get(url, verify=False, timeout=10)
15
+
16
+ if response.status_code != 200:
17
+ return None, None
18
+
19
+ root = ET.fromstring(response.content)
20
+
21
+ # Normalize search terms
22
+ search_name = product_name.lower()
23
+ search_variant = variant.lower() if variant else ""
24
+
25
+ # Turkish character normalization
26
+ tr_map = {'ı': 'i', 'ğ': 'g', 'ü': 'u', 'ş': 's', 'ö': 'o', 'ç': 'c'}
27
+ for tr, en in tr_map.items():
28
+ search_name = search_name.replace(tr, en)
29
+ search_variant = search_variant.replace(tr, en)
30
+
31
+ best_match = None
32
+ best_score = 0
33
+
34
+ for item in root.findall('item'):
35
+ # Get product name
36
+ rootlabel_elem = item.find('rootlabel')
37
+ if rootlabel_elem is None or not rootlabel_elem.text:
38
+ continue
39
+
40
+ item_name = rootlabel_elem.text.lower()
41
+ for tr, en in tr_map.items():
42
+ item_name = item_name.replace(tr, en)
43
+
44
+ # Calculate match score
45
+ score = 0
46
+ name_parts = search_name.split()
47
+ for part in name_parts:
48
+ if part in item_name:
49
+ score += 1
50
+
51
+ # Check variant if specified
52
+ if variant and search_variant in item_name:
53
+ score += 2 # Variant match is important
54
+
55
+ if score > best_score:
56
+ best_score = score
57
+ best_match = item
58
+
59
+ if best_match and best_score > 0:
60
+ # Extract price
61
+ price_elem = best_match.find('priceTaxWithCur')
62
+ price = price_elem.text if price_elem is not None and price_elem.text else None
63
+
64
+ # Round price
65
+ if price:
66
+ try:
67
+ price_float = float(price)
68
+ if price_float > 200000:
69
+ rounded = round(price_float / 5000) * 5000
70
+ price = f"{int(rounded):,}".replace(',', '.') + " TL"
71
+ elif price_float > 30000:
72
+ rounded = round(price_float / 1000) * 1000
73
+ price = f"{int(rounded):,}".replace(',', '.') + " TL"
74
+ elif price_float > 10000:
75
+ rounded = round(price_float / 100) * 100
76
+ price = f"{int(rounded):,}".replace(',', '.') + " TL"
77
+ else:
78
+ rounded = round(price_float / 10) * 10
79
+ price = f"{int(rounded):,}".replace(',', '.') + " TL"
80
+ except:
81
+ price = f"{price} TL"
82
+
83
+ # Extract link (field name is productLink, not productUrl!)
84
+ link_elem = best_match.find('productLink')
85
+ link = link_elem.text if link_elem is not None and link_elem.text else None
86
+
87
+ return price, link
88
+
89
+ return None, None
90
+
91
+ except Exception as e:
92
+ print(f"Error getting price/link: {e}")
93
+ return None, None
94
+
95
+ def get_warehouse_stock_smart_complete(user_message):
96
+ """Complete smart warehouse search for WhatsApp - BF algorithm"""
97
+
98
+ OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
99
+
100
+ # Check if user is asking about specific warehouse
101
+ warehouse_keywords = {
102
+ 'caddebostan': 'Caddebostan',
103
+ 'ortaköy': 'Ortaköy',
104
+ 'ortakoy': 'Ortaköy',
105
+ 'alsancak': 'Alsancak',
106
+ 'izmir': 'Alsancak',
107
+ 'bahçeköy': 'Bahçeköy',
108
+ 'bahcekoy': 'Bahçeköy'
109
+ }
110
+
111
+ user_lower = user_message.lower()
112
+ asked_warehouse = None
113
+ for keyword, warehouse in warehouse_keywords.items():
114
+ if keyword in user_lower:
115
+ asked_warehouse = warehouse
116
+ break
117
+
118
+ # Get XML data with retry
119
+ xml_text = None
120
+ for attempt in range(3):
121
+ try:
122
+ url = 'https://video.trek-turkey.com/bizimhesap-warehouse-xml-b2b-api-v2.php'
123
+ timeout_val = 10 + (attempt * 5)
124
+ response = requests.get(url, verify=False, timeout=timeout_val)
125
+ xml_text = response.text
126
+ print(f"DEBUG - XML fetched: {len(xml_text)} characters (attempt {attempt+1})")
127
+ break
128
+ except requests.exceptions.Timeout:
129
+ print(f"XML fetch timeout (attempt {attempt+1}/3, timeout={timeout_val}s)")
130
+ if attempt == 2:
131
+ print("All attempts failed - timeout")
132
+ return None
133
+ except Exception as e:
134
+ print(f"XML fetch error: {e}")
135
+ return None
136
+
137
+ # Extract product blocks
138
+ product_pattern = r'<Product>(.*?)</Product>'
139
+ all_products = re.findall(product_pattern, xml_text, re.DOTALL)
140
+
141
+ # Create simplified product list for GPT
142
+ products_summary = []
143
+ for i, product_block in enumerate(all_products):
144
+ name_match = re.search(r'<ProductName><!\[CDATA\[(.*?)\]\]></ProductName>', product_block)
145
+ variant_match = re.search(r'<ProductVariant><!\[CDATA\[(.*?)\]\]></ProductVariant>', product_block)
146
+
147
+ if name_match:
148
+ warehouses_with_stock = []
149
+ warehouse_regex = r'<Warehouse>.*?<Name><!\[CDATA\[(.*?)\]\]></Name>.*?<Stock>(.*?)</Stock>.*?</Warehouse>'
150
+ warehouses = re.findall(warehouse_regex, product_block, re.DOTALL)
151
+
152
+ for wh_name, wh_stock in warehouses:
153
+ try:
154
+ if int(wh_stock.strip()) > 0:
155
+ warehouses_with_stock.append(wh_name)
156
+ except:
157
+ pass
158
+
159
+ product_info = {
160
+ "index": i,
161
+ "name": name_match.group(1),
162
+ "variant": variant_match.group(1) if variant_match else "",
163
+ "warehouses": warehouses_with_stock
164
+ }
165
+ products_summary.append(product_info)
166
+
167
+ # Prepare warehouse filter if needed
168
+ warehouse_filter = ""
169
+ if asked_warehouse:
170
+ warehouse_filter = f"\nIMPORTANT: User is asking specifically about {asked_warehouse} warehouse. Only return products available in that warehouse."
171
+
172
+ # GPT-5 prompt with enhanced instructions
173
+ smart_prompt = f"""User is asking: "{user_message}"
174
+
175
+ Find ALL products that match this query from the list below.
176
+ If user asks about specific size (S, M, L, XL, XXL, SMALL, MEDIUM, LARGE, X-LARGE), return only that size.
177
+ If user asks generally (without size), return ALL variants of the product.
178
+ {warehouse_filter}
179
+
180
+ Use your GPT-5 intelligence to understand the query:
181
+ - Analyze the product list and match based on understanding, not hardcoded rules
182
+ - Be smart about Turkish/English variations and product types
183
+ - Understand context: what type of product the user is looking for
184
+ - Match intelligently without specific keyword rules
185
+
186
+ Products list (with warehouse availability):
187
+ {json.dumps(products_summary, ensure_ascii=False, indent=2)}
188
+
189
+ Return ONLY index numbers of ALL matching products as comma-separated list (e.g., "5,8,12,15").
190
+ If no products found, return ONLY: -1
191
+ DO NOT return empty string or any explanation, ONLY numbers or -1
192
+
193
+ Examples of correct responses:
194
+ - "2,5,8,12,15,20" (multiple products found)
195
+ - "45" (single product found)
196
+ - "-1" (no products found)"""
197
+
198
+ headers = {
199
+ "Content-Type": "application/json",
200
+ "Authorization": f"Bearer {OPENAI_API_KEY}"
201
+ }
202
+
203
+ payload = {
204
+ "model": "gpt-5-chat-latest",
205
+ "messages": [
206
+ {"role": "system", "content": "You are a product matcher. Find ALL matching products. Return only index numbers."},
207
+ {"role": "user", "content": smart_prompt}
208
+ ],
209
+ "temperature": 0,
210
+ "max_tokens": 100
211
+ }
212
+
213
+ try:
214
+ response = requests.post(
215
+ "https://api.openai.com/v1/chat/completions",
216
+ headers=headers,
217
+ json=payload,
218
+ timeout=10
219
+ )
220
+
221
+ if response.status_code == 200:
222
+ result = response.json()
223
+ indices_str = result['choices'][0]['message']['content'].strip()
224
+
225
+ print(f"DEBUG - GPT-5 response: '{indices_str}'")
226
+
227
+ # Handle empty response
228
+ if not indices_str or indices_str == "-1":
229
+ return ["Ürün bulunamadı"]
230
+
231
+ try:
232
+ # Filter out empty strings and parse indices
233
+ indices = []
234
+ for idx in indices_str.split(','):
235
+ idx = idx.strip()
236
+ if idx and idx.isdigit():
237
+ indices.append(int(idx))
238
+
239
+ # Collect all matching products with price/link
240
+ all_variants = []
241
+ warehouse_stock = {}
242
+
243
+ for idx in indices:
244
+ if 0 <= idx < len(all_products):
245
+ product_block = all_products[idx]
246
+
247
+ # Get product details
248
+ name_match = re.search(r'<ProductName><!\[CDATA\[(.*?)\]\]></ProductName>', product_block)
249
+ variant_match = re.search(r'<ProductVariant><!\[CDATA\[(.*?)\]\]></ProductVariant>', product_block)
250
+
251
+ if name_match:
252
+ product_name = name_match.group(1)
253
+ variant = variant_match.group(1) if variant_match else ""
254
+
255
+ # Get price and link from Trek website
256
+ price, link = get_product_price_and_link(product_name, variant)
257
+
258
+ variant_info = {
259
+ 'name': product_name,
260
+ 'variant': variant,
261
+ 'price': price,
262
+ 'link': link,
263
+ 'warehouses': []
264
+ }
265
+
266
+ # Get warehouse stock
267
+ warehouse_regex = r'<Warehouse>.*?<Name><!\[CDATA\[(.*?)\]\]></Name>.*?<Stock>(.*?)</Stock>.*?</Warehouse>'
268
+ warehouses = re.findall(warehouse_regex, product_block, re.DOTALL)
269
+
270
+ for wh_name, wh_stock in warehouses:
271
+ try:
272
+ stock = int(wh_stock.strip())
273
+ if stock > 0:
274
+ display_name = format_warehouse_name(wh_name)
275
+ variant_info['warehouses'].append({
276
+ 'name': display_name,
277
+ 'stock': stock
278
+ })
279
+
280
+ if display_name not in warehouse_stock:
281
+ warehouse_stock[display_name] = 0
282
+ warehouse_stock[display_name] += stock
283
+ except:
284
+ pass
285
+
286
+ if variant_info['warehouses']:
287
+ all_variants.append(variant_info)
288
+
289
+ # Format result
290
+ result = []
291
+
292
+ if asked_warehouse:
293
+ # Filter for specific warehouse
294
+ warehouse_variants = []
295
+ for variant in all_variants:
296
+ for wh in variant['warehouses']:
297
+ if asked_warehouse in wh['name']:
298
+ warehouse_variants.append(variant)
299
+ break
300
+
301
+ if warehouse_variants:
302
+ result.append(f"{format_warehouse_name(asked_warehouse)} mağazasında mevcut:")
303
+ for v in warehouse_variants:
304
+ variant_text = f" ({v['variant']})" if v['variant'] else ""
305
+ result.append(f"• {v['name']}{variant_text}")
306
+ if v['price']:
307
+ result.append(f" Fiyat: {v['price']}")
308
+ if v['link']:
309
+ result.append(f" Link: {v['link']}")
310
+ else:
311
+ result.append(f"{format_warehouse_name(asked_warehouse)} mağazasında bu ürün mevcut değil")
312
+ else:
313
+ # Show all variants
314
+ if all_variants:
315
+ # Group by product name for cleaner display
316
+ product_groups = {}
317
+ for variant in all_variants:
318
+ if variant['name'] not in product_groups:
319
+ product_groups[variant['name']] = []
320
+ product_groups[variant['name']].append(variant)
321
+
322
+ result.append(f"Bulunan ürünler:")
323
+
324
+ for product_name, variants in product_groups.items():
325
+ result.append(f"\n{product_name}:")
326
+
327
+ # Show first variant's price and link (usually same for all variants)
328
+ if variants[0]['price']:
329
+ result.append(f"Fiyat: {variants[0]['price']}")
330
+ if variants[0]['link']:
331
+ result.append(f"Link: {variants[0]['link']}")
332
+
333
+ # Show variants and their availability
334
+ for v in variants:
335
+ if v['variant']:
336
+ warehouses_str = ", ".join([w['name'].replace(' mağazası', '') for w in v['warehouses']])
337
+ result.append(f"• {v['variant']}: {warehouses_str}")
338
+
339
+ else:
340
+ result.append("Hiçbir mağazada stok yok")
341
+
342
+ return result
343
+
344
+ except (ValueError, IndexError) as e:
345
+ print(f"DEBUG - Error parsing indices: {e}")
346
+ return None
347
+ else:
348
+ print(f"GPT API error: {response.status_code}")
349
+ return None
350
+
351
+ except Exception as e:
352
+ print(f"Error calling GPT: {e}")
353
+ return None
354
+
355
+ def format_warehouse_name(wh_name):
356
+ """Format warehouse name nicely"""
357
+ if "CADDEBOSTAN" in wh_name:
358
+ return "Caddebostan mağazası"
359
+ elif "ORTAKÖY" in wh_name:
360
+ return "Ortaköy mağazası"
361
+ elif "ALSANCAK" in wh_name:
362
+ return "İzmir Alsancak mağazası"
363
+ elif "BAHCEKOY" in wh_name or "BAHÇEKÖY" in wh_name:
364
+ return "Bahçeköy mağazası"
365
+ else:
366
+ return wh_name.replace("MAGAZA DEPO", "").strip()