Timothy Eastridge commited on
Commit
7faf776
·
1 Parent(s): 84473fd

commit step 6

Browse files
agent/main.py CHANGED
@@ -3,11 +3,23 @@ import time
3
  import json
4
  import requests
5
  from datetime import datetime
 
 
6
 
7
  MCP_URL = os.getenv("MCP_URL", "http://mcp:8000/mcp")
8
  API_KEY = os.getenv("MCP_API_KEY", "dev-key-123")
9
  POLL_INTERVAL = int(os.getenv("AGENT_POLL_INTERVAL", "30"))
10
 
 
 
 
 
 
 
 
 
 
 
11
  def call_mcp(tool, params=None):
12
  response = requests.post(
13
  MCP_URL,
@@ -16,6 +28,28 @@ def call_mcp(tool, params=None):
16
  )
17
  return response.json()
18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  def handle_discover_schema(instruction):
20
  """Discover PostgreSQL schema and store in Neo4j"""
21
  print(f"[{datetime.now()}] Discovering PostgreSQL schema...")
@@ -115,6 +149,95 @@ def handle_discover_schema(instruction):
115
  "columns_discovered": sum(len(cols) for cols in schema.values())
116
  }
117
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  def main():
119
  print(f"[{datetime.now()}] Agent starting, polling every {POLL_INTERVAL}s")
120
 
@@ -135,6 +258,8 @@ def main():
135
  # Handle different instruction types
136
  if instruction['type'] == 'discover_schema':
137
  exec_result = handle_discover_schema(instruction)
 
 
138
  else:
139
  # Default dummy execution
140
  exec_result = {"status": "success", "result": "Dummy execution"}
 
3
  import json
4
  import requests
5
  from datetime import datetime
6
+ import openai
7
+ from anthropic import Anthropic
8
 
9
  MCP_URL = os.getenv("MCP_URL", "http://mcp:8000/mcp")
10
  API_KEY = os.getenv("MCP_API_KEY", "dev-key-123")
11
  POLL_INTERVAL = int(os.getenv("AGENT_POLL_INTERVAL", "30"))
12
 
13
+ # Configure LLM
14
+ LLM_MODEL = os.getenv("LLM_MODEL", "gpt-4")
15
+ LLM_API_KEY = os.getenv("LLM_API_KEY")
16
+
17
+ if "gpt" in LLM_MODEL:
18
+ openai.api_key = LLM_API_KEY
19
+ llm_client = None
20
+ else:
21
+ llm_client = Anthropic(api_key=LLM_API_KEY)
22
+
23
  def call_mcp(tool, params=None):
24
  response = requests.post(
25
  MCP_URL,
 
28
  )
29
  return response.json()
30
 
31
+ def get_llm_response(prompt):
32
+ """Get response from configured LLM"""
33
+ if "gpt" in LLM_MODEL:
34
+ response = openai.ChatCompletion.create(
35
+ model=LLM_MODEL,
36
+ messages=[
37
+ {"role": "system", "content": "You are a SQL expert. Generate only valid PostgreSQL queries."},
38
+ {"role": "user", "content": prompt}
39
+ ],
40
+ temperature=0,
41
+ max_tokens=500
42
+ )
43
+ return response.choices[0].message.content
44
+ else:
45
+ response = llm_client.messages.create(
46
+ model=LLM_MODEL,
47
+ max_tokens=500,
48
+ temperature=0,
49
+ messages=[{"role": "user", "content": prompt}]
50
+ )
51
+ return response.content[0].text
52
+
53
  def handle_discover_schema(instruction):
54
  """Discover PostgreSQL schema and store in Neo4j"""
55
  print(f"[{datetime.now()}] Discovering PostgreSQL schema...")
 
149
  "columns_discovered": sum(len(cols) for cols in schema.values())
150
  }
151
 
