Skip to content

Commit

Permalink
feat: realtime binary protocol (#3)
Browse files Browse the repository at this point in the history
* feat: initial high fps sample

* fix: remove prints

* feat: realtime turbo camera demo

* fix: fps count

* chore: docs and build update

* fix: remove non-exported targets

* feat: binary realtime protocol with msgpack

* fix: update macos support to v13

* chore: rename ObjectValue to Payload
  • Loading branch information
drochetti authored Dec 12, 2023
1 parent b05b059 commit 24f5310
Show file tree
Hide file tree
Showing 36 changed files with 1,583 additions and 232 deletions.
4 changes: 4 additions & 0 deletions .spi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
version: 1
builder:
configs:
- documentation_targets: [FalClient]

This file was deleted.

9 changes: 9 additions & 0 deletions Package.resolved
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
{
"pins" : [
{
"identity" : "msgpack-swift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/fumoboy007/msgpack-swift.git",
"state" : {
"revision" : "1e3124367973f45955f27f49a617862605e55288",
"version" : "2.0.0"
}
},
{
"identity" : "swiftformat",
"kind" : "remoteSourceControl",
Expand Down
32 changes: 15 additions & 17 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import PackageDescription
let package = Package(
name: "FalClient",
platforms: [
.iOS(.v15),
.macOS(.v12),
.macCatalyst(.v13),
.tvOS(.v13),
.watchOS(.v8),
.iOS(.v16),
.macOS(.v13),
.macCatalyst(.v16),
.tvOS(.v16),
.watchOS(.v9),
],
products: [
// Products define the executables and libraries a package produces, making them visible to other packages.
Expand All @@ -21,24 +21,22 @@ let package = Package(
],
dependencies: [
.package(url: "https://github.com/nicklockwood/SwiftFormat", from: "0.52.10"),
.package(url: "https://github.com/fumoboy007/msgpack-swift.git", from: "2.0.0")
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
// Targets can depend on other targets in this package and products from dependencies.
.target(name: "FalClient"),
.testTarget(
name: "FalClientTests",
dependencies: ["FalClient"]
),
.target(
name: "FalSampleApp",
dependencies: ["FalClient"],
path: "Sources/Samples/FalSampleApp"
name: "FalClient",
dependencies: [
.product(name: "DMMessagePack", package: "msgpack-swift")
],
path: "Sources/FalClient"
),
.target(
name: "FalRealtimeSampleApp",
.testTarget(
name: "FalClientTests",
dependencies: ["FalClient"],
path: "Sources/Samples/FalRealtimeSampleApp"
),
path: "Tests/FalClientTests"
)
]
)
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
## The fal.ai Swift Client

![FalClient Swift package](https://img.shields.io/badge/swift-package-brightgreen)
![Build](https://img.shields.io/badge/build-passing-brightgreen)
![License](https://img.shields.io/badge/license-MIT-blue)
[![Swift](https://img.shields.io/endpoint?style=flat-square&url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Ffal-ai%2Fserverless-client-swift%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/fal-ai/serverless-client-swift)
[![](https://img.shields.io/endpoint?style=flat-square&url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Ffal-ai%2Fserverless-client-swift%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/fal-ai/serverless-client-swift)
![Build](https://img.shields.io/github/actions/workflow/status/fal-ai/serverless-client-swift/build.yml?style=flat-square)
![License](https://img.shields.io/github/license/fal-ai/serverless-client-swift?style=flat-square)

## About the Project

Expand Down
2 changes: 1 addition & 1 deletion Sources/FalClient/Client+Request.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ extension Client {
throw FalError.invalidUrl(url: urlString)
}

if let queryParams = queryParams,
if let queryParams,
var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false)
{
urlComponents.queryItems = queryParams.map {
Expand Down
50 changes: 37 additions & 13 deletions Sources/FalClient/Client.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,36 +37,60 @@ public protocol Client {

var realtime: Realtime { get }

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

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

public extension Client {
func run(_ app: String, input: [String: Any]? = nil, options: RunOptions = DefaultRunOptions) async throws -> [String: Any] {
return try await run(app, input: input, options: options)
/// Sends a request to the given [id], an optional [path]. This method
/// is a direct request to the model API and it waits for the processing
/// to complete before returning the result.
///
/// This is useful for short running requests, but it's not recommended for
/// long running requests, for those see [submit].
///
/// - Parameters:
/// - app: The id of the model app.
/// - input: The input to the model.
/// - options: The request options.
func run(_ app: String, input: Payload? = nil, options: RunOptions = DefaultRunOptions) async throws -> Payload {
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.
///
/// This is useful for long running requests, and it's the preffered way
/// to interact with the model APIs.
///
/// - Parameters:
/// - app: The id of the model app.
/// - input: The input to the model.
/// - pollInterval: The interval to poll for the result. Defaults to 1 second.
/// - timeout: The timeout to wait for the result. Defaults to 3 minutes.
/// - includeLogs: Whether to include logs in the result. Defaults to false.
/// - onQueueUpdate: A callback to be called when the queue status is updated.
func subscribe(
to app: String,
input: [String: Any]? = nil,
input: Payload? = nil,
pollInterval: DispatchTimeInterval = .seconds(1),
timeout: DispatchTimeInterval = .minutes(3),
includeLogs: Bool = false,
onQueueUpdate: OnQueueUpdate? = nil
) async throws -> [String: Any] {
return try await subscribe(to: app,
input: input,
pollInterval: pollInterval,
timeout: timeout,
includeLogs: includeLogs,
onQueueUpdate: onQueueUpdate)
) async throws -> Payload {
try await subscribe(to: app,
input: input,
pollInterval: pollInterval,
timeout: timeout,
includeLogs: includeLogs,
onQueueUpdate: onQueueUpdate)
}
}
20 changes: 9 additions & 11 deletions Sources/FalClient/FalClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,25 +31,23 @@ public struct FalClient: Client {

public var realtime: Realtime { RealtimeClient(client: self) }

public func run(_ app: String, input: [String: Any]?, options: RunOptions) async throws -> [String: Any] {
let inputData = input != nil ? try JSONSerialization.data(withJSONObject: input as Any) : nil
public func run(_ app: String, input: Payload?, options: RunOptions) async throws -> Payload {
let inputData = input != nil ? try JSONEncoder().encode(input) : nil
let queryParams = options.httpMethod == .get ? input : nil
let url = buildUrl(fromId: app, path: options.path)
let data = try await sendRequest(url, input: inputData, queryParams: queryParams, options: options)
guard let result = try JSONSerialization.jsonObject(with: data) as? [String: Any] else {
throw FalError.invalidResultFormat
}
return result
let data = try await sendRequest(url, input: inputData, queryParams: queryParams?.asDictionary, options: options)
let decoder = JSONDecoder()
return try decoder.decode(Payload.self, from: data)
}

public func subscribe(
to app: String,
input: [String: Any]?,
input: Payload?,
pollInterval: DispatchTimeInterval,
timeout: DispatchTimeInterval,
includeLogs: Bool,
onQueueUpdate: OnQueueUpdate?
) async throws -> [String: Any] {
) async throws -> Payload {
let requestId = try await queue.submit(app, input: input)
let start = Int(Date().timeIntervalSince1970 * 1000)
var elapsed = 0
Expand All @@ -75,10 +73,10 @@ public struct FalClient: Client {

public extension FalClient {
static func withProxy(_ url: String) -> Client {
return FalClient(config: ClientConfig(requestProxy: url))
FalClient(config: ClientConfig(requestProxy: url))
}

static func withCredentials(_ credentials: ClientCredentials) -> Client {
return FalClient(config: ClientConfig(credentials: credentials))
FalClient(config: ClientConfig(credentials: credentials))
}
}
Loading

0 comments on commit 24f5310

Please sign in to comment.