Update app.py
Browse files
app.py
CHANGED
@@ -1,14 +1,225 @@
|
|
1 |
import os
|
2 |
import sqlite3
|
|
|
3 |
import json
|
4 |
import openai
|
|
|
|
|
5 |
from langgraph.graph import StateGraph, START
|
6 |
from typing import TypedDict, Optional
|
7 |
|
8 |
# Set your OpenAI API key from environment variable
|
9 |
openai.api_key = os.getenv("OPENAI_API_KEY")
|
10 |
|
11 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
12 |
class SQLExecutionState(TypedDict):
|
13 |
sql_query: str
|
14 |
structured_metadata: Optional[dict]
|
@@ -125,7 +336,6 @@ def execution_agent(state: SQLExecutionState) -> SQLExecutionState:
|
|
125 |
graph.add_node("SQL Execution", execution_agent)
|
126 |
|
127 |
# ------------------ Define Execution Flow ------------------
|
128 |
-
# Adding an edge from the reserved START node to the first agent establishes the entrypoint.
|
129 |
graph.add_edge(START, "Query Understanding")
|
130 |
graph.add_edge("Query Understanding", "Query Validation")
|
131 |
graph.add_edge("Query Validation", "Query Optimization")
|
|
|
1 |
import os
|
2 |
import sqlite3
|
3 |
+
import random
|
4 |
import json
|
5 |
import openai
|
6 |
+
from datetime import timedelta
|
7 |
+
from faker import Faker
|
8 |
from langgraph.graph import StateGraph, START
|
9 |
from typing import TypedDict, Optional
|
10 |
|
11 |
# Set your OpenAI API key from environment variable
|
12 |
openai.api_key = os.getenv("OPENAI_API_KEY")
|
13 |
|
14 |
+
# ------------------ Initialize SQLite Database with Faker Data ------------------
|
15 |
+
def init_db():
|
16 |
+
fake = Faker()
|
17 |
+
conn = sqlite3.connect("complex_test_db.sqlite", timeout=20)
|
18 |
+
cursor = conn.cursor()
|
19 |
+
|
20 |
+
# Drop existing tables if they exist (for a clean setup)
|
21 |
+
cursor.executescript("""
|
22 |
+
DROP TABLE IF EXISTS order_items;
|
23 |
+
DROP TABLE IF EXISTS orders;
|
24 |
+
DROP TABLE IF EXISTS products;
|
25 |
+
DROP TABLE IF EXISTS customers;
|
26 |
+
DROP TABLE IF EXISTS payments;
|
27 |
+
DROP TABLE IF EXISTS shipment;
|
28 |
+
""")
|
29 |
+
|
30 |
+
# Create Customers Table
|
31 |
+
cursor.execute("""
|
32 |
+
CREATE TABLE customers (
|
33 |
+
customer_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
34 |
+
name TEXT NOT NULL,
|
35 |
+
email TEXT UNIQUE NOT NULL,
|
36 |
+
phone TEXT NOT NULL,
|
37 |
+
address TEXT NOT NULL,
|
38 |
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
39 |
+
);
|
40 |
+
""")
|
41 |
+
|
42 |
+
# Create Products Table
|
43 |
+
cursor.execute("""
|
44 |
+
CREATE TABLE products (
|
45 |
+
product_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
46 |
+
name TEXT NOT NULL,
|
47 |
+
category TEXT NOT NULL,
|
48 |
+
price DECIMAL(10,2) NOT NULL,
|
49 |
+
stock_quantity INTEGER NOT NULL
|
50 |
+
);
|
51 |
+
""")
|
52 |
+
|
53 |
+
# Create Orders Table
|
54 |
+
cursor.execute("""
|
55 |
+
CREATE TABLE orders (
|
56 |
+
order_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
57 |
+
customer_id INTEGER NOT NULL,
|
58 |
+
order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
59 |
+
total_amount DECIMAL(10,2) NOT NULL,
|
60 |
+
status TEXT CHECK(status IN ('Pending', 'Shipped', 'Delivered', 'Cancelled')) NOT NULL,
|
61 |
+
FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
|
62 |
+
);
|
63 |
+
""")
|
64 |
+
|
65 |
+
# Create Order Items Table (Many-to-Many relationship between Orders and Products)
|
66 |
+
cursor.execute("""
|
67 |
+
CREATE TABLE order_items (
|
68 |
+
order_item_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
69 |
+
order_id INTEGER NOT NULL,
|
70 |
+
product_id INTEGER NOT NULL,
|
71 |
+
quantity INTEGER NOT NULL,
|
72 |
+
subtotal DECIMAL(10,2) NOT NULL,
|
73 |
+
FOREIGN KEY (order_id) REFERENCES orders(order_id),
|
74 |
+
FOREIGN KEY (product_id) REFERENCES products(product_id)
|
75 |
+
);
|
76 |
+
""")
|
77 |
+
|
78 |
+
# Create Payments Table (One-to-One relationship with Orders)
|
79 |
+
cursor.execute("""
|
80 |
+
CREATE TABLE payments (
|
81 |
+
payment_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
82 |
+
order_id INTEGER UNIQUE NOT NULL,
|
83 |
+
payment_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
84 |
+
amount DECIMAL(10,2) NOT NULL,
|
85 |
+
payment_method TEXT CHECK(payment_method IN ('Credit Card', 'Debit Card', 'PayPal', 'Bank Transfer')) NOT NULL,
|
86 |
+
status TEXT CHECK(status IN ('Success', 'Failed', 'Pending')) NOT NULL,
|
87 |
+
FOREIGN KEY (order_id) REFERENCES orders(order_id)
|
88 |
+
);
|
89 |
+
""")
|
90 |
+
|
91 |
+
# Create Shipment Table (One-to-One relationship with Orders)
|
92 |
+
cursor.execute("""
|
93 |
+
CREATE TABLE shipment (
|
94 |
+
shipment_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
95 |
+
order_id INTEGER UNIQUE NOT NULL,
|
96 |
+
shipment_date TIMESTAMP,
|
97 |
+
delivery_date TIMESTAMP,
|
98 |
+
carrier TEXT NOT NULL,
|
99 |
+
tracking_number TEXT UNIQUE,
|
100 |
+
status TEXT CHECK(status IN ('Processing', 'Shipped', 'Delivered')) NOT NULL,
|
101 |
+
FOREIGN KEY (order_id) REFERENCES orders(order_id)
|
102 |
+
);
|
103 |
+
""")
|
104 |
+
|
105 |
+
conn.commit()
|
106 |
+
|
107 |
+
# Insert substantial data
|
108 |
+
NUM_CUSTOMERS = 1000
|
109 |
+
NUM_PRODUCTS = 500
|
110 |
+
NUM_ORDERS = 2000
|
111 |
+
NUM_ORDER_ITEMS = 5000
|
112 |
+
NUM_PAYMENTS = NUM_ORDERS
|
113 |
+
NUM_SHIPMENTS = int(NUM_ORDERS * 0.8) # 80% of orders are shipped
|
114 |
+
|
115 |
+
# Insert Customers (Ensure unique emails)
|
116 |
+
customers = []
|
117 |
+
unique_emails = set()
|
118 |
+
while len(customers) < NUM_CUSTOMERS:
|
119 |
+
name = fake.name()
|
120 |
+
email = fake.email()
|
121 |
+
phone = fake.phone_number()
|
122 |
+
address = fake.address().replace("\n", ", ")
|
123 |
+
if email not in unique_emails:
|
124 |
+
customers.append((name, email, phone, address))
|
125 |
+
unique_emails.add(email)
|
126 |
+
|
127 |
+
cursor.executemany("""
|
128 |
+
INSERT INTO customers (name, email, phone, address)
|
129 |
+
VALUES (?, ?, ?, ?);
|
130 |
+
""", customers)
|
131 |
+
|
132 |
+
# Insert Products
|
133 |
+
products = []
|
134 |
+
categories = ["Electronics", "Clothing", "Books", "Home Appliances", "Toys"]
|
135 |
+
for _ in range(NUM_PRODUCTS):
|
136 |
+
products.append((fake.word().capitalize(), random.choice(categories), round(random.uniform(5, 500), 2), random.randint(10, 500)))
|
137 |
+
|
138 |
+
cursor.executemany("""
|
139 |
+
INSERT INTO products (name, category, price, stock_quantity)
|
140 |
+
VALUES (?, ?, ?, ?);
|
141 |
+
""", products)
|
142 |
+
|
143 |
+
# Fetch inserted customer and product IDs
|
144 |
+
cursor.execute("SELECT customer_id FROM customers;")
|
145 |
+
customer_ids = [row[0] for row in cursor.fetchall()]
|
146 |
+
|
147 |
+
cursor.execute("SELECT product_id FROM products;")
|
148 |
+
product_ids = [row[0] for row in cursor.fetchall()]
|
149 |
+
|
150 |
+
# Insert Orders
|
151 |
+
orders = []
|
152 |
+
statuses = ["Pending", "Shipped", "Delivered", "Cancelled"]
|
153 |
+
for _ in range(NUM_ORDERS):
|
154 |
+
customer_id = random.choice(customer_ids)
|
155 |
+
total_amount = round(random.uniform(20, 2000), 2)
|
156 |
+
status = random.choice(statuses)
|
157 |
+
order_date = fake.date_time_between(start_date="-1y", end_date="now")
|
158 |
+
orders.append((customer_id, order_date, total_amount, status))
|
159 |
+
|
160 |
+
cursor.executemany("""
|
161 |
+
INSERT INTO orders (customer_id, order_date, total_amount, status)
|
162 |
+
VALUES (?, ?, ?, ?);
|
163 |
+
""", orders)
|
164 |
+
|
165 |
+
# Fetch inserted order IDs
|
166 |
+
cursor.execute("SELECT order_id FROM orders;")
|
167 |
+
order_ids = [row[0] for row in cursor.fetchall()]
|
168 |
+
|
169 |
+
# Insert Order Items
|
170 |
+
order_items = []
|
171 |
+
for _ in range(NUM_ORDER_ITEMS):
|
172 |
+
order_id = random.choice(order_ids)
|
173 |
+
product_id = random.choice(product_ids)
|
174 |
+
quantity = random.randint(1, 5)
|
175 |
+
subtotal = round(quantity * random.uniform(5, 500), 2)
|
176 |
+
order_items.append((order_id, product_id, quantity, subtotal))
|
177 |
+
|
178 |
+
cursor.executemany("""
|
179 |
+
INSERT INTO order_items (order_id, product_id, quantity, subtotal)
|
180 |
+
VALUES (?, ?, ?, ?);
|
181 |
+
""", order_items)
|
182 |
+
|
183 |
+
# Insert Payments
|
184 |
+
payment_methods = ["Credit Card", "Debit Card", "PayPal", "Bank Transfer"]
|
185 |
+
payment_statuses = ["Success", "Failed", "Pending"]
|
186 |
+
payments = []
|
187 |
+
for order_id in order_ids[:NUM_PAYMENTS]:
|
188 |
+
amount = round(random.uniform(20, 2000), 2)
|
189 |
+
payment_method = random.choice(payment_methods)
|
190 |
+
status = random.choice(payment_statuses)
|
191 |
+
payments.append((order_id, fake.date_time_between(start_date="-1y", end_date="now"), amount, payment_method, status))
|
192 |
+
|
193 |
+
cursor.executemany("""
|
194 |
+
INSERT INTO payments (order_id, payment_date, amount, payment_method, status)
|
195 |
+
VALUES (?, ?, ?, ?, ?);
|
196 |
+
""", payments)
|
197 |
+
|
198 |
+
# Insert Shipments (for 80% of orders)
|
199 |
+
carriers = ["FedEx", "UPS", "DHL", "USPS"]
|
200 |
+
shipments = []
|
201 |
+
for order_id in order_ids[:NUM_SHIPMENTS]:
|
202 |
+
shipment_date = fake.date_time_between(start_date="-1y", end_date="now")
|
203 |
+
delivery_date = shipment_date + timedelta(days=random.randint(1, 10))
|
204 |
+
tracking_number = fake.uuid4()
|
205 |
+
carrier = random.choice(carriers)
|
206 |
+
status = random.choice(["Processing", "Shipped", "Delivered"])
|
207 |
+
shipments.append((order_id, shipment_date, delivery_date, carrier, tracking_number, status))
|
208 |
+
|
209 |
+
cursor.executemany("""
|
210 |
+
INSERT INTO shipment (order_id, shipment_date, delivery_date, carrier, tracking_number, status)
|
211 |
+
VALUES (?, ?, ?, ?, ?, ?);
|
212 |
+
""", shipments)
|
213 |
+
|
214 |
+
conn.commit()
|
215 |
+
cursor.close()
|
216 |
+
conn.close()
|
217 |
+
print("✅ Database setup complete with complex relationships and substantial data!")
|
218 |
+
|
219 |
+
# Initialize the database on app startup
|
220 |
+
init_db()
|
221 |
+
|
222 |
+
# ------------------ Define State for the Workflow ------------------
|
223 |
class SQLExecutionState(TypedDict):
|
224 |
sql_query: str
|
225 |
structured_metadata: Optional[dict]
|
|
|
336 |
graph.add_node("SQL Execution", execution_agent)
|
337 |
|
338 |
# ------------------ Define Execution Flow ------------------
|
|
|
339 |
graph.add_edge(START, "Query Understanding")
|
340 |
graph.add_edge("Query Understanding", "Query Validation")
|
341 |
graph.add_edge("Query Validation", "Query Optimization")
|