完成三种构建方法

This commit is contained in:
2025-06-11 17:30:06 +08:00
commit f34e4a464d
42 changed files with 190508 additions and 0 deletions

View File

@@ -0,0 +1,356 @@
import mysql.connector
from mysql.connector import Error
import pandas as pd
import logging
from datetime import datetime
import time
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler(f'magento_queries_{datetime.now().strftime("%Y%m%d_%H%M%S")}.log'),
logging.StreamHandler()
]
)
logger = logging.getLogger(__name__)
def create_db_connection(host_name, port, user_name, user_password, db_name, max_retries=3):
connection = None
retry_count = 0
while retry_count < max_retries:
try:
logger.info(f"Attempting to connect to database {db_name} at {host_name}:{port} (Attempt {retry_count + 1}/{max_retries})")
connection = mysql.connector.connect(
host=host_name,
port=port,
user=user_name,
passwd=user_password,
database=db_name,
connection_timeout=180, # 3 minutes timeout
pool_size=5, # Enable connection pooling
pool_name="magento_pool"
)
logger.info("MySQL Database connection successful")
return connection
except Error as err:
retry_count += 1
logger.error(f"Failed to connect to database (Attempt {retry_count}/{max_retries}): {err}")
if retry_count < max_retries:
wait_time = 2 ** retry_count # Exponential backoff
logger.info(f"Waiting {wait_time} seconds before retrying...")
time.sleep(wait_time)
else:
print(f"Error: '{err}'")
return None
def execute_query(connection, query, question_number, question_text, max_retries=3):
retry_count = 0
while retry_count < max_retries:
try:
cursor = connection.cursor(dictionary=True)
results = None
logger.info(f"Executing Question {question_number}: {question_text}")
logger.debug(f"SQL Query:\n{query}")
start_time = datetime.now()
cursor.execute(query)
execution_time = (datetime.now() - start_time).total_seconds()
logger.info(f"Query executed in {execution_time:.2f} seconds")
results = cursor.fetchall()
logger.info(f"Retrieved {len(results) if results else 0} rows")
print(f"\n--- Question {question_number} ---")
print(f"Question: {question_text}")
print(f"SQL:\n{query}")
print("Output:")
if results:
df = pd.DataFrame(results)
print(df.to_string())
logger.info(f"Results displayed successfully")
else:
print("No results found or query was an UPDATE/DELETE type.")
logger.info("Query returned no results")
if cursor.rowcount > -1 and not results:
logger.info(f"Rows affected: {cursor.rowcount}")
print(f"Rows affected: {cursor.rowcount}")
cursor.close()
logger.debug("Cursor closed")
return results
except Error as err:
retry_count += 1
logger.error(f"Error executing query (Attempt {retry_count}/{max_retries}): {err}")
if retry_count < max_retries:
wait_time = 2 ** retry_count # Exponential backoff
logger.info(f"Waiting {wait_time} seconds before retrying...")
time.sleep(wait_time)
# Try to reconnect if connection is lost
try:
connection.ping(reconnect=True, attempts=3, delay=5)
logger.info("Successfully reconnected to database")
except Error as reconnect_err:
logger.error(f"Failed to reconnect: {reconnect_err}")
connection = create_db_connection(DB_HOST, DB_PORT, DB_USER, DB_PASS, DB_NAME)
if not connection:
raise Exception("Failed to reestablish database connection")
else:
print(f"Error executing query: '{err}'")
raise
# --- Database Configuration ---
DB_HOST = "localhost"
DB_USER = "root"
DB_PORT = 23306
DB_PASS = "1234567890"
DB_NAME = "magentodb"
# --- Questions and SQL Queries ---
# Note: For EAV attributes, we often need to find the attribute_id first.
# Common entity_type_ids: customer=1, customer_address=2, catalog_category=3, catalog_product=4
# Common store_id for global/admin values is 0.
questions_and_queries = [
(
"Identify the product (SKU and Name) with the highest 'price' attribute value in the default store scope.",
"""
SELECT
cpe.sku,
cpev.value AS product_name,
cped.value AS price
FROM
catalog_product_entity cpe
JOIN
catalog_product_entity_decimal cped ON cpe.entity_id = cped.entity_id
JOIN
eav_attribute ea_price ON cped.attribute_id = ea_price.attribute_id AND ea_price.attribute_code = 'price'
AND ea_price.entity_type_id = (SELECT entity_type_id FROM eav_entity_type WHERE entity_type_code = 'catalog_product')
JOIN
catalog_product_entity_varchar cpev ON cpe.entity_id = cpev.entity_id
JOIN
eav_attribute ea_name ON cpev.attribute_id = ea_name.attribute_id AND ea_name.attribute_code = 'name'
AND ea_name.entity_type_id = (SELECT entity_type_id FROM eav_entity_type WHERE entity_type_code = 'catalog_product')
WHERE
cped.store_id = 0 AND cpev.store_id = 0 -- Default store scope
ORDER BY
cped.value DESC
LIMIT 1;
"""
),
(
"List all customers (First Name, Last Name, Email) who have not placed any orders.",
"""
SELECT
ce.firstname,
ce.lastname,
ce.email
FROM
customer_entity ce
LEFT JOIN
sales_order so ON ce.entity_id = so.customer_id
WHERE
so.entity_id IS NULL;
"""
),
(
"What is the total number of 'simple' products currently marked as 'Out of Stock' (status = 0) in the 'default' inventory source?",
"""
SELECT COUNT(DISTINCT isi.sku) AS total_out_of_stock_simple_products
FROM inventory_source_item isi
JOIN catalog_product_entity cpe ON isi.sku = cpe.sku
WHERE
isi.source_code = 'default'
AND isi.status = 0 -- 0 for Out of Stock, 1 for In Stock
AND cpe.type_id = 'simple';
"""
),
(
"Find the top 3 customer groups by the total 'grand_total' of orders placed. Show group name and total amount.",
"""
SELECT
cg.customer_group_code,
SUM(so.grand_total) AS total_order_amount
FROM
sales_order so
JOIN
customer_group cg ON so.customer_group_id = cg.customer_group_id
GROUP BY
cg.customer_group_code
ORDER BY
total_order_amount DESC
LIMIT 3;
"""
),
(
"Retrieve the SKU, name, and creation date of the 5 oldest products that are still 'enabled' (status attribute value = 1).",
"""
SELECT
p.sku,
name_val.value AS product_name,
p.created_at
FROM
catalog_product_entity p
JOIN
eav_attribute e_status ON e_status.attribute_code = 'status'
AND e_status.entity_type_id = (SELECT entity_type_id FROM eav_entity_type WHERE entity_type_code = 'catalog_product')
JOIN
catalog_product_entity_int status_val ON status_val.attribute_id = e_status.attribute_id
AND status_val.entity_id = p.entity_id
AND status_val.store_id = 0 -- Check default scope status
JOIN
eav_attribute e_name ON e_name.attribute_code = 'name'
AND e_name.entity_type_id = (SELECT entity_type_id FROM eav_entity_type WHERE entity_type_code = 'catalog_product')
JOIN
catalog_product_entity_varchar name_val ON name_val.attribute_id = e_name.attribute_id
AND name_val.entity_id = p.entity_id
AND name_val.store_id = 0 -- Name from default store view
WHERE
status_val.value = 1 -- 1 for Enabled
ORDER BY
p.created_at ASC
LIMIT 5;
"""
),
(
"List all URL rewrites of type 'product' that are NOT autogenerated and their target paths for store_id 1.",
"""
SELECT
request_path,
target_path,
description
FROM
url_rewrite
WHERE
entity_type = 'product'
AND is_autogenerated = 0
AND store_id = 1;
"""
),
(
"How many distinct customers placed orders in January 2023?",
"""
SELECT
COUNT(DISTINCT customer_id) AS distinct_customers_jan_2023
FROM
sales_order
WHERE
created_at >= '2023-01-01 00:00:00'
AND created_at < '2023-02-01 00:00:00'
AND customer_id IS NOT NULL; -- Only count registered customers
"""
),
(
"Identify orders (Increment ID and Grand Total) placed by customers with ' VIP ' in their customer group code, and the order status is 'complete'.",
"""
SELECT
so.increment_id,
so.grand_total,
cg.customer_group_code
FROM
sales_order so
JOIN
customer_group cg ON so.customer_group_id = cg.customer_group_id
WHERE
cg.customer_group_code LIKE '%VIP%'
AND so.status = 'complete';
"""
),
(
"Find the 3rd most recent review. Provide the review ID, nickname, title, and detail.",
"""
WITH RankedReviews AS (
SELECT
r.review_id,
rd.nickname,
rd.title,
rd.detail,
r.created_at,
DENSE_RANK() OVER (ORDER BY r.created_at DESC) as review_rank
FROM
review r
JOIN
review_detail rd ON r.review_id = rd.review_id
)
SELECT
review_id,
nickname,
title,
detail,
created_at
FROM
RankedReviews
WHERE
review_rank = 3;
""",
),
(
"List categories (Name and Path) that have no products assigned to them.",
"""
SELECT
cce.entity_id,
ccev.value AS category_name,
cce.path
FROM
catalog_category_entity cce
JOIN
eav_attribute ea_name ON ea_name.attribute_code = 'name'
AND ea_name.entity_type_id = (SELECT entity_type_id FROM eav_entity_type WHERE entity_type_code = 'catalog_category')
JOIN
catalog_category_entity_varchar ccev ON ccev.attribute_id = ea_name.attribute_id
AND ccev.entity_id = cce.entity_id
AND ccev.store_id = 0 -- Default store name
LEFT JOIN
catalog_category_product ccp ON cce.entity_id = ccp.category_id
WHERE
ccp.product_id IS NULL
AND cce.children_count = 0; -- Optionally, only leaf categories with no products
"""
),
(
"Which payment methods have been used for orders with a grand_total greater than $500 in the last 6 months? Show method and count of orders.",
# Assuming current date is around May 2024 for "last 6 months"
"""
SELECT
sop.method,
COUNT(DISTINCT so.entity_id) AS order_count
FROM
sales_order so
JOIN
sales_order_payment sop ON so.entity_id = sop.parent_id
WHERE
so.grand_total > 500
AND so.created_at >= DATE_SUB(CURDATE(), INTERVAL 6 MONTH)
GROUP BY
sop.method
ORDER BY
order_count DESC;
"""
)
]
if __name__ == '__main__':
logger.info("Initializing database connection")
connection = create_db_connection(DB_HOST, DB_PORT, DB_USER, DB_PASS, DB_NAME)
if connection:
try:
logger.info(f"Executing {len(questions_and_queries)} queries")
for i, (question, query) in enumerate(questions_and_queries):
try:
execute_query(connection, query, i + 1, question)
except Exception as e:
logger.error(f"Failed to execute query {i + 1}: {str(e)}")
continue # Continue with next query even if one fails
finally:
connection.close()
logger.info("MySQL connection closed")
print("\nMySQL connection is closed")
else:
logger.error("Failed to establish database connection")

View File

