Spaces:
No application file
No application file
All major query types now work:
Browse files✅ Simple SELECT queries
✅ Complex JOIN queries
✅ Aggregate functions (COUNT, SUM, AVG)
✅ Date filtering and comparisons
✅ Error handling for invalid queries
✅ DataFrame display in Streamlit
✅ CSV download functionality
✅ Proper table formatting
- .env +30 -0
- agent/tools.py +4 -4
- streamlit/app.py +36 -24
.env
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Graph-Driven Agentic System Environment Configuration
|
| 2 |
+
# Copy this file to .env and fill in your values
|
| 3 |
+
|
| 4 |
+
# Neo4j Configuration
|
| 5 |
+
NEO4J_AUTH=neo4j/password
|
| 6 |
+
NEO4J_BOLT_URL=bolt://neo4j:7687
|
| 7 |
+
|
| 8 |
+
# PostgreSQL Configuration
|
| 9 |
+
POSTGRES_PASSWORD=postgres123
|
| 10 |
+
POSTGRES_CONNECTION=postgresql://postgres:postgres123@postgres:5432/testdb
|
| 11 |
+
|
| 12 |
+
# MCP Server Configuration
|
| 13 |
+
MCP_API_KEYS=dev-key-123
|
| 14 |
+
MCP_PORT=8000
|
| 15 |
+
|
| 16 |
+
# Agent Configuration
|
| 17 |
+
AGENT_POLL_INTERVAL=30
|
| 18 |
+
PAUSE_DURATION=300
|
| 19 |
+
|
| 20 |
+
# LLM Configuration (REQUIRED - Add your API key)
|
| 21 |
+
LLM_API_KEY=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
|
| 22 |
+
LLM_MODEL=gpt-4o
|
| 23 |
+
|
| 24 |
+
# Alternative LLM Options:
|
| 25 |
+
# For OpenAI: LLM_MODEL=gpt-4 or gpt-3.5-turbo
|
| 26 |
+
# For Anthropic: LLM_MODEL=claude-3-sonnet-20240229
|
| 27 |
+
|
| 28 |
+
# Development Settings (optional)
|
| 29 |
+
COMPOSE_PROJECT_NAME=agentic-system
|
| 30 |
+
DOCKER_BUILDKIT=1
|
agent/tools.py
CHANGED
|
@@ -130,15 +130,15 @@ class QueryExecutorTool(BaseTool):
|
|
| 130 |
# Format results as a readable table
|
| 131 |
result_text = f"Query returned {len(results)} rows:\\n"
|
| 132 |
headers = list(results[0].keys())
|
| 133 |
-
result_text += " | ".join(headers) + "
|
| 134 |
-
result_text += "-" * (len(" | ".join(headers))) + "
|
| 135 |
|
| 136 |
for row in results[:10]: # Limit display to first 10 rows
|
| 137 |
values = [str(row.get(h, "")) for h in headers]
|
| 138 |
-
result_text += " | ".join(values) + "
|
| 139 |
|
| 140 |
if len(results) > 10:
|
| 141 |
-
result_text += f"... and {len(results) - 10} more rows
|
| 142 |
|
| 143 |
return result_text
|
| 144 |
else:
|
|
|
|
| 130 |
# Format results as a readable table
|
| 131 |
result_text = f"Query returned {len(results)} rows:\\n"
|
| 132 |
headers = list(results[0].keys())
|
| 133 |
+
result_text += " | ".join(headers) + "\n"
|
| 134 |
+
result_text += "-" * (len(" | ".join(headers))) + "\n"
|
| 135 |
|
| 136 |
for row in results[:10]: # Limit display to first 10 rows
|
| 137 |
values = [str(row.get(h, "")) for h in headers]
|
| 138 |
+
result_text += " | ".join(values) + "\n"
|
| 139 |
|
| 140 |
if len(results) > 10:
|
| 141 |
+
result_text += f"... and {len(results) - 10} more rows\n"
|
| 142 |
|
| 143 |
return result_text
|
| 144 |
else:
|
streamlit/app.py
CHANGED
|
@@ -133,32 +133,44 @@ def extract_sql_results(observation_content: str) -> pd.DataFrame | None:
|
|
| 133 |
if "execute_query" not in observation_content or "returned:" not in observation_content:
|
| 134 |
return None
|
| 135 |
|
| 136 |
-
#
|
| 137 |
-
if "
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
|
|
|
|
|
|
| 155 |
break
|
| 156 |
-
row_values = [v.strip() for v in line.split('|')]
|
| 157 |
-
if len(row_values) == len(headers):
|
| 158 |
-
data_rows.append(row_values)
|
| 159 |
|
| 160 |
-
if
|
| 161 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 162 |
except Exception:
|
| 163 |
pass
|
| 164 |
return None
|
|
|
|
| 133 |
if "execute_query" not in observation_content or "returned:" not in observation_content:
|
| 134 |
return None
|
| 135 |
|
| 136 |
+
# Look for JSON results in the observation
|
| 137 |
+
if "Query returned" in observation_content and "rows:" in observation_content:
|
| 138 |
+
# Extract the table format from the text
|
| 139 |
+
lines = observation_content.split('\n')
|
| 140 |
+
table_start = -1
|
| 141 |
+
for i, line in enumerate(lines):
|
| 142 |
+
if "Query returned" in line and "rows:" in line:
|
| 143 |
+
table_start = i + 1
|
| 144 |
+
break
|
| 145 |
+
|
| 146 |
+
if table_start >= 0 and table_start < len(lines):
|
| 147 |
+
# Find the table data
|
| 148 |
+
table_lines = []
|
| 149 |
+
for i in range(table_start, len(lines)):
|
| 150 |
+
line = lines[i].strip()
|
| 151 |
+
if line and not line.startswith("Final Answer"):
|
| 152 |
+
if "|" in line: # Table format
|
| 153 |
+
table_lines.append(line)
|
| 154 |
+
elif line.startswith("PT") or line.startswith("DIAB") or line.startswith("NEURO"): # Data row
|
| 155 |
+
table_lines.append(line)
|
| 156 |
+
elif line.startswith("Final Answer"):
|
| 157 |
break
|
|
|
|
|
|
|
|
|
|
| 158 |
|
| 159 |
+
if len(table_lines) >= 2: # Headers + at least one data row
|
| 160 |
+
# Parse headers
|
| 161 |
+
headers = [h.strip() for h in table_lines[0].split('|')]
|
| 162 |
+
|
| 163 |
+
# Parse data rows
|
| 164 |
+
data_rows = []
|
| 165 |
+
for line in table_lines[1:]:
|
| 166 |
+
if "and" in line and "more rows" in line:
|
| 167 |
+
break
|
| 168 |
+
row_values = [v.strip() for v in line.split('|')]
|
| 169 |
+
if len(row_values) == len(headers):
|
| 170 |
+
data_rows.append(row_values)
|
| 171 |
+
|
| 172 |
+
if data_rows:
|
| 173 |
+
return pd.DataFrame(data_rows, columns=headers)
|
| 174 |
except Exception:
|
| 175 |
pass
|
| 176 |
return None
|