Skip to content

Latest commit

 

History

History
397 lines (305 loc) · 9.44 KB

File metadata and controls

397 lines (305 loc) · 9.44 KB

Marketing Moment Analyzer - Technical Specification

Implementation Overview

A Vercel AI SDK tool integrated into the chatbot that analyzes X/Twitter data in real-time to determine optimal posting times for marketing content. The tool uses xAI's Grok API to query live social media signals and returns scored recommendations.


Architecture

Technology Stack

  • Framework: Vercel AI SDK 5.x with tool integration
  • API: xAI Grok API (https://api.x.ai/v1/chat/completions)
  • Model: Grok 2 (configurable via MARKETING_MOMENT_MODEL env var)
  • UI: React 18 with Framer Motion for progress animations
  • State: React Context API for progress tracking
  • Streaming: Vercel AI SDK's UIMessageStreamWriter for real-time updates

File Structure

lib/ai/tools/
  └── marketing-moment-analyzer.ts    # Core tool (379 lines)

components/
  └── marketing-progress.tsx           # Progress UI (76 lines)

hooks/
  └── use-marketing-progress.tsx      # State management (57 lines)

app/(chat)/api/chat/route.ts          # Tool registration
lib/ai/prompts.ts                     # System prompt
components/data-stream-handler.tsx    # Stream processing

Data Flow

User Question
    ↓
Grok 3 LLM detects timing intent
    ↓
Calls analyzeMarketingMoment tool
    ↓
4 parallel Grok API queries (trends, sentiment, competition, risks)
    ↓
Each query returns JSON with signals + insight
    ↓
Signals weighted and summed (base score: 50)
    ↓
Score mapped to GREEN/YELLOW/RED recommendation
    ↓
Progress streamed to UI via data-marketingProgress events
    ↓
Final response with actionable guidance

API Integration

Request Format

POST https://api.x.ai/v1/chat/completions
Headers:
  Content-Type: application/json
  Authorization: Bearer ${XAI_API_KEY}

Body:
{
  model: "grok-2",
  temperature: 0.3,
  messages: [
    { role: "system", content: "..." },
    { role: "user", content: "..." }
  ],
  search_parameters: {
    mode: "on",
    sources: [{ type: "x" }],
    return_citations: false,
    max_search_results: 10
  }
}

Query Structure

Each of 4 queries asks Grok to search X/Twitter for specific signals:

  1. Trends (6-hour window): Post volume, hashtags, influencer activity
  2. Sentiment (3-hour window): Emotions, positive/negative ratio, shifts
  3. Competition (12-hour window): Competitor posts, campaigns, share of voice
  4. Risks (24-hour window): Controversies, regulatory news, backlash

Response Parsing

Expected JSON from each query:

{
  "insight": "Posts about coffee increased 40% in last 3 hours...",
  "signals": ["TRENDING_HIGH", "INFLUENCER_SUPPORT"]
}

Scoring Algorithm

Base Score

  • Starts at 50 (neutral)

Signal Weights

Category Signal Weight
Trends TRENDING_SURGE +20
TRENDING_HIGH +15
TRENDING_STEADY +8
TRENDING_LOW -6
TRENDING_DROP -12
INFLUENCER_SUPPORT +6
INFLUENCER_PUSHBACK -8
FRESH_NEWS +10
DATA_GAP -2
Sentiment SENTIMENT_POSITIVE +15
SENTIMENT_WARM +10
SENTIMENT_NEUTRAL +4
SENTIMENT_MIXED +2
SENTIMENT_NEGATIVE -12
SENTIMENT_VOLATILE -8
DATA_GAP -2
Competition COMPETITOR_SILENT +12
COMPETITOR_LIGHT +6
COMPETITOR_ACTIVE -4
COMPETITOR_SPIKE -8
COMPETITOR_DOMINATING -12
DATA_GAP -2
Risks RISK_NONE +5
RISK_MINOR -6
RISK_SENSITIVE -12
RISK_CRISIS -20
POTENTIAL_BACKLASH -10
REGULATORY_SCRUTINY -8
RISK_UNKNOWN -3
DATA_GAP -3

Calculation

totalScore = BASE_SCORE
  + sum(trends signals)
  + sum(sentiment signals)
  + sum(competition signals)
  + sum(risks signals)

// Clamped to 0-100
finalScore = Math.max(0, Math.min(100, Math.round(totalScore)))

Recommendation Thresholds

  • 70-100: GREEN - "Post now"
  • 40-69: YELLOW - "Monitor before posting"
  • 0-39: RED - "Hold off"

Type Definitions

type MarketingMomentResponse = {
  score: number;              // 0-100
  recommendation: "GREEN" | "YELLOW" | "RED";
  summary: string;            // One-line summary
  analysis: {
    trends: { score: number; insight: string };
    sentiment: { score: number; insight: string };
    competition: { score: number; insight: string };
    risks: { score: number; insight: string };
  };
  action: string;             // Specific action guidance
  bestTimeWindow: string;     // e.g., "Next 1-2 hours"
  topic: string;              // User's topic
  analyzedAt: string;         // ISO timestamp
};

Tool Registration

Location: app/(chat)/api/chat/route.ts

const activeTools = selectedChatModel === "chat-model-reasoning"
  ? undefined
  : ["getWeather", "createDocument", "updateDocument",
     "requestSuggestions", "analyzeMarketingMoment"];

tools: {
  analyzeMarketingMoment: analyzeMarketingMoment({ dataStream }),
  // ... other tools
}

Input Schema:

{
  topic: z.string().min(1),      // Required
  context: z.string().max(400).optional()  // Optional
}

System Prompt Integration

Location: lib/ai/prompts.ts

The marketingMomentPrompt teaches the LLM when to use the tool:

Trigger patterns:

  • "Is it a good time to..."
  • "Should I launch..."
  • "Is now good for posting..."
  • "When should I announce..."

Not triggered for:

  • General marketing advice
  • Historical questions
  • Opinion requests

Real-Time Progress UI

Progress Stages

  1. starting - Initial message
  2. trends - Analyzing trends query
  3. sentiment - Analyzing sentiment query
  4. competition - Analyzing competition query
  5. risks - Analyzing risks query
  6. complete - Final result

Streaming Implementation

Tool → Stream:

dataStream.write({
  type: "data-marketingProgress",
  data: {
    stage: "trends",
    insight: "Posts increased 40%...",
    score: 15
  }
});

Stream → UI:

// data-stream-handler.tsx processes events
if (delta.type === "data-marketingProgress") {
  addStage(delta.data);
}

// marketing-progress.tsx displays animated progress
{stages.map(stage => (
  <motion.div>
    {stageConfig[stage.stage].icon} {stage.insight}
  </motion.div>
))}

Auto-dismiss

Progress UI auto-clears 3 seconds after completion (configurable via COMPLETION_DELAY_MS).


Error Handling

Scenario Behavior
Missing XAI_API_KEY Throws error: "Missing XAI_API_KEY"
API timeout Returns fallback with DATA_GAP signal. Timeouts: trends (16s), sentiment (12s), competition (10s), risks (10s)
API rate limit (429) Returns user message: "xAI API is currently overloaded"
Malformed JSON Parses raw text as insight with DATA_GAP signal
Network error Returns dimension score: 0, insight: error message
All queries fail Returns YELLOW fallback response

Graceful Degradation

Each query failure is isolated - other queries continue. If all 4 fail, returns:

{
  score: 45,
  recommendation: "YELLOW",
  summary: "Unable to query live signals. Defaulting to cautious guidance.",
  // ... fallback data
}

Environment Variables

# Required
XAI_API_KEY=xai-...

# Optional
MARKETING_MOMENT_MODEL=grok-2  # Default: grok-2

Integration Checklist

  • Tool function exported from lib/ai/tools/marketing-moment-analyzer.ts
  • Registered in route.ts active tools array
  • System prompt added to lib/ai/prompts.ts
  • Type added to lib/types.ts (MarketingMomentProgress)
  • Stream handler in data-stream-handler.tsx
  • Progress component in components/marketing-progress.tsx
  • Context provider in hooks/use-marketing-progress.tsx
  • Provider wrapped in app/(chat)/layout.tsx
  • Component rendered in components/chat.tsx

Testing

Manual Test Prompts

1. "Is it a good time to open a coffee shop in Detroit?"
2. "Should I launch my AI startup now?"
3. "Is now good for posting about electric vehicles?"
4. "We're launching sneakers in NYC tonight—safe to announce?"

Expected Behavior

  • Tool triggers automatically from natural language
  • Progress UI shows 5 stages with animated transitions
  • Final response includes score, recommendation, and specific action
  • Response time: 5-10 seconds (4 parallel API calls)

Performance

  • Queries: 4 parallel requests (not sequential)
  • Timeouts: Variable per query type (trends: 16s, sentiment: 12s, competition/risks: 10s)
  • Total time: ~16-18 seconds (limited by slowest query - trends)
  • Fallback: Graceful degradation if queries timeout

Limitations

  • Requires xAI API key with X search enabled
  • Only works with Grok 3 model (not reasoning model)
  • No caching - fresh query each time
  • No historical tracking
  • Rate limited by xAI API quotas
  • English language only (Grok limitation)

Maintenance

Modifying Signal Weights

Edit SIGNAL_WEIGHTS object in marketing-moment-analyzer.ts

Changing Thresholds

Modify the ternary in evaluateMarketingMoment:

const recommendation: Recommendation =
  totalScore >= 70 ? "GREEN" :   // Adjust 70
  totalScore >= 40 ? "YELLOW" :  // Adjust 40
  "RED";

Adding New Signals

  1. Add to SIGNAL_WEIGHTS[dimension]
  2. Add to valid signals list in QUERY_BLUEPRINTS
  3. Grok will return new signal when detected