Model Calling
Compiler makes it incredibly simple to integrate various LLM providers into your app with a unified API.
Supported Providers
Compiler currently supports:
- OpenAI: GPT-4o, GPT-4o Mini, O1, O1 Mini, O3 Mini
- Anthropic: Claude 3.5 Sonnet, Claude 3.5 Haiku, Claude 3.5 Opus
- Google: Gemini Flash, Gemini Flash Lite, Gemini 1.5 Flash, Gemini 1.5 Pro
- Perplexity: Sonar, Sonar Pro, Sonar Reasoning
- DeepSeek: DeepSeek Chat, DeepSeek Reasoner
Setting Up Your Client
The first step is to initialize the CompilerClient
with your app ID:
import CompilerSwiftAI
// Initialize with default configuration (OpenAI GPT-4o)
let client = CompilerClient(
appID: UUID(uuidString: "YOUR_COMPILER_APP_ID")!
)
// Or with a specific model configuration
let client = CompilerClient(
appID: UUID(uuidString: "YOUR_COMPILER_APP_ID")!,
configuration: CompilerClient.Configuration(
streamingChat: .google(.flash)
)
)
Basic Usage
Making a call to an LLM is as simple as:
import CompilerSwiftAI
let client = CompilerClient(appID: UUID(uuidString: "YOUR_COMPILER_APP_ID")!)
// Make a simple call to OpenAI's GPT-4o
let response = try await client.makeModelCall(
using: .openAI(.gpt4o),
messages: [
Message(role: .user, content: "Explain quantum computing in simple terms")
]
)
print(response.text)
Switching Between Providers
One of the biggest advantages of Compiler is the ability to easily switch between different LLM providers:
// Using OpenAI
let openAIResponse = try await client.makeModelCall(
using: .openAI(.gpt4o),
messages: messages
)
// Using Anthropic
let anthropicResponse = try await client.makeModelCall(
using: .anthropic(.claudeOpus),
messages: messages
)
// Using Google
let googleResponse = try await client.makeModelCall(
using: .google(.flash),
messages: messages
)
// Using Perplexity
let perplexityResponse = try await client.makeModelCall(
using: .perplexity(.sonarPro),
messages: messages
)
// Using DeepSeek
let deepseekResponse = try await client.makeModelCall(
using: .deepseek(.reasoner),
messages: messages
)
Streaming Responses
For a more interactive experience, you can stream responses:
try await client.makeStreamingModelCall(
using: .openAI(.gpt4o),
messages: [
Message(role: .user, content: "Write a short story about a robot learning to paint")
],
onUpdate: { partialResponse in
// Update your UI with each chunk of the response
print(partialResponse.text, terminator: "")
},
onComplete: { finalResponse in
// Handle the complete response
print("\nFinal response received!")
}
)
Advanced Options
Setting Temperature
Control the creativity of the model:
// When making a direct call
let response = try await client.makeModelCall(
using: .openAI(.gpt4o, temperature: 0.7), // Higher for more creative, lower for more deterministic
messages: messages
)
// Or when configuring the client
let client = CompilerClient(
appID: UUID(uuidString: "YOUR_COMPILER_APP_ID")!,
configuration: CompilerClient.Configuration(
streamingChat: .anthropic(.claudeSonnet, temperature: 0.7)
)
)
Maximum Tokens
Limit the length of the response:
// When making a direct call
let response = try await client.makeModelCall(
using: .anthropic(.claudeSonnet, maxTokens: 500), // Limit response to 500 tokens
messages: messages
)
// Or when configuring the client
let client = CompilerClient(
appID: UUID(uuidString: "YOUR_COMPILER_APP_ID")!,
configuration: CompilerClient.Configuration(
streamingChat: .google(.flash, maxTokens: 500)
)
)
System Messages
Set the behavior of the model with system messages:
let response = try await client.makeModelCall(
using: .openAI(.gpt4o),
messages: [
Message(role: .system, content: "You are a helpful assistant that specializes in explaining complex topics in simple terms."),
Message(role: .user, content: "Explain how blockchain works")
]
)
Updating Client Configuration
You can update the client’s configuration at any time:
// Update the streaming chat configuration
await client.updateStreamingChat { config in
// Switch to a different model
config = .anthropic(.claudeHaiku)
}
// Create an immutable streaming session with current configuration
let streamingSession = await client.makeStreamingSession()
Integration with UI Components
Compiler provides ready-to-use UI components that work seamlessly with the model calling API. Here’s how to set up a complete chat interface:
@MainActor
@Observable
class CompilerManager {
var isAuthenticated = false
var errorMessage: String?
var isCheckingAuth = true
let client: CompilerClient
init() {
// Initialize with Google Gemini Flash
client = CompilerClient(
appID: UUID(uuidString: "YOUR_COMPILER_APP_ID")!,
configuration: CompilerClient.Configuration(
streamingChat: .google(.flash)
)
)
// Check auth state on init
Task { @MainActor in
do {
isAuthenticated = try await client.attemptAutoLogin()
} catch {
errorMessage = error.localizedDescription
}
isCheckingAuth = false
}
}
}
struct ContentView: View {
@State private var compiler = CompilerManager()
@State private var currentNonce: String?
var body: some View {
VStack(spacing: 20) {
if compiler.isCheckingAuth {
ProgressView("Checking authentication...")
.progressViewStyle(.circular)
} else if compiler.isAuthenticated {
VStack(spacing: 20) {
Text("Chat")
.font(.title)
.fontWeight(.bold)
// The ChatView automatically uses the model configured in the client
ChatView(client: compiler.client, inputType: .combined)
.userBubbleStyle(backgroundColor: .blue, textColor: .white)
.assistantBubbleStyle(backgroundColor: .clear, textColor: .black)
.inputFieldStyle(
backgroundColor: .gray.opacity(0.1),
textColor: .primary,
placeholder: "Message"
)
.inputButtonStyle(
size: 32,
sendTint: .blue
)
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
} else {
SignInWithAppleButton(
onRequest: { request in
let nonce = CompilerClient.randomNonceString()
currentNonce = nonce
request.nonce = CompilerClient.sha256(nonce)
request.requestedScopes = [.fullName, .email]
},
onCompletion: { result in
Task {
do {
compiler.isAuthenticated = try await compiler.client.handleSignInWithApple(result, nonce: currentNonce)
} catch {
compiler.errorMessage = error.localizedDescription
}
}
}
)
.frame(width: 280, height: 45)
.signInWithAppleButtonStyle(.black)
}
if let errorMessage = compiler.errorMessage {
Text(errorMessage)
.foregroundColor(.red)
.padding()
}
}
.padding()
.frame(minWidth: 400, minHeight: 600)
.frame(maxWidth: 800, maxHeight: .infinity)
}
}
Error Handling
Proper error handling for model calls:
do {
let response = try await client.makeModelCall(
using: .openAI(.gpt4o),
messages: messages
)
print(response.text)
} catch CompilerError.rateLimitExceeded {
print("Rate limit exceeded. Please try again later.")
} catch CompilerError.modelUnavailable {
print("The requested model is currently unavailable.")
} catch CompilerError.invalidAPIKey {
print("There's an issue with your API key configuration.")
} catch {
print("An unexpected error occurred: \(error.localizedDescription)")
}
Available Models
OpenAI Models
public enum OpenAIModel: String, Codable {
case gpt4o = "chatgpt-4o-latest"
case gpt4oMini = "gpt-4o-mini"
case o1 = "o1"
case o1Mini = "o1-mini"
case o3Mini = "o3-mini"
}
Anthropic Models
public enum AnthropicModel: String, Codable {
case claudeSonnet = "claude-3-5-sonnet-latest"
case claudeHaiku = "claude-3-5-haiku-latest"
case claudeOpus = "claude-3-5-opus-latest"
}
Google Models
public enum GeminiModel: String, Codable {
case flash = "gemini-2.0-flash"
case flashLitePreview = "gemini-2.0-flash-lite-preview-02-05"
case flash15 = "gemini-1.5-flash"
case flash15_8b = "gemini-1.5-flash-8b"
case pro15 = "gemini-1.5-pro"
case textEmbedding = "text-embedding-004"
}
Perplexity Models
public enum PerplexityModel: String, Codable {
case sonarReasoning = "sonar-reasoning"
case sonarPro = "sonar-pro"
case sonar = "sonar"
}
DeepSeek Models
public enum DeepSeekModel: String, Codable {
case chat = "deepseek-chat"
case reasoner = "deepseek-reasoner"
}
Next Steps
After implementing model calling, you might want to: