SPB Git

spb/forge-studio Public

The Instruments of LLM training — a native macOS cockpit for Forge. Train language models from scratch on Apple Silicon without a terminal.

Swift 95.7% Shell 4.3%
2.6 KB · 67 lines swift
Raw Blame History
1// Author: Simon-Pierre Boucher — contact@spboucher.ai2//3// Finds and validates the forge binary: a candidate is accepted only if4// `forge info` runs and reports a device — the same probe surfaces the GPU5// name for the header. Paths persist in UserDefaults.6import Foundation78struct ForgeValidation: Equatable, Sendable {9    var version: String   // device line, e.g. "Apple M5 Max"10    var binaryPath: String11}1213enum ForgeBinaryLocator {14    static let defaultsKey = "forge.binary.path"15    static let workspaceKey = "forge.workspace.path"1617    static var savedBinaryURL: URL? {18        UserDefaults.standard.string(forKey: defaultsKey).map { URL(fileURLWithPath: $0) }19    }2021    static var savedWorkspaceURL: URL? {22        UserDefaults.standard.string(forKey: workspaceKey).map { URL(fileURLWithPath: $0) }23    }2425    static func save(binary: URL) {26        UserDefaults.standard.set(binary.path, forKey: defaultsKey)27    }2829    static func save(workspace: URL) {30        UserDefaults.standard.set(workspace.path, forKey: workspaceKey)31    }3233    /// Candidate locations, most specific first.34    static func candidates() -> [URL] {35        var urls: [URL] = []36        if let saved = savedBinaryURL { urls.append(saved) }37        let home = FileManager.default.homeDirectoryForCurrentUser38        urls.append(home.appendingPathComponent("Desktop/forge/build/forge"))39        urls.append(URL(fileURLWithPath: "/usr/local/bin/forge"))40        return urls.filter { FileManager.default.isExecutableFile(atPath: $0.path) }41    }4243    /// Runs `forge info` and parses the device line.44    static func validate(binary: URL) async -> Result<ForgeValidation, Error> {45        let runner = ProcessRunner()46        do {47            let (lines, exit) = try await runner.launch(48                executable: binary, arguments: ["info"],49                currentDirectory: binary.deletingLastPathComponent())50            var device = ""51            for await (line, _) in lines where line.hasPrefix("device: ") {52                device = String(line.dropFirst("device: ".count))53            }54            let status = await exit.value55            guard status.code == 0, !device.isEmpty else {56                throw NSError(domain: "ForgeStudio", code: 1, userInfo: [57                    NSLocalizedDescriptionKey:58                        "forge info a échoué (code \(status.code)). Vérifiez que forge.metallib est à côté du binaire.",59                ])60            }61            return .success(ForgeValidation(version: device, binaryPath: binary.path))62        } catch {63            return .failure(error)64        }65    }66}67