Spaces:
Paused
Paused
Upload folder using huggingface_hub
Browse files- app.py +24 -24
- frontend/index.html +84 -19
app.py
CHANGED
|
@@ -738,30 +738,30 @@ async def api_help():
|
|
| 738 |
"description": "Live multimodal code compiler: camera + audio + speech → observer LLM → builder LLM → code patches. Supports long-running terminal sessions for backtesting.",
|
| 739 |
"base_url": f"https://josephrw-csc-engine.hf.space",
|
| 740 |
"endpoints": [
|
| 741 |
-
{"method": "GET", "path": "/health", "description": "Health check — returns provider, version, active sessions"},
|
| 742 |
-
{"method": "POST", "path": "/session/start", "description": "Start a CSC Engine session"},
|
| 743 |
-
{"method": "POST", "path": "/session/stop", "description": "Stop a session by session_id"},
|
| 744 |
-
{"method": "GET", "path": "/state/current", "description": "Get current session state (requires session_id)"},
|
| 745 |
-
{"method": "POST", "path": "/generate/patch", "description": "Force a code generation cycle (requires session_id)"},
|
| 746 |
-
{"method": "POST", "path": "/run", "description": "Execute Python code — pass code or patch_hash. Returns stdout/stderr/exit_code"},
|
| 747 |
-
{"method": "POST", "path": "/debug", "description": "Send failed code + stderr to LLM for auto-fix. Pass patch_hash or code+stderr"},
|
| 748 |
-
{"method": "POST", "path": "/store-artifact", "description": "Store generated code + metadata for diary
|
| 749 |
-
{"method": "GET", "path": "/artifact/{patch_hash}", "description": "View artifact as HTML page with Run and Debug buttons"},
|
| 750 |
-
{"method": "GET", "path": "/api/artifacts", "description": "List all artifacts as JSON (limit param, default 50)"},
|
| 751 |
-
{"method": "GET", "path": "/api/artifacts/{patch_hash}", "description": "Get single artifact as JSON"},
|
| 752 |
-
{"method": "GET", "path": "/receipts", "description": "List all patch receipts (limit param)"},
|
| 753 |
-
{"method": "GET", "path": "/receipts/{receipt_id}", "description": "Get a specific receipt"},
|
| 754 |
-
{"method": "POST", "path": "/audio/store", "description": "Store base64-encoded audio recording alongside an artifact"},
|
| 755 |
-
{"method": "GET", "path": "/audio/{filename}", "description": "Serve an audio file"},
|
| 756 |
-
{"method": "GET", "path": "/diary", "description": "Lab diary page — notebook-style view of all artifacts"},
|
| 757 |
-
{"method": "POST", "path": "/terminal/create", "description": "Create a long-running terminal session for backtesting
|
| 758 |
-
{"method": "POST", "path": "/terminal/{session_id}/exec", "description": "Execute a command in a terminal session.
|
| 759 |
-
{"method": "GET", "path": "/terminal/{session_id}/output", "description": "Get accumulated output from a terminal session
|
| 760 |
-
{"method": "POST", "path": "/terminal/{session_id}/kill", "description": "Kill a terminal session"},
|
| 761 |
-
{"method": "GET", "path": "/terminal/{session_id}/status", "description": "Get status of a terminal session"},
|
| 762 |
-
{"method": "GET", "path": "/terminal/sessions", "description": "List all active terminal sessions"},
|
| 763 |
-
{"method": "GET", "path": "/api/help", "description": "This endpoint — lists all available endpoints"},
|
| 764 |
-
{"method": "GET", "path": "/
|
| 765 |
],
|
| 766 |
"chatgpt_integration": {
|
| 767 |
"schema_url": "/openapi.json",
|
|
|
|
| 738 |
"description": "Live multimodal code compiler: camera + audio + speech → observer LLM → builder LLM → code patches. Supports long-running terminal sessions for backtesting.",
|
| 739 |
"base_url": f"https://josephrw-csc-engine.hf.space",
|
| 740 |
"endpoints": [
|
| 741 |
+
{"method": "GET", "path": "/health", "description": "Health check — returns provider, version, active sessions", "params": []},
|
| 742 |
+
{"method": "POST", "path": "/session/start", "description": "Start a CSC Engine session", "params": []},
|
| 743 |
+
{"method": "POST", "path": "/session/stop", "description": "Stop a session by session_id", "params": [{"name": "session_id", "type": "text", "required": true, "placeholder": "session UUID"}]},
|
| 744 |
+
{"method": "GET", "path": "/state/current", "description": "Get current session state (requires session_id)", "params": [{"name": "session_id", "type": "text", "required": true, "placeholder": "session UUID"}]},
|
| 745 |
+
{"method": "POST", "path": "/generate/patch", "description": "Force a code generation cycle (requires session_id)", "params": [{"name": "session_id", "type": "text", "required": true, "placeholder": "session UUID"}]},
|
| 746 |
+
{"method": "POST", "path": "/run", "description": "Execute Python code — pass code or patch_hash. Returns stdout/stderr/exit_code", "params": [{"name": "code", "type": "textarea", "required": false, "placeholder": "print('hello')"}, {"name": "patch_hash", "type": "text", "required": false, "placeholder": "artifact hash"}]},
|
| 747 |
+
{"method": "POST", "path": "/debug", "description": "Send failed code + stderr to LLM for auto-fix. Pass patch_hash or code+stderr", "params": [{"name": "patch_hash", "type": "text", "required": false, "placeholder": "artifact hash"}, {"name": "code", "type": "textarea", "required": false, "placeholder": "failed code"}, {"name": "stderr", "type": "textarea", "required": false, "placeholder": "error output"}]},
|
| 748 |
+
{"method": "POST", "path": "/store-artifact", "description": "Store generated code + metadata for diary", "params": [{"name": "patch_hash", "type": "text", "required": true, "placeholder": "hash"}, {"name": "code", "type": "textarea", "required": false, "placeholder": "code"}, {"name": "reasoning", "type": "text", "required": false, "placeholder": "reasoning"}, {"name": "observer_output", "type": "textarea", "required": false, "placeholder": "observer output"}]},
|
| 749 |
+
{"method": "GET", "path": "/artifact/{patch_hash}", "description": "View artifact as HTML page with Run and Debug buttons", "params": [{"name": "patch_hash", "type": "text", "required": true, "placeholder": "artifact hash (in path)"}]},
|
| 750 |
+
{"method": "GET", "path": "/api/artifacts", "description": "List all artifacts as JSON (limit param, default 50)", "params": [{"name": "limit", "type": "text", "required": false, "placeholder": "50"}]},
|
| 751 |
+
{"method": "GET", "path": "/api/artifacts/{patch_hash}", "description": "Get single artifact as JSON", "params": [{"name": "patch_hash", "type": "text", "required": true, "placeholder": "artifact hash (in path)"}]},
|
| 752 |
+
{"method": "GET", "path": "/receipts", "description": "List all patch receipts (limit param)", "params": [{"name": "limit", "type": "text", "required": false, "placeholder": "50"}]},
|
| 753 |
+
{"method": "GET", "path": "/receipts/{receipt_id}", "description": "Get a specific receipt", "params": [{"name": "receipt_id", "type": "text", "required": true, "placeholder": "receipt ID (in path)"}]},
|
| 754 |
+
{"method": "POST", "path": "/audio/store", "description": "Store base64-encoded audio recording alongside an artifact", "params": [{"name": "patch_hash", "type": "text", "required": true, "placeholder": "hash"}, {"name": "audio_b64", "type": "textarea", "required": true, "placeholder": "base64 audio"}]},
|
| 755 |
+
{"method": "GET", "path": "/audio/{filename}", "description": "Serve an audio file", "params": [{"name": "filename", "type": "text", "required": true, "placeholder": "filename.webm (in path)"}]},
|
| 756 |
+
{"method": "GET", "path": "/diary", "description": "Lab diary page — notebook-style view of all artifacts", "params": []},
|
| 757 |
+
{"method": "POST", "path": "/terminal/create", "description": "Create a long-running terminal session for backtesting", "params": [{"name": "cwd", "type": "text", "required": false, "placeholder": "/tmp"}]},
|
| 758 |
+
{"method": "POST", "path": "/terminal/{session_id}/exec", "description": "Execute a command in a terminal session. Supports 300s timeout", "params": [{"name": "session_id", "type": "text", "required": true, "placeholder": "term_xxxx (in path)"}, {"name": "command", "type": "text", "required": true, "placeholder": "ls -la"}, {"name": "timeout", "type": "text", "required": false, "placeholder": "300"}]},
|
| 759 |
+
{"method": "GET", "path": "/terminal/{session_id}/output", "description": "Get accumulated output from a terminal session", "params": [{"name": "session_id", "type": "text", "required": true, "placeholder": "term_xxxx (in path)"}, {"name": "since", "type": "text", "required": false, "placeholder": "0"}]},
|
| 760 |
+
{"method": "POST", "path": "/terminal/{session_id}/kill", "description": "Kill a terminal session", "params": [{"name": "session_id", "type": "text", "required": true, "placeholder": "term_xxxx (in path)"}]},
|
| 761 |
+
{"method": "GET", "path": "/terminal/{session_id}/status", "description": "Get status of a terminal session", "params": [{"name": "session_id", "type": "text", "required": true, "placeholder": "term_xxxx (in path)"}]},
|
| 762 |
+
{"method": "GET", "path": "/terminal/sessions", "description": "List all active terminal sessions", "params": []},
|
| 763 |
+
{"method": "GET", "path": "/api/help", "description": "This endpoint — lists all available endpoints", "params": []},
|
| 764 |
+
{"method": "GET", "path": "/gpt-actions.json", "description": "ChatGPT Custom GPT Actions schema (OpenAPI 3.1)", "params": []},
|
| 765 |
],
|
| 766 |
"chatgpt_integration": {
|
| 767 |
"schema_url": "/openapi.json",
|
frontend/index.html
CHANGED
|
@@ -2043,39 +2043,104 @@
|
|
| 2043 |
try {
|
| 2044 |
const resp = await fetch("/api/help");
|
| 2045 |
const data = await resp.json();
|
| 2046 |
-
const el = document.getElementById("apiEndpoints");
|
| 2047 |
-
el.innerHTML = data.endpoints.map((e, i) =>
|
| 2048 |
-
'<div class="api-endpoint" onclick="toggleApiResponse(' + i + ')">' +
|
| 2049 |
-
'<span class="api-method ' + e.method + '">' + e.method + '</span>' +
|
| 2050 |
-
'<span class="api-path">' + e.path + '</span>' +
|
| 2051 |
-
'<div class="api-desc">' + e.description + '</div>' +
|
| 2052 |
-
'<button class="api-try-btn" onclick="event.stopPropagation();tryEndpoint(' + i + ')">Try it</button>' +
|
| 2053 |
-
'<div class="api-response" id="apiResp' + i + '"></div>' +
|
| 2054 |
-
'</div>'
|
| 2055 |
-
).join("");
|
| 2056 |
window._apiEndpoints = data.endpoints;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2057 |
} catch (e) {
|
| 2058 |
document.getElementById("apiEndpoints").innerHTML = '<div style="color:var(--red)">Error loading: ' + escapeHtml(e.message) + '</div>';
|
| 2059 |
}
|
| 2060 |
}
|
| 2061 |
|
| 2062 |
-
function
|
| 2063 |
-
const
|
| 2064 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2065 |
}
|
| 2066 |
|
| 2067 |
async function tryEndpoint(i) {
|
| 2068 |
const ep = window._apiEndpoints[i];
|
| 2069 |
-
const
|
| 2070 |
-
|
| 2071 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2072 |
try {
|
| 2073 |
const opts = { method: ep.method };
|
| 2074 |
-
const resp = await fetch(
|
| 2075 |
const text = await resp.text();
|
| 2076 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2077 |
} catch (e) {
|
| 2078 |
-
|
|
|
|
| 2079 |
}
|
| 2080 |
}
|
| 2081 |
|
|
|
|
| 2043 |
try {
|
| 2044 |
const resp = await fetch("/api/help");
|
| 2045 |
const data = await resp.json();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2046 |
window._apiEndpoints = data.endpoints;
|
| 2047 |
+
const el = document.getElementById("apiEndpoints");
|
| 2048 |
+
el.innerHTML = data.endpoints.map((e, i) => {
|
| 2049 |
+
let paramsHtml = '';
|
| 2050 |
+
if (e.params && e.params.length > 0) {
|
| 2051 |
+
paramsHtml = '<div class="api-params" style="margin-top:8px;display:flex;flex-direction:column;gap:6px">';
|
| 2052 |
+
for (const p of e.params) {
|
| 2053 |
+
const req = p.required ? ' <span style="color:var(--red)">*</span>' : '';
|
| 2054 |
+
if (p.type === 'textarea') {
|
| 2055 |
+
paramsHtml += '<label style="font-size:10px;color:var(--text2)">' + p.name + req + '</label>' +
|
| 2056 |
+
'<textarea id="apiparam_' + i + '_' + p.name + '" placeholder="' + (p.placeholder || '') + '" style="background:var(--bg);border:1px solid var(--border);border-radius:4px;padding:6px 8px;color:var(--text);font-family:var(--mono);font-size:11px;min-height:60px;resize:vertical;outline:none" onfocus="this.style.borderColor=\'var(--accent)\'" onblur="this.style.borderColor=\'var(--border)\'"></textarea>';
|
| 2057 |
+
} else {
|
| 2058 |
+
paramsHtml += '<div style="display:flex;align-items:center;gap:6px">' +
|
| 2059 |
+
'<label style="font-size:10px;color:var(--text2);min-width:80px">' + p.name + req + '</label>' +
|
| 2060 |
+
'<input id="apiparam_' + i + '_' + p.name + '" type="text" placeholder="' + (p.placeholder || '') + '" style="flex:1;background:var(--bg);border:1px solid var(--border);border-radius:4px;padding:4px 8px;color:var(--text);font-family:var(--mono);font-size:11px;outline:none" onfocus="this.style.borderColor=\'var(--accent)\'" onblur="this.style.borderColor=\'var(--border)\'" />' +
|
| 2061 |
+
'</div>';
|
| 2062 |
+
}
|
| 2063 |
+
}
|
| 2064 |
+
paramsHtml += '</div>';
|
| 2065 |
+
}
|
| 2066 |
+
return '<div class="api-endpoint" id="apiep_' + i + '">' +
|
| 2067 |
+
'<div style="display:flex;align-items:center;cursor:pointer" onclick="toggleEndpointExpand(' + i + ')">' +
|
| 2068 |
+
'<span class="api-method ' + e.method + '">' + e.method + '</span>' +
|
| 2069 |
+
'<span class="api-path">' + e.path + '</span>' +
|
| 2070 |
+
'<span style="margin-left:auto;font-size:10px;color:var(--text2)" id="apitoggle_' + i + '">▸</span>' +
|
| 2071 |
+
'</div>' +
|
| 2072 |
+
'<div class="api-desc">' + e.description + '</div>' +
|
| 2073 |
+
'<div class="api-expand" id="apiexpand_' + i + '" style="display:none">' +
|
| 2074 |
+
paramsHtml +
|
| 2075 |
+
'<button class="api-try-btn" onclick="event.stopPropagation();tryEndpoint(' + i + ')" style="margin-top:8px">▶ Send Request</button>' +
|
| 2076 |
+
'<div class="api-response" id="apiResp' + i + '"></div>' +
|
| 2077 |
+
'</div>' +
|
| 2078 |
+
'</div>';
|
| 2079 |
+
}).join("");
|
| 2080 |
} catch (e) {
|
| 2081 |
document.getElementById("apiEndpoints").innerHTML = '<div style="color:var(--red)">Error loading: ' + escapeHtml(e.message) + '</div>';
|
| 2082 |
}
|
| 2083 |
}
|
| 2084 |
|
| 2085 |
+
function toggleEndpointExpand(i) {
|
| 2086 |
+
const expand = document.getElementById("apiexpand_" + i);
|
| 2087 |
+
const toggle = document.getElementById("apitoggle_" + i);
|
| 2088 |
+
if (expand.style.display === "none") {
|
| 2089 |
+
expand.style.display = "block";
|
| 2090 |
+
toggle.textContent = "▾";
|
| 2091 |
+
} else {
|
| 2092 |
+
expand.style.display = "none";
|
| 2093 |
+
toggle.textContent = "▸";
|
| 2094 |
+
}
|
| 2095 |
}
|
| 2096 |
|
| 2097 |
async function tryEndpoint(i) {
|
| 2098 |
const ep = window._apiEndpoints[i];
|
| 2099 |
+
const respEl = document.getElementById("apiResp" + i);
|
| 2100 |
+
respEl.classList.add("open");
|
| 2101 |
+
respEl.textContent = "Sending...";
|
| 2102 |
+
respEl.style.color = "var(--text2)";
|
| 2103 |
+
|
| 2104 |
+
// Build URL — replace path params and add query params
|
| 2105 |
+
let url = ep.path;
|
| 2106 |
+
const queryParams = [];
|
| 2107 |
+
|
| 2108 |
+
for (const p of (ep.params || [])) {
|
| 2109 |
+
const input = document.getElementById("apiparam_" + i + "_" + p.name);
|
| 2110 |
+
if (!input) continue;
|
| 2111 |
+
const val = input.value.trim();
|
| 2112 |
+
if (!val) continue;
|
| 2113 |
+
|
| 2114 |
+
// Check if it's a path param ({name} in path)
|
| 2115 |
+
const pathPlaceholder = "{" + p.name + "}";
|
| 2116 |
+
if (url.includes(pathPlaceholder)) {
|
| 2117 |
+
url = url.replace(pathPlaceholder, encodeURIComponent(val));
|
| 2118 |
+
} else {
|
| 2119 |
+
// Query param
|
| 2120 |
+
queryParams.push(encodeURIComponent(p.name) + "=" + encodeURIComponent(val));
|
| 2121 |
+
}
|
| 2122 |
+
}
|
| 2123 |
+
|
| 2124 |
+
if (queryParams.length > 0) {
|
| 2125 |
+
url += (url.includes("?") ? "&" : "?") + queryParams.join("&");
|
| 2126 |
+
}
|
| 2127 |
+
|
| 2128 |
try {
|
| 2129 |
const opts = { method: ep.method };
|
| 2130 |
+
const resp = await fetch(url, opts);
|
| 2131 |
const text = await resp.text();
|
| 2132 |
+
// Try to pretty-print JSON
|
| 2133 |
+
try {
|
| 2134 |
+
const json = JSON.parse(text);
|
| 2135 |
+
respEl.textContent = JSON.stringify(json, null, 2).slice(0, 3000);
|
| 2136 |
+
respEl.style.color = resp.ok ? "var(--green)" : "var(--amber)";
|
| 2137 |
+
} catch {
|
| 2138 |
+
respEl.textContent = text.slice(0, 3000);
|
| 2139 |
+
respEl.style.color = resp.ok ? "var(--green)" : "var(--amber)";
|
| 2140 |
+
}
|
| 2141 |
} catch (e) {
|
| 2142 |
+
respEl.textContent = "Error: " + e.message;
|
| 2143 |
+
respEl.style.color = "var(--red)";
|
| 2144 |
}
|
| 2145 |
}
|
| 2146 |
|