Skip to content

Vercel AI SDK Integration

This document provides comprehensive documentation for the Vercel AI SDK integration in the Inttrest platform, covering implementation details, configuration, and best practices.

๐Ÿค– Overview

The Vercel AI SDK is the core AI framework powering Inttrest's intelligent event discovery capabilities. It provides a seamless way to integrate AI-powered features including natural language processing, intelligent search, and conversational interfaces.

graph TB
    subgraph "AI SDK Architecture"
        A[Vercel AI SDK Core] --> B[React Hooks]
        A --> C[OpenAI Provider]
        A --> D[Stream Processing]

        B --> E[useChat Hook]
        B --> F[useCompletion Hook]
        B --> G[useAssistant Hook]

        C --> H[GPT Models]
        C --> I[Function Calling]
        C --> J[Structured Output]

        D --> K[Real-time Streaming]
        D --> L[Message Processing]
        D --> M[State Management]
    end

    subgraph "Application Integration"
        N[Chat Interface] --> E
        O[Event Search] --> F
        P[AI Assistant] --> G

        Q[Event Discovery] --> I
        R[Location Processing] --> J
        S[User Interaction] --> K
    end

    style A fill:#e3f2fd
    style N fill:#e8f5e8
    style Q fill:#fff3e0

๐Ÿ“ฆ Dependencies

The AI SDK integration uses the following key packages:

{
  "dependencies": {
    "ai": "^5.0.28",
    "@ai-sdk/openai": "^2.0.23",
    "@ai-sdk/react": "^2.0.28",
    "zod": "^4.1.5"
  }
}

Package Details

Package Version Purpose
ai ^5.0.28 Core AI SDK with utilities and providers
@ai-sdk/openai ^2.0.23 OpenAI provider for the AI SDK
@ai-sdk/react ^2.0.28 React hooks and components
zod ^4.1.5 Schema validation for structured outputs

๐Ÿ› ๏ธ Core Implementation

AI Provider Configuration

// lib/ai-config.ts
import { openai } from '@ai-sdk/openai'
import { experimental_wrapLanguageModel as wrapLanguageModel } from 'ai'

// Configure OpenAI provider
export const aiModel = wrapLanguageModel({
  model: openai('gpt-4-turbo-preview'),
  middleware: {
    transformParams: async ({ params }) => {
      return {
        ...params,
        temperature: 0.7,
        max_tokens: 1000,
        top_p: 0.9,
      }
    }
  }
})

// Event discovery specific model
export const eventDiscoveryModel = openai('gpt-4-turbo-preview', {
  structuredOutputs: true,
})

// Configuration constants
export const AI_CONFIG = {
  models: {
    chat: 'gpt-4-turbo-preview',
    completion: 'gpt-3.5-turbo',
    embedding: 'text-embedding-3-small'
  },
  limits: {
    maxTokens: 1000,
    temperature: 0.7,
    topP: 0.9
  },
  features: {
    streaming: true,
    functionCalling: true,
    structuredOutput: true
  }
} as const

Chat API Implementation

// app/api/chat/route.ts
import { streamText, convertToCoreMessages } from 'ai'
import { aiModel } from '@/lib/ai-config'
import { z } from 'zod'

// Define schemas for structured outputs
const EventSearchSchema = z.object({
  location: z.string().optional(),
  category: z.string().optional(),
  dateRange: z.object({
    start: z.string().optional(),
    end: z.string().optional()
  }).optional(),
  keywords: z.array(z.string()).optional()
})

const EventActionSchema = z.object({
  action: z.enum(['search', 'filter', 'recommend', 'details']),
  parameters: EventSearchSchema
})

