Skip to content

v0.8.0

Choose a tag to compare

@github-actions github-actions released this 13 Aug 08:01
· 58 commits to master since this release

PerfLens 0.8.0

Highlights

A stabilization release. The feature set was frozen on 2026-08-13 with the
MCP server as the last capability added; everything since has been hygiene,
CI hardening and documentation truth. It also publishes the large body of
work that accumulated after 0.7.0 — the server module split, the React
frontend, and API v2.

Upgrading: API v2 renamed every REST endpoint (see below), and
perflens.server has been removed. The web UI and the agent wire protocol
are unaffected.

Removed

  • perflens.server — a compatibility shim introduced in the 0.6.0-era
    restructure and marked "kept for one release"; it has now outlived two.
    Import from the modules that actually hold the code: perflens.app
    (AppContext, build_context, main), perflens.config
    (ServerConfig), perflens.state (ProfilingState, MetricsState).
    This is a breaking change to a public import path. The repo-only
    server/perflens_server.py wrapper, the orphaned run_server.sh, and an
    empty requirements-server.txt went with it.

Added

  • MCP server (perflens mcp) — serves profiling data to LLM agents
    over the Model Context Protocol, so an agent can answer "why is this
    slow" against real perf data and drive a live run on a device.
    Nineteen tools: hot functions (self and total), hot stacks, hardware
    counters with derived IPC and miss rates, source hot lines, session
    comparison, per-thread breakdowns, device health, agent lifecycle, and
    file export. It is a client of the existing HTTP API — no core changes —
    and the SDK is an optional extra (pip install 'perflens[mcp]'), so the
    default install stays a four-dependency package. --read-only omits the
    device-control and export tools; config mutation, session deletion and
    the file browser are never exposed. Tools return ranked, capped views
    with an explicit next-page call, because a single event's snapshot runs
    from kilobytes to megabytes.
  • skills/perflens-profiling/ — a companion skill teaching an agent
    the method the tools support: orient first, pick the event that matches
    the question, read self and total, drill to hot source lines,
    corroborate with IPC and device health, and the pitfalls that produce
    confidently wrong answers.
  • API v2 — consistent REST surface (/api/snapshot, paginated
    /api/sessions with DELETE, /api/sessions/{id}/export,
    /api/live/export, /api/threads, /api/window, /api/agent,
    unified /api/config), a uniform {"error": {code, message}} envelope
    with real status codes, and SSE consolidated to status / agent /
    data_version / perf_stat / metrics.
  • React 19 + TypeScript + Vite frontend replacing the vanilla-JS UI at
    full feature parity, with types generated from the committed OpenAPI
    schema and a self-contained Playwright E2E suite. The wheel ships the
    prebuilt assets, so uvx perflens still needs no Node.
  • Typed Pydantic API models — the server split into AppContext
    modules (app, config, state, agentlink, sessions, export),
    with the OpenAPI schema exported to frontend/openapi.json and
    drift-checked in CI.
  • UX polish: keyboard shortcuts with a help overlay, loading skeletons,
    a diff legend, and accessibility roles on tabs and icon-only buttons.

Fixed

  • The no-UI fallback answered 503 for every unmatched path. Once the
    frontend became a gitignored Vite output, a source checkout (and the
    Node-free CI job) took create_app's fallback branch, whose catch-all
    route returned the "UI not built" page for everything — so path
    traversal probes got 503 instead of 404 and a mistyped API URL got HTML
    instead of the error envelope. It now answers / only and stays out of
    the OpenAPI schema; every other path 404s exactly as the static mount
    does.
  • OpenAPI drift across Python versions. The 413 response carried no
    explicit description, so FastAPI filled it from
    http.client.responses[413] — whose phrase changed in Python 3.13
    ("Request Entity Too Large" → "Content Too Large"). Pinned, so the
    exported schema no longer depends on the interpreter.
  • __version__ had drifted to 0.6.0 while the package was 0.7.0; all
    three version locations now agree.
  • The test suite no longer behaves differently depending on whether the
    frontend happened to be built locally.
  • Deep call stacks returned 500 from /api/snapshot, blanking the UI.
    orjson refuses to encode past a fixed nesting depth (254 containers), and
    a flame graph level costs two of them — the node and its children list —
    so a profile containing a stack deeper than ~126 frames could not be
    serialized at all. Deeply recursive workloads really do produce such
    stacks. Flame graph depth is now capped, with the cut point marked
    truncated, so one very deep stack renders short instead of taking the
    whole view down. Found by profiling a 25-thread local workload; the
    committed test fixtures are too shallow to reach it.
  • perflens_status (MCP) claimed source annotation was unavailable when
    it wasn't.
    It keyed the warning on symbols_loaded, which only counts
    the eager pre-index pass that runs when a binary is configured at runtime
    — so a server started with --binary reported zero while resolving
    symbols lazily and serving line-level annotation fine. The tool now also
    reports the source-index size and warns only when nothing is resolvable,
    so an agent is no longer told to skip a working feature.
  • The docs drawer displayed a version the build was not. It carried a
    hand-typed v0.8.0 string inside a package that shipped as 0.7.0, wired
    to nothing and absent from the release checklist. It is now injected from
    the canonical VERSION file at build time, and tools/check_version.py
    asserts every location agrees — including that no version literal has
    crept back into the frontend source. Wired into CI.
  • The committed test fixtures carried device IP addresses in their
    session_id and agent metadata, against this project's own rule that
    no addresses appear anywhere in the repo. Renamed to generic identifiers.
  • Replaying a saved session showed no hardware counters. Both fixture
    materializers discarded the captured metadata and wrote a stub with
    total_samples: 0 and an empty perf_stat, so a replayed session
    rendered a single empty stat card instead of the full counter set.

Changed

  • Docs screenshots and the demo GIF are regenerated from the React UI
    and now cover the differential view, timeline scrubbing, keyboard
    shortcuts, flame graph search and zoom, export, and the in-app docs
    drawer — twelve stills, up from seven captured before the rewrite. They
    come from a Playwright harness (frontend/docs-shots/) that CI
    smoke-runs on every pull request, so it cannot silently rot the way the
    puppeteer scripts it replaces did.
  • Every runtime dependency gained an upper bound (fastapi<1.0,
    uvicorn<1.0, orjson<4, zstandard<1.0, httpx<1.0). Floors alone
    allowed a future major release to resolve into a fresh uvx perflens
    install and break it with no change here. Note the consequence: when
    FastAPI 1.0 ships, installs pin to the last 0.x until a new release.
  • CI actions moved off the deprecated Node 20 runtime, lint now runs on
    pull requests (it had only ever run post-merge), and the release path
    verifies the schema and version before building.

Install

# Server (any machine with Python 3.10+; everything user-space):
uvx perflens                       # or: pipx install perflens
uvx --from ./perflens-0.8.0-py3-none-any.whl perflens   # from this release

# Agent (on the target device — zero dependencies, no sudo):
curl -fsSL https://raw.githubusercontent.com/harshithsunku/perflens/master/install-agent.sh | sh

Assets

Asset What it is
perflens-0.8.0-py3-none-any.whl Server — Python wheel
perflens-0.8.0.tar.gz Server — Python sdist
perflens-agent-c-0.8.0-linux-aarch64.tar.gz Agent — versioned tarball
perflens-agent-c-0.8.0-linux-aarch64_be.tar.gz Agent — versioned tarball
perflens-agent-c-0.8.0-linux-armeb.tar.gz Agent — versioned tarball
perflens-agent-c-0.8.0-linux-armv7l.tar.gz Agent — versioned tarball
perflens-agent-c-0.8.0-linux-x86_64.tar.gz Agent — versioned tarball
perflens-agent-linux-aarch64 Agent — static binary (stable name; used by install-agent.sh and --update)
perflens-agent-linux-aarch64_be Agent — static binary (stable name; used by install-agent.sh and --update)
perflens-agent-linux-armeb Agent — static binary (stable name; used by install-agent.sh and --update)
perflens-agent-linux-armv7l Agent — static binary (stable name; used by install-agent.sh and --update)
perflens-agent-linux-x86_64 Agent — static binary (stable name; used by install-agent.sh and --update)
perflens-tools-linux-aarch64.tar.gz Static addr2line+readelf for perflens provision
perflens-tools-linux-aarch64.tar.gz.sha256 Tools bundle checksum
perflens-tools-linux-x86_64.tar.gz Static addr2line+readelf for perflens provision
perflens-tools-linux-x86_64.tar.gz.sha256 Tools bundle checksum