refactor(whatsapp): make shopper conversation agent-first - #676
Conversation
Review or Edit in CodeSandboxOpen the branch in Web Editor • VS Code • Insiders |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
2 Skipped Deployments
|
|
Warning Review limit reached
Next review available in: 35 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThis PR changes WhatsApp intent handling to support bounded Gemini conversation, Redis-backed memory, explicit tool contracts, and recent product-detail retrieval with ambiguity handling and shopper-safe outputs. ChangesWhatsApp AI interaction flow
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Shopper
participant WhatsAppService
participant WhatsAppIntentService
participant GeminiClient
participant WhatsAppProductDiscoveryService
Shopper->>WhatsAppService: Send product-detail request
WhatsAppService->>WhatsAppIntentService: Parse intent with phone context
WhatsAppIntentService->>GeminiClient: Select tool or natural answer
GeminiClient-->>WhatsAppIntentService: get_product_details
WhatsAppIntentService-->>WhatsAppService: Product-detail intent
WhatsAppService->>WhatsAppProductDiscoveryService: Resolve recent product
WhatsAppProductDiscoveryService-->>WhatsAppService: Shopper-safe details or clarification
WhatsAppService-->>Shopper: Send response
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Follow-up hardening pushed in cf3b5a0. Added a shopper-safe get_product_details tool backed by revalidated recent-result context; bounded Redis natural-conversation memory (8 turns, 30-minute TTL, sanitized/untrusted); clearer ambiguous-reference handling; truthful non-retryable failure copy; and removed support/store-management routes from Gemini-visible declarations while keeping deterministic runtime handling. Updated WhatsApp contracts and added focused coverage. Validation: 215 focused tests passed, touched-file ESLint passed, backend TypeScript check passed, backend build passed, git diff checks and staged secret/private-output scans passed. |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/backend/src/channels/whatsapp/whatsapp-intent.service.ts (1)
104-282: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win"help"/"start" become dead-ends when Gemini is unavailable.
basicKeywordMatchis the deterministic fallback used both when Gemini isn't configured and when Gemini throws mid-request. The greeting branch (Lines 269-279) only matches"hi"|"hello"|"hey"|"good morning"|"good evening"exactly; "help" and "start" now match nothing and fall through tofriendly_fallback(Line 281). Previously these keywords deterministically routed to the menu, giving shoppers a recovery path during AI outages. Now, precisely when AI is down (the scenario this fallback exists for), asking for "help" gives no pointer to themenucommand.🩹 Proposed fix
if ( ["hi", "hello", "hey", "good morning", "good evening"].includes(lower) ) { return { functionName: "natural_answer", params: { answer: "Hi, I am WIZZA. Tell me what you are shopping for, your budget, or who you are buying for, and I will help you find a good match on Twizrr.", }, }; } + if (lower === "help" || lower === "start") { + return { + functionName: "natural_answer", + params: { + answer: + "Tell me what you are shopping for, your budget, or who you are buying for. You can also type menu to see options.", + }, + }; + } + return { functionName: "friendly_fallback", params: {} };🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/backend/src/channels/whatsapp/whatsapp-intent.service.ts` around lines 104 - 282, Update the exact-match greeting branch in basicKeywordMatch to also recognize “help” and “start”, routing both to the existing menu/recovery response rather than friendly_fallback. Preserve the current behavior for the existing greeting keywords and keep the deterministic fallback path unchanged otherwise.
🧹 Nitpick comments (2)
apps/backend/src/channels/whatsapp/whatsapp-conversation-memory.service.spec.ts (1)
18-18: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAvoid
anyfor the mockedRedisService.Use the double-cast idiom already used elsewhere in this PR instead of
any.♻️ Proposed fix
- service = new WhatsAppConversationMemoryService(redisService as any); + service = new WhatsAppConversationMemoryService( + redisService as unknown as RedisService, + );As per coding guidelines, "Use TypeScript strict mode and never use
any; useunknownwith type guards when necessary."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/backend/src/channels/whatsapp/whatsapp-conversation-memory.service.spec.ts` at line 18, Replace the any cast in the WhatsAppConversationMemoryService test setup with the double-cast idiom already established elsewhere in the PR, using unknown as the intermediate type when casting the mocked redisService to RedisService.Source: Coding guidelines
apps/backend/src/channels/whatsapp/whatsapp-intent.service.spec.ts (1)
20-41: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAvoid
anyfor the mockedconversationMemoryService.Line 40 uses
as anywhile the adjacent constructor args use theas unknown as Xidiom. Prefer consistency.♻️ Proposed fix
service = new WhatsAppIntentService( configService as unknown as ConfigService, geminiClient as unknown as GeminiClient, - conversationMemoryService as any, + conversationMemoryService as unknown as WhatsAppConversationMemoryService, );As per coding guidelines, "Use TypeScript strict mode and never use
any; useunknownwith type guards when necessary."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/backend/src/channels/whatsapp/whatsapp-intent.service.spec.ts` around lines 20 - 41, Replace the `as any` cast for `conversationMemoryService` in the `beforeEach` setup with the established `as unknown as ConversationMemoryService` pattern, importing or referencing the correct service type while leaving the mock behavior unchanged.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/backend/src/channels/whatsapp/whatsapp-product-discovery.service.ts`:
- Around line 748-758: Update the flow around sendProductDetails and the
returned publicProductUrl to derive the shopper URL from the revalidated product
record’s current public storeHandle and productCode, rather than
selected.productUrl from Redis. Use the canonical URL builder for the fetched
record and omit publicProductUrl when the required current values are
unavailable.
---
Outside diff comments:
In `@apps/backend/src/channels/whatsapp/whatsapp-intent.service.ts`:
- Around line 104-282: Update the exact-match greeting branch in
basicKeywordMatch to also recognize “help” and “start”, routing both to the
existing menu/recovery response rather than friendly_fallback. Preserve the
current behavior for the existing greeting keywords and keep the deterministic
fallback path unchanged otherwise.
---
Nitpick comments:
In
`@apps/backend/src/channels/whatsapp/whatsapp-conversation-memory.service.spec.ts`:
- Line 18: Replace the any cast in the WhatsAppConversationMemoryService test
setup with the double-cast idiom already established elsewhere in the PR, using
unknown as the intermediate type when casting the mocked redisService to
RedisService.
In `@apps/backend/src/channels/whatsapp/whatsapp-intent.service.spec.ts`:
- Around line 20-41: Replace the `as any` cast for `conversationMemoryService`
in the `beforeEach` setup with the established `as unknown as
ConversationMemoryService` pattern, importing or referencing the correct service
type while leaving the mock behavior unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 5ade4ea4-fe2b-4a85-91b4-a2079e63808d
📒 Files selected for processing (15)
TWIZRR_WHATSAPP_AI_CONTRACT.mdapps/backend/src/channels/whatsapp/AGENTS.mdapps/backend/src/channels/whatsapp/agent/shopper-agent-tool.registry.spec.tsapps/backend/src/channels/whatsapp/agent/shopper-agent-tool.registry.tsapps/backend/src/channels/whatsapp/agent/shopper-agent.types.tsapps/backend/src/channels/whatsapp/whatsapp-conversation-memory.service.spec.tsapps/backend/src/channels/whatsapp/whatsapp-conversation-memory.service.tsapps/backend/src/channels/whatsapp/whatsapp-intent.service.spec.tsapps/backend/src/channels/whatsapp/whatsapp-intent.service.tsapps/backend/src/channels/whatsapp/whatsapp-product-discovery.service.spec.tsapps/backend/src/channels/whatsapp/whatsapp-product-discovery.service.tsapps/backend/src/channels/whatsapp/whatsapp-shared.module.tsapps/backend/src/channels/whatsapp/whatsapp.service.spec.tsapps/backend/src/channels/whatsapp/whatsapp.service.tsapps/backend/src/integrations/ai/gemini.client.ts
What does this PR do?
Makes WIZZA conversation-first instead of menu-first. Gemini now uses automatic function selection: it may answer bounded shopping conversation or ask a clarifying question, while real Twizrr data and all actions still go through the typed shopper-agent tools and policy layer. Greetings,
help, andstartno longer force the menu;menuremains an explicit recovery command. The matching runtime contracts and regression tests are updated.Type of change
Area affected
How to test this
pnpm.cmd --filter @twizrr/backend test -- --runInBand whatsapp-intent.service.spec.ts.pnpm.cmd --filter @twizrr/backend test -- --runInBand shopper-agent-orchestrator.service.spec.ts whatsapp.service.spec.ts.hello,help, or a broad shopping question and confirm WIZZA replies naturally; sendmenuand confirm the menu still appears.Expected result: conversational turns do not force a menu or search, explicit menu requests still work, and linked reads/writes, confirmations, account linking, live commerce facts, and policy enforcement remain backend-controlled.
Pre-commit checklist
console.logleft in production code.envfiles committedanytypes addeddb push(no migration)Screenshots
N/A. Backend WhatsApp behavior and documentation only; no UI changed.
Notes for reviewer
ANYmode toAUTO; a no-tool planner result is now a valid conversational decision.git diff --check, cached diff check, and staged secret/private-prompt scan..env.local, secrets, or private prompt values were changed.Summary by CodeRabbit