export async function POST(req: Request) {
  try {
    const { messages } = await req.json()

    const result = await streamText({
      model: aiModel,
      messages: convertToCoreMessages(messages),
      system: `You are an intelligent event discovery assistant for Inttrest. 

Your role is to help users find and discover events based on their preferences. You can:
1. Search for events by location, category, date, or keywords
2. Provide personalized recommendations
3. Filter and sort events based on user criteria
4. Explain event details and help with planning

When users ask about events, try to understand their intent and provide helpful, actionable responses. 
If you need to perform an action like searching or filtering events, use the available tools.

Current capabilities:
- Search events across multiple platforms (Eventbrite, Meetup, LinkedIn, Instagram)
- Filter by location, category, date range, and other criteria
- Provide intelligent recommendations based on user preferences
- Help with event planning and discovery

Be conversational, helpful, and proactive in suggesting relevant events.`,

      tools: {
        searchEvents: {
          description: 'Search for events based on user criteria',
          parameters: EventSearchSchema,
          execute: async (params) => {
            // Implementation for event search
            return await searchEventsWithCriteria(params)
          }
        },

        filterEvents: {
          description: 'Filter existing events based on criteria',
          parameters: EventSearchSchema,
          execute: async (params) => {
            // Implementation for event filtering
            return await filterEventsWithCriteria(params)
          }
        },

        getEventRecommendations: {
          description: 'Get personalized event recommendations',
          parameters: z.object({
            userPreferences: z.array(z.string()),
            location: z.string().optional()
          }),
          execute: async (params) => {
            // Implementation for recommendations
            return await getPersonalizedRecommendations(params)
          }
        }
      },

      maxTokens: 1000,
      temperature: 0.7,
    })

    return result.toAIStreamResponse()
  } catch (error) {
    console.error('Chat API error:', error)
    return new Response('Internal Server Error', { status: 500 })
  }
}

// Helper functions for tool execution
async function searchEventsWithCriteria(params: z.infer<typeof EventSearchSchema>) {
  // Connect to MCP servers and search for events
  const searchResults = await fetch('/api/events/search', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(params)
  })

  return await searchResults.json()
}

async function filterEventsWithCriteria(params: z.infer<typeof EventSearchSchema>) {
  // Filter events from current dataset
  const filterResults = await fetch('/api/events/filter', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(params)
  })

  return await filterResults.json()
}

async function getPersonalizedRecommendations(params: any) {
  // Get AI-powered recommendations
  const recommendations = await fetch('/api/events/recommendations', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(params)
  })

  return await recommendations.json()
}

React Integration with useChat Hook

// components/Chat.tsx
'use client'

import { useChat } from 'ai/react'
import { useState, useRef, useEffect } from 'react'
import { Send, Bot, User, Loader2 } from 'lucide-react'

interface ChatProps {
  onEventAction?: (action: string, params: any) => void
}

export default function Chat({ onEventAction }: ChatProps) {
  const {
    messages,
    input,
    handleInputChange,
    handleSubmit,
    isLoading,
    error,
    append,
    reload,
    stop
  } = useChat({
    api: '/api/chat',
    onFinish: (message) => {
      // Handle completion of AI response
      handleAIResponse(message)
    },
    onError: (error) => {
      console.error('Chat error:', error)
    }
  })

  const messagesEndRef = useRef<HTMLDivElement>(null)
  const [suggestions] = useState([
    "Find tech events in San Francisco this week",
    "Show me networking events near me",
    "What business conferences are happening in New York?",
    "Find free events this weekend",
    "Recommend events based on my interests"
  ])

  const scrollToBottom = () => {
    messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' })
  }

  useEffect(() => {
    scrollToBottom()
  }, [messages])

  const handleAIResponse = (message: any) => {
    // Parse AI response for actions
    if (message.toolInvocations) {
      message.toolInvocations.forEach((invocation: any) => {
        switch (invocation.toolName) {
          case 'searchEvents':
            onEventAction?.('search', invocation.args)
            break
          case 'filterEvents':
            onEventAction?.('filter', invocation.args)
            break
          case 'getEventRecommendations':
            onEventAction?.('recommend', invocation.args)
            break
        }
      })
    }
  }

  const handleSuggestionClick = (suggestion: string) => {
    append({ role: 'user', content: suggestion })
  }

  return (
    <div className="flex flex-col h-full bg-white rounded-lg shadow-sm border">
      {/* Chat Header */}
      <div className="flex items-center justify-between p-4 border-b bg-blue-50">
        <div className="flex items-center space-x-2">
          <Bot className="w-6 h-6 text-blue-600" />
          <div>
            <h3 className="font-semibold text-gray-900">AI Event Assistant</h3>
            <p className="text-sm text-gray-600">Discover events with AI</p>
          </div>
        </div>

        {isLoading && (
          <button
            onClick={stop}
            className="p-2 text-gray-500 hover:text-red-500 transition-colors"
            title="Stop generation"
          >
            <Loader2 className="w-4 h-4 animate-spin" />
          </button>
        )}
      </div>

      {/* Messages Container */}
      <div className="flex-1 overflow-y-auto p-4 space-y-4">
        {messages.length === 0 && (
          <div className="text-center py-8">
            <Bot className="w-12 h-12 text-blue-400 mx-auto mb-4" />
            <h4 className="text-lg font-medium text-gray-900 mb-2">
              Welcome to AI Event Discovery
            </h4>
            <p className="text-gray-600 mb-6">
              Ask me anything about events! I can help you find, filter, and discover events based on your preferences.
            </p>

            {/* Suggestion Pills */}
            <div className="flex flex-wrap gap-2 justify-center">
              {suggestions.map((suggestion, index) => (
                <button
                  key={index}
                  onClick={() => handleSuggestionClick(suggestion)}
                  className="px-3 py-1 text-sm bg-blue-100 text-blue-700 rounded-full hover:bg-blue-200 transition-colors"
                >
                  {suggestion}
                </button>
              ))}
            </div>
          </div>
        )}

        {messages.map((message) => (
          <div
            key={message.id}
            className={`flex ${message.role === 'user' ? 'justify-end' : 'justify-start'}`}
          >
            <div
              className={`max-w-xs lg:max-w-md px-4 py-3 rounded-lg ${
                message.role === 'user'
                  ? 'bg-blue-600 text-white'
                  : 'bg-gray-100 text-gray-900'
              }`}
            >
              <div className="flex items-center space-x-2 mb-1">
                {message.role === 'user' ? (
                  <User className="w-4 h-4" />
                ) : (
                  <Bot className="w-4 h-4" />
                )}
                <span className="text-sm font-medium">
                  {message.role === 'user' ? 'You' : 'AI Assistant'}
                </span>
              </div>

              <div className="text-sm whitespace-pre-wrap">
                {message.content}
              </div>

              {/* Display tool invocations */}
              {message.toolInvocations && (
                <div className="mt-2 space-y-1">
                  {message.toolInvocations.map((invocation: any, index: number) => (
                    <div key={index} className="text-xs opacity-75">
                      ๐Ÿ”ง {invocation.toolName}: {JSON.stringify(invocation.args)}
                    </div>
                  ))}
                </div>
              )}
            </div>
          </div>
        ))}

        {error && (
          <div className="flex justify-center">
            <div className="bg-red-100 border border-red-300 text-red-700 px-4 py-2 rounded-lg">
              <p className="text-sm">Error: {error.message}</p>
              <button
                onClick={reload}
                className="text-red-600 hover:text-red-800 text-sm underline mt-1"
              >
                Try again
              </button>
            </div>
          </div>
        )}

        <div ref={messagesEndRef} />
      </div>

      {/* Input Form */}
      <div className="border-t p-4">
        <form onSubmit={handleSubmit} className="flex space-x-3">
          <input
            value={input}
            onChange={handleInputChange}
            placeholder="Ask about events..."
            disabled={isLoading}
            className="flex-1 border border-gray-300 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent disabled:bg-gray-100"
          />
          <button
            type="submit"
            disabled={!input.trim() || isLoading}
            className="bg-blue-600 text-white p-2 rounded-lg hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-400 disabled:cursor-not-allowed transition-colors"
          >
            {isLoading ? (
              <Loader2 className="w-5 h-5 animate-spin" />
            ) : (
              <Send className="w-5 h-5" />
            )}
          </button>
        </form>
      </div>
    </div>
  )
}

