Dmitry Beresnev commited on
Commit Β·
bbb7286
1
Parent(s): 15c8440
add nemoclaw from nvidia
Browse files
app.py
CHANGED
|
@@ -30,6 +30,7 @@ NANOBOT_CONFIG_PATH = Path(os.getenv("NANOBOT_CONFIG_PATH", "nanobot.json"))
|
|
| 30 |
PICOCLAW_CONFIG_PATH = Path(os.getenv("PICOCLAW_CONFIG_PATH", "picoclaw.json"))
|
| 31 |
ZEROCLAW_CONFIG_PATH = Path(os.getenv("ZEROCLAW_CONFIG_PATH", "zeroclaw.json"))
|
| 32 |
NULLCLAW_CONFIG_PATH = Path(os.getenv("NULLCLAW_CONFIG_PATH", "nullclaw.json"))
|
|
|
|
| 33 |
ENV_EXAMPLE_PATH = Path("config/openclaw.env.example")
|
| 34 |
LOG_MAX_LINES = 300
|
| 35 |
OPENCLAW_STANDARD_UI_URL = os.getenv(
|
|
@@ -92,6 +93,14 @@ NULLCLAW_ENABLED = os.getenv("NULLCLAW_ENABLED", "1") == "1"
|
|
| 92 |
NULLCLAW_PROXY_LOCAL_URL = os.getenv(
|
| 93 |
"NULLCLAW_PROXY_LOCAL_URL", f"http://127.0.0.1:7860{NULLCLAW_BASE_PATH}/"
|
| 94 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
IRONCLAW_LOG_PATH = Path(os.getenv("IRONCLAW_LOG_PATH", "/tmp/ironclaw.log"))
|
| 96 |
IRONCLAW_ERR_LOG_PATH = Path(os.getenv("IRONCLAW_ERR_LOG_PATH", "/tmp/ironclaw.err.log"))
|
| 97 |
IRONCLAW_ENABLED = os.getenv("IRONCLAW_ENABLED", "1") == "1"
|
|
@@ -119,6 +128,7 @@ def init_state() -> None:
|
|
| 119 |
st.session_state.setdefault("picoclaw_config_text", load_picoclaw_config_text())
|
| 120 |
st.session_state.setdefault("zeroclaw_config_text", load_zeroclaw_config_text())
|
| 121 |
st.session_state.setdefault("nullclaw_config_text", load_nullclaw_config_text())
|
|
|
|
| 122 |
st.session_state.setdefault("auto_started", False)
|
| 123 |
st.session_state.setdefault("auto_start_attempted", False)
|
| 124 |
st.session_state.setdefault("backtest_result", None)
|
|
@@ -207,6 +217,19 @@ def load_nullclaw_config_json() -> dict:
|
|
| 207 |
return {}
|
| 208 |
|
| 209 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 210 |
def gateway_process() -> subprocess.Popen | None:
|
| 211 |
proc = st.session_state.get("gateway_process")
|
| 212 |
if proc is None:
|
|
@@ -444,6 +467,12 @@ def is_nullclaw_port_open(timeout: float = 0.5) -> bool:
|
|
| 444 |
return sock.connect_ex(("127.0.0.1", NULLCLAW_PORT)) == 0
|
| 445 |
|
| 446 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 447 |
def supervisorctl(action: str, program: str) -> tuple[bool, str]:
|
| 448 |
cmd = shutil.which("supervisorctl")
|
| 449 |
if not cmd:
|
|
@@ -810,12 +839,14 @@ with svc_col_1:
|
|
| 810 |
picoclaw_up = is_picoclaw_port_open() if PICOCLAW_ENABLED else False
|
| 811 |
zeroclaw_up = is_zeroclaw_port_open() if ZEROCLAW_ENABLED else False
|
| 812 |
nullclaw_up = is_nullclaw_port_open() if NULLCLAW_ENABLED else False
|
|
|
|
| 813 |
st.write(f"OpenClaw: {'π’ Running' if openclaw_up else 'π΄ Down'} (port {OPENCLAW_PORT})")
|
| 814 |
st.write(f"NanoClaw: {'π’ Running' if nanoclaw_up else 'π΄ Down'} (port {NANOCLAW_PORT})")
|
| 815 |
st.write(f"NanoBot: {'π’ Running' if nanobot_up else 'π΄ Down'} (port {NANOBOT_PORT})")
|
| 816 |
st.write(f"PicoClaw: {'π’ Running' if picoclaw_up else 'π΄ Down'} (port {PICOCLAW_PORT})")
|
| 817 |
st.write(f"ZeroClaw: {'π’ Running' if zeroclaw_up else 'π΄ Down'} (port {ZEROCLAW_PORT})")
|
| 818 |
st.write(f"NullClaw: {'π’ Running' if nullclaw_up else 'π΄ Down'} (port {NULLCLAW_PORT})")
|
|
|
|
| 819 |
st.write(f"IronClaw: {'π’ Enabled' if IRONCLAW_ENABLED else 'π΄ Disabled'} (no gateway port)")
|
| 820 |
|
| 821 |
with svc_col_2:
|
|
@@ -838,6 +869,9 @@ with svc_col_2:
|
|
| 838 |
if st.button("Restart NullClaw", use_container_width=True):
|
| 839 |
ok, msg = supervisorctl("restart", "nullclaw")
|
| 840 |
(st.success if ok else st.error)(msg)
|
|
|
|
|
|
|
|
|
|
| 841 |
if st.button("Restart IronClaw", use_container_width=True):
|
| 842 |
ok, msg = supervisorctl("restart", "ironclaw")
|
| 843 |
(st.success if ok else st.error)(msg)
|
|
@@ -851,6 +885,8 @@ with svc_col_2:
|
|
| 851 |
st.info("ZeroClaw is disabled. Set `ZEROCLAW_ENABLED=1` to start it.")
|
| 852 |
if not NULLCLAW_ENABLED:
|
| 853 |
st.info("NullClaw is disabled. Set `NULLCLAW_ENABLED=1` to start it.")
|
|
|
|
|
|
|
| 854 |
if not IRONCLAW_ENABLED:
|
| 855 |
st.info("IronClaw is disabled. Set `IRONCLAW_ENABLED=1` to start it.")
|
| 856 |
|
|
@@ -1014,6 +1050,23 @@ with claw_ui_col_2:
|
|
| 1014 |
else:
|
| 1015 |
st.caption("Disabled")
|
| 1016 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1017 |
st.markdown("**IronClaw**")
|
| 1018 |
st.caption("No gateway port detected. Run `ironclaw` CLI after onboarding.")
|
| 1019 |
|
|
@@ -1146,6 +1199,22 @@ with cfg_b:
|
|
| 1146 |
except json.JSONDecodeError as exc:
|
| 1147 |
st.error(f"Invalid JSON: {exc}")
|
| 1148 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1149 |
st.divider()
|
| 1150 |
|
| 1151 |
test_col, logs_col = st.columns([2, 3])
|
|
@@ -1188,6 +1257,10 @@ st.expander("Service Diagnostics (Supervisor/Caddy/Streamlit/OpenClaw)").code(
|
|
| 1188 |
tail_text_file(NULLCLAW_ERR_LOG_PATH),
|
| 1189 |
"=== nullclaw.log ===",
|
| 1190 |
tail_text_file(NULLCLAW_LOG_PATH),
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1191 |
"=== ironclaw.err.log ===",
|
| 1192 |
tail_text_file(IRONCLAW_ERR_LOG_PATH),
|
| 1193 |
"=== ironclaw.log ===",
|
|
|
|
| 30 |
PICOCLAW_CONFIG_PATH = Path(os.getenv("PICOCLAW_CONFIG_PATH", "picoclaw.json"))
|
| 31 |
ZEROCLAW_CONFIG_PATH = Path(os.getenv("ZEROCLAW_CONFIG_PATH", "zeroclaw.json"))
|
| 32 |
NULLCLAW_CONFIG_PATH = Path(os.getenv("NULLCLAW_CONFIG_PATH", "nullclaw.json"))
|
| 33 |
+
NEMOCLAW_CONFIG_PATH = Path(os.getenv("NEMOCLAW_CONFIG_PATH", "nemoclaw.json"))
|
| 34 |
ENV_EXAMPLE_PATH = Path("config/openclaw.env.example")
|
| 35 |
LOG_MAX_LINES = 300
|
| 36 |
OPENCLAW_STANDARD_UI_URL = os.getenv(
|
|
|
|
| 93 |
NULLCLAW_PROXY_LOCAL_URL = os.getenv(
|
| 94 |
"NULLCLAW_PROXY_LOCAL_URL", f"http://127.0.0.1:7860{NULLCLAW_BASE_PATH}/"
|
| 95 |
)
|
| 96 |
+
NEMOCLAW_LOG_PATH = Path(os.getenv("NEMOCLAW_LOG_PATH", "/tmp/nemoclaw.log"))
|
| 97 |
+
NEMOCLAW_ERR_LOG_PATH = Path(os.getenv("NEMOCLAW_ERR_LOG_PATH", "/tmp/nemoclaw.err.log"))
|
| 98 |
+
NEMOCLAW_PORT = int(os.getenv("NEMOCLAW_PORT", "18793"))
|
| 99 |
+
NEMOCLAW_BASE_PATH = os.getenv("NEMOCLAW_BASE_PATH", "/nemoclaw")
|
| 100 |
+
NEMOCLAW_ENABLED = os.getenv("NEMOCLAW_ENABLED", "1") == "1"
|
| 101 |
+
NEMOCLAW_PROXY_LOCAL_URL = os.getenv(
|
| 102 |
+
"NEMOCLAW_PROXY_LOCAL_URL", f"http://127.0.0.1:7860{NEMOCLAW_BASE_PATH}/"
|
| 103 |
+
)
|
| 104 |
IRONCLAW_LOG_PATH = Path(os.getenv("IRONCLAW_LOG_PATH", "/tmp/ironclaw.log"))
|
| 105 |
IRONCLAW_ERR_LOG_PATH = Path(os.getenv("IRONCLAW_ERR_LOG_PATH", "/tmp/ironclaw.err.log"))
|
| 106 |
IRONCLAW_ENABLED = os.getenv("IRONCLAW_ENABLED", "1") == "1"
|
|
|
|
| 128 |
st.session_state.setdefault("picoclaw_config_text", load_picoclaw_config_text())
|
| 129 |
st.session_state.setdefault("zeroclaw_config_text", load_zeroclaw_config_text())
|
| 130 |
st.session_state.setdefault("nullclaw_config_text", load_nullclaw_config_text())
|
| 131 |
+
st.session_state.setdefault("nemoclaw_config_text", load_nemoclaw_config_text())
|
| 132 |
st.session_state.setdefault("auto_started", False)
|
| 133 |
st.session_state.setdefault("auto_start_attempted", False)
|
| 134 |
st.session_state.setdefault("backtest_result", None)
|
|
|
|
| 217 |
return {}
|
| 218 |
|
| 219 |
|
| 220 |
+
def load_nemoclaw_config_text() -> str:
|
| 221 |
+
if NEMOCLAW_CONFIG_PATH.exists():
|
| 222 |
+
return NEMOCLAW_CONFIG_PATH.read_text(encoding="utf-8")
|
| 223 |
+
return "{}"
|
| 224 |
+
|
| 225 |
+
|
| 226 |
+
def load_nemoclaw_config_json() -> dict:
|
| 227 |
+
try:
|
| 228 |
+
return json.loads(load_nemoclaw_config_text())
|
| 229 |
+
except json.JSONDecodeError:
|
| 230 |
+
return {}
|
| 231 |
+
|
| 232 |
+
|
| 233 |
def gateway_process() -> subprocess.Popen | None:
|
| 234 |
proc = st.session_state.get("gateway_process")
|
| 235 |
if proc is None:
|
|
|
|
| 467 |
return sock.connect_ex(("127.0.0.1", NULLCLAW_PORT)) == 0
|
| 468 |
|
| 469 |
|
| 470 |
+
def is_nemoclaw_port_open(timeout: float = 0.5) -> bool:
|
| 471 |
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
| 472 |
+
sock.settimeout(timeout)
|
| 473 |
+
return sock.connect_ex(("127.0.0.1", NEMOCLAW_PORT)) == 0
|
| 474 |
+
|
| 475 |
+
|
| 476 |
def supervisorctl(action: str, program: str) -> tuple[bool, str]:
|
| 477 |
cmd = shutil.which("supervisorctl")
|
| 478 |
if not cmd:
|
|
|
|
| 839 |
picoclaw_up = is_picoclaw_port_open() if PICOCLAW_ENABLED else False
|
| 840 |
zeroclaw_up = is_zeroclaw_port_open() if ZEROCLAW_ENABLED else False
|
| 841 |
nullclaw_up = is_nullclaw_port_open() if NULLCLAW_ENABLED else False
|
| 842 |
+
nemoclaw_up = is_nemoclaw_port_open() if NEMOCLAW_ENABLED else False
|
| 843 |
st.write(f"OpenClaw: {'π’ Running' if openclaw_up else 'π΄ Down'} (port {OPENCLAW_PORT})")
|
| 844 |
st.write(f"NanoClaw: {'π’ Running' if nanoclaw_up else 'π΄ Down'} (port {NANOCLAW_PORT})")
|
| 845 |
st.write(f"NanoBot: {'π’ Running' if nanobot_up else 'π΄ Down'} (port {NANOBOT_PORT})")
|
| 846 |
st.write(f"PicoClaw: {'π’ Running' if picoclaw_up else 'π΄ Down'} (port {PICOCLAW_PORT})")
|
| 847 |
st.write(f"ZeroClaw: {'π’ Running' if zeroclaw_up else 'π΄ Down'} (port {ZEROCLAW_PORT})")
|
| 848 |
st.write(f"NullClaw: {'π’ Running' if nullclaw_up else 'π΄ Down'} (port {NULLCLAW_PORT})")
|
| 849 |
+
st.write(f"NemoClaw: {'π’ Running' if nemoclaw_up else 'π΄ Down'} (port {NEMOCLAW_PORT})")
|
| 850 |
st.write(f"IronClaw: {'π’ Enabled' if IRONCLAW_ENABLED else 'π΄ Disabled'} (no gateway port)")
|
| 851 |
|
| 852 |
with svc_col_2:
|
|
|
|
| 869 |
if st.button("Restart NullClaw", use_container_width=True):
|
| 870 |
ok, msg = supervisorctl("restart", "nullclaw")
|
| 871 |
(st.success if ok else st.error)(msg)
|
| 872 |
+
if st.button("Restart NemoClaw", use_container_width=True):
|
| 873 |
+
ok, msg = supervisorctl("restart", "nemoclaw")
|
| 874 |
+
(st.success if ok else st.error)(msg)
|
| 875 |
if st.button("Restart IronClaw", use_container_width=True):
|
| 876 |
ok, msg = supervisorctl("restart", "ironclaw")
|
| 877 |
(st.success if ok else st.error)(msg)
|
|
|
|
| 885 |
st.info("ZeroClaw is disabled. Set `ZEROCLAW_ENABLED=1` to start it.")
|
| 886 |
if not NULLCLAW_ENABLED:
|
| 887 |
st.info("NullClaw is disabled. Set `NULLCLAW_ENABLED=1` to start it.")
|
| 888 |
+
if not NEMOCLAW_ENABLED:
|
| 889 |
+
st.info("NemoClaw is disabled. Set `NEMOCLAW_ENABLED=1` to start it.")
|
| 890 |
if not IRONCLAW_ENABLED:
|
| 891 |
st.info("IronClaw is disabled. Set `IRONCLAW_ENABLED=1` to start it.")
|
| 892 |
|
|
|
|
| 1050 |
else:
|
| 1051 |
st.caption("Disabled")
|
| 1052 |
|
| 1053 |
+
st.markdown("**NemoClaw**")
|
| 1054 |
+
nemoclaw_ok = False
|
| 1055 |
+
if NEMOCLAW_ENABLED:
|
| 1056 |
+
try:
|
| 1057 |
+
nemoclaw_resp = requests.get(NEMOCLAW_PROXY_LOCAL_URL, timeout=2, allow_redirects=True)
|
| 1058 |
+
nemoclaw_ok = nemoclaw_resp.status_code < 500
|
| 1059 |
+
except Exception:
|
| 1060 |
+
nemoclaw_ok = False
|
| 1061 |
+
if NEMOCLAW_ENABLED:
|
| 1062 |
+
st.markdown(
|
| 1063 |
+
f'<a href="{NEMOCLAW_PROXY_LOCAL_URL}" target="_blank" rel="noopener noreferrer">Open NemoClaw</a>',
|
| 1064 |
+
unsafe_allow_html=True,
|
| 1065 |
+
)
|
| 1066 |
+
st.caption(f"Proxy: {NEMOCLAW_PROXY_LOCAL_URL} ({'ok' if nemoclaw_ok else 'down'})")
|
| 1067 |
+
else:
|
| 1068 |
+
st.caption("Disabled")
|
| 1069 |
+
|
| 1070 |
st.markdown("**IronClaw**")
|
| 1071 |
st.caption("No gateway port detected. Run `ironclaw` CLI after onboarding.")
|
| 1072 |
|
|
|
|
| 1199 |
except json.JSONDecodeError as exc:
|
| 1200 |
st.error(f"Invalid JSON: {exc}")
|
| 1201 |
|
| 1202 |
+
nemoclaw_text = st.text_area(
|
| 1203 |
+
"nemoclaw.json",
|
| 1204 |
+
value=st.session_state.get("nemoclaw_config_text", load_nemoclaw_config_text()),
|
| 1205 |
+
height=220,
|
| 1206 |
+
)
|
| 1207 |
+
st.session_state["nemoclaw_config_text"] = nemoclaw_text
|
| 1208 |
+
if st.button("Save NemoClaw Config", use_container_width=True):
|
| 1209 |
+
try:
|
| 1210 |
+
parsed = json.loads(nemoclaw_text)
|
| 1211 |
+
NEMOCLAW_CONFIG_PATH.write_text(
|
| 1212 |
+
json.dumps(parsed, indent=2) + "\n", encoding="utf-8"
|
| 1213 |
+
)
|
| 1214 |
+
st.success(f"Saved {NEMOCLAW_CONFIG_PATH}.")
|
| 1215 |
+
except json.JSONDecodeError as exc:
|
| 1216 |
+
st.error(f"Invalid JSON: {exc}")
|
| 1217 |
+
|
| 1218 |
st.divider()
|
| 1219 |
|
| 1220 |
test_col, logs_col = st.columns([2, 3])
|
|
|
|
| 1257 |
tail_text_file(NULLCLAW_ERR_LOG_PATH),
|
| 1258 |
"=== nullclaw.log ===",
|
| 1259 |
tail_text_file(NULLCLAW_LOG_PATH),
|
| 1260 |
+
"=== nemoclaw.err.log ===",
|
| 1261 |
+
tail_text_file(NEMOCLAW_ERR_LOG_PATH),
|
| 1262 |
+
"=== nemoclaw.log ===",
|
| 1263 |
+
tail_text_file(NEMOCLAW_LOG_PATH),
|
| 1264 |
"=== ironclaw.err.log ===",
|
| 1265 |
tail_text_file(IRONCLAW_ERR_LOG_PATH),
|
| 1266 |
"=== ironclaw.log ===",
|