Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ let package = Package(
.executableTarget(name: "ImageSortDemo", dependencies: ["ImageSort"], exclude: ["README.md"]),
.executableTarget(name: "KevCheck", dependencies: ["FluidUse"]),
.executableTarget(name: "ClefVisionCheck", dependencies: ["FluidUse"]),
.executableTarget(name: "ClefFlashDemo", dependencies: ["FluidUse"], exclude: ["README.md", "demo.sh"]),
.executableTarget(name: "InternDecisionCheck", dependencies: ["FluidUse"]),
.executableTarget(name: "Decision2Check", dependencies: ["FluidUse"]),
.executableTarget(
Expand Down
28 changes: 28 additions & 0 deletions Sources/ClefFlashDemo/ClefFlashDemoApp.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import AppKit
import SwiftUI

@main
struct ClefFlashDemoApp: App {
@StateObject private var model = TriageModel()

init() {
setvbuf(stdout, nil, _IOLBF, 0)
// Bare SwiftPM executables start as background processes; make this one a regular windowed app.
for key in UserDefaults.standard.dictionaryRepresentation().keys where key.hasPrefix("NSWindow Frame") {
UserDefaults.standard.removeObject(forKey: key) // open at the default size, not a remembered one
}
NSApplication.shared.setActivationPolicy(.regular)
NSApplication.shared.activate(ignoringOtherApps: true)
}

var body: some Scene {
WindowGroup("clef-flash 9B — on this Mac") {
ContentView()
.environmentObject(model)
.frame(minWidth: 1000, minHeight: 640)
.task { await model.start() }
}
.defaultSize(width: 1280, height: 820)
.windowResizability(.contentMinSize)
}
}
187 changes: 187 additions & 0 deletions Sources/ClefFlashDemo/ContentView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
import SwiftUI

struct ContentView: View {
@EnvironmentObject var model: TriageModel

var body: some View {
VStack(spacing: 0) {
header
Divider()
if model.ready {
board
Divider()
composer
} else {
Spacer()
ProgressView(model.status).controlSize(.large)
Spacer()
}
}
.background(Color(nsColor: .windowBackgroundColor))
}

private var header: some View {
HStack(alignment: .firstTextBaseline, spacing: 18) {
VStack(alignment: .leading, spacing: 4) {
Text("Cloudflare clef-flash · 9B decision model")
.font(.system(size: 26, weight: .bold))
Text("Running on this Mac · Core ML on the GPU · no network")
.font(.system(size: 15)).foregroundStyle(.secondary)
}
Spacer()
if let median = model.medianMilliseconds {
VStack(alignment: .trailing, spacing: 0) {
Text(String(format: "%.2f s", median / 1000))
.font(.system(size: 44, weight: .heavy, design: .rounded)).monospacedDigit()
Text("per ticket · 3 decisions in one pass")
.font(.system(size: 13)).foregroundStyle(.secondary)
}
}
}
.padding(.horizontal, 28).padding(.vertical, 18)
}

private var board: some View {
HStack(alignment: .top, spacing: 16) {
IncomingLane()
.frame(width: 340)
DecidedFeed()
.frame(maxWidth: .infinity)
}
.padding(18)
}

private var composer: some View {
HStack(spacing: 12) {
TextField("Type your own ticket and press Return…", text: $model.draft)
.textFieldStyle(.roundedBorder).font(.system(size: 15))
.onSubmit { model.submitDraft() }
Button(model.running ? "Pause stream" : "Resume stream") { model.toggleStream() }
Button("Restart") { model.restart() }
if model.busy { ProgressView().controlSize(.small) }
}
.padding(.horizontal, 28).padding(.vertical, 14)
}
}

struct IncomingLane: View {
@EnvironmentObject var model: TriageModel

var body: some View {
VStack(alignment: .leading, spacing: 10) {
HStack {
Image(systemName: "tray.and.arrow.down.fill").foregroundStyle(.blue)
Text("Incoming").font(.system(size: 17, weight: .semibold))
Spacer()
Text("\(model.triagedCount) triaged").font(.system(size: 13)).foregroundStyle(.secondary)
.monospacedDigit()
}
if let current = model.current {
VStack(alignment: .leading, spacing: 8) {
HStack(spacing: 6) {
ProgressView().controlSize(.small)
Text("clef-flash is reading…").font(.system(size: 12, weight: .semibold)).foregroundStyle(.blue)
}
Text(current.text).font(.system(size: 14)).fixedSize(horizontal: false, vertical: true)
}
.padding(12)
.frame(maxWidth: .infinity, alignment: .leading)
.background(RoundedRectangle(cornerRadius: 10).fill(Color.blue.opacity(0.12)))
.overlay(RoundedRectangle(cornerRadius: 10).stroke(Color.blue, lineWidth: 2))
.id(current.id)
}
ForEach(model.incoming) { ticket in
Text(ticket.text).font(.system(size: 13)).foregroundStyle(.secondary).lineLimit(2)
.padding(10)
.frame(maxWidth: .infinity, alignment: .leading)
.background(RoundedRectangle(cornerRadius: 10).fill(Color(nsColor: .controlBackgroundColor)))
.overlay(
RoundedRectangle(cornerRadius: 10).stroke(
ticket.typed ? Color.accentColor : .clear, lineWidth: 2)
)
.transition(.move(edge: .bottom).combined(with: .opacity))
}
Spacer(minLength: 0)
}
.padding(14)
.frame(maxHeight: .infinity, alignment: .top)
.background(RoundedRectangle(cornerRadius: 14).fill(Color.blue.opacity(0.05)))
.clipped()
}
}

struct DecidedFeed: View {
@EnvironmentObject var model: TriageModel

var body: some View {
VStack(alignment: .leading, spacing: 12) {
HStack(spacing: 10) {
Image(systemName: "checkmark.circle.fill").foregroundStyle(.green)
Text("Decided").font(.system(size: 17, weight: .semibold))
Spacer()
ForEach(TriageModel.Team.allCases) { team in
HStack(spacing: 5) {
Circle().fill(team.color).frame(width: 8, height: 8)
Text(team.title).font(.system(size: 13))
Text("\(model.count(for: team))").font(.system(size: 13, weight: .semibold)).monospacedDigit()
}
.padding(.horizontal, 9).padding(.vertical, 4)
.background(Capsule().fill(team.color.opacity(0.12)))
}
}
ScrollView {
LazyVStack(spacing: 8) {
ForEach(model.tickets.reversed()) { ticket in
TicketRow(ticket: ticket)
.transition(.move(edge: .top).combined(with: .opacity))
}
}
}
}
.padding(14)
.frame(maxHeight: .infinity, alignment: .top)
.background(RoundedRectangle(cornerRadius: 14).fill(Color.green.opacity(0.04)))
}
}

struct TicketRow: View {
let ticket: TriageModel.Ticket

private var urgencyColor: Color { [Color.green, .blue, .orange, .red][ticket.urgency] }

var body: some View {
HStack(spacing: 12) {
Text(ticket.team.title)
.font(.system(size: 13, weight: .semibold)).lineLimit(1)
.frame(width: 104)
.padding(.vertical, 6)
.background(RoundedRectangle(cornerRadius: 7).fill(ticket.team.color.opacity(0.2)))
.foregroundStyle(ticket.team.color)
Text(ticket.text).font(.system(size: 14)).lineLimit(2)
.frame(maxWidth: .infinity, alignment: .leading)
Pill(text: TriageModel.urgencyLabels[ticket.urgency], color: urgencyColor)
.frame(width: 70, alignment: .leading)
Pill(text: "Refund", color: .pink).opacity(ticket.refund > 0.5 ? 1 : 0)
Text(String(format: "%.0f%%", ticket.teamConfidence * 100))
.font(.system(size: 12)).foregroundStyle(.secondary).monospacedDigit().frame(
width: 36, alignment: .trailing)
Text(String(format: "%.0f ms", ticket.milliseconds))
.font(.system(size: 13, weight: .semibold)).monospacedDigit().frame(width: 60, alignment: .trailing)
}
.padding(.horizontal, 12).padding(.vertical, 9)
.background(RoundedRectangle(cornerRadius: 10).fill(Color(nsColor: .controlBackgroundColor)))
.overlay(RoundedRectangle(cornerRadius: 10).stroke(ticket.typed ? Color.accentColor : .clear, lineWidth: 2))
}
}

struct Pill: View {
let text: String
let color: Color

var body: some View {
Text(text).font(.system(size: 11, weight: .semibold)).lineLimit(1).fixedSize()
.padding(.horizontal, 7).padding(.vertical, 3)
.background(Capsule().fill(color.opacity(0.18)))
.foregroundStyle(color)
}
}
18 changes: 18 additions & 0 deletions Sources/ClefFlashDemo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# ClefFlashDemo

Cloudflare [clef-flash](https://huggingface.co/Cloudflare/clef-flash) (Qwen3.5-9B decision model, Apache-2.0) triaging
support tickets on this Mac through Core ML. Each ticket gets three typed decisions in one forward pass: team (choice),
urgency (score), refund (noul). Tickets are fictional mock data; typed tickets jump the queue.

```bash
Sources/ClefFlashDemo/demo.sh # downloads FluidInference/clef-flash-coreml (~11 GB) on first run
Sources/ClefFlashDemo/demo.sh /path/to/bundle # or a local bundle (CLEF_FLASH_BUNDLE)
```

- Runtime: `ClefFlashManager` — 8 decoder packages (4 layers each, 8-bit weights, 6.5 GB) chained on the GPU + the
joint schema head (fp32), 512-token bucket. ~0.50 s per ticket on an M5 Pro; ~40 s to load (first launch also
compiles the packages into `<bundle>/compiled/`).
- Bundle: [FluidInference/clef-flash-coreml](https://huggingface.co/FluidInference/clef-flash-coreml), pinned in
`ClefFlashModelStore` with per-file SHA-256.
- GPU only: the Qwen3.5 decoder puts 0 ops on the Neural Engine (every op falls back to CPU under `CPU_AND_NE`).
- Same answers as the Python Core ML pipeline on the mock tickets; 8-bit vs fp32 reference: 1 flip / 611 ARC questions.
Loading
Loading