Commit 8fccd372 authored by Vũ Hoàng Anh's avatar Vũ Hoàng Anh

chore: ignore hehe, test, and scripts folders

parent 6e5f501c
......@@ -39,6 +39,11 @@ backend/*.pyc
# Preference folder (development/temporary)
preference/
# Development/Test folders
backend/hehe/
backend/test/
backend/scripts/
# OS
.DS_Store
Thumbs.db
......
"""
Performance Test API Server
Server giả lập để test tải với Locust - KHÔNG gọi OpenAI, KHÔNG gọi Postgres.
"""
import asyncio
import logging
import os
import sys
import time
from pathlib import Path
import uvicorn
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
# Setup path to import backend modules
sys.path.insert(0, str(Path(__file__).parent.parent))
# Setup Logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
handlers=[logging.FileHandler("perf_error.log", encoding="utf-8"), logging.StreamHandler(sys.stdout)],
)
logger = logging.getLogger(__name__)
# ============================================================
# 0. FORCE PROACTOR EVENT LOOP (CRITICAL FOR WINDOWS HIGH CONCURRENCY)
# ============================================================
if os.name == "nt":
try:
asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
logger.info("⚡ Windows ProactorEventLoopPolicy set (Supports high FD count)")
except Exception as e:
logger.warning(f"Could not set ProactorEventLoopPolicy: {e}")
# ============================================================
# 1. MOCK LLM - Trả lời siêu tốc, không gọi OpenAI
# ============================================================
from typing import Any
from langchain_core.language_models import BaseChatModel
from langchain_core.messages import AIMessage
from langchain_core.outputs import ChatGeneration, ChatResult
class MockHighSpeedLLM(BaseChatModel):
"""
Mock LLM siêu tốc cho Performance Testing.
Không gọi OpenAI - trả về response ngay lập tức.
"""
def _generate(
self, messages: list[Any], stop: list[str] | None = None, run_manager: Any | None = None, **kwargs: Any
) -> ChatResult:
return ChatResult(
generations=[
ChatGeneration(message=AIMessage(content="[MOCK] Xin chào! Đây là bot test, không phải OpenAI thật."))
]
)
@property
def _llm_type(self) -> str:
return "mock-high-speed"
def bind_tools(self, tools: Any, **kwargs: Any) -> Any:
"""Bypass tool binding - trả về self."""
return self
# ============================================================
# 2. PATCH create_llm TRƯỚC KHI IMPORT GRAPH
# ============================================================
def mock_create_llm(*args, **kwargs):
"""Factory function giả - trả về MockLLM."""
logger.info("🎭 MockLLM được gọi thay vì OpenAI!")
return MockHighSpeedLLM()
# Patch TRƯỚC khi import agent.graph
import common.llm_factory
common.llm_factory.create_llm = mock_create_llm
logger.info("🎭 PATCHED common.llm_factory.create_llm")
# ============================================================
# 3. PATCH Langfuse - Tắt để tránh Rate Limit
# ============================================================
def mock_get_callback_handler():
"""Trả về None - không gửi trace."""
return
import common.langfuse_client
common.langfuse_client.get_callback_handler = mock_get_callback_handler
logger.info("🔇 PATCHED common.langfuse_client.get_callback_handler")
# ============================================================
# 4. MOCK EMBEDDING - Để test DB mà không bị OpenAI Rate Limit
# ============================================================
import common.embedding_service
async def mock_create_embedding_async(text: str) -> list[float]:
"""Trả về vector giả lập (1536 dim) để test StarRocks."""
return [0.01] * 1536
common.embedding_service.create_embedding_async = mock_create_embedding_async
logger.info("🧠 PATCHED common.embedding_service.create_embedding_async (Using Mock Vector)")
# ============================================================
# 5. GIỜ MỚI IMPORT GRAPH (Sau khi patch xong)
# ============================================================
from langchain_core.messages import HumanMessage
from agent.graph import build_graph
from agent.models import get_config
# ============================================================
# 6. IMPORT CHO DB TEST
# ============================================================
from common.starrocks_connection import StarRocksConnection
# ============================================================
# FASTAPI APP
# ============================================================
app = FastAPI(title="Performance Test API")
# Request Models
class SearchRequest(BaseModel):
# Locust gửi 'message' hoặc 'query'
query: str = ""
message: str = ""
limit: int = 10
user_id: str = "perf_user"
thread_id: str = "perf_thread"
@property
def final_query(self) -> str:
return self.message or self.query
class MockParams(BaseModel):
query: str = "" # Phải là query, không phải query_text để build_starrocks_query nhận diện
limit: int = 10
sku: str = None
gender_by_product: str = None # Khớp với metadata fields
season: str = None
master_color: str = None
product_line_vn: str = None
price_min: float = None
price_max: float = None
# Global variables
mock_graph = None
@app.on_event("startup")
async def startup_event():
global mock_graph
logger.info("🚀 Performance Test API Server Starting...")
# 1. Pre-warm DB connection
conn = StarRocksConnection()
conn.connect()
logger.info("✅ StarRocks Connection initialized")
# 2. Build Mock Graph (LLM đã bị patch từ đầu file)
config = get_config()
mock_graph = build_graph(config)
logger.info("✅ Mock Graph built successfully (No OpenAI, No Postgres, No Langfuse)")
# ============================================================
# ENDPOINTS
# ============================================================
@app.post("/api/agent/chat")
async def api_agent_chat(request: SearchRequest):
"""
Test toàn bộ Flow Graph với MockLLM.
Dùng chung endpoint với hàng thật để Locust test đúng flow.
"""
if not mock_graph:
raise HTTPException(500, "Mock Graph not initialized")
start_time = time.time()
try:
# Lấy query thực tế (từ message hoặc query)
user_query = request.final_query
if not user_query:
return {"status": "error", "message": "Missing query/message"}
# Tạo thread_id
thread_id = request.thread_id or f"perf_test_{int(time.time() * 1000)}"
input_state = {
"messages": [HumanMessage(content=user_query)],
"user_id": request.user_id,
}
config_runnable = {"configurable": {"thread_id": thread_id}}
# Chạy Graph
async for _event in mock_graph.astream(input_state, config=config_runnable):
pass
process_time = time.time() - start_time
return {
"status": "success",
"process_time_seconds": round(process_time, 4),
"response": "[MOCK RESULT] Đây là câu trả lời giả lập từ Graph Flow.",
}
except Exception as e:
logger.error(f"Graph Error: {e}")
raise HTTPException(status_code=500, detail=str(e))
@app.post("/api/recommend/image")
async def api_recommend_image(body: dict = None):
"""Mock visual search."""
return {"status": "success", "products": []}
@app.post("/test/db-search")
async def test_db_search(request: SearchRequest):
"""
Test StarRocks Vector Search - Query trực tiếp, chỉ lấy internal_ref_code.
"""
start_time = time.time()
try:
# Tạo embedding vector từ query
embedding = await mock_create_embedding_async(request.final_query)
v_str = str(embedding)
# SQL query trực tiếp
sql = f"""
SELECT /*+ SET_VAR(ann_params='{{"ef_search":64}}') */
internal_ref_code,
approx_cosine_similarity(vector, {v_str}) as score
FROM shared_source.magento_product_dimension_with_text_embedding__tmp
ORDER BY score DESC
LIMIT 50
"""
# Execute query
db = StarRocksConnection()
results = await db.execute_query_async(sql)
# Trích xuất internal_ref_code
codes = [item.get("internal_ref_code") for item in results if item.get("internal_ref_code")]
process_time = time.time() - start_time
return {
"status": "success",
"count": len(codes),
"process_time_seconds": round(process_time, 4),
"codes": codes,
}
except Exception as e:
logger.error(f"DB Vector Search Error: {e}")
raise HTTPException(status_code=500, detail=str(e))
@app.post("/test/db-ping")
async def test_db_ping():
"""
Test kết nối DB thuần túy (SELECT 1).
"""
start_time = time.time()
try:
db = StarRocksConnection()
await db.execute_query_async("SELECT 1")
process_time = time.time() - start_time
return {"status": "success", "process_time_seconds": round(process_time, 4)}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
# Endpoint cũ để tương thích (Optional)
@app.post("/test/graph-mock-chat")
async def test_graph_mock_chat(request: SearchRequest):
return await api_agent_chat(request)
@app.get("/")
async def root():
return {"message": "Performance Test API is running!", "mode": "MOCK (No OpenAI)"}
# ============================================================
# MAIN
# ============================================================
if __name__ == "__main__":
print("=" * 60)
print("🚀 PERFORMANCE TEST SERVER")
print("=" * 60)
print("🎭 LLM: MockHighSpeedLLM (No OpenAI)")
print("🧠 Checkpointer: MemorySaver (No Postgres)")
print("🔇 Langfuse: Disabled")
print("⚡ Workers: 4")
print("⚙️ Loop: ProactorEventLoop (Windows Optimized)")
print("=" * 60)
# Note: On Windows, to use multiple workers, we must pass the app as an import string.
# reload MUST be False when using workers > 1.
uvicorn.run("hehe.api_server_perf:app", host="0.0.0.0", port=8000, workers=4, reload=False)
#!/usr/bin/env python3
"""
🦗 LOCUST MCP SERVER - Custom implementation
============================================================
MCP Server wrapper cho Locust performance testing.
Tích hợp trực tiếp vào project chatbot Canifa.
USAGE:
------
Add vào MCP config (mcp_config.json):
{
"mcpServers": {
"locust-canifa": {
"command": "python",
"args": [
"d:\\cnf\\chatbot_canifa\\backend\\locust\\locust_mcp_server.py"
],
"env": {
"LOCUST_HOST": "http://localhost:8000",
"LOCUST_USERS": "50",
"LOCUST_SPAWN_RATE": "10",
"LOCUST_RUN_TIME": "30s"
}
}
}
}
Sau đó hỏi AI: "Run locust test for locustfile_production.py"
"""
import asyncio
import json
import logging
import os
import subprocess
import sys
from pathlib import Path
from typing import Any, Optional
# MCP SDK imports
try:
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
except ImportError:
print("ERROR: MCP SDK not installed!", file=sys.stderr)
print("Install with: pip install mcp", file=sys.stderr)
sys.exit(1)
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("locust-mcp")
# ============================================================
# DEFAULT CONFIGS
# ============================================================
DEFAULT_CONFIG = {
"host": os.getenv("LOCUST_HOST", "http://localhost:8000"),
"users": int(os.getenv("LOCUST_USERS", "50")),
"spawn_rate": int(os.getenv("LOCUST_SPAWN_RATE", "10")),
"run_time": os.getenv("LOCUST_RUN_TIME", "30s"),
"locust_dir": str(Path(__file__).parent),
}
# ============================================================
# MCP SERVER IMPLEMENTATION
# ============================================================
app = Server("locust-canifa")
@app.list_tools()
async def list_tools() -> list[Tool]:
"""List available Locust testing tools."""
return [
Tool(
name="run_locust_test",
description="""
Run Locust performance test for Canifa chatbot.
Parameters:
- test_file: Locust test file (e.g., 'locustfile_production.py')
- headless: Run without UI (default: True)
- host: Target server (default: http://localhost:8000)
- users: Number of concurrent users (default: 50)
- spawn_rate: Users spawned per second (default: 10)
- run_time: Test duration (e.g., '30s', '2m', default: 30s)
Returns: Test results including RPS, response times, and failure rate.
""",
inputSchema={
"type": "object",
"properties": {
"test_file": {
"type": "string",
"description": "Locust test file name (e.g., 'locustfile_production.py')",
"default": "locustfile_production.py"
},
"headless": {
"type": "boolean",
"description": "Run in headless mode (no UI)",
"default": True
},
"host": {
"type": "string",
"description": "Target host URL",
"default": DEFAULT_CONFIG["host"]
},
"users": {
"type": "integer",
"description": "Number of concurrent users",
"default": DEFAULT_CONFIG["users"]
},
"spawn_rate": {
"type": "integer",
"description": "User spawn rate (users/second)",
"default": DEFAULT_CONFIG["spawn_rate"]
},
"run_time": {
"type": "string",
"description": "Test duration (e.g., '30s', '2m')",
"default": DEFAULT_CONFIG["run_time"]
}
},
"required": ["test_file"]
}
),
Tool(
name="list_locust_files",
description="List all available Locust test files in the project",
inputSchema={
"type": "object",
"properties": {}
}
),
Tool(
name="quick_performance_check",
description="""
Quick performance check using Python async benchmark (no Locust install needed).
Fast way to check if chatbot is responsive.
Parameters:
- endpoint: API endpoint to test (default: /api/agent/chat)
- requests: Total requests (default: 50)
- concurrency: Concurrent requests (default: 10)
""",
inputSchema={
"type": "object",
"properties": {
"endpoint": {
"type": "string",
"default": "/api/agent/chat"
},
"requests": {
"type": "integer",
"default": 50
},
"concurrency": {
"type": "integer",
"default": 10
}
}
}
)
]
@app.call_tool()
async def call_tool(name: str, arguments: Any) -> list[TextContent]:
"""Execute Locust testing tools."""
if name == "run_locust_test":
return await run_locust_test(arguments)
elif name == "list_locust_files":
return await list_locust_files()
elif name == "quick_performance_check":
return await quick_performance_check(arguments)
else:
raise ValueError(f"Unknown tool: {name}")
# ============================================================
# TOOL IMPLEMENTATIONS
# ============================================================
async def run_locust_test(args: dict) -> list[TextContent]:
"""Run Locust performance test."""
test_file = args.get("test_file", "locustfile_production.py")
headless = args.get("headless", True)
host = args.get("host", DEFAULT_CONFIG["host"])
users = args.get("users", DEFAULT_CONFIG["users"])
spawn_rate = args.get("spawn_rate", DEFAULT_CONFIG["spawn_rate"])
run_time = args.get("run_time", DEFAULT_CONFIG["run_time"])
locust_file_path = Path(DEFAULT_CONFIG["locust_dir"]) / test_file
if not locust_file_path.exists():
return [TextContent(
type="text",
text=f"❌ Error: Locust file not found: {locust_file_path}\n\n"
f"Available files:\n{await _get_locust_files_text()}"
)]
# Build Locust command
cmd = [
"locust",
"-f", str(locust_file_path),
"--host", host,
"-u", str(users),
"-r", str(spawn_rate),
"--run-time", run_time,
]
if headless:
cmd.append("--headless")
# Add stats output
cmd.extend(["--html", "locust_report.html"])
cmd.extend(["--csv", "locust_stats"])
logger.info(f"Running Locust: {' '.join(cmd)}")
try:
# Run Locust
result = subprocess.run(
cmd,
capture_output=True,
text=True,
timeout=300 # 5 minutes max
)
output = result.stdout + result.stderr
# Parse results
summary = _parse_locust_output(output)
response = f"""
🦗 LOCUST PERFORMANCE TEST RESULTS
{'='*60}
📝 Test Configuration:
- File: {test_file}
- Host: {host}
- Users: {users}
- Spawn Rate: {spawn_rate}/s
- Duration: {run_time}
{summary}
📊 Detailed Output:
{output}
💾 Reports Generated:
- HTML Report: locust_report.html
- CSV Stats: locust_stats_*.csv
"""
return [TextContent(type="text", text=response)]
except subprocess.TimeoutExpired:
return [TextContent(
type="text",
text="❌ Test timeout! Locust ran for more than 5 minutes."
)]
except FileNotFoundError:
return [TextContent(
type="text",
text="❌ Locust not installed!\n\n"
"Install with: pip install locust"
)]
except Exception as e:
logger.error(f"Locust error: {e}")
return [TextContent(
type="text",
text=f"❌ Error running Locust: {str(e)}"
)]
async def list_locust_files() -> list[TextContent]:
"""List available Locust test files."""
text = await _get_locust_files_text()
return [TextContent(type="text", text=text)]
async def _get_locust_files_text() -> str:
"""Get formatted list of Locust files."""
locust_dir = Path(DEFAULT_CONFIG["locust_dir"])
locust_files = list(locust_dir.glob("locustfile*.py"))
if not locust_files:
return "❌ No Locust test files found in locust directory!"
output = "📋 Available Locust Test Files:\n" + "="*60 + "\n\n"
for f in locust_files:
output += f" - {f.name}\n"
return output
async def quick_performance_check(args: dict) -> list[TextContent]:
"""Quick performance check without Locust."""
endpoint = args.get("endpoint", "/api/agent/chat")
requests = args.get("requests", 50)
concurrency = args.get("concurrency", 10)
# Run quick benchmark script
benchmark_script = Path(DEFAULT_CONFIG["locust_dir"]) / "quick_benchmark.py"
if not benchmark_script.exists():
return [TextContent(
type="text",
text="❌ Quick benchmark script not found!"
)]
cmd = [
"python",
str(benchmark_script),
"--method", "python",
"--endpoint", endpoint,
"--requests", str(requests),
"--concurrency", str(concurrency)
]
try:
result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
output = result.stdout + result.stderr
return [TextContent(type="text", text=output)]
except Exception as e:
return [TextContent(
type="text",
text=f"❌ Error running quick benchmark: {str(e)}"
)]
def _parse_locust_output(output: str) -> str:
"""Parse Locust output to extract key metrics."""
lines = output.split('\n')
summary = "📊 Key Metrics:\n"
# Look for key metrics in output
for line in lines:
if "requests/s" in line.lower() or "rps" in line.lower():
summary += f" {line.strip()}\n"
elif "response time" in line.lower():
summary += f" {line.strip()}\n"
elif "failure" in line.lower():
summary += f" {line.strip()}\n"
if summary == "📊 Key Metrics:\n":
summary += " (Check detailed output below)\n"
return summary
# ============================================================
# MAIN - Start MCP Server
# ============================================================
async def main():
"""Start the MCP server."""
logger.info("🦗 Starting Locust MCP Server for Canifa Chatbot...")
logger.info(f"📁 Locust directory: {DEFAULT_CONFIG['locust_dir']}")
logger.info(f"🎯 Default host: {DEFAULT_CONFIG['host']}")
async with stdio_server() as (read_stream, write_stream):
await app.run(
read_stream,
write_stream,
app.create_initialization_options()
)
if __name__ == "__main__":
asyncio.run(main())
This source diff could not be displayed because it is too large. You can view the blob instead.
Method,Name,Error,Occurrences
POST,/api/agent/chat [MAIN],CatchResponseError('Status: 404'),80
POST,/api/agent/chat [MAIN],CatchResponseError('Status: 0'),7
POST,/test/db-search [DB_ONLY],HTTPError('404 Client Error: Not Found for url: /test/db-search [DB_ONLY]'),231
Type,Name,Request Count,Failure Count,Median Response Time,Average Response Time,Min Response Time,Max Response Time,Average Content Size,Requests/s,Failures/s,50%,66%,75%,80%,90%,95%,98%,99%,99.9%,99.99%,100%
POST,/api/agent/chat [MAIN],87,87,1200.0,1715.7884011494702,1.9360000733286142,6251.831999979913,20.229885057471265,3.7430531309093538,3.7430531309093538,1200,1500,2600,2600,4300,6000,6200,6300,6300,6300,6300
GET,/health,6,0,760.0,1024.0447499866907,86.62169997114688,2807.93920008,17220.0,0.25814159523512786,0.0,770,770,1500,1500,2800,2800,2800,2800,2800,2800,2800
POST,/test/db-search [DB_ONLY],231,231,800.0,1095.216632898276,2.817099913954735,7268.948999932036,22.0,9.938451416552422,9.938451416552422,800,1200,1300,1500,2600,3100,5100,5800,7300,7300,7300
,Aggregated,324,318,890.0,1260.533646911808,1.9360000733286142,7268.948999932036,340.0061728395062,13.939646142696905,13.681504547461776,890,1300,1500,2100,2600,4200,6000,6200,7300,7300,7300
Timestamp,User Count,Type,Name,Requests/s,Failures/s,50%,66%,75%,80%,90%,95%,98%,99%,99.9%,99.99%,100%,Total Request Count,Total Failure Count,Total Median Response Time,Total Average Response Time,Total Min Response Time,Total Max Response Time,Total Average Content Size
1767665177,0,,Aggregated,0.000000,0.000000,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,0,0,0,0.0,0,0,0
1767665178,10,,Aggregated,0.000000,0.000000,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,0,0,0,0.0,0,0,0
1767665179,20,,Aggregated,0.000000,0.000000,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,0,0,0,0.0,0,0,0
1767665180,30,,Aggregated,0.000000,0.000000,2600,2600,2600,2600,2600,2800,2800,2800,2800,2800,2800,12,11,2600.0,2172.7166083583143,13.209500000812113,2807.93920008,1455.1666666666667
1767665181,40,,Aggregated,0.000000,0.000000,2600,2600,2600,2800,3000,3100,3200,3200,3200,3200,3200,23,22,2600.0,2127.1039913256614,13.209500000812113,3222.1904000034556,769.7391304347826
1767665182,50,,Aggregated,3.333333,3.000000,2600,2600,2600,2600,2900,3100,3400,3400,3400,3400,3400,41,40,2600.0,1878.9734097606524,13.209500000812113,3379.774900036864,441.4634146341463
1767665183,50,,Aggregated,4.000000,3.750000,2600,2600,2600,2600,2900,3100,3200,3400,3400,3400,3400,51,50,2600.0,1680.0740803934304,13.209500000812113,3379.774900036864,359.2156862745098
1767665185,50,,Aggregated,6.000000,5.800000,2400,2600,2600,2600,3100,3500,4100,4200,4200,4200,4200,68,67,1100.0,1646.725695587092,13.209500000812113,4216.733000008389,274.9117647058824
1767665186,50,,Aggregated,7.285714,7.142857,1400,2600,2600,2600,3400,4200,5200,5600,5600,5600,5600,80,79,1400.0,1743.8070987467654,13.209500000812113,5642.961099976674,236.975
1767665187,50,,Aggregated,8.500000,8.375000,1200,2600,2600,2600,3500,5000,5200,5600,6100,6100,6100,106,105,1200.0,1724.8162594327334,13.209500000812113,6071.023600059561,184.24528301886792
1767665188,50,,Aggregated,8.777778,8.666667,1100,2400,2600,2600,5000,6000,6300,7200,7300,7300,7300,137,136,1100.0,1771.4287751791046,13.209500000812113,7268.948999932036,147.53284671532847
1767665190,50,,Aggregated,13.000000,12.900000,850,1200,2600,2600,3600,5600,6200,7200,7300,7300,7300,169,166,850.0,1466.0903857917574,2.817099913954735,7268.948999932036,327.2899408284024
1767665191,50,,Aggregated,13.900000,13.800000,830,1200,2400,2600,3200,5200,6200,7200,7300,7300,7300,191,188,830.0,1378.066231930599,2.817099913954735,7268.948999932036,292.1256544502618
1767665192,50,,Aggregated,13.900000,13.800000,870,1200,1500,2600,3100,5200,6100,6300,7300,7300,7300,203,200,870.0,1357.2945172349082,2.817099913954735,7268.948999932036,275.83251231527095
1767665193,50,,Aggregated,16.600000,16.400000,950,1200,1500,2600,3000,5200,6100,6300,7300,7300,7300,217,214,950.0,1361.8354663539308,2.817099913954735,7268.948999932036,259.4562211981567
1767665194,50,,Aggregated,17.300000,17.100000,1100,1400,2100,2500,2900,5000,6100,6300,7300,7300,7300,249,246,1100.0,1419.1991526060308,2.817099913954735,7268.948999932036,228.93975903614458
1767665196,50,,Aggregated,16.600000,16.400000,1100,1400,1900,2500,2900,5000,6100,6300,7300,7300,7300,258,252,1000.0,1382.0461077478446,1.9360000733286142,7268.948999932036,421.69767441860466
1767665197,50,,Aggregated,18.600000,18.100000,950,1300,1600,2400,2600,4200,6100,6300,7300,7300,7300,283,277,950.0,1327.5479494664792,1.9360000733286142,7268.948999932036,386.3886925795053
1767665199,50,,Aggregated,17.600000,17.100000,840,1200,1500,2300,2600,4100,6000,6200,7300,7300,7300,308,302,830.0,1242.4250762961055,1.9360000733286142,7268.948999932036,356.7402597402597
1767665200,50,,Aggregated,16.000000,15.500000,890,1300,1500,2100,2600,4200,6000,6200,7300,7300,7300,324,318,890.0,1260.533646911808,1.9360000733286142,7268.948999932036,340.0061728395062
import random
from locust import HttpUser, between, task
# Danh sách từ khóa để random
PRODUCTS = [
"áo phông",
"áo khoác",
"áo len",
"áo polo",
"áo sơ mi",
"quần jean",
"quần âu",
"quần short",
"váy liền",
"chân váy",
]
COLORS = ["", "màu đen", "màu trắng", "màu đỏ", "màu xanh", "màu vàng", "màu be"]
GENDERS = ["", "nam", "nữ", "bé trai", "bé gái"]
class FashionUser(HttpUser):
# Thời gian nghỉ giữa các lần request (mô phỏng người thật: 1-3s)
wait_time = between(1, 3)
# --- DB TESTS (ĐÃ TEST XONG, TẠM TẮT) ---
# @task(3)
# def search_random_product(self):
# """
# Giả lập user tìm kiếm sản phẩm bất kỳ.
# Ghép random: [Sản phẩm] + [Giới tính] + [Màu]
# Ví dụ: "áo phông nam màu đen"
# """
# # Random query builder
# p = random.choice(PRODUCTS)
# g = random.choice(GENDERS)
# c = random.choice(COLORS)
# query = f"{p} {g} {c}".strip()
# # Loại bỏ khoảng trắng thừa
# query = " ".join(query.split())
# self.client.post("/test/db-search", json={"query": query, "limit": 10}, name="/test/db-search (Dynamic)")
# @task(1)
# def search_hot_items(self):
# """
# Giả lập các món Hot trend nhiều người tìm giống nhau (Cache test)
# """
# hot_item = "áo giữ nhiệt"
# self.client.post("/test/db-search", json={"query": hot_item, "limit": 10}, name="/test/db-search (Hero Item)")
# @task(3)
# def fast_ping_check(self):
# """
# Kiểm tra tốc độ thuần túy của việc lấy Connection + SELECT 1.
# Nếu cái này < 100ms mà Search > 3000ms --> Do câu Query nặng.
# Nếu cái này cũng > 3000ms --> Do Pool bị nghẽn (Server quá tải).
# """
# self.client.post("/test/db-ping", json={}, name="/test/db-ping (Isolate Latency)")
# --- LOGIC GRAPH TEST (BẬT) ---
@task(1)
def check_graph_overhead(self):
"""
Test Logic Flow (Mock LLM + MemorySaver).
⚠️ Chạy với 300-500 users thôi, không nên quá 1000.
"""
query = random.choice(["hello", "tìm áo", "giá bao nhiêu", "có màu gì"])
self.client.post("/test/graph-mock-chat", json={"query": query}, name="/test/graph Logic (Mock LLM)")
"""
🎯 Production-like Performance Testing with Locust
Test thực tế các endpoint QUAN TRỌNG của chatbot để tìm bottleneck.
CÁCH CHẠY:
1. Start API server:
cd backend
python run.py
2. Run Locust với file này:
locust -f locust/locustfile_production.py --host=http://localhost:8000
3. Mở browser: http://localhost:8089
- Number of users: 50 (tăng dần để test scalability)
- Spawn rate: 10 users/second
- Host: http://localhost:8000
📊 METRICS CẦN QUAN TÂM:
- Response Time (P50, P95, P99): Thời gian phản hồi
- RPS (Requests per second): Throughput
- Failure Rate: Tỷ lệ lỗi
"""
import random
import time
from locust import HttpUser, between, events, task
# ============================================================
# TEST DATA - Mô phỏng real user queries
# ============================================================
CHAT_QUERIES = [
# Simple product search (nhanh)
"tìm áo phông nam",
"quần jean nữ",
"váy liền",
# Complex search với filters (chậm hơn)
"áo khoác nam màu đen giá dưới 500k",
"quần short nữ mùa hè",
# Knowledge queries (test RAG knowledge base)
"Canifa có cửa hàng ở Hà Nội không?",
"chính sách đổi trả",
"bảng size áo nam",
# Conversational (test LLM reasoning)
"xin chào",
"cảm ơn bạn",
]
VISUAL_SEARCH_IMAGES = [
"https://canifa.com/img/500/750/resize/6/t/6ts24w007-sk010-m-1-u.webp",
"https://canifa.com/img/500/750/resize/6/a/6aw24w006-sb492-thumb-2.webp",
]
# ============================================================
# CUSTOM METRICS - Track specific bottlenecks
# ============================================================
chat_response_times = []
db_query_times = []
llm_call_times = []
@events.test_stop.add_listener
def on_test_stop(environment, **kwargs):
"""Print summary khi test xong."""
print("\n" + "=" * 60)
print("📊 PERFORMANCE TEST SUMMARY")
print("=" * 60)
if chat_response_times:
avg_chat = sum(chat_response_times) / len(chat_response_times)
print(f"💬 Chat Response Time (avg): {avg_chat:.2f}ms")
print(f" P50: {sorted(chat_response_times)[len(chat_response_times) // 2]:.2f}ms")
print(f" P95: {sorted(chat_response_times)[int(len(chat_response_times) * 0.95)]:.2f}ms")
print("\n🎯 RECOMMENDED ACTIONS:")
print("1. Check response times > 2000ms")
print("2. Identify which endpoint has highest latency")
print("3. Use profiling tools (below) for deep dive")
print("=" * 60 + "\n")
# ============================================================
# USER BEHAVIORS
# ============================================================
class ChatbotUser(HttpUser):
"""Mô phỏng user sử dụng chatbot."""
wait_time = between(2, 5) # User thường đợi 2-5s giữa các câu hỏi
def on_start(self):
"""Initialize user - giống như user mở chat lần đầu."""
self.user_id = f"user_{random.randint(1000, 9999)}"
self.thread_id = f"thread_{int(time.time() * 1000)}_{random.randint(0, 999)}"
@task(10)
def chat_message(self):
"""
Task quan trọng nhất - test FULL chatbot flow.
Weight: 10 (chiếm 10/12 = 83% traffic)
"""
query = random.choice(CHAT_QUERIES)
start_time = time.time()
with self.client.post(
"/api/agent/chat",
json={"message": query, "user_id": self.user_id, "thread_id": self.thread_id},
catch_response=True,
name="/api/agent/chat [MAIN]",
) as response:
duration = (time.time() - start_time) * 1000 # ms
chat_response_times.append(duration)
if response.status_code == 200:
response.success()
else:
response.failure(f"Status: {response.status_code}")
@task(1)
def health_check(self):
"""
Health check endpoint.
Weight: 1 (8% traffic)
"""
self.client.get("/", name="/health")
@task(1)
def visual_search(self):
"""
Test visual search nếu có.
Weight: 1 (8% traffic)
"""
img_url = random.choice(VISUAL_SEARCH_IMAGES)
self.client.post(
"/api/recommend/image",
json={"image_url": img_url},
name="/api/recommend/image [VISUAL]",
catch_response=True,
)
class DatabaseStressUser(HttpUser):
"""
Riêng để test ONLY database bottleneck.
Chạy riêng test này để isolate DB performance.
"""
wait_time = between(8, 20) # Mô phỏng người dùng thật: đợi 8-20s giữa các lần search
@task
def db_direct_query(self):
"""Test trực tiếp StarRocks query speed."""
self.client.post(
"/test/db-search",
json={"query": random.choice(["áo phông", "quần jean", "váy"]), "limit": 10},
name="/test/db-search [DB_ONLY]",
)
# ============================================================
# SHAPE CONFIGURATION - Simulate real traffic pattern
# ============================================================
class WebsiteUserShape:
"""
Mô phỏng traffic pattern thực tế:
- Sáng sớm: ít user
- Trưa + tối: peak hours
- Đêm khuya: vắng
CHỈ DÙNG KHI MUỐN TEST THEO PATTERN, KHÔNG DÙNG CHO TEST NHANH.
"""
pass # Implement nếu cần
if __name__ == "__main__":
print("🚀 Starting Locust Performance Test...")
print("📖 Read the docstring for instructions!")
{
"mcpServers": {
"dbeaver": {
"command": "dbeaver-mcp-server",
"env": {
"DBEAVER_DEBUG": "false",
"DBEAVER_TIMEOUT": "30000"
}
},
"sequential-thinking": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-sequential-thinking"
],
"env": {}
},
"docs-langchain": {
"serverUrl": "https://docs.langchain.com/mcp"
},
"playwright": {
"command": "npx",
"args": [
"@playwright/mcp@latest"
]
},
"locust-canifa": {
"command": "python",
"args": [
"d:\\cnf\\chatbot_canifa\\backend\\locust\\locust_mcp_server.py"
],
"env": {
"LOCUST_HOST": "http://localhost:8000",
"LOCUST_USERS": "50",
"LOCUST_SPAWN_RATE": "10",
"LOCUST_RUN_TIME": "30s"
}
}
}
}
\ No newline at end of file
"""
🔍 BOTTLENECK PROFILER - Tìm chỗ code chậm nhất
============================================================
Tool này sẽ:
1. Profile TOÀN BỘ API call (từ request → response)
2. Breakdown time cho từng bước: LLM, DB, Tool calls, etc.
3. Export chi tiết ra file để phân tích
CÁCH DÙNG:
----------
1. Chạy script này:
python locust/profiler_bottleneck.py
2. Xem kết quả trong:
- Terminal: Summary report
- File: profiler_results.json (chi tiết)
3. Phân tích bottleneck:
- LLM calls > 1000ms? → Cân nhắc streaming hoặc cache
- DB queries > 500ms? → Optimize index hoặc query
- Tool execution > 2000ms? → Check logic tool
============================================================
"""
import asyncio
import json
import logging
import sys
import time
from pathlib import Path
# Setup path
sys.path.insert(0, str(Path(__file__).parent.parent))
from agent.graph import build_graph
from agent.config import get_config
from langchain_core.messages import HumanMessage
from common.starrocks_connection import StarRocksConnection
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# ============================================================
# PROFILING DECORATORS
# ============================================================
class PerformanceProfiler:
"""Track và log performance của mỗi step."""
def __init__(self):
self.metrics = {
"total_time": 0,
"llm_calls": [],
"db_queries": [],
"tool_executions": [],
"graph_steps": []
}
self.current_step_start = None
def start_timer(self):
"""Bắt đầu đếm thời gian."""
self.current_step_start = time.time()
def record_llm_call(self, duration_ms: float, tokens: int = 0):
"""Record LLM call time."""
self.metrics["llm_calls"].append({
"duration_ms": duration_ms,
"tokens": tokens
})
def record_db_query(self, duration_ms: float, query_type: str):
"""Record DB query time."""
self.metrics["db_queries"].append({
"duration_ms": duration_ms,
"type": query_type
})
def record_tool_execution(self, tool_name: str, duration_ms: float):
"""Record tool execution time."""
self.metrics["tool_executions"].append({
"tool": tool_name,
"duration_ms": duration_ms
})
def record_graph_step(self, step_name: str, duration_ms: float):
"""Record graph node execution time."""
self.metrics["graph_steps"].append({
"step": step_name,
"duration_ms": duration_ms
})
def get_summary(self):
"""Tính toán summary statistics."""
llm_total = sum(c["duration_ms"] for c in self.metrics["llm_calls"])
llm_avg = llm_total / len(self.metrics["llm_calls"]) if self.metrics["llm_calls"] else 0
db_total = sum(q["duration_ms"] for q in self.metrics["db_queries"])
db_avg = db_total / len(self.metrics["db_queries"]) if self.metrics["db_queries"] else 0
tool_total = sum(t["duration_ms"] for t in self.metrics["tool_executions"])
return {
"total_time_ms": self.metrics["total_time"],
"llm": {
"total_ms": llm_total,
"avg_ms": llm_avg,
"count": len(self.metrics["llm_calls"]),
"percentage": (llm_total / self.metrics["total_time"] * 100) if self.metrics["total_time"] > 0 else 0
},
"db": {
"total_ms": db_total,
"avg_ms": db_avg,
"count": len(self.metrics["db_queries"]),
"percentage": (db_total / self.metrics["total_time"] * 100) if self.metrics["total_time"] > 0 else 0
},
"tools": {
"total_ms": tool_total,
"count": len(self.metrics["tool_executions"]),
"percentage": (tool_total / self.metrics["total_time"] * 100) if self.metrics["total_time"] > 0 else 0
},
"graph_steps": self.metrics["graph_steps"]
}
def print_report(self):
"""In báo cáo đẹp mắt ra terminal."""
summary = self.get_summary()
print("\n" + "="*60)
print("🔍 BOTTLENECK PROFILING REPORT")
print("="*60)
print(f"⏱️ TOTAL TIME: {summary['total_time_ms']:.2f}ms\n")
print("📊 BREAKDOWN BY COMPONENT:")
print(f" 🤖 LLM Calls:")
print(f" - Time: {summary['llm']['total_ms']:.2f}ms ({summary['llm']['percentage']:.1f}%)")
print(f" - Count: {summary['llm']['count']}")
print(f" - Avg: {summary['llm']['avg_ms']:.2f}ms/call\n")
print(f" 🗄️ Database Queries:")
print(f" - Time: {summary['db']['total_ms']:.2f}ms ({summary['db']['percentage']:.1f}%)")
print(f" - Count: {summary['db']['count']}")
print(f" - Avg: {summary['db']['avg_ms']:.2f}ms/query\n")
print(f" 🔧 Tool Executions:")
print(f" - Time: {summary['tools']['total_ms']:.2f}ms ({summary['tools']['percentage']:.1f}%)")
print(f" - Count: {summary['tools']['count']}\n")
print("📈 GRAPH EXECUTION STEPS:")
for step in summary['graph_steps']:
print(f" ├─ {step['step']}: {step['duration_ms']:.2f}ms")
print("\n" + "="*60)
print("🎯 BOTTLENECK ANALYSIS:")
# Identify bottleneck
components = [
("LLM", summary['llm']['percentage']),
("Database", summary['db']['percentage']),
("Tools", summary['tools']['percentage'])
]
bottleneck = max(components, key=lambda x: x[1])
if bottleneck[1] > 40:
print(f"⚠️ PRIMARY BOTTLENECK: {bottleneck[0]} ({bottleneck[1]:.1f}%)")
if bottleneck[0] == "LLM":
print("💡 RECOMMENDATIONS:")
print(" - Consider streaming response instead of waiting full completion")
print(" - Cache common queries/responses")
print(" - Use lighter model for simple tasks")
elif bottleneck[0] == "Database":
print("💡 RECOMMENDATIONS:")
print(" - Check if vector index is being used (EXPLAIN query)")
print(" - Optimize WHERE clauses and add proper indexes")
print(" - Consider connection pooling")
print(" - Cache frequent queries")
elif bottleneck[0] == "Tools":
print("💡 RECOMMENDATIONS:")
print(" - Profile individual tool execution")
print(" - Add caching for deterministic tools")
print(" - Parallelize independent tool calls")
else:
print("✅ No single dominant bottleneck - well balanced!")
print("="*60 + "\n")
# ============================================================
# TEST QUERIES
# ============================================================
TEST_QUERIES = [
"tìm áo phông nam màu đen",
"Canifa có cửa hàng ở Hà Nội không?",
"quần jean nữ giá dưới 500k",
"chính sách đổi trả như thế nào?",
"áo khoác mùa đông",
]
# ============================================================
# MAIN PROFILING FUNCTION
# ============================================================
async def profile_chatbot_performance():
"""Chạy test và profile performance."""
print("🚀 Starting Performance Profiling...")
print("="*60)
# Build graph
config = get_config()
graph = build_graph(config)
# Test với mỗi query
all_profiles = []
for i, query in enumerate(TEST_QUERIES, 1):
print(f"\n[{i}/{len(TEST_QUERIES)}] Testing: '{query}'")
profiler = PerformanceProfiler()
# Tạo unique thread
thread_id = f"profile_test_{int(time.time() * 1000)}"
# Run graph với profiling
start_time = time.time()
try:
input_state = {
"messages": [HumanMessage(content=query)],
"user_id": "profiler_user"
}
config_runnable = {"configurable": {"thread_id": thread_id}}
step_count = 0
async for event in graph.astream(input_state, config=config_runnable):
step_count += 1
step_name = list(event.keys())[0] if event else f"step_{step_count}"
step_time = time.time()
# Record step (simplified - trong production cần hook vào LangGraph callbacks)
profiler.record_graph_step(step_name, 0) # TODO: measure actual step time
total_time = (time.time() - start_time) * 1000
profiler.metrics["total_time"] = total_time
# Mock some metrics (in production, hook into actual LLM/DB calls)
# Đây chỉ là example - cần integrate với actual callbacks
profiler.record_llm_call(800, 150) # Example
profiler.record_db_query(300, "vector_search") # Example
all_profiles.append({
"query": query,
"profiler": profiler,
"summary": profiler.get_summary()
})
print(f" ✅ Completed in {total_time:.2f}ms")
except Exception as e:
print(f" ❌ Error: {e}")
# Print aggregate report
print("\n" + "="*60)
print("📊 AGGREGATE REPORT")
print("="*60)
for profile in all_profiles:
print(f"\nQuery: '{profile['query']}'")
profile['profiler'].print_report()
# Save detailed results
results_file = Path(__file__).parent / "profiler_results.json"
with open(results_file, "w", encoding="utf-8") as f:
json.dump(
{
"timestamp": time.time(),
"results": [
{
"query": p["query"],
"summary": p["summary"]
}
for p in all_profiles
]
},
f,
indent=2,
ensure_ascii=False
)
print(f"\n💾 Detailed results saved to: {results_file}")
print("\n✅ Profiling complete!")
# ============================================================
# BONUS: Quick DB Query Profiler
# ============================================================
async def profile_db_queries():
"""Profile ONLY database queries để tìm slow queries."""
print("\n🗄️ DATABASE QUERY PROFILER")
print("="*60)
test_queries = [
("Simple text search", """
SELECT * FROM shared_source.canifa_products_by_sku
WHERE LOWER(product_name) LIKE '%áo phông%'
LIMIT 10
"""),
("Vector search (no filters)", """
WITH top_candidates AS (
SELECT internal_ref_code,
approx_cosine_similarity(embedding, [0.1, 0.2, /* ... 766 more ... */]) as score
FROM shared_source.canifa_products_by_sku
ORDER BY score DESC
LIMIT 100
)
SELECT * FROM top_candidates WHERE score > 0.7
"""),
]
db = StarRocksConnection()
for name, sql in test_queries:
print(f"\nTesting: {name}")
start = time.time()
try:
# Execute query
await db.execute_query_async(sql)
duration = (time.time() - start) * 1000
print(f" ⏱️ Duration: {duration:.2f}ms")
if duration > 500:
print(f" ⚠️ SLOW QUERY DETECTED!")
print(f" 💡 Recommendation: Run EXPLAIN to check index usage")
except Exception as e:
print(f" ❌ Error: {e}")
print("\n" + "="*60)
# ============================================================
# ENTRY POINT
# ============================================================
if __name__ == "__main__":
print(__doc__)
# Chạy main profiler
asyncio.run(profile_chatbot_performance())
# Bonus: DB profiler
# asyncio.run(profile_db_queries())
"""
🚀 QUICK BENCHMARK TOOL - Test nhanh performance
============================================================
Multiple testing methods trong 1 file:
1. Pure Python asyncio benchmark
2. Apache Bench wrapper
3. wrk wrapper (if installed)
4. Quick Locust headless test
USAGE:
------
python locust/quick_benchmark.py --method all
python locust/quick_benchmark.py --method ab
python locust/quick_benchmark.py --method locust
python locust/quick_benchmark.py --endpoint /api/agent/chat
"""
import argparse
import asyncio
import json
import subprocess
import sys
import time
from pathlib import Path
from typing import List, Dict
import aiohttp
# ============================================================
# 1. PURE PYTHON ASYNC BENCHMARK
# ============================================================
async def async_benchmark(
url: str,
total_requests: int = 100,
concurrent: int = 10,
payload: Dict = None
):
"""
Pure Python async benchmark - không cần tool ngoài.
Giống ab nhưng flexible hơn.
"""
print(f"\n🐍 PYTHON ASYNC BENCHMARK")
print("="*60)
print(f"URL: {url}")
print(f"Total Requests: {total_requests}")
print(f"Concurrency: {concurrent}")
print("="*60)
results = {
"success": 0,
"failed": 0,
"response_times": []
}
async def single_request(session, sem):
async with sem:
start = time.time()
try:
if payload:
async with session.post(url, json=payload) as resp:
await resp.text()
duration = (time.time() - start) * 1000
results["response_times"].append(duration)
if resp.status == 200:
results["success"] += 1
else:
results["failed"] += 1
else:
async with session.get(url) as resp:
await resp.text()
duration = (time.time() - start) * 1000
results["response_times"].append(duration)
if resp.status == 200:
results["success"] += 1
else:
results["failed"] += 1
except Exception as e:
results["failed"] += 1
print(f"❌ Error: {e}")
# Run benchmark
sem = asyncio.Semaphore(concurrent)
start_time = time.time()
async with aiohttp.ClientSession() as session:
tasks = [single_request(session, sem) for _ in range(total_requests)]
await asyncio.gather(*tasks)
total_time = time.time() - start_time
# Calculate stats
if results["response_times"]:
sorted_times = sorted(results["response_times"])
p50 = sorted_times[len(sorted_times) // 2]
p95 = sorted_times[int(len(sorted_times) * 0.95)]
p99 = sorted_times[int(len(sorted_times) * 0.99)]
avg = sum(sorted_times) / len(sorted_times)
print(f"\n📊 RESULTS:")
print(f"Total Time: {total_time:.2f}s")
print(f"Requests/sec: {total_requests / total_time:.2f}")
print(f"Success: {results['success']}")
print(f"Failed: {results['failed']}")
print(f"\nResponse Times:")
print(f" Avg: {avg:.2f}ms")
print(f" P50: {p50:.2f}ms")
print(f" P95: {p95:.2f}ms")
print(f" P99: {p99:.2f}ms")
print(f" Min: {min(sorted_times):.2f}ms")
print(f" Max: {max(sorted_times):.2f}ms")
return results
# ============================================================
# 2. APACHE BENCH WRAPPER
# ============================================================
def run_apache_bench(url: str, requests: int = 1000, concurrency: int = 10):
"""
Sử dụng Apache Bench (ab) nếu có cài.
"""
print(f"\n🔨 APACHE BENCH TEST")
print("="*60)
try:
cmd = [
"ab",
"-n", str(requests),
"-c", str(concurrency),
"-T", "application/json",
url
]
result = subprocess.run(cmd, capture_output=True, text=True)
print(result.stdout)
if result.returncode != 0:
print(f"❌ Error: {result.stderr}")
except FileNotFoundError:
print("❌ Apache Bench (ab) not found!")
print("💡 Install: apt-get install apache2-utils (Linux)")
print(" or brew install ab (Mac)")
return None
# ============================================================
# 3. WRK WRAPPER
# ============================================================
def run_wrk(url: str, duration: str = "10s", threads: int = 4, connections: int = 10):
"""
Sử dụng wrk nếu có cài - tool siêu nhanh viết bằng C.
"""
print(f"\n⚡ WRK BENCHMARK")
print("="*60)
try:
cmd = [
"wrk",
"-t", str(threads),
"-c", str(connections),
"-d", duration,
url
]
result = subprocess.run(cmd, capture_output=True, text=True)
print(result.stdout)
if result.returncode != 0:
print(f"❌ Error: {result.stderr}")
except FileNotFoundError:
print("❌ wrk not found!")
print("💡 Install: https://github.com/wg/wrk")
print(" Mac: brew install wrk")
print(" Linux: build from source")
return None
# ============================================================
# 4. LOCUST HEADLESS
# ============================================================
def run_locust_headless(
locustfile: str,
host: str,
users: int = 50,
spawn_rate: int = 10,
run_time: str = "30s"
):
"""
Chạy Locust ở chế độ headless (không UI).
"""
print(f"\n🦗 LOCUST HEADLESS TEST")
print("="*60)
try:
cmd = [
"locust",
"-f", locustfile,
"--headless",
"--host", host,
"-u", str(users),
"-r", str(spawn_rate),
"--run-time", run_time,
"--html", "locust_report.html"
]
print(f"Running: {' '.join(cmd)}")
result = subprocess.run(cmd, text=True)
if result.returncode == 0:
print("\n✅ Test completed!")
print(f"📊 Report saved to: locust_report.html")
except FileNotFoundError:
print("❌ Locust not found!")
print("💡 Install: pip install locust")
return None
# ============================================================
# 5. K6 WRAPPER (Bonus)
# ============================================================
def generate_k6_script(url: str, output_file: str = "test.js"):
"""
Generate k6 test script.
"""
script = f"""
import http from 'k6/http';
import {{ check, sleep }} from 'k6';
export let options = {{
stages: [
{{ duration: '30s', target: 20 }},
{{ duration: '1m', target: 50 }},
{{ duration: '30s', target: 0 }},
],
thresholds: {{
http_req_duration: ['p(95)<500'], // 95% requests < 500ms
}},
}};
export default function () {{
let res = http.post('{url}', JSON.stringify({{
message: 'test',
user_id: 'test_user',
thread_id: 'test_thread'
}}), {{
headers: {{ 'Content-Type': 'application/json' }},
}});
check(res, {{
'status is 200': (r) => r.status === 200,
'response time < 2000ms': (r) => r.timings.duration < 2000,
}});
sleep(1);
}}
"""
with open(output_file, "w") as f:
f.write(script)
print(f"✅ k6 script generated: {output_file}")
print(f"💡 Run with: k6 run {output_file}")
# ============================================================
# MAIN CLI
# ============================================================
def main():
parser = argparse.ArgumentParser(description="Quick Performance Benchmark Tool")
parser.add_argument("--method", default="python",
choices=["python", "ab", "wrk", "locust", "k6-gen", "all"],
help="Benchmark method to use")
parser.add_argument("--endpoint", default="/api/agent/chat",
help="API endpoint to test")
parser.add_argument("--host", default="http://localhost:8000",
help="Server host")
parser.add_argument("--requests", type=int, default=100,
help="Total requests (for python/ab)")
parser.add_argument("--concurrency", type=int, default=10,
help="Concurrent requests")
args = parser.parse_args()
url = args.host + args.endpoint
print("\n" + "="*60)
print("🔥 QUICK BENCHMARK TOOL")
print("="*60)
print(f"Target: {url}")
print("="*60)
# Prepare payload for chat endpoint
payload = None
if "/chat" in args.endpoint:
payload = {
"message": "test performance",
"user_id": "benchmark_user",
"thread_id": "benchmark_thread"
}
if args.method == "python" or args.method == "all":
asyncio.run(async_benchmark(url, args.requests, args.concurrency, payload))
if args.method == "ab" or args.method == "all":
# ab không support POST JSON dễ, skip hoặc dùng cho GET
if not payload:
run_apache_bench(url, args.requests, args.concurrency)
else:
print("\n⚠️ Apache Bench: Skipped (POST JSON not supported easily)")
if args.method == "wrk" or args.method == "all":
run_wrk(url, "30s", 4, args.concurrency)
if args.method == "locust":
locustfile = str(Path(__file__).parent / "locustfile_production.py")
run_locust_headless(locustfile, args.host, 50, 10, "30s")
if args.method == "k6-gen":
generate_k6_script(url)
print("\n" + "="*60)
print("✅ Benchmark Complete!")
print("="*60 + "\n")
if __name__ == "__main__":
main()
This source diff could not be displayed because it is too large. You can view the blob instead.
# Chạy từ thư mục d:\cnf\chatbot_canifa\backend\hehe
# 1. Khởi động Server (4 Workers)
python api_server_perf.py
# 2. Chạy Locust Test (500 Users)
locust -f locustfile_production.py --host=http://localhost:8000 --headless -u 500 -r 20 --run-time 1m --html report.html
# 3. Lệnh "trảm" process nếu bị treo
Stop-Process -Name python -Force
\ No newline at end of file
# ============================================================
# 🚀 AUTO PERFORMANCE TEST RUNNER
# ============================================================
# Script tự động:
# 1. Start Mock Server (port 8000)
# 2. Đợi server ready
# 3. Run Locust performance test
# 4. Cleanup
# ============================================================
Write-Host "=" -NoNewline -ForegroundColor Cyan
Write-Host "=" * 59 -ForegroundColor Cyan
Write-Host "🚀 CANIFA CHATBOT - PERFORMANCE TEST AUTOMATION" -ForegroundColor Green
Write-Host "=" -NoNewline -ForegroundColor Cyan
Write-Host "=" * 59 -ForegroundColor Cyan
# Activate venv
Write-Host "`n📦 Activating virtual environment..." -ForegroundColor Yellow
& ..\..\.venv\Scripts\Activate.ps1
# Start Mock Server in background
Write-Host "🎭 Starting Mock Server (Port 8000)..." -ForegroundColor Yellow
$serverJob = Start-Job -ScriptBlock {
Set-Location "D:\cnf\chatbot_canifa\backend"
& .venv\Scripts\Activate.ps1
python locust/api_server_perf.py
}
Write-Host "⏳ Waiting for server to be ready..." -ForegroundColor Yellow
Start-Sleep -Seconds 5
# Check if server is running
try {
$response = Invoke-WebRequest -Uri "http://localhost:8000/" -UseBasicParsing -TimeoutSec 3
Write-Host "✅ Mock Server is ready!" -ForegroundColor Green
} catch {
Write-Host "❌ Mock Server failed to start!" -ForegroundColor Red
Stop-Job $serverJob
Remove-Job $serverJob
exit 1
}
# Run Locust Test
Write-Host "`n🦗 Running Locust Performance Test..." -ForegroundColor Yellow
Write-Host " - Users: 50" -ForegroundColor Cyan
Write-Host " - Spawn Rate: 10/s" -ForegroundColor Cyan
Write-Host " - Duration: 30s" -ForegroundColor Cyan
Write-Host ""
locust -f locust/locustfile_production.py `
--headless `
--host http://localhost:8000 `
-u 50 `
-r 10 `
--run-time 30s `
--html locust/locust_report.html `
--csv locust/locust_stats
# Cleanup
Write-Host "`n🧹 Stopping Mock Server..." -ForegroundColor Yellow
Stop-Job $serverJob
Remove-Job $serverJob
Write-Host "`n" -NoNewline
Write-Host "=" -NoNewline -ForegroundColor Cyan
Write-Host "=" * 59 -ForegroundColor Cyan
Write-Host "✅ PERFORMANCE TEST COMPLETE!" -ForegroundColor Green
Write-Host "=" -NoNewline -ForegroundColor Cyan
Write-Host "=" * 59 -ForegroundColor Cyan
Write-Host "`n📊 View Results:" -ForegroundColor Yellow
Write-Host " - HTML Report: locust/locust_report.html" -ForegroundColor Cyan
Write-Host " - CSV Stats: locust/locust_stats_*.csv" -ForegroundColor Cyan
Write-Host ""
import requests
import json
import sys
url = "http://localhost:5000/api/agent/stream/chat"
headers = {"Content-Type": "application/json"}
data = {
"query": "Xin chào từ Python script",
"user_id": "python_test_user"
}
print(f"Sending POST to {url}...")
try:
with requests.post(url, json=data, headers=headers, stream=True) as r:
print(f"Status Code: {r.status_code}")
if r.status_code != 200:
print(f"Error: {r.text}")
sys.exit(1)
print("Response Stream:")
for line in r.iter_lines():
if line:
decoded_line = line.decode('utf-8')
print(decoded_line)
except Exception as e:
print(f"Exception: {e}")
import sys
import time
import random
# 1. Giả lập class ConversationManager y hệt như trong code thật
class MockConversationManager:
def __init__(self):
# Giả vờ kết nối DB tốn thời gian
print(" [DB] 🐢 Connecting to Database... (Init)")
self.connection_id = random.randint(1000, 9999)
print(f" [DB] ✅ Connected! ID kết nối: {self.connection_id}")
def query(self):
return f"Result from conn {self.connection_id}"
# 2. Biến toàn cục lưu instance (Singleton pattern)
_instance = None
def get_conversation_manager():
global _instance
if _instance is None:
print(" [System] ⚠️ Chưa có instance, đang tạo mới...")
_instance = MockConversationManager()
else:
print(" [System] ♻️ Đã có instance, dùng lại hàng cũ.")
return _instance
# 3. Giả lập các Request từ User gọi vào Server
print("-" * 50)
print("DEMO: GIẢ LẬP SERVER NHẬN REQUEST LIÊN TỤC")
print("-" * 50)
# Request 1 (User A)
print("\n👉 REQUEST 1: User A vào Chat")
manager1 = get_conversation_manager()
print(f"User A dùng Manager ID: {id(manager1)} (Conn ID: {manager1.connection_id})")
# Request 2 (User B)
print("\n👉 REQUEST 2: User B vào Chat")
manager2 = get_conversation_manager()
print(f"User B dùng Manager ID: {id(manager2)} (Conn ID: {manager2.connection_id})")
# Request 3 (User C)
print("\n👉 REQUEST 3: User C vào Chat")
manager3 = get_conversation_manager()
print(f"User C dùng Manager ID: {id(manager3)} (Conn ID: {manager3.connection_id})")
print("\n" + "-" * 50)
print("KẾT QUẢ KIỂM TRA:")
if manager1 is manager2 is manager3:
print("✅ THÀNH CÔNG: Cả 3 request đều dùng chung 1 object duy nhất!")
print(" => Không bị tạo lại kết nối DB thừa thãi.")
else:
print("❌ THẤT BẠI: Các object khác nhau.")
"""
Test Script: Kiểm tra Vector Index - SIÊU GỌN
Chỉ đo thời gian từ lúc có full query đến khi nhận kết quả
"""
import sys
import os
import asyncio
import time
# Add parent dir to path
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from common.embedding_service import create_embedding_async
from common.starrocks_connection import StarRocksConnection
async def main():
print("="*60)
print("🔍 TEST VECTOR INDEX - PERFORMANCE CHECK")
print("="*60)
# 1. Chuẩn bị dữ liệu (Embedding)
test_query = "áo sơ mi"
print(f" Creating embedding for: '{test_query}'...")
query_vector = await create_embedding_async(test_query)
# 2. Tạo full query string (COPY Y HỆT câu bro bảo "ngon" nhưng thêm Hint chuẩn)
v_str = "[" + ",".join(str(v) for v in query_vector) + "]"
sql = f"""
SELECT /*+ SET_VAR(ann_params='{{"ef_search":64}}') */
internal_ref_code,
approx_cosine_similarity(vector, {v_str}) as score
FROM shared_source.magento_product_dimension_with_text_embedding__tmp
ORDER BY score DESC
LIMIT 50
"""
# 3. Kết nối DB
db = StarRocksConnection()
conn = db.connect()
conn.ping(reconnect=True)
try:
with conn.cursor() as cursor:
# --- KIỂM TRA PLAN ---
print("🔬 Đang kiểm tra Query Plan (XEM FULL)...")
cursor.execute(f"EXPLAIN {sql}")
plan = cursor.fetchall()
is_vector_on = False
for row in plan:
line = str(row)
print(f" > {line}") # In full để mình soi
if "VECTORINDEX: ON" in line:
is_vector_on = True
print(f"\n📊 Vector Index Status: {'ON ✅' if is_vector_on else 'OFF ❌'}")
# Baseline: Độ trễ mạng
t_base_0 = time.perf_counter()
cursor.execute("SELECT 1")
cursor.fetchone()
baseline_ms = (time.perf_counter() - t_base_0) * 1000
print(f"📡 Network Baseline: {baseline_ms:.2f} ms")
print(f"\n🚀 ĐANG GỬI TRUY VẤN (10 vòng lặp)...")
durations = []
for i in range(1, 11):
start_time = time.perf_counter()
cursor.execute(sql)
rows = cursor.fetchall()
duration_ms = (time.perf_counter() - start_time) * 1000
durations.append(duration_ms)
status_icon = "⚡" if duration_ms < 80 else "🐢"
print(f"🔄 Lần {i:02d}: {duration_ms:7.2f} ms {status_icon}")
print("-" * 30)
print(f"📈 AVG: {sum(durations)/10:.2f}ms | MIN: {min(durations):.2f}ms | MAX: {max(durations):.2f}ms")
print("-" * 30 + "\n")
except Exception as e:
print(f"\n❌ Lỗi: {e}\n")
finally:
pass
print("="*60 + "\n")
if __name__ == "__main__":
asyncio.run(main())
"""
Test Script: Kiểm tra Vector Index - SIÊU GỌN
Chỉ đo thời gian từ lúc có full query đến khi nhận kết quả
"""
import sys
import os
import asyncio
import time
# Add parent dir to path
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from common.embedding_service import create_embedding_async
from common.starrocks_connection import StarRocksConnection
async def main():
print("="*60)
print("🔍 TEST VECTOR INDEX - PERFORMANCE CHECK")
print("="*60)
# 1. Chuẩn bị dữ liệu (Embedding)
test_query = "áo sơ mi"
print(f" Creating embedding for: '{test_query}'...")
query_vector = await create_embedding_async(test_query)
# 2. Tạo full query string (COPY Y HỆT câu bro bảo "ngon" nhưng thêm Hint chuẩn)
v_str = "[" + ",".join(str(v) for v in query_vector) + "]"
sql = f"""
SELECT /*+ SET_VAR(ann_params='{{"ef_search":64}}') */
internal_ref_code,
approx_cosine_similarity(vector, {v_str}) as score
FROM shared_source.magento_product_dimension_with_text_embedding__tmp
ORDER BY score DESC
LIMIT 50
"""
# 3. Kết nối DB
db = StarRocksConnection()
conn = db.connect()
conn.ping(reconnect=True)
try:
with conn.cursor() as cursor:
# --- KIỂM TRA PLAN ---
print("🔬 Đang kiểm tra Query Plan (XEM FULL)...")
cursor.execute(f"EXPLAIN {sql}")
plan = cursor.fetchall()
is_vector_on = False
for row in plan:
line = str(row)
print(f" > {line}") # In full để mình soi
if "VECTORINDEX: ON" in line:
is_vector_on = True
print(f"\n📊 Vector Index Status: {'ON ✅' if is_vector_on else 'OFF ❌'}")
# Baseline: Độ trễ mạng
t_base_0 = time.perf_counter()
cursor.execute("SELECT 1")
cursor.fetchone()
baseline_ms = (time.perf_counter() - t_base_0) * 1000
print(f"📡 Network Baseline: {baseline_ms:.2f} ms")
print(f"\n🚀 ĐANG GỬI TRUY VẤN (10 vòng lặp)...")
durations = []
for i in range(1, 11):
start_time = time.perf_counter()
cursor.execute(sql)
rows = cursor.fetchall()
duration_ms = (time.perf_counter() - start_time) * 1000
durations.append(duration_ms)
status_icon = "⚡" if duration_ms < 80 else "🐢"
print(f"🔄 Lần {i:02d}: {duration_ms:7.2f} ms {status_icon}")
print("-" * 30)
print(f"📈 AVG: {sum(durations)/10:.2f}ms | MIN: {min(durations):.2f}ms | MAX: {max(durations):.2f}ms")
print("-" * 30 + "\n")
except Exception as e:
print(f"\n❌ Lỗi: {e}\n")
finally:
pass
print("="*60 + "\n")
if __name__ == "__main__":
asyncio.run(main())
SELECT
internal_ref_code,
approx_cosine_similarity(vector, [0.020767446607351303,-0.01121582742780447,-0.08832024037837982,-0.028268102556467056,0.003533512819558382,-0.024752169847488403,-0.020591648295521736,-0.0001370664540445432,0.03169027715921402,-0.02442401647567749,0.00906524807214737,-0.028127465397119522,-0.02951039932668209,0.06305240094661713,-0.0031262505799531937,-0.016677243635058403,-0.029158806428313255,-0.00627594068646431,0.01993534155189991,0.0018736994825303555,0.02501000463962555,0.035534366965293884,0.0066275340504944324,0.014262969605624676,0.04636343941092491,-0.010026269592344761,-0.002494847634807229,0.0042366995476186275,0.020474450662732124,-0.03675322234630585,-0.008643335662782192,-0.040128517895936966,0.043996043503284454,0.0010437926976010203,-0.027635235339403152,0.04992625117301941,0.018353171646595,-0.03260442242026329,-0.019114958122372627,0.0019908971153199673,0.014368447475135326,0.013372265733778477,0.03975348547101021,-0.0017740813782438636,0.04842612147331238,0.01095799170434475,-0.04455859586596489,0.019185276702046394,0.029885433614253998,0.03419831022620201,0.013231628574430943,0.0009485695045441389,0.02981511317193508,0.11541637033224106,0.0526452399790287,0.0007090465514920652,-0.01271595899015665,0.04903554916381836,0.052176449447870255,0.028150904923677444,-0.024377137422561646,-0.05241084471344948,0.01077047549188137,0.04875427484512329,-0.006352119613438845,-0.03342480584979057,0.0025182871613651514,0.005206511355936527,-0.04176928848028183,0.007260402198880911,-0.03417487069964409,0.012669079937040806,0.0004127559077460319,-0.011790095828473568,-0.028057146817445755,0.0025505165103822947,0.0011690477840602398,-0.030237026512622833,0.021083880215883255,-0.010975571349263191,-0.029651036486029625,0.03419831022620201,-0.003173129865899682,-0.01626705192029476,-0.018318012356758118,-0.035534366965293884,-0.051988933235406876,0.06319303810596466,0.004098992329090834,0.020462730899453163,-0.04474611207842827,0.010137607343494892,-0.027096126228570938,-0.03192467242479324,0.05981774628162384,0.0067037129774689674,-0.04115985706448555,-0.0386987067759037,-0.013501184061169624,0.017720304429531097,0.014122331514954567,-0.022548852488398552,0.04805108904838562,0.015188831835985184,0.02013457752764225,-0.00887187197804451,0.03126836568117142,-0.03330760821700096,-0.004008163698017597,0.015903737396001816,0.08049143105745316,-0.03759704530239105,0.009123846888542175,0.005540524609386921,0.012282326817512512,0.07224071025848389,0.030940212309360504,-0.07050618529319763,0.044417958706617355,-0.04657439514994621,0.01675928197801113,0.01855240762233734,0.005487785674631596,-0.03581564128398895,-0.007647154852747917,0.02272464893758297,-0.012926914729177952,-0.021880824118852615,0.014989595860242844,-0.00406383303925395,-0.014825518243014812,-0.03061205893754959,0.04165209084749222,-0.03546404838562012,-0.008280023001134396,0.002231152728199959,-0.010916972532868385,0.016888199374079704,-0.044300757348537445,0.007717473432421684,0.058458250015974045,0.006768171675503254,-0.03358888253569603,0.021083880215883255,0.007260402198880911,-0.046902548521757126,0.01002040971070528,-0.01723979227244854,0.007488938048481941,0.031549639999866486,0.06478693336248398,1.101590441976441e-05,-0.008848432451486588,0.02929944358766079,-0.022091779857873917,-0.05822385475039482,-0.015130233019590378,0.01122168730944395,0.004963325802236795,0.0424724742770195,0.020896364003419876,-0.012305766344070435,-0.019079796969890594,-0.025689752772450447,-0.05184829607605934,0.031174607574939728,-0.03101053088903427,-0.007553396746516228,-0.019103236496448517,-0.0032200089190155268,-0.006820910610258579,-0.02512720227241516,-0.026135103777050972,0.02053304947912693,-0.028783774003386497,-0.05067631974816322,0.04596496745944023,-0.012422963976860046,-0.04476955160498619,0.052973393350839615,0.033846717327833176,0.02770555391907692,0.04333973675966263,-0.025244401767849922,-0.04263655096292496,0.0014063733397051692,-0.012212008237838745,-0.015692781656980515,0.01824769377708435,0.01965406723320484,-0.025947587564587593,0.017685145139694214,-0.011286146007478237,-0.05873952433466911,0.0016803231555968523,-0.015212271362543106,-0.024564653635025024,-0.006533775944262743,0.009903212077915668,0.013442585244774818,0.009522318840026855,-0.031432442367076874,-0.05212957039475441,-0.02442401647567749,-0.026439817622303963,0.004424216225743294,-0.039636287838220596,-0.024189621210098267,0.03837055340409279,-0.03368264064192772,0.06586515158414841,0.0071373446844518185,0.0436210110783577,0.03541716933250427,-0.025549115613102913,0.02951039932668209,0.022783247753977776,0.01573966071009636,0.028877532109618187,0.008256583474576473,-0.05672372505068779,0.033752959221601486,0.06703712791204453,0.024845927953720093,-0.056348688900470734,0.0008826457196846604,-0.01585685834288597,-0.08719514310359955,-0.04462891444563866,-0.030776135623455048,0.022279297932982445,0.026346059516072273,0.005566894542425871,-0.02482248842716217,-0.013407425954937935,0.05245772376656532,-0.02252541296184063,-0.0018502598395571113,-0.029838554561138153,-0.06403686106204987,-0.05034816265106201,0.0232285987585783,0.0562080517411232,0.02382630854845047,0.010330984368920326,0.02510376274585724,-0.00832690205425024,-0.0020436362829059362,-0.005016064736992121,0.024588093161582947,0.032463785260915756,0.027939949184656143,0.006715432740747929,-0.023861467838287354,-0.0031526200473308563,0.03079957515001297,0.0144973648712039,-0.000141369819175452,0.002907969756051898,-0.004535553976893425,-0.053676582872867584,-0.00682677049189806,0.009510599076747894,-0.04312878102064133,0.00436268700286746,0.015892017632722855,-0.034245189279317856,0.021294835954904556,0.05175453796982765,0.012762838043272495,-0.04903554916381836,-0.01855240762233734,0.005520015023648739,0.04176928848028183,-0.00852613802999258,0.01754450611770153,-0.03490149602293968,0.05344218388199806,0.0022604521363973618,0.03119804710149765,-0.020954962819814682,-0.047605738043785095,0.05095759406685829,-0.0013177425134927034,0.00020509610476437956,-0.0064986166544258595,0.033143531531095505,-0.01795469969511032,0.030658937990665436,-0.020075978711247444,-0.03440926596522331,0.0420505627989769,0.011356464587152004,-0.01290347520262003,0.029533838853240013,0.01236436516046524,-0.0019908971153199673,-0.039120618253946304,-0.046996306627988815,-0.0076998937875032425,0.050582561641931534,0.06530260294675827,-0.01160843949764967,0.03900342062115669,-0.005522944964468479,-0.029651036486029625,0.0110634695738554,0.01606781594455242,0.03218251094222069,-0.0010350028751417994,-0.02013457752764225,0.022044900804758072,-0.0021139548625797033,-0.018868841230869293,0.03237002715468407,-0.06675585359334946,-0.0005453358753584325,-0.021400313824415207,0.06999050825834274,-0.01245812326669693,0.06328679621219635,-0.015927176922559738,0.021095599979162216,-0.021072160452604294,-0.008953910320997238,0.0055170850828289986,0.003990584053099155,-0.006258361041545868,0.0294166412204504,0.010682577267289162,-0.04038635268807411,0.026557015255093575,0.042824067175388336,-0.010600538924336433,0.007524097338318825,-0.02831498347222805,0.01573966071009636,0.03689385950565338,0.022947324439883232,0.06947483867406845,-0.040902022272348404,-0.01884540170431137,-0.0566299669444561,0.028760334476828575,0.003140900284051895,-0.09713351726531982,0.007758492603898048,-0.0322997085750103,0.013231628574430943,0.020158017054200172,0.024939686059951782,0.021294835954904556,-0.004377336706966162,-0.056067414581775665,0.039730045944452286,-0.032932575792074203,-0.01794297993183136,0.03269818052649498,-0.02333407662808895,0.048238605260849,-0.020790886133909225,-2.1058975107735023e-05,0.004090202506631613,-0.027752432972192764,0.01743902824819088,-0.001031340449117124,-0.01331366691738367,-0.0049838353879749775,0.048894912004470825,-0.00448574498295784,-0.017181193456053734,0.037925198674201965,-0.03640162944793701,-0.028057146817445755,-0.033448245376348495,0.07303765416145325,-0.009633657522499561,0.02533815987408161,-0.014977876096963882,-0.03290913626551628,-0.037339210510253906,0.02533815987408161,-0.009592638351023197,-0.0029343392234295607,0.019196996465325356,-0.011198247782886028,0.04439451918005943,-0.0221269391477108,0.012692519463598728,0.016981957480311394,-0.0018517248099669814,-0.04258967190980911,0.0540047362446785,0.015786539763212204,0.006170462816953659,0.024353697896003723,0.017790623009204865,0.020990122109651566,-0.0601927787065506,0.013641821220517159,0.008139385841786861,-0.01675928197801113,-0.03956596925854683,-0.014790358953177929,-0.016395969316363335,0.05475480109453201,-0.01616157405078411,0.0002561870205681771,0.04776981472969055,0.010231365449726582,0.029768234118819237,-0.045003946870565414,0.029065048322081566,0.03176059573888779,0.01742730848491192,-0.033448245376348495,-0.02312312088906765,0.028080586344003677,-0.0031584801618009806,-0.008778112940490246,-0.01160843949764967,-0.012001052498817444,-0.037620484828948975,0.03558124601840973,0.014122331514954567,0.010389583185315132,0.010541940107941628,-0.006533775944262743,0.036518827080726624,-0.06230233609676361,-0.044605474919080734,0.003395805601030588,-0.02981511317193508,0.027728993445634842,-0.030260466039180756,-0.0440429225564003,-0.04019883647561073,0.0041663809679448605,-0.017462467774748802,-0.05025440454483032,-0.015411507338285446,-0.006053265184164047,0.005895047914236784,-0.015974055975675583,-0.01555214449763298,-0.028361862525343895,0.02552567608654499,-0.021083880215883255,-0.003111600875854492,-0.04235527664422989,0.020368972793221474,-0.048004209995269775,-0.016970237717032433,-0.0034807738848030567,0.018318012356758118,0.0144973648712039,0.04884803295135498,0.01806017756462097,0.009457860141992569,-0.033448245376348495,0.05231708660721779,0.0010635697981342673,-0.004840268287807703,0.029088487848639488,0.02859625779092312,-0.0384877510368824,0.06830286234617233,-0.02651013620197773,-0.0021930632647126913,0.036729782819747925,-0.003272747853770852,-0.036940738558769226,0.00897734984755516,-0.012129969894886017,-0.021365154534578323,-0.05850512906908989,0.061739787459373474,0.021669868379831314,0.04066762700676918,0.018400050699710846,-0.0015294309705495834,-0.03668290376663208,-0.05386409908533096,-0.009153146296739578,-0.012645640410482883,0.056676845997571945,-0.0008797157788649201,0.0034016654826700687,-0.027564916759729385,0.017978139221668243,-0.023884907364845276,-0.006686132866889238,0.029065048322081566,-0.009692256338894367,0.04556649550795555,0.01693507842719555,-0.008912891149520874,-0.021505791693925858,-0.010395443066954613,-0.053582824766635895,0.013196469284594059,-0.010629838332533836,-0.04814484715461731,0.03750328719615936,0.013419145718216896,0.0222324188798666,0.01974782533943653,0.02211521938443184,0.01420437078922987,0.02421306073665619,-0.03487805649638176,0.019759545102715492,-0.02053304947912693,-0.025549115613102913,-0.046011846512556076,-0.027682114392518997,-0.017720304429531097,-0.01056537963449955,-0.00822728406637907,-0.03965972736477852,-0.021494071930646896,0.005836449097841978,-0.010811494663357735,-0.03609691560268402,-0.015892017632722855,-0.022865286096930504,0.05311403051018715,-0.027096126228570938,-0.02252541296184063,-0.020204896107316017,-0.022947324439883232,-0.012505002319812775,-0.014286409132182598,0.02442401647567749,0.01021964568644762,-0.01420437078922987,0.01225888729095459,-0.002012871904298663,0.012739398516714573,0.030940212309360504,0.03511245176196098,0.006639253813773394,-0.019372792914509773,-0.05105135217308998,-0.019595468416810036,0.007067025639116764,-0.029674476012587547,-0.017204632982611656,-0.005171352066099644,0.019900182262063026,-0.05569238215684891,-0.015434946864843369,-0.007277981843799353,0.01504819467663765,-0.023662229999899864,-0.0162553321570158,-0.000909015245269984,-0.01355978287756443,0.013337106443941593,0.008250723592936993,0.028455620631575584,0.025853829458355904,0.015399787575006485,0.015013035386800766,-0.02383802831172943,0.004336317535489798,0.011438502930104733,0.014591122977435589,0.019068077206611633,-0.03110428899526596,0.006475177127867937,0.025877268984913826,-0.002191598294302821,-0.04329285770654678,-0.011749076656997204,0.011643598787486553,-0.015505265444517136,0.01011416781693697,-0.05236396566033363,-0.005355938337743282,-0.003120390698313713,0.03949565067887306,-0.026767972856760025,0.016102975234389305,-0.011913154274225235,0.0172515120357275,0.036448508501052856,-0.019806424155831337,-0.024353697896003723,0.008092506788671017,-0.03935501351952553,-0.012411244213581085,-0.002704338636249304,-0.012540161609649658,0.02580695040524006,-0.06113035976886749,-0.011297865770757198,-0.025197520852088928,-0.05461416393518448,-0.032862257212400436,0.000848951400257647,0.006305240094661713,0.027869630604982376,-0.020286934450268745,0.03262786194682121,0.0011836974881589413,-0.04329285770654678,0.009903212077915668,-0.031057409942150116,-0.005707531701773405,0.0318777933716774,0.037315770983695984,0.014157491736114025,-0.03267474099993706,0.02629918046295643,-0.016864759847521782,-0.027541477233171463,-0.013184749521315098,0.019900182262063026,0.016372529789805412,-0.016524886712431908,0.011426783166825771,0.034503024071455,-0.01095799170434475,0.0069322483614087105,-0.032838817685842514,-0.034221749752759933,0.015141952782869339,-0.0029519188683480024,-0.008180405013263226,0.033940475434064865,0.0023263758048415184,0.014673161320388317,0.00011628216452663764,-0.0019322982989251614,0.027869630604982376,-0.026346059516072273,-0.009035948663949966,0.0036389909218996763,-0.00044388658716343343,-0.009188305586576462,0.009909071959555149,0.02981511317193508,-0.03140900284051895,0.05194205418229103,0.018693044781684875,0.013512903824448586,0.007389319594949484,0.008397220633924007,0.006809190846979618,0.0486605167388916,0.010834934189915657,-0.014473925344645977,-0.005520015023648739,-0.016009215265512466,-0.0067740315571427345,0.0030149128288030624,-0.049785614013671875,-0.001363156596198678,-0.007910849526524544,0.005086383316665888,0.03108084946870804,0.01953686960041523,-0.04805108904838562,-0.0005713391583412886,0.02043929137289524,-4.8481422709301114e-05,0.04746510088443756,-0.007242822553962469,0.0047494396567344666,-0.010137607343494892,0.014778639189898968,0.001453252392821014,-0.02761179581284523,0.06000526249408722,0.010793915018439293,-0.004960395861417055,0.016419408842921257,-0.008655055426061153,0.0037532588467001915,0.030166707932949066,-0.021845664829015732,-0.010729456320405006,-0.02861969731748104,0.04125361517071724,-0.011256846599280834,0.018224254250526428,0.0015470106154680252,-0.022255858406424522,-0.012188568711280823,-0.01544666662812233,0.02103700116276741,0.012985513545572758,-0.008397220633924007,9.375822264701128e-05,0.007623715326189995,0.016102975234389305,0.018505528569221497,0.013137870468199253,-0.015376348048448563,0.013149590231478214,-0.01685304008424282,0.029158806428313255,-0.009246904402971268,-0.022865286096930504,-0.020990122109651566,-0.01663036458194256,0.02033381350338459,-0.011104489676654339,-0.007494797930121422,-0.003058861941099167,0.007225242909044027,0.002918224548920989,0.03070581704378128,-0.00796944834291935,-0.043714769184589386,0.008250723592936993,-0.01684132032096386,-0.00803390797227621,-0.01311443094164133,-0.008567157201468945,0.03340136632323265,-0.05062944069504738,0.011344744823873043,-0.048097968101501465,-0.02601790614426136,0.02014629729092121,-0.010395443066954613,-0.008350341580808163,-0.014848957769572735,0.027143005281686783,0.00972155574709177,-0.0054672760888934135,0.01290347520262003,-0.007705753669142723,0.024048984050750732,-0.023439554497599602,0.025595994666218758,-0.035253092646598816,-0.032440345734357834,-0.010588819161057472,-0.0002464815624989569,-0.0012913730461150408,0.01633737049996853,-0.037222012877464294,-0.0004354629782028496,-0.008561297319829464,0.004611732438206673,-0.03297945484519005,0.023298917338252068,0.0050336443819105625,0.007260402198880911,0.017989858984947205,0.02969791553914547,-0.02381458878517151,-0.029440080747008324,0.00021223785006441176,-0.027260202914476395,0.022865286096930504,0.011491241864860058,0.013969974592328072,0.038347113877534866,-0.0034514744766056538,0.034127991646528244,0.005241670645773411,-0.021283116191625595,0.02852593921124935,-0.026767972856760025,0.023252038285136223,0.01106932945549488,0.013395706191658974,-0.042214639484882355,0.019888462498784065,-0.018095336854457855,-0.03776112198829651,-0.02451777458190918,-0.014884117059409618,-0.01813049614429474,0.0010181557154282928,0.01862272620201111,-0.013782458379864693,-0.021798785775899887,-0.02093152329325676,0.0004057972982991487,-0.016583485528826714,0.010336844250559807,-0.025056883692741394,0.004101922269910574,0.02711956575512886,-0.018165655434131622,0.02901816926896572,-0.03101053088903427,-0.015927176922559738,-0.003970074467360973,0.00542918685823679,-0.04249591380357742,0.039730045944452286,0.04448827728629112,0.03745640814304352,-0.012891755439341068,-0.023861467838287354,0.02711956575512886,0.0034280349500477314,0.003917335532605648,0.01715775392949581,-0.010829074308276176,-0.017298391088843346,-0.009211745113134384,0.028174344450235367,-0.026838291436433792,-0.005889188032597303,0.014016853645443916,-0.015013035386800766,-0.012762838043272495,0.019689226523041725,-0.002225292846560478,0.0072076632641255856,0.029861994087696075,0.0436210110783577,0.001957202795892954,0.036518827080726624,-0.006229061633348465,0.027049247175455093,-0.003908545710146427,0.009340662509202957,-0.003958354704082012,-0.0077291931957006454,0.016677243635058403,0.021599549800157547,0.009211745113134384,0.013149590231478214,0.005405747331678867,0.004702560603618622,-0.012411244213581085,0.010172766633331776,0.012622200883924961,0.013442585244774818,0.023369235917925835,-0.04556649550795555,0.007272121962159872,0.0316668376326561,0.027541477233171463,-0.052785877138376236,0.014098891988396645,-0.011485381983220577,0.014720040373504162,-0.01933763362467289,-0.006551355589181185,-0.006574795115739107,0.02233789674937725,0.01912667788565159,-0.013477744534611702,-0.015716221183538437,0.001050385064445436,-0.0292056854814291,0.0085202781483531,0.01832973212003708,0.00448574498295784,0.030377663671970367,-0.011825255118310452,0.0030823014676570892,0.048285484313964844,-0.039612848311662674,0.0028537658508867025,0.0243302583694458,0.005625493358820677,-0.03656570613384247,0.009217604994773865,-0.005821799393743277,-0.016478007659316063,-2.760556890279986e-05,0.020486170426011086,-0.039214376360177994,-0.05583301931619644,0.025478797033429146,-0.002304401248693466,-0.002839116146788001,0.0031555502209812403,-0.05873952433466911,-0.02702580764889717,0.02311140112578869,-0.0464571975171566,-0.005678232293576002,0.020193176344037056,0.03958940878510475,0.002820071531459689,0.00897734984755516,0.003032492473721504,-0.014579403214156628,0.00672715250402689,-0.007125624921172857,0.00034042290644720197,0.006305240094661713,-0.03916749730706215,-0.002223827876150608,0.012797997333109379,0.009317222982645035,0.003445614594966173,0.006516196299344301,0.010131747461855412,-0.010190346278250217,-0.005813009571284056,0.021365154534578323,0.015399787575006485,0.0017008327413350344,0.022900445386767387,0.02263089083135128,0.006422438193112612,-0.03049486130475998,0.002168158767744899,-0.006609954405575991,-0.008074927143752575,0.027752432972192764,0.025080323219299316,0.0376439243555069,-0.022091779857873917,-0.015036474913358688,-0.022888725623488426,0.006117723882198334,-0.009440280497074127,-0.010125887580215931,-0.038933102041482925,-0.004626382142305374,-0.014884117059409618,-0.015798259526491165,-0.0031467601656913757,-0.03532341122627258,0.01196003332734108,0.03419831022620201,-0.0091765858232975,-0.04134737700223923,-0.023486433550715446,-0.022162098437547684,-0.009791874326765537,0.04258967190980911,-0.010541940107941628,0.042824067175388336,-0.005868678446859121,-0.007565116509795189,-0.010354423895478249,0.012622200883924961,-0.0100145498290658,-0.01852896809577942,0.04115985706448555,-0.011004870757460594,-0.020017379894852638,-0.012141689658164978,0.0013272648211568594,0.015528704971075058,-0.010811494663357735,-0.014016853645443916,0.016325650736689568,-0.0006350654293783009,-0.01722807250916958,-0.01786094158887863,-0.0031057409942150116,0.005077593494206667,-0.0209198035299778,0.013899656012654305,-0.02890097163617611,-0.0008855757187120616,-0.042120881378650665,-0.01656004600226879,-0.015505265444517136,-0.002755612600594759,-0.005405747331678867,0.014063732698559761,-0.020568208768963814,0.02761179581284523,0.04273030906915665,-0.03447958454489708,-0.008192124776542187,0.006322820205241442,0.02283012680709362,0.004558993503451347,-0.023298917338252068,-0.026486696675419807,0.03201843053102493,-0.030283905565738678,-0.029838554561138153,-0.008930470794439316,0.04851987957954407,-0.01804845780134201,-0.02491624653339386,0.015516985207796097,-0.011086910031735897,0.053770340979099274,0.0006892693927511573,-0.0029211544897407293,-0.001788731082342565,0.043011583387851715,0.03190123289823532,0.0027966320049017668,-0.026088224723935127,0.017919540405273438,-0.0019029988907277584,-0.012727678753435612,-0.016431128606200218,0.011936593800783157,0.008578876964747906,-0.01925559528172016,-0.007371739950031042,-0.013020672835409641,-0.009580918587744236,-0.019489990547299385,0.009434420615434647,-0.013583222404122353,-0.004843198228627443,0.017802342772483826,0.012047931551933289,0.0028024918865412474,-0.006012246012687683,-0.024588093161582947,0.005681162234395742,0.03417487069964409,-0.014098891988396645,-0.004078482743352652,0.022900445386767387,-0.04387884587049484,0.027541477233171463,0.036940738558769226,-0.004301158245652914,0.022501973435282707,-0.01011416781693697,0.03696417808532715,-0.008455819450318813,0.02700236812233925,0.028268102556467056,0.042214639484882355,-0.009657097049057484,-0.04348037391901016,0.008801553398370743,0.019279034808278084,0.029768234118819237,-0.007459638640284538,0.03588595986366272,-0.030283905565738678,-0.005769060458987951,0.0015294309705495834,-0.03897998109459877,0.006445877719670534,0.023978665471076965,-0.0028816002886742353,0.031737156212329865,0.007459638640284538,-0.01803673803806305,-0.03506557270884514,0.009955951012670994,-0.03778456151485443,0.011034170165657997,0.0014012459432706237,-0.004790459293872118,0.004890077281743288,-0.016220172867178917,-0.004075552802532911,-0.0016129344003275037,0.03267474099993706,-0.007295561488717794,-0.0046996306627988815,-0.0081276660785079,-0.03021358698606491,0.01693507842719555,-0.022044900804758072,-0.00149573665112257,0.0325106643140316,-0.03841743245720863,0.028854092583060265,0.05325466766953468,0.0028215365018695593,0.01963062770664692,0.009282063692808151,0.023896627128124237,0.030049510300159454,0.03377639874815941,-0.00357453222386539,0.00842066016048193,-0.03326072916388512,-0.02062680758535862,0.011854555457830429,-0.007817091420292854,-0.005054153967648745,-0.011731497012078762,0.011567420326173306,-0.009768434800207615,0.0018839542753994465,-0.0030031930655241013,0.054661042988300323,0.008022187277674675,-0.011831114999949932,-0.01112206932157278,-0.01813049614429474,0.014673161320388317,0.0076998937875032425,0.02123623713850975,-0.009545758366584778,-0.026885170489549637,0.004444725811481476,0.004851988051086664,0.00906524807214737,0.022560572251677513,0.012973793782293797,0.005892117973417044,0.045214902609586716,-0.03958940878510475,-0.02622886188328266,-0.019419671967625618,-0.028783774003386497,0.00822728406637907,0.014016853645443916,-0.009381681680679321,0.020908083766698837,0.01095799170434475,-0.015458386391401291,-0.006328680086880922,0.015411507338285446,0.013255068100988865,0.00013624240818899125,-0.012821436859667301,-0.0044212862849235535,0.020872924476861954,-0.018470369279384613,-0.02421306073665619,-0.010049709118902683,0.017497627064585686,-0.018071897327899933,-0.019712666049599648,0.04662127420306206,0.01057123951613903,0.002532936865463853,-0.037432968616485596,0.005912627559155226,0.014989595860242844,0.02700236812233925,-0.016583485528826714,0.017392149195075035,0.0025109623093158007,0.011930733919143677,-0.03851119056344032,0.008180405013263226,0.018904000520706177,0.049598097801208496,0.013594942167401314,-0.008156965486705303,0.026346059516072273,-0.005326638929545879,0.020193176344037056,0.004140011500567198,0.09408637136220932,-0.027682114392518997,-0.000858473707921803,-0.025267841294407845,-0.009686396457254887,-0.013911375775933266,-0.025549115613102913,-0.00010117464262293652,-0.019267315044999123,0.030283905565738678,0.01375901885330677,-0.018107056617736816,-0.0221269391477108,0.0008716584416106343,-0.028244663029909134,-0.012926914729177952,-0.010582959279417992,-0.03140900284051895,0.006715432740747929,-0.009510599076747894,-0.02241993509232998,-0.038839343935251236,-0.0069029489532113075,-0.012118250131607056,0.030541740357875824,-0.03595627844333649,-0.02451777458190918,0.039026860147714615,-0.009487159550189972,0.0036887999158352613,0.04992625117301941,0.010541940107941628,0.0009273273753933609,0.0006574062863364816,-0.021576110273599625,-0.03696417808532715,-0.01875164359807968,-0.03445614501833916,0.0035921118687838316,-0.033049773424863815,0.008895311504602432,0.033448245376348495,-0.02374427020549774,-0.003211219096556306,0.008444099687039852,0.015997495502233505,0.04533210024237633,-0.02470529079437256,-0.004717210307717323,0.005382307805120945,-0.03684698045253754,0.018997758626937866,-0.026369499042630196,0.00715492432937026,0.022947324439883232,-0.011678758077323437,-0.02700236812233925,0.014520804397761822,0.02281840704381466,0.007389319594949484,0.02360363118350506,-0.06624018400907516,0.024752169847488403,-0.021904263645410538,0.029088487848639488,0.017989858984947205,0.004435935989022255,-0.011737356893718243,0.012575320899486542,-0.016489727422595024,-0.005253390409052372,0.0007808301597833633,0.0005336161120794713,0.011168948374688625,0.016899919137358665,-0.004931096453219652,-0.01614985428750515,-0.07275637984275818,-0.03119804710149765,-0.018915720283985138,-0.0025944658555090427,0.016372529789805412,0.0037649786099791527,0.039026860147714615,0.035042133182287216,-0.0017579665873199701,-0.014286409132182598,0.020697128027677536,0.03855806961655617,-0.04104265943169594,0.007178363855928183,-0.013430865481495857,0.006574795115739107,-0.015329468995332718,0.013688700273633003,-0.015306029468774796,-0.025572555139660835,0.010776335373520851,-0.00501020485535264,-0.016923358663916588,0.030846454203128815,-0.020462730899453163,0.024681851267814636,-0.0013704814482480288,-0.007225242909044027,-0.030963651835918427,-0.01225888729095459,-0.02969791553914547,-0.0003125884395558387,-0.01535290852189064,0.007430338766425848,-0.009457860141992569,0.019689226523041725,0.009504739195108414,-0.004192750435322523,-0.03640162944793701,0.005587404128164053,0.009522318840026855,-0.043996043503284454,0.006316960323601961,0.019173556938767433,0.01012002769857645,0.0044212862849235535,-0.024400576949119568,-0.027963388711214066,0.010723596438765526,-0.003029562532901764,0.05592677742242813,-0.009381681680679321,-0.008918751031160355,-0.010301684029400349,0.008531997911632061,0.032346587628126144,-0.03546404838562012,0.00046915735583752394,0.024799048900604248,0.016888199374079704,-0.016876479610800743,0.02503344416618347,0.00416052108630538,0.006797471083700657,0.005361798219382763,0.023673949763178825,-9.2796835815534e-05,-0.00240401946939528,-0.007899129763245583,0.00627594068646431,0.03658914566040039,-0.02104872092604637,-0.012434683740139008,0.030166707932949066,0.00734830042347312,-0.02103700116276741,-0.016700683161616325,-0.02482248842716217,0.013829337432980537,-0.0023380955681204796,-0.0022296877577900887,-0.004453515633940697,0.008467539213597775,0.02660389430820942,-0.01722807250916958,0.01489583682268858,-0.023884907364845276,-0.004980905447155237,0.005637213122099638,-0.018318012356758118,0.002049496164545417,-0.019396232441067696,-0.018575847148895264,-0.02892441116273403,0.00777021236717701,-0.008203844539821148,-0.019572028890252113,-0.004714280366897583,-0.0015294309705495834,-0.0005881863180547953,0.015997495502233505,-0.013899656012654305,0.014848957769572735,0.02583038993179798,0.022490253672003746,0.004822688642889261,0.024986565113067627,-0.026650773361325264,-0.027846191078424454,0.00523581076413393,-0.011491241864860058,0.03855806961655617,-0.02503344416618347,-0.0062642209231853485,-0.01663036458194256,-0.018704764544963837,-7.599543096148409e-06,0.009938371367752552,0.018282853066921234,0.01245812326669693,-0.024588093161582947,-0.010483341291546822,0.029885433614253998,-0.030260466039180756,0.014731760136783123,0.04219119995832443,0.009979390539228916,0.01564590260386467,-0.006510336417704821,0.025150641798973083,0.03965972736477852,0.024072423577308655,0.009698116220533848,0.052176449447870255,0.005736831109970808,-0.008678494952619076,-0.021974582225084305,-0.018259413540363312,0.027236763387918472,-0.023275477811694145,-0.01665380410850048,-0.008895311504602432,0.024002104997634888,0.019771264865994453,-0.0036038316320627928,0.010143467225134373,-0.02302936278283596,-0.05259836092591286,-0.017474187538027763,-0.025853829458355904,-0.0318777933716774,0.01563418284058571,-0.015575584024190903,0.02253713272511959,0.0027746574487537146,-0.008989069610834122,-0.023591911420226097,0.0027028736658394337,-0.002585676033049822,0.002090515336021781,0.004723070189356804,0.022044900804758072,0.029322883114218712,0.014087172225117683,-0.01170805748552084,0.007360020186752081,0.0007160051609389484,-0.005420397035777569,-0.011280286125838757,-0.01862272620201111,-0.0042659989558160305,-0.024095863103866577,0.018189094960689545,0.030447982251644135,-0.030049510300159454,-0.008092506788671017,-0.05325466766953468,-0.0376439243555069,-0.058645766228437424,-0.0019352282397449017,-0.05423913151025772,0.0014180931029841304,-0.0043451073579490185,0.04847300052642822,0.007301421370357275,0.012294046580791473,-0.01685304008424282,-0.017802342772483826,-0.022455094382166862,0.004670331254601479,0.009844613261520863,0.010653277859091759,-0.00026973799685947597,-0.01815393567085266,0.004084342624992132,-0.021693307906389236,0.010442322120070457,-0.005177211947739124,-0.01256360113620758,0.023193439468741417,-0.011836974881589413,0.020486170426011086,0.019056357443332672,-0.004084342624992132,-0.02082604542374611,-0.003788418136537075,-0.011344744823873043,-0.024541214108467102,-0.01933763362467289,0.011626019142568111,0.019372792914509773,-0.02042757160961628,0.0042659989558160305,0.021165918558835983,-0.01994706131517887,-0.023861467838287354,-0.01912667788565159,0.001190289855003357,0.029627596959471703,0.004342177417129278,-0.042120881378650665,-0.03907373920083046,-0.011180668137967587,0.028455620631575584,0.004764089360833168,-0.010817354544997215,0.028877532109618187,0.0024611533153802156,0.04148801416158676,-0.025783510878682137,0.017966419458389282,0.036448508501052856,0.02352159284055233,0.014544243924319744,0.0027951670344918966,-0.02222069911658764,0.001996756996959448,-0.0046498216688632965,0.02324031852185726,0.01656004600226879,0.018118776381015778,0.033331047743558884,0.008250723592936993,0.02402554452419281,0.007653014734387398,0.0038440870121121407,0.011778376065194607,-0.0019938270561397076,0.037737682461738586,-0.01944311149418354,-0.013266787864267826,0.030658937990665436,-0.005912627559155226,0.0008863082039169967,0.00052043137839064,0.015868578106164932,0.022970763966441154,-0.011274426244199276,0.007342440541833639,-0.04045667126774788,-0.004690840840339661,-0.04029259458184242,-0.008883591741323471,-0.010161046870052814,0.01270423922687769,0.019583748653531075,-0.017966419458389282,0.004151731263846159,0.009229324758052826,0.01596233621239662,-0.009967670775949955,-0.01504819467663765,-0.013618381693959236,-0.028877532109618187,-0.010184486396610737,0.008678494952619076,-0.024072423577308655,0.03454990312457085,0.005420397035777569,0.01912667788565159,-0.05531734973192215,-0.005938997492194176,0.012868315912783146,-0.015376348048448563,-0.002534401835873723,0.008080787025392056,0.03701105713844299,0.023908346891403198,0.006662693340331316,0.029838554561138153,0.013805897906422615,0.016407689079642296,-0.025478797033429146,-0.020872924476861954,-0.004277718719094992,-0.009481299668550491,-0.05405161529779434,0.025549115613102913,0.00532956887036562,0.022888725623488426,-0.012223728001117706,-0.008338621817529202,-0.0426834300160408,0.02442401647567749,0.030330784618854523,0.019466551020741463,0.022584011778235435,0.048191726207733154,-0.04146457463502884,0.003926125355064869,-0.005900907795876265,0.010084868408739567]) as score
FROM shared_source.magento_product_dimension_with_text_embedding__tmp
/*+ SET_VAR(ann_params='{"ef_search":64}') */
ORDER BY score DESC
LIMIT 10
\ No newline at end of file
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