Giter VIP home page Giter VIP logo

fal-swift's Introduction

The fal.ai Swift Client

Swift Build License

About the Project

The FalClient is a robust and user-friendly Swift package designed for seamless integration of fal serverless functions into Swift projects. This library, developed in pure Swift, provides developers with simple APIs to interact with AI models, suitable for both iOS and macOS platforms.

Getting Started

The FalClient library serves as a client for fal serverless Python functions. Before using this library, ensure you've set up your serverless functions as per the quickstart guide.

Client Library

This Swift client library is crafted as a lightweight layer atop Swift's networking standards like URLSession. It ensures hassle-free integration into your existing Swift codebase. The library is designed to address the nuances of Swift and Apple's ecosystem, ensuring smooth operation across different Apple platforms.

Note: Make sure to review the fal-serverless getting started guide to acquire your credentials and register your functions.

  1. Add FalClient as a dependency in your Swift Package Manager.

  2. Set up the client instance:

    import FalClient
    let fal = FalClient.withCredentials(.keyPair("FAL_KEY_ID:FAL_KEY_SECRET"))
    
    // You can also use a proxy to protect your credentials
    // let fal = FalClient.withProxy("http://localhost:3333/api/fal/proxy")
  3. Use fal.subscribe to dispatch requests to the model API:

    let result = try await fal.subscribe(to: "text-to-image",
        input: [
            "prompt": "a cute shih-tzu puppy",
            "model_name": "stabilityai/stable-diffusion-xl-base-1.0",
            "image_size": "square_hd"
        ]) { update in
            print(update)
        }

Notes:

  • Replace text-to-image with a valid model id. Check fal.ai/models for all available models.
  • It fully relies on async/await for asynchronous programming.
  • The result type in Swift will be a [String: Any] and the entries depend on the API output schema.
  • The Swift client also supports typed inputs and outputs through Codable.

Real-time

The client supports real-time model APIs. Checkout the FalRealtimeSampleApp for more details.

let connection = try fal.realtime.connect(
    to: OptimizedLatentConsistency,
    connectionKey: "PencilKitDemo",
    throttleInterval: .milliseconds(128)
) { (result: Result<LcmResponse, Error>)  in
    if case let .success(data) = result,
        let image = data.images.first {
        let data = try? Data(contentsOf: URL(string: image.url)!)
        DispatchQueue.main.async {
            self.currentImage = data
        }
    }
}

try connection.send(LcmInput(
    prompt: prompt,
    imageUrl: "data:image/jpeg;base64,\(drawing.base64EncodedString())",
    seed: 6_252_023,
    syncMode: true
))

Sample apps

Check the Sources/Samples folder for a handful of sample applications using the FalClient.

Open them with xed to quickly start playing with

xed Sources/Samples/FalSampleApp

Roadmap

See the open feature requests for a list of proposed features and join the discussion.

Contributing

Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make to the Swift version of the client are greatly appreciated.

License

Distributed under the MIT License. See LICENSE for more information.

fal-swift's People

Contributors

drochetti avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

fal-swift's Issues

Memory leak due to recursion when using 'subscribe'

The subscribe method on the client extension is referencing itself instead of the instance method on FalClient which causes a recursion loop creating infinite number of mallocs. Removing the new method from the extension and only leaving the deprecated pointing to the new method solves the issue.

No user found for Key ID and Secret

I'm getting a:

httpError(status: 401, message: "No user found for Key ID and Secret", payload: Optional(FalClient.Payload.dict(["detail": FalClient.Payload.string("No user found for Key ID and Secret")])))

When trying to run the image generation sample app.

All I did was change how FalClient was being initialized, to use my key ID + secret as directly copied from the dashboard:

let fal = FalClient.withCredentials(.key(id: <my id>, secret: <my secret>))

feature request: include request id in error result