152
+ def handle_generate_sql(instruction):
153
+ """Generate SQL from natural language using LLM"""
154
+ print(f"[{datetime.now()}] Generating SQL from natural language...")
155
+
156
+ # Get the user question from instruction parameters
157
+ params = json.loads(instruction.get('parameters', '{}'))
158
+ user_question = params.get('question', 'Show all data')
159
+
160
+ # Fetch schema from Neo4j
161
+ schema_result = call_mcp("query_graph", {
162
+ "query": """
163
+ MATCH (t:Table)-[:HAS_COLUMN]->(c:Column)
164
+ RETURN t.name as table_name,
165
+ collect({
166
+ name: c.name,
167
+ type: c.data_type,
168
+ nullable: c.nullable
169
+ }) as columns
170
+ """
171
+ })
172
+
173
+ # Format schema for LLM
174
+ schema_text = "PostgreSQL Schema:\n"
175
+ for record in schema_result['data']:
176
+ table = record['table_name']
177
+ columns = record['columns']
178
+ schema_text += f"\nTable: {table}\n"
179
+ for col in columns:
180
+ nullable = "NULL" if col['nullable'] else "NOT NULL"
181
+ schema_text += f" - {col['name']}: {col['type']} {nullable}\n"
182
+
183
+ # Create prompt
184
+ prompt = f"""Given this PostgreSQL schema:
185
+
186
+ {schema_text}
187
+
188
+ Generate a SQL query for this question: {user_question}
189
+
190
+ Return ONLY the SQL query, no explanations or markdown."""
191
+
192
+ try:
193
+ # Get SQL from LLM
194
+ generated_sql = get_llm_response(prompt)
195
+
196
+ # Clean up the SQL (remove markdown if present)
197
+ generated_sql = generated_sql.strip()
198
+ if generated_sql.startswith("```"):
199
+ generated_sql = generated_sql.split("```")[1]
200
+ if generated_sql.startswith("sql"):
201
+ generated_sql = generated_sql[3:]
202
+ generated_sql = generated_sql.strip()
203
+
204
+ print(f"[{datetime.now()}] Generated SQL: {generated_sql}")
205
+
206
+ # Execute the SQL
207
+ query_result = call_mcp("query_postgres", {"query": generated_sql})
208
+
209
+ if "error" in query_result:
210
+ return {
211
+ "status": "failed",
212
+ "generated_sql": generated_sql,
213
+ "error": query_result["error"]
214
+ }
215
+
216
+ # Store successful query as template
217
+ call_mcp("write_graph", {
218
+ "action": "create_node",
219
+ "label": "QueryTemplate",
220
+ "properties": {
221
+ "id": f"generated-{int(time.time())}",
222
+ "query": generated_sql,
223
+ "question": user_question,
224
+ "created_at": datetime.now().isoformat()
225
+ }
226
+ })
227
+
228
+ return {
229
+ "status": "success",
230
+ "generated_sql": generated_sql,
231
+ "row_count": query_result.get("row_count", 0),
232
+ "data": query_result.get("data", [])[:10] # Limit to 10 rows for storage
233
+ }
234
+
235
+ except Exception as e:
236
+ return {
237
+ "status": "failed",
238
+ "error": str(e)
239
+ }
240
+
241
  def main():
242
  print(f"[{datetime.now()}] Agent starting, polling every {POLL_INTERVAL}s")
243
 
 
258
  # Handle different instruction types
259
  if instruction['type'] == 'discover_schema':
260
  exec_result = handle_discover_schema(instruction)
261
+ elif instruction['type'] == 'generate_sql':
262
+ exec_result = handle_generate_sql(instruction)
263
  else:
264
  # Default dummy execution
265
  exec_result = {"status": "success", "result": "Dummy execution"}
agent/requirements.txt CHANGED
@@ -1,2 +1,4 @@
1
  requests==2.31.0
2
  python-dotenv==1.0.0
 
 
 
1
  requests==2.31.0
2
  python-dotenv==1.0.0
3
+ openai==0.28.1
4
+ anthropic==0.7.0
docker-compose.yml CHANGED
@@ -56,6 +56,8 @@ services:
56
  - MCP_URL=http://mcp:8000/mcp
57
  - MCP_API_KEY=dev-key-123
58
  - AGENT_POLL_INTERVAL=${AGENT_POLL_INTERVAL:-30}
 
 
59
  depends_on:
60
  - mcp
61
  - neo4j
 
56
  - MCP_URL=http://mcp:8000/mcp
57
  - MCP_API_KEY=dev-key-123
58
  - AGENT_POLL_INTERVAL=${AGENT_POLL_INTERVAL:-30}
59
+ - LLM_API_KEY=${LLM_API_KEY}
60
+ - LLM_MODEL=${LLM_MODEL:-gpt-4}
61
  depends_on:
62
  - mcp
63
  - neo4j
ops/scripts/seed.py CHANGED
@@ -28,9 +28,30 @@ print(f"Created workflow: {workflow}")
28
 
29
  # Create three instructions
30
  instructions = [
31
- {"id": "inst-1", "sequence": 1, "type": "discover_schema", "status": "pending", "pause_duration": 300},
32
- {"id": "inst-2", "sequence": 2, "type": "generate_sql", "status": "pending", "pause_duration": 300},
33
- {"id": "inst-3", "sequence": 3, "type": "review_results", "status": "pending", "pause_duration": 300}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  ]
35
 
36
  for inst in instructions:
 
28
 
29
  # Create three instructions
30
  instructions = [
31
+ {
32
+ "id": "inst-1",
33
+ "sequence": 1,
34
+ "type": "discover_schema",
35
+ "status": "pending",
36
+ "pause_duration": 300,
37
+ "parameters": "{}"
38
+ },
39
+ {
40
+ "id": "inst-2",
41
+ "sequence": 2,
42
+ "type": "generate_sql",
43
+ "status": "pending",
44
+ "pause_duration": 300,
45
+ "parameters": json.dumps({"question": "Show all customers who have placed orders"})
46
+ },
47
+ {
48
+ "id": "inst-3",
49
+ "sequence": 3,
50
+ "type": "review_results",
51
+ "status": "pending",
52
+ "pause_duration": 300,
53
+ "parameters": "{}"
54
+ }
55
  ]
56
 
57
  for inst in instructions:
seed_localhost.py CHANGED
@@ -1,73 +1,62 @@
1
  import requests
2
  import json
3
 
4
- MCP_URL = "http://localhost:8000/mcp"
5
- API_KEY = "dev-key-123"
6
-
7
  def call_mcp(tool, params=None):
8
  response = requests.post(
9
- MCP_URL,
10
- headers={"X-API-Key": API_KEY, "Content-Type": "application/json"},
11
- json={"tool": tool, "params": params or {}}
12
  )
13
  return response.json()
14
 
15
  # Create demo workflow
16
- workflow = call_mcp("write_graph", {
17
- "action": "create_node",
18
- "label": "Workflow",
19
- "properties": {
20
- "id": "demo-workflow-1",
21
- "name": "Entity Resolution Demo",
22
- "status": "active",
23
- "max_iterations": 10,
24
- "current_iteration": 0
25
  }
26
  })
27
- print(f"Created workflow: {workflow}")
28
 
29
- # Create three instructions
30
  instructions = [
31
- {"id": "inst-1", "sequence": 1, "type": "discover_schema", "status": "pending", "pause_duration": 300},
32
- {"id": "inst-2", "sequence": 2, "type": "generate_sql", "status": "pending", "pause_duration": 300},
33
- {"id": "inst-3", "sequence": 3, "type": "review_results", "status": "pending", "pause_duration": 300}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  ]
35
 
36
  for inst in instructions:
37
- result = call_mcp("write_graph", {
38
- "action": "create_node",
39
- "label": "Instruction",
40
- "properties": inst
41
  })
42
- print(f"Created instruction: {inst['id']}")
43
-
44
- # Link to workflow
45
- call_mcp("query_graph", {
46
- "query": '''
47
- MATCH (w:Workflow {id: }), (i:Instruction {id: })
48
- CREATE (w)-[:HAS_INSTRUCTION]->(i)
49
- ''',
50
- "parameters": {"wid": "demo-workflow-1", "iid": inst['id']}
51
- })
52
-
53
- # Create instruction chain
54
- call_mcp("query_graph", {
55
- "query": '''
56
- MATCH (i1:Instruction {id: 'inst-1'}), (i2:Instruction {id: 'inst-2'})
57
- CREATE (i1)-[:NEXT_INSTRUCTION]->(i2)
58
- '''
59
- })
60
-
61
- call_mcp("query_graph", {
62
- "query": '''
63
- MATCH (i2:Instruction {id: 'inst-2'}), (i3:Instruction {id: 'inst-3'})
64
- CREATE (i2)-[:NEXT_INSTRUCTION]->(i3)
65
- '''
66
- })
67
-
68
- # Create indexes
69
- call_mcp("query_graph", {"query": "CREATE INDEX workflow_id IF NOT EXISTS FOR (w:Workflow) ON (w.id)"})
70
- call_mcp("query_graph", {"query": "CREATE INDEX instruction_id IF NOT EXISTS FOR (i:Instruction) ON (i.id)"})
71
- call_mcp("query_graph", {"query": "CREATE INDEX instruction_status_seq IF NOT EXISTS FOR (i:Instruction) ON (i.status, i.sequence)"})
72
 
73
- print(" Seeding complete!")
 
1
  import requests
2
  import json
3
 
 
 
 
4
  def call_mcp(tool, params=None):
5
  response = requests.post(
6
+ 'http://localhost:8000/mcp',
7
+ headers={'X-API-Key': 'dev-key-123', 'Content-Type': 'application/json'},
8
+ json={'tool': tool, 'params': params or {}}
9
  )
10
  return response.json()
11
 
12
  # Create demo workflow
13
+ workflow = call_mcp('write_graph', {
14
+ 'action': 'create_node',
15
+ 'label': 'Workflow',
16
+ 'properties': {
17
+ 'id': 'demo-workflow-1',
18
+ 'name': 'Entity Resolution Demo',
19
+ 'status': 'active',
20
+ 'max_iterations': 10,
21
+ 'current_iteration': 0
22
  }
23
  })
24
+ print(f'Created workflow: {workflow}')
25
 
26
+ # Create three instructions with parameters
27
  instructions = [
28
+ {
29
+ 'id': 'inst-1',
30
+ 'sequence': 1,
31
+ 'type': 'discover_schema',
32
+ 'status': 'pending',
33
+ 'pause_duration': 300,
34
+ 'parameters': '{}'
35
+ },
36
+ {
37
+ 'id': 'inst-2',
38
+ 'sequence': 2,
39
+ 'type': 'generate_sql',
40
+ 'status': 'pending',
41
+ 'pause_duration': 300,
42
+ 'parameters': json.dumps({'question': 'Show all customers who have placed orders'})
43
+ },
44
+ {
45
+ 'id': 'inst-3',
46
+ 'sequence': 3,
47
+ 'type': 'review_results',
48
+ 'status': 'pending',
49
+ 'pause_duration': 300,
50
+ 'parameters': '{}'
51
+ }
52
  ]
53
 
54
  for inst in instructions:
55
+ result = call_mcp('write_graph', {
56
+ 'action': 'create_node',
57
+ 'label': 'Instruction',
58
+ 'properties': inst
59
  })
60
+ print(f'Created instruction: {inst["id"]}')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
 
62
+ print('✅ Seeding complete!')