๐Ÿง  AI-Powered Features

The AI SDK enables natural language event search with intelligent parameter extraction:

// lib/ai-search.ts
import { generateObject } from 'ai'
import { eventDiscoveryModel } from './ai-config'
import { z } from 'zod'

const SearchParametersSchema = z.object({
  location: z.string().optional().describe('Geographic location for events'),
  category: z.string().optional().describe('Event category (tech, business, networking, etc.)'),
  dateRange: z.object({
    start: z.string().optional().describe('Start date in ISO format'),
    end: z.string().optional().describe('End date in ISO format')
  }).optional(),
  keywords: z.array(z.string()).optional().describe('Keywords to search for'),
  priceRange: z.object({
    min: z.number().optional(),
    max: z.number().optional()
  }).optional(),
  eventType: z.enum(['online', 'in-person', 'hybrid']).optional(),
  interests: z.array(z.string()).optional().describe('User interests and preferences')
})

export async function parseSearchQuery(query: string): Promise<z.infer<typeof SearchParametersSchema>> {
  const { object } = await generateObject({
    model: eventDiscoveryModel,
    schema: SearchParametersSchema,
    prompt: `Parse this natural language event search query and extract relevant parameters: "${query}"

    Examples:
    - "tech events in San Francisco this week" โ†’ location: "San Francisco", category: "tech", dateRange with this week
    - "free networking events near me" โ†’ category: "networking", priceRange: {max: 0}, location: user location
    - "business conferences in New York next month" โ†’ category: "business", location: "New York", dateRange: next month

    Be smart about interpreting relative dates, informal location names, and implied preferences.`
  })

  return object
}

export async function generateEventRecommendations(userProfile: any, events: any[]) {
  const RecommendationSchema = z.object({
    recommendations: z.array(z.object({
      eventId: z.string(),
      score: z.number().min(0).max(1),
      reasons: z.array(z.string()),
      category: z.string()
    })),
    insights: z.array(z.string()).describe('Insights about user preferences')
  })

  const { object } = await generateObject({
    model: eventDiscoveryModel,
    schema: RecommendationSchema,
    prompt: `Based on this user profile and available events, generate personalized recommendations:

    User Profile: ${JSON.stringify(userProfile)}
    Available Events: ${JSON.stringify(events.slice(0, 20))} // Limit for context

    Provide a score (0-1) for each recommended event and explain why it matches the user's interests.
    Consider factors like location preferences, past event attendance, interests, and career goals.`
  })

  return object
}

Structured Event Data Processing

// lib/ai-processing.ts
import { generateObject } from 'ai'
import { eventDiscoveryModel } from './ai-config'
import { z } from 'zod'

const EventEnrichmentSchema = z.object({
  enhancedDescription: z.string().describe('AI-enhanced event description'),
  tags: z.array(z.string()).describe('Relevant tags for the event'),
  targetAudience: z.array(z.string()).describe('Target audience segments'),
  skillLevel: z.enum(['beginner', 'intermediate', 'advanced', 'all']).describe('Required skill level'),
  networking: z.boolean().describe('Whether this is primarily a networking event'),
  learningOutcomes: z.array(z.string()).describe('What attendees will learn'),
  industry: z.array(z.string()).describe('Relevant industries'),
  format: z.enum(['workshop', 'conference', 'meetup', 'webinar', 'hackathon', 'panel', 'other']),
  sentiment: z.object({
    excitement: z.number().min(0).max(1),
    professionalism: z.number().min(0).max(1),
    accessibility: z.number().min(0).max(1)
  })
})

