fix: previous commit redeployed

This commit is contained in:
talatops 2026-04-03 12:54:30 +05:00
parent aa871ff942
commit 9f5cd954e0
2 changed files with 9 additions and 164 deletions

View File

@ -7,16 +7,6 @@ type HistoryMessage = {
content: string content: string
} }
/** Customer is asking whether the store carries / sells a product (not policy/shipping). */
function isAskingAboutProductAvailability(query: string) {
return /\b(do you have|have any|have you got|carry|carrying|sell|selling|in stock|out of stock|stock|available|got any|do you sell|do you carry|looking for|can i (get|buy)|can you get)\b/i.test(
query.trim()
)
}
const NO_EVIDENCE_PRODUCT_REPLY =
"I do not see that item in this store’s catalog or FAQs I have access to, so I can’t confirm we carry it. If you have another product name or category from this shop, I can check again."
export async function generateStorefrontReply(params: { export async function generateStorefrontReply(params: {
storeId: string storeId: string
query: string query: string
@ -27,34 +17,6 @@ export async function generateStorefrontReply(params: {
const { storeId, query, visitorId, history } = params const { storeId, query, visitorId, history } = params
const retrieval = await getBlendedStorefrontContext({ storeId, query }) const retrieval = await getBlendedStorefrontContext({ storeId, query })
// Hard guard: do not invent inventory. If they ask “do you have X?” and we have no product row and no KB/FAQ hit, answer without the LLM.
if (
isAskingAboutProductAvailability(query) &&
retrieval.productCount === 0 &&
retrieval.kbCount === 0
) {
logger.info("[STOREFRONT_AI_REPLY]", {
storeId,
visitorId,
queryLength: query.length,
kbHits: retrieval.kbCount,
productHits: retrieval.productCount,
latencyMs: Date.now() - startedAt,
fallbackUsed: true,
groundedSkipLlm: true,
error: null,
})
return {
content: NO_EVIDENCE_PRODUCT_REPLY,
meta: {
kbHits: retrieval.kbCount,
productHits: retrieval.productCount,
fallbackUsed: true,
},
}
}
const contextLines = retrieval.items.map( const contextLines = retrieval.items.map(
(item, idx) => `[${idx + 1}] (${item.source}) ${item.title}: ${item.content}` (item, idx) => `[${idx + 1}] (${item.source}) ${item.title}: ${item.content}`
) )
@ -68,13 +30,12 @@ export async function generateStorefrontReply(params: {
})) as GroqChatMessage[] })) as GroqChatMessage[]
const system = [ const system = [
"You are the JUNO storefront assistant. You ONLY answer using the Context block below (products, FAQs, knowledge chunks).", "You are JUNO storefront assistant for an ecommerce store.",
"If the Context does not clearly support an answer, say you cannot find it in the catalog or docs you have—do NOT guess.", "Use the provided context first (products, FAQs, knowledge).",
"Never claim a product exists, is in stock, or that the store carries a category unless a Context line explicitly supports it.", "If context is insufficient, ask one concise clarifying question.",
"Never invent website layout: no tabs, menus, dropdowns, or navigation paths unless they appear verbatim in Context.", "Do not invent exact prices, stock or policy details that are not present.",
"Never invent URLs or deep links unless they appear in Context.", "Keep responses concise, friendly, and sales-supportive.",
"Do not affirm availability with “yes” unless a PRODUCT line or FAQ/KB line in Context clearly covers what the customer asked.", "Return plain text only.",
"Be concise and friendly. Plain text only.",
].join(" ") ].join(" ")
const user = [ const user = [
@ -92,13 +53,13 @@ export async function generateStorefrontReply(params: {
] ]
const out = await groqChatCompletion(messages, { const out = await groqChatCompletion(messages, {
temperature: 0.1, temperature: 0.2,
maxTokens: 420, maxTokens: 420,
timeoutMs: 28_000, timeoutMs: 28_000,
}) })
const fallback = const fallback =
"I don’t have enough in the store’s catalog or FAQ context to answer that accurately. What product name or topic from this shop should I look for?" "Thanks for your message. I do not have enough details right now. Could you share the product name or what exactly you want to know?"
const content = (out.content || "").trim() || fallback const content = (out.content || "").trim() || fallback

View File

@ -7,128 +7,12 @@ type RetrievedContextItem = {
score: number score: number
} }
/** Strip generic words so "do you have socks?" does not match random KB via "you"/"do". */
const STOPWORDS = new Set([
"the",
"a",
"an",
"and",
"or",
"but",
"in",
"on",
"at",
"to",
"for",
"of",
"as",
"by",
"with",
"from",
"is",
"are",
"was",
"were",
"be",
"been",
"being",
"have",
"has",
"had",
"do",
"does",
"did",
"will",
"would",
"could",
"should",
"may",
"might",
"must",
"can",
"this",
"that",
"these",
"those",
"i",
"me",
"my",
"you",
"your",
"it",
"its",
"we",
"our",
"they",
"them",
"their",
"what",
"which",
"who",
"whom",
"whose",
"where",
"when",
"why",
"how",
"all",
"each",
"every",
"both",
"few",
"more",
"most",
"other",
"some",
"such",
"no",
"nor",
"not",
"only",
"own",
"same",
"so",
"than",
"too",
"very",
"just",
"any",
"there",
"here",
"about",
"into",
"through",
"during",
"before",
"after",
"above",
"below",
"between",
"under",
"again",
"further",
"then",
"once",
"please",
"thanks",
"thank",
"hello",
"hi",
"hey",
"yes",
"no",
"tell",
"give",
"get",
"got",
])
function tokenize(text: string) { function tokenize(text: string) {
return text return text
.toLowerCase() .toLowerCase()
.split(/[^a-z0-9]+/g) .split(/[^a-z0-9]+/g)
.map((t) => t.trim()) .map((t) => t.trim())
.filter((t) => t.length >= 2 && !STOPWORDS.has(t)) .filter((t) => t.length >= 2)
} }
function scoreByTerms(text: string, terms: string[]) { function scoreByTerms(text: string, terms: string[]) {