Using the realtime client, our onResult handler can currently use the request_id parameter to identify the request corresponding to an api call, if that call is successful. However, since the onResult callback is typed as @escaping (Result<Output, Error>) -> Void, we can't identify requests for api call failures in the same way.

Some example code demonstrating the issue:

connection = try! falClient.realtime.connect(
    to: OptimizedLatentConsistency,
    connectionKey: "ReproDemo",
    throttleInterval: .milliseconds(0), // no need to throttle since i'm calling generate in response to a button tap
    onResult: { (result: Result<LcmResponse, Error>) in
        switch result {
        case .success(let data):
            // Can do things with data.requestId here
            print(data.requestId!)
        case .failure(let error):
            // ISSUE: Can't tie this back to the originating request, since there's no way to access request_id
            print(error)
        }
    }
)

bug: "Socket is not connected" errors

Using the realtime client and 110602490-lcm-sd15-i2i app, my onResult callback is being called twice for each generation, once with a successful result and once with a "Socket is not connected error"

Repro steps:

  1. run the repro app (code below)
  2. draw something and tap "Generate"
  3. wait a moment

Expected result:

  1. The onResult callback is called once, with a successful result
  2. The console output looks like:
GENERATING: <SOME_UUID>
SUCCESS

Actual result:

  1. The onResult callback is called twice, once with a successful result and once with a "Socket is not connected" error
  2. The console output looks like:
GENERATING: <SOME_UUID>
nw_connection_copy_connected_local_endpoint_block_invoke [C2] Client called nw_connection_copy_connected_local_endpoint on unconnected nw_connection
nw_connection_copy_connected_remote_endpoint_block_invoke [C2] Client called nw_connection_copy_connected_remote_endpoint on unconnected nw_connection
nw_connection_copy_protocol_metadata_internal_block_invoke [C2] Client called nw_connection_copy_protocol_metadata_internal on unconnected nw_connection
SUCCESS
Connection 3: received failure notification
nw_read_request_report [C3] Receive failed with error "Socket is not connected"
RESULT ERROR Error Domain=NSPOSIXErrorDomain Code=57 "Socket is not connected" UserInfo={NSErrorFailingURLStringKey=https://110602490-lcm-sd15-i2i.gateway.alpha.fal.ai/ws?fal_jwt_token=<SOME_JWT_TOKEN>, NSErrorFailingURLKey=https://110602490-lcm-sd15-i2i.gateway.alpha.fal.ai/ws?fal_jwt_token=<SOME_JWT_TOKEN>}
nw_flow_service_reads [C3 130.211.36.43:443 failed parent-flow (satisfied (Path is satisfied), interface: en0[802.11], ipv4, dns, uses wifi)] No output handler
nw_flow_add_write_request [C3 130.211.36.43:443 failed parent-flow (satisfied (Path is satisfied), interface: en0[802.11], ipv4, dns, uses wifi)] cannot accept write requests
nw_write_request_report [C3] Send failed with error "Socket is not connected"
tcp_input [C3.1.1:3] flags=4 seq=404,865,952, ack=0, win=0 state=8 rcv_nxt=404,865,952, snd_una=102,004,591
tcp_input [C3.1.1:3] flags=4 seq=404,865,952, ack=0, win=0 state=0 rcv_nxt=404,865,952, snd_una=102,004,591
tcp_input [C3.1.1:3] flags=4 seq=404,865,952, ack=0, win=0 state=0 rcv_nxt=404,865,952, snd_una=102,004,591
tcp_input [C3.1.1:3] flags=4 seq=404,865,952, ack=0, win=0 state=0 rcv_nxt=404,865,952, snd_una=102,004,591
tcp_input [C3.1.1:3] flags=4 seq=404,865,952, ack=0, win=0 state=0 rcv_nxt=404,865,952, snd_una=102,004,591

Repro app code:

import SwiftUI
import FalClient
import Kingfisher

let falClient = FalClient.withCredentials(.keyPair("\(FAL_KEY_ID):\(FAL_KEY_SECRET)"))

let OptimizedLatentConsistency = "110602490-lcm-sd15-i2i"

struct LcmInput: Encodable {
    let requestId: String
    let prompt: String
    let imageUrl: String
    let seed: Int?
    let syncMode: Bool

    enum CodingKeys: String, CodingKey {
        case requestId = "request_id"
        case prompt
        case imageUrl = "image_url"
        case seed
        case syncMode = "sync_mode"
    }
}

struct LcmImage: Decodable {
    let contentType: String
    let url: String
    let width: CGFloat
    let height: CGFloat

    enum CodingKeys: String, CodingKey {
        case contentType = "content_type"
        case url
        case width
        case height
    }
}

struct LcmResponse: Decodable {
    let requestId: String?
    let seed: Float
    let nsfwContentDetected: [Bool]
    let numInferenceSteps: Int
    let images: [LcmImage]

    enum CodingKeys: String, CodingKey {
        case requestId = "request_id"
        case seed
        case nsfwContentDetected = "nsfw_content_detected"
        case numInferenceSteps = "num_inference_steps"
        case images
    }
}

class ViewModel: ObservableObject {
    @Published var currentImage: Data?
    var connection: RealtimeConnection<LcmInput>?

    init() {
        connection = try! falClient.realtime.connect(
            to: OptimizedLatentConsistency,
            connectionKey: "ReproDemo",
            throttleInterval: .milliseconds(0), // no need to throttle since i'm calling generate in response to a button tap
            onResult: { (result: Result<LcmResponse, Error>) in
                switch result {
                case .success(let data):
                    print("SUCCESS")
                    if let image = data.images.first {
                        let data = try? Data(contentsOf: URL(string: image.url)!)
                        DispatchQueue.main.async {
                            self.currentImage = data
                        }
                    }
                case .failure(let error):
                    print("RESULT ERROR \(error)")
                }
            }
        )
    }

    func generate(_ input: LcmInput) {
        guard let connection = connection else { fatalError("No connection") }
        do {
            try connection.send(input)
        } catch {
            print("SEND ERROR \(error)")
        }
    }
}

struct CanvasView: View {
    @State var strokes: [[CGPoint]] = []
    @State var currentStroke: [CGPoint] = []

    var body: some View {
        Canvas { context, size in
            for stroke in strokes {
                var path = Path()
                path.addSmoothLines(stroke)
                context.stroke(path, with: .color(.black), lineWidth: 5)
            }
            var path = Path()
            path.addSmoothLines(currentStroke)
            context.stroke(path, with: .color(.black), lineWidth: 5)
        }
        .background(Color.canvasBackground)
        .gesture(DragGesture(minimumDistance: 0, coordinateSpace: .local)
            .onChanged({ value in
                currentStroke.append(value.location)
            })
            .onEnded({ value in
                strokes.append(currentStroke)
                currentStroke = []
            })
        )
    }
}

struct ReproView: View {
    @StateObject var model = ViewModel()

    var canvas: some View {
        CanvasView()
            .frame(width: 300, height: 300)
    }

    var body: some View {
        VStack {
            canvas

            if let image = model.currentImage {
                KFImage.data(image, cacheKey: UUID().uuidString)
            }

            Button("Generate") {
                let renderer = ImageRenderer(content: canvas)
                let image = renderer.uiImage!
                let base64 = image.jpegData(compressionQuality: 0.6)!.base64EncodedString()
                let requestId = UUID().uuidString
                print("GENERATING: \(requestId)")
                model.generate(
                    LcmInput(
                        requestId: requestId,
                        prompt: "a moon in a starry night sky",
                        imageUrl: "data:image/jpeg;base64,\(base64)",
                        seed: 6_252_023,
                        syncMode: true
                    )
                )
            }
        }
        .padding()
    }
}

@main
struct ReproApp: SwiftUI.App {
    var body: some Scene {
        WindowGroup {
            ReproView()
        }
    }
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.