@@ -0,0 +1,862 @@
import openai
import mysql.connector
from mysql.connector import Error
import json
import logging
from datetime import datetime
import time
import decimal # For handling Decimal from DB
import os
from dotenv import load_dotenv
import re # Added for regex
load_dotenv()
# --- LLM Configuration ---
# It's best to set API keys as environment variables or use a secrets manager
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY", "YOUR_OPENAI_API_KEY_FALLBACK")
OPENAI_BASE_URL = os.environ.get("OPENAI_BASE_URL", "https://api.openai.com/v1") # Default OpenAI URL
# LLM_MODEL_GENERATION = "gpt-4o" # Example, ensure it's correctly set
# LLM_MODEL_VALIDATION = "gpt-4o" # Example
LLM_MODEL_GENERATION = os.environ.get("LLM_MODEL_GENERATION", "gpt-4o")
LLM_MODEL_VALIDATION = os.environ.get("LLM_MODEL_VALIDATION", "gpt-4o")
# Configure logging
log_file_name = f'magento_main_pipeline_{datetime.now().strftime("%Y%m%d_%H%M%S")}.log'
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(module)s - %(funcName)s - %(message)s',
handlers=[
logging.FileHandler(log_file_name),
logging.StreamHandler()
]
)
logger = logging.getLogger(__name__)
# --- Database Configuration ---
DB_HOST = "localhost"
DB_USER = "root"
DB_PORT = 23306 # Make sure this is an integer
DB_PASS = "1234567890"
DB_NAME = "magentodb"
# --- Magento Schema (loaded in main_workflow) ---
MAGENTO_SCHEMA_CONTENT = ""
TABLE_SAMPLES_CACHE_FILE = "table_samples_cache.txt" # Cache file for table samples
TABLE_SAMPLES_CONTENT = "" # To store sample rows
# --- System Prompt (Loaded in main_workflow) ---
SYSTEM_PROMPT_TEMPLATE = ""
def _clean_llm_json_response(response_content: str) -> str:
"""Strips markdown code fences from LLM JSON responses."""
clean_response = response_content.strip()
if clean_response.startswith("```json"):
clean_response = clean_response[7:-3].strip()
elif clean_response.startswith("```"):
clean_response = clean_response[3:-3].strip()
return clean_response
def _clean_llm_python_code_response(response_content: str) -> str:
"""Strips markdown code fences from LLM Python code responses."""
clean_code = response_content.strip()
if clean_code.startswith("```python"):
clean_code = clean_code[10:-3].strip() # Handles ```python\n ... ```
elif clean_code.startswith("```"):
clean_code = clean_code[3:-3].strip()
return clean_code
def get_table_names_from_schema(schema_content):
"""Extracts table names from schema DDL using regex."""
# Regex to find "CREATE TABLE `table_name`" or "CREATE TABLE table_name"
# It captures the table name, optionally enclosed in backticks.
table_names = re.findall(r"CREATE TABLE(?: IF NOT EXISTS)?\s+`?(\w+)`?", schema_content, re.IGNORECASE)
logger.info(f"Extracted {len(table_names)} table names from schema.")
logger.debug(f"Table names: {table_names}")
return list(set(table_names)) # Return unique table names
def _fetch_and_format_table_samples(table_names, db_conn, cache_file_path):
"""
Fetches top 5 rows for each table, formats them, and saves to a cache file.
Returns the formatted string of all table samples.
"""
all_samples_str = ""
if not db_conn or not db_conn.is_connected():
logger.warning("Database connection not available. Cannot fetch fresh table samples.")
return ""
logger.info(f"Fetching top 5 rows for {len(table_names)} tables...")
for table_name in table_names:
try:
cursor = db_conn.cursor(dictionary=True)
query = f"SELECT * FROM `{table_name}` LIMIT 5" # Use backticks for table names
logger.debug(f"Executing sample query for {table_name}: {query}")
cursor.execute(query)
rows = cursor.fetchall()
current_table_sample_str = f"\n--- Sample rows for table: {table_name} ---\n"
if rows:
headers = ", ".join(rows[0].keys())
current_table_sample_str += headers + "\n"
for row in rows:
# Convert all values to string, handling None
values = ", ".join([str(v) if v is not None else "NULL" for v in row.values()])
current_table_sample_str += values + "\n"
else:
current_table_sample_str += "(No rows found or table is empty)\n"
all_samples_str += current_table_sample_str
cursor.close()
except Error as e:
logger.error(f"Error fetching samples for table {table_name}: {e}")
all_samples_str += f"\n--- Error fetching samples for table: {table_name}: {e} ---\n"
except Exception as ex: # Catch any other unexpected errors
logger.error(f"Unexpected error fetching samples for table {table_name}: {ex}")
all_samples_str += f"\n--- Unexpected error for table: {table_name}: {ex} ---\n"
try:
with open(cache_file_path, "w", encoding="utf-8") as f:
f.write(all_samples_str)
logger.info(f"Table samples cached successfully to {cache_file_path}")
except IOError as e:
logger.error(f"Failed to write table samples to cache file {cache_file_path}: {e}")
return all_samples_str
def initialize_system_prompt(db_conn_for_samples, current_script_dir):
global SYSTEM_PROMPT_TEMPLATE, MAGENTO_SCHEMA_CONTENT, TABLE_SAMPLES_CONTENT
if not MAGENTO_SCHEMA_CONTENT:
logger.error("Magento schema content is not loaded. Cannot initialize system prompt.")
# SYSTEM_PROMPT_TEMPLATE will remain empty or use a default if set elsewhere
return
sample_rows_cache_path = os.path.join(current_script_dir, TABLE_SAMPLES_CACHE_FILE)
try:
with open(sample_rows_cache_path, "r", encoding="utf-8") as f:
TABLE_SAMPLES_CONTENT = f.read()
logger.info(f"Table samples loaded successfully from cache: {sample_rows_cache_path}")
except FileNotFoundError:
logger.info(f"Table samples cache file not found: {sample_rows_cache_path}. Attempting to fetch from DB.")
if db_conn_for_samples and db_conn_for_samples.is_connected():
table_names = get_table_names_from_schema(MAGENTO_SCHEMA_CONTENT)
if table_names:
TABLE_SAMPLES_CONTENT = _fetch_and_format_table_samples(table_names, db_conn_for_samples, sample_rows_cache_path)
else:
logger.warning("No table names extracted from schema. Cannot fetch samples.")
TABLE_SAMPLES_CONTENT = " (Could not extract table names to fetch samples) "
else:
logger.warning("DB connection not available and cache miss. Proceeding without table samples in prompt.")
TABLE_SAMPLES_CONTENT = " (DB connection not available for fetching samples and no cache found) "
except Exception as e:
logger.error(f"Error loading table samples from cache {sample_rows_cache_path}: {e}")
TABLE_SAMPLES_CONTENT = f" (Error loading table samples from cache: {e}) "
# Update: Use curated_schema.txt in the prompt template
SYSTEM_PROMPT_TEMPLATE = f"""
You are an expert Magento 2 database analyst and Python programmer. Your task is to assist in creating a dataset of questions, SQL queries, and Python validation functions for a Magento 2 database.
**Database Schema:**
--- START OF FILE curated_schema.txt ---
{MAGENTO_SCHEMA_CONTENT}
--- END OF FILE curated_schema.txt ---
**Sample Data from Tables (Top 5 rows if available):**
--- START OF SAMPLE DATA ---
{TABLE_SAMPLES_CONTENT}
--- END OF SAMPLE DATA ---
**Key Magento Schema Characteristics & EAV Model:**
* **EAV (Entity-Attribute-Value):** Many entities (products, categories, customers) use EAV.
* Core entity table: e.g., `catalog_product_entity`.
* Attribute definition: `eav_attribute`.
* Value tables by data type: e.g., `catalog_product_entity_varchar`, `_int`, `_decimal`.
* To get an attribute value (e.g., product name), you typically JOIN `catalog_product_entity` with `eav_attribute` (to find the attribute_id for 'name') and then JOIN with `catalog_product_entity_varchar` using that attribute_id and the product's entity_id.
* **Store Scopes:** Data can be global (store_id=0 or admin), website-specific, or store-view-specific. Queries often need to specify `store_id`. `store_id = 0` is the admin/default scope for many attributes.
* **Product Types:** `catalog_product_entity.type_id` can be 'simple', 'configurable', 'virtual', 'bundle', 'downloadable', 'grouped'.
* **Inventory (MSI):** `inventory_source_item` manages stock per source (e.g., 'default'). `status = 1` (In Stock), `status = 0` (Out of Stock). `cataloginventory_stock_item` is the older/default system.
* **Order Workflow:** `quote` (cart) -> `sales_order` -> `sales_invoice`, `sales_shipment`, `sales_creditmemo`.
* **Flat/Grid Tables:** Tables like `sales_order_grid`, `customer_grid_flat` are denormalized for admin panel performance. Queries for direct user-facing info might use these, but detailed analysis often requires joining base tables.
* **Date/Time:** Timestamps are common (e.g., `created_at`, `updated_at`). Be mindful of timezones if applicable, though standard MySQL functions usually handle it.
* **Foreign Keys:** Pay attention to foreign key relationships for JOINs (e.g., `sales_order_item.order_id` -> `sales_order.entity_id`).
**Task-Specific Instructions (General):**
* Ensure SQL queries are compatible with MariaDB/MySQL.
* For EAV attributes, ensure you correctly identify the `entity_type_id` for the attribute (e.g., for 'catalog_product' from `eav_entity_type` WHERE entity_type_code = 'catalog_product') and the `attribute_code`.
* Use `store_id = 0` for admin/default scope attributes unless a specific store view is relevant.
* Aim for variety in questions: simple lookups, aggregations, joins, EAV traversals, date operations, DML (for operational tasks).
* Answers derived from queries should be strictly verifiable.
"""
def create_db_connection(host_name, port, user_name, user_password, db_name, max_retries=3):
connection = None
retry_count = 0
while retry_count < max_retries:
try:
logger.info(f"Attempting to connect to database {db_name} at {host_name}:{port} (Attempt {retry_count + 1}/{max_retries})")
connection = mysql.connector.connect(
host=host_name,
port=int(port), # Ensure port is an integer
user=user_name,
passwd=user_password,
database=db_name,
connection_timeout=180,
)
logger.info(f"MySQL Database connection successful to {db_name}")
return connection
except Error as err:
retry_count += 1
logger.error(f"Failed to connect to database (Attempt {retry_count}/{max_retries}): {err}")
if retry_count < max_retries:
wait_time = 2 ** retry_count
logger.info(f"Waiting {wait_time} seconds before retrying...")
time.sleep(wait_time)
else:
return None
def call_llm(prompt_messages, model_name, temperature=0.2, max_tokens=2048):
"""Generic function to call OpenAI compatible API."""
if OPENAI_API_KEY == "YOUR_OPENAI_API_KEY_FALLBACK":
logger.error("OpenAI API key is not configured. Please set the OPENAI_API_KEY environment variable or update the script.")
return None
# Ensure client is initialized for each call or manage a global client carefully
try:
client = openai.OpenAI(api_key=OPENAI_API_KEY, base_url=OPENAI_BASE_URL)
except Exception as e:
logger.error(f"Failed to initialize OpenAI client: {e}")
return None
try:
logger.info(f"Calling LLM model: {model_name} with temperature {temperature}")
logger.debug(f"LLM Request Messages: {json.dumps(prompt_messages, indent=2)}") # Log the prompt
response = client.chat.completions.create(
model=model_name,
messages=prompt_messages,
temperature=temperature,
max_tokens=max_tokens
)
content = response.choices[0].message.content.strip()
logger.info(f"LLM call successful. Tokens used: Completion={response.usage.completion_tokens}, Prompt={response.usage.prompt_tokens}, Total={response.usage.total_tokens}")
logger.debug(f"LLM Raw Response Content:\n{content}")
return content
except Exception as e:
logger.error(f"Error calling LLM: {e}")
return None
def generate_initial_tasks_and_prep_sql(num_tasks=10):
"""Step 1: LLM generates initial questions/tasks and preparatory (SELECT-only) SQL queries."""
logger.info(f"Requesting LLM to generate {num_tasks} initial tasks and preparatory SQLs.")
prompt_step1 = f"""
Based on the provided Magento 2 database schema, sample data, and its characteristics (from system prompt), generate a list of {num_tasks} diverse tasks. For each task:
1. Provide an **original_question** (string): This is the initial high-level question or operational intent.
2. Provide a **task_type** (string): Either "query" or "operational_check".
3. Provide a **preparatory_sql_list** (list of strings): A list of one or more **SELECT-ONLY SQL queries**.
* These SQLs are for **information gathering, pre-condition checking, or collecting data to answer a question.**
* For **"query" task_type**, these SQLs should aim to gather all necessary data to answer the `original_question`.
* For **"operational_check" task_type** (e.g., "Intent: Update product X's price" or "Intent: Cancel order Y"), these SQLs should **ONLY** check if the target entity exists, get its current state, or list potential entities. **ABSOLUTELY NO DML (UPDATE, INSERT, DELETE) should be generated in this list.**
* The results of these preparatory SQLs will be used by a subsequent LLM call to refine the question and assess the gathered information.
Format the output STRICTLY as a JSON list of objects. Each object must have "original_question", "task_type", and "preparatory_sql_list" keys.
Ensure the JSON is well-formed. Do not include any introductory text or markdown formatting around the JSON list itself.
**Example for an "operational_check" task:**
{{
"original_question": "Intent: Update the stock quantity of product with SKU 'TEST-SKU-XYZ' to 50 in the default source.",
"task_type": "operational_check",
"preparatory_sql_list": [
"SELECT entity_id, sku FROM catalog_product_entity WHERE sku = 'TEST-SKU-XYZ';",
"SELECT quantity, status FROM inventory_source_item WHERE sku = 'TEST-SKU-XYZ' AND source_code = 'default';"
]
}}
**Example for a "query" task:**
{{
"original_question": "What are the details (increment_id, status, grand_total) of the most recent order placed by customer_email 'test@example.com'?",
"task_type": "query",
"preparatory_sql_list": [
"SELECT entity_id FROM customer_entity WHERE email = 'test@example.com';",
"SELECT entity_id, increment_id, status, grand_total, created_at FROM sales_order WHERE customer_email = 'test@example.com' ORDER BY created_at DESC LIMIT 1;"
]
}}
Generate {num_tasks} new and distinct items.
"""
messages = [
{"role": "system", "content": SYSTEM_PROMPT_TEMPLATE},
{"role": "user", "content": prompt_step1}
]
response_content = call_llm(messages, LLM_MODEL_GENERATION, temperature=0.7, max_tokens=3500)
if response_content:
try:
clean_response = _clean_llm_json_response(response_content) # Use helper
generated_data = json.loads(clean_response)
if isinstance(generated_data, list) and all(
isinstance(item, dict) and
"original_question" in item and isinstance(item["original_question"], str) and
"task_type" in item and item["task_type"] in ["query", "operational_check"] and
"preparatory_sql_list" in item and isinstance(item["preparatory_sql_list"], list) and
all(isinstance(sql, str) and sql.strip().upper().startswith("SELECT") for sql in item["preparatory_sql_list"]) and item["preparatory_sql_list"]
for item in generated_data
):
logger.info(f"Successfully parsed {len(generated_data)} initial tasks from LLM.")
logger.info("--- LLM Generated Initial Tasks & Prep SQL ---")
for i, item in enumerate(generated_data):
logger.info(f" Item {i+1}/{len(generated_data)}:")
logger.info(f" Original Question: {item['original_question']}")
logger.info(f" Task Type: {item['task_type']}")
for j, sql in enumerate(item['preparatory_sql_list']):
logger.info(f" Prep SQL {j+1}: {sql}")
logger.info("--- End of LLM Generated Initial Tasks & Prep SQL ---")
return generated_data
else:
logger.error(f"LLM response was not a valid list of initial task objects or contained non-SELECT prep SQL. Content: {response_content}") # Log original content for debug
return []
except json.JSONDecodeError as e:
logger.error(f"Failed to parse JSON from LLM response for initial tasks: {e}")
logger.error(f"LLM Response Content (check for issues):\n{response_content}") # Log original content for debug
return []
return []
def refine_question_and_assess_info(original_question, task_type, prep_sql_list, prep_sql_results_repr_list):
"""
Step 2: LLM refines question & derives answer/assesses feasibility based on preparatory SQL results.
NO FINAL SQL IS GENERATED HERE.
"""
logger.info(f"Requesting LLM to refine question and assess info for: {original_question[:100]}...")
prep_info_str = ""
for i, sql in enumerate(prep_sql_list):
prep_info_str += f"Preparatory SQL {i+1}: {sql}\n"
prep_info_str += f"Result {i+1} (Python repr):\n{prep_sql_results_repr_list[i]}\n\n"
output_keys_guidance = ""
if task_type == "query":
output_keys_guidance = 'Return a single JSON object with: "revised_question", "llm_derived_answer", "revision_justification". The "llm_derived_answer" should be your attempt to answer the revised_question based *solely* on the provided prep_sql_results.'
elif task_type == "operational_check":
output_keys_guidance = 'Return a single JSON object with: "revised_question", "llm_feasibility_summary", "revision_justification". The "llm_feasibility_summary" should state whether the operational intent in revised_question seems feasible (e.g., "Product exists and is active") or not (e.g., "Order not found"), based *solely* on prep_sql_results.'
prompt_step2 = f"""
You are an expert Magento 2 database analyst.
You previously received an original question/task and a list of preparatory SELECT SQL queries. Those SQLs have been executed.
Your current task is to:
1. Review the original question, the preparatory SQLs, and their execution results.
2. Generate a **revised_question** (string). This might be the same as the original if it's still perfectly valid, or it might be adjusted based on the findings (e.g., if an ID doesn't exist, or if more specific information was found).
3. Based on the **task_type** (see below) and the preparatory SQL results:
* If **task_type** is "query": Generate an **llm_derived_answer** (string). This should be a natural language answer to the `revised_question`, formulated *exclusively* from the data in `prep_sql_results`. If the results are insufficient, state that.
* If **task_type** is "operational_check": Generate an **llm_feasibility_summary** (string). This should summarize if the operational intent in `revised_question` appears feasible based *exclusively* on the `prep_sql_results` (e.g., "Product XYZ exists and current price is $50, so update is feasible." or "Order ABC not found, operation not feasible.").
4. Provide a brief **revision_justification** (string) explaining why the question was or was not revised, and how the preparatory results informed your assessment or derived answer.
**Input Provided to You:**
* **Original Question:** {original_question}
* **Task Type (from previous step):** {task_type}
* **Preparatory SQLs and their Results:**
{prep_info_str}
**Output Format:**
{output_keys_guidance}
Ensure the JSON is well-formed and contains only the specified keys. Provide the JSON object directly.
**Example for "query" task_type:**
{{
"revised_question": "What is the status and grand_total of order increment_id '100000005'?",
"llm_derived_answer": "Order '100000005' has status 'complete' and grand_total '125.50'.",
"revision_justification": "Original question was specific. Prep SQL confirmed order existence and fetched details. Answer derived directly from prep results."
}}
**Example for "operational_check" task_type:**
{{
"revised_question": "Intent: Update stock for SKU 'ABC-123' from 10 to 5.",
"llm_feasibility_summary": "Product SKU 'ABC-123' exists and current stock is 10. The update is feasible.",
"revision_justification": "Prep SQLs confirmed product existence and current stock level, matching conditions for the intended operation."
}}
"""
messages = [
{"role": "system", "content": SYSTEM_PROMPT_TEMPLATE},
{"role": "user", "content": prompt_step2}
]
response_content = call_llm(messages, LLM_MODEL_GENERATION, temperature=0.2, max_tokens=1500)
if response_content:
try:
clean_response = _clean_llm_json_response(response_content) # Use helper
refined_data = json.loads(clean_response)
base_keys_valid = isinstance(refined_data, dict) and \
"revised_question" in refined_data and isinstance(refined_data["revised_question"], str) and \
"revision_justification" in refined_data and isinstance(refined_data["revision_justification"], str)
type_specific_keys_valid = False
if task_type == "query":
type_specific_keys_valid = "llm_derived_answer" in refined_data and isinstance(refined_data["llm_derived_answer"], str)
elif task_type == "operational_check":
type_specific_keys_valid = "llm_feasibility_summary" in refined_data and isinstance(refined_data["llm_feasibility_summary"], str)
if base_keys_valid and type_specific_keys_valid:
logger.info("Successfully parsed refined question and assessment from LLM.")
logger.info(f" Revised Question: {refined_data['revised_question']}")
if task_type == "query":
logger.info(f" LLM Derived Answer: {refined_data['llm_derived_answer']}")
elif task_type == "operational_check":
logger.info(f" LLM Feasibility Summary: {refined_data['llm_feasibility_summary']}")
logger.info(f" Revision Justification: {refined_data['revision_justification']}")
return refined_data
else:
logger.error(f"LLM response for refined assessment had missing or invalid keys for task_type '{task_type}'. Content: {response_content}") # Log original content
return None
except json.JSONDecodeError as e:
logger.error(f"Failed to parse JSON from LLM response for refined assessment: {e}")
logger.error(f"LLM Response Content (check for issues):\n{response_content}") # Log original content
return None
def execute_sql_and_get_results(db_conn, sql_query, question_text, q_num):
"""Step 2: Execute SQL and collect results. Handles SELECT and DML."""
logger.info(f"Attempting to execute SQL for Question {q_num}: {question_text[:100]}...")
logger.debug(f"Full SQL for Q{q_num}: {sql_query}")
# Ensure connection is active
try:
if db_conn is None or not db_conn.is_connected():
logger.warning(f"DB connection lost or not available for Q{q_num}. Attempting to reconnect...")
db_conn = create_db_connection(DB_HOST, DB_PORT, DB_USER, DB_PASS, DB_NAME)
if db_conn is None or not db_conn.is_connected():
logger.error(f"Failed to re-establish DB connection for Q{q_num}.")
return f"Error: Database connection lost and could not be re-established."
except Exception as e: # Catch broader exceptions if is_connected() fails
logger.error(f"Error checking DB connection status for Q{q_num}: {e}. Attempting to reconnect...")
db_conn = create_db_connection(DB_HOST, DB_PORT, DB_USER, DB_PASS, DB_NAME)
if db_conn is None :
logger.error(f"Failed to re-establish DB connection for Q{q_num} after check error.")
return f"Error: Database connection check failed and could not be re-established."
cursor = None
raw_results = []
# Normalize SQL query for DML check (uppercase, remove leading/trailing whitespace)
normalized_sql_query = sql_query.strip().upper()
is_dml = any(normalized_sql_query.startswith(dml_cmd) for dml_cmd in ["UPDATE", "INSERT", "DELETE"])
# CREATE, ALTER, DROP are DDL, not typically what we expect here but could be considered 'operational'
is_ddl = any(normalized_sql_query.startswith(ddl_cmd) for ddl_cmd in ["CREATE", "ALTER", "DROP"])
query_start_time = time.time()
try:
cursor = db_conn.cursor(dictionary=True)
logger.debug(f"Cursor created for Q{q_num}.")
cursor.execute(sql_query)
logger.debug(f"SQL executed for Q{q_num}.")
if not is_dml and not is_ddl: # It's a SELECT query
fetched_rows = cursor.fetchall()
# Convert Decimal to string for JSON serializability and consistent LLM input
for row in fetched_rows:
raw_results.append({
k: str(v) if isinstance(v, decimal.Decimal) else
v.strftime('%Y-%m-%d %H:%M:%S') if isinstance(v, datetime) else
v
for k, v in row.items()
})
logger.info(f"SELECT query for Q{q_num} fetched {len(raw_results)} rows.")
elif is_dml:
db_conn.commit()
raw_results = f"Rows affected: {cursor.rowcount}"
logger.info(f"DML query for Q{q_num} committed. {raw_results}")
elif is_ddl:
db_conn.commit() # Some DDL might need commit or are auto-committed
raw_results = f"DDL statement executed. Rows affected: {cursor.rowcount}" # rowcount might be -1 or 0 for DDL
logger.info(f"DDL query for Q{q_num} executed. {raw_results}")
except Error as e:
logger.error(f"Error executing SQL for Q{q_num}: {e}\nSQL: {sql_query}")
if db_conn and db_conn.is_connected() and (is_dml or is_ddl) : # Only rollback DML/DDL on error
try:
db_conn.rollback()
logger.info(f"Rolled back transaction for Q{q_num} due to error.")
except Error as rb_err:
logger.error(f"Error during rollback for Q{q_num}: {rb_err}")
return f"Error: {str(e)}" # Return error string
finally:
if cursor:
cursor.close()
logger.debug(f"Cursor closed for Q{q_num}.")
query_duration = time.time() - query_start_time
logger.info(f"SQL for Q{q_num} processed in {query_duration:.2f}s.")
return raw_results
def generate_validation_function(revised_question, task_type,
prep_sql_list_str,
prep_sql_results_repr_list_str,
llm_assessment_from_step2_str
):
"""
Step 3: LLM generates Python validation function.
- For "query": creates validate_query_answer(user_answer) with hardcoded expected answer.
- For "operational_check": creates validate_operational_state(db_connection) with hardcoded DB checks.
"""
logger.info(f"Requesting LLM to generate validation function for: {revised_question[:100]} (Type: {task_type})")
prompt_core_context = f"""
**Context Provided to You (for informing the validation logic you will create):**
1. **Revised Question/Operational Intent:**
```
{revised_question}
```
2. **Task Type:** "{task_type}"
3. **Preparatory SELECT SQL List (that led to the assessment below):**
```sql
{prep_sql_list_str}
```
4. **Preparatory SQL Execution Results (Python string repr of a list of results):**
```python
{prep_sql_results_repr_list_str}
```
"""
if task_type == "query":
prompt_step3_template = f"""
You are an expert Magento 2 database analyst and Python programmer.
Your task is to create a Python validation function `validate_query_answer(user_answer)`.
This function will take a `user_answer` (string) and compare it against an **expected answer** that you will determine and **hardcode** into the function.
{prompt_core_context}
5. **LLM's Derived Answer (this should be the basis for your hardcoded expected answer):**
```
{llm_assessment_from_step2_str}
```
**Your Task for "query" type:**
Create a Python function `validate_query_answer(user_answer)`:
* The function should **hardcode the expected answer** based on the "LLM's Derived Answer" provided above. This might involve storing the exact string, or if it's numerical/structured, parsing and storing it appropriately.
* It compares the input `user_answer` to this hardcoded expected answer.
* Return `(is_valid, message)`:
* `is_valid` (boolean): `True` if `user_answer` matches the hardcoded expected answer (allow for some flexibility like case-insensitivity or stripping whitespace for strings, or numerical tolerance if applicable).
* `message` (string): Explaining the outcome (e.g., "User answer matches expected.", "User answer 'X' does not match expected 'Y'.").
* The function must be self-contained (standard imports like `json`, `decimal` are ok if needed for handling the expected answer). It does **NOT** use a database connection.
**Example `validate_query_answer` structure:**
```python
import decimal # if needed
def validate_query_answer(user_answer):
# Based on LLM's Derived Answer: "The total number of customers is 157."
expected_answer_str = "The total number of customers is 157."
# Or, for numerical: expected_count = 157
# Simple string comparison (you can make this more robust)
if isinstance(user_answer, str) and user_answer.strip().lower() == expected_answer_str.strip().lower():
return True, "User answer matches the expected answer."
else:
# Attempt to extract number if question implies a number
try:
# This is just an example, adapt based on actual derived answer format
user_num_part = ''.join(filter(str.isdigit, user_answer))
expected_num_part = ''.join(filter(str.isdigit, expected_answer_str))
if user_num_part and expected_num_part and int(user_num_part) == int(expected_num_part):
return True, f"User answer contains the correct numerical part '{{user_num_part}}' matching expected."
except ValueError:
pass # Failed to parse numbers
return False, f"User answer '{{user_answer}}' does not sufficiently match the expected: '{{expected_answer_str}}'."
```
Now, provide *only* the Python code for `validate_query_answer(user_answer)` based on the specific inputs given.
"""
elif task_type == "operational_check":
prompt_step3_template = f"""
You are an expert Magento 2 database analyst and Python programmer.
Your task is to create a Python validation function `validate_operational_state(db_connection)`.
This function will use the provided `db_connection` to perform **new, hardcoded SELECT queries** to verify that the database state aligns with an expected condition or feasibility assessment.
{prompt_core_context}
5. **LLM's Feasibility Summary (this describes the state your function should verify):**
```
{llm_assessment_from_step2_str}
```
**Your Task for "operational_check" type:**
Create a Python function `validate_operational_state(db_connection)`:
* The function must contain **hardcoded SELECT SQL query/queries** that you design. These queries should aim to re-verify the conditions described in the "LLM's Feasibility Summary" and the "Revised Operational Intent".
* It uses the `db_connection` to execute these hardcoded SQLs.
* It then analyzes the results of its own SQLs to determine if the database state is as expected.
* Return `(is_valid, message)`:
* `is_valid` (boolean): `True` if the database state (queried by your hardcoded SQLs) matches the expected conditions.
* `message` (string): Explaining the outcome (e.g., "Verified: Product SKU 'XYZ' exists and is active.", "Verification failed: Order 123 status is 'shipped', not 'pending' as expected for the check.").
* The function must be self-contained (standard imports, `mysql.connector.Error` for db errors are ok).
* Handle potential errors during its own database operations. If `db_connection` is `None` or unusable, it should return `(False, "DB connection not available for validation.")`.
**Example `validate_operational_state` structure:**
```python
from mysql.connector import Error # If you need to catch DB errors
def validate_operational_state(db_connection):
# Revised Intent: "Check if product SKU 'ABC' is in stock (status=1) at source 'default'."
# LLM Feasibility Summary: "Product SKU 'ABC' exists. Its stock status at 'default' is 1 (In Stock)."
if not db_connection or not db_connection.is_connected():
return False, "Database connection not available for validation."
sku_to_check = "ABC" # Hardcoded based on context
source_to_check = "default" # Hardcoded
expected_status = 1 # Hardcoded
try:
cursor = db_connection.cursor(dictionary=True)
# Hardcoded SQL to re-verify the state
query = f"SELECT status FROM inventory_source_item WHERE sku = %s AND source_code = %s"
cursor.execute(query, (sku_to_check, source_to_check))
result = cursor.fetchone()
cursor.close()
if result:
if result['status'] == expected_status:
return True, f"Validation successful: SKU '{{sku_to_check}}' at source '{{source_to_check}}' has status '{{expected_status}}'."
else:
return False, f"Validation failed: SKU '{{sku_to_check}}' at source '{{source_to_check}}' has status '{{result['status']}}', expected '{{expected_status}}'."
else:
return False, f"Validation failed: SKU '{{sku_to_check}}' not found at source '{{source_to_check}}' during validation check."
except Error as e:
return False, f"Database error during validation: {{e}}"
except Exception as ex:
return False, f"Unexpected error during validation: {{ex}}"
```
Now, provide *only* the Python code for the function (`validate_query_answer` or `validate_operational_state`) based on the specific inputs given.
"""
else:
logger.error(f"Unknown task_type '{task_type}' for generating validation function prompt.")
return "# Error: Unknown task_type for validation function generation."
filled_prompt = prompt_step3_template
messages = [
{"role": "system", "content": SYSTEM_PROMPT_TEMPLATE},
{"role": "user", "content": filled_prompt}
]
validation_function_code = call_llm(messages, LLM_MODEL_VALIDATION, temperature=0.1, max_tokens=2500)
if validation_function_code:
clean_code = _clean_llm_python_code_response(validation_function_code) # Use helper
logger.info(f"Successfully generated validation function code for task type '{task_type}'.")
return clean_code
else:
logger.error(f"Failed to generate validation function code for task type '{task_type}'.")
return "# LLM failed to generate validation function code or an error occurred."
def main_workflow():
"""Main orchestrator for the multi-step QA generation (SELECT-only focus)."""
logger.info("=================================================")
logger.info("=== Magento QA Gen (SELECT-Only Info Gathering & Validation) ===")
logger.info("=================================================")
global MAGENTO_SCHEMA_CONTENT
script_dir = ""
try:
script_dir = os.path.dirname(os.path.abspath(__file__))
# schema_file_path = os.path.join(script_dir, "schema_nonempty.txt")
logger.info(f"curated_schema")
schema_file_path = os.path.join(script_dir, "curated_schema.txt")
with open(schema_file_path, "r", encoding="utf-8") as f:
MAGENTO_SCHEMA_CONTENT = f.read()
logger.info(f"Schema loaded successfully from {schema_file_path}")
except FileNotFoundError:
logger.error(f"curated_schema.txt not found at {schema_file_path}. Exiting.")
return
except Exception as e:
logger.error(f"Error loading schema file: {e}. Exiting.")
return
db_connection_main_loop = create_db_connection(DB_HOST, DB_PORT, DB_USER, DB_PASS, DB_NAME)
# This connection is primarily for prep_sql. Validation functions for 'operational_check' will also use it if passed.
if not db_connection_main_loop:
logger.error("Initial DB connection failed. Needed for samples and prep SQL. Exiting.")
return
initialize_system_prompt(db_connection_main_loop, script_dir)
if not SYSTEM_PROMPT_TEMPLATE:
logger.error("System prompt initialization failed. Exiting.")
if db_connection_main_loop and db_connection_main_loop.is_connected(): db_connection_main_loop.close()
return
# Step 1: Generate Initial Tasks and Preparatory SQL
logger.info("--- Starting Step 1: Generate Initial Tasks and Preparatory SQL ---")
initial_tasks = generate_initial_tasks_and_prep_sql(num_tasks=5) # Adjust num_tasks
if not initial_tasks:
logger.error("No initial tasks generated by LLM. Exiting.")
if db_connection_main_loop and db_connection_main_loop.is_connected(): db_connection_main_loop.close()
return
logger.info(f"Step 1 completed. Received {len(initial_tasks)} initial tasks.")
output_filename = f"magento_qa_info_gathering_{datetime.now().strftime('%Y%m%d_%H%M%S')}.jsonl"
try:
with open(output_filename, "w", encoding="utf-8") as outfile:
for i, task_data in enumerate(initial_tasks):
item_num = i + 1
logger.info(f"\nProcessing Item {item_num}/{len(initial_tasks)}: \"{task_data['original_question'][:100]}...\"")
original_question = task_data["original_question"]
task_type = task_data["task_type"]
preparatory_sql_list = task_data["preparatory_sql_list"]
prep_sql_actual_results_list = []
prep_sql_results_repr_list = []
logger.info(f" Executing {len(preparatory_sql_list)} preparatory SQLs for Item {item_num}...")
current_db_conn_for_item = db_connection_main_loop # Use the main loop's connection
for prep_sql_idx, prep_sql in enumerate(preparatory_sql_list):
logger.info(f" Prep SQL {prep_sql_idx+1}: {prep_sql}")
if not current_db_conn_for_item or not current_db_conn_for_item.is_connected():
logger.warning(f"DB connection lost before prep SQL for item {item_num}. Attempting reconnect...")
current_db_conn_for_item = create_db_connection(DB_HOST, DB_PORT, DB_USER, DB_PASS, DB_NAME)
if not current_db_conn_for_item: # If reconnect fails for this item's prep SQLs
db_connection_main_loop = None # Nullify main connection so next item tries fresh
err_msg = "Error: DB connection lost and failed to reconnect during prep SQL execution."
# Fill remaining prep results with error for this item
for _ in range(prep_sql_idx, len(preparatory_sql_list)):
prep_sql_actual_results_list.append(err_msg)
prep_sql_results_repr_list.append(repr(err_msg))
logger.error(f"Failed to reconnect. Skipping rest of prep SQLs for item {item_num}.")
break
current_prep_result = execute_sql_and_get_results(current_db_conn_for_item, prep_sql, f"Prep Q{item_num}.{prep_sql_idx+1}", item_num)
prep_sql_actual_results_list.append(current_prep_result)
prep_sql_results_repr_list.append(repr(current_prep_result))
if isinstance(current_prep_result, str) and current_prep_result.startswith("Error:"):
logger.warning(f" Prep SQL {prep_sql_idx+1} for item {item_num} resulted in error: {current_prep_result}")
if current_db_conn_for_item is not db_connection_main_loop and current_db_conn_for_item is not None:
db_connection_main_loop = current_db_conn_for_item # Update main connection if a new one was made and is good
elif not current_db_conn_for_item: # If connection was lost and couldn't be re-established for this item.
db_connection_main_loop = None
logger.info(f" Finished executing preparatory SQLs for Item {item_num}.")
logger.info(f" Starting Step 2: Refine Question and Assess Info for Item {item_num}...")
llm_assessment_data = refine_question_and_assess_info(
original_question, task_type, preparatory_sql_list, prep_sql_results_repr_list
)
if not llm_assessment_data:
logger.error(f"Failed to get assessment from LLM for Item {item_num}. Skipping validation and saving partial.")
record = { "item_number": item_num, "original_question": original_question, "task_type": task_type,
"preparatory_sql_list": preparatory_sql_list,
"preparatory_sql_actual_results_preview": [str(r)[:200] for r in prep_sql_actual_results_list],
"status": "Failed at LLM assessment step" }
outfile.write(json.dumps(record) + "\n"); outfile.flush()
continue
revised_question = llm_assessment_data["revised_question"]
revision_justification = llm_assessment_data["revision_justification"]
llm_assessment_from_step2_value = ""
if task_type == "query":
llm_assessment_from_step2_value = llm_assessment_data.get("llm_derived_answer", "Error: LLM did not provide derived answer.")
elif task_type == "operational_check":
llm_assessment_from_step2_value = llm_assessment_data.get("llm_feasibility_summary", "Error: LLM did not provide feasibility summary.")
logger.info(f" Starting Step 3: Generate Validation Function for Item {item_num}...")
time.sleep(1)
prep_sql_list_str_for_prompt = "\n".join(preparatory_sql_list)
prep_sql_results_repr_list_str_for_prompt = "[\n" + ",\n".join(f" {r}" for r in prep_sql_results_repr_list) + "\n]"
validation_function_code = generate_validation_function(
revised_question, task_type,
prep_sql_list_str_for_prompt,
prep_sql_results_repr_list_str_for_prompt,
llm_assessment_from_step2_value
)
if not validation_function_code or "# LLM failed" in validation_function_code:
logger.warning(f" Validation function generation failed or was incomplete for Item {item_num}.")
if not validation_function_code:
validation_function_code = "# LLM failed to generate validation function or returned empty."
record = {
"item_number": item_num, "original_question": original_question, "task_type": task_type,
"preparatory_sql_list": preparatory_sql_list,
"preparatory_sql_actual_results_preview": [str(r)[:200] for r in prep_sql_actual_results_list],
"full_preparatory_sql_actual_results_repr": prep_sql_results_repr_list, # Use repr for LLM consistency
"revised_question": revised_question, "revision_justification": revision_justification,
}
if task_type == "query":
record["llm_derived_answer_for_validation_func_gen"] = llm_assessment_from_step2_value
elif task_type == "operational_check":
record["llm_feasibility_summary_for_validation_func_gen"] = llm_assessment_from_step2_value
record["python_validation_function"] = validation_function_code
outfile.write(json.dumps(record) + "\n")
outfile.flush()
logger.info(f"Record {item_num} written to {output_filename}")
if i < len(initial_tasks) - 1:
llm_call_delay = int(os.environ.get("LLM_CALL_DELAY_SECONDS", "5"))
logger.info(f"Waiting {llm_call_delay} seconds before next item...")
time.sleep(llm_call_delay)
except Exception as e:
logger.error(f"An critical error occurred in the main workflow: {e}", exc_info=True)
finally:
if db_connection_main_loop and db_connection_main_loop.is_connected(): # Check the correct connection variable
db_connection_main_loop.close()
logger.info("Main database connection closed at the end of the workflow.")
elif db_connection_main_loop is None: # if it was nullified due to irrecoverable loss
logger.info("Main database connection was lost and not re-established.")
logger.info(f"Workflow finished. Log file: {log_file_name}")
if __name__ == "__main__":
if OPENAI_API_KEY == "YOUR_OPENAI_API_KEY_FALLBACK":
print("CRITICAL: OpenAI API key is not set. Please set the OPENAI_API_KEY environment variable or update the script.")
logger.critical("OpenAI API key is not set. Please set the OPENAI_API_KEY environment variable or update the script.")
else:
main_workflow()
logger.info(f"Workflow finished. Log file: {log_file_name}")
"""
**Before Running:**
1. **`schema_nonempty.txt`**: **CRITICAL:** Place your full Magento `schema_nonempty.txt` file in the same directory as this Python script. The script now tries to load it.
2. **OpenAI API Key:**
* **Best Method:** Set it as an environment variable: `export OPENAI_API_KEY="sk-..."`
* Alternatively, replace `"YOUR_OPENAI_API_KEY_FALLBACK"` in the script, but this is less secure.
3. **`OPENAI_BASE_URL`**: If you are using a proxy or a non-standard OpenAI endpoint, update this. Otherwise, the default `https://api.openai.com/v1` should work for official OpenAI.
4. **Database Credentials:** Ensure `DB_HOST`, `DB_PORT`, `DB_USER`, `DB_PASS`, `DB_NAME` are correct.
5. **Install Libraries:** `pip install openai mysql-connector-python pandas`
6. **LLM Models:** `LLM_MODEL_GENERATION` and `LLM_MODEL_VALIDATION` are set to `gpt-4-turbo-preview`. You might want to use `gpt-3.5-turbo` for `LLM_MODEL_VALIDATION` to save costs/time if its quality is sufficient for generating the validation functions.
7. **Rate Limiting:** The `time.sleep(5)` (now configurable via `LLM_CALL_DELAY_SECONDS` env var, defaulting to 5) is a very basic rate limiter. If you have higher API limits or make fewer calls, you can adjust this.
**Key Improvements in this Version:**
* **Schema Loading:** The script now explicitly loads `schema_nonempty.txt` and incorporates its content into the system prompt.
* **Environment Variables:** Encourages using environment variables for API keys.
* **Robust LLM JSON Parsing:** Added more cleaning for the JSON response from the LLM.
* **Error Handling:** More `try-except` blocks, especially around LLM calls and database operations.
* **DML Handling in `execute_sql_and_get_results`:**
* Detects DML (UPDATE, INSERT, DELETE) and DDL (CREATE, ALTER, DROP).
* Commits transactions for DML/DDL.
* Rolls back DML/DDL on error.
* Returns "Rows affected: X" for DML.
* **Stringification of Results:** `datetime` and `decimal.Decimal` objects from `SELECT` queries are converted to strings before being passed to `repr()` for the LLM prompt. This makes the LLM's job of understanding the "SQL Execution Result" string easier and more consistent.
* **Logging:** Enhanced logging for better traceability, including logging the prompts sent to the LLM (at DEBUG level) and token usage.
* **Connection Management:** Improved checks for database connection status and attempts to reconnect if lost.
* **Clearer Prompts:** Refined prompts for the LLM, especially for generating validation functions, to be more explicit about input formats and expected output.
* **Configurable LLM Call Delay:** Added an environment variable `LLM_CALL_DELAY_SECONDS` for easier adjustment of the delay between LLM calls.
* **Port as Integer:** Ensured `DB_PORT` is cast to an integer for `mysql.connector.connect`.
* **Full Result for LLM:** The `full_sql_execution_result_repr_for_llm` is now included in the JSONL, which can be useful for debugging or if you need to re-prompt the LLM for a specific validation function.
This script is now much more robust and production-ready for your task. Remember to monitor the LLM costs and API rate limits.
"""

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,405 @@
-- Essential Magento 2 Core Table Schemas (Curated)
-- For LLM Question Generation Context
-- ========= Product Related =========
-- catalog_product_entity: Core product table
DROP TABLE IF EXISTS `catalog_product_entity`;
CREATE TABLE `catalog_product_entity` (
`entity_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Entity ID',
`attribute_set_id` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'Attribute Set ID',
`type_id` varchar(32) NOT NULL DEFAULT 'simple' COMMENT 'Type ID',
`sku` varchar(64) NOT NULL COMMENT 'SKU',
`has_options` smallint(6) NOT NULL DEFAULT 0 COMMENT 'Has Options',
`required_options` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'Required Options',
`created_at` timestamp NOT NULL DEFAULT current_timestamp() COMMENT 'Creation Time',
`updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp() COMMENT 'Update Time',
PRIMARY KEY (`entity_id`),
KEY `CATALOG_PRODUCT_ENTITY_SKU` (`sku`)
) ENGINE=InnoDB COMMENT='Catalog Product Table';
-- eav_attribute: Defines attributes (used by products, categories, customers)
DROP TABLE IF EXISTS `eav_attribute`;
CREATE TABLE `eav_attribute` (
`attribute_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Attribute ID',
`entity_type_id` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'Entity Type ID',
`attribute_code` varchar(255) NOT NULL COMMENT 'Attribute Code',
`backend_type` varchar(8) NOT NULL DEFAULT 'static' COMMENT 'Backend Type',
`frontend_input` varchar(50) DEFAULT NULL COMMENT 'Frontend Input',
`frontend_label` varchar(255) DEFAULT NULL COMMENT 'Frontend Label',
`is_required` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'Defines Is Required',
`is_user_defined` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'Defines Is User Defined',
PRIMARY KEY (`attribute_id`),
UNIQUE KEY `EAV_ATTRIBUTE_ENTITY_TYPE_ID_ATTRIBUTE_CODE` (`entity_type_id`,`attribute_code`)
) ENGINE=InnoDB COMMENT='Eav Attribute';
-- eav_entity_type: Defines entity types for EAV
DROP TABLE IF EXISTS `eav_entity_type`;
CREATE TABLE `eav_entity_type` (
`entity_type_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Entity Type ID',
`entity_type_code` varchar(50) NOT NULL COMMENT 'Entity Type Code',
`entity_table` varchar(255) DEFAULT NULL COMMENT 'Entity Table',
PRIMARY KEY (`entity_type_id`)
) ENGINE=InnoDB COMMENT='Eav Entity Type';
-- catalog_product_entity_varchar: Product Varchar Attributes (e.g., name)
DROP TABLE IF EXISTS `catalog_product_entity_varchar`;
CREATE TABLE `catalog_product_entity_varchar` (
`value_id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Value ID',
`attribute_id` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'Attribute ID',
`store_id` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'Store ID',
`entity_id` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'Entity ID',
`value` varchar(255) DEFAULT NULL COMMENT 'Value',
PRIMARY KEY (`value_id`),
UNIQUE KEY `CATALOG_PRODUCT_ENTITY_VARCHAR_ENTITY_ID_ATTRIBUTE_ID_STORE_ID` (`entity_id`,`attribute_id`,`store_id`),
CONSTRAINT `CAT_PRD_ENTT_VCHR_ATTR_ID_EAV_ATTR_ATTR_ID` FOREIGN KEY (`attribute_id`) REFERENCES `eav_attribute` (`attribute_id`) ON DELETE CASCADE,
CONSTRAINT `CAT_PRD_ENTT_VCHR_ENTT_ID_CAT_PRD_ENTT_ENTT_ID` FOREIGN KEY (`entity_id`) REFERENCES `catalog_product_entity` (`entity_id`) ON DELETE CASCADE
) ENGINE=InnoDB COMMENT='Catalog Product Varchar Attribute Backend Table';
-- catalog_product_entity_int: Product Integer Attributes (e.g., status, visibility)
DROP TABLE IF EXISTS `catalog_product_entity_int`;
CREATE TABLE `catalog_product_entity_int` (
`value_id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Value ID',
`attribute_id` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'Attribute ID',
`store_id` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'Store ID',
`entity_id` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'Entity ID',
`value` int(11) DEFAULT NULL COMMENT 'Value',
PRIMARY KEY (`value_id`),
UNIQUE KEY `CATALOG_PRODUCT_ENTITY_INT_ENTITY_ID_ATTRIBUTE_ID_STORE_ID` (`entity_id`,`attribute_id`,`store_id`),
CONSTRAINT `CAT_PRD_ENTT_INT_ATTR_ID_EAV_ATTR_ATTR_ID` FOREIGN KEY (`attribute_id`) REFERENCES `eav_attribute` (`attribute_id`) ON DELETE CASCADE,
CONSTRAINT `CAT_PRD_ENTT_INT_ENTT_ID_CAT_PRD_ENTT_ENTT_ID` FOREIGN KEY (`entity_id`) REFERENCES `catalog_product_entity` (`entity_id`) ON DELETE CASCADE
) ENGINE=InnoDB COMMENT='Catalog Product Integer Attribute Backend Table';
-- catalog_product_entity_decimal: Product Decimal Attributes (e.g., price, weight)
DROP TABLE IF EXISTS `catalog_product_entity_decimal`;
CREATE TABLE `catalog_product_entity_decimal` (
`value_id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Value ID',
`attribute_id` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'Attribute ID',
`store_id` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'Store ID',
`entity_id` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'Entity ID',
`value` decimal(20,6) DEFAULT NULL COMMENT 'Value',
PRIMARY KEY (`value_id`),
UNIQUE KEY `CATALOG_PRODUCT_ENTITY_DECIMAL_ENTITY_ID_ATTRIBUTE_ID_STORE_ID` (`entity_id`,`attribute_id`,`store_id`),
CONSTRAINT `CAT_PRD_ENTT_DEC_ATTR_ID_EAV_ATTR_ATTR_ID` FOREIGN KEY (`attribute_id`) REFERENCES `eav_attribute` (`attribute_id`) ON DELETE CASCADE,
CONSTRAINT `CAT_PRD_ENTT_DEC_ENTT_ID_CAT_PRD_ENTT_ENTT_ID` FOREIGN KEY (`entity_id`) REFERENCES `catalog_product_entity` (`entity_id`) ON DELETE CASCADE
) ENGINE=InnoDB COMMENT='Catalog Product Decimal Attribute Backend Table';
-- catalog_product_index_price: Indexed product prices (important for frontend)
DROP TABLE IF EXISTS `catalog_product_index_price`;
CREATE TABLE `catalog_product_index_price` (
`entity_id` int(10) unsigned NOT NULL COMMENT 'Entity ID',
`customer_group_id` int(10) unsigned NOT NULL COMMENT 'Customer Group ID',
`website_id` smallint(5) unsigned NOT NULL COMMENT 'Website ID',
`price` decimal(20,6) DEFAULT NULL COMMENT 'Price',
`final_price` decimal(20,6) DEFAULT NULL COMMENT 'Final Price',
`min_price` decimal(20,6) DEFAULT NULL COMMENT 'Min Price',
`max_price` decimal(20,6) DEFAULT NULL COMMENT 'Max Price',
PRIMARY KEY (`entity_id`,`customer_group_id`,`website_id`)
) ENGINE=InnoDB COMMENT='Catalog Product Price Index Table';
-- ========= Category Related =========
-- catalog_category_entity: Core category table
DROP TABLE IF EXISTS `catalog_category_entity`;
CREATE TABLE `catalog_category_entity` (
`entity_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Entity ID',
`parent_id` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'Parent Category ID',
`path` varchar(255) NOT NULL COMMENT 'Tree Path',
`position` int(11) NOT NULL COMMENT 'Position',
`level` int(11) NOT NULL DEFAULT 0 COMMENT 'Tree Level',
`children_count` int(11) NOT NULL COMMENT 'Child Count',
`created_at` timestamp NOT NULL DEFAULT current_timestamp() COMMENT 'Creation Time',
`updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp() COMMENT 'Update Time',
PRIMARY KEY (`entity_id`),
KEY `CATALOG_CATEGORY_ENTITY_PATH` (`path`)
) ENGINE=InnoDB COMMENT='Catalog Category Table';
-- catalog_category_entity_varchar: Category Varchar Attributes (e.g., name)
DROP TABLE IF EXISTS `catalog_category_entity_varchar`;
CREATE TABLE `catalog_category_entity_varchar` (
`value_id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Value ID',
`attribute_id` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'Attribute ID',
`store_id` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'Store ID',
`entity_id` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'Entity ID',
`value` varchar(255) DEFAULT NULL COMMENT 'Value',
PRIMARY KEY (`value_id`),
UNIQUE KEY `CATALOG_CATEGORY_ENTITY_VARCHAR_ENTITY_ID_ATTRIBUTE_ID_STORE_ID` (`entity_id`,`attribute_id`,`store_id`),
CONSTRAINT `CAT_CTGR_ENTT_VCHR_ATTR_ID_EAV_ATTR_ATTR_ID` FOREIGN KEY (`attribute_id`) REFERENCES `eav_attribute` (`attribute_id`) ON DELETE CASCADE,
CONSTRAINT `CAT_CTGR_ENTT_VCHR_ENTT_ID_CAT_CTGR_ENTT_ENTT_ID` FOREIGN KEY (`entity_id`) REFERENCES `catalog_category_entity` (`entity_id`) ON DELETE CASCADE
) ENGINE=InnoDB COMMENT='Catalog Category Varchar Attribute Backend Table';
-- catalog_category_product: Links products to categories
DROP TABLE IF EXISTS `catalog_category_product`;
CREATE TABLE `catalog_category_product` (
`entity_id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Entity ID', -- This is just a link table ID, not product or category ID
`category_id` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'Category ID',
`product_id` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'Product ID',
`position` int(11) NOT NULL DEFAULT 0 COMMENT 'Position',
PRIMARY KEY (`entity_id`,`category_id`,`product_id`), -- Modified PK for clarity if entity_id is just an auto_increment
UNIQUE KEY `CATALOG_CATEGORY_PRODUCT_CATEGORY_ID_PRODUCT_ID` (`category_id`,`product_id`),
CONSTRAINT `CAT_CTGR_PRD_CTGR_ID_CAT_CTGR_ENTT_ENTT_ID` FOREIGN KEY (`category_id`) REFERENCES `catalog_category_entity` (`entity_id`) ON DELETE CASCADE,
CONSTRAINT `CAT_CTGR_PRD_PRD_ID_CAT_PRD_ENTT_ENTT_ID` FOREIGN KEY (`product_id`) REFERENCES `catalog_product_entity` (`entity_id`) ON DELETE CASCADE
) ENGINE=InnoDB COMMENT='Catalog Product To Category Linkage Table';
-- ========= Inventory Related =========
-- inventory_source_item: Multi-Source Inventory (MSI) stock levels
DROP TABLE IF EXISTS `inventory_source_item`;
CREATE TABLE `inventory_source_item` (
`source_item_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`source_code` varchar(255) NOT NULL COMMENT 'Usually "default" for single source',
`sku` varchar(64) NOT NULL,
`quantity` decimal(12,4) NOT NULL DEFAULT 0.0000,
`status` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT '0 = Out of Stock, 1 = In Stock',
PRIMARY KEY (`source_item_id`),
UNIQUE KEY `INVENTORY_SOURCE_ITEM_SOURCE_CODE_SKU` (`source_code`,`sku`)
) ENGINE=InnoDB COMMENT='Inventory Source Item (MSI)';
-- cataloginventory_stock_status: Indexed stock status (often used by frontend)
DROP TABLE IF EXISTS `cataloginventory_stock_status`;
CREATE TABLE `cataloginventory_stock_status` (
`product_id` int(10) unsigned NOT NULL COMMENT 'Product ID',
`website_id` smallint(5) unsigned NOT NULL COMMENT 'Website ID',
`stock_id` smallint(5) unsigned NOT NULL COMMENT 'Stock ID (usually 1 for default)',
`qty` decimal(12,4) NOT NULL DEFAULT 0.0000 COMMENT 'Qty',
`stock_status` smallint(5) unsigned NOT NULL COMMENT 'Stock Status (0=Out of Stock, 1=In Stock)',
PRIMARY KEY (`product_id`,`website_id`,`stock_id`)
) ENGINE=InnoDB COMMENT='Cataloginventory Stock Status';
-- ========= Customer Related =========
-- customer_entity: Core customer table
DROP TABLE IF EXISTS `customer_entity`;
CREATE TABLE `customer_entity` (
`entity_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Entity ID',
`website_id` smallint(5) unsigned DEFAULT NULL COMMENT 'Website ID',
`email` varchar(255) DEFAULT NULL COMMENT 'Email',
`group_id` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'Group ID',
`created_at` timestamp NOT NULL DEFAULT current_timestamp() COMMENT 'Created At',
`updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp() COMMENT 'Updated At',
`is_active` smallint(5) unsigned NOT NULL DEFAULT 1 COMMENT 'Is Active',
`firstname` varchar(255) DEFAULT NULL COMMENT 'First Name',
`lastname` varchar(255) DEFAULT NULL COMMENT 'Last Name',
`default_billing` int(10) unsigned DEFAULT NULL COMMENT 'Default Billing Address ID',
`default_shipping` int(10) unsigned DEFAULT NULL COMMENT 'Default Shipping Address ID',
PRIMARY KEY (`entity_id`),
UNIQUE KEY `CUSTOMER_ENTITY_EMAIL_WEBSITE_ID` (`email`,`website_id`)
) ENGINE=InnoDB COMMENT='Customer Entity';
-- customer_address_entity: Customer addresses
DROP TABLE IF EXISTS `customer_address_entity`;
CREATE TABLE `customer_address_entity` (
`entity_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Entity ID',
`parent_id` int(10) unsigned DEFAULT NULL COMMENT 'Customer ID (Parent)',
`created_at` timestamp NOT NULL DEFAULT current_timestamp() COMMENT 'Created At',
`updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp() COMMENT 'Updated At',
`city` varchar(255) NOT NULL COMMENT 'City',
`country_id` varchar(255) NOT NULL COMMENT 'Country',
`firstname` varchar(255) NOT NULL COMMENT 'First Name',
`lastname` varchar(255) NOT NULL COMMENT 'Last Name',
`postcode` varchar(255) DEFAULT NULL COMMENT 'Zip/Postal Code',
`region_id` int(10) unsigned DEFAULT NULL COMMENT 'State/Province ID',
`street` text NOT NULL COMMENT 'Street Address',
`telephone` varchar(255) NOT NULL COMMENT 'Phone Number',
PRIMARY KEY (`entity_id`),
KEY `CUSTOMER_ADDRESS_ENTITY_PARENT_ID` (`parent_id`),
CONSTRAINT `CUSTOMER_ADDRESS_ENTITY_PARENT_ID_CUSTOMER_ENTITY_ENTITY_ID` FOREIGN KEY (`parent_id`) REFERENCES `customer_entity` (`entity_id`) ON DELETE CASCADE
) ENGINE=InnoDB COMMENT='Customer Address Entity';
-- customer_group: Defines customer groups
DROP TABLE IF EXISTS `customer_group`;
CREATE TABLE `customer_group` (
`customer_group_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`customer_group_code` varchar(32) NOT NULL COMMENT 'Customer Group Code',
`tax_class_id` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'Tax Class ID',
PRIMARY KEY (`customer_group_id`)
) ENGINE=InnoDB COMMENT='Customer Group';
-- ========= Sales (Order) Related =========
-- sales_order: Core order table
DROP TABLE IF EXISTS `sales_order`;
CREATE TABLE `sales_order` (
`entity_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Entity ID',
`state` varchar(32) DEFAULT NULL COMMENT 'State (e.g. new, processing, complete)',
`status` varchar(32) DEFAULT NULL COMMENT 'Status (e.g. pending, processing, complete, canceled)',
`store_id` smallint(5) unsigned DEFAULT NULL COMMENT 'Store ID',
`customer_id` int(10) unsigned DEFAULT NULL COMMENT 'Customer ID',
`grand_total` decimal(20,4) DEFAULT NULL COMMENT 'Grand Total in order currency',
`base_grand_total` decimal(20,4) DEFAULT NULL COMMENT 'Base Grand Total in base currency',
`subtotal` decimal(20,4) DEFAULT NULL COMMENT 'Subtotal',
`base_subtotal` decimal(20,4) DEFAULT NULL COMMENT 'Base Subtotal',
`shipping_amount` decimal(20,4) DEFAULT NULL COMMENT 'Shipping Amount',
`base_shipping_amount` decimal(20,4) DEFAULT NULL COMMENT 'Base Shipping Amount',
`tax_amount` decimal(20,4) DEFAULT NULL COMMENT 'Tax Amount',
`base_tax_amount` decimal(20,4) DEFAULT NULL COMMENT 'Base Tax Amount',
`discount_amount` decimal(20,4) DEFAULT NULL COMMENT 'Discount Amount',
`base_discount_amount` decimal(20,4) DEFAULT NULL COMMENT 'Base Discount Amount',
`customer_email` varchar(128) DEFAULT NULL COMMENT 'Customer Email',
`customer_firstname` varchar(128) DEFAULT NULL COMMENT 'Customer Firstname',
`customer_lastname` varchar(128) DEFAULT NULL COMMENT 'Customer Lastname',
`customer_group_id` int(11) DEFAULT NULL,
`increment_id` varchar(50) DEFAULT NULL COMMENT 'Order Increment ID (human readable)',
`created_at` timestamp NOT NULL DEFAULT current_timestamp() COMMENT 'Created At',
`updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp() COMMENT 'Updated At',
PRIMARY KEY (`entity_id`),
UNIQUE KEY `SALES_ORDER_INCREMENT_ID_STORE_ID` (`increment_id`,`store_id`),
KEY `SALES_ORDER_CUSTOMER_ID` (`customer_id`),
CONSTRAINT `SALES_ORDER_CUSTOMER_ID_CUSTOMER_ENTITY_ENTITY_ID` FOREIGN KEY (`customer_id`) REFERENCES `customer_entity` (`entity_id`) ON DELETE SET NULL
) ENGINE=InnoDB COMMENT='Sales Flat Order';
-- sales_order_item: Items within an order
DROP TABLE IF EXISTS `sales_order_item`;
CREATE TABLE `sales_order_item` (
`item_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Item ID',
`order_id` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'Order ID',
`product_id` int(10) unsigned DEFAULT NULL COMMENT 'Product ID',
`store_id` smallint(5) unsigned DEFAULT NULL COMMENT 'Store ID',
`sku` varchar(255) DEFAULT NULL COMMENT 'Sku',
`name` varchar(255) DEFAULT NULL COMMENT 'Name',
`qty_ordered` decimal(12,4) DEFAULT 0.0000 COMMENT 'Qty Ordered',
`price` decimal(12,4) NOT NULL DEFAULT 0.0000 COMMENT 'Price in order currency',
`base_price` decimal(12,4) NOT NULL DEFAULT 0.0000 COMMENT 'Base Price in base currency',
`row_total` decimal(20,4) NOT NULL DEFAULT 0.0000 COMMENT 'Row Total',
`base_row_total` decimal(20,4) NOT NULL DEFAULT 0.0000 COMMENT 'Base Row Total',
`discount_amount` decimal(20,4) DEFAULT 0.0000 COMMENT 'Discount Amount',
`base_discount_amount` decimal(20,4) DEFAULT 0.0000 COMMENT 'Base Discount Amount',
`tax_amount` decimal(20,4) DEFAULT 0.0000 COMMENT 'Tax Amount',
`base_tax_amount` decimal(20,4) DEFAULT 0.0000 COMMENT 'Base Tax Amount',
PRIMARY KEY (`item_id`),
KEY `SALES_ORDER_ITEM_ORDER_ID` (`order_id`),
CONSTRAINT `SALES_ORDER_ITEM_ORDER_ID_SALES_ORDER_ENTITY_ID` FOREIGN KEY (`order_id`) REFERENCES `sales_order` (`entity_id`) ON DELETE CASCADE
) ENGINE=InnoDB COMMENT='Sales Flat Order Item';
-- sales_order_payment: Payment information for an order
DROP TABLE IF EXISTS `sales_order_payment`;
CREATE TABLE `sales_order_payment` (
`entity_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Entity ID',
`parent_id` int(10) unsigned NOT NULL COMMENT 'Order ID (Parent)',
`method` varchar(128) DEFAULT NULL COMMENT 'Payment Method Code',
`amount_ordered` decimal(20,4) DEFAULT NULL COMMENT 'Amount Ordered',
`amount_paid` decimal(20,4) DEFAULT NULL COMMENT 'Amount Paid',
`cc_last_4` varchar(100) DEFAULT NULL COMMENT 'Cc Last 4 (if applicable)',
`cc_type` varchar(32) DEFAULT NULL COMMENT 'Cc Type (if applicable)',
PRIMARY KEY (`entity_id`),
KEY `SALES_ORDER_PAYMENT_PARENT_ID` (`parent_id`),
CONSTRAINT `SALES_ORDER_PAYMENT_PARENT_ID_SALES_ORDER_ENTITY_ID` FOREIGN KEY (`parent_id`) REFERENCES `sales_order` (`entity_id`) ON DELETE CASCADE
) ENGINE=InnoDB COMMENT='Sales Flat Order Payment';
-- sales_order_grid: Denormalized order data for admin grid
DROP TABLE IF EXISTS `sales_order_grid`;
CREATE TABLE `sales_order_grid` (
`entity_id` int(10) unsigned NOT NULL COMMENT 'Entity ID (Order ID)',
`status` varchar(32) DEFAULT NULL COMMENT 'Status',
`store_id` smallint(5) unsigned DEFAULT NULL COMMENT 'Store ID',
`increment_id` varchar(50) DEFAULT NULL COMMENT 'Increment ID',
`customer_name` varchar(255) DEFAULT NULL COMMENT 'Customer Name',
`customer_email` varchar(255) DEFAULT NULL COMMENT 'Customer Email',
`grand_total` decimal(20,4) DEFAULT NULL COMMENT 'Grand Total',
`base_grand_total` decimal(20,4) DEFAULT NULL COMMENT 'Base Grand Total',
`created_at` timestamp NULL DEFAULT NULL COMMENT 'Created At',
`billing_name` varchar(255) DEFAULT NULL COMMENT 'Billing Name',
`shipping_name` varchar(255) DEFAULT NULL COMMENT 'Shipping Name',
PRIMARY KEY (`entity_id`),
UNIQUE KEY `SALES_ORDER_GRID_INCREMENT_ID_STORE_ID` (`increment_id`,`store_id`)
) ENGINE=InnoDB COMMENT='Sales Flat Order Grid';
-- ========= Review Related =========
-- review: Core review table
DROP TABLE IF EXISTS `review`;
CREATE TABLE `review` (
`review_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Review ID',
`created_at` timestamp NOT NULL DEFAULT current_timestamp() COMMENT 'Review create date',
`entity_pk_value` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'Product ID (usually)',
`status_id` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'Status code (e.g. pending, approved)',
PRIMARY KEY (`review_id`),
KEY `REVIEW_ENTITY_PK_VALUE` (`entity_pk_value`)
) ENGINE=InnoDB COMMENT='Review base information';
-- review_detail: Details of the review
DROP TABLE IF EXISTS `review_detail`;
CREATE TABLE `review_detail` (
`detail_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Review detail ID',
`review_id` bigint(20) unsigned NOT NULL DEFAULT 0 COMMENT 'Review ID',
`store_id` smallint(5) unsigned DEFAULT 0 COMMENT 'Store ID',
`title` varchar(255) NOT NULL COMMENT 'Title',
`detail` text NOT NULL COMMENT 'Detail description',
`nickname` varchar(128) NOT NULL COMMENT 'User nickname',
`customer_id` int(10) unsigned DEFAULT NULL COMMENT 'Customer ID',
PRIMARY KEY (`detail_id`),
KEY `REVIEW_DETAIL_REVIEW_ID` (`review_id`),
CONSTRAINT `REVIEW_DETAIL_REVIEW_ID_REVIEW_REVIEW_ID` FOREIGN KEY (`review_id`) REFERENCES `review` (`review_id`) ON DELETE CASCADE
) ENGINE=InnoDB COMMENT='Review detail information';
-- review_status: Defines review statuses
DROP TABLE IF EXISTS `review_status`;
CREATE TABLE `review_status` (
`status_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Status ID',
`status_code` varchar(32) NOT NULL COMMENT 'Status code (e.g. Pending, Approved, Not Approved)',
PRIMARY KEY (`status_id`)
) ENGINE=InnoDB COMMENT='Review statuses';
-- ========= Store Configuration =========
-- store: Defines store views
DROP TABLE IF EXISTS `store`;
CREATE TABLE `store` (
`store_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Store ID',
`code` varchar(32) DEFAULT NULL COMMENT 'Code',
`website_id` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'Website ID',
`group_id` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'Group ID',
`name` varchar(255) NOT NULL COMMENT 'Store Name',
`is_active` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'Store Activity',
PRIMARY KEY (`store_id`),
UNIQUE KEY `STORE_CODE` (`code`)
) ENGINE=InnoDB COMMENT='Stores';
-- store_website: Defines websites
DROP TABLE IF EXISTS `store_website`;
CREATE TABLE `store_website` (
`website_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Website ID',
`code` varchar(32) DEFAULT NULL COMMENT 'Code',
`name` varchar(64) DEFAULT NULL COMMENT 'Website Name',
`default_group_id` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'Default Group ID',
PRIMARY KEY (`website_id`),
UNIQUE KEY `STORE_WEBSITE_CODE` (`code`)
) ENGINE=InnoDB COMMENT='Websites';
-- core_config_data: Stores system configuration values
DROP TABLE IF EXISTS `core_config_data`;
CREATE TABLE `core_config_data` (
`config_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Config ID',
`scope` varchar(8) NOT NULL DEFAULT 'default' COMMENT 'Config Scope (default, websites, stores)',
`scope_id` int(11) NOT NULL DEFAULT 0 COMMENT 'Config Scope ID',
`path` varchar(255) NOT NULL DEFAULT 'general' COMMENT 'Config Path (e.g. web/seo/use_rewrites)',
`value` text DEFAULT NULL COMMENT 'Config Value',
`updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp() COMMENT 'Updated At',
PRIMARY KEY (`config_id`),
UNIQUE KEY `CORE_CONFIG_DATA_SCOPE_SCOPE_ID_PATH` (`scope`,`scope_id`,`path`)
) ENGINE=InnoDB COMMENT='Config Data';
-- ========= URL Rewrites =========
-- url_rewrite: Manages URL rewrites for SEO
DROP TABLE IF EXISTS `url_rewrite`;
CREATE TABLE `url_rewrite` (
`url_rewrite_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Rewrite ID',
`entity_type` varchar(32) NOT NULL COMMENT 'Entity type code (e.g. product, category, cms-page)',
`entity_id` int(10) unsigned NOT NULL COMMENT 'Entity ID',
`request_path` varchar(255) DEFAULT NULL COMMENT 'Request Path (SEO friendly URL)',
`target_path` varchar(255) DEFAULT NULL COMMENT 'Target Path (internal Magento path)',
`redirect_type` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'Redirect Type (0=No, 301, 302)',
`store_id` smallint(5) unsigned NOT NULL COMMENT 'Store ID',
`is_autogenerated` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'Is rewrite generated automatically flag',
PRIMARY KEY (`url_rewrite_id`),
UNIQUE KEY `URL_REWRITE_REQUEST_PATH_STORE_ID` (`request_path`,`store_id`)
) ENGINE=InnoDB COMMENT='Url Rewrites';
-- Note: Some FOREIGN KEY constraints referencing tables not included in this curated list have been removed or commented out
-- to ensure this curated schema can be loaded independently if needed for testing,
-- or that the LLM doesn't get confused by missing references in this specific context.
-- The original schema provided has the full set of constraints.

View File

@@ -0,0 +1,5 @@
{"item_number": 1, "original_question": "What is the average rating of product with SKU '24-MB01' across all stores?", "task_type": "query", "preparatory_sql_list": ["SELECT entity_id FROM catalog_product_entity WHERE sku = '24-MB01';", "SELECT AVG(percent) AS average_rating FROM rating_option_vote WHERE entity_pk_value = (SELECT entity_id FROM catalog_product_entity WHERE sku = '24-MB01');"], "preparatory_sql_actual_results_preview": ["[{'entity_id': 1}]", "[{'average_rating': '50.0000'}]"], "full_preparatory_sql_actual_results_repr": ["[{'entity_id': 1}]", "[{'average_rating': '50.0000'}]"], "revised_question": "What is the average rating of product with SKU '24-MB01' across all stores?", "revision_justification": "The original question was specific and valid. The preparatory SQLs confirmed the existence of the product with SKU '24-MB01' and provided the average rating, allowing for a direct answer.", "llm_derived_answer_for_validation_func_gen": "The average rating of the product with SKU '24-MB01' across all stores is 50.00.", "python_validation_function": "def validate_query_answer(user_answer):\n # Based on LLM's Derived Answer: \"The average rating of the product with SKU '24-MB01' across all stores is 50.00.\"\n expected_answer_str = \"The average rating of the product with SKU '24-MB01' across all stores is 50.00.\"\n\n # Normalize both expected and user answers for comparison\n normalized_user_answer = user_answer.strip().lower()\n normalized_expected_answer = expected_answer_str.strip().lower()\n\n # Compare normalized strings\n if normalized_user_answer == normalized_expected_answer:\n return True, \"User answer matches the expected answer.\"\n else:\n return False, f\"User answer '{user_answer}' does not match the expected: '{expected_answer_str}'.\""}
{"item_number": 2, "original_question": "Intent: Update the customer group of customer with email 'jane.doe@hotmail.com' to 'Retailer'.", "task_type": "operational_check", "preparatory_sql_list": ["SELECT entity_id, email FROM customer_entity WHERE email = 'jane.doe@hotmail.com';", "SELECT customer_group_id FROM customer_group WHERE customer_group_code = 'Retailer';", "SELECT group_id FROM customer_entity WHERE email = 'jane.doe@hotmail.com';"], "preparatory_sql_actual_results_preview": ["[{'entity_id': 3, 'email': 'jane.doe@hotmail.com'}]", "[{'customer_group_id': 3}]", "[{'group_id': 1}]"], "full_preparatory_sql_actual_results_repr": ["[{'entity_id': 3, 'email': 'jane.doe@hotmail.com'}]", "[{'customer_group_id': 3}]", "[{'group_id': 1}]"], "revised_question": "Intent: Update the customer group of customer with email 'jane.doe@hotmail.com' to 'Retailer'.", "revision_justification": "The original question was specific and valid. Prep SQLs confirmed the existence of the customer and the target customer group, making the intended operation feasible.", "llm_feasibility_summary_for_validation_func_gen": "Customer with email 'jane.doe@hotmail.com' exists with entity_id 3 and is currently in customer group 1. The target customer group 'Retailer' exists with customer_group_id 3. The update is feasible.", "python_validation_function": "# LLM failed to generate validation function code or an error occurred."}
{"item_number": 3, "original_question": "What products are currently out of stock in the default inventory source?", "task_type": "query", "preparatory_sql_list": ["SELECT sku, quantity FROM inventory_source_item WHERE source_code = 'default' AND status = 0;", "SELECT sku, name FROM catalog_product_entity INNER JOIN catalog_product_entity_varchar ON catalog_product_entity.entity_id = catalog_product_entity_varchar.entity_id WHERE catalog_product_entity.sku IN (SELECT sku FROM inventory_source_item WHERE source_code = 'default' AND status = 0) AND catalog_product_entity_varchar.attribute_id = (SELECT attribute_id FROM eav_attribute WHERE attribute_code = 'name' AND entity_type_id = (SELECT entity_type_id FROM eav_entity_type WHERE entity_type_code = 'catalog_product'));"], "preparatory_sql_actual_results_preview": ["[]", "Error: 1054 (42S22): Unknown column 'name' in 'field list'"], "full_preparatory_sql_actual_results_repr": ["[]", "\"Error: 1054 (42S22): Unknown column 'name' in 'field list'\""], "revised_question": "What products are currently out of stock in the default inventory source?", "revision_justification": "The original question remains valid as it accurately reflects the inquiry about out-of-stock products. The preparatory SQL results indicate that no products are out of stock in the default inventory source, and the second query encountered an error due to a missing column, which does not affect the answer derived from the first query.", "llm_derived_answer_for_validation_func_gen": "There are no products currently out of stock in the default inventory source.", "python_validation_function": "# LLM failed to generate validation function code or an error occurred."}
{"item_number": 4, "original_question": "What are the top 5 best-selling products in the last month?", "task_type": "query", "preparatory_sql_list": ["SELECT product_id, SUM(qty_ordered) AS total_qty FROM sales_bestsellers_aggregated_monthly WHERE period >= DATE_SUB(CURDATE(), INTERVAL 1 MONTH) GROUP BY product_id ORDER BY total_qty DESC LIMIT 5;", "SELECT cpe.sku, cpev.value AS product_name FROM catalog_product_entity cpe INNER JOIN catalog_product_entity_varchar cpev ON cpe.entity_id = cpev.entity_id WHERE cpe.entity_id IN (SELECT product_id FROM sales_bestsellers_aggregated_monthly WHERE period >= DATE_SUB(CURDATE(), INTERVAL 1 MONTH) GROUP BY product_id ORDER BY SUM(qty_ordered) DESC LIMIT 5) AND cpev.attribute_id = (SELECT attribute_id FROM eav_attribute WHERE attribute_code = 'name' AND entity_type_id = (SELECT entity_type_id FROM eav_entity_type WHERE entity_type_code = 'catalog_product'));"], "preparatory_sql_actual_results_preview": ["[]", "Error: 1235 (42000): This version of MariaDB doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'"], "status": "Failed at LLM assessment step"}
{"item_number": 5, "original_question": "What is the total sales value for each store in the current year?", "task_type": "query", "preparatory_sql_list": ["SELECT store_id, SUM(grand_total) AS total_sales FROM sales_order WHERE YEAR(created_at) = YEAR(CURDATE()) GROUP BY store_id;", "SELECT store_id, name FROM store WHERE store_id IN (SELECT DISTINCT store_id FROM sales_order WHERE YEAR(created_at) = YEAR(CURDATE()));"], "preparatory_sql_actual_results_preview": ["[]", "[]"], "status": "Failed at LLM assessment step"}

View File

@@ -0,0 +1,5 @@
{"item_number": 1, "original_question": "What is the current stock quantity and status for product SKU '24-MB01' in all inventory sources?", "task_type": "query", "preparatory_sql_list": ["SELECT entity_id FROM catalog_product_entity WHERE sku = '24-MB01';", "SELECT source_code, quantity, status FROM inventory_source_item WHERE sku = '24-MB01';"], "preparatory_sql_actual_results_preview": ["[{'entity_id': 1}]", "[{'source_code': 'default', 'quantity': '100.0000', 'status': 1}]"], "full_preparatory_sql_actual_results_repr": ["[{'entity_id': 1}]", "[{'source_code': 'default', 'quantity': '100.0000', 'status': 1}]"], "revised_question": "What is the current stock quantity and status for product SKU '24-MB01' in all inventory sources?", "revision_justification": "The original question was specific and valid. The preparatory SQLs confirmed the existence of the product and provided the necessary stock details from the inventory source. The answer is directly derived from the prep results.", "llm_derived_answer_for_validation_func_gen": "The product SKU '24-MB01' has a stock quantity of 100.0000 and is in stock (status 1) in the 'default' inventory source.", "python_validation_function": "def validate_query_answer(user_answer):\n # Hardcoded expected answer based on LLM's Derived Answer\n expected_answer_str = \"The product SKU '24-MB01' has a stock quantity of 100.0000 and is in stock (status 1) in the 'default' inventory source.\"\n\n # Normalize both user_answer and expected_answer_str for comparison\n normalized_user_answer = user_answer.strip().lower()\n normalized_expected_answer = expected_answer_str.strip().lower()\n\n # Compare the normalized answers\n if normalized_user_answer == normalized_expected_answer:\n return True, \"User answer matches the expected answer.\"\n else:\n return False, f\"User answer '{user_answer}' does not match the expected answer '{expected_answer_str}'.\""}
{"item_number": 2, "original_question": "Intent: Verify if customer with email 'janedoe@example.com' has an active wishlist.", "task_type": "operational_check", "preparatory_sql_list": ["SELECT entity_id FROM customer_entity WHERE email = 'janedoe@example.com';", "SELECT wishlist_id FROM wishlist WHERE customer_id = (SELECT entity_id FROM customer_entity WHERE email = 'janedoe@example.com') AND shared = 0;"], "preparatory_sql_actual_results_preview": ["[]", "[]"], "full_preparatory_sql_actual_results_repr": ["[]", "[]"], "revised_question": "Intent: Verify if customer with email 'janedoe@example.com' has an active wishlist.", "revision_justification": "The original question was valid, but the preparatory SQL results showed that the customer does not exist. Therefore, the operational intent is not feasible.", "llm_feasibility_summary_for_validation_func_gen": "Customer with email 'janedoe@example.com' does not exist in the database, making it impossible to verify an active wishlist.", "python_validation_function": "from mysql.connector import Error\n\ndef validate_operational_state(db_connection):\n # Revised Intent: \"Verify if customer with email 'janedoe@example.com' has an active wishlist.\"\n # LLM Feasibility Summary: \"Customer with email 'janedoe@example.com' does not exist in the database.\"\n\n if not db_connection or not db_connection.is_connected():\n return False, \"Database connection not available for validation.\"\n\n email_to_check = 'janedoe@example.com' # Hardcoded based on context\n\n try:\n cursor = db_connection.cursor(dictionary=True)\n # Hardcoded SQL to check if the customer exists\n query_customer = \"SELECT entity_id FROM customer_entity WHERE email = %s\"\n cursor.execute(query_customer, (email_to_check,))\n customer_result = cursor.fetchone()\n\n if customer_result:\n # If customer exists, check for active wishlist\n customer_id = customer_result['entity_id']\n query_wishlist = \"SELECT wishlist_id FROM wishlist WHERE customer_id = %s AND shared = 0\"\n cursor.execute(query_wishlist, (customer_id,))\n wishlist_result = cursor.fetchone()\n cursor.close()\n\n if wishlist_result:\n return False, f\"Validation failed: Customer '{email_to_check}' exists and has an active wishlist.\"\n else:\n return True, f\"Validation successful: Customer '{email_to_check}' exists but does not have an active wishlist.\"\n else:\n cursor.close()\n return True, f\"Validation successful: Customer '{email_to_check}' does not exist in the database.\"\n\n except Error as e:\n return False, f\"Database error during validation: {e}\"\n except Exception as ex:\n return False, f\"Unexpected error during validation: {ex}\""}
{"item_number": 3, "original_question": "List all active catalog rules applicable to the 'Women\u2019s Pants' category.", "task_type": "query", "preparatory_sql_list": ["SELECT rule_id, name FROM catalogrule WHERE is_active = 1 AND conditions_serialized LIKE '%category_ids\":\"27%';"], "preparatory_sql_actual_results_preview": ["[]"], "status": "Failed at LLM assessment step"}
{"item_number": 4, "original_question": "Intent: Check if the order with increment ID '000000001' is eligible for cancellation.", "task_type": "operational_check", "preparatory_sql_list": ["SELECT entity_id, status FROM sales_order WHERE increment_id = '000000001';", "SELECT status FROM sales_order_status_state WHERE state = 'canceled' AND status = (SELECT status FROM sales_order WHERE increment_id = '000000001');"], "preparatory_sql_actual_results_preview": ["[{'entity_id': 1, 'status': 'canceled'}]", "[{'status': 'canceled'}]"], "full_preparatory_sql_actual_results_repr": ["[{'entity_id': 1, 'status': 'canceled'}]", "[{'status': 'canceled'}]"], "revised_question": "Intent: Check if the order with increment ID '000000001' is eligible for cancellation.", "revision_justification": "The original question was valid, but the preparatory SQL results showed that the order is already in the 'canceled' status, making further cancellation unnecessary and not feasible.", "llm_feasibility_summary_for_validation_func_gen": "The order with increment ID '000000001' is already canceled, so it is not eligible for further cancellation.", "python_validation_function": "# LLM failed to generate validation function code or an error occurred."}
{"item_number": 5, "original_question": "Retrieve the top 5 best-selling products in store with ID 1 in the last month.", "task_type": "query", "preparatory_sql_list": ["SELECT product_id, product_name, SUM(qty_ordered) AS total_sold FROM sales_bestsellers_aggregated_monthly WHERE store_id = 1 AND period >= DATE_SUB(CURRENT_DATE, INTERVAL 1 MONTH) GROUP BY product_id, product_name ORDER BY total_sold DESC LIMIT 5;"], "preparatory_sql_actual_results_preview": ["[]"], "full_preparatory_sql_actual_results_repr": ["[]"], "revised_question": "Retrieve the top 5 best-selling products in store with ID 1 in the last month.", "revision_justification": "The original question was valid, but the preparatory SQL results returned no data, indicating that there were no sales recorded for store ID 1 in the specified period.", "llm_derived_answer_for_validation_func_gen": "There are no best-selling products recorded for store ID 1 in the last month.", "python_validation_function": "# LLM failed to generate validation function code or an error occurred."}

View File

@@ -0,0 +1,3 @@
{"item_number": 1, "original_question": "What is the current stock quantity and status of the product with SKU '24-MB01' in the default inventory source?", "task_type": "query", "preparatory_sql_list": ["SELECT entity_id FROM catalog_product_entity WHERE sku = '24-MB01';", "SELECT quantity, status FROM inventory_source_item WHERE sku = '24-MB01' AND source_code = 'default';"], "preparatory_sql_actual_results_preview": ["[{'entity_id': 1}]", "[{'quantity': '100.0000', 'status': 1}]"], "full_preparatory_sql_actual_results_repr": ["[{'entity_id': 1}]", "[{'quantity': '100.0000', 'status': 1}]"], "revised_question": "What is the current stock quantity and status of the product with SKU '24-MB01' in the default inventory source?", "revision_justification": "The original question was specific and clear. The preparatory SQL results provided the necessary data to answer the question directly, confirming the product's stock quantity and status.", "llm_derived_answer_for_validation_func_gen": "The product with SKU '24-MB01' has a stock quantity of 100.0000 and is currently in stock.", "python_validation_function": "def validate_query_answer(user_answer):\n # Based on LLM's Derived Answer\n expected_answer_str = \"The product with SKU '24-MB01' has a stock quantity of 100.0000 and is currently in stock.\"\n\n # Normalize both expected and user answers by stripping whitespace and converting to lowercase\n normalized_user_answer = user_answer.strip().lower()\n normalized_expected_answer = expected_answer_str.strip().lower()\n\n # Compare the normalized answers\n if normalized_user_answer == normalized_expected_answer:\n return True, \"User answer matches the expected answer.\"\n else:\n return False, f\"User answer '{user_answer}' does not match the expected: '{expected_answer_str}'.\""}
{"item_number": 2, "original_question": "List all the active catalog rules that are currently applied to products in the 'Men's Pants' category.", "task_type": "query", "preparatory_sql_list": ["SELECT rule_id, name FROM catalogrule WHERE is_active = 1;", "SELECT entity_id FROM catalog_category_entity_varchar WHERE value = 'Men's Pants';", "SELECT product_id FROM catalog_category_product WHERE category_id = (SELECT entity_id FROM catalog_category_entity_varchar WHERE value = 'Men's Pants');", "SELECT rule_id FROM catalogrule_product WHERE product_id IN (SELECT product_id FROM catalog_category_product WHERE category_id = (SELECT entity_id FROM catalog_category_entity_varchar WHERE value = 'Men's Pants'));"], "preparatory_sql_actual_results_preview": ["[{'rule_id': 1, 'name': '20% off all Women\u2019s and Men\u2019s Pants'}, {'rule_id': 2, 'name': 'Spring sale'}]", "Error: 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 's Pants'' at line 1", "Error: 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 's Pants')' at line 1", "Error: 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 's Pants'))' at line 1"], "full_preparatory_sql_actual_results_repr": ["[{'rule_id': 1, 'name': '20% off all Women\u2019s and Men\u2019s Pants'}, {'rule_id': 2, 'name': 'Spring sale'}]", "\"Error: 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 's Pants'' at line 1\"", "\"Error: 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 's Pants')' at line 1\"", "\"Error: 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 's Pants'))' at line 1\""], "revised_question": "List all the active catalog rules that are currently applied to products in the 'Men's Pants' category.", "revision_justification": "The original question remains valid, but the SQL syntax error prevented the retrieval of specific product and rule associations for the 'Men's Pants' category. The answer is based on the available data from the first preparatory SQL result.", "llm_derived_answer_for_validation_func_gen": "Due to a syntax error in the SQL queries related to the 'Men's Pants' category, the specific products and their associated catalog rules could not be determined. However, there are active catalog rules such as '20% off all Women\u2019s and Men\u2019s Pants' and 'Spring sale'.", "python_validation_function": "def validate_query_answer(user_answer):\n # Based on LLM's Derived Answer\n expected_answer_str = (\"Due to a syntax error in the SQL queries related to the 'Men's Pants' category, \"\n \"the specific products and their associated catalog rules could not be determined. \"\n \"However, there are active catalog rules such as '20% off all Women\u2019s and Men\u2019s Pants' \"\n \"and 'Spring sale'.\")\n\n # Normalize both expected and user answers by stripping whitespace and converting to lowercase\n normalized_user_answer = user_answer.strip().lower()\n normalized_expected_answer = expected_answer_str.strip().lower()\n\n # Compare the normalized answers\n if normalized_user_answer == normalized_expected_answer:\n return True, \"User answer matches the expected answer.\"\n else:\n return False, f\"User answer '{user_answer}' does not match the expected: '{expected_answer_str}'.\""}
{"item_number": 3, "original_question": "Intent: Update the price of product with SKU '24-MB03' to $35.00.", "task_type": "operational_check", "preparatory_sql_list": ["SELECT entity_id FROM catalog_product_entity WHERE sku = '24-MB03';", "SELECT value FROM catalog_product_entity_decimal WHERE entity_id = (SELECT entity_id FROM catalog_product_entity WHERE sku = '24-MB03') AND attribute_id = (SELECT attribute_id FROM eav_attribute WHERE attribute_code = 'price');"], "preparatory_sql_actual_results_preview": ["[{'entity_id': 3}]", "[{'value': '38.000000'}]"], "full_preparatory_sql_actual_results_repr": ["[{'entity_id': 3}]", "[{'value': '38.000000'}]"], "revised_question": "Intent: Update the price of product with SKU '24-MB03' to $35.00.", "revision_justification": "The original question remains valid as the product with SKU '24-MB03' exists and its current price was retrieved, confirming that a price update is feasible.", "llm_feasibility_summary_for_validation_func_gen": "Product with SKU '24-MB03' exists with a current price of $38.00. The update to $35.00 is feasible.", "python_validation_function": "from mysql.connector import Error\n\ndef validate_operational_state(db_connection):\n # Revised Intent: \"Update the price of product with SKU '24-MB03' to $35.00.\"\n # LLM Feasibility Summary: \"Product with SKU '24-MB03' exists with a current price of $38.00. The update to $35.00 is feasible.\"\n \n if not db_connection or not db_connection.is_connected():\n return False, \"Database connection not available for validation.\"\n\n sku_to_check = \"24-MB03\" # Hardcoded based on context\n expected_price = \"38.000000\" # Hardcoded based on feasibility summary\n\n try:\n cursor = db_connection.cursor(dictionary=True)\n # Hardcoded SQL to re-verify the state\n query = \"\"\"\n SELECT value FROM catalog_product_entity_decimal \n WHERE entity_id = (SELECT entity_id FROM catalog_product_entity WHERE sku = %s)\n AND attribute_id = (SELECT attribute_id FROM eav_attribute WHERE attribute_code = 'price')\n \"\"\"\n cursor.execute(query, (sku_to_check,))\n result = cursor.fetchone()\n cursor.close()\n\n if result:\n if result['value'] == expected_price:\n return True, f\"Validation successful: SKU '{sku_to_check}' has the current price '{expected_price}'.\"\n else:\n return False, f\"Validation failed: SKU '{sku_to_check}' has price '{result['value']}', expected '{expected_price}'.\"\n else:\n return False, f\"Validation failed: SKU '{sku_to_check}' not found during validation check.\"\n except Error as e:\n return False, f\"Database error during validation: {e}\"\n except Exception as ex:\n return False, f\"Unexpected error during validation: {ex}\""}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,5 @@
{"item_number": 1, "original_question": "What is the current stock status and quantity of product with SKU '24-MB01' across all sources?", "task_type": "query", "preparatory_sql_list": ["SELECT source_code, quantity, status FROM inventory_source_item WHERE sku = '24-MB01';"], "preparatory_sql_actual_results_preview": ["[{'source_code': 'default', 'quantity': '100.0000', 'status': 1}]"], "full_preparatory_sql_actual_results_repr": ["[{'source_code': 'default', 'quantity': '100.0000', 'status': 1}]"], "revised_question": "What is the current stock status and quantity of product with SKU '24-MB01' across all sources?", "revision_justification": "The original question was specific and the preparatory SQL results provided the necessary information to answer it directly. The stock status is 'out of stock' (status 0) and the quantity is 100.0000 for SKU '24-MB01' in the 'default' source.", "llm_derived_answer_for_validation_func_gen": "The product with SKU '24-MB01' has a stock quantity of 100.0000 and is currently out of stock (status 0) in the 'default' source.", "python_validation_function": "def validate_query_answer(user_answer):\n # Based on LLM's Derived Answer\n expected_answer_str = \"The current stock status and quantity of product with SKU '24-MB01' across all sources is: 'default' source has quantity 100.0000 and status 1 (In Stock).\"\n\n # Normalize the strings for comparison\n normalized_user_answer = user_answer.strip().lower()\n expected_answer_str_normalized = expected_answer_str.strip().lower()\n\n # Compare the normalized strings\n if isinstance(user_answer, str) and user_answer.strip().lower() == expected_answer_str.strip().lower():\n return True, \"User answer matches expected.\"\n else:\n return False, f\"User answer '{user_answer}' does not match expected '{expected_answer_str}'.\""}
{"item_number": 2, "original_question": "Intent: Cancel order with increment_id '000000003'.", "task_type": "operational_check", "preparatory_sql_list": ["SELECT entity_id, status FROM sales_order WHERE increment_id = '000000003';"], "preparatory_sql_actual_results_preview": ["[{'entity_id': 3, 'status': 'canceled'}]"], "full_preparatory_sql_actual_results_repr": ["[{'entity_id': 3, 'status': 'canceled'}]"], "revised_question": "Intent: Cancel order with increment_id '000000003'.", "revision_justification": "The preparatory SQL results show that the order is already in 'canceled' status, making the cancellation operation redundant and unnecessary.", "llm_feasibility_summary_for_validation_func_gen": "Order with increment_id '000000003' is already canceled. No further action is needed.", "llm_feasibility": false, "python_validation_function": "# Validation function generation skipped as operation was deemed not feasible."}
{"item_number": 3, "original_question": "How many products are currently out of stock across all websites?", "task_type": "query", "preparatory_sql_list": ["SELECT COUNT(DISTINCT product_id) AS out_of_stock_count FROM cataloginventory_stock_status WHERE stock_status = 0;"], "preparatory_sql_actual_results_preview": ["[{'out_of_stock_count': 2}]"], "full_preparatory_sql_actual_results_repr": ["[{'out_of_stock_count': 2}]"], "revised_question": "How many products are currently out of stock across all websites?", "revision_justification": "The original question was clear and specific. The preparatory SQL provided the exact count of out-of-stock products, allowing for a direct answer.", "llm_derived_answer_for_validation_func_gen": "There are currently 2 products out of stock across all websites.", "python_validation_function": "def validate_query_answer(user_answer):\n # Based on LLM's Derived Answer\n expected_answer_str = \"There are currently 2 products out of stock across all websites.\"\n\n # Normalize the strings for comparison\n normalized_user_answer = user_answer.strip().lower()\n expected_answer_str_normalized = expected_answer_str.strip().lower()\n\n # Compare the normalized strings\n if normalized_user_answer == expected_answer_str_normalized:\n return True, \"User answer matches expected.\"\n else:\n return False, f\"User answer '{user_answer}' does not match expected '{expected_answer_str}'.\""}
{"item_number": 4, "original_question": "Intent: Retrieve the current price of product with SKU '24-MB05' for the 'Wholesale' customer group.", "task_type": "operational_check", "preparatory_sql_list": ["SELECT entity_id FROM catalog_product_entity WHERE sku = '24-MB05';", "SELECT price FROM catalog_product_index_price WHERE entity_id = (SELECT entity_id FROM catalog_product_entity WHERE sku = '24-MB05') AND customer_group_id = 2;"], "preparatory_sql_actual_results_preview": ["[{'entity_id': 4}]", "[{'price': '45.000000'}]"], "full_preparatory_sql_actual_results_repr": ["[{'entity_id': 4}]", "[{'price': '45.000000'}]"], "revised_question": "Intent: Retrieve the current price of product with SKU '24-MB05' for the 'Wholesale' customer group.", "revision_justification": "The preparatory SQL results confirmed the existence of the product with SKU '24-MB05' and provided the price for the 'Wholesale' customer group, making the retrieval operation feasible.", "llm_feasibility_summary_for_validation_func_gen": "The product with SKU '24-MB05' exists and its current price for the 'Wholesale' customer group is 45.000000. Retrieval is feasible.", "llm_feasibility": true, "python_validation_function": "from mysql.connector import Error\n\ndef validate_operational_state(db_connection):\n # Revised Intent: \"Retrieve the current price of product with SKU '24-MB05' for the 'Wholesale' customer group.\"\n # LLM Feasibility Summary: \"The product with SKU '24-MB05' exists and its current price for the 'Wholesale' customer group is 45.000000.\"\n\n if not db_connection or not db_connection.is_connected():\n return False, \"DB connection not available for validation.\"\n\n sku_to_check = \"24-MB05\" # Hardcoded based on context\n customer_group_id_to_check = 2 # 'Wholesale' customer group ID\n expected_price = '45.000000' # Expected price\n\n try:\n cursor = db_connection.cursor(dictionary=True)\n \n # Hardcoded SQL to re-verify the product existence\n query_product = \"SELECT entity_id FROM catalog_product_entity WHERE sku = %s\"\n cursor.execute(query_product, (sku_to_check,))\n product_result = cursor.fetchone()\n\n if not product_result:\n cursor.close()\n return False, f\"Validation failed: Product with SKU '{sku_to_check}' does not exist.\"\n\n # Hardcoded SQL to re-verify the price for the 'Wholesale' customer group\n query_price = \"\"\"\n SELECT price FROM catalog_product_index_price \n WHERE entity_id = %s AND customer_group_id = %s\n \"\"\"\n cursor.execute(query_price, (product_result['entity_id'], customer_group_id_to_check))\n price_result = cursor.fetchone()\n cursor.close()\n\n if price_result and price_result['price'] == expected_price:\n return True, f\"Validation successful: SKU '{sku_to_check}' has price '{expected_price}' for 'Wholesale' customer group.\"\n else:\n return False, f\"Validation failed: SKU '{sku_to_check}' price for 'Wholesale' customer group is '{price_result['price']}' instead of expected '{expected_price}'.\"\n except Error as e:\n return False, f\"Database error during validation: {e}\"\n except Exception as ex:\n return False, f\"Unexpected error during validation: {ex}\""}
{"item_number": 5, "original_question": "What are the top 5 most reviewed products based on the number of reviews?", "task_type": "query", "preparatory_sql_list": ["SELECT entity_pk_value, COUNT(review_id) AS review_count FROM review GROUP BY entity_pk_value ORDER BY review_count DESC LIMIT 5;"], "preparatory_sql_actual_results_preview": ["[{'entity_pk_value': 676, 'review_count': 4}, {'entity_pk_value': 39, 'review_count': 4}, {'entity_pk_value': 1428, 'review_count': 4}, {'entity_pk_value': 1236, 'review_count': 4}, {'entity_pk_value'"], "full_preparatory_sql_actual_results_repr": ["[{'entity_pk_value': 676, 'review_count': 4}, {'entity_pk_value': 39, 'review_count': 4}, {'entity_pk_value': 1428, 'review_count': 4}, {'entity_pk_value': 1236, 'review_count': 4}, {'entity_pk_value': 1840, 'review_count': 4}]"], "revised_question": "What are the top 5 most reviewed products based on the number of reviews?", "revision_justification": "The original question was specific and clear. The preparatory SQL provided the necessary data to identify the top 5 most reviewed products, allowing for a direct answer.", "llm_derived_answer_for_validation_func_gen": "The top 5 most reviewed products, each with 4 reviews, have the following entity IDs: 676, 39, 1428, 1236, and 1840.", "python_validation_function": "def validate_query_answer(user_answer):\n # Based on LLM's Derived Answer\n expected_answer_str = \"The top 5 most reviewed products, each with 4 reviews, have the following entity IDs: 676, 39, 1428, 1236, and 1840.\"\n\n # Normalize the strings for comparison\n normalized_user_answer = user_answer.strip().lower()\n expected_answer_str_normalized = expected_answer_str.strip().lower()\n\n # Compare the normalized strings\n if normalized_user_answer == expected_answer_str_normalized:\n return True, \"User answer matches expected.\"\n else:\n return False, f\"User answer '{user_answer}' does not match expected '{expected_answer_str}'.\""}

View File

@@ -0,0 +1,890 @@
import openai
import mysql.connector
from mysql.connector import Error
import json
import logging
from datetime import datetime
import time
import decimal # For handling Decimal from DB
import os
from dotenv import load_dotenv
import re # Added for regex
load_dotenv()
# --- LLM Configuration ---
# It's best to set API keys as environment variables or use a secrets manager
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY", "YOUR_OPENAI_API_KEY_FALLBACK")
OPENAI_BASE_URL = os.environ.get("OPENAI_BASE_URL", "https://api.openai.com/v1") # Default OpenAI URL
# LLM_MODEL_GENERATION = "gpt-4o" # Example, ensure it's correctly set
# LLM_MODEL_VALIDATION = "gpt-4o" # Example
LLM_MODEL_GENERATION = os.environ.get("LLM_MODEL_GENERATION", "gpt-4o")
LLM_MODEL_VALIDATION = os.environ.get("LLM_MODEL_VALIDATION", "gpt-4o")
# Configure logging
log_file_name = f'magento_main_pipeline_{datetime.now().strftime("%Y%m%d_%H%M%S")}.log'
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(module)s - %(funcName)s - %(message)s',
handlers=[
logging.FileHandler(log_file_name),
logging.StreamHandler()
]
)
logger = logging.getLogger(__name__)
# --- Database Configuration ---
DB_HOST = "localhost"
DB_USER = "root"
DB_PORT = 23306 # Make sure this is an integer
DB_PASS = "1234567890"
DB_NAME = "magentodb"
# --- Magento Schema (loaded in main_workflow) ---
MAGENTO_SCHEMA_CONTENT = ""
TABLE_SAMPLES_CACHE_FILE = "table_samples_cache.txt" # Cache file for table samples
TABLE_SAMPLES_CONTENT = "" # To store sample rows
# --- System Prompt (Loaded in main_workflow) ---
SYSTEM_PROMPT_TEMPLATE = ""
def _clean_llm_json_response(response_content: str) -> str:
"""Strips markdown code fences from LLM JSON responses."""
clean_response = response_content.strip()
if clean_response.startswith("```json"):
clean_response = clean_response[7:-3].strip()
elif clean_response.startswith("```"):
clean_response = clean_response[3:-3].strip()
return clean_response
def _clean_llm_python_code_response(response_content: str) -> str:
"""Strips markdown code fences from LLM Python code responses."""
clean_code = response_content.strip()
if clean_code.startswith("```python"):
clean_code = clean_code[10:-3].strip() # Handles ```python\n ... ```
elif clean_code.startswith("```"):
clean_code = clean_code[3:-3].strip()
return clean_code
def get_table_names_from_schema(schema_content):
"""Extracts table names from schema DDL using regex."""
# Regex to find "CREATE TABLE `table_name`" or "CREATE TABLE table_name"
# It captures the table name, optionally enclosed in backticks.
table_names = re.findall(r"CREATE TABLE(?: IF NOT EXISTS)?\s+`?(\w+)`?", schema_content, re.IGNORECASE)
logger.info(f"Extracted {len(table_names)} table names from schema.")
logger.debug(f"Table names: {table_names}")
return list(set(table_names)) # Return unique table names
def _fetch_and_format_table_samples(table_names, db_conn, cache_file_path):
"""
Fetches top 5 rows for each table, formats them, and saves to a cache file.
Returns the formatted string of all table samples.
"""
all_samples_str = ""
if not db_conn or not db_conn.is_connected():
logger.warning("Database connection not available. Cannot fetch fresh table samples.")
return ""
logger.info(f"Fetching top 5 rows for {len(table_names)} tables...")
for table_name in table_names:
try:
cursor = db_conn.cursor(dictionary=True)
query = f"SELECT * FROM `{table_name}` LIMIT 5" # Use backticks for table names
logger.debug(f"Executing sample query for {table_name}: {query}")
cursor.execute(query)
rows = cursor.fetchall()
current_table_sample_str = f"\n--- Sample rows for table: {table_name} ---\n"
if rows:
headers = ", ".join(rows[0].keys())
current_table_sample_str += headers + "\n"
for row in rows:
# Convert all values to string, handling None
values = ", ".join([str(v) if v is not None else "NULL" for v in row.values()])
current_table_sample_str += values + "\n"
else:
current_table_sample_str += "(No rows found or table is empty)\n"
all_samples_str += current_table_sample_str
cursor.close()
except Error as e:
logger.error(f"Error fetching samples for table {table_name}: {e}")
all_samples_str += f"\n--- Error fetching samples for table: {table_name}: {e} ---\n"
except Exception as ex: # Catch any other unexpected errors
logger.error(f"Unexpected error fetching samples for table {table_name}: {ex}")
all_samples_str += f"\n--- Unexpected error for table: {table_name}: {ex} ---\n"
try:
with open(cache_file_path, "w", encoding="utf-8") as f:
f.write(all_samples_str)
logger.info(f"Table samples cached successfully to {cache_file_path}")
except IOError as e:
logger.error(f"Failed to write table samples to cache file {cache_file_path}: {e}")
return all_samples_str
def initialize_system_prompt(db_conn_for_samples, current_script_dir):
global SYSTEM_PROMPT_TEMPLATE, MAGENTO_SCHEMA_CONTENT, TABLE_SAMPLES_CONTENT
if not MAGENTO_SCHEMA_CONTENT:
logger.error("Magento schema content is not loaded. Cannot initialize system prompt.")
# SYSTEM_PROMPT_TEMPLATE will remain empty or use a default if set elsewhere
return
sample_rows_cache_path = os.path.join(current_script_dir, TABLE_SAMPLES_CACHE_FILE)
try:
with open(sample_rows_cache_path, "r", encoding="utf-8") as f:
TABLE_SAMPLES_CONTENT = f.read()
logger.info(f"Table samples loaded successfully from cache: {sample_rows_cache_path}")
except FileNotFoundError:
logger.info(f"Table samples cache file not found: {sample_rows_cache_path}. Attempting to fetch from DB.")
if db_conn_for_samples and db_conn_for_samples.is_connected():
table_names = get_table_names_from_schema(MAGENTO_SCHEMA_CONTENT)
if table_names:
TABLE_SAMPLES_CONTENT = _fetch_and_format_table_samples(table_names, db_conn_for_samples, sample_rows_cache_path)
else:
logger.warning("No table names extracted from schema. Cannot fetch samples.")
TABLE_SAMPLES_CONTENT = " (Could not extract table names to fetch samples) "
else:
logger.warning("DB connection not available and cache miss. Proceeding without table samples in prompt.")
TABLE_SAMPLES_CONTENT = " (DB connection not available for fetching samples and no cache found) "
except Exception as e:
logger.error(f"Error loading table samples from cache {sample_rows_cache_path}: {e}")
TABLE_SAMPLES_CONTENT = f" (Error loading table samples from cache: {e}) "
SYSTEM_PROMPT_TEMPLATE = f"""
You are an expert Magento 2 database analyst and Python programmer. Your task is to assist in creating a dataset of questions, SQL queries, and Python validation functions for a Magento 2 database.
**Database Schema:**
--- START OF FILE schema_nonempty.txt ---
{MAGENTO_SCHEMA_CONTENT}
--- END OF FILE schema_nonempty.txt ---
**Sample Data from Tables (Top 5 rows if available):**
--- START OF SAMPLE DATA ---
{TABLE_SAMPLES_CONTENT}
--- END OF SAMPLE DATA ---
**Key Magento Schema Characteristics & EAV Model:**
* **EAV (Entity-Attribute-Value):** Many entities (products, categories, customers) use EAV.
* Core entity table: e.g., `catalog_product_entity`.
* Attribute definition: `eav_attribute`.
* Value tables by data type: e.g., `catalog_product_entity_varchar`, `_int`, `_decimal`.
* To get an attribute value (e.g., product name), you typically JOIN `catalog_product_entity` with `eav_attribute` (to find the attribute_id for 'name') and then JOIN with `catalog_product_entity_varchar` using that attribute_id and the product's entity_id.
* **Store Scopes:** Data can be global (store_id=0 or admin), website-specific, or store-view-specific. Queries often need to specify `store_id`. `store_id = 0` is the admin/default scope for many attributes.
* **Product Types:** `catalog_product_entity.type_id` can be 'simple', 'configurable', 'virtual', 'bundle', 'downloadable', 'grouped'.
* **Inventory (MSI):** `inventory_source_item` manages stock per source (e.g., 'default'). `status = 1` (In Stock), `status = 0` (Out of Stock). `cataloginventory_stock_item` is the older/default system.
* **Order Workflow:** `quote` (cart) -> `sales_order` -> `sales_invoice`, `sales_shipment`, `sales_creditmemo`.
* **Flat/Grid Tables:** Tables like `sales_order_grid`, `customer_grid_flat` are denormalized for admin panel performance. Queries for direct user-facing info might use these, but detailed analysis often requires joining base tables.
* **Date/Time:** Timestamps are common (e.g., `created_at`, `updated_at`). Be mindful of timezones if applicable, though standard MySQL functions usually handle it.
* **Foreign Keys:** Pay attention to foreign key relationships for JOINs (e.g., `sales_order_item.order_id` -> `sales_order.entity_id`).
**Task-Specific Instructions (General):**
* Ensure SQL queries are compatible with MariaDB/MySQL.
* For EAV attributes, ensure you correctly identify the `entity_type_id` for the attribute (e.g., for 'catalog_product' from `eav_entity_type` WHERE entity_type_code = 'catalog_product') and the `attribute_code`.
* Use `store_id = 0` for admin/default scope attributes unless a specific store view is relevant.
* Aim for variety in questions: simple lookups, aggregations, joins, EAV traversals, date operations, DML (for operational tasks).
* Answers derived from queries should be strictly verifiable.
"""
def create_db_connection(host_name, port, user_name, user_password, db_name, max_retries=3):
connection = None
retry_count = 0
while retry_count < max_retries:
try:
logger.info(f"Attempting to connect to database {db_name} at {host_name}:{port} (Attempt {retry_count + 1}/{max_retries})")
connection = mysql.connector.connect(
host=host_name,
port=int(port), # Ensure port is an integer
user=user_name,
passwd=user_password,
database=db_name,
connection_timeout=180,
)
logger.info(f"MySQL Database connection successful to {db_name}")
return connection
except Error as err:
retry_count += 1
logger.error(f"Failed to connect to database (Attempt {retry_count}/{max_retries}): {err}")
if retry_count < max_retries:
wait_time = 2 ** retry_count
logger.info(f"Waiting {wait_time} seconds before retrying...")
time.sleep(wait_time)
else:
return None
def call_llm(prompt_messages, model_name, temperature=0.2, max_tokens=2048):
"""Generic function to call OpenAI compatible API."""
if OPENAI_API_KEY == "YOUR_OPENAI_API_KEY_FALLBACK":
logger.error("OpenAI API key is not configured. Please set the OPENAI_API_KEY environment variable or update the script.")
return None
# Ensure client is initialized for each call or manage a global client carefully
try:
client = openai.OpenAI(api_key=OPENAI_API_KEY, base_url=OPENAI_BASE_URL)
except Exception as e:
logger.error(f"Failed to initialize OpenAI client: {e}")
return None
try:
logger.info(f"Calling LLM model: {model_name} with temperature {temperature}")
# Log the prompt (which is now the full conversation history)
# For very long histories, consider logging only the last few messages or a summary.
if len(json.dumps(prompt_messages, indent=2)) < 2000: # Avoid overly verbose logs for long histories
logger.debug(f"LLM Request Messages: {json.dumps(prompt_messages, indent=2)}")
else:
logger.debug(f"LLM Request Messages: (History too long to log fully - {len(prompt_messages)} messages)")
response = client.chat.completions.create(
model=model_name,
messages=prompt_messages,
temperature=temperature,
max_tokens=max_tokens
)
content = response.choices[0].message.content.strip()
logger.info(f"LLM call successful. Tokens used: Completion={response.usage.completion_tokens}, Prompt={response.usage.prompt_tokens}, Total={response.usage.total_tokens}")
logger.debug(f"LLM Raw Response Content:\n{content}")
return content
except Exception as e:
logger.error(f"Error calling LLM: {e}")
return None
def generate_initial_tasks_and_prep_sql(conversation_history, num_tasks=10):
"""Step 1: LLM generates initial questions/tasks and preparatory (SELECT-only) SQL queries."""
logger.info(f"Requesting LLM to generate {num_tasks} initial tasks and preparatory SQLs.")
prompt_step1 = f"""
Based on the provided Magento 2 database schema, sample data, and its characteristics (from system prompt which is already part of our conversation history), generate a list of {num_tasks} diverse tasks. For each task:
1. Provide an **original_question** (string): This is the initial high-level question or operational intent.
2. Provide a **task_type** (string): Either "query" or "operational_check".
3. Provide a **preparatory_sql_list** (list of strings): A list of one or more **SELECT-ONLY SQL queries**.
* These SQLs are for **information gathering, pre-condition checking, or collecting data to answer a question.**
* For **"query" task_type**, these SQLs should aim to gather all necessary data to answer the `original_question`.
* For **"operational_check" task_type** (e.g., "Intent: Update product X's price" or "Intent: Cancel order Y"), these SQLs should **ONLY** check if the target entity exists, get its current state, or list potential entities. **ABSOLUTELY NO DML (UPDATE, INSERT, DELETE) should be generated in this list.**
* The results of these preparatory SQLs will be used by a subsequent LLM call to refine the question and assess the gathered information.
Format the output STRICTLY as a JSON list of objects. Each object must have "original_question", "task_type", and "preparatory_sql_list" keys.
Ensure the JSON is well-formed. Do not include any introductory text or markdown formatting around the JSON list itself.
**Example for an "operational_check" task:**
{{
"original_question": "Intent: Update the stock quantity of product with SKU 'TEST-SKU-XYZ' to 50 in the default source.",
"task_type": "operational_check",
"preparatory_sql_list": [
"SELECT entity_id, sku FROM catalog_product_entity WHERE sku = 'TEST-SKU-XYZ';",
"SELECT quantity, status FROM inventory_source_item WHERE sku = 'TEST-SKU-XYZ' AND source_code = 'default';"
]
}}
**Example for a "query" task:**
{{
"original_question": "What are the details (increment_id, status, grand_total) of the most recent order placed by customer_email 'test@example.com'?",
"task_type": "query",
"preparatory_sql_list": [
"SELECT entity_id FROM customer_entity WHERE email = 'test@example.com';",
"SELECT entity_id, increment_id, status, grand_total, created_at FROM sales_order WHERE customer_email = 'test@example.com' ORDER BY created_at DESC LIMIT 1;"
]
}}
Generate {num_tasks} new and distinct items.
"""
conversation_history.append({"role": "user", "content": prompt_step1})
response_content = call_llm(conversation_history, LLM_MODEL_GENERATION, temperature=0.7, max_tokens=3500)
if response_content:
conversation_history.append({"role": "assistant", "content": response_content})
try:
clean_response = _clean_llm_json_response(response_content) # Use helper
generated_data = json.loads(clean_response)
if isinstance(generated_data, list) and all(
isinstance(item, dict) and
"original_question" in item and isinstance(item["original_question"], str) and
"task_type" in item and item["task_type"] in ["query", "operational_check"] and
"preparatory_sql_list" in item and isinstance(item["preparatory_sql_list"], list) and
all(isinstance(sql, str) and sql.strip().upper().startswith("SELECT") for sql in item["preparatory_sql_list"]) and item["preparatory_sql_list"]
for item in generated_data
):
logger.info(f"Successfully parsed {len(generated_data)} initial tasks from LLM.")
logger.info("--- LLM Generated Initial Tasks & Prep SQL ---")
for i, item in enumerate(generated_data):
logger.info(f" Item {i+1}/{len(generated_data)}:")
logger.info(f" Original Question: {item['original_question']}")
logger.info(f" Task Type: {item['task_type']}")
for j, sql in enumerate(item['preparatory_sql_list']):
logger.info(f" Prep SQL {j+1}: {sql}")
logger.info("--- End of LLM Generated Initial Tasks & Prep SQL ---")
return generated_data
else:
logger.error(f"LLM response was not a valid list of initial task objects or contained non-SELECT prep SQL. Content: {response_content}")
return []
except json.JSONDecodeError as e:
logger.error(f"Failed to parse JSON from LLM response for initial tasks: {e}")
logger.error(f"LLM Response Content (check for issues):\n{response_content}")
return []
else:
logger.error("LLM call failed for generate_initial_tasks_and_prep_sql. Removing last user prompt from history.")
if conversation_history and conversation_history[-1]["role"] == "user":
conversation_history.pop()
return []
def refine_question_and_assess_info(conversation_history, original_question, task_type, prep_sql_list, prep_sql_results_repr_list):
"""
Step 2: LLM refines question & derives answer/assesses feasibility based on preparatory SQL results.
NO FINAL SQL IS GENERATED HERE.
"""
logger.info(f"Requesting LLM to refine question and assess info for: {original_question[:100]}...")
prep_info_str = ""
for i, sql in enumerate(prep_sql_list):
prep_info_str += f"Preparatory SQL {i+1}: {sql}\n"
prep_info_str += f"Result {i+1} (Python repr):\n{prep_sql_results_repr_list[i]}\n\n"
output_keys_guidance = ""
if task_type == "query":
output_keys_guidance = 'Return a single JSON object with: "revised_question", "llm_derived_answer", "revision_justification". The "llm_derived_answer" should be your attempt to answer the revised_question based *solely* on the provided prep_sql_results.'
elif task_type == "operational_check":
output_keys_guidance = 'Return a single JSON object with: "revised_question", "llm_feasibility_summary", "llm_feasibility" (boolean, true if the operation described in revised_question is feasible based on prep_sql_results, false otherwise), "revision_justification". The "llm_feasibility_summary" should state whether the operational intent in revised_question seems feasible (e.g., "Product exists and is active") or not (e.g., "Order not found"), based *solely* on prep_sql_results.'
prompt_step2 = f"""
You are an expert Magento 2 database analyst.
You previously received an original question/task (as part of our ongoing conversation) and a list of preparatory SELECT SQL queries. Those SQLs have been executed.
Your current task is to:
1. Review the original question, the preparatory SQLs, and their execution results.
2. Generate a **revised_question** (string). This might be the same as the original if it's still perfectly valid, or it might be adjusted based on the findings (e.g., if an ID doesn't exist, or if more specific information was found).
3. Based on the **task_type** (see below) and the preparatory SQL results:
* If **task_type** is "query": Generate an **llm_derived_answer** (string). This should be a natural language answer to the `revised_question`, formulated *exclusively* from the data in `prep_sql_results`. If the results are insufficient, state that.
* If **task_type** is "operational_check": Generate an **llm_feasibility_summary** (string). This should summarize if the operational intent in `revised_question` appears feasible based *exclusively* on the `prep_sql_results` (e.g., "Product XYZ exists and current price is $50, so update is feasible." or "Order ABC not found, operation not feasible."). Also, generate an **llm_feasibility** (boolean): `true` if the summary indicates feasibility, `false` otherwise.
4. Provide a brief **revision_justification** (string) explaining why the question was or was not revised, and how the preparatory results informed your assessment or derived answer.
**Input Provided to You for this specific refinement task:**
* **Original Question (for context, you might have seen it or similar ones in our conversation):** {original_question}
* **Task Type (from previous step):** {task_type}
* **Preparatory SQLs and their Results for THIS task:**
{prep_info_str}
**Output Format:**
{output_keys_guidance}
Ensure the JSON is well-formed and contains only the specified keys. Provide the JSON object directly.
**Example for "query" task_type:**
{{
"revised_question": "What is the status and grand_total of order increment_id '100000005'?",
"llm_derived_answer": "Order '100000005' has status 'complete' and grand_total '125.50'.",
"revision_justification": "Original question was specific. Prep SQL confirmed order existence and fetched details. Answer derived directly from prep results."
}}
**Example for "operational_check" task_type:**
{{
"revised_question": "Intent: Update stock for SKU 'ABC-123' from 10 to 5.",
"llm_feasibility_summary": "Product SKU 'ABC-123' exists and current stock is 10. The update is feasible.",
"llm_feasibility": true,
"revision_justification": "Prep SQLs confirmed product existence and current stock level, matching conditions for the intended operation."
}}
"""
conversation_history.append({"role": "user", "content": prompt_step2})
response_content = call_llm(conversation_history, LLM_MODEL_GENERATION, temperature=0.2, max_tokens=1500)
if response_content:
conversation_history.append({"role": "assistant", "content": response_content})
try:
clean_response = _clean_llm_json_response(response_content) # Use helper
refined_data = json.loads(clean_response)
base_keys_valid = isinstance(refined_data, dict) and \
"revised_question" in refined_data and isinstance(refined_data["revised_question"], str) and \
"revision_justification" in refined_data and isinstance(refined_data["revision_justification"], str)
type_specific_keys_valid = False
if task_type == "query":
type_specific_keys_valid = "llm_derived_answer" in refined_data and isinstance(refined_data["llm_derived_answer"], str)
elif task_type == "operational_check":
type_specific_keys_valid = "llm_feasibility_summary" in refined_data and isinstance(refined_data["llm_feasibility_summary"], str) and \
"llm_feasibility" in refined_data and isinstance(refined_data["llm_feasibility"], bool)
if base_keys_valid and type_specific_keys_valid:
logger.info("Successfully parsed refined question and assessment from LLM.")
logger.info(f" Revised Question: {refined_data['revised_question']}")
if task_type == "query":
logger.info(f" LLM Derived Answer: {refined_data['llm_derived_answer']}")
elif task_type == "operational_check":
logger.info(f" LLM Feasibility Summary: {refined_data['llm_feasibility_summary']}")
logger.info(f" LLM Feasibility: {refined_data['llm_feasibility']}")
logger.info(f" Revision Justification: {refined_data['revision_justification']}")
return refined_data
else:
logger.error(f"LLM response for refined assessment had missing or invalid keys for task_type '{task_type}'. Content: {response_content}")
return None
except json.JSONDecodeError as e:
logger.error(f"Failed to parse JSON from LLM response for refined assessment: {e}")
logger.error(f"LLM Response Content (check for issues):\n{response_content}")
return None
else:
logger.error("LLM call failed for refine_question_and_assess_info. Removing last user prompt from history.")
if conversation_history and conversation_history[-1]["role"] == "user":
conversation_history.pop()
return None
def execute_sql_and_get_results(db_conn, sql_query, question_text, q_num):
"""Step 2: Execute SQL and collect results. Handles SELECT and DML."""
logger.info(f"Attempting to execute SQL for Question {q_num}: {question_text[:100]}...")
logger.debug(f"Full SQL for Q{q_num}: {sql_query}")
# Ensure connection is active
try:
if db_conn is None or not db_conn.is_connected():
logger.warning(f"DB connection lost or not available for Q{q_num}. Attempting to reconnect...")
db_conn = create_db_connection(DB_HOST, DB_PORT, DB_USER, DB_PASS, DB_NAME)
if db_conn is None or not db_conn.is_connected():
logger.error(f"Failed to re-establish DB connection for Q{q_num}.")
return f"Error: Database connection lost and could not be re-established."
except Exception as e: # Catch broader exceptions if is_connected() fails
logger.error(f"Error checking DB connection status for Q{q_num}: {e}. Attempting to reconnect...")
db_conn = create_db_connection(DB_HOST, DB_PORT, DB_USER, DB_PASS, DB_NAME)
if db_conn is None :
logger.error(f"Failed to re-establish DB connection for Q{q_num} after check error.")
return f"Error: Database connection check failed and could not be re-established."
cursor = None
raw_results = []
# Normalize SQL query for DML check (uppercase, remove leading/trailing whitespace)
normalized_sql_query = sql_query.strip().upper()
is_dml = any(normalized_sql_query.startswith(dml_cmd) for dml_cmd in ["UPDATE", "INSERT", "DELETE"])
# CREATE, ALTER, DROP are DDL, not typically what we expect here but could be considered 'operational'
is_ddl = any(normalized_sql_query.startswith(ddl_cmd) for ddl_cmd in ["CREATE", "ALTER", "DROP"])
query_start_time = time.time()
try:
cursor = db_conn.cursor(dictionary=True)
logger.debug(f"Cursor created for Q{q_num}.")
cursor.execute(sql_query)
logger.debug(f"SQL executed for Q{q_num}.")
if not is_dml and not is_ddl: # It's a SELECT query
fetched_rows = cursor.fetchall()
# Convert Decimal to string for JSON serializability and consistent LLM input
for row in fetched_rows:
raw_results.append({
k: str(v) if isinstance(v, decimal.Decimal) else
v.strftime('%Y-%m-%d %H:%M:%S') if isinstance(v, datetime) else
v
for k, v in row.items()
})
logger.info(f"SELECT query for Q{q_num} fetched {len(raw_results)} rows.")
elif is_dml:
db_conn.commit()
raw_results = f"Rows affected: {cursor.rowcount}"
logger.info(f"DML query for Q{q_num} committed. {raw_results}")
elif is_ddl:
db_conn.commit() # Some DDL might need commit or are auto-committed
raw_results = f"DDL statement executed. Rows affected: {cursor.rowcount}" # rowcount might be -1 or 0 for DDL
logger.info(f"DDL query for Q{q_num} executed. {raw_results}")
except Error as e:
logger.error(f"Error executing SQL for Q{q_num}: {e}\nSQL: {sql_query}")
if db_conn and db_conn.is_connected() and (is_dml or is_ddl) : # Only rollback DML/DDL on error
try:
db_conn.rollback()
logger.info(f"Rolled back transaction for Q{q_num} due to error.")
except Error as rb_err:
logger.error(f"Error during rollback for Q{q_num}: {rb_err}")
return f"Error: {str(e)}" # Return error string
finally:
if cursor:
cursor.close()
logger.debug(f"Cursor closed for Q{q_num}.")
query_duration = time.time() - query_start_time
logger.info(f"SQL for Q{q_num} processed in {query_duration:.2f}s.")
return raw_results
def generate_validation_function(conversation_history, revised_question, task_type,
prep_sql_list_str,
prep_sql_results_repr_list_str,
llm_assessment_from_step2_str
):
"""
Step 3: LLM generates Python validation function based on ongoing conversation.
- For "query": creates validate_query_answer(user_answer) with hardcoded expected answer.
- For "operational_check": creates validate_operational_state(db_connection) with hardcoded DB checks.
"""
logger.info(f"Requesting LLM to generate validation function for: {revised_question[:100]} (Type: {task_type})")
prompt_core_context = f"""
**Context Provided to You (for informing the validation logic you will create, building upon our conversation):**
1. **Revised Question/Operational Intent (from a previous turn in our conversation):**
```
{revised_question}
```
2. **Task Type:** "{task_type}"
3. **Preparatory SELECT SQL List (that led to the assessment below):**
```sql
{prep_sql_list_str}
```
4. **Preparatory SQL Execution Results (Python string repr of a list of results):**
```python
{prep_sql_results_repr_list_str}
```
"""
if task_type == "query":
prompt_step3_template = f"""
You are an expert Magento 2 database analyst and Python programmer, continuing our session.
Your task is to create a Python validation function `validate_query_answer(user_answer)`.
This function will take a `user_answer` (string) and compare it against an **expected answer** that you will determine and **hardcode** into the function.
{prompt_core_context}
5. **LLM's Derived Answer (this was your answer in a previous turn and should be the basis for your hardcoded expected answer):**
```
{llm_assessment_from_step2_str}
```
**Your Task for "query" type (based on our discussion):**
Create a Python function `validate_query_answer(user_answer)`:
* The function should **hardcode the expected answer** based on the "LLM's Derived Answer" provided above. This might involve storing the exact string, or if it's numerical/structured, parsing and storing it appropriately.
* It compares the input `user_answer` to this hardcoded expected answer.
* Return `(is_valid, message)`:
* `is_valid` (boolean): `True` if `user_answer` matches the hardcoded expected answer (allow for some flexibility like case-insensitivity or stripping whitespace for strings, or numerical tolerance if applicable).
* `message` (string): Explaining the outcome (e.g., "User answer matches expected.", "User answer 'X' does not match expected 'Y'.").
* The function must be self-contained (standard imports like `json`, `decimal` are ok if needed for handling the expected answer). It does **NOT** use a database connection.
**Example `validate_query_answer` structure:**
```python
import decimal # if needed
def validate_query_answer(user_answer):
# Based on LLM's Derived Answer: "The total number of customers is 157."
expected_answer_str = "The total number of customers is 157."
# Or, for numerical: expected_count = 157
# Simple string comparison (you can make this more robust)
if isinstance(user_answer, str) and user_answer.strip().lower() == expected_answer_str.strip().lower():
return True, "User answer matches the expected answer."
else:
# Attempt to extract number if question implies a number
try:
# This is just an example, adapt based on actual derived answer format
user_num_part = ''.join(filter(str.isdigit, user_answer))
expected_num_part = ''.join(filter(str.isdigit, expected_answer_str))
if user_num_part and expected_num_part and int(user_num_part) == int(expected_num_part):
return True, f"User answer contains the correct numerical part '{{user_num_part}}' matching expected."
except ValueError:
pass # Failed to parse numbers
return False, f"User answer '{{user_answer}}' does not sufficiently match the expected: '{{expected_answer_str}}'."
```
Now, provide *only* the Python code for `validate_query_answer(user_answer)` based on the specific inputs given.
"""
elif task_type == "operational_check":
prompt_step3_template = f"""
You are an expert Magento 2 database analyst and Python programmer, continuing our session.
Your task is to create a Python validation function `validate_operational_state(db_connection)`.
This function will use the provided `db_connection` to perform **new, hardcoded SELECT queries** to verify that the database state aligns with an expected condition or feasibility assessment derived in our conversation.
{prompt_core_context}
5. **LLM's Feasibility Summary (this was your summary in a previous turn and describes the state your function should verify):**
```
{llm_assessment_from_step2_str}
```
**Your Task for "operational_check" type (based on our discussion):**
Create a Python function `validate_operational_state(db_connection)`:
* The function must contain **hardcoded SELECT SQL query/queries** that you design. These queries should aim to re-verify the conditions described in the "LLM's Feasibility Summary" and the "Revised Operational Intent".
* It uses the `db_connection` to execute these hardcoded SQLs.
* It then analyzes the results of its own SQLs to determine if the database state is as expected.
* Return `(is_valid, message)`:
* `is_valid` (boolean): `True` if the database state (queried by your hardcoded SQLs) matches the expected conditions.
* `message` (string): Explaining the outcome (e.g., "Verified: Product SKU 'XYZ' exists and is active.", "Verification failed: Order 123 status is 'shipped', not 'pending' as expected for the check.").
* The function must be self-contained (standard imports, `mysql.connector.Error` for db errors are ok).
* Handle potential errors during its own database operations. If `db_connection` is `None` or unusable, it should return `(False, "DB connection not available for validation.")`.
**Example `validate_operational_state` structure:**
```python
from mysql.connector import Error # If you need to catch DB errors
def validate_operational_state(db_connection):
# Revised Intent: "Check if product SKU 'ABC' is in stock (status=1) at source 'default'."
# LLM Feasibility Summary: "Product SKU 'ABC' exists. Its stock status at 'default' is 1 (In Stock)."
if not db_connection or not db_connection.is_connected():
return False, "Database connection not available for validation."
sku_to_check = "ABC" # Hardcoded based on context
source_to_check = "default" # Hardcoded
expected_status = 1 # Hardcoded
try:
cursor = db_connection.cursor(dictionary=True)
# Hardcoded SQL to re-verify the state
query = f"SELECT status FROM inventory_source_item WHERE sku = %s AND source_code = %s"
cursor.execute(query, (sku_to_check, source_to_check))
result = cursor.fetchone()
cursor.close()
if result:
if result['status'] == expected_status:
return True, f"Validation successful: SKU '{{sku_to_check}}' at source '{{source_to_check}}' has status '{{expected_status}}'."
else:
return False, f"Validation failed: SKU '{{sku_to_check}}' at source '{{source_to_check}}' has status '{{result['status']}}', expected '{{expected_status}}'."
else:
return False, f"Validation failed: SKU '{{sku_to_check}}' not found at source '{{source_to_check}}' during validation check."
except Error as e:
return False, f"Database error during validation: {{e}}"
except Exception as ex:
return False, f"Unexpected error during validation: {{ex}}"
```
Now, provide *only* the Python code for the function (`validate_query_answer` or `validate_operational_state`) based on the specific inputs given.
"""
else:
logger.error(f"Unknown task_type '{task_type}' for generating validation function prompt.")
return "# Error: Unknown task_type for validation function generation."
filled_prompt = prompt_step3_template
conversation_history.append({"role": "user", "content": filled_prompt})
validation_function_code = call_llm(conversation_history, LLM_MODEL_VALIDATION, temperature=0.1, max_tokens=2500)
if validation_function_code:
conversation_history.append({"role": "assistant", "content": validation_function_code})
clean_code = _clean_llm_python_code_response(validation_function_code) # Use helper
logger.info(f"Successfully generated validation function code for task type '{task_type}'.")
return clean_code
else:
logger.error(f"Failed to generate validation function code for task type '{task_type}'. Removing last user prompt from history.")
if conversation_history and conversation_history[-1]["role"] == "user":
conversation_history.pop()
return "# LLM failed to generate validation function or an error occurred."
def main_workflow():
"""Main orchestrator for the multi-step QA generation (SELECT-only focus)."""
logger.info("=================================================")
logger.info("=== Magento QA Gen (SELECT-Only Info Gathering & Validation) ===")
logger.info("=================================================")
global MAGENTO_SCHEMA_CONTENT, SYSTEM_PROMPT_TEMPLATE # Ensure SYSTEM_PROMPT_TEMPLATE is global
script_dir = ""
try:
script_dir = os.path.dirname(os.path.abspath(__file__))
schema_file_path = os.path.join(script_dir, "curated_schema.txt")
with open(schema_file_path, "r", encoding="utf-8") as f:
MAGENTO_SCHEMA_CONTENT = f.read()
logger.info(f"Schema loaded successfully from {schema_file_path}")
except FileNotFoundError:
logger.error(f"schema_nonempty.txt not found at {schema_file_path}. Exiting.")
return
except Exception as e:
logger.error(f"Error loading schema file: {e}. Exiting.")
return
db_connection_main_loop = create_db_connection(DB_HOST, DB_PORT, DB_USER, DB_PASS, DB_NAME)
if not db_connection_main_loop: # db_connection_main_loop is used for samples and passed around
logger.error("Initial DB connection failed. Needed for samples and prep SQL. Exiting.")
return
initialize_system_prompt(db_connection_main_loop, script_dir)
if not SYSTEM_PROMPT_TEMPLATE: # SYSTEM_PROMPT_TEMPLATE is populated by initialize_system_prompt
logger.error("System prompt initialization failed. Exiting.")
if db_connection_main_loop and db_connection_main_loop.is_connected(): db_connection_main_loop.close()
return
# Initialize conversation history with the system prompt
conversation_history = [{"role": "system", "content": SYSTEM_PROMPT_TEMPLATE}]
# Step 1: Generate Initial Tasks and Preparatory SQL
logger.info("--- Starting Step 1: Generate Initial Tasks and Preparatory SQL ---")
# Pass the conversation_history to be appended to
initial_tasks = generate_initial_tasks_and_prep_sql(conversation_history, num_tasks=5)
if not initial_tasks:
logger.error("No initial tasks generated by LLM. Exiting.")
if db_connection_main_loop and db_connection_main_loop.is_connected(): db_connection_main_loop.close()
return
logger.info(f"Step 1 completed. Received {len(initial_tasks)} initial tasks.")
output_filename = f"magento_qa_info_gathering_{datetime.now().strftime('%Y%m%d_%H%M%S')}.jsonl"
try:
with open(output_filename, "w", encoding="utf-8") as outfile:
for i, task_data in enumerate(initial_tasks):
item_num = i + 1
logger.info(f"\nProcessing Item {item_num}/{len(initial_tasks)}: \"{task_data['original_question'][:100]}...\"")
original_question = task_data["original_question"]
task_type = task_data["task_type"]
preparatory_sql_list = task_data["preparatory_sql_list"]
prep_sql_actual_results_list = []
prep_sql_results_repr_list = []
logger.info(f" Executing {len(preparatory_sql_list)} preparatory SQLs for Item {item_num}...")
current_db_conn_for_item = db_connection_main_loop
for prep_sql_idx, prep_sql in enumerate(preparatory_sql_list):
logger.info(f" Prep SQL {prep_sql_idx+1}: {prep_sql}")
if not current_db_conn_for_item or not current_db_conn_for_item.is_connected():
logger.warning(f"DB connection lost before prep SQL for item {item_num}. Attempting reconnect...")
current_db_conn_for_item = create_db_connection(DB_HOST, DB_PORT, DB_USER, DB_PASS, DB_NAME)
if not current_db_conn_for_item:
db_connection_main_loop = None
err_msg = "Error: DB connection lost and failed to reconnect during prep SQL execution."
for _ in range(prep_sql_idx, len(preparatory_sql_list)):
prep_sql_actual_results_list.append(err_msg)
prep_sql_results_repr_list.append(repr(err_msg))
logger.error(f"Failed to reconnect. Skipping rest of prep SQLs for item {item_num}.")
break
current_prep_result = execute_sql_and_get_results(current_db_conn_for_item, prep_sql, f"Prep Q{item_num}.{prep_sql_idx+1}", item_num)
prep_sql_actual_results_list.append(current_prep_result)
prep_sql_results_repr_list.append(repr(current_prep_result))
if isinstance(current_prep_result, str) and current_prep_result.startswith("Error:"):
logger.warning(f" Prep SQL {prep_sql_idx+1} for item {item_num} resulted in error: {current_prep_result}")
if current_db_conn_for_item is not db_connection_main_loop and current_db_conn_for_item is not None:
db_connection_main_loop = current_db_conn_for_item
elif not current_db_conn_for_item:
db_connection_main_loop = None
logger.info(f" Finished executing preparatory SQLs for Item {item_num}.")
logger.info(f" Starting Step 2: Refine Question and Assess Info for Item {item_num}...")
# Pass conversation_history to be appended to
llm_assessment_data = refine_question_and_assess_info(
conversation_history, original_question, task_type, preparatory_sql_list, prep_sql_results_repr_list
)
if not llm_assessment_data:
logger.error(f"Failed to get assessment from LLM for Item {item_num}. Skipping validation and saving partial.")
record = { "item_number": item_num, "original_question": original_question, "task_type": task_type,
"preparatory_sql_list": preparatory_sql_list,
"preparatory_sql_actual_results_preview": [str(r)[:200] for r in prep_sql_actual_results_list],
"status": "Failed at LLM assessment step" }
outfile.write(json.dumps(record) + "\n"); outfile.flush()
continue
revised_question = llm_assessment_data["revised_question"]
revision_justification = llm_assessment_data["revision_justification"]
llm_assessment_from_step2_value = ""
llm_feasibility_flag = None
if task_type == "query":
llm_assessment_from_step2_value = llm_assessment_data.get("llm_derived_answer", "Error: LLM did not provide derived answer.")
elif task_type == "operational_check":
llm_assessment_from_step2_value = llm_assessment_data.get("llm_feasibility_summary", "Error: LLM did not provide feasibility_summary.")
llm_feasibility_flag = llm_assessment_data.get("llm_feasibility", False) # Default to False if missing
validation_function_code = "# Validation function not generated."
if task_type == "query" or (task_type == "operational_check" and llm_feasibility_flag is True):
logger.info(f" Starting Step 3: Generate Validation Function for Item {item_num}...")
time.sleep(1)
prep_sql_list_str_for_prompt = "\n".join(preparatory_sql_list)
prep_sql_results_repr_list_str_for_prompt = "[\n" + ",\n".join(f" {r}" for r in prep_sql_results_repr_list) + "\n]"
# Pass conversation_history to be appended to
current_validation_code = generate_validation_function(
conversation_history, revised_question, task_type,
prep_sql_list_str_for_prompt,
prep_sql_results_repr_list_str_for_prompt,
llm_assessment_from_step2_value
)
if not current_validation_code or "# LLM failed" in current_validation_code:
logger.warning(f" Validation function generation failed or was incomplete for Item {item_num}.")
validation_function_code = current_validation_code if current_validation_code else "# LLM failed to generate validation function or returned empty."
else:
validation_function_code = current_validation_code
elif task_type == "operational_check" and llm_feasibility_flag is False:
logger.info(f" Skipping Step 3 (Validation Function Generation) for Item {item_num} because llm_feasibility is False.")
validation_function_code = "# Validation function generation skipped as operation was deemed not feasible."
record = {
"item_number": item_num, "original_question": original_question, "task_type": task_type,
"preparatory_sql_list": preparatory_sql_list,
"preparatory_sql_actual_results_preview": [str(r)[:200] for r in prep_sql_actual_results_list],
"full_preparatory_sql_actual_results_repr": prep_sql_results_repr_list,
"revised_question": revised_question, "revision_justification": revision_justification,
}
if task_type == "query":
record["llm_derived_answer_for_validation_func_gen"] = llm_assessment_from_step2_value
elif task_type == "operational_check":
record["llm_feasibility_summary_for_validation_func_gen"] = llm_assessment_from_step2_value
record["llm_feasibility"] = llm_feasibility_flag # Record the boolean feasibility
record["python_validation_function"] = validation_function_code
outfile.write(json.dumps(record) + "\n")
outfile.flush()
logger.info(f"Record {item_num} written to {output_filename}")
if i < len(initial_tasks) - 1:
llm_call_delay = int(os.environ.get("LLM_CALL_DELAY_SECONDS", "5"))
logger.info(f"Waiting {llm_call_delay} seconds before next item (and its LLM calls)...")
time.sleep(llm_call_delay)
except Exception as e:
logger.error(f"An critical error occurred in the main workflow: {e}", exc_info=True)
finally:
if db_connection_main_loop and db_connection_main_loop.is_connected():
db_connection_main_loop.close()
logger.info("Main database connection closed at the end of the workflow.")
elif db_connection_main_loop is None:
logger.info("Main database connection was lost and not re-established.")
logger.info(f"Workflow finished. Log file: {log_file_name}")
if __name__ == "__main__":
if OPENAI_API_KEY == "YOUR_OPENAI_API_KEY_FALLBACK":
print("CRITICAL: OpenAI API key is not set. Please set the OPENAI_API_KEY environment variable or update the script.")
logger.critical("OpenAI API key is not set. Please set the OPENAI_API_KEY environment variable or update the script.")
else:
main_workflow()
logger.info(f"Workflow finished. Log file: {log_file_name}")
"""
**Before Running:**
1. **`schema_nonempty.txt`**: **CRITICAL:** Place your full Magento `schema_nonempty.txt` file in the same directory as this Python script. The script now tries to load it.
2. **OpenAI API Key:**
* **Best Method:** Set it as an environment variable: `export OPENAI_API_KEY="sk-..."`
* Alternatively, replace `"YOUR_OPENAI_API_KEY_FALLBACK"` in the script, but this is less secure.
3. **`OPENAI_BASE_URL`**: If you are using a proxy or a non-standard OpenAI endpoint, update this. Otherwise, the default `https://api.openai.com/v1` should work for official OpenAI.
4. **Database Credentials:** Ensure `DB_HOST`, `DB_PORT`, `DB_USER`, `DB_PASS`, `DB_NAME` are correct.
5. **Install Libraries:** `pip install openai mysql-connector-python pandas`
6. **LLM Models:** `LLM_MODEL_GENERATION` and `LLM_MODEL_VALIDATION` are set to `gpt-4-turbo-preview`. You might want to use `gpt-3.5-turbo` for `LLM_MODEL_VALIDATION` to save costs/time if its quality is sufficient for generating the validation functions.
7. **Rate Limiting:** The `time.sleep(5)` (now configurable via `LLM_CALL_DELAY_SECONDS` env var, defaulting to 5) is a very basic rate limiter. If you have higher API limits or make fewer calls, you can adjust this.
**Key Improvements in this Version:**
* **Schema Loading:** The script now explicitly loads `schema_nonempty.txt` and incorporates its content into the system prompt.
* **Environment Variables:** Encourages using environment variables for API keys.
* **Robust LLM JSON Parsing:** Added more cleaning for the JSON response from the LLM.
* **Error Handling:** More `try-except` blocks, especially around LLM calls and database operations.
* **DML Handling in `execute_sql_and_get_results`:**
* Detects DML (UPDATE, INSERT, DELETE) and DDL (CREATE, ALTER, DROP).
* Commits transactions for DML/DDL.
* Rolls back DML/DDL on error.
* Returns "Rows affected: X" for DML.
* **Stringification of Results:** `datetime` and `decimal.Decimal` objects from `SELECT` queries are converted to strings before being passed to `repr()` for the LLM prompt. This makes the LLM's job of understanding the "SQL Execution Result" string easier and more consistent.
* **Logging:** Enhanced logging for better traceability, including logging the prompts sent to the LLM (at DEBUG level) and token usage.
* **Connection Management:** Improved checks for database connection status and attempts to reconnect if lost.
* **Clearer Prompts:** Refined prompts for the LLM, especially for generating validation functions, to be more explicit about input formats and expected output.
* **Configurable LLM Call Delay:** Added an environment variable `LLM_CALL_DELAY_SECONDS` for easier adjustment of the delay between LLM calls.
* **Port as Integer:** Ensured `DB_PORT` is cast to an integer for `mysql.connector.connect`.
* **Full Result for LLM:** The `full_sql_execution_result_repr_for_llm` is now included in the JSONL, which can be useful for debugging or if you need to re-prompt the LLM for a specific validation function.
This script is now much more robust and production-ready for your task. Remember to monitor the LLM costs and API rate limits.
"""

View File

@@ -0,0 +1,219 @@
--- Sample rows for table: sales_order ---
entity_id, state, status, coupon_code, protect_code, shipping_description, is_virtual, store_id, customer_id, base_discount_amount, base_discount_canceled, base_discount_invoiced, base_discount_refunded, base_grand_total, base_shipping_amount, base_shipping_canceled, base_shipping_invoiced, base_shipping_refunded, base_shipping_tax_amount, base_shipping_tax_refunded, base_subtotal, base_subtotal_canceled, base_subtotal_invoiced, base_subtotal_refunded, base_tax_amount, base_tax_canceled, base_tax_invoiced, base_tax_refunded, base_to_global_rate, base_to_order_rate, base_total_canceled, base_total_invoiced, base_total_invoiced_cost, base_total_offline_refunded, base_total_online_refunded, base_total_paid, base_total_qty_ordered, base_total_refunded, discount_amount, discount_canceled, discount_invoiced, discount_refunded, grand_total, shipping_amount, shipping_canceled, shipping_invoiced, shipping_refunded, shipping_tax_amount, shipping_tax_refunded, store_to_base_rate, store_to_order_rate, subtotal, subtotal_canceled, subtotal_invoiced, subtotal_refunded, tax_amount, tax_canceled, tax_invoiced, tax_refunded, total_canceled, total_invoiced, total_offline_refunded, total_online_refunded, total_paid, total_qty_ordered, total_refunded, can_ship_partially, can_ship_partially_item, customer_is_guest, customer_note_notify, billing_address_id, customer_group_id, edit_increment, email_sent, send_email, forced_shipment_with_invoice, payment_auth_expiration, quote_address_id, quote_id, shipping_address_id, adjustment_negative, adjustment_positive, base_adjustment_negative, base_adjustment_positive, base_shipping_discount_amount, base_subtotal_incl_tax, base_total_due, payment_authorization_amount, shipping_discount_amount, subtotal_incl_tax, total_due, weight, customer_dob, increment_id, applied_rule_ids, base_currency_code, customer_email, customer_firstname, customer_lastname, customer_middlename, customer_prefix, customer_suffix, customer_taxvat, discount_description, ext_customer_id, ext_order_id, global_currency_code, hold_before_state, hold_before_status, order_currency_code, original_increment_id, relation_child_id, relation_child_real_id, relation_parent_id, relation_parent_real_id, remote_ip, shipping_method, store_currency_code, store_name, x_forwarded_for, customer_note, created_at, updated_at, total_item_count, customer_gender, discount_tax_compensation_amount, base_discount_tax_compensation_amount, shipping_discount_tax_compensation_amount, base_shipping_discount_tax_compensation_amnt, discount_tax_compensation_invoiced, base_discount_tax_compensation_invoiced, discount_tax_compensation_refunded, base_discount_tax_compensation_refunded, shipping_incl_tax, base_shipping_incl_tax, coupon_rule_name, gift_message_id, paypal_ipn_customer_notified
1, canceled, canceled, NULL, a097e3c6e0615193119a5e6365064aae, Flat Rate - Fixed, 0, 1, 1, 0.0000, NULL, 0.0000, NULL, 36.3900, 5.0000, NULL, 5.0000, NULL, 0.0000, NULL, 29.0000, NULL, 29.0000, NULL, 2.3900, NULL, 2.3900, NULL, 1.0000, 1.0000, NULL, 36.3900, 0.0000, NULL, NULL, 36.3900, NULL, NULL, 0.0000, NULL, 0.0000, NULL, 36.3900, 5.0000, NULL, 5.0000, NULL, 0.0000, NULL, 0.0000, 0.0000, 29.0000, NULL, 29.0000, NULL, 2.3900, NULL, 2.3900, NULL, NULL, 36.3900, NULL, NULL, 36.3900, 1.0000, NULL, NULL, NULL, 0, 1, 2, 1, NULL, NULL, NULL, NULL, NULL, NULL, 1, 1, NULL, NULL, NULL, NULL, 0.0000, 31.3900, 36.3900, NULL, 0.0000, 31.3900, 36.3900, 1.0000, 1973-12-15 00:00:00, 000000001, 1, USD, roni_cost@example.com, Veronica, Costello, NULL, NULL, NULL, NULL, NULL, NULL, NULL, USD, NULL, NULL, USD, NULL, NULL, NULL, NULL, NULL, NULL, flatrate_flatrate, USD, Main Website
Main Website Store
Default Store View, NULL, NULL, 2022-03-30 01:36:37, 2023-04-23 23:35:55, 1, 2, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, NULL, NULL, 5.0000, 5.0000, NULL, NULL, 0
2, closed, closed, NULL, 4af608fd362986ce93d8aad4acba5e3c, Flat Rate - Fixed, 0, 1, 1, 0.0000, NULL, 0.0000, 0.0000, 39.6400, 5.0000, NULL, 5.0000, 5.0000, 0.0000, 0.0000, 32.0000, NULL, 32.0000, 32.0000, 2.6400, NULL, 2.6400, 2.6400, 1.0000, 1.0000, NULL, 39.6400, 0.0000, 39.6400, NULL, 39.6400, NULL, 39.6400, 0.0000, NULL, 0.0000, 0.0000, 39.6400, 5.0000, NULL, 5.0000, 5.0000, 0.0000, 0.0000, 0.0000, 0.0000, 32.0000, NULL, 32.0000, 32.0000, 2.6400, NULL, 2.6400, 2.6400, NULL, 39.6400, 39.6400, NULL, 39.6400, 1.0000, 39.6400, NULL, NULL, 0, 1, 4, 1, NULL, NULL, NULL, NULL, NULL, NULL, 2, 3, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 34.6400, 0.0000, NULL, 0.0000, 34.6400, 0.0000, 1.0000, 1973-12-15 00:00:00, 000000002, 1, USD, roni_cost@example.com, Veronica, Costello, NULL, NULL, NULL, NULL, NULL, NULL, NULL, USD, NULL, NULL, USD, NULL, NULL, NULL, NULL, NULL, NULL, flatrate_flatrate, USD, Main Website
Main Website Store
Default Store View, NULL, NULL, 2022-04-24 20:36:28, 2023-04-23 23:35:55, 1, 2, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 5.0000, 5.0000, NULL, NULL, 0
3, canceled, canceled, NULL, cbba92780eeda9267bc3c64ff4bdad71, Flat Rate - Fixed, 0, 1, 34, 0.0000, NULL, NULL, NULL, 160.2500, 15.0000, NULL, NULL, NULL, 0.0000, NULL, 145.2500, NULL, NULL, NULL, 0.0000, NULL, NULL, NULL, 1.0000, 1.0000, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0.0000, NULL, NULL, NULL, 160.2500, 15.0000, NULL, NULL, NULL, 0.0000, NULL, 0.0000, 0.0000, 145.2500, NULL, NULL, NULL, 0.0000, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 3.0000, NULL, NULL, NULL, 0, 1, 6, 1, NULL, 1, 1, NULL, NULL, NULL, 4, 5, NULL, NULL, NULL, NULL, 0.0000, 145.2500, 160.2500, NULL, 0.0000, 145.2500, 160.2500, 3.0000, NULL, 000000003, 2, USD, brian.smith@yahoo.com, Brian, Smith, NULL, NULL, NULL, NULL, NULL, NULL, NULL, USD, NULL, NULL, USD, NULL, NULL, NULL, NULL, NULL, 128.2.205.52, flatrate_flatrate, USD, Main Website
Main Website Store
Default Store View, NULL, NULL, 2022-10-27 04:32:53, 2023-04-23 23:35:56, 3, NULL, 0.0000, 0.0000, 0.0000, 0.0000, NULL, NULL, NULL, NULL, 15.0000, 15.0000, NULL, NULL, 0
4, complete, complete, NULL, 193721b51b6bd9cbbad80daeb9c2c0b9, Flat Rate - Fixed, 0, 1, 15, 0.0000, NULL, NULL, NULL, 106.0000, 15.0000, NULL, NULL, NULL, 0.0000, NULL, 91.0000, NULL, NULL, NULL, 0.0000, NULL, NULL, NULL, 1.0000, 1.0000, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0.0000, NULL, NULL, NULL, 106.0000, 15.0000, NULL, NULL, NULL, 0.0000, NULL, 0.0000, 0.0000, 91.0000, NULL, NULL, NULL, 0.0000, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 3.0000, NULL, NULL, NULL, 0, 1, 8, 1, NULL, 1, 1, NULL, NULL, NULL, 5, 7, NULL, NULL, NULL, NULL, 0.0000, 91.0000, 106.0000, NULL, 0.0000, 91.0000, 106.0000, 3.0000, NULL, 000000004, 1,2, USD, janesmith456@yahoo.com, Jane, Smith, NULL, NULL, NULL, NULL, NULL, NULL, NULL, USD, NULL, NULL, USD, NULL, NULL, NULL, NULL, NULL, 128.2.205.52, flatrate_flatrate, USD, Main Website
Main Website Store
Default Store View, NULL, NULL, 2023-02-03 23:08:03, 2023-04-23 23:35:56, 3, NULL, 0.0000, 0.0000, 0.0000, 0.0000, NULL, NULL, NULL, NULL, 15.0000, 15.0000, NULL, NULL, 0
5, canceled, canceled, NULL, 021fe2f4b27218c35c6ed8cd120cc914, Flat Rate - Fixed, 0, 1, 18, 0.0000, NULL, NULL, NULL, 137.0000, 15.0000, NULL, NULL, NULL, 0.0000, NULL, 122.0000, NULL, NULL, NULL, 0.0000, NULL, NULL, NULL, 1.0000, 1.0000, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0.0000, NULL, NULL, NULL, 137.0000, 15.0000, NULL, NULL, NULL, 0.0000, NULL, 0.0000, 0.0000, 122.0000, NULL, NULL, NULL, 0.0000, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 3.0000, NULL, NULL, NULL, 0, 1, 10, 1, NULL, 1, 1, NULL, NULL, NULL, 6, 9, NULL, NULL, NULL, NULL, 0.0000, 122.0000, 137.0000, NULL, 0.0000, 122.0000, 137.0000, 3.0000, NULL, 000000005, 2, USD, avidreader99@yahoo.com, Grace, Nguyen, NULL, NULL, NULL, NULL, NULL, NULL, NULL, USD, NULL, NULL, USD, NULL, NULL, NULL, NULL, NULL, 128.2.205.52, flatrate_flatrate, USD, Main Website
Main Website Store
Default Store View, NULL, NULL, 2022-08-24 05:11:45, 2023-04-23 23:35:57, 3, NULL, 0.0000, 0.0000, 0.0000, 0.0000, NULL, NULL, NULL, NULL, 15.0000, 15.0000, NULL, NULL, 0
--- Sample rows for table: sales_order_item ---
item_id, order_id, parent_item_id, quote_item_id, store_id, created_at, updated_at, product_id, product_type, product_options, weight, is_virtual, sku, name, description, applied_rule_ids, additional_data, is_qty_decimal, no_discount, qty_backordered, qty_canceled, qty_invoiced, qty_ordered, qty_refunded, qty_shipped, base_cost, price, base_price, original_price, base_original_price, tax_percent, tax_amount, base_tax_amount, tax_invoiced, base_tax_invoiced, discount_percent, discount_amount, base_discount_amount, discount_invoiced, base_discount_invoiced, amount_refunded, base_amount_refunded, row_total, base_row_total, row_invoiced, base_row_invoiced, row_weight, base_tax_before_discount, tax_before_discount, ext_order_item_id, locked_do_invoice, locked_do_ship, price_incl_tax, base_price_incl_tax, row_total_incl_tax, base_row_total_incl_tax, discount_tax_compensation_amount, base_discount_tax_compensation_amount, discount_tax_compensation_invoiced, base_discount_tax_compensation_invoiced, discount_tax_compensation_refunded, base_discount_tax_compensation_refunded, tax_canceled, discount_tax_compensation_canceled, tax_refunded, base_tax_refunded, discount_refunded, base_discount_refunded, gift_message_id, gift_message_available, free_shipping, weee_tax_applied, weee_tax_applied_amount, weee_tax_applied_row_amount, weee_tax_disposition, weee_tax_row_disposition, base_weee_tax_applied_amount, base_weee_tax_applied_row_amnt, base_weee_tax_disposition, base_weee_tax_row_disposition
1, 1, NULL, NULL, 1, 2023-04-19 16:15:45, 2023-04-19 21:53:06, 1428, configurable, {"info_buyRequest":{"qty":"1.0000","super_attribute":{"144":"166","93":"58"},"options":[]},"attributes_info":[{"label":"Size","value":"XS","option_id":144,"option_value":"166"},{"label":"Color","value":"Red","option_id":93,"option_value":"58"}],"simple_name":"Iris Workout Top-XS-Red","simple_sku":"WS03-XS-Red","product_calculations":1,"shipment_type":0}, 1.0000, NULL, WS03-XS-Red, Iris Workout Top, NULL, 1, NULL, 0, 0, NULL, 0.0000, 1.0000, 1.0000, 0.0000, 1.0000, NULL, 29.0000, 29.0000, 29.0000, 29.0000, 8.2500, 2.3900, 2.3900, 2.3900, 2.3900, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 29.0000, 29.0000, 29.0000, 29.0000, 1.0000, NULL, NULL, NULL, NULL, NULL, 31.3900, 31.3900, 31.3900, 31.3900, 0.0000, 0.0000, 0.0000, 0.0000, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
2, 2, NULL, NULL, 1, 2023-04-19 16:15:46, 2023-04-19 16:15:47, 1492, configurable, {"info_buyRequest":{"qty":"1.0000","super_attribute":{"144":"166","93":"50"},"options":[]},"attributes_info":[{"label":"Size","value":"XS","option_id":144,"option_value":"166"},{"label":"Color","value":"Blue","option_id":93,"option_value":"50"}],"simple_name":"Minerva LumaTech&trade; V-Tee-XS-Blue","simple_sku":"WS08-XS-Blue","product_calculations":1,"shipment_type":0}, 1.0000, NULL, WS08-XS-Blue, Minerva LumaTech&trade; V-Tee, NULL, 1, NULL, 0, 0, NULL, 0.0000, 1.0000, 1.0000, 1.0000, 1.0000, NULL, 32.0000, 32.0000, 32.0000, 32.0000, 8.2500, 2.6400, 2.6400, 2.6400, 2.6400, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 32.0000, 32.0000, 32.0000, 32.0000, 32.0000, 32.0000, 1.0000, NULL, NULL, NULL, NULL, NULL, 34.6400, 34.6400, 34.6400, 34.6400, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, NULL, NULL, 2.6400, 2.6400, 0.0000, 0.0000, NULL, NULL, 0, [], NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
3, 3, NULL, 7, 1, 2023-04-19 21:52:45, 2023-04-19 21:52:45, 1222, simple, {"info_buyRequest":{"qty":1}}, 1.0000, 0, WJ02-XS-Blue, Josie Yoga Jacket-XS-Blue, NULL, 2, NULL, 0, 0, NULL, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, NULL, 56.2500, 56.2500, 56.2500, 56.2500, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 56.2500, 56.2500, 0.0000, 0.0000, 0.0000, NULL, NULL, NULL, NULL, NULL, 56.2500, 56.2500, 56.2500, 56.2500, 0.0000, 0.0000, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
4, 3, NULL, 8, 1, 2023-04-19 21:52:45, 2023-04-19 21:52:45, 1943, simple, {"info_buyRequest":{"qty":1}}, 1.0000, 0, WSH03-30-Gray, Gwen Drawstring Bike Short-30-Gray, NULL, 2, NULL, 0, 0, NULL, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, NULL, 50.0000, 50.0000, 50.0000, 50.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 50.0000, 50.0000, 0.0000, 0.0000, 0.0000, NULL, NULL, NULL, NULL, NULL, 50.0000, 50.0000, 50.0000, 50.0000, 0.0000, 0.0000, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
5, 3, NULL, 9, 1, 2023-04-19 21:52:45, 2023-04-19 21:52:45, 1149, simple, {"info_buyRequest":{"qty":1}}, 1.0000, 0, WH09-XS-Red, Ariel Roll Sleeve Sweatshirt-XS-Red, NULL, 2, NULL, 0, 0, NULL, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, NULL, 39.0000, 39.0000, 39.0000, 39.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 39.0000, 39.0000, 0.0000, 0.0000, 0.0000, NULL, NULL, NULL, NULL, NULL, 39.0000, 39.0000, 39.0000, 39.0000, 0.0000, 0.0000, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
--- Sample rows for table: eav_entity_type ---
entity_type_id, entity_type_code, entity_model, attribute_model, entity_table, value_table_prefix, entity_id_field, is_data_sharing, data_sharing_key, default_attribute_set_id, increment_model, increment_per_store, increment_pad_length, increment_pad_char, additional_attribute_table, entity_attribute_collection
1, customer, Magento\Customer\Model\ResourceModel\Customer, Magento\Customer\Model\Attribute, customer_entity, NULL, NULL, 1, default, 1, Magento\Eav\Model\Entity\Increment\NumericValue, 0, 8, 0, customer_eav_attribute, Magento\Customer\Model\ResourceModel\Attribute\Collection
2, customer_address, Magento\Customer\Model\ResourceModel\Address, Magento\Customer\Model\Attribute, customer_address_entity, NULL, NULL, 1, default, 2, NULL, 0, 8, 0, customer_eav_attribute, Magento\Customer\Model\ResourceModel\Address\Attribute\Collection
3, catalog_category, Magento\Catalog\Model\ResourceModel\Category, Magento\Catalog\Model\ResourceModel\Eav\Attribute, catalog_category_entity, NULL, NULL, 1, default, 3, NULL, 0, 8, 0, catalog_eav_attribute, Magento\Catalog\Model\ResourceModel\Category\Attribute\Collection
4, catalog_product, Magento\Catalog\Model\ResourceModel\Product, Magento\Catalog\Model\ResourceModel\Eav\Attribute, catalog_product_entity, NULL, NULL, 1, default, 4, NULL, 0, 8, 0, catalog_eav_attribute, Magento\Catalog\Model\ResourceModel\Product\Attribute\Collection
5, order, Magento\Sales\Model\ResourceModel\Order, NULL, sales_order, NULL, NULL, 1, default, 5, Magento\Eav\Model\Entity\Increment\NumericValue, 1, 8, 0, NULL, NULL
--- Sample rows for table: customer_group ---
customer_group_id, customer_group_code, tax_class_id
0, NOT LOGGED IN, 3
1, General, 3
2, Wholesale, 3
3, Retailer, 3
--- Sample rows for table: catalog_product_entity_decimal ---
value_id, attribute_id, store_id, entity_id, value
1, 77, 0, 1, 34.000000
2, 77, 0, 2, 32.000000
3, 78, 0, 2, 32.000000
4, 77, 0, 3, 38.000000
5, 77, 0, 4, 45.000000
--- Sample rows for table: catalog_product_entity_int ---
value_id, attribute_id, store_id, entity_id, value
1, 97, 0, 1, 1
2, 99, 0, 1, 4
3, 97, 0, 2, 1
4, 99, 0, 2, 4
5, 136, 0, 2, 2
--- Sample rows for table: customer_entity ---
entity_id, website_id, email, group_id, increment_id, store_id, created_at, updated_at, is_active, disable_auto_group_change, created_in, prefix, firstname, middlename, lastname, suffix, dob, password_hash, rp_token, rp_token_created_at, default_billing, default_shipping, taxvat, confirmation, gender, failures_num, first_failure, lock_expires, session_cutoff
1, 1, roni_cost@example.com, 1, NULL, 1, 2023-04-19 16:15:35, 2023-04-19 16:15:36, 1, 0, Default Store View, NULL, Veronica, NULL, Costello, NULL, 1973-12-15, 48e901af81a098ea54ecb0b014805e7e47c68212c09e8f5e15bbc589bf7bd3a5:Q9WIXO5QZq4u8gZwjuFRbd4BhIjPPauQ:3_32_2_67108864, 0:3:+isFgVuaSN8keSmd9FhTpIXE/xH61xUg0JLYNVcXek/liRqi1rIXf3P0lvfgNGCxJRLCu1L0e9g75xwB, 2023-04-19 16:15:36, 1, 1, NULL, NULL, 2, 0, NULL, NULL, NULL
2, 1, john.smith.xyz@gmail.com, 1, NULL, 1, 2023-04-19 21:44:57, 2023-04-19 21:44:57, 1, 0, Default Store View, NULL, John, NULL, Smith, NULL, NULL, 33fe6d4fd6e7014839809617b8c2b6aa23e53ad98a34e24a950655829742cad1:cPQxgya3eXBYfqLXbNl3CQ5TgNvOInLA:3_32_2_67108864, 0:3:/mGGadZj8KkXjgeoFhwbXS/8IQ5yzvuFTrDYv3h/ZlJn3vHUBCz1jDOmFqNsqu4lBhrb1INIs1lKtfr3, 2023-04-19 21:44:57, 2, 2, NULL, NULL, NULL, 0, NULL, NULL, NULL
3, 1, jane.doe@hotmail.com, 1, NULL, 1, 2023-04-19 21:45:01, 2023-04-19 21:45:01, 1, 0, Default Store View, NULL, Jane, NULL, Doe, NULL, NULL, 884b0e2aa55762cebecda73e15e1f85a90006644e1158a1d7de76e93ece21987:aVjGvRn6eeiBIukVfKte6voIjdagqQO7:3_32_2_67108864, 0:3:5X5LH8SQd/K3EZbVdtNtHFxcqI4hEE1+KTKjHeZYjJiLbWHjwH8LfiV2U5RjnvJzm/hzwGHbiR0265pG, 2023-04-19 21:45:01, 3, 3, NULL, NULL, NULL, 0, NULL, NULL, NULL
4, 1, bbjones@gmail.com, 1, NULL, 1, 2023-04-19 21:45:04, 2023-04-19 21:45:04, 1, 0, Default Store View, NULL, Bob, NULL, Jones, NULL, NULL, 7f16d0c52333dc1f969a349b235de3657e1b73ab6739df865a92c4d31f8b2ce4:BVjUGoW9jOrGyS27egyHNDyXOkY7rBWL:3_32_2_67108864, 0:3:S62cumQcL9vOv3m8xhTYxAWMokWO6b0EiuneijkrpbkU4ABJXg2iOqMSfeTpPm6yD2vS5QkmJ75NBhq8, 2023-04-19 21:45:04, 4, 4, NULL, NULL, NULL, 0, NULL, NULL, NULL
5, 1, helloworld@yahoo.com, 1, NULL, 1, 2023-04-19 21:45:07, 2023-04-19 21:45:07, 1, 0, Default Store View, NULL, Sarah, NULL, Miller, NULL, NULL, 4b24b99f36869873948cb6dcb502a520b06fe87a34492af1682c87880f94c9e5:hrDFhhNuSpBIOROn2TZwgZaV5GPyOgLQ:3_32_2_67108864, 0:3:8iKQHmfSso2/C+Xni572SJv6PNxsNZpftsH+PRf4zgdZczDKP8xD4sAtxgBSTCdP6agG875Bu78cntI2, 2023-04-19 21:45:07, 5, 5, NULL, NULL, NULL, 0, NULL, NULL, NULL
--- Sample rows for table: catalog_category_entity ---
entity_id, attribute_set_id, parent_id, created_at, updated_at, path, position, level, children_count
1, 3, 0, 2023-04-19 15:41:34, 2023-04-19 16:15:40, 1, 0, 0, 39
2, 3, 1, 2023-04-19 15:41:34, 2023-04-19 16:15:40, 1/2, 1, 1, 38
3, 3, 2, 2023-04-19 16:12:35, 2023-04-19 16:12:35, 1/2/3, 4, 2, 3
4, 3, 3, 2023-04-19 16:12:35, 2023-04-19 16:12:35, 1/2/3/4, 1, 3, 0
5, 3, 3, 2023-04-19 16:12:35, 2023-04-19 16:12:35, 1/2/3/5, 2, 3, 0
--- Sample rows for table: customer_address_entity ---
entity_id, increment_id, parent_id, created_at, updated_at, is_active, city, company, country_id, fax, firstname, lastname, middlename, postcode, prefix, region, region_id, street, suffix, telephone, vat_id, vat_is_valid, vat_request_date, vat_request_id, vat_request_success
1, NULL, 1, 2023-04-19 16:15:36, 2023-04-19 16:15:36, 1, Calder, NULL, US, NULL, Veronica, Costello, NULL, 49628-7978, NULL, Michigan, 33, 6146 Honey Bluff Parkway, NULL, (555) 229-3326, NULL, NULL, NULL, NULL, NULL
2, NULL, 2, 2023-04-19 21:44:57, 2023-04-19 23:36:29, 1, Birmingham, NULL, US, NULL, John, Smith, NULL, 35213, NULL, Alabama, 1, 123 Main Street, NULL, 2058812302, NULL, NULL, NULL, NULL, NULL
3, NULL, 3, 2023-04-19 21:45:01, 2023-04-19 23:41:21, 1, Miami, NULL, US, NULL, Jane, Doe, NULL, 33139, NULL, Florida, 18, 567 Ocean Drive, NULL, 4123671901, NULL, NULL, NULL, NULL, NULL
4, NULL, 4, 2023-04-19 21:45:04, 2023-04-19 23:38:55, 1, Dallas, NULL, US, NULL, Bob, Jones, NULL, 75202, NULL, Texas, 57, 890 Elm Street, NULL, 2141918677, NULL, NULL, NULL, NULL, NULL
5, NULL, 5, 2023-04-19 21:45:07, 2023-04-19 23:40:48, 1, Oakland, NULL, US, NULL, Sarah, Miller, NULL, 94602, NULL, California, 12, 321 Maple Avenue, NULL, 5107819902, NULL, NULL, NULL, NULL, NULL
--- Sample rows for table: sales_order_payment ---
entity_id, parent_id, base_shipping_captured, shipping_captured, amount_refunded, base_amount_paid, amount_canceled, base_amount_authorized, base_amount_paid_online, base_amount_refunded_online, base_shipping_amount, shipping_amount, amount_paid, amount_authorized, base_amount_ordered, base_shipping_refunded, shipping_refunded, base_amount_refunded, amount_ordered, base_amount_canceled, quote_payment_id, additional_data, cc_exp_month, cc_ss_start_year, echeck_bank_name, method, cc_debug_request_body, cc_secure_verify, protection_eligibility, cc_approval, cc_last_4, cc_status_description, echeck_type, cc_debug_response_serialized, cc_ss_start_month, echeck_account_type, last_trans_id, cc_cid_status, cc_owner, cc_type, po_number, cc_exp_year, cc_status, echeck_routing_number, account_status, anet_trans_method, cc_debug_response_body, cc_ss_issue, echeck_account_name, cc_avs_status, cc_number_enc, cc_trans_id, address_status, additional_information
1, 1, 5.0000, 5.0000, NULL, 36.3900, NULL, NULL, NULL, NULL, 5.0000, 5.0000, 36.3900, NULL, 36.3900, NULL, NULL, NULL, 36.3900, NULL, NULL, NULL, NULL, NULL, NULL, checkmo, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, {"method_title":"Check \/ Money order"}
2, 2, 5.0000, 5.0000, 39.6400, 39.6400, NULL, NULL, NULL, NULL, 5.0000, 5.0000, 39.6400, NULL, 39.6400, 5.0000, 5.0000, 39.6400, 39.6400, NULL, NULL, NULL, NULL, NULL, NULL, checkmo, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, {"method_title":"Check \/ Money order"}
3, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 15.0000, 15.0000, NULL, NULL, 160.2500, NULL, NULL, NULL, 160.2500, NULL, NULL, NULL, NULL, NULL, NULL, checkmo, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, {"method_title":"Check \/ Money order"}
4, 4, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 15.0000, 15.0000, NULL, NULL, 106.0000, NULL, NULL, NULL, 106.0000, NULL, NULL, NULL, NULL, NULL, NULL, checkmo, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, {"method_title":"Check \/ Money order"}
5, 5, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 15.0000, 15.0000, NULL, NULL, 137.0000, NULL, NULL, NULL, 137.0000, NULL, NULL, NULL, NULL, NULL, NULL, checkmo, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, {"method_title":"Check \/ Money order"}
--- Sample rows for table: review ---
review_id, created_at, entity_id, entity_pk_value, status_id
1, 2023-04-19 16:15:10, 1, 1, 1
2, 2023-04-19 16:15:11, 1, 1, 1
3, 2023-04-19 16:15:11, 1, 6, 1
4, 2023-04-19 16:15:11, 1, 6, 1
5, 2023-04-19 16:15:11, 1, 6, 1
--- Sample rows for table: review_status ---
status_id, status_code
1, Approved
2, Pending
3, Not Approved
--- Sample rows for table: url_rewrite ---
url_rewrite_id, entity_type, entity_id, request_path, target_path, redirect_type, store_id, description, is_autogenerated, metadata
1, cms-page, 1, no-route, cms/page/view/page_id/1, 0, 1, NULL, 1, NULL
2, cms-page, 2, home, cms/page/view/page_id/2, 0, 1, NULL, 1, NULL
3, cms-page, 3, enable-cookies, cms/page/view/page_id/3, 0, 1, NULL, 1, NULL
4, cms-page, 4, privacy-policy-cookie-restriction-mode, cms/page/view/page_id/4, 0, 1, NULL, 1, NULL
5, category, 3, gear.html, catalog/category/view/id/3, 0, 1, NULL, 1, NULL
--- Sample rows for table: catalog_category_product ---
entity_id, category_id, product_id, position
1, 3, 1, 0
2, 4, 1, 0
3, 3, 2, 0
4, 7, 2, 0
5, 4, 2, 0
--- Sample rows for table: catalog_product_entity_varchar ---
value_id, attribute_id, store_id, entity_id, value
1, 73, 0, 1, Joust Duffle Bag
2, 121, 0, 1, joust-duffle-bag
3, 87, 0, 1, /c/o/computer-test.jpg
4, 88, 0, 1, /c/o/computer-test.jpg
5, 89, 0, 1, /c/o/computer-test.jpg
--- Sample rows for table: eav_attribute ---
attribute_id, entity_type_id, attribute_code, attribute_model, backend_model, backend_type, backend_table, frontend_model, frontend_input, frontend_label, frontend_class, source_model, is_required, is_user_defined, default_value, is_unique, note
1, 1, website_id, NULL, Magento\Customer\Model\Customer\Attribute\Backend\Website, static, NULL, NULL, select, Associate to Website, NULL, Magento\Customer\Model\Customer\Attribute\Source\Website, 1, 0, NULL, 0, NULL
2, 1, store_id, NULL, Magento\Customer\Model\Customer\Attribute\Backend\Store, static, NULL, NULL, select, Create In, NULL, Magento\Customer\Model\Customer\Attribute\Source\Store, 1, 0, NULL, 0, NULL
3, 1, created_in, NULL, NULL, static, NULL, NULL, text, Created From, NULL, NULL, 0, 0, NULL, 0, NULL
4, 1, prefix, NULL, NULL, static, NULL, NULL, text, Name Prefix, NULL, NULL, 0, 0, NULL, 0, NULL
5, 1, firstname, NULL, NULL, static, NULL, NULL, text, First Name, NULL, NULL, 1, 0, NULL, 0, NULL
--- Sample rows for table: store_website ---
website_id, code, name, sort_order, default_group_id, is_default
0, admin, Admin, 0, 0, 0
1, base, Main Website, 0, 1, 1
--- Sample rows for table: review_detail ---
detail_id, review_id, store_id, title, detail, nickname, customer_id
1, 1, 1, I prefer more compartments, I prefer more compartments. If you don't mind putting everything in one space, it's fine. Good for the gym., Chi, NULL
2, 2, 1, I use it a lot , It's a good size and I use it a lot. My only issue with it was I wanted the handles to be longer so I can wear it on my back., Filiberto, NULL
3, 3, 1, I've had this thing for really long, I've had this thing for a really long time and it barely shows any signs of wear and tear. It's really big, too! I've taken it on day trips as well as short vacations and usually have no trouble finding room for my stuff., Herb, NULL
4, 4, 1, Decent bag, Decent bag. I keep my stuff in it for work and the gym. It's nice and roomy. I wish it had a more sophisticated design, though. Kinda looks like it's for kids., Craig, NULL
5, 5, 1, Screwed up my back, I can't believe they're claiming these straps are "padded." Wearing this thing to class for a semester totally screwed up my back, and my shoulders would start to ache after a few minutes where the straps dug in., Orville, NULL
--- Sample rows for table: core_config_data ---
config_id, scope, scope_id, path, value, updated_at
1, default, 0, web/seo/use_rewrites, 1, 2023-04-19 15:41:22
2, default, 0, web/unsecure/base_url, http://localhost:28083/, 2025-06-03 03:27:41
3, default, 0, general/locale/code, en_US, 2023-04-19 15:41:23
4, default, 0, general/locale/timezone, America/New_York, 2023-04-19 15:41:23
5, default, 0, currency/options/base, USD, 2023-04-19 15:41:24
--- Sample rows for table: sales_order_grid ---
entity_id, status, store_id, store_name, customer_id, base_grand_total, base_total_paid, grand_total, total_paid, increment_id, base_currency_code, order_currency_code, shipping_name, billing_name, created_at, updated_at, billing_address, shipping_address, shipping_information, customer_email, customer_group, subtotal, shipping_and_handling, customer_name, payment_method, total_refunded, pickup_location_code
1, canceled, 1, Main Website
Main Website Store
Default Store View, 1, 36.3900, 36.3900, 36.3900, 36.3900, 000000001, USD, USD, Veronica Costello, Veronica Costello, 2022-03-30 01:36:37, 2023-04-23 23:35:55, 6146 Honey Bluff Parkway,Calder,Michigan,49628-7978, 6146 Honey Bluff Parkway,Calder,Michigan,49628-7978, Flat Rate - Fixed, roni_cost@example.com, 1, 29.0000, 5.0000, Veronica Costello, checkmo, NULL, NULL
2, closed, 1, Main Website
Main Website Store
Default Store View, 1, 39.6400, 39.6400, 39.6400, 39.6400, 000000002, USD, USD, Veronica Costello, Veronica Costello, 2022-04-24 20:36:28, 2023-04-23 23:35:55, 6146 Honey Bluff Parkway,Calder,Michigan,49628-7978, 6146 Honey Bluff Parkway,Calder,Michigan,49628-7978, Flat Rate - Fixed, roni_cost@example.com, 1, 32.0000, 5.0000, Veronica Costello, checkmo, 39.6400, NULL
3, canceled, 1, Main Website
Main Website Store
Default Store View, 34, 160.2500, NULL, 160.2500, NULL, 000000003, USD, USD, Brian Smith, Brian Smith, 2022-10-27 04:32:53, 2023-04-23 23:35:56, 456 Las Vegas Blvd S,Las Vegas,Nevada,89109, 456 Las Vegas Blvd S,Las Vegas,Nevada,89109, Flat Rate - Fixed, brian.smith@yahoo.com, 1, 145.2500, 15.0000, Brian Smith, checkmo, NULL, NULL
4, complete, 1, Main Website
Main Website Store
Default Store View, 15, 106.0000, NULL, 106.0000, NULL, 000000004, USD, USD, Jane Smith, Jane Smith, 2023-02-03 23:08:03, 2023-04-23 23:35:56, 456 Beverly Hills Blvd,Beverly Hills,California,90210, 456 Beverly Hills Blvd,Beverly Hills,California,90210, Flat Rate - Fixed, janesmith456@yahoo.com, 1, 91.0000, 15.0000, Jane Smith, checkmo, NULL, NULL
5, canceled, 1, Main Website
Main Website Store
Default Store View, 18, 137.0000, NULL, 137.0000, NULL, 000000005, USD, USD, Grace Nguyen, Grace Nguyen, 2022-08-24 05:11:45, 2023-04-23 23:35:57, 789 Harvard Square,Cambridge,Massachusetts,02138, 789 Harvard Square,Cambridge,Massachusetts,02138, Flat Rate - Fixed, avidreader99@yahoo.com, 1, 122.0000, 15.0000, Grace Nguyen, checkmo, NULL, NULL
--- Sample rows for table: cataloginventory_stock_status ---
product_id, website_id, stock_id, qty, stock_status
1, 0, 1, 100.0000, 0
2, 0, 1, 100.0000, 1
3, 0, 1, 100.0000, 1
4, 0, 1, 100.0000, 1
5, 0, 1, 100.0000, 1
--- Sample rows for table: inventory_source_item ---
source_item_id, source_code, sku, quantity, status
1, default, 24-MB01, 100.0000, 1
2, default, 24-MB04, 100.0000, 1
3, default, 24-MB03, 100.0000, 1
4, default, 24-MB05, 100.0000, 1
5, default, 24-MB06, 100.0000, 1
--- Sample rows for table: catalog_product_index_price ---
entity_id, customer_group_id, website_id, tax_class_id, price, final_price, min_price, max_price, tier_price
1, 0, 1, 0, 34.000000, 34.000000, 34.000000, 34.000000, NULL
1, 1, 1, 0, 34.000000, 27.200000, 27.200000, 34.000000, NULL
1, 2, 1, 0, 34.000000, 34.000000, 34.000000, 34.000000, NULL
1, 3, 1, 0, 34.000000, 34.000000, 34.000000, 34.000000, NULL
2, 0, 1, 2, 32.000000, 32.000000, 32.000000, 32.000000, NULL
--- Sample rows for table: store ---
store_id, code, website_id, group_id, name, sort_order, is_active
0, admin, 0, 0, Admin, 0, 1
1, default, 1, 1, Default Store View, 0, 1
--- Sample rows for table: catalog_product_entity ---
entity_id, attribute_set_id, type_id, sku, has_options, required_options, created_at, updated_at
1, 15, simple, 24-MB01, 0, 0, 2023-04-19 16:12:35, 2023-04-19 16:48:23
2, 15, simple, 24-MB04, 0, 0, 2023-04-19 16:12:37, 2023-04-19 16:12:37
3, 15, simple, 24-MB03, 0, 0, 2023-04-19 16:12:38, 2023-04-19 16:12:38
4, 15, simple, 24-MB05, 0, 0, 2023-04-19 16:12:38, 2023-04-19 16:12:38
5, 15, simple, 24-MB06, 0, 0, 2023-04-19 16:12:39, 2023-04-19 16:12:39
--- Sample rows for table: catalog_category_entity_varchar ---
value_id, attribute_id, store_id, entity_id, value
1, 45, 0, 1, Root Catalog
2, 45, 0, 2, Default Category
3, 52, 0, 2, PRODUCTS
4, 45, 0, 3, Gear
5, 52, 0, 3, PAGE