Commit 4be13d17 authored by Vũ Hoàng Anh's avatar Vũ Hoàng Anh

Add Docker stage/prod, entrypoint auto-scaling, mock APIs, comment log functions

parent f057ad1e
FROM grafana/k6:latest
# Copy K6 test script
COPY backend/hehe/k6-chatbot-test.js /scripts/chatbot-test.js
# Default command - chạy K6 test
CMD ["run", "--out", "json=/tmp/results.json", "/scripts/chatbot-test.js"]
# ============================================================
# DOCKERFILE.PROD - Production (Multi-Worker Gunicorn)
# ============================================================
# Multi-stage build để optimize dung lượng image
FROM python:3.11-slim as builder
WORKDIR /app
# Copy requirements.txt
COPY requirements.txt .
# Install dependencies to a local directory
RUN pip install --user --no-cache-dir -r requirements.txt
# ============================================================
# Final stage - Production image (tối giản)
FROM python:3.11-slim
WORKDIR /app
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1
ENV ENV=production
# Copy Python packages từ builder stage
COPY --from=builder /root/.local /root/.local
ENV PATH=/root/.local/bin:$PATH
# Copy source code
COPY . .
# Copy entrypoint script
COPY entrypoint.sh /app/entrypoint.sh
RUN chmod +x /app/entrypoint.sh
# Expose port 5000
EXPOSE 5000
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD python -c "import requests; requests.get('http://localhost:5000/health')" || exit 1
ENTRYPOINT ["/app/entrypoint.sh"]
# ============================================================
# DOCKERFILE.STAGE - Development/Staging (Hot Reload)
# ============================================================
# Sử dụng Python 3.11 Slim để tối ưu dung lượng # Sử dụng Python 3.11 Slim để tối ưu dung lượng
FROM python:3.11-slim FROM python:3.11-slim
...@@ -7,6 +10,7 @@ WORKDIR /app ...@@ -7,6 +10,7 @@ WORKDIR /app
# Thiết lập biến môi trường để log in ra ngay lập tức # Thiết lập biến môi trường để log in ra ngay lập tức
ENV PYTHONUNBUFFERED=1 ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1 ENV PYTHONDONTWRITEBYTECODE=1
ENV ENV=development
# Copy requirements.txt trước để tận dụng Docker cache # Copy requirements.txt trước để tận dụng Docker cache
COPY requirements.txt . COPY requirements.txt .
...@@ -21,5 +25,6 @@ COPY . . ...@@ -21,5 +25,6 @@ COPY . .
# Expose port 5000 (Port chạy server) # Expose port 5000 (Port chạy server)
EXPOSE 5000 EXPOSE 5000
# Lệnh chạy server dùng uvicorn # Lệnh chạy server dùng uvicorn với hot reload
CMD ["uvicorn", "server:app", "--host", "0.0.0.0", "--port", "5000"] # ⚡ Hot reload - tự động restart khi code thay đổi (dùng cho dev/stage)
CMD ["uvicorn", "server:app", "--host", "0.0.0.0", "--port", "5000", "--reload"]
"""
Fashion Q&A Agent Controller
Langfuse will auto-trace via LangChain integration (no code changes needed).
"""
import json
import logging
import uuid
from fastapi import BackgroundTasks
from langchain_core.messages import AIMessage, HumanMessage, ToolMessage
from langchain_core.runnables import RunnableConfig
from common.conversation_manager import ConversationManager, get_conversation_manager
from common.langfuse_client import get_callback_handler, langfuse_trace_context
from common.llm_factory import create_llm
from config import DEFAULT_MODEL
from .graph import build_graph
from .models import AgentState, get_config
from .tools.get_tools import get_all_tools
logger = logging.getLogger(__name__)
async def chat_controller(
query: str,
user_id: str,
background_tasks: BackgroundTasks,
model_name: str = DEFAULT_MODEL,
images: list[str] | None = None,
) -> dict:
"""
Controller main logic for non-streaming chat requests.
Langfuse will automatically trace all LangChain operations.
"""
logger.info(f"▶️ Starting chat_controller with model: {model_name} for user: {user_id}")
config = get_config()
config.model_name = model_name
# Enable JSON mode to ensure structured output
llm = create_llm(model_name=model_name, streaming=False, json_mode=True)
tools = get_all_tools()
graph = build_graph(config, llm=llm, tools=tools)
# Init ConversationManager (Singleton)
memory = await get_conversation_manager()
# LOAD HISTORY & Prepare State (Optimize: history logic remains solid)
history_dicts = await memory.get_chat_history(user_id, limit=20)
history = []
for h in reversed(history_dicts):
msg_cls = HumanMessage if h["is_human"] else AIMessage
history.append(msg_cls(content=h["message"]))
initial_state, exec_config = _prepare_execution_context(
query=query, user_id=user_id, history=history, images=images
)
try:
# 🔥 Wrap graph execution với langfuse_trace_context để set user_id cho tất cả observations
with langfuse_trace_context(user_id=user_id, session_id=user_id):
# TỐI ƯU: Chạy Graph
result = await graph.ainvoke(initial_state, config=exec_config)
# TỐI ƯU: Extract IDs từ Tool Messages một lần duy nhất
all_product_ids = _extract_product_ids(result.get("messages", []))
# TỐI ƯU: Xử lý AI Response
ai_raw_content = result.get("ai_response").content if result.get("ai_response") else ""
logger.info(f"💾 [RAW AI OUTPUT]:\n{ai_raw_content}")
# Parse JSON để lấy text response và product_ids từ AI
ai_text_response = ai_raw_content
try:
# Vì json_mode=True, OpenAI sẽ nhả raw JSON
ai_json = json.loads(ai_raw_content)
# Extract text response từ JSON
ai_text_response = ai_json.get("ai_response", ai_raw_content)
# Merge product_ids từ AI JSON (nếu có) - KHÔNG dùng set() vì dict unhashable
explicit_ids = ai_json.get("product_ids", [])
if explicit_ids and isinstance(explicit_ids, list):
# Merge và deduplicate by SKU
seen_skus = {p["sku"] for p in all_product_ids if "sku" in p}
for product in explicit_ids:
if isinstance(product, dict) and product.get("sku") not in seen_skus:
all_product_ids.append(product)
seen_skus.add(product.get("sku"))
except (json.JSONDecodeError, Exception) as e:
# Nếu AI trả về text thường (hiếm khi xảy ra trong JSON mode) thì ignore
logger.warning(f"Could not parse AI response as JSON: {e}")
pass
# BACKGROUND TASK: Lưu history nhanh gọn
background_tasks.add_task(
_handle_post_chat_async,
memory=memory,
user_id=user_id,
human_query=query,
ai_msg=AIMessage(content=ai_text_response),
)
return {
"ai_response": ai_text_response, # CHỈ text, không phải JSON
"product_ids": all_product_ids, # Array of product objects
}
except Exception as e:
logger.error(f"💥 Chat error for user {user_id}: {e}", exc_info=True)
raise
def _extract_product_ids(messages: list) -> list[dict]:
"""
Extract full product info from tool messages (data_retrieval_tool results).
Returns list of product objects with: sku, name, price, sale_price, url, thumbnail_image_url.
"""
products = []
seen_skus = set()
for msg in messages:
if isinstance(msg, ToolMessage):
try:
# Tool result is JSON string
tool_result = json.loads(msg.content)
# Check if tool returned products
if tool_result.get("status") == "success" and "products" in tool_result:
for product in tool_result["products"]:
sku = product.get("internal_ref_code")
if sku and sku not in seen_skus:
seen_skus.add(sku)
# Extract full product info
product_obj = {
"sku": sku,
"name": product.get("magento_product_name", ""),
"price": product.get("price_vnd", 0),
"sale_price": product.get("sale_price_vnd"), # null nếu không sale
"url": product.get("magento_url_key", ""),
"thumbnail_image_url": product.get("thumbnail_image_url", ""),
}
products.append(product_obj)
except (json.JSONDecodeError, KeyError, TypeError) as e:
logger.debug(f"Could not parse tool message for products: {e}")
continue
return products
def _prepare_execution_context(query: str, user_id: str, history: list, images: list | None):
"""Prepare initial state and execution config for the graph run."""
initial_state: AgentState = {
"user_query": HumanMessage(content=query),
"messages": [HumanMessage(content=query)],
"history": history,
"user_id": user_id,
"images_embedding": [],
"ai_response": None,
}
run_id = str(uuid.uuid4())
# Metadata for LangChain (tags for logging/filtering)
metadata = {
"run_id": run_id,
"tags": "chatbot,production",
}
# 🔥 CallbackHandler - sẽ được wrap trong langfuse_trace_context để set user_id
# Per Langfuse docs: propagate_attributes() handles user_id propagation
langfuse_handler = get_callback_handler()
exec_config = RunnableConfig(
configurable={
"user_id": user_id,
"transient_images": images or [],
"run_id": run_id,
},
run_id=run_id,
metadata=metadata,
callbacks=[langfuse_handler] if langfuse_handler else [],
)
return initial_state, exec_config
async def _handle_post_chat_async(
memory: ConversationManager, user_id: str, human_query: str, ai_msg: AIMessage | None
):
"""Save chat history in background task after response is sent."""
if ai_msg:
try:
await memory.save_conversation_turn(user_id, human_query, ai_msg.content)
logger.debug(f"Saved conversation for user {user_id}")
except Exception as e:
logger.error(f"Failed to save conversation for user {user_id}: {e}", exc_info=True)
"""
CANIFA Data Retrieval Tool - Tối giản cho Agentic Workflow.
Hỗ trợ Hybrid Search: Semantic (Vector) + Metadata Filter.
"""
import asyncio
import json
import logging
import time
from decimal import Decimal
from langchain_core.tools import tool
from pydantic import BaseModel, Field
from agent.tools.product_search_helpers import build_starrocks_query
from common.starrocks_connection import StarRocksConnection
# from langsmith import traceable
logger = logging.getLogger(__name__)
class DecimalEncoder(json.JSONEncoder):
"""Xử lý kiểu Decimal từ Database khi convert sang JSON."""
def default(self, obj):
if isinstance(obj, Decimal):
return float(obj)
return super().default(obj)
class SearchItem(BaseModel):
"""Cấu trúc một mục tìm kiếm đơn lẻ trong Multi-Search."""
query: str = Field(
...,
description="Câu hỏi/mục đích tự do của user (đi chơi, dự tiệc, phỏng vấn,...) - dùng cho Semantic Search",
)
keywords: str | None = Field(
..., description="Từ khóa sản phẩm cụ thể (áo polo, quần jean,...) - dùng cho LIKE search"
)
magento_ref_code: str | None = Field(
..., description="Mã sản phẩm hoặc mã màu/SKU (Ví dụ: 8TS24W001 hoặc 8TS24W001-SK010)."
)
product_line_vn: str | None = Field(..., description="Dòng sản phẩm (Áo phông, Quần short,...)")
gender_by_product: str | None = Field(..., description="Giới tính: male, female")
age_by_product: str | None = Field(..., description="Độ tuổi: adult, kids, baby, others")
master_color: str | None = Field(..., description="Màu sắc chính (Đen/ Black, Trắng/ White,...)")
material_group: str | None = Field(
...,
description="Nhóm chất liệu. BẮT BUỘC dùng đúng: 'Yarn - Sợi', 'Knit - Dệt Kim', 'Woven - Dệt Thoi', 'Knit/Woven - Dệt Kim/Dệt Thoi'.",
)
season: str | None = Field(..., description="Mùa (Spring Summer, Autumn Winter)")
style: str | None = Field(..., description="Phong cách (Basic Update, Fashion,...)")
fitting: str | None = Field(..., description="Form dáng (Regular, Slim, Loose,...)")
form_neckline: str | None = Field(..., description="Kiểu cổ (Crew Neck, V-neck,...)")
form_sleeve: str | None = Field(..., description="Kiểu tay (Short Sleeve, Long Sleeve,...)")
price_min: float | None = Field(..., description="Giá thấp nhất")
price_max: float | None = Field(..., description="Giá cao nhất")
action: str = Field(..., description="Hành động: 'search' (tìm kiếm) hoặc 'visual_search' (phân tích ảnh)")
class MultiSearchParams(BaseModel):
"""Tham số cho Parallel Multi-Search."""
searches: list[SearchItem] = Field(..., description="Danh sách các truy vấn tìm kiếm chạy song song")
@tool(args_schema=MultiSearchParams)
# @traceable(run_type="tool", name="data_retrieval_tool")
async def data_retrieval_tool(searches: list[SearchItem]) -> str:
"""
Siêu công cụ tìm kiếm sản phẩm CANIFA - Hỗ trợ Parallel Multi-Search (Chạy song song nhiều query).
💡 ĐIỂM ĐẶC BIỆT:
Công cụ này cho phép thực hiện NHIỀU truy vấn tìm kiếm CÙNG LÚC.
Hãy dùng nó khi cần SO SÁNH sản phẩm hoặc tìm trọn bộ OUTFIT (mix & match).
⚠️ QUAN TRỌNG - KHI NÀO DÙNG GÌ:
1️⃣ DÙNG 'query' (Semantic Search - BUỘC PHẢI CÓ):
- Áp dụng cho mọi lượt search để cung cấp bối cảnh (context).
- Ví dụ: "áo thun nam đi biển", "quần tây công sở", "đồ cho bé màu xanh"...
2️⃣ DÙNG METADATA FILTERS (Exact/Partial Match):
- Khi khách nói rõ THUỘC TÍNH: Màu sắc, giá, giới tính, độ tuổi, mã sản phẩm.
- **QUY TẮC MÃ SẢN PHẨM:** Mọi loại mã (VD: `8TS...` hoặc `8TS...-SK...`) → Điền vào `magento_ref_code`.
- **QUY TẮC CHẤT LIÊU (material_group):** Chỉ dùng: `Yarn - Sợi`, `Knit - Dệt Kim`, `Woven - Dệt Thoi`, `Knit/Woven - Dệt Kim/Dệt Thoi`.
📝 VÍ DỤ CHI TIẾT (Single Search):
- Example 1: searches=[{"query": "áo polo nam giá dưới 400k", "keywords": "áo polo", "gender_by_product": "male", "price_max": 400000}]
- Example 2: searches=[{"query": "sản phẩm mã 8TS24W001", "magento_ref_code": "8TS24W001"}]
🚀 VÍ DỤ CẤP CAO (Multi-Search Parallel):
- Example 3 - So sánh: "So sánh áo thun nam đen và áo sơ mi trắng dưới 500k"
Tool Call: searches=[
{"query": "áo thun nam màu đen dưới 500k", "keywords": "áo thun", "master_color": "Đen", "gender_by_product": "male", "price_max": 500000},
{"query": "áo sơ mi nam trắng dưới 500k", "keywords": "áo sơ mi", "master_color": "Trắng", "gender_by_product": "male", "price_max": 500000}
]
- Example 4 - Phối đồ: "Tìm cho mình một cái quần jean và một cái áo khoác để đi chơi"
Tool Call: searches=[
{"query": "quần jean đi chơi năng động", "keywords": "quần jean"},
{"query": "áo khoác đi chơi năng động", "keywords": "áo khoác"}
]
- Example 5 - Cả gia đình: "Tìm áo phông màu xanh cho bố, mẹ và bé trai"
Tool Call: searches=[
{"query": "áo phông nam người lớn màu xanh", "keywords": "áo phông", "master_color": "Xanh", "gender_by_product": "male", "age_by_product": "adult"},
{"query": "áo phông nữ người lớn màu xanh", "keywords": "áo phông", "master_color": "Xanh", "gender_by_product": "female", "age_by_product": "adult"},
{"query": "áo phông bé trai màu xanh", "keywords": "áo phông", "master_color": "Xanh", "gender_by_product": "male", "age_by_product": "others"}
]
"""
logger.info("🔧 [DEBUG] data_retrieval_tool STARTED")
try:
logger.info("🔧 [DEBUG] Creating StarRocksConnection instance")
db = StarRocksConnection()
logger.info("🔧 [DEBUG] StarRocksConnection created successfully")
# 0. Log input parameters (Đúng ý bro)
logger.info(f"📥 [Tool Input] data_retrieval_tool received {len(searches)} items:")
for idx, item in enumerate(searches):
logger.info(f" 🔹 Item [{idx}]: {item.dict(exclude_none=True)}")
# 1. Tạo tasks chạy song song (Parallel)
logger.info("🔧 [DEBUG] Creating parallel tasks")
tasks = []
for item in searches:
tasks.append(_execute_single_search(db, item))
logger.info(f"🚀 [Parallel Search] Executing {len(searches)} queries simultaneously...")
logger.info("🔧 [DEBUG] About to call asyncio.gather()")
results = await asyncio.gather(*tasks)
logger.info(f"🔧 [DEBUG] asyncio.gather() completed with {len(results)} results")
# 2. Tổng hợp kết quả
combined_results = []
for i, products in enumerate(results):
combined_results.append(
{
"search_index": i,
"search_criteria": searches[i].dict(exclude_none=True),
"count": len(products),
"products": products,
}
)
return json.dumps({"status": "success", "results": combined_results}, ensure_ascii=False, cls=DecimalEncoder)
except Exception as e:
logger.error(f"Error in Multi-Search data_retrieval_tool: {e}")
return json.dumps({"status": "error", "message": str(e)})
async def _execute_single_search(db: StarRocksConnection, item: SearchItem) -> list[dict]:
"""Thực thi một search query đơn lẻ (Async)."""
try:
logger.info(f"🔧 [DEBUG] _execute_single_search STARTED for query: {item.query[:50] if item.query else 'None'}")
# ⏱️ Timer: Build query (bao gồm embedding nếu có)
query_build_start = time.time()
logger.info("🔧 [DEBUG] Calling build_starrocks_query()")
sql = await build_starrocks_query(item)
query_build_time = (time.time() - query_build_start) * 1000 # Convert to ms
logger.info(f"🔧 [DEBUG] SQL query built, length: {len(sql)}")
logger.info(f"⏱️ [TIMER] Query Build Time (bao gồm embedding): {query_build_time:.2f}ms")
# ⏱️ Timer: Execute DB query
db_start = time.time()
logger.info("🔧 [DEBUG] Calling db.execute_query_async()")
products = await db.execute_query_async(sql)
db_time = (time.time() - db_start) * 1000 # Convert to ms
logger.info(f"🔧 [DEBUG] Query executed, got {len(products)} products")
logger.info(f"⏱️ [TIMER] DB Query Execution Time: {db_time:.2f}ms")
logger.info(f"⏱️ [TIMER] Total Time (Build + DB): {query_build_time + db_time:.2f}ms")
return _format_product_results(products)
except Exception as e:
logger.error(f"Single search error for item {item}: {e}")
return []
def _format_product_results(products: list[dict]) -> list[dict]:
"""Lọc và format kết quả trả về cho Agent."""
allowed_fields = {
"internal_ref_code",
"description_text_full",
}
return [{k: v for k, v in p.items() if k in allowed_fields} for p in products[:5]]
import asyncio
import time import time
from common.embedding_service import create_embedding_async from common.embedding_service import create_embedding_async
...@@ -114,7 +113,7 @@ async def build_starrocks_query(params, query_vector: list[float] | None = None) ...@@ -114,7 +113,7 @@ async def build_starrocks_query(params, query_vector: list[float] | None = None)
print("✅ [CODE SEARCH] Query built - No vector search needed!") print("✅ [CODE SEARCH] Query built - No vector search needed!")
# Ghi log debug query FULL vào Background Task (Không làm chậm Request) # Ghi log debug query FULL vào Background Task (Không làm chậm Request)
asyncio.create_task(save_query_to_log(sql)) # asyncio.create_task(save_query_to_log(sql))
return sql return sql
...@@ -173,60 +172,62 @@ async def build_starrocks_query(params, query_vector: list[float] | None = None) ...@@ -173,60 +172,62 @@ async def build_starrocks_query(params, query_vector: list[float] | None = None)
LIMIT 10 LIMIT 10
""" """
# Ghi log debug query FULL vào Background Task (Không làm chậm Request)
asyncio.create_task(save_query_to_log(sql))
return sql return sql
async def save_query_to_log(sql: str): # ============================================================
"""Lưu query full vào file hyde_pure_query.txt.""" # TEMPORARILY COMMENTED OUT - save_query_to_log
import os # ============================================================
log_path = r"D:\cnf\chatbot_canifa\backend\logs\hyde_pure_query.txt" # async def save_query_to_log(sql: str):
try: # """Lưu query full vào file hyde_pure_query.txt."""
log_dir = os.path.dirname(log_path) # import os
if not os.path.exists(log_dir): # log_path = r"D:\cnf\chatbot_canifa\backend\logs\hyde_pure_query.txt"
os.makedirs(log_dir) # try:
with open(log_path, "w", encoding="utf-8") as f: # log_dir = os.path.dirname(log_path)
f.write(sql) # if not os.path.exists(log_dir):
print(f"💾 Full Query saved to: {log_path}") # os.makedirs(log_dir)
except Exception as e: # with open(log_path, "w", encoding="utf-8") as f:
print(f"Save query log failed: {e}") # f.write(sql)
# print(f"💾 Full Query saved to: {log_path}")
# except Exception as e:
async def save_preview_to_log(search_query: str, products: list[dict]): # print(f"Save query log failed: {e}")
"""Lưu kết quả DB trả về vào db_preview.txt (Format đẹp cho AI)."""
import json
import os # ============================================================
preview_path = r"D:\cnf\chatbot_canifa\backend\logs\db_preview.txt" # TEMPORARILY COMMENTED OUT - save_preview_to_log
try: # ============================================================
log_dir = os.path.dirname(preview_path) # async def save_preview_to_log(search_query: str, products: list[dict]):
if not os.path.exists(log_dir): # """Lưu kết quả DB trả về vào db_preview.txt (Format đẹp cho AI)."""
os.makedirs(log_dir) # import os
# preview_path = r"D:\cnf\chatbot_canifa\backend\logs\db_preview.txt"
with open(preview_path, "a", encoding="utf-8") as f: # try:
f.write(f"\n{'='*60}\n") # log_dir = os.path.dirname(preview_path)
f.write(f"⏰ TIME: {time.strftime('%Y-%m-%d %H:%M:%S')}\n") # if not os.path.exists(log_dir):
f.write(f"🔍 SEARCH: {search_query}\n") # os.makedirs(log_dir)
f.write(f"📊 RESULTS COUNT: {len(products)}\n") #
f.write(f"{'-'*60}\n") # with open(preview_path, "a", encoding="utf-8") as f:
# f.write(f"\n{'='*60}\n")
if not products: # f.write(f"⏰ TIME: {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
f.write("❌ NO PRODUCTS FOUND\n") # f.write(f"🔍 SEARCH: {search_query}\n")
else: # f.write(f"📊 RESULTS COUNT: {len(products)}\n")
for idx, p in enumerate(products[:5], 1): # f.write(f"{'-'*60}\n")
code = p.get("internal_ref_code", "N/A") #
sale = p.get("sale_price", "N/A") # if not products:
orig = p.get("original_price", "N/A") # f.write("❌ NO PRODUCTS FOUND\n")
disc = p.get("discount_amount", "0") # else:
score = p.get("max_score", p.get("similarity_score", "N/A")) # for idx, p in enumerate(products[:5], 1):
desc = p.get("description_text_full", "No Description") # code = p.get("internal_ref_code", "N/A")
# sale = p.get("sale_price", "N/A")
f.write(f"{idx}. [{code}] Score: {score}\n") # orig = p.get("original_price", "N/A")
f.write(f" 💰 Price: {sale} (Orig: {orig}, Disc: {disc}%)\n") # disc = p.get("discount_amount", "0")
f.write(f" 📝 Desc: {desc}\n") # score = p.get("max_score", p.get("similarity_score", "N/A"))
# desc = p.get("description_text_full", "No Description")
f.write(f"{'='*60}\n") #
print(f"💾 DB Preview (Results) saved to: {preview_path}") # f.write(f"{idx}. [{code}] Score: {score}\n")
except Exception as e: # f.write(f" 💰 Price: {sale} (Orig: {orig}, Disc: {disc}%)\n")
print(f"Save preview log failed: {e}") # f.write(f" 📝 Desc: {desc}\n")
#
# f.write(f"{'='*60}\n")
# print(f"💾 DB Preview (Results) saved to: {preview_path}")
# except Exception as e:
# print(f"Save preview log failed: {e}")
# import logging
# from common.embedding_service import create_embedding_async
# logger = logging.getLogger(__name__)
# def _escape(val: str) -> str:
# """Thoát dấu nháy đơn để tránh SQL Injection cơ bản."""
# return val.replace("'", "''")
# def _get_where_clauses(params) -> list[str]:
# """
# Xây dựng WHERE clauses theo thứ tự ưu tiên dựa trên selectivity thực tế
# FILTER PRIORITY (Based on Canifa catalog analysis):
# 🔥 TIER 1 (99% selectivity):
# 1. SKU Code → 1-5 records
# 🎯 TIER 2 (50-70% selectivity):
# 2. Gender → Splits catalog in half
# 3. Age → Kids vs Adults split
# 4. Product Category → 10-15 categories
# 💎 TIER 3 (30-50% selectivity):
# 5. Material Group → Knit vs Woven (2 groups)
# 6. Price Range → Numeric filtering
# 🎨 TIER 4 (10-30% selectivity):
# 7. Season → 4 seasons
# 8. Style/Fitting → Multiple options
# ⚠️ TIER 5 (<10% selectivity):
# 9. Form details → Granular attributes
# 10. Color → LOWEST selectivity (many SKUs share colors)
# Early return: If SKU exists, skip low-selectivity filters
# """
# clauses = []
# # 🔥 TIER 1: SKU/Product Code (Unique identifier)
# # Selectivity: ~99% → 1 SKU = 1 style (3-5 colors max)
# sku_clause = _get_sku_clause(params)
# if sku_clause:
# clauses.append(sku_clause)
# # Early return optimization: SKU đã xác định product rõ ràng
# # CHỈ GIỮ LẠI price filter (nếu có) để verify budget constraint
# # BỎ QUA: gender, color, style, fitting... vì SKU đã unique
# price_clauses = _get_price_clauses(params)
# if price_clauses:
# clauses.extend(price_clauses)
# return clauses # ⚡ STOP - Không thêm filter khác!
# # 🎯 TIER 2: High-level categorization (50-70% reduction)
# # Gender + Age + Category có selectivity cao nhất trong non-SKU filters
# clauses.extend(_get_high_selectivity_clauses(params))
# # 💎 TIER 3: Material & Price (30-50% reduction)
# material_clause = _get_material_clause(params)
# if material_clause:
# clauses.append(material_clause)
# clauses.extend(_get_price_clauses(params))
# # 🎨 TIER 4: Attributes (10-30% reduction)
# clauses.extend(_get_attribute_clauses(params))
# # ⚠️ TIER 5: Granular details & Color (LAST - lowest selectivity)
# clauses.extend(_get_form_detail_clauses(params))
# color_clause = _get_color_clause(params)
# if color_clause:
# clauses.append(color_clause) # Color ALWAYS LAST!
# return clauses
# def _get_sku_clause(params) -> str | None:
# """
# TIER 1: SKU/Product Code (Highest selectivity - 99%)
# 1 SKU code = 1 product style (may have 3-5 color variants)
# WHY SKU is always priority #1:
# - 1 code = 1 unique product design
# - Adding other filters (color, style, gender) is redundant
# - Only price filter may be kept for budget validation
# Example queries:
# - "Mã 6OT25W010" → Only SKU needed
# - "Mã 6OT25W010 màu xám" → Only SKU (color is for display/selection, not filtering)
# - "Mã 6OT25W010 dưới 500k" → SKU + price (validate budget)
# """
# m_code = getattr(params, "magento_ref_code", None)
# if m_code:
# m = _escape(m_code)
# return f"(magento_ref_code = '{m}' OR internal_ref_code = '{m}')"
# return None
# def _get_color_clause(params) -> str | None:
# """
# TIER 5: Color (LOWEST selectivity - 5-10%)
# Multiple SKUs share the same color (e.g., 50+ gray products)
# ALWAYS filter color LAST after other constraints
# """
# color = getattr(params, "master_color", None)
# if color:
# c = _escape(color).lower()
# return f"(LOWER(master_color) LIKE '%{c}%' OR LOWER(product_color_name) LIKE '%{c}%')"
# return None
# def _get_high_selectivity_clauses(params) -> list[str]:
# """
# TIER 2: High-level categorization (50-70% reduction per filter)
# Order: Gender → Age → Product Category
# """
# clauses = []
# # Gender: Male/Female/Unisex split (50-70% reduction)
# gender = getattr(params, "gender_by_product", None)
# if gender:
# clauses.append(f"gender_by_product = '{_escape(gender)}'")
# # Age: Kids/Adults split (50% reduction of remaining)
# age = getattr(params, "age_by_product", None)
# if age:
# clauses.append(f"age_by_product = '{_escape(age)}'")
# # Product Category: Váy/Áo/Quần... (30-50% reduction)
# product_line = getattr(params, "product_line_vn", None)
# if product_line:
# p = _escape(product_line).lower()
# clauses.append(f"LOWER(product_line_vn) LIKE '%{p}%'")
# return clauses
# def _get_material_clause(params) -> str | None:
# """TIER 3: Material Group - Knit vs Woven (50% split)"""
# material = getattr(params, "material_group", None)
# if material:
# m = _escape(material).lower()
# return f"LOWER(material_group) LIKE '%{m}%'"
# return None
# def _get_price_clauses(params) -> list[str]:
# """TIER 3: Price Range - Numeric filtering (30-40% reduction)"""
# clauses = []
# p_min = getattr(params, "price_min", None)
# if p_min is not None:
# clauses.append(f"sale_price >= {p_min}")
# p_max = getattr(params, "price_max", None)
# if p_max is not None:
# clauses.append(f"sale_price <= {p_max}")
# return clauses
# def _get_attribute_clauses(params) -> list[str]:
# """
# TIER 4: Attributes (10-30% reduction)
# Season, Style, Fitting
# """
# clauses = []
# # Season: 4 seasons (~25% each)
# season = getattr(params, "season", None)
# if season:
# s = _escape(season).lower()
# clauses.append(f"LOWER(season) LIKE '%{s}%'")
# # Style: Basic/Feminine/Sporty... (~15-20% reduction)
# style = getattr(params, "style", None)
# if style:
# st = _escape(style).lower()
# clauses.append(f"LOWER(style) LIKE '%{st}%'")
# # Fitting: Regular/Slim/Loose (~15% reduction)
# fitting = getattr(params, "fitting", None)
# if fitting:
# f = _escape(fitting).lower()
# clauses.append(f"LOWER(fitting) LIKE '%{f}%'")
# # Size Scale: S, M, L, 29, 30... (Specific filtering)
# size = getattr(params, "size_scale", None)
# if size:
# sz = _escape(size).lower()
# clauses.append(f"LOWER(size_scale) LIKE '%{sz}%'")
# return clauses
# def _get_form_detail_clauses(params) -> list[str]:
# """
# TIER 5: Granular form details (<10% reduction each)
# Neckline, Sleeve type
# """
# clauses = []
# form_fields = [
# ("form_neckline", "form_neckline"),
# ("form_sleeve", "form_sleeve"),
# ]
# for param_name, col_name in form_fields:
# val = getattr(params, param_name, None)
# if val:
# v = _escape(val).lower()
# clauses.append(f"LOWER({col_name}) LIKE '%{v}%'")
# return clauses
# async def build_starrocks_query(params, query_vector: list[float] | None = None) -> str:
# """
# Build SQL Hybrid tối ưu với Filter Priority:
# 1. Pre-filtering theo độ ưu tiên (SKU → Exact → Price → Partial)
# 2. Vector Search (HNSW Index) - Semantic understanding
# 3. Flexible Keyword Search (OR + Scoring) - Fuzzy matching fallback
# 4. Grouping (Gom màu theo style)
# """
# # --- Process vector in query field ---
# query_text = getattr(params, "query", None)
# # if query_text and query_vector is None:
# # query_vector = await create_embedding_async(query_text)
# # --- Build filter clauses (OPTIMIZED ORDER) ---
# where_clauses = _get_where_clauses(params)
# where_sql = " AND ".join(where_clauses) if where_clauses else "1=1"
# # --- Build SQL ---
# if query_vector and len(query_vector) > 0:
# v_str = "[" + ",".join(str(v) for v in query_vector) + "]"
# sql = f"""
# WITH top_sku_candidates AS (
# SELECT
# approx_cosine_similarity(vector, {v_str}) as similarity_score,
# internal_ref_code,
# product_name,
# sale_price,
# original_price,
# master_color,
# product_image_url,
# product_image_url_thumbnail,
# product_web_url,
# description_text,
# material,
# material_group,
# gender_by_product,
# age_by_product,
# season,
# style,
# fitting,
# form_neckline,
# form_sleeve,
# product_line_vn,
# product_color_name
# FROM shared_source.magento_product_dimension_with_text_embedding
# WHERE {where_sql} AND vector IS NOT NULL
# ORDER BY similarity_score DESC
# LIMIT 50
# )
# SELECT
# internal_ref_code,
# ANY_VALUE(product_name) as product_name,
# ANY_VALUE(sale_price) as sale_price,
# ANY_VALUE(original_price) as original_price,
# GROUP_CONCAT(DISTINCT master_color ORDER BY master_color SEPARATOR ', ') as available_colors,
# ANY_VALUE(product_image_url) as product_image_url,
# ANY_VALUE(product_image_url_thumbnail) as product_image_url_thumbnail,
# ANY_VALUE(product_web_url) as product_web_url,
# ANY_VALUE(description_text) as description_text,
# ANY_VALUE(material) as material,
# ANY_VALUE(material_group) as material_group,
# ANY_VALUE(gender_by_product) as gender_by_product,
# ANY_VALUE(age_by_product) as age_by_product,
# ANY_VALUE(season) as season,
# ANY_VALUE(style) as style,
# ANY_VALUE(fitting) as fitting,
# ANY_VALUE(form_neckline) as form_neckline,
# ANY_VALUE(form_sleeve) as form_sleeve,
# ANY_VALUE(product_line_vn) as product_line_vn,
# MAX(similarity_score) as max_score
# FROM top_sku_candidates
# GROUP BY internal_ref_code
# ORDER BY max_score DESC
# LIMIT 10
# """
# else:
# # ⚡ FALLBACK: FLEXIBLE KEYWORD SEARCH (OR + SCORING)
# # Giải quyết case: User search "áo khoác nỉ" → DB có "Áo nỉ nam"
# keywords = getattr(params, "keywords", None)
# keyword_score_sql = ""
# keyword_filter = ""
# if keywords:
# k_clean = _escape(keywords).lower().strip()
# if k_clean:
# words = k_clean.split()
# # Build scoring expression: Each matched word = +1 point
# # Example: "áo khoác nỉ" (3 words)
# # - "Áo nỉ nam" matches 2/3 → Score = 2
# # - "Áo khoác nỉ hoodie" matches 3/3 → Score = 3
# score_terms = [
# f"(CASE WHEN LOWER(product_name) LIKE '%{w}%' THEN 1 ELSE 0 END)"
# for w in words
# ]
# keyword_score_sql = f"({' + '.join(score_terms)}) as keyword_match_score"
# # Minimum threshold: At least 50% of words must match
# # Example: 3 words → need at least 2 matches (66%)
# # 2 words → need at least 1 match (50%)
# min_matches = max(1, len(words) // 2)
# keyword_filter = f" AND ({' + '.join(score_terms)}) >= {min_matches}"
# # Select clause with optional scoring
# select_score = f", {keyword_score_sql}" if keyword_score_sql else ""
# order_by = "keyword_match_score DESC, sale_price ASC" if keyword_score_sql else "sale_price ASC"
# sql = f"""
# SELECT
# internal_ref_code,
# ANY_VALUE(product_name) as product_name,
# ANY_VALUE(sale_price) as sale_price,
# ANY_VALUE(original_price) as original_price,
# GROUP_CONCAT(DISTINCT master_color ORDER BY master_color SEPARATOR ', ') as available_colors,
# ANY_VALUE(product_image_url) as product_image_url,
# ANY_VALUE(product_image_url_thumbnail) as product_image_url_thumbnail,
# ANY_VALUE(product_web_url) as product_web_url,
# ANY_VALUE(description_text) as description_text,
# ANY_VALUE(material) as material,
# ANY_VALUE(material_group) as material_group,
# ANY_VALUE(gender_by_product) as gender_by_product,
# ANY_VALUE(age_by_product) as age_by_product,
# ANY_VALUE(season) as season,
# ANY_VALUE(style) as style,
# ANY_VALUE(fitting) as fitting,
# ANY_VALUE(form_neckline) as form_neckline,
# ANY_VALUE(form_sleeve) as form_sleeve,
# ANY_VALUE(product_line_vn) as product_line_vn
# {select_score}
# FROM shared_source.magento_product_dimension_with_text_embedding
# WHERE {where_sql} {keyword_filter}
# GROUP BY internal_ref_code
# HAVING COUNT(*) > 0
# ORDER BY {order_by}
# LIMIT 10
# """
# # Log filter statistics
# filter_info = f"Mode: {'Vector' if query_vector else 'Keyword'}, Filters: {len(where_clauses)}"
# if where_clauses:
# # Identify high-priority filters used
# has_sku = any('internal_ref_code' in c or 'magento_ref_code' in c for c in where_clauses)
# has_gender = any('gender_by_product' in c for c in where_clauses)
# has_category = any('product_line_vn' in c for c in where_clauses)
# priority_info = []
# if has_sku:
# priority_info.append("SKU")
# if has_gender:
# priority_info.append("Gender")
# if has_category:
# priority_info.append("Category")
# if priority_info:
# filter_info += f", Priority: {'+'.join(priority_info)}"
# logger.info(f"📊 {filter_info}")
# # Write SQL to file for debugging
# try:
# with open(r"d:\cnf\chatbot_canifa\backend\embedding.txt", "w", encoding="utf-8") as f:
# f.write(sql)
# except Exception as e:
# logger.error(f"Failed to write SQL to embedding.txt: {e}")
# return sql
import logging
import time
from common.embedding_service import create_embedding_async
logger = logging.getLogger(__name__)
def _escape(val: str) -> str:
"""Thoát dấu nháy đơn để tránh SQL Injection cơ bản."""
return val.replace("'", "''")
def _get_where_clauses(params) -> list[str]:
"""Xây dựng danh sách các điều kiện lọc từ params."""
clauses = []
clauses.extend(_get_price_clauses(params))
clauses.extend(_get_metadata_clauses(params))
clauses.extend(_get_special_clauses(params))
return clauses
def _get_price_clauses(params) -> list[str]:
"""Lọc theo giá."""
clauses = []
p_min = getattr(params, "price_min", None)
if p_min is not None:
clauses.append(f"sale_price >= {p_min}")
p_max = getattr(params, "price_max", None)
if p_max is not None:
clauses.append(f"sale_price <= {p_max}")
return clauses
def _get_metadata_clauses(params) -> list[str]:
"""Xây dựng điều kiện lọc từ metadata (Phối hợp Exact và Partial)."""
clauses = []
# 1. Exact Match (Giới tính, Độ tuổi) - Các trường này cần độ chính xác tuyệt đối
exact_fields = [
("gender_by_product", "gender_by_product"),
("age_by_product", "age_by_product"),
]
for param_name, col_name in exact_fields:
val = getattr(params, param_name, None)
if val:
clauses.append(f"{col_name} = '{_escape(val)}'")
# 2. Partial Match (LIKE) - Giúp map text linh hoạt hơn (Chất liệu, Dòng SP, Phong cách...)
# Cái này giúp map: "Yarn" -> "Yarn - Sợi", "Knit" -> "Knit - Dệt Kim"
partial_fields = [
("season", "season"),
("material_group", "material_group"),
("product_line_vn", "product_line_vn"),
("style", "style"),
("fitting", "fitting"),
("form_neckline", "form_neckline"),
("form_sleeve", "form_sleeve"),
]
for param_name, col_name in partial_fields:
val = getattr(params, param_name, None)
if val:
v = _escape(val).lower()
# Dùng LOWER + LIKE để cân mọi loại ký tự thừa hoặc hoa/thường
clauses.append(f"LOWER({col_name}) LIKE '%{v}%'")
return clauses
def _get_special_clauses(params) -> list[str]:
"""Các trường hợp đặc biệt: Mã sản phẩm, Màu sắc."""
clauses = []
# Mã sản phẩm / SKU
m_code = getattr(params, "magento_ref_code", None)
if m_code:
m = _escape(m_code)
clauses.append(f"(magento_ref_code = '{m}' OR internal_ref_code = '{m}')")
# Màu sắc
color = getattr(params, "master_color", None)
if color:
c = _escape(color).lower()
clauses.append(f"(LOWER(master_color) LIKE '%{c}%' OR LOWER(product_color_name) LIKE '%{c}%')")
return clauses
async def build_starrocks_query(params, query_vector: list[float] | None = None) -> str:
"""
Build SQL Hybrid tối ưu với POST-FILTERING Strategy & Anti-Duplication.
🔥 CHIẾN LƯỢC TỐI ƯU:
1. Vector Search TRƯỚC (LIMIT 100) để tận dụng HNSW Index (tốc độ ~50ms).
2. JOIN chính xác theo (code + màu) để tránh bùng nổ dữ liệu (Data Explosion).
3. Dùng MAX_BY để lấy description của đúng thằng có score cao nhất.
"""
logger.info("🔧 [DEBUG] build_starrocks_query STARTED")
# --- 1. Xử lý Vector ---
query_text = getattr(params, "query", None)
if query_text and query_vector is None:
emb_start = time.time()
query_vector = await create_embedding_async(query_text)
emb_time = (time.time() - emb_start) * 1000
logger.info(f"⏱️ [TIMER] Embedding Generation: {emb_time:.2f}ms")
# --- 2. Xây dựng Filter cho POST-FILTERING ---
where_clauses = _get_where_clauses(params)
post_filter_sql = " AND ".join(where_clauses) if where_clauses else "1=1"
# --- 3. Build SQL ---
if query_vector and len(query_vector) > 0:
v_str = "[" + ",".join(str(v) for v in query_vector) + "]"
# Alias các trường trong filter sang bảng t2 để tránh lỗi ambiguous
post_filter_aliased = post_filter_sql
fields_to_alias = [
"sale_price",
"gender_by_product",
"age_by_product",
"material_group",
"season",
"style",
"fitting",
"form_neckline",
"form_sleeve",
"product_line_vn",
"magento_ref_code",
"internal_ref_code",
"master_color",
"product_color_name",
]
for field in fields_to_alias:
post_filter_aliased = post_filter_aliased.replace(field, f"t2.{field}")
sql = f"""
WITH top_candidates AS (
SELECT /*+ SET_VAR(ann_params='{{"ef_search":64}}') */
internal_ref_code,
product_color_code,
approx_cosine_similarity(vector, {v_str}) as similarity_score
FROM shared_source.magento_product_dimension_with_text_embedding
WHERE vector IS NOT NULL
ORDER BY similarity_score DESC
LIMIT 100
)
SELECT
t1.internal_ref_code,
-- MAX_BY đảm bảo mô tả đi kèm đúng với thằng cao điểm nhất (Data Integrity)
MAX_BY(t2.description_text_full, t1.similarity_score) as description_text_full,
MAX(t1.similarity_score) as max_score
FROM top_candidates t1
JOIN shared_source.magento_product_dimension_with_text_embedding t2
ON t1.internal_ref_code = t2.internal_ref_code
AND t1.product_color_code = t2.product_color_code -- QUAN TRỌNG: Tránh nhân bản dòng theo màu
WHERE {post_filter_aliased}
GROUP BY t1.internal_ref_code
ORDER BY max_score DESC
LIMIT 10
"""
else:
# FALLBACK: Keyword search
keywords = getattr(params, "keywords", None)
k_filter = ""
if keywords:
k = _escape(keywords).lower()
k_filter = f" AND LOWER(product_name) LIKE '%{k}%'"
where_sql = " AND ".join(where_clauses) if where_clauses else "1=1"
sql = f"""
SELECT
internal_ref_code,
-- Lấy đại diện 1 mô tả cho keyword search
MAX(description_text_full) as description_text_full,
MIN(sale_price) as min_price
FROM shared_source.magento_product_dimension_with_text_embedding
WHERE {where_sql} {k_filter}
GROUP BY internal_ref_code
ORDER BY min_price ASC
LIMIT 10
"""
# --- 4. Ghi Log Debug ---
try:
debug_path = r"d:\cnf\chatbot_canifa\backend\query.txt"
with open(debug_path, "w", encoding="utf-8") as f:
f.write(sql)
logger.info(f"💾 SQL saved to: {debug_path}")
except Exception as e:
logger.error(f"Save log failed: {e}")
return sql
from fastapi import APIRouter, HTTPException, BackgroundTasks import json
from pydantic import BaseModel import logging
from typing import List, Optional
import time
import random import random
import time
from fastapi import APIRouter, BackgroundTasks, HTTPException
from pydantic import BaseModel
from agent.tools.data_retrieval_tool import SearchItem, data_retrieval_tool
logger = logging.getLogger(__name__)
router = APIRouter() router = APIRouter()
# --- MODELS --- # --- MODELS ---
class MockQueryRequest(BaseModel): class MockQueryRequest(BaseModel):
user_query: str user_query: str
user_id: Optional[str] = "test_user" user_id: str | None = "test_user"
session_id: Optional[str] = None session_id: str | None = None
class MockDBRequest(BaseModel): class MockDBRequest(BaseModel):
vector: Optional[List[float]] = None query: str | None = None
magento_ref_code: str | None = None
price_min: float | None = None
price_max: float | None = None
top_k: int = 10 top_k: int = 10
# --- MOCK DATA ---
MOCK_PRODUCTS = [ class MockRetrieverRequest(BaseModel):
{ user_query: str
"internal_ref_code": "8TE24W001", price_min: float | None = None
"description_text_full": "Áo phông nam cotton thoáng mát, form regular fit.", price_max: float | None = None
"sale_price": 199000.0, magento_ref_code: str | None = None
"original_price": 299000.0, user_id: str | None = "test_user"
"discount_amount": 100000.0, session_id: str | None = None
"max_score": 0.89
},
{ # --- MOCK LLM RESPONSES (không gọi OpenAI) ---
"internal_ref_code": "8JE24W002", MOCK_AI_RESPONSES = [
"description_text_full": "Quần Jeans nam ống đứng, chất liệu denim co giãn.", "Dựa trên tìm kiếm của bạn, tôi tìm thấy các sản phẩm phù hợp với nhu cầu của bạn. Những mặt hàng này có chất lượng tốt và giá cả phải chăng.",
"sale_price": 399000.0, "Tôi gợi ý cho bạn những sản phẩm sau. Chúng đều là những lựa chọn phổ biến và nhận được đánh giá cao từ khách hàng.",
"original_price": 499000.0, "Dựa trên tiêu chí tìm kiếm của bạn, đây là những sản phẩm tốt nhất mà tôi có thể giới thiệu.",
"discount_amount": 100000.0, "Những sản phẩm này hoàn toàn phù hợp với yêu cầu của bạn. Hãy xem chi tiết để chọn sản phẩm yêu thích nhất.",
"max_score": 0.85 "Tôi đã tìm được các mặt hàng tuyệt vời cho bạn. Hãy kiểm tra chúng để tìm ra lựa chọn tốt nhất.",
},
{
"internal_ref_code": "8SK24W003",
"description_text_full": "Váy liền thân nữ dáng xòe, họa tiết hoa nhí.",
"sale_price": 350000.0,
"original_price": 450000.0,
"discount_amount": 100000.0,
"max_score": 0.82
}
] ]
# --- ENDPOINTS --- # --- ENDPOINTS ---
@router.post("/mock/agent/chat", summary="Mock LLM Chat Endpoint (Fast)")
async def mock_chat(req: MockQueryRequest): @router.post("/mock/agent/chat", summary="Mock Agent Chat (Fake LLM - Stress Test)")
async def mock_chat(req: MockQueryRequest, background_tasks: BackgroundTasks):
""" """
Giả lập phản hồi của Chatbot (bỏ qua LLM & DB thật). Mock Agent Chat với FAKE LLM (không gọi OpenAI):
Dùng để test tải (Load Test) xem server chịu được bao nhiêu request/s. - Dùng data_retrieval_tool THẬT (retriever thật, embedding thật)
- LLM fake (return response mock nhanh)
- Dùng để STRESS TEST mà không tốn tiền OpenAI
Để test toàn bộ flow chatbot + retriever mà không lo chi phí API.
""" """
# Simulate slight processing time (10ms - 50ms) try:
time.sleep(random.uniform(0.01, 0.05)) logger.info(f"🚀 [Mock Agent Chat] Starting with query: {req.user_query}")
start_time = time.time()
# Step 1: Gọi data_retrieval_tool THẬT để lấy sản phẩm
logger.info("🔍 Calling data_retrieval_tool...")
search_item = SearchItem(
query=req.user_query, magento_ref_code=None, price_min=None, price_max=None, action="search"
)
result_json = await data_retrieval_tool([search_item])
result = json.loads(result_json)
search_results = result.get("results", [{}])[0]
products = search_results.get("products", [])
# Step 2: LLM FAKE (không gọi OpenAI, chỉ return mock response)
logger.info("🤖 [FAKE LLM] Generating mock response...")
fake_llm_time = random.uniform(0.01, 0.05) # Simulate LLM latency
time.sleep(fake_llm_time)
mock_response = random.choice(MOCK_AI_RESPONSES)
product_ids = [p.get("internal_ref_code", "") for p in products[:3]]
elapsed_time = time.time() - start_time
logger.info(f"✅ Mock Agent Chat completed in {elapsed_time:.3f}s")
return { return {
"status": "success", "status": "success",
"user_query": req.user_query, "user_query": req.user_query,
"user_id": req.user_id,
"session_id": req.session_id,
"ai_response": { "ai_response": {
"content": f"Đây là câu trả lời giả lập cho: '{req.user_query}'. Hệ thống đang hoạt động tốt!", "content": mock_response,
"role": "assistant" "role": "assistant",
"is_mock": True,
}, },
"product_ids": ["8TE24W001", "8JE24W002"], "product_ids": product_ids,
"processing_time": "0.03s" "total_products_found": len(products),
"processing_time_ms": round(elapsed_time * 1000, 2),
} }
@router.post("/mock/db/search", summary="Mock StarRocks Vector Search") except Exception as e:
logger.error(f"❌ Error in mock agent chat: {e!s}", exc_info=True)
raise HTTPException(status_code=500, detail=f"Mock Agent Chat Error: {e!s}")
@router.post("/mock/db/search", summary="Real Data Retrieval Tool (Agent Tool)")
async def mock_db_search(req: MockDBRequest): async def mock_db_search(req: MockDBRequest):
""" """
Giả lập truy vấn Vector Database (StarRocks). Dùng `data_retrieval_tool` THẬT từ Agent:
Input: Vector embedding (nếu có). - Nếu có magento_ref_code → CODE SEARCH (không cần embedding)
Output: Danh sách sản phẩm mock giống cấu trúc SQL thật. - Nếu có query → HYDE SEMANTIC SEARCH (embedding + vector search)
- Lọc theo giá nếu có price_min/price_max
- Trả về sản phẩm thực từ StarRocks
Format input giống SearchItem của agent tool.
""" """
# Simulate DB Latency (e.g., 50ms - 100ms) try:
time.sleep(random.uniform(0.05, 0.1)) logger.info("📍 Data Retrieval Tool called")
start_time = time.time()
# Xây dựng SearchItem từ request
search_item = SearchItem(
query=req.query or "sản phẩm",
magento_ref_code=req.magento_ref_code,
price_min=req.price_min,
price_max=req.price_max,
action="search",
)
logger.info(f"🔧 Search params: {search_item.dict(exclude_none=True)}")
# Gọi data_retrieval_tool THẬT
result_json = await data_retrieval_tool([search_item])
result = json.loads(result_json)
# Hardcoded vector from user request (Just for checking format) elapsed_time = time.time() - start_time
# [0.01643567718565464, -0.0008633101242594421, ...] logger.info(f"✅ Data Retrieval completed in {elapsed_time:.3f}s")
return { return {
"status": "success", "status": result.get("status", "success"),
"total_hits": 1340, "search_params": search_item.dict(exclude_none=True),
"results": MOCK_PRODUCTS, # Return existing mock data "total_results": len(result.get("results", [{}])[0].get("products", [])),
"query_vector_dim": len(req.vector) if req.vector else 0, "products": result.get("results", [{}])[0].get("products", []),
"note": "This is MOCK data from memory, no actual DB connection." "processing_time_ms": round(elapsed_time * 1000, 2),
"raw_result": result,
} }
except Exception as e:
logger.error(f"❌ Error in DB search: {e!s}", exc_info=True)
raise HTTPException(status_code=500, detail=f"DB Search Error: {e!s}")
@router.post("/mock/retriverdb", summary="Real Embedding + Real DB Vector Search")
async def mock_retriever_db(req: MockRetrieverRequest):
"""
API thực tế để test Retriever + DB Search (dùng agent tool):
- Lấy query từ user
- Embedding THẬT (gọi OpenAI embedding trong tool)
- Vector search THẬT trong StarRocks
- Trả về kết quả sản phẩm thực (bỏ qua LLM)
Dùng để test performance của embedding + vector search riêng biệt.
"""
try:
logger.info(f"📍 Retriever DB started: {req.user_query}")
start_time = time.time()
# Xây dựng SearchItem từ request
search_item = SearchItem(
query=req.user_query,
magento_ref_code=req.magento_ref_code,
price_min=req.price_min,
price_max=req.price_max,
action="search",
)
logger.info(f"🔧 Retriever params: {search_item.dict(exclude_none=True)}")
# Gọi data_retrieval_tool THẬT (embedding + vector search)
result_json = await data_retrieval_tool([search_item])
result = json.loads(result_json)
elapsed_time = time.time() - start_time
logger.info(f"✅ Retriever completed in {elapsed_time:.3f}s")
# Parse kết quả
search_results = result.get("results", [{}])[0]
products = search_results.get("products", [])
return {
"status": result.get("status", "success"),
"user_query": req.user_query,
"user_id": req.user_id,
"session_id": req.session_id,
"search_params": search_item.dict(exclude_none=True),
"total_results": len(products),
"products": products,
"processing_time_ms": round(elapsed_time * 1000, 2),
}
except Exception as e:
logger.error(f"❌ Error in retriever DB: {e!s}", exc_info=True)
raise HTTPException(status_code=500, detail=f"Retriever DB Error: {e!s}")
"""
Clerk Authentication Middleware
Verifies Clerk JWT tokens from frontend requests using Clerk Backend API SDK
"""
import base64
import json
import logging
from fastapi import Depends, HTTPException, Request, status
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
try:
from clerk_backend_api import Clerk
CLERK_AVAILABLE = True
except ImportError:
Clerk = None
CLERK_AVAILABLE = False
from config import CLERK_SECRET_KEY
logger = logging.getLogger(__name__)
# Security scheme for OpenAPI docs
security = HTTPBearer(auto_error=False)
# Global Clerk client instance
_clerk_client = None
def get_clerk_client():
"""Get or initialize Clerk client"""
global _clerk_client
if not CLERK_AVAILABLE:
logger.warning("⚠️ clerk-backend-api not installed. Install with: pip install clerk-backend-api")
return None
if _clerk_client is None and CLERK_SECRET_KEY:
try:
if Clerk is None:
return None
_clerk_client = Clerk(bearer_auth=CLERK_SECRET_KEY)
logger.info("✅ Clerk client initialized")
except Exception as e:
logger.error(f"❌ Failed to initialize Clerk client: {e}")
return None
return _clerk_client
async def verify_clerk_token(request: Request, credentials: HTTPAuthorizationCredentials | None = None) -> dict:
"""
Verify Clerk JWT token and return user info using Clerk Backend API SDK.
Args:
request: FastAPI Request object
credentials: HTTP Bearer token from Authorization header (optional)
Returns:
dict: User information from Clerk
Raises:
HTTPException: If token is invalid or missing
"""
clerk = get_clerk_client()
if not clerk:
# In development, if Clerk is not configured, allow requests
# In production, you should raise an error
logger.warning("⚠️ Clerk not configured, allowing request (dev mode)")
return {"user_id": "dev_user", "email": "dev@example.com", "clerk_user_id": "dev_user"}
try:
# Convert FastAPI Request to httpx.Request for Clerk SDK
import httpx
# Create httpx.Request from FastAPI Request
url = str(request.url)
method = request.method
headers = dict(request.headers)
# Get body if exists
body = await request.body() if hasattr(request, "body") else b""
httpx_request = httpx.Request(method=method, url=url, headers=headers, content=body)
# Authenticate request using Clerk SDK
# Try with options first, fallback to without options
try:
# Debug: Log token presence
auth_header = request.headers.get("Authorization", "")
has_token = bool(auth_header and auth_header.startswith("Bearer "))
token_preview = ""
if auth_header and auth_header.startswith("Bearer "):
token = auth_header.replace("Bearer ", "")
token_preview = token[:20] + "..." if len(token) > 20 else token[:10] + "..."
logger.info(f"🔍 Clerk auth: has_token={has_token}, path={request.url.path}, token_preview={token_preview}")
# Try to import AuthenticateRequestOptions if available
AuthenticateRequestOptions = None
try:
from clerk_backend_api.jwks_helpers import AuthenticateRequestOptions # type: ignore
except ImportError:
try:
from clerk_backend_api.security.types import AuthenticateRequestOptions # type: ignore
except ImportError:
pass
if AuthenticateRequestOptions:
request_state = clerk.authenticate_request(httpx_request, AuthenticateRequestOptions())
else:
# Fallback: authenticate without options (if SDK supports it)
# Some SDK versions require options, so we'll handle the error
try:
# Try with empty dict as options
request_state = clerk.authenticate_request(httpx_request, {}) # type: ignore
except (TypeError, ValueError):
# If that fails, try without any options
request_state = clerk.authenticate_request(httpx_request) # type: ignore
except Exception as auth_error:
logger.error(f"❌ Clerk authenticate_request failed: {auth_error}")
logger.error(f" Path: {request.url.path}, Method: {request.method}")
logger.error(f" Has Auth Header: {bool(request.headers.get('Authorization'))}")
auth_header = request.headers.get("Authorization", "")
if auth_header and auth_header.startswith("Bearer "):
token = auth_header.replace("Bearer ", "")
logger.error(f" Token preview: {token[:30]}... (length: {len(token)})")
logger.error(f" Error type: {type(auth_error).__name__}")
logger.error(f" Error details: {auth_error!s}")
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, detail=f"Authentication failed: {auth_error!s}"
)
if not request_state.is_signed_in:
logger.warning(f"⚠️ Clerk auth: request_state.is_signed_in=False for {request.url.path}")
auth_header = request.headers.get("Authorization", "")
logger.warning(f" Auth header present: {bool(auth_header)}")
if auth_header and auth_header.startswith("Bearer "):
token = auth_header.replace("Bearer ", "")
logger.warning(f" Token preview: {token[:30]}... (length: {len(token)})")
logger.warning(f" Token starts with 'eyJ': {token.startswith('eyJ')}")
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, detail="Not signed in. Please check your Clerk token."
)
# Extract user info from request state
# Try different ways to get user_id based on SDK version
user_id = None
try:
# Try to get from auth object
auth = request_state.to_auth() if hasattr(request_state, "to_auth") else None
if auth:
user_id = getattr(auth, "user_id", None) or getattr(auth, "userId", None)
except Exception:
pass
# If still no user_id, try direct access
if not user_id:
user_id = getattr(request_state, "user_id", None) or getattr(request_state, "userId", None)
if not user_id:
# Fallback: try decode JWT sub from Authorization header (best effort, no verify)
token = None
if credentials and credentials.credentials:
token = credentials.credentials
elif request.headers.get("Authorization", "").startswith("Bearer "):
token = request.headers.get("Authorization", "")[7:]
if token:
try:
parts = token.split(".")
if len(parts) >= 2:
payload_b64 = parts[1] + "=" * (-len(parts[1]) % 4)
payload = json.loads(base64.urlsafe_b64decode(payload_b64).decode("utf-8"))
user_id = payload.get("sub") or payload.get("userId") or payload.get("uid")
except Exception as decode_err:
logger.warning(f"⚠️ Failed to decode Clerk JWT payload for user_id: {decode_err}")
if not user_id:
logger.warning("⚠️ Could not extract user_id from Clerk request_state or JWT; using 'unknown'")
user_id = "unknown"
return {
"user_id": user_id,
"clerk_user_id": user_id,
"email": None, # Will be fetched if needed via API
"first_name": None,
"last_name": None,
}
except HTTPException:
raise
except Exception as e:
logger.error(f"❌ Clerk token verification failed: {e}")
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid or expired token")
async def get_current_user(
request: Request, credentials: HTTPAuthorizationCredentials | None = Depends(security)
) -> dict:
"""
Get current authenticated user from Clerk token.
Can be used as FastAPI dependency: user = Depends(get_current_user)
Also checks X-User-Id header as fallback for development.
"""
# Try to verify Clerk token first
try:
return await verify_clerk_token(request, credentials)
except HTTPException:
pass
# Fallback: Check X-User-Id header (for development/testing)
user_id_header = request.headers.get("X-User-Id")
if user_id_header:
logger.warning(f"⚠️ Using X-User-Id header (dev mode): {user_id_header}")
return {
"user_id": user_id_header,
"email": f"{user_id_header}@example.com",
"clerk_user_id": user_id_header,
}
# No auth found
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Authentication required")
async def get_optional_user(
request: Request, credentials: HTTPAuthorizationCredentials | None = Depends(security)
) -> dict | None:
"""
Get current user if authenticated, otherwise return None.
Useful for endpoints that work with or without auth.
"""
try:
return await get_current_user(request, credentials)
except HTTPException:
return None
def get_user_from_request(request: Request) -> dict:
"""
Helper function để lấy user info từ request.state (đã được middleware verify).
Middleware đã verify token và attach vào request.state.user,
nên routes chỉ cần gọi hàm này để lấy user info.
Args:
request: FastAPI Request object
Returns:
dict: User info từ request.state.user
Raises:
HTTPException 401: Nếu không có user trong request.state (chưa được authenticate)
"""
user = getattr(request.state, "user", None)
if not user:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Authentication required")
return user
def get_user_id_from_request(request: Request) -> str:
"""
Helper function để lấy user_id từ request.state (đã được middleware verify).
Args:
request: FastAPI Request object
Returns:
str: User ID (Clerk ID) từ request.state.user_id
Raises:
HTTPException 401: Nếu không có user_id trong request.state
"""
user_id = getattr(request.state, "user_id", None)
if not user_id:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, detail="User ID not found. Authentication required."
)
return user_id
def get_clerk_token_from_request(request: Request) -> str | None:
"""
Helper function để lấy Clerk token từ request.state (để forward cho Supabase RLS).
Args:
request: FastAPI Request object
Returns:
Optional[str]: Clerk token nếu có, None nếu không có
"""
return getattr(request.state, "clerk_token", None)
# services/law_db.py
import asyncio
import os
from typing import Any
import httpx
# Support absolute import when run as module, and fallback when run as script
try:
from common.supabase_client import (
close_supabase_client,
init_supabase_client,
supabase_rpc_call,
)
from common.openai_client import get_openai_client
except ImportError:
import os as _os
import sys
_ROOT = _os.path.dirname(_os.path.dirname(_os.path.dirname(_os.path.abspath(__file__))))
if _ROOT not in sys.path:
sys.path.append(_ROOT)
from common.supabase_client import (
init_supabase_client,
supabase_rpc_call,
)
from common.openai_client import get_openai_client
# ====================== CONFIG ======================
def get_supabase_config():
"""Lazy load config để tránh lỗi khi chưa có env vars"""
return {
"url": f"{os.environ['SUPABASE_URL']}/rest/v1/rpc/hoi_phap_luat_all_in_one",
"headers": {
"apikey": os.environ["SUPABASE_ANON_KEY"],
"Authorization": f"Bearer {os.environ['SUPABASE_ANON_KEY']}",
"Content-Type": "application/json; charset=utf-8",
"Accept": "application/json",
},
}
# ====================== HTTP HELPERS ======================
async def _post_with_retry(client: httpx.AsyncClient, url: str, **kw) -> httpx.Response:
"""POST với retry/backoff."""
last_exc: Exception | None = None
for i in range(3):
try:
r = await client.post(url, **kw)
r.raise_for_status()
return r
except Exception as e:
last_exc = e
if i == 2:
raise
await asyncio.sleep(0.4 * (2**i))
raise last_exc # về lý thuyết không tới đây
# ====================== LABEL HELPER ======================
def _label_for_call(call: dict[str, Any], index: int) -> str:
"""Tạo nhãn hiển thị cho mỗi lệnh gọi dựa trên tham số (để phân biệt kết quả)."""
params = call.get("params") or {}
if params.get("p_so_hieu"):
return str(params["p_so_hieu"])
if params.get("p_vb_pattern"):
return str(params["p_vb_pattern"])
return f"Truy vấn {index}"
# ====================== RAW FETCHERS ======================
async def _get_embedding(text: str) -> list[float]:
"""Gọi OpenAI API để lấy embedding vector của đoạn văn bản."""
if not text:
return []
# already imported get_openai_client above with fallback
OAI = get_openai_client()
normalized_text = (text or "").strip().lower()
input_text = normalized_text[:8000] # Giới hạn độ dài
try:
resp = await OAI.embeddings.create(
model="text-embedding-3-small",
input=input_text,
)
return resp.data[0].embedding
except Exception as e:
print(f"❌ Lỗi OpenAI embedding: {e}")
return []
async def law_db_fetch_one(params: dict[str, Any]) -> list[dict[str, Any]]:
"""
Gọi trực tiếp bằng httpx thay vì RPC
"""
# Xử lý p_vector_text thành embedding
processed_params = dict(params)
if processed_params.get("p_vector_text"):
vector_text = processed_params.pop("p_vector_text")
embedding = await _get_embedding(vector_text)
if embedding and len(embedding) > 0:
processed_params["p_vector"] = embedding
# Set mode to semantic nếu chưa có
if "p_mode" not in processed_params and "_mode" not in processed_params:
processed_params["p_mode"] = "semantic"
# Map parameters to correct function signature
mapped_params = {}
for key, value in processed_params.items():
# Skip None values
if value is None:
continue
# Validate p_vector format
if key == "p_vector":
if isinstance(value, list) and len(value) > 0:
# Check for NaN or invalid values
import math
valid_vector = [v for v in value if isinstance(v, (int, float)) and not math.isnan(v)]
if len(valid_vector) == len(value):
mapped_params["p_vector"] = value
else:
print("⚠️ Warning: p_vector contains invalid values (NaN/inf), skipping")
elif isinstance(value, list) and len(value) == 0:
print("⚠️ Warning: p_vector is empty, skipping")
else:
mapped_params["p_vector"] = value
elif key in {"_mode", "mode"}:
mapped_params["p_mode"] = value
elif key == "p_vb_pattern":
mapped_params["p_vb_pattern"] = value
elif key == "p_so_hieu":
mapped_params["p_so_hieu"] = value
elif key == "p_trang_thai":
mapped_params["p_trang_thai"] = value
elif key == "p_co_quan":
mapped_params["p_co_quan"] = value
elif key == "p_loai_vb":
mapped_params["p_loai_vb"] = value
elif key == "p_nam_from":
# Validate integer
if isinstance(value, (int, float)) and not (isinstance(value, float) and value != int(value)):
mapped_params["p_nam_from"] = int(value)
elif value is not None:
print(f"⚠️ Warning: p_nam_from has invalid type/value: {value}, skipping")
elif key == "p_nam_to":
# Validate integer
if isinstance(value, (int, float)) and not (isinstance(value, float) and value != int(value)):
mapped_params["p_nam_to"] = int(value)
elif value is not None:
print(f"⚠️ Warning: p_nam_to has invalid type/value: {value}, skipping")
elif key == "p_only_source":
mapped_params["p_only_source"] = value
elif key == "p_chapter":
mapped_params["p_chapter"] = value
elif key == "p_article":
mapped_params["p_article"] = value
elif key == "p_phu_luc":
mapped_params["p_phu_luc"] = value
elif key == "p_limit":
# Validate integer
if isinstance(value, (int, float)) and not (isinstance(value, float) and value != int(value)):
mapped_params["p_limit"] = int(value)
elif value is not None:
print(f"⚠️ Warning: p_limit has invalid type/value: {value}, skipping")
elif key == "p_ef_search":
# Validate integer
if isinstance(value, (int, float)) and not (isinstance(value, float) and value != int(value)):
mapped_params["p_ef_search"] = int(value)
elif value is not None:
print(f"⚠️ Warning: p_ef_search has invalid type/value: {value}, skipping")
# Gọi qua Supabase shared client (đảm bảo đã init)
try:
await init_supabase_client()
# DEBUG: Print ra JSON sẽ gửi
print("📤 GỬI JSON PAYLOAD:")
import json
debug_payload = json.dumps(mapped_params, ensure_ascii=False, indent=2)
print(debug_payload[:500]) # Print 500 chars đầu để check
rows = await supabase_rpc_call("hoi_phap_luat_all_in_one", mapped_params)
print(f"✅ NHẬN RESULT: {len(rows)} rows")
if rows:
print(f"First row keys: {list(rows[0].keys())}")
_nd = rows[0].get("NoiDung") or rows[0].get("NoiDungDieu") or rows[0].get("NoiDungPhuLuc") or ""
try:
print(f"NoiDung length: {len(_nd)}")
except Exception:
print("NoiDung length: (unavailable)")
return rows or []
except Exception as e:
print(f"❌ HTTPX call failed: {e}")
import traceback
traceback.print_exc()
return []
async def law_db_fetch_plan(calls: list[dict[str, Any]]) -> list[dict[str, Any]]:
"""
Nhận danh sách calls (mỗi call có .params) -> chạy song song -> trả:
[
{"label": "...", "rows": [ ... ]},
...
]
"""
if not calls:
return []
async def run_one(call: dict[str, Any], idx: int) -> dict[str, Any]:
label = _label_for_call(call, idx)
params = call.get("params", {})
# Truyền mode xuống để map thành p_mode trong law_db_fetch_one
mode_value = call.get("mode")
print(f"DEBUG: call.mode = {mode_value}")
if mode_value is not None and "_mode" not in params and "mode" not in params:
# Ưu tiên dùng _mode để tránh đè lên tên trường khác
params = dict(params)
params["_mode"] = mode_value
print(f"DEBUG: added _mode = {mode_value} to params")
print(f"DEBUG: final params keys = {list(params.keys())}")
try:
rows = await law_db_fetch_one(params)
return {"label": label, "rows": rows}
except Exception:
return {"label": label, "rows": []}
tasks = [run_one(c, i) for i, c in enumerate(calls, start=1)]
return await asyncio.gather(*tasks)
# ====================== PREVIEW BUILDERS ======================
def build_db_preview(rows: list[dict[str, Any]]) -> str:
"""
Xây dựng chuỗi văn bản nội dung từ danh sách hàng kết quả (có nội dung văn bản).
Nhóm theo văn bản pháp luật và các đơn vị (điều, phụ lục) bên trong.
"""
if not rows:
return ""
docs: dict[str, dict[str, Any]] = {}
for r in rows:
so_hieu = (r.get("SoHieu") or "").strip() or "(Không rõ số hiệu)"
title = (r.get("TieuDe") or r.get("TieuDeVanBan") or r.get("TieuDeDieu") or so_hieu).strip()
docs.setdefault(so_hieu, {"title": title, "groups": {}})
content = (r.get("NoiDung") or r.get("NoiDungDieu") or r.get("NoiDungPhuLuc") or "").strip()
if not content:
continue
chunk_idx = int(r.get("chunk_idx") or r.get("ChunkIdx") or r.get("ChunkIndex") or 1)
phu_luc = r.get("PhuLuc") or r.get("Phu_Luc") or r.get("Phu_luc")
dieu = r.get("Dieu")
chuong = r.get("Chuong")
tieu_de_dieu = r.get("TieuDeDieu") or ""
tieu_de_pl = r.get("TieuDePhuLuc") or r.get("TenPhuLuc") or ""
if phu_luc is not None:
group_key = ("PL", str(phu_luc))
group_title = f"Phụ lục {phu_luc}"
subtitle = tieu_de_pl
elif dieu is not None:
group_key = ("DIEU", str(dieu))
group_title = f"Điều {dieu}"
subtitle = tieu_de_dieu
else:
group_key = ("KHAC", f"Chương {chuong}" if chuong is not None else "Khác")
group_title = group_key[1]
subtitle = ""
groups = docs[so_hieu]["groups"]
groups.setdefault(group_key, {"title": group_title, "subtitle": subtitle, "segs": []})
groups[group_key]["segs"].append((chunk_idx, content))
parts: list[str] = []
for so_hieu, doc in docs.items():
header = f"=== {doc['title']} ({so_hieu}) ==="
parts.append(header)
for group_key, group in sorted(
doc["groups"].items(),
key=lambda x: (
{"PL": 0, "DIEU": 1, "KHAC": 2}.get(x[0][0], 3),
int(x[0][1]) if x[0][1].isdigit() else float("inf"),
),
):
title_line = group["title"] + (f" — {group['subtitle']}" if group["subtitle"] else "")
parts.append(title_line)
for _, text in sorted(group["segs"], key=lambda seg: seg[0]):
parts.append(text)
parts.append("") # ngắt giữa các nhóm
parts.append("") # ngắt giữa các văn bản
return "\n".join(parts).strip()
def build_meta_preview(rows: list[dict[str, Any]]) -> str:
"""
Xây dựng chuỗi văn bản liệt kê các văn bản (chỉ meta, không có nội dung chi tiết).
"""
if not rows:
return ""
lines = []
for i, r in enumerate(rows[:50], start=1):
title = r.get("TieuDe") or r.get("TieuDeVanBan") or "(Không rõ tiêu đề)"
so_hieu = r.get("SoHieu") or "—"
loai = r.get("LoaiVanBan") or ""
cq = r.get("CoQuanBanHanh") or ""
nam = r.get("NamBanHanh") or ""
trang_thai = r.get("TrangThaiVB") or r.get("TrangThai") or ""
meta = " • ".join(filter(None, [loai, cq, f"Năm {nam}" if nam else "", trang_thai]))
lines.append(f"{i}. {title} — {so_hieu}{(' (' + meta + ')') if meta else ''}")
return "\n".join(lines)
def _build_multi_preview(labeled_results: list[dict[str, Any]]) -> str:
"""
Ghép nội dung từ nhiều kết quả truy vấn thành một chuỗi,
có tiêu đề cho từng nhóm kết quả tương ứng với từng truy vấn.
"""
sections: list[str] = []
for item in labeled_results:
label = item.get("label", "Truy vấn")
rows = item.get("rows") or []
if any(r.get("NoiDung") or r.get("NoiDungDieu") or r.get("NoiDungPhuLuc") for r in rows):
preview_text = build_db_preview(rows)
else:
preview_text = build_meta_preview(rows)
if not preview_text:
preview_text = "(Không có dữ liệu)"
sections.append(f"### {label}\n{preview_text}")
return "\n\n".join(sections).strip()
# ====================== PUBLIC APIS ======================
async def fetch_data_db_law(calls: list[dict[str, Any]]) -> str:
"""
Hàm chính: nhận calls -> fetch raw data -> build preview theo mode -> return preview
"""
# Bước 1: Fetch raw data song song
labeled = await law_db_fetch_plan(calls or [])
# Bước 2: Build preview theo mode của từng call
sections: list[str] = []
for item in labeled:
label = item.get("label", "Truy vấn")
rows = item.get("rows") or []
# Tìm mode từ calls tương ứng
call_mode = "content" # default
for call in calls:
if (call.get("params", {}).get("p_so_hieu") and str(call.get("params", {}).get("p_so_hieu")) in label) or (
call.get("params", {}).get("p_vb_pattern") and str(call.get("params", {}).get("p_vb_pattern")) in label
):
call_mode = call.get("mode", "content")
break
# Build preview theo mode
if call_mode == "content":
preview_text = build_db_preview(rows)
elif call_mode == "meta":
preview_text = build_meta_preview(rows)
elif call_mode == "semantic":
preview_text = build_db_preview(rows)
else:
preview_text = build_db_preview(rows)
if not preview_text:
preview_text = "(Không có dữ liệu)"
sections.append(f"### {label}\n{preview_text}")
return "\n\n".join(sections).strip()
__all__ = [
"build_db_preview",
"build_meta_preview",
"fetch_data_db_law",
"law_db_fetch_one",
"law_db_fetch_plan",
]
# if __name__ == "__main__":
# import argparse
# import json as _json
# async def main():
# parser = argparse.ArgumentParser(description="Test fetch_data_db_law nhanh qua CLI")
# parser.add_argument("--mode", "-m", type=str, default="content", help="content|semantic|meta (có thể phân tách bằng dấu phẩy)")
# parser.add_argument("--so_hieu", type=str, default=None, help="Giá trị cho p_so_hieu")
# parser.add_argument("--vb_pattern", type=str, default=None, help="Regex/từ khóa cho p_vb_pattern")
# parser.add_argument("--vector_text", type=str, default=None, help="Văn bản để embedding semantic (p_vector_text)")
# parser.add_argument("--loai_vb", type=str, default=None)
# parser.add_argument("--co_quan", type=str, default=None)
# parser.add_argument("--nam_from", type=int, default=None)
# parser.add_argument("--nam_to", type=int, default=None)
# parser.add_argument("--only_source", type=str, default=None, help="chinh_thong|dia_phuong")
# parser.add_argument("--limit", type=int, default=50)
# parser.add_argument("--article", type=int, default=None)
# parser.add_argument("--chapter", type=int, default=None)
# parser.add_argument("--phu_luc", type=str, default=None)
# parser.add_argument("--multi", action="store_true", help="Nếu bật, tạo nhiều calls mẫu để so sánh")
# args = parser.parse_args()
# modes = [m.strip() for m in (args.mode or "content").split(",") if m.strip()]
# def _build_params():
# return {
# "p_so_hieu": args.so_hieu,
# "p_vb_pattern": args.vb_pattern,
# "p_co_quan": args.co_quan,
# "p_loai_vb": args.loai_vb,
# "p_nam_from": args.nam_from,
# "p_nam_to": args.nam_to,
# "p_only_source": args.only_source,
# "p_article": args.article,
# "p_chapter": args.chapter,
# "p_phu_luc": args.phu_luc,
# "p_limit": args.limit,
# "p_vector_text": args.vector_text,
# }
# calls = []
# if args.multi:
# # Tạo một số tổ hợp mẫu để tiện so sánh
# sample_targets = []
# if args.so_hieu:
# sample_targets.append({"p_so_hieu": args.so_hieu})
# if args.vb_pattern:
# sample_targets.append({"p_vb_pattern": args.vb_pattern})
# if args.vector_text:
# sample_targets.append({"p_vector_text": args.vector_text})
# if not sample_targets:
# # nếu không có gì, dùng một pattern mặc định
# sample_targets = [
# {"p_vb_pattern": "BVMT|bảo vệ môi trường|môi trường"},
# {"p_vector_text": "nội dung nghị định chưa được xác định theo yêu cầu của người hỏi"},
# ]
# base_params = _build_params()
# for mode in modes:
# for target in sample_targets:
# params = dict(base_params)
# params.update({k: v for k, v in target.items() if v is not None})
# calls.append({"mode": mode, "params": {k: v for k, v in params.items() if v is not None}})
# else:
# params = {k: v for k, v in _build_params().items() if v is not None}
# if not params:
# # nếu không truyền gì, tạo ví dụ tối thiểu
# params = {"p_vb_pattern": "BVMT|bảo vệ môi trường|môi trường", "p_limit": 30}
# for mode in modes:
# calls.append({"mode": mode, "params": params})
# print("CLI calls:")
# print(_json.dumps(calls, ensure_ascii=False, indent=2))
# # Init Supabase client trước khi gọi
# await init_supabase_client()
# try:
# preview = await fetch_data_db_law(calls)
# print("\n===== PREVIEW =====\n")
# print(preview)
# finally:
# await close_supabase_client()
# asyncio.run(main())
...@@ -6,12 +6,17 @@ services: ...@@ -6,12 +6,17 @@ services:
env_file: .env env_file: .env
ports: ports:
- "5000:5000" - "5000:5000"
# volumes: volumes:
# - .:/app - .:/app
environment: environment:
- PORT=5000 - PORT=5000
restart: unless-stopped restart: unless-stopped
deploy:
resources:
limits:
memory: 8g
logging: logging:
driver: "json-file" driver: "json-file"
options: options:
tag: "{{.Name}}" tag: "{{.Name}}"
=
# Lấy số CPU cores
NUM_CORES=$(nproc)
WORKERS=$((2 * NUM_CORES + 1))
echo "🔧 [STARTUP] Detected CPU cores: $NUM_CORES"
echo "🔧 [STARTUP] Gunicorn workers: $WORKERS"
echo "🔧 [STARTUP] Environment: ${ENV:-production}"
# Chạy Gunicorn với số workers tính toán được
exec gunicorn \
server:app \
--workers "$WORKERS" \
--worker-class uvicorn.workers.UvicornWorker \
--worker-connections 1000 \
--max-requests 1000 \
--max-requests-jitter 100 \
--timeout 30 \
--access-logfile - \
--error-logfile - \
--bind 0.0.0.0:5000 \
--log-level info
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
sale_price, sale_price,
original_price, original_price,
discount_amount, discount_amount,
approx_cosine_similarity(vector, [0.04000994190573692,0.011043205857276917,-0.0579720139503479,-0.031009631231427193,0.028677646070718765,-0.04494372755289078,0.023743856698274612,-0.0034859334118664265,0.020371150225400925,-0.08372022211551666,-0.02802237682044506,0.02436058036983013,-0.026654021814465523,-0.01709480583667755,0.026114387437701225,0.0547727607190609,0.018983522430062294,-0.03484487906098366,-0.03993285074830055,-0.019465336576104164,0.0009889258071780205,0.021469688042998314,0.007068230304867029,-0.0009979598689824343,0.043055012822151184,-0.0037846590857952833,0.06074726954102516,-0.05361640453338623,-0.02200932241976261,-0.006822504103183746,0.02890891581773758,-0.02324276976287365,0.03380415961146355,-0.06271307915449142,0.010349391959607601,0.054965484887361526,0.016767172142863274,0.00028080795891582966,0.031240902841091156,-0.028465647250413895,0.0035100241657346487,-0.01633353717625141,0.0070152305997908115,0.027848923578858376,0.02393658272922039,0.03629032522439957,0.016545535996556282,0.04744916781783104,-0.0037653862964361906,-0.01040721032768488,0.008523312397301197,-0.026576930657029152,-0.014512276276946068,0.02912091463804245,-0.0060082366690039635,0.03650232404470444,-0.0074055008590221405,-0.004704926162958145,0.01617935672402382,0.004276110790669918,0.011447930708527565,-0.019041338935494423,-0.027559833601117134,-0.005545693915337324,-0.02281877212226391,-0.014213550835847855,-0.01695026084780693,-0.005314422305673361,-0.002498212270438671,0.010792662389576435,0.0056276023387908936,-0.05589057132601738,-0.02973763830959797,0.023069314658641815,-0.024437671527266502,-0.031183084473013878,0.0114190224558115,-0.028060922399163246,-0.03168417140841484,-0.022433320060372353,-0.03232016786932945,0.016612989827990532,-0.016372082754969597,0.0066586872562766075,-0.01386664342135191,-0.05118805542588234,-0.05242150276899338,0.0507640577852726,-0.04656262695789337,-0.017191169783473015,-0.05492694303393364,0.031028904020786285,0.01639135554432869,0.0017140578711405396,0.04629281163215637,0.028099466115236282,0.004844652488827705,-0.02532421052455902,-0.018386071547865868,0.016506990417838097,-0.03613614663481712,0.005381876602768898,0.04475100338459015,0.05978363752365112,-0.014001552015542984,0.034960515797138214,0.02268386259675026,0.008296859450638294,-0.07296610623598099,0.01776934787631035,-0.01860770583152771,0.05997636541724205,-0.015572269447147846,-0.0018598069436848164,0.024668941274285316,0.010705935768783092,0.02212495729327202,0.031221630051732063,0.03627105429768562,-0.025555482134222984,-0.007217593025416136,0.025902388617396355,-0.024726759642362595,-0.079094797372818,-0.07763008028268814,0.05581348016858101,-0.039451032876968384,0.047564804553985596,-0.013143920339643955,0.029583457857370377,0.009419488720595837,-0.015755359083414078,0.016005903482437134,0.016728626564145088,0.0013490827986970544,0.002486166777089238,-0.03881504014134407,0.0729275569319725,-0.006793595384806395,-0.02754056081175804,0.03950885310769081,0.029448550194501877,0.034825608134269714,0.015234999358654022,0.008932854980230331,0.006417779251933098,0.018077708780765533,0.027020201086997986,0.04386446252465248,0.030315816402435303,-0.012479015626013279,-0.018906431272625923,0.055774934589862823,0.008725674822926521,-0.008638948202133179,-0.022452590987086296,0.011785201728343964,0.018087346106767654,0.011091387830674648,-0.02324276976287365,-0.007381409872323275,-0.023435495793819427,0.011226295493543148,-0.03189617022871971,-0.031240902841091156,-0.02447621524333954,-0.001967010786756873,-0.024996576830744743,-0.031780537217855453,0.004415837116539478,0.02158532477915287,0.029718365520238876,0.03536524251103401,0.03812122344970703,0.06024618074297905,-0.06232762336730957,0.04525209218263626,0.030354361981153488,-0.0009046081686392426,0.025343483313918114,0.039663031697273254,0.018482433632016182,0.027694741263985634,-0.021334780380129814,-0.019831515848636627,-0.06479451805353165,-0.013934098184108734,-0.028677646070718765,0.0377935916185379,-0.004957878962159157,0.0008588357013650239,-0.02842710167169571,-0.010397573933005333,-0.014213550835847855,0.03293689340353012,-0.020872237160801888,-0.012189926579594612,0.02345476672053337,0.030335089191794395,-0.004365246277302504,0.03584705665707588,-0.013384828343987465,0.009766395203769207,-0.028600554913282394,0.013972642831504345,0.006933321710675955,-0.037196140736341476,0.01373173575848341,0.017123714089393616,-0.04802734777331352,0.04251537844538689,0.009732668288052082,-0.02802237682044506,0.023763129487633705,-0.017239350825548172,-0.01652626320719719,0.06826359033584595,-0.03376561403274536,0.05442585423588753,0.027791105210781097,-0.009405034594237804,-0.01318246591836214,0.013828098773956299,0.043132103979587555,0.024264216423034668,0.011129932478070259,0.04205283895134926,0.014473730698227882,-0.04201429337263107,-0.012797013856470585,-0.03380415961146355,0.005251786671578884,-0.05338513106107712,0.007294683251529932,0.022433320060372353,-0.0003273332549724728,-0.036907050758600235,0.029236551374197006,-0.021199872717261314,0.010985388420522213,-0.06082436069846153,-0.04081939160823822,0.0574323795735836,0.025613300502300262,0.018867885693907738,-0.004225519951432943,0.003322116332128644,-0.014743546955287457,-0.015109727159142494,0.044712457805871964,0.032069623470306396,-0.004201429430395365,-0.0025150757282972336,-0.03800559043884277,-0.014309913851320744,-0.01881006918847561,-0.020448239520192146,0.07381410151720047,0.00778131652623415,-0.011091387830674648,0.0005191559321247041,0.016304628923535347,0.004827789030969143,0.02428348921239376,0.024399125948548317,0.020448239520192146,0.0076994081027805805,0.023975128307938576,0.01709480583667755,0.02511221170425415,-0.026885291561484337,-0.04883679747581482,0.05693129450082779,0.04906806722283363,-0.030103817582130432,-0.02199004963040352,0.01303792092949152,0.03950885310769081,0.030547088012099266,0.08973328024148941,-0.04328628256917,-0.02586384490132332,0.009766395203769207,-0.009824213571846485,0.01819334551692009,-0.024052219465374947,0.015668632462620735,-0.06487160921096802,0.004726607818156481,-0.017759710550308228,-0.0010220506228506565,0.01970624551177025,-0.03251289576292038,0.002419917145743966,0.01646844670176506,-0.006870685610920191,-0.044172823429107666,-0.01839570701122284,-0.004406200721859932,-0.008436585776507854,0.015263907611370087,0.06475597620010376,0.006504506338387728,0.023204224184155464,0.008479949086904526,-0.0036136144772171974,0.06941994279623032,-0.018299344927072525,-0.013991915620863438,0.02081442065536976,0.049414973706007004,0.03700341284275055,0.007241683546453714,-0.021488960832357407,-0.05407894775271416,-0.013808825984597206,-0.022394774481654167,-0.010571027174592018,-0.007362137548625469,-0.02648056671023369,0.013895552605390549,0.02649983949959278,0.028195830062031746,0.002187441335991025,-0.019494246691465378,0.03397761285305023,-0.018443888053297997,0.003717204788699746,0.034073974937200546,-0.025960206985473633,-0.016902079805731773,-0.00125874241348356,0.025574754923582077,-0.06259743869304657,-0.03548087552189827,0.01695026084780693,0.03149144724011421,0.010879389010369778,0.010619208216667175,0.008499221876263618,-0.05754801630973816,-0.013124648481607437,-0.018569160252809525,0.023050041869282722,0.07046066224575043,-0.011611748486757278,0.019214794039726257,-0.029101641848683357,0.0026572111528366804,0.03788995370268822,0.04980042576789856,0.02436058036983013,-0.0009841077262535691,0.02530493773519993,-0.0051361508667469025,-0.055852025747299194,0.0157649964094162,-0.010368664748966694,-0.031048176810145378,0.0292558241635561,-0.018299344927072525,-0.008687129244208336,0.014348458498716354,-0.008200496435165405,0.003813567804172635,0.019253337755799294,0.002234418410807848,0.0379284992814064,0.05492694303393364,-0.02254895493388176,0.028138011693954468,0.05257568135857582,-0.02815728448331356,-0.03762013837695122,-0.00018911249935626984,-0.004767562262713909,-0.008084860630333424,-5.375251566874795e-05,0.021276962012052536,-0.058280374854803085,-0.001610467559657991,-0.026808202266693115,0.03777431696653366,0.013018649071455002,0.016487719491124153,0.0011376863112673163,-0.041320476680994034,0.01701771467924118,0.019725516438484192,0.018790796399116516,-0.0006558710592798889,0.003642523428425193,0.019831515848636627,0.013009012676775455,-0.006875503808259964,0.038352496922016144,0.049414973706007004,0.0041363839991390705,-0.04371028020977974,0.04891388490796089,-0.012045381590723991,0.011043205857276917,-0.012700650840997696,-0.04552190750837326,0.013500464148819447,0.023146405816078186,0.011081751435995102,0.03339943662285805,0.03091326728463173,0.011823747307062149,-0.011775565333664417,-0.07470063865184784,0.021084235981106758,0.08726638555526733,-0.009935030713677406,-0.009005127474665642,-0.039470307528972626,0.033457253128290176,-0.04081939160823822,0.0096507603302598,-0.021527506411075592,0.006234689615666866,0.005097605753690004,-6.835754174971953e-05,0.0002053737553069368,0.016227537766098976,0.04586881399154663,-0.0419372022151947,-0.03630959987640381,-0.021700959652662277,-0.03802486136555672,-0.013635372743010521,0.038834311068058014,0.004704926162958145,0.04760335013270378,-0.03783213719725609,-0.017460985109210014,0.023608949035406113,0.03802486136555672,-0.033303070813417435,-0.00985794048756361,-0.01358719076961279,-0.04856697842478752,-0.024225672706961632,0.019870061427354813,-0.016237175092101097,0.06957412511110306,0.019677335396409035,-0.08826855570077896,-0.0038232041988521814,-0.018713705241680145,0.03598196431994438,-0.04787316545844078,0.006808049976825714,0.015446998178958893,-0.014888091944158077,0.006928503513336182,0.008282404392957687,0.00920267216861248,-0.011023933067917824,0.04031830281019211,0.0315299928188324,0.03623250871896744,0.04980042576789856,-0.02946782298386097,-0.03415106609463692,0.016449173912405968,-0.021431144326925278,0.04159029573202133,-0.02703947387635708,-0.024649670347571373,-0.04829716309905052,0.0016755126416683197,-0.03548087552189827,0.0193015206605196,0.01577463187277317,0.0005817919736728072,-0.02808019332587719,-0.05939818546175957,-0.005560148507356644,-0.01832825317978859,0.0176344383507967,-0.03037363477051258,-0.024264216423034668,-0.04097357019782066,0.01861734315752983,0.017374258488416672,-0.003360661445185542,-0.02094932831823826,-0.0006305757560767233,-0.001877875067293644,-0.001800784608349204,0.001993510639294982,-0.003271525725722313,0.029660549014806747,0.000632984796538949,-0.0072705927304923534,-0.04694807901978493,0.01563972420990467,-0.0609014518558979,-0.02158532477915287,-0.07315883040428162,0.010956479236483574,-0.09829031676054001,0.005290331784635782,-0.001141902175731957,0.02973763830959797,-0.02732856199145317,-0.004398973658680916,0.03307180106639862,-0.00830167718231678,0.04833570867776871,0.02829219214618206,-0.00971339549869299,-0.014521912671625614,-0.02532421052455902,0.03145290166139603,0.024187127128243446,-0.014126824215054512,0.027656197547912598,-0.021662414073944092,-0.0008142678416334093,-0.0007998133660294116,-0.004174929577857256,-0.007126047741621733,-0.06868758797645569,0.01771152950823307,-0.055659301578998566,0.02621075138449669,0.0034088431857526302,0.005603511817753315,0.019205156713724136,-0.02802237682044506,0.005063878372311592,-0.007082684431225061,0.0023476448841392994,0.035519421100616455,0.022934406995773315,0.01462791208177805,0.012161017395555973,0.006446688435971737,0.005415603518486023,-0.010041030123829842,-0.0176344383507967,0.0157649964094162,-0.023686038330197334,-0.0027776651550084352,-0.006947776302695274,-0.017268259078264236,0.009867576882243156,-0.011351567693054676,-0.019619518890976906,0.017595894634723663,-0.015379543416202068,0.0073765916749835014,-0.003271525725722313,0.0008419721852988005,-0.004712153226137161,-0.01998569816350937,-0.00281861936673522,0.016102265566587448,0.005020515061914921,0.032069623470306396,-0.002329576760530472,0.03430524840950966,0.004839834291487932,-0.012777741067111492,-0.0084462221711874,-0.037253957241773605,-0.028870372101664543,-0.024514760822057724,0.0027656196616590023,-0.006687595974653959,-0.046793900430202484,-3.80935198336374e-05,-0.017586257308721542,-0.01044575497508049,0.020178424194455147,-0.02904382534325123,-0.015861358493566513,-0.012719923630356789,0.005526421125978231,0.013375191949307919,0.0013490827986970544,0.0031872079707682133,-0.00037491251714527607,0.009400215931236744,0.023358404636383057,0.011871928349137306,0.011910473927855492,-0.01777898333966732,0.00010637578088790178,0.019735153764486313,0.0015092863468453288,-0.018376434221863747,0.0009889258071780205,-0.0710773915052414,-0.01998569816350937,0.0048807887360453606,-0.0545029453933239,0.030123090371489525,0.017306804656982422,-0.027232199907302856,0.00988684967160225,0.006244326010346413,0.022433320060372353,-0.009323125705122948,0.014252095483243465,0.0168924443423748,-0.021566051989793777,0.016506990417838097,0.025420574471354485,0.021045690402388573,-0.037311773747205734,-0.025902388617396355,-0.0025921661872416735,-0.00660568755120039,0.011120297014713287,-0.01577463187277317,-0.005839601159095764,-0.0074055008590221405,-0.0063744159415364265,0.0018357161898165941,-0.024514760822057724,-0.021411871537566185,-0.0014225596096366644,0.007786134723573923,-0.06957412511110306,-0.012922286055982113,-0.008744947612285614,-0.02310786023736,-0.00968930497765541,0.009371306747198105,0.021546779200434685,-0.02040969580411911,-0.007790952920913696,0.002563257236033678,-0.04544481635093689,0.006061236374080181,0.0178368017077446,0.005974509287625551,0.027868196368217468,0.015678269788622856,-0.07439228147268295,0.05361640453338623,0.012132108211517334,-0.028003104031085968,-0.02006278745830059,-0.027656197547912598,0.006244326010346413,0.08595584332942963,0.004512200132012367,-0.002857164479792118,-0.030354361981153488,-0.007916225120425224,0.006494869943708181,-0.016169721260666847,0.0005390308215282857,-0.010802298784255981,0.010359028354287148,-0.018347525969147682,-0.017952436581254005,0.022799499332904816,-0.028446374461054802,0.0028089829720556736,0.007675317116081715,-0.012189926579594612,0.0539633110165596,-0.05188186839222908,0.013683553785085678,0.026808202266693115,0.03318743780255318,-0.012151381000876427,-0.006138326600193977,0.031298719346523285,0.0002829159202519804,-0.012700650840997696,-0.005675783846527338,0.023878764361143112,0.02580602653324604,0.03147217258810997,-0.00916894432157278,-0.009236399084329605,-0.023396950215101242,0.0030763905961066484,-0.02233695611357689,0.005068696569651365,0.004666381049901247,-0.02428348921239376,-0.026461295783519745,-0.025092938914895058,-0.011679202318191528,-0.01867515966296196,-0.014184641651809216,-0.012122472748160362,0.028003104031085968,-0.017393531277775764,0.02567111700773239,0.014984454959630966,-0.045676089823246,-0.03605905547738075,0.008797947317361832,0.013365555554628372,-0.011101024225354195,-0.006441870238631964,-0.019609881564974785,-0.007328410167247057,0.003078799694776535,0.006793595384806395,0.009751941077411175,0.002640347694978118,-0.004647108260542154,-0.002110350877046585,0.017528438940644264,-0.029371459037065506,0.013943733647465706,-0.007366955745965242,-0.002354872180148959,0.014955545775592327,-0.00659123295918107,0.018925704061985016,0.059244006872177124,-0.002970391185954213,0.030007455497980118,-0.022529682144522667,-0.02455330640077591,0.035307422280311584,0.01072520762681961,-0.022664589807391167,-0.019725516438484192,0.02420639991760254,-0.012671741656959057,-0.005473421420902014,0.04101211577653885,-0.022741680964827538,0.010243392549455166,0.012064654380083084,0.04872116073966026,0.014955545775592327,-0.03232016786932945,0.032262351363897324,-0.025131484493613243,0.006836958695203066,0.0030330270528793335,-0.028658373281359673,0.03160708025097847,-0.013722099363803864,0.004548336379230022,0.006543051451444626,-0.0050735147669911385,-0.0031101175118237734,0.001432195887900889,-0.02407149039208889,0.024880940094590187,0.001897147623822093,-0.04401864483952522,0.0020465103443711996,-0.045907359570264816,0.02503512240946293,-0.020216969773173332,0.036560144275426865,0.0292558241635561,0.02705874666571617,-0.006046781782060862,-0.008773855865001678,0.02671183831989765,0.01771152950823307,0.0013225829461589456,-0.0073765916749835014,-0.01037830114364624,-0.009289398789405823,0.014049733057618141,-0.0032739348243921995,0.006875503808259964,-0.021122781559824944,-0.008735311217606068,-0.0271165631711483,0.04058811813592911,0.015668632462620735,0.015957722440361977,0.00288848252967,0.0016201038379222155,0.041474658995866776,0.0007811430259607732,-0.0037774317897856236,-0.012093563564121723,0.0018212617142125964,0.00631178030744195,-0.01044575497508049,-0.0229729525744915,0.0011340726632624865,-0.013741371221840382,0.017875347286462784,-0.0024789394810795784,0.03332234546542168,-0.0016441945917904377,0.007087502628564835,0.037870679050683975,0.015157908201217651,0.0027752560563385487,0.011958654969930649,0.03251289576292038,-0.048258617520332336,-0.03216598927974701,0.009014763869345188,-0.011313023045659065,-0.019696608185768127,0.026576930657029152,-0.005295149981975555,0.01407864224165678,0.016015538945794106,0.008070405572652817,-0.00888949166983366,0.017258623614907265,0.025208575651049614,0.007930679246783257,-0.01266210526227951,0.01813552714884281,-0.004957878962159157,0.05246004834771156,0.0676083192229271,0.01778862066566944,-0.030932540073990822,0.03191544488072395,0.007877679541707039,-0.02649983949959278,-0.003372706938534975,-0.020775875076651573,-0.010763753205537796,0.042091380804777145,-0.009723031893372536,-0.023763129487633705,0.03366925194859505,-0.026037298142910004,-0.010041030123829842,0.03154926374554634,0.008142678067088127,0.003876203903928399,-0.009038854390382767,0.041744474321603775,0.03713832050561905,0.018145162612199783,-0.025844572111964226,0.023743856698274612,-0.021874412894248962,0.004654335789382458,-0.009660396724939346,0.009525488130748272,-0.003379934234544635,0.011900837533175945,-0.01979297213256359,0.013943733647465706,0.007020048331469297,-0.03195399045944214,-0.039258308708667755,-0.012459742836654186,-0.015157908201217651,-0.00520360516384244,0.012093563564121723,0.011158841662108898,-0.008923218585550785,-0.037600863724946976,-0.04297792166471481,0.0011973109794780612,0.013982279226183891,-0.01970624551177025,-0.02518930286169052,0.0016574445180594921,-0.006456324830651283,-0.02033260464668274,-0.006041963584721088,0.007415137253701687,0.007415137253701687,-0.018039163202047348,-0.005295149981975555,0.012729560025036335,0.036348145455121994,0.0699981227517128,0.0039388397708535194,0.00044929274008609354,0.012835558503866196,-0.01887752301990986,-0.012546469457447529,-0.01186229195445776,0.02532421052455902,-0.01854025200009346,0.015321725979447365,-0.009226762689650059,0.0051024239510297775,-0.00011857172648888081,-0.012353743426501751,-0.01290301326662302,-0.009424306452274323,0.007328410167247057,-0.027000928297638893,-0.0013394465204328299,-0.02129623480141163,-0.014984454959630966,0.019330428913235664,0.004801289178431034,0.043825916945934296,0.003326934529468417,-0.005006060935556889,0.030971085652709007,-0.0383332222700119,0.04717935249209404,0.016834625974297523,-0.0009594146395102143,0.012083927169442177,0.002989663742482662,-0.05404040217399597,-0.012257380411028862,0.0008732901769690216,-0.020159151405096054,0.016217902302742004,0.027347834780812263,-0.002442803466692567,-0.022799499332904816,-0.0050108786672353745,-0.03694559633731842,-0.022876588627696037,0.005781783256679773,-0.01466645672917366,-0.039470307528972626,0.004249610938131809,0.021527506411075592,0.0009521874599158764,0.022857315838336945,0.014955545775592327,0.013510100543498993,-0.02655765786767006,0.05380912870168686,-0.053153861314058304,-0.008393222466111183,0.025690389797091484,0.010031393729150295,0.0042616561986505985,-9.764287824509665e-05,-0.026307113468647003,-0.01127447746694088,-0.014001552015542984,0.02434130758047104,-0.003582296660169959,0.027887467294931412,0.02455330640077591,0.026519112288951874,0.006167235318571329,0.002864391775801778,-0.003953294362872839,0.004201429430395365,0.04822007194161415,0.015100090764462948,-0.020448239520192146,-0.024880940094590187,0.0011063683778047562,0.020525330677628517,0.042708106338977814,0.047063715755939484,-0.0030956631526350975,-0.021411871537566185,-0.024668941274285316,0.0005736612947657704,-0.017412804067134857,-0.009303852915763855,0.005478239618241787,0.020795147866010666,0.01839570701122284,-0.013529373332858086,0.03228162229061127,-0.010436118580400944,0.01351973693817854,-0.008826855570077896,0.08503075689077377,-0.0007323592435568571,-0.007251319941133261,0.019908607006072998,-0.026596203446388245,0.003117344807833433,-0.010811935178935528,-0.028272921219468117,-0.034343793988227844,0.008166768588125706,0.009144853800535202,-0.017875347286462784,-0.009419488720595837,0.017480257898569107,0.0032112987246364355,-0.0036882958374917507,0.007260956335812807,0.03141435608267784,-0.006441870238631964,0.021199872717261314,0.0029173914808779955,-0.009906122460961342,-0.017547711730003357,0.029236551374197006,-0.008841310627758503,-0.010937206447124481,0.01006993930786848,0.019600246101617813,0.001402082503773272,-0.020795147866010666,0.029371459037065506,0.02281877212226391,0.02890891581773758,-0.0005158434505574405,-0.005304786376655102,0.014618275687098503,-0.0056276023387908936,-0.009896486066281796,-0.05037860572338104,0.0008738924516364932,-0.006104599684476852,0.001045539160259068,0.05203605070710182,-0.00013159579248167574,0.0027776651550084352,0.01633353717625141,0.021411871537566185,0.03131799399852753,-0.01639135554432869,-0.031048176810145378,0.031163811683654785,0.03284052759408951,0.030836177989840508,-0.00799331534653902,0.024244945496320724,-0.008576312102377415,0.03802486136555672,0.021758778020739555,-0.0026283024344593287,0.013172829523682594,0.002330781426280737,0.0006116042495705187,0.0617109015583992,-0.002883664332330227,0.005873328074812889,-0.0019236474763602018,-0.017403168603777885,0.020428968593478203,0.026750383898615837,-0.02081442065536976,0.03341870754957199,-0.008345040492713451,-0.03284052759408951,-0.04737207666039467,-0.006711686961352825,0.031433627009391785,-0.0005884168785996735,-0.027000928297638893,0.010677026584744453,-0.023474039509892464,0.0007727112388238311,0.01570717804133892,-0.04290083050727844,-0.0015321725513786077,0.032975438982248306,0.008484766818583012,-0.030103817582130432,-0.024572579190135002,-0.00860522035509348,-0.005642056930810213,-0.014165368862450123,0.020236240699887276,0.012045381590723991,0.01327882893383503,0.02081442065536976,0.009024400264024734,-0.0016116721089929342,-0.001265969593077898,0.011997200548648834,0.03133726492524147,0.01688280701637268,0.005006060935556889,-0.015938449651002884,-0.017470622435212135,-0.015331362374126911,0.006600869353860617,0.02048678509891033,0.029159460216760635,-0.032763440161943436,-0.016131175681948662,0.024919485673308372,0.0012358562089502811,0.020313331857323647,-0.008388403803110123,0.029024552553892136,0.005719147156924009,-0.009583305567502975,-0.007159775123000145,0.017730802297592163,0.007318774238228798,0.02572893537580967,0.04239974543452263,0.007530772592872381,-0.023416223004460335,-0.005362603813409805,-0.006865867879241705,0.03264780342578888,0.03681068867444992,-0.0031101175118237734,0.020159151405096054,-0.020602421835064888,-0.013567917980253696,0.0023910081945359707,-0.025767480954527855,0.004815743770450354,0.005772146862000227,0.02214423008263111,0.008831674233078957,0.030103817582130432,-0.025960206985473633,0.00415083859115839,-0.012498288415372372,0.011351567693054676,0.01563008688390255,-0.02214423008263111,-0.05334658548235893,-0.014570093713700771,-0.010571027174592018,-0.03278271108865738,0.01351973693817854,0.00631178030744195,0.006090145092457533,-0.014994091354310513,-0.025266392156481743,0.005757692735642195,0.02877400815486908,-0.004565199837088585,-0.036984141916036606,0.05257568135857582,0.010792662389576435,-0.020236240699887276,-0.010744480416178703,-0.0317998081445694,-0.0002672569244168699,0.00482297083362937,-0.027232199907302856,-0.03058563359081745,-0.011168478056788445,0.011496112681925297,-0.011390113271772861,-0.02732856199145317,-0.00444474583491683,0.015090454369783401,-0.002633120398968458,0.016574446111917496,0.06830213218927383,-0.025748208165168762,-0.0072465017437934875,-0.043748825788497925,-0.015398816205561161,0.014444821514189243,0.001099743414670229,0.015080817975103855,0.0387379489839077,0.012324835173785686,0.020602421835064888,-0.0068899584002792835,0.009641123935580254,0.0253820288926363,-0.007135684136301279,-0.01840534433722496,-0.008903946727514267,-0.04066520929336548,-0.010628844611346722,0.009530305862426758,-0.02649983949959278,-0.013712462969124317,0.020082060247659683,-0.009308671578764915,0.018569160252809525,-0.0005037980736233294,-0.021411871537566185,0.010243392549455166,0.029930364340543747,0.005863691680133343,0.053654950112104416,0.036560144275426865,0.0008233018452301621,-0.006832140497863293,0.0053336950950324535,-0.06375379860401154,-0.000632984796538949,-0.03625177964568138,0.03492197021842003,-0.04394155368208885,0.008942491374909878,0.04752625897526741,0.005728783551603556,0.0011997200781479478,0.02572893537580967,0.02719365432858467,-0.0038087498396635056,-0.0038689766079187393,-0.006909231189638376,-0.027559833601117134,-0.018164435401558876,-0.020313331857323647,-0.021662414073944092,-0.018501706421375275,0.002122396370396018,0.004762744065374136,0.024322034791111946,-0.0072802286595106125,-0.00399665767326951,0.01833788864314556,0.03272489458322525,-0.07924897968769073,0.018781159073114395,-0.02089150995016098,-0.012267016805708408,0.051650598645210266,-0.05847310274839401,0.01511936355382204,-0.017884982749819756,-0.0005185537156648934,0.027347834780812263,0.012411561794579029,0.0013671508058905602,0.020351877436041832,0.0025463937781751156,-0.008388403803110123,-0.0048422436229884624,-0.048181526362895966,-0.00885094702243805,0.058203283697366714,-0.005415603518486023,-0.011534657329320908,0.007207956630736589,-0.0028523465152829885,0.017239350825548172,-0.0034835245460271835,-0.0004119520599488169,0.005420421715825796,0.007646408397704363,-0.04848989099264145,-0.014261731877923012,-0.0047145625576376915,-0.018212618306279182,0.01979297213256359,-0.0012057427084073424,-0.026962382718920708,-0.04051102697849274,0.033437978476285934,0.005796237848699093,0.009255671873688698,-0.030874723568558693,-0.007940315641462803,0.035731419920921326,-0.01072520762681961,-0.017547711730003357,0.01438700407743454,0.009318307042121887,0.014541185460984707,-0.012132108211517334,-0.03440161049365997,-0.01459900289773941,0.009698941372334957,0.019696608185768127,-0.010349391959607601,-0.0319347158074379,0.026268567889928818,0.011544293724000454,-0.010484300553798676,-0.023531857877969742,0.019070249050855637,0.008860582485795021,0.007689771708101034,0.011052842251956463,-0.004909697454422712,-0.023493312299251556,0.0035557968076318502,-0.006624959874898195,0.041204843670129776,0.014213550835847855,-0.013857007026672363,-0.01127447746694088,-0.0019465336808934808,-0.011284113861620426,0.013943733647465706,-0.007126047741621733,-0.003840067656710744,0.0019886924419552088,-0.02505439519882202,0.060708723962306976,-0.0014876046916469932,0.008248677477240562,0.03505687788128853,0.03043145313858986,0.004030384588986635,0.007858406752347946,0.026866020634770393,0.013461918570101261,0.03411252051591873,0.012132108211517334,0.008195677772164345,0.014647183939814568,0.007810225710272789,-0.00816195085644722,-0.039547398686409,-0.006764686666429043,0.005829964764416218,-0.011129932478070259,-0.01585172303020954,0.01597699522972107,0.015109727159142494,0.0025680754333734512,0.03652159869670868,0.03382343053817749,0.0036545689217746258,0.0084462221711874,-0.024919485673308372,0.0008618470747023821,0.003148662857711315,0.006326234433799982,-0.010522845201194286,-0.03935467079281807,-0.0013876280281692743,0.005044606048613787,0.03239725902676582,0.005290331784635782,0.028311464935541153,0.01875225082039833,0.02829219214618206,0.0285812821239233,0.009670032188296318,0.004321882966905832,-0.02511221170425415,-0.001246697036549449,0.018790796399116516,-0.02193223126232624,0.00010343971371185035,-0.00889431033283472,-0.013433010317385197,0.01563008688390255,0.00813304167240858,0.02580602653324604,-0.008619675412774086,0.012189926579594612,0.017335712909698486,0.002339213155210018,-0.009862758219242096,-0.018559524789452553,0.009260489605367184,-0.020987873896956444,-0.012883740477263927,-0.005487876012921333,0.018578797578811646,0.039740122854709625,-0.000577877217438072,0.047487713396549225,0.006229871418327093,0.0014550820924341679,0.003844885854050517,0.011659929528832436,0.013028285466134548,0.029853275045752525,-0.002659620251506567,0.02420639991760254,-0.0292558241635561,-0.02214423008263111,-0.017595894634723663,0.02484239637851715,0.026307113468647003,0.007521136663854122,-0.02794528566300869,0.01985078863799572,0.016025176271796227,-0.030489271506667137,0.0007034502923488617,-0.005829964764416218,-0.004456791095435619,0.0020573511719703674,-0.014637548476457596,0.023531857877969742,0.015234999358654022,0.010416845791041851,0.01059029996395111,0.006721322890371084,0.009698941372334957,0.014194278046488762,-0.0029800275806337595,-0.02863910049200058,0.011428657919168472,0.03029654361307621,-0.027155108749866486,-0.0054926942102611065,-0.05111096426844597,-0.03197326138615608,-0.004938606638461351,-0.03154926374554634,-0.0539633110165596,-0.005675783846527338,-0.018453525379300117,-0.011688838712871075,0.02268386259675026,0.02863910049200058,0.0030956631526350975,-0.002192259533330798,0.016025176271796227,-0.06818649917840958,0.00422070175409317,-0.022934406995773315,-0.011428657919168472,-0.03800559043884277,-0.04829716309905052,0.009405034594237804,-0.00717904744669795,-0.012324835173785686,0.0008756992756389081,0.002474121516570449,-0.0018513752147555351,-0.007024866528809071,0.027424925938248634,0.034613609313964844,-0.021084235981106758,0.004644699394702911,0.01764407567679882,-0.02808019332587719,0.004331519361585379,-0.02075660228729248,-0.011129932478070259,0.02484239637851715,-0.013664280995726585,-0.022915134206414223,-0.001974238082766533,-0.0390077643096447,0.0071115936152637005,0.005372240208089352,0.008981036953628063,-0.0033510252833366394,-0.011496112681925297,0.011043205857276917,0.008619675412774086,0.025767480954527855,-0.01617935672402382,0.018713705241680145,0.010243392549455166,0.00416529318317771,0.010908298194408417,0.03451724722981453,0.005309604108333588,-0.0010365050984546542,0.0017020124942064285,-0.0379284992814064,-0.04186011105775833,0.028195830062031746,0.006706868764013052,0.012893376871943474,0.040087029337882996,0.011997200548648834,0.05535094067454338,-0.004177338443696499,0.0021705778781324625,0.040857937186956406,0.01210319995880127,0.015157908201217651,-0.024379853159189224,-0.002854755613952875,0.011611748486757278,-0.007492227479815483,0.01597699522972107,-0.003974976018071175,0.024399125948548317,-0.005352967884391546,-0.00038846355164423585,0.009800123050808907,0.03222380578517914,0.01632390171289444,0.0012123676715418696,-0.03245507553219795,0.009207489900290966,-0.04806589335203171,0.046100083738565445,0.032686349004507065,0.023647494614124298,0.02199004963040352,-0.01680571772158146,0.0229729525744915,-0.031086722388863564,-0.019340064376592636,-0.03230089694261551,-0.0012984921922907233,0.015244635753333569,0.0045868814922869205,-0.018665524199604988,0.0051361508667469025,0.02324276976287365,0.016352809965610504,-0.021411871537566185,0.01653590053319931,0.008638948202133179,-0.013307738117873669,-0.018106618896126747,-0.0007167002186179161,0.02518930286169052,-0.00593114597722888,-0.007819862104952335,-0.01245010644197464,-0.01577463187277317,0.04143611341714859,0.01134193129837513,-1.8312744941795245e-05,-0.00764159020036459,0.006933321710675955,-0.025709662586450577,0.03985575959086418,-0.004408609587699175,0.031144538894295692,-0.037446681410074234,0.0026620293501764536,-0.028735462576150894,-0.01214174460619688,0.01303792092949152,-0.021893685683608055,-0.020660238340497017,0.012536833062767982,-0.01680571772158146,0.014396640472114086,-0.004798880312591791,0.002330781426280737,-0.009511033073067665,-0.016169721260666847,-0.024880940094590187,-0.04891388490796089,-0.007338046561926603,-0.0665290504693985,-0.05901273339986801,0.03494124487042427,-0.001830897992476821,-0.014001552015542984,0.004329110030084848,0.020583149045705795,0.0065671419724822044,-0.018106618896126747]) as similarity_score approx_cosine_similarity(vector, [0.04629291594028473,0.02745584025979042,-0.08543489873409271,0.020191991701722145,0.004935088567435741,-0.01836661994457245,0.03951834514737129,0.06763282418251038,-0.0420023538172245,-0.043695997446775436,-0.04971783980727196,0.07783231884241104,-0.008049510419368744,0.015007561072707176,-0.009583198465406895,-0.016475385054945946,-0.059653881937265396,-0.052615851163864136,0.03846452012658119,-0.021358724683523178,0.03951834514737129,0.01546860858798027,0.05566440895199776,0.02297709323465824,0.03948070853948593,0.018921758979558945,0.02094472199678421,-0.10621025413274765,0.011592047289013863,0.018178438767790794,-0.06142279878258705,-0.025649286806583405,0.026232652366161346,-0.040609803050756454,0.0009279754012823105,-0.004264688119292259,-0.0008038925006985664,-0.042115263640880585,-0.012297731824219227,-0.013266872614622116,0.0033849342726171017,-0.005758387036621571,0.04693273827433586,0.02933766506612301,-0.023259367793798447,-0.00999249517917633,-0.041625987738370895,0.0538954921066761,0.012608233839273453,0.03445623070001602,0.03799406439065933,0.03582996502518654,0.017632707953453064,0.05099748075008392,-0.014113694429397583,0.008364716544747353,0.04885220155119896,0.005330272018909454,0.01922285184264183,-0.004673984833061695,-0.011093363165855408,-0.023259367793798447,0.024313190951943398,0.029638757929205894,-0.047572556883096695,-0.00028815458063036203,-0.0477607399225235,-0.005085634533315897,-0.02937530167400837,-0.007249734364449978,0.011648502200841904,0.0037330721970647573,-0.053105127066373825,0.02340991422533989,0.027041837573051453,-0.005621954798698425,-0.031219491735100746,-0.040647439658641815,0.016258975490927696,0.009004537016153336,-0.01599551923573017,0.015289835631847382,0.018667712807655334,-0.05205130577087402,-0.041550714522600174,-0.029638757929205894,-0.048664018511772156,0.009211537428200245,-0.0019782695453613997,-0.033590592443943024,-0.004429347813129425,0.027267657220363617,0.0020041444804519415,-0.023146457970142365,0.0659768134355545,0.04591655358672142,-0.030222123488783836,-0.054121311753988266,0.011836685240268707,-0.001036180416122079,0.010133632458746433,0.01116863638162613,0.009028059430420399,0.028133297339081764,0.007517894264310598,0.001463119639083743,0.02749347686767578,-0.013295100070536137,-0.05269112437963486,0.023616915568709373,0.005330272018909454,0.029017755761742592,0.03825752064585686,0.027305293828248978,-0.0109804542735219,0.00196297955699265,0.027549931779503822,-0.00024154999118763953,0.01826312020421028,-0.026289107277989388,0.039141979068517685,-0.0016712966607883573,0.010321814566850662,-0.054046038538217545,-0.022788912057876587,0.07388048619031906,-0.027625204995274544,0.014414786361157894,0.03645096719264984,0.012166004627943039,0.025141194462776184,0.028227388858795166,-0.001547801773995161,0.013840829022228718,0.021753907203674316,0.0034672641195356846,-0.030033940449357033,0.01713402383029461,0.0027239429764449596,0.04429818317294121,0.014875833876430988,0.02245018258690834,0.007099188398569822,-0.05144912004470825,0.005904228892177343,-0.010641725733876228,0.01847952976822853,0.016070792451500893,0.003991823177784681,0.052615851163864136,-0.004572837147861719,-0.016277793794870377,0.01924167014658451,-0.03451268747448921,-0.03737306222319603,-0.008087147027254105,-0.029732849448919296,-0.000839764776173979,-0.024294372648000717,-0.004728087689727545,-0.017199888825416565,-0.01249532401561737,-0.018216075375676155,0.012645870447158813,-0.019251078367233276,0.014358331449329853,0.003140297019854188,0.04045925661921501,-0.0003687202697619796,0.015732064843177795,0.017707981169223785,-0.012956371530890465,0.05649241432547569,-0.03037266992032528,0.020191991701722145,-0.06074533984065056,-0.00306502403691411,-0.012834052555263042,-0.04779837653040886,0.01249532401561737,0.03400459513068199,0.040685076266527176,-0.009371493011713028,-0.007188574876636267,-0.0458412803709507,0.013473873026669025,0.02995866909623146,-0.05637950450181961,0.06608972698450089,0.0076308040879666805,0.03174640238285065,-0.006322935223579407,-0.005847773980349302,-0.05032002553343773,0.003933016210794449,-0.03833279386162758,-5.810872607980855e-05,-0.005414953920990229,-0.007376757450401783,-0.06383153796195984,0.009870177134871483,0.03727897256612778,-0.02051190286874771,-0.034625597298145294,0.019759172573685646,0.011469729244709015,-0.015835564583539963,-0.008275329135358334,-0.0007145057898014784,-0.027060655876994133,0.024125007912516594,0.029036574065685272,0.005993615370243788,-0.05942806228995323,-0.05935278907418251,-0.01689879596233368,0.0330825001001358,0.00824239756911993,0.03699669614434242,0.02090708538889885,-0.07071901857852936,-0.008040101267397404,-0.002325231209397316,0.02943175658583641,-0.05137384682893753,-0.0005389666766859591,0.018338393419981003,-0.04245399311184883,-0.015026379376649857,-0.03502077981829643,-0.026627836748957634,0.00042576310806907713,0.008891627192497253,-0.033609408885240555,0.007165051996707916,0.017077570781111717,-0.02788865938782692,-0.007118006236851215,-0.03791879117488861,0.036620330065488815,-0.0314076729118824,-0.0177173912525177,0.011864912696182728,-0.00024096191918943077,0.0023793336004018784,0.0048010083846747875,0.001306692836806178,0.032160405069589615,-0.05502459034323692,0.009423242881894112,0.08535962551832199,0.017491571605205536,-0.014631195925176144,0.030297396704554558,-0.02593156136572361,0.011516774073243141,0.0028015682473778725,0.03338359296321869,-0.0014007841236889362,0.010030131787061691,0.009352674707770348,-0.004448166117072105,-0.01564738154411316,-0.0467069186270237,0.007085074670612812,0.002695715520530939,-0.008209465071558952,0.05054584518074989,0.03502077981829643,0.03831397369503975,-0.0036977878771722317,-0.009277401491999626,0.021678633987903595,0.03729778900742531,0.017068160697817802,-0.027775749564170837,-0.05099748075008392,-0.0046104732900857925,-0.0008338841143995523,0.04034634679555893,-0.028095660731196404,0.021321088075637817,-0.004064743872731924,0.019552171230316162,-0.045615460723638535,0.013633828610181808,0.027135929092764854,-0.007706076838076115,-0.024727191776037216,0.008510557934641838,-0.003086194396018982,-0.002260543406009674,-0.04283035546541214,0.003991823177784681,-0.010585270822048187,0.024651918560266495,-0.026138560846447945,-0.003951834514737129,-0.06345517188310623,0.005541977472603321,0.002773340791463852,-0.0046104732900857925,0.02147163264453411,0.052653487771749496,-0.04681982845067978,0.0022370205260813236,0.009526743553578854,0.03153940290212631,0.06503590196371078,0.024708373472094536,-0.01572265475988388,0.013586782850325108,0.0498683862388134,0.005019770469516516,-0.013850238174200058,-0.05171257629990578,-0.055062226951122284,-0.0037801177240908146,-0.0075743491761386395,0.011930775828659534,0.03293195366859436,-0.03295077010989189,-0.044561635702848434,0.02352282404899597,-0.005302044562995434,0.01392551138997078,0.010114814154803753,0.027794567868113518,0.014179558493196964,-0.04102380573749542,-0.04821237921714783,-0.02888602763414383,0.021584542468190193,-0.009028059430420399,-0.00787073653191328,-0.0268536563962698,0.035566508769989014,-0.015835564583539963,-0.04053452983498573,0.03436214104294777,-0.04618000611662865,-0.00762139493599534,-0.027004200965166092,0.0028180342633277178,0.07075665146112442,-0.03492669016122818,0.006529935635626316,0.02694774605333805,-0.013812602497637272,0.030165668576955795,0.04580364376306534,0.0273805670440197,0.003937720786780119,-0.00019876785518135875,-0.0006686362903565168,-0.07056847214698792,-0.027117110788822174,0.017707981169223785,0.029657576233148575,0.05995497107505798,-0.02497182972729206,-0.034569140523672104,-0.036074601113796234,-0.029243573546409607,-0.0248401015996933,0.006468776613473892,-0.018234893679618835,-0.04241635650396347,0.023579278960824013,-0.00993604026734829,-0.0428679920732975,0.011432092636823654,0.004506973084062338,0.016240157186985016,-0.00015525062917731702,0.002338168676942587,0.014405377209186554,-0.022243181243538857,0.0023193503729999065,-0.047610193490982056,0.0025898627936840057,-0.00547611340880394,0.03302604332566261,0.014377149753272533,-0.01945807971060276,-0.009841949678957462,0.006920414511114359,-0.04072271287441254,0.00948440283536911,0.046104732900857925,-0.011921366676688194,-0.004906861111521721,0.024012098088860512,0.03558532893657684,-0.029657576233148575,-0.0007544945692643523,-0.007183870300650597,0.0039047887548804283,-0.04399709030985832,0.0060877068899571896,0.017670344561338425,-0.022111454978585243,-0.051562029868364334,-0.016240157186985016,-0.011573228985071182,0.035208962857723236,-0.002563987858593464,-0.005396135617047548,0.015129880048334599,0.007776645477861166,0.024764828383922577,-0.011319182813167572,-0.008548194542527199,-0.02246900089085102,0.014198376797139645,-0.0027215906884521246,-0.03658269718289375,-0.009089219383895397,-0.03547241911292076,0.0379752479493618,-0.008783422410488129,0.007442621514201164,-0.025592831894755363,-0.002905068686231971,0.015637973323464394,0.04147544130682945,0.044072363525629044,0.008181237615644932,0.040158163756132126,-0.03229213133454323,-0.013022235594689846,-0.030579671263694763,-0.05487404391169548,-0.014443013817071915,-0.005570204462856054,-0.06526172161102295,-0.0248401015996933,0.003704844741150737,0.057546235620975494,0.02352282404899597,0.008665808476507664,-0.009155082516372204,0.030749035999178886,-0.002775693079456687,0.026552563533186913,-0.011394456028938293,-0.006609913427382708,0.004492859356105328,-0.046142369508743286,-0.035641781985759735,0.013078689575195312,-0.005904228892177343,-0.004770428407937288,0.01148854661732912,0.032630860805511475,0.004090618807822466,0.06360571831464767,0.00611593434587121,-0.011460320092737675,-0.04825001582503319,0.05095984414219856,0.04098616912961006,0.014574741944670677,0.05671823397278786,0.030654944479465485,-0.002295827493071556,-0.032612044364213943,-0.026176197454333305,-0.014537105336785316,-0.012166004627943039,0.019589807838201523,0.0090751051902771,0.04380890727043152,0.03249913454055786,-0.007070960942655802,-0.00973844900727272,0.045690733939409256,-0.013502100482583046,-0.018827667459845543,-0.0050621116533875465,-0.004187062382698059,-0.029695212841033936,-0.04328199476003647,-0.029996303841471672,-0.05276639759540558,0.04572837054729462,-0.000630999740678817,-0.01701170578598976,-0.013831419870257378,0.06921355426311493,-0.0067792776972055435,0.005876001436263323,0.010641725733876228,-0.0025357604026794434,0.006925119087100029,0.025536376982927322,0.03238622471690178,-0.02047426626086235,-0.04904038459062576,0.007334416266530752,0.0051091574132442474,-0.013408009894192219,-0.008995127864181995,0.02090708538889885,0.0029450575821101665,-0.03248031437397003,0.03793761134147644,0.024030916392803192,-0.01916639693081379,-0.0001515751937404275,-0.006680482067167759,0.04550255089998245,-0.027267657220363617,0.0022476057056337595,-0.0245013739913702,-0.00316617195494473,0.025818651542067528,0.005250294227153063,0.014631195925176144,-0.0127305518835783,-0.024125007912516594,0.017162252217531204,0.04437345266342163,-0.05020711570978165,0.012570597231388092,-0.015035788528621197,0.026213834062218666,0.0014748810790479183,-0.024106189608573914,0.02489655651152134,0.0003781293926294893,-0.004321142565459013,0.014292467385530472,-0.007357939146459103,-0.00305561488494277,-0.012006049044430256,0.021283451467752457,0.006191207095980644,-0.025781014934182167,0.038615066558122635,0.029638757929205894,0.02199854515492916,0.023240549489855766,0.010500588454306126,-0.013586782850325108,-0.003624867182224989,-0.0013325680047273636,-0.03872797638177872,0.0036977878771722317,-0.0008574069361202419,0.0007862503989599645,0.004753962624818087,-0.016146065667271614,-0.011629683896899223,-0.005565499886870384,-0.035735875368118286,0.010030131787061691,-0.01393492054194212,0.04275508597493172,-0.014762924052774906,-0.002114701783284545,0.021321088075637817,-0.002836852567270398,-0.013615010306239128,-0.021528087556362152,-0.028265025466680527,0.025649286806583405,0.010387678630650043,0.010350042022764683,-0.03439977765083313,-0.014518287032842636,0.028058024123311043,0.012834052555263042,-0.03522777929902077,-0.018705349415540695,-0.058148421347141266,0.011968412436544895,-0.014697059988975525,-0.02297709323465824,-0.007136824540793896,-0.037655334919691086,-0.016945842653512955,-0.009164491668343544,-0.007339120842516422,0.0284343883395195,0.022694820538163185,0.021095268428325653,-0.030259760096669197,-0.00029932791949249804,0.0014760572230443358,0.05175021290779114,-0.007988350465893745,0.004311733413487673,0.005203248467296362,-0.01069818064570427,0.012627052143216133,-0.05728277936577797,-0.0045610754750669,-0.012843461707234383,-0.010039540939033031,0.037241335958242416,-0.008976309560239315,0.026609018445014954,0.0194204431027174,0.0308807622641325,-0.007296780124306679,-0.01398196630179882,-0.0016501260688528419,-0.005970092490315437,0.014734696596860886,-0.02145281434059143,0.03353413566946983,0.01520515326410532,0.02086944878101349,-0.021678633987903595,-0.02997748553752899,-0.005254998803138733,0.001831251778639853,0.007362643722444773,0.025611650198698044,-0.013850238174200058,0.0019088771659880877,0.012288322672247887,-0.010914590209722519,-0.025517558678984642,0.0022593671455979347,0.00914096925407648,0.023278186097741127,-0.00821417011320591,-0.02297709323465824,-0.0075790537521243095,-0.0042929151095449924,0.031671129167079926,0.008124783635139465,-0.030636126175522804,-0.033684682101011276,0.0020805937238037586,-0.05430949479341507,-0.016437748447060585,0.01541215367615223,0.029695212841033936,-0.014330103993415833,-0.014010193757712841,-0.021659815683960915,0.01541215367615223,0.0437336340546608,0.019646262750029564,0.00173951277974993,-0.03895379602909088,0.01120627298951149,0.053669676184654236,-0.007974237203598022,0.012043685652315617,0.010425315238535404,-0.018724167719483376,0.0011620274744927883,0.0036131057422608137,-0.05589022859930992,0.011046318337321281,-0.017369253560900688,0.0177173912525177,-0.018583031371235847,-0.018630076199769974,-0.04843819886445999,-0.03441859409213066,0.02939411997795105,-0.039668891578912735,0.033195409923791885,-0.02786984108388424,-0.02937530167400837,0.019646262750029564,0.04922856390476227,-0.003958891145884991,-0.032103948295116425,-0.02397446148097515,0.009573789313435555,-0.017943209037184715,0.021057631820440292,0.02995866909623146,0.01949571631848812,0.013285690918564796,0.00485981535166502,-0.022036181762814522,-0.003149706171825528,0.04072271287441254,-0.0026698405854403973,0.017595071345567703,-0.01221305038779974,-0.025272922590374947,-0.0398947075009346,-0.0367332398891449,0.009315038099884987,0.013624419458210468,-0.02988339588046074,-0.026665473356842995,0.006878073792904615,0.02199854515492916,-0.004746905993670225,0.0011790815042331815,-0.09055346250534058,0.002916830126196146,0.01120627298951149,-0.0024157939478754997,-0.02549874037504196,0.0008538785041309893,-0.024275554344058037,-0.032612044364213943,0.015797927975654602,-0.04185180738568306,0.013125735335052013,0.017491571605205536,0.030015122145414352,0.04527673125267029,-0.007207393180578947,0.025781014934182167,0.0023993279319256544,0.01000190433114767,0.028622571378946304,-0.015393335372209549,0.002318174345418811,0.004318790510296822,-0.023108821362257004,0.003535480471327901,0.014188967645168304,-0.01652243174612522,0.0036413331981748343,0.02896130084991455,-0.02596919797360897,0.010716998018324375,-0.0015336880460381508,-0.023805096745491028,0.011667320504784584,-0.03304486349225044,-0.03825752064585686,-0.023579278960824013,0.01595788449048996,0.0013455054722726345,-0.00936678797006607,-0.008722263388335705,0.008087147027254105,-0.0057348646223545074,0.003998879808932543,0.02997748553752899,-0.002109997207298875,-0.018394848331809044,0.0721115693449974,-0.021753907203674316,-0.01652243174612522,-0.008632875978946686,-0.019815627485513687,0.019627444446086884,0.0003737188526429236,-0.01497933454811573,0.012156595475971699,-0.0169740691781044,-0.005673705134540796,-0.021791543811559677,-0.04904038459062576,0.0322733148932457,-0.0314076729118824,0.013088098727166653,0.04535200446844101,-0.03981943428516388,-0.003356707049533725,0.03541596233844757,-0.0467069186270237,0.03428686782717705,-0.017604481428861618,0.020041445270180702,0.011996639892458916,-0.033233046531677246,-0.015035788528621197,0.03345886617898941,-0.012071913108229637,0.0002596331760287285,-0.01914757862687111,-0.00485981535166502,-0.04192708060145378,0.03808815777301788,0.03129476308822632,0.00347902555949986,-0.004735144320875406,0.019307533279061317,0.011450910940766335,-0.02446373738348484,-0.036714423447847366,-0.0369214229285717,0.0003593111177906394,-0.023805096745491028,0.004897451959550381,-0.02882957272231579,0.016983479261398315,0.03537832573056221,-0.036168694496154785,-0.004125903360545635,-0.01875239424407482,0.007315597962588072,0.061159342527389526,0.06842318922281265,-0.004121198784559965,5.6123986723832786e-05,0.014772333204746246,-0.02147163264453411,0.01686115935444832,0.018093755468726158,-0.0033402410335838795,-0.00043811259092763066,-0.01397255714982748,0.0036789695732295513,0.004212937783449888,-0.021114086732268333,0.0498683862388134,0.02894248254597187,0.010669953189790249,0.02201736345887184,-0.010895771905779839,0.022544274106621742,-0.021151723340153694,0.02745584025979042,0.034173958003520966,0.007113301660865545,0.004791599232703447,-0.007165051996707916,-0.022167909890413284,0.038577429950237274,-0.036074601113796234,0.024708373472094536,0.018376030027866364,-0.018451303243637085,0.06627790629863739,0.01820666529238224,-0.006144161336123943,-0.013342145830392838,-0.008915150538086891,-0.004170596599578857,0.021189359948039055,0.03479496017098427,0.013464463874697685,-0.03697787970304489,-0.0008609353681094944,-0.0019747409969568253,0.011808457784354687,0.010651134885847569,-0.022186726331710815,-0.0026416131295263767,0.008077737875282764,0.015515654347836971,0.0040012323297560215,0.024708373472094536,0.03739187866449356,0.015223971568048,-0.0029332961421459913,-0.00887751393020153,0.020587176084518433,-0.021339906379580498,-0.0007327359635382891,0.016117839142680168,0.006440549157559872,0.01998499035835266,-0.007386166602373123,-0.009479697793722153,0.004920974839478731,-0.05137384682893753,-0.0023934473283588886,-0.00010291235230397433,-0.022638365626335144,-0.017726799473166466,-0.009832540526986122,-0.02288300357758999,0.01875239424407482,0.0097102215513587,-0.014038421213626862,-0.007070960942655802,-0.022243181243538857,0.03789997473359108,0.01722811535000801,0.021528087556362152,0.04693273827433586,-0.0001843601348809898,0.0015583870699629188,0.02499064803123474,-0.029149483889341354,0.01891235075891018,0.016353067010641098,0.021095268428325653,-0.012852870859205723,0.01123450044542551,-0.015129880048334599,0.0018641838105395436,-0.008463512174785137,-0.017397480085492134,0.003721310757100582,0.04226581007242203,-0.008614057675004005,-0.01891235075891018,0.024783646687865257,0.009766676463186741,0.03340240940451622,0.008806944824755192,0.010820498690009117,-0.020079081878066063,0.026289107277989388,0.0028533185832202435,0.05532567948102951,0.009079810231924057,-0.00787073653191328,-0.005640773102641106,-0.010143041610717773,-0.01673884131014347,0.003232036018744111,-0.01574147306382656,0.0076825544238090515,0.0188935324549675,-0.014555923640727997,0.0016936432803049684,-0.021640997380018234,0.009917222894728184,-0.021189359948039055,0.009173900820314884,0.02090708538889885,-0.012043685652315617,-0.018394848331809044,-0.037241335958242416,-0.04388418048620224,0.003549594199284911,0.024802464991807938,-0.021509269252419472,0.009126855991780758,0.018272530287504196,0.02487773820757866,-0.048099469393491745,0.01664474979043007,-0.009479697793722153,0.007437916938215494,-0.009832540526986122,0.010368860326707363,0.012852870859205723,-0.020191991701722145,0.0027639318723231554,0.007964828051626682,0.015873201191425323,-0.007155642844736576,0.0017877345671877265,-0.007343825418502092,0.07478375732898712,0.020643629133701324,0.004464631900191307,-0.007710781414061785,0.010716998018324375,0.043056175112724304,-0.023278186097741127,0.010764043778181076,-0.026740746572613716,-0.0248401015996933,-0.02884839102625847,-0.02337227761745453,0.04941674694418907,0.004398767836391926,-0.036074601113796234,0.02051190286874771,-0.003624867182224989,-0.0091221509501338,0.022600729018449783,-0.0195145346224308,0.01947689801454544,0.01719047874212265,0.05182548612356186,0.00547140883281827,0.053029853850603104,0.01391610223799944,-0.02491537481546402,-0.011140408925712109,-0.011780230328440666,0.007442621514201164,0.020568357780575752,0.0068310280330479145,0.024087371304631233,-0.009578493423759937,0.008981013670563698,0.017369253560900688,-0.01818784698843956,0.033214226365089417,-0.024764828383922577,-0.02386155165731907,0.016296612098813057,0.019665081053972244,0.013520918786525726,-0.03095603547990322,-0.02692892774939537,-0.020323719829320908,-0.027568750083446503,0.03453150391578674,-0.018705349415540695,-0.050056569278240204,0.01544979028403759,0.0067228227853775024,0.0006210025167092681,0.000787426542956382,0.010707588866353035,0.016616523265838623,0.0016477737808600068,0.01496051624417305,-0.008609353564679623,-0.012994008138775826,-0.02047426626086235,0.006162979640066624,0.0519760325551033,-0.0364886038005352,0.0009773733327165246,0.006732231937348843,0.0198532622307539,-0.04339490458369255,0.027135929092764854,0.024049734696745872,-0.00847762543708086,0.014875833876430988,-0.005400840193033218,-0.010190087370574474,-0.03131358325481415,0.01811257377266884,-0.0022182022221386433,-0.012570597231388092,0.0382198840379715,-0.0038177543319761753,0.02452019229531288,-0.024143826216459274,0.01750098168849945,0.0011737889144569635,0.03974416106939316,0.01541215367615223,0.01945807971060276,0.013464463874697685,0.03981943428516388,0.029714031144976616,0.0016948194243013859,-0.026533745229244232,-0.018630076199769974,-0.00394477741792798,0.002329935785382986,-0.020568357780575752,0.0013549146242439747,-0.015543881803750992,-0.0084399888291955,0.05491168051958084,-0.011535592377185822,-0.030222123488783836,-0.028622571378946304,0.03042912483215332,0.004991543013602495,0.023259367793798447,0.021057631820440292,-0.014452422969043255,-0.009606720879673958,-0.026627836748957634,-0.008190646767616272,-0.017566844820976257,0.03831397369503975,-0.0010591151658445597,0.029619939625263214,0.0037871745880693197,-0.028716662898659706,0.009380902163684368,0.0059795016422867775,-0.004441109020262957,-0.009597311727702618,0.004791599232703447,0.022186726331710815,0.010829907841980457,0.01121568214148283,0.01802789233624935,0.0011185102630406618,-0.0012749370653182268,-0.00758375832810998,-0.012834052555263042,0.019345169886946678,-0.02689129114151001,-0.02898011915385723,-0.013539737090468407,0.000229200508329086,0.01998499035835266,-0.0017206945922225714,0.01000190433114767,0.02235609106719494,-0.006407617125660181,-0.025084739550948143,0.016070792451500893,0.05129857361316681,-0.041625987738370895,-0.025536376982927322,-0.008750490844249725,0.0016748250927776098,0.0019135817419737577,0.014160740189254284,0.021584542468190193,-0.010048950091004372,-0.014038421213626862,-0.017632707953453064,-0.01393492054194212,0.020286083221435547,0.014264239929616451,-0.0038624475710093975,-0.0015560347819700837,-0.021565724164247513,-0.02233727276325226,-0.026157379150390625,-0.00974785815924406,0.008891627192497253,-0.015280426479876041,0.008411761373281479,0.008712854236364365,-0.01391610223799944,0.0244072824716568,0.015732064843177795,0.021546905860304832,-0.009776085615158081,0.01773620955646038,-0.025705741718411446,-0.04102380573749542,-0.014791151508688927,-0.0006592271383851767,0.016672978177666664,-0.011262727901339531,0.0005016241921111941,-0.017613889649510384,-0.005015065893530846,-0.023202912881970406,0.004490506835281849,-0.04237871989607811,-0.014188967645168304,0.004182358272373676,-0.01764211803674698,-0.0013431531842797995,0.0034766732715070248,-0.002180565847083926,0.00699568772688508,-0.011657911352813244,-0.023711007088422775,0.006017138250172138,0.011799048632383347,-0.017548026517033577,0.03891615942120552,0.007221506908535957,0.02399327978491783,0.0003810697526205331,0.03240504115819931,-0.005273817107081413,0.021584542468190193,0.06529936194419861,-0.02446373738348484,-0.0002536054525990039,-0.025649286806583405,-0.014367740601301193,-0.008571716956794262,-0.006017138250172138,0.057583872228860855,-0.009917222894728184,-0.01392551138997078,0.04252926632761955,-0.017218707129359245,0.006600504275411367,0.007433212362229824,0.02188563533127308,0.014800560660660267,-0.010905181057751179,-0.003881265874952078,-0.026119742542505264,-0.010359451174736023,0.007743713445961475,0.007014506030827761,0.021132905036211014,-0.019072305411100388,0.01270232442766428,-0.01123450044542551,0.024275554344058037,0.022638365626335144,0.019834445789456367,0.014734696596860886,-0.016832932829856873,-0.004335256293416023,0.028716662898659706,-0.009432652033865452,0.030316215008497238,-0.01615547575056553,-0.0194204431027174,-0.04098616912961006,-0.00012643517402466387,-0.010397087782621384,0.05077166110277176,0.00453284801915288,-0.018940577283501625,-0.03502077981829643,0.02051190286874771,0.004172949120402336,-0.01116863638162613,-0.03537832573056221,0.04919092729687691,-0.02691010944545269,-0.06029370054602623,-0.008952786214649677,0.011977821588516235,5.1603194151539356e-05,0.018234893679618835,0.022073818370699883,-0.04294326528906822,0.016710614785552025,0.0019794455729424953,0.03208513185381889,0.03876561298966408,-0.04490036517381668,0.006473481189459562,-0.04474981874227524,-0.018940577283501625,0.020060263574123383,0.0015513302059844136,0.0164471585303545,-0.009423242881894112,0.012975189834833145,-0.008971604518592358,-0.020286083221435547,0.00635586678981781,-0.0035989920143038034,-0.006586390547454357,0.011309773661196232,-0.02237490937113762,-0.02636438049376011,0.01793380081653595,0.03697787970304489,-0.027173565700650215,0.04034634679555893,-0.00016157238860614598,-0.018413666635751724,0.020549539476633072,-0.03483259677886963,-0.027023019269108772,-0.006468776613473892,-0.016531839966773987,-0.043093811720609665,-0.0033026044256985188,-0.022036181762814522,0.0140007846057415,-0.010321814566850662,0.0038342203479260206,-0.026119742542505264,-0.00023302297631744295,0.001808905159123242,0.025197649374604225,0.0398947075009346,-0.03400459513068199,-0.03784351795911789,0.022638365626335144,-0.02946939319372177,0.005254998803138733,-0.022243181243538857,-0.011930775828659534,-0.01785852760076523,-0.03899143263697624,0.021640997380018234,-0.015327472239732742,0.036657966673374176,-0.0013431531842797995,0.008124783635139465,-0.009573789313435555,0.0020558948162943125,0.00996426772326231,0.010848726145923138,-0.021264633163809776,0.028810754418373108,0.023184094578027725,-0.014809969812631607,0.01662593148648739,0.0219609085470438,0.006920414511114359,0.011704957112669945,-0.011366228573024273,0.01574147306382656,0.03487023338675499,0.0012243629898875952,-0.02645847201347351,-0.0004204704600851983,0.0007633156492374837,0.0014784095110371709,0.0034625595435500145,0.021095268428325653,0.006134752184152603,0.0010391207179054618,-0.01226009614765644,-0.016908206045627594,0.016531839966773987,-0.000643349252641201,0.053142763674259186,-0.01269291527569294,0.025122376158833504,0.017124615609645844,0.030240941792726517,0.023579278960824013,-0.029996303841471672,0.00609241146594286,0.027662839740514755,0.010811089538037777,0.0447874553501606,-0.019533352926373482,-0.008317670784890652,-0.0008427051361650229,-0.013417419046163559,0.03981943428516388,0.014838197268545628,0.012918734923005104,0.01095222681760788,-0.017077570781111717,0.015111061744391918,-0.0078048729337751865,0.027813386172056198,0.0020594231318682432,-0.0309748537838459,0.02741820365190506,-0.004121198784559965,0.010293588042259216,-0.03579232841730118,0.039217252284288406,0.01272114273160696,-0.008614057675004005,-0.0025404649786651134,-0.04437345266342163,-0.01243886910378933,0.01926048845052719,-0.0314076729118824,0.04441108927130699,0.013793784193694592,-0.007390871178358793,0.013135144487023354,0.0343809574842453,-0.00380128831602633,4.733968307846226e-05,-0.01673884131014347,-0.004149426240473986,0.03095603547990322,-0.023278186097741127,-0.01943926140666008,0.03093721717596054,-0.041174352169036865,0.053632039576768875,0.012937553226947784,0.029676394537091255,-0.03274377062916756,-0.030504398047924042,-0.002961523598060012,-0.006600504275411367,-0.006214729975908995,-0.013455054722726345,0.040647439658641815,-0.03746715188026428,0.04350781440734863,-0.018724167719483376,0.015111061744391918,0.018376030027866364,-0.01990971714258194,0.023598097264766693,-0.02096354030072689,-0.011930775828659534,0.006177093368023634,0.001768916379660368,-0.016832932829856873,0.0014090171316638589,-0.043169084936380386,-0.02585628814995289,-0.02595037966966629,-0.009841949678957462,-0.01068877149373293,0.004274097271263599,0.018724167719483376,0.0048880428075790405,-0.03558532893657684,-0.059126969426870346,-0.01793380081653595,-0.020568357780575752,0.010208905674517155,0.004643405321985483,0.02000380866229534,-0.01518633496016264,-0.024332009255886078,0.03622514754533768,0.021358724683523178,0.013774965889751911,-0.023729825392365456,0.006412321701645851,0.0008832820458337665,0.01399137545377016,0.0021229349076747894,0.015694428235292435,-0.04426054656505585,0.010387678630650043,0.00398476654663682,-0.010387678630650043,-0.005984206218272448,-0.010067768394947052,-0.05630423128604889,0.0032108654268085957,0.02796393260359764,-0.01373732928186655,0.030128031969070435,0.018140802159905434,0.001632483908906579,-0.013342145830392838,-0.017811482772231102,-0.026759564876556396,0.020079081878066063,-0.0363004207611084,0.010716998018324375,0.016099020838737488,0.03046676144003868,0.035114869475364685,-0.015844974666833878,-0.013342145830392838,0.01564738154411316,0.015619155019521713,0.03039148822426796,0.0024816577788442373,-0.002178213559091091,-0.018074937164783478,0.010331223718822002,0.041550714522600174,0.0017453934997320175,-0.014170149341225624,-0.010011313483119011,0.025705741718411446,-0.03137003630399704,-0.030504398047924042,0.00421528983861208,0.023560460656881332,-0.017181070521473885,-0.05137384682893753,-0.016362475231289864,-0.011752002872526646,-0.030033940449357033,-0.005240885075181723,0.0005580789875239134,-0.012278913520276546,-0.014443013817071915,0.0023711007088422775,-0.010820498690009117,0.0009985438082367182,0.03654506057500839,0.014631195925176144,0.012062503956258297,-0.007795463781803846,-0.007560235448181629,-0.0027309998404234648,0.04000761732459068,0.03048557974398136,-0.01593906618654728,0.017529208213090897,-0.013248054310679436,0.06706827133893967,-0.008731672540307045,0.003304956713691354,0.05329331010580063,0.008985718712210655,0.017962027341127396,-0.001289050793275237,-0.013822010718286037,0.021076450124382973,-0.020737720653414726,0.0022770094219595194,0.026684291660785675,-0.007376757450401783,0.04979311302304268,0.0018512462265789509,0.022713638842105865,0.0023169980850070715,-0.030259760096669197,0.021603360772132874,-0.03272495046257973,0.011921366676688194,-0.016851751133799553,-0.00971963070333004,0.036714423447847366,0.029243573546409607,0.029281210154294968,-0.005551386158913374,-0.014621786773204803,-0.038182247430086136,-0.03737306222319603,-0.032668497413396835,0.03829515725374222,0.017707981169223785,0.01800907403230667,-0.030033940449357033,-0.011272137053310871,0.053180400282144547,0.021847998723387718,0.016560068354010582,0.02145281434059143,0.027211202308535576,0.00636057136580348,-0.025781014934182167,-0.04196471720933914,0.029187120497226715,0.0019935593008995056,0.011592047289013863,-0.032630860805511475,-0.022261999547481537,0.03850215673446655,0.029676394537091255,-0.015713246539235115,-0.05724514275789261,0.010048950091004372,0.012307140976190567,0.005946569610387087,-0.024821283295750618,0.00883046817034483,-0.005217362195253372,0.012994008138775826,0.005240885075181723,-0.02886720933020115,0.0507340244948864,-0.021264633163809776,-0.011911957524716854,0.00950322113931179,-0.0517878495156765,-0.01998499035835266,-0.024670736864209175,-0.004591654986143112,-0.03599932789802551,0.026063289493322372,-0.004332904238253832,-0.021057631820440292,-0.015562700107693672,-0.041588351130485535,-0.03876561298966408,-0.016324840486049652,0.00031520583434030414,0.021640997380018234,0.030222123488783836,-0.008863399736583233,0.0273805670440197,0.006558163091540337]) as similarity_score
FROM shared_source.magento_product_dimension_with_text_embedding FROM shared_source.magento_product_dimension_with_text_embedding
ORDER BY similarity_score DESC ORDER BY similarity_score DESC
LIMIT 100 LIMIT 100
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
MAX_BY(discount_amount, similarity_score) as discount_amount, MAX_BY(discount_amount, similarity_score) as discount_amount,
MAX(similarity_score) as max_score MAX(similarity_score) as max_score
FROM top_matches FROM top_matches
WHERE 1=1 AND sale_price <= 500000.0 WHERE 1=1
GROUP BY internal_ref_code GROUP BY internal_ref_code
ORDER BY max_score DESC ORDER BY max_score DESC
LIMIT 10 LIMIT 10
...@@ -50,10 +50,10 @@ app.add_middleware( ...@@ -50,10 +50,10 @@ app.add_middleware(
allow_headers=["*"], allow_headers=["*"],
) )
app.include_router(conservation_router)
app.include_router(conservation_router) app.include_router(conservation_router)
app.include_router(chatbot_router) app.include_router(chatbot_router)
# --- MOCK API FOR LOAD TESTING --- # --- MOCK API FOR LOAD TESTING ---
try: try:
from api.mock_api_route import router as mock_router from api.mock_api_route import router as mock_router
......
version: '3.8'
services:
n8n:
image: n8nio/n8n:latest
container_name: n8n_local
ports:
- "5678:5678"
volumes:
- n8n_data:/home/node/.n8n
environment:
- N8N_HOST=localhost
- N8N_PORT=5678
- N8N_PROTOCOL=http
- WEBHOOK_URL=http://localhost:5678/
- GENERIC_TIMEZONE=Asia/Ho_Chi_Minh
restart: unless-stopped
volumes:
n8n_data:
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment