Timothy Eastridge commited on
Commit ·
795ed51
1
Parent(s): 9930ba9
commit updates
Browse files- agent/Dockerfile +10 -0
- agent/main.py +77 -0
- agent/requirements.txt +2 -0
- docker-compose.yml +11 -0
- ops/health/neo4j.sh +2 -0
- ops/scripts/seed.py +73 -0
agent/Dockerfile
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.11-slim
|
| 2 |
+
|
| 3 |
+
WORKDIR /app
|
| 4 |
+
|
| 5 |
+
COPY requirements.txt .
|
| 6 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
| 7 |
+
|
| 8 |
+
COPY main.py .
|
| 9 |
+
|
| 10 |
+
CMD ["python", "main.py"]
|
agent/main.py
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
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,
|
| 14 |
+
headers={"X-API-Key": API_KEY, "Content-Type": "application/json"},
|
| 15 |
+
json={"tool": tool, "params": params or {}}
|
| 16 |
+
)
|
| 17 |
+
return response.json()
|
| 18 |
+
|
| 19 |
+
def main():
|
| 20 |
+
print(f"[{datetime.now()}] Agent starting, polling every {POLL_INTERVAL}s")
|
| 21 |
+
|
| 22 |
+
while True:
|
| 23 |
+
try:
|
| 24 |
+
# Get next instruction
|
| 25 |
+
result = call_mcp("get_next_instruction")
|
| 26 |
+
instruction = result.get("instruction")
|
| 27 |
+
|
| 28 |
+
if instruction:
|
| 29 |
+
print(f"[{datetime.now()}] Found instruction: {instruction['id']}, type: {instruction['type']}")
|
| 30 |
+
|
| 31 |
+
# Update status to executing
|
| 32 |
+
call_mcp("query_graph", {
|
| 33 |
+
"query": "MATCH (i:Instruction {id: $id}) SET i.status = 'executing'",
|
| 34 |
+
"parameters": {"id": instruction['id']}
|
| 35 |
+
})
|
| 36 |
+
|
| 37 |
+
# Create dummy execution for now
|
| 38 |
+
exec_result = call_mcp("write_graph", {
|
| 39 |
+
"action": "create_node",
|
| 40 |
+
"label": "Execution",
|
| 41 |
+
"properties": {
|
| 42 |
+
"id": f"exec-{instruction['id']}-{int(time.time())}",
|
| 43 |
+
"started_at": datetime.now().isoformat(),
|
| 44 |
+
"completed_at": datetime.now().isoformat(),
|
| 45 |
+
"result": "Dummy execution completed"
|
| 46 |
+
}
|
| 47 |
+
})
|
| 48 |
+
|
| 49 |
+
# Link execution to instruction
|
| 50 |
+
call_mcp("query_graph", {
|
| 51 |
+
"query": """
|
| 52 |
+
MATCH (i:Instruction {id: $iid}), (e:Execution {id: $eid})
|
| 53 |
+
CREATE (i)-[:EXECUTED_AS]->(e)
|
| 54 |
+
""",
|
| 55 |
+
"parameters": {
|
| 56 |
+
"iid": instruction['id'],
|
| 57 |
+
"eid": exec_result['created']['id']
|
| 58 |
+
}
|
| 59 |
+
})
|
| 60 |
+
|
| 61 |
+
# Mark complete
|
| 62 |
+
call_mcp("query_graph", {
|
| 63 |
+
"query": "MATCH (i:Instruction {id: $id}) SET i.status = 'complete'",
|
| 64 |
+
"parameters": {"id": instruction['id']}
|
| 65 |
+
})
|
| 66 |
+
|
| 67 |
+
print(f"[{datetime.now()}] Completed instruction: {instruction['id']}")
|
| 68 |
+
else:
|
| 69 |
+
print(f"[{datetime.now()}] No pending instructions")
|
| 70 |
+
|
| 71 |
+
except Exception as e:
|
| 72 |
+
print(f"[{datetime.now()}] Error: {e}")
|
| 73 |
+
|
| 74 |
+
time.sleep(POLL_INTERVAL)
|
| 75 |
+
|
| 76 |
+
if __name__ == "__main__":
|
| 77 |
+
main()
|
agent/requirements.txt
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
requests==2.31.0
|
| 2 |
+
python-dotenv==1.0.0
|
docker-compose.yml
CHANGED
|
@@ -40,6 +40,17 @@ services:
|
|
| 40 |
depends_on:
|
| 41 |
- neo4j
|
| 42 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
volumes:
|
| 44 |
neo4j_data:
|
| 45 |
postgres_data:
|
|
|
|
| 40 |
depends_on:
|
| 41 |
- neo4j
|
| 42 |
|
| 43 |
+
agent:
|
| 44 |
+
build: ./agent
|
| 45 |
+
container_name: agent
|
| 46 |
+
environment:
|
| 47 |
+
- MCP_URL=http://mcp:8000/mcp
|
| 48 |
+
- MCP_API_KEY=dev-key-123
|
| 49 |
+
- AGENT_POLL_INTERVAL=${AGENT_POLL_INTERVAL:-30}
|
| 50 |
+
depends_on:
|
| 51 |
+
- mcp
|
| 52 |
+
- neo4j
|
| 53 |
+
|
| 54 |
volumes:
|
| 55 |
neo4j_data:
|
| 56 |
postgres_data:
|
ops/health/neo4j.sh
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
docker-compose exec neo4j cypher-shell -u neo4j -p password "MATCH (n) RETURN count(n) LIMIT 1" && echo "✅ Neo4j healthy"
|
ops/scripts/seed.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import requests
|
| 2 |
+
import json
|
| 3 |
+
|
| 4 |
+
MCP_URL = "http://mcp: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: $wid}), (i:Instruction {id: $iid})
|
| 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!")
|