Class: Assistant::ProductToolBuilder
- Inherits:
-
Object
- Object
- Assistant::ProductToolBuilder
- Defined in:
- app/services/assistant/product_tool_builder.rb
Overview
Builds RubyLLM::Tool subclasses for product catalog queries.
Provides 3 tools:
get_product — full rendered snapshot of a single SKU
search_products — semantic search with pricing + availability
browse_product_line — list products within a product line hierarchy
All tools are locale-aware (en-US = catalog 1, en-CA = catalog 2).
Pricing and availability flags mirror what ProductCatalogPresenter renders.
Usage (via ChatToolBuilder):
tools = Assistant::ProductToolBuilder.tools
Constant Summary collapse
- CRM_ITEM_URL =
"#{CRM_URL}/en-US/items".freeze
- WEB_PRODUCT_URL =
"https://www.warmlyyours.com".freeze
- PRODUCT_DATA_GUIDE =
Data guide injected into tool descriptions so the LLM understands
the rendering flags and pricing cascade without a separate prompt. <<~GUIDE ## Product Data Guide ### Page title cascade (what renders as the <title> tag) seo_title → public_short_name → item_name effective_page_title is pre-computed for you. ### SEO description cascade (what renders as the <meta description> tag) seo_description → short_description → item description → item_name effective_seo_description is pre-computed for you. ### Pricing cascade effective_price = sale_price (when sale_price_in_effect = true) OR price All amounts are in the currency field (USD or CAD). msrp and retail_price are informational (not always present). ### Multi-catalog pricing (catalog_pricing[]) catalog_pricing lists pricing for all main WarmlyYours catalogs the SKU is in: catalog_id 1 → en-US (warmlyyours.com, USD) catalog_id 2 → en-CA (warmlyyours.com/en-CA, CAD) catalog_id 125 → EU (European catalog, EUR) Each entry includes: price, effective_price, sale_price_in_effect, map_price, sale_price_effective_date, sale_price_expiration_date, product_stock_status, item_is_web_accessible, catalog_item_state. Use catalog_pricing to compare US vs CA pricing or check sale windows across markets. ### Availability flags — what controls whether a product is purchasable item_is_web_accessible = true → product IS listed on the website product_stock_status = "InStock" or "OutOfStock" item_condition = "new" | "refurbished" | "damaged" item_is_discontinued = true → item record permanently discontinued store_item_is_discontinued = true → store item discontinued separately hide_from_feed = true → excluded from Google Shopping / feeds ### catalog_item_state — the product lifecycle state machine Each CatalogItem has a `state` that controls visibility and workflow. item_is_web_accessible = true ONLY when ALL of: - item.is_discontinued = false - store_item.is_discontinued = false - catalog_item.state NOT IN hidden_states States and their meaning: active — publicly listed AND purchasable. The goal state. active_hidden — purchasable by direct URL but NOT in search/navigation. Used for: kit components sold as part of a kit only, recently decommissioned products still fulfilling orders, trade-only items not shown publicly. pending_onboarding — needs setup/upload in customer or retailer systems. pending_discontinue — flagged for removal; unboarding in progress (non-primary catalogs). pending_client_review — submitted to a retailer/client; awaiting their acceptance. require_vendor_update — update is required in the retailer's catalog (action needed). pending_vendor_update — update sent to retailer; awaiting propagation. discontinued — permanently discontinued. Set by background job or explicit event. Cannot be ordered. Can be reactivated via `activate` event. invalid_catalog_item — data quality issue (e.g. missing required field). Must be corrected before reactivation. hidden_states (item_is_web_accessible = false): active_hidden, discontinued, pending_client_review, invalid_catalog_item, pending_onboarding Available events and valid transitions: activate discontinued → active hide_from_public active → active_hidden unboard active → pending_discontinue discontinue most states → discontinued (main WY catalogs: 1,2,125) active → pending_discontinue (third-party catalogs) ready_for_onboarding pending_client_review | discontinued → pending_onboarding items_are_onboarded pending_onboarding → active submitted_to_client_for_review invalid | discontinued | active → pending_client_review invalidate any → invalid_catalog_item item_specs_or_price_updated active | pending_vendor_update → require_vendor_update submitted_updates_to_client active | require_vendor_update → pending_vendor_update client_completed_updates require_vendor_update | pending_vendor_update → active Common workflows: Make product public: discontinued → activate → active Hide without discontinuing: active → hide_from_public → active_hidden Start wind-down (gentle): active → unboard → pending_discontinue → discontinue → discontinued Direct discontinue (WY): active → discontinue → discontinued Retailer onboarding: active → submitted_to_client_for_review → pending_client_review → ready_for_onboarding → pending_onboarding → items_are_onboarded → active ### allow_add_to_cart true when: item_is_web_accessible = true AND restricted_to_trade = false restricted_to_trade = true for TempZone Ruler Cable heating cables (these are trade-only; show "Contact us for a quote" instead of cart) ### item_condition: refurbished refurbished_item = true means this IS a refurbished SKU (sku ends in "-BTK"). has_new_version = true means there is a newer non-refurbished counterpart. Refurbished SKUs show a discount vs original_price. ### Item vs ProductLine hierarchy product_line = the direct ProductLine the item belongs to (primary) primary_pl_path_slugs = ltree path e.g. "floor_heating.tempzone.flex_roll" Ancestors: floor_heating → tempzone → flex_roll product_category = the product category (PC) ltree path pc_path_slugs = ltree path e.g. "goods.heating_elements.heating_mats" show_in_sales_portal — product line is shown in the public sales portal available_to_public — product line is available to public ### Specifications specifications are pre-rendered, grouped by grouping (e.g. "Electrical", "Dimensions"). Only "open_visibility" specs are shown publicly. Features (grouping = "Features" / "Highlights") are listed separately as features[]. ### Variants Products in the same item_grouping_identifier are variants of each other (e.g. different sizes of the same mat). variants[] lists all SKUs with prices. ### Smart services (PC path starts with "services") These are professional installation services, not physical goods. They have terms_and_conditions and potential surcharges instead of a normal cart. ### Kit items item_is_kit = true → this SKU is a kit composed of multiple component items. ### Successor / discontinue successor_sku — the recommended replacement when this item is discontinued. ### Website images (images[]) images[] contains WarmlyYours website image profiles (image_type starts with WYS_): WYS_CARD — listing / product-card thumbnail (not on PDP carousel or feeds) WYS_MAIN — main product image (also referenced as primary_image_url) WYS_I01–I15 — additional product images (gallery) Each entry has: image_type, url (full ImageKit CDN URL), locale. primary_image_url is the URL of the item's designated primary_image directly. ### Videos (videos[]) videos[] are DigitalAsset records of type Video linked to this item. Each entry has: id, title, category (e.g. "installation", "product", "training"), cloudflare_uid (for Cloudflare Stream embed), duration_seconds, locales. Videos with cloudflare_uid are streamable; others may be external links. ### Publications (publications[]) publications[] are linked Items of the "publication" type (PDFs, manuals, datasheets, installation guides). Each entry has: id, sku, title, category, locales (available language editions), url (download URL). These represent the technical documentation attached to this product. ### Mobility translations (translations{}) Item text fields (name, seo_title, seo_description, short_description, etc.) support multiple locales via Mobility (container backend with locale_accessors). translations{} shows non-default locale values that have been entered. Default locale is 'en' (English). Fields in the default locale are returned directly as the top-level seo_title, name, etc. fields. Non-default locales appear under their locale key, e.g.: "fr-CA": { name: "...", seo_title: "..." } To EDIT a translated field, write to the locale-specific accessor, e.g.: Mobility.with_locale(:'fr-CA') { item.seo_title = "..." } Column accessors also exist (e.g. item.seo_title_fr_ca), but Mobility.with_locale is the safe, backend-agnostic approach and correctly triggers callbacks. _en suffix is the default; e.g. item.name_en is the English name. European products (EU catalog 125) may have fr, de, nl, es, it etc. locales. Always check translations{} to see what already exists before overwriting. GUIDE
Class Method Summary collapse
-
.management_tools(audit_context: {}) ⇒ Array<RubyLLM::Tool>
Write tools for product lifecycle management — require item_manager role.
-
.tools ⇒ Array<RubyLLM::Tool>
Read-only product catalog tools — available to all authenticated users.
Class Method Details
.management_tools(audit_context: {}) ⇒ Array<RubyLLM::Tool>
Write tools for product lifecycle management — require item_manager role.
197 198 199 200 201 |
# File 'app/services/assistant/product_tool_builder.rb', line 197 def management_tools(audit_context: {}) [ build_update_catalog_item_state_tool(audit_context) ] end |
.tools ⇒ Array<RubyLLM::Tool>
Read-only product catalog tools — available to all authenticated users.
184 185 186 187 188 189 190 191 192 |
# File 'app/services/assistant/product_tool_builder.rb', line 184 def tools [ build_list_product_lines_tool, build_get_product_tool, build_search_products_tool, build_browse_product_line_tool, build_get_heating_recommendation_tool ] end |