Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 133 additions & 0 deletions gui-demo/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,12 @@ def discover_scenarios() -> list:
# Get deployment scripts
deployment_scripts = discover_deployment_scripts(item)

# Check for local scripts
local_dir = item / "deployment" / "local"
has_preprocess = (local_dir / "preprocess.sh").exists()
has_save_model = (local_dir / "save-model.sh").exists()
has_train = (local_dir / "train.sh").exists()

scenarios.append({
"name": item.name,
"path": str(item),
Expand All @@ -188,6 +194,11 @@ def discover_scenarios() -> list:
"common_vars": vars_info["common_vars"],
"scenario_vars": vars_info["scenario_vars"],
"deployment_scripts": deployment_scripts,
"local_scripts": {
"has_preprocess": has_preprocess,
"has_save_model": has_save_model,
"has_train": has_train,
},
})

return scenarios
Expand Down Expand Up @@ -477,6 +488,128 @@ def generate():
return Response(generate(), mimetype='text/event-stream')


@app.route("/api/run-local-script-stream", methods=["POST"])
def run_local_script_stream():
"""Run a local deployment script with streaming output."""
data = request.json
script_name = data.get("script")

scenario = state["current_scenario"]
if not scenario:
return jsonify({"success": False, "error": "No scenario selected"})

script_path = SCENARIOS_DIR / scenario / "deployment" / "local" / script_name

if not script_path.exists():
return jsonify({"success": False, "error": f"Script not found: {script_path}"})

script_key = script_name.replace(".sh", "").replace("-", "_")
state["script_status"][script_key] = "running"

def generate():
cwd = script_path.parent
cmd = f"bash {script_path}"
process = run_command(cmd, cwd=str(cwd), stream=True)
state["running_process"] = process

try:
for line in iter(process.stdout.readline, ''):
if line:
yield f"data: {json.dumps({'line': line})}\n\n"

process.wait()
if process.returncode == 0:
state["script_status"][script_key] = "completed"
yield f"data: {json.dumps({'status': 'completed'})}\n\n"
else:
state["script_status"][script_key] = "failed"
yield f"data: {json.dumps({'status': 'failed'})}\n\n"
finally:
state["running_process"] = None

return Response(generate(), mimetype='text/event-stream')


@app.route("/api/pull-containers-stream", methods=["POST"])
def pull_containers_stream():
"""Run both pull-containers.sh scripts (repo-level and scenario-level) with streaming output."""
scenario = state["current_scenario"]
if not scenario:
return jsonify({"success": False, "error": "No scenario selected"})

repo_pull_script = Path(REPO_ROOT) / "ci" / "pull-containers.sh"
scenario_pull_script = SCENARIOS_DIR / scenario / "ci" / "pull-containers.sh"

if not repo_pull_script.exists():
return jsonify({"success": False, "error": f"Repo pull-containers.sh not found: {repo_pull_script}"})

if not scenario_pull_script.exists():
return jsonify({"success": False, "error": f"Scenario pull-containers.sh not found: {scenario_pull_script}"})

state["script_status"]["pull_containers"] = "running"

def generate():
# First run repo-level pull-containers.sh
yield f"data: {json.dumps({'line': '=== Pulling common containers ===\n'})}\n\n"
cwd = repo_pull_script.parent
cmd = f"bash {repo_pull_script}"
process = run_command(cmd, cwd=str(cwd), stream=True)
state["running_process"] = process

try:
for line in iter(process.stdout.readline, ''):
if line:
yield f"data: {json.dumps({'line': line})}\n\n"

process.wait()
if process.returncode != 0:
state["script_status"]["pull_containers"] = "failed"
yield f"data: {json.dumps({'status': 'failed', 'line': 'Failed to pull common containers\n'})}\n\n"
return
finally:
state["running_process"] = None

# Then run scenario-specific pull-containers.sh
yield f"data: {json.dumps({'line': '\n=== Pulling scenario-specific containers ===\n'})}\n\n"
cwd = scenario_pull_script.parent
cmd = f"bash {scenario_pull_script}"
process = run_command(cmd, cwd=str(cwd), stream=True)
state["running_process"] = process

try:
for line in iter(process.stdout.readline, ''):
if line:
yield f"data: {json.dumps({'line': line})}\n\n"

process.wait()
if process.returncode == 0:
state["script_status"]["pull_containers"] = "completed"
yield f"data: {json.dumps({'status': 'completed'})}\n\n"
else:
state["script_status"]["pull_containers"] = "failed"
yield f"data: {json.dumps({'status': 'failed'})}\n\n"
finally:
state["running_process"] = None

return Response(generate(), mimetype='text/event-stream')


@app.route("/api/check-local-script", methods=["POST"])
def check_local_script():
"""Check if a local script exists."""
data = request.json
script_name = data.get("script")

scenario = state["current_scenario"]
if not scenario:
return jsonify({"success": False, "error": "No scenario selected"})

script_path = SCENARIOS_DIR / scenario / "deployment" / "local" / script_name
exists = script_path.exists()

return jsonify({"success": True, "exists": exists})


@app.route("/api/deploy", methods=["POST"])
def deploy():
"""Run the deploy.sh script with contract number and pipeline config."""
Expand Down
40 changes: 39 additions & 1 deletion gui-demo/static/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* A warm, amber-tinted dark theme with soft gradients
*/

