Skip to content

Commit 63d80b1

Browse files
simbasimba
authored andcommitted
fix(ux): add autonomous task-driven progress bar and restore MB counts
1 parent 266e6f1 commit 63d80b1

1 file changed

Lines changed: 69 additions & 51 deletions

File tree

Sources/mlx-server/Server.swift

Lines changed: 69 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,14 @@ import MLXVLM
2222
// ── CLI ──────────────────────────────────────────────────────────────────────
2323

2424
final class ProgressTracker {
25-
var lastUpdate: TimeInterval = 0
26-
var lastBytes: Int64 = 0
27-
var speedStr = "0.0 MB/s"
2825
var isDone = false
2926
var spinnerFrames = ["", "", "", "", "", "", "", "", "", ""]
3027
var frameIndex = 0
3128
let modelId: String
29+
private var trackingTask: Task<Void, Never>?
30+
private var lastUpdate: TimeInterval = 0
31+
private var lastBytes: Int64 = 0
32+
private var speedStr = "0.0 MB/s"
3233

3334
init(modelId: String) {
3435
self.modelId = modelId
@@ -44,7 +45,6 @@ final class ProgressTracker {
4445
var total: Int64 = 0
4546
if let enumerator = FileManager.default.enumerator(at: dir, includingPropertiesForKeys: [.fileSizeKey]) {
4647
for case let file as URL in enumerator {
47-
// Quick check to skip symlinks from inflating size
4848
if let attr = try? file.resourceValues(forKeys: [.fileSizeKey, .isSymbolicLinkKey]),
4949
let size = attr.fileSize,
5050
attr.isSymbolicLink != true {
@@ -59,56 +59,74 @@ final class ProgressTracker {
5959
}
6060

6161
func printProgress(_ progress: Progress) {
62-
if isDone { return }
63-
let now = Date().timeIntervalSince1970
64-
let fraction = progress.fractionCompleted
65-
66-
if lastUpdate == 0 {
67-
lastUpdate = now
62+
if trackingTask == nil {
63+
lastUpdate = Date().timeIntervalSince1970
6864
lastBytes = getDownloadedBytes()
69-
}
70-
let interval = now - lastUpdate
71-
72-
if interval > 0.5 {
73-
frameIndex = (frameIndex + 1) % spinnerFrames.count
7465

75-
let currentBytes = getDownloadedBytes()
76-
let diff = Double(currentBytes - lastBytes)
77-
if diff >= 0 {
78-
let speedMBps = (diff / interval) / 1_048_576.0
79-
speedStr = String(format: "%.1f MB/s", speedMBps)
66+
trackingTask = Task {
67+
while !self.isDone && !Task.isCancelled {
68+
let now = Date().timeIntervalSince1970
69+
let fraction = progress.fractionCompleted
70+
let pct = Int(fraction * 100)
71+
72+
let interval = now - self.lastUpdate
73+
if interval >= 0.25 {
74+
self.frameIndex = (self.frameIndex + 1) % self.spinnerFrames.count
75+
76+
let currentBytes = self.getDownloadedBytes()
77+
let diff = Double(currentBytes - self.lastBytes)
78+
if diff >= 0 {
79+
let speedMBps = (diff / interval) / 1_048_576.0
80+
self.speedStr = String(format: "%.1f MB/s", speedMBps)
81+
} else {
82+
// File moved/cleaned up cache, omit negative speed
83+
}
84+
85+
self.lastBytes = currentBytes
86+
self.lastUpdate = now
87+
}
88+
89+
var completedMB = String(format: "%.1f", Double(self.lastBytes) / 1_048_576)
90+
var totalMB = "???"
91+
if fraction > 0.001 {
92+
let extrapolated = (Double(self.lastBytes) / fraction) / 1_048_576.0
93+
totalMB = String(format: "%.1f", extrapolated)
94+
} else if fraction == 0.0 {
95+
completedMB = "0.0"
96+
}
97+
98+
let barLength = 20
99+
let completedBars = min(barLength, Int(fraction * Double(barLength)))
100+
let emptyBars = max(0, barLength - completedBars)
101+
102+
var bars = ""
103+
if completedBars > 0 {
104+
bars += String(repeating: "=", count: completedBars - 1) + ">"
105+
}
106+
bars += String(repeating: " ", count: emptyBars)
107+
108+
let pctStr = String(format: "%3d%%", pct)
109+
let spinner = self.spinnerFrames[self.frameIndex]
110+
let speedText = "| Speed: \(self.speedStr)"
111+
112+
let msg = String(format: "\r[mlx-server] Download: [%@] %@ %@ (%@ MB / %@ MB) %@", bars, pctStr, spinner, completedMB, totalMB, speedText)
113+
114+
print(msg.padding(toLength: 100, withPad: " ", startingAt: 0), terminator: "")
115+
fflush(stdout)
116+
117+
if fraction >= 1.0 {
118+
print("")
119+
self.isDone = true
120+
break
121+
}
122+
123+
do {
124+
try await Task.sleep(nanoseconds: 100_000_000) // 100ms
125+
} catch {
126+
break
127+
}
128+
}
80129
}
81-
82-
lastBytes = currentBytes
83-
lastUpdate = now
84-
}
85-
86-
let pct = Int(fraction * 100)
87-
88-
let barLength = 20
89-
let completedBars = min(barLength, Int(fraction * Double(barLength)))
90-
let emptyBars = max(0, barLength - completedBars)
91-
92-
var bars = ""
93-
if completedBars > 0 {
94-
bars += String(repeating: "=", count: completedBars - 1) + ">"
95-
} else {
96-
bars += ""
97-
}
98-
bars += String(repeating: " ", count: emptyBars)
99-
100-
let pctStr = String(format: "%3d%%", pct)
101-
let spinner = spinnerFrames[frameIndex]
102-
let speedText = "| Speed: \(speedStr)"
103-
104-
let msg = String(format: "\r[mlx-server] Download: [%@] %@ %@ %@", bars, pctStr, spinner, speedText)
105-
106-
print(msg.padding(toLength: 90, withPad: " ", startingAt: 0), terminator: "")
107-
fflush(stdout)
108-
109-
if fraction >= 1.0 {
110-
print("")
111-
isDone = true
112130
}
113131
}
114132
}

0 commit comments

Comments
 (0)