Skip to main content

Overview

SkyCast IA includes an interactive chat interface powered by Groq’s LLaMA 3.1 model. Users can ask weather-related questions and receive personalized, context-aware recommendations.

WeatherChat Component

The WeatherChat component provides a floating chat button and modal interface:
import WeatherChat from '@/components/ui/WeatherChat';

<WeatherChat
  city="New York"
  temp={22}
  weatherType="clear"
  humidity={65}
  feels_like={24}
  wind_speed={12}
  description="clear sky"
  pressure={1013}
  pop={0.1}
/>

Props Reference

city
string
required
Current city name for context
temp
number
required
Current temperature in Celsius
weatherType
string
required
Weather condition type: snow, hot, rain, thunder, clear, clouds, default
humidity
number
required
Relative humidity percentage (0-100)
feels_like
number
required
Thermal sensation temperature in Celsius
wind_speed
number
required
Wind speed in km/h
description
string
required
Weather description text (e.g., “partly cloudy”)
pressure
number
Atmospheric pressure in hPa
pop
number
Probability of precipitation (0-1)

Security with reCAPTCHA

All chat interactions are protected with Google reCAPTCHA v2 to prevent abuse:

Client-Side Integration

import ReCAPTCHA from 'react-google-recaptcha';

const [captchaToken, setCaptchaToken] = useState<string | null>(null);

<ReCAPTCHA
  sitekey={process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY}
  onChange={(token) => setCaptchaToken(token)}
  theme={isSnow ? "light" : "dark"}
/>

Server-Side Verification

The /api/chat endpoint verifies CAPTCHA tokens before processing:
// Only verify on first message
if (messages.length <= 1 && captchaToken) {
  const verifyRes = await fetch(
    `https://www.google.com/recaptcha/api/siteverify?secret=${recaptchaSecret}&response=${captchaToken}`,
    { method: "POST" }
  );
  
  const verifyData = await verifyRes.json();
  if (!verifyData.success) {
    return NextResponse.json(
      { answer: "Error de seguridad. Reintenta." },
      { status: 403 }
    );
  }
}
CAPTCHA verification only runs on the first message to avoid duplicate verification errors on subsequent messages

AI System Prompt

The chat uses a carefully crafted system prompt for accurate, helpful responses:
const systemPrompt = {
  role: "system",
  content: `Eres SkyCast IA en ${context.city}. Tu estilo es directo, sincero y ligeramente sarcástico. No endulces el clima.
  
  DATOS REALES:
  - Temperatura: ${context.temp}°C (Sensación: ${context.feels_like}°C)
  - Humedad: ${context.humidity}%
  - Viento: ${context.wind_speed} km/h
  - Presión: ${context.pressure}
  - Probabilidad de lluvia: ${context.pop}
  - Estado: ${context.description}

  REGLAS CRÍTICAS:
  1. Usa los datos técnicos (especialmente presión y probabilidad de lluvia) para dar consejos reales.
  2. Sé breve (máximo 25 palabras).
  3. Tono: Divertido pero sincero. Si el clima es malo, dilo sin vueltas.
  4. EMOJIS: Máximo 2 por respuesta. No más.
  5. Si preguntan qué ponerse, usa la sensación térmica de ${context.feels_like}°C.`
};

AI Response Characteristics

  • Direct: No unnecessary fluff or pleasantries
  • Honest: Tells it like it is (“It’s cold, bundle up”)
  • Slightly sarcastic: Playful tone without being rude
  • Context-aware: Uses real weather data in responses
  • Brief: Maximum 25 words per response
  • Uses thermal sensation (feels_like) for clothing advice
  • References precipitation probability for rain warnings
  • Considers wind speed for outdoor activity recommendations
  • Maximum 2 emojis per message
  • Responds in Spanish by default

Example Conversations

Clothing Advice

User: ¿Qué me pongo hoy? SkyCast IA: Con 24°C de sensación térmica, ropa ligera. Lleva gafas de sol. ☀️

Rain Warning

User: ¿Necesito paraguas? SkyCast IA: 80% probabilidad de lluvia. Sí, llévalo o te mojas. ☔

Activity Recommendation

User: ¿Está bien para correr? SkyCast IA: Perfecto. 18°C, viento suave (8km/h). Hora de moverte. 🏃

Dynamic Theming

The chat interface adapts its colors based on weather:
const theme = {
  header: isHot ? "bg-[#FF5733]" : isSnow ? "bg-[#3498DB]" : "bg-[#000000]",
  container: isSnow
    ? "bg-white/95 border-2 border-[#3498DB] text-slate-900"
    : "bg-[#1A1A1A]/95 border-2 border-white/20 text-white",
  input: isSnow
    ? "bg-white border-2 border-[#3498DB] text-slate-900"
    : "bg-[#2D2D2D] border-2 border-white/10 text-white",
  userMsg: "bg-[#E74C3C] text-white",
  aiMsg: isSnow ? "bg-[#ECF0F1] text-slate-800" : "bg-[#3D3D3D] text-white",
  button: "bg-[#2ECC71]"
};

Message Flow

1

User Opens Chat

User clicks the floating chat button. CAPTCHA challenge appears.
2

CAPTCHA Verification

User completes reCAPTCHA v2 challenge. Token is stored in state.
3

User Sends Message

User types question and presses Enter or clicks Send button.
4

API Request

Client sends POST request to /api/chat with message, context, and CAPTCHA token.
5

Server Processing

Server verifies CAPTCHA (first message only), builds system prompt with weather context, calls Groq API.
6

AI Response

LLaMA 3.1 generates response based on weather data and user question.
7

Display Response

Response appears in chat with proper formatting and auto-scroll to bottom.

API Configuration

Model Parameters

{
  model: "llama-3.1-8b-instant",
  messages: [systemPrompt, ...messages],
  max_tokens: 150,
  temperature: 0.7  // Balanced creativity and consistency
}

Environment Variables

GROQ_API_KEY=your_groq_api_key_here
NEXT_PUBLIC_RECAPTCHA_SITE_KEY=your_recaptcha_site_key
RECAPTCHA_SECRET_KEY=your_recaptcha_secret_key
Get your Groq API key from console.groq.com and reCAPTCHA keys from Google reCAPTCHA Admin

Character Limits

  • User input: 300 characters maximum
  • AI response: 150 tokens maximum (≈25 words)
  • Message history: Unlimited (sent with each request)

Error Handling

try {
  const response = await fetch('/api/chat', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ messages, captchaToken, context })
  });
  
  if (!response.ok) throw new Error(data.answer);
  
  setMessages(prev => [
    ...prev,
    { role: 'assistant', content: data.answer }
  ]);
} catch (error) {
  setMessages(prev => [
    ...prev,
    { role: 'assistant', content: 'Error de conexión. 📡' }
  ]);
}

Accessibility Features

  • Keyboard navigation (Enter to send)
  • Focus management
  • ARIA labels on interactive elements
  • High contrast message bubbles
  • Screen reader friendly

Build docs developers (and LLMs) love