Skip to content

Commit

Permalink
feat: new app path + realtime url support (#11)
Browse files Browse the repository at this point in the history
* feat: new app path + realtime url support

* fix: legacy path support
  • Loading branch information
drochetti authored Feb 21, 2024
1 parent 172f1fb commit 98b078b
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 15 deletions.
35 changes: 31 additions & 4 deletions Sources/FalClient/Client.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public protocol Client {

func run(_ id: String, input: Payload?, options: RunOptions) async throws -> Payload

@available(*, deprecated, message: "Pass the path as part of the app identifier instead")
func subscribe(
to app: String,
path: String?,
Expand All @@ -50,6 +51,15 @@ public protocol Client {
includeLogs: Bool,
onQueueUpdate: OnQueueUpdate?
) async throws -> Payload

func subscribe(
to app: String,
input: Payload?,
pollInterval: DispatchTimeInterval,
timeout: DispatchTimeInterval,
includeLogs: Bool,
onQueueUpdate: OnQueueUpdate?
) async throws -> Payload
}

public extension Client {
Expand All @@ -68,8 +78,8 @@ public extension Client {
try await run(app, input: input, options: options)
}

/// Submits a request to the given [app], an optional [path]. This method
/// uses the [queue] API to submit the request and poll for the result.
/// Submits a request to the given [app]. This method uses the [queue] API
/// to submit the request and poll for the result.
///
/// This is useful for long running requests, and it's the preffered way
/// to interact with the model APIs.
Expand All @@ -83,19 +93,36 @@ public extension Client {
/// - onQueueUpdate: A callback to be called when the queue status is updated.
func subscribe(
to app: String,
path: String? = nil,
input: Payload? = nil,
pollInterval: DispatchTimeInterval = .seconds(1),
timeout: DispatchTimeInterval = .minutes(3),
includeLogs: Bool = false,
onQueueUpdate: OnQueueUpdate? = nil
) async throws -> Payload {
try await subscribe(to: app,
path: path,
input: input,
pollInterval: pollInterval,
timeout: timeout,
includeLogs: includeLogs,
onQueueUpdate: onQueueUpdate)
}

@available(*, deprecated, message: "Pass the path as part of the app identifier instead")
func subscribe(
to app: String,
path: String? = nil,
input: Payload? = nil,
pollInterval: DispatchTimeInterval = .seconds(1),
timeout: DispatchTimeInterval = .minutes(3),
includeLogs: Bool = false,
onQueueUpdate: OnQueueUpdate? = nil
) async throws -> Payload {
let appId = path.map { "\(app)\($0)" } ?? app
return try await subscribe(to: appId,
input: input,
pollInterval: pollInterval,
timeout: timeout,
includeLogs: includeLogs,
onQueueUpdate: onQueueUpdate)
}
}
22 changes: 19 additions & 3 deletions Sources/FalClient/Realtime.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ func hasBinaryField(_ type: Encodable) -> Bool {
if child.value is Data {
return true
}
if case FalImageContent.raw = child.value {
return true
}
}
return false
}
Expand Down Expand Up @@ -112,17 +115,30 @@ public class RealtimeConnection: BaseRealtimeConnection<Payload> {}
/// Connection implementation that can be used to send messages using a custom `Encodable` type.
public class TypedRealtimeConnection<Input: Encodable>: BaseRealtimeConnection<Input> {}

/// This is a list of apps deployed before formal realtime support. Their URLs follow
/// a different pattern and will be kept here until we fully sunset them.
let LegacyApps = [
"lcm-sd15-i2i",
"lcm",
"sdxl-turbo-realtime",
"sd-turbo-real-time-high-fps-msgpack-a10g",
"lcm-plexed-sd15-i2i",
"sd-turbo-real-time-high-fps-msgpack",
]

func buildRealtimeUrl(forApp app: String, token: String? = nil) -> URL {
guard var components = URLComponents(string: buildUrl(fromId: app, path: "/ws")) else {
// Some basic support for old ids, this should be removed during 1.0.0 release
// For full-support of old ids, users can point to version 0.4.x
let appAlias = (try? appAlias(fromId: app)) ?? app
let path = LegacyApps.contains(appAlias) || !app.contains("/") ? "/ws" : "/realtime"
guard var components = URLComponents(string: buildUrl(fromId: app, path: path)) else {
preconditionFailure("Invalid URL. This is unexpected and likely a problem in the client library.")
}
components.scheme = "wss"

if let token {
components.queryItems = [URLQueryItem(name: "fal_jwt_token", value: token)]
}

print(components.url!)
// swiftlint:disable:next force_unwrapping
return components.url!
}
Expand Down
4 changes: 2 additions & 2 deletions Sources/FalClient/Utility.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ func buildUrl(fromId id: String, path: String? = nil, subdomain: String? = nil)

func ensureAppIdFormat(_ id: String) throws -> String {
let parts = id.split(separator: "/")
if parts.count == 2 {
if parts.count > 1 {
return id
}
let regex = try NSRegularExpression(pattern: "^([0-9]+)-([a-zA-Z0-9-]+)$")
Expand All @@ -26,7 +26,7 @@ func ensureAppIdFormat(_ id: String) throws -> String {

func appAlias(fromId id: String) throws -> String {
let appId = try ensureAppIdFormat(id)
guard let alias = appId.split(separator: "/").last else {
guard let alias = appId.split(separator: "/").dropFirst().first else {
throw FalError.invalidUrl(url: id)
}
return String(describing: alias)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import FalClient
import SwiftUI

// See https://www.fal.ai/models/latent-consistency-sd/api for API documentation

let OptimizedLatentConsistency = "fal-ai/lcm-sd15-i2i"

struct LcmInput: Encodable {
let prompt: String
let image: FalImageContent
Expand All @@ -28,12 +24,13 @@ class LiveImage: ObservableObject {

// This example demonstrates the support to Codable types, but
// RealtimeConnection can also be used for untyped input / output
// using dictionary-like ObjectValue
// using dictionary-like Payload
private var connection: TypedRealtimeConnection<LcmInput>?

init() {
connection = try? fal.realtime.connect(
to: OptimizedLatentConsistency,
// See https://fal.ai/models/latent-consistency-sd/api
to: "fal-ai/lcm-sd15-i2i",
connectionKey: "PencilKitDemo",
throttleInterval: .milliseconds(128)
) { (result: Result<LcmResponse, Error>) in
Expand Down

0 comments on commit 98b078b

Please sign in to comment.