LeapOpenAIClient / leap-openai-client (introduced in v0.10.0) is a small, dependency-light client for any OpenAI-compatible chat-completions endpoint β OpenAI itself, OpenRouter, vLLM, llama-server, or your own proxy. It ships in the same SDK release as LeapSDK, so you can route requests between an on-device LFM and a cloud model from a single app.
When to use it
- Hybrid on-device + cloud routing. Run small / fast models on-device with
LeapSDK, fall back to a larger cloud model for hard prompts. - Standardised cloud API. Talk to any OpenAI-compatible backend without pulling in a heavier OpenAI SDK.
- Streaming first. SSE streaming is the only mode β non-streaming requests arenβt exposed.
streamChatCompletion(...)forcesstream = trueon the outgoing request regardless of thestreamfield on theChatCompletionRequestyou pass in.
Add the dependency
- iOS / macOS (SPM)
- Android (Gradle)
- JVM (Gradle)
- Kotlin/Native (Gradle)
Add the In Swift sources,
LeapOpenAIClient product to your target. See the Quick Start for the full SPM setup.import LeapOpenAIClient. The Darwin (URLSession) Ktor engine is bundled β no extra HTTP setup needed.Basic usage
- Swift (iOS / macOS)
- Kotlin (all platforms)
New in v0.10.8. SKIE is now applied to
leap-sdk-openai-client, matching LeapSDK / LeapModelDownloader / LeapUI. Swift consumers get a real AsyncSequence, exhaustive onEnum(of:) switching, nested Kotlin class names (ChatCompletionEvent.Delta instead of the previously flattened ChatCompletionEventDelta), and an OpenAiClient(config:) convenience init β no more OpenAiClientKt. prefix. If you need the pre-SKIE manual-collector surface frozen for some reason, pin to 0.10.7; otherwise use the v0.10.8 surface below.onEnum(of:) gives exhaustive switching β the Swift compiler errors if a new ChatCompletionEvent case is added.Errors are delivered in-band: a non-2xx HTTP response arrives as a
.error event β handle it in the switch and show err.message. Malformed SSE chunks are logged and skipped (no event is emitted). Transport-level failures (network drop, TLS error) are not delivered to the bridged stream as .error events and are not rethrown to Swift, so a do/catch around the loop wonβt catch them β guard connectivity at a higher level (request timeouts, reachability checks) instead.Configuration
OpenAiClientConfig is a Kotlin data class bridged identically on every platform.
OpenRouter
- Swift (iOS / macOS)
- Kotlin (all platforms)
Self-hosted vLLM / llama-server
- Swift (iOS / macOS)
- Kotlin (all platforms)
Request shape
ChatCompletionRequest covers standard OpenAI fields plus a few OpenRouter-specific extensions. OpenRouter-only fields are silently ignored by stock OpenAI-compatible APIs.
ChatMessage (the OpenAI-client one, distinct from the on-device ChatMessage in LeapSDK / LeapModelDownloader) is a sealed type with three cases β System, User, Assistant.
Response shape
streamChatCompletion(request) returns a Flow<ChatCompletionEvent> (Kotlin) β SKIE bridges this as a Swift AsyncSequence since v0.10.8, so Swift consumers can iterate it with for try await event in client.streamChatCompletion(request: ...). Events:
Hybrid routing example
Route simple prompts to a small on-device LFM; escalate harder prompts to a cloud model.- Swift (iOS / macOS)
- Kotlin (Android)
- Kotlin (JVM / native)
Lifecycle
TheOpenAiClient(config:) factory (Kotlin fun OpenAiClient(config:) β exported as a SKIE-bundled Swift convenience init since v0.10.8) creates an HttpClient internally and ties it to the returned client β call close() when youβre done.
- Swift (iOS / macOS)
- Kotlin (all platforms)
HttpClient is part of the Kotlin/Ktor surface and isnβt a useful entry point from Swift β the Ktor engine machinery isnβt bridged into the public Swift API. Use OpenAiClient(config:) and let the SDK own the session. If multiple consumers share a client, share the OpenAiClient instance and close() once at teardown.