From 351280130a6d76ad3211d0cfa764f9796db5d6bc Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 11:05:09 +0530 Subject: [PATCH 001/168] radeon forge: add package skeleton --- extra/radeon_forge/__init__.py | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 extra/radeon_forge/__init__.py diff --git a/extra/radeon_forge/__init__.py b/extra/radeon_forge/__init__.py new file mode 100644 index 0000000000000..198fa149c96ed --- /dev/null +++ b/extra/radeon_forge/__init__.py @@ -0,0 +1,7 @@ +"""Radeon Forge: oracle-guided synthesis and autotuning for AMD inference workloads.""" + +from .contracts import Candidate, Objective, TrialResult, WorkloadContract +from .ledger import ExperimentLedger +from .tuner import successive_halving + +__all__ = ["Candidate", "ExperimentLedger", "Objective", "TrialResult", "WorkloadContract", "successive_halving"] From f5c5c91426b0d38e66842d772490090a11d46352 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 11:05:28 +0530 Subject: [PATCH 002/168] radeon forge: define workload and trial contracts --- extra/radeon_forge/contracts.py | 97 +++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 extra/radeon_forge/contracts.py diff --git a/extra/radeon_forge/contracts.py b/extra/radeon_forge/contracts.py new file mode 100644 index 0000000000000..b5e9159b7bafd --- /dev/null +++ b/extra/radeon_forge/contracts.py @@ -0,0 +1,97 @@ +from __future__ import annotations + +from dataclasses import asdict, dataclass, field +from enum import Enum +from typing import Any, Mapping + + +class Objective(str, Enum): + MEDIAN_LATENCY_US = "median_latency_us" + P95_LATENCY_US = "p95_latency_us" + END_TO_END_P95_MS = "end_to_end_p95_ms" + + +@dataclass(frozen=True) +class WorkloadContract: + name: str + target: str = "gfx1100" + objective: Objective = Objective.P95_LATENCY_US + max_abs_error: float = 1e-3 + max_rel_error: float = 1e-3 + max_vram_bytes: int | None = None + max_vgprs: int | None = None + max_lds_bytes: int | None = None + forbid_spills: bool = True + metadata: Mapping[str, Any] = field(default_factory=dict) + + +@dataclass(frozen=True) +class Candidate: + candidate_id: str + family: str + parameters: Mapping[str, int | float | str | bool] + source_path: str | None = None + hypothesis: str = "" + parent_id: str | None = None + + def to_dict(self) -> dict[str, Any]: + return asdict(self) + + +@dataclass(frozen=True) +class ResourceReport: + vgprs: int | None = None + sgprs: int | None = None + lds_bytes: int | None = None + scratch_bytes: int | None = None + occupancy: int | None = None + spilled_vgprs: int = 0 + spilled_sgprs: int = 0 + + @property + def has_spills(self) -> bool: + return self.spilled_vgprs > 0 or self.spilled_sgprs > 0 + + +@dataclass(frozen=True) +class CorrectnessReport: + passed: bool + max_abs_error: float = 0.0 + max_rel_error: float = 0.0 + checked_values: int = 0 + reason: str = "" + + +@dataclass(frozen=True) +class TrialResult: + candidate: Candidate + correctness: CorrectnessReport + samples_us: tuple[float, ...] = () + median_latency_us: float | None = None + p95_latency_us: float | None = None + end_to_end_p95_ms: float | None = None + resources: ResourceReport = field(default_factory=ResourceReport) + compile_ok: bool = True + stable: bool = True + rejected_reason: str = "" + evidence: Mapping[str, Any] = field(default_factory=dict) + + def metric(self, objective: Objective) -> float: + value = { + Objective.MEDIAN_LATENCY_US: self.median_latency_us, + Objective.P95_LATENCY_US: self.p95_latency_us, + Objective.END_TO_END_P95_MS: self.end_to_end_p95_ms, + }[objective] + return float("inf") if value is None else value + + def is_feasible(self, contract: WorkloadContract) -> bool: + if not self.compile_ok or not self.stable or not self.correctness.passed: return False + if contract.forbid_spills and self.resources.has_spills: return False + if contract.max_vgprs is not None and self.resources.vgprs is not None and self.resources.vgprs > contract.max_vgprs: return False + if contract.max_lds_bytes is not None and self.resources.lds_bytes is not None and self.resources.lds_bytes > contract.max_lds_bytes: return False + if self.correctness.max_abs_error > contract.max_abs_error: return False + if self.correctness.max_rel_error > contract.max_rel_error: return False + return self.metric(contract.objective) != float("inf") + + def to_dict(self) -> dict[str, Any]: + return asdict(self) From ef8e61e2652c0f5f7d5625e1a34423d9e076da1b Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 11:05:42 +0530 Subject: [PATCH 003/168] radeon forge: add append-only experiment ledger --- extra/radeon_forge/ledger.py | 49 ++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 extra/radeon_forge/ledger.py diff --git a/extra/radeon_forge/ledger.py b/extra/radeon_forge/ledger.py new file mode 100644 index 0000000000000..fcdc8300b9c1d --- /dev/null +++ b/extra/radeon_forge/ledger.py @@ -0,0 +1,49 @@ +from __future__ import annotations + +import json +import os +from dataclasses import asdict, is_dataclass +from datetime import datetime, timezone +from pathlib import Path +from typing import Any, Iterable + + +class ExperimentLedger: + """Append-only JSONL evidence store. + + Generated implementations are disposable; this ledger preserves hypotheses, + contracts, measurements, rejections, and approvals needed to reproduce why a + candidate was selected. + """ + + def __init__(self, path: str | os.PathLike[str]): + self.path = Path(path) + self.path.parent.mkdir(parents=True, exist_ok=True) + + @staticmethod + def _jsonable(value: Any) -> Any: + if is_dataclass(value): return asdict(value) + if isinstance(value, Path): return str(value) + if isinstance(value, dict): return {str(k): ExperimentLedger._jsonable(v) for k, v in value.items()} + if isinstance(value, (list, tuple)): return [ExperimentLedger._jsonable(v) for v in value] + return value + + def append(self, event: str, payload: Any) -> None: + record = { + "schema_version": 1, + "timestamp_utc": datetime.now(timezone.utc).isoformat(), + "event": event, + "payload": self._jsonable(payload), + } + line = json.dumps(record, sort_keys=True, separators=(",", ":")) + "\n" + fd = os.open(self.path, os.O_APPEND | os.O_CREAT | os.O_WRONLY, 0o600) + try: + os.write(fd, line.encode("utf-8")) + os.fsync(fd) + finally: + os.close(fd) + + def records(self) -> Iterable[dict[str, Any]]: + if not self.path.exists(): return () + with self.path.open("r", encoding="utf-8") as f: + return tuple(json.loads(line) for line in f if line.strip()) From af475b617db482722b2b62136878b5430f45d076 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 11:06:04 +0530 Subject: [PATCH 004/168] radeon forge: add hard-gated successive halving tuner --- extra/radeon_forge/tuner.py | 69 +++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 extra/radeon_forge/tuner.py diff --git a/extra/radeon_forge/tuner.py b/extra/radeon_forge/tuner.py new file mode 100644 index 0000000000000..be5414e7aa86b --- /dev/null +++ b/extra/radeon_forge/tuner.py @@ -0,0 +1,69 @@ +from __future__ import annotations + +from collections.abc import Callable, Sequence +from dataclasses import dataclass +from math import ceil + +from .contracts import Candidate, TrialResult, WorkloadContract +from .ledger import ExperimentLedger + + +Evaluator = Callable[[Candidate, int], TrialResult] + + +@dataclass(frozen=True) +class TuningSummary: + winner: TrialResult | None + evaluated: tuple[TrialResult, ...] + rounds: int + + +def _rank(results: Sequence[TrialResult], contract: WorkloadContract) -> list[TrialResult]: + return sorted(results, key=lambda result: (not result.is_feasible(contract), result.metric(contract.objective), result.candidate.candidate_id)) + + +def successive_halving(candidates: Sequence[Candidate], evaluator: Evaluator, contract: WorkloadContract, budgets: Sequence[int] = (5, 20, 100), + reduction: int = 4, ledger: ExperimentLedger | None = None) -> TuningSummary: + """Evaluate candidates with increasing benchmark budgets. + + Correctness and resource constraints are hard gates. A faster candidate can + never outrank a candidate that passes the workload contract. + """ + if not candidates: return TuningSummary(None, (), 0) + if not budgets or any(budget <= 0 for budget in budgets): raise ValueError("budgets must contain positive iteration counts") + if reduction < 2: raise ValueError("reduction must be at least 2") + + active = list(candidates) + all_results: list[TrialResult] = [] + completed_rounds = 0 + for round_index, budget in enumerate(budgets): + completed_rounds += 1 + round_results: list[TrialResult] = [] + for candidate in active: + if ledger is not None: ledger.append("trial_started", {"round": round_index, "budget": budget, "candidate": candidate}) + try: + result = evaluator(candidate, budget) + except Exception as exc: # the optimizer records failures rather than silently losing candidates + from .contracts import CorrectnessReport + result = TrialResult(candidate=candidate, correctness=CorrectnessReport(False, reason=f"evaluator exception: {exc!r}"), + compile_ok=False, stable=False, rejected_reason="evaluator_exception") + round_results.append(result) + all_results.append(result) + if ledger is not None: ledger.append("trial_finished", {"round": round_index, "budget": budget, "result": result}) + + ranked = _rank(round_results, contract) + feasible = [result for result in ranked if result.is_feasible(contract)] + if not feasible: + if ledger is not None: ledger.append("round_failed", {"round": round_index, "reason": "no feasible candidates"}) + return TuningSummary(None, tuple(all_results), completed_rounds) + + if round_index == len(budgets) - 1: + winner = feasible[0] + if ledger is not None: ledger.append("winner_selected", winner) + return TuningSummary(winner, tuple(all_results), completed_rounds) + + keep = max(1, ceil(len(feasible) / reduction)) + active = [result.candidate for result in feasible[:keep]] + if ledger is not None: ledger.append("round_survivors", {"round": round_index, "candidate_ids": [candidate.candidate_id for candidate in active]}) + + raise AssertionError("unreachable") From 9185982bbf89cfc24d33e4b57f565bf291f077b6 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 11:08:27 +0530 Subject: [PATCH 005/168] radeon forge: parse AMD kernel resource metadata --- extra/radeon_forge/amd_metadata.py | 54 ++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 extra/radeon_forge/amd_metadata.py diff --git a/extra/radeon_forge/amd_metadata.py b/extra/radeon_forge/amd_metadata.py new file mode 100644 index 0000000000000..28999dfd90ad3 --- /dev/null +++ b/extra/radeon_forge/amd_metadata.py @@ -0,0 +1,54 @@ +from __future__ import annotations + +import re +from collections.abc import Mapping +from typing import Any + +from .contracts import ResourceReport + + +_PATTERNS: dict[str, tuple[str, ...]] = { + "vgprs": (r"\bNumVgprs:\s*(\d+)", r"\.vgpr_count:\s*(\d+)", r"\bvgpr_count:\s*(\d+)"), + "sgprs": (r"\bNumSgprs:\s*(\d+)", r"\.sgpr_count:\s*(\d+)", r"\bsgpr_count:\s*(\d+)"), + "scratch_bytes": (r"\bScratchSize:\s*(\d+)", r"\.private_segment_fixed_size:\s*(\d+)", r"\bprivate_segment_fixed_size:\s*(\d+)"), + "lds_bytes": (r"\bLDSByteSize:\s*(\d+)", r"\.group_segment_fixed_size:\s*(\d+)", r"\bgroup_segment_fixed_size:\s*(\d+)"), + "occupancy": (r"\bOccupancy:\s*(\d+)",), + "spilled_vgprs": (r"\.vgpr_spill_count:\s*(\d+)", r"\bvgpr_spill_count:\s*(\d+)"), + "spilled_sgprs": (r"\.sgpr_spill_count:\s*(\d+)", r"\bsgpr_spill_count:\s*(\d+)"), +} + + +def _first_int(text: str, patterns: tuple[str, ...]) -> int | None: + for pattern in patterns: + if match := re.search(pattern, text, flags=re.IGNORECASE): return int(match.group(1)) + return None + + +def resource_report_from_text(text: str) -> ResourceReport: + values = {name: _first_int(text, patterns) for name, patterns in _PATTERNS.items()} + return ResourceReport( + vgprs=values["vgprs"], sgprs=values["sgprs"], lds_bytes=values["lds_bytes"], scratch_bytes=values["scratch_bytes"], + occupancy=values["occupancy"], spilled_vgprs=values["spilled_vgprs"] or 0, spilled_sgprs=values["spilled_sgprs"] or 0, + ) + + +def _as_int(value: Any) -> int | None: + if value is None: return None + try: return int(value) + except (TypeError, ValueError): return None + + +def resource_report_from_comgr(metadata: Mapping[str, Any], kernel_index: int = 0) -> ResourceReport: + """Parse COMGR executable metadata such as the structure exposed in tinygrad PR #3641.""" + kernels = metadata.get("amdhsa.kernels") + if not isinstance(kernels, list) or kernel_index >= len(kernels) or not isinstance(kernels[kernel_index], Mapping): + raise ValueError("COMGR metadata does not contain the requested amdhsa kernel") + kernel = kernels[kernel_index] + return ResourceReport( + vgprs=_as_int(kernel.get(".vgpr_count", kernel.get("vgpr_count"))), + sgprs=_as_int(kernel.get(".sgpr_count", kernel.get("sgpr_count"))), + lds_bytes=_as_int(kernel.get(".group_segment_fixed_size", kernel.get("group_segment_fixed_size"))), + scratch_bytes=_as_int(kernel.get(".private_segment_fixed_size", kernel.get("private_segment_fixed_size"))), + spilled_vgprs=_as_int(kernel.get(".vgpr_spill_count", kernel.get("vgpr_spill_count"))) or 0, + spilled_sgprs=_as_int(kernel.get(".sgpr_spill_count", kernel.get("sgpr_spill_count"))) or 0, + ) From e67955c956ec6a643b5e728f7747023569695764 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 11:08:42 +0530 Subject: [PATCH 006/168] radeon forge: add discoverable kernel-family search spaces --- extra/radeon_forge/families.py | 55 ++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 extra/radeon_forge/families.py diff --git a/extra/radeon_forge/families.py b/extra/radeon_forge/families.py new file mode 100644 index 0000000000000..d3a84681785d3 --- /dev/null +++ b/extra/radeon_forge/families.py @@ -0,0 +1,55 @@ +from __future__ import annotations + +import hashlib +import itertools +from dataclasses import dataclass +from pathlib import Path +from typing import Any, Mapping, Sequence + +from .contracts import Candidate + + +@dataclass(frozen=True) +class KernelFamily: + name: str + source_path: Path + fixed_defines: Mapping[str, int | float | str | bool] + search_space: Mapping[str, Sequence[int | float | str | bool]] + hypothesis: str + + def candidates(self) -> list[Candidate]: + names = tuple(self.search_space) + values = [self.search_space[name] for name in names] + candidates: list[Candidate] = [] + for combination in itertools.product(*values): + params: dict[str, int | float | str | bool] = dict(self.fixed_defines) + params.update(zip(names, combination)) + digest = hashlib.sha256(repr(sorted(params.items())).encode()).hexdigest()[:10] + candidates.append(Candidate(candidate_id=f"{self.name}-{digest}", family=self.name, parameters=params, + source_path=str(self.source_path), hypothesis=self.hypothesis)) + return candidates + + +def compiler_defines(parameters: Mapping[str, Any]) -> list[str]: + defines: list[str] = [] + for key, value in sorted(parameters.items()): + if isinstance(value, bool): value = int(value) + defines.append(f"-D{key}={value}") + return defines + + +def rdna3_rmsnorm_fp8_family(root: str | Path, n_elems: int, hidden: int, eps_literal: str = "1e-5f") -> KernelFamily: + """Seed family from tinygrad's fused RMSNorm/multiply/FP8 kernel. + + The source was originally tuned for a different AMD target. Forge treats it + as a pattern and recompiles every candidate for gfx1100; unsupported or + resource-heavy candidates are rejected before benchmarking. + """ + source = Path(root) / "extra/llama_kernels/fused_rmsnorm_mul_quantize_fp8/fused_rmsnorm_mul_quantize_fp8.cpp" + return KernelFamily( + name="rdna3-fused-rmsnorm-mul-quantize-fp8", + source_path=source, + fixed_defines={"N_ELEMS": n_elems, "HIDDEN": hidden, "EPS_LITERAL": eps_literal, "HAS_RESIDUAL": 0}, + search_space={"NUM_WG": (128, 256, 512, 1024), "THREADS_PER_WG": (64, 128, 256)}, + hypothesis="Tune work distribution for gfx1100 while preserving the fused single-HBM-pass algorithm.", + ) From f568d1d4c276a35d8ad5501887e590db8da06d94 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 11:09:05 +0530 Subject: [PATCH 007/168] radeon forge: compile candidate families for gfx1100 --- extra/radeon_forge/compile_backend.py | 50 +++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 extra/radeon_forge/compile_backend.py diff --git a/extra/radeon_forge/compile_backend.py b/extra/radeon_forge/compile_backend.py new file mode 100644 index 0000000000000..f9b6773433c89 --- /dev/null +++ b/extra/radeon_forge/compile_backend.py @@ -0,0 +1,50 @@ +from __future__ import annotations + +import shutil +import subprocess +import tempfile +from dataclasses import dataclass +from pathlib import Path + +from tinygrad.runtime.support.compiler_amd import HIPCCCompiler + +from .amd_metadata import resource_report_from_text +from .contracts import Candidate, ResourceReport +from .families import compiler_defines + + +@dataclass(frozen=True) +class CompiledCandidate: + candidate: Candidate + library: bytes + resources: ResourceReport + metadata_text: str + + +def _read_metadata(library: bytes) -> str: + tools_and_args = ( + ("/opt/rocm/llvm/bin/llvm-readobj", "--notes"), + ("/opt/rocm/llvm/bin/llvm-objdump", "--notes"), + ("llvm-readobj", "--notes"), + ("llvm-objdump", "--notes"), + ) + with tempfile.NamedTemporaryFile(suffix=".hsaco") as f: + f.write(library) + f.flush() + outputs: list[str] = [] + for tool, flag in tools_and_args: + executable = tool if Path(tool).exists() else shutil.which(tool) + if executable is None: continue + proc = subprocess.run([executable, flag, f.name], text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, check=False) + if proc.stdout: outputs.append(proc.stdout) + if proc.returncode == 0 and proc.stdout: break + return "\n".join(outputs) + + +def compile_candidate(candidate: Candidate, target: str = "gfx1100") -> CompiledCandidate: + if candidate.source_path is None: raise ValueError(f"candidate {candidate.candidate_id} has no source_path") + source = Path(candidate.source_path).read_text(encoding="utf-8") + options = ["-std=c++20", "-ffast-math", *compiler_defines(candidate.parameters)] + library = HIPCCCompiler(target, options).compile_cached(source) + metadata_text = _read_metadata(library) + return CompiledCandidate(candidate, library, resource_report_from_text(metadata_text), metadata_text) From 8ac0ebed99b7df647520be348eaa07e0e90e886f Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 11:09:35 +0530 Subject: [PATCH 008/168] radeon forge: add executable benchmark protocol --- extra/radeon_forge/command_backend.py | 81 +++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 extra/radeon_forge/command_backend.py diff --git a/extra/radeon_forge/command_backend.py b/extra/radeon_forge/command_backend.py new file mode 100644 index 0000000000000..84bf002b913fc --- /dev/null +++ b/extra/radeon_forge/command_backend.py @@ -0,0 +1,81 @@ +from __future__ import annotations + +import json +import os +import statistics +import subprocess +from dataclasses import dataclass +from pathlib import Path +from typing import Any, Mapping, Sequence + +from .contracts import Candidate, CorrectnessReport, ResourceReport, TrialResult + + +def _percentile(values: Sequence[float], q: float) -> float | None: + if not values: return None + ordered = sorted(values) + index = min(len(ordered) - 1, max(0, int((len(ordered) - 1) * q + 0.999999))) + return ordered[index] + + +@dataclass(frozen=True) +class CommandHarness: + """Evaluate candidates through a workload-specific executable. + + The executable receives the candidate and budget through environment + variables and must print one JSON object on its final non-empty stdout line. + This keeps Forge independent of any one kernel launch abstraction while the + reference implementation and benchmark protocol remain explicit. + """ + + command: Sequence[str] + cwd: str | Path | None = None + env: Mapping[str, str] | None = None + timeout_seconds: int = 300 + + def __call__(self, candidate: Candidate, budget: int) -> TrialResult: + run_env = dict(os.environ) + if self.env is not None: run_env.update(self.env) + run_env["RADEON_FORGE_CANDIDATE_JSON"] = json.dumps(candidate.to_dict(), sort_keys=True) + run_env["RADEON_FORGE_BUDGET"] = str(budget) + proc = subprocess.run(list(self.command), cwd=self.cwd, env=run_env, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + timeout=self.timeout_seconds, check=False) + lines = [line for line in proc.stdout.splitlines() if line.strip()] + if proc.returncode != 0 or not lines: + reason = f"command failed with exit {proc.returncode}: {proc.stderr[-2000:]}" + return TrialResult(candidate, CorrectnessReport(False, reason=reason), compile_ok=False, stable=False, + rejected_reason="command_failed", evidence={"stdout": proc.stdout[-2000:], "stderr": proc.stderr[-2000:]}) + try: + payload: dict[str, Any] = json.loads(lines[-1]) + except json.JSONDecodeError as exc: + return TrialResult(candidate, CorrectnessReport(False, reason=f"invalid JSON result: {exc}"), compile_ok=False, stable=False, + rejected_reason="invalid_result", evidence={"stdout": proc.stdout[-4000:], "stderr": proc.stderr[-2000:]}) + + correctness_data = payload.get("correctness", {}) + resource_data = payload.get("resources", {}) + samples = tuple(float(value) for value in payload.get("samples_us", ())) + correctness = CorrectnessReport( + passed=bool(correctness_data.get("passed", False)), + max_abs_error=float(correctness_data.get("max_abs_error", 0.0)), + max_rel_error=float(correctness_data.get("max_rel_error", 0.0)), + checked_values=int(correctness_data.get("checked_values", 0)), + reason=str(correctness_data.get("reason", "")), + ) + resources = ResourceReport( + vgprs=resource_data.get("vgprs"), sgprs=resource_data.get("sgprs"), lds_bytes=resource_data.get("lds_bytes"), + scratch_bytes=resource_data.get("scratch_bytes"), occupancy=resource_data.get("occupancy"), + spilled_vgprs=int(resource_data.get("spilled_vgprs", 0)), spilled_sgprs=int(resource_data.get("spilled_sgprs", 0)), + ) + return TrialResult( + candidate=candidate, + correctness=correctness, + samples_us=samples, + median_latency_us=float(payload["median_latency_us"]) if "median_latency_us" in payload else (statistics.median(samples) if samples else None), + p95_latency_us=float(payload["p95_latency_us"]) if "p95_latency_us" in payload else _percentile(samples, 0.95), + end_to_end_p95_ms=float(payload["end_to_end_p95_ms"]) if "end_to_end_p95_ms" in payload else None, + resources=resources, + compile_ok=bool(payload.get("compile_ok", True)), + stable=bool(payload.get("stable", True)), + rejected_reason=str(payload.get("rejected_reason", "")), + evidence={"stdout": proc.stdout, "stderr": proc.stderr, **payload.get("evidence", {})}, + ) From 0439d06ed274922941f797145776d36186555ee1 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 11:10:28 +0530 Subject: [PATCH 009/168] radeon forge: test contracts metadata and hard-gated tuning --- test/test_radeon_forge.py | 59 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 test/test_radeon_forge.py diff --git a/test/test_radeon_forge.py b/test/test_radeon_forge.py new file mode 100644 index 0000000000000..d038b43d2a4af --- /dev/null +++ b/test/test_radeon_forge.py @@ -0,0 +1,59 @@ +import tempfile +import unittest +from pathlib import Path + +from extra.radeon_forge.amd_metadata import resource_report_from_comgr, resource_report_from_text +from extra.radeon_forge.contracts import Candidate, CorrectnessReport, ResourceReport, TrialResult, WorkloadContract +from extra.radeon_forge.families import rdna3_rmsnorm_fp8_family +from extra.radeon_forge.ledger import ExperimentLedger +from extra.radeon_forge.tuner import successive_halving + + +class TestRadeonForge(unittest.TestCase): + def test_metadata_text(self): + text = """ +; NumSgprs: 18 +; NumVgprs: 54 +; ScratchSize: 0 +; LDSByteSize: 8192 bytes/workgroup +; Occupancy: 8 + .vgpr_spill_count: 0 + .sgpr_spill_count: 1 +""" + report = resource_report_from_text(text) + self.assertEqual((report.sgprs, report.vgprs, report.lds_bytes, report.occupancy), (18, 54, 8192, 8)) + self.assertTrue(report.has_spills) + + def test_metadata_comgr(self): + report = resource_report_from_comgr({"amdhsa.kernels": [{".vgpr_count": "44", ".sgpr_count": "20", + ".group_segment_fixed_size": "4096", ".private_segment_fixed_size": "0", ".vgpr_spill_count": "0", ".sgpr_spill_count": "0"}]}) + self.assertEqual((report.vgprs, report.sgprs, report.lds_bytes), (44, 20, 4096)) + self.assertFalse(report.has_spills) + + def test_family_grid(self): + family = rdna3_rmsnorm_fp8_family("/tmp/tinygrad", 4096 * 16, 4096) + candidates = family.candidates() + self.assertEqual(len(candidates), 12) + self.assertEqual(len({candidate.candidate_id for candidate in candidates}), 12) + self.assertTrue(all(candidate.parameters["HIDDEN"] == 4096 for candidate in candidates)) + + def test_hard_gate_beats_fast_invalid_candidate(self): + candidates = [Candidate("bad", "test", {}), Candidate("good", "test", {})] + contract = WorkloadContract("unit", max_vgprs=96) + + def evaluate(candidate, budget): + if candidate.candidate_id == "bad": + return TrialResult(candidate, CorrectnessReport(True), samples_us=(1.0,) * budget, median_latency_us=1.0, p95_latency_us=1.0, + resources=ResourceReport(vgprs=128)) + return TrialResult(candidate, CorrectnessReport(True), samples_us=(2.0,) * budget, median_latency_us=2.0, p95_latency_us=2.0, + resources=ResourceReport(vgprs=64)) + + with tempfile.TemporaryDirectory() as directory: + ledger = ExperimentLedger(Path(directory) / "events.jsonl") + summary = successive_halving(candidates, evaluate, contract, budgets=(1, 2), reduction=2, ledger=ledger) + self.assertIsNotNone(summary.winner) + self.assertEqual(summary.winner.candidate.candidate_id, "good") + self.assertTrue(any(record["event"] == "winner_selected" for record in ledger.records())) + + +if __name__ == "__main__": unittest.main() From 6b41c8349b295f6ee54f971d187d1049e512124b Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 11:14:54 +0530 Subject: [PATCH 010/168] radeon forge: document architecture metrics and current boundary --- extra/radeon_forge/README.md | 132 +++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 extra/radeon_forge/README.md diff --git a/extra/radeon_forge/README.md b/extra/radeon_forge/README.md new file mode 100644 index 0000000000000..93d7e57cdcf47 --- /dev/null +++ b/extra/radeon_forge/README.md @@ -0,0 +1,132 @@ +# Radeon Forge + +Radeon Forge is an oracle-guided, fully local performance-engineering agent for private AI workloads on AMD Radeon / ROCm. + +The durable project is the workload intent, invariants, correctness oracle, benchmark protocol, hardware evidence, and experiment history. Generated kernel implementations are disposable candidates: Forge may regenerate or replace them, but it may not deploy one that fails a hard oracle. + +## Track 2 scenario + +A user asks Forge to optimize a locally deployed private agent under explicit constraints, for example: + +```text +Minimize P95 end-to-end task latency on a Radeon PRO W7900. +Do not use remote model APIs. Preserve task success within 1% of baseline, +require valid tool calls, forbid kernel spills, and stay below the VRAM limit. +``` + +Forge decomposes the request, invokes compiler/test/benchmark tools, remembers prior experiments, requests permission for consequential actions, and deploys or rolls back a measured configuration. + +## Optimization loop + +```text +contract + -> baseline + -> choose kernel/runtime family + -> compile for gfx1100 + -> reject invalid resource usage + -> correctness oracle + -> short hardware trials + -> successive halving + -> held-out validation + -> approval + -> deploy or revert +``` + +The agent may propose structural rewrites. The deterministic tuner evaluates parameters within each implementation family. Neither may override a failed correctness, quality, stability, privacy, or resource gate. + +## Metrics + +Primary submission metric: + +- P95 end-to-end latency on a frozen private-agent task suite. + +Supporting metrics: + +- time to first token; +- inter-token latency and decode throughput; +- kernel/subgraph median and P95 latency; +- task success and tool-call validity; +- numerical maximum absolute and relative error; +- VGPR, SGPR, LDS, scratch, spills, occupancy, and peak VRAM; +- crash rate and repeated-run stability; +- external network calls. + +Speed is optimized subject to correctness and quality constraints. A faster invalid candidate cannot win. + +## Tinygrad's role + +Tinygrad is used as: + +1. the trusted reference implementation and end-to-end workload; +2. the baseline and fallback runtime; +3. a corpus of AMD kernel implementations and optimization patterns; +4. the low-level AMD execution path for selected generated candidates. + +Radeon Forge is kept under `extra/radeon_forge` instead of being embedded deeply into tinygrad's compiler. This makes the oracle and tuning loop independently auditable and lets generated HIP/UOp/AMD-DSL implementations remain replaceable. + +## Current implementation + +Implemented: + +- workload, objective, correctness, and resource contracts; +- hard feasibility gates; +- append-only JSONL experiment ledger; +- successive-halving tuner; +- AMD metadata parsing for registers, LDS, scratch, occupancy, and spills; +- target-specific HIP compilation backend; +- executable JSON benchmark protocol; +- kernel-family search-space representation; +- seed family based on tinygrad's fused RMSNorm/multiply/FP8 implementation; +- unit tests proving metadata extraction and that invalid fast candidates cannot win. + +Not yet claimed or implemented: + +- no W7900 performance result has been recorded; +- the gfx950 Llama kernels are pattern references until individually compiled and validated on gfx1100; +- no generated candidate is approved for deployment; +- local planner-model serving and the interactive approval UI are still pending; +- end-to-end private-agent evaluation is still pending. + +## Benchmark executable protocol + +A workload harness receives: + +- `RADEON_FORGE_CANDIDATE_JSON` +- `RADEON_FORGE_BUDGET` + +Its final non-empty stdout line must be a JSON object such as: + +```json +{ + "compile_ok": true, + "stable": true, + "samples_us": [8.4, 8.2, 8.3], + "correctness": { + "passed": true, + "max_abs_error": 0.0002, + "max_rel_error": 0.0008, + "checked_values": 65536 + }, + "resources": { + "vgprs": 72, + "sgprs": 32, + "lds_bytes": 8192, + "scratch_bytes": 0, + "spilled_vgprs": 0, + "spilled_sgprs": 0 + }, + "evidence": { + "target": "gfx1100", + "reference": "tinygrad" + } +} +``` + +## Development validation + +```bash +python3 -m unittest test.test_radeon_forge -v +python3 -m compileall -q extra/radeon_forge +``` + +Hardware evidence must be generated on the assigned Radeon Cloud W7900 before any speed claim is made. From ea875965c0c778b1c784fcf4edd63a95be75a984 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 11:15:27 +0530 Subject: [PATCH 011/168] radeon forge: record RDNA3 kernel corpus and provenance --- extra/radeon_forge/KERNEL_CORPUS.md | 104 ++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 extra/radeon_forge/KERNEL_CORPUS.md diff --git a/extra/radeon_forge/KERNEL_CORPUS.md b/extra/radeon_forge/KERNEL_CORPUS.md new file mode 100644 index 0000000000000..1e44c187697a7 --- /dev/null +++ b/extra/radeon_forge/KERNEL_CORPUS.md @@ -0,0 +1,104 @@ +# Kernel and optimization corpus + +This file records the provenance and intended use of initial Radeon Forge implementation families. Inclusion does not imply that a kernel is correct or fast on the W7900. Every candidate must still pass target compilation, resource, numerical, benchmark, and held-out gates. + +## Native RDNA3 / gfx1100 seed + +### `extra/gemm/amd_asm_matmul.py` + +Status: first hardware family. + +Properties: + +- direct RDNA3 instruction construction through tinygrad's AMD DSL; +- explicitly targets gfx1100; +- 128x128 float32 GEMM tile; +- 128-thread workgroup; +- VOPD-aware accumulator and operand register placement; +- LDS staging and double-buffer-style global prefetch; +- reference check against tinygrad matmul already exists. + +Initial tuning/synthesis dimensions: + +- FMAC pair order and instruction scheduling; +- prefetch placement and distance; +- `s_clause` grouping; +- waitcnt placement; +- LDS swizzle and padding; +- occupancy limiter used by the harness; +- matrix-shape specialization. + +Unsafe dimensions are not exposed as blind scalar knobs. Structural variants must regenerate a complete program and pass the reference oracle. + +## Llama fused-kernel patterns + +Most current files under `extra/llama_kernels` compile with `HIPCCCompiler("gfx950", ...)`. They are therefore architectural patterns, not accepted gfx1100 candidates. + +### Fused RMSNorm / multiply / FP8 quantization + +Source: + +- `extra/llama_kernels/fused_rmsnorm_mul_quantize_fp8/` + +Useful ideas: + +- remove intermediate HBM materializations; +- one workgroup per row with grid-stride row processing; +- vectorized BF16 loads; +- fused normalization, weighting, quantization, saved backward state, and amax; +- compile-time workgroup and grid parameters. + +Candidate structural variants: + +- per-workgroup amax followed by a second reduction; +- scalar global atomic amax; +- workgroup/thread-count choices valid for hidden dimension; +- residual-add fusion; +- direct C/HIP versus UOp representation. + +Required before use on W7900: + +- successful gfx1100 compilation; +- confirmation that the relevant dtype/intrinsics are supported and performant; +- numerical validation against tinygrad; +- resource metadata and no-spill gate; +- end-to-end relevance to the selected inference model. + +### Fused cross entropy + +Provenance pattern: upstream tinygrad PR #16263 replaced handwritten C with a portable UOp version while preserving fusion and memory savings. The PR notes that the then-current BEAM-tuned UOp forward remained much slower than handwritten C, while end-to-end training differed far less. + +Use in Forge: + +- compare direct target-specific code and portable UOp families; +- optimize the end-to-end objective, not an isolated kernel ratio; +- retain the portable family as fallback. + +### Atomic amax + +Provenance pattern: upstream tinygrad PR #17063 replaces a two-stage amax reduction with atomics to remove repeated reduction-kernel launches. + +Use in Forge: + +- structural alternative, not assumed winner; +- compare under representative tensor size and contention; +- reject if numerical or end-to-end behavior regresses. + +## Compiler-resource oracle + +Provenance pattern: upstream tinygrad PR #3641 demonstrates extraction of COMGR executable metadata including VGPR/SGPR counts, LDS, scratch, occupancy, and spill counts. + +Forge uses these as hard or diagnostic signals: + +- spills may be forbidden by workload contract; +- excess VGPR/LDS use can reject a candidate before expensive trials; +- a kernel that loses occupancy is not automatically rejected, but the trade-off is measured and retained in evidence. + +## Corpus policy + +1. Record source and target architecture. +2. Distinguish a reusable idea from code verified on gfx1100. +3. Preserve rejected variants and reasons. +4. Never import a claimed benchmark without reproducing it on the assigned W7900. +5. Prefer end-to-end inference improvement over isolated-kernel speedup. +6. Keep tinygrad/UOp implementations as reference and fallback even when direct generated code wins. From 125ae83d2a175707d43f98f48eeddd91de7a3950 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 11:15:53 +0530 Subject: [PATCH 012/168] radeon forge: add scoped deny-by-default permissions --- extra/radeon_forge/permissions.py | 76 +++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 extra/radeon_forge/permissions.py diff --git a/extra/radeon_forge/permissions.py b/extra/radeon_forge/permissions.py new file mode 100644 index 0000000000000..e1e7e8b68d84d --- /dev/null +++ b/extra/radeon_forge/permissions.py @@ -0,0 +1,76 @@ +from __future__ import annotations + +import secrets +from dataclasses import dataclass +from datetime import datetime, timedelta, timezone +from enum import Enum +from typing import Iterable + + +class Action(str, Enum): + INSPECT = "inspect" + COMPILE = "compile" + BENCHMARK = "benchmark" + WRITE_GENERATED_SOURCE = "write_generated_source" + RESTART_LOCAL_SERVICE = "restart_local_service" + DEPLOY = "deploy" + DOWNLOAD_MODEL = "download_model" + + +@dataclass(frozen=True) +class PermissionGrant: + token: str + actions: frozenset[Action] + reason: str + issued_at_utc: datetime + expires_at_utc: datetime + max_uses: int + + +class PermissionDenied(RuntimeError): pass + + +class PermissionController: + """Deny-by-default session permission controller. + + Grants are explicit, scoped, expiring, and usage-limited. The controller is + intentionally independent of the language model; an agent cannot authorize + its own consequential action. + """ + + def __init__(self): + self._grants: dict[str, PermissionGrant] = {} + self._uses: dict[str, int] = {} + + def issue(self, actions: Iterable[Action], reason: str, ttl_seconds: int = 900, max_uses: int = 1) -> PermissionGrant: + scope = frozenset(actions) + if not scope: raise ValueError("a permission grant must include at least one action") + if not reason.strip(): raise ValueError("a permission grant requires a reason") + if ttl_seconds <= 0 or max_uses <= 0: raise ValueError("ttl_seconds and max_uses must be positive") + now = datetime.now(timezone.utc) + grant = PermissionGrant(secrets.token_urlsafe(24), scope, reason.strip(), now, now + timedelta(seconds=ttl_seconds), max_uses) + self._grants[grant.token] = grant + self._uses[grant.token] = 0 + return grant + + def revoke(self, token: str) -> None: + self._grants.pop(token, None) + self._uses.pop(token, None) + + def authorize(self, token: str | None, action: Action) -> PermissionGrant: + if token is None or token not in self._grants: raise PermissionDenied(f"no active grant for {action.value}") + grant = self._grants[token] + if datetime.now(timezone.utc) >= grant.expires_at_utc: + self.revoke(token) + raise PermissionDenied(f"grant expired for {action.value}") + if action not in grant.actions: raise PermissionDenied(f"grant does not include {action.value}") + uses = self._uses[token] + if uses >= grant.max_uses: + self.revoke(token) + raise PermissionDenied(f"grant exhausted for {action.value}") + self._uses[token] = uses + 1 + if self._uses[token] >= grant.max_uses: + # The returned immutable grant remains valid as evidence, but the token + # cannot be reused for a second action. + self._grants.pop(token, None) + return grant From 9082329ec3af50fbf75ea86ef323a8986dba9c7e Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 11:16:27 +0530 Subject: [PATCH 013/168] radeon forge: test command protocol and permission gates --- test/test_radeon_forge.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/test/test_radeon_forge.py b/test/test_radeon_forge.py index d038b43d2a4af..b6684b6711bed 100644 --- a/test/test_radeon_forge.py +++ b/test/test_radeon_forge.py @@ -1,11 +1,15 @@ +import json +import sys import tempfile import unittest from pathlib import Path from extra.radeon_forge.amd_metadata import resource_report_from_comgr, resource_report_from_text +from extra.radeon_forge.command_backend import CommandHarness from extra.radeon_forge.contracts import Candidate, CorrectnessReport, ResourceReport, TrialResult, WorkloadContract from extra.radeon_forge.families import rdna3_rmsnorm_fp8_family from extra.radeon_forge.ledger import ExperimentLedger +from extra.radeon_forge.permissions import Action, PermissionController, PermissionDenied from extra.radeon_forge.tuner import successive_halving @@ -55,5 +59,30 @@ def evaluate(candidate, budget): self.assertEqual(summary.winner.candidate.candidate_id, "good") self.assertTrue(any(record["event"] == "winner_selected" for record in ledger.records())) + def test_permissions_are_scoped_and_single_use(self): + controller = PermissionController() + with self.assertRaises(PermissionDenied): controller.authorize(None, Action.BENCHMARK) + grant = controller.issue([Action.BENCHMARK], "run two short hardware trials", max_uses=1) + self.assertEqual(controller.authorize(grant.token, Action.BENCHMARK).reason, grant.reason) + with self.assertRaises(PermissionDenied): controller.authorize(grant.token, Action.BENCHMARK) + deploy_grant = controller.issue([Action.BENCHMARK], "benchmark only") + with self.assertRaises(PermissionDenied): controller.authorize(deploy_grant.token, Action.DEPLOY) + + def test_command_harness_json_protocol(self): + candidate = Candidate("candidate", "unit", {"THREADS": 128}) + with tempfile.TemporaryDirectory() as directory: + runner = Path(directory) / "runner.py" + payload = { + "samples_us": [3.0, 2.0, 4.0], + "correctness": {"passed": True, "checked_values": 16}, + "resources": {"vgprs": 32, "spilled_vgprs": 0, "spilled_sgprs": 0}, + } + runner.write_text(f"import json\nprint(json.dumps({json.dumps(payload)}))\n", encoding="utf-8") + result = CommandHarness([sys.executable, str(runner)])(candidate, 3) + self.assertTrue(result.correctness.passed) + self.assertEqual(result.median_latency_us, 3.0) + self.assertEqual(result.p95_latency_us, 4.0) + self.assertEqual(result.resources.vgprs, 32) + if __name__ == "__main__": unittest.main() From cfe177076ff388b7d53735ea60168a295d8b91ca Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 11:17:50 +0530 Subject: [PATCH 014/168] radeon forge: add native gfx1100 assembly GEMM family --- extra/radeon_forge/families.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/extra/radeon_forge/families.py b/extra/radeon_forge/families.py index d3a84681785d3..6a21d4cf8d999 100644 --- a/extra/radeon_forge/families.py +++ b/extra/radeon_forge/families.py @@ -38,6 +38,27 @@ def compiler_defines(parameters: Mapping[str, Any]) -> list[str]: return defines +def rdna3_asm_matmul_family(root: str | Path, n: int = 1024) -> KernelFamily: + """Autotuning family around tinygrad's native gfx1100 AMD-DSL GEMM. + + Each FMAC ordering contains exactly the same mathematical operations; only + instruction order changes. LIMIT_OCC controls the deliberate LDS occupancy + limiter already present in the kernel harness. Correctness is still checked + for every candidate against tinygrad matmul. + """ + source = Path(root) / "extra/gemm/amd_asm_matmul.py" + return KernelFamily( + name="rdna3-asm-matmul", + source_path=source, + fixed_defines={"N": n}, + search_space={ + "FMAC_ORDER": ("optimized", "row_major", "column_major", "snake"), + "LIMIT_OCC": (2, 4, 8), + }, + hypothesis="Search VOPD FMAC issue order and occupancy trade-offs for the existing native gfx1100 GEMM.", + ) + + def rdna3_rmsnorm_fp8_family(root: str | Path, n_elems: int, hidden: int, eps_literal: str = "1e-5f") -> KernelFamily: """Seed family from tinygrad's fused RMSNorm/multiply/FP8 kernel. From 5d5d4e8e074851599db84473ca37e5085dd2925b Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 11:18:15 +0530 Subject: [PATCH 015/168] radeon forge: add workload package --- extra/radeon_forge/workloads/__init__.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 extra/radeon_forge/workloads/__init__.py diff --git a/extra/radeon_forge/workloads/__init__.py b/extra/radeon_forge/workloads/__init__.py new file mode 100644 index 0000000000000..15c1fb0f09635 --- /dev/null +++ b/extra/radeon_forge/workloads/__init__.py @@ -0,0 +1 @@ +"""Hardware workload adapters for Radeon Forge.""" From fc44f9b7f103d38f4f6dfe8c7df0a48ff49f0ee0 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 11:18:48 +0530 Subject: [PATCH 016/168] radeon forge: add gfx1100 assembly GEMM benchmark oracle --- .../workloads/rdna3_asm_matmul.py | 133 ++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 extra/radeon_forge/workloads/rdna3_asm_matmul.py diff --git a/extra/radeon_forge/workloads/rdna3_asm_matmul.py b/extra/radeon_forge/workloads/rdna3_asm_matmul.py new file mode 100644 index 0000000000000..2079e112d77d7 --- /dev/null +++ b/extra/radeon_forge/workloads/rdna3_asm_matmul.py @@ -0,0 +1,133 @@ +from __future__ import annotations + +import json +import math +import os +import statistics + +import numpy as np + +from tinygrad import Context, Device, GlobalCounters, Tensor +from tinygrad.dtype import AddrSpace, dtypes +from tinygrad.engine.realize import Estimates, run_linear +from tinygrad.uop.ops import KernelInfo, Ops, UOp + +from extra.gemm import amd_asm_matmul as gemm + + +_ORDER_BUILDERS = { + "optimized": lambda: list(gemm.FMAC_PAIR_ORDER), + "row_major": lambda: [(a, b) for a in range(4) for b in range(8)], + "column_major": lambda: [(a, b) for b in range(8) for a in range(4)], + "snake": lambda: [(a, b) for a in range(4) for b in (range(8) if a % 2 == 0 else range(7, -1, -1))], +} + + +def _candidate() -> tuple[dict, int]: + candidate = json.loads(os.environ["RADEON_FORGE_CANDIDATE_JSON"]) + budget = int(os.environ.get("RADEON_FORGE_BUDGET", "5")) + if budget <= 0: raise ValueError("RADEON_FORGE_BUDGET must be positive") + return candidate, budget + + +def _install_order(name: str) -> None: + if name not in _ORDER_BUILDERS: raise ValueError(f"unknown FMAC order {name!r}") + order = _ORDER_BUILDERS[name]() + expected = {(a, b) for a in range(4) for b in range(8)} + if len(order) != 32 or set(order) != expected: raise ValueError(f"invalid FMAC order {name!r}") + gemm.FMAC_PAIR_ORDER = order + gemm.FMAC_PATTERN = gemm.derive_fmac_pattern(gemm.ACC_GRID, gemm.V_A_TILE_REGS, gemm.V_B_TILE_REGS) + + +def _percentile(values: list[float], q: float) -> float: + ordered = sorted(values) + return ordered[min(len(ordered) - 1, max(0, math.ceil(q * len(ordered)) - 1))] + + +def run() -> dict: + candidate, budget = _candidate() + parameters = candidate["parameters"] + n = int(parameters["N"]) + limit_occ = int(parameters["LIMIT_OCC"]) + order_name = str(parameters["FMAC_ORDER"]) + if limit_occ <= 0: raise ValueError("LIMIT_OCC must be positive") + _install_order(order_name) + + dev = Device[Device.DEFAULT] + arch = getattr(getattr(dev, "renderer", None), "target", None) + arch_name = getattr(arch, "arch", None) + if arch_name != "gfx1100": raise RuntimeError(f"RDNA3 workload requires gfx1100, got {arch_name!r}") + + instructions = gemm.build_kernel(n) + rng = np.random.default_rng(42) + a = Tensor(rng.random((n, n), dtype=np.float32) - 0.5) + b = Tensor(rng.random((n, n), dtype=np.float32) - 0.5) + c = Tensor.empty(n, n) + Tensor.realize(a, b, c) + + grid, local = (n // gemm.BLOCK_N, n // gemm.BLOCK_M, 1), (gemm.THREADS, 1, 1) + lds_size = max(gemm.LDS_SIZE, 65536 // limit_occ) + + def asm_kernel(A: UOp, B: UOp, C: UOp) -> UOp: + gidxs = [UOp.special(size, f"gidx{index}") for index, size in enumerate(grid)] + lidxs = [UOp.special(size, f"lidx{index}") for index, size in enumerate(local)] + lds = UOp.placeholder((lds_size,), dtypes.uint8, 0, AddrSpace.LOCAL) + sink = UOp.sink(A.base, B.base, C.base, lds, *gidxs, *lidxs, + arg=KernelInfo(name=f"radeon_forge_{candidate['candidate_id']}", estimates=Estimates(ops=n*n*n*2, mem=n*n*4*3))) + return UOp(Ops.PROGRAM, src=(sink, UOp(Ops.LINEAR, src=tuple(UOp(Ops.INS, arg=instruction) for instruction in instructions)))) + + c = Tensor.custom_kernel(a, b, c, fxn=asm_kernel)[2] + linear = c.schedule_linear() + with Context(DEBUG=0): + run_linear(linear) # warmup and realize all lazy dependencies + samples_us: list[float] = [] + for _ in range(budget): + start = GlobalCounters.time_sum_s + run_linear(linear) + elapsed = (GlobalCounters.time_sum_s - start) * 1e6 + if not math.isfinite(elapsed) or elapsed <= 0: raise RuntimeError(f"invalid timing sample {elapsed}") + samples_us.append(elapsed) + + reference = (a @ b).realize() + output_np, reference_np = c.numpy(), reference.numpy() + difference = np.abs(output_np - reference_np) + denominator = np.maximum(np.abs(reference_np), 1e-6) + max_abs_error = float(difference.max()) + max_rel_error = float((difference / denominator).max()) + mse = float(np.square(output_np - reference_np).mean()) + stable = len(samples_us) < 3 or statistics.pstdev(samples_us) / statistics.mean(samples_us) <= 0.10 + + return { + "compile_ok": True, + "stable": stable, + "samples_us": samples_us, + "median_latency_us": statistics.median(samples_us), + "p95_latency_us": _percentile(samples_us, 0.95), + "correctness": { + "passed": bool(np.isfinite(mse) and mse <= 1e-6), + "max_abs_error": max_abs_error, + "max_rel_error": max_rel_error, + "checked_values": int(output_np.size), + "reason": "" if np.isfinite(mse) and mse <= 1e-6 else f"mean squared error {mse}", + }, + "resources": { + "vgprs": 179, + "sgprs": 56, + "lds_bytes": lds_size, + "scratch_bytes": 0, + "spilled_vgprs": 0, + "spilled_sgprs": 0, + }, + "evidence": { + "target": arch_name, + "reference": "tinygrad matmul", + "mean_squared_error": mse, + "fmac_order": order_name, + "limit_occ": limit_occ, + "resource_source": "static register assignments and explicit LDS allocation", + "kernel_instruction_count": len(instructions), + }, + } + + +if __name__ == "__main__": print(json.dumps(run(), sort_keys=True)) From 5f75d669eb7f4b2dc613fe9ed68d157696521cba Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 11:19:29 +0530 Subject: [PATCH 017/168] radeon forge: add permissioned gfx1100 tuning CLI --- extra/radeon_forge/cli.py | 91 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 extra/radeon_forge/cli.py diff --git a/extra/radeon_forge/cli.py b/extra/radeon_forge/cli.py new file mode 100644 index 0000000000000..c7a1a7db262fd --- /dev/null +++ b/extra/radeon_forge/cli.py @@ -0,0 +1,91 @@ +from __future__ import annotations + +import argparse +import json +import sys +from pathlib import Path + +from .command_backend import CommandHarness +from .contracts import Objective, WorkloadContract +from .families import rdna3_asm_matmul_family +from .ledger import ExperimentLedger +from .permissions import Action, PermissionController +from .tuner import successive_halving + + +def _parse_budgets(value: str) -> tuple[int, ...]: + budgets = tuple(int(part) for part in value.split(",") if part.strip()) + if not budgets or any(budget <= 0 for budget in budgets): raise argparse.ArgumentTypeError("budgets must be positive comma-separated integers") + return budgets + + +def build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description="Oracle-guided Radeon Forge autotuner") + parser.add_argument("--root", default=".", help="tinygrad repository root") + parser.add_argument("--family", choices=("rdna3-asm-matmul",), default="rdna3-asm-matmul") + parser.add_argument("--n", type=int, default=1024, help="square GEMM dimension for the first gfx1100 hardware family") + parser.add_argument("--budgets", type=_parse_budgets, default=(3, 10, 30), help="successive-halving trial counts, e.g. 3,10,30") + parser.add_argument("--reduction", type=int, default=4) + parser.add_argument("--ledger", default="evidence/radeon_forge/events.jsonl") + parser.add_argument("--dry-run", action="store_true", help="print the plan without compiling or benchmarking") + parser.add_argument("--approve-benchmark", action="store_true", help="explicitly approve local W7900 benchmark execution") + return parser + + +def main(argv: list[str] | None = None) -> int: + args = build_parser().parse_args(argv) + root = Path(args.root).resolve() + family = rdna3_asm_matmul_family(root, args.n) + candidates = family.candidates() + plan = { + "family": family.name, + "candidate_count": len(candidates), + "budgets": args.budgets, + "reduction": args.reduction, + "objective": Objective.P95_LATENCY_US.value, + "target": "gfx1100", + "reference": "tinygrad matmul", + "candidate_ids": [candidate.candidate_id for candidate in candidates], + } + print(json.dumps({"plan": plan}, indent=2, sort_keys=True)) + if args.dry_run: return 0 + if not args.approve_benchmark: + print("Refusing hardware execution: pass --approve-benchmark after reviewing the plan.", file=sys.stderr) + return 2 + + ledger = ExperimentLedger(root / args.ledger) + ledger.append("optimization_requested", plan) + controller = PermissionController() + # Upper bound: every candidate in every round. The actual tuner consumes fewer + # uses after successive halving removes candidates. + grant = controller.issue([Action.BENCHMARK], f"approved benchmark plan for {family.name}", max_uses=len(candidates) * len(args.budgets)) + harness = CommandHarness([sys.executable, "-m", "extra.radeon_forge.workloads.rdna3_asm_matmul"], cwd=root) + + def approved_evaluator(candidate, budget): + authorized = controller.authorize(grant.token, Action.BENCHMARK) + ledger.append("permission_used", {"action": Action.BENCHMARK.value, "reason": authorized.reason, + "candidate_id": candidate.candidate_id, "budget": budget}) + return harness(candidate, budget) + + contract = WorkloadContract( + name="rdna3-asm-matmul", + target="gfx1100", + objective=Objective.P95_LATENCY_US, + max_abs_error=0.1, + max_rel_error=1000.0, + max_vgprs=192, + max_lds_bytes=32768, + forbid_spills=True, + metadata={"final_submission_metric": False, "purpose": "validate the Forge hardware tuning loop"}, + ) + ledger.append("workload_contract", contract) + summary = successive_halving(candidates, approved_evaluator, contract, budgets=args.budgets, reduction=args.reduction, ledger=ledger) + if summary.winner is None: + print(json.dumps({"status": "no_feasible_candidate", "evaluations": len(summary.evaluated)}, indent=2)) + return 1 + print(json.dumps({"status": "winner", "rounds": summary.rounds, "evaluations": len(summary.evaluated), + "result": summary.winner.to_dict()}, indent=2, sort_keys=True)) + return 0 + + +if __name__ == "__main__": raise SystemExit(main()) From 2e475df723403c6b70bd39e005201418737de907 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 11:20:50 +0530 Subject: [PATCH 018/168] radeon forge: add deterministic local knowledge retrieval --- extra/radeon_forge/knowledge.py | 75 +++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 extra/radeon_forge/knowledge.py diff --git a/extra/radeon_forge/knowledge.py b/extra/radeon_forge/knowledge.py new file mode 100644 index 0000000000000..a0ed8af401755 --- /dev/null +++ b/extra/radeon_forge/knowledge.py @@ -0,0 +1,75 @@ +from __future__ import annotations + +import math +import re +from collections import Counter +from dataclasses import dataclass +from pathlib import Path +from typing import Iterable, Sequence + + +_TOKEN = re.compile(r"[A-Za-z0-9_+#.-]+") + + +@dataclass(frozen=True) +class KnowledgeChunk: + source: str + start_line: int + end_line: int + text: str + + +@dataclass(frozen=True) +class RetrievalHit: + chunk: KnowledgeChunk + score: float + + @property + def citation(self) -> str: + return f"{self.chunk.source}:{self.chunk.start_line}-{self.chunk.end_line}" + + +class LocalKnowledgeBase: + """Small deterministic local retriever for architecture and experiment knowledge. + + This deliberately has no network path. It is not intended to replace a full + embedding index; its job is to provide auditable local excerpts and citations + to the planning model for the supported project corpus. + """ + + def __init__(self, chunks: Sequence[KnowledgeChunk]): + self.chunks = tuple(chunks) + self._term_counts = [Counter(self._tokens(chunk.text)) for chunk in self.chunks] + document_frequency: Counter[str] = Counter() + for counts in self._term_counts: document_frequency.update(counts.keys()) + self._idf = {term: math.log((1 + len(self.chunks)) / (1 + frequency)) + 1.0 for term, frequency in document_frequency.items()} + + @staticmethod + def _tokens(text: str) -> list[str]: + return [token.lower() for token in _TOKEN.findall(text)] + + @classmethod + def from_paths(cls, paths: Iterable[str | Path], lines_per_chunk: int = 40, overlap: int = 5) -> "LocalKnowledgeBase": + if lines_per_chunk <= 0 or overlap < 0 or overlap >= lines_per_chunk: raise ValueError("invalid chunk dimensions") + chunks: list[KnowledgeChunk] = [] + step = lines_per_chunk - overlap + for path_value in paths: + path = Path(path_value) + lines = path.read_text(encoding="utf-8").splitlines() + for start in range(0, len(lines), step): + selected = lines[start:start + lines_per_chunk] + if not selected: continue + chunks.append(KnowledgeChunk(str(path), start + 1, start + len(selected), "\n".join(selected))) + return cls(chunks) + + def search(self, query: str, top_k: int = 5) -> tuple[RetrievalHit, ...]: + if top_k <= 0: raise ValueError("top_k must be positive") + query_counts = Counter(self._tokens(query)) + if not query_counts: return () + hits: list[RetrievalHit] = [] + for chunk, counts in zip(self.chunks, self._term_counts): + length_norm = max(1.0, math.sqrt(sum(value * value for value in counts.values()))) + score = sum(query_count * counts.get(term, 0) * self._idf.get(term, 1.0) for term, query_count in query_counts.items()) / length_norm + if score > 0: hits.append(RetrievalHit(chunk, score)) + hits.sort(key=lambda hit: (-hit.score, hit.chunk.source, hit.chunk.start_line)) + return tuple(hits[:top_k]) From 1d54befb1b5fa9134214345450dd0e56a2b987e6 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 11:21:19 +0530 Subject: [PATCH 019/168] radeon forge: add loopback-only structured local planner --- extra/radeon_forge/planner.py | 125 ++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 extra/radeon_forge/planner.py diff --git a/extra/radeon_forge/planner.py b/extra/radeon_forge/planner.py new file mode 100644 index 0000000000000..249a162565552 --- /dev/null +++ b/extra/radeon_forge/planner.py @@ -0,0 +1,125 @@ +from __future__ import annotations + +import ipaddress +import json +import urllib.error +import urllib.request +from dataclasses import dataclass +from typing import Any, Mapping, Sequence +from urllib.parse import urlparse + +from .contracts import WorkloadContract +from .knowledge import RetrievalHit +from .permissions import Action + + +@dataclass(frozen=True) +class PlannerDecision: + summary: str + hypothesis: str + candidate_family: str + proposed_actions: tuple[Action, ...] + benchmark_budget: int + rationale: str + unknowns: tuple[str, ...] = () + + +class LocalEndpointRequired(ValueError): pass +class PlannerProtocolError(RuntimeError): pass + + +def validate_local_endpoint(endpoint: str) -> str: + parsed = urlparse(endpoint) + if parsed.scheme != "http" or not parsed.hostname: raise LocalEndpointRequired("planner endpoint must be a local HTTP URL") + hostname = parsed.hostname.lower() + is_loopback = hostname == "localhost" + if not is_loopback: + try: is_loopback = ipaddress.ip_address(hostname).is_loopback + except ValueError: is_loopback = False + if not is_loopback: raise LocalEndpointRequired(f"remote planner endpoint is forbidden: {hostname}") + if parsed.username or parsed.password: raise LocalEndpointRequired("credentials must not be embedded in the planner URL") + return endpoint.rstrip("/") + + +class LocalPlanner: + """OpenAI-compatible planner client restricted to loopback. + + Planner output is advisory structured data. It cannot execute tools, issue + permissions, or override deterministic oracle results. + """ + + def __init__(self, endpoint: str, model: str, timeout_seconds: int = 120): + self.endpoint = validate_local_endpoint(endpoint) + self.model = model + self.timeout_seconds = timeout_seconds + + @staticmethod + def _evidence(hits: Sequence[RetrievalHit]) -> list[dict[str, Any]]: + return [{"citation": hit.citation, "score": hit.score, "text": hit.chunk.text} for hit in hits] + + def plan(self, request: str, contract: WorkloadContract, evidence: Sequence[RetrievalHit] = (), + memory: Sequence[Mapping[str, Any]] = ()) -> PlannerDecision: + system = """You are Radeon Forge, a private AMD performance-engineering planner. +Return one JSON object only. Propose falsifiable hypotheses and bounded tool actions. +Never claim a speedup before hardware measurement. Never bypass correctness, quality, +privacy, stability, resource, or permission gates. Generated code is disposable; the +contract and oracle are authoritative.""" + user = { + "request": request, + "workload_contract": { + "name": contract.name, + "target": contract.target, + "objective": contract.objective.value, + "max_abs_error": contract.max_abs_error, + "max_rel_error": contract.max_rel_error, + "max_vgprs": contract.max_vgprs, + "max_lds_bytes": contract.max_lds_bytes, + "forbid_spills": contract.forbid_spills, + "metadata": dict(contract.metadata), + }, + "local_evidence": self._evidence(evidence), + "recent_memory": list(memory), + "required_schema": { + "summary": "string", + "hypothesis": "string", + "candidate_family": "string", + "proposed_actions": [action.value for action in Action], + "benchmark_budget": "positive integer", + "rationale": "string", + "unknowns": ["string"], + }, + } + payload = { + "model": self.model, + "temperature": 0, + "messages": [{"role": "system", "content": system}, {"role": "user", "content": json.dumps(user, sort_keys=True)}], + "response_format": {"type": "json_object"}, + } + request_obj = urllib.request.Request( + self.endpoint + "/chat/completions", + data=json.dumps(payload).encode("utf-8"), + headers={"Content-Type": "application/json"}, + method="POST", + ) + try: + with urllib.request.urlopen(request_obj, timeout=self.timeout_seconds) as response: + result = json.loads(response.read().decode("utf-8")) + except (urllib.error.URLError, TimeoutError, json.JSONDecodeError) as exc: + raise PlannerProtocolError(f"local planner request failed: {exc}") from exc + try: + content = result["choices"][0]["message"]["content"] + data = json.loads(content) + actions = tuple(Action(value) for value in data["proposed_actions"]) + budget = int(data["benchmark_budget"]) + if budget <= 0: raise ValueError("benchmark_budget must be positive") + return PlannerDecision( + summary=str(data["summary"]), + hypothesis=str(data["hypothesis"]), + candidate_family=str(data["candidate_family"]), + proposed_actions=actions, + benchmark_budget=budget, + rationale=str(data["rationale"]), + unknowns=tuple(str(value) for value in data.get("unknowns", ())), + ) + except (KeyError, TypeError, ValueError, json.JSONDecodeError) as exc: + raise PlannerProtocolError(f"invalid planner response: {exc}") from exc From db455b1a24e8d2d4fd27663aa02f9a3cd0c0737a Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 11:21:56 +0530 Subject: [PATCH 020/168] radeon forge: test local planner privacy and RAG citations --- test/test_radeon_forge.py | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/test/test_radeon_forge.py b/test/test_radeon_forge.py index b6684b6711bed..ceb19868c67a3 100644 --- a/test/test_radeon_forge.py +++ b/test/test_radeon_forge.py @@ -7,9 +7,11 @@ from extra.radeon_forge.amd_metadata import resource_report_from_comgr, resource_report_from_text from extra.radeon_forge.command_backend import CommandHarness from extra.radeon_forge.contracts import Candidate, CorrectnessReport, ResourceReport, TrialResult, WorkloadContract -from extra.radeon_forge.families import rdna3_rmsnorm_fp8_family +from extra.radeon_forge.families import rdna3_asm_matmul_family, rdna3_rmsnorm_fp8_family +from extra.radeon_forge.knowledge import LocalKnowledgeBase from extra.radeon_forge.ledger import ExperimentLedger from extra.radeon_forge.permissions import Action, PermissionController, PermissionDenied +from extra.radeon_forge.planner import LocalEndpointRequired, validate_local_endpoint from extra.radeon_forge.tuner import successive_halving @@ -34,12 +36,14 @@ def test_metadata_comgr(self): self.assertEqual((report.vgprs, report.sgprs, report.lds_bytes), (44, 20, 4096)) self.assertFalse(report.has_spills) - def test_family_grid(self): - family = rdna3_rmsnorm_fp8_family("/tmp/tinygrad", 4096 * 16, 4096) - candidates = family.candidates() - self.assertEqual(len(candidates), 12) - self.assertEqual(len({candidate.candidate_id for candidate in candidates}), 12) - self.assertTrue(all(candidate.parameters["HIDDEN"] == 4096 for candidate in candidates)) + def test_family_grids(self): + rmsnorm = rdna3_rmsnorm_fp8_family("/tmp/tinygrad", 4096 * 16, 4096).candidates() + self.assertEqual(len(rmsnorm), 12) + self.assertEqual(len({candidate.candidate_id for candidate in rmsnorm}), 12) + self.assertTrue(all(candidate.parameters["HIDDEN"] == 4096 for candidate in rmsnorm)) + matmul = rdna3_asm_matmul_family("/tmp/tinygrad", 1024).candidates() + self.assertEqual(len(matmul), 12) + self.assertEqual({candidate.parameters["FMAC_ORDER"] for candidate in matmul}, {"optimized", "row_major", "column_major", "snake"}) def test_hard_gate_beats_fast_invalid_candidate(self): candidates = [Candidate("bad", "test", {}), Candidate("good", "test", {})] @@ -84,5 +88,21 @@ def test_command_harness_json_protocol(self): self.assertEqual(result.p95_latency_us, 4.0) self.assertEqual(result.resources.vgprs, 32) + def test_planner_rejects_remote_endpoints(self): + self.assertEqual(validate_local_endpoint("http://127.0.0.1:8000/v1"), "http://127.0.0.1:8000/v1") + self.assertEqual(validate_local_endpoint("http://[::1]:8000/v1"), "http://[::1]:8000/v1") + with self.assertRaises(LocalEndpointRequired): validate_local_endpoint("https://api.example.com/v1") + with self.assertRaises(LocalEndpointRequired): validate_local_endpoint("http://192.168.1.5:8000/v1") + + def test_local_knowledge_retrieval_has_citations(self): + with tempfile.TemporaryDirectory() as directory: + path = Path(directory) / "knowledge.md" + path.write_text("RDNA3 uses VOPD scheduling.\nSpilled VGPRs are rejected.\nRMSNorm can remove HBM materialization.\n", encoding="utf-8") + knowledge = LocalKnowledgeBase.from_paths([path], lines_per_chunk=2, overlap=1) + hits = knowledge.search("VGPR spills", top_k=2) + self.assertTrue(hits) + self.assertIn(str(path), hits[0].citation) + self.assertIn("VGPR", hits[0].chunk.text) + if __name__ == "__main__": unittest.main() From f9f86d18e3ada489199cb9ce813466a8022cd567 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 11:22:48 +0530 Subject: [PATCH 021/168] radeon forge: add multi-turn proposal and approval state machine --- extra/radeon_forge/agent.py | 139 ++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 extra/radeon_forge/agent.py diff --git a/extra/radeon_forge/agent.py b/extra/radeon_forge/agent.py new file mode 100644 index 0000000000000..edcdb6a7d146d --- /dev/null +++ b/extra/radeon_forge/agent.py @@ -0,0 +1,139 @@ +from __future__ import annotations + +import secrets +from dataclasses import asdict, dataclass, replace +from enum import Enum +from typing import Any, Mapping, Protocol, Sequence + +from .contracts import WorkloadContract +from .knowledge import LocalKnowledgeBase, RetrievalHit +from .ledger import ExperimentLedger +from .permissions import Action, PermissionController, PermissionGrant +from .planner import PlannerDecision + + +class AgentState(str, Enum): + READY = "ready" + AWAITING_APPROVAL = "awaiting_approval" + APPROVED = "approved" + RUNNING = "running" + COMPLETED = "completed" + FAILED = "failed" + + +class AgentStateError(RuntimeError): pass + + +class PlannerLike(Protocol): + def plan(self, request: str, contract: WorkloadContract, evidence: Sequence[RetrievalHit] = (), + memory: Sequence[Mapping[str, Any]] = ()) -> PlannerDecision: ... + + +@dataclass(frozen=True) +class Proposal: + proposal_id: str + user_request: str + decision: PlannerDecision + evidence_citations: tuple[str, ...] + contract: WorkloadContract + supersedes: str | None = None + + +@dataclass(frozen=True) +class ApprovalReceipt: + proposal_id: str + approved_actions: tuple[Action, ...] + user_reason: str + grant: PermissionGrant + + +class ForgeAgent: + """Stateful multi-turn coordinator for planning and permissioned execution. + + This object does not execute kernels by itself. It coordinates the local + planner, retrieval, memory, and user approval boundary. Deterministic tools + must call `authorize` immediately before each consequential action. + """ + + def __init__(self, contract: WorkloadContract, planner: PlannerLike, knowledge: LocalKnowledgeBase, + ledger: ExperimentLedger, permissions: PermissionController | None = None): + self.contract = contract + self.planner = planner + self.knowledge = knowledge + self.ledger = ledger + self.permissions = permissions or PermissionController() + self.state = AgentState.READY + self.current_proposal: Proposal | None = None + self.approval: ApprovalReceipt | None = None + self._conversation: list[dict[str, Any]] = [] + + def _memory(self, limit: int = 20) -> tuple[Mapping[str, Any], ...]: + records = tuple(self.ledger.records()) + return tuple(records[-limit:]) + + def propose(self, user_request: str, top_k: int = 5) -> Proposal: + if self.state is AgentState.RUNNING: raise AgentStateError("cannot create a new proposal while tools are running") + request = user_request.strip() + if not request: raise ValueError("user_request must not be empty") + hits = self.knowledge.search(request, top_k=top_k) + decision = self.planner.plan(request, self.contract, evidence=hits, memory=self._memory()) + previous = self.current_proposal.proposal_id if self.current_proposal is not None else None + proposal = Proposal(secrets.token_urlsafe(12), request, decision, tuple(hit.citation for hit in hits), self.contract, previous) + self.current_proposal = proposal + self.approval = None + self.state = AgentState.AWAITING_APPROVAL + self._conversation.append({"role": "user", "content": request}) + self._conversation.append({"role": "agent", "proposal_id": proposal.proposal_id, "decision": asdict(decision)}) + self.ledger.append("proposal_created", proposal) + return proposal + + def revise(self, user_request: str, contract_changes: Mapping[str, Any] | None = None) -> Proposal: + if contract_changes: + allowed = {field for field in self.contract.__dataclass_fields__ if field not in {"name", "target"}} + unknown = set(contract_changes) - allowed + if unknown: raise ValueError(f"unsupported contract fields: {sorted(unknown)}") + self.contract = replace(self.contract, **dict(contract_changes)) + self.ledger.append("contract_revised", {"changes": dict(contract_changes), "contract": self.contract}) + return self.propose(user_request) + + def approve(self, proposal_id: str, user_reason: str, max_tool_uses: int | None = None) -> ApprovalReceipt: + if self.state is not AgentState.AWAITING_APPROVAL or self.current_proposal is None: + raise AgentStateError("there is no proposal awaiting approval") + if proposal_id != self.current_proposal.proposal_id: raise AgentStateError("approval does not match the current proposal") + actions = self.current_proposal.decision.proposed_actions + if not actions: raise AgentStateError("proposal contains no executable actions") + reason = user_reason.strip() + if not reason: raise ValueError("approval requires a user reason") + uses = max_tool_uses if max_tool_uses is not None else max(1, self.current_proposal.decision.benchmark_budget + len(actions)) + grant = self.permissions.issue(actions, reason, max_uses=uses) + receipt = ApprovalReceipt(proposal_id, actions, reason, grant) + self.approval = receipt + self.state = AgentState.APPROVED + # Never persist the bearer token. The receipt fields excluding the grant are + # enough to audit what the user approved. + self.ledger.append("proposal_approved", {"proposal_id": proposal_id, "approved_actions": [action.value for action in actions], + "user_reason": reason, "max_tool_uses": uses}) + return receipt + + def authorize(self, action: Action) -> PermissionGrant: + if self.state not in {AgentState.APPROVED, AgentState.RUNNING} or self.approval is None: + raise AgentStateError(f"action {action.value} is not approved") + grant = self.permissions.authorize(self.approval.grant.token, action) + self.state = AgentState.RUNNING + self.ledger.append("tool_authorized", {"proposal_id": self.approval.proposal_id, "action": action.value, "reason": grant.reason}) + return grant + + def complete(self, result: Mapping[str, Any]) -> None: + if self.state not in {AgentState.APPROVED, AgentState.RUNNING}: raise AgentStateError("no approved execution is active") + self.state = AgentState.COMPLETED + self.ledger.append("execution_completed", {"proposal_id": self.approval.proposal_id if self.approval else None, "result": dict(result)}) + + def fail(self, reason: str, evidence: Mapping[str, Any] | None = None) -> None: + if self.state not in {AgentState.APPROVED, AgentState.RUNNING}: raise AgentStateError("no approved execution is active") + self.state = AgentState.FAILED + self.ledger.append("execution_failed", {"proposal_id": self.approval.proposal_id if self.approval else None, + "reason": reason, "evidence": dict(evidence or {})}) + + @property + def conversation(self) -> tuple[Mapping[str, Any], ...]: + return tuple(self._conversation) From 38eb2b51316bcc50fd455e9bc884a84b1a853799 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 11:23:31 +0530 Subject: [PATCH 022/168] radeon forge: test multi-turn approval state machine --- test/test_radeon_forge.py | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/test/test_radeon_forge.py b/test/test_radeon_forge.py index ceb19868c67a3..5135d95352abd 100644 --- a/test/test_radeon_forge.py +++ b/test/test_radeon_forge.py @@ -4,6 +4,7 @@ import unittest from pathlib import Path +from extra.radeon_forge.agent import AgentState, AgentStateError, ForgeAgent from extra.radeon_forge.amd_metadata import resource_report_from_comgr, resource_report_from_text from extra.radeon_forge.command_backend import CommandHarness from extra.radeon_forge.contracts import Candidate, CorrectnessReport, ResourceReport, TrialResult, WorkloadContract @@ -11,10 +12,22 @@ from extra.radeon_forge.knowledge import LocalKnowledgeBase from extra.radeon_forge.ledger import ExperimentLedger from extra.radeon_forge.permissions import Action, PermissionController, PermissionDenied -from extra.radeon_forge.planner import LocalEndpointRequired, validate_local_endpoint +from extra.radeon_forge.planner import LocalEndpointRequired, PlannerDecision, validate_local_endpoint from extra.radeon_forge.tuner import successive_halving +class FakePlanner: + def plan(self, request, contract, evidence=(), memory=()): + return PlannerDecision( + summary=f"Tune {contract.name}", + hypothesis="A different FMAC order may reduce exposed stalls.", + candidate_family="rdna3-asm-matmul", + proposed_actions=(Action.BENCHMARK,), + benchmark_budget=2, + rationale=f"Measure on {contract.target}; evidence={len(evidence)}, memory={len(memory)}", + ) + + class TestRadeonForge(unittest.TestCase): def test_metadata_text(self): text = """ @@ -104,5 +117,24 @@ def test_local_knowledge_retrieval_has_citations(self): self.assertIn(str(path), hits[0].citation) self.assertIn("VGPR", hits[0].chunk.text) + def test_multi_turn_agent_requires_matching_approval(self): + with tempfile.TemporaryDirectory() as directory: + path = Path(directory) / "knowledge.md" + path.write_text("RDNA3 VOPD ordering is benchmark-sensitive.\n", encoding="utf-8") + knowledge = LocalKnowledgeBase.from_paths([path]) + ledger = ExperimentLedger(Path(directory) / "events.jsonl") + agent = ForgeAgent(WorkloadContract("private-code-agent"), FakePlanner(), knowledge, ledger) + proposal = agent.propose("Optimize P95 latency without changing precision") + self.assertEqual(agent.state, AgentState.AWAITING_APPROVAL) + with self.assertRaises(AgentStateError): agent.approve("wrong-id", "approved") + agent.approve(proposal.proposal_id, "I approve two local benchmark trials", max_tool_uses=2) + agent.authorize(Action.BENCHMARK) + agent.complete({"status": "accepted", "speedup": 1.1}) + self.assertEqual(agent.state, AgentState.COMPLETED) + events = [record["event"] for record in ledger.records()] + self.assertIn("proposal_created", events) + self.assertIn("proposal_approved", events) + self.assertIn("execution_completed", events) + if __name__ == "__main__": unittest.main() From 9bd33e24e9ca1334c40f3775b23084ecfeb4dd83 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 11:24:09 +0530 Subject: [PATCH 023/168] radeon forge: document agent layer and first hardware gate --- extra/radeon_forge/README.md | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/extra/radeon_forge/README.md b/extra/radeon_forge/README.md index 93d7e57cdcf47..45cc01acebc01 100644 --- a/extra/radeon_forge/README.md +++ b/extra/radeon_forge/README.md @@ -76,16 +76,44 @@ Implemented: - target-specific HIP compilation backend; - executable JSON benchmark protocol; - kernel-family search-space representation; -- seed family based on tinygrad's fused RMSNorm/multiply/FP8 implementation; -- unit tests proving metadata extraction and that invalid fast candidates cannot win. +- native gfx1100 assembly-GEMM family with valid alternate FMAC schedules and occupancy limits; +- gfx1100 hardware workload adapter with a tinygrad numerical oracle; +- fused RMSNorm/multiply/FP8 pattern family, gated as unverified on gfx1100; +- deterministic local retrieval with file-and-line citations; +- loopback-only OpenAI-compatible planner client with no remote fallback; +- deny-by-default, scoped, expiring permission grants; +- multi-turn proposal, revision, approval, authorization, completion, and failure state machine; +- unit tests for metadata, hard gates, command protocol, permissions, local endpoint restrictions, retrieval, and multi-turn approval. Not yet claimed or implemented: - no W7900 performance result has been recorded; - the gfx950 Llama kernels are pattern references until individually compiled and validated on gfx1100; - no generated candidate is approved for deployment; -- local planner-model serving and the interactive approval UI are still pending; -- end-to-end private-agent evaluation is still pending. +- no local planner model/runtime has yet been selected and benchmarked on the W7900; +- the interactive terminal/web surface is still pending; +- the frozen end-to-end private-agent task suite and held-out evaluation are still pending. + +## First gfx1100 hardware gate + +Review the candidate plan without running hardware: + +```bash +python3 -m extra.radeon_forge.cli --root . --family rdna3-asm-matmul --n 1024 --dry-run +``` + +After reviewing the printed plan, explicitly approve the local benchmark: + +```bash +python3 -m extra.radeon_forge.cli \ + --root . \ + --family rdna3-asm-matmul \ + --n 1024 \ + --budgets 3,10,30 \ + --approve-benchmark +``` + +This family validates the Forge search, permission, oracle, and evidence path. It is not the final Track 2 submission metric. ## Benchmark executable protocol From e9dece11a171e6a28108c084f337daf3a503532a Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 12:00:51 +0530 Subject: [PATCH 024/168] radeon forge: add unified trace recorder --- extra/radeon_forge/runtime/events.py | 81 ++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 extra/radeon_forge/runtime/events.py diff --git a/extra/radeon_forge/runtime/events.py b/extra/radeon_forge/runtime/events.py new file mode 100644 index 0000000000000..d5d94ade2504f --- /dev/null +++ b/extra/radeon_forge/runtime/events.py @@ -0,0 +1,81 @@ +from __future__ import annotations + +import contextlib, json, threading, time, uuid +from dataclasses import asdict, dataclass, field +from pathlib import Path +from typing import Any, Iterator, Mapping + + +def now_ns() -> int: return time.perf_counter_ns() + + +@dataclass(frozen=True) +class TraceEvent: + event_id: str + trace_id: str + kind: str + name: str + start_ns: int + end_ns: int | None = None + parent_id: str | None = None + attributes: Mapping[str, Any] = field(default_factory=dict) + + @property + def duration_ms(self) -> float | None: + return None if self.end_ns is None else (self.end_ns - self.start_ns) / 1e6 + + def to_dict(self) -> dict[str, Any]: + ret = asdict(self) + ret["duration_ms"] = self.duration_ms + return ret + + +class TraceRecorder: + """Thread-safe hierarchical trace recorder for agent, model, tool and GPU events.""" + def __init__(self, trace_id: str | None = None): + self.trace_id = trace_id or uuid.uuid4().hex + self._events: list[TraceEvent] = [] + self._lock = threading.RLock() + + def point(self, kind: str, name: str, parent_id: str | None = None, **attributes: Any) -> TraceEvent: + ev = TraceEvent(uuid.uuid4().hex, self.trace_id, kind, name, now_ns(), now_ns(), parent_id, attributes) + with self._lock: self._events.append(ev) + return ev + + @contextlib.contextmanager + def span(self, kind: str, name: str, parent_id: str | None = None, **attributes: Any) -> Iterator[TraceEvent]: + start = now_ns() + provisional = TraceEvent(uuid.uuid4().hex, self.trace_id, kind, name, start, None, parent_id, attributes) + try: yield provisional + except BaseException as exc: + attrs = dict(attributes) + attrs.update({"status": "error", "error_type": type(exc).__name__, "error": str(exc)}) + with self._lock: self._events.append(TraceEvent(provisional.event_id, self.trace_id, kind, name, start, now_ns(), parent_id, attrs)) + raise + else: + attrs = dict(attributes) + attrs.setdefault("status", "ok") + with self._lock: self._events.append(TraceEvent(provisional.event_id, self.trace_id, kind, name, start, now_ns(), parent_id, attrs)) + + def extend(self, events: list[TraceEvent]) -> None: + with self._lock: self._events.extend(events) + + def events(self) -> tuple[TraceEvent, ...]: + with self._lock: return tuple(sorted(self._events, key=lambda x: (x.start_ns, x.event_id))) + + def to_dict(self) -> dict[str, Any]: return {"trace_id": self.trace_id, "events": [x.to_dict() for x in self.events()]} + + def write_json(self, path: str | Path) -> Path: + out = Path(path) + out.parent.mkdir(parents=True, exist_ok=True) + out.write_text(json.dumps(self.to_dict(), indent=2, default=str), encoding="utf-8") + return out + + def chrome_trace(self) -> dict[str, Any]: + events = [] + for ev in self.events(): + if ev.end_ns is None: continue + events.append({"name": ev.name, "cat": ev.kind, "ph": "X", "ts": ev.start_ns / 1000, + "dur": (ev.end_ns - ev.start_ns) / 1000, "pid": 1, "tid": ev.kind, + "args": dict(ev.attributes)}) + return {"traceEvents": events, "displayTimeUnit": "ms"} From fb2d88888851b08c568c28422396ca008b98f604 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 12:01:15 +0530 Subject: [PATCH 025/168] radeon forge: add persistent local backend protocol --- extra/radeon_forge/runtime/backend.py | 107 ++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 extra/radeon_forge/runtime/backend.py diff --git a/extra/radeon_forge/runtime/backend.py b/extra/radeon_forge/runtime/backend.py new file mode 100644 index 0000000000000..dafcd7fc7b21e --- /dev/null +++ b/extra/radeon_forge/runtime/backend.py @@ -0,0 +1,107 @@ +from __future__ import annotations + +import json, queue, subprocess, threading +from dataclasses import dataclass, field +from typing import Any, Iterator, Mapping, Protocol, Sequence + + +@dataclass(frozen=True) +class BackendCapabilities: + streaming: bool = True + structured_tools: bool = False + prefix_cache: bool = False + persistent_kv: bool = False + kernel_metrics: bool = False + cancellation: bool = False + + +@dataclass(frozen=True) +class GenerationRequest: + session_id: str + messages: Sequence[Mapping[str, Any]] + tools: Sequence[Mapping[str, Any]] = () + max_tokens: int = 256 + temperature: float = 0.0 + stop: Sequence[str] = () + metadata: Mapping[str, Any] = field(default_factory=dict) + + +@dataclass(frozen=True) +class GenerationEvent: + kind: str + text: str = "" + tool_call: Mapping[str, Any] | None = None + finish_reason: str | None = None + metrics: Mapping[str, Any] = field(default_factory=dict) + + +class InferenceBackend(Protocol): + @property + def name(self) -> str: ... + @property + def capabilities(self) -> BackendCapabilities: ... + def stream(self, request: GenerationRequest) -> Iterator[GenerationEvent]: ... + def close(self) -> None: ... + + +class JsonlProcessBackend: + """Long-lived local model subprocess using a strict JSON-lines protocol.""" + def __init__(self, command: Sequence[str], env: Mapping[str, str] | None = None): + import os + child_env = os.environ.copy() + child_env.update(env or {}) + self._proc = subprocess.Popen(list(command), stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + text=True, bufsize=1, env=child_env) + if self._proc.stdin is None or self._proc.stdout is None or self._proc.stderr is None: raise RuntimeError("failed to open model process pipes") + self._stdin, self._stdout, self._stderr = self._proc.stdin, self._proc.stdout, self._proc.stderr + self._lock = threading.RLock() + self._stderr_lines: queue.Queue[str] = queue.Queue() + threading.Thread(target=self._drain_stderr, daemon=True).start() + ready = self._stdout.readline() + if not ready: raise RuntimeError(f"model process exited during startup: {self.stderr_tail()}") + payload = json.loads(ready) + if payload.get("kind") != "ready": raise RuntimeError(f"invalid backend handshake: {payload}") + self._name = str(payload.get("name", "jsonl-local-model")) + self._capabilities = BackendCapabilities(**payload.get("capabilities", {})) + + @property + def name(self) -> str: return self._name + @property + def capabilities(self) -> BackendCapabilities: return self._capabilities + + def _drain_stderr(self) -> None: + for line in self._stderr: + self._stderr_lines.put(line.rstrip()) + while self._stderr_lines.qsize() > 200: + try: self._stderr_lines.get_nowait() + except queue.Empty: break + + def stderr_tail(self, n: int = 20) -> str: + lines = list(self._stderr_lines.queue) + return "\n".join(lines[-n:]) + + def stream(self, request: GenerationRequest) -> Iterator[GenerationEvent]: + with self._lock: + self._stdin.write(json.dumps({"op": "generate", "request": { + "session_id": request.session_id, "messages": list(request.messages), "tools": list(request.tools), + "max_tokens": request.max_tokens, "temperature": request.temperature, "stop": list(request.stop), + "metadata": dict(request.metadata)}}) + "\n") + self._stdin.flush() + while True: + line = self._stdout.readline() + if not line: raise RuntimeError(f"model process terminated: {self.stderr_tail()}") + payload = json.loads(line) + if payload.get("kind") == "error": raise RuntimeError(str(payload.get("error", "backend error"))) + ev = GenerationEvent(kind=str(payload["kind"]), text=str(payload.get("text", "")), + tool_call=payload.get("tool_call"), finish_reason=payload.get("finish_reason"), + metrics=payload.get("metrics", {})) + yield ev + if ev.kind == "done": break + + def close(self) -> None: + if self._proc.poll() is not None: return + try: + self._stdin.write(json.dumps({"op": "shutdown"}) + "\n") + self._stdin.flush() + self._proc.wait(timeout=5) + except Exception: self._proc.kill() From 6a40c7a323c3044b27c3ec4cecedc6622ea8ce29 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 12:01:47 +0530 Subject: [PATCH 026/168] radeon forge: add permissioned local tools --- extra/radeon_forge/runtime/tools.py | 133 ++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 extra/radeon_forge/runtime/tools.py diff --git a/extra/radeon_forge/runtime/tools.py b/extra/radeon_forge/runtime/tools.py new file mode 100644 index 0000000000000..0ef7d182f1d25 --- /dev/null +++ b/extra/radeon_forge/runtime/tools.py @@ -0,0 +1,133 @@ +from __future__ import annotations + +import json, os, re, shlex, subprocess, time, uuid +from dataclasses import dataclass +from pathlib import Path +from typing import Any, Callable, Mapping, Sequence + +from ..permissions import Action, PermissionController + + +@dataclass(frozen=True) +class ToolSpec: + name: str + description: str + parameters: Mapping[str, Any] + action: Action = Action.INSPECT + + def openai_schema(self) -> dict[str, Any]: + return {"type": "function", "function": {"name": self.name, "description": self.description, "parameters": dict(self.parameters)}} + + +@dataclass(frozen=True) +class ToolCall: + call_id: str + name: str + arguments: Mapping[str, Any] + + +@dataclass(frozen=True) +class ToolResult: + call_id: str + name: str + ok: bool + output: Any + elapsed_ms: float + + +@dataclass +class _RegisteredTool: + spec: ToolSpec + fn: Callable[[Mapping[str, Any]], Any] + + +class ToolRegistry: + def __init__(self, permissions: PermissionController): + self.permissions = permissions + self._tools: dict[str, _RegisteredTool] = {} + + def register(self, spec: ToolSpec, fn: Callable[[Mapping[str, Any]], Any]) -> None: + if spec.name in self._tools: raise ValueError(f"duplicate tool {spec.name}") + self._tools[spec.name] = _RegisteredTool(spec, fn) + + def schemas(self) -> list[dict[str, Any]]: return [tool.spec.openai_schema() for tool in self._tools.values()] + def spec(self, name: str) -> ToolSpec: return self._tools[name].spec + + def execute(self, call: ToolCall, permission_token: str | None) -> ToolResult: + if call.name not in self._tools: return ToolResult(call.call_id, call.name, False, {"error": "unknown tool"}, 0.0) + tool = self._tools[call.name] + self.permissions.authorize(permission_token, tool.spec.action) + started = time.perf_counter_ns() + try: output, ok = tool.fn(call.arguments), True + except Exception as exc: output, ok = {"error": str(exc), "type": type(exc).__name__}, False + return ToolResult(call.call_id, call.name, ok, output, (time.perf_counter_ns() - started) / 1e6) + + +class WorkspaceTools: + """Safe-by-construction tools rooted in one private repository workspace.""" + def __init__(self, root: str | Path, command_allowlist: Sequence[str] = ("python", "python3", "pytest", "git", "rg")): + self.root = Path(root).resolve() + self.command_allowlist = frozenset(command_allowlist) + + def _path(self, value: str) -> Path: + path = (self.root / value).resolve() + if path != self.root and self.root not in path.parents: raise ValueError("path escapes workspace") + return path + + def list_files(self, args: Mapping[str, Any]) -> Any: + base = self._path(str(args.get("path", "."))) + limit = max(1, min(int(args.get("limit", 200)), 2000)) + files = [] + for path in base.rglob("*"): + if path.is_file() and ".git" not in path.parts: + files.append(str(path.relative_to(self.root))) + if len(files) >= limit: break + return {"files": files, "truncated": len(files) >= limit} + + def read_file(self, args: Mapping[str, Any]) -> Any: + path = self._path(str(args["path"])) + start, end = max(1, int(args.get("start_line", 1))), int(args.get("end_line", 400)) + if end < start or end - start > 2000: raise ValueError("invalid line range") + lines = path.read_text(encoding="utf-8", errors="replace").splitlines() + return {"path": str(path.relative_to(self.root)), "start_line": start, "end_line": min(end, len(lines)), + "content": "\n".join(f"{i}: {lines[i-1]}" for i in range(start, min(end, len(lines)) + 1))} + + def search_text(self, args: Mapping[str, Any]) -> Any: + pattern = re.compile(str(args["query"])) + base = self._path(str(args.get("path", "."))) + limit = max(1, min(int(args.get("limit", 50)), 500)) + hits = [] + for path in base.rglob("*"): + if not path.is_file() or ".git" in path.parts or path.stat().st_size > 2_000_000: continue + try: lines = path.read_text(encoding="utf-8", errors="replace").splitlines() + except OSError: continue + for number, line in enumerate(lines, 1): + if pattern.search(line): + hits.append({"path": str(path.relative_to(self.root)), "line": number, "text": line[:500]}) + if len(hits) >= limit: return {"hits": hits, "truncated": True} + return {"hits": hits, "truncated": False} + + def run_command(self, args: Mapping[str, Any]) -> Any: + command = args.get("command") + argv = shlex.split(command) if isinstance(command, str) else [str(x) for x in command] + if not argv or Path(argv[0]).name not in self.command_allowlist: raise ValueError("command is not allowlisted") + timeout = max(1, min(int(args.get("timeout_seconds", 60)), 600)) + proc = subprocess.run(argv, cwd=self.root, text=True, capture_output=True, timeout=timeout, + env={**os.environ, "PYTHONUNBUFFERED": "1"}) + return {"argv": argv, "returncode": proc.returncode, "stdout": proc.stdout[-20000:], "stderr": proc.stderr[-20000:]} + + def install(self, registry: ToolRegistry) -> None: + registry.register(ToolSpec("list_files", "List files inside the private workspace", {"type":"object","properties":{"path":{"type":"string"},"limit":{"type":"integer"}}}), self.list_files) + registry.register(ToolSpec("read_file", "Read a line range from a workspace file", {"type":"object","required":["path"],"properties":{"path":{"type":"string"},"start_line":{"type":"integer"},"end_line":{"type":"integer"}}}), self.read_file) + registry.register(ToolSpec("search_text", "Regex-search private workspace text", {"type":"object","required":["query"],"properties":{"query":{"type":"string"},"path":{"type":"string"},"limit":{"type":"integer"}}}), self.search_text) + registry.register(ToolSpec("run_command", "Run an allowlisted local development command", {"type":"object","required":["command"],"properties":{"command":{"type":["string","array"]},"timeout_seconds":{"type":"integer"}}}, Action.BENCHMARK), self.run_command) + + +def parse_tool_call(text: str) -> ToolCall | None: + match = re.fullmatch(r"\s*\s*(\{.*\})\s*\s*", text, flags=re.S) + if not match: return None + payload = json.loads(match.group(1)) + name = payload.get("name") + arguments = payload.get("arguments", {}) + if not isinstance(name, str) or not isinstance(arguments, dict): raise ValueError("invalid tool call payload") + return ToolCall(str(payload.get("id") or uuid.uuid4().hex), name, arguments) From 6bb2d25e679a6a63317c5fb9f4ee8a61a7b6b64b Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 12:02:18 +0530 Subject: [PATCH 027/168] radeon forge: add agent-native session loop --- extra/radeon_forge/runtime/session.py | 144 ++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 extra/radeon_forge/runtime/session.py diff --git a/extra/radeon_forge/runtime/session.py b/extra/radeon_forge/runtime/session.py new file mode 100644 index 0000000000000..02229b1e63696 --- /dev/null +++ b/extra/radeon_forge/runtime/session.py @@ -0,0 +1,144 @@ +from __future__ import annotations + +import threading, time, uuid +from dataclasses import asdict, dataclass, field +from enum import Enum +from typing import Any, Mapping + +from .backend import GenerationRequest, InferenceBackend +from .events import TraceRecorder +from .tools import ToolCall, ToolRegistry, parse_tool_call + + +class SessionState(str, Enum): + IDLE = "idle" + GENERATING = "generating" + AWAITING_TOOL_APPROVAL = "awaiting_tool_approval" + RUNNING_TOOL = "running_tool" + COMPLETED = "completed" + FAILED = "failed" + + +@dataclass(frozen=True) +class SessionEvent: + sequence: int + kind: str + data: Mapping[str, Any] + timestamp_s: float = field(default_factory=time.time) + + +class AgentSession: + """Persistent private-agent session with explicit tool approval and full tracing.""" + def __init__(self, backend: InferenceBackend, tools: ToolRegistry, system_prompt: str, session_id: str | None = None, + max_agent_steps: int = 8, trace: TraceRecorder | None = None): + self.session_id = session_id or uuid.uuid4().hex + self.backend, self.tools, self.system_prompt = backend, tools, system_prompt + self.max_agent_steps = max_agent_steps + self.trace = trace or TraceRecorder() + self.messages: list[dict[str, Any]] = [{"role": "system", "content": self._system_prompt()}] + self.events: list[SessionEvent] = [] + self.pending_tool_call: ToolCall | None = None + self.state = SessionState.IDLE + self._sequence = 0 + self._lock = threading.RLock() + + def _system_prompt(self) -> str: + schemas = self.tools.schemas() + tool_text = "\n".join(f"- {x['function']['name']}: {x['function']['description']} schema={x['function']['parameters']}" for x in schemas) + return self.system_prompt.strip() + ("\n\nAvailable local tools:\n" + tool_text if schemas else "") + """ + +Tool protocol: when a tool is required, output exactly one object wrapped as +{"name":"tool_name","arguments":{...}} +and no surrounding prose. Tool execution always requires user approval. Never +claim a tool result before it is returned. All inference and tools are local. +""" + + def _emit(self, kind: str, **data: Any) -> SessionEvent: + self._sequence += 1 + event = SessionEvent(self._sequence, kind, data) + self.events.append(event) + return event + + def send(self, content: str, max_tokens: int = 512, temperature: float = 0.0) -> tuple[SessionEvent, ...]: + with self._lock: + if self.state in {SessionState.GENERATING, SessionState.RUNNING_TOOL}: raise RuntimeError("session is busy") + if self.pending_tool_call is not None: raise RuntimeError("approve or reject the pending tool call first") + text = content.strip() + if not text: raise ValueError("message must not be empty") + self.messages.append({"role": "user", "content": text}) + self._emit("message", role="user", content=text) + return self._generate(max_tokens, temperature) + + def _generate(self, max_tokens: int, temperature: float) -> tuple[SessionEvent, ...]: + self.state = SessionState.GENERATING + started_at = len(self.events) + pieces: list[str] = [] + step = sum(1 for event in self.events if event.kind == "generation_started") + 1 + if step > self.max_agent_steps: raise RuntimeError("agent step limit exceeded") + with self.trace.span("agent", "model_turn", session_id=self.session_id, step=step) as parent: + self._emit("generation_started", backend=self.backend.name, step=step, capabilities=asdict(self.backend.capabilities)) + request = GenerationRequest(self.session_id, tuple(self.messages), tuple(self.tools.schemas()), max_tokens, temperature, + metadata={"trace_id": self.trace.trace_id, "step": step}) + try: + for event in self.backend.stream(request): + if event.kind == "token": + pieces.append(event.text) + self._emit("token", text=event.text, metrics=dict(event.metrics)) + self.trace.point("inference", "token", parent.event_id, text=event.text, **dict(event.metrics)) + elif event.kind in {"prefill", "decode", "kernel", "metric"}: + self._emit(event.kind, **dict(event.metrics)) + self.trace.point("inference", event.kind, parent.event_id, **dict(event.metrics)) + elif event.kind == "tool_call" and event.tool_call is not None: + self.pending_tool_call = ToolCall(str(event.tool_call.get("id") or uuid.uuid4().hex), str(event.tool_call["name"]), event.tool_call.get("arguments", {})) + elif event.kind == "done": self._emit("generation_done", finish_reason=event.finish_reason, metrics=dict(event.metrics)) + except Exception as exc: + self.state = SessionState.FAILED + self._emit("error", error=str(exc), error_type=type(exc).__name__) + raise + output = "".join(pieces) + if self.pending_tool_call is None: + try: self.pending_tool_call = parse_tool_call(output) + except Exception as exc: self._emit("tool_parse_error", error=str(exc), raw=output) + if self.pending_tool_call is not None: + self.state = SessionState.AWAITING_TOOL_APPROVAL + self._emit("tool_approval_required", call=asdict(self.pending_tool_call), action=self.tools.spec(self.pending_tool_call.name).action.value) + else: + self.messages.append({"role": "assistant", "content": output}) + self.state = SessionState.COMPLETED + self._emit("message", role="assistant", content=output) + return tuple(self.events[started_at:]) + + def approve_tool(self, permission_token: str, max_tokens: int = 512) -> tuple[SessionEvent, ...]: + with self._lock: + if self.state is not SessionState.AWAITING_TOOL_APPROVAL or self.pending_tool_call is None: raise RuntimeError("no tool call awaits approval") + call = self.pending_tool_call + started_at = len(self.events) + self.state = SessionState.RUNNING_TOOL + with self.trace.span("tool", call.name, call_id=call.call_id) as span: + self._emit("tool_started", call=asdict(call)) + result = self.tools.execute(call, permission_token) + self.trace.point("tool", "tool_result", span.event_id, ok=result.ok, elapsed_ms=result.elapsed_ms) + self._emit("tool_result", result=asdict(result)) + self.messages.append({"role": "assistant", "content": f"{{\"name\":\"{call.name}\"}}"}) + self.messages.append({"role": "tool", "name": call.name, "tool_call_id": call.call_id, "content": str(result.output)}) + self.pending_tool_call = None + if not result.ok: + self.state = SessionState.FAILED + return tuple(self.events[started_at:]) + self.state = SessionState.IDLE + self._generate(max_tokens, 0.0) + return tuple(self.events[started_at:]) + + def reject_tool(self, reason: str) -> SessionEvent: + with self._lock: + if self.pending_tool_call is None: raise RuntimeError("no pending tool call") + call = self.pending_tool_call + self.pending_tool_call = None + self.messages.append({"role": "tool", "name": call.name, "tool_call_id": call.call_id, "content": f"User rejected tool call: {reason}"}) + self.state = SessionState.IDLE + return self._emit("tool_rejected", call_id=call.call_id, reason=reason) + + def snapshot(self) -> dict[str, Any]: + return {"session_id": self.session_id, "state": self.state.value, "messages": list(self.messages), + "events": [asdict(x) for x in self.events], "pending_tool_call": asdict(self.pending_tool_call) if self.pending_tool_call else None, + "trace_id": self.trace.trace_id} From 0923d1c4334e95a27f3cee687f01dfc46e931fce Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 12:02:29 +0530 Subject: [PATCH 028/168] radeon forge: add unified inference engine --- extra/radeon_forge/runtime/engine.py | 48 ++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 extra/radeon_forge/runtime/engine.py diff --git a/extra/radeon_forge/runtime/engine.py b/extra/radeon_forge/runtime/engine.py new file mode 100644 index 0000000000000..e30c53a57edb9 --- /dev/null +++ b/extra/radeon_forge/runtime/engine.py @@ -0,0 +1,48 @@ +from __future__ import annotations + +import threading +from pathlib import Path +from typing import Any + +from ..permissions import PermissionController +from ..profiling.report import build_profile_report +from .backend import InferenceBackend +from .session import AgentSession +from .tools import ToolRegistry, WorkspaceTools + + +DEFAULT_SYSTEM_PROMPT = """You are Radeon Forge, a private local software and inference performance engineer. +Use evidence before making performance claims. Separate observations, inferences and unknowns. Prefer reversible changes and preserve correctness. You may inspect the private workspace and request permission to run local tools.""" + + +class ForgeEngine: + def __init__(self, backend: InferenceBackend, workspace: str | Path, system_prompt: str = DEFAULT_SYSTEM_PROMPT): + self.backend, self.workspace, self.system_prompt = backend, Path(workspace).resolve(), system_prompt + self.permissions = PermissionController() + self.tools = ToolRegistry(self.permissions) + WorkspaceTools(self.workspace).install(self.tools) + self._sessions: dict[str, AgentSession] = {} + self._lock = threading.RLock() + + def create_session(self) -> AgentSession: + with self._lock: + session = AgentSession(self.backend, self.tools, self.system_prompt) + self._sessions[session.session_id] = session + return session + + def session(self, session_id: str) -> AgentSession: + try: return self._sessions[session_id] + except KeyError as exc: raise KeyError(f"unknown session {session_id}") from exc + + def sessions(self) -> list[dict[str, Any]]: + with self._lock: return [{"session_id": x.session_id, "state": x.state.value, "trace_id": x.trace.trace_id} for x in self._sessions.values()] + + def grant_for_pending_tool(self, session_id: str, reason: str, max_uses: int = 1) -> str: + session = self.session(session_id) + if session.pending_tool_call is None: raise RuntimeError("session has no pending tool") + action = self.tools.spec(session.pending_tool_call.name).action + return self.permissions.issue([action], reason, max_uses=max_uses).token + + def profile(self, session_id: str) -> dict[str, Any]: return build_profile_report(self.session(session_id).trace.events()) + + def close(self) -> None: self.backend.close() From d1343d288b68fa4d0beaf6a9434c1be1839952a2 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 12:02:39 +0530 Subject: [PATCH 029/168] radeon forge: export runtime API --- extra/radeon_forge/runtime/__init__.py | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 extra/radeon_forge/runtime/__init__.py diff --git a/extra/radeon_forge/runtime/__init__.py b/extra/radeon_forge/runtime/__init__.py new file mode 100644 index 0000000000000..edb8c53326723 --- /dev/null +++ b/extra/radeon_forge/runtime/__init__.py @@ -0,0 +1,9 @@ +from .backend import BackendCapabilities, GenerationEvent, GenerationRequest, InferenceBackend, JsonlProcessBackend +from .engine import ForgeEngine +from .events import TraceEvent, TraceRecorder +from .session import AgentSession, SessionState +from .tools import ToolCall, ToolRegistry, ToolResult, ToolSpec, WorkspaceTools + +__all__ = ["AgentSession", "BackendCapabilities", "ForgeEngine", "GenerationEvent", "GenerationRequest", "InferenceBackend", + "JsonlProcessBackend", "SessionState", "ToolCall", "ToolRegistry", "ToolResult", "ToolSpec", "TraceEvent", + "TraceRecorder", "WorkspaceTools"] From 4f5ba3970035a99f73c52d3e7e6dac17ef0034ad Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 12:02:58 +0530 Subject: [PATCH 030/168] radeon forge: add agentic profile analysis --- extra/radeon_forge/profiling/report.py | 68 ++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 extra/radeon_forge/profiling/report.py diff --git a/extra/radeon_forge/profiling/report.py b/extra/radeon_forge/profiling/report.py new file mode 100644 index 0000000000000..0156e4d1ce027 --- /dev/null +++ b/extra/radeon_forge/profiling/report.py @@ -0,0 +1,68 @@ +from __future__ import annotations + +import math, statistics +from dataclasses import asdict, dataclass +from typing import Any, Iterable + +from ..runtime.events import TraceEvent + + +@dataclass(frozen=True) +class Finding: + severity: str + status: str + title: str + evidence: tuple[str, ...] + recommendation: str + + +def _percentile(values: list[float], percentile: float) -> float | None: + if not values: return None + ordered = sorted(values) + idx = min(len(ordered) - 1, max(0, math.ceil(percentile * len(ordered)) - 1)) + return ordered[idx] + + +def build_profile_report(events: Iterable[TraceEvent]) -> dict[str, Any]: + evs = list(events) + durations = {kind: sum(x.duration_ms or 0.0 for x in evs if x.kind == kind) for kind in {x.kind for x in evs}} + token_events = [x for x in evs if x.name == "token"] + token_wall = [float(x.attributes.get("wall_ms", 0.0)) for x in token_events if float(x.attributes.get("wall_ms", 0.0)) > 0] + token_gpu = [float(x.attributes.get("gpu_ms", 0.0)) for x in token_events if float(x.attributes.get("gpu_ms", 0.0)) > 0] + kernels = [int(x.attributes.get("kernel_count", 0)) for x in token_events] + prefill = [x for x in evs if x.name == "prefill"] + tools = [x for x in evs if x.kind == "tool" and x.duration_ms is not None] + findings: list[Finding] = [] + + if kernels and statistics.mean(kernels) >= 20: + findings.append(Finding("high", "observed", "High kernel-launch count per decoded token", + (f"mean launches/token={statistics.mean(kernels):.1f}",), + "Inspect launch gaps and fuse the dominant decode subgraph or generate a persistent megakernel candidate.")) + if token_wall and token_gpu and sum(token_gpu) / max(sum(token_wall), 1e-9) < 0.65: + findings.append(Finding("high", "inferred", "GPU execution explains only part of decode wall time", + (f"GPU/wall ratio={sum(token_gpu)/sum(token_wall):.2f}",), + "Profile CPU submission, synchronization and launch bubbles before optimizing arithmetic throughput.")) + if prefill and sum(x.duration_ms or 0 for x in prefill) > sum(token_wall): + findings.append(Finding("medium", "observed", "Prefill dominates this agent turn", + (f"prefill_ms={sum(x.duration_ms or 0 for x in prefill):.2f}", f"decode_ms={sum(token_wall):.2f}"), + "Increase stable-prefix reuse and compact repeated tool schemas/repository context.")) + if tools and sum(x.duration_ms or 0 for x in tools) > sum(token_wall): + findings.append(Finding("medium", "observed", "Tool execution dominates model decode", + (f"tool_ms={sum(x.duration_ms or 0 for x in tools):.2f}",), + "Parallelize independent tools or reduce tool round trips; a faster kernel will not materially improve task latency.")) + if not any(x.name == "kernel" for x in evs): + findings.append(Finding("info", "unknown", "No kernel-level trace is attached", + ("Only agent/model aggregate counters are available.",), + "Capture tinygrad PROFILE events or ROCm/SQTT evidence before making a causal hardware diagnosis.")) + + return { + "summary": { + "event_count": len(evs), "durations_ms_by_kind": durations, "decode_tokens": len(token_events), + "token_wall_ms_p50": statistics.median(token_wall) if token_wall else None, + "token_wall_ms_p95": _percentile(token_wall, 0.95), + "token_gpu_ms_p50": statistics.median(token_gpu) if token_gpu else None, + "mean_kernel_count_per_token": statistics.mean(kernels) if kernels else None, + "tool_time_ms": sum(x.duration_ms or 0 for x in tools), + }, + "findings": [asdict(x) for x in findings], + } From e11c8612d2d2e865b307ae788410bec53c014f4b Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 12:03:09 +0530 Subject: [PATCH 031/168] radeon forge: export profiling API --- extra/radeon_forge/profiling/__init__.py | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 extra/radeon_forge/profiling/__init__.py diff --git a/extra/radeon_forge/profiling/__init__.py b/extra/radeon_forge/profiling/__init__.py new file mode 100644 index 0000000000000..d28d85b81703c --- /dev/null +++ b/extra/radeon_forge/profiling/__init__.py @@ -0,0 +1,2 @@ +from .report import Finding, build_profile_report +__all__ = ["Finding", "build_profile_report"] From 1fb12e1faf45787f4b4080f2ce16657bb1836b8b Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 12:03:26 +0530 Subject: [PATCH 032/168] radeon forge: add deterministic development backend --- extra/radeon_forge/backends/fake.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 extra/radeon_forge/backends/fake.py diff --git a/extra/radeon_forge/backends/fake.py b/extra/radeon_forge/backends/fake.py new file mode 100644 index 0000000000000..50baf93a8471c --- /dev/null +++ b/extra/radeon_forge/backends/fake.py @@ -0,0 +1,22 @@ +from __future__ import annotations + +from typing import Iterator +from ..runtime.backend import BackendCapabilities, GenerationEvent, GenerationRequest + + +class ScriptedBackend: + """Deterministic backend for UI development and runtime tests without a GPU.""" + def __init__(self, responses: list[str] | None = None): self.responses = responses or ["Radeon Forge is ready."] + @property + def name(self) -> str: return "scripted-local" + @property + def capabilities(self) -> BackendCapabilities: return BackendCapabilities(kernel_metrics=True) + def stream(self, request: GenerationRequest) -> Iterator[GenerationEvent]: + text = self.responses.pop(0) if self.responses else "Done." + yield GenerationEvent("prefill", metrics={"wall_ms": 4.0, "prompt_tokens": 32, "prefix_reused_tokens": 24}) + parts = text.split(" ") + for index, token in enumerate(parts): + yield GenerationEvent("token", token + (" " if index < len(parts) - 1 else ""), + metrics={"index": index, "wall_ms": 2.0, "gpu_ms": 1.4, "kernel_count": 24}) + yield GenerationEvent("done", finish_reason="stop", metrics={"generated_tokens": len(parts)}) + def close(self) -> None: pass From 5e2d9fe8575ee5ffe898c639c153909230421a7b Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 12:05:27 +0530 Subject: [PATCH 033/168] radeon forge: add native tinygrad Llama backend --- .../backends/tinygrad_llama_worker.py | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 extra/radeon_forge/backends/tinygrad_llama_worker.py diff --git a/extra/radeon_forge/backends/tinygrad_llama_worker.py b/extra/radeon_forge/backends/tinygrad_llama_worker.py new file mode 100644 index 0000000000000..92a042cef4869 --- /dev/null +++ b/extra/radeon_forge/backends/tinygrad_llama_worker.py @@ -0,0 +1,110 @@ +from __future__ import annotations + +import argparse, json, sys, time +from pathlib import Path + +from tinygrad import Device, GlobalCounters, Tensor +from tinygrad.nn.state import get_parameters +from examples.llama3 import Tokenizer, build_transformer + + +def send(payload): + sys.stdout.write(json.dumps(payload, default=str) + "\n") + sys.stdout.flush() + + +def main() -> None: + parser = argparse.ArgumentParser(description="Persistent tinygrad Llama backend for Radeon Forge") + parser.add_argument("--model", type=Path, required=True) + parser.add_argument("--tokenizer", type=Path) + parser.add_argument("--size", choices=("1B", "8B", "70B", "405B"), default="1B") + parser.add_argument("--quantize", choices=("int8", "nf4", "float16", "fp8")) + parser.add_argument("--max-context", type=int, default=8192) + parser.add_argument("--seed", type=int, default=42) + args = parser.parse_args() + + if not args.model.exists(): raise FileNotFoundError(args.model) + tokenizer_path = args.tokenizer or ((args.model if args.model.is_dir() else args.model.parent) / "tokenizer.model") + if not tokenizer_path.exists(): raise FileNotFoundError(tokenizer_path) + Tensor.manual_seed(args.seed) + + tokenizer = Tokenizer(str(tokenizer_path)) + device = Device.DEFAULT + model = build_transformer(args.model, model_size=args.size, quantize=args.quantize, device=device, max_context=args.max_context) + param_bytes = sum(x.nbytes() for x in get_parameters(model)) + + def encode_role(role: str) -> list[int]: + return [tokenizer.special_tokens["<|start_header_id|>"]] + tokenizer.encode(role) + [tokenizer.special_tokens["<|end_header_id|>"]] + tokenizer.encode("\n\n") + + def encode_message(message) -> list[int]: + content = message.get("content", "") + if not isinstance(content, str): content = json.dumps(content, default=str) + return encode_role(str(message.get("role", "user"))) + tokenizer.encode(content.strip()) + [tokenizer.special_tokens["<|eot_id|>"]] + + active_session: str | None = None + active_tokens: list[int] = [] + send({"kind": "ready", "name": f"tinygrad-llama-{args.size}", "device": str(device), "model": str(args.model), + "parameter_bytes": param_bytes, "capabilities": {"streaming": True, "structured_tools": False, + "prefix_cache": True, "persistent_kv": True, "kernel_metrics": True, "cancellation": False}}) + + for line in sys.stdin: + try: + payload = json.loads(line) + if payload.get("op") == "shutdown": return + if payload.get("op") != "generate": raise ValueError("unsupported operation") + request = payload["request"] + session_id = str(request["session_id"]) + messages = request["messages"] + max_tokens = max(1, min(int(request.get("max_tokens", 256)), args.max_context)) + temperature = float(request.get("temperature", 0.0)) + + prompt = [tokenizer.bos_id] + for message in messages: prompt += encode_message(message) + if not messages or messages[-1].get("role") != "assistant": prompt += encode_role("assistant") + if len(prompt) >= args.max_context: raise ValueError(f"prompt has {len(prompt)} tokens, max context is {args.max_context}") + + common = 0 + if active_session == session_id: + for old, new in zip(active_tokens, prompt): + if old != new: break + common += 1 + active_session = session_id + prefill_start = time.perf_counter_ns() + prefill_gpu_s, prefill_kernels, prefill_mem, prefill_ops = 0.0, 0, 0, 0 + for position in range(common, len(prompt) - 1): + GlobalCounters.reset() + model(Tensor([[prompt[position]]], device=device), position, 0.0, 0, 0.0, 0.0, 0.0).realize() + prefill_gpu_s += GlobalCounters.time_sum_s + prefill_kernels += GlobalCounters.kernel_count + prefill_mem += GlobalCounters.global_mem + prefill_ops += GlobalCounters.global_ops + prefill_wall_ms = (time.perf_counter_ns() - prefill_start) / 1e6 + active_tokens = list(prompt) + send({"kind": "prefill", "metrics": {"wall_ms": prefill_wall_ms, "gpu_ms": prefill_gpu_s * 1e3, + "prompt_tokens": len(prompt), "prefix_reused_tokens": common, "new_prompt_tokens": max(0, len(prompt) - 1 - common), + "kernel_count": prefill_kernels, "global_mem_bytes": prefill_mem, "global_ops": prefill_ops}}) + + start_pos, last_tok = len(prompt) - 1, prompt[-1] + generated = 0 + for index in range(max_tokens): + GlobalCounters.reset() + wall_start = time.perf_counter_ns() + tok = model(Tensor([[last_tok]], device=device), start_pos, temperature, 0, 0.0, 0.0, 0.0).item() + wall_ms = (time.perf_counter_ns() - wall_start) / 1e6 + gpu_ms = GlobalCounters.time_sum_s * 1e3 + start_pos += 1 + last_tok = tok + if tok in tokenizer.stop_tokens: + send({"kind": "done", "finish_reason": "stop", "metrics": {"generated_tokens": generated}}) + break + active_tokens.append(tok) + generated += 1 + send({"kind": "token", "text": tokenizer.decode([tok]), "metrics": {"index": index, "wall_ms": wall_ms, + "gpu_ms": gpu_ms, "kernel_count": GlobalCounters.kernel_count, "global_mem_bytes": GlobalCounters.global_mem, + "global_ops": GlobalCounters.global_ops, "parameter_bandwidth_gbs": (param_bytes / max(GlobalCounters.time_sum_s, 1e-12)) / 1e9}}) + else: send({"kind": "done", "finish_reason": "length", "metrics": {"generated_tokens": generated}}) + except Exception as exc: + send({"kind": "error", "error": str(exc), "error_type": type(exc).__name__}) + + +if __name__ == "__main__": main() From 4c4ecde4c398ddf2991ddd05ed99275062649074 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 12:05:45 +0530 Subject: [PATCH 034/168] radeon forge: add tinygrad MockGPU correctness oracle --- extra/radeon_forge/oracles/mockgpu.py | 45 +++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 extra/radeon_forge/oracles/mockgpu.py diff --git a/extra/radeon_forge/oracles/mockgpu.py b/extra/radeon_forge/oracles/mockgpu.py new file mode 100644 index 0000000000000..b1f9ddec36438 --- /dev/null +++ b/extra/radeon_forge/oracles/mockgpu.py @@ -0,0 +1,45 @@ +from __future__ import annotations + +import json, os, shlex, subprocess, time +from dataclasses import asdict, dataclass +from pathlib import Path +from typing import Mapping, Sequence + + +@dataclass(frozen=True) +class MockGPUResult: + passed: bool + returncode: int + elapsed_ms: float + command: tuple[str, ...] + stdout: str + stderr: str + target: str = "gfx1100" + backend: str = "DEV=MOCK+AMD" + + def to_dict(self): return asdict(self) + + +class MockGPUOracle: + """Runs candidate correctness checks through tinygrad's integrated RDNA3 mock GPU. + + This is a semantic/instruction support gate, never a performance oracle. + Real W7900 execution remains authoritative for latency and resource behavior. + """ + def __init__(self, root: str | Path, timeout_seconds: int = 600): + self.root = Path(root).resolve() + self.timeout_seconds = timeout_seconds + + def run(self, command: Sequence[str] | str, env: Mapping[str, str] | None = None) -> MockGPUResult: + argv = tuple(shlex.split(command) if isinstance(command, str) else (str(x) for x in command)) + if not argv: raise ValueError("command must not be empty") + child_env = os.environ.copy() + child_env.update({"DEV": "MOCK+AMD", "PYTHONUNBUFFERED": "1"}) + child_env.update(env or {}) + started = time.perf_counter_ns() + proc = subprocess.run(argv, cwd=self.root, env=child_env, text=True, capture_output=True, timeout=self.timeout_seconds) + return MockGPUResult(proc.returncode == 0, proc.returncode, (time.perf_counter_ns() - started) / 1e6, argv, + proc.stdout[-50000:], proc.stderr[-50000:]) + + def run_json(self, command: Sequence[str] | str, env: Mapping[str, str] | None = None) -> str: + return json.dumps(self.run(command, env).to_dict(), default=str) From 883251dc92c5ea121c28d30975dd850429b9e376 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 12:06:20 +0530 Subject: [PATCH 035/168] radeon forge: add local web application server --- extra/radeon_forge/ui/server.py | 122 ++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 extra/radeon_forge/ui/server.py diff --git a/extra/radeon_forge/ui/server.py b/extra/radeon_forge/ui/server.py new file mode 100644 index 0000000000000..55234e127b095 --- /dev/null +++ b/extra/radeon_forge/ui/server.py @@ -0,0 +1,122 @@ +from __future__ import annotations + +import argparse, json, mimetypes, re, sys +from dataclasses import asdict +from http import HTTPStatus +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +from pathlib import Path +from typing import Any +from urllib.parse import urlparse + +from ..backends.fake import ScriptedBackend +from ..runtime import ForgeEngine, JsonlProcessBackend + + +class ForgeRequestHandler(BaseHTTPRequestHandler): + engine: ForgeEngine + static_root = Path(__file__).parent / "static" + + def log_message(self, format, *args): print(f"[forge-ui] {self.address_string()} {format % args}") + + def _json(self, payload: Any, status: int = 200): + body = json.dumps(payload, default=str).encode() + self.send_response(status) + self.send_header("Content-Type", "application/json") + self.send_header("Content-Length", str(len(body))) + self.send_header("Cache-Control", "no-store") + self.end_headers() + self.wfile.write(body) + + def _body(self) -> dict[str, Any]: + length = int(self.headers.get("Content-Length", "0")) + return json.loads(self.rfile.read(length) or b"{}") + + def _static(self, name: str): + path = (self.static_root / name).resolve() + if self.static_root.resolve() not in path.parents and path != self.static_root.resolve(): return self.send_error(404) + if not path.is_file(): return self.send_error(404) + body = path.read_bytes() + self.send_response(200) + self.send_header("Content-Type", mimetypes.guess_type(path.name)[0] or "application/octet-stream") + self.send_header("Content-Length", str(len(body))) + self.end_headers() + self.wfile.write(body) + + def _session_route(self): + match = re.fullmatch(r"/api/sessions/([a-f0-9]+)(?:/(.*))?", urlparse(self.path).path) + if not match: return None, None + return self.engine.session(match.group(1)), match.group(2) or "" + + def do_GET(self): + path = urlparse(self.path).path + try: + if path == "/": return self._static("index.html") + if path in {"/app.js", "/style.css"}: return self._static(path[1:]) + if path == "/api/health": return self._json({"ok": True, "backend": self.engine.backend.name, + "capabilities": asdict(self.engine.backend.capabilities)}) + if path == "/api/sessions": return self._json(self.engine.sessions()) + session, tail = self._session_route() + if session is None: return self.send_error(404) + if tail == "": return self._json(session.snapshot()) + if tail == "profile": return self._json(self.engine.profile(session.session_id)) + if tail == "trace": return self._json(session.trace.to_dict()) + if tail == "trace/chrome": return self._json(session.trace.chrome_trace()) + return self.send_error(404) + except KeyError as exc: return self._json({"error": str(exc)}, 404) + except Exception as exc: return self._json({"error": str(exc), "type": type(exc).__name__}, 500) + + def do_POST(self): + path = urlparse(self.path).path + try: + if path == "/api/sessions": return self._json(self.engine.create_session().snapshot(), HTTPStatus.CREATED) + session, tail = self._session_route() + if session is None: return self.send_error(404) + body = self._body() + if tail == "messages": + events = session.send(str(body.get("content", "")), int(body.get("max_tokens", 512)), float(body.get("temperature", 0.0))) + return self._json({"events": [asdict(x) for x in events], "session": session.snapshot()}) + if tail == "tools/approve": + reason = str(body.get("reason", "Approved from Radeon Forge UI")) + token = self.engine.grant_for_pending_tool(session.session_id, reason) + events = session.approve_tool(token, int(body.get("max_tokens", 512))) + return self._json({"events": [asdict(x) for x in events], "session": session.snapshot()}) + if tail == "tools/reject": + event = session.reject_tool(str(body.get("reason", "Rejected by user"))) + return self._json({"event": asdict(event), "session": session.snapshot()}) + return self.send_error(404) + except (ValueError, RuntimeError) as exc: return self._json({"error": str(exc), "type": type(exc).__name__}, 400) + except Exception as exc: return self._json({"error": str(exc), "type": type(exc).__name__}, 500) + + +def build_backend(args): + if args.backend == "scripted": return ScriptedBackend() + if args.model is None: raise SystemExit("--model is required for --backend tinygrad-llama") + command = [sys.executable, "-m", "extra.radeon_forge.backends.tinygrad_llama_worker", "--model", str(args.model), + "--size", args.size, "--max-context", str(args.max_context)] + if args.tokenizer: command += ["--tokenizer", str(args.tokenizer)] + if args.quantize: command += ["--quantize", args.quantize] + return JsonlProcessBackend(command) + + +def main(): + parser = argparse.ArgumentParser(description="Radeon Forge local inference and profiling UI") + parser.add_argument("--workspace", type=Path, default=Path.cwd()) + parser.add_argument("--backend", choices=("scripted", "tinygrad-llama"), default="scripted") + parser.add_argument("--model", type=Path) + parser.add_argument("--tokenizer", type=Path) + parser.add_argument("--size", choices=("1B", "8B", "70B", "405B"), default="1B") + parser.add_argument("--quantize", choices=("int8", "nf4", "float16", "fp8")) + parser.add_argument("--max-context", type=int, default=8192) + parser.add_argument("--host", default="127.0.0.1") + parser.add_argument("--port", type=int, default=7790) + args = parser.parse_args() + if args.host not in {"127.0.0.1", "localhost", "::1"}: raise SystemExit("Forge UI binds to loopback only") + engine = ForgeEngine(build_backend(args), args.workspace) + ForgeRequestHandler.engine = engine + server = ThreadingHTTPServer((args.host, args.port), ForgeRequestHandler) + print(f"Radeon Forge UI: http://{args.host}:{args.port} backend={engine.backend.name} workspace={args.workspace.resolve()}") + try: server.serve_forever() + finally: engine.close() + + +if __name__ == "__main__": main() From d13e976e20423b1dfcec3adf1b4072bcc2d150c8 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 12:06:44 +0530 Subject: [PATCH 036/168] radeon forge: add integrated UI shell --- extra/radeon_forge/ui/static/index.html | 75 +++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 extra/radeon_forge/ui/static/index.html diff --git a/extra/radeon_forge/ui/static/index.html b/extra/radeon_forge/ui/static/index.html new file mode 100644 index 0000000000000..b706efb04e8fc --- /dev/null +++ b/extra/radeon_forge/ui/static/index.html @@ -0,0 +1,75 @@ + + + + + + Radeon Forge + + + +
+ + +
+
+

Agent Console

Connecting to local backend…

+
+ + + +
+
+ +
+
+
RF

Optimize the whole agent loop.

Run a private model, inspect the repository, approve tools, and trace every turn down to GPU counters.

+
+ +
+ + +
+
+ +
+
+
P50 token—wall time
+
P95 token—wall time
+
Launches/token—tinygrad counters
+
Tool time—current trace
+
+
+

Agentic diagnosis

+

Time attribution

+
+
+ +
+

Unified execution timeline

Agent → model → token → tool → kernel evidence

+
+
+ + +
+
+ + + From 4463dbd1ea687c8f57530807e741860d752928d3 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 12:07:33 +0530 Subject: [PATCH 037/168] radeon forge: style the local inference UI --- extra/radeon_forge/ui/static/style.css | 1 + 1 file changed, 1 insertion(+) create mode 100644 extra/radeon_forge/ui/static/style.css diff --git a/extra/radeon_forge/ui/static/style.css b/extra/radeon_forge/ui/static/style.css new file mode 100644 index 0000000000000..73a72cf69dab7 --- /dev/null +++ b/extra/radeon_forge/ui/static/style.css @@ -0,0 +1 @@ +:root{--bg:#0b0d12;--panel:#11151d;--panel2:#171c26;--line:#252c39;--text:#edf2f7;--muted:#8e99aa;--accent:#ff5c35;--accent2:#ff8b69;--good:#3ddc97;--warn:#f5b942;--bad:#ff5f6d;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;color-scheme:dark}*{box-sizing:border-box}body{margin:0;background:radial-gradient(circle at 50% -20%,#1b2130 0,#0b0d12 34%);color:var(--text);height:100vh;overflow:hidden}.shell{display:grid;grid-template-columns:248px minmax(0,1fr) 286px;height:100vh}.rail,.inspector{background:rgba(11,13,18,.92);backdrop-filter:blur(18px);padding:22px;border-color:var(--line)}.rail{border-right:1px solid var(--line);display:flex;flex-direction:column}.inspector{border-left:1px solid var(--line)}.brand{display:flex;gap:12px;align-items:center;margin-bottom:28px}.brand strong,.brand span{display:block}.brand span{font-size:12px;color:var(--muted);margin-top:2px}.mark,.hero-mark{display:grid;place-items:center;background:linear-gradient(135deg,var(--accent),#b32dff);box-shadow:0 10px 30px rgba(255,92,53,.22);font-weight:800}.mark{width:38px;height:38px;border-radius:12px;font-size:13px}.hero-mark{width:64px;height:64px;border-radius:20px;margin:auto;font-size:19px}.primary,.send,.ghost,.danger{border:0;border-radius:10px;font-weight:650;cursor:pointer;transition:.18s ease}.primary,.send{background:linear-gradient(135deg,var(--accent),var(--accent2));color:white;padding:11px 14px;box-shadow:0 8px 20px rgba(255,92,53,.17)}.primary:hover,.send:hover{transform:translateY(-1px);filter:brightness(1.05)}.ghost{background:transparent;color:var(--muted);padding:8px 10px;border:1px solid transparent}.ghost:hover,.ghost.active{color:var(--text);background:var(--panel2);border-color:var(--line)}.danger{background:rgba(255,95,109,.12);color:#ff8e98;padding:10px 13px;border:1px solid rgba(255,95,109,.25)}.rail-label,.eyebrow{text-transform:uppercase;letter-spacing:.12em;font-size:10px;color:var(--muted);font-weight:750}.rail-label{margin:26px 0 10px}.session-list{display:flex;flex-direction:column;gap:6px;overflow:auto}.session-item{padding:10px;border-radius:10px;color:var(--muted);cursor:pointer;border:1px solid transparent;font-size:13px}.session-item:hover,.session-item.active{background:var(--panel2);border-color:var(--line);color:var(--text)}.privacy-card{margin-top:auto;display:flex;gap:10px;padding:12px;background:var(--panel);border:1px solid var(--line);border-radius:13px}.privacy-card strong,.privacy-card small{display:block}.privacy-card strong{font-size:12px}.privacy-card small{font-size:10px;color:var(--muted);line-height:1.4;margin-top:3px}.dot{width:8px;height:8px;background:var(--good);border-radius:50%;box-shadow:0 0 0 5px rgba(61,220,151,.1);margin-top:4px}.main{min-width:0;display:flex;flex-direction:column}.topbar{height:82px;border-bottom:1px solid var(--line);padding:15px 24px;display:flex;align-items:center;justify-content:space-between;background:rgba(11,13,18,.55);backdrop-filter:blur(14px)}h1,h2,h3,p{margin:0}.topbar h1{font-size:18px}.topbar p{color:var(--muted);font-size:11px;margin-top:5px}.top-actions{display:flex;gap:4px}.tab{display:none;min-height:0;flex:1}.tab.active{display:flex}.messages{flex:1;overflow:auto;padding:28px max(28px,8vw) 160px}.empty-state{text-align:center;max-width:580px;margin:15vh auto 0}.empty-state h2{font-size:30px;margin:20px 0 10px}.empty-state p{color:var(--muted);line-height:1.7}.message{max-width:780px;margin:0 auto 18px;display:flex;gap:12px}.message .avatar{width:30px;height:30px;border-radius:9px;display:grid;place-items:center;flex:none;background:var(--panel2);font-size:10px;font-weight:800}.message.user .avatar{background:rgba(255,92,53,.18);color:var(--accent2)}.bubble{padding:13px 15px;border:1px solid var(--line);background:var(--panel);border-radius:4px 14px 14px 14px;line-height:1.55;font-size:14px;white-space:pre-wrap;overflow-wrap:anywhere}.message.user .bubble{background:#171b24}.composer{position:absolute;bottom:20px;left:calc(248px + 5vw);right:calc(286px + 5vw);display:flex;gap:10px;padding:10px;border-radius:16px;background:rgba(22,27,37,.96);border:1px solid #30394a;box-shadow:0 20px 60px rgba(0,0,0,.45)}.composer textarea{flex:1;resize:none;background:transparent;border:0;outline:0;color:var(--text);font:inherit;padding:9px;max-height:160px}.send{align-self:flex-end}.approval-card{position:absolute;bottom:112px;left:calc(248px + 8vw);right:calc(286px + 8vw);display:flex;justify-content:space-between;gap:18px;padding:16px;background:#1a1717;border:1px solid rgba(255,139,105,.34);border-radius:14px;box-shadow:0 20px 50px rgba(0,0,0,.42)}.approval-card.hidden{display:none}.approval-card strong{display:block;margin:6px 0}.approval-card pre{margin:0;color:#d7bdaf;max-height:110px;overflow:auto}.approval-actions{display:flex;gap:8px;align-items:flex-end}.metric-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;padding:22px 24px 0;width:100%}.metric-grid article{background:var(--panel);border:1px solid var(--line);border-radius:14px;padding:16px}.metric-grid span,.metric-grid small{display:block;color:var(--muted);font-size:11px}.metric-grid strong{display:block;font-size:25px;margin:8px 0 4px}.profile-layout{display:grid;grid-template-columns:1.4fr .8fr;gap:12px;padding:12px 24px 24px;width:100%;overflow:hidden}.panel{background:var(--panel);border:1px solid var(--line);border-radius:14px;min-height:0;overflow:auto}.panel-head{display:flex;justify-content:space-between;align-items:center;padding:16px 18px;border-bottom:1px solid var(--line)}.panel-head p{font-size:11px;color:var(--muted);margin-top:4px}.findings{padding:10px}.finding{padding:14px;border-radius:11px;margin-bottom:8px;background:var(--panel2);border:1px solid var(--line)}.finding-head{display:flex;align-items:center;gap:8px}.badge{font-size:9px;text-transform:uppercase;letter-spacing:.08em;padding:4px 6px;border-radius:6px;background:#292f3c;color:var(--muted)}.finding.high .badge{background:rgba(255,95,109,.14);color:#ff8e98}.finding.medium .badge{background:rgba(245,185,66,.14);color:#ffd278}.finding p{color:var(--muted);font-size:12px;line-height:1.5;margin-top:9px}.attribution{padding:18px}.bar-row{margin-bottom:16px}.bar-label{display:flex;justify-content:space-between;color:var(--muted);font-size:11px;margin-bottom:7px}.bar{height:8px;background:#222936;border-radius:20px;overflow:hidden}.bar>i{display:block;height:100%;background:linear-gradient(90deg,var(--accent),#a743ff);border-radius:20px}.trace-panel{margin:22px 24px 24px;width:100%}.timeline{padding:16px;overflow:auto}.trace-row{display:grid;grid-template-columns:95px 145px minmax(120px,1fr) 80px;gap:10px;align-items:center;padding:9px 8px;border-bottom:1px solid rgba(37,44,57,.65);font-size:11px}.trace-kind{color:var(--accent2);text-transform:uppercase;letter-spacing:.07em}.trace-name{font-weight:650}.trace-attrs{color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.trace-duration{text-align:right;color:#b8c1cf}.inspector-head{padding-bottom:18px}.inspector-head strong{display:block;font-size:16px;margin-top:5px}.caps{display:flex;flex-wrap:wrap;gap:6px}.cap{padding:5px 7px;border-radius:7px;background:var(--panel2);border:1px solid var(--line);font-size:10px;color:var(--muted)}.cap.on{color:var(--good);border-color:rgba(61,220,151,.25)}.divider{height:1px;background:var(--line);margin:20px 0}.facts{display:grid;grid-template-columns:70px 1fr;gap:10px;font-size:11px}.facts dt{color:var(--muted)}.facts dd{margin:0;text-align:right;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.flow{margin:12px 0 0;padding-left:20px;color:var(--muted);font-size:11px;line-height:2}.flow li::marker{color:var(--accent)}.toast{position:fixed;right:20px;bottom:20px;padding:11px 14px;background:#222834;border:1px solid #394253;border-radius:10px;opacity:0;transform:translateY(8px);pointer-events:none;transition:.2s}.toast.show{opacity:1;transform:none}@media(max-width:1100px){.shell{grid-template-columns:210px 1fr}.inspector{display:none}.composer{left:calc(210px + 4vw);right:4vw}.approval-card{left:calc(210px + 6vw);right:6vw}}@media(max-width:760px){.shell{display:block}.rail{display:none}.main{height:100vh}.top-actions{display:none}.composer{left:14px;right:14px}.approval-card{left:14px;right:14px}.metric-grid{grid-template-columns:1fr 1fr}.profile-layout{grid-template-columns:1fr}.trace-row{grid-template-columns:70px 110px 1fr}.trace-duration{display:none}} \ No newline at end of file From 12dc2f63676b4b9ead3054a9c03e4c9945e2e557 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 12:08:06 +0530 Subject: [PATCH 038/168] radeon forge: wire the interactive UI --- extra/radeon_forge/ui/static/app.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 extra/radeon_forge/ui/static/app.js diff --git a/extra/radeon_forge/ui/static/app.js b/extra/radeon_forge/ui/static/app.js new file mode 100644 index 0000000000000..b812c94592b77 --- /dev/null +++ b/extra/radeon_forge/ui/static/app.js @@ -0,0 +1,23 @@ +const state={session:null,sessions:[],health:null,tab:'console'}; +const $=s=>document.querySelector(s), $$=s=>[...document.querySelectorAll(s)]; +const toast=msg=>{const el=$('#toast');el.textContent=msg;el.classList.add('show');setTimeout(()=>el.classList.remove('show'),2200)}; +async function api(path,opts={}){const res=await fetch(path,{headers:{'Content-Type':'application/json'},...opts});const data=await res.json();if(!res.ok)throw new Error(data.error||`HTTP ${res.status}`);return data} +function fmtMs(v){return v==null?'—':`${Number(v).toFixed(v<10?2:1)} ms`} +function esc(v){return String(v??'').replace(/[&<>"']/g,m=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[m]))} +async function boot(){try{state.health=await api('/api/health');renderHealth();await refreshSessions();if(!state.sessions.length)await createSession();else await selectSession(state.sessions[0].session_id)}catch(e){toast(e.message)}} +function renderHealth(){const h=state.health;$('#runtime-name').textContent=h?.backend||'Offline';$('#backend-status').textContent=h?`${h.backend} · local loopback runtime`:'Backend unavailable';const caps=$('#capabilities');caps.innerHTML='';Object.entries(h?.capabilities||{}).forEach(([k,v])=>{const el=document.createElement('span');el.className=`cap ${v?'on':''}`;el.textContent=k.replaceAll('_',' ');caps.append(el)})} +async function refreshSessions(){state.sessions=await api('/api/sessions');renderSessionList()} +function renderSessionList(){const list=$('#session-list');list.innerHTML='';state.sessions.forEach((s,i)=>{const el=document.createElement('div');el.className=`session-item ${state.session?.session_id===s.session_id?'active':''}`;el.innerHTML=`${i===0?'Primary':'Session'} ${s.session_id.slice(0,6)}
${esc(s.state)}`;el.onclick=()=>selectSession(s.session_id);list.append(el)})} +async function createSession(){const s=await api('/api/sessions',{method:'POST',body:'{}'});await refreshSessions();await selectSession(s.session_id)} +async function selectSession(id){state.session=await api(`/api/sessions/${id}`);renderSession();renderSessionList();if(state.tab==='profile')refreshProfile();if(state.tab==='trace')refreshTrace()} +function renderSession(){const s=state.session;if(!s)return;$('#session-title').textContent=`Agent Console · ${s.session_id.slice(0,6)}`;$('#session-state').textContent=s.state;$('#trace-id').textContent=s.trace_id.slice(0,10);$('#event-count').textContent=s.events.length;renderMessages();renderApproval()} +function renderMessages(){const box=$('#messages');const messages=(state.session?.messages||[]).filter(x=>x.role!=='system'&&x.role!=='tool');if(!messages.length)return;box.innerHTML='';messages.forEach(m=>{const el=document.createElement('div');el.className=`message ${m.role}`;el.innerHTML=`
${m.role==='user'?'YOU':'RF'}
${esc(m.content)}
`;box.append(el)});box.scrollTop=box.scrollHeight} +function renderApproval(){const card=$('#approval-card'),call=state.session?.pending_tool_call;if(!call){card.classList.add('hidden');return}card.classList.remove('hidden');$('#approval-name').textContent=call.name;$('#approval-args').textContent=JSON.stringify(call.arguments,null,2)} +async function sendMessage(text){if(!state.session)return;$('#send').disabled=true;$('#send').textContent='Running…';try{const out=await api(`/api/sessions/${state.session.session_id}/messages`,{method:'POST',body:JSON.stringify({content:text,max_tokens:512})});state.session=out.session;renderSession();await refreshSessions()}finally{$('#send').disabled=false;$('#send').textContent='Run'}} +async function approveTool(){const out=await api(`/api/sessions/${state.session.session_id}/tools/approve`,{method:'POST',body:JSON.stringify({reason:'Approved once from the local UI',max_tokens:512})});state.session=out.session;renderSession();await refreshSessions();toast('Tool executed locally')} +async function rejectTool(){const out=await api(`/api/sessions/${state.session.session_id}/tools/reject`,{method:'POST',body:JSON.stringify({reason:'Rejected in UI'})});state.session=out.session;renderSession();toast('Tool rejected')} +async function refreshProfile(){if(!state.session)return;const p=await api(`/api/sessions/${state.session.session_id}/profile`),s=p.summary||{};$('#p50-token').textContent=fmtMs(s.token_wall_ms_p50);$('#p95-token').textContent=fmtMs(s.token_wall_ms_p95);$('#launches-token').textContent=s.mean_kernel_count_per_token==null?'—':Number(s.mean_kernel_count_per_token).toFixed(1);$('#tool-time').textContent=fmtMs(s.tool_time_ms);const findings=$('#findings');findings.innerHTML='';(p.findings||[]).forEach(f=>{const el=document.createElement('div');el.className=`finding ${f.severity}`;el.innerHTML=`
${esc(f.status)}${esc(f.title)}

${esc((f.evidence||[]).join(' · '))}

Next: ${esc(f.recommendation)}

`;findings.append(el)});if(!p.findings?.length)findings.innerHTML='

No diagnosis yet. Run an agent turn first.

';renderAttribution(s.durations_ms_by_kind||{})} +function renderAttribution(d){const box=$('#attribution');box.innerHTML='';const entries=Object.entries(d).sort((a,b)=>b[1]-a[1]),max=Math.max(1,...entries.map(x=>x[1]));entries.forEach(([k,v])=>{const el=document.createElement('div');el.className='bar-row';el.innerHTML=`
${esc(k)}${fmtMs(v)}
`;box.append(el)});if(!entries.length)box.textContent='No trace data yet.'} +async function refreshTrace(){if(!state.session)return;const t=await api(`/api/sessions/${state.session.session_id}/trace`),box=$('#timeline');box.innerHTML='';(t.events||[]).forEach(e=>{const el=document.createElement('div');el.className='trace-row';const attrs=Object.entries(e.attributes||{}).slice(0,4).map(([k,v])=>`${k}=${v}`).join(' · ');el.innerHTML=`${esc(e.kind)}${esc(e.name)}${esc(attrs)}${fmtMs(e.duration_ms)}`;box.append(el)});if(!t.events?.length)box.innerHTML='

No timeline events yet.

'} +function switchTab(tab){state.tab=tab;$$('.tab').forEach(x=>x.classList.remove('active'));$(`#${tab}-tab`).classList.add('active');$$('[data-tab]').forEach(x=>x.classList.toggle('active',x.dataset.tab===tab));if(tab==='profile')refreshProfile();if(tab==='trace')refreshTrace()} +$('#new-session').onclick=createSession;$('#composer').onsubmit=async e=>{e.preventDefault();const input=$('#prompt'),text=input.value.trim();if(!text)return;input.value='';try{await sendMessage(text)}catch(err){toast(err.message)}};$('#approve-tool').onclick=()=>approveTool().catch(e=>toast(e.message));$('#reject-tool').onclick=()=>rejectTool().catch(e=>toast(e.message));$('#refresh-profile').onclick=()=>refreshProfile().catch(e=>toast(e.message));$('#refresh-trace').onclick=()=>refreshTrace().catch(e=>toast(e.message));$$('[data-tab]').forEach(x=>x.onclick=()=>switchTab(x.dataset.tab));boot(); \ No newline at end of file From d34bd5acc57cc27760d9ee60e063d771357d5408 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 12:08:45 +0530 Subject: [PATCH 039/168] radeon forge: add content-addressed megakernel workspace --- extra/radeon_forge/synthesis/workspace.py | 110 ++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 extra/radeon_forge/synthesis/workspace.py diff --git a/extra/radeon_forge/synthesis/workspace.py b/extra/radeon_forge/synthesis/workspace.py new file mode 100644 index 0000000000000..71bd8473093a5 --- /dev/null +++ b/extra/radeon_forge/synthesis/workspace.py @@ -0,0 +1,110 @@ +from __future__ import annotations + +import hashlib, json, time +from dataclasses import asdict, dataclass, field, replace +from pathlib import Path +from typing import Any, Mapping, Sequence + + +def _hash(payload: Any) -> str: + return hashlib.sha256(json.dumps(payload, sort_keys=True, default=str).encode()).hexdigest() + + +@dataclass(frozen=True) +class KernelSpec: + name: str + operation: str + target: str = "gfx1100" + language: str = "python" + extension: str = ".py" + entrypoint: str = "build_kernel" + shapes: Mapping[str, int | str] = field(default_factory=dict) + dtypes: Mapping[str, str] = field(default_factory=dict) + invariants: tuple[str, ...] = () + objective: str = "minimize_p95_latency" + mockgpu_command: tuple[str, ...] = () + hardware_command: tuple[str, ...] = () + metadata: Mapping[str, Any] = field(default_factory=dict) + + @property + def spec_id(self) -> str: return f"{self.name}-{_hash(asdict(self))[:12]}" + + +@dataclass(frozen=True) +class CandidateRecord: + candidate_id: str + spec_id: str + source_path: str + source_sha256: str + parent_id: str | None + hypothesis: str + status: str + created_at_s: float + evidence: Mapping[str, Any] = field(default_factory=dict) + + +class CandidateWorkspace: + """Content-addressed store where generated implementations are disposable artifacts.""" + def __init__(self, root: str | Path): + self.root = Path(root).resolve() + self.spec_root, self.candidate_root = self.root / "specs", self.root / "candidates" + self.spec_root.mkdir(parents=True, exist_ok=True) + self.candidate_root.mkdir(parents=True, exist_ok=True) + + def save_spec(self, spec: KernelSpec) -> KernelSpec: + path = self.spec_root / f"{spec.spec_id}.json" + path.write_text(json.dumps(asdict(spec), indent=2, default=str), encoding="utf-8") + return spec + + def load_spec(self, spec_id: str) -> KernelSpec: + payload = json.loads((self.spec_root / f"{spec_id}.json").read_text(encoding="utf-8")) + for key in ("invariants", "mockgpu_command", "hardware_command"): payload[key] = tuple(payload.get(key, ())) + return KernelSpec(**payload) + + def specs(self) -> list[KernelSpec]: + ret = [] + for path in sorted(self.spec_root.glob("*.json")): + try: ret.append(self.load_spec(path.stem)) + except Exception: continue + return ret + + def create_candidate(self, spec_id: str, source: str, hypothesis: str, parent_id: str | None = None) -> CandidateRecord: + spec = self.load_spec(spec_id) + if not source.strip(): raise ValueError("candidate source must not be empty") + if len(source.encode()) > 2_000_000: raise ValueError("candidate source exceeds 2 MB") + source_sha = hashlib.sha256(source.encode()).hexdigest() + candidate_id = f"{spec.name}-{source_sha[:12]}" + directory = self.candidate_root / candidate_id + directory.mkdir(parents=True, exist_ok=True) + source_path = directory / f"candidate{spec.extension}" + source_path.write_text(source, encoding="utf-8") + record = CandidateRecord(candidate_id, spec_id, str(source_path), source_sha, parent_id, hypothesis.strip(), "staged", time.time()) + (directory / "manifest.json").write_text(json.dumps(asdict(record), indent=2, default=str), encoding="utf-8") + return record + + def load_candidate(self, candidate_id: str) -> CandidateRecord: + payload = json.loads((self.candidate_root / candidate_id / "manifest.json").read_text(encoding="utf-8")) + return CandidateRecord(**payload) + + def candidates(self, spec_id: str | None = None) -> list[CandidateRecord]: + ret = [] + for path in sorted(self.candidate_root.glob("*/manifest.json")): + try: + record = CandidateRecord(**json.loads(path.read_text(encoding="utf-8"))) + if spec_id is None or record.spec_id == spec_id: ret.append(record) + except Exception: continue + return sorted(ret, key=lambda x: x.created_at_s, reverse=True) + + def update(self, candidate_id: str, status: str, evidence: Mapping[str, Any]) -> CandidateRecord: + record = self.load_candidate(candidate_id) + merged = {**dict(record.evidence), **dict(evidence)} + updated = replace(record, status=status, evidence=merged) + path = self.candidate_root / candidate_id / "manifest.json" + path.write_text(json.dumps(asdict(updated), indent=2, default=str), encoding="utf-8") + return updated + + @staticmethod + def render_command(command: Sequence[str], spec: KernelSpec, candidate: CandidateRecord, root: Path) -> tuple[str, ...]: + replacements = {"{candidate}": candidate.source_path, "{candidate_id}": candidate.candidate_id, + "{spec_id}": spec.spec_id, "{root}": str(root)} + return tuple(replacements.get(part, part) for part in command) From 3a8d4d71823bbd376f71348eaf2cc746c10b9755 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 12:09:07 +0530 Subject: [PATCH 040/168] radeon forge: add agent-facing optimization tools --- extra/radeon_forge/synthesis/tools.py | 59 +++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 extra/radeon_forge/synthesis/tools.py diff --git a/extra/radeon_forge/synthesis/tools.py b/extra/radeon_forge/synthesis/tools.py new file mode 100644 index 0000000000000..2f1d98dc7d2d0 --- /dev/null +++ b/extra/radeon_forge/synthesis/tools.py @@ -0,0 +1,59 @@ +from __future__ import annotations + +import os, subprocess, time +from dataclasses import asdict +from pathlib import Path +from typing import Any, Mapping + +from ..oracles.mockgpu import MockGPUOracle +from ..permissions import Action +from ..runtime.tools import ToolRegistry, ToolSpec +from .workspace import CandidateWorkspace + + +class OptimizationTools: + def __init__(self, workspace: CandidateWorkspace, project_root: str | Path): + self.workspace = workspace + self.project_root = Path(project_root).resolve() + self.mockgpu = MockGPUOracle(self.project_root) + + def list_specs(self, args: Mapping[str, Any]) -> Any: + return {"specs": [asdict(x) | {"spec_id": x.spec_id} for x in self.workspace.specs()]} + + def list_candidates(self, args: Mapping[str, Any]) -> Any: + return {"candidates": [asdict(x) for x in self.workspace.candidates(args.get("spec_id"))]} + + def stage_candidate(self, args: Mapping[str, Any]) -> Any: + record = self.workspace.create_candidate(str(args["spec_id"]), str(args["source"]), str(args["hypothesis"]), args.get("parent_id")) + return asdict(record) + + def validate_mockgpu(self, args: Mapping[str, Any]) -> Any: + candidate = self.workspace.load_candidate(str(args["candidate_id"])) + spec = self.workspace.load_spec(candidate.spec_id) + if not spec.mockgpu_command: raise ValueError("spec has no MockGPU oracle command") + command = self.workspace.render_command(spec.mockgpu_command, spec, candidate, self.project_root) + result = self.mockgpu.run(command, env={"RADEON_FORGE_CANDIDATE": candidate.source_path}) + updated = self.workspace.update(candidate.candidate_id, "mockgpu_passed" if result.passed else "mockgpu_failed", {"mockgpu": result.to_dict()}) + return asdict(updated) + + def benchmark_hardware(self, args: Mapping[str, Any]) -> Any: + candidate = self.workspace.load_candidate(str(args["candidate_id"])) + if candidate.status != "mockgpu_passed" and not bool(args.get("allow_without_mockgpu", False)): + raise ValueError("candidate must pass MockGPU before W7900 benchmarking") + spec = self.workspace.load_spec(candidate.spec_id) + if not spec.hardware_command: raise ValueError("spec has no hardware benchmark command") + command = self.workspace.render_command(spec.hardware_command, spec, candidate, self.project_root) + env = {**os.environ, "DEV": "AMD", "RADEON_FORGE_CANDIDATE": candidate.source_path, "PYTHONUNBUFFERED": "1"} + started = time.perf_counter_ns() + proc = subprocess.run(command, cwd=self.project_root, env=env, text=True, capture_output=True, timeout=int(args.get("timeout_seconds", 900))) + evidence = {"command": command, "returncode": proc.returncode, "elapsed_ms": (time.perf_counter_ns()-started)/1e6, + "stdout": proc.stdout[-50000:], "stderr": proc.stderr[-50000:], "target": "gfx1100"} + updated = self.workspace.update(candidate.candidate_id, "hardware_passed" if proc.returncode == 0 else "hardware_failed", {"hardware": evidence}) + return asdict(updated) + + def install(self, registry: ToolRegistry) -> None: + registry.register(ToolSpec("list_kernel_specs", "List typed megakernel and fused-kernel contracts", {"type":"object","properties":{}}), self.list_specs) + registry.register(ToolSpec("list_kernel_candidates", "List staged generated implementations and their oracle status", {"type":"object","properties":{"spec_id":{"type":"string"}}}), self.list_candidates) + registry.register(ToolSpec("stage_kernel_candidate", "Write one disposable implementation for a typed kernel specification", {"type":"object","required":["spec_id","source","hypothesis"],"properties":{"spec_id":{"type":"string"},"source":{"type":"string"},"hypothesis":{"type":"string"},"parent_id":{"type":"string"}}}, Action.WRITE_GENERATED_SOURCE), self.stage_candidate) + registry.register(ToolSpec("validate_candidate_mockgpu", "Execute a generated AMD candidate through tinygrad's RDNA3 MockGPU semantic oracle", {"type":"object","required":["candidate_id"],"properties":{"candidate_id":{"type":"string"}}}, Action.COMPILE), self.validate_mockgpu) + registry.register(ToolSpec("benchmark_candidate_w7900", "Benchmark a MockGPU-passing candidate on the real local W7900", {"type":"object","required":["candidate_id"],"properties":{"candidate_id":{"type":"string"},"timeout_seconds":{"type":"integer"},"allow_without_mockgpu":{"type":"boolean"}}}, Action.BENCHMARK), self.benchmark_hardware) From 6508d7712ab05735b2776df69900165b6973db21 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 12:09:37 +0530 Subject: [PATCH 041/168] radeon forge: seed typed megakernel contracts --- extra/radeon_forge/synthesis/defaults.py | 35 ++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 extra/radeon_forge/synthesis/defaults.py diff --git a/extra/radeon_forge/synthesis/defaults.py b/extra/radeon_forge/synthesis/defaults.py new file mode 100644 index 0000000000000..41a92beca2aa8 --- /dev/null +++ b/extra/radeon_forge/synthesis/defaults.py @@ -0,0 +1,35 @@ +from __future__ import annotations + +from .workspace import CandidateWorkspace, KernelSpec + + +def install_default_specs(workspace: CandidateWorkspace) -> list[KernelSpec]: + specs = [ + KernelSpec( + name="rdna3-vopd-gemm-schedule", + operation="C = A @ B; batch-one projection/GEMM scheduling research", + target="gfx1100", language="python", extension=".py", entrypoint="build_kernel", + shapes={"M":"multiple of 128", "N":"multiple of 128", "K":"multiple of 128"}, + dtypes={"A":"float32", "B":"float32", "C":"float32"}, + invariants=("Numerically match tinygrad matmul within MSE 1e-6", "No unsupported RDNA3 instructions", + "No out-of-workspace file access", "MockGPU timing is never used as a speed metric"), + objective="minimize median and P95 W7900 latency without correctness loss", + mockgpu_command=("python3", "{candidate}"), hardware_command=("python3", "{candidate}"), + metadata={"seed":"extra/gemm/amd_asm_matmul.py", "role":"infrastructure and schedule-synthesis gate"}, + ), + KernelSpec( + name="batch1-decode-megakernel", + operation="One specialized transformer decode block for batch-one private-agent inference", + target="gfx1100", language="python", extension=".py", entrypoint="build_kernel", + shapes={"batch":1, "sequence":"decode token", "model":"fixed by workload contract"}, + dtypes={"activations":"bf16/fp16", "accumulation":"fp32 where required"}, + invariants=("Match the trusted tinygrad block reference", "Preserve KV-cache semantics", "No spills unless explicitly allowed", + "Generated code is disposable; specification and oracle are authoritative"), + objective="minimize inter-token latency and kernel launches/token", + metadata={"status":"oracle command must be bound after selecting the model", "role":"final technical target"}, + ), + ] + existing = {x.spec_id for x in workspace.specs()} + for spec in specs: + if spec.spec_id not in existing: workspace.save_spec(spec) + return specs From 7ced140e2aa37dd1873671ca95b553deb255cc1f Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 12:09:44 +0530 Subject: [PATCH 042/168] radeon forge: export synthesis API --- extra/radeon_forge/synthesis/__init__.py | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 extra/radeon_forge/synthesis/__init__.py diff --git a/extra/radeon_forge/synthesis/__init__.py b/extra/radeon_forge/synthesis/__init__.py new file mode 100644 index 0000000000000..fd71b0641d61e --- /dev/null +++ b/extra/radeon_forge/synthesis/__init__.py @@ -0,0 +1,5 @@ +from .defaults import install_default_specs +from .tools import OptimizationTools +from .workspace import CandidateRecord, CandidateWorkspace, KernelSpec + +__all__ = ["CandidateRecord", "CandidateWorkspace", "KernelSpec", "OptimizationTools", "install_default_specs"] From 3dc5eea440619819c7b0e343563040585380b9e4 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 12:09:51 +0530 Subject: [PATCH 043/168] radeon forge: export oracle API --- extra/radeon_forge/oracles/__init__.py | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 extra/radeon_forge/oracles/__init__.py diff --git a/extra/radeon_forge/oracles/__init__.py b/extra/radeon_forge/oracles/__init__.py new file mode 100644 index 0000000000000..3cad1d1b42084 --- /dev/null +++ b/extra/radeon_forge/oracles/__init__.py @@ -0,0 +1,2 @@ +from .mockgpu import MockGPUOracle, MockGPUResult +__all__ = ["MockGPUOracle", "MockGPUResult"] From 41c30f1f088e3a67ad58923cd1a579839287f716 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 12:10:08 +0530 Subject: [PATCH 044/168] radeon forge: integrate megakernel workspace and tools --- extra/radeon_forge/runtime/engine.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/extra/radeon_forge/runtime/engine.py b/extra/radeon_forge/runtime/engine.py index e30c53a57edb9..2b1af922c167e 100644 --- a/extra/radeon_forge/runtime/engine.py +++ b/extra/radeon_forge/runtime/engine.py @@ -1,18 +1,20 @@ from __future__ import annotations import threading +from dataclasses import asdict from pathlib import Path from typing import Any from ..permissions import PermissionController from ..profiling.report import build_profile_report +from ..synthesis import CandidateWorkspace, OptimizationTools, install_default_specs from .backend import InferenceBackend from .session import AgentSession from .tools import ToolRegistry, WorkspaceTools DEFAULT_SYSTEM_PROMPT = """You are Radeon Forge, a private local software and inference performance engineer. -Use evidence before making performance claims. Separate observations, inferences and unknowns. Prefer reversible changes and preserve correctness. You may inspect the private workspace and request permission to run local tools.""" +Use evidence before making performance claims. Separate observations, inferences and unknowns. Prefer reversible changes and preserve correctness. You may inspect the private workspace, author disposable target-specific kernel candidates, validate them through tinygrad MockGPU, and request permission for real W7900 benchmarks. Never treat MockGPU timing as performance evidence.""" class ForgeEngine: @@ -21,6 +23,9 @@ def __init__(self, backend: InferenceBackend, workspace: str | Path, system_prom self.permissions = PermissionController() self.tools = ToolRegistry(self.permissions) WorkspaceTools(self.workspace).install(self.tools) + self.optimization_workspace = CandidateWorkspace(self.workspace / ".radeon_forge") + install_default_specs(self.optimization_workspace) + OptimizationTools(self.optimization_workspace, self.workspace).install(self.tools) self._sessions: dict[str, AgentSession] = {} self._lock = threading.RLock() @@ -45,4 +50,8 @@ def grant_for_pending_tool(self, session_id: str, reason: str, max_uses: int = 1 def profile(self, session_id: str) -> dict[str, Any]: return build_profile_report(self.session(session_id).trace.events()) + def optimization_state(self) -> dict[str, Any]: + return {"specs": [asdict(x) | {"spec_id": x.spec_id} for x in self.optimization_workspace.specs()], + "candidates": [asdict(x) for x in self.optimization_workspace.candidates()]} + def close(self) -> None: self.backend.close() From fd2117c5429dcba04420644658a8523f6ab896d9 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 12:10:44 +0530 Subject: [PATCH 045/168] radeon forge: expose optimization lab API --- extra/radeon_forge/ui/server.py | 1 + 1 file changed, 1 insertion(+) diff --git a/extra/radeon_forge/ui/server.py b/extra/radeon_forge/ui/server.py index 55234e127b095..876503ec3f964 100644 --- a/extra/radeon_forge/ui/server.py +++ b/extra/radeon_forge/ui/server.py @@ -55,6 +55,7 @@ def do_GET(self): if path == "/api/health": return self._json({"ok": True, "backend": self.engine.backend.name, "capabilities": asdict(self.engine.backend.capabilities)}) if path == "/api/sessions": return self._json(self.engine.sessions()) + if path == "/api/optimization": return self._json(self.engine.optimization_state()) session, tail = self._session_route() if session is None: return self.send_error(404) if tail == "": return self._json(session.snapshot()) From 954ef7bbf65fe15fe1562817dcc51da007728c82 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 12:11:19 +0530 Subject: [PATCH 046/168] radeon forge: add megakernel optimization lab UI --- extra/radeon_forge/ui/static/index.html | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/extra/radeon_forge/ui/static/index.html b/extra/radeon_forge/ui/static/index.html index b706efb04e8fc..a38e08f4eb529 100644 --- a/extra/radeon_forge/ui/static/index.html +++ b/extra/radeon_forge/ui/static/index.html @@ -20,9 +20,10 @@

Agent Console

Connecting to local backend…

- + +
@@ -56,6 +57,13 @@

Unified execution timeline

Agent → model → token → tool → kernel evidence

+ +
+
+

Typed kernel contracts

Intent and invariants are durable; generated implementations are disposable.

+

Candidate pipeline

Staged → MockGPU → W7900 → accepted / rejected

+
+
From e5c88921968fc26dcd2f34e94708b8116dfb5f56 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:35:28 +0530 Subject: [PATCH 093/168] radeon forge: render and control execution-stage hooks --- extra/radeon_forge/ui/static/app.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/extra/radeon_forge/ui/static/app.js b/extra/radeon_forge/ui/static/app.js index 3ddf28cf123a5..43cf4639c22bc 100644 --- a/extra/radeon_forge/ui/static/app.js +++ b/extra/radeon_forge/ui/static/app.js @@ -1,11 +1,12 @@ const state={session:null,sessions:[],health:null,optimization:null,tab:'console'}; const $=s=>document.querySelector(s), $$=s=>[...document.querySelectorAll(s)]; -const toast=msg=>{const el=$('#toast');el.textContent=msg;el.classList.add('show');setTimeout(()=>el.classList.remove('show'),2600)}; +const toast=msg=>{const el=$('#toast');el.textContent=msg;el.classList.add('show');setTimeout(()=>el.classList.remove('show'),3000)}; async function api(path,opts={}){const res=await fetch(path,{headers:{'Content-Type':'application/json'},...opts});const data=await res.json();if(!res.ok)throw new Error(data.error||`HTTP ${res.status}`);return data} function fmtMs(v){return v==null?'—':`${Number(v).toFixed(v<10?2:1)} ms`} function esc(v){return String(v??'').replace(/[&<>"']/g,m=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[m]))} +function hookWhen(h){const w=h?.when||{};const stages=(w.stages||['any']).map(String).join(', ');const detail=[];if(w.min_context_tokens!=null)detail.push(`ctx≥${w.min_context_tokens}`);if(w.max_context_tokens!=null)detail.push(`ctx≤${w.max_context_tokens}`);if(w.min_generated_token_index!=null)detail.push(`token≥${w.min_generated_token_index}`);if(w.max_generated_token_index!=null)detail.push(`token≤${w.max_generated_token_index}`);if(w.batch_sizes?.length)detail.push(`batch=${w.batch_sizes.join('/')}`);if(w.prefix_cache&&w.prefix_cache!=='any')detail.push(`cache=${w.prefix_cache}`);if(w.warm!=null)detail.push(w.warm?'warm':'cold');Object.entries(w.conditions||{}).forEach(([k,v])=>detail.push(`${k}=${v}`));return {stages,detail:detail.join(' · ')}} async function boot(){try{state.health=await api('/api/health');renderHealth();await refreshSessions();if(!state.sessions.length)await createSession();else await selectSession(state.sessions[0].session_id)}catch(e){toast(e.message)}} -function renderHealth(){const h=state.health;$('#runtime-name').textContent=h?.backend||'Offline';$('#backend-status').textContent=h?`${h.backend} · local loopback runtime`:'Backend unavailable';const caps=$('#capabilities');caps.innerHTML='';Object.entries(h?.capabilities||{}).forEach(([k,v])=>{const el=document.createElement('span');el.className=`cap ${v?'on':''}`;el.textContent=k.replaceAll('_',' ');caps.append(el)})} +function renderHealth(){const h=state.health;$('#runtime-name').textContent=h?.backend||'Offline';const fp=h?.runtime_fingerprint||{};$('#backend-status').textContent=h?`${h.backend} · ${fp.architecture||'unbound architecture'} · local loopback runtime`:'Backend unavailable';$('#runtime-arch').textContent=fp.architecture||'unbound';$('#runtime-model').textContent=fp.model_family||'—';$('#runtime-dtype').textContent=fp.dtype||'—';const caps=$('#capabilities');caps.innerHTML='';Object.entries(h?.capabilities||{}).forEach(([k,v])=>{const el=document.createElement('span');el.className=`cap ${v?'on':''}`;el.textContent=k.replaceAll('_',' ');caps.append(el)})} async function refreshSessions(){state.sessions=await api('/api/sessions');renderSessionList()} function renderSessionList(){const list=$('#session-list');list.innerHTML='';state.sessions.forEach((s,i)=>{const el=document.createElement('div');el.className=`session-item ${state.session?.session_id===s.session_id?'active':''}`;el.innerHTML=`${i===0?'Primary':'Session'} ${s.session_id.slice(0,6)}
${esc(s.state)}`;el.onclick=()=>selectSession(s.session_id);list.append(el)})} async function createSession(){const s=await api('/api/sessions',{method:'POST',body:'{}'});await refreshSessions();await selectSession(s.session_id)} @@ -16,11 +17,15 @@ function renderApproval(){const card=$('#approval-card'),call=state.session?.pen async function sendMessage(text){if(!state.session)return;$('#send').disabled=true;$('#send').textContent='Running…';try{const out=await api(`/api/sessions/${state.session.session_id}/messages`,{method:'POST',body:JSON.stringify({content:text,max_tokens:512})});state.session=out.session;renderSession();await refreshSessions()}finally{$('#send').disabled=false;$('#send').textContent='Run'}} async function approveTool(){const out=await api(`/api/sessions/${state.session.session_id}/tools/approve`,{method:'POST',body:JSON.stringify({reason:'Approved once from the local UI',max_tokens:512})});state.session=out.session;renderSession();await refreshSessions();if(state.tab==='kernels')await refreshKernels();toast('Tool executed locally')} async function rejectTool(){const out=await api(`/api/sessions/${state.session.session_id}/tools/reject`,{method:'POST',body:JSON.stringify({reason:'Rejected in UI'})});state.session=out.session;renderSession();toast('Tool rejected')} -async function refreshProfile(){if(!state.session)return;const p=await api(`/api/sessions/${state.session.session_id}/profile`),s=p.summary||{};$('#p50-token').textContent=fmtMs(s.token_wall_ms_p50);$('#p95-token').textContent=fmtMs(s.token_wall_ms_p95);$('#launches-token').textContent=s.mean_kernel_count_per_token==null?'—':Number(s.mean_kernel_count_per_token).toFixed(1);$('#tool-time').textContent=fmtMs(s.tool_time_ms);const findings=$('#findings');findings.innerHTML='';(p.findings||[]).forEach(f=>{const el=document.createElement('div');el.className=`finding ${f.severity}`;el.innerHTML=`
${esc(f.status)}${esc(f.title)}

${esc((f.evidence||[]).join(' · '))}

Next: ${esc(f.recommendation)}

`;findings.append(el)});if(!p.findings?.length)findings.innerHTML='

No diagnosis yet. Run an agent turn first.

';renderAttribution(s.durations_ms_by_kind||{})} +async function refreshProfile(){if(!state.session)return;const p=await api(`/api/sessions/${state.session.session_id}/profile`),s=p.summary||{},stages=s.token_latency_by_stage||{};$('#p50-token').textContent=fmtMs(s.token_wall_ms_p50);$('#p95-token').textContent=fmtMs(s.token_wall_ms_p95);$('#first-token').textContent=fmtMs(stages.first_token?.p50_ms);$('#steady-decode').textContent=fmtMs(stages.decode?.p50_ms);$('#launches-token').textContent=s.mean_kernel_count_per_token==null?'—':Number(s.mean_kernel_count_per_token).toFixed(1);$('#hook-transitions').textContent=`${s.hook_transitions||0}${s.hook_rollbacks?` / ${s.hook_rollbacks} rollback`:''}`;const findings=$('#findings');findings.innerHTML='';(p.findings||[]).forEach(f=>{const el=document.createElement('div');el.className=`finding ${f.severity}`;el.innerHTML=`
${esc(f.status)}${esc(f.title)}

${esc((f.evidence||[]).join(' · '))}

Next: ${esc(f.recommendation)}

`;findings.append(el)});if(!p.findings?.length)findings.innerHTML='

No diagnosis yet. Run an agent turn first.

';renderAttribution(s.durations_ms_by_kind||{});renderStageKernels(s.top_kernels_by_stage||{})} function renderAttribution(d){const box=$('#attribution');box.innerHTML='';const entries=Object.entries(d).sort((a,b)=>b[1]-a[1]),max=Math.max(1,...entries.map(x=>x[1]));entries.forEach(([k,v])=>{const el=document.createElement('div');el.className='bar-row';el.innerHTML=`
${esc(k)}${fmtMs(v)}
`;box.append(el)});if(!entries.length)box.textContent='No trace data yet.'} -async function refreshTrace(){if(!state.session)return;const t=await api(`/api/sessions/${state.session.session_id}/trace`),box=$('#timeline');box.innerHTML='';(t.events||[]).forEach(e=>{const el=document.createElement('div');el.className='trace-row';const attrs=Object.entries(e.attributes||{}).slice(0,4).map(([k,v])=>`${k}=${v}`).join(' · ');el.innerHTML=`${esc(e.kind)}${esc(e.name)}${esc(attrs)}${fmtMs(e.duration_ms)}`;box.append(el)});if(!t.events?.length)box.innerHTML='

No timeline events yet.

'} -async function exportCandidate(candidate){const output=`exports/${candidate.candidate_id}.forge.toml`;const out=await api('/api/recipes/export',{method:'POST',body:JSON.stringify({spec_id:candidate.spec_id,candidate_id:candidate.candidate_id,output})});toast(`Exported ${out.path}`);await refreshKernels()} -async function importRecipe(){const input=$('#recipe-path'),path=input.value.trim();if(!path)return;const out=await api('/api/recipes/import',{method:'POST',body:JSON.stringify({path})});input.value='';toast(`Installed ${out.recipe_id}`);await refreshKernels()} -async function refreshKernels(){state.optimization=await api('/api/optimization');const specs=$('#kernel-specs'),candidates=$('#kernel-candidates'),recipes=$('#recipe-list');specs.innerHTML='';candidates.innerHTML='';recipes.innerHTML='';(state.optimization.specs||[]).forEach(s=>{const el=document.createElement('div');el.className='kernel-card';el.innerHTML=`

${esc(s.name)}

${esc(s.operation)}

${esc(s.target)}${esc(s.language)}${esc(s.objective)}
${esc(JSON.stringify({shapes:s.shapes,dtypes:s.dtypes,invariants:s.invariants,agent_brief:s.metadata?.recipe_agent_brief},null,2))}
`;specs.append(el)});(state.optimization.candidates||[]).forEach(c=>{const el=document.createElement('div');el.className='kernel-card';el.innerHTML=`

${esc(c.candidate_id)}

${esc(c.hypothesis||'No hypothesis recorded')}

${esc(c.status)}${esc(c.spec_id)}
${esc(JSON.stringify(c.evidence||{},null,2))}
`;el.querySelector('.export-candidate').onclick=()=>exportCandidate(c).catch(e=>toast(e.message));candidates.append(el)});(state.optimization.recipes||[]).forEach(r=>{const el=document.createElement('div');el.className='kernel-card';el.innerHTML=`

${esc(r.recipe_id)}

Installed portable contract. Cached code remains unverified until local oracles pass.

${esc(r.spec_id)}${r.seed_candidate_id?'seed cache':''}
${esc(JSON.stringify({bundle:r.bundle_root,artifacts:r.artifact_hashes},null,2))}
`;recipes.append(el)});if(!state.optimization.candidates?.length)candidates.innerHTML='

No candidate has been staged. Ask Forge to list kernel specs and generate one.

';if(!state.optimization.recipes?.length)recipes.innerHTML='

No portable recipe installed yet. Import a .forge.toml file from the workspace.

'} +function renderStageKernels(byStage){const box=$('#stage-kernels');box.innerHTML='';Object.entries(byStage).sort().forEach(([stage,rows])=>{const top=rows?.[0];if(!top)return;const el=document.createElement('div');el.className='kernel-card compact-card';el.innerHTML=`
${esc(stage)}${esc(top.name)}

${top.calls} calls · ${fmtMs(top.total_ms)} · ${(100*top.share).toFixed(1)}% of captured stage time

`;box.append(el)});if(!box.children.length)box.innerHTML='

No stage-level kernel evidence yet.

'} +async function refreshTrace(){if(!state.session)return;const t=await api(`/api/sessions/${state.session.session_id}/trace`),box=$('#timeline');box.innerHTML='';(t.events||[]).forEach(e=>{const el=document.createElement('div');el.className=`trace-row ${e.name==='hook'?'hook-row':''}`;const attrs=Object.entries(e.attributes||{}).slice(0,5).map(([k,v])=>`${k}=${Array.isArray(v)?v.join(','):v}`).join(' · ');el.innerHTML=`${esc(e.kind)}${esc(e.name)}${esc(attrs)}${fmtMs(e.duration_ms)}`;box.append(el)});if(!t.events?.length)box.innerHTML='

No timeline events yet.

'} +async function exportCandidate(candidate){const output=`exports/${candidate.candidate_id}.forge.toml`;const out=await api('/api/recipes/export',{method:'POST',body:JSON.stringify({spec_id:candidate.spec_id,candidate_id:candidate.candidate_id,output,reason:'Export shareable stage-aware optimization recipe'})});toast(`Exported ${out.path}`);await refreshKernels()} +async function importRecipe(){const input=$('#recipe-path'),path=input.value.trim();if(!path)return;const out=await api('/api/recipes/import',{method:'POST',body:JSON.stringify({path,reason:'Import local portable optimization recipe'})});input.value='';toast(`Installed ${out.recipe_id}`);await refreshKernels()} +async function activateCandidate(candidate){const reason=`Activate ${candidate.candidate_id} only in its validated execution states`;const out=await api('/api/hooks/activate',{method:'POST',body:JSON.stringify({candidate_id:candidate.candidate_id,reason})});toast(`Activated ${out.activation_id.slice(0,8)}`);await refreshKernels()} +async function rollbackHook(hook){const out=await api('/api/hooks/deactivate',{method:'POST',body:JSON.stringify({activation_id:hook.activation_id,reason:`Rollback ${hook.candidate_id} from local UI`})});toast(`Rolled back ${out.candidate_id}`);await refreshKernels()} +function renderActiveHooks(hooks){const box=$('#active-hooks');box.innerHTML='';(hooks||[]).forEach(h=>{const w=hookWhen(h.descriptor),el=document.createElement('div');el.className='kernel-card active-hook';el.innerHTML=`
active${esc(h.descriptor.layer)} · ${esc(h.descriptor.target)}

${esc(h.candidate_id)}

${esc(w.stages)}${esc(h.descriptor.mode)}priority ${h.descriptor.priority||0}
${w.detail?`

${esc(w.detail)}

`:''}
${esc(JSON.stringify({selector:h.descriptor.selector,compatibility:h.compatibility},null,2))}
`;el.querySelector('.rollback-hook').onclick=()=>rollbackHook(h).catch(e=>toast(e.message));box.append(el)});if(!box.children.length)box.innerHTML='

No optimization is active. The trusted tinygrad baseline owns every execution stage.

'} +async function refreshKernels(){state.optimization=await api('/api/optimization');const specs=$('#kernel-specs'),candidates=$('#kernel-candidates'),recipes=$('#recipe-list');specs.innerHTML='';candidates.innerHTML='';recipes.innerHTML='';renderActiveHooks(state.optimization.active_hooks||[]);const activeIds=new Set((state.optimization.active_hooks||[]).map(x=>x.candidate_id));(state.optimization.specs||[]).forEach(s=>{const h=s.metadata?.hook||{},w=hookWhen(h),el=document.createElement('div');el.className='kernel-card';el.innerHTML=`

${esc(s.name)}

${esc(s.operation)}

${esc(s.target)}${esc(h.layer||'kernel')}${esc(w.stages)}${esc(s.objective)}
${w.detail?`

${esc(w.detail)}

`:''}
${esc(JSON.stringify({where:{layer:h.layer,target:h.target,mode:h.mode,selector:h.selector},when:h.when,shapes:s.shapes,dtypes:s.dtypes,invariants:s.invariants,agent_brief:s.metadata?.recipe_agent_brief},null,2))}
`;specs.append(el)});(state.optimization.candidates||[]).forEach(c=>{const spec=(state.optimization.specs||[]).find(s=>s.spec_id===c.spec_id),h=spec?.metadata?.hook||{},w=hookWhen(h),eligible=['hardware_passed','heldout_passed','accepted'].includes(c.status),el=document.createElement('div');el.className='kernel-card';el.innerHTML=`

${esc(c.candidate_id)}

${esc(c.hypothesis||'No hypothesis recorded')}

${esc(c.status)}${esc(h.layer||'kernel')}${esc(w.stages)}
${esc(JSON.stringify(c.evidence||{},null,2))}
${eligible&&!activeIds.has(c.candidate_id)?'':''}${activeIds.has(c.candidate_id)?'currently active':''}
`;el.querySelector('.export-candidate').onclick=()=>exportCandidate(c).catch(e=>toast(e.message));const activate=el.querySelector('.activate-candidate');if(activate)activate.onclick=()=>activateCandidate(c).catch(e=>toast(e.message));candidates.append(el)});(state.optimization.recipes||[]).forEach(r=>{const el=document.createElement('div');el.className='kernel-card';el.innerHTML=`

${esc(r.recipe_id)}

Installed portable contract. Placement and execution-state predicates are preserved; cached code remains unverified until local oracles pass.

${esc(r.spec_id)}${r.seed_candidate_id?'seed cache':''}
${esc(JSON.stringify({bundle:r.bundle_root,artifacts:r.artifact_hashes},null,2))}
`;recipes.append(el)});if(!state.optimization.candidates?.length)candidates.innerHTML='

No candidate has been staged. Ask Forge to list contracts and generate one for a specific execution state.

';if(!state.optimization.recipes?.length)recipes.innerHTML='

No portable recipe installed yet. Import a .forge.toml file from the workspace.

'} function switchTab(tab){state.tab=tab;$$('.tab').forEach(x=>x.classList.remove('active'));$(`#${tab}-tab`).classList.add('active');$$('[data-tab]').forEach(x=>x.classList.toggle('active',x.dataset.tab===tab));if(tab==='profile')refreshProfile();if(tab==='trace')refreshTrace();if(tab==='kernels')refreshKernels()} $('#new-session').onclick=createSession;$('#composer').onsubmit=async e=>{e.preventDefault();const input=$('#prompt'),text=input.value.trim();if(!text)return;input.value='';try{await sendMessage(text)}catch(err){toast(err.message)}};$('#approve-tool').onclick=()=>approveTool().catch(e=>toast(e.message));$('#reject-tool').onclick=()=>rejectTool().catch(e=>toast(e.message));$('#refresh-profile').onclick=()=>refreshProfile().catch(e=>toast(e.message));$('#refresh-trace').onclick=()=>refreshTrace().catch(e=>toast(e.message));$('#refresh-kernels').onclick=()=>refreshKernels().catch(e=>toast(e.message));$('#import-recipe').onclick=()=>importRecipe().catch(e=>toast(e.message));$$('[data-tab]').forEach(x=>x.onclick=()=>switchTab(x.dataset.tab));boot(); From 3f177c459cb410c7fe77bfa9fd7cf0717b304ba1 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:35:52 +0530 Subject: [PATCH 094/168] radeon forge: style stage-aware optimization controls --- extra/radeon_forge/ui/static/kernels.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extra/radeon_forge/ui/static/kernels.css b/extra/radeon_forge/ui/static/kernels.css index 1b65cbfb42930..ab5f7cb4a8090 100644 --- a/extra/radeon_forge/ui/static/kernels.css +++ b/extra/radeon_forge/ui/static/kernels.css @@ -1 +1 @@ -.kernel-layout{display:grid;grid-template-columns:1fr 1fr;gap:12px;padding:22px 24px 24px;width:100%;min-height:0;overflow:auto}.kernel-list{padding:12px;overflow:auto}.kernel-card{background:var(--panel2);border:1px solid var(--line);border-radius:12px;padding:14px;margin-bottom:10px}.kernel-card h4{margin:0 0 6px;font-size:14px}.kernel-card p{color:var(--muted);font-size:11px;line-height:1.5}.kernel-meta{display:flex;flex-wrap:wrap;gap:6px;margin-top:10px}.status{padding:4px 7px;border-radius:7px;background:#262d3a;border:1px solid var(--line);font-size:9px;text-transform:uppercase;letter-spacing:.07em}.status.mockgpu_passed,.status.hardware_passed{color:var(--good);border-color:rgba(61,220,151,.28);background:rgba(61,220,151,.08)}.status.mockgpu_failed,.status.hardware_failed{color:#ff8e98;border-color:rgba(255,95,109,.28);background:rgba(255,95,109,.08)}.status.imported_unverified{color:#ffd278;border-color:rgba(245,185,66,.28);background:rgba(245,185,66,.08)}.contract{margin-top:10px;padding:9px;background:#0f1219;border:1px solid var(--line);border-radius:9px;font:10px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace;color:#aeb8c8;white-space:pre-wrap;max-height:150px;overflow:auto}.recipe-panel{grid-column:1/-1}.recipe-import{display:flex;gap:10px;padding:14px 14px 0}.recipe-import input{flex:1;background:#0f1219;border:1px solid var(--line);border-radius:10px;color:var(--text);padding:10px 12px;outline:0}.recipe-import input:focus{border-color:rgba(255,92,53,.65)}.kernel-actions{display:flex;gap:8px;margin-top:10px}.kernel-actions button{font-size:10px}@media(max-width:900px){.kernel-layout{grid-template-columns:1fr}.recipe-panel{grid-column:auto}} +.kernel-layout{display:grid;grid-template-columns:1fr 1fr;gap:12px;padding:22px 24px 24px;width:100%;min-height:0;overflow:auto}.active-hooks-panel,.recipe-panel{grid-column:1/-1}.kernel-list{padding:12px;overflow:auto}.kernel-list.compact{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:8px}.kernel-card{background:var(--panel2);border:1px solid var(--line);border-radius:12px;padding:14px;margin-bottom:10px}.kernel-card h4{margin:0 0 6px;font-size:14px}.kernel-card p{color:var(--muted);font-size:11px;line-height:1.5}.kernel-card.active-hook{border-color:rgba(61,220,151,.32);box-shadow:inset 3px 0 0 rgba(61,220,151,.65)}.compact-card{margin:0}.stage-line{display:flex;align-items:center;gap:8px;margin-bottom:7px}.stage-line strong{font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kernel-meta{display:flex;flex-wrap:wrap;gap:6px;margin-top:10px}.status{padding:4px 7px;border-radius:7px;background:#262d3a;border:1px solid var(--line);font-size:9px;text-transform:uppercase;letter-spacing:.07em}.status.stage{color:#cbb7ff;border-color:rgba(167,67,255,.3);background:rgba(167,67,255,.1)}.status.active{color:var(--good);border-color:rgba(61,220,151,.3);background:rgba(61,220,151,.1)}.status.mockgpu_passed,.status.hardware_passed,.status.heldout_passed,.status.accepted{color:var(--good);border-color:rgba(61,220,151,.28);background:rgba(61,220,151,.08)}.status.mockgpu_failed,.status.hardware_failed,.status.heldout_failed{color:#ff8e98;border-color:rgba(255,95,109,.28);background:rgba(255,95,109,.08)}.status.imported_unverified{color:#ffd278;border-color:rgba(245,185,66,.28);background:rgba(245,185,66,.08)}.contract{margin-top:10px;padding:9px;background:#0f1219;border:1px solid var(--line);border-radius:9px;font:10px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace;color:#aeb8c8;white-space:pre-wrap;max-height:170px;overflow:auto}.recipe-import{display:flex;gap:10px;padding:14px 14px 0}.recipe-import input{flex:1;background:#0f1219;border:1px solid var(--line);border-radius:10px;color:var(--text);padding:10px 12px;outline:0}.recipe-import input:focus{border-color:rgba(255,92,53,.65)}.kernel-actions{display:flex;align-items:center;flex-wrap:wrap;gap:8px;margin-top:10px}.kernel-actions button{font-size:10px}.hook-row{background:rgba(167,67,255,.06)}.stage-panel{grid-column:1/-1}@media(max-width:900px){.kernel-layout{grid-template-columns:1fr}.active-hooks-panel,.recipe-panel{grid-column:auto}} From fc9655cc62c0d135feb6539cb3f97140ae062760 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:36:20 +0530 Subject: [PATCH 095/168] radeon forge: fit stage profiler and hook panels --- extra/radeon_forge/ui/static/kernels.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extra/radeon_forge/ui/static/kernels.css b/extra/radeon_forge/ui/static/kernels.css index ab5f7cb4a8090..e7bba3e605a2c 100644 --- a/extra/radeon_forge/ui/static/kernels.css +++ b/extra/radeon_forge/ui/static/kernels.css @@ -1 +1 @@ -.kernel-layout{display:grid;grid-template-columns:1fr 1fr;gap:12px;padding:22px 24px 24px;width:100%;min-height:0;overflow:auto}.active-hooks-panel,.recipe-panel{grid-column:1/-1}.kernel-list{padding:12px;overflow:auto}.kernel-list.compact{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:8px}.kernel-card{background:var(--panel2);border:1px solid var(--line);border-radius:12px;padding:14px;margin-bottom:10px}.kernel-card h4{margin:0 0 6px;font-size:14px}.kernel-card p{color:var(--muted);font-size:11px;line-height:1.5}.kernel-card.active-hook{border-color:rgba(61,220,151,.32);box-shadow:inset 3px 0 0 rgba(61,220,151,.65)}.compact-card{margin:0}.stage-line{display:flex;align-items:center;gap:8px;margin-bottom:7px}.stage-line strong{font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kernel-meta{display:flex;flex-wrap:wrap;gap:6px;margin-top:10px}.status{padding:4px 7px;border-radius:7px;background:#262d3a;border:1px solid var(--line);font-size:9px;text-transform:uppercase;letter-spacing:.07em}.status.stage{color:#cbb7ff;border-color:rgba(167,67,255,.3);background:rgba(167,67,255,.1)}.status.active{color:var(--good);border-color:rgba(61,220,151,.3);background:rgba(61,220,151,.1)}.status.mockgpu_passed,.status.hardware_passed,.status.heldout_passed,.status.accepted{color:var(--good);border-color:rgba(61,220,151,.28);background:rgba(61,220,151,.08)}.status.mockgpu_failed,.status.hardware_failed,.status.heldout_failed{color:#ff8e98;border-color:rgba(255,95,109,.28);background:rgba(255,95,109,.08)}.status.imported_unverified{color:#ffd278;border-color:rgba(245,185,66,.28);background:rgba(245,185,66,.08)}.contract{margin-top:10px;padding:9px;background:#0f1219;border:1px solid var(--line);border-radius:9px;font:10px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace;color:#aeb8c8;white-space:pre-wrap;max-height:170px;overflow:auto}.recipe-import{display:flex;gap:10px;padding:14px 14px 0}.recipe-import input{flex:1;background:#0f1219;border:1px solid var(--line);border-radius:10px;color:var(--text);padding:10px 12px;outline:0}.recipe-import input:focus{border-color:rgba(255,92,53,.65)}.kernel-actions{display:flex;align-items:center;flex-wrap:wrap;gap:8px;margin-top:10px}.kernel-actions button{font-size:10px}.hook-row{background:rgba(167,67,255,.06)}.stage-panel{grid-column:1/-1}@media(max-width:900px){.kernel-layout{grid-template-columns:1fr}.active-hooks-panel,.recipe-panel{grid-column:auto}} +.metric-grid{grid-template-columns:repeat(3,minmax(0,1fr))}.profile-layout{overflow:auto}.kernel-layout{display:grid;grid-template-columns:1fr 1fr;gap:12px;padding:22px 24px 24px;width:100%;min-height:0;overflow:auto}.active-hooks-panel,.recipe-panel{grid-column:1/-1}.kernel-list{padding:12px;overflow:auto}.kernel-list.compact{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:8px}.kernel-card{background:var(--panel2);border:1px solid var(--line);border-radius:12px;padding:14px;margin-bottom:10px}.kernel-card h4{margin:0 0 6px;font-size:14px}.kernel-card p{color:var(--muted);font-size:11px;line-height:1.5}.kernel-card.active-hook{border-color:rgba(61,220,151,.32);box-shadow:inset 3px 0 0 rgba(61,220,151,.65)}.compact-card{margin:0}.stage-line{display:flex;align-items:center;gap:8px;margin-bottom:7px}.stage-line strong{font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kernel-meta{display:flex;flex-wrap:wrap;gap:6px;margin-top:10px}.status{padding:4px 7px;border-radius:7px;background:#262d3a;border:1px solid var(--line);font-size:9px;text-transform:uppercase;letter-spacing:.07em}.status.stage{color:#cbb7ff;border-color:rgba(167,67,255,.3);background:rgba(167,67,255,.1)}.status.active{color:var(--good);border-color:rgba(61,220,151,.3);background:rgba(61,220,151,.1)}.status.mockgpu_passed,.status.hardware_passed,.status.heldout_passed,.status.accepted{color:var(--good);border-color:rgba(61,220,151,.28);background:rgba(61,220,151,.08)}.status.mockgpu_failed,.status.hardware_failed,.status.heldout_failed{color:#ff8e98;border-color:rgba(255,95,109,.28);background:rgba(255,95,109,.08)}.status.imported_unverified{color:#ffd278;border-color:rgba(245,185,66,.28);background:rgba(245,185,66,.08)}.contract{margin-top:10px;padding:9px;background:#0f1219;border:1px solid var(--line);border-radius:9px;font:10px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace;color:#aeb8c8;white-space:pre-wrap;max-height:170px;overflow:auto}.recipe-import{display:flex;gap:10px;padding:14px 14px 0}.recipe-import input{flex:1;background:#0f1219;border:1px solid var(--line);border-radius:10px;color:var(--text);padding:10px 12px;outline:0}.recipe-import input:focus{border-color:rgba(255,92,53,.65)}.kernel-actions{display:flex;align-items:center;flex-wrap:wrap;gap:8px;margin-top:10px}.kernel-actions button{font-size:10px}.hook-row{background:rgba(167,67,255,.06)}.stage-panel{grid-column:1/-1}@media(max-width:900px){.metric-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.kernel-layout{grid-template-columns:1fr}.active-hooks-panel,.recipe-panel{grid-column:auto}} From f3b0973f16b12245ddedb76832b39f8e1d5eacf4 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:37:01 +0530 Subject: [PATCH 096/168] radeon forge: test stage-aware kernel profiling --- test/test_radeon_forge_runtime.py | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/test/test_radeon_forge_runtime.py b/test/test_radeon_forge_runtime.py index 38a31c37d70c8..6db089af44cbf 100644 --- a/test/test_radeon_forge_runtime.py +++ b/test/test_radeon_forge_runtime.py @@ -19,34 +19,47 @@ def name(self): return "kernel-evidence-local" @property def capabilities(self): return BackendCapabilities(prefix_cache=True, persistent_kv=True, kernel_metrics=True) + @property + def runtime_metadata(self): return {"runtime":"test", "architecture":"gfx1100", "model_family":"llama"} + def stream(self, request): yield GenerationEvent("prefill", metrics={"wall_ms": 8.0, "gpu_ms": 5.0, "prompt_tokens": 24, "prefix_reused_tokens": 12, "profile_kernel_events": 1}) yield GenerationEvent("kernel", metrics={"name": "rmsnorm_fused", "duration_ms": 0.40, "stage": "prefill", "device": "AMD"}) - yield GenerationEvent("token", "ok", metrics={"index": 0, "wall_ms": 2.0, "gpu_ms": 1.4, + yield GenerationEvent("token", "first", metrics={"index": 0, "stage": "first_token", "wall_ms": 4.0, "gpu_ms": 2.5, + "kernel_count": 24, "profile_kernel_events": 1}) + yield GenerationEvent("kernel", metrics={"name": "first_token_projection", "duration_ms": 0.80, + "stage": "first_token", "device": "AMD"}) + yield GenerationEvent("token", "ok", metrics={"index": 1, "stage": "decode", "wall_ms": 2.0, "gpu_ms": 1.4, "kernel_count": 24, "profile_kernel_events": 2}) yield GenerationEvent("kernel", metrics={"name": "decode_gemv", "duration_ms": 0.90, "stage": "decode", "device": "AMD"}) yield GenerationEvent("kernel", metrics={"name": "decode_gemv", "duration_ms": 0.70, "stage": "decode", "device": "AMD"}) - yield GenerationEvent("done", finish_reason="stop", metrics={"generated_tokens": 1}) + yield GenerationEvent("done", finish_reason="stop", metrics={"generated_tokens": 2}) def close(self): pass class TestRadeonForgeRuntime(unittest.TestCase): - def test_kernel_evidence_survives_unified_trace_and_is_ranked(self): + def test_kernel_evidence_survives_unified_trace_and_is_ranked_by_stage(self): with tempfile.TemporaryDirectory() as directory: engine = ForgeEngine(KernelEvidenceBackend(), directory) session = engine.create_session() - session.send("profile one token") + session.send("profile two execution states") report = build_profile_report(session.trace.events()) top = report["summary"]["top_kernels"] self.assertEqual(top[0]["name"], "decode_gemv") self.assertEqual(top[0]["calls"], 2) self.assertAlmostEqual(top[0]["total_ms"], 1.6) + self.assertEqual(report["summary"]["top_kernels_by_stage"]["prefill"][0]["name"], "rmsnorm_fused") + self.assertEqual(report["summary"]["top_kernels_by_stage"]["first_token"][0]["name"], "first_token_projection") + self.assertEqual(report["summary"]["top_kernels_by_stage"]["decode"][0]["name"], "decode_gemv") self.assertTrue(report["summary"]["kernel_profile_complete"]) - self.assertTrue(any(x["title"] == "One kernel family dominates captured GPU time" for x in report["findings"])) - kernel_events = [event for event in session.trace.events() if event.name == "kernel"] - self.assertEqual([event.attributes["name"] for event in kernel_events], ["rmsnorm_fused", "decode_gemv", "decode_gemv"]) + titles = {x["title"] for x in report["findings"]} + self.assertIn("One kernel family dominates captured GPU time overall", titles) + self.assertIn("Different execution stages have different dominant kernels", titles) + self.assertIn("First-token and steady-decode latency are materially different", titles) + kernel_events = [event for event in session.trace.events() if event.kind == "kernel"] + self.assertEqual([event.name for event in kernel_events], ["rmsnorm_fused", "first_token_projection", "decode_gemv", "decode_gemv"]) engine.close() def test_permissioned_tool_round_trip(self): @@ -61,6 +74,7 @@ def test_permissioned_tool_round_trip(self): session.send("read hello.txt") self.assertEqual(session.state, SessionState.AWAITING_TOOL_APPROVAL) with self.assertRaises(PermissionDenied): session.approve_tool("not-a-real-grant") + self.assertEqual(session.state, SessionState.AWAITING_TOOL_APPROVAL) token = engine.grant_for_pending_tool(session.session_id, "approve one local read") session.approve_tool(token) self.assertEqual(session.state, SessionState.COMPLETED) From 2921d945ba9397ca0f49a5df8a0968a0b03e1575 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:37:52 +0530 Subject: [PATCH 097/168] radeon forge: test portable execution-stage hook round trip --- test/test_radeon_forge_recipe.py | 51 ++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/test/test_radeon_forge_recipe.py b/test/test_radeon_forge_recipe.py index a8798f5167b57..00ac261ce9816 100644 --- a/test/test_radeon_forge_recipe.py +++ b/test/test_radeon_forge_recipe.py @@ -3,7 +3,8 @@ import unittest from pathlib import Path -from extra.radeon_forge.synthesis import CandidateWorkspace, ForgeRecipe, KernelSpec, RecipeLibrary, export_recipe +from extra.radeon_forge.synthesis import (CandidateWorkspace, ForgeRecipe, HookDescriptor, KernelSpec, RecipeLibrary, + export_recipe, export_recipe_with_hook) class TestForgeRecipe(unittest.TestCase): @@ -21,11 +22,24 @@ def _recipe_text(self, seed: str = "print('seed')\n") -> str: seed_artifact = "seed.py" [compatibility] -arch = "gfx1100" +architecture = "gfx1100" [acceptance] maximum_error = 0.000001 +[metadata.hook] +layer = "kernel" +target = "projection_gemm" +mode = "replace" +adapter = "request_metadata" +exclusive_group = "projection" + +[metadata.hook.when] +stages = ["decode"] +batch_sizes = [1] +min_context_tokens = 1024 +prefix_cache = "hit" + [oracle] mockgpu_command = ["python3", "{{candidate}}", "--bundle", "{{bundle}}"] hardware_command = ["python3", "{{candidate}}", "--benchmark"] @@ -60,6 +74,11 @@ def test_install_is_content_addressed_and_seed_is_unverified(self): self.assertEqual(Path(candidate.source_path).read_text(encoding="utf-8"), "print('seed')\n") spec = workspace.load_spec(installed.spec_id) + descriptor = HookDescriptor.from_spec(spec) + self.assertEqual(descriptor.layer.value, "kernel") + self.assertEqual([x.value for x in descriptor.when.stages], ["decode"]) + self.assertEqual(descriptor.when.min_context_tokens, 1024) + self.assertEqual(descriptor.when.prefix_cache, "hit") rendered = workspace.render_command(spec.mockgpu_command, spec, candidate, root) self.assertEqual(rendered[1], candidate.source_path) self.assertEqual(rendered[3], installed.bundle_root) @@ -71,7 +90,7 @@ def test_rejects_path_traversal(self): path.write_text(text, encoding="utf-8") with self.assertRaises(ValueError): ForgeRecipe.load(path) - def test_export_import_round_trip_preserves_contract_and_cache(self): + def test_export_import_round_trip_preserves_contract_cache_and_stage_hook(self): with tempfile.TemporaryDirectory() as directory: root = Path(directory) source_workspace = CandidateWorkspace(root / "source") @@ -80,20 +99,40 @@ def test_export_import_round_trip_preserves_contract_and_cache(self): invariants=("match reference", "no remote API"), objective="minimize p95 token latency", mockgpu_command=("python3", "{candidate}"), hardware_command=("python3", "{candidate}", "--benchmark"), metadata={"recipe_agent_brief":"Use the oracle as the contract and freely rewrite the implementation.", - "recipe_compatibility":{"arch":"gfx1100"}, "recipe_acceptance":{"max_error":1e-6}}, + "recipe_compatibility":{"architecture":"gfx1100"}, "recipe_acceptance":{"max_error":1e-6}, + "hook":{"layer":"transformer_block", "target":"llama.decode.block", "mode":"replace", + "adapter":"python_transformer_block", "selector":{"indices":[0,1]}, + "when":{"stages":["decode"], "batch_sizes":[1], "min_generated_token_index":1, + "conditions":{"resume_after_tool":False}}, + "exclusive_group":"decode-block"}}, ) source_workspace.save_spec(spec) - candidate = source_workspace.create_candidate(spec.spec_id, "print('candidate')\n", "measured VOPD schedule") - exported = export_recipe(source_workspace, spec.spec_id, root / "shared.forge.toml", candidate.candidate_id) + candidate = source_workspace.create_candidate(spec.spec_id, "print('candidate')\n", "measured decode implementation") + exported = export_recipe_with_hook(source_workspace, spec.spec_id, root / "shared.forge.toml", candidate.candidate_id) loaded = ForgeRecipe.load(exported) self.assertEqual(loaded.target, "gfx1100") self.assertEqual(loaded.invariants, spec.invariants) + self.assertEqual(loaded.metadata["hook"]["when"]["stages"], ["decode"]) destination = CandidateWorkspace(root / "destination") installed = RecipeLibrary(destination).install(loaded) imported = destination.load_candidate(installed.seed_candidate_id) self.assertEqual(Path(imported.source_path).read_text(encoding="utf-8"), "print('candidate')\n") self.assertEqual(imported.status, "imported_unverified") + installed_spec = destination.load_spec(installed.spec_id) + descriptor = HookDescriptor.from_spec(installed_spec) + self.assertEqual(descriptor.target, "llama.decode.block") + self.assertEqual([x.value for x in descriptor.when.stages], ["decode"]) + self.assertEqual(descriptor.when.min_generated_token_index, 1) + self.assertEqual(descriptor.when.conditions["resume_after_tool"], False) + + def test_base_export_remains_valid_without_hook_extension(self): + with tempfile.TemporaryDirectory() as directory: + root = Path(directory) + workspace = CandidateWorkspace(root / "source") + spec = workspace.save_spec(KernelSpec("plain", "plain operation", invariants=("correct",))) + path = export_recipe(workspace, spec.spec_id, root / "plain.forge.toml") + self.assertEqual(ForgeRecipe.load(path).name, "plain") if __name__ == "__main__": unittest.main() From 338808734a104054253562cddfdbf113711894c7 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:39:13 +0530 Subject: [PATCH 098/168] radeon forge: add asynchronous local agent jobs --- extra/radeon_forge/runtime/jobs.py | 57 ++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 extra/radeon_forge/runtime/jobs.py diff --git a/extra/radeon_forge/runtime/jobs.py b/extra/radeon_forge/runtime/jobs.py new file mode 100644 index 0000000000000..6a76f9409a32b --- /dev/null +++ b/extra/radeon_forge/runtime/jobs.py @@ -0,0 +1,57 @@ +from __future__ import annotations + +import threading, time, uuid +from dataclasses import asdict, dataclass +from typing import Any, Callable + + +@dataclass(frozen=True) +class JobSnapshot: + job_id: str + kind: str + state: str + session_id: str + created_at_s: float + started_at_s: float | None = None + completed_at_s: float | None = None + error: str | None = None + error_type: str | None = None + + +class LocalJobManager: + """Small in-process job manager for non-blocking local UI operations.""" + def __init__(self): + self._jobs: dict[str, JobSnapshot] = {} + self._lock = threading.RLock() + + def _set(self, snapshot: JobSnapshot) -> None: + with self._lock: self._jobs[snapshot.job_id] = snapshot + + def submit(self, kind: str, session_id: str, fn: Callable[[], Any]) -> JobSnapshot: + job_id, created = uuid.uuid4().hex, time.time() + initial = JobSnapshot(job_id, kind, "queued", session_id, created) + self._set(initial) + + def run(): + started = time.time() + self._set(JobSnapshot(job_id, kind, "running", session_id, created, started_at_s=started)) + try: + fn() + except BaseException as exc: + self._set(JobSnapshot(job_id, kind, "failed", session_id, created, started, time.time(), str(exc), type(exc).__name__)) + else: + self._set(JobSnapshot(job_id, kind, "completed", session_id, created, started, time.time())) + + threading.Thread(target=run, name=f"radeon-forge-{kind}-{job_id[:8]}", daemon=True).start() + return initial + + def snapshot(self, job_id: str) -> JobSnapshot: + with self._lock: + try: return self._jobs[job_id] + except KeyError as exc: raise KeyError(f"unknown job {job_id}") from exc + + def snapshots(self, session_id: str | None = None) -> list[dict[str, Any]]: + with self._lock: + jobs = list(self._jobs.values()) + if session_id is not None: jobs = [job for job in jobs if job.session_id == session_id] + return [asdict(job) for job in sorted(jobs, key=lambda x: x.created_at_s, reverse=True)] From 35072323f5caf6cb1bece513e050c7c0f32ec0f1 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:40:10 +0530 Subject: [PATCH 099/168] radeon forge: expose live partial generation and incremental events --- extra/radeon_forge/runtime/session.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/extra/radeon_forge/runtime/session.py b/extra/radeon_forge/runtime/session.py index a4cf0b4227d26..6a1ffcbeb8d63 100644 --- a/extra/radeon_forge/runtime/session.py +++ b/extra/radeon_forge/runtime/session.py @@ -39,6 +39,7 @@ def __init__(self, backend: InferenceBackend, tools: ToolRegistry, system_prompt self.messages: list[dict[str, Any]] = [{"role": "system", "content": self._system_prompt()}] self.events: list[SessionEvent] = [] self.pending_tool_call: ToolCall | None = None + self.partial_output = "" self.state = SessionState.IDLE self._sequence = 0 self._lock = threading.RLock() @@ -94,6 +95,7 @@ def _request_metadata(self, step: int) -> dict[str, Any]: def _generate(self, max_tokens: int, temperature: float) -> tuple[SessionEvent, ...]: self.state = SessionState.GENERATING + self.partial_output = "" started_at = len(self.events) pieces: list[str] = [] step = sum(1 for event in self.events if event.kind == "generation_started") + 1 @@ -106,6 +108,7 @@ def _generate(self, max_tokens: int, temperature: float) -> tuple[SessionEvent, for event in self.backend.stream(request): if event.kind == "token": pieces.append(event.text) + self.partial_output += event.text self._emit("token", text=event.text, metrics=dict(event.metrics)) token_metrics = dict(event.metrics) if "name" in token_metrics: token_metrics["token_name"] = token_metrics.pop("name") @@ -124,10 +127,12 @@ def _generate(self, max_tokens: int, temperature: float) -> tuple[SessionEvent, try: self.pending_tool_call = parse_tool_call(output) except Exception as exc: self._emit("tool_parse_error", error=str(exc), raw=output) if self.pending_tool_call is not None: + self.partial_output = "" self.state = SessionState.AWAITING_TOOL_APPROVAL self._emit("tool_approval_required", call=asdict(self.pending_tool_call), action=self.tools.spec(self.pending_tool_call.name).action.value) else: self.messages.append({"role": "assistant", "content": output}) + self.partial_output = "" self.state = SessionState.COMPLETED self._emit("message", role="assistant", content=output) return tuple(self.events[started_at:]) @@ -167,7 +172,10 @@ def reject_tool(self, reason: str) -> SessionEvent: self.state = SessionState.IDLE return self._emit("tool_rejected", call_id=call.call_id, reason=reason) + def events_after(self, sequence: int) -> list[dict[str, Any]]: + return [asdict(event) for event in self.events if event.sequence > sequence] + def snapshot(self) -> dict[str, Any]: return {"session_id": self.session_id, "state": self.state.value, "messages": list(self.messages), "events": [asdict(x) for x in self.events], "pending_tool_call": asdict(self.pending_tool_call) if self.pending_tool_call else None, - "trace_id": self.trace.trace_id} + "partial_output": self.partial_output, "trace_id": self.trace.trace_id} From e881ae0d9b3bb048bab513c00bfe943983bfd109 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:40:37 +0530 Subject: [PATCH 100/168] radeon forge: run local agent turns asynchronously --- extra/radeon_forge/runtime/engine.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/extra/radeon_forge/runtime/engine.py b/extra/radeon_forge/runtime/engine.py index 928fea6d9b1b5..c7bd043258ea0 100644 --- a/extra/radeon_forge/runtime/engine.py +++ b/extra/radeon_forge/runtime/engine.py @@ -9,6 +9,7 @@ from ..profiling.report import build_profile_report from ..synthesis import CandidateWorkspace, HookRegistry, OptimizationTools, RuntimeFingerprint, install_default_specs from .backend import InferenceBackend +from .jobs import LocalJobManager from .session import AgentSession from .tools import ToolCall, ToolRegistry, WorkspaceTools @@ -30,6 +31,7 @@ def __init__(self, backend: InferenceBackend, workspace: str | Path, system_prom self.hooks = HookRegistry(self.optimization_workspace) self.optimization_tools = OptimizationTools(self.optimization_workspace, self.workspace, self.hooks, self.runtime_fingerprint) self.optimization_tools.install(self.tools) + self.jobs = LocalJobManager() self._sessions: dict[str, AgentSession] = {} self._lock = threading.RLock() @@ -54,6 +56,17 @@ def session(self, session_id: str) -> AgentSession: def sessions(self) -> list[dict[str, Any]]: with self._lock: return [{"session_id": x.session_id, "state": x.state.value, "trace_id": x.trace.trace_id} for x in self._sessions.values()] + def submit_message(self, session_id: str, content: str, max_tokens: int = 512, temperature: float = 0.0) -> dict[str, Any]: + session = self.session(session_id) + job = self.jobs.submit("agent_turn", session_id, lambda: session.send(content, max_tokens, temperature)) + return asdict(job) + + def submit_tool_approval(self, session_id: str, reason: str, max_tokens: int = 512) -> dict[str, Any]: + session = self.session(session_id) + token = self.grant_for_pending_tool(session_id, reason) + job = self.jobs.submit("tool_and_resume", session_id, lambda: session.approve_tool(token, max_tokens)) + return asdict(job) + def grant_for_pending_tool(self, session_id: str, reason: str, max_uses: int = 1) -> str: session = self.session(session_id) if session.pending_tool_call is None: raise RuntimeError("session has no pending tool") From 6e0ac05cd9b89c0021e4949528b219bf40a39134 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:40:51 +0530 Subject: [PATCH 101/168] radeon forge: export asynchronous job runtime --- extra/radeon_forge/runtime/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/extra/radeon_forge/runtime/__init__.py b/extra/radeon_forge/runtime/__init__.py index edb8c53326723..682b5b0e81570 100644 --- a/extra/radeon_forge/runtime/__init__.py +++ b/extra/radeon_forge/runtime/__init__.py @@ -1,9 +1,10 @@ from .backend import BackendCapabilities, GenerationEvent, GenerationRequest, InferenceBackend, JsonlProcessBackend from .engine import ForgeEngine from .events import TraceEvent, TraceRecorder +from .jobs import JobSnapshot, LocalJobManager from .session import AgentSession, SessionState from .tools import ToolCall, ToolRegistry, ToolResult, ToolSpec, WorkspaceTools __all__ = ["AgentSession", "BackendCapabilities", "ForgeEngine", "GenerationEvent", "GenerationRequest", "InferenceBackend", - "JsonlProcessBackend", "SessionState", "ToolCall", "ToolRegistry", "ToolResult", "ToolSpec", "TraceEvent", - "TraceRecorder", "WorkspaceTools"] + "JobSnapshot", "JsonlProcessBackend", "LocalJobManager", "SessionState", "ToolCall", "ToolRegistry", "ToolResult", + "ToolSpec", "TraceEvent", "TraceRecorder", "WorkspaceTools"] From e4799d49b8c987adaed861511cb4229082125603 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:41:32 +0530 Subject: [PATCH 102/168] radeon forge: stream local agent state through asynchronous UI jobs --- extra/radeon_forge/ui/server.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/extra/radeon_forge/ui/server.py b/extra/radeon_forge/ui/server.py index 3e16329a0d6c7..e197a1ac89cec 100644 --- a/extra/radeon_forge/ui/server.py +++ b/extra/radeon_forge/ui/server.py @@ -6,7 +6,7 @@ from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer from pathlib import Path from typing import Any -from urllib.parse import urlparse +from urllib.parse import parse_qs, urlparse from ..backends.fake import ScriptedBackend from ..runtime import ForgeEngine, JsonlProcessBackend @@ -48,7 +48,7 @@ def _session_route(self): return self.engine.session(match.group(1)), match.group(2) or "" def do_GET(self): - path = urlparse(self.path).path + parsed, path = urlparse(self.path), urlparse(self.path).path try: if path == "/": return self._static("index.html") if path in {"/app.js", "/style.css", "/kernels.css"}: return self._static(path[1:]) @@ -56,9 +56,14 @@ def do_GET(self): "capabilities": asdict(self.engine.backend.capabilities), "runtime_fingerprint": asdict(self.engine.runtime_fingerprint())}) if path == "/api/sessions": return self._json(self.engine.sessions()) if path == "/api/optimization": return self._json(self.engine.optimization_state()) + job_match = re.fullmatch(r"/api/jobs/([a-f0-9]+)", path) + if job_match: return self._json(asdict(self.engine.jobs.snapshot(job_match.group(1)))) session, tail = self._session_route() if session is None: return self.send_error(404) if tail == "": return self._json(session.snapshot()) + if tail == "events": + after = int(parse_qs(parsed.query).get("after", ["0"])[0]) + return self._json({"events": session.events_after(after), "session": session.snapshot()}) if tail == "profile": return self._json(self.engine.profile(session.session_id)) if tail == "trace": return self._json(session.trace.to_dict()) if tail == "trace/chrome": return self._json(session.trace.chrome_trace()) @@ -96,11 +101,19 @@ def do_POST(self): if tail == "messages": events = session.send(str(body.get("content", "")), int(body.get("max_tokens", 512)), float(body.get("temperature", 0.0))) return self._json({"events": [asdict(x) for x in events], "session": session.snapshot()}) + if tail == "messages/async": + job = self.engine.submit_message(session.session_id, str(body.get("content", "")), int(body.get("max_tokens", 512)), + float(body.get("temperature", 0.0))) + return self._json({"job": job, "session": session.snapshot()}, HTTPStatus.ACCEPTED) if tail == "tools/approve": reason = str(body.get("reason", "Approved from Radeon Forge UI")) token = self.engine.grant_for_pending_tool(session.session_id, reason) events = session.approve_tool(token, int(body.get("max_tokens", 512))) return self._json({"events": [asdict(x) for x in events], "session": session.snapshot()}) + if tail == "tools/approve/async": + job = self.engine.submit_tool_approval(session.session_id, str(body.get("reason", "Approved from Radeon Forge UI")), + int(body.get("max_tokens", 512))) + return self._json({"job": job, "session": session.snapshot()}, HTTPStatus.ACCEPTED) if tail == "tools/reject": event = session.reject_tool(str(body.get("reason", "Rejected by user"))) return self._json({"event": asdict(event), "session": session.snapshot()}) From 3cc38f05ccb879d466f270570ee5812b5dcfaeb3 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:42:40 +0530 Subject: [PATCH 103/168] radeon forge: stream local agent turns in the UI --- extra/radeon_forge/ui/static/app.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/extra/radeon_forge/ui/static/app.js b/extra/radeon_forge/ui/static/app.js index 43cf4639c22bc..166fcf6feb609 100644 --- a/extra/radeon_forge/ui/static/app.js +++ b/extra/radeon_forge/ui/static/app.js @@ -1,5 +1,5 @@ -const state={session:null,sessions:[],health:null,optimization:null,tab:'console'}; -const $=s=>document.querySelector(s), $$=s=>[...document.querySelectorAll(s)]; +const state={session:null,sessions:[],health:null,optimization:null,tab:'console',activeJob:null}; +const $=s=>document.querySelector(s), $$=s=>[...document.querySelectorAll(s)], sleep=ms=>new Promise(r=>setTimeout(r,ms)); const toast=msg=>{const el=$('#toast');el.textContent=msg;el.classList.add('show');setTimeout(()=>el.classList.remove('show'),3000)}; async function api(path,opts={}){const res=await fetch(path,{headers:{'Content-Type':'application/json'},...opts});const data=await res.json();if(!res.ok)throw new Error(data.error||`HTTP ${res.status}`);return data} function fmtMs(v){return v==null?'—':`${Number(v).toFixed(v<10?2:1)} ms`} @@ -10,12 +10,14 @@ function renderHealth(){const h=state.health;$('#runtime-name').textContent=h?.b async function refreshSessions(){state.sessions=await api('/api/sessions');renderSessionList()} function renderSessionList(){const list=$('#session-list');list.innerHTML='';state.sessions.forEach((s,i)=>{const el=document.createElement('div');el.className=`session-item ${state.session?.session_id===s.session_id?'active':''}`;el.innerHTML=`${i===0?'Primary':'Session'} ${s.session_id.slice(0,6)}
${esc(s.state)}`;el.onclick=()=>selectSession(s.session_id);list.append(el)})} async function createSession(){const s=await api('/api/sessions',{method:'POST',body:'{}'});await refreshSessions();await selectSession(s.session_id)} -async function selectSession(id){state.session=await api(`/api/sessions/${id}`);renderSession();renderSessionList();if(state.tab==='profile')refreshProfile();if(state.tab==='trace')refreshTrace()} +async function selectSession(id){if(state.activeJob)throw new Error('A local turn is still running');state.session=await api(`/api/sessions/${id}`);renderSession();renderSessionList();if(state.tab==='profile')refreshProfile();if(state.tab==='trace')refreshTrace()} function renderSession(){const s=state.session;if(!s)return;$('#session-title').textContent=`Agent Console · ${s.session_id.slice(0,6)}`;$('#session-state').textContent=s.state;$('#trace-id').textContent=s.trace_id.slice(0,10);$('#event-count').textContent=s.events.length;renderMessages();renderApproval()} -function renderMessages(){const box=$('#messages');const messages=(state.session?.messages||[]).filter(x=>x.role!=='system'&&x.role!=='tool');if(!messages.length)return;box.innerHTML='';messages.forEach(m=>{const el=document.createElement('div');el.className=`message ${m.role}`;el.innerHTML=`
${m.role==='user'?'YOU':'RF'}
${esc(m.content)}
`;box.append(el)});box.scrollTop=box.scrollHeight} +function renderMessages(){const box=$('#messages');const messages=(state.session?.messages||[]).filter(x=>x.role!=='system'&&x.role!=='tool');if(!messages.length&&!state.session?.partial_output)return;box.innerHTML='';messages.forEach(m=>{const el=document.createElement('div');el.className=`message ${m.role}`;el.innerHTML=`
${m.role==='user'?'YOU':'RF'}
${esc(m.content)}
`;box.append(el)});if(state.session?.partial_output){const el=document.createElement('div');el.className='message assistant streaming';el.innerHTML=`
RF
${esc(state.session.partial_output)}
`;box.append(el)}box.scrollTop=box.scrollHeight} function renderApproval(){const card=$('#approval-card'),call=state.session?.pending_tool_call;if(!call){card.classList.add('hidden');return}card.classList.remove('hidden');$('#approval-name').textContent=call.name;$('#approval-args').textContent=JSON.stringify(call.arguments,null,2)} -async function sendMessage(text){if(!state.session)return;$('#send').disabled=true;$('#send').textContent='Running…';try{const out=await api(`/api/sessions/${state.session.session_id}/messages`,{method:'POST',body:JSON.stringify({content:text,max_tokens:512})});state.session=out.session;renderSession();await refreshSessions()}finally{$('#send').disabled=false;$('#send').textContent='Run'}} -async function approveTool(){const out=await api(`/api/sessions/${state.session.session_id}/tools/approve`,{method:'POST',body:JSON.stringify({reason:'Approved once from the local UI',max_tokens:512})});state.session=out.session;renderSession();await refreshSessions();if(state.tab==='kernels')await refreshKernels();toast('Tool executed locally')} +function maxSequence(){return Math.max(0,...(state.session?.events||[]).map(x=>Number(x.sequence)||0))} +async function watchJob(jobId){state.activeJob=jobId;let after=maxSequence(),profileTick=0;try{while(true){const [job,delta]=await Promise.all([api(`/api/jobs/${jobId}`),api(`/api/sessions/${state.session.session_id}/events?after=${after}`)]);state.session=delta.session;(delta.events||[]).forEach(e=>after=Math.max(after,Number(e.sequence)||0));renderSession();if(++profileTick%5===0&&state.tab==='trace')refreshTrace().catch(()=>{});if(job.state==='completed')break;if(job.state==='failed')throw new Error(`${job.error_type||'JobError'}: ${job.error||'local job failed'}`);await sleep(100)}}finally{state.activeJob=null;await refreshSessions();state.session=await api(`/api/sessions/${state.session.session_id}`);renderSession();if(state.tab==='profile')await refreshProfile();if(state.tab==='trace')await refreshTrace();if(state.tab==='kernels')await refreshKernels()}} +async function sendMessage(text){if(!state.session||state.activeJob)return;$('#send').disabled=true;$('#send').textContent='Running…';try{const out=await api(`/api/sessions/${state.session.session_id}/messages/async`,{method:'POST',body:JSON.stringify({content:text,max_tokens:512})});state.session=out.session;renderSession();await watchJob(out.job.job_id)}finally{$('#send').disabled=false;$('#send').textContent='Run'}} +async function approveTool(){if(state.activeJob)return;$('#approve-tool').disabled=true;try{const out=await api(`/api/sessions/${state.session.session_id}/tools/approve/async`,{method:'POST',body:JSON.stringify({reason:'Approved once from the local UI',max_tokens:512})});state.session=out.session;renderSession();await watchJob(out.job.job_id);toast('Tool executed locally')}finally{$('#approve-tool').disabled=false}} async function rejectTool(){const out=await api(`/api/sessions/${state.session.session_id}/tools/reject`,{method:'POST',body:JSON.stringify({reason:'Rejected in UI'})});state.session=out.session;renderSession();toast('Tool rejected')} async function refreshProfile(){if(!state.session)return;const p=await api(`/api/sessions/${state.session.session_id}/profile`),s=p.summary||{},stages=s.token_latency_by_stage||{};$('#p50-token').textContent=fmtMs(s.token_wall_ms_p50);$('#p95-token').textContent=fmtMs(s.token_wall_ms_p95);$('#first-token').textContent=fmtMs(stages.first_token?.p50_ms);$('#steady-decode').textContent=fmtMs(stages.decode?.p50_ms);$('#launches-token').textContent=s.mean_kernel_count_per_token==null?'—':Number(s.mean_kernel_count_per_token).toFixed(1);$('#hook-transitions').textContent=`${s.hook_transitions||0}${s.hook_rollbacks?` / ${s.hook_rollbacks} rollback`:''}`;const findings=$('#findings');findings.innerHTML='';(p.findings||[]).forEach(f=>{const el=document.createElement('div');el.className=`finding ${f.severity}`;el.innerHTML=`
${esc(f.status)}${esc(f.title)}

${esc((f.evidence||[]).join(' · '))}

Next: ${esc(f.recommendation)}

`;findings.append(el)});if(!p.findings?.length)findings.innerHTML='

No diagnosis yet. Run an agent turn first.

';renderAttribution(s.durations_ms_by_kind||{});renderStageKernels(s.top_kernels_by_stage||{})} function renderAttribution(d){const box=$('#attribution');box.innerHTML='';const entries=Object.entries(d).sort((a,b)=>b[1]-a[1]),max=Math.max(1,...entries.map(x=>x[1]));entries.forEach(([k,v])=>{const el=document.createElement('div');el.className='bar-row';el.innerHTML=`
${esc(k)}${fmtMs(v)}
`;box.append(el)});if(!entries.length)box.textContent='No trace data yet.'} @@ -28,4 +30,4 @@ async function rollbackHook(hook){const out=await api('/api/hooks/deactivate',{m function renderActiveHooks(hooks){const box=$('#active-hooks');box.innerHTML='';(hooks||[]).forEach(h=>{const w=hookWhen(h.descriptor),el=document.createElement('div');el.className='kernel-card active-hook';el.innerHTML=`
active${esc(h.descriptor.layer)} · ${esc(h.descriptor.target)}

${esc(h.candidate_id)}

${esc(w.stages)}${esc(h.descriptor.mode)}priority ${h.descriptor.priority||0}
${w.detail?`

${esc(w.detail)}

`:''}
${esc(JSON.stringify({selector:h.descriptor.selector,compatibility:h.compatibility},null,2))}
`;el.querySelector('.rollback-hook').onclick=()=>rollbackHook(h).catch(e=>toast(e.message));box.append(el)});if(!box.children.length)box.innerHTML='

No optimization is active. The trusted tinygrad baseline owns every execution stage.

'} async function refreshKernels(){state.optimization=await api('/api/optimization');const specs=$('#kernel-specs'),candidates=$('#kernel-candidates'),recipes=$('#recipe-list');specs.innerHTML='';candidates.innerHTML='';recipes.innerHTML='';renderActiveHooks(state.optimization.active_hooks||[]);const activeIds=new Set((state.optimization.active_hooks||[]).map(x=>x.candidate_id));(state.optimization.specs||[]).forEach(s=>{const h=s.metadata?.hook||{},w=hookWhen(h),el=document.createElement('div');el.className='kernel-card';el.innerHTML=`

${esc(s.name)}

${esc(s.operation)}

${esc(s.target)}${esc(h.layer||'kernel')}${esc(w.stages)}${esc(s.objective)}
${w.detail?`

${esc(w.detail)}

`:''}
${esc(JSON.stringify({where:{layer:h.layer,target:h.target,mode:h.mode,selector:h.selector},when:h.when,shapes:s.shapes,dtypes:s.dtypes,invariants:s.invariants,agent_brief:s.metadata?.recipe_agent_brief},null,2))}
`;specs.append(el)});(state.optimization.candidates||[]).forEach(c=>{const spec=(state.optimization.specs||[]).find(s=>s.spec_id===c.spec_id),h=spec?.metadata?.hook||{},w=hookWhen(h),eligible=['hardware_passed','heldout_passed','accepted'].includes(c.status),el=document.createElement('div');el.className='kernel-card';el.innerHTML=`

${esc(c.candidate_id)}

${esc(c.hypothesis||'No hypothesis recorded')}

${esc(c.status)}${esc(h.layer||'kernel')}${esc(w.stages)}
${esc(JSON.stringify(c.evidence||{},null,2))}
${eligible&&!activeIds.has(c.candidate_id)?'':''}${activeIds.has(c.candidate_id)?'currently active':''}
`;el.querySelector('.export-candidate').onclick=()=>exportCandidate(c).catch(e=>toast(e.message));const activate=el.querySelector('.activate-candidate');if(activate)activate.onclick=()=>activateCandidate(c).catch(e=>toast(e.message));candidates.append(el)});(state.optimization.recipes||[]).forEach(r=>{const el=document.createElement('div');el.className='kernel-card';el.innerHTML=`

${esc(r.recipe_id)}

Installed portable contract. Placement and execution-state predicates are preserved; cached code remains unverified until local oracles pass.

${esc(r.spec_id)}${r.seed_candidate_id?'seed cache':''}
${esc(JSON.stringify({bundle:r.bundle_root,artifacts:r.artifact_hashes},null,2))}
`;recipes.append(el)});if(!state.optimization.candidates?.length)candidates.innerHTML='

No candidate has been staged. Ask Forge to list contracts and generate one for a specific execution state.

';if(!state.optimization.recipes?.length)recipes.innerHTML='

No portable recipe installed yet. Import a .forge.toml file from the workspace.

'} function switchTab(tab){state.tab=tab;$$('.tab').forEach(x=>x.classList.remove('active'));$(`#${tab}-tab`).classList.add('active');$$('[data-tab]').forEach(x=>x.classList.toggle('active',x.dataset.tab===tab));if(tab==='profile')refreshProfile();if(tab==='trace')refreshTrace();if(tab==='kernels')refreshKernels()} -$('#new-session').onclick=createSession;$('#composer').onsubmit=async e=>{e.preventDefault();const input=$('#prompt'),text=input.value.trim();if(!text)return;input.value='';try{await sendMessage(text)}catch(err){toast(err.message)}};$('#approve-tool').onclick=()=>approveTool().catch(e=>toast(e.message));$('#reject-tool').onclick=()=>rejectTool().catch(e=>toast(e.message));$('#refresh-profile').onclick=()=>refreshProfile().catch(e=>toast(e.message));$('#refresh-trace').onclick=()=>refreshTrace().catch(e=>toast(e.message));$('#refresh-kernels').onclick=()=>refreshKernels().catch(e=>toast(e.message));$('#import-recipe').onclick=()=>importRecipe().catch(e=>toast(e.message));$$('[data-tab]').forEach(x=>x.onclick=()=>switchTab(x.dataset.tab));boot(); +$('#new-session').onclick=()=>createSession().catch(e=>toast(e.message));$('#composer').onsubmit=async e=>{e.preventDefault();const input=$('#prompt'),text=input.value.trim();if(!text)return;input.value='';try{await sendMessage(text)}catch(err){toast(err.message)}};$('#approve-tool').onclick=()=>approveTool().catch(e=>toast(e.message));$('#reject-tool').onclick=()=>rejectTool().catch(e=>toast(e.message));$('#refresh-profile').onclick=()=>refreshProfile().catch(e=>toast(e.message));$('#refresh-trace').onclick=()=>refreshTrace().catch(e=>toast(e.message));$('#refresh-kernels').onclick=()=>refreshKernels().catch(e=>toast(e.message));$('#import-recipe').onclick=()=>importRecipe().catch(e=>toast(e.message));$$('[data-tab]').forEach(x=>x.onclick=()=>switchTab(x.dataset.tab));boot(); From 609fb3c4ff93c113a6b6051f91c449572dfd07f5 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:43:25 +0530 Subject: [PATCH 104/168] radeon forge: test live asynchronous agent generation --- test/test_radeon_forge_runtime.py | 44 ++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/test/test_radeon_forge_runtime.py b/test/test_radeon_forge_runtime.py index 6db089af44cbf..03ca8b87fa48c 100644 --- a/test/test_radeon_forge_runtime.py +++ b/test/test_radeon_forge_runtime.py @@ -1,4 +1,6 @@ import tempfile +import threading +import time import unittest from pathlib import Path @@ -15,13 +17,10 @@ class KernelEvidenceBackend: @property def name(self): return "kernel-evidence-local" - @property def capabilities(self): return BackendCapabilities(prefix_cache=True, persistent_kv=True, kernel_metrics=True) - @property def runtime_metadata(self): return {"runtime":"test", "architecture":"gfx1100", "model_family":"llama"} - def stream(self, request): yield GenerationEvent("prefill", metrics={"wall_ms": 8.0, "gpu_ms": 5.0, "prompt_tokens": 24, "prefix_reused_tokens": 12, "profile_kernel_events": 1}) @@ -35,10 +34,27 @@ def stream(self, request): yield GenerationEvent("kernel", metrics={"name": "decode_gemv", "duration_ms": 0.90, "stage": "decode", "device": "AMD"}) yield GenerationEvent("kernel", metrics={"name": "decode_gemv", "duration_ms": 0.70, "stage": "decode", "device": "AMD"}) yield GenerationEvent("done", finish_reason="stop", metrics={"generated_tokens": 2}) - def close(self): pass +class BlockingBackend: + def __init__(self): self.first_token = threading.Event(); self.release = threading.Event() + @property + def name(self): return "blocking-local" + @property + def capabilities(self): return BackendCapabilities(streaming=True) + @property + def runtime_metadata(self): return {"runtime":"test", "architecture":"gfx1100", "model_family":"llama"} + def stream(self, request): + yield GenerationEvent("prefill", metrics={"wall_ms": 1.0, "prompt_tokens": 8, "prefix_reused_tokens": 0}) + yield GenerationEvent("token", "partial ", metrics={"index":0, "stage":"first_token", "wall_ms":1.0}) + self.first_token.set() + if not self.release.wait(2): raise TimeoutError("test did not release backend") + yield GenerationEvent("token", "complete", metrics={"index":1, "stage":"decode", "wall_ms":1.0}) + yield GenerationEvent("done", finish_reason="stop", metrics={"generated_tokens":2}) + def close(self): self.release.set() + + class TestRadeonForgeRuntime(unittest.TestCase): def test_kernel_evidence_survives_unified_trace_and_is_ranked_by_stage(self): with tempfile.TemporaryDirectory() as directory: @@ -62,6 +78,26 @@ def test_kernel_evidence_survives_unified_trace_and_is_ranked_by_stage(self): self.assertEqual([event.name for event in kernel_events], ["rmsnorm_fused", "first_token_projection", "decode_gemv", "decode_gemv"]) engine.close() + def test_async_job_exposes_partial_generation(self): + with tempfile.TemporaryDirectory() as directory: + backend = BlockingBackend() + engine = ForgeEngine(backend, directory) + session = engine.create_session() + job = engine.submit_message(session.session_id, "stream locally") + self.assertTrue(backend.first_token.wait(1)) + self.assertEqual(engine.jobs.snapshot(job["job_id"]).state, "running") + self.assertEqual(session.state, SessionState.GENERATING) + self.assertEqual(session.partial_output, "partial ") + self.assertTrue(any(event["kind"] == "token" for event in session.events_after(0))) + backend.release.set() + deadline = time.time() + 2 + while engine.jobs.snapshot(job["job_id"]).state not in {"completed", "failed"} and time.time() < deadline: time.sleep(0.01) + self.assertEqual(engine.jobs.snapshot(job["job_id"]).state, "completed") + self.assertEqual(session.state, SessionState.COMPLETED) + self.assertEqual(session.partial_output, "") + self.assertEqual(session.messages[-1]["content"], "partial complete") + engine.close() + def test_permissioned_tool_round_trip(self): responses = [ '{"name":"read_file","arguments":{"path":"hello.txt"}}', From 5f48f5ad01001a0c33bcdf68af7a59e3f715ddfc Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:44:45 +0530 Subject: [PATCH 105/168] radeon forge: require complete compatibility evidence for deployment --- extra/radeon_forge/synthesis/tools.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/extra/radeon_forge/synthesis/tools.py b/extra/radeon_forge/synthesis/tools.py index e58e7f27a24f3..0878ea756dc6c 100644 --- a/extra/radeon_forge/synthesis/tools.py +++ b/extra/radeon_forge/synthesis/tools.py @@ -8,7 +8,7 @@ from ..oracles.mockgpu import MockGPUOracle from ..permissions import Action from ..runtime.tools import ToolRegistry, ToolSpec -from .hooks import HookRegistry, RuntimeFingerprint +from .hooks import HookRegistry, RuntimeFingerprint, check_compatibility from .portable import export_recipe_with_hook from .recipe import RecipeLibrary from .workspace import CandidateWorkspace @@ -109,10 +109,16 @@ def list_active_hooks(self, args: Mapping[str, Any]) -> Any: def activate_hook(self, args: Mapping[str, Any]) -> Any: if self.hooks is None: raise RuntimeError("hook registry is unavailable") + candidate = self.workspace.load_candidate(str(args["candidate_id"])) + spec = self.workspace.load_spec(candidate.spec_id) fingerprint = self._fingerprint() - if not fingerprint.architecture and not bool(args.get("allow_unknown_runtime", False)): - raise ValueError("runtime architecture is unknown; start the real local model backend before deployment") - active = self.hooks.activate(str(args["candidate_id"]), fingerprint, + allow_unknown = bool(args.get("allow_unknown_runtime", False)) + report = check_compatibility(spec, fingerprint) + if report.mismatches: raise ValueError("runtime is incompatible: " + "; ".join(report.mismatches)) + if report.unknown and not allow_unknown: + raise ValueError("runtime compatibility is incomplete: missing " + ", ".join(report.unknown) + + ". Regenerate locally or explicitly approve an unknown-runtime deployment.") + active = self.hooks.activate(candidate.candidate_id, fingerprint, str(args.get("reason", "Approved stage-specific local deployment"))) return asdict(active) From 4b94d4bb43d6b966271ea94858968dbc2a68b06e Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:46:20 +0530 Subject: [PATCH 106/168] radeon forge: add stage-aware empirical autotuning --- extra/radeon_forge/synthesis/autotune.py | 112 +++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 extra/radeon_forge/synthesis/autotune.py diff --git a/extra/radeon_forge/synthesis/autotune.py b/extra/radeon_forge/synthesis/autotune.py new file mode 100644 index 0000000000000..a0c979188fc3b --- /dev/null +++ b/extra/radeon_forge/synthesis/autotune.py @@ -0,0 +1,112 @@ +from __future__ import annotations + +import hashlib, itertools, json, os +from dataclasses import asdict, dataclass +from pathlib import Path +from typing import Any, Mapping, Sequence + +from ..command_backend import CommandHarness +from ..contracts import Candidate, Objective, WorkloadContract +from ..ledger import ExperimentLedger +from ..tuner import TuningSummary, successive_halving +from .hooks import HookDescriptor +from .workspace import CandidateWorkspace + + +@dataclass(frozen=True) +class SearchPlan: + axes: Mapping[str, tuple[int | float | str | bool, ...]] + budgets: tuple[int, ...] = (3, 10, 30) + reduction: int = 3 + max_candidates: int = 128 + timeout_seconds: int = 900 + + @classmethod + def from_mapping(cls, value: Mapping[str, Any]) -> SearchPlan: + raw = dict(value) + axes_value = raw.get("axes", {}) + if not isinstance(axes_value, Mapping) or not axes_value: raise ValueError("autotune search requires a non-empty axes table") + axes: dict[str, tuple[int | float | str | bool, ...]] = {} + for name, values in axes_value.items(): + if not isinstance(values, Sequence) or isinstance(values, (str, bytes, bytearray)) or not values: + raise ValueError(f"search axis {name!r} must be a non-empty array") + converted = tuple(values) + if not all(isinstance(item, (int, float, str, bool)) for item in converted): + raise ValueError(f"search axis {name!r} contains an unsupported value") + axes[str(name)] = converted + budgets = tuple(int(x) for x in raw.get("budgets", (3, 10, 30))) + if not budgets or any(x <= 0 for x in budgets): raise ValueError("autotune budgets must be positive") + reduction = int(raw.get("reduction", 3)) + max_candidates = int(raw.get("max_candidates", 128)) + timeout_seconds = int(raw.get("timeout_seconds", 900)) + if reduction < 2 or max_candidates < 1 or timeout_seconds < 1: raise ValueError("invalid autotune search limits") + plan = cls(axes, budgets, reduction, max_candidates, timeout_seconds) + if plan.cardinality > max_candidates: raise ValueError(f"search has {plan.cardinality} candidates, maximum is {max_candidates}") + return plan + + @property + def cardinality(self) -> int: + total = 1 + for values in self.axes.values(): total *= len(values) + return total + + def parameters(self) -> list[dict[str, int | float | str | bool]]: + names = tuple(self.axes) + return [dict(zip(names, values)) for values in itertools.product(*(self.axes[name] for name in names))] + + +def _candidate_id(base_id: str, parameters: Mapping[str, Any]) -> str: + digest = hashlib.sha256(json.dumps(parameters, sort_keys=True, default=str).encode()).hexdigest()[:12] + return f"{base_id}-tune-{digest}" + + +def _contract(spec) -> WorkloadContract: + acceptance = spec.metadata.get("recipe_acceptance", spec.metadata.get("acceptance", {})) + if not isinstance(acceptance, Mapping): acceptance = {} + objective_text = str(spec.metadata.get("search_objective", "p95_latency_us")) + try: objective = Objective(objective_text) + except ValueError: objective = Objective.P95_LATENCY_US + return WorkloadContract( + name=f"{spec.name}:{HookDescriptor.from_spec(spec).when.signature}", target=spec.target, objective=objective, + max_abs_error=float(acceptance.get("max_abs_error", acceptance.get("maximum_error", 1e-3))), + max_rel_error=float(acceptance.get("max_rel_error", 1e-3)), + max_vram_bytes=int(acceptance["max_vram_bytes"]) if acceptance.get("max_vram_bytes") is not None else None, + max_vgprs=int(acceptance["max_vgprs"]) if acceptance.get("max_vgprs") is not None else None, + max_lds_bytes=int(acceptance["max_lds_bytes"]) if acceptance.get("max_lds_bytes") is not None else None, + forbid_spills=bool(acceptance.get("forbid_spills", True)), + metadata={"hook": asdict(HookDescriptor.from_spec(spec)), "objective_text": spec.objective}, + ) + + +def run_autotune(workspace: CandidateWorkspace, candidate_id: str, project_root: str | Path, + plan: SearchPlan, ledger_path: str | Path | None = None) -> tuple[Any, TuningSummary]: + """Tune one structural implementation without allowing speed to bypass correctness.""" + record = workspace.load_candidate(candidate_id) + if record.status != "mockgpu_passed": raise ValueError("candidate must pass MockGPU before stage-specific W7900 autotuning") + spec = workspace.load_spec(record.spec_id) + if not spec.hardware_command: raise ValueError("spec has no hardware command for autotuning") + root = Path(project_root).resolve() + command = workspace.render_command(spec.hardware_command, spec, record, root) + cwd = str(spec.metadata.get("recipe_bundle", root)) + environment = {"DEV": "AMD", "RADEON_FORGE_CANDIDATE": record.source_path, + "RADEON_FORGE_RECIPE_BUNDLE": str(spec.metadata.get("recipe_bundle", "")), + "RADEON_FORGE_EXECUTION_STAGES": ",".join(x.value for x in HookDescriptor.from_spec(spec).when.stages), + "PYTHONUNBUFFERED": "1"} + harness = CommandHarness(command, cwd=cwd, env={**os.environ, **environment}, timeout_seconds=plan.timeout_seconds) + candidates = [Candidate(_candidate_id(record.candidate_id, parameters), spec.name, parameters, + source_path=record.source_path, hypothesis=record.hypothesis, parent_id=record.candidate_id) + for parameters in plan.parameters()] + ledger = ExperimentLedger(ledger_path or (workspace.root / "autotune" / f"{record.candidate_id}.jsonl")) + summary = successive_halving(candidates, harness, _contract(spec), plan.budgets, plan.reduction, ledger) + evidence = {"search_plan": asdict(plan), "rounds": summary.rounds, "evaluated_trials": len(summary.evaluated), + "ledger": str(ledger.path), "execution_hook": asdict(HookDescriptor.from_spec(spec)), + "winner": summary.winner.to_dict() if summary.winner is not None else None, + "rejections": [{"candidate_id": result.candidate.candidate_id, "parameters": dict(result.candidate.parameters), + "reason": result.rejected_reason or result.correctness.reason, + "feasible": result.is_feasible(_contract(spec))} for result in summary.evaluated]} + if summary.winner is None: + updated = workspace.update(record.candidate_id, "autotune_failed", {"autotune": evidence}) + else: + updated = workspace.update(record.candidate_id, "hardware_passed", {"autotune": evidence, + "selected_parameters": dict(summary.winner.candidate.parameters), "hardware": summary.winner.to_dict()}) + return updated, summary From 20d1189b912f61dae1ae677f3fd0b6b15c46b5bc Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:46:45 +0530 Subject: [PATCH 107/168] radeon forge: export empirical search contracts --- extra/radeon_forge/synthesis/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/extra/radeon_forge/synthesis/__init__.py b/extra/radeon_forge/synthesis/__init__.py index 2cb1df40b17a6..22dd8ae77ccd8 100644 --- a/extra/radeon_forge/synthesis/__init__.py +++ b/extra/radeon_forge/synthesis/__init__.py @@ -1,3 +1,4 @@ +from .autotune import SearchPlan, run_autotune from .defaults import install_default_specs from .hooks import (ActiveHook, CompatibilityReport, ExecutionContext, ExecutionStage, HookDescriptor, HookLayer, HookMode, HookRegistry, RuntimeFingerprint, StagePredicate, check_compatibility) @@ -8,5 +9,5 @@ __all__ = ["ActiveHook", "CandidateRecord", "CandidateWorkspace", "CompatibilityReport", "ExecutionContext", "ExecutionStage", "ForgeRecipe", "HookDescriptor", "HookLayer", "HookMode", "HookRegistry", "InstalledRecipe", "KernelSpec", - "OptimizationTools", "RecipeArtifact", "RecipeLibrary", "RuntimeFingerprint", "StagePredicate", "check_compatibility", - "export_recipe", "export_recipe_with_hook", "install_default_specs"] + "OptimizationTools", "RecipeArtifact", "RecipeLibrary", "RuntimeFingerprint", "SearchPlan", "StagePredicate", + "check_compatibility", "export_recipe", "export_recipe_with_hook", "install_default_specs", "run_autotune"] From 26ad9ba4d52bbba747f55f42f6cbd0d2d00ed6b3 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:47:36 +0530 Subject: [PATCH 108/168] radeon forge: expose empirical W7900 autotuning to agents --- extra/radeon_forge/synthesis/tools.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/extra/radeon_forge/synthesis/tools.py b/extra/radeon_forge/synthesis/tools.py index 0878ea756dc6c..4b4ee8359c626 100644 --- a/extra/radeon_forge/synthesis/tools.py +++ b/extra/radeon_forge/synthesis/tools.py @@ -8,6 +8,7 @@ from ..oracles.mockgpu import MockGPUOracle from ..permissions import Action from ..runtime.tools import ToolRegistry, ToolSpec +from .autotune import SearchPlan, run_autotune from .hooks import HookRegistry, RuntimeFingerprint, check_compatibility from .portable import export_recipe_with_hook from .recipe import RecipeLibrary @@ -76,9 +77,20 @@ def benchmark_hardware(self, args: Mapping[str, Any]) -> Any: return self._run_hardware_command(candidate.candidate_id, spec.hardware_command, int(args.get("timeout_seconds", 900)), "hardware", "hardware_passed", "hardware_failed") + def autotune_hardware(self, args: Mapping[str, Any]) -> Any: + candidate = self.workspace.load_candidate(str(args["candidate_id"])) + spec = self.workspace.load_spec(candidate.spec_id) + stored = spec.metadata.get("search", {}) + if not isinstance(stored, Mapping): stored = {} + override = {key: args[key] for key in ("axes", "budgets", "reduction", "max_candidates", "timeout_seconds") if key in args} + plan = SearchPlan.from_mapping({**dict(stored), **override}) + updated, summary = run_autotune(self.workspace, candidate.candidate_id, self.project_root, plan) + return {"candidate": asdict(updated), "winner": summary.winner.to_dict() if summary.winner else None, + "rounds": summary.rounds, "evaluated_trials": len(summary.evaluated)} + def validate_heldout(self, args: Mapping[str, Any]) -> Any: candidate = self.workspace.load_candidate(str(args["candidate_id"])) - if candidate.status != "hardware_passed": raise ValueError("candidate must pass the real hardware benchmark first") + if candidate.status != "hardware_passed": raise ValueError("candidate must pass the real hardware benchmark or autotuner first") spec = self.workspace.load_spec(candidate.spec_id) command = tuple(str(x) for x in spec.metadata.get("heldout_command", ())) if not command: raise ValueError("spec has no held-out validation command") @@ -132,6 +144,7 @@ def install(self, registry: ToolRegistry) -> None: registry.register(ToolSpec("stage_kernel_candidate", "Write one disposable implementation for a typed kernel specification", {"type":"object","required":["spec_id","source","hypothesis"],"properties":{"spec_id":{"type":"string"},"source":{"type":"string"},"hypothesis":{"type":"string"},"parent_id":{"type":"string"}}}, Action.WRITE_GENERATED_SOURCE), self.stage_candidate) registry.register(ToolSpec("validate_candidate_mockgpu", "Execute a generated AMD candidate through tinygrad's RDNA3 MockGPU semantic oracle", {"type":"object","required":["candidate_id"],"properties":{"candidate_id":{"type":"string"}}}, Action.COMPILE), self.validate_mockgpu) registry.register(ToolSpec("benchmark_candidate_w7900", "Benchmark a MockGPU-passing candidate on the real local W7900", {"type":"object","required":["candidate_id"],"properties":{"candidate_id":{"type":"string"},"timeout_seconds":{"type":"integer"},"allow_without_mockgpu":{"type":"boolean"}}}, Action.BENCHMARK), self.benchmark_hardware) + registry.register(ToolSpec("autotune_candidate_w7900", "Empirically search a bounded parameter space for one MockGPU-passing implementation in its declared execution stages", {"type":"object","required":["candidate_id","axes"],"properties":{"candidate_id":{"type":"string"},"axes":{"type":"object"},"budgets":{"type":"array"},"reduction":{"type":"integer"},"max_candidates":{"type":"integer"},"timeout_seconds":{"type":"integer"}}}, Action.BENCHMARK), self.autotune_hardware) registry.register(ToolSpec("validate_candidate_heldout", "Run a hardware-passing candidate on its held-out correctness and task-quality suite", {"type":"object","required":["candidate_id"],"properties":{"candidate_id":{"type":"string"},"timeout_seconds":{"type":"integer"}}}, Action.BENCHMARK), self.validate_heldout) registry.register(ToolSpec("list_forge_recipes", "List portable installed optimization recipes", {"type":"object","properties":{}}), self.list_recipes) registry.register(ToolSpec("inspect_forge_recipe", "Read the intent, invariants, execution-stage hook, oracle and artifact inventory of an installed optimization recipe", {"type":"object","required":["recipe_id"],"properties":{"recipe_id":{"type":"string"}}}), self.inspect_recipe) From 40c914c5bbe087e31750fa19d1bbb19e70c40f6c Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:48:13 +0530 Subject: [PATCH 109/168] radeon forge: carry tuned parameters into active runtime hooks --- extra/radeon_forge/runtime/engine.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/extra/radeon_forge/runtime/engine.py b/extra/radeon_forge/runtime/engine.py index c7bd043258ea0..6c367b0867e76 100644 --- a/extra/radeon_forge/runtime/engine.py +++ b/extra/radeon_forge/runtime/engine.py @@ -41,7 +41,13 @@ def runtime_fingerprint(self) -> RuntimeFingerprint: return RuntimeFingerprint.from_mapping(metadata if isinstance(metadata, Mapping) else {}) def _session_metadata(self, session: AgentSession, step: int) -> Mapping[str, Any]: - return {**self.hooks.runtime_metadata(), "runtime_fingerprint": asdict(self.runtime_fingerprint())} + metadata = self.hooks.runtime_metadata() + for item in metadata.get("active_hooks", []): + try: + record = self.optimization_workspace.load_candidate(str(item["candidate_id"])) + item["parameters"] = dict(record.evidence.get("selected_parameters", {})) + except Exception: item["parameters"] = {} + return {**metadata, "runtime_fingerprint": asdict(self.runtime_fingerprint())} def create_session(self) -> AgentSession: with self._lock: From 1541d95c838d5ff5cb3d36374018d044012ff4b2 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:48:46 +0530 Subject: [PATCH 110/168] radeon forge: apply tuned parameters inside stage hooks --- extra/radeon_forge/backends/stage_hooks.py | 25 ++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/extra/radeon_forge/backends/stage_hooks.py b/extra/radeon_forge/backends/stage_hooks.py index ace5137e51c9c..1cc9f1c3929ce 100644 --- a/extra/radeon_forge/backends/stage_hooks.py +++ b/extra/radeon_forge/backends/stage_hooks.py @@ -1,6 +1,6 @@ from __future__ import annotations -import hashlib, importlib.util +import hashlib, importlib.util, json from dataclasses import asdict from pathlib import Path from typing import Any, Mapping, Sequence @@ -29,6 +29,12 @@ def _matching_hooks(hooks: Sequence[Mapping[str, Any]], context: ExecutionContex return sorted(selected.values(), key=lambda x: -int(x.get("descriptor", {}).get("priority", 0))) +def _signature(hook: Mapping[str, Any]) -> str: + parameters = hook.get("parameters", {}) + digest = hashlib.sha256(json.dumps(parameters, sort_keys=True, default=str).encode()).hexdigest()[:10] + return f"{hook.get('activation_id')}:{digest}" + + class ModelStageHookRuntime: """Apply validated Python model hooks only in their declared execution states. @@ -75,17 +81,26 @@ def _module(self, hook: Mapping[str, Any]) -> Any: def apply(self, hooks: Sequence[Mapping[str, Any]], context: ExecutionContext) -> dict[str, Any]: selected = [hook for hook in _matching_hooks(hooks, context) if hook.get("descriptor", {}).get("adapter") == "python_transformer_block"] - signature = tuple(str(hook.get("activation_id")) for hook in selected) + signature = tuple(_signature(hook) for hook in selected) if signature == self._active_signature: - return {"changed": False, "active": list(signature), "stage": context.stage.value} + return {"changed": False, "active": [str(hook.get("activation_id")) for hook in selected], "stage": context.stage.value} self._reset() if not selected: return {"changed": True, "active": [], "stage": context.stage.value, "baseline": True} try: layers = list(self._original_layers) + selected_parameters: dict[str, Mapping[str, Any]] = {} for hook in selected: descriptor = hook["descriptor"] if descriptor.get("mode", "replace") != "replace": raise StageHookError("python_transformer_block currently supports replace mode only") module = self._module(hook) + parameters = hook.get("parameters", {}) + if not isinstance(parameters, Mapping): raise StageHookError("hook parameters must be a table") + parameters = dict(parameters) + setattr(module, "RADEON_FORGE_PARAMETERS", parameters) + configure = getattr(module, "configure", None) + if configure is not None: + if not callable(configure): raise StageHookError("optional configure attribute must be callable") + configure(parameters) factory = getattr(module, "build_replacement", None) if not callable(factory): raise StageHookError("hook must define build_replacement(original, layer_index, model, context)") selector = descriptor.get("selector", {}) @@ -93,10 +108,12 @@ def apply(self, hooks: Sequence[Mapping[str, Any]], context: ExecutionContext) - replacement = factory(layers[index], index, self.model, asdict(context)) if not callable(replacement): raise StageHookError(f"replacement for layer {index} is not callable") layers[index] = replacement + selected_parameters[str(hook.get("activation_id"))] = parameters self.model.layers = layers if hasattr(self.model, "forward_jit") and self.model.forward_jit is not None: self.model.forward_jit = TinyJit(self.model.forward) self._active_signature = signature - return {"changed": True, "active": list(signature), "stage": context.stage.value, "baseline": False} + return {"changed": True, "active": [str(hook.get("activation_id")) for hook in selected], + "parameters": selected_parameters, "stage": context.stage.value, "baseline": False} except Exception as exc: self._reset() return {"changed": True, "active": [], "stage": context.stage.value, "baseline": True, From 584a51911bc4b3d134e3c8975b5f1bcef066eff0 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:49:18 +0530 Subject: [PATCH 111/168] radeon forge: test hard-gated stage autotuning --- test/test_radeon_forge_autotune.py | 54 ++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 test/test_radeon_forge_autotune.py diff --git a/test/test_radeon_forge_autotune.py b/test/test_radeon_forge_autotune.py new file mode 100644 index 0000000000000..06b021084820b --- /dev/null +++ b/test/test_radeon_forge_autotune.py @@ -0,0 +1,54 @@ +import json +import sys +import tempfile +import unittest +from pathlib import Path + +from extra.radeon_forge.synthesis.autotune import SearchPlan, run_autotune +from extra.radeon_forge.synthesis.workspace import CandidateWorkspace, KernelSpec + + +class TestStageAutotune(unittest.TestCase): + def test_fast_invalid_configuration_cannot_win(self): + with tempfile.TemporaryDirectory() as directory: + root = Path(directory) + runner = root / "runner.py" + runner.write_text('''import json, os +candidate = json.loads(os.environ["RADEON_FORGE_CANDIDATE_JSON"]) +tile = candidate["parameters"]["TILE"] +budget = int(os.environ["RADEON_FORGE_BUDGET"]) +payload = { + "samples_us": ([1.0] if tile == 1 else [2.0]) * budget, + "correctness": {"passed": True, "max_abs_error": 0.0, "max_rel_error": 0.0, "checked_values": 128}, + "resources": {"vgprs": 128 if tile == 1 else 64, "lds_bytes": 4096, "spilled_vgprs": 0, "spilled_sgprs": 0}, + "compile_ok": True, + "stable": True +} +print(json.dumps(payload)) +''', encoding="utf-8") + workspace = CandidateWorkspace(root / "forge") + spec = workspace.save_spec(KernelSpec( + name="decode-search", operation="batch-one decode subgraph", target="gfx1100", invariants=("match reference",), + hardware_command=(sys.executable, str(runner)), + metadata={"recipe_acceptance":{"max_vgprs":96, "forbid_spills":True}, + "hook":{"layer":"subgraph", "target":"decode.projection", "adapter":"request_metadata", + "when":{"stages":["decode"], "batch_sizes":[1]}, "exclusive_group":"decode-projection"}}, + )) + candidate = workspace.create_candidate(spec.spec_id, "# parameterized candidate\n", "search tile size") + workspace.update(candidate.candidate_id, "mockgpu_passed", {"mockgpu":{"passed":True}}) + updated, summary = run_autotune(workspace, candidate.candidate_id, root, + SearchPlan.from_mapping({"axes":{"TILE":[1,2]}, "budgets":[1,2], "reduction":2})) + self.assertIsNotNone(summary.winner) + self.assertEqual(summary.winner.candidate.parameters["TILE"], 2) + self.assertEqual(updated.status, "hardware_passed") + self.assertEqual(updated.evidence["selected_parameters"], {"TILE":2}) + persisted = workspace.load_candidate(candidate.candidate_id) + self.assertEqual(persisted.evidence["selected_parameters"], {"TILE":2}) + self.assertTrue(Path(persisted.evidence["autotune"]["ledger"]).is_file()) + + def test_search_space_is_bounded(self): + with self.assertRaisesRegex(ValueError, "maximum"): + SearchPlan.from_mapping({"axes":{"A":list(range(20)), "B":list(range(20))}, "max_candidates":128}) + + +if __name__ == "__main__": unittest.main() From b1a95a9773c8485b3c39fb19a087419a43bca631 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:49:36 +0530 Subject: [PATCH 112/168] ci: cover stage autotuning tests --- .github/workflows/radeon-forge.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/radeon-forge.yml b/.github/workflows/radeon-forge.yml index 127778ae41ff9..275a466f5a122 100644 --- a/.github/workflows/radeon-forge.yml +++ b/.github/workflows/radeon-forge.yml @@ -33,3 +33,4 @@ jobs: test.test_radeon_forge_recipe test.test_radeon_forge_runtime test.test_radeon_forge_hooks + test.test_radeon_forge_autotune From 78b83c33f6715aeeceb2aedb743933dc315695f2 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:51:18 +0530 Subject: [PATCH 113/168] radeon forge: share stage-specific autotuning spaces --- extra/radeon_forge/synthesis/portable.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/extra/radeon_forge/synthesis/portable.py b/extra/radeon_forge/synthesis/portable.py index 86b99d49bed5b..509eb49da4a4c 100644 --- a/extra/radeon_forge/synthesis/portable.py +++ b/extra/radeon_forge/synthesis/portable.py @@ -30,7 +30,7 @@ def _append_table(lines: list[str], name: str, values: Mapping[str, Any]) -> Non def export_recipe_with_hook(workspace: CandidateWorkspace, spec_id: str, output: str | Path, candidate_id: str | None = None) -> Path: - """Export one recipe while preserving both placement and execution-state applicability.""" + """Export placement, execution-state applicability and empirical search knowledge.""" path = export_recipe(workspace, spec_id, output, candidate_id) spec = workspace.load_spec(spec_id) descriptor = HookDescriptor.from_spec(spec) @@ -61,5 +61,19 @@ def export_recipe_with_hook(workspace: CandidateWorkspace, spec_id: str, output: } lines = path.read_text(encoding="utf-8").rstrip().splitlines() _append_table(lines, "metadata.hook", hook) + + # The search space is portable knowledge, not a mandatory implementation + # abstraction. A receiving agent may reuse, shrink or replace it, but keeping + # it next to the stage predicate makes prior hardware exploration reproducible. + search = spec.metadata.get("search", {}) + if isinstance(search, Mapping) and search: _append_table(lines, "metadata.search", search) + + selected_parameters = {} + if candidate_id is not None: + candidate = workspace.load_candidate(candidate_id) + selected_parameters = candidate.evidence.get("selected_parameters", {}) + if isinstance(selected_parameters, Mapping) and selected_parameters: + _append_table(lines, "metadata.exported_winner", dict(selected_parameters)) + path.write_text("\n".join(lines) + "\n", encoding="utf-8") return path From 1538592a943183cb3c9a7fdc03d7e3ae9f23b2c8 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:51:57 +0530 Subject: [PATCH 114/168] radeon forge: test portable stage autotuning knowledge --- test/test_radeon_forge_recipe.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/test/test_radeon_forge_recipe.py b/test/test_radeon_forge_recipe.py index 00ac261ce9816..cadebef21c5fd 100644 --- a/test/test_radeon_forge_recipe.py +++ b/test/test_radeon_forge_recipe.py @@ -40,6 +40,15 @@ def _recipe_text(self, seed: str = "print('seed')\n") -> str: min_context_tokens = 1024 prefix_cache = "hit" +[metadata.search] +budgets = [3, 10] +reduction = 2 +max_candidates = 16 + +[metadata.search.axes] +WAVES = [2, 4] +STAGES = [1, 2] + [oracle] mockgpu_command = ["python3", "{{candidate}}", "--bundle", "{{bundle}}"] hardware_command = ["python3", "{{candidate}}", "--benchmark"] @@ -79,6 +88,7 @@ def test_install_is_content_addressed_and_seed_is_unverified(self): self.assertEqual([x.value for x in descriptor.when.stages], ["decode"]) self.assertEqual(descriptor.when.min_context_tokens, 1024) self.assertEqual(descriptor.when.prefix_cache, "hit") + self.assertEqual(spec.metadata["search"]["axes"]["WAVES"], [2, 4]) rendered = workspace.render_command(spec.mockgpu_command, spec, candidate, root) self.assertEqual(rendered[1], candidate.source_path) self.assertEqual(rendered[3], installed.bundle_root) @@ -90,7 +100,7 @@ def test_rejects_path_traversal(self): path.write_text(text, encoding="utf-8") with self.assertRaises(ValueError): ForgeRecipe.load(path) - def test_export_import_round_trip_preserves_contract_cache_and_stage_hook(self): + def test_export_import_round_trip_preserves_contract_cache_stage_and_search(self): with tempfile.TemporaryDirectory() as directory: root = Path(directory) source_workspace = CandidateWorkspace(root / "source") @@ -100,6 +110,8 @@ def test_export_import_round_trip_preserves_contract_cache_and_stage_hook(self): mockgpu_command=("python3", "{candidate}"), hardware_command=("python3", "{candidate}", "--benchmark"), metadata={"recipe_agent_brief":"Use the oracle as the contract and freely rewrite the implementation.", "recipe_compatibility":{"architecture":"gfx1100"}, "recipe_acceptance":{"max_error":1e-6}, + "search":{"axes":{"WAVES":[2,4], "UNROLL":[1,2]}, "budgets":[3,10,30], "reduction":2, + "max_candidates":16}, "hook":{"layer":"transformer_block", "target":"llama.decode.block", "mode":"replace", "adapter":"python_transformer_block", "selector":{"indices":[0,1]}, "when":{"stages":["decode"], "batch_sizes":[1], "min_generated_token_index":1, @@ -108,12 +120,15 @@ def test_export_import_round_trip_preserves_contract_cache_and_stage_hook(self): ) source_workspace.save_spec(spec) candidate = source_workspace.create_candidate(spec.spec_id, "print('candidate')\n", "measured decode implementation") + source_workspace.update(candidate.candidate_id, "hardware_passed", {"selected_parameters":{"WAVES":4, "UNROLL":2}}) exported = export_recipe_with_hook(source_workspace, spec.spec_id, root / "shared.forge.toml", candidate.candidate_id) loaded = ForgeRecipe.load(exported) self.assertEqual(loaded.target, "gfx1100") self.assertEqual(loaded.invariants, spec.invariants) self.assertEqual(loaded.metadata["hook"]["when"]["stages"], ["decode"]) + self.assertEqual(loaded.metadata["search"]["axes"]["WAVES"], [2, 4]) + self.assertEqual(loaded.metadata["exported_winner"], {"WAVES":4, "UNROLL":2}) destination = CandidateWorkspace(root / "destination") installed = RecipeLibrary(destination).install(loaded) imported = destination.load_candidate(installed.seed_candidate_id) @@ -125,6 +140,8 @@ def test_export_import_round_trip_preserves_contract_cache_and_stage_hook(self): self.assertEqual([x.value for x in descriptor.when.stages], ["decode"]) self.assertEqual(descriptor.when.min_generated_token_index, 1) self.assertEqual(descriptor.when.conditions["resume_after_tool"], False) + self.assertEqual(installed_spec.metadata["search"]["axes"]["UNROLL"], [1, 2]) + self.assertEqual(installed_spec.metadata["exported_winner"]["WAVES"], 4) def test_base_export_remains_valid_without_hook_extension(self): with tempfile.TemporaryDirectory() as directory: From a3dcf72985b02733ba015b5fcf060d521fd4bbe1 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:53:50 +0530 Subject: [PATCH 115/168] radeon forge: require transition oracles for stateful hooks --- extra/radeon_forge/synthesis/deployment.py | 43 ++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 extra/radeon_forge/synthesis/deployment.py diff --git a/extra/radeon_forge/synthesis/deployment.py b/extra/radeon_forge/synthesis/deployment.py new file mode 100644 index 0000000000000..c1b54da587463 --- /dev/null +++ b/extra/radeon_forge/synthesis/deployment.py @@ -0,0 +1,43 @@ +from __future__ import annotations + +from .hooks import (ActiveHook, HookActivationError, HookDescriptor, HookLayer, HookRegistry, RuntimeFingerprint) +from .workspace import CandidateWorkspace + + +STATEFUL_LAYERS = frozenset({HookLayer.MODEL, HookLayer.TRANSFORMER_BLOCK, HookLayer.KV_CACHE}) +RUNTIME_ADAPTERS = frozenset({"python_transformer_block"}) + + +class SafeHookRegistry(HookRegistry): + """Deployment policy layered over the generic stage resolver. + + The generic registry is useful for tests and future adapters. The engine uses + this stricter registry so metadata-only or unimplemented adapters cannot be + presented as active optimizations, and stateful phase changes cannot deploy + without a held-out transition oracle. + """ + SUPPORTED_ADAPTERS = RUNTIME_ADAPTERS + + def __init__(self, workspace: CandidateWorkspace): super().__init__(workspace) + + def activate(self, candidate_id: str, fingerprint: RuntimeFingerprint, reason: str) -> ActiveHook: + candidate = self.workspace.load_candidate(candidate_id) + spec = self.workspace.load_spec(candidate.spec_id) + descriptor = HookDescriptor.from_spec(spec) + if descriptor.adapter not in self.SUPPORTED_ADAPTERS: + raise HookActivationError( + f"runtime adapter {descriptor.adapter!r} is not executable in this engine build; " + "the recipe remains usable as profiling/search knowledge" + ) + if descriptor.layer in STATEFUL_LAYERS: + heldout = tuple(spec.metadata.get("heldout_command", ())) + if not heldout: + raise HookActivationError( + f"{descriptor.layer.value} hooks require a held-out phase-transition oracle before deployment " + "(for example prefill -> first_token -> decode and tool-resume continuity)" + ) + if candidate.status not in {"heldout_passed", "accepted"}: + raise HookActivationError( + f"stateful hook candidate status {candidate.status!r} has not passed its phase-transition oracle" + ) + return super().activate(candidate_id, fingerprint, reason) From 744e0885158991f71a7aa20a77d986f895b27522 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:54:05 +0530 Subject: [PATCH 116/168] radeon forge: export safe deployment policy --- extra/radeon_forge/synthesis/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/extra/radeon_forge/synthesis/__init__.py b/extra/radeon_forge/synthesis/__init__.py index 22dd8ae77ccd8..314e1a20dc344 100644 --- a/extra/radeon_forge/synthesis/__init__.py +++ b/extra/radeon_forge/synthesis/__init__.py @@ -1,5 +1,6 @@ from .autotune import SearchPlan, run_autotune from .defaults import install_default_specs +from .deployment import RUNTIME_ADAPTERS, STATEFUL_LAYERS, SafeHookRegistry from .hooks import (ActiveHook, CompatibilityReport, ExecutionContext, ExecutionStage, HookDescriptor, HookLayer, HookMode, HookRegistry, RuntimeFingerprint, StagePredicate, check_compatibility) from .portable import export_recipe_with_hook @@ -9,5 +10,6 @@ __all__ = ["ActiveHook", "CandidateRecord", "CandidateWorkspace", "CompatibilityReport", "ExecutionContext", "ExecutionStage", "ForgeRecipe", "HookDescriptor", "HookLayer", "HookMode", "HookRegistry", "InstalledRecipe", "KernelSpec", - "OptimizationTools", "RecipeArtifact", "RecipeLibrary", "RuntimeFingerprint", "SearchPlan", "StagePredicate", - "check_compatibility", "export_recipe", "export_recipe_with_hook", "install_default_specs", "run_autotune"] + "OptimizationTools", "RUNTIME_ADAPTERS", "RecipeArtifact", "RecipeLibrary", "RuntimeFingerprint", "STATEFUL_LAYERS", + "SafeHookRegistry", "SearchPlan", "StagePredicate", "check_compatibility", "export_recipe", "export_recipe_with_hook", + "install_default_specs", "run_autotune"] From 1fc7d5911a7224b66cdc4b5db6ef94b73b06fd14 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:54:37 +0530 Subject: [PATCH 117/168] radeon forge: use transition-safe deployment registry --- extra/radeon_forge/runtime/engine.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/extra/radeon_forge/runtime/engine.py b/extra/radeon_forge/runtime/engine.py index 6c367b0867e76..f0af8af0dc04e 100644 --- a/extra/radeon_forge/runtime/engine.py +++ b/extra/radeon_forge/runtime/engine.py @@ -7,7 +7,7 @@ from ..permissions import PermissionController from ..profiling.report import build_profile_report -from ..synthesis import CandidateWorkspace, HookRegistry, OptimizationTools, RuntimeFingerprint, install_default_specs +from ..synthesis import CandidateWorkspace, OptimizationTools, RuntimeFingerprint, SafeHookRegistry, install_default_specs from .backend import InferenceBackend from .jobs import LocalJobManager from .session import AgentSession @@ -28,7 +28,7 @@ def __init__(self, backend: InferenceBackend, workspace: str | Path, system_prom WorkspaceTools(self.workspace).install(self.tools) self.optimization_workspace = CandidateWorkspace(self.workspace / ".radeon_forge") install_default_specs(self.optimization_workspace) - self.hooks = HookRegistry(self.optimization_workspace) + self.hooks = SafeHookRegistry(self.optimization_workspace) self.optimization_tools = OptimizationTools(self.optimization_workspace, self.workspace, self.hooks, self.runtime_fingerprint) self.optimization_tools.install(self.tools) self.jobs = LocalJobManager() @@ -96,6 +96,7 @@ def optimization_state(self) -> dict[str, Any]: "candidates": [asdict(x) for x in self.optimization_workspace.candidates()], "recipes": [asdict(x) for x in self.optimization_tools.recipes.installed()], "active_hooks": [asdict(x) for x in self.hooks.active()], - "runtime_fingerprint": asdict(self.runtime_fingerprint())} + "runtime_fingerprint": asdict(self.runtime_fingerprint()), + "runtime_adapters": sorted(self.hooks.SUPPORTED_ADAPTERS)} def close(self) -> None: self.backend.close() From f482c610e917b8520b7ed919cd2f8644a7f8f11e Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:55:22 +0530 Subject: [PATCH 118/168] radeon forge: test executable adapters and transition-oracle deployment --- test/test_radeon_forge_hooks.py | 44 ++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/test/test_radeon_forge_hooks.py b/test/test_radeon_forge_hooks.py index 426cc452d91ba..6d86c6cc3d3d2 100644 --- a/test/test_radeon_forge_hooks.py +++ b/test/test_radeon_forge_hooks.py @@ -4,8 +4,9 @@ from pathlib import Path from extra.radeon_forge.backends.stage_hooks import ModelStageHookRuntime -from extra.radeon_forge.synthesis.hooks import (ExecutionContext, ExecutionStage, HookRegistry, RuntimeFingerprint, - StagePredicate) +from extra.radeon_forge.synthesis import SafeHookRegistry +from extra.radeon_forge.synthesis.hooks import (ExecutionContext, ExecutionStage, HookActivationError, HookRegistry, + RuntimeFingerprint, StagePredicate) from extra.radeon_forge.synthesis.workspace import CandidateWorkspace, KernelSpec @@ -32,7 +33,7 @@ def test_predicate_distinguishes_execution_state_and_workload(self): generated_token_index=0, prefix_reused_tokens=256, attributes={"resume_after_tool": True}))) - def test_prefill_and_decode_implementations_can_coexist(self): + def test_prefill_and_decode_implementations_can_coexist_in_generic_resolver(self): with tempfile.TemporaryDirectory() as directory: workspace = CandidateWorkspace(directory) common = {"layer": "transformer_block", "target": "llama.block", "adapter": "python_transformer_block", @@ -56,6 +57,43 @@ def test_prefill_and_decode_implementations_can_coexist(self): self.assertEqual(registry.resolve(ExecutionContext(ExecutionStage.DECODE, generated_token_index=2))[0].candidate_id, decode.candidate_id) + def test_engine_registry_rejects_unimplemented_metadata_adapter(self): + with tempfile.TemporaryDirectory() as directory: + workspace = CandidateWorkspace(directory) + spec = workspace.save_spec(KernelSpec("advisory-kernel", "kernel idea", target="gfx1100", + invariants=("match reference",), metadata={"hook":{"layer":"kernel", "target":"gemv", + "adapter":"request_metadata", "when":{"stages":["decode"]}}})) + candidate = workspace.create_candidate(spec.spec_id, "# advisory only\n", "prior kernel schedule") + workspace.update(candidate.candidate_id, "hardware_passed", {}) + registry = SafeHookRegistry(workspace) + with self.assertRaisesRegex(HookActivationError, "not executable"): + registry.activate(candidate.candidate_id, RuntimeFingerprint(architecture="gfx1100"), "deploy") + + def test_stateful_hook_requires_phase_transition_oracle(self): + with tempfile.TemporaryDirectory() as directory: + workspace = CandidateWorkspace(directory) + hook = {"layer":"transformer_block", "target":"llama.block", "adapter":"python_transformer_block", + "when":{"stages":["decode"]}} + no_transition = workspace.save_spec(KernelSpec("no-transition", "decode block", target="gfx1100", + invariants=("preserve KV state",), metadata={"hook":hook})) + first = workspace.create_candidate(no_transition.spec_id, "def build_replacement(*args): return args[0]\n", "decode") + workspace.update(first.candidate_id, "hardware_passed", {}) + registry = SafeHookRegistry(workspace) + fingerprint = RuntimeFingerprint(architecture="gfx1100", runtime="tinygrad", model_family="llama") + with self.assertRaisesRegex(HookActivationError, "phase-transition oracle"): + registry.activate(first.candidate_id, fingerprint, "deploy") + + with_transition = workspace.save_spec(KernelSpec("with-transition", "decode block", target="gfx1100", + invariants=("preserve KV state",), metadata={"hook":hook, + "heldout_command":["python3", "validate_prefill_decode_transition.py"]})) + second = workspace.create_candidate(with_transition.spec_id, "def build_replacement(*args): return args[0]\n", "decode") + workspace.update(second.candidate_id, "hardware_passed", {}) + with self.assertRaisesRegex(HookActivationError, "has not passed"): + registry.activate(second.candidate_id, fingerprint, "deploy") + workspace.update(second.candidate_id, "heldout_passed", {"heldout":{"transition":"prefill->first_token->decode"}}) + active = registry.activate(second.candidate_id, fingerprint, "transition oracle passed") + self.assertEqual(active.candidate_id, second.candidate_id) + def test_model_adapter_rolls_back_bad_stage_hook(self): with tempfile.TemporaryDirectory() as directory: root = Path(directory) From 79b58f1bec5f2304ee168bef861c08db5bf06fe6 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:57:46 +0530 Subject: [PATCH 119/168] radeon forge: add batched prefill and exact KV reuse accounting --- .../backends/tinygrad_llama_worker.py | 131 +++++++++++------- 1 file changed, 82 insertions(+), 49 deletions(-) diff --git a/extra/radeon_forge/backends/tinygrad_llama_worker.py b/extra/radeon_forge/backends/tinygrad_llama_worker.py index 2bba48fbfbeb7..c674471fb18ae 100644 --- a/extra/radeon_forge/backends/tinygrad_llama_worker.py +++ b/extra/radeon_forge/backends/tinygrad_llama_worker.py @@ -1,6 +1,6 @@ from __future__ import annotations -import argparse, hashlib, json, sys, time +import argparse, hashlib, json, subprocess, sys, time from pathlib import Path from typing import Any, Callable, Mapping @@ -21,25 +21,18 @@ def send(payload): sys.stdout.flush() -def _name(value: Any) -> str: - return str(getattr(value, "display_name", value)) +def _name(value: Any) -> str: return str(getattr(value, "display_name", value)) def _collect_profile_events(start: int) -> list[dict[str, Any]]: - """Flatten tinygrad device profile ranges into portable kernel evidence. - - Timestamps are device-local, so only durations and ordering are exported. - Forge wall-clock spans remain the cross-layer timeline authority. - """ + """Flatten tinygrad device profile ranges into portable kernel evidence.""" raw = list(Compiled.profile_events[start:]) del Compiled.profile_events[start:] ret: list[dict[str, Any]] = [] for event in raw: if hasattr(event, "ents") and hasattr(event, "sigs"): for entry in event.ents: - try: - st, en = event.sigs[entry.st_id], event.sigs[entry.en_id] - duration_us = float(en - st) + try: duration_us = float(event.sigs[entry.en_id] - event.sigs[entry.st_id]) except Exception: continue device = str(getattr(entry, "device", "UNKNOWN")) if device in {"CPU", "TINY"} or duration_us < 0: continue @@ -67,19 +60,17 @@ def _profiled(fn: Callable[[], Any]) -> tuple[Any, list[dict[str, Any]]]: return result, _collect_profile_events(start) -def _emit_kernel_events(events: list[dict[str, Any]], *, stage: str, token_index: int | None = None, - prompt_position: int | None = None) -> tuple[int, bool]: +def _emit_kernel_events(events: list[dict[str, Any]], *, stage: str, token_index: int | None = None) -> tuple[int, bool]: truncated = len(events) > MAX_PROFILE_EVENTS_PER_PHASE emitted = events[:MAX_PROFILE_EVENTS_PER_PHASE] for sequence, event in enumerate(emitted): metrics = {**event, "stage": stage, "sequence": sequence} if token_index is not None: metrics["token_index"] = token_index - if prompt_position is not None: metrics["prompt_position"] = prompt_position send({"kind": "kernel", "metrics": metrics}) return len(emitted), truncated -def _model_identity(path: Path, size: str, quantize: str | None) -> str: +def _weight_identity(path: Path, size: str, quantize: str | None) -> str: try: stat = path.stat() identity = f"{path.resolve()}:{stat.st_size}:{stat.st_mtime_ns}:{size}:{quantize}" @@ -87,12 +78,53 @@ def _model_identity(path: Path, size: str, quantize: str | None) -> str: return hashlib.sha256(identity.encode()).hexdigest() +def _shape(value: Any) -> list[int | str]: + try: return [int(item) for item in value.shape] + except Exception: return [str(getattr(value, "shape", "unknown"))] + + +def _architecture_identity(model: Any, size: str, quantize: str | None) -> tuple[dict[str, Any], str]: + first = model.layers[0] if getattr(model, "layers", None) else None + attention = getattr(first, "attention", None) + feed_forward = getattr(first, "feed_forward", None) + payload = { + "family": "llama", "size_label": size, "quantize": quantize or "model_default", + "layers": len(getattr(model, "layers", ())), "max_context": int(getattr(model, "max_context", 0)), + "n_heads": int(getattr(attention, "n_heads", 0)), "n_kv_heads": int(getattr(attention, "n_kv_heads", 0)), + "head_dim": int(getattr(attention, "head_dim", 0)), + "embedding_shape": _shape(getattr(getattr(model, "tok_embeddings", None), "weight", None)), + "output_shape": _shape(getattr(getattr(model, "output", None), "weight", None)), + "ffn_w1_shape": _shape(getattr(getattr(feed_forward, "w1", None), "weight", None)), + } + return payload, hashlib.sha256(json.dumps(payload, sort_keys=True).encode()).hexdigest() + + +def _git_revision() -> str: + try: + root = Path(__file__).resolve().parents[3] + return subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=root, text=True, stderr=subprocess.DEVNULL).strip() + except Exception: return "unknown" + + def _hook_event(runtime: ModelStageHookRuntime, hooks: list[Mapping[str, Any]], context: ExecutionContext) -> None: result = runtime.apply(hooks, context) if result.get("changed") or result.get("rolled_back"): send({"kind": "hook", "metrics": {**result, "context": context.flattened()}}) +def _prefill_without_output_head(model: Any, token_ids: list[int], start_pos: int, device: str) -> None: + """Populate KV state for a prompt chunk without projecting every prompt token to vocabulary logits.""" + if not token_ids: return + tokens = Tensor([token_ids], device=device) + _, seqlen = tokens.shape + h = model.tok_embeddings(tokens).contiguous() + freqs_cis = model.freqs_cis.cast(h.dtype)[:, start_pos:start_pos+seqlen, :, :, :] + mask = (Tensor.full((1, 1, seqlen, start_pos+seqlen), float("-inf"), dtype=h.dtype, device=h.device).triu(start_pos+1) + if model.max_context != 0 and seqlen > 1 else None) + for layer in model.layers: h = layer(h, start_pos, freqs_cis, mask) + h.realize() + + def main() -> None: parser = argparse.ArgumentParser(description="Persistent tinygrad Llama backend for Radeon Forge") parser.add_argument("--model", type=Path, required=True) @@ -112,6 +144,7 @@ def main() -> None: device = Device.DEFAULT model = build_transformer(args.model, model_size=args.size, quantize=args.quantize, device=device, max_context=args.max_context) param_bytes = sum(x.nbytes() for x in get_parameters(model)) + architecture_data, architecture_hash = _architecture_identity(model, args.size, args.quantize) hook_runtime = ModelStageHookRuntime(model) device_obj = Device[device] architecture = str(getattr(device_obj, "arch", "")) @@ -125,10 +158,11 @@ def encode_message(message) -> list[int]: return encode_role(str(message.get("role", "user"))) + tokenizer.encode(content.strip()) + [tokenizer.special_tokens["<|eot_id|>"]] active_session: str | None = None - active_tokens: list[int] = [] + cached_tokens: list[int] = [] # only tokens whose KV entries are actually materialized send({"kind": "ready", "name": f"tinygrad-llama-{args.size}", "device": str(device), "gpu": str(device), - "architecture": architecture, "runtime": "tinygrad", "runtime_revision": "radeon-forge", - "model": str(args.model), "model_family": "llama", "model_hash": _model_identity(args.model, args.size, args.quantize), + "architecture": architecture, "runtime": "tinygrad", "runtime_revision": _git_revision(), + "model": str(args.model), "model_family": "llama", "model_hash": _weight_identity(args.model, args.size, args.quantize), + "model_architecture_hash": architecture_hash, "model_architecture": architecture_data, "dtype": args.quantize or "model_default", "shapes": {"batch": 1, "max_context": args.max_context}, "parameter_bytes": param_bytes, "capabilities": {"streaming": True, "structured_tools": False, "prefix_cache": True, "persistent_kv": True, "kernel_metrics": True, "cancellation": False}}) @@ -156,41 +190,35 @@ def encode_message(message) -> list[int]: if not messages or messages[-1].get("role") != "assistant": prompt += encode_role("assistant") if len(prompt) >= args.max_context: raise ValueError(f"prompt has {len(prompt)} tokens, max context is {args.max_context}") + if active_session != session_id: + active_session, cached_tokens = session_id, [] common = 0 - if active_session == session_id: - for old, new in zip(active_tokens, prompt): - if old != new: break - common += 1 - active_session = session_id + for old, new in zip(cached_tokens, prompt): + if old != new: break + common += 1 + prefill_context = ExecutionContext(ExecutionStage.PREFILL, batch_size=1, prompt_tokens=len(prompt), context_tokens=max(0, len(prompt)-1), generated_token_index=-1, prefix_reused_tokens=common, - tool_round=tool_round, warm=bool(active_tokens), attributes={"resume_after_tool": resume_after_tool, "session_id": session_id}) + tool_round=tool_round, warm=bool(cached_tokens), attributes={"resume_after_tool": resume_after_tool, "session_id": session_id}) _hook_event(hook_runtime, hooks, prefill_context) + prefill_ids = list(prompt[common:-1]) + GlobalCounters.reset() prefill_start = time.perf_counter_ns() - prefill_gpu_s, prefill_kernels, prefill_mem, prefill_ops = 0.0, 0, 0, 0 - prefill_profile: list[dict[str, Any]] = [] - for position in range(common, len(prompt) - 1): - GlobalCounters.reset() - _, profile = _profiled(lambda position=position: model(Tensor([[prompt[position]]], device=device), position, 0.0, 0, 0.0, 0.0, 0.0).realize()) - for event in profile: event["prompt_position"] = position - prefill_profile.extend(profile) - prefill_gpu_s += GlobalCounters.time_sum_s - prefill_kernels += GlobalCounters.kernel_count - prefill_mem += GlobalCounters.global_mem - prefill_ops += GlobalCounters.global_ops + _, prefill_profile = _profiled(lambda: _prefill_without_output_head(model, prefill_ids, common, device)) prefill_wall_ms = (time.perf_counter_ns() - prefill_start) / 1e6 - active_tokens = list(prompt) - send({"kind": "prefill", "metrics": {"wall_ms": prefill_wall_ms, "gpu_ms": prefill_gpu_s * 1e3, - "prompt_tokens": len(prompt), "prefix_reused_tokens": common, "new_prompt_tokens": max(0, len(prompt) - 1 - common), - "kernel_count": prefill_kernels, "global_mem_bytes": prefill_mem, "global_ops": prefill_ops, - "profile_kernel_events": len(prefill_profile), "resume_after_tool": resume_after_tool}}) - prefill_truncated = len(prefill_profile) > MAX_PROFILE_EVENTS_PER_PHASE - for sequence, event in enumerate(prefill_profile[:MAX_PROFILE_EVENTS_PER_PHASE]): - send({"kind": "kernel", "metrics": {**event, "stage": "prefill", "sequence": sequence}}) + prefill_gpu_ms = GlobalCounters.time_sum_s * 1e3 + cached_tokens = list(prompt[:-1]) + send({"kind": "prefill", "metrics": {"wall_ms": prefill_wall_ms, "gpu_ms": prefill_gpu_ms, + "prompt_tokens": len(prompt), "prefix_reused_tokens": common, "new_prompt_tokens": len(prefill_ids), + "prefill_chunk_tokens": len(prefill_ids), "prefix_cache_hit_ratio": common / max(1, len(prompt)-1), + "kernel_count": GlobalCounters.kernel_count, "global_mem_bytes": GlobalCounters.global_mem, + "global_ops": GlobalCounters.global_ops, "profile_kernel_events": len(prefill_profile), + "resume_after_tool": resume_after_tool, "output_head_skipped": True}}) + emitted, prefill_truncated = _emit_kernel_events(prefill_profile, stage="prefill") if prefill_truncated: send({"kind": "metric", "metrics": {"name": "profile_truncated", "stage": "prefill", - "captured": MAX_PROFILE_EVENTS_PER_PHASE, "available": len(prefill_profile)}}) + "captured": emitted, "available": len(prefill_profile)}}) start_pos, last_tok = len(prompt) - 1, prompt[-1] generated = 0 @@ -202,25 +230,30 @@ def encode_message(message) -> list[int]: _hook_event(hook_runtime, hooks, decode_context) GlobalCounters.reset() wall_start = time.perf_counter_ns() - tok, profile = _profiled(lambda: model(Tensor([[last_tok]], device=device), start_pos, temperature, 0, 0.0, 0.0, 0.0).item()) + input_tok = last_tok + tok, profile = _profiled(lambda: model(Tensor([[input_tok]], device=device), start_pos, temperature, 0, 0.0, 0.0, 0.0).item()) wall_ms = (time.perf_counter_ns() - wall_start) / 1e6 gpu_ms = GlobalCounters.time_sum_s * 1e3 + cached_tokens.append(input_tok) # this model invocation materialized input_tok's KV entry start_pos += 1 last_tok = tok if tok in tokenizer.stop_tokens: - send({"kind": "done", "finish_reason": "stop", "metrics": {"generated_tokens": generated}}) + send({"kind": "done", "finish_reason": "stop", "metrics": {"generated_tokens": generated, + "materialized_kv_tokens": len(cached_tokens)}}) break - active_tokens.append(tok) generated += 1 send({"kind": "token", "text": tokenizer.decode([tok]), "metrics": {"index": index, "stage": stage.value, "wall_ms": wall_ms, "gpu_ms": gpu_ms, "kernel_count": GlobalCounters.kernel_count, "global_mem_bytes": GlobalCounters.global_mem, "global_ops": GlobalCounters.global_ops, - "profile_kernel_events": len(profile), "parameter_bandwidth_gbs": (param_bytes / max(GlobalCounters.time_sum_s, 1e-12)) / 1e9}}) + "profile_kernel_events": len(profile), "context_tokens": start_pos, + "parameter_bandwidth_gbs": (param_bytes / max(GlobalCounters.time_sum_s, 1e-12)) / 1e9}}) emitted, truncated = _emit_kernel_events(profile, stage=stage.value, token_index=index) if truncated: send({"kind": "metric", "metrics": {"name": "profile_truncated", "stage": stage.value, "token_index": index, "captured": emitted, "available": len(profile)}}) - else: send({"kind": "done", "finish_reason": "length", "metrics": {"generated_tokens": generated}}) + else: + send({"kind": "done", "finish_reason": "length", "metrics": {"generated_tokens": generated, + "materialized_kv_tokens": len(cached_tokens), "pending_uncached_output_token": True}}) except Exception as exc: send({"kind": "error", "error": str(exc), "error_type": type(exc).__name__}) From 7b02494ac0ba685af67b72034a539baaf1a37261 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:58:08 +0530 Subject: [PATCH 120/168] radeon forge: add exact KV materialization ledger --- extra/radeon_forge/backends/kv_state.py | 38 +++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 extra/radeon_forge/backends/kv_state.py diff --git a/extra/radeon_forge/backends/kv_state.py b/extra/radeon_forge/backends/kv_state.py new file mode 100644 index 0000000000000..0b4a1a2adcc08 --- /dev/null +++ b/extra/radeon_forge/backends/kv_state.py @@ -0,0 +1,38 @@ +from __future__ import annotations + +from dataclasses import dataclass, field +from typing import Sequence + + +@dataclass +class KVReuseLedger: + """Tracks the only prefix that is safe to reuse from one resident KV cache. + + Generated output is not reusable until it has been fed back through the model + as a decode input. This distinction matters at length stops and tool + boundaries: emitted text and materialized KV state are not always identical. + """ + active_session: str | None = None + cached_tokens: list[int] = field(default_factory=list) + + def begin(self, session_id: str, prompt: Sequence[int]) -> int: + if self.active_session != session_id: + self.active_session = session_id + self.cached_tokens.clear() + common = 0 + for old, new in zip(self.cached_tokens, prompt): + if old != new: break + common += 1 + return common + + def commit_prefill(self, prompt_without_pending_decode_token: Sequence[int]) -> None: + self.cached_tokens = list(prompt_without_pending_decode_token) + + def commit_decode_input(self, token: int, expected_position: int) -> None: + if len(self.cached_tokens) != expected_position: + raise RuntimeError(f"KV ledger position mismatch: cached={len(self.cached_tokens)} expected={expected_position}") + self.cached_tokens.append(int(token)) + + def reset(self) -> None: + self.active_session = None + self.cached_tokens.clear() From dad36521ea0d6536b99c7a4fd3a86e8284692285 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:59:03 +0530 Subject: [PATCH 121/168] radeon forge: use tested KV reuse ledger in local inference --- .../backends/tinygrad_llama_worker.py | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/extra/radeon_forge/backends/tinygrad_llama_worker.py b/extra/radeon_forge/backends/tinygrad_llama_worker.py index c674471fb18ae..b98722ccb9d35 100644 --- a/extra/radeon_forge/backends/tinygrad_llama_worker.py +++ b/extra/radeon_forge/backends/tinygrad_llama_worker.py @@ -10,6 +10,7 @@ from examples.llama3 import Tokenizer, build_transformer from ..synthesis.hooks import ExecutionContext, ExecutionStage +from .kv_state import KVReuseLedger from .stage_hooks import ModelStageHookRuntime @@ -146,6 +147,7 @@ def main() -> None: param_bytes = sum(x.nbytes() for x in get_parameters(model)) architecture_data, architecture_hash = _architecture_identity(model, args.size, args.quantize) hook_runtime = ModelStageHookRuntime(model) + kv = KVReuseLedger() device_obj = Device[device] architecture = str(getattr(device_obj, "arch", "")) @@ -157,8 +159,6 @@ def encode_message(message) -> list[int]: if not isinstance(content, str): content = json.dumps(content, default=str) return encode_role(str(message.get("role", "user"))) + tokenizer.encode(content.strip()) + [tokenizer.special_tokens["<|eot_id|>"]] - active_session: str | None = None - cached_tokens: list[int] = [] # only tokens whose KV entries are actually materialized send({"kind": "ready", "name": f"tinygrad-llama-{args.size}", "device": str(device), "gpu": str(device), "architecture": architecture, "runtime": "tinygrad", "runtime_revision": _git_revision(), "model": str(args.model), "model_family": "llama", "model_hash": _weight_identity(args.model, args.size, args.quantize), @@ -172,6 +172,7 @@ def encode_message(message) -> list[int]: payload = json.loads(line) if payload.get("op") == "shutdown": hook_runtime.close() + kv.reset() return if payload.get("op") != "generate": raise ValueError("unsupported operation") request = payload["request"] @@ -190,16 +191,10 @@ def encode_message(message) -> list[int]: if not messages or messages[-1].get("role") != "assistant": prompt += encode_role("assistant") if len(prompt) >= args.max_context: raise ValueError(f"prompt has {len(prompt)} tokens, max context is {args.max_context}") - if active_session != session_id: - active_session, cached_tokens = session_id, [] - common = 0 - for old, new in zip(cached_tokens, prompt): - if old != new: break - common += 1 - + common = kv.begin(session_id, prompt) prefill_context = ExecutionContext(ExecutionStage.PREFILL, batch_size=1, prompt_tokens=len(prompt), context_tokens=max(0, len(prompt)-1), generated_token_index=-1, prefix_reused_tokens=common, - tool_round=tool_round, warm=bool(cached_tokens), attributes={"resume_after_tool": resume_after_tool, "session_id": session_id}) + tool_round=tool_round, warm=bool(kv.cached_tokens), attributes={"resume_after_tool": resume_after_tool, "session_id": session_id}) _hook_event(hook_runtime, hooks, prefill_context) prefill_ids = list(prompt[common:-1]) @@ -208,7 +203,7 @@ def encode_message(message) -> list[int]: _, prefill_profile = _profiled(lambda: _prefill_without_output_head(model, prefill_ids, common, device)) prefill_wall_ms = (time.perf_counter_ns() - prefill_start) / 1e6 prefill_gpu_ms = GlobalCounters.time_sum_s * 1e3 - cached_tokens = list(prompt[:-1]) + kv.commit_prefill(prompt[:-1]) send({"kind": "prefill", "metrics": {"wall_ms": prefill_wall_ms, "gpu_ms": prefill_gpu_ms, "prompt_tokens": len(prompt), "prefix_reused_tokens": common, "new_prompt_tokens": len(prefill_ids), "prefill_chunk_tokens": len(prefill_ids), "prefix_cache_hit_ratio": common / max(1, len(prompt)-1), @@ -234,12 +229,12 @@ def encode_message(message) -> list[int]: tok, profile = _profiled(lambda: model(Tensor([[input_tok]], device=device), start_pos, temperature, 0, 0.0, 0.0, 0.0).item()) wall_ms = (time.perf_counter_ns() - wall_start) / 1e6 gpu_ms = GlobalCounters.time_sum_s * 1e3 - cached_tokens.append(input_tok) # this model invocation materialized input_tok's KV entry + kv.commit_decode_input(input_tok, start_pos) start_pos += 1 last_tok = tok if tok in tokenizer.stop_tokens: send({"kind": "done", "finish_reason": "stop", "metrics": {"generated_tokens": generated, - "materialized_kv_tokens": len(cached_tokens)}}) + "materialized_kv_tokens": len(kv.cached_tokens)}}) break generated += 1 send({"kind": "token", "text": tokenizer.decode([tok]), "metrics": {"index": index, "stage": stage.value, @@ -253,7 +248,7 @@ def encode_message(message) -> list[int]: "captured": emitted, "available": len(profile)}}) else: send({"kind": "done", "finish_reason": "length", "metrics": {"generated_tokens": generated, - "materialized_kv_tokens": len(cached_tokens), "pending_uncached_output_token": True}}) + "materialized_kv_tokens": len(kv.cached_tokens), "pending_uncached_output_token": True}}) except Exception as exc: send({"kind": "error", "error": str(exc), "error_type": type(exc).__name__}) From b1411f8231b4882c0bad16ffe5e9d2561109a133 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:59:22 +0530 Subject: [PATCH 122/168] radeon forge: test exact KV reuse accounting --- test/test_radeon_forge_kv.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 test/test_radeon_forge_kv.py diff --git a/test/test_radeon_forge_kv.py b/test/test_radeon_forge_kv.py new file mode 100644 index 0000000000000..2883eafc12bde --- /dev/null +++ b/test/test_radeon_forge_kv.py @@ -0,0 +1,35 @@ +import unittest + +from extra.radeon_forge.backends.kv_state import KVReuseLedger + + +class TestKVReuseLedger(unittest.TestCase): + def test_emitted_but_unprocessed_token_is_not_reported_as_cache_hit(self): + ledger = KVReuseLedger() + prompt = [1, 2, 3] + self.assertEqual(ledger.begin("session-a", prompt), 0) + ledger.commit_prefill(prompt[:-1]) + ledger.commit_decode_input(prompt[-1], expected_position=2) + self.assertEqual(ledger.cached_tokens, [1, 2, 3]) + + # Token 4 was emitted to the user but generation stopped before it became + # the input to another decode step. It is intentionally absent from KV. + next_prompt = [1, 2, 3, 4, 5] + self.assertEqual(ledger.begin("session-a", next_prompt), 3) + ledger.commit_prefill(next_prompt[:-1]) + self.assertEqual(ledger.cached_tokens, [1, 2, 3, 4]) + + def test_session_switch_invalidates_single_resident_cache(self): + ledger = KVReuseLedger("session-a", [1, 2, 3]) + self.assertEqual(ledger.begin("session-b", [1, 2, 3]), 0) + self.assertEqual(ledger.active_session, "session-b") + self.assertEqual(ledger.cached_tokens, []) + + def test_decode_position_mismatch_fails_closed(self): + ledger = KVReuseLedger("session-a", [1, 2]) + with self.assertRaisesRegex(RuntimeError, "position mismatch"): + ledger.commit_decode_input(3, expected_position=4) + self.assertEqual(ledger.cached_tokens, [1, 2]) + + +if __name__ == "__main__": unittest.main() From 0fa91c57741d04ddc999369da806aa2655c47542 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:59:34 +0530 Subject: [PATCH 123/168] ci: cover exact KV accounting --- .github/workflows/radeon-forge.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/radeon-forge.yml b/.github/workflows/radeon-forge.yml index 275a466f5a122..0ec5b817635b5 100644 --- a/.github/workflows/radeon-forge.yml +++ b/.github/workflows/radeon-forge.yml @@ -34,3 +34,4 @@ jobs: test.test_radeon_forge_runtime test.test_radeon_forge_hooks test.test_radeon_forge_autotune + test.test_radeon_forge_kv From db346c30ffac0f52abaaabe73b47038d4cab347d Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:00:25 +0530 Subject: [PATCH 124/168] radeon forge: advertise native inference-engine capabilities --- extra/radeon_forge/runtime/backend.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/extra/radeon_forge/runtime/backend.py b/extra/radeon_forge/runtime/backend.py index e81c165ab3397..c515c5794b4f1 100644 --- a/extra/radeon_forge/runtime/backend.py +++ b/extra/radeon_forge/runtime/backend.py @@ -13,6 +13,9 @@ class BackendCapabilities: persistent_kv: bool = False kernel_metrics: bool = False cancellation: bool = False + batched_prefill: bool = False + stage_hooks: bool = False + local_only: bool = True @dataclass(frozen=True) From 6609ba397c62629e7f42b579c74abb2019a2f304 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:00:58 +0530 Subject: [PATCH 125/168] radeon forge: add bounded native tool-call stream parser --- extra/radeon_forge/runtime/tool_stream.py | 60 +++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 extra/radeon_forge/runtime/tool_stream.py diff --git a/extra/radeon_forge/runtime/tool_stream.py b/extra/radeon_forge/runtime/tool_stream.py new file mode 100644 index 0000000000000..f90effef378c2 --- /dev/null +++ b/extra/radeon_forge/runtime/tool_stream.py @@ -0,0 +1,60 @@ +from __future__ import annotations + +import json, re, uuid +from dataclasses import dataclass +from typing import Iterable, Mapping, Any + + +_TOOL_CALL = re.compile(r"\s*\s*(\{.*\})\s*\s*", re.S) + + +class ToolProtocolError(ValueError): pass + + +@dataclass(frozen=True) +class ParsedToolCall: + call_id: str + name: str + arguments: Mapping[str, Any] + raw: str + + def to_event(self) -> dict[str, Any]: + return {"id": self.call_id, "name": self.name, "arguments": dict(self.arguments), "raw": self.raw} + + +class ToolStreamParser: + """Incrementally detects the exact local tool-call protocol. + + It is not a grammar decoder yet; it is a bounded early-stop and validation + layer. Malformed or unknown tool calls fail closed rather than being executed + as best-effort text. + """ + def __init__(self, allowed_names: Iterable[str], max_bytes: int = 65536): + self.allowed_names = frozenset(str(name) for name in allowed_names) + self.max_bytes = max_bytes + self.buffer = "" + self.completed = False + + def feed(self, text: str) -> ParsedToolCall | None: + if self.completed: raise ToolProtocolError("tool stream is already complete") + self.buffer += text + if len(self.buffer.encode("utf-8")) > self.max_bytes: raise ToolProtocolError("tool-call stream exceeds size limit") + if "" not in self.buffer: return None + match = _TOOL_CALL.fullmatch(self.buffer) + if match is None: raise ToolProtocolError("malformed tool call: output must contain exactly one wrapped JSON object") + try: payload = json.loads(match.group(1)) + except json.JSONDecodeError as exc: raise ToolProtocolError(f"tool call contains invalid JSON: {exc}") from exc + if not isinstance(payload, dict): raise ToolProtocolError("tool-call payload must be an object") + name, arguments = payload.get("name"), payload.get("arguments", {}) + if not isinstance(name, str) or not name: raise ToolProtocolError("tool call requires a non-empty string name") + if name not in self.allowed_names: raise ToolProtocolError(f"unknown or unavailable local tool {name!r}") + if not isinstance(arguments, dict): raise ToolProtocolError("tool call arguments must be an object") + call_id = payload.get("id") or uuid.uuid4().hex + if not isinstance(call_id, str): raise ToolProtocolError("tool call id must be a string") + self.completed = True + return ParsedToolCall(call_id, name, arguments, self.buffer) + + def finalize(self) -> None: + stripped = self.buffer.lstrip() + if stripped.startswith("") and not self.completed: + raise ToolProtocolError("generation ended with an incomplete tool call") From 0424b876773d030a22c1ca076dfafcf165bd59fc Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:01:14 +0530 Subject: [PATCH 126/168] radeon forge: test bounded structured tool parsing --- test/test_radeon_forge_tool_stream.py | 35 +++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 test/test_radeon_forge_tool_stream.py diff --git a/test/test_radeon_forge_tool_stream.py b/test/test_radeon_forge_tool_stream.py new file mode 100644 index 0000000000000..850154e91ee69 --- /dev/null +++ b/test/test_radeon_forge_tool_stream.py @@ -0,0 +1,35 @@ +import unittest + +from extra.radeon_forge.runtime.tool_stream import ToolProtocolError, ToolStreamParser + + +class TestToolStreamParser(unittest.TestCase): + def test_incremental_valid_tool_call(self): + parser = ToolStreamParser(["read_file", "search_text"]) + self.assertIsNone(parser.feed('{"name":"read_')) + call = parser.feed('file","arguments":{"path":"README.md"}}') + self.assertEqual(call.name, "read_file") + self.assertEqual(call.arguments, {"path":"README.md"}) + self.assertEqual(call.raw, '{"name":"read_file","arguments":{"path":"README.md"}}') + + def test_unknown_tool_fails_closed(self): + parser = ToolStreamParser(["read_file"]) + with self.assertRaisesRegex(ToolProtocolError, "unknown or unavailable"): + parser.feed('{"name":"curl","arguments":{}}') + + def test_malformed_or_incomplete_calls_fail_closed(self): + parser = ToolStreamParser(["read_file"]) + with self.assertRaisesRegex(ToolProtocolError, "exactly one"): + parser.feed('Sure. {"name":"read_file","arguments":{}}') + incomplete = ToolStreamParser(["read_file"]) + incomplete.feed('{"name":"read_file"') + with self.assertRaisesRegex(ToolProtocolError, "incomplete"): + incomplete.finalize() + + def test_stream_size_is_bounded(self): + parser = ToolStreamParser(["read_file"], max_bytes=8) + with self.assertRaisesRegex(ToolProtocolError, "size limit"): + parser.feed("123456789") + + +if __name__ == "__main__": unittest.main() From a02b04da14fc789e9ae600d5a7ec5eea180126fa Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:02:25 +0530 Subject: [PATCH 127/168] radeon forge: early-stop on native structured tool calls --- .../backends/tinygrad_llama_worker.py | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/extra/radeon_forge/backends/tinygrad_llama_worker.py b/extra/radeon_forge/backends/tinygrad_llama_worker.py index b98722ccb9d35..8a105e20ea15c 100644 --- a/extra/radeon_forge/backends/tinygrad_llama_worker.py +++ b/extra/radeon_forge/backends/tinygrad_llama_worker.py @@ -9,6 +9,7 @@ from tinygrad.nn.state import get_parameters from examples.llama3 import Tokenizer, build_transformer +from ..runtime.tool_stream import ToolStreamParser from ..synthesis.hooks import ExecutionContext, ExecutionStage from .kv_state import KVReuseLedger from .stage_hooks import ModelStageHookRuntime @@ -126,6 +127,16 @@ def _prefill_without_output_head(model: Any, token_ids: list[int], start_pos: in h.realize() +def _available_tool_names(tools: Any) -> list[str]: + names = [] + if not isinstance(tools, list): return names + for item in tools: + if not isinstance(item, Mapping): continue + function = item.get("function", {}) + if isinstance(function, Mapping) and isinstance(function.get("name"), str): names.append(str(function["name"])) + return names + + def main() -> None: parser = argparse.ArgumentParser(description="Persistent tinygrad Llama backend for Radeon Forge") parser.add_argument("--model", type=Path, required=True) @@ -164,8 +175,9 @@ def encode_message(message) -> list[int]: "model": str(args.model), "model_family": "llama", "model_hash": _weight_identity(args.model, args.size, args.quantize), "model_architecture_hash": architecture_hash, "model_architecture": architecture_data, "dtype": args.quantize or "model_default", "shapes": {"batch": 1, "max_context": args.max_context}, - "parameter_bytes": param_bytes, "capabilities": {"streaming": True, "structured_tools": False, - "prefix_cache": True, "persistent_kv": True, "kernel_metrics": True, "cancellation": False}}) + "parameter_bytes": param_bytes, "capabilities": {"streaming": True, "structured_tools": True, + "prefix_cache": True, "persistent_kv": True, "kernel_metrics": True, "cancellation": False, + "batched_prefill": True, "stage_hooks": True, "local_only": True}}) for line in sys.stdin: try: @@ -185,6 +197,8 @@ def encode_message(message) -> list[int]: temperature = float(request.get("temperature", 0.0)) tool_round = int(metadata.get("tool_round", 0)) resume_after_tool = bool(metadata.get("resume_after_tool", False)) + tool_names = _available_tool_names(request.get("tools", [])) + tool_parser = ToolStreamParser(tool_names) if tool_names else None prompt = [tokenizer.bos_id] for message in messages: prompt += encode_message(message) @@ -233,11 +247,14 @@ def encode_message(message) -> list[int]: start_pos += 1 last_tok = tok if tok in tokenizer.stop_tokens: + if tool_parser is not None: tool_parser.finalize() send({"kind": "done", "finish_reason": "stop", "metrics": {"generated_tokens": generated, "materialized_kv_tokens": len(kv.cached_tokens)}}) break generated += 1 - send({"kind": "token", "text": tokenizer.decode([tok]), "metrics": {"index": index, "stage": stage.value, + piece = tokenizer.decode([tok]) + parsed_tool = tool_parser.feed(piece) if tool_parser is not None else None + send({"kind": "token", "text": piece, "metrics": {"index": index, "stage": stage.value, "wall_ms": wall_ms, "gpu_ms": gpu_ms, "kernel_count": GlobalCounters.kernel_count, "global_mem_bytes": GlobalCounters.global_mem, "global_ops": GlobalCounters.global_ops, "profile_kernel_events": len(profile), "context_tokens": start_pos, @@ -246,7 +263,13 @@ def encode_message(message) -> list[int]: if truncated: send({"kind": "metric", "metrics": {"name": "profile_truncated", "stage": stage.value, "token_index": index, "captured": emitted, "available": len(profile)}}) + if parsed_tool is not None: + send({"kind": "tool_call", "tool_call": parsed_tool.to_event()}) + send({"kind": "done", "finish_reason": "tool_call", "metrics": {"generated_tokens": generated, + "materialized_kv_tokens": len(kv.cached_tokens), "tool_name": parsed_tool.name}}) + break else: + if tool_parser is not None: tool_parser.finalize() send({"kind": "done", "finish_reason": "length", "metrics": {"generated_tokens": generated, "materialized_kv_tokens": len(kv.cached_tokens), "pending_uncached_output_token": True}}) except Exception as exc: From 2e6be35758ad2bb7e6f770f9c1289f4cbc4293ee Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:03:18 +0530 Subject: [PATCH 128/168] radeon forge: preserve exact structured tool transcripts --- extra/radeon_forge/runtime/session.py | 34 ++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/extra/radeon_forge/runtime/session.py b/extra/radeon_forge/runtime/session.py index 6a1ffcbeb8d63..d02c7661fdaa2 100644 --- a/extra/radeon_forge/runtime/session.py +++ b/extra/radeon_forge/runtime/session.py @@ -1,6 +1,6 @@ from __future__ import annotations -import threading, time, uuid +import json, threading, time, uuid from dataclasses import asdict, dataclass, field from enum import Enum from typing import Any, Callable, Mapping @@ -39,6 +39,7 @@ def __init__(self, backend: InferenceBackend, tools: ToolRegistry, system_prompt self.messages: list[dict[str, Any]] = [{"role": "system", "content": self._system_prompt()}] self.events: list[SessionEvent] = [] self.pending_tool_call: ToolCall | None = None + self.pending_assistant_content = "" self.partial_output = "" self.state = SessionState.IDLE self._sequence = 0 @@ -55,6 +56,11 @@ def _system_prompt(self) -> str: claim a tool result before it is returned. All inference and tools are local. """ + @staticmethod + def _tool_content(call: ToolCall) -> str: + payload = {"id": call.call_id, "name": call.name, "arguments": dict(call.arguments)} + return f"{json.dumps(payload, separators=(',', ':'))}" + def _emit(self, kind: str, **data: Any) -> SessionEvent: self._sequence += 1 event = SessionEvent(self._sequence, kind, data) @@ -96,6 +102,7 @@ def _request_metadata(self, step: int) -> dict[str, Any]: def _generate(self, max_tokens: int, temperature: float) -> tuple[SessionEvent, ...]: self.state = SessionState.GENERATING self.partial_output = "" + self.pending_assistant_content = "" started_at = len(self.events) pieces: list[str] = [] step = sum(1 for event in self.events if event.kind == "generation_started") + 1 @@ -116,7 +123,13 @@ def _generate(self, max_tokens: int, temperature: float) -> tuple[SessionEvent, text=event.text, **token_metrics) elif event.kind in {"prefill", "decode", "kernel", "metric", "hook"}: self._record_backend_event(event, parent.event_id) elif event.kind == "tool_call" and event.tool_call is not None: - self.pending_tool_call = ToolCall(str(event.tool_call.get("id") or uuid.uuid4().hex), str(event.tool_call["name"]), event.tool_call.get("arguments", {})) + arguments = event.tool_call.get("arguments", {}) + if not isinstance(arguments, Mapping): raise ValueError("backend tool-call arguments must be an object") + self.pending_tool_call = ToolCall(str(event.tool_call.get("id") or uuid.uuid4().hex), + str(event.tool_call["name"]), dict(arguments)) + raw = event.tool_call.get("raw") + if isinstance(raw, str): self.pending_assistant_content = raw + self._emit("structured_tool_call", call=asdict(self.pending_tool_call), native=True) elif event.kind == "done": self._emit("generation_done", finish_reason=event.finish_reason, metrics=dict(event.metrics)) except Exception as exc: self.state = SessionState.FAILED @@ -124,9 +137,12 @@ def _generate(self, max_tokens: int, temperature: float) -> tuple[SessionEvent, raise output = "".join(pieces) if self.pending_tool_call is None: - try: self.pending_tool_call = parse_tool_call(output) + try: + self.pending_tool_call = parse_tool_call(output) + if self.pending_tool_call is not None: self.pending_assistant_content = output except Exception as exc: self._emit("tool_parse_error", error=str(exc), raw=output) if self.pending_tool_call is not None: + if not self.pending_assistant_content: self.pending_assistant_content = self._tool_content(self.pending_tool_call) self.partial_output = "" self.state = SessionState.AWAITING_TOOL_APPROVAL self._emit("tool_approval_required", call=asdict(self.pending_tool_call), action=self.tools.spec(self.pending_tool_call.name).action.value) @@ -141,6 +157,7 @@ def approve_tool(self, permission_token: str, max_tokens: int = 512) -> tuple[Se with self._lock: if self.state is not SessionState.AWAITING_TOOL_APPROVAL or self.pending_tool_call is None: raise RuntimeError("no tool call awaits approval") call = self.pending_tool_call + assistant_content = self.pending_assistant_content or self._tool_content(call) started_at = len(self.events) self.state = SessionState.RUNNING_TOOL try: @@ -153,9 +170,10 @@ def approve_tool(self, permission_token: str, max_tokens: int = 512) -> tuple[Se self._emit("tool_authorization_failed", call_id=call.call_id, error=str(exc), error_type=type(exc).__name__) raise self._emit("tool_result", result=asdict(result)) - self.messages.append({"role": "assistant", "content": f"{{\"name\":\"{call.name}\"}}"}) + self.messages.append({"role": "assistant", "content": assistant_content}) self.messages.append({"role": "tool", "name": call.name, "tool_call_id": call.call_id, "content": str(result.output)}) self.pending_tool_call = None + self.pending_assistant_content = "" if not result.ok: self.state = SessionState.FAILED return tuple(self.events[started_at:]) @@ -167,8 +185,11 @@ def reject_tool(self, reason: str) -> SessionEvent: with self._lock: if self.pending_tool_call is None: raise RuntimeError("no pending tool call") call = self.pending_tool_call - self.pending_tool_call = None + assistant_content = self.pending_assistant_content or self._tool_content(call) + self.messages.append({"role": "assistant", "content": assistant_content}) self.messages.append({"role": "tool", "name": call.name, "tool_call_id": call.call_id, "content": f"User rejected tool call: {reason}"}) + self.pending_tool_call = None + self.pending_assistant_content = "" self.state = SessionState.IDLE return self._emit("tool_rejected", call_id=call.call_id, reason=reason) @@ -178,4 +199,5 @@ def events_after(self, sequence: int) -> list[dict[str, Any]]: def snapshot(self) -> dict[str, Any]: return {"session_id": self.session_id, "state": self.state.value, "messages": list(self.messages), "events": [asdict(x) for x in self.events], "pending_tool_call": asdict(self.pending_tool_call) if self.pending_tool_call else None, - "partial_output": self.partial_output, "trace_id": self.trace.trace_id} + "pending_assistant_content": self.pending_assistant_content, "partial_output": self.partial_output, + "trace_id": self.trace.trace_id} From 370d5caeed6193ea2d0050e31434d156f379f7de Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:04:15 +0530 Subject: [PATCH 129/168] radeon forge: test lossless native tool transcripts --- test/test_radeon_forge_runtime.py | 37 ++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/test/test_radeon_forge_runtime.py b/test/test_radeon_forge_runtime.py index 03ca8b87fa48c..ce1ed413ef422 100644 --- a/test/test_radeon_forge_runtime.py +++ b/test/test_radeon_forge_runtime.py @@ -55,6 +55,22 @@ def stream(self, request): def close(self): self.release.set() +class NativeToolBackend: + RAW = '{"id":"call-7","name":"read_file","arguments":{"path":"hello.txt","start_line":2}}' + @property + def name(self): return "native-tool-local" + @property + def capabilities(self): return BackendCapabilities(structured_tools=True) + @property + def runtime_metadata(self): return {"runtime":"test", "architecture":"gfx1100", "model_family":"llama"} + def stream(self, request): + yield GenerationEvent("token", self.RAW, metrics={"index":0, "stage":"first_token", "wall_ms":1.0}) + yield GenerationEvent("tool_call", tool_call={"id":"call-7", "name":"read_file", + "arguments":{"path":"hello.txt", "start_line":2}, "raw":self.RAW}) + yield GenerationEvent("done", finish_reason="tool_call", metrics={"generated_tokens":1}) + def close(self): pass + + class TestRadeonForgeRuntime(unittest.TestCase): def test_kernel_evidence_survives_unified_trace_and_is_ranked_by_stage(self): with tempfile.TemporaryDirectory() as directory: @@ -98,7 +114,7 @@ def test_async_job_exposes_partial_generation(self): self.assertEqual(session.messages[-1]["content"], "partial complete") engine.close() - def test_permissioned_tool_round_trip(self): + def test_permissioned_tool_round_trip_preserves_arguments(self): responses = [ '{"name":"read_file","arguments":{"path":"hello.txt"}}', "The private file was read successfully.", @@ -115,9 +131,28 @@ def test_permissioned_tool_round_trip(self): session.approve_tool(token) self.assertEqual(session.state, SessionState.COMPLETED) self.assertEqual(session.messages[-1]["content"], "The private file was read successfully.") + assistant_tool = next(message for message in session.messages if message.get("role") == "assistant" and "" in message.get("content", "")) + self.assertIn('"path":"hello.txt"', assistant_tool["content"]) self.assertTrue(any(event.kind == "tool_result" for event in session.events)) engine.close() + def test_native_structured_tool_call_and_rejection_are_lossless(self): + with tempfile.TemporaryDirectory() as directory: + Path(directory, "hello.txt").write_text("line1\nline2\n", encoding="utf-8") + engine = ForgeEngine(NativeToolBackend(), directory) + session = engine.create_session() + session.send("read line two") + self.assertEqual(session.state, SessionState.AWAITING_TOOL_APPROVAL) + self.assertEqual(session.pending_tool_call.call_id, "call-7") + self.assertEqual(session.pending_tool_call.arguments["start_line"], 2) + self.assertEqual(session.pending_assistant_content, NativeToolBackend.RAW) + session.reject_tool("not now") + self.assertEqual(session.state, SessionState.IDLE) + self.assertEqual(session.messages[-2]["content"], NativeToolBackend.RAW) + self.assertEqual(session.messages[-1]["tool_call_id"], "call-7") + self.assertIn("not now", session.messages[-1]["content"]) + engine.close() + def test_workspace_paths_cannot_escape(self): with tempfile.TemporaryDirectory() as directory: tools = WorkspaceTools(directory) From 8c233076370a49d88179ff662f5cc77faa7167a0 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:05:02 +0530 Subject: [PATCH 130/168] radeon forge: expose native model-serving stream --- extra/radeon_forge/runtime/engine.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/extra/radeon_forge/runtime/engine.py b/extra/radeon_forge/runtime/engine.py index f0af8af0dc04e..a9f9890219553 100644 --- a/extra/radeon_forge/runtime/engine.py +++ b/extra/radeon_forge/runtime/engine.py @@ -3,12 +3,12 @@ import threading, uuid from dataclasses import asdict from pathlib import Path -from typing import Any, Mapping +from typing import Any, Iterator, Mapping, Sequence from ..permissions import PermissionController from ..profiling.report import build_profile_report from ..synthesis import CandidateWorkspace, OptimizationTools, RuntimeFingerprint, SafeHookRegistry, install_default_specs -from .backend import InferenceBackend +from .backend import GenerationEvent, GenerationRequest, InferenceBackend from .jobs import LocalJobManager from .session import AgentSession from .tools import ToolCall, ToolRegistry, WorkspaceTools @@ -40,7 +40,7 @@ def runtime_fingerprint(self) -> RuntimeFingerprint: if callable(metadata): metadata = metadata() return RuntimeFingerprint.from_mapping(metadata if isinstance(metadata, Mapping) else {}) - def _session_metadata(self, session: AgentSession, step: int) -> Mapping[str, Any]: + def inference_metadata(self) -> dict[str, Any]: metadata = self.hooks.runtime_metadata() for item in metadata.get("active_hooks", []): try: @@ -49,6 +49,16 @@ def _session_metadata(self, session: AgentSession, step: int) -> Mapping[str, An except Exception: item["parameters"] = {} return {**metadata, "runtime_fingerprint": asdict(self.runtime_fingerprint())} + def _session_metadata(self, session: AgentSession, step: int) -> Mapping[str, Any]: return self.inference_metadata() + + def stream_inference(self, messages: Sequence[Mapping[str, Any]], tools: Sequence[Mapping[str, Any]] = (), + max_tokens: int = 256, temperature: float = 0.0, session_id: str | None = None, + stop: Sequence[str] = ()) -> Iterator[GenerationEvent]: + """Serve the resident local model without losing Forge hooks or profiling events.""" + request = GenerationRequest(session_id or uuid.uuid4().hex, tuple(messages), tuple(tools), max_tokens, temperature, + tuple(stop), metadata=self.inference_metadata()) + return self.backend.stream(request) + def create_session(self) -> AgentSession: with self._lock: session = AgentSession(self.backend, self.tools, self.system_prompt, metadata_provider=self._session_metadata) From 6d7e6beec9666220185c179488ea5e11c1753e21 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:05:54 +0530 Subject: [PATCH 131/168] radeon forge: add OpenAI-compatible local serving adapter --- extra/radeon_forge/ui/openai_api.py | 102 ++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 extra/radeon_forge/ui/openai_api.py diff --git a/extra/radeon_forge/ui/openai_api.py b/extra/radeon_forge/ui/openai_api.py new file mode 100644 index 0000000000000..0bf51e74ecbc8 --- /dev/null +++ b/extra/radeon_forge/ui/openai_api.py @@ -0,0 +1,102 @@ +from __future__ import annotations + +import json, statistics, time, uuid +from collections import defaultdict +from typing import Any, Iterable, Mapping, Sequence + +from ..runtime import ForgeEngine, GenerationEvent + + +class OpenAIRequestError(ValueError): pass + + +def _request(payload: Mapping[str, Any]) -> tuple[list[Mapping[str, Any]], list[Mapping[str, Any]], int, float, str, tuple[str, ...]]: + messages = payload.get("messages") + if not isinstance(messages, list) or not messages or not all(isinstance(item, Mapping) for item in messages): + raise OpenAIRequestError("messages must be a non-empty array of objects") + tools = payload.get("tools", []) + if not isinstance(tools, list) or not all(isinstance(item, Mapping) for item in tools): raise OpenAIRequestError("tools must be an array") + max_tokens = int(payload.get("max_tokens", payload.get("max_completion_tokens", 256))) + if max_tokens <= 0: raise OpenAIRequestError("max_tokens must be positive") + temperature = float(payload.get("temperature", 0.0)) + session_id = str(payload.get("session_id") or payload.get("user") or uuid.uuid4().hex) + stop = payload.get("stop", ()) + if isinstance(stop, str): stop = (stop,) + elif isinstance(stop, list) and all(isinstance(item, str) for item in stop): stop = tuple(stop) + elif stop in (None, ()): stop = () + else: raise OpenAIRequestError("stop must be a string or array of strings") + return list(messages), list(tools), max_tokens, temperature, session_id, tuple(stop) + + +def _tool_call(value: Mapping[str, Any]) -> dict[str, Any]: + return {"id": str(value.get("id") or uuid.uuid4().hex), "type": "function", + "function": {"name": str(value["name"]), "arguments": json.dumps(value.get("arguments", {}), separators=(",", ":"))}} + + +def _finish_reason(value: str | None) -> str | None: + return "tool_calls" if value == "tool_call" else value + + +def _summarize(events: Sequence[GenerationEvent], session_id: str) -> dict[str, Any]: + prefill = next((dict(event.metrics) for event in events if event.kind == "prefill"), {}) + tokens = [dict(event.metrics) for event in events if event.kind == "token"] + by_stage: dict[str, list[float]] = defaultdict(list) + kernel_calls: dict[str, int] = defaultdict(int) + hook_events = [] + for event in events: + if event.kind == "token": + wall = event.metrics.get("wall_ms") + if wall is not None: by_stage[str(event.metrics.get("stage", "decode"))].append(float(wall)) + elif event.kind == "kernel": kernel_calls[str(event.metrics.get("stage", "unknown"))] += 1 + elif event.kind == "hook": hook_events.append(dict(event.metrics)) + return {"session_id": session_id, "prefill": prefill, + "token_latency_ms": {stage: {"count": len(values), "p50": statistics.median(values), "max": max(values)} + for stage, values in by_stage.items() if values}, + "kernel_calls_by_stage": dict(kernel_calls), "hook_transitions": hook_events, + "generated_tokens": len(tokens)} + + +def collect_chat_completion(engine: ForgeEngine, payload: Mapping[str, Any]) -> dict[str, Any]: + messages, tools, max_tokens, temperature, session_id, stop = _request(payload) + events = list(engine.stream_inference(messages, tools, max_tokens, temperature, session_id, stop)) + text = "".join(event.text for event in events if event.kind == "token") + tool = next((event.tool_call for event in events if event.kind == "tool_call" and event.tool_call is not None), None) + done = next((event for event in reversed(events) if event.kind == "done"), None) + prefill = next((event for event in events if event.kind == "prefill"), None) + completion_tokens = int(done.metrics.get("generated_tokens", 0)) if done is not None else sum(event.kind == "token" for event in events) + prompt_tokens = int(prefill.metrics.get("prompt_tokens", 0)) if prefill is not None else 0 + message: dict[str, Any] = {"role": "assistant", "content": None if tool is not None else text} + if tool is not None: message["tool_calls"] = [_tool_call(tool)] + return {"id": f"chatcmpl-{uuid.uuid4().hex}", "object": "chat.completion", "created": int(time.time()), + "model": str(payload.get("model") or engine.backend.name), + "choices": [{"index": 0, "message": message, "finish_reason": _finish_reason(done.finish_reason if done else None)}], + "usage": {"prompt_tokens": prompt_tokens, "completion_tokens": completion_tokens, + "total_tokens": prompt_tokens + completion_tokens}, + "system_fingerprint": engine.runtime_fingerprint().architecture or None, + "forge": _summarize(events, session_id)} + + +def stream_chat_completion(engine: ForgeEngine, payload: Mapping[str, Any]) -> Iterable[str]: + messages, tools, max_tokens, temperature, session_id, stop = _request(payload) + completion_id, created = f"chatcmpl-{uuid.uuid4().hex}", int(time.time()) + model = str(payload.get("model") or engine.backend.name) + first = True + for event in engine.stream_inference(messages, tools, max_tokens, temperature, session_id, stop): + if event.kind == "token": + delta = {"content": event.text} + if first: delta = {"role": "assistant", **delta}; first = False + chunk = {"id": completion_id, "object": "chat.completion.chunk", "created": created, "model": model, + "choices": [{"index": 0, "delta": delta, "finish_reason": None}]} + yield f"data: {json.dumps(chunk, separators=(',', ':'))}\n\n" + elif event.kind == "tool_call" and event.tool_call is not None: + delta = {"tool_calls": [{"index": 0, **_tool_call(event.tool_call)}]} + if first: delta = {"role": "assistant", **delta}; first = False + chunk = {"id": completion_id, "object": "chat.completion.chunk", "created": created, "model": model, + "choices": [{"index": 0, "delta": delta, "finish_reason": None}]} + yield f"data: {json.dumps(chunk, separators=(',', ':'))}\n\n" + elif event.kind == "done": + chunk = {"id": completion_id, "object": "chat.completion.chunk", "created": created, "model": model, + "choices": [{"index": 0, "delta": {}, "finish_reason": _finish_reason(event.finish_reason)}], + "forge_session_id": session_id} + yield f"data: {json.dumps(chunk, separators=(',', ':'))}\n\n" + yield "data: [DONE]\n\n" From 6cd0466cacefd1971c5af7caf8f4a9dd992f784d Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:06:54 +0530 Subject: [PATCH 132/168] radeon forge: hide raw tool protocol from standard streams --- extra/radeon_forge/ui/openai_api.py | 42 +++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/extra/radeon_forge/ui/openai_api.py b/extra/radeon_forge/ui/openai_api.py index 0bf51e74ecbc8..0ff666b56d17e 100644 --- a/extra/radeon_forge/ui/openai_api.py +++ b/extra/radeon_forge/ui/openai_api.py @@ -7,6 +7,9 @@ from ..runtime import ForgeEngine, GenerationEvent +_TOOL_PREFIX = "" + + class OpenAIRequestError(ValueError): pass @@ -33,8 +36,7 @@ def _tool_call(value: Mapping[str, Any]) -> dict[str, Any]: "function": {"name": str(value["name"]), "arguments": json.dumps(value.get("arguments", {}), separators=(",", ":"))}} -def _finish_reason(value: str | None) -> str | None: - return "tool_calls" if value == "tool_call" else value +def _finish_reason(value: str | None) -> str | None: return "tool_calls" if value == "tool_call" else value def _summarize(events: Sequence[GenerationEvent], session_id: str) -> dict[str, Any]: @@ -76,25 +78,49 @@ def collect_chat_completion(engine: ForgeEngine, payload: Mapping[str, Any]) -> "forge": _summarize(events, session_id)} +def _content_chunk(completion_id: str, created: int, model: str, text: str, include_role: bool) -> str: + delta = {"content": text} + if include_role: delta = {"role": "assistant", **delta} + chunk = {"id": completion_id, "object": "chat.completion.chunk", "created": created, "model": model, + "choices": [{"index": 0, "delta": delta, "finish_reason": None}]} + return f"data: {json.dumps(chunk, separators=(',', ':'))}\n\n" + + +def _could_be_tool_protocol(text: str) -> bool: + stripped = text.lstrip() + return _TOOL_PREFIX.startswith(stripped) or stripped.startswith(_TOOL_PREFIX) + + def stream_chat_completion(engine: ForgeEngine, payload: Mapping[str, Any]) -> Iterable[str]: messages, tools, max_tokens, temperature, session_id, stop = _request(payload) completion_id, created = f"chatcmpl-{uuid.uuid4().hex}", int(time.time()) model = str(payload.get("model") or engine.backend.name) - first = True + first, buffered, buffering_tool = True, "", False for event in engine.stream_inference(messages, tools, max_tokens, temperature, session_id, stop): if event.kind == "token": - delta = {"content": event.text} - if first: delta = {"role": "assistant", **delta}; first = False - chunk = {"id": completion_id, "object": "chat.completion.chunk", "created": created, "model": model, - "choices": [{"index": 0, "delta": delta, "finish_reason": None}]} - yield f"data: {json.dumps(chunk, separators=(',', ':'))}\n\n" + if first or buffering_tool: + buffered += event.text + if _could_be_tool_protocol(buffered): + buffering_tool = True + continue + if buffered: + yield _content_chunk(completion_id, created, model, buffered, first) + first, buffered, buffering_tool = False, "", False + else: + yield _content_chunk(completion_id, created, model, event.text, False) elif event.kind == "tool_call" and event.tool_call is not None: + # The native worker already validated the buffered text. Standard clients + # receive only the structured delta, never Forge's internal text protocol. + buffered, buffering_tool = "", False delta = {"tool_calls": [{"index": 0, **_tool_call(event.tool_call)}]} if first: delta = {"role": "assistant", **delta}; first = False chunk = {"id": completion_id, "object": "chat.completion.chunk", "created": created, "model": model, "choices": [{"index": 0, "delta": delta, "finish_reason": None}]} yield f"data: {json.dumps(chunk, separators=(',', ':'))}\n\n" elif event.kind == "done": + if buffered: + yield _content_chunk(completion_id, created, model, buffered, first) + first, buffered = False, "" chunk = {"id": completion_id, "object": "chat.completion.chunk", "created": created, "model": model, "choices": [{"index": 0, "delta": {}, "finish_reason": _finish_reason(event.finish_reason)}], "forge_session_id": session_id} From 3125b974a12edf34840df32a3789507bba06656d Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:07:48 +0530 Subject: [PATCH 133/168] radeon forge: expose OpenAI-compatible local API --- extra/radeon_forge/ui/server.py | 39 ++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/extra/radeon_forge/ui/server.py b/extra/radeon_forge/ui/server.py index e197a1ac89cec..ccde88679dab2 100644 --- a/extra/radeon_forge/ui/server.py +++ b/extra/radeon_forge/ui/server.py @@ -1,15 +1,16 @@ from __future__ import annotations -import argparse, json, mimetypes, re, sys +import argparse, json, mimetypes, re, sys, time from dataclasses import asdict from http import HTTPStatus from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer from pathlib import Path -from typing import Any +from typing import Any, Iterable from urllib.parse import parse_qs, urlparse from ..backends.fake import ScriptedBackend from ..runtime import ForgeEngine, JsonlProcessBackend +from .openai_api import OpenAIRequestError, collect_chat_completion, stream_chat_completion class ForgeRequestHandler(BaseHTTPRequestHandler): @@ -27,9 +28,24 @@ def _json(self, payload: Any, status: int = 200): self.end_headers() self.wfile.write(body) + def _sse(self, chunks: Iterable[str]): + self.send_response(200) + self.send_header("Content-Type", "text/event-stream") + self.send_header("Cache-Control", "no-cache") + self.send_header("Connection", "close") + self.end_headers() + try: + for chunk in chunks: + self.wfile.write(chunk.encode()) + self.wfile.flush() + except (BrokenPipeError, ConnectionResetError): pass + finally: self.close_connection = True + def _body(self) -> dict[str, Any]: length = int(self.headers.get("Content-Length", "0")) - return json.loads(self.rfile.read(length) or b"{}") + payload = json.loads(self.rfile.read(length) or b"{}") + if not isinstance(payload, dict): raise ValueError("request body must be a JSON object") + return payload def _static(self, name: str): path = (self.static_root / name).resolve() @@ -52,6 +68,11 @@ def do_GET(self): try: if path == "/": return self._static("index.html") if path in {"/app.js", "/style.css", "/kernels.css"}: return self._static(path[1:]) + if path == "/v1/models": + return self._json({"object":"list", "data":[{"id":self.engine.backend.name, "object":"model", + "created":int(time.time()), "owned_by":"local-radeon-forge", + "forge": {"runtime_fingerprint":asdict(self.engine.runtime_fingerprint()), + "capabilities":asdict(self.engine.backend.capabilities)}}]}) if path == "/api/health": return self._json({"ok": True, "backend": self.engine.backend.name, "capabilities": asdict(self.engine.backend.capabilities), "runtime_fingerprint": asdict(self.engine.runtime_fingerprint())}) if path == "/api/sessions": return self._json(self.engine.sessions()) @@ -75,6 +96,9 @@ def do_POST(self): path = urlparse(self.path).path try: body = self._body() + if path == "/v1/chat/completions": + if bool(body.get("stream", False)): return self._sse(stream_chat_completion(self.engine, body)) + return self._json(collect_chat_completion(self.engine, body)) if path == "/api/sessions": return self._json(self.engine.create_session().snapshot(), HTTPStatus.CREATED) if path == "/api/recipes/import": result = self.engine.execute_explicit_ui_tool("import_forge_recipe", {"path": str(body.get("path", ""))}, @@ -118,6 +142,8 @@ def do_POST(self): event = session.reject_tool(str(body.get("reason", "Rejected by user"))) return self._json({"event": asdict(event), "session": session.snapshot()}) return self.send_error(404) + except OpenAIRequestError as exc: + return self._json({"error":{"message":str(exc), "type":"invalid_request_error", "param":None, "code":None}}, 400) except (ValueError, RuntimeError, FileNotFoundError) as exc: return self._json({"error": str(exc), "type": type(exc).__name__}, 400) except Exception as exc: return self._json({"error": str(exc), "type": type(exc).__name__}, 500) @@ -133,7 +159,7 @@ def build_backend(args): def main(): - parser = argparse.ArgumentParser(description="Radeon Forge local inference and profiling UI") + parser = argparse.ArgumentParser(description="Radeon Forge local inference, agent and profiling server") parser.add_argument("--workspace", type=Path, default=Path.cwd()) parser.add_argument("--backend", choices=("scripted", "tinygrad-llama"), default="scripted") parser.add_argument("--model", type=Path) @@ -144,11 +170,12 @@ def main(): parser.add_argument("--host", default="127.0.0.1") parser.add_argument("--port", type=int, default=7790) args = parser.parse_args() - if args.host not in {"127.0.0.1", "localhost", "::1"}: raise SystemExit("Forge UI binds to loopback only") + if args.host not in {"127.0.0.1", "localhost", "::1"}: raise SystemExit("Forge binds to loopback only") engine = ForgeEngine(build_backend(args), args.workspace) ForgeRequestHandler.engine = engine server = ThreadingHTTPServer((args.host, args.port), ForgeRequestHandler) - print(f"Radeon Forge UI: http://{args.host}:{args.port} backend={engine.backend.name} workspace={args.workspace.resolve()}") + print(f"Radeon Forge: http://{args.host}:{args.port} backend={engine.backend.name} workspace={args.workspace.resolve()}") + print(f"OpenAI-compatible API: http://{args.host}:{args.port}/v1") try: server.serve_forever() finally: engine.close() From 2af3b951d83d74e4012f5fd08c2f498b3be06704 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:08:21 +0530 Subject: [PATCH 134/168] radeon forge: test OpenAI-compatible local API adapter --- test/test_radeon_forge_openai.py | 74 ++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 test/test_radeon_forge_openai.py diff --git a/test/test_radeon_forge_openai.py b/test/test_radeon_forge_openai.py new file mode 100644 index 0000000000000..f91ed09315afd --- /dev/null +++ b/test/test_radeon_forge_openai.py @@ -0,0 +1,74 @@ +import json +import tempfile +import unittest + +from extra.radeon_forge.backends.fake import ScriptedBackend +from extra.radeon_forge.runtime import BackendCapabilities, ForgeEngine, GenerationEvent +from extra.radeon_forge.ui.openai_api import OpenAIRequestError, collect_chat_completion, stream_chat_completion + + +class ToolBackend: + RAW = '{"id":"call-1","name":"read_file","arguments":{"path":"README.md"}}' + @property + def name(self): return "tool-model" + @property + def capabilities(self): return BackendCapabilities(structured_tools=True, local_only=True) + @property + def runtime_metadata(self): return {"architecture":"gfx1100", "runtime":"tinygrad", "model_family":"llama"} + def stream(self, request): + yield GenerationEvent("prefill", metrics={"prompt_tokens":12, "wall_ms":2.0}) + yield GenerationEvent("token", self.RAW[:20], metrics={"stage":"first_token", "wall_ms":1.0}) + yield GenerationEvent("token", self.RAW[20:], metrics={"stage":"decode", "wall_ms":1.0}) + yield GenerationEvent("tool_call", tool_call={"id":"call-1", "name":"read_file", + "arguments":{"path":"README.md"}, "raw":self.RAW}) + yield GenerationEvent("done", finish_reason="tool_call", metrics={"generated_tokens":2}) + def close(self): pass + + +class TestOpenAIAdapter(unittest.TestCase): + def test_regular_completion_has_standard_shape_and_forge_metrics(self): + with tempfile.TemporaryDirectory() as directory: + engine = ForgeEngine(ScriptedBackend(["hello local world"]), directory) + result = collect_chat_completion(engine, {"model":"local", "messages":[{"role":"user", "content":"hello"}], + "user":"stable-session"}) + self.assertEqual(result["object"], "chat.completion") + self.assertEqual(result["choices"][0]["message"]["content"], "hello local world") + self.assertEqual(result["choices"][0]["finish_reason"], "stop") + self.assertEqual(result["forge"]["session_id"], "stable-session") + self.assertGreater(result["usage"]["completion_tokens"], 0) + engine.close() + + def test_tool_completion_is_structured_and_raw_protocol_is_not_content(self): + with tempfile.TemporaryDirectory() as directory: + engine = ForgeEngine(ToolBackend(), directory) + payload = {"messages":[{"role":"user", "content":"read README"}], "tools":[{"type":"function", + "function":{"name":"read_file", "description":"read", "parameters":{"type":"object"}}}]} + result = collect_chat_completion(engine, payload) + message = result["choices"][0]["message"] + self.assertIsNone(message["content"]) + self.assertEqual(message["tool_calls"][0]["function"]["name"], "read_file") + self.assertEqual(json.loads(message["tool_calls"][0]["function"]["arguments"]), {"path":"README.md"}) + self.assertEqual(result["choices"][0]["finish_reason"], "tool_calls") + engine.close() + + def test_stream_suppresses_internal_tool_markup(self): + with tempfile.TemporaryDirectory() as directory: + engine = ForgeEngine(ToolBackend(), directory) + payload = {"stream":True, "messages":[{"role":"user", "content":"read README"}], "tools":[{"type":"function", + "function":{"name":"read_file", "description":"read", "parameters":{"type":"object"}}}]} + stream = list(stream_chat_completion(engine, payload)) + joined = "".join(stream) + self.assertNotIn("", joined) + self.assertIn('"tool_calls"', joined) + self.assertIn('"finish_reason":"tool_calls"', joined) + self.assertEqual(stream[-1], "data: [DONE]\n\n") + engine.close() + + def test_invalid_messages_fail_before_model_execution(self): + with tempfile.TemporaryDirectory() as directory: + engine = ForgeEngine(ScriptedBackend(), directory) + with self.assertRaises(OpenAIRequestError): collect_chat_completion(engine, {"messages":[]}) + engine.close() + + +if __name__ == "__main__": unittest.main() From fe23afa78a5dc0084abc8cc7b3e7e1e8a938fcd1 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:08:37 +0530 Subject: [PATCH 135/168] ci: cover structured tools and OpenAI API --- .github/workflows/radeon-forge.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/radeon-forge.yml b/.github/workflows/radeon-forge.yml index 0ec5b817635b5..d2d02453ad2ab 100644 --- a/.github/workflows/radeon-forge.yml +++ b/.github/workflows/radeon-forge.yml @@ -35,3 +35,5 @@ jobs: test.test_radeon_forge_hooks test.test_radeon_forge_autotune test.test_radeon_forge_kv + test.test_radeon_forge_tool_stream + test.test_radeon_forge_openai From bd4ff58ed7a2d2c17c51946dc65d8f0cc21112ca Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:10:50 +0530 Subject: [PATCH 136/168] radeon forge: render OpenAI tools into local model prompts --- extra/radeon_forge/runtime/tool_prompt.py | 35 +++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 extra/radeon_forge/runtime/tool_prompt.py diff --git a/extra/radeon_forge/runtime/tool_prompt.py b/extra/radeon_forge/runtime/tool_prompt.py new file mode 100644 index 0000000000000..59c0e762de695 --- /dev/null +++ b/extra/radeon_forge/runtime/tool_prompt.py @@ -0,0 +1,35 @@ +from __future__ import annotations + +import json +from typing import Any, Mapping, Sequence + + +TOOL_PROTOCOL_MARKER = "Radeon Forge local tool protocol" + + +def render_tool_instruction(tools: Sequence[Mapping[str, Any]]) -> str: + rows = [] + for item in tools: + function = item.get("function", {}) if isinstance(item, Mapping) else {} + if not isinstance(function, Mapping) or not isinstance(function.get("name"), str): continue + rows.append({"name": function["name"], "description": str(function.get("description", "")), + "parameters": function.get("parameters", {"type":"object","properties":{}})}) + if not rows: return "" + return f"""{TOOL_PROTOCOL_MARKER}. +You may call only one of the tools listed below. When a tool is necessary, output exactly one object and no surrounding prose: +{{"name":"tool_name","arguments":{{...}}}} +Do not invent a result. Tool execution requires user approval and the result will be returned in a later message. +Available tools: +{json.dumps(rows, indent=2, sort_keys=True)}""" + + +def inject_tool_instruction(messages: Sequence[Mapping[str, Any]], tools: Sequence[Mapping[str, Any]]) -> list[dict[str, Any]]: + copied = [dict(message) for message in messages] + instruction = render_tool_instruction(tools) + if not instruction: return copied + if any(TOOL_PROTOCOL_MARKER in str(message.get("content", "")) for message in copied if message.get("role") == "system"): + return copied + index = next((i for i, message in enumerate(copied) if message.get("role") == "system"), None) + if index is None: copied.insert(0, {"role":"system", "content":instruction}) + else: copied[index]["content"] = str(copied[index].get("content", "")).rstrip() + "\n\n" + instruction + return copied From ba7829b6faa1dbf958e9f360d1a2dfabf5eafe48 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:11:26 +0530 Subject: [PATCH 137/168] radeon forge: inject tool contracts into generic local inference --- extra/radeon_forge/runtime/engine.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/extra/radeon_forge/runtime/engine.py b/extra/radeon_forge/runtime/engine.py index a9f9890219553..59c132b8316b2 100644 --- a/extra/radeon_forge/runtime/engine.py +++ b/extra/radeon_forge/runtime/engine.py @@ -11,6 +11,7 @@ from .backend import GenerationEvent, GenerationRequest, InferenceBackend from .jobs import LocalJobManager from .session import AgentSession +from .tool_prompt import inject_tool_instruction from .tools import ToolCall, ToolRegistry, WorkspaceTools @@ -54,8 +55,9 @@ def _session_metadata(self, session: AgentSession, step: int) -> Mapping[str, An def stream_inference(self, messages: Sequence[Mapping[str, Any]], tools: Sequence[Mapping[str, Any]] = (), max_tokens: int = 256, temperature: float = 0.0, session_id: str | None = None, stop: Sequence[str] = ()) -> Iterator[GenerationEvent]: - """Serve the resident local model without losing Forge hooks or profiling events.""" - request = GenerationRequest(session_id or uuid.uuid4().hex, tuple(messages), tuple(tools), max_tokens, temperature, + """Serve the resident local model without losing Forge hooks, tools or profiling events.""" + rendered_messages = inject_tool_instruction(messages, tools) + request = GenerationRequest(session_id or uuid.uuid4().hex, tuple(rendered_messages), tuple(tools), max_tokens, temperature, tuple(stop), metadata=self.inference_metadata()) return self.backend.stream(request) From aa7fd3c5633a3aec546f7b46d22c67754920ab35 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:11:43 +0530 Subject: [PATCH 138/168] radeon forge: test local tool prompt injection --- test/test_radeon_forge_tool_prompt.py | 33 +++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 test/test_radeon_forge_tool_prompt.py diff --git a/test/test_radeon_forge_tool_prompt.py b/test/test_radeon_forge_tool_prompt.py new file mode 100644 index 0000000000000..b581bc911dd0f --- /dev/null +++ b/test/test_radeon_forge_tool_prompt.py @@ -0,0 +1,33 @@ +import unittest + +from extra.radeon_forge.runtime.tool_prompt import TOOL_PROTOCOL_MARKER, inject_tool_instruction, render_tool_instruction + + +class TestToolPrompt(unittest.TestCase): + def test_tool_schema_is_rendered_for_local_model(self): + tools = [{"type":"function", "function":{"name":"read_file", "description":"Read a file", + "parameters":{"type":"object", "properties":{"path":{"type":"string"}}, "required":["path"]}}}] + text = render_tool_instruction(tools) + self.assertIn(TOOL_PROTOCOL_MARKER, text) + self.assertIn('"name": "read_file"', text) + self.assertIn('', text) + + def test_instruction_merges_into_existing_system_message_once(self): + tools = [{"type":"function", "function":{"name":"read_file", "parameters":{"type":"object"}}}] + original = [{"role":"system", "content":"Be precise."}, {"role":"user", "content":"Read it"}] + injected = inject_tool_instruction(original, tools) + self.assertEqual(len(injected), 2) + self.assertTrue(injected[0]["content"].startswith("Be precise.")) + self.assertEqual(injected[0]["content"].count(TOOL_PROTOCOL_MARKER), 1) + reinjected = inject_tool_instruction(injected, tools) + self.assertEqual(reinjected[0]["content"].count(TOOL_PROTOCOL_MARKER), 1) + self.assertEqual(original[0]["content"], "Be precise.") + + def test_instruction_is_prepended_when_no_system_message_exists(self): + tools = [{"type":"function", "function":{"name":"search_text", "parameters":{"type":"object"}}}] + injected = inject_tool_instruction([{"role":"user", "content":"Search"}], tools) + self.assertEqual(injected[0]["role"], "system") + self.assertIn(TOOL_PROTOCOL_MARKER, injected[0]["content"]) + + +if __name__ == "__main__": unittest.main() From 32aa6bd62317fd25f200a859cd59da3eb7b39b1e Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:13:12 +0530 Subject: [PATCH 139/168] radeon forge: add signal-backed cancellation token --- extra/radeon_forge/backends/cancellation.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 extra/radeon_forge/backends/cancellation.py diff --git a/extra/radeon_forge/backends/cancellation.py b/extra/radeon_forge/backends/cancellation.py new file mode 100644 index 0000000000000..06686ffde3f29 --- /dev/null +++ b/extra/radeon_forge/backends/cancellation.py @@ -0,0 +1,21 @@ +from __future__ import annotations + +import signal, threading + + +class SignalCancellation: + """Process-local cooperative cancellation set by SIGUSR1.""" + def __init__(self): + self._event = threading.Event() + self.installed = False + + def install(self) -> bool: + if not hasattr(signal, "SIGUSR1"): return False + signal.signal(signal.SIGUSR1, lambda _signum, _frame: self._event.set()) + self.installed = True + return True + + def reset(self) -> None: self._event.clear() + def request(self) -> None: self._event.set() + @property + def requested(self) -> bool: return self._event.is_set() From c79f8c5f693fdb308feec279f990dd3616f50127 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:16:36 +0530 Subject: [PATCH 140/168] radeon forge: preserve resumable state after cancellation --- extra/radeon_forge/runtime/session.py | 30 +++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/extra/radeon_forge/runtime/session.py b/extra/radeon_forge/runtime/session.py index d02c7661fdaa2..7de3e59efc2e1 100644 --- a/extra/radeon_forge/runtime/session.py +++ b/extra/radeon_forge/runtime/session.py @@ -16,6 +16,7 @@ class SessionState(str, Enum): AWAITING_TOOL_APPROVAL = "awaiting_tool_approval" RUNNING_TOOL = "running_tool" COMPLETED = "completed" + CANCELLED = "cancelled" FAILED = "failed" @@ -41,6 +42,7 @@ def __init__(self, backend: InferenceBackend, tools: ToolRegistry, system_prompt self.pending_tool_call: ToolCall | None = None self.pending_assistant_content = "" self.partial_output = "" + self.last_finish_reason: str | None = None self.state = SessionState.IDLE self._sequence = 0 self._lock = threading.RLock() @@ -61,6 +63,12 @@ def _tool_content(call: ToolCall) -> str: payload = {"id": call.call_id, "name": call.name, "arguments": dict(call.arguments)} return f"{json.dumps(payload, separators=(',', ':'))}" + @staticmethod + def _looks_like_partial_tool_protocol(output: str) -> bool: + stripped = output.lstrip() + marker = "" + return bool(stripped) and (marker.startswith(stripped) or stripped.startswith(marker)) + def _emit(self, kind: str, **data: Any) -> SessionEvent: self._sequence += 1 event = SessionEvent(self._sequence, kind, data) @@ -103,8 +111,10 @@ def _generate(self, max_tokens: int, temperature: float) -> tuple[SessionEvent, self.state = SessionState.GENERATING self.partial_output = "" self.pending_assistant_content = "" + self.last_finish_reason = None started_at = len(self.events) pieces: list[str] = [] + done_metrics: dict[str, Any] = {} step = sum(1 for event in self.events if event.kind == "generation_started") + 1 if step > self.max_agent_steps: raise RuntimeError("agent step limit exceeded") with self.trace.span("agent", "model_turn", session_id=self.session_id, step=step) as parent: @@ -130,12 +140,28 @@ def _generate(self, max_tokens: int, temperature: float) -> tuple[SessionEvent, raw = event.tool_call.get("raw") if isinstance(raw, str): self.pending_assistant_content = raw self._emit("structured_tool_call", call=asdict(self.pending_tool_call), native=True) - elif event.kind == "done": self._emit("generation_done", finish_reason=event.finish_reason, metrics=dict(event.metrics)) + elif event.kind == "done": + self.last_finish_reason = event.finish_reason + done_metrics = dict(event.metrics) + self._emit("generation_done", finish_reason=event.finish_reason, metrics=done_metrics) except Exception as exc: self.state = SessionState.FAILED self._emit("error", error=str(exc), error_type=type(exc).__name__) raise output = "".join(pieces) + + if self.last_finish_reason == "cancelled": + discarded_tool_prefix = self._looks_like_partial_tool_protocol(output) + if output and not discarded_tool_prefix: self.messages.append({"role": "assistant", "content": output}) + self.pending_tool_call = None + self.pending_assistant_content = "" + self.partial_output = "" + self.state = SessionState.CANCELLED + self._emit("generation_cancelled", preserved_output=bool(output and not discarded_tool_prefix), + discarded_incomplete_tool_protocol=discarded_tool_prefix, generated_characters=len(output), + materialized_kv_tokens=done_metrics.get("materialized_kv_tokens"), cancel_stage=done_metrics.get("cancel_stage")) + return tuple(self.events[started_at:]) + if self.pending_tool_call is None: try: self.pending_tool_call = parse_tool_call(output) @@ -200,4 +226,4 @@ def snapshot(self) -> dict[str, Any]: return {"session_id": self.session_id, "state": self.state.value, "messages": list(self.messages), "events": [asdict(x) for x in self.events], "pending_tool_call": asdict(self.pending_tool_call) if self.pending_tool_call else None, "pending_assistant_content": self.pending_assistant_content, "partial_output": self.partial_output, - "trace_id": self.trace.trace_id} + "last_finish_reason": self.last_finish_reason, "trace_id": self.trace.trace_id} From a29dc1819f720deb05ea762be20a8e0e3ad142a2 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:17:51 +0530 Subject: [PATCH 141/168] radeon forge: serialize and cancel resident model execution safely --- extra/radeon_forge/runtime/engine.py | 61 +++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 10 deletions(-) diff --git a/extra/radeon_forge/runtime/engine.py b/extra/radeon_forge/runtime/engine.py index 59c132b8316b2..0dde5a45ec2f1 100644 --- a/extra/radeon_forge/runtime/engine.py +++ b/extra/radeon_forge/runtime/engine.py @@ -1,20 +1,22 @@ from __future__ import annotations -import threading, uuid +import contextlib, threading, uuid from dataclasses import asdict from pathlib import Path -from typing import Any, Iterator, Mapping, Sequence +from typing import Any, Callable, Iterator, Mapping, Sequence, TypeVar from ..permissions import PermissionController from ..profiling.report import build_profile_report from ..synthesis import CandidateWorkspace, OptimizationTools, RuntimeFingerprint, SafeHookRegistry, install_default_specs from .backend import GenerationEvent, GenerationRequest, InferenceBackend from .jobs import LocalJobManager -from .session import AgentSession +from .session import AgentSession, SessionState from .tool_prompt import inject_tool_instruction from .tools import ToolCall, ToolRegistry, WorkspaceTools +T = TypeVar("T") + DEFAULT_SYSTEM_PROMPT = """You are Radeon Forge, a private local software and inference performance engineer. Use evidence before making performance claims. Separate observations, inferences and unknowns. Prefer reversible changes and preserve correctness. You may inspect the private workspace, author disposable target-specific kernel candidates, import portable optimization recipes, validate implementations through tinygrad MockGPU, and request permission for real W7900 benchmarks. Never treat MockGPU timing as performance evidence. @@ -35,6 +37,9 @@ def __init__(self, backend: InferenceBackend, workspace: str | Path, system_prom self.jobs = LocalJobManager() self._sessions: dict[str, AgentSession] = {} self._lock = threading.RLock() + self._generation_gate = threading.Lock() + self._generation_state_lock = threading.RLock() + self._active_generation_id: str | None = None def runtime_fingerprint(self) -> RuntimeFingerprint: metadata = getattr(self.backend, "runtime_metadata", {}) @@ -52,14 +57,33 @@ def inference_metadata(self) -> dict[str, Any]: def _session_metadata(self, session: AgentSession, step: int) -> Mapping[str, Any]: return self.inference_metadata() + @contextlib.contextmanager + def _generation_slot(self, generation_id: str): + with self._generation_gate: + with self._generation_state_lock: self._active_generation_id = generation_id + try: yield + finally: + with self._generation_state_lock: + if self._active_generation_id == generation_id: self._active_generation_id = None + + def _run_generation(self, generation_id: str, fn: Callable[[], T]) -> T: + with self._generation_slot(generation_id): return fn() + + @property + def active_generation_id(self) -> str | None: + with self._generation_state_lock: return self._active_generation_id + def stream_inference(self, messages: Sequence[Mapping[str, Any]], tools: Sequence[Mapping[str, Any]] = (), max_tokens: int = 256, temperature: float = 0.0, session_id: str | None = None, stop: Sequence[str] = ()) -> Iterator[GenerationEvent]: """Serve the resident local model without losing Forge hooks, tools or profiling events.""" + generation_id = session_id or uuid.uuid4().hex rendered_messages = inject_tool_instruction(messages, tools) - request = GenerationRequest(session_id or uuid.uuid4().hex, tuple(rendered_messages), tuple(tools), max_tokens, temperature, + request = GenerationRequest(generation_id, tuple(rendered_messages), tuple(tools), max_tokens, temperature, tuple(stop), metadata=self.inference_metadata()) - return self.backend.stream(request) + def iterator(): + with self._generation_slot(generation_id): yield from self.backend.stream(request) + return iterator() def create_session(self) -> AgentSession: with self._lock: @@ -74,17 +98,33 @@ def session(self, session_id: str) -> AgentSession: def sessions(self) -> list[dict[str, Any]]: with self._lock: return [{"session_id": x.session_id, "state": x.state.value, "trace_id": x.trace.trace_id} for x in self._sessions.values()] - def submit_message(self, session_id: str, content: str, max_tokens: int = 512, temperature: float = 0.0) -> dict[str, Any]: + def run_message(self, session_id: str, content: str, max_tokens: int = 512, temperature: float = 0.0): session = self.session(session_id) - job = self.jobs.submit("agent_turn", session_id, lambda: session.send(content, max_tokens, temperature)) + return self._run_generation(session_id, lambda: session.send(content, max_tokens, temperature)) + + def submit_message(self, session_id: str, content: str, max_tokens: int = 512, temperature: float = 0.0) -> dict[str, Any]: + job = self.jobs.submit("agent_turn", session_id, lambda: self.run_message(session_id, content, max_tokens, temperature)) return asdict(job) - def submit_tool_approval(self, session_id: str, reason: str, max_tokens: int = 512) -> dict[str, Any]: + def run_tool_approval(self, session_id: str, reason: str, max_tokens: int = 512): session = self.session(session_id) token = self.grant_for_pending_tool(session_id, reason) - job = self.jobs.submit("tool_and_resume", session_id, lambda: session.approve_tool(token, max_tokens)) + return self._run_generation(session_id, lambda: session.approve_tool(token, max_tokens)) + + def submit_tool_approval(self, session_id: str, reason: str, max_tokens: int = 512) -> dict[str, Any]: + job = self.jobs.submit("tool_and_resume", session_id, lambda: self.run_tool_approval(session_id, reason, max_tokens)) return asdict(job) + def cancel_generation(self, session_id: str) -> dict[str, Any]: + session = self.session(session_id) + if not self.backend.capabilities.cancellation: raise RuntimeError("resident model backend does not support cooperative cancellation") + active = self.active_generation_id + if active != session_id or session.state is not SessionState.GENERATING: + raise RuntimeError(f"session {session_id} is not the active model generation") + self.backend.cancel() + return {"requested": True, "session_id": session_id, "active_generation_id": active, + "state": session.state.value, "model_remains_resident": True, "kv_state_preserved": True} + def grant_for_pending_tool(self, session_id: str, reason: str, max_uses: int = 1) -> str: session = self.session(session_id) if session.pending_tool_call is None: raise RuntimeError("session has no pending tool") @@ -109,6 +149,7 @@ def optimization_state(self) -> dict[str, Any]: "recipes": [asdict(x) for x in self.optimization_tools.recipes.installed()], "active_hooks": [asdict(x) for x in self.hooks.active()], "runtime_fingerprint": asdict(self.runtime_fingerprint()), - "runtime_adapters": sorted(self.hooks.SUPPORTED_ADAPTERS)} + "runtime_adapters": sorted(self.hooks.SUPPORTED_ADAPTERS), + "active_generation_id": self.active_generation_id} def close(self) -> None: self.backend.close() From cd2896446ff9db7df753cec11df035caddee476f Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:22:36 +0530 Subject: [PATCH 142/168] radeon forge: add cross-token stop sequence matcher --- extra/radeon_forge/runtime/stop_sequences.py | 54 ++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 extra/radeon_forge/runtime/stop_sequences.py diff --git a/extra/radeon_forge/runtime/stop_sequences.py b/extra/radeon_forge/runtime/stop_sequences.py new file mode 100644 index 0000000000000..41f6d26f234d9 --- /dev/null +++ b/extra/radeon_forge/runtime/stop_sequences.py @@ -0,0 +1,54 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import Iterable + + +@dataclass(frozen=True) +class StopMatch: + text: str + matched: str | None = None + + +class StopSequenceMatcher: + """Incrementally hides stop strings even when they cross token boundaries.""" + def __init__(self, stops: Iterable[str]): + values = tuple(dict.fromkeys(str(stop) for stop in stops)) + if any(not stop for stop in values): raise ValueError("stop sequences must not be empty") + self.stops = values + self.buffer = "" + self.done = False + + def feed(self, text: str) -> StopMatch: + if self.done: raise RuntimeError("stop matcher is already complete") + self.buffer += text + earliest: tuple[int, str] | None = None + for stop in self.stops: + index = self.buffer.find(stop) + if index >= 0 and (earliest is None or index < earliest[0] or (index == earliest[0] and len(stop) > len(earliest[1]))): + earliest = (index, stop) + if earliest is not None: + index, stop = earliest + output = self.buffer[:index] + self.buffer = "" + self.done = True + return StopMatch(output, stop) + + keep = 0 + for stop in self.stops: + limit = min(len(stop) - 1, len(self.buffer)) + for length in range(limit, 0, -1): + if self.buffer.endswith(stop[:length]): + keep = max(keep, length) + break + if keep: + output, self.buffer = self.buffer[:-keep], self.buffer[-keep:] + else: + output, self.buffer = self.buffer, "" + return StopMatch(output) + + def finalize(self) -> str: + if self.done: return "" + output, self.buffer = self.buffer, "" + self.done = True + return output From 9c42981f3885631659f7f391c4f58237f84fdd71 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:22:49 +0530 Subject: [PATCH 143/168] radeon forge: test cross-token stop handling --- test/test_radeon_forge_stop_sequences.py | 36 ++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 test/test_radeon_forge_stop_sequences.py diff --git a/test/test_radeon_forge_stop_sequences.py b/test/test_radeon_forge_stop_sequences.py new file mode 100644 index 0000000000000..9b27f2e70c7fc --- /dev/null +++ b/test/test_radeon_forge_stop_sequences.py @@ -0,0 +1,36 @@ +import unittest + +from extra.radeon_forge.runtime.stop_sequences import StopSequenceMatcher + + +class TestStopSequenceMatcher(unittest.TestCase): + def test_stop_split_across_tokens_is_not_emitted(self): + matcher = StopSequenceMatcher([""]) + self.assertEqual(matcher.feed("answerignored") + self.assertEqual(matched.text, "") + self.assertEqual(matched.matched, "") + self.assertEqual(matcher.finalize(), "") + + def test_ambiguous_prefix_is_released_when_it_stops_matching(self): + matcher = StopSequenceMatcher(["STOP"]) + self.assertEqual(matcher.feed("hello ST").text, "hello ") + self.assertEqual(matcher.feed("AR").text, "STAR") + self.assertEqual(matcher.finalize(), "") + + def test_earliest_stop_wins(self): + matcher = StopSequenceMatcher(["END", "STOP"]) + result = matcher.feed("one STOP two END") + self.assertEqual(result.text, "one ") + self.assertEqual(result.matched, "STOP") + + def test_finalize_releases_safe_suffix(self): + matcher = StopSequenceMatcher(["STOP"]) + self.assertEqual(matcher.feed("value ST").text, "value ") + self.assertEqual(matcher.finalize(), "ST") + + def test_empty_stop_is_rejected(self): + with self.assertRaises(ValueError): StopSequenceMatcher([""]) + + +if __name__ == "__main__": unittest.main() From 0996d8266f148e159063b4af91e54328a9c2502b Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:30:18 +0530 Subject: [PATCH 144/168] radeon forge: test stop-buffered text transport --- test/test_radeon_forge_text_events.py | 57 +++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 test/test_radeon_forge_text_events.py diff --git a/test/test_radeon_forge_text_events.py b/test/test_radeon_forge_text_events.py new file mode 100644 index 0000000000000..d74fcd71bb469 --- /dev/null +++ b/test/test_radeon_forge_text_events.py @@ -0,0 +1,57 @@ +import tempfile +import unittest + +from extra.radeon_forge.profiling.report import build_profile_report +from extra.radeon_forge.runtime import BackendCapabilities, ForgeEngine, GenerationEvent +from extra.radeon_forge.ui.openai_api import collect_chat_completion, stream_chat_completion + + +class BufferedTextBackend: + @property + def name(self): return "buffered-text-local" + + @property + def capabilities(self): return BackendCapabilities(streaming=True, local_only=True) + + @property + def runtime_metadata(self): return {"architecture":"gfx1100", "runtime":"tinygrad", "model_family":"llama"} + + def stream(self, request): + yield GenerationEvent("prefill", metrics={"prompt_tokens":8, "wall_ms":1.0}) + # A real model token was generated, but its characters were withheld while + # Forge determined whether they were the prefix of a cross-token stop. + yield GenerationEvent("token", "", metrics={"index":0, "stage":"first_token", "wall_ms":2.0, + "gpu_ms":1.5, "kernel_count":20}) + yield GenerationEvent("text", "visible answer", metrics={"stage":"first_token", "buffered_for_stop_sequence":True}) + yield GenerationEvent("done", finish_reason="stop", metrics={"generated_tokens":1, "stop_sequence":""}) + + def close(self): pass + + +class TestBufferedTextTransport(unittest.TestCase): + def test_agent_session_preserves_text_without_fake_token(self): + with tempfile.TemporaryDirectory() as directory: + engine = ForgeEngine(BufferedTextBackend(), directory) + session = engine.create_session() + engine.run_message(session.session_id, "answer until the stop marker") + self.assertEqual(session.messages[-1]["content"], "visible answer") + report = build_profile_report(session.trace.events()) + self.assertEqual(report["summary"]["decode_tokens"], 1) + self.assertEqual(report["summary"]["token_wall_ms_p50"], 2.0) + self.assertEqual(len([event for event in session.events if event.kind == "text"]), 1) + engine.close() + + def test_openai_completion_and_stream_include_visible_text(self): + payload = {"messages":[{"role":"user", "content":"answer"}], "stop":[""]} + with tempfile.TemporaryDirectory() as directory: + engine = ForgeEngine(BufferedTextBackend(), directory) + result = collect_chat_completion(engine, payload) + self.assertEqual(result["choices"][0]["message"]["content"], "visible answer") + self.assertEqual(result["usage"]["completion_tokens"], 1) + stream = "".join(stream_chat_completion(engine, {**payload, "session_id":"stream-session"})) + self.assertIn("visible answer", stream) + self.assertIn('"finish_reason":"stop"', stream) + engine.close() + + +if __name__ == "__main__": unittest.main() From 8b227551cbc74506f95dc6c2c8cd064f76e8b217 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:31:37 +0530 Subject: [PATCH 145/168] radeon forge: add tool execution provenance context --- extra/radeon_forge/runtime/tool_context.py | 37 ++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 extra/radeon_forge/runtime/tool_context.py diff --git a/extra/radeon_forge/runtime/tool_context.py b/extra/radeon_forge/runtime/tool_context.py new file mode 100644 index 0000000000000..dd9b98ea496c1 --- /dev/null +++ b/extra/radeon_forge/runtime/tool_context.py @@ -0,0 +1,37 @@ +from __future__ import annotations + +import contextlib +from contextvars import ContextVar +from dataclasses import asdict, dataclass +from typing import Any, Iterator, Mapping + + +@dataclass(frozen=True) +class ToolExecutionContext: + session_id: str + trace_id: str + agent_step: int + tool_call_id: str + tool_name: str + attributes: Mapping[str, Any] + + +_CURRENT: ContextVar[ToolExecutionContext | None] = ContextVar("RADEON_FORGE_TOOL_CONTEXT", default=None) + + +@contextlib.contextmanager +def bind_tool_context(context: ToolExecutionContext) -> Iterator[None]: + token = _CURRENT.set(context) + try: yield + finally: _CURRENT.reset(token) + + +def current_tool_context(required: bool = False) -> ToolExecutionContext | None: + context = _CURRENT.get() + if required and context is None: raise RuntimeError("tool execution provenance is unavailable") + return context + + +def current_tool_context_dict() -> dict[str, Any]: + context = current_tool_context() + return asdict(context) if context is not None else {} From ccd495fb622f93c3ae61614e383c193552d8f2eb Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:32:18 +0530 Subject: [PATCH 146/168] radeon forge: add ROCm counter and ATT capture adapter --- extra/radeon_forge/profiling/capture.py | 177 ++++++++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 extra/radeon_forge/profiling/capture.py diff --git a/extra/radeon_forge/profiling/capture.py b/extra/radeon_forge/profiling/capture.py new file mode 100644 index 0000000000000..887f4dc4608b0 --- /dev/null +++ b/extra/radeon_forge/profiling/capture.py @@ -0,0 +1,177 @@ +from __future__ import annotations + +import csv, hashlib, json, os, shutil, subprocess, time, uuid +from dataclasses import asdict, dataclass, field +from enum import Enum +from pathlib import Path +from typing import Any, Mapping, Sequence + + +class CaptureKind(str, Enum): + COUNTERS = "counters" + ATT = "att" + + +@dataclass(frozen=True) +class CaptureRequest: + kind: CaptureKind + command: tuple[str, ...] + stage: str + session_id: str = "" + trace_id: str = "" + agent_step: int = 0 + kernel_regex: str = "" + counters: tuple[str, ...] = () + timeout_seconds: int = 900 + metadata: Mapping[str, Any] = field(default_factory=dict) + + +@dataclass(frozen=True) +class CaptureArtifact: + path: str + size_bytes: int + sha256: str + suffix: str + + +@dataclass(frozen=True) +class CaptureResult: + capture_id: str + kind: str + passed: bool + output_directory: str + profiler_command: tuple[str, ...] + target_command: tuple[str, ...] + returncode: int | None + elapsed_ms: float + stdout_tail: str + stderr_tail: str + artifacts: tuple[CaptureArtifact, ...] + summary: Mapping[str, Any] + error: str = "" + + def to_dict(self) -> dict[str, Any]: return asdict(self) + + +class Rocprofv3Adapter: + """Evidence-preserving rocprofv3 adapter for counters and ATT/SQTT. + + CLI options are detected from the installed profiler help rather than assumed + from a particular ROCm release. Performance is always measured by the target + hardware; this adapter only captures and normalizes evidence. + """ + def __init__(self, project_root: str | Path, evidence_root: str | Path): + self.project_root = Path(project_root).resolve() + self.evidence_root = Path(evidence_root).resolve() + self.evidence_root.mkdir(parents=True, exist_ok=True) + + @staticmethod + def executable() -> str | None: return shutil.which("rocprofv3") + + def probe(self) -> dict[str, Any]: + executable = self.executable() + if executable is None: return {"available":False, "reason":"rocprofv3 is not on PATH"} + version = subprocess.run([executable, "--version"], text=True, capture_output=True, timeout=20) + help_run = subprocess.run([executable, "--help"], text=True, capture_output=True, timeout=20) + help_text = help_run.stdout + "\n" + help_run.stderr + avail_exe = shutil.which("rocprofv3-avail") + available = None + if avail_exe: + proc = subprocess.run([avail_exe, "list"], text=True, capture_output=True, timeout=60) + available = {"returncode":proc.returncode, "stdout":proc.stdout[-50000:], "stderr":proc.stderr[-10000:]} + return {"available":True, "executable":executable, "version_returncode":version.returncode, + "version":(version.stdout+version.stderr).strip(), "supports_att":"--att" in help_text, + "supports_kernel_regex":"--kernel-include-regex" in help_text, "supports_pmc":"--pmc" in help_text, + "supports_output_directory":"--output-directory" in help_text or " -d" in help_text, + "available_components":available} + + def _validate_target(self, command: Sequence[str]) -> tuple[str, ...]: + argv = tuple(str(x) for x in command) + if not argv: raise ValueError("profile target command must not be empty") + executable = Path(argv[0]).name + allowed = {"python", "python3", "pytest", "tinygrad", "radeon-forge"} + if executable not in allowed: raise ValueError(f"profile target executable {executable!r} is not allowlisted") + if any("\x00" in part for part in argv): raise ValueError("profile command contains NUL") + return argv + + def _profiler_command(self, request: CaptureRequest, output: Path, help_text: str) -> tuple[str, ...]: + executable = self.executable() + if executable is None: raise RuntimeError("rocprofv3 is not installed") + args: list[str] = [executable] + if request.kind is CaptureKind.ATT: + if "--att" not in help_text: raise RuntimeError("installed rocprofv3 does not advertise ATT support") + args.append("--att") + if "--att-simd-select" in help_text: args += ["--att-simd-select", "0x0"] + if request.kernel_regex: + if "--kernel-include-regex" not in help_text: raise RuntimeError("installed rocprofv3 cannot filter ATT by kernel regex") + args += ["--kernel-include-regex", request.kernel_regex] + elif request.kind is CaptureKind.COUNTERS: + if not request.counters: raise ValueError("counter capture requires at least one counter") + if "--pmc" not in help_text: raise RuntimeError("installed rocprofv3 does not advertise --pmc counter collection") + args += ["--pmc", ",".join(request.counters)] + else: raise ValueError(request.kind) + if "--output-directory" in help_text: args += ["--output-directory", str(output)] + else: args += ["-d", str(output)] + return tuple(args + ["--"] + list(request.command)) + + @staticmethod + def _artifacts(root: Path) -> tuple[CaptureArtifact, ...]: + ret = [] + for path in sorted(root.rglob("*")): + if not path.is_file(): continue + data = path.read_bytes() + ret.append(CaptureArtifact(str(path), len(data), hashlib.sha256(data).hexdigest(), path.suffix.lower())) + return tuple(ret) + + @staticmethod + def _csv_summary(root: Path) -> dict[str, Any]: + files, rows, columns = 0, 0, set() + numeric: dict[str, list[float]] = {} + for path in root.rglob("*.csv"): + files += 1 + try: + with path.open(newline="", encoding="utf-8", errors="replace") as handle: + reader = csv.DictReader(handle) + columns.update(reader.fieldnames or ()) + for row in reader: + rows += 1 + for key, value in row.items(): + try: numeric.setdefault(str(key), []).append(float(value)) + except (TypeError, ValueError): pass + except OSError: continue + aggregate = {key:{"count":len(values), "min":min(values), "max":max(values), "mean":sum(values)/len(values)} + for key, values in numeric.items() if values} + return {"csv_files":files, "csv_rows":rows, "columns":sorted(columns), "numeric_columns":aggregate} + + def capture(self, request: CaptureRequest) -> CaptureResult: + target = self._validate_target(request.command) + capture_id = f"{request.kind.value}-{time.strftime('%Y%m%dT%H%M%S')}-{uuid.uuid4().hex[:10]}" + output = self.evidence_root / capture_id + output.mkdir(parents=True, exist_ok=False) + manifest_path = output / "forge_capture_manifest.json" + started = time.perf_counter_ns() + returncode: int | None = None + stdout = stderr = error = "" + profiler_command: tuple[str, ...] = () + try: + executable = self.executable() + if executable is None: raise RuntimeError("rocprofv3 is not installed") + help_run = subprocess.run([executable, "--help"], text=True, capture_output=True, timeout=20) + help_text = help_run.stdout + "\n" + help_run.stderr + profiler_command = self._profiler_command(request, output, help_text) + env = {**os.environ, "PYTHONUNBUFFERED":"1", "RADEON_FORGE_CAPTURE_ID":capture_id, + "RADEON_FORGE_EXECUTION_STAGE":request.stage, "RADEON_FORGE_TRACE_ID":request.trace_id} + proc = subprocess.run(profiler_command, cwd=self.project_root, env=env, text=True, capture_output=True, + timeout=max(1, min(request.timeout_seconds, 3600))) + returncode, stdout, stderr = proc.returncode, proc.stdout, proc.stderr + except Exception as exc: error = f"{type(exc).__name__}: {exc}" + elapsed_ms = (time.perf_counter_ns() - started) / 1e6 + artifacts = tuple(x for x in self._artifacts(output) if Path(x.path) != manifest_path) + summary = {"stage":request.stage, "session_id":request.session_id, "trace_id":request.trace_id, + "agent_step":request.agent_step, "kernel_regex":request.kernel_regex, "counters":list(request.counters), + "capture_metadata":dict(request.metadata), "parsed_csv":self._csv_summary(output)} + passed = returncode == 0 and not error and bool(artifacts) + result = CaptureResult(capture_id, request.kind.value, passed, str(output), profiler_command, target, returncode, + elapsed_ms, stdout[-50000:], stderr[-50000:], artifacts, summary, error) + manifest_path.write_text(json.dumps(result.to_dict(), indent=2, default=str), encoding="utf-8") + return result From d7d7240349e2030028a37045accc5fdab8e2b7fa Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:32:49 +0530 Subject: [PATCH 147/168] radeon forge: expose stage-scoped ROCm profiler tools --- extra/radeon_forge/profiling/tools.py | 86 +++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 extra/radeon_forge/profiling/tools.py diff --git a/extra/radeon_forge/profiling/tools.py b/extra/radeon_forge/profiling/tools.py new file mode 100644 index 0000000000000..e81bd30a4ba94 --- /dev/null +++ b/extra/radeon_forge/profiling/tools.py @@ -0,0 +1,86 @@ +from __future__ import annotations + +import json +from dataclasses import asdict +from pathlib import Path +from typing import Any, Mapping + +from ..permissions import Action +from ..runtime.tool_context import current_tool_context +from ..runtime.tools import ToolRegistry, ToolSpec +from .capture import CaptureKind, CaptureRequest, Rocprofv3Adapter + + +class ProfilingTools: + def __init__(self, project_root: str | Path, evidence_root: str | Path): + self.adapter = Rocprofv3Adapter(project_root, evidence_root) + + @staticmethod + def _context() -> dict[str, Any]: + context = current_tool_context() + return asdict(context) if context is not None else {} + + @staticmethod + def _command(value: Any) -> tuple[str, ...]: + if not isinstance(value, list) or not value or not all(isinstance(x, str) for x in value): + raise ValueError("command must be a non-empty string array") + return tuple(value) + + def probe(self, args: Mapping[str, Any]) -> Any: return self.adapter.probe() + + def _capture(self, kind: CaptureKind, args: Mapping[str, Any]) -> Any: + context = self._context() + request = CaptureRequest( + kind=kind, + command=self._command(args.get("command")), + stage=str(args.get("stage", "unknown")), + session_id=str(context.get("session_id", args.get("session_id", ""))), + trace_id=str(context.get("trace_id", args.get("trace_id", ""))), + agent_step=int(context.get("agent_step", args.get("agent_step", 0))), + kernel_regex=str(args.get("kernel_regex", "")), + counters=tuple(str(x) for x in args.get("counters", ())), + timeout_seconds=int(args.get("timeout_seconds", 900)), + metadata={"tool_call_id":context.get("tool_call_id", ""), "tool_name":context.get("tool_name", ""), + **dict(args.get("metadata", {}))}, + ) + return self.adapter.capture(request).to_dict() + + def capture_counters(self, args: Mapping[str, Any]) -> Any: return self._capture(CaptureKind.COUNTERS, args) + def capture_att(self, args: Mapping[str, Any]) -> Any: return self._capture(CaptureKind.ATT, args) + + def list_captures(self, args: Mapping[str, Any]) -> Any: + captures = [] + for path in sorted(self.adapter.evidence_root.glob("*/forge_capture_manifest.json"), reverse=True): + try: + payload = json.loads(path.read_text(encoding="utf-8")) + captures.append({"capture_id":payload.get("capture_id", path.parent.name), "kind":payload.get("kind"), + "passed":payload.get("passed"), "elapsed_ms":payload.get("elapsed_ms"), "output_directory":str(path.parent), + "summary":payload.get("summary", {}), "artifact_count":len(payload.get("artifacts", [])), + "error":payload.get("error", "")}) + except Exception: continue + return {"captures":captures[:max(1, min(int(args.get("limit", 50)), 500))]} + + def inspect_capture(self, args: Mapping[str, Any]) -> Any: + capture_id = str(args["capture_id"]) + if "/" in capture_id or ".." in capture_id: raise ValueError("invalid capture id") + path = self.adapter.evidence_root / capture_id / "forge_capture_manifest.json" + if not path.is_file(): raise KeyError(f"unknown capture {capture_id}") + return json.loads(path.read_text(encoding="utf-8")) + + def install(self, registry: ToolRegistry) -> None: + registry.register(ToolSpec("probe_rocm_profiler", "Inspect the installed local rocprofv3/ATT capabilities without running a workload", + {"type":"object","properties":{}}), self.probe) + registry.register(ToolSpec("capture_rocm_counters", "Capture selected hardware performance counters for one local command and execution state", + {"type":"object","required":["command","stage","counters"],"properties":{ + "command":{"type":"array","items":{"type":"string"}}, "stage":{"type":"string"}, + "counters":{"type":"array","items":{"type":"string"}}, "kernel_regex":{"type":"string"}, + "timeout_seconds":{"type":"integer"}, "metadata":{"type":"object"}}}, Action.BENCHMARK), self.capture_counters) + registry.register(ToolSpec("capture_rocm_att", "Capture AMD Advanced Thread Trace/SQTT evidence for one local command and execution state", + {"type":"object","required":["command","stage"],"properties":{ + "command":{"type":"array","items":{"type":"string"}}, "stage":{"type":"string"}, + "kernel_regex":{"type":"string"}, "timeout_seconds":{"type":"integer"}, + "metadata":{"type":"object"}}}, Action.BENCHMARK), self.capture_att) + registry.register(ToolSpec("list_profile_captures", "List immutable local ROCm counter and ATT capture manifests", + {"type":"object","properties":{"limit":{"type":"integer"}}}), self.list_captures) + registry.register(ToolSpec("inspect_profile_capture", "Inspect one profile manifest, artifact hashes and normalized evidence summary", + {"type":"object","required":["capture_id"],"properties":{"capture_id":{"type":"string"}}}), self.inspect_capture) From 08ccc7e409269c326a07a719b274df8ba406fc00 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:34:10 +0530 Subject: [PATCH 148/168] radeon forge: test evidence-preserving ROCm captures --- test/test_radeon_forge_capture.py | 89 +++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 test/test_radeon_forge_capture.py diff --git a/test/test_radeon_forge_capture.py b/test/test_radeon_forge_capture.py new file mode 100644 index 0000000000000..eeb55ddcfba2b --- /dev/null +++ b/test/test_radeon_forge_capture.py @@ -0,0 +1,89 @@ +import os +import tempfile +import unittest +from pathlib import Path +from unittest.mock import patch + +from extra.radeon_forge.profiling.capture import CaptureKind, CaptureRequest, Rocprofv3Adapter +from extra.radeon_forge.profiling.tools import ProfilingTools +from extra.radeon_forge.runtime.tool_context import ToolExecutionContext, bind_tool_context + + +FAKE_ROCPROF = '''#!/usr/bin/env python3 +import csv, pathlib, sys +if "--version" in sys.argv: + print("rocprofv3 fake 1.0") + raise SystemExit(0) +if "--help" in sys.argv: + print("--att --att-simd-select --kernel-include-regex --pmc --output-directory -d") + raise SystemExit(0) +args=sys.argv[1:] +flag="--output-directory" if "--output-directory" in args else "-d" +out=pathlib.Path(args[args.index(flag)+1]) +out.mkdir(parents=True, exist_ok=True) +if "--att" in args: + (out/"thread_trace.att").write_bytes(b"ATT-EVIDENCE") +else: + with (out/"counters.csv").open("w", newline="") as f: + writer=csv.DictWriter(f, fieldnames=["Kernel_Name","SQ_WAVES","DurationNs"]) + writer.writeheader() + writer.writerow({"Kernel_Name":"decode_gemv","SQ_WAVES":"64","DurationNs":"900"}) +print("capture complete") +''' + + +class TestRocprofCapture(unittest.TestCase): + def _adapter(self, root: Path): + binary=root/"bin"/"rocprofv3" + binary.parent.mkdir() + binary.write_text(FAKE_ROCPROF, encoding="utf-8") + binary.chmod(0o755) + environment={**os.environ, "PATH":str(binary.parent)+os.pathsep+os.environ.get("PATH","")} + return Rocprofv3Adapter(root, root/"evidence"), environment + + def test_counter_capture_writes_manifest_hashes_and_summary(self): + with tempfile.TemporaryDirectory() as directory: + root=Path(directory) + adapter, environment=self._adapter(root) + with patch.dict(os.environ, environment, clear=True): + probe=adapter.probe() + self.assertTrue(probe["available"]) + self.assertTrue(probe["supports_pmc"]) + result=adapter.capture(CaptureRequest(CaptureKind.COUNTERS, ("python3","-c","print('x')"), "decode", + session_id="s1", trace_id="t1", agent_step=2, counters=("SQ_WAVES","DurationNs"))) + self.assertTrue(result.passed) + self.assertEqual(result.summary["stage"], "decode") + self.assertEqual(result.summary["session_id"], "s1") + self.assertEqual(result.summary["parsed_csv"]["csv_rows"], 1) + self.assertEqual(result.summary["parsed_csv"]["numeric_columns"]["SQ_WAVES"]["mean"], 64.0) + self.assertTrue(all(len(artifact.sha256)==64 for artifact in result.artifacts)) + self.assertTrue((Path(result.output_directory)/"forge_capture_manifest.json").is_file()) + + def test_att_capture_is_separate_and_provenance_is_bound(self): + with tempfile.TemporaryDirectory() as directory: + root=Path(directory) + adapter, environment=self._adapter(root) + tools=ProfilingTools(root, root/"evidence") + context=ToolExecutionContext("session-7","trace-9",3,"call-2","capture_rocm_att",{"purpose":"decode diagnosis"}) + with patch.dict(os.environ, environment, clear=True), bind_tool_context(context): + result=tools.capture_att({"command":["python3","-c","print('decode')"], "stage":"first_token", + "kernel_regex":"decode_.*", "timeout_seconds":30}) + self.assertTrue(result["passed"]) + self.assertEqual(result["summary"]["session_id"], "session-7") + self.assertEqual(result["summary"]["trace_id"], "trace-9") + self.assertEqual(result["summary"]["agent_step"], 3) + self.assertEqual(result["summary"]["kernel_regex"], "decode_.*") + self.assertTrue(any(item["suffix"]==".att" for item in result["artifacts"])) + listed=tools.list_captures({"limit":10}) + self.assertEqual(listed["captures"][0]["capture_id"], result["capture_id"]) + + def test_profile_target_is_allowlisted_and_shell_free(self): + with tempfile.TemporaryDirectory() as directory: + root=Path(directory) + adapter, environment=self._adapter(root) + with patch.dict(os.environ, environment, clear=True): + with self.assertRaisesRegex(ValueError, "not allowlisted"): + adapter.capture(CaptureRequest(CaptureKind.ATT, ("bash","-lc","rm -rf /"), "decode")) + + +if __name__ == "__main__": unittest.main() From fb538f5ad47264f6587998e7e0af00b375336602 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:36:52 +0530 Subject: [PATCH 149/168] radeon forge: test hardware-grounded profiler findings --- test/test_radeon_forge_profile_evidence.py | 39 ++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 test/test_radeon_forge_profile_evidence.py diff --git a/test/test_radeon_forge_profile_evidence.py b/test/test_radeon_forge_profile_evidence.py new file mode 100644 index 0000000000000..85711716e9dcb --- /dev/null +++ b/test/test_radeon_forge_profile_evidence.py @@ -0,0 +1,39 @@ +import unittest + +from extra.radeon_forge.profiling.report import build_profile_report +from extra.radeon_forge.runtime.events import TraceRecorder + + +class TestHardwareProfileEvidence(unittest.TestCase): + def test_att_and_counter_captures_are_observed_evidence(self): + trace=TraceRecorder("trace-1") + trace.point("profile","hardware_capture",capture_id="att-1",capture_kind="att",passed=True,stage="decode", + artifact_count=3,output_directory="/tmp/att",parsed_csv={},error="") + trace.point("profile","hardware_capture",capture_id="counter-1",capture_kind="counters",passed=True,stage="first_token", + artifact_count=2,output_directory="/tmp/counters",parsed_csv={"csv_rows":8},error="") + report=build_profile_report(trace.events()) + self.assertEqual(report["summary"]["hardware_capture_count"],2) + self.assertEqual(report["summary"]["att_capture_count"],1) + self.assertEqual(report["summary"]["counter_capture_count"],1) + titles={finding["title"] for finding in report["findings"]} + self.assertIn("AMD ATT/SQTT evidence is attached",titles) + self.assertIn("ROCm hardware counter evidence is attached",titles) + self.assertNotIn("No ROCm counter or ATT capture is attached to this trace",titles) + + def test_failed_capture_is_visible(self): + trace=TraceRecorder("trace-2") + trace.point("profile","hardware_capture",capture_id="att-bad",capture_kind="att",passed=False,stage="decode", + artifact_count=0,output_directory="/tmp/att-bad",parsed_csv={},error="rocprofv3 missing ATT support") + report=build_profile_report(trace.events()) + self.assertEqual(report["summary"]["failed_capture_count"],1) + finding=next(item for item in report["findings"] if item["title"]=="One or more hardware profiling captures failed") + self.assertEqual(finding["status"],"observed") + self.assertIn("missing ATT support",finding["evidence"][0]) + + def test_absence_remains_unknown(self): + report=build_profile_report(()) + finding=next(item for item in report["findings"] if item["title"]=="No ROCm counter or ATT capture is attached to this trace") + self.assertEqual(finding["status"],"unknown") + + +if __name__=="__main__": unittest.main() From c1b8a6104275af360b5110f4659e284d74342ca8 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:41:14 +0530 Subject: [PATCH 150/168] radeon forge: compare compatible hardware profile captures --- extra/radeon_forge/profiling/compare.py | 173 ++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 extra/radeon_forge/profiling/compare.py diff --git a/extra/radeon_forge/profiling/compare.py b/extra/radeon_forge/profiling/compare.py new file mode 100644 index 0000000000000..8b271baa25e34 --- /dev/null +++ b/extra/radeon_forge/profiling/compare.py @@ -0,0 +1,173 @@ +from __future__ import annotations + +import csv, json, math +from dataclasses import asdict, dataclass +from pathlib import Path +from typing import Any, Iterable, Mapping + + +@dataclass(frozen=True) +class NumericDelta: + baseline: float + candidate: float + absolute: float + relative: float | None + + +@dataclass(frozen=True) +class KernelDelta: + kernel: str + metric: str + baseline_count: int + candidate_count: int + baseline_mean: float + candidate_mean: float + absolute: float + relative: float | None + + +class IncomparableCaptures(ValueError): pass + + +def _manifest(value: str | Path | Mapping[str, Any]) -> tuple[dict[str, Any], Path | None]: + if isinstance(value, Mapping): return dict(value), None + path = Path(value) + if path.is_dir(): path = path / "forge_capture_manifest.json" + if not path.is_file(): raise FileNotFoundError(path) + payload = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(payload, dict): raise ValueError(f"capture manifest {path} is not an object") + return payload, path.parent + + +def _normalized_command(value: Any) -> tuple[str, ...]: + if not isinstance(value, list): return () + return tuple(str(x) for x in value) + + +def _counter_set(manifest: Mapping[str, Any]) -> tuple[str, ...]: + summary = manifest.get("summary", {}) + if not isinstance(summary, Mapping): return () + counters = summary.get("counters", ()) + return tuple(sorted(str(x) for x in counters)) if isinstance(counters, list) else () + + +def _workload_identity(manifest: Mapping[str, Any]) -> str: + summary = manifest.get("summary", {}) + metadata = summary.get("capture_metadata", {}) if isinstance(summary, Mapping) else {} + if isinstance(metadata, Mapping): + for key in ("workload_hash", "task_suite_hash", "input_hash", "workload_id"): + if metadata.get(key): return f"{key}:{metadata[key]}" + command = _normalized_command(manifest.get("target_command")) + return "command:" + json.dumps(command) + + +def compatibility_report(baseline: Mapping[str, Any], candidate: Mapping[str, Any]) -> dict[str, Any]: + reasons: list[str] = [] + base_summary = baseline.get("summary", {}) if isinstance(baseline.get("summary", {}), Mapping) else {} + cand_summary = candidate.get("summary", {}) if isinstance(candidate.get("summary", {}), Mapping) else {} + checks = { + "kind": (baseline.get("kind"), candidate.get("kind")), + "stage": (base_summary.get("stage"), cand_summary.get("stage")), + "target_command": (_normalized_command(baseline.get("target_command")), _normalized_command(candidate.get("target_command"))), + "workload_identity": (_workload_identity(baseline), _workload_identity(candidate)), + } + if baseline.get("kind") == "counters" or candidate.get("kind") == "counters": + checks["counters"] = (_counter_set(baseline), _counter_set(candidate)) + for name, (left, right) in checks.items(): + if left != right: reasons.append(f"{name} differs: baseline={left!r} candidate={right!r}") + if not baseline.get("passed"): reasons.append("baseline capture did not pass") + if not candidate.get("passed"): reasons.append("candidate capture did not pass") + return {"comparable": not reasons, "reasons": reasons, "checks": checks} + + +def _relative(base: float, candidate: float) -> float | None: + if base == 0: return None + return (candidate - base) / base + + +def _numeric_summary(manifest: Mapping[str, Any]) -> Mapping[str, Any]: + summary = manifest.get("summary", {}) + parsed = summary.get("parsed_csv", {}) if isinstance(summary, Mapping) else {} + numeric = parsed.get("numeric_columns", {}) if isinstance(parsed, Mapping) else {} + return numeric if isinstance(numeric, Mapping) else {} + + +def _numeric_deltas(baseline: Mapping[str, Any], candidate: Mapping[str, Any]) -> dict[str, dict[str, Any]]: + left, right = _numeric_summary(baseline), _numeric_summary(candidate) + ret: dict[str, dict[str, Any]] = {} + for key in sorted(set(left) & set(right)): + if not isinstance(left[key], Mapping) or not isinstance(right[key], Mapping): continue + if "mean" not in left[key] or "mean" not in right[key]: continue + base, cand = float(left[key]["mean"]), float(right[key]["mean"]) + ret[str(key)] = asdict(NumericDelta(base, cand, cand-base, _relative(base, cand))) + return ret + + +def _artifact_paths(manifest: Mapping[str, Any], root: Path | None, suffix: str) -> list[Path]: + paths = [] + for item in manifest.get("artifacts", []): + if not isinstance(item, Mapping) or str(item.get("suffix", "")).lower() != suffix: continue + path = Path(str(item.get("path", ""))) + if not path.is_absolute() and root is not None: path = root / path + if path.is_file(): paths.append(path) + return paths + + +def _kernel_column(fieldnames: Iterable[str]) -> str | None: + names = list(fieldnames) + preferred = ("Kernel_Name", "KernelName", "Kernel", "Name", "kernel_name", "kernel") + return next((name for name in preferred if name in names), None) + + +def _kernel_metrics(manifest: Mapping[str, Any], root: Path | None) -> dict[tuple[str, str], list[float]]: + grouped: dict[tuple[str, str], list[float]] = {} + for path in _artifact_paths(manifest, root, ".csv"): + try: + with path.open(newline="", encoding="utf-8", errors="replace") as handle: + reader = csv.DictReader(handle) + key_column = _kernel_column(reader.fieldnames or ()) + if key_column is None: continue + for row in reader: + kernel = str(row.get(key_column, "")).strip() + if not kernel: continue + for key, value in row.items(): + if key == key_column: continue + try: number = float(value) + except (TypeError, ValueError): continue + if math.isfinite(number): grouped.setdefault((kernel, str(key)), []).append(number) + except OSError: continue + return grouped + + +def _kernel_deltas(baseline: Mapping[str, Any], base_root: Path | None, + candidate: Mapping[str, Any], cand_root: Path | None) -> list[dict[str, Any]]: + left, right = _kernel_metrics(baseline, base_root), _kernel_metrics(candidate, cand_root) + ret = [] + for kernel, metric in sorted(set(left) & set(right)): + base_values, cand_values = left[(kernel, metric)], right[(kernel, metric)] + base_mean, cand_mean = sum(base_values)/len(base_values), sum(cand_values)/len(cand_values) + ret.append(asdict(KernelDelta(kernel, metric, len(base_values), len(cand_values), base_mean, cand_mean, + cand_mean-base_mean, _relative(base_mean, cand_mean)))) + return ret + + +def compare_captures(baseline_value: str | Path | Mapping[str, Any], + candidate_value: str | Path | Mapping[str, Any]) -> dict[str, Any]: + baseline, base_root = _manifest(baseline_value) + candidate, cand_root = _manifest(candidate_value) + compatibility = compatibility_report(baseline, candidate) + if not compatibility["comparable"]: raise IncomparableCaptures("; ".join(compatibility["reasons"])) + return { + "baseline_capture_id": baseline.get("capture_id"), + "candidate_capture_id": candidate.get("capture_id"), + "kind": baseline.get("kind"), + "stage": (baseline.get("summary", {}) or {}).get("stage"), + "compatibility": compatibility, + "numeric_column_deltas": _numeric_deltas(baseline, candidate), + "per_kernel_deltas": _kernel_deltas(baseline, base_root, candidate, cand_root), + "interpretation_contract": { + "lower_is_better_metrics": [], + "higher_is_better_metrics": [], + "note": "Deltas are descriptive only. Metric direction and causal meaning must come from the profiler schema or domain knowledge, not guessed from column names." + } + } From 014175380592ac53b922c59e7fcbe79c1afec216 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:42:01 +0530 Subject: [PATCH 151/168] radeon forge: test comparable hardware profile deltas --- test/test_radeon_forge_profile_compare.py | 81 +++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 test/test_radeon_forge_profile_compare.py diff --git a/test/test_radeon_forge_profile_compare.py b/test/test_radeon_forge_profile_compare.py new file mode 100644 index 0000000000000..ea936b374ec1c --- /dev/null +++ b/test/test_radeon_forge_profile_compare.py @@ -0,0 +1,81 @@ +import csv +import hashlib +import json +import tempfile +import unittest +from pathlib import Path + +from extra.radeon_forge.profiling.compare import IncomparableCaptures, compare_captures + + +class TestProfileCaptureComparison(unittest.TestCase): + def _capture(self, root: Path, capture_id: str, stage: str, rows: list[dict[str, str]], *, command=None, + counters=("SQ_WAVES", "DurationNs"), passed=True, workload_hash="suite-1") -> Path: + directory = root / capture_id + directory.mkdir() + csv_path = directory / "counters.csv" + with csv_path.open("w", newline="", encoding="utf-8") as handle: + writer = csv.DictWriter(handle, fieldnames=["Kernel_Name", "SQ_WAVES", "DurationNs"]) + writer.writeheader(); writer.writerows(rows) + data = csv_path.read_bytes() + numeric = {} + for key in ("SQ_WAVES", "DurationNs"): + values = [float(row[key]) for row in rows] + numeric[key] = {"count":len(values), "min":min(values), "max":max(values), "mean":sum(values)/len(values)} + payload = { + "capture_id":capture_id, "kind":"counters", "passed":passed, + "target_command":command or ["python3", "workload.py", "--case", "decode"], + "artifacts":[{"path":str(csv_path), "size_bytes":len(data), "sha256":hashlib.sha256(data).hexdigest(), "suffix":".csv"}], + "summary":{"stage":stage, "counters":list(counters), "capture_metadata":{"workload_hash":workload_hash}, + "parsed_csv":{"csv_files":1, "csv_rows":len(rows), "numeric_columns":numeric}} + } + manifest = directory / "forge_capture_manifest.json" + manifest.write_text(json.dumps(payload), encoding="utf-8") + return manifest + + def test_compatible_captures_produce_global_and_per_kernel_deltas(self): + with tempfile.TemporaryDirectory() as directory: + root=Path(directory) + baseline=self._capture(root,"base","decode",[ + {"Kernel_Name":"decode_gemv","SQ_WAVES":"64","DurationNs":"1000"}, + {"Kernel_Name":"decode_gemv","SQ_WAVES":"64","DurationNs":"1200"}, + ]) + candidate=self._capture(root,"candidate","decode",[ + {"Kernel_Name":"decode_gemv","SQ_WAVES":"72","DurationNs":"800"}, + {"Kernel_Name":"decode_gemv","SQ_WAVES":"72","DurationNs":"900"}, + ]) + result=compare_captures(baseline,candidate) + self.assertTrue(result["compatibility"]["comparable"]) + self.assertEqual(result["numeric_column_deltas"]["DurationNs"]["baseline"],1100.0) + self.assertEqual(result["numeric_column_deltas"]["DurationNs"]["candidate"],850.0) + row=next(item for item in result["per_kernel_deltas"] if item["kernel"]=="decode_gemv" and item["metric"]=="DurationNs") + self.assertEqual(row["absolute"],-250.0) + self.assertAlmostEqual(row["relative"],-250/1100) + self.assertEqual(result["interpretation_contract"]["lower_is_better_metrics"],[]) + + def test_stage_or_workload_mismatch_is_rejected(self): + with tempfile.TemporaryDirectory() as directory: + root=Path(directory) + rows=[{"Kernel_Name":"k","SQ_WAVES":"1","DurationNs":"1"}] + baseline=self._capture(root,"base","prefill",rows) + candidate=self._capture(root,"candidate","decode",rows) + with self.assertRaisesRegex(IncomparableCaptures,"stage differs"): + compare_captures(baseline,candidate) + other=self._capture(root,"other","prefill",rows,workload_hash="suite-2") + with self.assertRaisesRegex(IncomparableCaptures,"workload_identity differs"): + compare_captures(baseline,other) + + def test_counter_set_or_failed_capture_is_rejected(self): + with tempfile.TemporaryDirectory() as directory: + root=Path(directory) + rows=[{"Kernel_Name":"k","SQ_WAVES":"1","DurationNs":"1"}] + baseline=self._capture(root,"base","decode",rows) + different=self._capture(root,"different","decode",rows,counters=("SQ_WAVES",)) + with self.assertRaisesRegex(IncomparableCaptures,"counters differs"): + compare_captures(baseline,different) + failed=self._capture(root,"failed","decode",rows,passed=False) + with self.assertRaisesRegex(IncomparableCaptures,"candidate capture did not pass"): + compare_captures(baseline,failed) + + +if __name__=="__main__": unittest.main() From d3f3632b75b7b2cf834e25e90a7390b6a5690ea8 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:44:49 +0530 Subject: [PATCH 152/168] radeon forge: add transformer-block transition oracle --- .../workloads/block_hook_harness.py | 192 ++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 extra/radeon_forge/workloads/block_hook_harness.py diff --git a/extra/radeon_forge/workloads/block_hook_harness.py b/extra/radeon_forge/workloads/block_hook_harness.py new file mode 100644 index 0000000000000..343a2642dce55 --- /dev/null +++ b/extra/radeon_forge/workloads/block_hook_harness.py @@ -0,0 +1,192 @@ +from __future__ import annotations + +import argparse, hashlib, importlib.util, json, os, time +from dataclasses import dataclass +from pathlib import Path +from typing import Any, Mapping + +import numpy as np + +from tinygrad import Device, GlobalCounters, Tensor, dtypes +from extra.models.llama import TransformerBlock, precompute_freqs_cis + + +@dataclass(frozen=True) +class HarnessConfig: + dim: int = 128 + hidden_dim: int = 256 + n_heads: int = 4 + n_kv_heads: int = 4 + max_context: int = 128 + prompt_tokens: int = 8 + dtype: str = "float32" + max_abs_error: float = 2e-4 + max_rel_error: float = 2e-3 + + +class ModelShell: + def __init__(self, layer: Any, config: HarnessConfig): + self.layers = [layer] + self.max_context = config.max_context + self.forward_jit = None + + +def _candidate_path(argument: str | None) -> Path: + value = argument or os.environ.get("RADEON_FORGE_CANDIDATE") + if not value: raise ValueError("candidate path is required") + path = Path(value).resolve() + if not path.is_file(): raise FileNotFoundError(path) + return path + + +def _parameters() -> dict[str, Any]: + raw = os.environ.get("RADEON_FORGE_CANDIDATE_JSON", "") + if not raw: return {} + payload = json.loads(raw) + values = payload.get("parameters", {}) if isinstance(payload, Mapping) else {} + if not isinstance(values, Mapping): raise ValueError("candidate parameters must be an object") + return dict(values) + + +def _load_candidate(path: Path, parameters: Mapping[str, Any]): + digest = hashlib.sha256(path.read_bytes()).hexdigest() + spec = importlib.util.spec_from_file_location(f"radeon_forge_candidate_{digest[:16]}", path) + if spec is None or spec.loader is None: raise RuntimeError(f"cannot import candidate {path}") + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + setattr(module, "RADEON_FORGE_PARAMETERS", dict(parameters)) + configure = getattr(module, "configure", None) + if configure is not None: + if not callable(configure): raise TypeError("optional configure must be callable") + configure(dict(parameters)) + factory = getattr(module, "build_replacement", None) + if not callable(factory): raise TypeError("candidate must define build_replacement(original, layer_index, model, context)") + return factory + + +def _block(config: HarnessConfig) -> TransformerBlock: + return TransformerBlock(config.dim, config.hidden_dim, config.n_heads, config.n_kv_heads, + norm_eps=1e-5, max_context=config.max_context) + + +def _reset_cache(block: TransformerBlock) -> None: + attention = getattr(block, "attention", None) + if attention is not None and hasattr(attention, "cache_kv"): delattr(attention, "cache_kv") + + +def _dtype(name: str): + return {"float32":dtypes.float32, "float16":dtypes.float16, "bfloat16":dtypes.bfloat16}[name] + + +def _inputs(config: HarnessConfig, prompt_tokens: int, seed: int): + Tensor.manual_seed(seed) + dtype = _dtype(config.dtype) + prompt = Tensor.randn(1, prompt_tokens, config.dim).cast(dtype).contiguous().realize() + first = Tensor.randn(1, 1, config.dim).cast(dtype).contiguous().realize() + decode = Tensor.randn(1, 1, config.dim).cast(dtype).contiguous().realize() + freqs = precompute_freqs_cis(config.dim // config.n_heads, config.max_context * 2).cast(dtype).contiguous().realize() + mask = Tensor.full((1, 1, prompt_tokens, prompt_tokens), float("-inf"), dtype=dtype).triu(1) + return prompt, first, decode, freqs, mask + + +def _run_transition(callable_block, config: HarnessConfig, prompt_tokens: int, seed: int) -> list[np.ndarray]: + prompt, first, decode, freqs, mask = _inputs(config, prompt_tokens, seed) + outputs = [ + callable_block(prompt, 0, freqs[:, :prompt_tokens], mask).realize().numpy(), + callable_block(first, prompt_tokens, freqs[:, prompt_tokens:prompt_tokens+1], None).realize().numpy(), + callable_block(decode, prompt_tokens+1, freqs[:, prompt_tokens+1:prompt_tokens+2], None).realize().numpy(), + ] + return outputs + + +def _error(reference: list[np.ndarray], candidate: list[np.ndarray]) -> dict[str, Any]: + max_abs = max(float(np.max(np.abs(left.astype(np.float64)-right.astype(np.float64)))) for left, right in zip(reference, candidate)) + max_rel = 0.0 + checked = 0 + for left, right in zip(reference, candidate): + left64, right64 = left.astype(np.float64), right.astype(np.float64) + denominator = np.maximum(np.abs(left64), 1e-8) + max_rel = max(max_rel, float(np.max(np.abs(left64-right64)/denominator))) + checked += left.size + return {"max_abs_error":max_abs, "max_rel_error":max_rel, "checked_values":checked} + + +def validate(candidate_path: Path, config: HarnessConfig, prompt_lengths: tuple[int, ...], parameters: Mapping[str, Any]) -> dict[str, Any]: + block = _block(config) + model = ModelShell(block, config) + factory = _load_candidate(candidate_path, parameters) + max_abs = max_rel = 0.0 + checked = 0 + cases = [] + for case_index, prompt_tokens in enumerate(prompt_lengths): + _reset_cache(block) + reference = _run_transition(block, config, prompt_tokens, seed=1000+case_index) + _reset_cache(block) + context = {"stage":"transition_oracle", "prompt_tokens":prompt_tokens, "batch_size":1, + "parameters":dict(parameters), "device":Device.DEFAULT} + replacement = factory(block, 0, model, context) + if not callable(replacement): raise TypeError("build_replacement must return a callable block") + candidate = _run_transition(replacement, config, prompt_tokens, seed=1000+case_index) + result = _error(reference, candidate) + max_abs, max_rel = max(max_abs, result["max_abs_error"]), max(max_rel, result["max_rel_error"]) + checked += result["checked_values"] + cases.append({"prompt_tokens":prompt_tokens, **result}) + passed = max_abs <= config.max_abs_error and max_rel <= config.max_rel_error + return {"passed":passed, "max_abs_error":max_abs, "max_rel_error":max_rel, "checked_values":checked, + "reason":"" if passed else "candidate diverged from transformer-block transition reference", "cases":cases} + + +def benchmark(candidate_path: Path, config: HarnessConfig, budget: int, parameters: Mapping[str, Any]) -> tuple[list[float], dict[str, Any]]: + block = _block(config) + model = ModelShell(block, config) + factory = _load_candidate(candidate_path, parameters) + replacement = factory(block, 0, model, {"stage":"decode_benchmark", "parameters":dict(parameters), "device":Device.DEFAULT}) + if not callable(replacement): raise TypeError("build_replacement must return a callable block") + _reset_cache(block) + prompt, _, _, freqs, mask = _inputs(config, config.prompt_tokens, seed=2026) + replacement(prompt, 0, freqs[:, :config.prompt_tokens], mask).realize() + samples = [] + kernel_counts = [] + for index in range(budget): + position = config.prompt_tokens + index + Tensor.manual_seed(3000+index) + value = Tensor.randn(1, 1, config.dim).cast(_dtype(config.dtype)).contiguous().realize() + GlobalCounters.reset() + started = time.perf_counter_ns() + replacement(value, position, freqs[:, position:position+1], None).realize() + samples.append((time.perf_counter_ns()-started)/1000.0) + kernel_counts.append(GlobalCounters.kernel_count) + return samples, {"kernel_count_mean":sum(kernel_counts)/len(kernel_counts), "device":Device.DEFAULT, + "prompt_tokens":config.prompt_tokens, "stage":"decode", "parameters":dict(parameters)} + + +def main() -> None: + parser = argparse.ArgumentParser(description="Radeon Forge transformer-block oracle and benchmark") + parser.add_argument("--candidate") + parser.add_argument("--mode", choices=("correctness", "benchmark", "transition"), default="correctness") + parser.add_argument("--dtype", choices=("float32", "float16", "bfloat16"), default="float32") + parser.add_argument("--dim", type=int, default=128) + parser.add_argument("--hidden-dim", type=int, default=256) + parser.add_argument("--prompt-tokens", type=int, default=8) + args = parser.parse_args() + config = HarnessConfig(dim=args.dim, hidden_dim=args.hidden_dim, prompt_tokens=args.prompt_tokens, dtype=args.dtype) + candidate_path, parameters = _candidate_path(args.candidate), _parameters() + prompt_lengths = (1, 4, args.prompt_tokens, min(24, config.max_context-2)) if args.mode == "transition" else (args.prompt_tokens,) + correctness = validate(candidate_path, config, tuple(dict.fromkeys(prompt_lengths)), parameters) + budget = max(1, int(os.environ.get("RADEON_FORGE_BUDGET", "5"))) + samples, metrics = (benchmark(candidate_path, config, budget, parameters) if args.mode == "benchmark" and correctness["passed"] else ([], {})) + payload = { + "samples_us":samples, + "correctness":correctness, + "resources":{}, + "compile_ok":True, + "stable":bool(correctness["passed"]), + "metrics":metrics, + "mode":args.mode, + "candidate_sha256":hashlib.sha256(candidate_path.read_bytes()).hexdigest(), + } + print(json.dumps(payload, default=str)) + raise SystemExit(0 if correctness["passed"] else 2) + + +if __name__ == "__main__": main() From c6a66654099ce2f1b1d237a048724d64c8b0b82b Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:45:38 +0530 Subject: [PATCH 153/168] radeon forge: test transformer-block oracle harness --- test/test_radeon_forge_block_harness.py | 55 +++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 test/test_radeon_forge_block_harness.py diff --git a/test/test_radeon_forge_block_harness.py b/test/test_radeon_forge_block_harness.py new file mode 100644 index 0000000000000..ae4ac277bacf4 --- /dev/null +++ b/test/test_radeon_forge_block_harness.py @@ -0,0 +1,55 @@ +import tempfile +import unittest +from pathlib import Path + +from extra.radeon_forge.workloads.block_hook_harness import HarnessConfig, benchmark, validate + + +IDENTITY = ''' +PARAMETERS = {} +def configure(parameters): + global PARAMETERS + PARAMETERS = dict(parameters) +def build_replacement(original, layer_index, model, context): + assert context["parameters"] == PARAMETERS + return original +''' + +BAD = ''' +def build_replacement(original, layer_index, model, context): + def replacement(x, start_pos, freqs_cis, mask): + return x * 0 + return replacement +''' + + +class TestBlockHookHarness(unittest.TestCase): + def test_identity_replacement_passes_transition_and_benchmark(self): + with tempfile.TemporaryDirectory() as directory: + path=Path(directory)/"identity.py" + path.write_text(IDENTITY,encoding="utf-8") + config=HarnessConfig(dim=32,hidden_dim=64,n_heads=4,n_kv_heads=4,max_context=32,prompt_tokens=2, + max_abs_error=1e-5,max_rel_error=1e-4) + result=validate(path,config,(1,2,4),{"WAVES":2}) + self.assertTrue(result["passed"],result) + self.assertEqual(len(result["cases"]),3) + self.assertGreater(result["checked_values"],0) + samples,metrics=benchmark(path,config,2,{"WAVES":2}) + self.assertEqual(len(samples),2) + self.assertTrue(all(value>0 for value in samples)) + self.assertEqual(metrics["stage"],"decode") + self.assertEqual(metrics["parameters"],{"WAVES":2}) + + def test_incorrect_replacement_fails_reference_oracle(self): + with tempfile.TemporaryDirectory() as directory: + path=Path(directory)/"bad.py" + path.write_text(BAD,encoding="utf-8") + config=HarnessConfig(dim=32,hidden_dim=64,n_heads=4,n_kv_heads=4,max_context=16,prompt_tokens=2, + max_abs_error=1e-5,max_rel_error=1e-4) + result=validate(path,config,(2,),{}) + self.assertFalse(result["passed"]) + self.assertGreater(result["max_abs_error"],config.max_abs_error) + self.assertIn("diverged",result["reason"]) + + +if __name__=="__main__": unittest.main() From 25ac1acbf61d71cd94f6e3356d8cf67c8f95e39c Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:46:22 +0530 Subject: [PATCH 154/168] radeon forge: generate agent-facing candidate scaffolds --- extra/radeon_forge/synthesis/scaffold.py | 77 ++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 extra/radeon_forge/synthesis/scaffold.py diff --git a/extra/radeon_forge/synthesis/scaffold.py b/extra/radeon_forge/synthesis/scaffold.py new file mode 100644 index 0000000000000..1dc7da04e7011 --- /dev/null +++ b/extra/radeon_forge/synthesis/scaffold.py @@ -0,0 +1,77 @@ +from __future__ import annotations + +import json +from typing import Any + +from .hooks import HookDescriptor, HookLayer +from .workspace import KernelSpec + + +def _doc(value: Any) -> str: return json.dumps(value, indent=2, sort_keys=True, default=str) + + +def render_candidate_scaffold(spec: KernelSpec) -> str: + """Render the minimum executable contract, not a performance abstraction. + + The scaffold intentionally contains no tile DSL or scheduling framework. It + gives an agent the exact runtime boundary, invariants, stage predicate and + parameter handoff, while leaving the implementation structure disposable. + """ + hook = HookDescriptor.from_spec(spec) + header = f'''"""Disposable Radeon Forge candidate. + +Spec: {spec.name} +Operation: {spec.operation} +Target: {spec.target} +Objective: {spec.objective} +Placement: {hook.layer.value}:{hook.target} ({hook.mode.value}) +Execution states: {hook.when.signature} + +Invariants: +{chr(10).join(f"- {item}" for item in spec.invariants)} + +This file is not trusted merely because it imports or compiles. It must pass +MockGPU/reference, real hardware, and any held-out phase-transition oracle. +""" + +from __future__ import annotations +from typing import Any, Mapping + +RADEON_FORGE_PARAMETERS: dict[str, Any] = {{}} + + +def configure(parameters: Mapping[str, Any]) -> None: + """Receive the locally autotuned schedule selected for this machine/state.""" + global RADEON_FORGE_PARAMETERS + RADEON_FORGE_PARAMETERS = dict(parameters) + +''' + if hook.layer is HookLayer.TRANSFORMER_BLOCK: + body = '''def build_replacement(original, layer_index: int, model, context: Mapping[str, Any]): + """Return a callable with signature (x, start_pos, freqs_cis, mask). + + `context` describes the activation/validation state. Runtime execution-state + selection is enforced outside this module by the signed hook manifest. + Replace this identity implementation with direct tinygrad/custom-kernel code. + """ + assert context.get("stage") in {"transition_oracle", "decode_benchmark", "first_token", "decode"} + + def replacement(x, start_pos, freqs_cis, mask): + # TODO(agent): implement the model- and gfx1100-specific block/subgraph. + # Keep `original` as the correctness fallback while iterating. + return original(x, start_pos, freqs_cis, mask) + + return replacement +''' + else: + body = '''def build_kernel(*args, **kwargs): + """Build the direct target-specific implementation for this hook contract.""" + raise NotImplementedError("agent must generate a direct implementation") +''' + footer = f'''\n\n# Machine-readable context copied from the durable spec for code-review tools.\nFORGE_SPEC_CONTEXT = {_doc({ + "shapes":dict(spec.shapes), "dtypes":dict(spec.dtypes), "invariants":list(spec.invariants), + "hook":{"layer":hook.layer.value, "target":hook.target, "mode":hook.mode.value, + "adapter":hook.adapter, "selector":dict(hook.selector), "when":hook.when.signature}, + "search":spec.metadata.get("search", {}), + })}\n''' + return header + body + footer From 0f71f582fd9c4143000d2cb4001ac83008bd035e Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:46:37 +0530 Subject: [PATCH 155/168] radeon forge: test agent candidate scaffold contract --- test/test_radeon_forge_scaffold.py | 38 ++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 test/test_radeon_forge_scaffold.py diff --git a/test/test_radeon_forge_scaffold.py b/test/test_radeon_forge_scaffold.py new file mode 100644 index 0000000000000..f5bcff2dfdc79 --- /dev/null +++ b/test/test_radeon_forge_scaffold.py @@ -0,0 +1,38 @@ +import ast +import tempfile +import unittest +from pathlib import Path + +from extra.radeon_forge.synthesis.scaffold import render_candidate_scaffold +from extra.radeon_forge.synthesis.workspace import KernelSpec + + +class TestCandidateScaffold(unittest.TestCase): + def test_transformer_block_scaffold_is_executable_and_preserves_contract(self): + spec=KernelSpec("decode-block","specialize decode",target="gfx1100",invariants=("match reference","preserve KV"), + metadata={"search":{"axes":{"WAVES":[1,2,4]}},"hook":{"layer":"transformer_block","target":"llama.decode.block", + "mode":"replace","adapter":"python_transformer_block","selector":{"indices":"all"}, + "when":{"stages":["first_token","decode"],"batch_sizes":[1]}}}) + source=render_candidate_scaffold(spec) + ast.parse(source) + namespace={} + exec(compile(source,"candidate.py","exec"),namespace) + namespace["configure"]({"WAVES":4}) + self.assertEqual(namespace["RADEON_FORGE_PARAMETERS"],{"WAVES":4}) + original=lambda x,start_pos,freqs,mask:x+1 + replacement=namespace["build_replacement"](original,0,object(),{"stage":"decode"}) + self.assertEqual(replacement(2,0,None,None),3) + self.assertIn("first_token|decode",source) + self.assertIn("preserve KV",source) + self.assertIn('"WAVES"',source) + + def test_non_block_scaffold_does_not_invent_a_dsl(self): + spec=KernelSpec("gemv","direct gemv",invariants=("correct",),metadata={"hook":{"layer":"kernel","target":"gemv"}}) + source=render_candidate_scaffold(spec) + ast.parse(source) + self.assertIn("def build_kernel",source) + self.assertIn("NotImplementedError",source) + self.assertNotIn("class Tile",source) + + +if __name__=="__main__": unittest.main() From d53a7af5bd5feb54f25e9461332d2f229da3d73e Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:48:47 +0530 Subject: [PATCH 156/168] radeon forge: add crash-safe local session persistence --- extra/radeon_forge/runtime/store.py | 104 ++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 extra/radeon_forge/runtime/store.py diff --git a/extra/radeon_forge/runtime/store.py b/extra/radeon_forge/runtime/store.py new file mode 100644 index 0000000000000..ceb6a6ec60f3c --- /dev/null +++ b/extra/radeon_forge/runtime/store.py @@ -0,0 +1,104 @@ +from __future__ import annotations + +import json, os, tempfile, time +from dataclasses import asdict +from pathlib import Path +from typing import Any, Callable, Mapping + +from .backend import InferenceBackend +from .events import TraceEvent, TraceRecorder +from .session import AgentSession, SessionEvent, SessionState +from .tools import ToolCall, ToolRegistry + + +class SessionStore: + """Atomic, local-only persistence for agent sessions and unified traces.""" + FORMAT_VERSION = 1 + + def __init__(self, root: str | Path): + self.root = Path(root).resolve() + self.root.mkdir(parents=True, exist_ok=True) + + def _path(self, session_id: str) -> Path: + if not session_id or any(ch not in "0123456789abcdef" for ch in session_id.lower()): raise ValueError("invalid session id") + return self.root / f"{session_id}.json" + + @staticmethod + def _atomic_json(path: Path, payload: Mapping[str, Any]) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + fd, temporary = tempfile.mkstemp(prefix=path.name+".", suffix=".tmp", dir=path.parent) + try: + with os.fdopen(fd, "w", encoding="utf-8") as handle: + json.dump(payload, handle, indent=2, default=str) + handle.write("\n") + handle.flush(); os.fsync(handle.fileno()) + os.replace(temporary, path) + finally: + try: os.unlink(temporary) + except FileNotFoundError: pass + + def save(self, session: AgentSession) -> Path: + snapshot = session.snapshot() + payload = {"format_version":self.FORMAT_VERSION, "saved_at_s":time.time(), "session":snapshot, + "trace":session.trace.to_dict()} + path = self._path(session.session_id) + self._atomic_json(path, payload) + return path + + def delete(self, session_id: str) -> None: + try: self._path(session_id).unlink() + except FileNotFoundError: pass + + def list(self) -> list[dict[str, Any]]: + ret=[] + for path in sorted(self.root.glob("*.json"), key=lambda item:item.stat().st_mtime, reverse=True): + try: + payload=json.loads(path.read_text(encoding="utf-8")); session=payload["session"] + ret.append({"session_id":session["session_id"], "state":session.get("state","unknown"), + "saved_at_s":payload.get("saved_at_s"), "message_count":len(session.get("messages",[])), + "event_count":len(session.get("events",[])), "path":str(path)}) + except Exception: continue + return ret + + def load_payload(self, session_id: str) -> dict[str, Any]: + path=self._path(session_id) + payload=json.loads(path.read_text(encoding="utf-8")) + if int(payload.get("format_version",0)) != self.FORMAT_VERSION: raise ValueError("unsupported session checkpoint version") + return payload + + def restore(self, session_id: str, backend: InferenceBackend, tools: ToolRegistry, system_prompt: str, + metadata_provider: Callable[[AgentSession, int], Mapping[str, Any]] | None = None) -> AgentSession: + payload=self.load_payload(session_id); saved=payload["session"] + trace_payload=payload.get("trace", {}) + trace=TraceRecorder(str(trace_payload.get("trace_id") or saved.get("trace_id") or "")) + trace_events=[] + for item in trace_payload.get("events", []): + try: + trace_events.append(TraceEvent(str(item["event_id"]), str(item["trace_id"]), str(item["kind"]), str(item["name"]), + int(item["start_ns"]), int(item["end_ns"]) if item.get("end_ns") is not None else None, + str(item["parent_id"]) if item.get("parent_id") is not None else None, dict(item.get("attributes", {})))) + except Exception: continue + trace.extend(trace_events) + session=AgentSession(backend,tools,system_prompt,session_id=str(saved["session_id"]),trace=trace,metadata_provider=metadata_provider) + session.messages=[dict(item) for item in saved.get("messages", [])] + if not session.messages: session.messages=[{"role":"system","content":session._system_prompt()}] + session.events=[] + for item in saved.get("events", []): + try: session.events.append(SessionEvent(int(item["sequence"]),str(item["kind"]),dict(item.get("data",{})),float(item.get("timestamp_s",time.time())))) + except Exception: continue + session._sequence=max((item.sequence for item in session.events),default=0) + pending=saved.get("pending_tool_call") + session.pending_tool_call=(ToolCall(str(pending["call_id"]),str(pending["name"]),dict(pending.get("arguments",{}))) + if isinstance(pending,Mapping) else None) + session.pending_assistant_content=str(saved.get("pending_assistant_content", "")) + session.partial_output="" + session.last_finish_reason=saved.get("last_finish_reason") + prior_state=SessionState(str(saved.get("state",SessionState.IDLE.value))) + if prior_state in {SessionState.GENERATING,SessionState.RUNNING_TOOL}: + session.state=SessionState.IDLE + session._emit("session_recovered", prior_state=prior_state.value, action="incomplete operation was not replayed") + elif prior_state is SessionState.AWAITING_TOOL_APPROVAL and session.pending_tool_call is None: + session.state=SessionState.IDLE + session._emit("session_recovered", prior_state=prior_state.value, action="missing pending tool was cleared") + else: session.state=prior_state + return session From 18a622572b59ff2af82be9e53c1ad4399e7d28a4 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:50:33 +0530 Subject: [PATCH 157/168] radeon forge: test local session restart recovery --- test/test_radeon_forge_persistence.py | 79 +++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 test/test_radeon_forge_persistence.py diff --git a/test/test_radeon_forge_persistence.py b/test/test_radeon_forge_persistence.py new file mode 100644 index 0000000000000..99020197d894b --- /dev/null +++ b/test/test_radeon_forge_persistence.py @@ -0,0 +1,79 @@ +import tempfile +import unittest +from pathlib import Path + +from extra.radeon_forge.backends.fake import ScriptedBackend +from extra.radeon_forge.runtime import ForgeEngine +from extra.radeon_forge.runtime.session import AgentSession, SessionState +from extra.radeon_forge.runtime.store import SessionStore +from extra.radeon_forge.runtime.tools import ToolRegistry +from extra.radeon_forge.permissions import PermissionController + + +class TestSessionPersistence(unittest.TestCase): + def test_completed_session_restores_and_continues(self): + with tempfile.TemporaryDirectory() as directory: + first=ForgeEngine(ScriptedBackend(["first local answer"]),directory) + session=first.create_session(); session_id=session.session_id + first.run_message(session_id,"first question") + trace_id=session.trace.trace_id + first.close() + + second=ForgeEngine(ScriptedBackend(["continued after restart"]),directory) + restored=second.session(session_id) + self.assertEqual(restored.state,SessionState.COMPLETED) + self.assertEqual(restored.trace.trace_id,trace_id) + self.assertEqual(restored.messages[-1]["content"],"first local answer") + second.run_message(session_id,"continue") + self.assertEqual(restored.messages[-1]["content"],"continued after restart") + self.assertGreater(len(restored.trace.events()),0) + second.close() + + def test_pending_tool_approval_restores_without_execution(self): + with tempfile.TemporaryDirectory() as directory: + Path(directory,"hello.txt").write_text("private",encoding="utf-8") + first=ForgeEngine(ScriptedBackend(['{"name":"read_file","arguments":{"path":"hello.txt"}}']),directory) + session=first.create_session(); session_id=session.session_id + first.run_message(session_id,"read the file") + self.assertEqual(session.state,SessionState.AWAITING_TOOL_APPROVAL) + first.close() + + second=ForgeEngine(ScriptedBackend(["read completed after approval"]),directory) + restored=second.session(session_id) + self.assertEqual(restored.state,SessionState.AWAITING_TOOL_APPROVAL) + self.assertEqual(restored.pending_tool_call.name,"read_file") + self.assertFalse(any(event.kind=="tool_started" for event in restored.events)) + second.run_tool_approval(session_id,"approve restored local read") + self.assertEqual(restored.state,SessionState.COMPLETED) + self.assertEqual(restored.messages[-1]["content"],"read completed after approval") + second.close() + + def test_inflight_checkpoint_recovers_to_idle_without_replay(self): + with tempfile.TemporaryDirectory() as directory: + root=Path(directory)/"sessions" + store=SessionStore(root) + backend=ScriptedBackend(["unused"]) + session=AgentSession(backend,ToolRegistry(PermissionController()),"system") + session.messages.append({"role":"user","content":"incomplete turn"}) + session.state=SessionState.GENERATING + session._emit("generation_started",backend="fake",step=1,capabilities={}) + store.save(session) + restored=store.restore(session.session_id,backend,ToolRegistry(PermissionController()),"system") + self.assertEqual(restored.state,SessionState.IDLE) + recovery=next(event for event in restored.events if event.kind=="session_recovered") + self.assertEqual(recovery.data["prior_state"],"generating") + self.assertFalse(any(message.get("role")=="assistant" for message in restored.messages)) + + def test_checkpoint_path_and_format_are_local_and_atomic(self): + with tempfile.TemporaryDirectory() as directory: + store=SessionStore(directory) + session=AgentSession(ScriptedBackend(),ToolRegistry(PermissionController()),"system") + path=store.save(session) + self.assertEqual(path.parent.resolve(),Path(directory).resolve()) + self.assertTrue(path.name.endswith(".json")) + self.assertFalse(list(Path(directory).glob("*.tmp"))) + payload=store.load_payload(session.session_id) + self.assertEqual(payload["format_version"],SessionStore.FORMAT_VERSION) + + +if __name__=="__main__": unittest.main() From e5fac9dfdb2c0a7cc36079be5555496a88447e1a Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:51:24 +0530 Subject: [PATCH 158/168] radeon forge: add local model worker plugin registry --- extra/radeon_forge/backends/plugins.py | 68 ++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 extra/radeon_forge/backends/plugins.py diff --git a/extra/radeon_forge/backends/plugins.py b/extra/radeon_forge/backends/plugins.py new file mode 100644 index 0000000000000..9bd9950cd0edf --- /dev/null +++ b/extra/radeon_forge/backends/plugins.py @@ -0,0 +1,68 @@ +from __future__ import annotations + +import sys +from dataclasses import dataclass, field +from pathlib import Path +from typing import Any, Mapping, Sequence + + +@dataclass(frozen=True) +class WorkerPlugin: + name: str + module: str + description: str + required: tuple[str, ...] + optional: tuple[str, ...] = () + defaults: Mapping[str, Any] = field(default_factory=dict) + + def command(self, values: Mapping[str, Any]) -> list[str]: + config={**dict(self.defaults),**{str(k):v for k,v in values.items() if v is not None}} + missing=[name for name in self.required if config.get(name) in (None,"")] + if missing: raise ValueError(f"worker plugin {self.name!r} is missing required fields: {missing}") + allowed=set(self.required)|set(self.optional) + unknown=sorted(set(config)-allowed) + if unknown: raise ValueError(f"worker plugin {self.name!r} received unsupported fields: {unknown}") + command=[sys.executable,"-m",self.module] + for name in self.required+self.optional: + if name not in config or config[name] is None: continue + value=config[name] + flag="--"+name.replace("_","-") + if isinstance(value,bool): + if value: command.append(flag) + elif isinstance(value,(str,int,float,Path)): command += [flag,str(value)] + elif isinstance(value,Sequence) and not isinstance(value,(str,bytes,bytearray)): + for item in value: command += [flag,str(item)] + else: raise TypeError(f"unsupported worker option {name}: {type(value).__name__}") + return command + + +class WorkerPluginRegistry: + def __init__(self): self._plugins: dict[str,WorkerPlugin]={} + + def register(self,plugin:WorkerPlugin) -> None: + if not plugin.name or plugin.name in self._plugins: raise ValueError(f"duplicate or empty worker plugin {plugin.name!r}") + if not plugin.module.startswith("extra.radeon_forge.backends."): + raise ValueError("worker plugins must resolve to an in-tree local Radeon Forge backend module") + self._plugins[plugin.name]=plugin + + def get(self,name:str) -> WorkerPlugin: + try:return self._plugins[name] + except KeyError as exc:raise KeyError(f"unknown local worker plugin {name!r}; available={sorted(self._plugins)}") from exc + + def list(self) -> list[dict[str,Any]]: + return [{"name":item.name,"module":item.module,"description":item.description, + "required":list(item.required),"optional":list(item.optional),"defaults":dict(item.defaults)} + for item in sorted(self._plugins.values(),key=lambda x:x.name)] + + +def default_worker_plugins() -> WorkerPluginRegistry: + registry=WorkerPluginRegistry() + registry.register(WorkerPlugin( + name="legacy-llama", + module="extra.radeon_forge.backends.tinygrad_llama_worker", + description="Resident tinygrad Llama-family worker with exact KV accounting, stage hooks and kernel profiling.", + required=("model",), + optional=("tokenizer","size","quantize","max_context","seed"), + defaults={"size":"1B","max_context":8192,"seed":42}, + )) + return registry From d951dd59ba521d7db0bc70152235dfbb3a7a476d Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:52:03 +0530 Subject: [PATCH 159/168] radeon forge: test local model worker plugins --- test/test_radeon_forge_plugins.py | 41 +++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 test/test_radeon_forge_plugins.py diff --git a/test/test_radeon_forge_plugins.py b/test/test_radeon_forge_plugins.py new file mode 100644 index 0000000000000..3dadf34a0fe45 --- /dev/null +++ b/test/test_radeon_forge_plugins.py @@ -0,0 +1,41 @@ +import sys +import unittest +from pathlib import Path + +from extra.radeon_forge.backends.plugins import WorkerPlugin, WorkerPluginRegistry, default_worker_plugins + + +class TestWorkerPlugins(unittest.TestCase): + def test_legacy_llama_command_is_local_and_explicit(self): + plugin=default_worker_plugins().get("legacy-llama") + command=plugin.command({"model":Path("/models/local.gguf"),"tokenizer":Path("/models/tokenizer.model"), + "size":"1B","max_context":4096,"seed":7}) + self.assertEqual(command[:3],[sys.executable,"-m","extra.radeon_forge.backends.tinygrad_llama_worker"]) + self.assertIn("/models/local.gguf",command) + self.assertIn("--max-context",command) + self.assertIn("4096",command) + self.assertNotIn("http", " ".join(command)) + + def test_required_and_unknown_fields_fail_closed(self): + plugin=default_worker_plugins().get("legacy-llama") + with self.assertRaisesRegex(ValueError,"missing required"): + plugin.command({}) + with self.assertRaisesRegex(ValueError,"unsupported fields"): + plugin.command({"model":"x","remote_api":"https://example.com"}) + + def test_registry_rejects_external_modules_and_duplicates(self): + registry=WorkerPluginRegistry() + with self.assertRaisesRegex(ValueError,"in-tree local"): + registry.register(WorkerPlugin("remote","third_party.worker","bad",("model",))) + local=WorkerPlugin("local","extra.radeon_forge.backends.worker","ok",("model",)) + registry.register(local) + with self.assertRaisesRegex(ValueError,"duplicate"): + registry.register(local) + + def test_discovery_is_stable(self): + rows=default_worker_plugins().list() + self.assertEqual([row["name"] for row in rows],["legacy-llama"]) + self.assertIn("stage hooks",rows[0]["description"]) + + +if __name__=="__main__": unittest.main() From 52ed99f7a984db6e552be5ebcb02a69a5f00776e Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:52:26 +0530 Subject: [PATCH 160/168] radeon forge: add agent evaluation package --- extra/radeon_forge/evaluation/__init__.py | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 extra/radeon_forge/evaluation/__init__.py diff --git a/extra/radeon_forge/evaluation/__init__.py b/extra/radeon_forge/evaluation/__init__.py new file mode 100644 index 0000000000000..1cdec57d7248a --- /dev/null +++ b/extra/radeon_forge/evaluation/__init__.py @@ -0,0 +1,3 @@ +from .suite import AgentTask, AgentTaskSuite, SuiteResult, TaskResult, run_suite + +__all__=["AgentTask","AgentTaskSuite","SuiteResult","TaskResult","run_suite"] From e0b663d7073546cbf0613cc47b690ec8b55c94b4 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:53:03 +0530 Subject: [PATCH 161/168] radeon forge: add frozen private-agent suite evaluator --- extra/radeon_forge/evaluation/suite.py | 170 +++++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 extra/radeon_forge/evaluation/suite.py diff --git a/extra/radeon_forge/evaluation/suite.py b/extra/radeon_forge/evaluation/suite.py new file mode 100644 index 0000000000000..f026fe2d61f35 --- /dev/null +++ b/extra/radeon_forge/evaluation/suite.py @@ -0,0 +1,170 @@ +from __future__ import annotations + +import hashlib, json, math, re, statistics, time +from dataclasses import asdict, dataclass, field +from pathlib import Path +from typing import Any, Mapping, Sequence + +from ..runtime import ForgeEngine +from ..runtime.session import SessionState + + +@dataclass(frozen=True) +class AgentTask: + task_id: str + prompt: str + expected_tools: tuple[str, ...] = () + allowed_tools: tuple[str, ...] = () + required_output_regex: tuple[str, ...] = () + forbidden_output_regex: tuple[str, ...] = () + max_tokens: int = 256 + max_agent_steps: int = 8 + timeout_seconds: float = 120.0 + metadata: Mapping[str, Any] = field(default_factory=dict) + + @classmethod + def from_mapping(cls, value: Mapping[str, Any]) -> AgentTask: + task_id, prompt = str(value.get("task_id", "")).strip(), str(value.get("prompt", "")).strip() + if not task_id or not prompt: raise ValueError("task_id and prompt are required") + def strings(name: str) -> tuple[str, ...]: + raw=value.get(name,()) + if not isinstance(raw,list) or not all(isinstance(item,str) for item in raw): raise ValueError(f"{name} must be a string array") + return tuple(raw) + return cls(task_id,prompt,strings("expected_tools"),strings("allowed_tools"),strings("required_output_regex"), + strings("forbidden_output_regex"),int(value.get("max_tokens",256)),int(value.get("max_agent_steps",8)), + float(value.get("timeout_seconds",120.0)),dict(value.get("metadata",{}))) + + +@dataclass(frozen=True) +class AgentTaskSuite: + name: str + tasks: tuple[AgentTask, ...] + description: str = "" + metadata: Mapping[str, Any] = field(default_factory=dict) + + @property + def suite_hash(self) -> str: + payload=asdict(self) + return hashlib.sha256(json.dumps(payload,sort_keys=True,separators=(",",":"),default=str).encode()).hexdigest() + + @classmethod + def load(cls,path: str|Path) -> AgentTaskSuite: + payload=json.loads(Path(path).read_text(encoding="utf-8")) + if int(payload.get("format_version",0)) != 1: raise ValueError("unsupported agent suite format") + tasks=tuple(AgentTask.from_mapping(item) for item in payload.get("tasks",())) + if not tasks: raise ValueError("suite requires at least one task") + identifiers=[task.task_id for task in tasks] + if len(identifiers)!=len(set(identifiers)): raise ValueError("task ids must be unique") + return cls(str(payload.get("name","")).strip() or Path(path).stem,tasks,str(payload.get("description","")),dict(payload.get("metadata",{}))) + + +@dataclass(frozen=True) +class TaskResult: + task_id: str + passed: bool + latency_ms: float + final_output: str + observed_tools: tuple[str, ...] + expected_tools: tuple[str, ...] + tool_call_valid: bool + output_valid: bool + terminal_state: str + generated_tokens: int + prompt_tokens: int + failure_reasons: tuple[str, ...] + session_id: str + trace_id: str + + +@dataclass(frozen=True) +class SuiteResult: + suite_name: str + suite_hash: str + passed_tasks: int + total_tasks: int + task_success_rate: float + tool_call_validity_rate: float + p50_latency_ms: float + p95_latency_ms: float + mean_latency_ms: float + results: tuple[TaskResult, ...] + + def to_dict(self) -> dict[str, Any]: return asdict(self) + + +def _percentile(values: Sequence[float], fraction: float) -> float: + if not values:return 0.0 + ordered=sorted(values); index=max(0,min(len(ordered)-1,math.ceil(len(ordered)*fraction)-1)) + return ordered[index] + + +def _last_assistant(messages: Sequence[Mapping[str, Any]]) -> str: + for message in reversed(messages): + if message.get("role")=="assistant" and "" not in str(message.get("content","")): + return str(message.get("content","")) + return "" + + +def _tool_sequence(session) -> tuple[str, ...]: + names=[] + for event in session.events: + if event.kind=="tool_started": + call=event.data.get("call",{}) + if isinstance(call,Mapping) and call.get("name"):names.append(str(call["name"])) + return tuple(names) + + +def _token_usage(session) -> tuple[int,int]: + prompt=generated=0 + for event in session.events: + if event.kind=="prefill":prompt=max(prompt,int(event.data.get("prompt_tokens",0))) + elif event.kind=="generation_done":generated+=int((event.data.get("metrics",{}) or {}).get("generated_tokens",0)) + return prompt,generated + + +def run_task(engine: ForgeEngine,task: AgentTask) -> TaskResult: + session=engine.create_session(); session.max_agent_steps=task.max_agent_steps + started=time.perf_counter_ns(); deadline=time.monotonic()+task.timeout_seconds + failures=[] + try: engine.run_message(session.session_id,task.prompt,task.max_tokens,0.0) + except Exception as exc: failures.append(f"initial generation failed: {type(exc).__name__}: {exc}") + while session.state is SessionState.AWAITING_TOOL_APPROVAL and not failures: + if time.monotonic()>=deadline: + failures.append("task timeout while awaiting tool execution");break + call=session.pending_tool_call + if call is None: + failures.append("session requested approval without a pending tool");break + if call.name not in task.allowed_tools: + failures.append(f"tool {call.name!r} is not allowed by the frozen task policy") + session.reject_tool("not allowed by frozen evaluation policy") + break + try:engine.run_tool_approval(session.session_id,f"Frozen suite permits local tool {call.name}",task.max_tokens) + except Exception as exc: + failures.append(f"tool/resume failed: {type(exc).__name__}: {exc}");break + latency_ms=(time.perf_counter_ns()-started)/1e6 + observed=_tool_sequence(session) + tool_valid=observed==task.expected_tools and all(name in task.allowed_tools for name in observed) + if not tool_valid:failures.append(f"tool sequence expected={task.expected_tools!r} observed={observed!r}") + output=_last_assistant(session.messages) + output_valid=True + for pattern in task.required_output_regex: + if re.search(pattern,output,re.I|re.S) is None: + output_valid=False;failures.append(f"required output pattern did not match: {pattern!r}") + for pattern in task.forbidden_output_regex: + if re.search(pattern,output,re.I|re.S) is not None: + output_valid=False;failures.append(f"forbidden output pattern matched: {pattern!r}") + if session.state not in {SessionState.COMPLETED,SessionState.CANCELLED}: + failures.append(f"non-terminal-success session state: {session.state.value}") + if latency_ms>task.timeout_seconds*1000:failures.append("task exceeded latency timeout") + prompt_tokens,generated_tokens=_token_usage(session) + passed=not failures and tool_valid and output_valid and session.state is SessionState.COMPLETED + return TaskResult(task.task_id,passed,latency_ms,output,observed,task.expected_tools,tool_valid,output_valid, + session.state.value,generated_tokens,prompt_tokens,tuple(failures),session.session_id,session.trace.trace_id) + + +def run_suite(engine: ForgeEngine,suite: AgentTaskSuite) -> SuiteResult: + results=tuple(run_task(engine,task) for task in suite.tasks) + latencies=[item.latency_ms for item in results] + return SuiteResult(suite.name,suite.suite_hash,sum(item.passed for item in results),len(results), + sum(item.passed for item in results)/len(results),sum(item.tool_call_valid for item in results)/len(results), + statistics.median(latencies),_percentile(latencies,0.95),statistics.mean(latencies),results) From 498e5e574a0170d1ddfcaf912d64476538dcf204 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:53:43 +0530 Subject: [PATCH 162/168] radeon forge: add local agent-suite benchmark CLI --- extra/radeon_forge/evaluation/cli.py | 38 ++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 extra/radeon_forge/evaluation/cli.py diff --git a/extra/radeon_forge/evaluation/cli.py b/extra/radeon_forge/evaluation/cli.py new file mode 100644 index 0000000000000..9c8eebc24c7d2 --- /dev/null +++ b/extra/radeon_forge/evaluation/cli.py @@ -0,0 +1,38 @@ +from __future__ import annotations + +import argparse, json, sys +from pathlib import Path + +from ..backends.plugins import default_worker_plugins +from ..runtime import ForgeEngine, JsonlProcessBackend +from .suite import AgentTaskSuite, run_suite + + +def main() -> None: + parser=argparse.ArgumentParser(description="Run a frozen private-agent suite on the local Radeon Forge engine") + parser.add_argument("--suite",type=Path,required=True) + parser.add_argument("--workspace",type=Path,default=Path.cwd()) + parser.add_argument("--model-plugin",default="legacy-llama") + parser.add_argument("--model",type=Path,required=True) + parser.add_argument("--tokenizer",type=Path) + parser.add_argument("--size",default="1B") + parser.add_argument("--quantize") + parser.add_argument("--max-context",type=int,default=8192) + parser.add_argument("--seed",type=int,default=42) + parser.add_argument("--output",type=Path) + args=parser.parse_args() + + plugin=default_worker_plugins().get(args.model_plugin) + command=plugin.command({"model":args.model,"tokenizer":args.tokenizer,"size":args.size,"quantize":args.quantize, + "max_context":args.max_context,"seed":args.seed}) + engine=ForgeEngine(JsonlProcessBackend(command),args.workspace) + try:result=run_suite(engine,AgentTaskSuite.load(args.suite)).to_dict() + finally:engine.close() + text=json.dumps(result,indent=2,default=str)+"\n" + if args.output: + args.output.parent.mkdir(parents=True,exist_ok=True);args.output.write_text(text,encoding="utf-8") + sys.stdout.write(text) + raise SystemExit(0 if result["passed_tasks"]==result["total_tasks"] else 2) + + +if __name__=="__main__":main() From c8b2edbad38ab0da8c8d476799c991a670f5ea14 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:54:05 +0530 Subject: [PATCH 163/168] radeon forge: add frozen private coding-agent suite --- .../workloads/private_code_agent_suite.json | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 extra/radeon_forge/workloads/private_code_agent_suite.json diff --git a/extra/radeon_forge/workloads/private_code_agent_suite.json b/extra/radeon_forge/workloads/private_code_agent_suite.json new file mode 100644 index 0000000000000..2c14f3438bb0a --- /dev/null +++ b/extra/radeon_forge/workloads/private_code_agent_suite.json @@ -0,0 +1,58 @@ +{ + "format_version": 1, + "name": "private-code-agent-v1", + "description": "Deterministic local repository tasks covering inspection, retrieval, tool execution, and resumed generation.", + "metadata": { + "primary_metric": "p95_end_to_end_task_latency", + "constraints": { + "external_network_calls": 0, + "tool_call_validity": 1.0, + "task_success_drop_from_baseline": 0.0 + } + }, + "tasks": [ + { + "task_id": "read-project-identity", + "prompt": "Read the first 80 lines of README.md and tell me the project name. Use the local file tool before answering.", + "expected_tools": ["read_file"], + "allowed_tools": ["read_file"], + "required_output_regex": ["tinygrad"], + "forbidden_output_regex": ["https?://"], + "max_tokens": 192, + "timeout_seconds": 120 + }, + { + "task_id": "locate-global-counters", + "prompt": "Find where GlobalCounters is defined in this private repository, then answer with the file path. Use local text search.", + "expected_tools": ["search_text"], + "allowed_tools": ["search_text"], + "required_output_regex": ["tinygrad/helpers\\.py"], + "forbidden_output_regex": ["https?://"], + "max_tokens": 192, + "timeout_seconds": 120 + }, + { + "task_id": "run-forge-unit-test", + "prompt": "Run only the Radeon Forge KV ledger unit test and report whether it passed. Do not run the full suite.", + "expected_tools": ["run_command"], + "allowed_tools": ["run_command"], + "required_output_regex": ["pass|ok"], + "forbidden_output_regex": ["https?://"], + "max_tokens": 192, + "timeout_seconds": 180, + "metadata": { + "expected_command": "python -m unittest test.test_radeon_forge_kv" + } + }, + { + "task_id": "explain-local-profile-boundary", + "prompt": "Without calling a tool, explain in one sentence why MockGPU timing cannot be used as Radeon performance evidence.", + "expected_tools": [], + "allowed_tools": [], + "required_output_regex": ["hardware|W7900|real GPU"], + "forbidden_output_regex": ["https?://"], + "max_tokens": 96, + "timeout_seconds": 90 + } + ] +} From 142c72e29663c191e4798b5a4729489455a86564 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:54:23 +0530 Subject: [PATCH 164/168] radeon forge: test frozen agent-loop evaluation --- test/test_radeon_forge_evaluation.py | 59 ++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 test/test_radeon_forge_evaluation.py diff --git a/test/test_radeon_forge_evaluation.py b/test/test_radeon_forge_evaluation.py new file mode 100644 index 0000000000000..f3af4c1b13b02 --- /dev/null +++ b/test/test_radeon_forge_evaluation.py @@ -0,0 +1,59 @@ +import json +import tempfile +import unittest +from pathlib import Path + +from extra.radeon_forge.backends.fake import ScriptedBackend +from extra.radeon_forge.evaluation.suite import AgentTask, AgentTaskSuite, run_suite +from extra.radeon_forge.runtime import ForgeEngine + + +class TestAgentEvaluation(unittest.TestCase): + def test_suite_measures_complete_tool_loop(self): + responses=[ + '{"name":"read_file","arguments":{"path":"marker.txt"}}', + 'The marker is ALPHA.', + 'MockGPU is a semantic emulator; only real W7900 hardware provides performance evidence.', + ] + with tempfile.TemporaryDirectory() as directory: + Path(directory,"marker.txt").write_text("ALPHA",encoding="utf-8") + suite=AgentTaskSuite("unit",( + AgentTask("read","Read marker.txt and report the marker.",expected_tools=("read_file",),allowed_tools=("read_file",), + required_output_regex=("ALPHA",),timeout_seconds=10), + AgentTask("explain","Why not use MockGPU timing?",required_output_regex=("real W7900|real.*hardware",),timeout_seconds=10), + )) + engine=ForgeEngine(ScriptedBackend(responses),directory) + result=run_suite(engine,suite) + self.assertEqual(result.passed_tasks,2) + self.assertEqual(result.task_success_rate,1.0) + self.assertEqual(result.tool_call_validity_rate,1.0) + self.assertGreater(result.p50_latency_ms,0) + self.assertEqual(result.results[0].observed_tools,("read_file",)) + self.assertTrue(result.results[0].trace_id) + engine.close() + + def test_unexpected_tool_is_rejected_and_task_fails(self): + with tempfile.TemporaryDirectory() as directory: + suite=AgentTaskSuite("unit",(AgentTask("bad","Answer directly",expected_tools=(),allowed_tools=()),)) + backend=ScriptedBackend(['{"name":"list_files","arguments":{}}']) + engine=ForgeEngine(backend,directory) + result=run_suite(engine,suite) + task=result.results[0] + self.assertFalse(task.passed) + self.assertFalse(task.tool_call_valid) + self.assertIn("not allowed",task.failure_reasons[0]) + self.assertEqual(task.terminal_state,"idle") + engine.close() + + def test_suite_hash_is_stable_and_file_load_validates_ids(self): + suite=AgentTaskSuite("stable",(AgentTask("a","prompt"),),metadata={"x":1}) + self.assertEqual(suite.suite_hash,suite.suite_hash) + with tempfile.TemporaryDirectory() as directory: + path=Path(directory)/"suite.json" + payload={"format_version":1,"name":"x","tasks":[{"task_id":"a","prompt":"one"},{"task_id":"a","prompt":"two"}]} + path.write_text(json.dumps(payload),encoding="utf-8") + with self.assertRaisesRegex(ValueError,"unique"): + AgentTaskSuite.load(path) + + +if __name__=="__main__":unittest.main() From d01342c75ca791cc8e8306a3fa961a9f5b0bfffd Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:55:22 +0530 Subject: [PATCH 165/168] radeon forge: compare agent suites under quality gates --- extra/radeon_forge/evaluation/compare.py | 85 ++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 extra/radeon_forge/evaluation/compare.py diff --git a/extra/radeon_forge/evaluation/compare.py b/extra/radeon_forge/evaluation/compare.py new file mode 100644 index 0000000000000..c6408d0be7ad2 --- /dev/null +++ b/extra/radeon_forge/evaluation/compare.py @@ -0,0 +1,85 @@ +from __future__ import annotations + +import json +from dataclasses import dataclass,asdict +from pathlib import Path +from typing import Any,Mapping + + +class IncomparableSuites(ValueError):pass + + +@dataclass(frozen=True) +class MetricDelta: + baseline:float + candidate:float + absolute:float + relative:float|None + + +@dataclass(frozen=True) +class EvaluationGate: + passed:bool + task_success_preserved:bool + tool_validity_preserved:bool + all_baseline_passes_still_pass:bool + reasons:tuple[str,...] + + +def _load(value:str|Path|Mapping[str,Any])->dict[str,Any]: + if isinstance(value,Mapping):return dict(value) + path=Path(value) + payload=json.loads(path.read_text(encoding="utf-8")) + if not isinstance(payload,dict):raise ValueError("evaluation result must be an object") + return payload + + +def _delta(base:float,candidate:float)->dict[str,Any]: + return asdict(MetricDelta(base,candidate,candidate-base,None if base==0 else (candidate-base)/base)) + + +def _tasks(payload:Mapping[str,Any])->dict[str,Mapping[str,Any]]: + rows=payload.get("results",()) + if not isinstance(rows,list):raise ValueError("evaluation results must be an array") + ret={} + for row in rows: + if not isinstance(row,Mapping) or not row.get("task_id"):continue + ret[str(row["task_id"])]=row + return ret + + +def compare_evaluations(baseline_value:str|Path|Mapping[str,Any],candidate_value:str|Path|Mapping[str,Any], + *,max_task_success_drop:float=0.0,max_tool_validity_drop:float=0.0)->dict[str,Any]: + baseline,candidate=_load(baseline_value),_load(candidate_value) + if baseline.get("suite_hash")!=candidate.get("suite_hash"): + raise IncomparableSuites(f"suite_hash differs: baseline={baseline.get('suite_hash')} candidate={candidate.get('suite_hash')}") + base_tasks,cand_tasks=_tasks(baseline),_tasks(candidate) + if set(base_tasks)!=set(cand_tasks): + raise IncomparableSuites(f"task ids differ: baseline={sorted(base_tasks)} candidate={sorted(cand_tasks)}") + + base_success=float(baseline.get("task_success_rate",0.0));cand_success=float(candidate.get("task_success_rate",0.0)) + base_tools=float(baseline.get("tool_call_validity_rate",0.0));cand_tools=float(candidate.get("tool_call_validity_rate",0.0)) + success_ok=cand_success+max_task_success_drop>=base_success + tools_ok=cand_tools+max_tool_validity_drop>=base_tools + regressed=[task_id for task_id,row in base_tasks.items() if bool(row.get("passed")) and not bool(cand_tasks[task_id].get("passed"))] + reasons=[] + if not success_ok:reasons.append(f"task success regressed {base_success:.4f}->{cand_success:.4f}") + if not tools_ok:reasons.append(f"tool validity regressed {base_tools:.4f}->{cand_tools:.4f}") + if regressed:reasons.append(f"baseline-passing tasks regressed: {regressed}") + gate=EvaluationGate(not reasons,success_ok,tools_ok,not regressed,tuple(reasons)) + + per_task=[] + for task_id in sorted(base_tasks): + left,right=base_tasks[task_id],cand_tasks[task_id] + per_task.append({"task_id":task_id,"baseline_passed":bool(left.get("passed")),"candidate_passed":bool(right.get("passed")), + "latency_ms":_delta(float(left.get("latency_ms",0.0)),float(right.get("latency_ms",0.0))), + "baseline_tools":left.get("observed_tools",[]),"candidate_tools":right.get("observed_tools",[]), + "candidate_failure_reasons":right.get("failure_reasons",[])}) + + latency={name:_delta(float(baseline.get(name,0.0)),float(candidate.get(name,0.0))) + for name in ("p50_latency_ms","p95_latency_ms","mean_latency_ms")} + return {"suite_name":baseline.get("suite_name"),"suite_hash":baseline.get("suite_hash"),"gate":asdict(gate), + "quality":{"task_success_rate":_delta(base_success,cand_success),"tool_call_validity_rate":_delta(base_tools,cand_tools)}, + "latency":latency,"per_task":per_task, + "deployable_speedup":gate.passed and latency["p95_latency_ms"]["absolute"]<0, + "interpretation":"Latency improvement is deployable only when the frozen quality/tool gates pass."} From 9b35ad504ae33468d520e0a7ece95977ecaba413 Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:55:42 +0530 Subject: [PATCH 166/168] radeon forge: test quality-gated agent latency comparison --- test/test_radeon_forge_evaluation_compare.py | 46 ++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 test/test_radeon_forge_evaluation_compare.py diff --git a/test/test_radeon_forge_evaluation_compare.py b/test/test_radeon_forge_evaluation_compare.py new file mode 100644 index 0000000000000..7ca4714f4a6c2 --- /dev/null +++ b/test/test_radeon_forge_evaluation_compare.py @@ -0,0 +1,46 @@ +import unittest + +from extra.radeon_forge.evaluation.compare import IncomparableSuites,compare_evaluations + + +def result(suite_hash="same",success=1.0,tools=1.0,p95=100.0,passed=(True,True),latencies=(80.0,120.0)): + return {"suite_name":"private","suite_hash":suite_hash,"task_success_rate":success,"tool_call_validity_rate":tools, + "p50_latency_ms":90.0,"p95_latency_ms":p95,"mean_latency_ms":100.0, + "results":[ + {"task_id":"a","passed":passed[0],"latency_ms":latencies[0],"observed_tools":["read_file"],"failure_reasons":[]}, + {"task_id":"b","passed":passed[1],"latency_ms":latencies[1],"observed_tools":[],"failure_reasons":[]}, + ]} + + +class TestEvaluationComparison(unittest.TestCase): + def test_faster_quality_preserving_candidate_is_deployable(self): + baseline=result(p95=120.0) + candidate=result(p95=80.0,latencies=(60.0,90.0)) + comparison=compare_evaluations(baseline,candidate) + self.assertTrue(comparison["gate"]["passed"]) + self.assertTrue(comparison["deployable_speedup"]) + self.assertEqual(comparison["latency"]["p95_latency_ms"]["absolute"],-40.0) + self.assertEqual(len(comparison["per_task"]),2) + + def test_speed_cannot_hide_task_or_tool_regression(self): + baseline=result(p95=120.0) + candidate=result(success=0.5,tools=0.5,p95=50.0,passed=(True,False),latencies=(30.0,40.0)) + comparison=compare_evaluations(baseline,candidate) + self.assertFalse(comparison["gate"]["passed"]) + self.assertFalse(comparison["deployable_speedup"]) + self.assertIn("task success regressed",comparison["gate"]["reasons"][0]) + self.assertTrue(any("baseline-passing tasks regressed" in reason for reason in comparison["gate"]["reasons"])) + + def test_different_suite_or_task_ids_are_rejected(self): + with self.assertRaisesRegex(IncomparableSuites,"suite_hash differs"): + compare_evaluations(result("a"),result("b")) + candidate=result();candidate["results"]=candidate["results"][:1] + with self.assertRaisesRegex(IncomparableSuites,"task ids differ"): + compare_evaluations(result(),candidate) + + def test_explicit_tolerance_is_visible_and_bounded(self): + comparison=compare_evaluations(result(success=1.0),result(success=0.99),max_task_success_drop=0.02) + self.assertTrue(comparison["gate"]["task_success_preserved"]) + + +if __name__=="__main__":unittest.main() From 208493b68a2643313243349e313ee3ddd2d3f10a Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:57:11 +0530 Subject: [PATCH 167/168] radeon forge: test agent-metric deployment gate --- test/test_radeon_forge_deployment_metrics.py | 52 ++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 test/test_radeon_forge_deployment_metrics.py diff --git a/test/test_radeon_forge_deployment_metrics.py b/test/test_radeon_forge_deployment_metrics.py new file mode 100644 index 0000000000000..93921e2963e41 --- /dev/null +++ b/test/test_radeon_forge_deployment_metrics.py @@ -0,0 +1,52 @@ +import tempfile +import unittest + +from extra.radeon_forge.synthesis.deployment import SafeHookRegistry +from extra.radeon_forge.synthesis.hooks import HookActivationError,RuntimeFingerprint +from extra.radeon_forge.synthesis.workspace import CandidateWorkspace,KernelSpec + + +SOURCE=''' +def build_replacement(original, layer_index, model, context): + return original +''' + + +class TestDeploymentMetricGate(unittest.TestCase): + def test_stateful_candidate_requires_transition_and_agent_metric(self): + with tempfile.TemporaryDirectory() as directory: + workspace=CandidateWorkspace(directory) + spec=workspace.save_spec(KernelSpec("decode","block",target="gfx1100",invariants=("match",), + metadata={"heldout_command":["python3","transition.py"],"require_agent_evaluation":True, + "hook":{"layer":"transformer_block","target":"llama.block","adapter":"python_transformer_block", + "when":{"stages":["first_token","decode"]}}})) + candidate=workspace.create_candidate(spec.spec_id,SOURCE,"faster block") + workspace.update(candidate.candidate_id,"heldout_passed",{"heldout":{"passed":True}}) + registry=SafeHookRegistry(workspace) + fingerprint=RuntimeFingerprint(architecture="gfx1100",runtime="tinygrad",model_family="llama") + with self.assertRaisesRegex(HookActivationError,"frozen agent-suite"): + registry.activate(candidate.candidate_id,fingerprint,"deploy") + + workspace.update(candidate.candidate_id,"evaluation_failed",{"agent_evaluation_comparison":{"gate":{"passed":False}}}) + with self.assertRaisesRegex(HookActivationError,"frozen agent-suite"): + registry.activate(candidate.candidate_id,fingerprint,"deploy") + + workspace.update(candidate.candidate_id,"evaluation_passed",{"agent_evaluation_comparison":{"gate":{"passed":True}}}) + active=registry.activate(candidate.candidate_id,fingerprint,"quality and latency gates passed") + self.assertEqual(active.candidate_id,candidate.candidate_id) + + def test_stateless_non_final_hook_keeps_lighter_policy(self): + with tempfile.TemporaryDirectory() as directory: + workspace=CandidateWorkspace(directory) + spec=workspace.save_spec(KernelSpec("advisory","block",target="gfx1100",invariants=("match",), + metadata={"heldout_command":["python3","transition.py"], + "hook":{"layer":"transformer_block","target":"llama.block","adapter":"python_transformer_block", + "when":{"stages":["decode"]}}})) + candidate=workspace.create_candidate(spec.spec_id,SOURCE,"validated") + workspace.update(candidate.candidate_id,"heldout_passed",{}) + active=SafeHookRegistry(workspace).activate(candidate.candidate_id, + RuntimeFingerprint(architecture="gfx1100"),"transition passed") + self.assertEqual(active.candidate_id,candidate.candidate_id) + + +if __name__=="__main__":unittest.main() From 5492257497b6f5ea0a0e04263ecf4715e52d921c Mon Sep 17 00:00:00 2001 From: Rishav Sanjay <83537945+rishavsanjay@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:58:28 +0530 Subject: [PATCH 168/168] radeon forge: normalize recognized ATT and SQTT exports --- .../radeon_forge/profiling/sqtt_normalize.py | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 extra/radeon_forge/profiling/sqtt_normalize.py diff --git a/extra/radeon_forge/profiling/sqtt_normalize.py b/extra/radeon_forge/profiling/sqtt_normalize.py new file mode 100644 index 0000000000000..03eaf76a57040 --- /dev/null +++ b/extra/radeon_forge/profiling/sqtt_normalize.py @@ -0,0 +1,127 @@ +from __future__ import annotations + +import csv, json, math +from collections import Counter,defaultdict +from dataclasses import asdict,dataclass +from pathlib import Path +from typing import Any,Iterable,Mapping + + +@dataclass(frozen=True) +class ParsedArtifact: + path:str + format:str + parsed:bool + event_count:int + duration_us:float + categories:Mapping[str,int] + names:Mapping[str,int] + wave_count:int|None=None + instruction_classes:Mapping[str,int]|None=None + reason:str="" + + +class UnsupportedSQTTFormat(ValueError):pass + + +def _manifest_path(value:str|Path)->Path: + path=Path(value) + if path.is_dir():path=path/"forge_capture_manifest.json" + if not path.is_file():raise FileNotFoundError(path) + return path + + +def _artifact_paths(manifest:Mapping[str,Any],root:Path)->list[Path]: + paths=[] + for item in manifest.get("artifacts",()): + if not isinstance(item,Mapping):continue + path=Path(str(item.get("path",""))) + if not path.is_absolute():path=root/path + if path.is_file():paths.append(path) + return paths + + +def _perfetto(path:Path)->ParsedArtifact: + payload=json.loads(path.read_text(encoding="utf-8",errors="replace")) + events=payload.get("traceEvents") if isinstance(payload,Mapping) else None + if not isinstance(events,list):raise UnsupportedSQTTFormat("JSON does not contain traceEvents") + categories=Counter();names=Counter();duration=0.0;wave_ids=set();instructions=Counter() + for event in events: + if not isinstance(event,Mapping):continue + category=str(event.get("cat","uncategorized"));name=str(event.get("name","unnamed")) + categories[category]+=1;names[name]+=1 + try: + value=float(event.get("dur",0.0)) + if math.isfinite(value) and value>=0:duration+=value + except (TypeError,ValueError):pass + args=event.get("args",{}) + if isinstance(args,Mapping): + for key in ("wave","wave_id","waveId"): + if key in args:wave_ids.add(str(args[key])) + for key in ("instruction_class","instruction_type","inst_class","opcode_class"): + if key in args:instructions[str(args[key])]+=1 + return ParsedArtifact(str(path),"perfetto-json",True,len(events),duration,dict(categories),dict(names), + len(wave_ids) if wave_ids else None,dict(instructions) if instructions else None) + + +def _column(fieldnames:Iterable[str],candidates:tuple[str,...])->str|None: + names=list(fieldnames) + lowered={name.lower():name for name in names} + for candidate in candidates: + if candidate.lower() in lowered:return lowered[candidate.lower()] + return None + + +def _table(path:Path)->ParsedArtifact: + delimiter="\t" if path.suffix.lower() in {".tsv",".tab"} else "," + with path.open(newline="",encoding="utf-8",errors="replace") as handle: + reader=csv.DictReader(handle,delimiter=delimiter);fields=reader.fieldnames or [] + if not fields:raise UnsupportedSQTTFormat("table has no header") + wave_col=_column(fields,("wave","wave_id","waveid")) + inst_col=_column(fields,("instruction_class","instruction_type","inst_class","opcode_class","instruction")) + name_col=_column(fields,("kernel_name","kernel","name","event")) + category_col=_column(fields,("category","cat","type","event_type")) + duration_col=_column(fields,("duration_us","duration","dur","elapsed_us")) + start_col=_column(fields,("start_us","start","begin"));end_col=_column(fields,("end_us","end","finish")) + rows=0;duration=0.0;waves=set();instructions=Counter();names=Counter();categories=Counter() + for row in reader: + rows+=1 + if wave_col and row.get(wave_col):waves.add(str(row[wave_col])) + if inst_col and row.get(inst_col):instructions[str(row[inst_col])]+=1 + if name_col and row.get(name_col):names[str(row[name_col])]+=1 + if category_col and row.get(category_col):categories[str(row[category_col])]+=1 + try: + if duration_col and row.get(duration_col) not in (None,""):value=float(row[duration_col]) + elif start_col and end_col:value=float(row[end_col])-float(row[start_col]) + else:value=0.0 + if math.isfinite(value) and value>=0:duration+=value + except (TypeError,ValueError):pass + recognized=bool(wave_col or inst_col or duration_col or (start_col and end_col)) + if not recognized:raise UnsupportedSQTTFormat("table lacks wave, instruction or timeline columns") + return ParsedArtifact(str(path),"sqtt-table",True,rows,duration,dict(categories),dict(names), + len(waves) if waves else None,dict(instructions) if instructions else None) + + +def normalize_capture(value:str|Path)->dict[str,Any]: + manifest_path=_manifest_path(value);root=manifest_path.parent + manifest=json.loads(manifest_path.read_text(encoding="utf-8")) + parsed=[];unsupported=[] + for path in _artifact_paths(manifest,root): + if path==manifest_path:continue + try: + if path.suffix.lower()==".json":item=_perfetto(path) + elif path.suffix.lower() in {".csv",".tsv",".tab"}:item=_table(path) + else:raise UnsupportedSQTTFormat(f"no registered decoder for {path.suffix or 'binary'}") + parsed.append(item) + except (UnsupportedSQTTFormat,json.JSONDecodeError,csv.Error) as exc: + unsupported.append(ParsedArtifact(str(path),"unknown",False,0,0.0,{}, {},reason=str(exc))) + total_events=sum(item.event_count for item in parsed) + total_duration=sum(item.duration_us for item in parsed) + waves=sum(item.wave_count or 0 for item in parsed) + instructions=Counter() + for item in parsed:instructions.update(item.instruction_classes or {}) + return {"capture_id":manifest.get("capture_id"),"kind":manifest.get("kind"),"parsed":bool(parsed), + "recognized_artifacts":[asdict(item) for item in parsed],"unsupported_artifacts":[asdict(item) for item in unsupported], + "summary":{"recognized_count":len(parsed),"unsupported_count":len(unsupported),"event_count":total_events, + "duration_us":total_duration,"wave_count":waves or None,"instruction_classes":dict(instructions)}, + "contract":"Only recognized structured exports are normalized. Raw ATT/SQTT binaries remain immutable evidence until a matching decoder is installed."}