Spaces:
No application file
No application file
Timothy Eastridge commited on
Commit ·
84473fd
1
Parent(s): 795ed51
commit step 6
Browse files- .gitignore +1 -0
- agent/main.py +116 -10
- docker-compose.yml +10 -1
- mcp/main.py +57 -0
- mcp/requirements.txt +1 -0
- postgres/init.sql +28 -0
- seed_localhost.py +73 -0
- seed_simple.py +45 -0
.gitignore
CHANGED
|
@@ -1,2 +1,3 @@
|
|
| 1 |
*.db
|
| 2 |
/graph-agentic-system/neo4j/data
|
|
|
|
|
|
| 1 |
*.db
|
| 2 |
/graph-agentic-system/neo4j/data
|
| 3 |
+
/neo4j/data
|
agent/main.py
CHANGED
|
@@ -16,12 +16,110 @@ def call_mcp(tool, params=None):
|
|
| 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 |
|
|
@@ -34,15 +132,22 @@ def main():
|
|
| 34 |
"parameters": {"id": instruction['id']}
|
| 35 |
})
|
| 36 |
|
| 37 |
-
#
|
| 38 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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":
|
| 46 |
}
|
| 47 |
})
|
| 48 |
|
|
@@ -54,17 +159,18 @@ def main():
|
|
| 54 |
""",
|
| 55 |
"parameters": {
|
| 56 |
"iid": instruction['id'],
|
| 57 |
-
"eid":
|
| 58 |
}
|
| 59 |
})
|
| 60 |
|
| 61 |
-
#
|
|
|
|
| 62 |
call_mcp("query_graph", {
|
| 63 |
-
"query": "MATCH (i:Instruction {id: $id}) SET i.status =
|
| 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 |
|
|
@@ -74,4 +180,4 @@ def main():
|
|
| 74 |
time.sleep(POLL_INTERVAL)
|
| 75 |
|
| 76 |
if __name__ == "__main__":
|
| 77 |
-
main()
|
|
|
|
| 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...")
|
| 22 |
+
|
| 23 |
+
# Call MCP to discover schema
|
| 24 |
+
schema_result = call_mcp("discover_postgres_schema")
|
| 25 |
+
|
| 26 |
+
if "error" in schema_result:
|
| 27 |
+
return {"status": "failed", "error": schema_result["error"]}
|
| 28 |
+
|
| 29 |
+
schema = schema_result["schema"]
|
| 30 |
+
|
| 31 |
+
# Create SourceSystem node
|
| 32 |
+
call_mcp("write_graph", {
|
| 33 |
+
"action": "create_node",
|
| 34 |
+
"label": "SourceSystem",
|
| 35 |
+
"properties": {
|
| 36 |
+
"id": "postgres-main",
|
| 37 |
+
"name": "Main PostgreSQL Database",
|
| 38 |
+
"type": "postgresql",
|
| 39 |
+
"discovered_at": datetime.now().isoformat()
|
| 40 |
+
}
|
| 41 |
+
})
|
| 42 |
+
|
| 43 |
+
# For each table, create nodes
|
| 44 |
+
for table_name, columns in schema.items():
|
| 45 |
+
# Create Table node
|
| 46 |
+
table_result = call_mcp("write_graph", {
|
| 47 |
+
"action": "create_node",
|
| 48 |
+
"label": "Table",
|
| 49 |
+
"properties": {
|
| 50 |
+
"name": table_name,
|
| 51 |
+
"schema": "public",
|
| 52 |
+
"column_count": len(columns)
|
| 53 |
+
}
|
| 54 |
+
})
|
| 55 |
+
|
| 56 |
+
# Link Table to SourceSystem
|
| 57 |
+
call_mcp("query_graph", {
|
| 58 |
+
"query": """
|
| 59 |
+
MATCH (s:SourceSystem {id: 'postgres-main'}),
|
| 60 |
+
(t:Table {name: $table_name})
|
| 61 |
+
MERGE (s)-[:HAS_TABLE]->(t)
|
| 62 |
+
""",
|
| 63 |
+
"parameters": {"table_name": table_name}
|
| 64 |
+
})
|
| 65 |
+
|
| 66 |
+
# Create Column nodes
|
| 67 |
+
for col in columns:
|
| 68 |
+
col_result = call_mcp("write_graph", {
|
| 69 |
+
"action": "create_node",
|
| 70 |
+
"label": "Column",
|
| 71 |
+
"properties": {
|
| 72 |
+
"name": col['column_name'],
|
| 73 |
+
"data_type": col['data_type'],
|
| 74 |
+
"nullable": col['is_nullable'] == 'YES',
|
| 75 |
+
"table_name": table_name
|
| 76 |
+
}
|
| 77 |
+
})
|
| 78 |
+
|
| 79 |
+
# Link Column to Table
|
| 80 |
+
call_mcp("query_graph", {
|
| 81 |
+
"query": """
|
| 82 |
+
MATCH (t:Table {name: $table_name}),
|
| 83 |
+
(c:Column {name: $col_name, table_name: $table_name})
|
| 84 |
+
MERGE (t)-[:HAS_COLUMN]->(c)
|
| 85 |
+
""",
|
| 86 |
+
"parameters": {
|
| 87 |
+
"table_name": table_name,
|
| 88 |
+
"col_name": col['column_name']
|
| 89 |
+
}
|
| 90 |
+
})
|
| 91 |
+
|
| 92 |
+
# Generate sample queries
|
| 93 |
+
for table_name in schema.keys():
|
| 94 |
+
sample_queries = [
|
| 95 |
+
f"SELECT * FROM {table_name} LIMIT 10",
|
| 96 |
+
f"SELECT COUNT(*) FROM {table_name}",
|
| 97 |
+
f"SELECT * FROM {table_name} WHERE id = 1"
|
| 98 |
+
]
|
| 99 |
+
|
| 100 |
+
for idx, query in enumerate(sample_queries):
|
| 101 |
+
call_mcp("write_graph", {
|
| 102 |
+
"action": "create_node",
|
| 103 |
+
"label": "QueryTemplate",
|
| 104 |
+
"properties": {
|
| 105 |
+
"id": f"template-{table_name}-{idx}",
|
| 106 |
+
"table_name": table_name,
|
| 107 |
+
"query": query,
|
| 108 |
+
"description": f"Sample query {idx+1} for {table_name}"
|
| 109 |
+
}
|
| 110 |
+
})
|
| 111 |
+
|
| 112 |
+
return {
|
| 113 |
+
"status": "success",
|
| 114 |
+
"tables_discovered": len(schema),
|
| 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 |
|
| 121 |
while True:
|
| 122 |
try:
|
|
|
|
| 123 |
result = call_mcp("get_next_instruction")
|
| 124 |
instruction = result.get("instruction")
|
| 125 |
|
|
|
|
| 132 |
"parameters": {"id": instruction['id']}
|
| 133 |
})
|
| 134 |
|
| 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"}
|
| 141 |
+
|
| 142 |
+
# Create Execution node
|
| 143 |
+
exec_node = call_mcp("write_graph", {
|
| 144 |
"action": "create_node",
|
| 145 |
"label": "Execution",
|
| 146 |
"properties": {
|
| 147 |
"id": f"exec-{instruction['id']}-{int(time.time())}",
|
| 148 |
"started_at": datetime.now().isoformat(),
|
| 149 |
"completed_at": datetime.now().isoformat(),
|
| 150 |
+
"result": json.dumps(exec_result)
|
| 151 |
}
|
| 152 |
})
|
| 153 |
|
|
|
|
| 159 |
""",
|
| 160 |
"parameters": {
|
| 161 |
"iid": instruction['id'],
|
| 162 |
+
"eid": exec_node['created']['id']
|
| 163 |
}
|
| 164 |
})
|
| 165 |
|
| 166 |
+
# Update instruction status
|
| 167 |
+
final_status = 'complete' if exec_result.get('status') == 'success' else 'failed'
|
| 168 |
call_mcp("query_graph", {
|
| 169 |
+
"query": "MATCH (i:Instruction {id: $id}) SET i.status = $status",
|
| 170 |
+
"parameters": {"id": instruction['id'], "status": final_status}
|
| 171 |
})
|
| 172 |
|
| 173 |
+
print(f"[{datetime.now()}] Completed instruction: {instruction['id']} with status: {final_status}")
|
| 174 |
else:
|
| 175 |
print(f"[{datetime.now()}] No pending instructions")
|
| 176 |
|
|
|
|
| 180 |
time.sleep(POLL_INTERVAL)
|
| 181 |
|
| 182 |
if __name__ == "__main__":
|
| 183 |
+
main()
|
docker-compose.yml
CHANGED
|
@@ -22,10 +22,17 @@ services:
|
|
| 22 |
ports:
|
| 23 |
- "5432:5432"
|
| 24 |
environment:
|
| 25 |
-
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
|
| 26 |
- POSTGRES_DB=testdb
|
|
|
|
|
|
|
| 27 |
volumes:
|
| 28 |
- postgres_data:/var/lib/postgresql/data
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
|
| 30 |
mcp:
|
| 31 |
build: ./mcp
|
|
@@ -37,8 +44,10 @@ services:
|
|
| 37 |
- MCP_API_KEYS=dev-key-123,external-key-456
|
| 38 |
- NEO4J_BOLT_URL=bolt://neo4j:7687
|
| 39 |
- NEO4J_AUTH=neo4j/password
|
|
|
|
| 40 |
depends_on:
|
| 41 |
- neo4j
|
|
|
|
| 42 |
|
| 43 |
agent:
|
| 44 |
build: ./agent
|
|
|
|
| 22 |
ports:
|
| 23 |
- "5432:5432"
|
| 24 |
environment:
|
|
|
|
| 25 |
- POSTGRES_DB=testdb
|
| 26 |
+
- POSTGRES_USER=postgres
|
| 27 |
+
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
|
| 28 |
volumes:
|
| 29 |
- postgres_data:/var/lib/postgresql/data
|
| 30 |
+
- ./postgres/init.sql:/docker-entrypoint-initdb.d/init.sql
|
| 31 |
+
healthcheck:
|
| 32 |
+
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
| 33 |
+
interval: 10s
|
| 34 |
+
timeout: 5s
|
| 35 |
+
retries: 5
|
| 36 |
|
| 37 |
mcp:
|
| 38 |
build: ./mcp
|
|
|
|
| 44 |
- MCP_API_KEYS=dev-key-123,external-key-456
|
| 45 |
- NEO4J_BOLT_URL=bolt://neo4j:7687
|
| 46 |
- NEO4J_AUTH=neo4j/password
|
| 47 |
+
- POSTGRES_CONNECTION=postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/testdb
|
| 48 |
depends_on:
|
| 49 |
- neo4j
|
| 50 |
+
- postgres
|
| 51 |
|
| 52 |
agent:
|
| 53 |
build: ./agent
|
mcp/main.py
CHANGED
|
@@ -3,6 +3,8 @@ from neo4j import GraphDatabase
|
|
| 3 |
import os
|
| 4 |
import json
|
| 5 |
from datetime import datetime
|
|
|
|
|
|
|
| 6 |
|
| 7 |
app = FastAPI()
|
| 8 |
driver = GraphDatabase.driver(
|
|
@@ -11,6 +13,7 @@ driver = GraphDatabase.driver(
|
|
| 11 |
)
|
| 12 |
|
| 13 |
VALID_API_KEYS = os.getenv("MCP_API_KEYS", "").split(",")
|
|
|
|
| 14 |
|
| 15 |
@app.get("/health")
|
| 16 |
def health():
|
|
@@ -63,4 +66,58 @@ async def execute_tool(request: dict, x_api_key: str = Header(None)):
|
|
| 63 |
record = result.single()
|
| 64 |
return {"instruction": dict(record["i"]) if record else None}
|
| 65 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 66 |
return {"error": "Unknown tool"}
|
|
|
|
| 3 |
import os
|
| 4 |
import json
|
| 5 |
from datetime import datetime
|
| 6 |
+
import psycopg2
|
| 7 |
+
from psycopg2.extras import RealDictCursor
|
| 8 |
|
| 9 |
app = FastAPI()
|
| 10 |
driver = GraphDatabase.driver(
|
|
|
|
| 13 |
)
|
| 14 |
|
| 15 |
VALID_API_KEYS = os.getenv("MCP_API_KEYS", "").split(",")
|
| 16 |
+
POSTGRES_CONN = os.getenv("POSTGRES_CONNECTION")
|
| 17 |
|
| 18 |
@app.get("/health")
|
| 19 |
def health():
|
|
|
|
| 66 |
record = result.single()
|
| 67 |
return {"instruction": dict(record["i"]) if record else None}
|
| 68 |
|
| 69 |
+
elif tool == "query_postgres":
|
| 70 |
+
query = params.get("query")
|
| 71 |
+
try:
|
| 72 |
+
conn = psycopg2.connect(POSTGRES_CONN)
|
| 73 |
+
with conn.cursor(cursor_factory=RealDictCursor) as cur:
|
| 74 |
+
cur.execute(query)
|
| 75 |
+
if cur.description: # SELECT query
|
| 76 |
+
results = cur.fetchall()
|
| 77 |
+
return {"data": results, "row_count": len(results)}
|
| 78 |
+
else: # INSERT/UPDATE/DELETE
|
| 79 |
+
conn.commit()
|
| 80 |
+
return {"affected_rows": cur.rowcount}
|
| 81 |
+
except Exception as e:
|
| 82 |
+
return {"error": str(e)}
|
| 83 |
+
finally:
|
| 84 |
+
if 'conn' in locals():
|
| 85 |
+
conn.close()
|
| 86 |
+
|
| 87 |
+
elif tool == "discover_postgres_schema":
|
| 88 |
+
try:
|
| 89 |
+
conn = psycopg2.connect(POSTGRES_CONN)
|
| 90 |
+
with conn.cursor(cursor_factory=RealDictCursor) as cur:
|
| 91 |
+
# Get all tables
|
| 92 |
+
cur.execute("""
|
| 93 |
+
SELECT table_name, table_schema
|
| 94 |
+
FROM information_schema.tables
|
| 95 |
+
WHERE table_schema = 'public'
|
| 96 |
+
AND table_type = 'BASE TABLE'
|
| 97 |
+
""")
|
| 98 |
+
tables = cur.fetchall()
|
| 99 |
+
|
| 100 |
+
schema_info = {}
|
| 101 |
+
for table in tables:
|
| 102 |
+
table_name = table['table_name']
|
| 103 |
+
|
| 104 |
+
# Get columns for each table
|
| 105 |
+
cur.execute("""
|
| 106 |
+
SELECT column_name, data_type, is_nullable,
|
| 107 |
+
column_default, character_maximum_length
|
| 108 |
+
FROM information_schema.columns
|
| 109 |
+
WHERE table_schema = 'public'
|
| 110 |
+
AND table_name = %s
|
| 111 |
+
ORDER BY ordinal_position
|
| 112 |
+
""", (table_name,))
|
| 113 |
+
|
| 114 |
+
schema_info[table_name] = cur.fetchall()
|
| 115 |
+
|
| 116 |
+
return {"schema": schema_info}
|
| 117 |
+
except Exception as e:
|
| 118 |
+
return {"error": str(e)}
|
| 119 |
+
finally:
|
| 120 |
+
if 'conn' in locals():
|
| 121 |
+
conn.close()
|
| 122 |
+
|
| 123 |
return {"error": "Unknown tool"}
|
mcp/requirements.txt
CHANGED
|
@@ -3,3 +3,4 @@ uvicorn==0.24.0
|
|
| 3 |
neo4j==5.14.0
|
| 4 |
pydantic==2.4.0
|
| 5 |
requests==2.31.0
|
|
|
|
|
|
| 3 |
neo4j==5.14.0
|
| 4 |
pydantic==2.4.0
|
| 5 |
requests==2.31.0
|
| 6 |
+
psycopg2-binary==2.9.9
|
postgres/init.sql
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-- Create sample tables for testing
|
| 2 |
+
CREATE TABLE customers (
|
| 3 |
+
id SERIAL PRIMARY KEY,
|
| 4 |
+
email VARCHAR(255) UNIQUE NOT NULL,
|
| 5 |
+
first_name VARCHAR(100),
|
| 6 |
+
last_name VARCHAR(100),
|
| 7 |
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
| 8 |
+
);
|
| 9 |
+
|
| 10 |
+
CREATE TABLE orders (
|
| 11 |
+
id SERIAL PRIMARY KEY,
|
| 12 |
+
customer_id INTEGER REFERENCES customers(id),
|
| 13 |
+
order_date DATE NOT NULL,
|
| 14 |
+
total_amount DECIMAL(10,2),
|
| 15 |
+
status VARCHAR(50)
|
| 16 |
+
);
|
| 17 |
+
|
| 18 |
+
-- Insert sample data
|
| 19 |
+
INSERT INTO customers (email, first_name, last_name) VALUES
|
| 20 |
+
('john.doe@email.com', 'John', 'Doe'),
|
| 21 |
+
('jane.smith@email.com', 'Jane', 'Smith'),
|
| 22 |
+
('bob.johnson@email.com', 'Bob', 'Johnson');
|
| 23 |
+
|
| 24 |
+
INSERT INTO orders (customer_id, order_date, total_amount, status) VALUES
|
| 25 |
+
(1, '2024-01-15', 99.99, 'completed'),
|
| 26 |
+
(1, '2024-02-01', 149.99, 'completed'),
|
| 27 |
+
(2, '2024-01-20', 79.99, 'pending'),
|
| 28 |
+
(3, '2024-02-10', 199.99, 'completed');
|
seed_localhost.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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!")
|
seed_simple.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 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 |
+
print(" Basic seeding complete! Workflow and instructions created.")
|
| 45 |
+
print("Note: Relationships skipped due to parameterized query issue - but agent should still work!")
|