Swift withTaskGroup: four Sume video submits in flight

A sliding window in Swift 6: withTaskGroup keeps four POST /v1/videos requests in flight, re-arming as each finishes. One file, URLSession, no packages.

5 min readSume
All posts

Swift concurrency makes a bounded fan-out compact. Start a task group with four child tasks, and every time one finishes (group.next()) add the next shot. That sliding window keeps exactly four submits in flight with no semaphore.

The file below posts 12 five-second Wan 3.0 clips at 480p. It ran as a single-file script with swift fan.swift on Swift 6.3.2 against a local stand-in server that answers 202; it was not pointed at the live API. Set SUME_BASE and SUME_API_KEY in the environment first.

The code (23 lines)

Each task returns (shot, statusCode), so the sorted output at the end is in shot order even though tasks finish in any order. A transport error returns 0. The per-shot Idempotency-Key means a rerun replays any job Sume already accepted.

import Foundation

func submit(_ i: Int) async -> Int {
    var req = URLRequest(url: URL(string: ProcessInfo.processInfo.environment["SUME_BASE"]! + "/v1/videos")!)
    req.httpMethod = "POST"
    req.setValue(ProcessInfo.processInfo.environment["SUME_API_KEY"]!, forHTTPHeaderField: "x-api-key")
    req.setValue("application/json", forHTTPHeaderField: "content-type")
    req.setValue(String(format: "trailer-v3-shot-%02d", i), forHTTPHeaderField: "Idempotency-Key")
    req.httpBody = Data(#"{"model":"wan-3.0","prompt":"shot \#(i)","duration":5,"resolution":"480p"}"#.utf8)
    guard let (_, res) = try? await URLSession.shared.data(for: req) else { return 0 }
    return (res as? HTTPURLResponse)?.statusCode ?? 0
}

let codes = await withTaskGroup(of: (Int, Int).self) { group in
    var next = 0, out: [(Int, Int)] = []
    for _ in 0..<4 { group.addTask { [n = next] in (n, await submit(n)) }; next += 1 }  // 4 in flight
    while let done = await group.next() {
        out.append(done)
        if next < 12 { group.addTask { [n = next] in (n, await submit(n)) }; next += 1 }
    }
    return out.sorted { $0.0 < $1.0 }.map { $0.1 }
}
print(codes)

Script mode versus an app target

Top-level await is allowed here because swift fan.swift runs the file as a script. Inside an app or a package you would wrap the same body in an async function and call it from your entry point. Compiling the same file with swiftc -parse-as-library rejects the top-level statements (expressions are not allowed at the top level).

Choosing the window

Four is the processing concurrency of a Pro workspace. Sume queues anything it cannot start, so the hard cap on submits is the accepted capacity (24 on Pro), after which a paid submit returns 429 queue_full. Treat the window as pacing, and read the live generation_limits from a submit response if you want to adapt it.

For polling after the submits, the related Swift post covers next_poll_after_seconds.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume