Remove Uber Eats JSON-LD fast path — let Claude extract modifiers

The JSON-LD fast path only got items/categories/prices but no modifiers.
Removing it lets Uber Eats pages fall through to Claude AI extraction
which handles modifiers like every other platform.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
John Mizerek 2026-03-13 00:08:57 -07:00
parent 6cb0eefb5f
commit bace74aa23
2 changed files with 2 additions and 161 deletions

View file

@ -2190,166 +2190,7 @@
</cfif> </cfif>
<!--- ============================================================ ---> <!--- ============================================================ --->
<!--- UBER EATS FAST PATH: Parse JSON-LD structured menu data ---> <!--- Uber Eats: No fast path — JSON-LD lacks modifiers. Falls through to Claude for full extraction. --->
<!--- ============================================================ --->
<cfif findNoCase("ubereats.com", pageHtml) OR findNoCase("uber.com/store", pageHtml)>
<cfset arrayAppend(response.steps, "Uber Eats page detected - looking for JSON-LD menu data")>
<cftry>
<!--- Extract all JSON-LD blocks --->
<cfset jsonLdBlocks = reMatchNoCase('<script[^>]*type\s*=\s*["'']application/ld\+json["''][^>]*>([\s\S]*?)</script>', pageHtml)>
<cfset arrayAppend(response.steps, "Found " & arrayLen(jsonLdBlocks) & " JSON-LD blocks")>
<cfset ueRestaurant = "">
<cfloop array="#jsonLdBlocks#" index="ldBlock">
<!--- Extract content between script tags --->
<cfset ldContent = reReplaceNoCase(ldBlock, '<script[^>]*>([\s\S]*?)</script>', '\1')>
<cfset ldContent = trim(ldContent)>
<cfif len(ldContent)>
<cftry>
<!--- Unescape unicode \u002F etc --->
<cfset ldContent = replace(ldContent, '\u002F', '/', 'all')>
<cfset ldContent = replace(ldContent, '\u0026', '&', 'all')>
<cfset ldContent = replace(ldContent, '\u0022', '"', 'all')>
<cfset ldContent = replace(ldContent, '\u0027', "'", 'all')>
<cfset ldParsed = deserializeJSON(ldContent)>
<cfif isStruct(ldParsed) AND structKeyExists(ldParsed, "@type") AND ldParsed["@type"] EQ "Restaurant" AND structKeyExists(ldParsed, "hasMenu")>
<cfset ueRestaurant = ldParsed>
</cfif>
<cfcatch><!--- skip unparseable blocks ---></cfcatch>
</cftry>
</cfif>
</cfloop>
<cfif isStruct(ueRestaurant) AND structKeyExists(ueRestaurant, "hasMenu")>
<cfset arrayAppend(response.steps, "Found Restaurant JSON-LD with menu data")>
<!--- Parse business info --->
<cfset ueBusiness = structNew()>
<cfset ueBusiness["name"] = structKeyExists(ueRestaurant, "name") ? ueRestaurant.name : "">
<!--- Unescape HTML entities in name --->
<cfset ueBusiness["name"] = replace(ueBusiness.name, "&amp;", "&", "all")>
<cfset ueBusiness["name"] = replace(ueBusiness.name, "&lt;", "<", "all")>
<cfset ueBusiness["name"] = replace(ueBusiness.name, "&gt;", ">", "all")>
<cfset ueBusiness["name"] = replace(ueBusiness.name, "&##39;", "'", "all")>
<cfset ueBusiness["name"] = replace(ueBusiness.name, "&apos;", "'", "all")>
<cfif structKeyExists(ueRestaurant, "address") AND isStruct(ueRestaurant.address)>
<cfset ueAddr = ueRestaurant.address>
<cfif structKeyExists(ueAddr, "streetAddress")><cfset ueBusiness["addressLine1"] = ueAddr.streetAddress></cfif>
<cfif structKeyExists(ueAddr, "addressLocality")><cfset ueBusiness["city"] = ueAddr.addressLocality></cfif>
<cfif structKeyExists(ueAddr, "addressRegion")><cfset ueBusiness["state"] = ueAddr.addressRegion></cfif>
<cfif structKeyExists(ueAddr, "postalCode")><cfset ueBusiness["zip"] = ueAddr.postalCode></cfif>
</cfif>
<!--- Parse menu sections --->
<cfset ueMenu = ueRestaurant.hasMenu>
<cfset ueCategories = []>
<cfset ueItems = []>
<cfset ueImageMappings = structNew()>
<cfset ueItemId = 1>
<cfif structKeyExists(ueMenu, "hasMenuSection") AND isArray(ueMenu.hasMenuSection)>
<cfloop array="#ueMenu.hasMenuSection#" index="ueSection">
<cfset ueCatName = structKeyExists(ueSection, "name") ? trim(ueSection.name) : "Menu">
<!--- Unescape HTML entities --->
<cfset ueCatName = replace(ueCatName, "&amp;", "&", "all")>
<cfset ueCatName = replace(ueCatName, "&##39;", "'", "all")>
<cfset ueCatName = replace(ueCatName, "&apos;", "'", "all")>
<cfset ueSectionItemCount = 0>
<cfif structKeyExists(ueSection, "hasMenuItem") AND isArray(ueSection.hasMenuItem)>
<cfloop array="#ueSection.hasMenuItem#" index="ueMenuItem">
<cfset ueItemName = structKeyExists(ueMenuItem, "name") ? trim(ueMenuItem.name) : "">
<cfif NOT len(ueItemName)><cfcontinue></cfif>
<!--- Unescape HTML entities in name and description --->
<cfset ueItemName = replace(ueItemName, "&amp;", "&", "all")>
<cfset ueItemName = replace(ueItemName, "&##39;", "'", "all")>
<cfset ueItemName = replace(ueItemName, "&apos;", "'", "all")>
<cfset ueItemName = replace(ueItemName, "&lt;", "<", "all")>
<cfset ueItemName = replace(ueItemName, "&gt;", ">", "all")>
<cfset ueItemDesc = structKeyExists(ueMenuItem, "description") ? trim(ueMenuItem.description) : "">
<cfset ueItemDesc = replace(ueItemDesc, "&amp;", "&", "all")>
<cfset ueItemDesc = replace(ueItemDesc, "&##39;", "'", "all")>
<cfset ueItemDesc = replace(ueItemDesc, "&apos;", "'", "all")>
<!--- Extract price from offers --->
<cfset uePrice = 0>
<cfif structKeyExists(ueMenuItem, "offers") AND isStruct(ueMenuItem.offers)>
<cfif structKeyExists(ueMenuItem.offers, "price")>
<cfset uePrice = val(ueMenuItem.offers.price)>
</cfif>
</cfif>
<!--- Extract image if available --->
<cfset ueItemImage = "">
<cfif structKeyExists(ueMenuItem, "image") AND len(trim(ueMenuItem.image))>
<cfset ueItemImage = trim(ueMenuItem.image)>
<cfset ueImageMappings[ueItemName] = ueItemImage>
</cfif>
<cfset arrayAppend(ueItems, {
"id": "item_" & ueItemId,
"name": ueItemName,
"price": uePrice,
"description": ueItemDesc,
"category": ueCatName,
"modifiers": [],
"imageUrl": ueItemImage
})>
<cfset ueItemId = ueItemId + 1>
<cfset ueSectionItemCount = ueSectionItemCount + 1>
</cfloop>
</cfif>
<cfif ueSectionItemCount GT 0>
<cfset arrayAppend(ueCategories, { "name": ueCatName, "itemCount": ueSectionItemCount })>
</cfif>
</cfloop>
</cfif>
<cfset arrayAppend(response.steps, "Parsed " & arrayLen(ueItems) & " items in " & arrayLen(ueCategories) & " categories from Uber Eats JSON-LD")>
<cfif arrayLen(ueItems) GT 0>
<!--- Try to get restaurant header image from JSON-LD images --->
<cfset ueHeaderImage = "">
<cfif structKeyExists(ueRestaurant, "image") AND isArray(ueRestaurant.image) AND arrayLen(ueRestaurant.image) GT 0>
<cfset ueHeaderImage = ueRestaurant.image[1]>
</cfif>
<cfset menuData = {
"business": ueBusiness,
"categories": ueCategories,
"items": ueItems,
"modifiers": [],
"imageUrls": [],
"imageMappings": ueImageMappings,
"headerCandidateIndices": [],
"headerImage": ueHeaderImage
}>
<cfset response["OK"] = true>
<cfset response["DATA"] = menuData>
<cfset response["sourceUrl"] = len(targetUrl) ? targetUrl : "ubereats-upload">
<cfset response["parsedVia"] = "ubereats_jsonld">
<cfset response["pagesProcessed"] = 1>
<cfset response["imagesFound"] = structCount(ueImageMappings)>
<cfcontent type="application/json" reset="true">
<cfoutput>#serializeJSON(response)#</cfoutput>
<cfabort>
</cfif>
<cfelse>
<cfset arrayAppend(response.steps, "No Restaurant JSON-LD with menu found - falling through")>
</cfif>
<cfcatch>
<cfset ueError = "Uber Eats JSON-LD parsing failed: " & cfcatch.message>
<cfif len(cfcatch.detail)><cfset ueError = ueError & " | Detail: " & cfcatch.detail></cfif>
<cfset arrayAppend(response.steps, ueError & " - falling back to Claude")>
</cfcatch>
</cftry>
</cfif>
<!--- ========== END UBER EATS FAST PATH ========== --->
<!--- Look for embedded JSON data (Next.js __NEXT_DATA__, Toast state, etc.) ---> <!--- Look for embedded JSON data (Next.js __NEXT_DATA__, Toast state, etc.) --->
<cfset embeddedJsonData = ""> <cfset embeddedJsonData = "">

