Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -286,7 +286,9 @@ def generate_chat_response(message, chat_history_from_client):
|
|
| 286 |
for p in products:
|
| 287 |
if p.get('in_stock', True):
|
| 288 |
price_display = f"{p.get('price', 0):.2f}".replace('.00', '')
|
| 289 |
-
|
|
|
|
|
|
|
| 290 |
product_list_str = "\n".join(product_info_list) if product_info_list else "В данный момент нет товаров в наличии."
|
| 291 |
|
| 292 |
category_list_str = ", ".join(categories) if categories else "Категорий пока нет."
|
|
@@ -309,7 +311,7 @@ def generate_chat_response(message, chat_history_from_client):
|
|
| 309 |
"Твоя задача - помогать пользователям находить товары, отвечать на вопросы о них, предлагать варианты, а также предоставлять информацию о магазине. "
|
| 310 |
"Всегда будь вежлив, информативен и стремись решить проблему пользователя. "
|
| 311 |
"Никогда не выдумывай товары или категории, которых нет в предоставленных списках. "
|
| 312 |
-
"Когда ты предлагаешь товар, всегда указывай его название и ID, используя
|
| 313 |
"Если пользователь ищет товар или категорию, предлагай несколько наиболее подходящих вариантов или перечисляй доступные из этой категории.\n\n"
|
| 314 |
f"Список доступных категорий: {category_list_str}.\n\n"
|
| 315 |
f"Список доступных товаров в магазине:\n"
|
|
@@ -601,19 +603,64 @@ CATALOG_TEMPLATE = '''
|
|
| 601 |
#chat-send-button:hover { background-color: #005CBF; }
|
| 602 |
#chat-send-button:disabled { background-color: #cccccc; cursor: not-allowed; }
|
| 603 |
|
| 604 |
-
|
| 605 |
-
.chat-product-card
|
| 606 |
-
|
| 607 |
-
|
| 608 |
-
|
| 609 |
-
|
| 610 |
-
|
| 611 |
-
|
| 612 |
-
|
| 613 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 614 |
}
|
| 615 |
-
.chat-product-link:hover, .chat-add-to-cart:hover { background-color: #BBDEFB; }
|
| 616 |
-
.chat-product-card-actions .fa-cart-plus { font-size: 0.9em; }
|
| 617 |
|
| 618 |
</style>
|
| 619 |
</head>
|
|
@@ -1065,14 +1112,18 @@ CATALOG_TEMPLATE = '''
|
|
| 1065 |
const messageElement = document.createElement('div');
|
| 1066 |
messageElement.className = `chat-message ${role}`;
|
| 1067 |
|
|
|
|
|
|
|
| 1068 |
const productMatchRegex = /\[ID_ТОВАРА:\s*([a-fA-F0-9]+)\s*Название:\s*([^\]]+)\]/g;
|
| 1069 |
let lastIndex = 0;
|
| 1070 |
const contentFragment = document.createDocumentFragment();
|
| 1071 |
let match;
|
| 1072 |
|
| 1073 |
while ((match = productMatchRegex.exec(text)) !== null) {
|
|
|
|
| 1074 |
if (match.index > lastIndex) {
|
| 1075 |
const textPart = document.createElement('span');
|
|
|
|
| 1076 |
textPart.innerHTML = text.substring(lastIndex, match.index).replace(/\\n/g, '<br>');
|
| 1077 |
contentFragment.appendChild(textPart);
|
| 1078 |
}
|
|
@@ -1100,14 +1151,16 @@ CATALOG_TEMPLATE = '''
|
|
| 1100 |
`;
|
| 1101 |
contentFragment.appendChild(card);
|
| 1102 |
} else {
|
|
|
|
| 1103 |
const productName = match[2];
|
| 1104 |
-
const notFoundText = document.createElement('
|
| 1105 |
-
notFoundText.
|
| 1106 |
contentFragment.appendChild(notFoundText);
|
| 1107 |
}
|
| 1108 |
lastIndex = match.index + match[0].length;
|
| 1109 |
}
|
| 1110 |
|
|
|
|
| 1111 |
if (lastIndex < text.length) {
|
| 1112 |
const textPart = document.createElement('span');
|
| 1113 |
textPart.innerHTML = text.substring(lastIndex).replace(/\\n/g, '<br>');
|
|
@@ -1123,20 +1176,21 @@ CATALOG_TEMPLATE = '''
|
|
| 1123 |
localStorage.setItem('evaChatHistory', JSON.stringify(chatHistory));
|
| 1124 |
}
|
| 1125 |
|
|
|
|
| 1126 |
messageElement.querySelectorAll('.chat-product-link').forEach(link => {
|
| 1127 |
link.addEventListener('click', (e) => {
|
| 1128 |
-
e.preventDefault();
|
| 1129 |
const id = e.currentTarget.dataset.productId;
|
| 1130 |
-
closeModal('chatModal');
|
| 1131 |
-
openModalById(id);
|
| 1132 |
});
|
| 1133 |
});
|
| 1134 |
messageElement.querySelectorAll('.chat-add-to-cart').forEach(link => {
|
| 1135 |
link.addEventListener('click', (e) => {
|
| 1136 |
-
e.preventDefault();
|
| 1137 |
const id = e.currentTarget.dataset.productId;
|
| 1138 |
-
closeModal('chatModal');
|
| 1139 |
-
openQuantityModalById(id);
|
| 1140 |
});
|
| 1141 |
});
|
| 1142 |
}
|
|
@@ -1819,6 +1873,7 @@ def catalog():
|
|
| 1819 |
|
| 1820 |
products_in_stock = [p for p in all_products_raw if p.get('in_stock', True)]
|
| 1821 |
|
|
|
|
| 1822 |
products_sorted_for_js = sorted(products_in_stock, key=lambda p: (not p.get('is_top', False), p.get('name', '').lower()))
|
| 1823 |
|
| 1824 |
products_by_category = {cat: [] for cat in all_cat_names}
|
|
@@ -1834,7 +1889,7 @@ def catalog():
|
|
| 1834 |
CATALOG_TEMPLATE,
|
| 1835 |
products_by_category=products_by_category,
|
| 1836 |
ordered_categories=ordered_categories,
|
| 1837 |
-
products_json=json.dumps(products_sorted_for_js),
|
| 1838 |
repo_id=REPO_ID,
|
| 1839 |
store_address=STORE_ADDRESS,
|
| 1840 |
currency_code=CURRENCY_CODE
|
|
|
|
| 286 |
for p in products:
|
| 287 |
if p.get('in_stock', True):
|
| 288 |
price_display = f"{p.get('price', 0):.2f}".replace('.00', '')
|
| 289 |
+
# AI is instructed to output in format: [ID_ТОВАРА: <product_id> Название: <product_name>]
|
| 290 |
+
# We add a placeholder here for the AI to pick up on it.
|
| 291 |
+
product_info_list.append(f"- [ID_ТОВАРА: {p.get('product_id', 'N/A')} Название: {p.get('name', 'Без названия')}], Категория: {p.get('category', 'Без категории')}, Цена: {price_display} {CURRENCY_CODE}, Описание: {p.get('description', '')[:100]}...")
|
| 292 |
product_list_str = "\n".join(product_info_list) if product_info_list else "В данный момент нет товаров в наличии."
|
| 293 |
|
| 294 |
category_list_str = ", ".join(categories) if categories else "Категорий пока нет."
|
|
|
|
| 311 |
"Твоя задача - помогать пользователям находить товары, отвечать на вопросы о них, предлагать варианты, а также предоставлять информацию о магазине. "
|
| 312 |
"Всегда будь вежлив, информативен и стремись решить проблему пользователя. "
|
| 313 |
"Никогда не выдумывай товары или категории, которых нет в предоставленных списках. "
|
| 314 |
+
"Когда ты предлагаешь товар, всегда указывай его название и ID, используя *точный формат*: [ID_ТОВАРА: <product_id> Название: <product_name>]. Это *очень важно* для клиента. "
|
| 315 |
"Если пользователь ищет товар или категорию, предлагай несколько наиболее подходящих вариантов или перечисляй доступные из этой категории.\n\n"
|
| 316 |
f"Список доступных категорий: {category_list_str}.\n\n"
|
| 317 |
f"Список доступных товаров в магазине:\n"
|
|
|
|
| 603 |
#chat-send-button:hover { background-color: #005CBF; }
|
| 604 |
#chat-send-button:disabled { background-color: #cccccc; cursor: not-allowed; }
|
| 605 |
|
| 606 |
+
/* New styles for chat product cards */
|
| 607 |
+
.chat-product-card {
|
| 608 |
+
background-color: #f0f2f5;
|
| 609 |
+
border-radius: 12px;
|
| 610 |
+
padding: 10px;
|
| 611 |
+
margin-top: 8px; /* Give some space after text, if any */
|
| 612 |
+
display: flex;
|
| 613 |
+
align-items: center;
|
| 614 |
+
gap: 12px;
|
| 615 |
+
border: 1px solid #e0e0e0;
|
| 616 |
+
}
|
| 617 |
+
.chat-product-card img {
|
| 618 |
+
width: 50px;
|
| 619 |
+
height: 50px;
|
| 620 |
+
object-fit: cover;
|
| 621 |
+
border-radius: 8px;
|
| 622 |
+
flex-shrink: 0;
|
| 623 |
+
}
|
| 624 |
+
.chat-product-card-info {
|
| 625 |
+
flex-grow: 1;
|
| 626 |
+
}
|
| 627 |
+
.chat-product-card-info strong {
|
| 628 |
+
display: block;
|
| 629 |
+
font-size: 0.9rem;
|
| 630 |
+
color: #333;
|
| 631 |
+
margin-bottom: 2px;
|
| 632 |
+
}
|
| 633 |
+
.chat-product-card-info span {
|
| 634 |
+
font-size: 0.85rem;
|
| 635 |
+
color: #0A2A66;
|
| 636 |
+
font-weight: 500;
|
| 637 |
+
}
|
| 638 |
+
.chat-product-card-actions {
|
| 639 |
+
display: flex;
|
| 640 |
+
flex-direction: column;
|
| 641 |
+
gap: 5px;
|
| 642 |
+
}
|
| 643 |
+
.chat-product-link, .chat-add-to-cart {
|
| 644 |
+
display: inline-block;
|
| 645 |
+
background-color: #E3F2FD;
|
| 646 |
+
color: #0070D1;
|
| 647 |
+
padding: 5px 10px;
|
| 648 |
+
border-radius: 15px;
|
| 649 |
+
cursor: pointer;
|
| 650 |
+
font-size: 0.85rem;
|
| 651 |
+
text-decoration: none;
|
| 652 |
+
transition: background-color 0.2s;
|
| 653 |
+
font-weight: 500;
|
| 654 |
+
text-align: center;
|
| 655 |
+
box-sizing: border-box;
|
| 656 |
+
width: 100%; /* Make buttons take full width of their container */
|
| 657 |
+
}
|
| 658 |
+
.chat-product-link:hover, .chat-add-to-cart:hover {
|
| 659 |
+
background-color: #BBDEFB;
|
| 660 |
+
}
|
| 661 |
+
.chat-product-card-actions .fa-cart-plus {
|
| 662 |
+
font-size: 0.9em; /* Adjust icon size slightly */
|
| 663 |
}
|
|
|
|
|
|
|
| 664 |
|
| 665 |
</style>
|
| 666 |
</head>
|
|
|
|
| 1112 |
const messageElement = document.createElement('div');
|
| 1113 |
messageElement.className = `chat-message ${role}`;
|
| 1114 |
|
| 1115 |
+
// Regex to find the product pattern: [ID_ТОВАРА: <product_id> Название: <product_name>]
|
| 1116 |
+
// This regex is slightly different from the original prompt to match the AI's output exactly
|
| 1117 |
const productMatchRegex = /\[ID_ТОВАРА:\s*([a-fA-F0-9]+)\s*Название:\s*([^\]]+)\]/g;
|
| 1118 |
let lastIndex = 0;
|
| 1119 |
const contentFragment = document.createDocumentFragment();
|
| 1120 |
let match;
|
| 1121 |
|
| 1122 |
while ((match = productMatchRegex.exec(text)) !== null) {
|
| 1123 |
+
// Add any preceding text
|
| 1124 |
if (match.index > lastIndex) {
|
| 1125 |
const textPart = document.createElement('span');
|
| 1126 |
+
// Replace newlines for proper rendering
|
| 1127 |
textPart.innerHTML = text.substring(lastIndex, match.index).replace(/\\n/g, '<br>');
|
| 1128 |
contentFragment.appendChild(textPart);
|
| 1129 |
}
|
|
|
|
| 1151 |
`;
|
| 1152 |
contentFragment.appendChild(card);
|
| 1153 |
} else {
|
| 1154 |
+
// If product not found in allProducts, display it as plain text with a note
|
| 1155 |
const productName = match[2];
|
| 1156 |
+
const notFoundText = document.createElement('span');
|
| 1157 |
+
notFoundText.innerHTML = `[ID_ТОВАРА: ${productId} Название: ${productName}] (товар не найден) `;
|
| 1158 |
contentFragment.appendChild(notFoundText);
|
| 1159 |
}
|
| 1160 |
lastIndex = match.index + match[0].length;
|
| 1161 |
}
|
| 1162 |
|
| 1163 |
+
// Add any remaining text after the last product card
|
| 1164 |
if (lastIndex < text.length) {
|
| 1165 |
const textPart = document.createElement('span');
|
| 1166 |
textPart.innerHTML = text.substring(lastIndex).replace(/\\n/g, '<br>');
|
|
|
|
| 1176 |
localStorage.setItem('evaChatHistory', JSON.stringify(chatHistory));
|
| 1177 |
}
|
| 1178 |
|
| 1179 |
+
// Attach event listeners to dynamically created links/buttons
|
| 1180 |
messageElement.querySelectorAll('.chat-product-link').forEach(link => {
|
| 1181 |
link.addEventListener('click', (e) => {
|
| 1182 |
+
e.preventDefault(); // Prevent default link behavior
|
| 1183 |
const id = e.currentTarget.dataset.productId;
|
| 1184 |
+
closeModal('chatModal'); // Close chat modal
|
| 1185 |
+
openModalById(id); // Open product detail modal
|
| 1186 |
});
|
| 1187 |
});
|
| 1188 |
messageElement.querySelectorAll('.chat-add-to-cart').forEach(link => {
|
| 1189 |
link.addEventListener('click', (e) => {
|
| 1190 |
+
e.preventDefault(); // Prevent default link behavior
|
| 1191 |
const id = e.currentTarget.dataset.productId;
|
| 1192 |
+
closeModal('chatModal'); // Close chat modal
|
| 1193 |
+
openQuantityModalById(id); // Open quantity modal for adding to cart
|
| 1194 |
});
|
| 1195 |
});
|
| 1196 |
}
|
|
|
|
| 1873 |
|
| 1874 |
products_in_stock = [p for p in all_products_raw if p.get('in_stock', True)]
|
| 1875 |
|
| 1876 |
+
# Sort products for consistent display and indexing
|
| 1877 |
products_sorted_for_js = sorted(products_in_stock, key=lambda p: (not p.get('is_top', False), p.get('name', '').lower()))
|
| 1878 |
|
| 1879 |
products_by_category = {cat: [] for cat in all_cat_names}
|
|
|
|
| 1889 |
CATALOG_TEMPLATE,
|
| 1890 |
products_by_category=products_by_category,
|
| 1891 |
ordered_categories=ordered_categories,
|
| 1892 |
+
products_json=json.dumps(products_sorted_for_js), # Pass sorted products to JS
|
| 1893 |
repo_id=REPO_ID,
|
| 1894 |
store_address=STORE_ADDRESS,
|
| 1895 |
currency_code=CURRENCY_CODE
|