Spaces:
Sleeping
Sleeping
Soroush commited on
Commit ·
7a2907b
1
Parent(s): fa84149
firt54
Browse files
app.py
CHANGED
|
@@ -0,0 +1,1327 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import gradio as gr
|
| 3 |
+
import requests
|
| 4 |
+
from typing import Dict, List, Any, Optional
|
| 5 |
+
from dataclasses import asdict, dataclass
|
| 6 |
+
from langgraph.graph import StateGraph, END
|
| 7 |
+
from langgraph.checkpoint.sqlite import SqliteSaver
|
| 8 |
+
from langchain_core.messages import HumanMessage, AIMessage
|
| 9 |
+
from mistralai import Mistral
|
| 10 |
+
import json
|
| 11 |
+
import base64
|
| 12 |
+
import io
|
| 13 |
+
from io import BytesIO
|
| 14 |
+
from bs4 import BeautifulSoup
|
| 15 |
+
import re
|
| 16 |
+
from PIL import Image
|
| 17 |
+
|
| 18 |
+
# Constants
|
| 19 |
+
MAX_IMAGE_SIZE_KB = 50 # 50KB maximum size for images
|
| 20 |
+
MAX_RESIZE_ATTEMPTS = 3 # Maximum number of resize attempts
|
| 21 |
+
from langchain_core.runnables.graph import MermaidDrawMethod
|
| 22 |
+
from IPython.display import Image
|
| 23 |
+
|
| 24 |
+
from dotenv import load_dotenv
|
| 25 |
+
load_dotenv()
|
| 26 |
+
|
| 27 |
+
# Initialize Mistral client
|
| 28 |
+
client = Mistral(api_key=os.getenv("MISTRAL_API_KEY"))
|
| 29 |
+
|
| 30 |
+
@dataclass
|
| 31 |
+
class SimulationState:
|
| 32 |
+
"""Enhanced state management for the simulation workflow"""
|
| 33 |
+
url: str = ""
|
| 34 |
+
content: str = ""
|
| 35 |
+
content_type: str = "" # 'article' or 'image'
|
| 36 |
+
summary: str = ""
|
| 37 |
+
agents: List[Dict[str, Any]] = None
|
| 38 |
+
behaviors: List[str] = None
|
| 39 |
+
emojis: Dict[str, str] = None
|
| 40 |
+
simulation_code: str = ""
|
| 41 |
+
documentation: str = ""
|
| 42 |
+
messages: List = None
|
| 43 |
+
# New fields for enhanced simulation
|
| 44 |
+
environment: Dict[str, Any] = None
|
| 45 |
+
hypothesis: str = ""
|
| 46 |
+
story_narrative: str = ""
|
| 47 |
+
research_question: str = ""
|
| 48 |
+
expected_outcomes: List[str] = None
|
| 49 |
+
agent_interactions: List[Dict[str, Any]] = None
|
| 50 |
+
environmental_factors: List[Dict[str, Any]] = None
|
| 51 |
+
# New fields for code validation
|
| 52 |
+
code_errors: List[str] = None
|
| 53 |
+
validation_attempts: int = 0
|
| 54 |
+
max_validation_attempts: int = 3
|
| 55 |
+
code_is_valid: bool = False
|
| 56 |
+
|
| 57 |
+
def __post_init__(self):
|
| 58 |
+
if self.agents is None:
|
| 59 |
+
self.agents = []
|
| 60 |
+
if self.behaviors is None:
|
| 61 |
+
self.behaviors = []
|
| 62 |
+
if self.emojis is None:
|
| 63 |
+
self.emojis = {}
|
| 64 |
+
if self.messages is None:
|
| 65 |
+
self.messages = []
|
| 66 |
+
if self.environment is None:
|
| 67 |
+
self.environment = {}
|
| 68 |
+
if self.expected_outcomes is None:
|
| 69 |
+
self.expected_outcomes = []
|
| 70 |
+
if self.agent_interactions is None:
|
| 71 |
+
self.agent_interactions = []
|
| 72 |
+
if self.environmental_factors is None:
|
| 73 |
+
self.environmental_factors = []
|
| 74 |
+
if self.code_errors is None:
|
| 75 |
+
self.code_errors = []
|
| 76 |
+
|
| 77 |
+
def to_dict(self):
|
| 78 |
+
return asdict(self)
|
| 79 |
+
|
| 80 |
+
import time
|
| 81 |
+
from tenacity import retry, stop_after_attempt, wait_exponential
|
| 82 |
+
|
| 83 |
+
class MistralModelHub:
|
| 84 |
+
def __init__(self, api_key: str):
|
| 85 |
+
self.client = Mistral(api_key=api_key)
|
| 86 |
+
|
| 87 |
+
# Available Mistral models
|
| 88 |
+
self.models = {
|
| 89 |
+
'large': MistralLLM(self.client, "mistral-large-latest"),
|
| 90 |
+
'medium': MistralLLM(self.client, "magistral-small-2506"),
|
| 91 |
+
'small': MistralLLM(self.client, "ministral-3b-2410"),
|
| 92 |
+
'codestral': MistralLLM(self.client, "codestral-latest"),
|
| 93 |
+
'pixtral': MistralLLM(self.client, "pixtral-12b-2409"),
|
| 94 |
+
'nemo': MistralLLM(self.client, "open-mistral-nemo"),
|
| 95 |
+
'saba': MistralLLM(self.client, "mistral-saba-latest"),
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
def __getattr__(self, name):
|
| 99 |
+
if name in self.models:
|
| 100 |
+
return self.models[name]
|
| 101 |
+
raise AttributeError(f"Model '{name}' not available")
|
| 102 |
+
|
| 103 |
+
class MistralLLM:
|
| 104 |
+
def __init__(self, client, model: str):
|
| 105 |
+
self.model = model
|
| 106 |
+
self.client = client
|
| 107 |
+
|
| 108 |
+
@retry(
|
| 109 |
+
stop=stop_after_attempt(3),
|
| 110 |
+
wait=wait_exponential(multiplier=1, min=4, max=10)
|
| 111 |
+
)
|
| 112 |
+
def chat(self, messages: List[Dict[str, str]], temperature: float = 0.7, max_tokens: Optional[int] = None) -> str:
|
| 113 |
+
try:
|
| 114 |
+
chat_messages = [
|
| 115 |
+
{"role": msg["role"], "content": msg["content"]}
|
| 116 |
+
for msg in messages
|
| 117 |
+
]
|
| 118 |
+
print(f"Calling model: {self.model}")
|
| 119 |
+
response = self.client.chat.complete(
|
| 120 |
+
model=self.model,
|
| 121 |
+
messages=chat_messages,
|
| 122 |
+
temperature=temperature,
|
| 123 |
+
max_tokens=max_tokens,
|
| 124 |
+
response_format={"type": "text"}
|
| 125 |
+
)
|
| 126 |
+
|
| 127 |
+
if response.choices and len(response.choices) > 0:
|
| 128 |
+
return response.choices[0].message.content
|
| 129 |
+
return ""
|
| 130 |
+
|
| 131 |
+
except Exception as e:
|
| 132 |
+
print(f"Error calling Mistral API: {str(e)}")
|
| 133 |
+
if hasattr(e, 'status_code') and e.status_code == 429:
|
| 134 |
+
print("Rate limit exceeded. Waiting before retry...")
|
| 135 |
+
time.sleep(5)
|
| 136 |
+
raise
|
| 137 |
+
|
| 138 |
+
# Initialize LLM
|
| 139 |
+
llm_large = MistralModelHub(api_key=os.getenv("MISTRAL_API_KEY")).large
|
| 140 |
+
llm_codestral = MistralModelHub(api_key=os.getenv("MISTRAL_API_KEY")).codestral
|
| 141 |
+
llm_pixtral = MistralModelHub(api_key=os.getenv("MISTRAL_API_KEY")).pixtral
|
| 142 |
+
llm_medium = MistralModelHub(api_key=os.getenv("MISTRAL_API_KEY")).medium
|
| 143 |
+
llm_small = MistralModelHub(api_key=os.getenv("MISTRAL_API_KEY")).small
|
| 144 |
+
llm_saba = MistralModelHub(api_key=os.getenv("MISTRAL_API_KEY")).saba
|
| 145 |
+
llm_nemo = MistralModelHub(api_key=os.getenv("MISTRAL_API_KEY")).nemo
|
| 146 |
+
|
| 147 |
+
def resize_image_to_size_limit(image_data: bytes, content_type: str, max_size_kb: int = MAX_IMAGE_SIZE_KB, max_attempts: int = MAX_RESIZE_ATTEMPTS) -> str:
|
| 148 |
+
"""
|
| 149 |
+
Resize image to fit within the specified size limit (in KB)
|
| 150 |
+
Returns base64 encoded image data URL
|
| 151 |
+
"""
|
| 152 |
+
def get_size_kb(data):
|
| 153 |
+
return len(data) / 1024 # Convert bytes to KB
|
| 154 |
+
|
| 155 |
+
current_data = image_data
|
| 156 |
+
current_size_kb = get_size_kb(current_data)
|
| 157 |
+
|
| 158 |
+
if current_size_kb <= max_size_kb:
|
| 159 |
+
return f"data:{content_type};base64,{base64.b64encode(current_data).decode()}"
|
| 160 |
+
|
| 161 |
+
# Try resizing up to max_attempts times
|
| 162 |
+
for attempt in range(1, max_attempts + 1):
|
| 163 |
+
try:
|
| 164 |
+
# Open the image
|
| 165 |
+
img = Image.open(BytesIO(current_data))
|
| 166 |
+
|
| 167 |
+
# Calculate new dimensions (reduce by 20% each time)
|
| 168 |
+
new_width = int(img.width * 0.8)
|
| 169 |
+
new_height = int(img.height * 0.8)
|
| 170 |
+
|
| 171 |
+
# Resize the image
|
| 172 |
+
img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
|
| 173 |
+
|
| 174 |
+
# Convert back to bytes
|
| 175 |
+
output = BytesIO()
|
| 176 |
+
format = content_type.split('/')[-1].upper()
|
| 177 |
+
if format not in ['JPEG', 'PNG', 'GIF']: # Default to JPEG
|
| 178 |
+
format = 'JPEG'
|
| 179 |
+
img.save(output, format=format, quality=85, optimize=True)
|
| 180 |
+
current_data = output.getvalue()
|
| 181 |
+
|
| 182 |
+
# Check new size
|
| 183 |
+
current_size_kb = get_size_kb(current_data)
|
| 184 |
+
if current_size_kb <= max_size_kb:
|
| 185 |
+
return f"data:{content_type};base64,{base64.b64encode(current_data).decode()}"
|
| 186 |
+
|
| 187 |
+
except Exception as e:
|
| 188 |
+
print(f"Error resizing image (attempt {attempt}): {str(e)}")
|
| 189 |
+
break
|
| 190 |
+
|
| 191 |
+
# If we get here, we couldn't resize within limits after max_attempts
|
| 192 |
+
raise ValueError(f"Could not resize image below {max_size_kb}KB after {max_attempts} attempts")
|
| 193 |
+
|
| 194 |
+
def extract_content_node(state: SimulationState) -> SimulationState:
|
| 195 |
+
"""Extract content from URL - handles both articles and images. Only keep main article text, filter out navigation/copyright/unrelated sections."""
|
| 196 |
+
try:
|
| 197 |
+
response = requests.get(state.url, timeout=10)
|
| 198 |
+
content_type = response.headers.get('content-type', '').lower()
|
| 199 |
+
|
| 200 |
+
if 'image' in content_type:
|
| 201 |
+
state.content_type = 'image'
|
| 202 |
+
# Check image size and resize if needed
|
| 203 |
+
image_data = response.content
|
| 204 |
+
image_size_kb = len(image_data) / 1024 # Convert to KB
|
| 205 |
+
if image_size_kb > MAX_IMAGE_SIZE_KB:
|
| 206 |
+
print(f"Image size ({image_size_kb:.2f}KB) exceeds maximum allowed size ({MAX_IMAGE_SIZE_KB}KB). Resizing...")
|
| 207 |
+
state.content = resize_image_to_size_limit(image_data, content_type)
|
| 208 |
+
else:
|
| 209 |
+
# Convert to base64 if within size limit
|
| 210 |
+
image_data = base64.b64encode(image_data).decode()
|
| 211 |
+
state.content = f"data:{content_type};base64,{image_data}"
|
| 212 |
+
return state
|
| 213 |
+
|
| 214 |
+
# Otherwise, treat as article
|
| 215 |
+
state.content_type = 'article'
|
| 216 |
+
soup = BeautifulSoup(response.text, 'html.parser')
|
| 217 |
+
article_content = ''
|
| 218 |
+
|
| 219 |
+
# Try to extract from <article> tag first
|
| 220 |
+
article_tag = soup.find('article')
|
| 221 |
+
if article_tag:
|
| 222 |
+
article_content = article_tag.get_text(separator=' ', strip=True)
|
| 223 |
+
else:
|
| 224 |
+
# Fallback: find the largest <div> with <p> tags
|
| 225 |
+
divs = soup.find_all('div')
|
| 226 |
+
max_p_div = None
|
| 227 |
+
max_p_count = 0
|
| 228 |
+
for div in divs:
|
| 229 |
+
p_count = len(div.find_all('p'))
|
| 230 |
+
if p_count > max_p_count:
|
| 231 |
+
max_p_count = p_count
|
| 232 |
+
max_p_div = div
|
| 233 |
+
if max_p_div:
|
| 234 |
+
article_content = max_p_div.get_text(separator=' ', strip=True)
|
| 235 |
+
|
| 236 |
+
# Fallback: all paragraphs
|
| 237 |
+
if not article_content:
|
| 238 |
+
paragraphs = soup.find_all('p')
|
| 239 |
+
article_content = ' '.join([p.get_text().strip() for p in paragraphs])
|
| 240 |
+
|
| 241 |
+
# Remove unwanted sections by keywords (navigation, copyright, etc.)
|
| 242 |
+
unwanted_keywords = [
|
| 243 |
+
'riproduzione riservata', 'copyright', 'da non perdere', 'prevPageLabel', 'nextPageLabel', 'condividi', 'link copiato', 'cookie', 'accetta', 'rifiuta'
|
| 244 |
+
]
|
| 245 |
+
for keyword in unwanted_keywords:
|
| 246 |
+
article_content = re.sub(rf'(?i){keyword}.*', '', article_content)
|
| 247 |
+
|
| 248 |
+
# Remove extra whitespace and code blocks
|
| 249 |
+
article_content = re.sub(r'\s+', ' ', article_content)
|
| 250 |
+
article_content = re.sub(r'``````', '', article_content, flags=re.DOTALL)
|
| 251 |
+
article_content = article_content.strip()
|
| 252 |
+
state.content = article_content[:5000] # Limit content size
|
| 253 |
+
|
| 254 |
+
except Exception as e:
|
| 255 |
+
state.content = f"Error extracting content: {str(e)}"
|
| 256 |
+
state.content_type = 'error'
|
| 257 |
+
return state
|
| 258 |
+
|
| 259 |
+
def process_image_node(state: SimulationState) -> SimulationState:
|
| 260 |
+
"""Process image content using Mistral's vision capabilities"""
|
| 261 |
+
|
| 262 |
+
if state.content_type != 'image':
|
| 263 |
+
return state
|
| 264 |
+
|
| 265 |
+
messages = [
|
| 266 |
+
{
|
| 267 |
+
"role": "system",
|
| 268 |
+
"content": """You are an expert image analyzer. Describe the image in detail, focusing on:
|
| 269 |
+
1. Main subjects and objects
|
| 270 |
+
2. Activities or interactions happening
|
| 271 |
+
3. Social dynamics or relationships visible
|
| 272 |
+
4. Setting and context
|
| 273 |
+
5. Any text or symbols present
|
| 274 |
+
|
| 275 |
+
Provide a comprehensive description that could be used to understand social dynamics for simulation purposes."""
|
| 276 |
+
},
|
| 277 |
+
{
|
| 278 |
+
"role": "user",
|
| 279 |
+
"content": f"Please describe this image in detail: {state.content}"
|
| 280 |
+
}
|
| 281 |
+
]
|
| 282 |
+
|
| 283 |
+
description = llm_pixtral.chat(messages)
|
| 284 |
+
state.content = description
|
| 285 |
+
state.content_type = 'article' # Convert to article for further processing
|
| 286 |
+
|
| 287 |
+
return state
|
| 288 |
+
|
| 289 |
+
def analyze_content_node(state: SimulationState) -> SimulationState:
|
| 290 |
+
"""Enhanced analysis to extract comprehensive simulation components"""
|
| 291 |
+
|
| 292 |
+
system_prompt = """You are an expert in complex systems theory, social network analysis, and agent-based modeling.
|
| 293 |
+
|
| 294 |
+
Your task is to conduct a deep scientific analysis of the content to create a meaningful agent-based simulation. Consider:
|
| 295 |
+
|
| 296 |
+
1. System Dynamics: Identify feedback loops, tipping points, and emergent behaviors
|
| 297 |
+
2. Social Physics: Power dynamics, influence propagation, coalition formation
|
| 298 |
+
3. Environmental Context: Physical, social, economic, and cultural environments
|
| 299 |
+
4. Conflict and Cooperation: Competition for resources, alliance formation, negotiation
|
| 300 |
+
5. Temporal Dynamics: How relationships and behaviors evolve over time
|
| 301 |
+
|
| 302 |
+
Focus on creating agents with rich internal states and meaningful interactions that produce observable phenomena."""
|
| 303 |
+
|
| 304 |
+
user_prompt = f"""
|
| 305 |
+
Analyze this content for an advanced agent-based simulation:
|
| 306 |
+
|
| 307 |
+
Content: {state.content}
|
| 308 |
+
|
| 309 |
+
Provide a comprehensive JSON response with:
|
| 310 |
+
{{
|
| 311 |
+
"summary": "Deep analytical summary focusing on system dynamics",
|
| 312 |
+
"research_domain": "Primary field of study (sociology, economics, politics, etc.)",
|
| 313 |
+
"environment": {{
|
| 314 |
+
"name": "Environment name",
|
| 315 |
+
"description": "Detailed environment description",
|
| 316 |
+
"resources": ["resource1", "resource2"],
|
| 317 |
+
"constraints": ["constraint1", "constraint2"],
|
| 318 |
+
"spatial_properties": "Physical or abstract space description",
|
| 319 |
+
"temporal_dynamics": "How environment changes over time"
|
| 320 |
+
}},
|
| 321 |
+
"agents": [
|
| 322 |
+
{{
|
| 323 |
+
"name": "Agent name",
|
| 324 |
+
"type": "Agent type (individual, organization, group, institution)",
|
| 325 |
+
"description": "Detailed agent description",
|
| 326 |
+
"attributes": {{
|
| 327 |
+
"influence_level": 1-10,
|
| 328 |
+
"resources": ["resource1", "resource2"],
|
| 329 |
+
"goals": ["goal1", "goal2"],
|
| 330 |
+
"constraints": ["constraint1", "constraint2"],
|
| 331 |
+
"decision_strategy": "How agent makes decisions",
|
| 332 |
+
"memory_span": "How long agent remembers interactions",
|
| 333 |
+
"cooperation_tendency": 1-10,
|
| 334 |
+
"aggression_level": 1-10,
|
| 335 |
+
"adaptation_rate": 1-10
|
| 336 |
+
}},
|
| 337 |
+
"initial_state": {{
|
| 338 |
+
"position": "starting position or status",
|
| 339 |
+
"energy": 1-100,
|
| 340 |
+
"relationships": {{}},
|
| 341 |
+
"knowledge": ["known_fact1", "known_fact2"]
|
| 342 |
+
}}
|
| 343 |
+
}}
|
| 344 |
+
],
|
| 345 |
+
"agent_interactions": [
|
| 346 |
+
{{
|
| 347 |
+
"interaction_type": "cooperation/competition/negotiation/conflict/information_exchange",
|
| 348 |
+
"participants": ["agent1", "agent2"],
|
| 349 |
+
"conditions": "When this interaction occurs",
|
| 350 |
+
"outcomes": {{
|
| 351 |
+
"positive": "What happens if interaction succeeds",
|
| 352 |
+
"negative": "What happens if interaction fails",
|
| 353 |
+
"environmental_impact": "How this affects the environment"
|
| 354 |
+
}},
|
| 355 |
+
"probability_factors": ["factor1", "factor2"]
|
| 356 |
+
}}
|
| 357 |
+
],
|
| 358 |
+
"environmental_factors": [
|
| 359 |
+
{{
|
| 360 |
+
"name": "Factor name",
|
| 361 |
+
"type": "resource/constraint/event/pressure",
|
| 362 |
+
"description": "How this factor affects the system",
|
| 363 |
+
"impact_on_agents": "Specific effects on different agent types",
|
| 364 |
+
"temporal_pattern": "constant/periodic/random/triggered"
|
| 365 |
+
}}
|
| 366 |
+
],
|
| 367 |
+
"system_dynamics": {{
|
| 368 |
+
"feedback_loops": ["description of feedback loop"],
|
| 369 |
+
"tipping_points": ["conditions that cause system change"],
|
| 370 |
+
"equilibrium_states": ["possible stable states"],
|
| 371 |
+
"emergent_behaviors": ["behaviors that emerge from interactions"]
|
| 372 |
+
}}
|
| 373 |
+
}}
|
| 374 |
+
"""
|
| 375 |
+
|
| 376 |
+
messages = [
|
| 377 |
+
{"role": "system", "content": system_prompt},
|
| 378 |
+
{"role": "user", "content": user_prompt}
|
| 379 |
+
]
|
| 380 |
+
|
| 381 |
+
response = llm_medium.chat(messages, temperature=0.6)
|
| 382 |
+
|
| 383 |
+
try:
|
| 384 |
+
json_match = re.search(r'\{.*\}', response, re.DOTALL)
|
| 385 |
+
if json_match:
|
| 386 |
+
data = json.loads(json_match.group())
|
| 387 |
+
state.summary = data.get("summary", "")
|
| 388 |
+
state.environment = data.get("environment", {})
|
| 389 |
+
state.agents = data.get("agents", [])
|
| 390 |
+
state.agent_interactions = data.get("agent_interactions", [])
|
| 391 |
+
state.environmental_factors = data.get("environmental_factors", [])
|
| 392 |
+
|
| 393 |
+
# Extract system dynamics for later use
|
| 394 |
+
system_dynamics = data.get("system_dynamics", {})
|
| 395 |
+
state.behaviors = (
|
| 396 |
+
system_dynamics.get("feedback_loops", []) +
|
| 397 |
+
system_dynamics.get("emergent_behaviors", [])
|
| 398 |
+
)
|
| 399 |
+
else:
|
| 400 |
+
state.summary = response
|
| 401 |
+
except Exception as e:
|
| 402 |
+
state.summary = f"Analysis error: {str(e)}"
|
| 403 |
+
|
| 404 |
+
return state
|
| 405 |
+
|
| 406 |
+
def develop_hypothesis_node(state: SimulationState) -> SimulationState:
|
| 407 |
+
"""Develop scientific hypothesis and narrative story for the simulation"""
|
| 408 |
+
|
| 409 |
+
system_prompt = """You are a research scientist developing hypotheses for agent-based social simulations.
|
| 410 |
+
|
| 411 |
+
Create a compelling research hypothesis that can be tested through simulation, along with a narrative story
|
| 412 |
+
that makes the simulation engaging and meaningful. The hypothesis should be:
|
| 413 |
+
|
| 414 |
+
1. Testable through agent interactions
|
| 415 |
+
2. Based on established social/behavioral theories
|
| 416 |
+
3. Relevant to the analyzed content
|
| 417 |
+
4. Capable of producing measurable outcomes
|
| 418 |
+
|
| 419 |
+
The story should provide context and meaning to make the simulation educational and engaging."""
|
| 420 |
+
|
| 421 |
+
agents_summary = "\n".join([
|
| 422 |
+
f"- {agent['name']}: {agent['description']} (Influence: {agent['attributes']['influence_level']})"
|
| 423 |
+
for agent in state.agents
|
| 424 |
+
])
|
| 425 |
+
|
| 426 |
+
interactions_summary = "\n".join([
|
| 427 |
+
f"- {interaction['interaction_type']}: {interaction['conditions']}"
|
| 428 |
+
for interaction in state.agent_interactions
|
| 429 |
+
])
|
| 430 |
+
|
| 431 |
+
user_prompt = f"""
|
| 432 |
+
Develop a research hypothesis and story for this simulation:
|
| 433 |
+
|
| 434 |
+
Summary: {state.summary}
|
| 435 |
+
|
| 436 |
+
Environment: {state.environment.get('description', 'N/A')}
|
| 437 |
+
|
| 438 |
+
Agents:
|
| 439 |
+
{agents_summary}
|
| 440 |
+
|
| 441 |
+
Key Interactions:
|
| 442 |
+
{interactions_summary}
|
| 443 |
+
|
| 444 |
+
Provide a JSON response with:
|
| 445 |
+
{{
|
| 446 |
+
"research_question": "Clear, testable research question",
|
| 447 |
+
"hypothesis": "Scientific hypothesis that can be tested through simulation",
|
| 448 |
+
"theoretical_framework": "Underlying social/behavioral theories",
|
| 449 |
+
"story_narrative": "Engaging narrative that contextualizes the simulation",
|
| 450 |
+
"expected_outcomes": [
|
| 451 |
+
"Specific measurable outcome 1",
|
| 452 |
+
"Specific measurable outcome 2",
|
| 453 |
+
"Specific measurable outcome 3"
|
| 454 |
+
],
|
| 455 |
+
"success_metrics": [
|
| 456 |
+
"How to measure if hypothesis is supported",
|
| 457 |
+
"Key indicators to track"
|
| 458 |
+
],
|
| 459 |
+
"variables_to_manipulate": [
|
| 460 |
+
{{
|
| 461 |
+
"name": "Variable name",
|
| 462 |
+
"description": "What this controls",
|
| 463 |
+
"range": "Possible values",
|
| 464 |
+
"impact": "Expected effect on system"
|
| 465 |
+
}}
|
| 466 |
+
]
|
| 467 |
+
}}
|
| 468 |
+
"""
|
| 469 |
+
|
| 470 |
+
messages = [
|
| 471 |
+
{"role": "system", "content": system_prompt},
|
| 472 |
+
{"role": "user", "content": user_prompt}
|
| 473 |
+
]
|
| 474 |
+
|
| 475 |
+
response = llm_large.chat(messages, temperature=0.7)
|
| 476 |
+
|
| 477 |
+
try:
|
| 478 |
+
json_match = re.search(r'\{.*\}', response, re.DOTALL)
|
| 479 |
+
if json_match:
|
| 480 |
+
data = json.loads(json_match.group())
|
| 481 |
+
state.research_question = data.get("research_question", "")
|
| 482 |
+
state.hypothesis = data.get("hypothesis", "")
|
| 483 |
+
state.story_narrative = data.get("story_narrative", "")
|
| 484 |
+
state.expected_outcomes = data.get("expected_outcomes", [])
|
| 485 |
+
except Exception as e:
|
| 486 |
+
state.hypothesis = f"Hypothesis development error: {str(e)}"
|
| 487 |
+
|
| 488 |
+
return state
|
| 489 |
+
|
| 490 |
+
def generate_emojis_node(state: SimulationState) -> SimulationState:
|
| 491 |
+
"""Generate emojis and visual representations for agents"""
|
| 492 |
+
|
| 493 |
+
system_prompt = """You are a creative designer specializing in visual representation of social agents.
|
| 494 |
+
Generate appropriate emojis and visual symbols for each agent that represent their role, characteristics, and function in the social network."""
|
| 495 |
+
|
| 496 |
+
agents_text = "\n".join([f"- {agent['name']}: {agent['description']}" for agent in state.agents])
|
| 497 |
+
|
| 498 |
+
user_prompt = f"""
|
| 499 |
+
Generate emojis for these social network agents:
|
| 500 |
+
{agents_text}
|
| 501 |
+
|
| 502 |
+
Provide a JSON response with emoji assignments:
|
| 503 |
+
{{
|
| 504 |
+
"agent_name": "🎭",
|
| 505 |
+
"agent_name2": "👥"
|
| 506 |
+
}}
|
| 507 |
+
|
| 508 |
+
Choose emojis that best represent each agent's role and characteristics.
|
| 509 |
+
"""
|
| 510 |
+
|
| 511 |
+
messages = [
|
| 512 |
+
{"role": "system", "content": system_prompt},
|
| 513 |
+
{"role": "user", "content": user_prompt}
|
| 514 |
+
]
|
| 515 |
+
|
| 516 |
+
response = llm_small.chat(messages, temperature=0.7)
|
| 517 |
+
|
| 518 |
+
try:
|
| 519 |
+
json_match = re.search(r'\{.*\}', response, re.DOTALL)
|
| 520 |
+
if json_match:
|
| 521 |
+
state.emojis = json.loads(json_match.group())
|
| 522 |
+
except Exception as e:
|
| 523 |
+
# Default emojis if parsing fails
|
| 524 |
+
state.emojis = {agent['name']: "👤" for agent in state.agents}
|
| 525 |
+
|
| 526 |
+
return state
|
| 527 |
+
|
| 528 |
+
def generate_simulation_node(state: SimulationState) -> SimulationState:
|
| 529 |
+
"""Generate advanced NetLogo-style simulation with comprehensive controls"""
|
| 530 |
+
|
| 531 |
+
system_prompt = """You are an expert in advanced agent-based modeling and interactive simulations.
|
| 532 |
+
|
| 533 |
+
Create a sophisticated HTML5 simulation with:
|
| 534 |
+
|
| 535 |
+
1. ADVANCED CONTROLS:
|
| 536 |
+
- Speed control (1-10 scale)
|
| 537 |
+
- Population controls for each agent type
|
| 538 |
+
- Environmental parameter controls
|
| 539 |
+
- Interaction probability controls
|
| 540 |
+
- Resource availability controls
|
| 541 |
+
- Scenario trigger buttons
|
| 542 |
+
|
| 543 |
+
2. VISUAL FEATURES:
|
| 544 |
+
- Dynamic environment visualization with color coding
|
| 545 |
+
- Agent trails showing movement history
|
| 546 |
+
- Network connections with varying thickness
|
| 547 |
+
- Real-time statistics dashboard
|
| 548 |
+
- Interactive agent information on hover/click
|
| 549 |
+
|
| 550 |
+
3. SCIENTIFIC FEATURES:
|
| 551 |
+
- Hypothesis testing interface
|
| 552 |
+
- Data collection and export
|
| 553 |
+
- Parameter sensitivity analysis
|
| 554 |
+
- Scenario comparison tools
|
| 555 |
+
- Statistical significance indicators
|
| 556 |
+
|
| 557 |
+
4. TECHNICAL REQUIREMENTS:
|
| 558 |
+
- Proper object-oriented agent architecture
|
| 559 |
+
- Efficient spatial indexing for large populations
|
| 560 |
+
- Event-driven interaction system
|
| 561 |
+
- Configurable random seed for reproducibility
|
| 562 |
+
- Performance monitoring and optimization
|
| 563 |
+
|
| 564 |
+
FOCUS HEAVILY ON THE IMPLEMENTATION OF THE LOGIC AND THE AGENTS INTERACTIONS. DO NOT SKIP ANY IMPLEMENTAION. THE PROJECT MUST BE COMPLETE.
|
| 565 |
+
|
| 566 |
+
The simulation must test the provided hypothesis and produce measurable outcomes."""
|
| 567 |
+
|
| 568 |
+
agents_detail = "\n".join([
|
| 569 |
+
f"""
|
| 570 |
+
Agent: {agent['name']} ({state.emojis.get(agent['name'], '👤')})
|
| 571 |
+
- Type: {agent['type']}
|
| 572 |
+
- Goals: {agent['attributes']['goals']}
|
| 573 |
+
- Decision Strategy: {agent['attributes']['decision_strategy']}
|
| 574 |
+
- Cooperation: {agent['attributes']['cooperation_tendency']}/10
|
| 575 |
+
- Aggression: {agent['attributes']['aggression_level']}/10
|
| 576 |
+
- Resources: {agent['attributes']['resources']}
|
| 577 |
+
"""
|
| 578 |
+
for agent in state.agents
|
| 579 |
+
])
|
| 580 |
+
|
| 581 |
+
interactions_detail = "\n".join([
|
| 582 |
+
f"""
|
| 583 |
+
Interaction: {interaction['interaction_type']}
|
| 584 |
+
- Participants: {interaction['participants']}
|
| 585 |
+
- Conditions: {interaction['conditions']}
|
| 586 |
+
- Success: {interaction['outcomes']['positive']}
|
| 587 |
+
- Failure: {interaction['outcomes']['negative']}
|
| 588 |
+
- Environmental Impact: {interaction['outcomes']['environmental_impact']}
|
| 589 |
+
"""
|
| 590 |
+
for interaction in state.agent_interactions
|
| 591 |
+
])
|
| 592 |
+
|
| 593 |
+
environment_detail = f"""
|
| 594 |
+
Environment: {state.environment.get('name', 'Unknown')}
|
| 595 |
+
- Description: {state.environment.get('description', '')}
|
| 596 |
+
- Resources: {state.environment.get('resources', [])}
|
| 597 |
+
- Constraints: {state.environment.get('constraints', [])}
|
| 598 |
+
- Spatial Properties: {state.environment.get('spatial_properties', '')}
|
| 599 |
+
"""
|
| 600 |
+
|
| 601 |
+
if isinstance(state, dict):
|
| 602 |
+
research_question = state['research_question']
|
| 603 |
+
expected_outcomes = state['expected_outcomes']
|
| 604 |
+
else:
|
| 605 |
+
research_question = state.research_question
|
| 606 |
+
expected_outcomes = state.expected_outcomes
|
| 607 |
+
user_prompt = f"""
|
| 608 |
+
Create an advanced agent-based simulation for:
|
| 609 |
+
|
| 610 |
+
HYPOTHESIS TO TEST: {state.hypothesis}
|
| 611 |
+
|
| 612 |
+
STORY CONTEXT: {state.story_narrative}
|
| 613 |
+
|
| 614 |
+
RESEARCH QUESTION: {research_question}
|
| 615 |
+
|
| 616 |
+
ENVIRONMENT:
|
| 617 |
+
{environment_detail}
|
| 618 |
+
|
| 619 |
+
AGENTS:
|
| 620 |
+
{agents_detail}
|
| 621 |
+
|
| 622 |
+
INTERACTIONS:
|
| 623 |
+
{interactions_detail}
|
| 624 |
+
|
| 625 |
+
EXPECTED OUTCOMES: {expected_outcomes}
|
| 626 |
+
|
| 627 |
+
Generate a complete HTML file with:
|
| 628 |
+
|
| 629 |
+
1. CONTROL PANEL (left side):
|
| 630 |
+
- Simulation speed slider (1-1000 fps)
|
| 631 |
+
- Play/Pause/Reset/Step buttons
|
| 632 |
+
- Population controls for each agent type (0-100)
|
| 633 |
+
- Environmental parameter sliders based on the environment
|
| 634 |
+
- Interaction probability controls
|
| 635 |
+
- Scenario buttons for testing different conditions
|
| 636 |
+
- Random seed input for reproducibility
|
| 637 |
+
|
| 638 |
+
2. MAIN VISUALIZATION (center):
|
| 639 |
+
- Large canvas (400x400) showing the environment. dO NOT MAKE THE CANVAS BIGGER THAN 400x400.
|
| 640 |
+
- Dynamic background representing environmental state
|
| 641 |
+
- Agents with emojis, trails, and status indicators
|
| 642 |
+
- Connection lines showing relationships/interactions
|
| 643 |
+
- Spatial zones for different environment areas
|
| 644 |
+
- Ensure using a single emoji for each agent type. The emojis are provided for each agent.
|
| 645 |
+
- Main visualization must not overlap with control panel or statistics panel.
|
| 646 |
+
|
| 647 |
+
3. STATISTICS PANEL (right side):
|
| 648 |
+
- STATISTIC PANEL MUST BE UPDATED IN REAL-TIME
|
| 649 |
+
- Real-time metrics tracking hypothesis variables
|
| 650 |
+
- Population counts and survival rates
|
| 651 |
+
- Resource distribution graphs
|
| 652 |
+
- Interaction frequency charts
|
| 653 |
+
- Hypothesis testing results
|
| 654 |
+
- Data export functionality
|
| 655 |
+
|
| 656 |
+
4. ADVANCED FEATURES:
|
| 657 |
+
- Agent inspector showing detailed state on click
|
| 658 |
+
- Heatmaps for resource density and activity
|
| 659 |
+
- Timeline scrubber for replay functionality
|
| 660 |
+
- Parameter sweeping tools
|
| 661 |
+
- Automated experiment runner
|
| 662 |
+
|
| 663 |
+
Technical Implementation:
|
| 664 |
+
- Use requestAnimationFrame for smooth animation
|
| 665 |
+
- Implement spatial hashing for performance
|
| 666 |
+
- Object-oriented agent and environment classes
|
| 667 |
+
- Event system for interactions
|
| 668 |
+
- Proper initialization and cleanup
|
| 669 |
+
- Error handling and validation
|
| 670 |
+
- Responsive design
|
| 671 |
+
|
| 672 |
+
The simulation should automatically start with meaningful default values and immediately show the hypothesis being tested through agent behaviors.
|
| 673 |
+
"""
|
| 674 |
+
|
| 675 |
+
messages = [
|
| 676 |
+
{"role": "system", "content": system_prompt},
|
| 677 |
+
{"role": "user", "content": user_prompt}
|
| 678 |
+
]
|
| 679 |
+
|
| 680 |
+
state.simulation_code = llm_codestral.chat(messages, temperature=0.2, max_tokens=8000)
|
| 681 |
+
|
| 682 |
+
return state
|
| 683 |
+
|
| 684 |
+
def check_simulation_code_node(state: SimulationState) -> SimulationState:
|
| 685 |
+
"""Check and validate the generated simulation code using Codestral"""
|
| 686 |
+
|
| 687 |
+
print(f"---CHECKING SIMULATION CODE (Attempt {state.validation_attempts + 1})---")
|
| 688 |
+
|
| 689 |
+
# Increment validation attempts
|
| 690 |
+
state.validation_attempts += 1
|
| 691 |
+
|
| 692 |
+
system_prompt = """You are an expert code reviewer and HTML/JavaScript validator specializing in agent-based simulations.
|
| 693 |
+
|
| 694 |
+
Your task is to:
|
| 695 |
+
1. Analyze the provided HTML simulation code for syntax errors, logic issues, and functionality problems
|
| 696 |
+
2. Check for proper implementation of agent behaviors, interactions, and controls
|
| 697 |
+
3. Verify that all promised features are implemented correctly
|
| 698 |
+
4. Identify any missing functionality or broken components
|
| 699 |
+
5. Provide specific fixes and improvements
|
| 700 |
+
|
| 701 |
+
Focus on:
|
| 702 |
+
- HTML/CSS/JavaScript syntax validation
|
| 703 |
+
- Proper agent implementation with movement, interactions, and behaviors
|
| 704 |
+
- Working control panels with functional sliders and buttons
|
| 705 |
+
- Real-time statistics and data display
|
| 706 |
+
- Canvas rendering and animation loops
|
| 707 |
+
- Event handling and user interactions
|
| 708 |
+
- Performance and efficiency issues
|
| 709 |
+
"""
|
| 710 |
+
|
| 711 |
+
# Extract HTML code if it's wrapped in markdown
|
| 712 |
+
code_to_check = state.simulation_code
|
| 713 |
+
if '```html' in code_to_check:
|
| 714 |
+
html_match = re.search(r'```html\n(.*?)\n```', code_to_check, re.DOTALL)
|
| 715 |
+
if html_match:
|
| 716 |
+
code_to_check = html_match.group(1)
|
| 717 |
+
|
| 718 |
+
user_prompt = f"""
|
| 719 |
+
Analyze this agent-based simulation code and identify any issues:
|
| 720 |
+
|
| 721 |
+
CODE TO CHECK:
|
| 722 |
+
{code_to_check}
|
| 723 |
+
|
| 724 |
+
EXPECTED FEATURES (check if properly implemented):
|
| 725 |
+
- Hypothesis: {state.hypothesis}
|
| 726 |
+
- Agents: {[agent['name'] for agent in state.agents]}
|
| 727 |
+
- Emojis: {state.emojis}
|
| 728 |
+
- Environment: {state.environment.get('name', 'Unknown')}
|
| 729 |
+
- Interactions: {[interaction['interaction_type'] for interaction in state.agent_interactions]}
|
| 730 |
+
|
| 731 |
+
Previous validation attempts: {state.validation_attempts - 1}
|
| 732 |
+
Previous errors found: {state.code_errors}
|
| 733 |
+
|
| 734 |
+
Provide a comprehensive JSON response:
|
| 735 |
+
{{
|
| 736 |
+
"is_valid": boolean,
|
| 737 |
+
"syntax_errors": ["list of syntax errors found"],
|
| 738 |
+
"logic_errors": ["list of logic/functionality errors"],
|
| 739 |
+
"missing_features": ["list of features that should be implemented but are missing"],
|
| 740 |
+
"performance_issues": ["list of performance problems"],
|
| 741 |
+
"agent_issues": ["specific problems with agent implementation"],
|
| 742 |
+
"interaction_issues": ["problems with agent interactions"],
|
| 743 |
+
"control_issues": ["problems with UI controls"],
|
| 744 |
+
"overall_assessment": "summary of code quality and functionality"
|
| 745 |
+
}}
|
| 746 |
+
"""
|
| 747 |
+
|
| 748 |
+
messages = [
|
| 749 |
+
{"role": "system", "content": system_prompt},
|
| 750 |
+
{"role": "user", "content": user_prompt}
|
| 751 |
+
]
|
| 752 |
+
|
| 753 |
+
response = llm_codestral.chat(messages, temperature=0.3)
|
| 754 |
+
|
| 755 |
+
try:
|
| 756 |
+
json_match = re.search(r'\{.*\}', response, re.DOTALL)
|
| 757 |
+
if json_match:
|
| 758 |
+
validation_result = json.loads(json_match.group())
|
| 759 |
+
|
| 760 |
+
# Collect all errors
|
| 761 |
+
all_errors = (
|
| 762 |
+
validation_result.get("syntax_errors", []) +
|
| 763 |
+
validation_result.get("logic_errors", []) +
|
| 764 |
+
validation_result.get("missing_features", []) +
|
| 765 |
+
validation_result.get("performance_issues", []) +
|
| 766 |
+
validation_result.get("agent_issues", []) +
|
| 767 |
+
validation_result.get("interaction_issues", []) +
|
| 768 |
+
validation_result.get("control_issues", [])
|
| 769 |
+
)
|
| 770 |
+
|
| 771 |
+
state.code_errors = all_errors
|
| 772 |
+
state.code_is_valid = validation_result.get("is_valid", False) and len(all_errors) == 0
|
| 773 |
+
|
| 774 |
+
print(f"Code validation result: Valid={state.code_is_valid}, Errors found: {len(all_errors)}")
|
| 775 |
+
|
| 776 |
+
else:
|
| 777 |
+
state.code_errors = ["Failed to parse validation response"]
|
| 778 |
+
state.code_is_valid = False
|
| 779 |
+
|
| 780 |
+
except Exception as e:
|
| 781 |
+
state.code_errors = [f"Validation error: {str(e)}"]
|
| 782 |
+
state.code_is_valid = False
|
| 783 |
+
|
| 784 |
+
return state
|
| 785 |
+
|
| 786 |
+
def fix_simulation_code_node(state: SimulationState) -> SimulationState:
|
| 787 |
+
"""Fix the simulation code based on validation errors"""
|
| 788 |
+
|
| 789 |
+
print(f"---FIXING SIMULATION CODE (Attempt {state.validation_attempts})---")
|
| 790 |
+
|
| 791 |
+
system_prompt = """You are an expert programmer specializing in HTML5 agent-based simulations.
|
| 792 |
+
|
| 793 |
+
Your task is to fix the provided simulation code based on the identified errors and issues.
|
| 794 |
+
Focus on:
|
| 795 |
+
1. Fixing syntax errors and logic problems
|
| 796 |
+
2. Implementing missing agent behaviors and interactions
|
| 797 |
+
3. Ensuring proper canvas rendering and animation
|
| 798 |
+
4. Making sure all UI controls work correctly
|
| 799 |
+
5. Implementing real-time statistics updates
|
| 800 |
+
6. Optimizing performance issues
|
| 801 |
+
|
| 802 |
+
Provide ONLY the corrected HTML code without any explanations or markdown formatting.
|
| 803 |
+
"""
|
| 804 |
+
|
| 805 |
+
# Extract HTML code if it's wrapped in markdown
|
| 806 |
+
current_code = state.simulation_code
|
| 807 |
+
if '```html' in current_code:
|
| 808 |
+
html_match = re.search(r'```html\n(.*?)\n```', current_code, re.DOTALL)
|
| 809 |
+
if html_match:
|
| 810 |
+
current_code = html_match.group(1)
|
| 811 |
+
|
| 812 |
+
errors_text = "\n".join([f"- {error}" for error in state.code_errors])
|
| 813 |
+
|
| 814 |
+
user_prompt = f"""
|
| 815 |
+
Fix this agent-based simulation code based on the identified errors:
|
| 816 |
+
|
| 817 |
+
CURRENT CODE:
|
| 818 |
+
{current_code}
|
| 819 |
+
|
| 820 |
+
ERRORS TO FIX:
|
| 821 |
+
{errors_text}
|
| 822 |
+
|
| 823 |
+
REQUIREMENTS TO MAINTAIN:
|
| 824 |
+
- Hypothesis: {state.hypothesis}
|
| 825 |
+
- Agents: {[agent['name'] for agent in state.agents]}
|
| 826 |
+
- Emojis: {state.emojis}
|
| 827 |
+
- Environment: {state.environment.get('name', 'Unknown')}
|
| 828 |
+
- Canvas size: 400x400 maximum
|
| 829 |
+
- Real-time statistics panel
|
| 830 |
+
- Working control panels
|
| 831 |
+
- Proper agent movement and interactions
|
| 832 |
+
|
| 833 |
+
Provide the complete fixed HTML code:
|
| 834 |
+
"""
|
| 835 |
+
|
| 836 |
+
messages = [
|
| 837 |
+
{"role": "system", "content": system_prompt},
|
| 838 |
+
{"role": "user", "content": user_prompt}
|
| 839 |
+
]
|
| 840 |
+
|
| 841 |
+
fixed_code = llm_codestral.chat(messages, temperature=0.2, max_tokens=8000)
|
| 842 |
+
|
| 843 |
+
# Clean up the response to ensure it's proper HTML
|
| 844 |
+
if '```html' in fixed_code:
|
| 845 |
+
html_match = re.search(r'```html\n(.*?)\n```', fixed_code, re.DOTALL)
|
| 846 |
+
if html_match:
|
| 847 |
+
fixed_code = html_match.group(1)
|
| 848 |
+
|
| 849 |
+
state.simulation_code = fixed_code
|
| 850 |
+
|
| 851 |
+
return state
|
| 852 |
+
|
| 853 |
+
def create_workflow() -> StateGraph:
|
| 854 |
+
"""Create the enhanced LangGraph workflow with code validation"""
|
| 855 |
+
|
| 856 |
+
workflow = StateGraph(SimulationState)
|
| 857 |
+
|
| 858 |
+
# Add all nodes
|
| 859 |
+
workflow.add_node("extract_content", extract_content_node)
|
| 860 |
+
workflow.add_node("process_image", process_image_node)
|
| 861 |
+
workflow.add_node("analyze_content", analyze_content_node)
|
| 862 |
+
workflow.add_node("develop_hypothesis", develop_hypothesis_node)
|
| 863 |
+
workflow.add_node("generate_emojis", generate_emojis_node)
|
| 864 |
+
workflow.add_node("generate_simulation", generate_simulation_node)
|
| 865 |
+
workflow.add_node("check_simulation_code", check_simulation_code_node)
|
| 866 |
+
workflow.add_node("fix_simulation_code", fix_simulation_code_node)
|
| 867 |
+
|
| 868 |
+
# Define the enhanced flow
|
| 869 |
+
workflow.set_entry_point("extract_content")
|
| 870 |
+
|
| 871 |
+
# Conditional routing based on content type
|
| 872 |
+
def route_content(state: SimulationState) -> str:
|
| 873 |
+
if state.content_type == "image":
|
| 874 |
+
return "process_image"
|
| 875 |
+
else:
|
| 876 |
+
state.content_type = "article"
|
| 877 |
+
return "analyze_content"
|
| 878 |
+
|
| 879 |
+
# Conditional routing for code validation
|
| 880 |
+
def should_fix_code(state: SimulationState) -> str:
|
| 881 |
+
if state.code_is_valid or state.validation_attempts >= state.max_validation_attempts:
|
| 882 |
+
return "end"
|
| 883 |
+
else:
|
| 884 |
+
return "fix_code"
|
| 885 |
+
|
| 886 |
+
workflow.add_conditional_edges(
|
| 887 |
+
"extract_content",
|
| 888 |
+
route_content,
|
| 889 |
+
{
|
| 890 |
+
"process_image": "process_image",
|
| 891 |
+
"analyze_content": "analyze_content"
|
| 892 |
+
}
|
| 893 |
+
)
|
| 894 |
+
|
| 895 |
+
# Enhanced workflow: analyze -> hypothesis -> emojis -> simulation -> validation loop
|
| 896 |
+
workflow.add_edge("process_image", "analyze_content")
|
| 897 |
+
workflow.add_edge("analyze_content", "develop_hypothesis")
|
| 898 |
+
workflow.add_edge("develop_hypothesis", "generate_emojis")
|
| 899 |
+
workflow.add_edge("generate_emojis", "generate_simulation")
|
| 900 |
+
workflow.add_edge("generate_simulation", "check_simulation_code")
|
| 901 |
+
|
| 902 |
+
# Conditional edge for validation loop
|
| 903 |
+
workflow.add_conditional_edges(
|
| 904 |
+
"check_simulation_code",
|
| 905 |
+
should_fix_code,
|
| 906 |
+
{
|
| 907 |
+
"fix_code": "fix_simulation_code",
|
| 908 |
+
"end": END
|
| 909 |
+
}
|
| 910 |
+
)
|
| 911 |
+
|
| 912 |
+
# After fixing code, check again
|
| 913 |
+
workflow.add_edge("fix_simulation_code", "check_simulation_code")
|
| 914 |
+
|
| 915 |
+
return workflow
|
| 916 |
+
|
| 917 |
+
# Create workflow and app
|
| 918 |
+
workflow = create_workflow()
|
| 919 |
+
memory = SqliteSaver.from_conn_string(":memory:")
|
| 920 |
+
app = workflow.compile()
|
| 921 |
+
|
| 922 |
+
def save_graph_as_png() -> bytes:
|
| 923 |
+
"""Save the LangGraph mermaid graph as PNG"""
|
| 924 |
+
try:
|
| 925 |
+
return app.get_graph().draw_mermaid_png(
|
| 926 |
+
draw_method=MermaidDrawMethod.API,
|
| 927 |
+
background_color="white",
|
| 928 |
+
padding=10
|
| 929 |
+
)
|
| 930 |
+
except Exception as e:
|
| 931 |
+
print(f"Error saving graph: {e}")
|
| 932 |
+
return None
|
| 933 |
+
|
| 934 |
+
def create_gradio_interface():
|
| 935 |
+
"""Create the enhanced Gradio interface"""
|
| 936 |
+
|
| 937 |
+
with gr.Blocks(title="Advanced Social Network Simulation Generator", theme=gr.themes.Soft()) as demo:
|
| 938 |
+
gr.Markdown("# 🌐 AI-Powered Advanced Social Network Simulation Generator")
|
| 939 |
+
gr.Markdown("### 📚 Using Mistral Models:")
|
| 940 |
+
for _, model in MistralModelHub(api_key=os.getenv("MISTRAL_API_KEY")).models.items():
|
| 941 |
+
gr.Markdown(f"- {model.model}")
|
| 942 |
+
gr.Markdown("Generate advanced NetLogo-style social network simulations with scientific hypothesis testing from articles or images using AI agents.")
|
| 943 |
+
gr.Markdown("### ✨ NEW: Code validation with up to 3 retry attempts using Codestral")
|
| 944 |
+
|
| 945 |
+
gr.Markdown("### 📚 Examples:")
|
| 946 |
+
gr.Markdown("- https://www.ansa.it/sito/notizie/mondo/nordamerica")
|
| 947 |
+
gr.Markdown("- https://www.ansa.it/sito/notizie/mondo/nordamerica/2025/06/10/dilagano-le-proteste-negli-usa150-arresti-a-san-francisco_3d6b794b-462c-4dd1-9c46-33ecf2bc5790.html")
|
| 948 |
+
gr.Markdown("- https://theonion.com/trump-issues-executive-order-reversing-all-vasectomies/")
|
| 949 |
+
|
| 950 |
+
with gr.Row():
|
| 951 |
+
with gr.Column(scale=2):
|
| 952 |
+
# Input section
|
| 953 |
+
url_input = gr.Textbox(
|
| 954 |
+
label="🔗 URL",
|
| 955 |
+
placeholder="Enter URL to article or image...",
|
| 956 |
+
lines=1
|
| 957 |
+
)
|
| 958 |
+
|
| 959 |
+
extract_btn = gr.Button("📄 Extract Content", variant="primary")
|
| 960 |
+
|
| 961 |
+
# Content display
|
| 962 |
+
content_display = gr.Markdown(label="📋 Extracted Content")
|
| 963 |
+
|
| 964 |
+
# Editable summary and agents
|
| 965 |
+
with gr.Group():
|
| 966 |
+
gr.Markdown("### ✏️ Edit Summary and Analysis")
|
| 967 |
+
summary_edit = gr.Textbox(
|
| 968 |
+
label="Summary",
|
| 969 |
+
lines=3,
|
| 970 |
+
placeholder="Summary will appear here..."
|
| 971 |
+
)
|
| 972 |
+
|
| 973 |
+
agents_edit = gr.Textbox(
|
| 974 |
+
label="Agents (JSON format - Edit as needed)",
|
| 975 |
+
lines=8,
|
| 976 |
+
placeholder="Agents JSON will appear here...",
|
| 977 |
+
info="Edit the JSON to modify agents before generating simulation"
|
| 978 |
+
)
|
| 979 |
+
|
| 980 |
+
behaviors_edit = gr.Textbox(
|
| 981 |
+
label="Behaviors (JSON format - Edit as needed)",
|
| 982 |
+
lines=4,
|
| 983 |
+
placeholder="Behaviors JSON will appear here...",
|
| 984 |
+
info="Edit the JSON to modify behaviors before generating simulation"
|
| 985 |
+
)
|
| 986 |
+
|
| 987 |
+
# Enhanced fields
|
| 988 |
+
with gr.Group():
|
| 989 |
+
gr.Markdown("### 🔬 Research Hypothesis")
|
| 990 |
+
hypothesis_edit = gr.Textbox(
|
| 991 |
+
label="Research Hypothesis",
|
| 992 |
+
lines=3,
|
| 993 |
+
placeholder="Hypothesis will appear here..."
|
| 994 |
+
)
|
| 995 |
+
|
| 996 |
+
story_edit = gr.Textbox(
|
| 997 |
+
label="Story Narrative",
|
| 998 |
+
lines=4,
|
| 999 |
+
placeholder="Story narrative will appear here..."
|
| 1000 |
+
)
|
| 1001 |
+
|
| 1002 |
+
environment_edit = gr.Textbox(
|
| 1003 |
+
label="Environment (JSON format)",
|
| 1004 |
+
lines=6,
|
| 1005 |
+
placeholder="Environment JSON will appear here..."
|
| 1006 |
+
)
|
| 1007 |
+
|
| 1008 |
+
interactions_edit = gr.Textbox(
|
| 1009 |
+
label="Agent Interactions (JSON format)",
|
| 1010 |
+
lines=8,
|
| 1011 |
+
placeholder="Interactions JSON will appear here..."
|
| 1012 |
+
)
|
| 1013 |
+
|
| 1014 |
+
# Emoji selection
|
| 1015 |
+
with gr.Group():
|
| 1016 |
+
gr.Markdown("### 🎭 Agent Emojis")
|
| 1017 |
+
emojis_edit = gr.Textbox(
|
| 1018 |
+
label="Emojis for Agents (JSON format - Edit as needed)",
|
| 1019 |
+
lines=4,
|
| 1020 |
+
placeholder="Emojis JSON will appear here...",
|
| 1021 |
+
info="Edit the JSON to modify emojis before generating simulation"
|
| 1022 |
+
)
|
| 1023 |
+
|
| 1024 |
+
generate_emojis_btn = gr.Button("🎨 Generate/Regenerate Emojis")
|
| 1025 |
+
|
| 1026 |
+
# Generate simulation
|
| 1027 |
+
confirm_btn = gr.Button("🚀 Generate Advanced Simulation", variant="primary", size="lg")
|
| 1028 |
+
|
| 1029 |
+
with gr.Column(scale=3):
|
| 1030 |
+
# Results section
|
| 1031 |
+
with gr.Tabs():
|
| 1032 |
+
with gr.TabItem("🎮 Simulation"):
|
| 1033 |
+
simulation_frame = gr.HTML(
|
| 1034 |
+
label="Advanced Social Network Simulation",
|
| 1035 |
+
value="<div style='padding: 20px; text-align: center; color: #666;'>Advanced simulation will appear here after generation...</div>"
|
| 1036 |
+
)
|
| 1037 |
+
|
| 1038 |
+
with gr.TabItem("💻 Code Editor"):
|
| 1039 |
+
simulation_code_editor = gr.Code(
|
| 1040 |
+
label="Generated Simulation Code (Editable)",
|
| 1041 |
+
language="html",
|
| 1042 |
+
value="<!-- Advanced simulation code will appear here... -->",
|
| 1043 |
+
lines=20,
|
| 1044 |
+
interactive=True
|
| 1045 |
+
)
|
| 1046 |
+
|
| 1047 |
+
update_simulation_btn = gr.Button("🔄 Update Simulation from Code", variant="secondary")
|
| 1048 |
+
|
| 1049 |
+
with gr.TabItem("📖 Documentation"):
|
| 1050 |
+
documentation_display = gr.Markdown(
|
| 1051 |
+
label="Scientific Simulation Documentation",
|
| 1052 |
+
value="Comprehensive documentation will be generated with the simulation..."
|
| 1053 |
+
)
|
| 1054 |
+
|
| 1055 |
+
with gr.TabItem("🗺️ Workflow Graph"):
|
| 1056 |
+
graph_display = gr.Image(
|
| 1057 |
+
label="Enhanced LangGraph Workflow",
|
| 1058 |
+
value=None
|
| 1059 |
+
)
|
| 1060 |
+
|
| 1061 |
+
# State management
|
| 1062 |
+
state = gr.State(SimulationState())
|
| 1063 |
+
|
| 1064 |
+
# Event handlers
|
| 1065 |
+
def extract_content(url, current_state):
|
| 1066 |
+
current_state = SimulationState(url=url)
|
| 1067 |
+
config = {"configurable": {"thread_id": "simulation_thread"}}
|
| 1068 |
+
|
| 1069 |
+
print("Current state:")
|
| 1070 |
+
print(f"{type(current_state)=}")
|
| 1071 |
+
if isinstance(current_state, SimulationState):
|
| 1072 |
+
current_state_dict = current_state.to_dict()
|
| 1073 |
+
else:
|
| 1074 |
+
current_state_dict = current_state
|
| 1075 |
+
for key, value in current_state_dict.items():
|
| 1076 |
+
print(f"{key}: {str(value)[:50]}")
|
| 1077 |
+
|
| 1078 |
+
# Run extraction and analysis
|
| 1079 |
+
result = app.invoke(current_state, config)
|
| 1080 |
+
|
| 1081 |
+
if not isinstance(result, dict):
|
| 1082 |
+
if isinstance(result, SimulationState):
|
| 1083 |
+
result = result.to_dict()
|
| 1084 |
+
else:
|
| 1085 |
+
raise ValueError(f"Unexpected result type: {type(result)}")
|
| 1086 |
+
|
| 1087 |
+
print("Result:")
|
| 1088 |
+
print(f"{type(result)=}")
|
| 1089 |
+
for key, value in result.items():
|
| 1090 |
+
print(f"{key}: {str(value)[:50]}")
|
| 1091 |
+
|
| 1092 |
+
# Update displays with enhanced content
|
| 1093 |
+
cleaned_content = re.sub(r'\s+', ' ', result['content']).strip()
|
| 1094 |
+
content_md = f"**Content Type:** {result['content_type']}\n\n**Content:**\n{cleaned_content[:1000]}..."
|
| 1095 |
+
|
| 1096 |
+
return (
|
| 1097 |
+
result, # Updated state
|
| 1098 |
+
content_md, # Content display
|
| 1099 |
+
result['summary'], # Summary
|
| 1100 |
+
json.dumps(result['agents'], indent=2), # Agents as JSON string
|
| 1101 |
+
json.dumps(result['behaviors'], indent=2), # Behaviors as JSON string
|
| 1102 |
+
result['hypothesis'], # New: Hypothesis
|
| 1103 |
+
result['story_narrative'], # New: Story
|
| 1104 |
+
json.dumps(result['environment'], indent=2), # New: Environment
|
| 1105 |
+
json.dumps(result['agent_interactions'], indent=2), # New: Interactions
|
| 1106 |
+
json.dumps(result['emojis'], indent=2, ensure_ascii=False) # Emojis as JSON string
|
| 1107 |
+
)
|
| 1108 |
+
|
| 1109 |
+
def generate_emojis_only(current_state, agents_json):
|
| 1110 |
+
# Update current state with potentially edited agents
|
| 1111 |
+
try:
|
| 1112 |
+
current_state.agents = json.loads(agents_json) if agents_json else []
|
| 1113 |
+
except:
|
| 1114 |
+
pass # Keep existing agents if JSON parsing fails
|
| 1115 |
+
|
| 1116 |
+
if not current_state.agents:
|
| 1117 |
+
return current_state, "{}"
|
| 1118 |
+
|
| 1119 |
+
current_state = generate_emojis_node(current_state)
|
| 1120 |
+
return current_state, json.dumps(current_state.emojis, indent=2, ensure_ascii=False)
|
| 1121 |
+
|
| 1122 |
+
def generate_final_simulation(current_state, summary, agents_json, behaviors_json, hypothesis, story, environment_json, interactions_json, emojis_json):
|
| 1123 |
+
# Update state with edited values
|
| 1124 |
+
current_state.summary = summary
|
| 1125 |
+
current_state.hypothesis = hypothesis
|
| 1126 |
+
current_state.story_narrative = story
|
| 1127 |
+
|
| 1128 |
+
try:
|
| 1129 |
+
current_state.agents = json.loads(agents_json) if agents_json else []
|
| 1130 |
+
except json.JSONDecodeError as e:
|
| 1131 |
+
print(f"Error parsing agents JSON: {e}")
|
| 1132 |
+
current_state.agents = []
|
| 1133 |
+
|
| 1134 |
+
try:
|
| 1135 |
+
current_state.behaviors = json.loads(behaviors_json) if behaviors_json else []
|
| 1136 |
+
except json.JSONDecodeError as e:
|
| 1137 |
+
print(f"Error parsing behaviors JSON: {e}")
|
| 1138 |
+
current_state.behaviors = []
|
| 1139 |
+
|
| 1140 |
+
try:
|
| 1141 |
+
current_state.environment = json.loads(environment_json) if environment_json else {}
|
| 1142 |
+
except json.JSONDecodeError as e:
|
| 1143 |
+
print(f"Error parsing environment JSON: {e}")
|
| 1144 |
+
current_state.environment = {}
|
| 1145 |
+
|
| 1146 |
+
try:
|
| 1147 |
+
current_state.agent_interactions = json.loads(interactions_json) if interactions_json else []
|
| 1148 |
+
except json.JSONDecodeError as e:
|
| 1149 |
+
print(f"Error parsing interactions JSON: {e}")
|
| 1150 |
+
current_state.agent_interactions = []
|
| 1151 |
+
|
| 1152 |
+
try:
|
| 1153 |
+
current_state.emojis = json.loads(emojis_json) if emojis_json else {}
|
| 1154 |
+
except json.JSONDecodeError as e:
|
| 1155 |
+
print(f"Error parsing emojis JSON: {e}")
|
| 1156 |
+
current_state.emojis = {}
|
| 1157 |
+
|
| 1158 |
+
# Reset validation state for new generation
|
| 1159 |
+
current_state.validation_attempts = 0
|
| 1160 |
+
current_state.code_errors = []
|
| 1161 |
+
current_state.code_is_valid = False
|
| 1162 |
+
|
| 1163 |
+
# Generate simulation (now includes validation loop)
|
| 1164 |
+
current_state = generate_simulation_node(current_state)
|
| 1165 |
+
|
| 1166 |
+
# The validation loop is now handled in the workflow
|
| 1167 |
+
config = {"configurable": {"thread_id": "validation_thread"}}
|
| 1168 |
+
validated_result = app.invoke(current_state, config)
|
| 1169 |
+
|
| 1170 |
+
if isinstance(validated_result, dict):
|
| 1171 |
+
current_state.simulation_code = validated_result.get('simulation_code', current_state.simulation_code)
|
| 1172 |
+
current_state.validation_attempts = validated_result.get('validation_attempts', 0)
|
| 1173 |
+
current_state.code_errors = validated_result.get('code_errors', [])
|
| 1174 |
+
current_state.code_is_valid = validated_result.get('code_is_valid', False)
|
| 1175 |
+
|
| 1176 |
+
# Generate documentation only after validation is complete
|
| 1177 |
+
if isinstance(current_state, dict):
|
| 1178 |
+
research_question = current_state['research_question']
|
| 1179 |
+
expected_outcomes = current_state['expected_outcomes']
|
| 1180 |
+
else:
|
| 1181 |
+
research_question = current_state.research_question
|
| 1182 |
+
expected_outcomes = current_state.expected_outcomes
|
| 1183 |
+
|
| 1184 |
+
environment_detail = f"""
|
| 1185 |
+
Environment: {current_state.environment.get('name', 'Unknown')}
|
| 1186 |
+
- Description: {current_state.environment.get('description', '')}
|
| 1187 |
+
- Resources: {current_state.environment.get('resources', [])}
|
| 1188 |
+
- Constraints: {current_state.environment.get('constraints', [])}
|
| 1189 |
+
- Spatial Properties: {current_state.environment.get('spatial_properties', '')}
|
| 1190 |
+
"""
|
| 1191 |
+
|
| 1192 |
+
agents_detail = "\n".join([
|
| 1193 |
+
f"""
|
| 1194 |
+
Agent: {agent['name']} ({current_state.emojis.get(agent['name'], '👤')})
|
| 1195 |
+
- Type: {agent['type']}
|
| 1196 |
+
- Goals: {agent['attributes']['goals']}
|
| 1197 |
+
"""
|
| 1198 |
+
for agent in current_state.agents
|
| 1199 |
+
])
|
| 1200 |
+
|
| 1201 |
+
interactions_detail = "\n".join([
|
| 1202 |
+
f"""
|
| 1203 |
+
Interaction: {interaction['interaction_type']}
|
| 1204 |
+
- Participants: {interaction['participants']}
|
| 1205 |
+
"""
|
| 1206 |
+
for interaction in current_state.agent_interactions
|
| 1207 |
+
])
|
| 1208 |
+
|
| 1209 |
+
doc_prompt = f"""
|
| 1210 |
+
Create comprehensive documentation for this social network simulation based on:
|
| 1211 |
+
|
| 1212 |
+
HYPOTHESIS: {current_state.hypothesis}
|
| 1213 |
+
STORY: {current_state.story_narrative}
|
| 1214 |
+
RESEARCH QUESTION: {research_question}
|
| 1215 |
+
ENVIRONMENT: {environment_detail}
|
| 1216 |
+
AGENTS: {agents_detail}
|
| 1217 |
+
INTERACTIONS: {interactions_detail}
|
| 1218 |
+
EXPECTED OUTCOMES: {expected_outcomes}
|
| 1219 |
+
|
| 1220 |
+
VALIDATION RESULTS:
|
| 1221 |
+
- Code validation attempts: {current_state.validation_attempts}
|
| 1222 |
+
- Code is valid: {current_state.code_is_valid}
|
| 1223 |
+
- Errors found: {current_state.code_errors}
|
| 1224 |
+
|
| 1225 |
+
Include:
|
| 1226 |
+
1. Overview and scientific purpose
|
| 1227 |
+
2. Hypothesis explanation and testing methodology
|
| 1228 |
+
3. Agent descriptions and behavioral models
|
| 1229 |
+
4. Environment and interaction explanations
|
| 1230 |
+
5. How to use advanced controls
|
| 1231 |
+
6. Parameters and their scientific effects
|
| 1232 |
+
7. Interpretation of results and statistical measures
|
| 1233 |
+
8. Educational objectives and learning outcomes
|
| 1234 |
+
9. Code quality assurance and validation process
|
| 1235 |
+
|
| 1236 |
+
Make it accessible for users while being scientifically rigorous.
|
| 1237 |
+
"""
|
| 1238 |
+
|
| 1239 |
+
doc_messages = [
|
| 1240 |
+
{"role": "system", "content": "You are a scientific technical writer creating educational documentation."},
|
| 1241 |
+
{"role": "user", "content": doc_prompt}
|
| 1242 |
+
]
|
| 1243 |
+
|
| 1244 |
+
current_state.documentation = llm_nemo.chat(doc_messages, temperature=0.4)
|
| 1245 |
+
|
| 1246 |
+
# Extract HTML from markdown for the iframe
|
| 1247 |
+
simulation_html = current_state.simulation_code
|
| 1248 |
+
if '```html' in simulation_html:
|
| 1249 |
+
# Extract content between ``````
|
| 1250 |
+
html_match = re.search(r'```html\n(.*?)\n```', simulation_html, re.DOTALL)
|
| 1251 |
+
if html_match:
|
| 1252 |
+
simulation_html = html_match.group(1)
|
| 1253 |
+
# Use update_preview to wrap the HTML
|
| 1254 |
+
simulation_preview = update_preview(simulation_html)
|
| 1255 |
+
|
| 1256 |
+
# Get workflow graph
|
| 1257 |
+
graph_png = None
|
| 1258 |
+
try:
|
| 1259 |
+
graph_bytes = save_graph_as_png()
|
| 1260 |
+
if graph_bytes:
|
| 1261 |
+
from PIL import Image
|
| 1262 |
+
import io
|
| 1263 |
+
graph_png = Image.open(io.BytesIO(graph_bytes))
|
| 1264 |
+
except Exception as e:
|
| 1265 |
+
print(f"Error processing graph image: {e}")
|
| 1266 |
+
graph_png = None
|
| 1267 |
+
|
| 1268 |
+
return (
|
| 1269 |
+
current_state,
|
| 1270 |
+
simulation_preview, # Clean HTML for iframe (now wrapped)
|
| 1271 |
+
current_state.simulation_code, # Original markdown for code editor
|
| 1272 |
+
current_state.documentation,
|
| 1273 |
+
graph_png
|
| 1274 |
+
)
|
| 1275 |
+
|
| 1276 |
+
def update_preview(code):
|
| 1277 |
+
"""Update the iframe preview using srcdoc for in-memory HTML preview."""
|
| 1278 |
+
if not code.strip():
|
| 1279 |
+
return "<div style='text-align:center; padding:50px;'>No code to preview</div>"
|
| 1280 |
+
# Escape quotes for srcdoc
|
| 1281 |
+
safe_code = code.replace('"', '"').replace("'", "'")
|
| 1282 |
+
return f'''<iframe style="width:100%;height:900px;border:1px solid #ccc;" srcdoc="{safe_code}"></iframe>'''
|
| 1283 |
+
|
| 1284 |
+
def update_simulation_from_code(current_state, code):
|
| 1285 |
+
"""Update simulation iframe from manually edited code"""
|
| 1286 |
+
current_state.simulation_code = code
|
| 1287 |
+
# Extract HTML from markdown if needed
|
| 1288 |
+
|
| 1289 |
+
simulation_html = code
|
| 1290 |
+
if '```html' in code:
|
| 1291 |
+
html_match = re.search(r'```html\n(.*?)\n```', code, re.DOTALL)
|
| 1292 |
+
if html_match:
|
| 1293 |
+
simulation_html = html_match.group(1)
|
| 1294 |
+
# Use update_preview to wrap the HTML
|
| 1295 |
+
return current_state, update_preview(simulation_html)
|
| 1296 |
+
|
| 1297 |
+
# Wire up events
|
| 1298 |
+
extract_btn.click(
|
| 1299 |
+
extract_content,
|
| 1300 |
+
inputs=[url_input, state],
|
| 1301 |
+
outputs=[state, content_display, summary_edit, agents_edit, behaviors_edit, hypothesis_edit, story_edit, environment_edit, interactions_edit, emojis_edit]
|
| 1302 |
+
)
|
| 1303 |
+
|
| 1304 |
+
generate_emojis_btn.click(
|
| 1305 |
+
generate_emojis_only,
|
| 1306 |
+
inputs=[state, agents_edit],
|
| 1307 |
+
outputs=[state, emojis_edit]
|
| 1308 |
+
)
|
| 1309 |
+
|
| 1310 |
+
confirm_btn.click(
|
| 1311 |
+
generate_final_simulation,
|
| 1312 |
+
inputs=[state, summary_edit, agents_edit, behaviors_edit, hypothesis_edit, story_edit, environment_edit, interactions_edit, emojis_edit],
|
| 1313 |
+
outputs=[state, simulation_frame, simulation_code_editor, documentation_display, graph_display]
|
| 1314 |
+
)
|
| 1315 |
+
|
| 1316 |
+
update_simulation_btn.click(
|
| 1317 |
+
update_simulation_from_code,
|
| 1318 |
+
inputs=[state, simulation_code_editor],
|
| 1319 |
+
outputs=[state, simulation_frame]
|
| 1320 |
+
)
|
| 1321 |
+
|
| 1322 |
+
return demo
|
| 1323 |
+
|
| 1324 |
+
# Launch the application
|
| 1325 |
+
if __name__ == "__main__":
|
| 1326 |
+
demo = create_gradio_interface()
|
| 1327 |
+
demo.launch(share=False, debug=False)
|