uncloseai.
Swift Examples - Free LLM & TTS AI Service
Swift Examples
This page demonstrates how to use the uncloseai. API endpoints with Swift using the SwiftOpenAI community library. All examples use the same OpenAI-compatible API interface, making it easy to switch between different models and endpoints.
Available Endpoints:
- hermes.ai.unturf.com:
https://hermes.ai.unturf.com/v1- chat completions on an RTX 4090 - qwen.ai.unturf.com:
https://qwen.ai.unturf.com/v1- chat completions on an RTX 3090 - TTS:
https://speech.ai.unturf.com/v1- Text-to-speech generation
Both chat hostnames currently serve the same model, turboderp/Qwen3.8-27B-exl3 (read live from /v1/models). Hostnames are routing labels, not model names: every example below asks the endpoint which model it serves instead of hard-coding an id, so a model swap on the cards never breaks your code. The served model reasons before it answers (the reasoning arrives in a separate reasoning field), so give it room: the samples ask for 600 tokens because at 150 the budget ran out before the answer began.
Many endpoints from environment variables
Our clients (uncloseai-cli, uncloseai.js, opencompletion and the downloadable implementation for every language on this site) share one convention, so an endpoint is configuration, never code:
| Variable | Meaning |
|---|---|
MODEL_ENDPOINT_N | Base URL of the N-th OpenAI-compatible endpoint, ending in /v1. The only required variable. Scan N from 0 upward and skip gaps: uncloseai-cli counts from 0, the per-language implementations from 1. |
MODEL_API_KEY_N | Bearer key for endpoint N. Unset means no key, which is all our free relay needs; a cloud endpoint sets one. |
MODEL_ID_N | Model id to send. Unset means ask /v1/models and take the first id, the rule used by every sample on this page. |
MODEL_LABEL_N | A display name for endpoint N; defaults to its hostname. |
TTS_ENDPOINT_N | The same shape for text-to-speech endpoints. |
export MODEL_ENDPOINT_0="https://hermes.ai.unturf.com/v1"
export MODEL_ENDPOINT_1="https://qwen.ai.unturf.com/v1"
# export MODEL_API_KEY_1="sk-..." # only for an endpoint that needs one
export TTS_ENDPOINT_0="https://speech.ai.unturf.com/v1"
The first endpoint is the default; the rest fail over, in order. The downloadable implementations under /languages/ read these variables and discover each endpoint's models at startup.
Swift Client Installation
Add SwiftOpenAI to your Package.swift dependencies:
dependencies: [
.package(url: "https://github.com/jamesrochabrun/SwiftOpenAI", from: "3.8.5")
]
Non-Streaming Examples
Non-streaming mode waits for the complete response before returning. This is simpler to use but provides no intermediate feedback during generation.
Using hermes.ai.unturf.com (RTX 4090)
import SwiftOpenAI
let service = OpenAIServiceFactory.service(
apiKey: "choose-any-value",
baseURL: "https://hermes.ai.unturf.com/v1"
)
// Ask the endpoint which model it serves; never hard-code an id.
let model = try await service.listModels().data[0].id
let parameters = ChatCompletionParameters(
messages: [.init(role: .user, content: .text("Give a Python Fizzbuzz solution in one line of code?"))],
model: .custom(model),
temperature: 0.5,
maxTokens: 600
)
do {
let completion = try await service.startChat(parameters: parameters)
if let content = completion.choices.first?.message.content {
print(content)
}
} catch {
print("Error: \(error)")
}
Using qwen.ai.unturf.com (RTX 3090)
import SwiftOpenAI
let service = OpenAIServiceFactory.service(
apiKey: "choose-any-value",
baseURL: "https://qwen.ai.unturf.com/v1"
)
// Ask the endpoint which model it serves; never hard-code an id.
let model = try await service.listModels().data[0].id
let parameters = ChatCompletionParameters(
messages: [.init(role: .user, content: .text("Give a Python Fizzbuzz solution in one line of code?"))],
model: .custom(model),
temperature: 0.5,
maxTokens: 600
)
do {
let completion = try await service.startChat(parameters: parameters)
if let content = completion.choices.first?.message.content {
print(content)
}
} catch {
print("Error: \(error)")
}
Streaming Examples
Streaming mode returns chunks of the response as they are generated, providing real-time feedback. This is ideal for interactive applications and long responses.
Using hermes.ai.unturf.com (RTX 4090)
import SwiftOpenAI
let service = OpenAIServiceFactory.service(
apiKey: "choose-any-value",
baseURL: "https://hermes.ai.unturf.com/v1"
)
// Ask the endpoint which model it serves; never hard-code an id.
let model = try await service.listModels().data[0].id
let parameters = ChatCompletionParameters(
messages: [.init(role: .user, content: .text("Give a Python Fizzbuzz solution in one line of code?"))],
model: .custom(model),
temperature: 0.5,
maxTokens: 600
)
do {
let stream = try await service.startStreamedChat(parameters: parameters)
for try await chunk in stream {
if let content = chunk.choices.first?.delta.content {
print(content, terminator: "")
}
}
} catch {
print("Error: \(error)")
}
Using qwen.ai.unturf.com (RTX 3090)
import SwiftOpenAI
let service = OpenAIServiceFactory.service(
apiKey: "choose-any-value",
baseURL: "https://qwen.ai.unturf.com/v1"
)
// Ask the endpoint which model it serves; never hard-code an id.
let model = try await service.listModels().data[0].id
let parameters = ChatCompletionParameters(
messages: [.init(role: .user, content: .text("Give a Python Fizzbuzz solution in one line of code?"))],
model: .custom(model),
temperature: 0.5,
maxTokens: 600
)
do {
let stream = try await service.startStreamedChat(parameters: parameters)
for try await chunk in stream {
if let content = chunk.choices.first?.delta.content {
print(content, terminator: "")
}
}
} catch {
print("Error: \(error)")
}
Text-to-Speech Example
Generate audio speech from text using the TTS endpoint. The audio is saved as an MP3 file.
import Foundation
// SwiftOpenAI's voice enum is closed to OpenAI's voice names, so call the
// speech endpoint directly: it is one JSON POST.
var request = URLRequest(url: URL(string: "https://speech.ai.unturf.com/v1/audio/speech")!)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("Bearer YOLO", forHTTPHeaderField: "Authorization")
request.httpBody = try JSONSerialization.data(withJSONObject: [
"model": "tts-1-f5",
"voice": "foxhop",
"speed": 0.9,
"input": "I think so therefore, Today is a wonderful day to grow something people love!",
])
do {
let (audioData, _) = try await URLSession.shared.data(for: request)
let fileURL = FileManager.default.temporaryDirectory.appendingPathComponent("speech.mp3")
try audioData.write(to: fileURL)
print("Audio saved to: \(fileURL)")
} catch {
print("Error: \(error)")
}