Skip to main content
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(...) forces stream = true on the outgoing request regardless of the stream field on the ChatCompletionRequest you pass in.

Add the dependency

Add the LeapOpenAIClient product to your target. See the Quick Start for the full SPM setup.
In Swift sources, import LeapOpenAIClient. The Darwin (URLSession) Ktor engine is bundled β€” no extra HTTP setup needed.

Basic usage

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

Self-hosted vLLM / llama-server

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.
See Cloud AI Comparison for a side-by-side feature breakdown.

Lifecycle

The OpenAiClient(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.
The lower-level constructor that accepts an externally-managed 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.