Skip to content

Commit 2e74797

Browse files
Groq support (#93)
* Suppot for Groq * Updating Read me
1 parent 742384d commit 2e74797

File tree

8 files changed

+152
-4
lines changed

8 files changed

+152
-4
lines changed

Examples/SwiftOpenAIExample/SwiftOpenAIExample.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
7B436BC32AE7B027003CE281 /* ModerationDemoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B436BC22AE7B027003CE281 /* ModerationDemoView.swift */; };
4242
7B50DD282C2A9A390070A64D /* LocalHostEntryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B50DD272C2A9A390070A64D /* LocalHostEntryView.swift */; };
4343
7B50DD2B2C2A9D2F0070A64D /* LocalChatDemoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B50DD2A2C2A9D2F0070A64D /* LocalChatDemoView.swift */; };
44+
7B6B1C132CBA4DE300B1A290 /* OverridableURLProxyPathIntroView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B6B1C122CBA4DE300B1A290 /* OverridableURLProxyPathIntroView.swift */; };
4445
7B7239A02AF625F200646679 /* ChatFluidConversationProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B72399F2AF625F200646679 /* ChatFluidConversationProvider.swift */; };
4546
7B7239A22AF6260D00646679 /* ChatDisplayMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B7239A12AF6260D00646679 /* ChatDisplayMessage.swift */; };
4647
7B7239A42AF6289900646679 /* ChatStreamFluidConversationDemoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B7239A32AF6289900646679 /* ChatStreamFluidConversationDemoView.swift */; };
@@ -123,6 +124,7 @@
123124
7B436BC22AE7B027003CE281 /* ModerationDemoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModerationDemoView.swift; sourceTree = "<group>"; };
124125
7B50DD272C2A9A390070A64D /* LocalHostEntryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalHostEntryView.swift; sourceTree = "<group>"; };
125126
7B50DD2A2C2A9D2F0070A64D /* LocalChatDemoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalChatDemoView.swift; sourceTree = "<group>"; };
127+
7B6B1C122CBA4DE300B1A290 /* OverridableURLProxyPathIntroView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverridableURLProxyPathIntroView.swift; sourceTree = "<group>"; };
126128
7B72399F2AF625F200646679 /* ChatFluidConversationProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatFluidConversationProvider.swift; sourceTree = "<group>"; };
127129
7B7239A12AF6260D00646679 /* ChatDisplayMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatDisplayMessage.swift; sourceTree = "<group>"; };
128130
7B7239A32AF6289900646679 /* ChatStreamFluidConversationDemoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatStreamFluidConversationDemoView.swift; sourceTree = "<group>"; };
@@ -399,6 +401,7 @@
399401
7B436B972AE25045003CE281 /* Utilities */,
400402
7BBE7E922AFCC9300096A693 /* Vision */,
401403
7BA788CE2AE23A48008825D5 /* ApiKeyIntroView.swift */,
404+
7B6B1C122CBA4DE300B1A290 /* OverridableURLProxyPathIntroView.swift */,
402405
7B50DD272C2A9A390070A64D /* LocalHostEntryView.swift */,
403406
0DF957852BB543F100DD2013 /* AIProxyIntroView.swift */,
404407
7B436B952AE24A04003CE281 /* OptionsListView.swift */,
@@ -630,6 +633,7 @@
630633
7B99C2E92C0718FF00E701B3 /* FileAttachmentView.swift in Sources */,
631634
7BBE7EA52B02E8A70096A693 /* Sizes.swift in Sources */,
632635
7B7239A22AF6260D00646679 /* ChatDisplayMessage.swift in Sources */,
636+
7B6B1C132CBA4DE300B1A290 /* OverridableURLProxyPathIntroView.swift in Sources */,
633637
0DF957862BB543F100DD2013 /* AIProxyIntroView.swift in Sources */,
634638
7B1268052B08246400400694 /* AssistantConfigurationDemoView.swift in Sources */,
635639
7B436BB72AE7A2EA003CE281 /* ImagesProvider.swift in Sources */,