View file

@ -856,7 +856,7 @@
<p style="margin:0 0 2px;"><strong style="color:var(--gray-500);">Toast</strong> — full menu with modifiers &amp; images (URL or saved page)</p> <p style="margin:0 0 2px;"><strong style="color:var(--gray-500);">Toast</strong> — full menu with modifiers &amp; images (URL or saved page)</p>
<p style="margin:0 0 2px;"><strong style="color:var(--gray-500);">Grubhub</strong> — full menu with modifiers (URL only)</p> <p style="margin:0 0 2px;"><strong style="color:var(--gray-500);">Grubhub</strong> — full menu with modifiers (URL only)</p>
<p style="margin:0 0 2px;"><strong style="color:var(--gray-500);">DoorDash / order.online</strong> — items, categories, prices (URL or saved page)</p> <p style="margin:0 0 2px;"><strong style="color:var(--gray-500);">DoorDash / order.online</strong> — items, categories, prices (URL or saved page)</p>
<p style="margin:0 0 2px;"><strong style="color:var(--gray-500);">Uber Eats</strong>items, categories, prices (URL or saved page)</p> <p style="margin:0 0 2px;"><strong style="color:var(--gray-500);">Uber Eats</strong>full menu with modifiers (saved page)</p>
<p style="margin:0;"><strong style="color:var(--gray-500);">Other sites</strong> — upload saved page, images, or PDFs for AI extraction</p> <p style="margin:0;"><strong style="color:var(--gray-500);">Other sites</strong> — upload saved page, images, or PDFs for AI extraction</p>
</div> </div>