Overview
The Weave Ad Format embeds AdMesh links directly into your LLM responses using an event-driven architecture. Your backend weaves recommendations into the response, and the frontend automatically detects them, adds transparency labels, and tracks engagement. What you get:- ✅ Event-driven link detection (no race conditions)
- ✅ Automatic exposure tracking when links are detected
- ✅ Transparency labels (
[Ad]added automatically) - ✅ “Why this ad?” tooltips on hover
- ✅ Fallback recommendations if no links detected
- ✅ Zero duplicate API calls
How It Works
The Weave Ad Format uses an event-driven architecture to eliminate race conditions and ensure accurate link detection:The Flow
- Backend Integration → Your backend fetches recommendations using the backend SDK (
admesh-weave-nodeoradmesh-weave-python) and passes them to your LLM - LLM Weaving → Your LLM naturally weaves AdMesh links into the response text
- Streaming Starts → Your chat component dispatches
streamingStartevent with assistant message ID - Response Streams → LLM response chunks stream to frontend (may or may not contain AdMesh links)
- Streaming Completes → Your chat component dispatches
streamingCompleteevent - Link Detection →
WeaveAdFormatContainerwaits for event, then scans for AdMesh links - Conditional Rendering:
- Links found → Adds
[Ad]labels, fires exposure tracking, shows tooltips (no fallback) - No links found → Renders fallback recommendations (tail or product format)
- Links found → Adds
Why Event-Driven?
Traditional timeout-based detection causes race conditions:- ❌ Timeout expires before streaming completes → false negative (shows fallback when links exist)
- ❌ Multiple detection cycles → duplicate API calls
- ❌ Unpredictable timing → inconsistent behavior
- ✅ Waits for streaming to complete before detecting links
- ✅ Single detection cycle per message
- ✅ Predictable, reliable behavior
- ✅ Zero duplicate API calls
Component: WeaveAdFormatContainer
TheWeaveAdFormatContainer component wraps your LLM response content and uses event-driven detection to handle AdMesh links.
Use this component if:
- ✅ You embed AdMesh links directly in LLM responses
- ✅ You want automatic link detection with event-driven timing
- ✅ You want fallback recommendations if no links present
- ✅ You want automatic tracking and transparency labels
- ❌ You want a separate recommendations panel (use Tail & Product Format instead)
Installation
Backend Integration
Your backend is responsible for fetching recommendations and passing them to your LLM. The LLM then weaves these recommendations into the response text.Step 1: Install Backend SDK
Step 2: Fetch Recommendations and Pass to LLM
UseAdMeshClient to fetch recommendations before calling your LLM:
- ✅ Backend fetches recommendations from AdMesh
- ✅ Backend passes recommendations to your LLM as context
- ✅ LLM naturally weaves them into the response as links
- ✅ Response contains AdMesh tracking links (e.g.,
http://localhost:8000/click/r/abc123...)
See the Node.js SDK documentation or Python SDK documentation for complete backend integration details.
Frontend Integration (admesh-ui-sdk)
The frontend integration has three parts:- Wrap your app with
AdMeshProvider - Wrap LLM response content with
WeaveAdFormatContainer - Dispatch streaming events from your chat component
Step 1: Wrap Your App with AdMeshProvider
Step 2: Wrap LLM Response Content with WeaveAdFormatContainer
In your message rendering component (e.g.,MessageBox.tsx):
messageId: The assistant message ID (from backend, not user message ID)query: The user’s query that prompted this responsefallbackFormat:"tail"or"product"(format for fallback recommendations)
followups_container_id: DOM element ID where follow-ups will be renderedonExecuteQuery: Callback when a follow-up is clicked (required for follow-up functionality)isContainerReady: Signal when the follow-up container is ready in DOM
Step 3: Dispatch Streaming Events from Chat Component
In your chat component (e.g.,ChatWindow.tsx), dispatch events during the streaming flow:
What Happens Automatically
Once you’ve completed the integration,WeaveAdFormatContainer automatically:
- Waits for
streamingCompleteevent (no premature detection) - Scans for AdMesh links in the LLM response
- If links found:
- Adds
[Ad]labels next to links - Fires exposure tracking pixels
- Shows “Why this ad?” tooltips on hover
- Does NOT render fallback recommendations
- Adds
- If no links found:
- Renders fallback recommendations (tail or product format)
- Makes single API call to fetch recommendations
Best Practices
✅ DO:- Dispatch
streamingStartevent when you receive assistant message ID from backend - Dispatch
streamingCompleteevent when streaming finishes - Use assistant message ID (from backend) in events, not user message ID
- Wrap each assistant message with
WeaveAdFormatContainer - Provide the user’s query in the
queryprop - Keep AdMesh links intact in your LLM response
- Let the SDK handle tracking automatically
- Use user message ID in streaming events (must use assistant message ID)
- Dispatch events before you have the assistant message ID
- Modify or remove AdMesh tracking links
- Manually fire tracking pixels
- Remove
[Ad]labels added by the SDK - Create new sessions for every message
Complete End-to-End Example
This example shows the complete event-driven flow based on the Perplexica reference implementation.Backend
Frontend - Chat Component (ChatWindow.tsx)
Frontend - Message Component (MessageBox.tsx)
Optional Follow-Up Recommendations
AdMesh can inject sponsored follow-up queries into your existing follow-up suggestions UI when usingWeaveAdFormatContainer. Follow-ups work in both scenarios: when AdMesh links are detected in the LLM response AND when fallback recommendations are displayed, as long as the fetched recommendations contain followup_query.
Setting Up Follow-Up Recommendations
If your platform already has a follow-up suggestions section (e.g., “Related Questions”, “Suggested Queries”, or similar), AdMesh can add sponsored follow-ups directly into that existing container. Step 1: Identify your existing follow-up container (or create one if you don’t have one):WeaveAdFormatContainer:
followup_query, the SDK will automatically inject the sponsored follow-up into your container using React portals. It will appear alongside your existing suggestions, seamlessly integrated into your UI, regardless of whether links were detected or fallback recommendations are shown.
The SDK automatically:
- Detects follow-up queries from recommendations (works for both link-detected and fallback scenarios)
- Renders the sponsored follow-up in your existing container
- Handles engagement tracking when users interact with follow-ups
- Calls your
onExecuteQuerycallback when a user clicks the sponsored follow-up
Complete Example
Here’s how to integrate follow-ups with WeaveAdFormatContainer:Props Reference
| Prop | Type | Required | Description |
|---|---|---|---|
followups_container_id | string | No | DOM element ID where the SDK should render follow-ups. When provided, the SDK uses portal rendering. |
onExecuteQuery | (query: string) => void | Promise<void> | No | Callback invoked when a user clicks a follow-up. Required for follow-up functionality. Typically executes the query to continue the conversation. |
onFollowupDetected | (followupQuery: string, engagementUrl: string, recommendationId: string) => void | No | Optional callback when a sponsored follow-up is detected. Use this for custom integrations if you prefer to handle rendering yourself (advanced use case). |
isContainerReady | boolean | No | Signal indicating if the follow-up container is ready in the DOM. Useful for streaming or delayed rendering scenarios. |
How It Works
-
Detection: When recommendations fetched by
WeaveAdFormatContainerinclude afollowup_query, the SDK detects it automatically. -
Rendering: When
followups_container_idis provided, the SDK injects the sponsored follow-up into your existing container using React portals. The follow-up appears alongside your existing suggestions, matching your platform’s styling. -
Click Handling: When a user clicks a follow-up:
- The SDK automatically fires engagement tracking (
followup_engagement_url) - Your
onExecuteQuerycallback is invoked with the follow-up query - You execute the query to continue the conversation (e.g., via
sendMessage())
- The SDK automatically fires engagement tracking (
Notes
- Follow-ups are displayed if recommendations include
followup_queryfrom the backend, regardless of whether links are detected or fallback is shown. - The SDK handles all engagement tracking automatically—you only need to provide
onExecuteQueryto continue the conversation. - Use
isContainerReadywhen rendering containers conditionally or after streaming completes. - Follow-ups work with recommendations fetched for link detection, not requiring separate API calls.
Troubleshooting
Fallback recommendations showing even when links exist
Fallback recommendations showing even when links exist
Cause: Events are being dispatched with user message ID instead of assistant message ID.Solution:The
messageId in events MUST match the messageId prop in WeaveAdFormatContainer.Links not being detected
Links not being detected
Check:
- Backend is successfully weaving AdMesh links into LLM response
- Links are in the format:
http://localhost:8000/click/r/...orhttps://tracking.useadmesh.com/click/... streamingCompleteevent is being dispatched after streaming finishes- Assistant message ID is being used in events (not user message ID)
Duplicate API calls
Duplicate API calls
Cause: Multiple detection cycles or timeout-based detection still running.Solution:
- Ensure you’re using the latest version of
admesh-ui-sdk(v1.0.7+) - Verify
streamingCompleteevent is dispatched only once per message - Check console logs for multiple “Setting up listener” messages
Events not being received
Events not being received
Check:
streamingStartevent is dispatched when you receive assistant message IDstreamingCompleteevent is dispatched when streaming finishes- Both events use the same
messageId(assistant message ID) - Both events use the same
sessionId - Events are dispatched BEFORE the component unmounts
[Ad] labels not appearing
[Ad] labels not appearing
Check:
- AdMesh links are present in the LLM response
- Links are being detected (check console logs)
- WeaveResponseProcessor is initialized correctly
- No CSS conflicts hiding the labels
Follow-Ups Not Showing
Follow-Ups Not Showing
If you’re using
followups_container_id but follow-ups aren’t appearing:Check:- Container element with the specified ID exists in the DOM
onExecuteQuerycallback is provided (required for follow-up functionality)- Recommendations from backend include
followup_queryfield - Container is ready before SDK tries to render (use
isContainerReadyif rendering is delayed) - Follow-ups work for both link-detected and fallback scenarios
Key Takeaways
✅ Event-Driven Architecture- Eliminates race conditions and duplicate API calls
- Waits for streaming to complete before detecting links
- Predictable, reliable behavior
- Backend: Fetch recommendations with
admesh-weave-nodeand pass to LLM - Frontend: Wrap responses with
WeaveAdFormatContainerand dispatch events
- Events MUST use assistant message ID (from backend)
- NOT user message ID (generated in frontend)
- Must match the
messageIdprop inWeaveAdFormatContainer
- Link detection happens automatically after
streamingCompleteevent - Exposure tracking fires automatically when links detected
- Fallback recommendations render automatically when no links found
- Zero manual tracking required