Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
7B436BC32AE7B027003CE281 /* ModerationDemoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B436BC22AE7B027003CE281 /* ModerationDemoView.swift */; };
7B50DD282C2A9A390070A64D /* LocalHostEntryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B50DD272C2A9A390070A64D /* LocalHostEntryView.swift */; };
7B50DD2B2C2A9D2F0070A64D /* LocalChatDemoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B50DD2A2C2A9D2F0070A64D /* LocalChatDemoView.swift */; };
7B6B1C132CBA4DE300B1A290 /* OverridableURLProxyPathIntroView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B6B1C122CBA4DE300B1A290 /* OverridableURLProxyPathIntroView.swift */; };
7B7239A02AF625F200646679 /* ChatFluidConversationProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B72399F2AF625F200646679 /* ChatFluidConversationProvider.swift */; };
7B7239A22AF6260D00646679 /* ChatDisplayMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B7239A12AF6260D00646679 /* ChatDisplayMessage.swift */; };
7B7239A42AF6289900646679 /* ChatStreamFluidConversationDemoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B7239A32AF6289900646679 /* ChatStreamFluidConversationDemoView.swift */; };
Expand Down Expand Up @@ -123,6 +124,7 @@
7B436BC22AE7B027003CE281 /* ModerationDemoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModerationDemoView.swift; sourceTree = "<group>"; };
7B50DD272C2A9A390070A64D /* LocalHostEntryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalHostEntryView.swift; sourceTree = "<group>"; };
7B50DD2A2C2A9D2F0070A64D /* LocalChatDemoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalChatDemoView.swift; sourceTree = "<group>"; };
7B6B1C122CBA4DE300B1A290 /* OverridableURLProxyPathIntroView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverridableURLProxyPathIntroView.swift; sourceTree = "<group>"; };
7B72399F2AF625F200646679 /* ChatFluidConversationProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatFluidConversationProvider.swift; sourceTree = "<group>"; };
7B7239A12AF6260D00646679 /* ChatDisplayMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatDisplayMessage.swift; sourceTree = "<group>"; };
7B7239A32AF6289900646679 /* ChatStreamFluidConversationDemoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatStreamFluidConversationDemoView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -399,6 +401,7 @@
7B436B972AE25045003CE281 /* Utilities */,
7BBE7E922AFCC9300096A693 /* Vision */,
7BA788CE2AE23A48008825D5 /* ApiKeyIntroView.swift */,
7B6B1C122CBA4DE300B1A290 /* OverridableURLProxyPathIntroView.swift */,
7B50DD272C2A9A390070A64D /* LocalHostEntryView.swift */,
0DF957852BB543F100DD2013 /* AIProxyIntroView.swift */,
7B436B952AE24A04003CE281 /* OptionsListView.swift */,
Expand Down Expand Up @@ -630,6 +633,7 @@
7B99C2E92C0718FF00E701B3 /* FileAttachmentView.swift in Sources */,
7BBE7EA52B02E8A70096A693 /* Sizes.swift in Sources */,
7B7239A22AF6260D00646679 /* ChatDisplayMessage.swift in Sources */,
7B6B1C132CBA4DE300B1A290 /* OverridableURLProxyPathIntroView.swift in Sources */,
0DF957862BB543F100DD2013 /* AIProxyIntroView.swift in Sources */,
7B1268052B08246400400694 /* AssistantConfigurationDemoView.swift in Sources */,
7B436BB72AE7A2EA003CE281 /* ImagesProvider.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ struct ChatDemoView: View {
@State private var isLoading = false
@State private var prompt = ""
@State private var selectedSegment: ChatConfig = .chatCompeltionStream
@State private var model: String = ""
@FocusState private var focus

enum ChatConfig {
case chatCompletion
Expand All @@ -27,6 +29,13 @@ struct ChatDemoView: View {
var body: some View {
ScrollView {
VStack {
VStack {
Text("Add a model name.")
.bold()
TextField("Enter Model e.g: llama3-8b-8192", text: $model)
.focused($focus)
}
.padding()
picker
textArea
Text(chatProvider.errorMessage)
Expand All @@ -39,6 +48,9 @@ struct ChatDemoView: View {
}
}
}
.onAppear {
focus = true
}
.overlay(
Group {
if isLoading {
Expand Down Expand Up @@ -75,9 +87,7 @@ struct ChatDemoView: View {
messages: [.init(
role: .user,
content: content)],
model: .o1Preview,
logProbs: true,
topLogprobs: 1)
model: .custom(model))
switch selectedSegment {
case .chatCompletion:
try await chatProvider.startChat(parameters: parameters)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//
// OverridableURLProxyPathIntroView.swift
// SwiftOpenAIExample
//
// Created by James Rochabrun on 10/11/24.
//

import Foundation
import SwiftOpenAI
import SwiftUI

struct OverridableURLProxyPathIntroView: View {

@State private var apiKey = ""
@State private var url = ""
@State private var proxyPath: String? = nil

private var isReady: Bool {
!apiKey.isEmpty && !url.isEmpty
}

var body: some View {
NavigationStack {
VStack {
Spacer()
VStack(spacing: 24) {
TextField("Enter API Key", text: $apiKey)
TextField("Enter url e.g: https://api.groq.com", text: $url)
TextField("Enter proxy path e.g: openai", text: .init(get: {
proxyPath ?? ""
}, set: { new in
proxyPath = new
}))
}
.padding()
.textFieldStyle(.roundedBorder)
NavigationLink(destination: OptionsListView(
openAIService: OpenAIServiceFactory.service(
apiKey: apiKey,
overrideBaseURL: url,
proxyPath: proxyPath), options: OptionsListView.APIOption.allCases.filter({ $0 != .localChat }))) {
Text("Continue")
.padding()
.padding(.horizontal, 48)
.foregroundColor(.white)
.background(
Capsule()
.foregroundColor(!isReady ? .gray.opacity(0.2) : Color(red: 64/255, green: 195/255, blue: 125/255)))
}
.disabled(!isReady)
Spacer()
Group {
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.")
}
.font(.caption)
}
.padding()
.navigationTitle("Enter OpenAI API KEY")
}
}
}

#Preview {
ApiKeyIntroView()
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,18 @@ struct ServiceSelectionView: View {
.fontWeight(.light)
}
}

NavigationLink(destination: OverridableURLProxyPathIntroView()) {
VStack(alignment: .leading) {
Text("Overridable URL and proxy path e.g: Groq")
.padding(.bottom, 10)
Group {
Text("Use this service to test SwiftOpenAI functionality by providing your own url and proxy path.")
}
.font(.caption)
.fontWeight(.light)
}
}
}
}
}
Expand Down
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ An open-source Swift package designed for effortless interaction with OpenAI's p
- [Azure OpenAI](#azure-openai)
- [AIProxy](#aiproxy)
- [Ollama](#ollama)
- [Groq](#groq)

- [Collaboration](#collaboration)

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

## Groq

<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">

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:

```swift
let apiKey = "your_api_key"
let service = OpenAIServiceFactory.service(apiKey: apiKey, overrideBaseURL: "https://api.groq.com/", proxyPath: "openai")

```

For Supported API's using Groq visit its [documentation](https://console.groq.com/docs/openai).

⚠️ Note: You can probably use the `OpenAIServiceFactory.service(apiKey:overrideBaseURL:proxyPath)` for any OpenAI compatible service.

### Resources:

[Ollama OpenAI compatibility docs.](https://github.com/ollama/ollama/blob/main/docs/openai.md)
Expand Down
12 changes: 11 additions & 1 deletion Sources/OpenAI/Private/Networking/OpenAIAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import Foundation
enum OpenAIAPI {

static var overrideBaseURL: String? = nil

static var proxyPath: String? = nil

case assistant(AssistantCategory) // https://platform.openai.com/docs/api-reference/assistants
case audio(AudioCategory) // https://platform.openai.com/docs/api-reference/audio
case chat /// https://platform.openai.com/docs/api-reference/chat
Expand Down Expand Up @@ -143,6 +144,15 @@ extension OpenAIAPI: Endpoint {
}

var path: String {
guard
let proxyPath = Self.proxyPath
else {
return openAIPath
}
return "/\(proxyPath)\(openAIPath)"
}

var openAIPath: String {
switch self {
case .assistant(let category):
switch category {
Expand Down
2 changes: 2 additions & 0 deletions Sources/OpenAI/Public/Service/DefaultOpenAIService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ struct DefaultOpenAIService: OpenAIService {
apiKey: String,
organizationID: String? = nil,
baseURL: String? = nil,
proxyPath: String? = nil,
configuration: URLSessionConfiguration = .default,
decoder: JSONDecoder = .init(),
debugEnabled: Bool)
Expand All @@ -35,6 +36,7 @@ struct DefaultOpenAIService: OpenAIService {
self.apiKey = .bearer(apiKey)
self.organizationID = organizationID
OpenAIAPI.overrideBaseURL = baseURL
OpenAIAPI.proxyPath = proxyPath
self.debugEnabled = debugEnabled
}

Expand Down
27 changes: 27 additions & 0 deletions Sources/OpenAI/Public/Service/OpenAIServiceFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -121,4 +121,31 @@ public class OpenAIServiceFactory {
baseURL: baseURL,
debugEnabled: debugEnabled)
}

// MARK: Proxy Path

/// Creates and returns an instance of `OpenAIService`.
///
/// Use this service if you need to provide a custom URL with a proxy path, for example to run Groq.
///
/// - Parameters:
/// - apiKey: The optional API key required for authentication.
/// - baseURL: The local host URL. defaults to "https://api.groq.com"
/// - proxyPath: The proxy path e.g `openai`
/// - debugEnabled: If `true` service prints event on DEBUG builds, default to `false`.
///
/// - Returns: A fully configured object conforming to `OpenAIService`.
public static func service(
apiKey: String,
overrideBaseURL: String,
proxyPath: String? = nil,
debugEnabled: Bool = false)
-> OpenAIService
{
DefaultOpenAIService(
apiKey: apiKey,
baseURL: overrideBaseURL,
proxyPath: proxyPath,
debugEnabled: debugEnabled)
}
}