"""Analyzer: extracts a Sales Kit (product facts) + initial pain-fit from inputs. Product data is used primarily to derive pains that persona generation can build against. The result also carries a `scenario` prompt that frames persona creation. """ from __future__ import annotations from typing import Any from ..llm import LLMClient SALES_KIT_SYSTEM = """You are an expert ecommerce/B2B analyst. Given product information (typed in a form and/or extracted from uploaded files), produce a structured Sales Kit. Rules: - Output ONLY valid JSON with the exact keys requested. - Pain-fit: judge which pains / customer pain categories the product can PLAUSIBLY solve, and clearly distinguish "strong fit" from "partial / weak fit". - The product info is only initial grounding; personas may be reused across similar products. - If some fields are unknown, leave them as empty lists / empty strings (never invent specifics). Output schema: { "productName": string, "category": string, "valueProps": [string], "features": [string], "pricingAnchors": [string], "targetAudience": { "segment": string, "demographics": string, "useCases": [string] }, "objectionHandlers": [string], "initialPainFit": [ { "pain": string, "fit": "strong"|"partial"|"weak", "evidence": string } ], "scenarioFrame": string } The scenarioFrame is a one-paragraph description of the selling situation (who the seller, what channel, target segment) that will frame persona creation. """ class Analyzer: def __init__(self, llm: LLMClient) -> None: self.llm = llm def analyze( self, *, product: str = "", segment: str = "", description: str = "", file_text: str = "", channel: str = "facebook", ) -> dict[str, Any]: # Build the merged product context (form wins over file text) product_src = product.strip() or file_text.strip() or "" context = ( f"PRODUCT (form/typed):\n{product}\n\n" if product.strip() else "" ) if segment.strip(): context += f"INITIAL CUSTOMER SEGMENT:\n{segment}\n\n" if description.strip(): context += f"ADDITIONAL DESCRIPTION / SCENARIO:\n{description}\n\n" if file_text.strip(): context += f"UPLOADED FILE CONTENT:\n{file_text[:12000]}\n" if not context.strip(): raise ValueError("no product information provided (form or file)") user_prompt = ( f"Channel: {channel}\n\n" f"Analyze the following and return the Sales Kit JSON:\n\n{context}" ) result = self.llm.complete_json( SALES_KIT_SYSTEM, user_prompt, temperature=0.2, max_tokens=5000 ) # Normalize shape defensively result.setdefault("productName", product_src[:200] or "Untitled product") result.setdefault("category", "") result.setdefault("valueProps", []) result.setdefault("features", []) result.setdefault("pricingAnchors", []) result.setdefault("targetAudience", { "segment": segment or "", "demographics": "", "useCases": [], }) result.setdefault("objectionHandlers", []) result.setdefault("initialPainFit", []) result.setdefault("scenarioFrame", description or "") for k in ("valueProps", "features", "pricingAnchors", "objectionHandlers"): if not isinstance(result[k], list): result[k] = [] if not isinstance(result.get("initialPainFit"), list): result["initialPainFit"] = [] return result