Examples/SwiftOpenAIExample/SwiftOpenAIExample/ChatDemo/ChatDemoView.swift

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ struct ChatDemoView: View {
1414
@State private var isLoading = false
1515
@State private var prompt = ""
1616
@State private var selectedSegment: ChatConfig = .chatCompeltionStream
17+
@State private var model: String = ""
18+
@FocusState private var focus
1719

1820
enum ChatConfig {
1921
case chatCompletion
@@ -27,6 +29,13 @@ struct ChatDemoView: View {
2729
var body: some View {
2830
ScrollView {
2931
VStack {
32+
VStack {
33+
Text("Add a model name.")
34+
.bold()
35+
TextField("Enter Model e.g: llama3-8b-8192", text: $model)
36+
.focused($focus)
37+
}
38+
.padding()
3039
picker
3140
textArea
3241
Text(chatProvider.errorMessage)
@@ -39,6 +48,9 @@ struct ChatDemoView: View {
3948
}
4049
}
4150
}
51+
.onAppear {
52+
focus = true
53+
}
4254
.overlay(
4355
Group {
4456
if isLoading {
@@ -75,9 +87,7 @@ struct ChatDemoView: View {
7587
messages: [.init(
7688
role: .user,
7789
content: content)],
78-
model: .o1Preview,
79-
logProbs: true,
80-
topLogprobs: 1)
90+
model: .custom(model))
8191
switch selectedSegment {
8292
case .chatCompletion:
8393
try await chatProvider.startChat(parameters: parameters)
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//
2+
// OverridableURLProxyPathIntroView.swift
3+
// SwiftOpenAIExample
4+
//
5+
// Created by James Rochabrun on 10/11/24.
6+
//
7+
8+
import Foundation
9+
import SwiftOpenAI
10+
import SwiftUI
11+
12+
struct OverridableURLProxyPathIntroView: View {
13+
14+
@State private var apiKey = ""
15+
@State private var url = ""
16+
@State private var proxyPath: String? = nil
17+
18+
private var isReady: Bool {
19+
!apiKey.isEmpty && !url.isEmpty
20+
}
21+
22+
var body: some View {
23+
NavigationStack {
24+
VStack {
25+
Spacer()
26+
VStack(spacing: 24) {
27+
TextField("Enter API Key", text: $apiKey)
28+
TextField("Enter url e.g: https://api.groq.com", text: $url)
29+
TextField("Enter proxy path e.g: openai", text: .init(get: {
30+
proxyPath ?? ""
31+
}, set: { new in
32+
proxyPath = new
33+
}))
34+
}
35+
.padding()
36+
.textFieldStyle(.roundedBorder)
37+
NavigationLink(destination: OptionsListView(
38+
openAIService: OpenAIServiceFactory.service(
39+
apiKey: apiKey,
40+
overrideBaseURL: url,
41+
proxyPath: proxyPath), options: OptionsListView.APIOption.allCases.filter({ $0 != .localChat }))) {
42+
Text("Continue")
43+
.padding()
44+
.padding(.horizontal, 48)
45+
.foregroundColor(.white)
46+
.background(
47+
Capsule()
48+
.foregroundColor(!isReady ? .gray.opacity(0.2) : Color(red: 64/255, green: 195/255, blue: 125/255)))
49+
}
50+
.disabled(!isReady)
51+
Spacer()
52+
Group {
53+
Text("If you don't have a valid API KEY yet, you can visit ") + Text("[this link](https://platform.openai.com/account/api-keys)") + Text(" to get started.")
54+
}
55+
.font(.caption)
56+
}
57+
.padding()
58+
.navigationTitle("Enter OpenAI API KEY")
59+
}
60+
}
61+
}
62+
63+
#Preview {
64+
ApiKeyIntroView()
65+
}

Examples/SwiftOpenAIExample/SwiftOpenAIExample/ServiceSelectionView.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,18 @@ struct ServiceSelectionView: View {
4848
.fontWeight(.light)
4949
}
5050
}
51+
52+
NavigationLink(destination: OverridableURLProxyPathIntroView()) {
53+
VStack(alignment: .leading) {
54+
Text("Overridable URL and proxy path e.g: Groq")
55+
.padding(.bottom, 10)
56+
Group {
57+
Text("Use this service to test SwiftOpenAI functionality by providing your own url and proxy path.")
58+
}
59+
.font(.caption)
60+
.fontWeight(.light)
61+
}
62+
}
5163
}
5264
}
5365
}

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ An open-source Swift package designed for effortless interaction with OpenAI's p
1818
- [Azure OpenAI](#azure-openai)
1919
- [AIProxy](#aiproxy)
2020
- [Ollama](#ollama)
21+
- [Groq](#groq)
22+
2123
- [Collaboration](#collaboration)
2224

2325
## Description
@@ -3287,6 +3289,22 @@ let parameters = ChatCompletionParameters(messages: [.init(role: .user, content:
32873289
let chatCompletionObject = service.startStreamedChat(parameters: parameters)
32883290
```
32893291

3292+
## Groq
3293+
3294+
<img width="792" alt="Screenshot 2024-10-11 at 11 49 04 PM" src="https://github.com/user-attachments/assets/7afb36a2-b2d8-4f89-9592-f4cece20d469">
3295+
3296+
Groq API is mostly compatible with OpenAI's client libraries like `SwiftOpenAI` to use Groq using this library you just need to create an instance of `OpenAIService` like this:
3297+
3298+
```swift
3299+
let apiKey = "your_api_key"
3300+
let service = OpenAIServiceFactory.service(apiKey: apiKey, overrideBaseURL: "https://api.groq.com/", proxyPath: "openai")
3301+
3302+
```
3303+
3304+
For Supported API's using Groq visit its [documentation](https://console.groq.com/docs/openai).
3305+
3306+
⚠️ Note: You can probably use the `OpenAIServiceFactory.service(apiKey:overrideBaseURL:proxyPath)` for any OpenAI compatible service.
3307+
32903308
### Resources:
32913309

32923310
[Ollama OpenAI compatibility docs.](https://github.com/ollama/ollama/blob/main/docs/openai.md)

Sources/OpenAI/Private/Networking/OpenAIAPI.swift

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ import Foundation
1212
enum OpenAIAPI {
1313

1414
static var overrideBaseURL: String? = nil
15-
15+
static var proxyPath: String? = nil
16+
1617
case assistant(AssistantCategory) // https://platform.openai.com/docs/api-reference/assistants
1718
case audio(AudioCategory) // https://platform.openai.com/docs/api-reference/audio
1819
case chat /// https://platform.openai.com/docs/api-reference/chat
@@ -143,6 +144,15 @@ extension OpenAIAPI: Endpoint {
143144
}
144145

145146
var path: String {
147+
guard
148+
let proxyPath = Self.proxyPath
149+
else {
150+
return openAIPath
151+
}
152+
return "/\(proxyPath)\(openAIPath)"
153+
}
154+
155+
var openAIPath: String {
146156
switch self {
147157
case .assistant(let category):
148158
switch category {

Sources/OpenAI/Public/Service/DefaultOpenAIService.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ struct DefaultOpenAIService: OpenAIService {
2626
apiKey: String,
2727
organizationID: String? = nil,
2828
baseURL: String? = nil,
29+
proxyPath: String? = nil,
2930
configuration: URLSessionConfiguration = .default,
3031
decoder: JSONDecoder = .init(),
3132
debugEnabled: Bool)
@@ -35,6 +36,7 @@ struct DefaultOpenAIService: OpenAIService {
3536
self.apiKey = .bearer(apiKey)
3637
self.organizationID = organizationID
3738
OpenAIAPI.overrideBaseURL = baseURL
39+
OpenAIAPI.proxyPath = proxyPath
3840
self.debugEnabled = debugEnabled
3941
}
4042

Sources/OpenAI/Public/Service/OpenAIServiceFactory.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,4 +121,31 @@ public class OpenAIServiceFactory {
121121
baseURL: baseURL,
122122
debugEnabled: debugEnabled)
123123
}
124+
125+
// MARK: Proxy Path
126+
127+
/// Creates and returns an instance of `OpenAIService`.
128+
///
129+
/// Use this service if you need to provide a custom URL with a proxy path, for example to run Groq.
130+
///
131+
/// - Parameters:
132+
/// - apiKey: The optional API key required for authentication.
133+
/// - baseURL: The local host URL. defaults to "https://api.groq.com"
134+
/// - proxyPath: The proxy path e.g `openai`
135+
/// - debugEnabled: If `true` service prints event on DEBUG builds, default to `false`.
136+
///
137+
/// - Returns: A fully configured object conforming to `OpenAIService`.
138+
public static func service(
139+
apiKey: String,
140+
overrideBaseURL: String,
141+
proxyPath: String? = nil,
142+
debugEnabled: Bool = false)
143+
-> OpenAIService
144+
{
145+
DefaultOpenAIService(
146+
apiKey: apiKey,
147+
baseURL: overrideBaseURL,
148+
proxyPath: proxyPath,
149+
debugEnabled: debugEnabled)
150+
}
124151
}

0 commit comments

Comments
 (0)