docs / build with the engine

Local quick start

Open once, clean repeatedly, close when done.

Cleanup Engine is a public beta. These guides are generated from the SDK repository at v0.2.0-beta.1; file links open it on GitHub.

Quick start

1. Add the package

The root package has no MLX dependency. Local inference is distributed as the standalone PomvoxCleanupMLX package, generated from Runtime/MLX in this source repository. Add the public runtime repository in Xcode at exact version 0.2.0-beta.1, or declare:

// Package.swift dependency
.package(url: "https://github.com/pomvox/pomvox-cleanup-mlx.git", exact: "0.2.0-beta.1")

// Application target dependency
.product(name: "PomvoxCleanupMLX", package: "pomvox-cleanup-mlx")

Use an Xcode build to package the dependency's Metal shader resources. A plain swift build is insufficient to package the required shader library. The separate consumer demonstrates a working application build. The runtime pulls the matching core SDK version automatically; no submodule is required. Core/cloud-only consumers can instead add https://github.com/pomvox/pomvox-cleanup-engine.git at exact version 0.2.0-beta.1 and select PomvoxCleanup, PomvoxNotes, PomvoxDiarization or PomvoxCleanupCloud.

2. Prepare assets already on disk

The baseline requires the exact snapshot recorded in pack.json. Given that snapshot:

python3 scripts/prepare-local-pack.py /path/to/pinned/snapshot .local/simplewords-v3

Apps can use the native PackInstaller.install(snapshot:manifestData:destination:) with the trusted bundled manifest instead of invoking Python. Install outside cloud-synced folders, keep the pack immutable, and pass the returned handle to .validated(installedPack) when opening. Model access is separate from SDK installation: the pinned upstream model currently requires accepting access conditions.

The tool copies and verifies the seven pinned artifacts, refuses existing destinations and cleans up failed copies. It never downloads or modifies the source. The upstream model currently has an access gate; reproducible public acquisition and an archived license/notice record remain release requirements. See installed packs.

3. Open once, clean repeatedly, close when done

import Foundation
import PomvoxCleanupMLX // Exports the runtime-independent PomvoxCleanup API.

let cleaner = try await Cleaner.open(
    pack: .directory(URL(fileURLWithPath: "/absolute/path/to/installed/pack")),
    runtime: .mlx,
    policy: .local
)

let result = try await cleaner.clean(CleanupRequest(
    "um please send the pomvox report tomorrow",
    vocabulary: ["Pomvox"],
    deadline: .seconds(2)
))
print(result.text)
print(result.status)
await cleaner.close()

open verifies hashes, loads the model, prepares the prefix and warms Metal kernels. Missing or incompatible assets throw before a usable cleaner is returned. Keep the pack immutable while open. The host should close its cleaner on both success and error paths when retiring it; opening per request wastes preparation work.

The baseline supports English and bounded vocabulary. It rejects nonempty context or settings, including style. Unicode-safe edits do not imply multilingual model quality. Output guards are heuristics and cannot guarantee meaning preservation.

Outcomes and cancellation

OutcomeMeaningHost action
.cleanedAccepted output differs from inputInspect/apply the result
.unchangedAccepted output equals input, or input was emptyPreserve the result
.fallback(reason)Cleanup did not produce an accepted resultResult contains the exact input bytes and zero edits
CancellationErrorThe caller canceled or its session was supersededDo not insert text
Other thrown errorInvalid request, unsupported configuration or setup failureCorrect configuration or present an error

Fallback reasons include timeout, rejection, unavailability, queue saturation and token limits. The local cleaner runs one request and queues at most two. A canceled/timed-out worker keeps its resources until it returns; replacement work is refused while it remains unresponsive. close() stops admission immediately and may return before that worker releases resources. A GPU kernel cannot be forcibly interrupted by this SDK.

Explicit deadlines cover admitted queueing and request work. The default is min(60, max(5, 2 + 0.012 × characterCount)) seconds. Preparation is measured separately. See lifecycle and request limits.

Applying edits

let reconstructed = try TextEdit.applying(result.edits, to: originalTranscript)
// Reconstructed bytes equal result.text. Offsets refer to the original transcript.
for edit in result.edits {
    let swiftRange = try edit.range(in: originalTranscript)
    let textViewRange = try edit.utf16Range(in: originalTranscript)
    // Use the appropriate range for your host editor.
}

Edits use half-open UTF-8 byte offsets, not character offsets. Invalid or overlapping ranges throw. The local engine emits one minimal contiguous replacement; it may include an unchanged interior span and does not describe editorial intent. Keep the original transcript for mapping these ranges.

Speaker transcripts and notes

For recordings with more than one voice, the engine cleans a speaker-attributed transcript one turn per ordinary request and keeps the structure intact. The host supplies recognized words with timings and who-spoke-when segments; Transcript.attribute aligns them, cleaner.clean(transcript) cleans each turn through the same admission, guards and fallback, and PomvoxNotes extracts deterministic notes (action items, decisions, questions, list items, participants) as verbatim quotes with provenance, rendered to Markdown. No model writes the notes.

import PomvoxNotes // re-exports the transcript types

let transcript = Transcript.attribute(words: timedWords, segments: speakerSegments)
let cleaned = try await cleaner.clean(transcript, options: TranscriptCleanOptions(overallDeadline: .seconds(120)))
let markdown = NotesRenderer.markdown(Notes.extract(from: cleaned))

A turn the model never sees keeps its exact bytes and says why in a warning. Diarization itself needs audio and a model: the root package carries only the PomvoxDiarization contract and a verified diarizer pack format, and the optional Runtime/Diarization package (PomvoxDiarizationFluid) runs FluidAudio 0.15.5's offline pyannote community-1 pipeline on the Neural Engine from a pinned, hash-checked pack (packs/pyannote-community1-coreml), never downloading. See speaker transcripts and notes.

Try it from a browser

A browser cannot run the SDK, but it can talk to a local companion process that does. Examples/LocalServer serves the cleanup and diarization wire protocols on 127.0.0.1 over the real local runtimes, plus a demo endpoint for the whole speaker-notes pipeline, with a one-page web app: record in the browser, get who-said-what, cleaned turns and notes back.