export async function enrichEventData(eventData: any) {
  const { object } = await generateObject({
    model: eventDiscoveryModel,
    schema: EventEnrichmentSchema,
    prompt: `Analyze this event and provide enriched metadata:

    Event Data: ${JSON.stringify(eventData)}

    Provide structured insights that will help users understand what this event offers,
    who should attend, and what they can expect to gain from it.`
  })

  return object
}

export async function categorizeEvent(eventData: any) {
  const CategorySchema = z.object({
    primaryCategory: z.enum([
      'technology', 'business', 'networking', 'education', 'entertainment',
      'sports', 'health', 'arts', 'food', 'travel', 'other'
    ]),
    secondaryCategories: z.array(z.string()),
    confidence: z.number().min(0).max(1),
    reasoning: z.string()
  })

  const { object } = await generateObject({
    model: eventDiscoveryModel,
    schema: CategorySchema,
    prompt: `Categorize this event based on its content and context:

    Event: ${JSON.stringify(eventData)}

    Determine the most appropriate primary category and any relevant secondary categories.
    Provide a confidence score and explain your reasoning.`
  })

  return object
}

โšก Performance Optimization

Streaming Responses

The AI SDK provides built-in streaming capabilities for better user experience:

// components/StreamingChat.tsx
import { useChat } from 'ai/react'
import { useEffect, useState } from 'react'

export function StreamingChat() {
  const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat({
    api: '/api/chat',
    streamMode: 'stream-data', // Enable streaming
    onFinish: (message, { usage, finishReason }) => {
      console.log('Completion finished:', { usage, finishReason })
    }
  })

  return (
    <div className="chat-container">
      {messages.map((message) => (
        <div key={message.id} className={`message ${message.role}`}>
          {/* Render streaming content as it arrives */}
          <div className="content">
            {message.content}
          </div>

          {/* Show typing indicator for assistant messages being streamed */}
          {message.role === 'assistant' && isLoading && (
            <div className="typing-indicator">
              <span className="dot"></span>
              <span className="dot"></span>
              <span className="dot"></span>
            </div>
          )}
        </div>
      ))}

      <form onSubmit={handleSubmit}>
        <input
          value={input}
          onChange={handleInputChange}
          placeholder="Type your message..."
          disabled={isLoading}
        />
        <button type="submit" disabled={isLoading}>
          Send
        </button>
      </form>
    </div>
  )
}

Caching and Rate Limiting

// lib/ai-cache.ts
import { LRUCache } from 'lru-cache'

interface CacheEntry {
  result: any
  timestamp: number
  ttl: number
}

class AICache {
  private cache: LRUCache<string, CacheEntry>

  constructor(maxSize = 1000, defaultTTL = 1000 * 60 * 15) { // 15 minutes
    this.cache = new LRUCache({
      max: maxSize,
      ttl: defaultTTL
    })
  }

  generateKey(input: any): string {
    return JSON.stringify(input)
  }

  get(key: string): any | null {
    const entry = this.cache.get(key)
    if (!entry) return null

    const now = Date.now()
    if (now - entry.timestamp > entry.ttl) {
      this.cache.delete(key)
      return null
    }

    return entry.result
  }

  set(key: string, result: any, ttl?: number): void {
    this.cache.set(key, {
      result,
      timestamp: Date.now(),
      ttl: ttl || 1000 * 60 * 15
    })
  }

  async getOrCompute<T>(
    key: string,
    computeFn: () => Promise<T>,
    ttl?: number
  ): Promise<T> {
    const cached = this.get(key)
    if (cached) return cached

    const result = await computeFn()
    this.set(key, result, ttl)
    return result
  }
}

export const aiCache = new AICache()

// Usage in API routes
export async function cachedGenerateObject(params: any) {
  const cacheKey = aiCache.generateKey(params)

  return await aiCache.getOrCompute(
    cacheKey,
    () => generateObject(params),
    1000 * 60 * 30 // 30 minutes for AI generation
  )
}

๐Ÿ”ง Configuration & Environment

Environment Variables

# .env.local
OPENAI_API_KEY=sk-your-openai-api-key-here
AI_SDK_LOG_LEVEL=info
AI_CACHE_TTL=900000
AI_MAX_TOKENS=1000
AI_TEMPERATURE=0.7
AI_MODEL_CHAT=gpt-4-turbo-preview
AI_MODEL_COMPLETION=gpt-3.5-turbo
AI_MODEL_EMBEDDING=text-embedding-3-small

Runtime Configuration

// lib/config.ts
export const aiConfig = {
  openai: {
    apiKey: process.env.OPENAI_API_KEY!,
    baseURL: process.env.OPENAI_BASE_URL,
    organization: process.env.OPENAI_ORG_ID,
  },

  models: {
    chat: process.env.AI_MODEL_CHAT || 'gpt-4-turbo-preview',
    completion: process.env.AI_MODEL_COMPLETION || 'gpt-3.5-turbo',
    embedding: process.env.AI_MODEL_EMBEDDING || 'text-embedding-3-small',
  },

  limits: {
    maxTokens: parseInt(process.env.AI_MAX_TOKENS || '1000'),
    temperature: parseFloat(process.env.AI_TEMPERATURE || '0.7'),
    topP: parseFloat(process.env.AI_TOP_P || '0.9'),
  },

  cache: {
    ttl: parseInt(process.env.AI_CACHE_TTL || '900000'), // 15 minutes
    maxSize: parseInt(process.env.AI_CACHE_MAX_SIZE || '1000'),
  },

  features: {
    streaming: process.env.AI_STREAMING !== 'false',
    functionCalling: process.env.AI_FUNCTION_CALLING !== 'false',
    structuredOutput: process.env.AI_STRUCTURED_OUTPUT !== 'false',
  }
} as const

// Validation
if (!aiConfig.openai.apiKey) {
  throw new Error('OPENAI_API_KEY environment variable is required')
}

๐Ÿงช Testing AI Features

Unit Testing

// __tests__/ai/search.test.ts
import { parseSearchQuery, generateEventRecommendations } from '@/lib/ai-search'
import { describe, it, expect, vi } from 'vitest'

describe('AI Search Functions', () => {
  it('should parse natural language search queries', async () => {
    const query = "tech events in San Francisco this week"
    const result = await parseSearchQuery(query)

    expect(result.location).toBe("San Francisco")
    expect(result.category).toBe("tech")
    expect(result.dateRange).toBeDefined()
  })

  it('should generate relevant event recommendations', async () => {
    const userProfile = {
      interests: ['technology', 'AI', 'startups'],
      location: 'San Francisco',
      experience: 'senior'
    }

    const events = [
      { id: '1', title: 'AI Conference 2024', category: 'tech' },
      { id: '2', title: 'Cooking Class', category: 'food' }
    ]

    const recommendations = await generateEventRecommendations(userProfile, events)

    expect(recommendations.recommendations).toHaveLength(2)
    expect(recommendations.recommendations[0].score).toBeGreaterThan(
      recommendations.recommendations[1].score
    )
  })
})

Integration Testing

// __tests__/api/chat.test.ts
import { POST } from '@/app/api/chat/route'
import { NextRequest } from 'next/server'

describe('/api/chat', () => {
  it('should handle event search requests', async () => {
    const request = new NextRequest('http://localhost:3000/api/chat', {
      method: 'POST',
      body: JSON.stringify({
        messages: [
          { role: 'user', content: 'Find tech events in San Francisco' }
        ]
      })
    })

    const response = await POST(request)

    expect(response.status).toBe(200)
    expect(response.headers.get('content-type')).toContain('text/plain')
  })
})

๐Ÿ“Š Monitoring & Analytics

Usage Tracking

// lib/ai-analytics.ts
interface AIUsageMetrics {
  requestCount: number
  tokenUsage: {
    prompt: number
    completion: number
    total: number
  }
  responseTime: number
  cacheHitRate: number
}

class AIAnalytics {
  private metrics: Map<string, AIUsageMetrics> = new Map()

