Larry Hao(郝卓远)Add Codex JSONL shape benchmark (#1017)dd2b3cc5 months agoHistory
289 lines · 9.9 KB · Swift
RawDownload
#!/usr/bin/env swift import Foundation struct SurveyOptions {    var root: URL = FileManager.default.homeDirectoryForCurrentUser        .appendingPathComponent(".codex", isDirectory: true)        .appendingPathComponent("sessions", isDirectory: true)    var days: Double = 30} struct Survey {    var files = 0    var totalBytes: Int64 = 0    var lines = 0    var relevantLines = 0    var lineLengths: [Int] = []    var linesOver32KiB = 0    var linesOver256KiB = 0    var turnContextLines = 0    var turnContextOver32KiB = 0    var turnContextOver256KiB = 0    var turnContextModelOffsets: [Int] = []    var turnContextModelOffsetUnder32KiB = 0    var turnContextModelOffsetUnder256KiB = 0    var tokenCountLines = 0    var tokenCountMissingExplicitModel = 0     mutating func recordFile(byteCount: Int64) {        self.files += 1        self.totalBytes += byteCount    }     mutating func recordLine(_ line: Data) {        guard !line.isEmpty else { return }         let length = line.count        self.lines += 1        self.lineLengths.append(length)        if length > 32 * 1024 {            self.linesOver32KiB += 1        }        if length > 256 * 1024 {            self.linesOver256KiB += 1        }         let isRelevant = line.contains(Marker.eventMessage)            || line.contains(Marker.turnContext)            || line.contains(Marker.sessionMetadata)        if isRelevant {            self.relevantLines += 1        }         if line.contains(Marker.turnContext) {            self.turnContextLines += 1            if length > 32 * 1024 {                self.turnContextOver32KiB += 1            }            if length > 256 * 1024 {                self.turnContextOver256KiB += 1            }            if let offset = line.firstOffset(of: Marker.modelField)                ?? line.firstOffset(of: Marker.modelNameField)            {                self.turnContextModelOffsets.append(offset)                if offset < 32 * 1024 {                    self.turnContextModelOffsetUnder32KiB += 1                }                if offset < 256 * 1024 {                    self.turnContextModelOffsetUnder256KiB += 1                }            }        }         if line.contains(Marker.tokenCount) {            self.tokenCountLines += 1            if !line.contains(Marker.modelField), !line.contains(Marker.modelNameField) {                self.tokenCountMissingExplicitModel += 1            }        }    }} enum Marker {    static let eventMessage = Data(#""type":"event_msg""#.utf8)    static let turnContext = Data(#""type":"turn_context""#.utf8)    static let sessionMetadata = Data(#""type":"session_meta""#.utf8)    static let tokenCount = Data(#""token_count""#.utf8)    static let modelField = Data(#""model""#.utf8)    static let modelNameField = Data(#""model_name""#.utf8)} extension Data {    func contains(_ marker: Data) -> Bool {        self.range(of: marker) != nil    }     func firstOffset(of marker: Data) -> Int? {        guard let range = self.range(of: marker) else { return nil }        return self.distance(from: self.startIndex, to: range.lowerBound)    }} func parseOptions(arguments: [String]) throws -> SurveyOptions {    var options = SurveyOptions()    var index = 1    while index < arguments.count {        switch arguments[index] {        case "--root":            index += 1            guard index < arguments.count else {                throw UsageError.message("--root requires a path")            }            options.root = URL(fileURLWithPath: expandTilde(arguments[index]), isDirectory: true)        case "--days":            index += 1            guard index < arguments.count, let days = Double(arguments[index]) else {                throw UsageError.message("--days requires a number")            }            options.days = days        case "--help", "-h":            printUsage()            Foundation.exit(0)        default:            throw UsageError.message("unknown argument: \(arguments[index])")        }        index += 1    }    return options} func expandTilde(_ path: String) -> String {    guard path == "~" || path.hasPrefix("~/") else { return path }    let home = FileManager.default.homeDirectoryForCurrentUser.path    if path == "~" {        return home    }    return home + String(path.dropFirst())} enum UsageError: Error, CustomStringConvertible {    case message(String)     var description: String {        switch self {        case let .message(text):            text        }    }} func printUsage() {    print(        """        Usage: Scripts/cost_jsonl_shape_survey.swift [--root PATH] [--days N]         Scans local Codex JSONL logs and prints aggregate shape only. It does not        print prompts, tool payloads, model values, file paths, or raw log lines.        """)} func jsonlFiles(root: URL, modifiedSince cutoff: Date) -> [URL] {    guard let enumerator = FileManager.default.enumerator(        at: root,        includingPropertiesForKeys: [.isRegularFileKey, .contentModificationDateKey, .fileSizeKey],        options: [.skipsHiddenFiles]) else { return [] }     var files: [URL] = []    for case let fileURL as URL in enumerator {        guard fileURL.pathExtension == "jsonl" else { continue }        let values = try? fileURL.resourceValues(forKeys: [.isRegularFileKey, .contentModificationDateKey])        guard values?.isRegularFile == true else { continue }        guard let modifiedAt = values?.contentModificationDate, modifiedAt >= cutoff else { continue }        files.append(fileURL)    }    return files.sorted { $0.path < $1.path }} func validateRoot(_ root: URL) throws {    var isDirectory: ObjCBool = false    let exists = FileManager.default.fileExists(atPath: root.path, isDirectory: &isDirectory)    guard exists, isDirectory.boolValue else {        throw UsageError.message("root does not exist or is not a directory")    }} func scan(fileURL: URL, into survey: inout Survey) throws {    let values = try fileURL.resourceValues(forKeys: [.fileSizeKey])    survey.recordFile(byteCount: Int64(values.fileSize ?? 0))     let handle = try FileHandle(forReadingFrom: fileURL)    defer { try? handle.close() }     var current = Data()    current.reserveCapacity(4 * 1024)     while true {        let chunk = try handle.read(upToCount: 256 * 1024) ?? Data()        if chunk.isEmpty {            survey.recordLine(current)            break        }         var segmentStart = chunk.startIndex        while let newline = chunk[segmentStart...].firstIndex(of: 0x0A) {            current.append(contentsOf: chunk[segmentStart..<newline])            survey.recordLine(current)            current.removeAll(keepingCapacity: true)            segmentStart = chunk.index(after: newline)        }         if segmentStart < chunk.endIndex {            current.append(contentsOf: chunk[segmentStart..<chunk.endIndex])        }    }} func percentile(_ values: [Int], _ percentile: Double) -> Int {    guard !values.isEmpty else { return 0 }    let sorted = values.sorted()    let index = Int((percentile * Double(sorted.count - 1)).rounded())    return sorted[max(0, min(index, sorted.count - 1))]} func printSummary(_ survey: Survey, options: SurveyOptions) {    print("root: \(redactedRootDescription(options.root))")    print("window days: \(Int(options.days))")    print("files: \(survey.files)")    print("total bytes: \(survey.totalBytes)")    print("lines: \(survey.lines)")    print("relevant Codex scanner lines: \(survey.relevantLines)")    print(        "line length p50/p90/p95/p99/max: " +            "\(percentile(survey.lineLengths, 0.50)) / " +            "\(percentile(survey.lineLengths, 0.90)) / " +            "\(percentile(survey.lineLengths, 0.95)) / " +            "\(percentile(survey.lineLengths, 0.99)) / " +            "\(percentile(survey.lineLengths, 1.00)) bytes")    print("lines > 32 KiB: \(survey.linesOver32KiB)")    print("lines > 256 KiB: \(survey.linesOver256KiB)")    print("turn_context lines: \(survey.turnContextLines)")    print("turn_context lines > 32 KiB: \(survey.turnContextOver32KiB)")    print("turn_context lines > 256 KiB: \(survey.turnContextOver256KiB)")    print(        "turn_context model offset p50/p95/max: " +            "\(percentile(survey.turnContextModelOffsets, 0.50)) / " +            "\(percentile(survey.turnContextModelOffsets, 0.95)) / " +            "\(percentile(survey.turnContextModelOffsets, 1.00)) bytes")    print(        "turn_context model offset < 32 KiB: " +            "\(survey.turnContextModelOffsetUnder32KiB) / \(survey.turnContextLines)")    print(        "turn_context model offset < 256 KiB: " +            "\(survey.turnContextModelOffsetUnder256KiB) / \(survey.turnContextLines)")    print(        "token_count rows missing an explicit model: " +            "\(survey.tokenCountMissingExplicitModel) / \(survey.tokenCountLines)")} func redactedRootDescription(_ root: URL) -> String {    let defaultRoot = SurveyOptions().root.standardizedFileURL.path    let currentRoot = root.standardizedFileURL.path    if currentRoot == defaultRoot {        return "default Codex sessions"    }    return "custom root (redacted)"} do {    let options = try parseOptions(arguments: CommandLine.arguments)    try validateRoot(options.root)    let cutoff = Date().addingTimeInterval(-options.days * 24 * 60 * 60)    let files = jsonlFiles(root: options.root, modifiedSince: cutoff)    guard !files.isEmpty else {        throw UsageError.message("no .jsonl files found in the selected time window")    }    var survey = Survey()    for fileURL in files {        try scan(fileURL: fileURL, into: &survey)    }    printSummary(survey, options: options)} catch let error as UsageError {    fputs("error: \(error.description)\n\n", stderr)    printUsage()    Foundation.exit(2)} catch {    fputs("error: \(error.localizedDescription)\n", stderr)    Foundation.exit(1)}