Skip to content

Commit

Permalink
Add JetpackAIServiceRemote (#804)
Browse files Browse the repository at this point in the history
  • Loading branch information
kean authored May 23, 2024
2 parents 30e9472 + e49f101 commit c064bf5
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 5 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ _None._

### New Features

_None._
- Add `JetpackAIServiceRemote`

### Bug Fixes

Expand Down
7 changes: 7 additions & 0 deletions Sources/CoreAPI/HTTPRequestBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,13 @@ final class HTTPRequestBuilder {
.append(query: urlComponents.queryItems ?? [])
}

func headers(_ headers: [String: String]) -> Self {
for (key, value) in headers {
self.headers[key] = value
}
return self
}

func header(name: String, value: String?) -> Self {
headers[name] = value
return self
Expand Down
10 changes: 6 additions & 4 deletions Sources/CoreAPI/WordPressComRestApi.swift
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ open class WordPressComRestApi: NSObject {
return "\(String(describing: oAuthToken)),\(String(describing: userAgent))".hashValue
}

private func requestBuilder(URLString: String) throws -> HTTPRequestBuilder {
func requestBuilder(URLString: String) throws -> HTTPRequestBuilder {
guard let url = URL(string: URLString, relativeTo: baseURL) else {
throw URLError(.badURL)
}
Expand Down Expand Up @@ -414,9 +414,9 @@ open class WordPressComRestApi: NSObject {
return await perform(request: builder, fulfilling: progress, decoder: decoder)
}

private func perform<T>(
func perform<T>(
request: HTTPRequestBuilder,
fulfilling progress: Progress?,
fulfilling progress: Progress? = nil,
decoder: @escaping (Data) throws -> T,
taskCreated: ((Int) -> Void)? = nil,
session: URLSession? = nil
Expand Down Expand Up @@ -449,7 +449,8 @@ open class WordPressComRestApi: NSObject {

public func upload(
URLString: String,
parameters: [String: AnyObject]?,
parameters: [String: AnyObject]? = nil,
httpHeaders: [String: String]? = nil,
fileParts: [FilePart],
requestEnqueued: RequestEnqueuedBlock? = nil,
fulfilling progress: Progress? = nil
Expand All @@ -462,6 +463,7 @@ open class WordPressComRestApi: NSObject {
builder = try requestBuilder(URLString: URLString)
.method(.post)
.body(form: form)
.headers(httpHeaders ?? [:])
} catch {
return .failure(.requestEncodingFailure(underlyingError: error))
}
Expand Down
78 changes: 78 additions & 0 deletions Sources/WordPressKit/Services/JetpackAIServiceRemote.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import Foundation

public final class JetpackAIServiceRemote: SiteServiceRemoteWordPressComREST {
/// Returns short-lived JWT token (lifetime is in minutes).
public func getAuthorizationToken() async throws -> String {
struct Response: Decodable {
let token: String
}
let path = path(forEndpoint: "sites/\(siteID)/jetpack-openai-query/jwt", withVersion: ._2_0)
let response = await wordPressComRestApi.perform(.post, URLString: path, type: Response.self)
return try response.get().body.token
}

/// - parameter token: Token retrieved using ``JetpackAIServiceRemote/getAuthorizationToken``.
public func transcribeAudio(from fileURL: URL, token: String) async throws -> String {
let path = path(forEndpoint: "jetpack-ai-transcription?feature=voice-to-content", withVersion: ._2_0)
let file = FilePart(parameterName: "audio_file", url: fileURL, fileName: "voice_recording", mimeType: "audio/m4a")
let result = await wordPressComRestApi.upload(URLString: path, httpHeaders: [
"Authorization": "Bearer \(token)"
], fileParts: [file])
guard let body = try result.get().body as? [String: Any],
let text = body["text"] as? String else {
throw URLError(.unknown)
}
return text
}

/// - parameter token: Token retrieved using ``JetpackAIServiceRemote/getAuthorizationToken``.
public func makePostContent(fromPlainText plainText: String, token: String) async throws -> String {
let path = path(forEndpoint: "jetpack-ai-query", withVersion: ._2_0)
let request = JetpackAIQueryRequest(messages: [
.init(role: "jetpack-ai", context: .init(type: "voice-to-content-simple-draft", content: plainText))
], feature: "voice-to-content", stream: false)
let builder = try wordPressComRestApi.requestBuilder(URLString: path)
.method(.post)
.headers(["Authorization": "Bearer \(token)"])
.body(json: request, jsonEncoder: JSONEncoder())
let result = await wordPressComRestApi.perform(request: builder) { data in
try JSONDecoder().decode(JetpackAIQueryResponse.self, from: data)
}
let response = try result.get().body
guard let content = response.choices.first?.message.content else {
throw URLError(.unknown)
}
return content
}
}

private struct JetpackAIQueryRequest: Encodable {
let messages: [Message]
let feature: String
let stream: Bool

struct Message: Encodable {
let role: String
let context: Context
}

struct Context: Codable {
let type: String
let content: String
}
}

private struct JetpackAIQueryResponse: Decodable {
let model: String?
let choices: [Choice]

struct Choice: Codable {
let index: Int
let message: Message
}

struct Message: Codable {
let role: String?
let content: String
}
}
4 changes: 4 additions & 0 deletions WordPressKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
0C1C08412B9CD79900E52F8C /* PostServiceRemoteExtended.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C1C08402B9CD79900E52F8C /* PostServiceRemoteExtended.swift */; };
0C1C08432B9CD8D200E52F8C /* PostServiceRemoteREST+Extended.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C1C08422B9CD8D200E52F8C /* PostServiceRemoteREST+Extended.swift */; };
0C1C08452B9CDB0B00E52F8C /* PostServiceRemoteXMLRPC+Extended.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C1C08442B9CDB0B00E52F8C /* PostServiceRemoteXMLRPC+Extended.swift */; };
0C674E302BF3A91300F3B3D4 /* JetpackAIServiceRemote.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C674E2F2BF3A91300F3B3D4 /* JetpackAIServiceRemote.swift */; };
0C9CD7992B9A107E0045BE03 /* RemotePostParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C9CD7982B9A107E0045BE03 /* RemotePostParameters.swift */; };
0CB1905E2A2A5E83004D3E80 /* BlazeCampaign.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CB1905D2A2A5E83004D3E80 /* BlazeCampaign.swift */; };
0CB190612A2A6A13004D3E80 /* blaze-campaigns-search.json in Resources */ = {isa = PBXBuildFile; fileRef = 0CB1905F2A2A6943004D3E80 /* blaze-campaigns-search.json */; };
Expand Down Expand Up @@ -773,6 +774,7 @@
0C1C08422B9CD8D200E52F8C /* PostServiceRemoteREST+Extended.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PostServiceRemoteREST+Extended.swift"; sourceTree = "<group>"; };
0C1C08442B9CDB0B00E52F8C /* PostServiceRemoteXMLRPC+Extended.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PostServiceRemoteXMLRPC+Extended.swift"; sourceTree = "<group>"; };
0C3A2A412A2E7BA500FD91D6 /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = "<group>"; };
0C674E2F2BF3A91300F3B3D4 /* JetpackAIServiceRemote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JetpackAIServiceRemote.swift; sourceTree = "<group>"; };
0C9CD7982B9A107E0045BE03 /* RemotePostParameters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemotePostParameters.swift; sourceTree = "<group>"; };
0CB1905D2A2A5E83004D3E80 /* BlazeCampaign.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlazeCampaign.swift; sourceTree = "<group>"; };
0CB1905F2A2A6943004D3E80 /* blaze-campaigns-search.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "blaze-campaigns-search.json"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1943,6 +1945,7 @@
730E869E21E44EFD00753E1A /* WordPressComServiceRemote+SiteVerticals.swift */,
73A2F38921E7F81E00388609 /* WordPressComServiceRemote+SiteVerticalsPrompt.swift */,
803DE80E28FFA787007D4E9C /* RemoteConfigRemote.swift */,
0C674E2F2BF3A91300F3B3D4 /* JetpackAIServiceRemote.swift */,
);
path = Services;
sourceTree = "<group>";
Expand Down Expand Up @@ -3414,6 +3417,7 @@
7397F01A220A072500C723F3 /* ActivityServiceRemote_ApiVersion1_0.swift in Sources */,
4A68E3D429406AA0004AC3DC /* RemoteMenuItem.swift in Sources */,
8B2F4BEF24ACCC120056C08A /* RemoteReaderCard.swift in Sources */,
0C674E302BF3A91300F3B3D4 /* JetpackAIServiceRemote.swift in Sources */,
4A11239C2B1926B7004690CF /* HTTPRequestBuilder.swift in Sources */,
40E7FEB1220FB3B60032834E /* StatsAnnualAndMostPopularTimeInsight.swift in Sources */,
3F758FD324F6C68200BBA2FC /* AnnouncementServiceRemote.swift in Sources */,
Expand Down

0 comments on commit c064bf5

Please sign in to comment.