Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v1.1 iOS #52

Merged
merged 13 commits into from
Sep 20, 2024
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
2 changes: 1 addition & 1 deletion .github/workflows/ios-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
PicoLLMAppTestUITests/BaseTest.swift

- name: Inject Resource URL
run: sed -i '.bak' 's?{TESTING_MODEL_URL_HERE}?http://${{secrets.PV_CICD_RES_SERVER_AUTHORITY}}/github/picollm/res/phi2-290.pllm/latest/phi2-290.pllm?'
run: sed -i '.bak' 's?{TESTING_MODEL_URL_HERE}?http://${{secrets.PV_CICD_RES_SERVER_AUTHORITY}}/github/picollm/res/phi2-290.pllm/03-280e68c/phi2-290.pllm?'
PicoLLMAppTestUITests/BaseTest.swift

- name: XCode Build
Expand Down
32 changes: 25 additions & 7 deletions binding/ios/PicoLLM.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

import PvPicoLLM

// Usage information.
/// Usage information.
public struct PicoLLMUsage: Codable {
public let promptTokens: Int

Expand All @@ -23,11 +23,12 @@ public struct PicoLLMUsage: Codable {
}
}

// Reasons for ending the generation process.
/// Reasons for ending the generation process.
public enum PicoLLMEndpoint: Codable {
case endOfSentence
case completionTokenLimitReached
case stopPhraseEncountered
case interrupted

public static func fromC(cEndpoint: pv_picollm_endpoint_t) -> PicoLLMEndpoint? {
switch cEndpoint {
Expand All @@ -37,13 +38,15 @@ public enum PicoLLMEndpoint: Codable {
return PicoLLMEndpoint.completionTokenLimitReached
case PV_PICOLLM_ENDPOINT_STOP_PHRASE_ENCOUNTERED:
return PicoLLMEndpoint.stopPhraseEncountered
case PV_PICOLLM_ENDPOINT_INTERRUPTED:
return PicoLLMEndpoint.interrupted
default:
return nil
}
}
}

// Generated token and its log probability.
/// Generated token and its log probability.
public struct PicoLLMToken: Codable {
public let token: String

Expand All @@ -57,7 +60,7 @@ public struct PicoLLMToken: Codable {
}
}

// Generated token within completion and top alternative tokens.
/// Generated token within completion and top alternative tokens.
public struct PicoLLMCompletionToken: Codable {
public let token: PicoLLMToken

Expand All @@ -71,7 +74,7 @@ public struct PicoLLMCompletionToken: Codable {
}
}

// Result object containing stats and generated tokens.
/// Result object containing stats and generated tokens.
public struct PicoLLMCompletion: Codable {
public let usage: PicoLLMUsage

Expand All @@ -93,7 +96,7 @@ public struct PicoLLMCompletion: Codable {
}
}

// Private callback for hoisting C callback into Swift callback.
/// Private callback for hoisting C callback into Swift callback.
func cStreamCallback (completion: UnsafePointer<CChar>?, context: UnsafeMutableRawPointer?) {
let object = Unmanaged<PicoLLM>.fromOpaque(context!).takeUnretainedValue()

Expand Down Expand Up @@ -312,6 +315,20 @@ public class PicoLLM {
completion: completion)
}

/// Interrupts `pv_picollm_generate()` if generation is in progress. Otherwise, it has no effect.
/// - Throws: PicoLLMError
public func interrupt() throws {
if handle == nil {
throw PicoLLMInvalidStateError("PicoLLM must be initialized before calling interrupt")
}

let status = pv_picollm_interrupt(self.handle)
if status != PV_STATUS_SUCCESS {
let messageStack = try PicoLLM.getMessageStack()
throw PicoLLM.pvStatusToPicoLLMError(status, "PicoLLM interrupt failed", messageStack)
}
}

/// Tokenizes a given text using the model's tokenizer.
/// This is a low-level function meant for benchmarking and advanced usage.
/// `.generate()` should be used when possible.
Expand Down Expand Up @@ -496,7 +513,8 @@ public class PicoLLM {
"llama-3-70b-chat": Llama3ChatDialog.self,
"mistral-7b-instruct-v0.1": MistralChatDialog.self,
"mistral-7b-instruct-v0.2": MistralChatDialog.self,
"mixtral-8x7b-instruct-v0.1": MixtralChatDialog.self
"mixtral-8x7b-instruct-v0.1": MixtralChatDialog.self,
"phi3": Phi3ChatDialog.self
]

private static let phi2Dialogs: [String: Phi2Dialog.Type] = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,27 @@ class PicoLLMAppTestUITests: BaseTest {
XCTAssertEqual(pieces, expected)
}

func testInterrupt() throws {
let testCase = PicollmTestCase(name: "default", data: self.picollmTestData!)

let group = DispatchGroup()

var res: PicoLLMCompletion?

group.enter()
DispatchQueue.global(qos: .background).async {
do {
res = try self.handle!.generate(prompt: testCase.prompt)
} catch { }
group.leave()
}
sleep(1)
try handle!.interrupt()

group.wait()
XCTAssertEqual(res!.endpoint, .interrupted)
}

func testTokenize() throws {
let tokenizeData = self.picollmTestData!["tokenize"] as! [String: Any]
let text = tokenizeData["text"] as! String
Expand Down Expand Up @@ -401,10 +422,10 @@ class PicoLLMAppTestUITests: BaseTest {
"llama-3-chat-dialog": Llama3ChatDialog.self,
"mistral-chat-dialog": MistralChatDialog.self,
"phi2-chat-dialog": Phi2ChatDialog.self,
"phi2-qa-dialog": Phi2QADialog.self
"phi2-qa-dialog": Phi2QADialog.self,
"phi3-chat-dialog": Phi3ChatDialog.self
]
let dialogPrompts = self.dialogTestData![testName] as! [String: String]

let conversation = self.dialogTestData!["conversation"] as! [[String]]

for (dialogClassName, dialogClassType) in dialogClasses {
Expand All @@ -415,6 +436,7 @@ class PicoLLMAppTestUITests: BaseTest {
try dialog.addHumanRequest(content: conversation[i][0])
try dialog.addLLMResponse(content: conversation[i][1])
}

try dialog.addHumanRequest(content: conversation.last![0])
XCTAssertEqual(try dialog.prompt(), prompt)
}
Expand Down
4 changes: 2 additions & 2 deletions binding/ios/PicoLLMAppTest/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ source 'https://cdn.cocoapods.org/'
platform :ios, '16.0'

target 'PicoLLMAppTest' do
pod 'picoLLM-iOS', '~> 1.0.0'
pod 'picoLLM-iOS', :podspec => 'https://raw.githubusercontent.com/Picovoice/picollm/v1.1-ios/binding/ios/picoLLM-iOS.podspec'
end

target 'PicoLLMAppTestUITests' do
pod 'picoLLM-iOS', '~> 1.0.0'
pod 'picoLLM-iOS', :podspec => 'https://raw.githubusercontent.com/Picovoice/picollm/v1.1-ios/binding/ios/picoLLM-iOS.podspec'
end
16 changes: 8 additions & 8 deletions binding/ios/PicoLLMAppTest/Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
PODS:
- picoLLM-iOS (1.0.0)
- picoLLM-iOS (1.1.0)

DEPENDENCIES:
- picoLLM-iOS (~> 1.0.0)
- picoLLM-iOS (from `https://raw.githubusercontent.com/Picovoice/picollm/v1.1-ios/binding/ios/picoLLM-iOS.podspec`)

SPEC REPOS:
trunk:
- picoLLM-iOS
EXTERNAL SOURCES:
picoLLM-iOS:
:podspec: https://raw.githubusercontent.com/Picovoice/picollm/v1.1-ios/binding/ios/picoLLM-iOS.podspec

SPEC CHECKSUMS:
picoLLM-iOS: 02cdb501b4beb74a9c1dea29d5cf461d65ea4a6c
picoLLM-iOS: dc03cd7e992c702ff34c667f9a35dd9a8084c061

PODFILE CHECKSUM: 5223638a26efe0676b29af87115eb47f41185dc4
PODFILE CHECKSUM: 8557d5c1b8e4eb632f8926bbc8eeb29802224b3d

COCOAPODS: 1.15.2
COCOAPODS: 1.11.3
41 changes: 41 additions & 0 deletions binding/ios/PicoLLMDialog.swift
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,47 @@ public class Phi2ChatDialog: Phi2Dialog {
}
}

/// Dialog helper for `phi3`.
public class Phi3ChatDialog: BasePicoLLMDialog {
public override func prompt() throws -> String {
if self.humanRequests.count == self.llmResponses.count {
throw PicoLLMInvalidStateError("Only subclasses of PicoLLMDialog can return create prompts.")
}

let humanRequests = (self.history == nil) ?
self.humanRequests[...] :
self.humanRequests[(self.humanRequests.count - Int(self.history!) - 1)...]
let llmResponses = (self.history == nil) ?
self.llmResponses[...] :
self.llmResponses[(self.llmResponses.count - Int(self.history!))...]

var res = ""
if system != nil {
res += String(format: "<|system|>\n%@<|end|>\n", system!)
}
for i in 0..<llmResponses.count {
res += String(
format: "<|user|>\n%@<|end|>\n",
humanRequests[i].trimmingCharacters(in: .whitespacesAndNewlines)
)
res += String(
format: "<|assistant|>\n%@<|end|>\n",
llmResponses[i].trimmingCharacters(in: .whitespacesAndNewlines)
)
}

res += String(
format: "<|user|>\n%@<|end|>\n",
humanRequests.last!.trimmingCharacters(in: .whitespacesAndNewlines)
)
res += String(
format: "<|assistant|>\n"
)

return res
}
}

/// Dialog helper for `mistral-7b-instruct-v0.1` and `mistral-7b-instruct-v0.2`.
public class MistralChatDialog: BasePicoLLMDialog {
public override func prompt() throws -> String {
Expand Down
4 changes: 2 additions & 2 deletions binding/ios/picoLLM-iOS.podspec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|
s.name = 'picoLLM-iOS'
s.module_name = 'PicoLLM'
s.version = '1.0.0'
s.version = '1.1.0'
s.license = {:type => 'Apache 2.0'}
s.summary = 'picoLLM Inference Engine'
s.description =
Expand All @@ -10,7 +10,7 @@ Pod::Spec.new do |s|
DESC
s.homepage = 'https://github.com/Picovoice/picollm/tree/master/binding/ios'
s.author = { 'Picovoice' => '[email protected]' }
s.source = { :git => "https://github.com/Picovoice/picollm.git", :tag => "picoLLM-iOS-v1.0.0" }
s.source = { :git => "https://github.com/Picovoice/picollm.git", :tag => "picoLLM-iOS-v1.1.0" }
s.ios.deployment_target = '16.0'
s.swift_version = '5.0'
s.vendored_frameworks = 'lib/ios/PvPicoLLM.xcframework'
Expand Down
18 changes: 9 additions & 9 deletions demo/ios/Chat/PicoLLMChatDemo/ChatView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ struct ChatView: View {
Spacer()

HStack(alignment: .center) {
if !viewModel.enableGenerateButton {
if viewModel.isGenerating {
ProgressView(value: 0).progressViewStyle(CircularProgressViewStyle())
Text("Generating...")
.padding(.horizontal, 12)
Expand All @@ -53,20 +53,20 @@ struct ChatView: View {
.onSubmit {
viewModel.generate()
}
.disabled(isError || !viewModel.enableGenerateButton)
.disabled(isError || viewModel.isGenerating)
HStack(alignment: .center) {
Spacer()
Button(action: viewModel.generate) {
Image(systemName: "arrow.up")
Button(action: viewModel.isGenerating ? viewModel.interrupt : viewModel.generate) {
Image(systemName: viewModel.isGenerating ? "stop.fill" : "arrow.up")
.imageScale(.medium)
.background(Constants.btnColor(viewModel.enableGenerateButton && !isError))
.background(Constants.btnColor(!isError))
.foregroundColor(.white)
.padding(6)
}.background(
Capsule().fill(Constants.btnColor(viewModel.enableGenerateButton && !isError))
Capsule().fill(Constants.btnColor(!isError))
)
.padding(.horizontal, 4)
.disabled(isError || !viewModel.enableGenerateButton)
.disabled(isError)
}
}
.padding(.vertical, 12)
Expand Down Expand Up @@ -111,7 +111,7 @@ struct ChatView: View {
.imageScale(.large)
}
.padding(.horizontal, 12)
.disabled(!viewModel.enableGenerateButton)
.disabled(viewModel.isGenerating)
Spacer()
Button(action: viewModel.clearText) {
Image(systemName: "arrow.counterclockwise")
Expand All @@ -120,7 +120,7 @@ struct ChatView: View {
.padding(.horizontal, 12)
.disabled(
viewModel.errorMessage.count > 0 ||
!viewModel.enableGenerateButton ||
viewModel.isGenerating ||
viewModel.chatText.isEmpty)
}
.padding(.bottom, 12)
Expand Down
17 changes: 14 additions & 3 deletions demo/ios/Chat/PicoLLMChatDemo/ViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ You can download directly to your device or airdrop from a Mac.
@Published var picoLLMLoaded = false

@Published var promptText = ""
@Published var enableGenerateButton = true
@Published var isGenerating = false

@Published var chatText: [Message] = []

Expand Down Expand Up @@ -116,7 +116,7 @@ You can download directly to your device or airdrop from a Mac.

errorMessage = ""

enableGenerateButton = false
isGenerating = true
numTokens = 0

DispatchQueue.global(qos: .userInitiated).async { [self] in
Expand All @@ -143,7 +143,18 @@ You can download directly to your device or airdrop from a Mac.

DispatchQueue.main.async { [self] in
promptText = ""
enableGenerateButton = true
isGenerating = false
}
}
}

public func interrupt() {
do {
try picollm?.interrupt()
} catch {
DispatchQueue.main.async { [self] in
errorMessage = "\(error.localizedDescription)"
enableLoadModelButton = true
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion demo/ios/Chat/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ source 'https://cdn.cocoapods.org/'
platform :ios, '16.0'

target 'PicoLLMChatDemo' do
pod 'picoLLM-iOS', '~> 1.0.0'
pod 'picoLLM-iOS', :podspec => 'https://raw.githubusercontent.com/Picovoice/picollm/v1.1-ios/binding/ios/picoLLM-iOS.podspec'
end
16 changes: 8 additions & 8 deletions demo/ios/Chat/Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
PODS:
- picoLLM-iOS (1.0.0)
- picoLLM-iOS (1.1.0)

DEPENDENCIES:
- picoLLM-iOS (~> 1.0.0)
- picoLLM-iOS (from `https://raw.githubusercontent.com/Picovoice/picollm/v1.1-ios/binding/ios/picoLLM-iOS.podspec`)

SPEC REPOS:
trunk:
- picoLLM-iOS
EXTERNAL SOURCES:
picoLLM-iOS:
:podspec: https://raw.githubusercontent.com/Picovoice/picollm/v1.1-ios/binding/ios/picoLLM-iOS.podspec

SPEC CHECKSUMS:
picoLLM-iOS: 02cdb501b4beb74a9c1dea29d5cf461d65ea4a6c
picoLLM-iOS: dc03cd7e992c702ff34c667f9a35dd9a8084c061

PODFILE CHECKSUM: 5ceffe351e8a95d803e0000324674ffa525cbb43
PODFILE CHECKSUM: 51304d5689a6025865f0d87106b6efef0b049eb8

COCOAPODS: 1.15.2
COCOAPODS: 1.11.3
Loading
Loading