CafeAgentX / agents /database_agent.py
LKTs's picture
Upload 65 files
ba71fca verified
Raw History Blame Contribute Delete
5.01 kB
from agents.base import BaseAgent
import sqlite3
import pandas as pd
from datetime import datetime
import re
class CoffeeDatabaseAgent(BaseAgent):
def __init__(self, db_path: str, llm_agent):
self.db_path = db_path
self.llm_agent = llm_agent
def check_connection(self) -> bool:
try:
conn = sqlite3.connect(self.db_path)
conn.execute("SELECT 1;")
conn.close()
return True
except Exception as e:
print(f"[DatabaseAgent] DB Connection Error: {e}")
return False
def get_schema_overview(self) -> str:
try:
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
tables = cursor.execute("SELECT name FROM sqlite_master WHERE type='table'").fetchall()
if not tables:
return "(No tables found)"
lines = []
for (table,) in tables:
columns = cursor.execute(f"PRAGMA table_info({table})").fetchall()
col_list = [col[1] for col in columns]
col_meta = []
for col in col_list:
if any(kw in col.lower() for kw in ["status", "type", "category", "state", "flag"]):
vals = cursor.execute(f"SELECT DISTINCT {col} FROM {table} LIMIT 21").fetchall()
vals = [str(v[0]) for v in vals if v[0] is not None]
if 1 < len(vals) <= 20:
col_meta.append(f"{col}: [{', '.join(vals)}]")
if col_meta:
lines.append(f"{table}({', '.join(col_list)}) # " + "; ".join(col_meta))
else:
lines.append(f"{table}({', '.join(col_list)})")
conn.close()
return "\n".join(lines)
except Exception as e:
return f"(Schema error: {e})"
def llm_to_sql(self, command: str, schema: str) -> str:
prompt = (
"You are a database specialist agent. Given the schema and user request, generate a single, safe SQL SELECT statement. "
"If you cannot answer with available tables/columns, reply NO_SQL.\n"
f"Database schema overview:\n{schema}\n"
f"User request: {command}\nSQL:"
)
response = self.llm_agent.invoke([{"role": "user", "content": prompt}])
if hasattr(response, 'content'):
sql = response.content
else:
sql = str(response)
match = re.search(r"```sql\s*(.*?)```", sql, re.DOTALL | re.IGNORECASE)
if match:
sql = match.group(1).strip()
elif sql.startswith("```") and sql.endswith("```"):
sql = sql[3:-3].strip()
elif sql.upper().startswith("SQL:"):
sql = sql[4:].strip()
return sql
def query_database(self, sql: str, max_rows: int = 15) -> pd.DataFrame:
if sql.upper() == "NO_SQL":
raise ValueError("Cannot answer this question with the given schema.")
conn = sqlite3.connect(self.db_path)
try:
df = pd.read_sql_query(sql, conn)
finally:
conn.close()
return df.head(max_rows)
def process(self, state: dict) -> dict:
command = ""
for a in state.get("assigned_agents", []):
if a["agent"] == "coffee_db_agent":
command = a["command"]
break
logs = []
now = datetime.now().isoformat(timespec='seconds')
logs.append(f"[{now}] [DatabaseAgent] Received command: '{command}'")
if not self.check_connection():
log = f"[{now}] [DatabaseAgent][ERROR] Cannot connect to database ({self.db_path})"
return {"result": "❌ Database connection failed.", "logs": [log]}
schema = self.get_schema_overview()
logs.append(f"[{now}] [DatabaseAgent] Schema overview: {schema}")
try:
sql = self.llm_to_sql(command, schema)
logs.append(f"[{now}] [DatabaseAgent] LLM generated SQL: {sql}")
except Exception as e:
log = f"[{now}] [DatabaseAgent][ERROR] LLM to SQL failed: {e}"
return {"result": f"❌ [LLM Error]: {e}", "logs": logs + [log]}
try:
df = self.query_database(sql)
if df.empty:
result = "No data found for your request."
else:
result = f"Query result:\n{df.to_string(index=False)}"
logs.append(f"[{now}] [DatabaseAgent] Query executed successfully. Rows: {len(df)}")
except Exception as e:
result = f"❌ [DB Error]: {e}\n(SQL: {sql})"
logs.append(f"[{now}] [DatabaseAgent][ERROR] Query execution failed: {e}")
logs.append(f"[{now}] [DatabaseAgent] Done. Result set in assigned_agents.")
for a in state.get("assigned_agents", []):
if a["agent"] == "coffee_db_agent":
a["result"] = result
return {"logs": logs}