Skip to content

Commit

Permalink
Product Creation with AI: update prompt to generate 3 options (#13258)
Browse files Browse the repository at this point in the history
  • Loading branch information
pmusolino authored Jul 8, 2024
2 parents 46be20c + 26eb2c3 commit 44d13b6
Show file tree
Hide file tree
Showing 16 changed files with 82 additions and 79 deletions.
4 changes: 2 additions & 2 deletions Fakes/Fakes/Networking.generated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ extension Networking.AIProduct {
///
public static func fake() -> Networking.AIProduct {
.init(
name: .fake(),
description: .fake(),
names: .fake(),
descriptions: .fake(),
shortDescription: .fake(),
virtual: .fake(),
shipping: .fake(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@ import Foundation

extension Networking.AIProduct {
public func copy(
name: CopiableProp<String> = .copy,
description: CopiableProp<String> = .copy,
names: CopiableProp<[String]> = .copy,
descriptions: CopiableProp<[String]> = .copy,
shortDescription: CopiableProp<String> = .copy,
virtual: CopiableProp<Bool> = .copy,
shipping: CopiableProp<AIProduct.Shipping> = .copy,
tags: CopiableProp<[String]> = .copy,
price: CopiableProp<String> = .copy,
categories: CopiableProp<[String]> = .copy
) -> Networking.AIProduct {
let name = name ?? self.name
let description = description ?? self.description
let names = names ?? self.names
let descriptions = descriptions ?? self.descriptions
let shortDescription = shortDescription ?? self.shortDescription
let virtual = virtual ?? self.virtual
let shipping = shipping ?? self.shipping
Expand All @@ -24,8 +24,8 @@ extension Networking.AIProduct {
let categories = categories ?? self.categories

return Networking.AIProduct(
name: name,
description: description,
names: names,
descriptions: descriptions,
shortDescription: shortDescription,
virtual: virtual,
shipping: shipping,
Expand Down
28 changes: 14 additions & 14 deletions Networking/Networking/Model/Product/AIProduct.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,25 +53,25 @@ public struct AIProduct: Codable, GeneratedFakeable, GeneratedCopiable, Equatabl
case height
}
}
public let name: String
public let description: String
public let names: [String]
public let descriptions: [String]
public let shortDescription: String
public let virtual: Bool
public let shipping: Shipping
public let tags: [String]
public let price: String
public let categories: [String]

public init(name: String,
description: String,
public init(names: [String],
descriptions: [String],
shortDescription: String,
virtual: Bool,
shipping: Shipping,
tags: [String],
price: String,
categories: [String]) {
self.name = name
self.description = description
self.names = names
self.descriptions = descriptions
self.shortDescription = shortDescription
self.virtual = virtual
self.shipping = shipping
Expand All @@ -83,8 +83,8 @@ public struct AIProduct: Codable, GeneratedFakeable, GeneratedCopiable, Equatabl
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)

let name = container.failsafeDecodeIfPresent(String.self, forKey: .name) ?? ""
let description = try container.decode(String.self, forKey: .description)
let names = container.failsafeDecodeIfPresent([String].self, forKey: .names) ?? []
let descriptions = try container.decode([String].self, forKey: .descriptions)
let shortDescription = container.failsafeDecodeIfPresent(String.self, forKey: .shortDescription) ?? ""
let virtual = container.failsafeDecodeIfPresent(Bool.self, forKey: .virtual) ?? false
let shipping = container.failsafeDecodeIfPresent(Shipping.self, forKey: .shipping) ?? Shipping(length: "", weight: "", width: "", height: "")
Expand All @@ -94,8 +94,8 @@ public struct AIProduct: Codable, GeneratedFakeable, GeneratedCopiable, Equatabl
alternativeTypes: [.decimal(transform: { NSDecimalNumber(decimal: $0).stringValue })]) ?? ""
let categories = container.failsafeDecodeIfPresent([String].self, forKey: .categories) ?? []

self.init(name: name,
description: description,
self.init(names: names,
descriptions: descriptions,
shortDescription: shortDescription,
virtual: virtual,
shipping: shipping,
Expand All @@ -105,8 +105,8 @@ public struct AIProduct: Codable, GeneratedFakeable, GeneratedCopiable, Equatabl
}

enum CodingKeys: String, CodingKey {
case name = "name"
case description = "description"
case names = "names"
case descriptions = "descriptions"
case shortDescription = "short_description"
case virtual = "virtual"
case shipping = "shipping"
Expand All @@ -125,7 +125,7 @@ public extension Product {
tags: [ProductTag]) {
self.init(siteID: siteID,
productID: 0,
name: aiProduct.name,
name: aiProduct.names.first ?? "",
slug: "",
permalink: "",
date: Date(),
Expand All @@ -137,7 +137,7 @@ public extension Product {
statusKey: ProductStatus.draft.rawValue,
featured: false,
catalogVisibilityKey: ProductCatalogVisibility.visible.rawValue,
fullDescription: aiProduct.description,
fullDescription: aiProduct.descriptions.first,
shortDescription: aiProduct.shortDescription,
sku: "",
price: "",
Expand Down
31 changes: 21 additions & 10 deletions Networking/Networking/Remote/GenerativeContentRemote.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public protocol GenerativeContentRemoteProtocol {
/// Generates a product using provided info
/// - Parameters:
/// - siteID: WPCOM ID of the site.
/// - productName: Product name to input to AI prompt
/// - productName: Product name to input to AI prompt (optional)
/// - keywords: Keywords describing the product to input for AI prompt
/// - language: Language to generate the product details
/// - tone: Tone of AI - Represented by `AIToneVoice`
Expand All @@ -53,7 +53,7 @@ public protocol GenerativeContentRemoteProtocol {
/// - tags: Existing tags
/// - Returns: Generated `AIProduct`
func generateAIProduct(siteID: Int64,
productName: String,
productName: String?,
keywords: String,
language: String,
tone: String,
Expand Down Expand Up @@ -107,7 +107,7 @@ public final class GenerativeContentRemote: Remote, GenerativeContentRemoteProto
}

public func generateAIProduct(siteID: Int64,
productName: String,
productName: String?,
keywords: String,
language: String,
tone: String,
Expand Down Expand Up @@ -209,7 +209,7 @@ private extension GenerativeContentRemote {
}

func generateAIProduct(siteID: Int64,
productName: String,
productName: String?,
keywords: String,
language: String,
tone: String,
Expand All @@ -219,13 +219,20 @@ private extension GenerativeContentRemote {
categories: [ProductCategory],
tags: [ProductTag],
token: JWToken) async throws -> AIProduct {
let input = [
var inputComponents = [
"You are a WooCommerce SEO and marketing expert, perform in-depth research about the product " +
"using the provided name, keywords and tone, and give your response in the below JSON format.",
"name: ```\(productName)```",
"keywords: ```\(keywords)```",
"tone: ```\(tone)```",
].joined(separator: "\n")
]

// Name will be added only if `productName` is available.
// TODO: this code related to `productName` can be removed after releasing the new product creation with AI flow. Github issue: 13108
if let productName = productName, !productName.isEmpty {
inputComponents.insert("name: ```\(productName)```", at: 1)
}

let input = inputComponents.joined(separator: "\n")

let jsonResponseFormatDict: [String: Any] = {
let tagsPrompt: String = {
Expand Down Expand Up @@ -260,8 +267,9 @@ private extension GenerativeContentRemote {
return dict
}()

return ["name": "The name of the product, written in the language with ISO code ```\(language)```",
"description": "Product description of around 100 words long in a ```\(tone)``` tone, "
// swiftlint:disable line_length
return ["names": "An array of strings, containing three different names of the product, written in the language with ISO code ```\(language)```",
"descriptions": "An array of strings, each containing three different product descriptions of around 100 words long each in a ```\(tone)``` tone, "
+ "written in the language with ISO code ```\(language)```",
"short_description": "Product's short description, written in the language with ISO code ```\(language)```",
"virtual": "A boolean value that shows whether the product is virtual or physical",
Expand All @@ -286,7 +294,8 @@ private extension GenerativeContentRemote {
ParameterKey.gptModel: ParameterValue.gptModel,
ParameterKey.responseFormat: GenerativeContentRemoteResponseFormat.json.rawValue,
ParameterKey.feature: GenerativeContentRemoteFeature.productCreation.rawValue,
ParameterKey.fields: ParameterValue.choices]
ParameterKey.fields: ParameterValue.choices,
ParameterKey.maxTokens: ParameterValue.maxTokens]
let request = DotcomRequest(wordpressApiVersion: .wpcomMark2,
method: .get,
path: Path.jetpackAIQuery,
Expand All @@ -311,6 +320,7 @@ private extension GenerativeContentRemote {
static let feature = "feature"
static let fields = "_fields"
static let stream = "stream"
static let maxTokens = "max_tokens"
static let responseFormat = "response_format"
static let gptModel = "model"
}
Expand All @@ -319,6 +329,7 @@ private extension GenerativeContentRemote {
static let choices = "choices"
static let stream = false
static let gptModel = "gpt-4o"
static let maxTokens = 4000
}

enum TokenExpiredError {
Expand Down
16 changes: 8 additions & 8 deletions Networking/NetworkingTests/Mapper/AIProductMapperTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ final class AIProductMapperTests: XCTestCase {
let product = try mapper.map(response: data)

// Then
XCTAssertEqual(product.name, "Cookie")
XCTAssertEqual(product.names, ["Cookie", "Biscuits", "Crunchy Cookies Delight"])
// swiftlint:disable line_length
XCTAssertEqual(product.description, "Introducing Cookie, the ultimate crunchy and crispy treat that will satisfy your snacking cravings. Made with the finest ingredients, these irresistible cookies are baked to perfection, delivering a delightful texture with every bite. Whether you're enjoying them with a cup of tea or sharing them with friends, Cookie is the go-to snack for any casual occasion. Indulge in the mouthwatering flavors and experience a taste sensation that will leave you wanting more. Get your hands on Cookie today and discover why it's the ultimate snack companion.")
XCTAssertEqual(product.descriptions, ["Introducing Cookie, the ultimate crunchy and crispy treat that will satisfy your snacking cravings. Made with the finest ingredients, these irresistible cookies are baked to perfection, delivering a delightful texture with every bite. Whether you're enjoying them with a cup of tea or sharing them with friends, Cookie is the go-to snack for any casual occasion. Indulge in the mouthwatering flavors and experience a taste sensation that will leave you wanting more. Get your hands on Cookie today and discover why it's the ultimate snack companion.", "Experience the ultimate crunchy delight with our premium crispy cookies. Perfectly baked to ensure every bite is packed with a satisfying crunch that will keep you coming back for more. These cookies are a great treat for any time of the day, whether you're enjoying them with a cup of coffee or as an after-meal dessert", "Our crispy cookies are crafted with the finest ingredients to deliver a superior crunch and taste. Each cookie is baked to a golden perfection, providing a uniquely satisfying munch that’s sure to please. A delicious snack that’s perfect for sharing and savoring every moment of crispy goodness."])
XCTAssertEqual(product.shortDescription, "The ultimate crunchy and crispy treat that will satisfy your snacking cravings. Made with the finest ingredients, these irresistible cookies are baked to perfection, delivering a delightful texture with every bite. Indulge in the mouthwatering flavors of Cookie today!")
// swiftlint:enable line_length
XCTAssertFalse(product.virtual)
Expand All @@ -35,9 +35,9 @@ final class AIProductMapperTests: XCTestCase {
let product = try mapper.map(response: data)

// Then
XCTAssertEqual(product.name, "Cookie")
XCTAssertEqual(product.names, ["Cookie"])
// swiftlint:disable line_length
XCTAssertEqual(product.description, "Introducing Cookie, the ultimate crunchy and crispy treat that will satisfy your snacking cravings. Made with the finest ingredients, these irresistible cookies are baked to perfection, delivering a delightful texture with every bite. Whether you're enjoying them with a cup of tea or sharing them with friends, Cookie is the go-to snack for any casual occasion. Indulge in the mouthwatering flavors and experience a taste sensation that will leave you wanting more. Get your hands on Cookie today and discover why it's the ultimate snack companion.")
XCTAssertEqual(product.descriptions, ["Introducing Cookie, the ultimate crunchy and crispy treat that will satisfy your snacking cravings. Made with the finest ingredients, these irresistible cookies are baked to perfection, delivering a delightful texture with every bite. Whether you're enjoying them with a cup of tea or sharing them with friends, Cookie is the go-to snack for any casual occasion. Indulge in the mouthwatering flavors and experience a taste sensation that will leave you wanting more. Get your hands on Cookie today and discover why it's the ultimate snack companion."])
XCTAssertEqual(product.shortDescription, "The ultimate crunchy and crispy treat that will satisfy your snacking cravings. Made with the finest ingredients, these irresistible cookies are baked to perfection, delivering a delightful texture with every bite. Indulge in the mouthwatering flavors of Cookie today!")
// swiftlint:enable line_length
XCTAssertFalse(product.virtual)
Expand All @@ -57,9 +57,9 @@ final class AIProductMapperTests: XCTestCase {
let product = try mapper.map(response: data)

// Then
XCTAssertEqual(product.name, "Cookie")
XCTAssertEqual(product.names, ["Cookie"])
// swiftlint:disable line_length
XCTAssertEqual(product.description, "Introducing Cookie, the ultimate crunchy and crispy treat that will satisfy your snacking cravings. Made with the finest ingredients, these irresistible cookies are baked to perfection, delivering a delightful texture with every bite. Whether you're enjoying them with a cup of tea or sharing them with friends, Cookie is the go-to snack for any casual occasion. Indulge in the mouthwatering flavors and experience a taste sensation that will leave you wanting more. Get your hands on Cookie today and discover why it's the ultimate snack companion.")
XCTAssertEqual(product.descriptions, ["Introducing Cookie, the ultimate crunchy and crispy treat that will satisfy your snacking cravings. Made with the finest ingredients, these irresistible cookies are baked to perfection, delivering a delightful texture with every bite. Whether you're enjoying them with a cup of tea or sharing them with friends, Cookie is the go-to snack for any casual occasion. Indulge in the mouthwatering flavors and experience a taste sensation that will leave you wanting more. Get your hands on Cookie today and discover why it's the ultimate snack companion."])
XCTAssertEqual(product.shortDescription, "The ultimate crunchy and crispy treat that will satisfy your snacking cravings. Made with the finest ingredients, these irresistible cookies are baked to perfection, delivering a delightful texture with every bite. Indulge in the mouthwatering flavors of Cookie today!")
// swiftlint:enable line_length
XCTAssertFalse(product.virtual)
Expand All @@ -79,9 +79,9 @@ final class AIProductMapperTests: XCTestCase {
let product = try mapper.map(response: data)

// Then
XCTAssertEqual(product.name, "Cookie")
XCTAssertEqual(product.names, ["Cookie"])
// swiftlint:disable line_length
XCTAssertEqual(product.description, "Introducing Cookie, the ultimate crunchy and crispy treat that will satisfy your snacking cravings. Made with the finest ingredients, these irresistible cookies are baked to perfection, delivering a delightful texture with every bite. Whether you're enjoying them with a cup of tea or sharing them with friends, Cookie is the go-to snack for any casual occasion. Indulge in the mouthwatering flavors and experience a taste sensation that will leave you wanting more. Get your hands on Cookie today and discover why it's the ultimate snack companion.")
XCTAssertEqual(product.descriptions, ["Introducing Cookie, the ultimate crunchy and crispy treat that will satisfy your snacking cravings. Made with the finest ingredients, these irresistible cookies are baked to perfection, delivering a delightful texture with every bite. Whether you're enjoying them with a cup of tea or sharing them with friends, Cookie is the go-to snack for any casual occasion. Indulge in the mouthwatering flavors and experience a taste sensation that will leave you wanting more. Get your hands on Cookie today and discover why it's the ultimate snack companion."])
XCTAssertEqual(product.shortDescription, "The ultimate crunchy and crispy treat that will satisfy your snacking cravings. Made with the finest ingredients, these irresistible cookies are baked to perfection, delivering a delightful texture with every bite. Indulge in the mouthwatering flavors of Cookie today!")
// swiftlint:enable line_length
XCTAssertFalse(product.virtual)
Expand Down
Loading

0 comments on commit 44d13b6

Please sign in to comment.