""" Search Agent — Product search in the catalog. Pipeline without LLM: 1. SEARCH: the resolved_query (in English, translated by the Router) is searched in the products FAISS index (k=12) with multilingual embeddings 2. RESPONSE: the FAISS results are returned as a structured list (the frontend shows the cards with thumbnails, the user filters visually) Total: 0 LLM calls. Uses the factory pattern: create_search_node(product_retriever) -> node function. """ from langchain_core.messages import AIMessage from utils.i18n import t def create_search_node(product_retriever): """Creates the Search Agent node with injected dependencies. Args: product_retriever: FAISS retriever of the product catalog (100k items) """ def search_node(state): if not product_retriever: return {"messages": [AIMessage(content=t("search_catalog_unavailable"))]} # Use the query resolved by the Router (with context), fallback to the last message user_query = state.get("resolved_query", state["messages"][-1].content) # --- FAISS search (resolved_query in English from the Router) --- print("--- [SEARCH AGENT] Querying catalog... ---") print(f" Query: '{user_query}'") results = product_retriever.invoke(user_query) if not results: return { "messages": [AIMessage(content=t("search_no_results"))], "products": [], } # Build structured product list products = [] for doc in results: meta = doc.metadata asin = meta.get("asin", "") title = meta.get("title", t("unknown_product")) brand = meta.get("brand", "") price = meta.get("price", "") if brand == "Generic": brand = "" if price == "N/A": price = "" products.append({ "asin": asin, "title": title, "brand": brand, "price": price, "similarity": None, }) print(f" -> {title[:60]}") msg = t("search_results") return { "messages": [AIMessage(content=msg)], "products": products, } return search_node