AI_Development_Automation_Manager / adam /ui /model_intelligence.py
SyntheticMDProductions's picture
ADAM October 2026 source release: PixelRow, INRFlow, Wan Video, Oasis player and field guide
f8c73f9 verified
Raw History Blame Contribute Delete
14.3 kB
from __future__ import annotations
from pathlib import Path
from PySide6.QtCore import QSize, Qt, QTimer, QUrl, Signal
from PySide6.QtGui import QDesktopServices, QIcon, QImageReader, QPixmap
from PySide6.QtWidgets import (
QAbstractItemView, QFrame, QGridLayout, QHBoxLayout, QHeaderView, QLabel, QListWidget,
QListWidgetItem, QPlainTextEdit, QPushButton, QScrollArea, QTableWidget,
QTableWidgetItem, QVBoxLayout, QWidget,
)
from adam.experiment_tracker import ExperimentStore
from adam.generations import load_generation_history
from adam.intelligence import ModelIntelligence, build_model_intelligence, recommended_training_request
from adam.ui.theme import COLORS
from adam.ui.background import BackgroundRead
def _card() -> QFrame:
frame = QFrame()
frame.setProperty("card", True)
return frame
def _title(text: str) -> QLabel:
label = QLabel(text)
label.setObjectName("CardTitle")
return label
def _thumbnail(path: Path, width: int, height: int) -> QPixmap:
reader = QImageReader(str(path))
reader.setAutoTransform(True)
size = reader.size()
if size.isValid():
size.scale(QSize(width, height), Qt.KeepAspectRatio)
reader.setScaledSize(size)
image = reader.read()
return QPixmap.fromImage(image) if not image.isNull() else QPixmap()
class ModelIntelligencePage(QWidget):
"""A visual, evidence-first bridge between experiment history and generations."""
plan_requested = Signal(str)
open_experiments_requested = Signal()
def __init__(self, root: Path, store: ExperimentStore) -> None:
super().__init__()
self.root = root.resolve()
self.store = store
self.profiles: list[ModelIntelligence] = []
self.current_key = ""
root_layout = QVBoxLayout(self)
root_layout.setContentsMargins(24, 20, 24, 17)
root_layout.setSpacing(12)
header = QHBoxLayout()
head = QWidget()
head_layout = QVBoxLayout(head)
head_layout.setContentsMargins(0, 0, 0, 0)
page_title = QLabel("Model Intelligence")
page_title.setObjectName("PageTitle")
detail = QLabel("Turn your local training and generation history into evidence-led follow-up experiments.")
detail.setProperty("muted", True)
detail.setWordWrap(True)
head_layout.addWidget(page_title)
head_layout.addWidget(detail)
header.addWidget(head, 1)
self.refresh_button = QPushButton("Refresh intelligence")
self.refresh_button.clicked.connect(self.refresh)
header.addWidget(self.refresh_button, 0, Qt.AlignTop)
root_layout.addLayout(header)
summary = QHBoxLayout()
self.metric_models = self._metric(summary, "MODELS TRACKED")
self.metric_runs = self._metric(summary, "TRAINING RUNS")
self.metric_generations = self._metric(summary, "TEST GENERATIONS")
self.metric_reviews = self._metric(summary, "REVIEW SIGNAL")
root_layout.addLayout(summary)
split = QHBoxLayout()
model_card = _card()
model_card.setMinimumWidth(265)
model_layout = QVBoxLayout(model_card)
model_layout.addWidget(_title("MODEL LIBRARY"))
model_hint = QLabel("Choose a model to see its evolution, evidence, and next experiment.")
model_hint.setProperty("muted", True)
model_hint.setWordWrap(True)
model_layout.addWidget(model_hint)
self.models = QListWidget()
self.models.setSelectionMode(QAbstractItemView.SingleSelection)
self.models.itemSelectionChanged.connect(self._select_profile)
model_layout.addWidget(self.models, 1)
split.addWidget(model_card, 1)
detail_scroll = QScrollArea()
detail_scroll.setWidgetResizable(True)
detail_scroll.setFrameShape(QFrame.NoFrame)
detail_page = QWidget()
detail_layout = QVBoxLayout(detail_page)
detail_layout.setContentsMargins(0, 0, 0, 0)
detail_layout.setSpacing(12)
detail_scroll.setWidget(detail_page)
hero = _card()
hero_layout = QVBoxLayout(hero)
hero_layout.setContentsMargins(18, 16, 18, 16)
self.model_title = QLabel("Select a model")
self.model_title.setStyleSheet("font-size: 20px; font-weight: 650;")
self.state = QLabel("Waiting for local experiment records")
self.state.setProperty("muted", True)
self.diagnosis = QLabel("ADAM will show a diagnosis only when it has local evidence to support one.")
self.diagnosis.setWordWrap(True)
self.recommendation = QLabel("")
self.recommendation.setWordWrap(True)
self.recommendation.setStyleSheet(f"color: {COLORS['blue_2']};")
hero_layout.addWidget(self.model_title)
hero_layout.addWidget(self.state)
hero_layout.addWidget(self.diagnosis)
hero_layout.addWidget(self.recommendation)
actions = QHBoxLayout()
self.create_follow_up = QPushButton("Create recommended experiment")
self.create_follow_up.setProperty("primary", True)
self.create_follow_up.setEnabled(False)
self.create_follow_up.clicked.connect(self._create_follow_up)
self.open_experiments = QPushButton("Open Experiment Tracker")
self.open_experiments.clicked.connect(self.open_experiments_requested.emit)
actions.addWidget(self.create_follow_up)
actions.addWidget(self.open_experiments)
actions.addStretch(1)
hero_layout.addLayout(actions)
detail_layout.addWidget(hero)
evolution = _card()
evolution_layout = QVBoxLayout(evolution)
evolution_layout.setContentsMargins(18, 16, 18, 16)
evolution_layout.addWidget(_title("EVOLUTION TIMELINE"))
self.timeline = QTableWidget(0, 7)
self.timeline.setHorizontalHeaderLabels(["RUN", "STATUS", "EPOCHS", "LOSS", "TIME", "QUALITY", "DATASET"])
self.timeline.setEditTriggers(QAbstractItemView.NoEditTriggers)
self.timeline.setSelectionBehavior(QAbstractItemView.SelectRows)
self.timeline.verticalHeader().hide()
header_view = self.timeline.horizontalHeader()
header_view.setStretchLastSection(True)
for column in range(6):
header_view.setSectionResizeMode(column, QHeaderView.ResizeToContents)
self.timeline.setMinimumHeight(180)
evolution_layout.addWidget(self.timeline)
detail_layout.addWidget(evolution)
gallery_card = _card()
gallery_layout = QVBoxLayout(gallery_card)
gallery_layout.setContentsMargins(18, 16, 18, 16)
gallery_layout.addWidget(_title("VISUAL EVIDENCE"))
self.gallery_summary = QLabel("Training previews and generated images will appear here.")
self.gallery_summary.setProperty("muted", True)
self.gallery = QListWidget()
self.gallery.setViewMode(QListWidget.IconMode)
self.gallery.setIconSize(QSize(160, 116))
self.gallery.setGridSize(QSize(184, 158))
self.gallery.setResizeMode(QListWidget.Adjust)
self.gallery.setMaximumHeight(220)
self.gallery.itemDoubleClicked.connect(self._open_image)
gallery_layout.addWidget(self.gallery_summary)
gallery_layout.addWidget(self.gallery)
detail_layout.addWidget(gallery_card)
detail_layout.addStretch(1)
split.addWidget(detail_scroll, 3)
root_layout.addLayout(split, 1)
# Loading generation metadata can be substantial. MainWindow creates
# every workspace at startup, so defer that work until this tab opens.
self._show_profile(None)
self._reader = BackgroundRead(self)
self._reader.ready.connect(self._loaded)
self._reader.failed.connect(self._load_failed)
def _metric(self, layout: QHBoxLayout, label: str) -> QLabel:
card = _card()
card_layout = QVBoxLayout(card)
card_layout.setContentsMargins(14, 10, 14, 10)
heading = QLabel(label)
heading.setObjectName("CardTitle")
value = QLabel("0")
value.setStyleSheet("font-size: 22px; font-weight: 700;")
card_layout.addWidget(heading)
card_layout.addWidget(value)
layout.addWidget(card, 1)
return value
def refresh(self) -> None:
self._request_refresh(force=True)
def refresh_if_stale(self) -> None:
self._request_refresh(force=False)
def _request_refresh(self, *, force: bool) -> None:
root, store = self.root, self.store
def read():
runs = store.list_runs(limit=1000)
records = load_generation_history(root, limit=250)
return runs, build_model_intelligence(runs, records, root=root)
if not self.profiles:
self.state.setText("Loading local training and generation history…")
self._reader.request(read, force=force)
def _load_failed(self, error: str) -> None:
self.state.setText(f"Could not refresh intelligence: {error}")
def _loaded(self, result: object) -> None:
selected = self.current_key
runs, self.profiles = result
self.metric_models.setText(str(len(self.profiles)))
self.metric_runs.setText(str(len(runs)))
self.metric_generations.setText(str(sum(len(profile.generations) for profile in self.profiles)))
reviewed = sum(profile.rated_images for profile in self.profiles)
self.metric_reviews.setText(f"{reviewed} reviewed" if reviewed else "Needs ratings")
self.models.blockSignals(True)
self.models.clear()
for profile in self.profiles:
item = QListWidgetItem(f"{profile.model_name}\n{profile.architecture.upper()} · {len(profile.runs)} run(s) · {profile.state}")
item.setData(Qt.UserRole, profile.key)
self.models.addItem(item)
self.models.blockSignals(False)
if self.models.count():
index = next((index for index in range(self.models.count()) if self.models.item(index).data(Qt.UserRole) == selected), 0)
self.models.setCurrentRow(index)
else:
self._show_profile(None)
def _select_profile(self) -> None:
item = self.models.currentItem()
key = str(item.data(Qt.UserRole) or "") if item else ""
profile = next((entry for entry in self.profiles if entry.key == key), None)
self._show_profile(profile)
def _show_profile(self, profile: ModelIntelligence | None) -> None:
self.gallery.clear()
self.timeline.setRowCount(0)
self.current_key = profile.key if profile else ""
if profile is None:
self.model_title.setText("No training records yet")
self.state.setText("Train a model in ADAM and it will appear here automatically.")
self.diagnosis.setText("This page only uses local evidence ADAM has actually recorded.")
self.recommendation.setText("")
self.gallery_summary.setText("No visual evidence available.")
self.create_follow_up.setEnabled(False)
return
self.model_title.setText(profile.model_name)
self.state.setText(f"{profile.architecture.upper()} · {profile.state}")
state_color = COLORS["green"] if profile.state == "Promising" else COLORS["orange"] if "Needs" not in profile.state else COLORS["blue_2"]
self.state.setStyleSheet(f"color: {state_color}; font-weight: 700;")
self.diagnosis.setText("Evidence: " + profile.diagnosis)
self.recommendation.setText("Training Coach: " + profile.recommendation)
self.create_follow_up.setEnabled(bool(profile.runs and profile.runs[0].dataset_path))
self.timeline.setRowCount(len(profile.runs))
for row, run in enumerate(profile.runs):
values = [
run.id, run.status, str(run.epochs or "—"),
"—" if run.final_loss is None else f"{run.final_loss:.5f}",
self._duration(run.training_time_seconds),
"—" if run.quality_score is None else f"{run.quality_score}/100",
run.dataset_name or Path(run.dataset_path).name or "—",
]
for column, value in enumerate(values):
item = QTableWidgetItem(value)
if column in {1, 2, 3, 4, 5}:
item.setTextAlignment(Qt.AlignCenter)
self.timeline.setItem(row, column, item)
paths: list[tuple[Path, str]] = []
for run in profile.runs:
paths.extend((Path(path), f"Training preview · {run.id}") for path in run.preview_images)
for record in profile.generations:
paths.extend((path, f"Generation · seed {record.seed + index}") for index, path in enumerate(record.images))
deduplicated: dict[str, tuple[Path, str]] = {}
for path, label in paths:
key = str(path)
if key not in deduplicated and path.is_file():
deduplicated[key] = (path, label)
if len(deduplicated) >= 24:
break
for path, label in list(deduplicated.values())[:24]:
pixmap = _thumbnail(path, 160, 116)
item = QListWidgetItem(QIcon(pixmap), label)
item.setData(Qt.UserRole, str(path))
self.gallery.addItem(item)
self.gallery_summary.setText(
f"{len(profile.runs)} training run(s) · {len(profile.generations)} generation batch(es) · "
f"{profile.rated_images} reviewed image signal(s). Double-click an image to open it."
)
def _create_follow_up(self) -> None:
profile = next((entry for entry in self.profiles if entry.key == self.current_key), None)
if profile:
self.plan_requested.emit(recommended_training_request(profile))
def _open_image(self, item: QListWidgetItem) -> None:
path = str(item.data(Qt.UserRole) or "")
if path:
QDesktopServices.openUrl(QUrl.fromLocalFile(path))
@staticmethod
def _duration(seconds: int) -> str:
if not seconds:
return "—"
if seconds >= 3600:
return f"{seconds // 3600}h {(seconds % 3600) // 60}m"
return f"{seconds // 60}m {seconds % 60}s"