from __future__ import annotations
import html
import json
import re
from dataclasses import asdict
from datetime import datetime, timezone
from pathlib import Path
from typing import Any
from adam.executor import ToolContext
from adam.monitoring import SystemMonitor
def _slug(value: str) -> str:
cleaned = re.sub(r"[^A-Za-z0-9._ -]+", "", value).strip(" .")
cleaned = re.sub(r"\s+", "_", cleaned)
return (cleaned or "adam_project")[:80]
def _project_folder(context: ToolContext, project_name: str) -> Path:
base = (context.root / "data" / "projects").resolve()
folder = (base / _slug(project_name)).resolve()
if base != folder and base not in folder.parents:
raise ValueError("Project folder resolved outside ADAM's data directory.")
folder.mkdir(parents=True, exist_ok=True)
return folder
def _write_json(path: Path, payload: dict[str, Any]) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
temporary = path.with_suffix(path.suffix + ".tmp")
temporary.write_text(json.dumps(payload, indent=2), encoding="utf-8")
temporary.replace(path)
def _simulate(
context: ToolContext,
updates: list[tuple[int, str]],
*,
multiplier: float = 1.0,
) -> None:
for percent, message in updates:
context.log(message)
context.progress(percent, message)
context.wait(multiplier)
def collect_dataset(
context: ToolContext,
subject: str,
image_count: int,
project_name: str,
) -> dict[str, Any]:
folder = _project_folder(context, project_name)
dataset = folder / "dataset"
dataset.mkdir(exist_ok=True)
_simulate(
context,
[
(8, f"Preparing collection query for {subject}"),
(28, "Checking collector configuration"),
(52, "Creating candidate image manifest"),
(76, "Recording source and license review fields"),
(100, "Dataset collection manifest is ready"),
],
)
manifest = {
"mode": "demo",
"notice": (
"No images were downloaded. Connect your Dataset Collector backend in "
"config/tools.json to perform real collection."
),
"subject": subject,
"requested_images": int(image_count),
"candidates": [],
"created_at": datetime.now(timezone.utc).isoformat(),
}
_write_json(dataset / "collection_manifest.json", manifest)
return {"output_folder": str(folder)}
def prepare_dataset(context: ToolContext, project_name: str) -> dict[str, Any]:
folder = _project_folder(context, project_name)
dataset = folder / "dataset"
dataset.mkdir(exist_ok=True)
_simulate(
context,
[
(12, "Validating dataset manifest"),
(34, "Checking file integrity and dimensions"),
(58, "Running duplicate analysis"),
(81, "Preparing normalized dataset layout"),
(100, "Dataset preparation report is ready"),
],
)
report = {
"mode": "demo",
"valid_images": 0,
"duplicates_removed": 0,
"rejected_images": 0,
"ready_for_captioning": False,
"notice": "Connect a real preparation backend to process collected files.",
}
_write_json(dataset / "preparation_report.json", report)
return {"output_folder": str(folder)}
def generate_captions(
context: ToolContext,
subject: str,
project_name: str,
) -> dict[str, Any]:
folder = _project_folder(context, project_name)
captions = folder / "captions"
captions.mkdir(exist_ok=True)
_simulate(
context,
[
(15, "Loading prepared dataset report"),
(39, "Preparing captioning policy"),
(67, "Creating editable caption template"),
(88, "Checking caption consistency"),
(100, "Caption review file is ready"),
],
)
(captions / "captions_demo.txt").write_text(
"# ADAM demo caption template\n"
f"# Subject: {subject}\n"
"# No image captions were generated because no real backend is connected.\n",
encoding="utf-8",
)
return {"output_folder": str(folder)}
def train_lora(
context: ToolContext,
subject: str,
project_name: str,
epochs: int,
) -> dict[str, Any]:
folder = _project_folder(context, project_name)
training = folder / "training"
training.mkdir(exist_ok=True)
updates = [(4, "Validating trainer configuration")]
for epoch in range(1, max(1, int(epochs)) + 1):
percent = 8 + int(epoch / max(1, int(epochs)) * 84)
updates.append((percent, f"Simulating epoch {epoch}/{epochs}"))
updates.extend(
[(96, "Writing transparent demo summary"), (100, "Training simulation complete")]
)
_simulate(context, updates, multiplier=0.65)
_write_json(
training / "training_summary.json",
{
"mode": "demo",
"subject": subject,
"epochs_requested": int(epochs),
"model_created": False,
"notice": (
"This was a workflow simulation. No GPU training ran and no model "
"weights were created."
),
},
)
return {"output_folder": str(folder)}
def generate_previews(
context: ToolContext,
subject: str,
project_name: str,
preview_count: int,
model_name: str = "",
checkpoint: str = "",
prompt: str = "",
seed: int = 0,
) -> dict[str, Any]:
folder = _project_folder(context, project_name)
previews = folder / "previews"
previews.mkdir(exist_ok=True)
count = max(1, min(int(preview_count), 100))
_simulate(
context,
[
(10, "Loading preview generator configuration"),
(30, f"Preparing {count} preview tasks"),
(62, "Rendering demo preview cards"),
(86, "Writing preview manifest"),
(100, "Preview outputs are ready"),
],
)
safe_subject = html.escape(subject)
for index in range(1, count + 1):
svg = f""""""
(previews / f"preview_{index:02d}.svg").write_text(svg, encoding="utf-8")
_write_json(
previews / "preview_manifest.json",
{
"mode": "demo",
"subject": subject,
"model_name": model_name,
"checkpoint": checkpoint,
"prompt": prompt,
"seed": int(seed),
"preview_count": count,
"notice": "These are branded placeholders, not model-generated images.",
},
)
return {"output_folder": str(folder)}
def notify_complete(context: ToolContext, project_name: str) -> dict[str, Any]:
folder = _project_folder(context, project_name)
_simulate(
context,
[
(30, "Collecting workflow results"),
(70, "Recording completion status"),
(100, "Workflow complete"),
],
multiplier=0.5,
)
_write_json(
folder / "completion.json",
{
"job_id": context.job_id,
"project_name": project_name,
"completed_at": datetime.now(timezone.utc).isoformat(),
},
)
return {"output_folder": str(folder)}
def inspect_system(context: ToolContext, project_name: str) -> dict[str, Any]:
del project_name
context.progress(20, "Reading system sensors")
monitor = SystemMonitor(context.root)
try:
snapshot = monitor.snapshot()
finally:
monitor.close()
context.log(
f"CPU {snapshot.cpu_percent:.0f}% · RAM {snapshot.memory_percent:.0f}% · "
f"GPU {snapshot.gpu_percent:.0f}% · {snapshot.gpu_name}"
)
context.progress(100, "System snapshot complete")
return {"system_snapshot": asdict(snapshot)}