Skip to content

Commit 28cde88

Browse files
Fix issue with JSON parsing in Product AI creation (#14116)
2 parents 270dc47 + 0174cd1 commit 28cde88

File tree

4 files changed

+53
-0
lines changed

4 files changed

+53
-0
lines changed

Networking/Networking.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,7 @@
633633
93D8BBFD226BBEE800AD2EB3 /* AccountSettingsMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93D8BBFC226BBEE800AD2EB3 /* AccountSettingsMapper.swift */; };
634634
93D8BBFF226BC1DA00AD2EB3 /* me-settings.json in Resources */ = {isa = PBXBuildFile; fileRef = 93D8BBFE226BC1DA00AD2EB3 /* me-settings.json */; };
635635
93D8BC01226BC20600AD2EB3 /* AccountSettingsRemoteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93D8BC00226BC20600AD2EB3 /* AccountSettingsRemoteTests.swift */; };
636+
95122E3E2CB82C0A0079FF0A /* generate-product-success-wrapped.json in Resources */ = {isa = PBXBuildFile; fileRef = 95122E3D2CB82C0A0079FF0A /* generate-product-success-wrapped.json */; };
636637
A69FE19D2588D70E0059A96B /* order-with-deleted-refunds.json in Resources */ = {isa = PBXBuildFile; fileRef = A69FE19C2588D70E0059A96B /* order-with-deleted-refunds.json */; };
637638
AE1950F3296DB2C2004D37D2 /* ProductsBulkUpdateMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE1950F2296DB2C2004D37D2 /* ProductsBulkUpdateMapper.swift */; };
638639
AE2D6623272A8F6E004A2C3A /* TelemetryRemoteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE2D6622272A8F6E004A2C3A /* TelemetryRemoteTests.swift */; };
@@ -1786,6 +1787,7 @@
17861787
93D8BBFC226BBEE800AD2EB3 /* AccountSettingsMapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountSettingsMapper.swift; sourceTree = "<group>"; };
17871788
93D8BBFE226BC1DA00AD2EB3 /* me-settings.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "me-settings.json"; sourceTree = "<group>"; };
17881789
93D8BC00226BC20600AD2EB3 /* AccountSettingsRemoteTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountSettingsRemoteTests.swift; sourceTree = "<group>"; };
1790+
95122E3D2CB82C0A0079FF0A /* generate-product-success-wrapped.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "generate-product-success-wrapped.json"; sourceTree = "<group>"; };
17891791
9BD9C6C44CAC220B3C3B90B7 /* Pods-Networking.release-alpha.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Networking.release-alpha.xcconfig"; path = "../Pods/Target Support Files/Pods-Networking/Pods-Networking.release-alpha.xcconfig"; sourceTree = "<group>"; };
17901792
A69FE19C2588D70E0059A96B /* order-with-deleted-refunds.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "order-with-deleted-refunds.json"; sourceTree = "<group>"; };
17911793
AE1950F2296DB2C2004D37D2 /* ProductsBulkUpdateMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductsBulkUpdateMapper.swift; sourceTree = "<group>"; };
@@ -3466,6 +3468,7 @@
34663468
45B79AC52C9355F800DCCB2C /* meta-data-products-and-orders_nested_in_data.json */,
34673469
453954D12C90D84200A3E64A /* meta-data-products-and-orders.json */,
34683470
453954D52C9193BC00A3E64A /* meta-data-products-orders-update.json */,
3471+
95122E3D2CB82C0A0079FF0A /* generate-product-success-wrapped.json */,
34693472
);
34703473
path = Responses;
34713474
sourceTree = "<group>";
@@ -4582,6 +4585,7 @@
45824585
D865CE5B278CA10B002C8520 /* stripe-payment-intent-requires-payment-method.json in Resources */,
45834586
CC9A254626442CA7005DE56E /* shipping-label-eligibility-failure.json in Resources */,
45844587
DE78DE4E2B2BF0EA002E58DE /* product-subscription-alternative-types.json in Resources */,
4588+
95122E3E2CB82C0A0079FF0A /* generate-product-success-wrapped.json in Resources */,
45854589
4524CD9C242CEFAB00B2F20A /* product-on-sale-with-empty-sale-price.json in Resources */,
45864590
020220E223966CD900290165 /* product-shipping-classes-load-one.json in Resources */,
45874591
3158FE7826129DF300E566B9 /* wcpay-account-restricted.json in Resources */,

Networking/Networking/Mapper/AIProductMapper.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ struct AIProductMapper: Mapper {
88
func map(response: Data) throws -> AIProduct {
99
let decoder = JSONDecoder()
1010
let textCompletion = try decoder.decode(JetpackAIQueryResponse.self, from: response).aiResponse()
11+
// OpenAI sometimes returns the JSON response wrapped in a code block Markdown syntax, we remove it
12+
// see: https://community.openai.com/t/why-do-some-responses-message-content-start-with-json/573289
13+
.removingPrefix("```json")
14+
.removingSuffix("```")
1115
return try decoder.decode(AIProduct.self, from: Data(textCompletion.utf8))
1216
}
1317
}

Networking/NetworkingTests/Remote/GenerativeContentRemoteTests.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,28 @@ final class GenerativeContentRemoteTests: XCTestCase {
513513
XCTAssertEqual(product.price, "250")
514514
}
515515

516+
func test_generateAIProduct_with_wrapped_json_returns_AIProduct() async throws {
517+
// Given
518+
let remote = GenerativeContentRemote(network: network)
519+
network.simulateResponse(requestUrlSuffix: "sites/\(sampleSiteID)/jetpack-openai-query/jwt", filename: "jwt-token-success")
520+
network.simulateResponse(requestUrlSuffix: "jetpack-ai-query", filename: "generate-product-success-wrapped")
521+
522+
// When
523+
let product = try await remote.generateAIProduct(siteID: sampleSiteID,
524+
productName: "Cookie",
525+
keywords: "Crunchy, Crispy",
526+
language: "en",
527+
tone: "Casual",
528+
currencySymbol: "INR",
529+
dimensionUnit: "cm",
530+
weightUnit: "kg",
531+
categories: [ProductCategory.fake(), ProductCategory.fake()],
532+
tags: [ProductTag.fake(), ProductTag.fake()])
533+
534+
// Then
535+
XCTAssertNotNil(product)
536+
}
537+
516538
func test_generateAIProduct_with_failure_returns_error() async throws {
517539
// Given
518540
let remote = GenerativeContentRemote(network: network)
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"id": "chatcmpl-adfada",
3+
"object": "chat.completion",
4+
"created": 1719307068,
5+
"model": "gpt-4o-2024-05-13",
6+
"choices": [
7+
{
8+
"index": 0,
9+
"message": {
10+
"role": "assistant",
11+
"content": "```json\n{\"names\":[\"Cookie\", \"Biscuits\", \"Crunchy Cookies Delight\"], \n\"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.\"], \n\"short_descriptions\":[\"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!\"], \n\"virtual\": false, \n\"shipping\":{\"length\":\"15\", \"weight\":\"0.2\", \"width\":\"10\", \"height\":\"5\"},\n\"tags\":[\"Food\"], \n\"price\":\"250\", \n\"categories\":[\"Biscuits\"]\n}\n```"
12+
},
13+
"logprobs": null,
14+
"finish_reason": "stop"
15+
}
16+
],
17+
"usage": {
18+
"prompt_tokens": 44,
19+
"completion_tokens": 1,
20+
"total_tokens": 45
21+
},
22+
"system_fingerprint": "adefavacda"
23+
}

0 commit comments

Comments
 (0)