:root {
:root {
/* Warm Dark Palette */
--bg-base: #0d0d0f;
--bg-card: #141416;
Expand Down Expand Up @@ -484,6 +484,44 @@ body {
50% { transform: scale(1.1); }
}

/* Pre-deployment Steps */
.pre-deployment-buttons {
display: flex;
flex-direction: column;
gap: var(--sp-3);
}

.pre-deployment-buttons .btn {
display: flex;
align-items: center;
gap: var(--sp-2);
padding: var(--sp-3) var(--sp-4);
justify-content: flex-start;
text-align: left;
}

.pre-deployment-buttons .btn svg {
width: 18px;
height: 18px;
flex-shrink: 0;
}

.pre-deployment-buttons .btn-hint {
font-size: 0.75rem;
color: var(--text-muted);
font-weight: normal;
margin-left: auto;
font-style: italic;
}

.pre-deployment-buttons .btn-primary .btn-hint {
color: rgba(13, 13, 15, 0.7);
}

.pre-deployment-buttons .btn.hidden {
display: none;
}

.step-content {
flex: 1;
min-width: 0;
Expand Down
119 changes: 119 additions & 0 deletions gui-demo/static/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ class DepaTrainingApp {
});
document.getElementById('btnRefreshScenarios').addEventListener('click', () => this.refreshScenarios());

// Pre-deployment steps
document.getElementById('btnPullContainers').addEventListener('click', () => this.pullContainers());
document.getElementById('btnPreprocess').addEventListener('click', () => this.runPreprocess());
document.getElementById('btnSaveModel').addEventListener('click', () => this.runSaveModel());
document.getElementById('btnTestTraining').addEventListener('click', () => this.runTestTraining());

// Deploy
document.getElementById('btnDeploy').addEventListener('click', () => this.deploy());
document.getElementById('btnDownload').addEventListener('click', () => this.downloadModel());
Expand Down Expand Up @@ -153,6 +159,7 @@ class DepaTrainingApp {
this.renderScenarioInfo(data.scenario);
this.renderPipeline(data.scenario);
this.renderScenarioSettings(data.scenario_vars);
this.updatePreDeploymentButtons(data.scenario);
this.toast(`Selected: ${this.formatName(scenarioName)}`, 'success');
}
} catch (e) {
Expand Down Expand Up @@ -290,6 +297,118 @@ class DepaTrainingApp {
setTimeout(poll, 5000);
}

// ============= Pre-deployment Steps =============

updatePreDeploymentButtons(scenario) {
const localScripts = scenario.local_scripts || {};
const saveModelBtn = document.getElementById('btnSaveModel');

if (localScripts.has_save_model) {
saveModelBtn.classList.remove('hidden');
} else {
saveModelBtn.classList.add('hidden');
}
}

async pullContainers() {
if (!this.state.currentScenario) {
return this.toast('Select a scenario first', 'error');
}

this.openModal('pull-containers.sh');

const terminal = document.getElementById('terminalOutput');
terminal.textContent = `$ ./ci/pull-containers.sh\n$ ./scenarios/${this.state.currentScenario}/ci/pull-containers.sh\n\n`;

try {
const res = await fetch('/api/pull-containers-stream', {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
});

const reader = res.body.getReader();
const decoder = new TextDecoder();

while (true) {
const { done, value } = await reader.read();
if (done) break;

for (const line of decoder.decode(value).split('\n')) {
if (line.startsWith('data: ')) {
try {
const data = JSON.parse(line.slice(6));
if (data.line) {
terminal.textContent += data.line;
terminal.scrollTop = terminal.scrollHeight;
}
if (data.status) {
this.updateModalStatus(data.status);
}
} catch {}
}
}
}
} catch (e) {
this.updateModalStatus('failed');
}
}

async runLocalScript(scriptName, displayName) {
if (!this.state.currentScenario) {
return this.toast('Select a scenario first', 'error');
}

this.openModal(displayName || scriptName);

const terminal = document.getElementById('terminalOutput');
terminal.textContent = `$ ./${scriptName}\n\n`;

try {
const res = await fetch('/api/run-local-script-stream', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ script: scriptName })
});

const reader = res.body.getReader();
const decoder = new TextDecoder();

while (true) {
const { done, value } = await reader.read();
if (done) break;

for (const line of decoder.decode(value).split('\n')) {
if (line.startsWith('data: ')) {
try {
const data = JSON.parse(line.slice(6));
if (data.line) {
terminal.textContent += data.line;
terminal.scrollTop = terminal.scrollHeight;
}
if (data.status) {
this.updateModalStatus(data.status);
}
} catch {}
}
}
}
} catch (e) {
this.updateModalStatus('failed');
}
}

async runPreprocess() {
await this.runLocalScript('preprocess.sh', 'preprocess.sh');
}

async runSaveModel() {
await this.runLocalScript('save-model.sh', 'save-model.sh');
}

async runTestTraining() {
await this.runLocalScript('train.sh', 'train.sh');
}

// ============= Scripts =============

async runScript(scriptName, scriptKey) {
Expand Down
Loading
Loading