  trackRequest(
    endpoint: string,
    tokenUsage: any,
    responseTime: number,
    cacheHit: boolean
  ) {
    const current = this.metrics.get(endpoint) || {
      requestCount: 0,
      tokenUsage: { prompt: 0, completion: 0, total: 0 },
      responseTime: 0,
      cacheHitRate: 0
    }

    current.requestCount++
    current.tokenUsage.prompt += tokenUsage.promptTokens || 0
    current.tokenUsage.completion += tokenUsage.completionTokens || 0
    current.tokenUsage.total += tokenUsage.totalTokens || 0
    current.responseTime = (current.responseTime + responseTime) / 2

    if (cacheHit) {
      current.cacheHitRate = (current.cacheHitRate + 1) / current.requestCount
    }

    this.metrics.set(endpoint, current)
  }

  getMetrics(endpoint?: string) {
    if (endpoint) {
      return this.metrics.get(endpoint)
    }
    return Object.fromEntries(this.metrics)
  }

  exportMetrics() {
    return {
      timestamp: new Date().toISOString(),
      metrics: this.getMetrics()
    }
  }
}

export const aiAnalytics = new AIAnalytics()

๐Ÿš€ Best Practices

1. Prompt Engineering

// lib/prompts.ts
export const SYSTEM_PROMPTS = {
  eventDiscovery: `You are an expert event discovery assistant with deep knowledge of:
- Event platforms (Eventbrite, Meetup, LinkedIn Events, etc.)
- Geographic locations and venues
- Event categories and industry trends
- User preferences and behavior patterns

Your goal is to help users find the most relevant events based on their needs.
Always be helpful, accurate, and proactive in suggesting alternatives.`,

  eventCategorization: `Analyze event content and categorize accurately.
Consider the event title, description, venue, organizer, and other metadata.
Use consistent categorization that helps users filter and discover events effectively.`,

  eventRecommendation: `Provide personalized event recommendations based on:
- User's expressed interests and preferences
- Past event attendance (if available)
- Geographic proximity and convenience
- Event quality and relevance
- Networking opportunities and career growth potential`
}

2. Error Handling

// lib/ai-error-handling.ts
export class AIError extends Error {
  constructor(
    message: string,
    public code: string,
    public originalError?: Error
  ) {
    super(message)
    this.name = 'AIError'
  }
}

export async function withErrorHandling<T>(
  operation: () => Promise<T>,
  context: string
): Promise<T> {
  try {
    return await operation()
  } catch (error) {
    if (error instanceof Error) {
      // Log error for monitoring
      console.error(`AI operation failed in ${context}:`, error)

      // Categorize error type
      if (error.message.includes('rate limit')) {
        throw new AIError(
          'AI service is temporarily unavailable due to rate limiting',
          'RATE_LIMITED',
          error
        )
      }

      if (error.message.includes('timeout')) {
        throw new AIError(
          'AI service request timed out',
          'TIMEOUT',
          error
        )
      }

      // Generic error
      throw new AIError(
        'AI service encountered an error',
        'GENERAL_ERROR',
        error
      )
    }

    throw error
  }
}

3. Performance Optimization

// lib/ai-optimization.ts
export function optimizePrompt(prompt: string): string {
  // Remove excessive whitespace
  let optimized = prompt.replace(/\s+/g, ' ').trim()

  // Truncate if too long (keep under token limits)
  const maxLength = 4000 // Approximately 1000 tokens
  if (optimized.length > maxLength) {
    optimized = optimized.substring(0, maxLength) + '...'
  }

  return optimized
}

export function batchRequests<T>(
  items: T[],
  batchSize: number = 5
): T[][] {
  const batches: T[][] = []
  for (let i = 0; i < items.length; i += batchSize) {
    batches.push(items.slice(i, i + batchSize))
  }
  return batches
}

This comprehensive documentation covers the Vercel AI SDK integration in Inttrest, providing developers with everything they need to understand, implement, and maintain the AI-powered features! ๐Ÿค–