OpenAI provider for the OpenFoundationModels framework, enabling the use of OpenAI's latest GPT and Reasoning models through Apple's Foundation Models API interface. Features a unified model interface with automatic constraint handling and self-contained architecture.
- π Transcript-Based Interface: Full support for OpenFoundationModels' Transcript-centric design
- π€ Complete Model Support: GPT-4o, GPT-4o Mini, GPT-4 Turbo, and all Reasoning models (o1, o1-pro, o3, o3-pro, o4-mini)
- π§ Reasoning Models: Native support for o1, o1-pro, o3, o3-pro, and o4-mini with automatic constraint handling
- π Streaming Support: Real-time response streaming with Server-Sent Events
- π― Unified Interface: Single API for all models with automatic parameter validation
- π§ Multimodal Support: Text, image, and audio input support (GPT models only)
- π¦ Self-Contained: No external dependencies beyond OpenFoundationModels
- β‘ Performance Optimized: Custom HTTP client with actor-based concurrency
- π‘οΈ Type Safety: Compile-time model validation and constraint checking
- π οΈ Tool Support: Automatic extraction and conversion of tool definitions from Transcript
Add this package to your Package.swift:
dependencies: [
.package(url: "https://github.com/1amageek/OpenFoundationModels-OpenAI.git", from: "1.0.0")
]Or add it through Xcode:
- File β Add Package Dependencies
- Enter:
https://github.com/1amageek/OpenFoundationModels-OpenAI.git
import OpenFoundationModels
import OpenFoundationModelsOpenAI
// Simple model creation with convenience initializers
let gptModel = OpenAILanguageModel(apiKey: "your-openai-api-key", model: .gpt4o)
let reasoningModel = OpenAILanguageModel(apiKey: "your-openai-api-key", model: .o3)
// Use with LanguageModelSession (Transcript-based)
let session = LanguageModelSession(
model: gptModel, // or reasoningModel
tools: [],
instructions: "You are a helpful assistant."
)
// Generate text - Session manages the Transcript
let response = try await session.respond(to: "Tell me about Swift programming")
print(response.content)
// Continue conversation - Transcript automatically updated
let followUp = try await session.respond(to: "What are its main features?")
print(followUp.content)// Create with API key and model
let model = OpenAILanguageModel(apiKey: "your-api-key", model: .gpt4o)
// Create with default model (GPT-4o)
let defaultModel = OpenAILanguageModel(apiKey: "your-api-key")
// Create with custom base URL
let customModel = OpenAILanguageModel(
apiKey: "your-api-key",
model: .o3,
baseURL: URL(string: "https://custom.openai.com/v1")!
)// Create configuration with custom settings
let configuration = OpenAIConfiguration(
apiKey: "your-api-key",
timeout: 120.0,
retryPolicy: .exponentialBackoff(maxAttempts: 3),
rateLimits: .tier3
)
// Create model with custom configuration
let model = OpenAILanguageModel(configuration: configuration, model: .gpt4o)| Model Family | Model | Context Window | Max Output | Vision | Reasoning | Knowledge Cutoff |
|---|---|---|---|---|---|---|
| GPT | gpt-4o | 128,000 | 16,384 | β | β | October 2023 |
| GPT | gpt-4o-mini | 128,000 | 16,384 | β | β | October 2023 |
| GPT | gpt-4-turbo | 128,000 | 4,096 | β | β | April 2024 |
| Reasoning | o1 | 200,000 | 32,768 | β | β | October 2023 |
| Reasoning | o1-pro | 200,000 | 65,536 | β | β | October 2023 |
| Reasoning | o3 | 200,000 | 32,768 | β | β | October 2023 |
| Reasoning | o3-pro | 200,000 | 65,536 | β | β | October 2023 |
| Reasoning | o4-mini | 200,000 | 16,384 | β | β | October 2023 |
| Parameter | GPT Models | Reasoning Models | Notes |
|---|---|---|---|
temperature |
β (0.0-2.0) | β | Reasoning models use deterministic generation |
topP |
β (0.0-1.0) | β | Alternative to temperature for nucleus sampling |
frequencyPenalty |
β (-2.0-2.0) | β | Reduces repetition based on frequency |
presencePenalty |
β (-2.0-2.0) | β | Reduces repetition based on presence |
stop |
β | β | Custom stop sequences |
maxTokens |
β | Reasoning models use maxCompletionTokens |
|
stream |
β | β | All models support streaming |
functionCalling |
β | β | All models support function calling |
-
Temperature Control: Reasoning models (o1, o3, etc.) do not support temperature, topP, or penalty parameters. They always use deterministic generation for consistent reasoning.
-
Parameter Names:
- GPT models: Use
max_tokensparameter - Reasoning models: Use
max_completion_tokensparameter
- GPT models: Use
-
Vision Support: Only GPT models support image inputs. Reasoning models are text-only.
-
Response Time: Reasoning models typically take longer to respond due to their complex thinking process.
// GPT model - all parameters supported
let gptResponse = try await gptModel.generate(
prompt: "Write a creative story",
options: GenerationOptions(
temperature: 0.9, // β
Supported
topP: 0.95, // β
Supported
maxTokens: 1000, // β
Supported
frequencyPenalty: 0.5 // β
Supported
)
)
// Reasoning model - limited parameters
let reasoningResponse = try await reasoningModel.generate(
prompt: "Solve this complex problem",
options: GenerationOptions(
temperature: 0.9, // β Ignored
topP: 0.95, // β Ignored
maxTokens: 2000, // β οΈ Converted to maxCompletionTokens
frequencyPenalty: 0.5 // β Ignored
)
)- GPT-4o: Best for general-purpose tasks with vision support (standard tier)
- GPT-4o Mini: Cost-efficient option with vision capabilities (economy tier)
- o3: Advanced reasoning for complex problem-solving (standard tier)
- o3-pro: Highest reasoning capability for difficult tasks (premium tier)
- o4-mini: Cost-effective reasoning model (economy tier)
OpenFoundationModels-OpenAI fully embraces the Transcript-centric design of OpenFoundationModels:
- LanguageModelSession manages the conversation state via
Transcript - OpenAILanguageModel receives the complete
Transcriptfor each request - The provider converts
Transcriptentries to OpenAI's message format
// Internal conversion from Transcript to OpenAI messages
Transcript.Entry.instructions -> ChatMessage.system // System instructions
Transcript.Entry.prompt -> ChatMessage.user // User messages
Transcript.Entry.response -> ChatMessage.assistant // Assistant responses
Transcript.Entry.toolCalls -> ChatMessage.assistant // Tool invocations
Transcript.Entry.toolOutput -> ChatMessage.system // Tool results// Create a Transcript manually (usually handled by LanguageModelSession)
var transcript = Transcript()
// Add instructions
transcript.entries.append(.instructions(
Transcript.Instructions(
segments: [.text(Transcript.TextSegment(content: "You are a helpful assistant."))]
)
))
// Add user prompt
transcript.entries.append(.prompt(
Transcript.Prompt(
segments: [.text(Transcript.TextSegment(content: "What is Swift?"))]
)
))
// Generate response using the transcript
let response = try await model.generate(
transcript: transcript,
options: GenerationOptions(maximumResponseTokens: 500)
)- Stateless Model: OpenAILanguageModel doesn't maintain state between calls
- Complete Context: Every request includes the full conversation history
- Provider Flexibility: Clean separation between OpenFoundationModels and OpenAI APIs
- Tool Support: Automatic extraction of tool definitions from Instructions
// Create model
let model = OpenAILanguageModel(apiKey: apiKey, model: .gpt4o)
// Use with LanguageModelSession for automatic Transcript management
let session = LanguageModelSession(
model: model,
tools: [],
instructions: "You are a helpful assistant."
)
// Generate response
let response = try await session.respond(to: "Explain quantum computing")
print(response.content)
// Reasoning model for complex problems
let reasoningModel = OpenAILanguageModel(apiKey: apiKey, model: .o3)
let reasoningSession = LanguageModelSession(model: reasoningModel)
let solution = try await reasoningSession.respond(
to: "Solve this complex mathematical proof step by step...",
options: GenerationOptions(maximumResponseTokens: 2000)
)let model = OpenAILanguageModel(apiKey: apiKey, model: .gpt4o)
let session = LanguageModelSession(model: model)
// Stream response
let stream = session.streamResponse(to: "Write a story about AI")
for try await partial in stream {
print(partial.content, terminator: "")
}@Generable
struct BookReview {
@Guide(description: "Book title")
let title: String
@Guide(description: "Rating from 1-5", .range(1...5))
let rating: Int
@Guide(description: "Review summary", .maxLength(200))
let summary: String
}
// Create model and session
let model = OpenAILanguageModel(apiKey: apiKey, model: .gpt4o)
let session = LanguageModelSession(
model: model,
tools: [],
instructions: "You are a literary critic."
)
// Generate structured response
let review = try await session.respond(
to: "Review the book '1984' by George Orwell",
generating: BookReview.self
)
print("Title: \(review.title)")
print("Rating: \(review.rating)/5")
print("Summary: \(review.summary)")let imageData = // ... your image data
let prompt = Prompt.multimodal(
text: "What's in this image?",
image: imageData
)
let response = try await session.respond { prompt }// Creative writing (high temperature, diverse output)
let creative = GenerationOptions(
temperature: 0.9,
maxTokens: 2000,
topP: 0.95
)
// Precise, factual responses (low temperature)
let precise = GenerationOptions(
temperature: 0.1,
maxTokens: 1000
)
// Code generation (structured, deterministic)
let coding = GenerationOptions(
temperature: 0.0,
maxTokens: 4000
)
// Conversational (balanced settings)
let chat = GenerationOptions(
temperature: 0.7,
maxTokens: 1500
)The provider includes built-in rate limiting with several predefined tiers:
// Tier 1: 500 RPM, 30K TPM
let config1 = OpenAIConfiguration(apiKey: apiKey, rateLimits: .tier1)
// Tier 2: 3,500 RPM, 90K TPM
let config2 = OpenAIConfiguration(apiKey: apiKey, rateLimits: .tier2)
// Tier 3: 10,000 RPM, 150K TPM
let config3 = OpenAIConfiguration(apiKey: apiKey, rateLimits: .tier3)
// Custom rate limits
let custom = RateLimitConfiguration(
requestsPerMinute: 1000,
tokensPerMinute: 50000
)do {
let response = try await openAI.generate(prompt: "Hello")
} catch let error as OpenAIModelError {
switch error {
case .rateLimitExceeded:
print("Rate limited. Please try again later")
case .contextLengthExceeded(let model, let maxTokens):
print("Context too long for model \(model). Maximum: \(maxTokens) tokens")
case .modelNotAvailable(let model):
print("Model \(model) not available")
case .parameterNotSupported(let parameter, let model):
print("Parameter \(parameter) not supported by model \(model)")
default:
print("Error: \(error.localizedDescription)")
}
}- iOS 17.0+ / macOS 14.0+ / tvOS 17.0+ / watchOS 10.0+ / visionOS 1.0+
- Swift 6.1+
- Xcode 16.0+
- OpenFoundationModels - Core framework (only dependency)
The OpenFoundationModels framework has transitioned to a Transcript-centric design. Here's how to migrate:
// Direct prompt-based calls
let response = try await model.generate(
prompt: "Hello",
options: options,
tools: tools
)// Use LanguageModelSession for automatic Transcript management
let session = LanguageModelSession(model: model, tools: tools)
let response = try await session.respond(to: "Hello", options: options)
// Or use Transcript directly (low-level)
var transcript = Transcript()
transcript.entries.append(.prompt(/*...*/))
let response = try await model.generate(transcript: transcript, options: options)- LanguageModel Protocol: Now accepts
Transcriptinstead ofpromptandtools - Tool Management: Tools are defined in
Transcript.Instructions.toolDefinitions - Conversation Context: All history is contained in the
Transcript - Stateless Models: Models no longer maintain conversation state
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests for new functionality
- Submit a pull request
This project is licensed under the MIT License - see the LICENSE file for details.
- π Documentation
- π Report Issues
- π¬ Discussions
- OpenFoundationModels - Core framework
- OpenFoundationModels-Anthropic - Anthropic provider
- OpenFoundationModels-Local - Local model provider