> ## Documentation Index
> Fetch the complete documentation index at: https://aitutorial.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Structured Prompt Engineering

> Design structured prompts that often reduce hallucinations

export const QuizQuestion = ({question, options, answer, explanation}) => {
  const [selected, setSelected] = useState(null);
  const [revealed, setRevealed] = useState(false);
  const handleSelect = index => {
    if (revealed) return;
    setSelected(index);
    setRevealed(true);
  };
  const isCorrect = selected === answer;
  const getOptionClass = i => {
    const classes = ['quiz-option'];
    if (revealed) {
      classes.push('quiz-option-disabled');
      if (i === answer) classes.push('quiz-option-correct'); else if (i === selected && !isCorrect) classes.push('quiz-option-wrong');
    }
    return classes.join(' ');
  };
  return <div className="quiz-card">
      <p className="quiz-question">{question}</p>
      <div className="quiz-options">
        {options.map((option, i) => <button key={i} onClick={() => handleSelect(i)} className={getOptionClass(i)}>
            <span className="quiz-letter">{String.fromCharCode(65 + i)}</span>
            {option}
          </button>)}
      </div>
      {revealed && <div className={`quiz-feedback ${isCorrect ? 'quiz-feedback-correct' : 'quiz-feedback-wrong'}`}>
          <strong>{isCorrect ? 'Correct!' : 'Incorrect.'}</strong> {explanation}
        </div>}
    </div>;
};

export const Quiz = ({title = "Check Your Understanding", children}) => {
  return <div style={{
    marginTop: '24px'
  }}>
      <div className="quiz-title">{title}</div>
      {children}
    </div>;
};

export const LLMPlayground = ({defaultInput = '', defaultModel = 'gpt-4o-mini', defaultTemperature = 0.7, height = '600px', keepInput = false, defaultMode = 'chat', defaultMessages = [], response = '', forceSettingsOpen = false, title, theme: userTheme}) => {
  const {useState, useEffect, useMemo, useCallback, useRef} = React;
  const OPENAI_STORAGE_KEY = 'openai_api_key';
  const GEMINI_STORAGE_KEY = 'gemini_api_key';
  const ANTHROPIC_STORAGE_KEY = 'anthropic_api_key';
  const PROVIDER_STORAGE_KEY = 'llm_playground_provider';
  const SETTINGS_PANEL_KEY = 'llm_playground_settings_open';
  const SETTINGS_PANEL_WIDTH = 220;
  const OPENAI_API_URL = 'https://api.openai.com/v1/chat/completions';
  const GEMINI_API_URL = 'https://generativelanguage.googleapis.com/v1beta/openai/chat/completions';
  const ANTHROPIC_API_URL = 'https://api.anthropic.com/v1/messages';
  const MAX_TOKENS = 2000;
  const OPENAI_MODELS = [{
    value: 'gpt-4.1-nano',
    label: 'GPT-4.1 Nano'
  }, {
    value: 'gpt-4.1-mini',
    label: 'GPT-4.1 Mini'
  }, {
    value: 'gpt-4.1',
    label: 'GPT-4.1'
  }, {
    value: 'o4-mini',
    label: 'o4 Mini'
  }, {
    value: 'o3',
    label: 'o3'
  }];
  const GEMINI_MODELS = [{
    value: 'gemini-2.5-flash-lite',
    label: 'Gemini 2.5 Flash Lite'
  }, {
    value: 'gemini-2.5-flash',
    label: 'Gemini 2.5 Flash'
  }, {
    value: 'gemini-2.0-flash',
    label: 'Gemini 2.0 Flash'
  }, {
    value: 'gemini-2.5-pro',
    label: 'Gemini 2.5 Pro'
  }];
  const ANTHROPIC_MODELS = [{
    value: 'claude-haiku-4-5-20251001',
    label: 'Claude 4.5 Haiku'
  }, {
    value: 'claude-sonnet-4-6-20260326',
    label: 'Claude 4.6 Sonnet'
  }, {
    value: 'claude-opus-4-6-20260326',
    label: 'Claude 4.6 Opus'
  }];
  const PROVIDERS = {
    gemini: {
      label: 'Gemini',
      models: GEMINI_MODELS,
      storageKey: GEMINI_STORAGE_KEY,
      apiUrl: GEMINI_API_URL
    },
    openai: {
      label: 'OpenAI',
      models: OPENAI_MODELS,
      storageKey: OPENAI_STORAGE_KEY,
      apiUrl: OPENAI_API_URL
    },
    anthropic: {
      label: 'Claude',
      models: ANTHROPIC_MODELS,
      storageKey: ANTHROPIC_STORAGE_KEY,
      apiUrl: ANTHROPIC_API_URL
    }
  };
  const IconWarningSun = ({size = 14, className = ''}) => <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" className={className}>
      <path d="M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" />
    </svg>;
  const IconLoadingSpinner = ({size = 14, className = '', strokeColor = 'currentColor', strokeWidth = '2', strokeLinecap = 'butt'}) => <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke={strokeColor} strokeWidth={strokeWidth} strokeLinecap={strokeLinecap} className={className}>
      <path d="M21 12a9 9 0 11-6.219-8.56"></path>
    </svg>;
  const IconWarningTriangle = ({size = 16, strokeColor = '#f59e0b', className = ''}) => <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke={strokeColor} strokeWidth="2" className={className}>
      <path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path>
      <line x1="12" y1="9" x2="12" y2="13"></line>
      <line x1="12" y1="17" x2="12.01" y2="17"></line>
    </svg>;
  const IconClose = ({size = 10, className = ''}) => <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" className={className}>
      <line x1="18" y1="6" x2="6" y2="18"></line>
      <line x1="6" y1="6" x2="18" y2="18"></line>
    </svg>;
  const IconPlus = ({size = 12, className = ''}) => <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" className={className}>
      <line x1="12" y1="5" x2="12" y2="19"></line>
      <line x1="5" y1="12" x2="19" y2="12"></line>
    </svg>;
  const IconError = ({size = 14, className = ''}) => <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" className={className}>
      <circle cx="12" cy="12" r="10"></circle>
      <line x1="12" y1="8" x2="12" y2="12"></line>
      <line x1="12" y1="16" x2="12.01" y2="16"></line>
    </svg>;
  const IconTrash = ({size = 14, className = ''}) => <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}>
      <polyline points="3 6 5 6 21 6"></polyline>
      <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
      <line x1="10" y1="11" x2="10" y2="17"></line>
      <line x1="14" y1="11" x2="14" y2="17"></line>
    </svg>;
  const IconSend = ({size = 16, fillColor = '#000', className = ''}) => <svg width={size} height={size} viewBox="0 0 24 24" fill={fillColor} className={className}>
      <path d="M1.101 21.757L23.8 12.028 1.101 2.3l.011 7.912 13.623 1.816-13.623 1.817-.011 7.912z" />
    </svg>;
  const IconCheckmark = ({size = 14, strokeColor = '#22c55e', className = ''}) => <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke={strokeColor} strokeWidth="2" className={className}>
      <polyline points="9 11 12 14 22 4"></polyline>
      <path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"></path>
    </svg>;
  const IconXStatus = ({size = 14, strokeColor = '#ef4444', className = ''}) => <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke={strokeColor} strokeWidth="2" className={className}>
      <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
      <line x1="9" y1="9" x2="15" y2="15"></line>
      <line x1="15" y1="9" x2="9" y2="15"></line>
    </svg>;
  const IconChevron = ({size = 12, isOpen = false, className = ''}) => <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}>
      {isOpen ? <polyline points="18 15 12 9 6 15"></polyline> : <polyline points="6 9 12 15 18 9"></polyline>}
    </svg>;
  const IconInfo = ({size = 12, className = ''}) => <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}>
      <circle cx="12" cy="12" r="10"></circle>
      <line x1="12" y1="16" x2="12" y2="12"></line>
      <line x1="12" y1="8" x2="12.01" y2="8"></line>
    </svg>;
  const isAdvancedMode = defaultMode === 'advanced' || defaultMessages && defaultMessages.length > 0;
  const mode = isAdvancedMode ? 'advanced' : 'chat';
  const [input, setInput] = useState(defaultInput);
  const [output, setOutput] = useState('');
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState('');
  const [provider, setProvider] = useState(() => {
    if (typeof window === 'undefined') return 'gemini';
    return localStorage.getItem(PROVIDER_STORAGE_KEY) || 'gemini';
  });
  const [model, setModel] = useState(() => {
    const initialProvider = typeof window !== 'undefined' && localStorage.getItem(PROVIDER_STORAGE_KEY) || 'gemini';
    return initialProvider === 'gemini' ? 'gemini-2.5-flash-lite' : defaultModel;
  });
  const [temperature, setTemperature] = useState(defaultTemperature);
  const [topP, setTopP] = useState(1.0);
  const [apiKey, setApiKey] = useState('');
  const [responseCount, setResponseCount] = useState(0);
  const responseCountRef = useRef(0);
  const [apiKeyInput, setApiKeyInput] = useState('');
  const [apiKeyProvider, setApiKeyProvider] = useState('gemini');
  const previousProviderRef = useRef(null);
  const [isSavingKey, setIsSavingKey] = useState(false);
  const textareaRef = useRef(null);
  const advancedTextareaRefs = useRef({});
  const responseAreaRef = useRef(null);
  const [messages, setMessages] = useState(() => {
    if (defaultMessages && defaultMessages.length > 0) {
      return defaultMessages;
    }
    return [{
      role: 'system',
      content: ''
    }, {
      role: 'user',
      content: ''
    }];
  });
  const [conversationHistory, setConversationHistory] = useState([]);
  const messagesEndRef = useRef(null);
  const isInitialMount = useRef(true);
  const [lastSentJson, setLastSentJson] = useState(null);
  const [lastResponseJson, setLastResponseJson] = useState(null);
  const [hasSubmitted, setHasSubmitted] = useState(false);
  const [isSettingsOpen, setIsSettingsOpen] = useState(() => {
    if (forceSettingsOpen) return true;
    if (typeof window === 'undefined') return true;
    const stored = localStorage.getItem(SETTINGS_PANEL_KEY);
    return stored !== null ? stored === 'true' : true;
  });
  const [isApiCallsOpen, setIsApiCallsOpen] = useState(false);
  const [apiCallTab, setApiCallTab] = useState('request');
  const [isMaximized, setIsMaximized] = useState(false);
  const [isCollapsed, setIsCollapsed] = useState(false);
  const toggleMaximize = () => setIsMaximized(!isMaximized);
  const toggleCollapse = () => setIsCollapsed(!isCollapsed);
  const headerTitle = title || (defaultMode === 'advanced' ? 'Advanced Playground' : 'LLM Playground');
  const [detectedTheme, setDetectedTheme] = useState('dark');
  useEffect(() => {
    if (typeof window === 'undefined') return;
    const checkTheme = () => {
      const isDark = document.documentElement.classList.contains('dark');
      setDetectedTheme(isDark ? 'dark' : 'light');
    };
    checkTheme();
    const observer = new MutationObserver(checkTheme);
    observer.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ['class']
    });
    return () => observer.disconnect();
  }, []);
  const theme = userTheme || detectedTheme;
  useEffect(() => {
    if (typeof window === 'undefined') return;
    const currentProvider = localStorage.getItem(PROVIDER_STORAGE_KEY) || 'gemini';
    const storageKey = PROVIDERS[currentProvider].storageKey;
    const storedKey = localStorage.getItem(storageKey);
    if (storedKey) {
      setApiKey(storedKey);
    } else {
      for (const [provKey, prov] of Object.entries(PROVIDERS)) {
        if (provKey === currentProvider) continue;
        const otherKey = localStorage.getItem(prov.storageKey);
        if (otherKey) {
          setApiKey(otherKey);
          setProvider(provKey);
          setModel(prov.models[0].value);
          localStorage.setItem(PROVIDER_STORAGE_KEY, provKey);
          break;
        }
      }
    }
    const handleApiKeyChanged = () => {
      const activeProvider = localStorage.getItem(PROVIDER_STORAGE_KEY) || 'gemini';
      const key = localStorage.getItem(PROVIDERS[activeProvider].storageKey);
      if (key) {
        setApiKey(key);
        setProvider(activeProvider);
        setModel(PROVIDERS[activeProvider].models[0].value);
      }
    };
    window.addEventListener('apiKeyChanged', handleApiKeyChanged);
    return () => window.removeEventListener('apiKeyChanged', handleApiKeyChanged);
  }, []);
  useEffect(() => {
    if (typeof window === 'undefined') return;
    if (forceSettingsOpen) {
      setIsSettingsOpen(true);
      return;
    }
    localStorage.setItem(SETTINGS_PANEL_KEY, String(isSettingsOpen));
  }, [isSettingsOpen, forceSettingsOpen]);
  useEffect(() => {
    responseCountRef.current = responseCount;
  }, [responseCount]);
  const filterComments = useCallback(text => {
    return text.split('\n').filter(line => !line.trim().startsWith('//')).join('\n').trim();
  }, []);
  const autoResizeTextarea = useCallback(textarea => {
    if (!textarea) return;
    textarea.style.height = '0px';
    const scrollHeight = textarea.scrollHeight;
    const minHeight = 48;
    const maxHeight = 240;
    const newHeight = Math.min(Math.max(scrollHeight, minHeight), maxHeight);
    textarea.style.height = `${newHeight}px`;
  }, []);
  useEffect(() => {
    if (textareaRef.current) {
      textareaRef.current.style.height = '0px';
      textareaRef.current.style.height = `${Math.max(textareaRef.current.scrollHeight, 60)}px`;
    }
  }, []);
  useEffect(() => {
    if (isInitialMount.current) {
      isInitialMount.current = false;
      return;
    }
    if (messagesEndRef.current && conversationHistory.length > 0) {
      setTimeout(() => {
        if (messagesEndRef.current) {
          messagesEndRef.current.scrollIntoView({
            behavior: 'smooth',
            block: 'nearest'
          });
        }
      }, 100);
    }
  }, [conversationHistory, isLoading]);
  useEffect(() => {
    Object.values(advancedTextareaRefs.current).forEach(textarea => {
      if (textarea) {
        autoResizeTextarea(textarea);
      }
    });
  }, [messages, autoResizeTextarea]);
  useEffect(() => {
    if (mode === 'advanced' && hasSubmitted && responseAreaRef.current && !isLoading) {
      setTimeout(() => {
        if (responseAreaRef.current) {
          responseAreaRef.current.scrollIntoView({
            behavior: 'smooth',
            block: 'start'
          });
        }
      }, 100);
    }
  }, [hasSubmitted, output, mode, isLoading]);
  const constructAdvancedMessages = useCallback(() => {
    return messages.filter(msg => msg.content.trim().length > 0);
  }, [messages]);
  const isFormValid = useMemo(() => {
    if (!apiKey) return false;
    if (mode === 'chat') {
      return input.trim().length > 0;
    } else {
      return messages.some(msg => msg.content.trim().length > 0);
    }
  }, [mode, apiKey, input, messages]);
  const handleApiKeySubmit = useCallback(async e => {
    e.preventDefault();
    setIsSavingKey(true);
    setError('');
    if (!apiKeyInput.trim()) {
      setError(`Please enter your ${PROVIDERS[apiKeyProvider].label} API key`);
      setIsSavingKey(false);
      return;
    }
    const trimmedKey = apiKeyInput.trim();
    if (apiKeyProvider === 'openai' && !trimmedKey.startsWith('sk-')) {
      setError('Invalid API key format. OpenAI API keys should start with "sk-"');
      setIsSavingKey(false);
      return;
    }
    try {
      const storageKey = PROVIDERS[apiKeyProvider].storageKey;
      localStorage.setItem(storageKey, trimmedKey);
      localStorage.setItem(PROVIDER_STORAGE_KEY, apiKeyProvider);
      setProvider(apiKeyProvider);
      setApiKey(trimmedKey);
      setApiKeyInput('');
      previousProviderRef.current = null;
      const providerModels = PROVIDERS[apiKeyProvider].models;
      setModel(providerModels[0].value);
      window.dispatchEvent(new CustomEvent('apiKeyChanged', {
        detail: {
          configured: true
        }
      }));
    } catch (err) {
      setError('Failed to save API key. Please try again.');
    } finally {
      setIsSavingKey(false);
    }
  }, [apiKeyInput, apiKeyProvider]);
  const handleRemoveProvider = useCallback(() => {
    if (!provider || typeof window === 'undefined') return;
    const confirmRemove = window.confirm(`Are you sure you want to remove the ${PROVIDERS[provider].label} API key?`);
    if (!confirmRemove) return;
    try {
      const storageKey = PROVIDERS[provider].storageKey;
      localStorage.removeItem(storageKey);
      const otherProviderKey = Object.keys(PROVIDERS).find(p => p !== provider && localStorage.getItem(PROVIDERS[p].storageKey));
      if (otherProviderKey) {
        const newApiKey = localStorage.getItem(PROVIDERS[otherProviderKey].storageKey);
        setProvider(otherProviderKey);
        setApiKey(newApiKey);
        setModel(PROVIDERS[otherProviderKey].models[0].value);
        localStorage.setItem(PROVIDER_STORAGE_KEY, otherProviderKey);
      } else {
        setApiKey('');
        setProvider('gemini');
        localStorage.removeItem(PROVIDER_STORAGE_KEY);
        if (!forceSettingsOpen) {
          setIsSettingsOpen(false);
        }
      }
      window.dispatchEvent(new CustomEvent('apiKeyChanged', {
        detail: {
          configured: !!otherProviderKey
        }
      }));
    } catch (err) {
      setError('Failed to remove provider. Please try again.');
    }
  }, [provider, forceSettingsOpen]);
  const handleSubmit = useCallback(async e => {
    if (e?.preventDefault) {
      e.preventDefault();
    }
    if (!apiKey || isLoading || !isFormValid) {
      return;
    }
    setIsLoading(true);
    setError('');
    setHasSubmitted(true);
    if (mode === 'advanced') {
      setLastResponseJson(null);
    }
    let requestMessages = [];
    let userMessageContent = '';
    if (mode === 'chat') {
      const filteredInput = filterComments(input);
      if (!filteredInput) {
        setError('Please enter a prompt (comments are not sent to the API)');
        setIsLoading(false);
        return;
      }
      userMessageContent = filteredInput;
      requestMessages = [{
        role: 'user',
        content: filteredInput
      }];
      setConversationHistory(prev => [...prev, {
        role: 'user',
        content: filteredInput
      }]);
      if (!keepInput) {
        setInput('');
      }
    } else {
      const advancedMessages = constructAdvancedMessages();
      if (advancedMessages.length === 0) {
        setError('Please fill in at least one field in Advanced mode');
        setIsLoading(false);
        return;
      }
      requestMessages = advancedMessages;
    }
    const isAnthropic = provider === 'anthropic';
    const jsonPayload = isAnthropic ? {
      model,
      messages: requestMessages.filter(m => m.role !== 'system'),
      ...requestMessages.find(m => m.role === 'system') && ({
        system: requestMessages.find(m => m.role === 'system').content
      }),
      temperature,
      top_p: topP,
      max_tokens: MAX_TOKENS
    } : {
      model,
      messages: requestMessages,
      temperature,
      top_p: topP,
      max_tokens: MAX_TOKENS
    };
    setLastSentJson(jsonPayload);
    try {
      const apiUrl = PROVIDERS[provider].apiUrl;
      const maxRetries = 3;
      let response;
      const headers = isAnthropic ? {
        'Content-Type': 'application/json',
        'x-api-key': apiKey,
        'anthropic-version': '2023-06-01',
        'anthropic-dangerous-direct-browser-access': 'true'
      } : {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${apiKey}`
      };
      for (let attempt = 0; attempt <= maxRetries; attempt++) {
        response = await fetch(apiUrl, {
          method: 'POST',
          headers,
          body: JSON.stringify(jsonPayload)
        });
        if (response.status === 429 && attempt < maxRetries) {
          const delay = Math.pow(2, attempt) * 1000;
          setError(`Rate limited. Retrying in ${delay / 1000}s... (attempt ${attempt + 1}/${maxRetries})`);
          await new Promise(resolve => setTimeout(resolve, delay));
          setError('');
          continue;
        }
        break;
      }
      if (!response.ok) {
        const errorData = await response.json().catch(() => ({}));
        if (response.status === 429) {
          throw Object.assign(new Error('Rate limit exceeded. You have used all your free tier quota.'), {
            isRateLimit: true
          });
        }
        throw new Error(errorData.error?.message || `HTTP ${response.status}: Failed to get response from ${PROVIDERS[provider].label}`);
      }
      const data = await response.json();
      const newResponse = isAnthropic ? data.content?.[0]?.text || 'No response generated' : data.choices?.[0]?.message?.content || 'No response generated';
      setLastResponseJson(data);
      if (mode === 'advanced') {
        setOutput(newResponse);
      } else {
        setConversationHistory(prev => [...prev, {
          role: 'assistant',
          content: newResponse
        }]);
        setOutput(newResponse);
      }
    } catch (err) {
      setError(err.isRateLimit ? {
        message: err.message,
        isRateLimit: true
      } : err.message || 'An error occurred while processing your request');
      if (mode === 'chat') {
        setConversationHistory(prev => prev.slice(0, -1));
      }
    } finally {
      setIsLoading(false);
    }
  }, [mode, input, apiKey, provider, isLoading, keepInput, constructAdvancedMessages, filterComments, isFormValid]);
  const errorMessage = typeof error === 'object' ? error.message : error;
  const isRateLimitError = typeof error === 'object' && error.isRateLimit;
  const renderErrorContent = () => <>
      {errorMessage}
      {isRateLimitError && <>
          {' '}
          <a href="https://ai.google.dev/gemini-api/docs/rate-limits" target="_blank" rel="noopener noreferrer" style={{
    color: 'inherit',
    textDecoration: 'underline'
  }}>
            Check your rate limits
          </a>
        </>}
    </>;
  const renderMarkdown = text => {
    const parts = text.split(/(\*\*[^*]+\*\*)/g);
    return parts.map((part, i) => {
      if (part.startsWith('**') && part.endsWith('**')) {
        return <strong key={i}>{part.slice(2, -2)}</strong>;
      }
      return part;
    });
  };
  const renderChatMessage = (message, index, isPlaceholder = false) => {
    const isUser = message.role === 'user';
    return <div key={index} className={isUser ? 'llm-chat-message llm-chat-message-user' : 'llm-chat-message llm-chat-message-assistant'}>
        <div className={`llm-chat-bubble ${isUser ? 'llm-chat-bubble-user' : 'llm-chat-bubble-assistant'} ${isPlaceholder ? 'llm-chat-bubble-placeholder' : ''}`}>
          {renderMarkdown(message.content)}
        </div>
      </div>;
  };
  const renderChatInterface = () => {
    const allMessages = [...conversationHistory];
    const placeholderMessages = [];
    if (response && !hasSubmitted && conversationHistory.length === 0) {
      if (defaultInput && defaultInput.trim()) {
        const filteredInput = filterComments(defaultInput);
        if (filteredInput) {
          placeholderMessages.push({
            role: 'user',
            content: filteredInput,
            isPlaceholder: true
          });
          placeholderMessages.push({
            role: 'assistant',
            content: response,
            isPlaceholder: true
          });
        }
      }
    }
    const hasPlaceholderMessages = placeholderMessages.length > 0;
    return <div className="llm-chat-interface">
        {hasPlaceholderMessages && <div className="llm-warning-banner">
            <IconWarningSun size={14} />
            <span>This is a sample conversation. Click the send button to make a real API call.</span>
          </div>}
        {allMessages.length === 0 && placeholderMessages.length === 0 && !isLoading && <div className="llm-chat-empty-state">
            Start a conversation using the API key you provided!
          </div>}
        {placeholderMessages.map((msg, idx) => renderChatMessage(msg, `placeholder-${idx}`, true))}
        {allMessages.map((msg, idx) => renderChatMessage(msg, `real-${idx}`, false))}
        {isLoading && <div className="llm-chat-loading">
            <div className="llm-chat-loading-bubble">
              <IconLoadingSpinner size={14} className="llm-chat-loading-spinner" />
              <span>Thinking...</span>
            </div>
          </div>}
        <div ref={messagesEndRef} />
      </div>;
  };
  const renderApiKeyForm = () => <div className="llm-api-key-form">
      <div className="llm-api-key-form-section">
        <h3 className="llm-api-key-form-title">
          <IconWarningTriangle size={16} strokeColor="#f59e0b" />
          <span>Configure an API Key to make the most of the tutorial</span>
        </h3>
        <p className="llm-api-key-form-description">
          Your API key is stored locally in your browser's storage and is never transmitted to external servers.
        </p>
      </div>

      <div className="llm-provider-tabs">
        {Object.entries(PROVIDERS).map(([key, prov]) => <button key={key} type="button" onClick={() => {
    setApiKeyProvider(key);
    setError('');
    setApiKeyInput('');
  }} className={`llm-provider-tab ${apiKeyProvider === key ? 'llm-provider-tab-active' : ''}`}>
            {prov.label}
            {key === 'gemini' && <span className="llm-provider-tab-badge">Free Tier</span>}
          </button>)}
      </div>

      {apiKeyProvider === 'gemini' && <div className="llm-gemini-recommendation">
          Gemini offers a generous free tier — great for learning! Get your free API key at{' '}
          <a href="https://aistudio.google.com/apikey" target="_blank" rel="noopener noreferrer" className="llm-api-key-form-link">
            aistudio.google.com/apikey
          </a>
        </div>}

      {apiKeyProvider === 'openai' && <p className="llm-api-key-form-description" style={{
    marginTop: '4px'
  }}>
          <a href="https://platform.openai.com/api-keys" target="_blank" rel="noopener noreferrer" className="llm-api-key-form-link">
            Don't have an API key? Get one here
          </a>
        </p>}

      <form onSubmit={handleApiKeySubmit}>
        <div className="llm-form-group">
          <div className="llm-form-row">
            <input id="api-key-input" type="password" value={apiKeyInput} onChange={e => {
    setApiKeyInput(e.target.value);
    setError('');
  }} placeholder={apiKeyProvider === 'openai' ? 'OpenAI API Key (sk-...)' : 'Gemini API Key'} disabled={isSavingKey} className={error ? 'llm-api-key-input llm-api-key-input-error' : 'llm-api-key-input'} onBlur={e => {
    e.target.style.borderColor = error ? 'var(--llm-accent-red)' : 'var(--llm-border-color)';
  }} />
            <button type="submit" disabled={isSavingKey || !apiKeyInput.trim()} className="llm-api-key-save-button">
              {isSavingKey ? 'Saving...' : 'Save'}
            </button>
            {previousProviderRef.current && <button type="button" className="llm-api-key-cancel-button" onClick={() => {
    const prev = previousProviderRef.current;
    const prevKey = localStorage.getItem(PROVIDERS[prev].storageKey);
    setProvider(prev);
    setApiKey(prevKey || '');
    setModel(PROVIDERS[prev].models[0].value);
    localStorage.setItem(PROVIDER_STORAGE_KEY, prev);
    setApiKeyInput('');
    setError('');
    previousProviderRef.current = null;
  }}>
                Cancel
              </button>}
          </div>
        </div>
        {error && <div className="llm-error-message llm-error-message-small">
            {renderErrorContent()}
          </div>}
      </form>
    </div>;
  const renderChatInput = () => {
    return <div className="llm-chat-input-container">
        <textarea ref={textareaRef} value={input} onChange={e => setInput(e.target.value)} placeholder="Type a message" disabled={isLoading} className="llm-chat-input-textarea" />
      </div>;
  };
  const renderAdvancedInput = () => <div className="llm-advanced-input-container">
      {messages.map((message, index) => <div key={index} className="llm-advanced-message-box">
          <div className="llm-advanced-message-header">
            <div className="llm-advanced-message-header-row">
              <span className="llm-advanced-message-number">
                #{index + 1}
              </span>
              <select value={message.role} onChange={e => {
    const newMessages = [...messages];
    newMessages[index].role = e.target.value;
    setMessages(newMessages);
  }} disabled={isLoading} className={`llm-advanced-role-select ${message.role === 'system' ? 'llm-advanced-role-select-system' : message.role === 'user' ? 'llm-advanced-role-select-user' : 'llm-advanced-role-select-assistant'}`}>
                <option value="system">system</option>
                <option value="user">user</option>
                <option value="assistant">assistant</option>
              </select>
            </div>
            <button type="button" onClick={() => {
    const newMessages = messages.filter((_, i) => i !== index);
    setMessages(newMessages.length > 0 ? newMessages : [{
      role: 'system',
      content: ''
    }]);
  }} disabled={isLoading || messages.length <= 1} className="llm-advanced-remove-button" title="Remove message">
              <IconClose size={10} />
            </button>
          </div>
          <textarea ref={el => {
    if (el) {
      advancedTextareaRefs.current[index] = el;
    }
  }} value={message.content} onChange={e => {
    const newMessages = [...messages];
    newMessages[index].content = e.target.value;
    setMessages(newMessages);
    autoResizeTextarea(e.target);
  }} placeholder={`Enter ${message.role} message content...`} disabled={isLoading} className="llm-textarea-base llm-textarea-enabled llm-advanced-textarea" />
        </div>)}

      <button type="button" onClick={() => {
    setMessages([...messages, {
      role: 'user',
      content: ''
    }]);
  }} disabled={isLoading} className="llm-advanced-add-button">
        <IconPlus size={12} />
        Add Message
      </button>
    </div>;
  return <div className={`code-editor-wrapper ${isMaximized ? 'maximized' : ''} ${isCollapsed ? 'collapsed' : ''}`} data-theme={theme}>
      <div className="code-editor-header">
          <div className="code-editor-title">
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
            </svg>
            {headerTitle}
          </div>
          <div className="code-editor-controls">
            {!isMaximized && <button className="code-editor-collapse-button" onClick={toggleCollapse} title={isCollapsed ? "Expand" : "Collapse"} type="button">
              <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                {isCollapsed ? <polyline points="6 9 12 15 18 9" /> : <polyline points="6 15 12 9 18 15" />}
              </svg>
            </button>}
            <button className="code-editor-maximize-button" onClick={toggleMaximize} title={isMaximized ? "Minimize" : "Maximize (Focus Mode)"} type="button">
              <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                {isMaximized ? <><path d="M4 14h6v6" /><path d="M20 10h-6V4" /><path d="M14 10l7-7" /><path d="M3 21l7-7" /></> : <><path d="M15 3h6v6" /><path d="M9 21H3v-6" /><path d="M21 3l-7 7" /><path d="M3 21l7-7" /></>}
              </svg>
            </button>
          </div>
        </div>

      {!isCollapsed && <div className={mode === 'chat' ? 'llm-playground-container llm-playground-container-chat llm-playground-container-base' : 'llm-playground-container llm-playground-container-base'} style={{
    height: isMaximized ? 'auto' : height,
    flex: isMaximized ? 1 : 'none'
  }}>
        <div className="llm-playground-main">
          <div className="llm-playground-content">
            {mode === 'chat' ? <>
                {}
                {!apiKey && <div className="llm-api-key-form-wrapper">
                    {renderApiKeyForm()}
                  </div>}

                {}
                {renderChatInterface()}

                {}
                {error && <div className="llm-error-wrapper">
                    <div className="llm-error-message">
                      <IconError size={14} />
                      {renderErrorContent()}
                    </div>
                  </div>}

                {}
                <div className="llm-playground-input-area">
                  <div className="llm-chat-input-wrapper">
                    {renderChatInput()}
                  </div>
                  {apiKey && <button onClick={handleSubmit} disabled={isLoading || !isFormValid} className="llm-chat-send-button" aria-label="Send message">
                      {isLoading ? <IconLoadingSpinner size={16} strokeColor="#000" strokeWidth="2.5" strokeLinecap="round" className="llm-chat-send-spinner" /> : <IconSend size={16} fillColor="#000" className="llm-chat-send-icon" />}
                    </button>}
                </div>
              </> : <>
                {}
                <div className="llm-playground-advanced-input-area" style={{
    flex: hasSubmitted || response && response.trim() ? '0 0 auto' : '1 1 100%',
    borderBottom: hasSubmitted || response && response.trim() ? '1px solid var(--llm-bg-secondary)' : 'none'
  }}>
                  {renderAdvancedInput()}
                  {!apiKey && renderApiKeyForm()}
                  {error && <div className="llm-error-message llm-error-message-top">
                      <IconError size={14} />
                      {renderErrorContent()}
                    </div>}
                </div>

                {(hasSubmitted || response && response.trim()) && <div ref={responseAreaRef} className="llm-playground-response-area">
                    {!apiKey && !(response && response.trim()) ? <>
                        <label className="llm-section-label">Prompt</label>
                        <textarea value={input} onChange={e => setInput(e.target.value)} placeholder="Enter your prompt here... (Configure API key to enable)" disabled={true} className="llm-textarea-base llm-textarea-disabled" />
                      </> : <>
                        {output ? <div className="llm-response-box">{renderMarkdown(output)}</div> : isLoading ? <div className="llm-tab-loading">
                            <span className="llm-tab-loading-content">
                              <IconLoadingSpinner size={14} className="llm-chat-loading-spinner" />
                              Generating response...
                            </span>
                          </div> : response ? <div className="llm-response-section">
                            <label className="llm-section-label">Response (Expected response, press submit to get actual response)</label>
                            <div className="llm-response-box llm-response-box-placeholder">
                              {response}
                            </div>
                          </div> : <div className="llm-tab-empty">
                            Response will appear here
                          </div>}
                      </>}
                  </div>}

                {(apiKey || response && response.trim()) && <div className="llm-playground-actions-area">
                    {apiKey ? <button onClick={handleSubmit} disabled={isLoading || !isFormValid} className="llm-submit-button">
                        {isLoading ? <span className="llm-submit-button-loading">
                            <IconLoadingSpinner size={12} className="llm-submit-button-loading-spinner" />
                            Processing...
                          </span> : 'Submit'}
                      </button> : <div className="llm-config-message">
                        Configure API key above to submit and get actual response
                      </div>}
                  </div>}
              </>}
          </div>

          {isSettingsOpen && <div className="llm-settings-panel" style={{
    width: `${SETTINGS_PANEL_WIDTH}px`
  }}>
              <h4 className="llm-settings-title">Settings</h4>

              <div>
                <label className="llm-settings-label">Provider</label>
                <div className="llm-settings-row-with-action">
                  <select value={provider} onChange={e => {
    const newProvider = e.target.value;
    const storageKey = PROVIDERS[newProvider].storageKey;
    const storedKey = localStorage.getItem(storageKey);
    if (apiKey) {
      previousProviderRef.current = provider;
    }
    setProvider(newProvider);
    setModel(PROVIDERS[newProvider].models[0].value);
    localStorage.setItem(PROVIDER_STORAGE_KEY, newProvider);
    if (storedKey) {
      setApiKey(storedKey);
    } else {
      setApiKey('');
    }
  }} disabled={isLoading} className="llm-settings-select">
                    {Object.entries(PROVIDERS).map(([key, prov]) => {
    const hasKey = typeof window !== 'undefined' && localStorage.getItem(prov.storageKey);
    return <option key={key} value={key} className="llm-settings-option">
                          {prov.label}{!hasKey ? ' (no key)' : ''}
                        </option>;
  })}
                  </select>
                  <button type="button" onClick={handleRemoveProvider} disabled={isLoading} className="llm-settings-remove-button" title="Remove Provider">
                    <IconTrash size={14} />
                  </button>
                </div>
              </div>

              <div>
                <label className="llm-settings-label">
                  Model
                </label>
                <select value={model} onChange={e => setModel(e.target.value)} disabled={isLoading} className="llm-settings-select">
                  {PROVIDERS[provider].models.map(m => <option key={m.value} value={m.value} className="llm-settings-option">
                      {m.label}
                    </option>)}
                </select>
              </div>

              <div>
                <div className="llm-settings-range-container">
                  <label className="llm-settings-label">
                    Temperature
                    <span className="llm-settings-info" title="Controls randomness. Lower values make output more focused and deterministic. Higher values make it more creative and varied.">
                      <IconInfo size={11} />
                    </span>
                  </label>
                  <span className="llm-settings-range-value">
                    {temperature.toFixed(1)}
                  </span>
                </div>
                <input type="range" min="0" max="2" step="0.1" value={temperature} onChange={e => setTemperature(parseFloat(e.target.value))} disabled={isLoading} className="llm-settings-range" />
                <div className="llm-settings-range-labels">
                  <span>0.0</span>
                  <span>1.0</span>
                  <span>2.0</span>
                </div>
              </div>

              <div>
                <div className="llm-settings-range-container">
                  <label className="llm-settings-label">
                    Top P
                    <span className="llm-settings-info" title="Nucleus sampling. Controls the cumulative probability cutoff. Lower values consider fewer tokens, making output more focused. Usually adjusted as an alternative to temperature.">
                      <IconInfo size={11} />
                    </span>
                  </label>
                  <span className="llm-settings-range-value">
                    {topP.toFixed(2)}
                  </span>
                </div>
                <input type="range" min="0" max="1" step="0.05" value={topP} onChange={e => setTopP(parseFloat(e.target.value))} disabled={isLoading} className="llm-settings-range" />
                <div className="llm-settings-range-labels">
                  <span>0.0</span>
                  <span>0.5</span>
                  <span>1.0</span>
                </div>
              </div>

            </div>}
        </div>

        {isApiCallsOpen && (lastSentJson || lastResponseJson) && <div className="llm-api-calls-content">
            <div className="llm-api-calls-tabs">
              {lastSentJson && <button type="button" className={`llm-api-calls-tab${apiCallTab === 'request' ? ' llm-api-calls-tab-active' : ''}`} onClick={() => setApiCallTab('request')}>
                  Request
                </button>}
              {lastResponseJson && <button type="button" className={`llm-api-calls-tab${apiCallTab === 'response' ? ' llm-api-calls-tab-active' : ''}`} onClick={() => setApiCallTab('response')}>
                  Response
                </button>}
            </div>
            {apiCallTab === 'request' && lastSentJson && <div className="llm-api-calls-block">
                <div className="llm-textarea-base llm-textarea-enabled llm-json-viewer">
                  {JSON.stringify(lastSentJson, null, 2)}
                </div>
              </div>}
            {apiCallTab === 'response' && lastResponseJson && <div className="llm-api-calls-block">
                <div className="llm-textarea-base llm-textarea-enabled llm-json-viewer">
                  {JSON.stringify(lastResponseJson, null, 2)}
                </div>
              </div>}
          </div>}

        <div className="llm-footer">
          <div className="llm-footer-status-container">
            <span>{PROVIDERS[provider].label} key:</span>
            <div className="llm-footer-status-icon" title={apiKey ? 'API Key configured' : 'API Key not configured'}>
              {apiKey ? <IconCheckmark size={14} strokeColor="#22c55e" /> : <IconXStatus size={14} strokeColor="#ef4444" />}
            </div>
          </div>
          <div className="llm-footer-actions">
            {hasSubmitted && (lastSentJson || lastResponseJson) && <button type="button" onClick={() => setIsApiCallsOpen(!isApiCallsOpen)} className={isApiCallsOpen ? 'llm-footer-toggle-button llm-footer-toggle-button-active' : 'llm-footer-toggle-button'} aria-label="Toggle API Calls">
                <IconChevron size={12} isOpen={isApiCallsOpen} className={isApiCallsOpen ? 'llm-footer-toggle-icon llm-footer-toggle-icon-open' : 'llm-footer-toggle-icon'} />
                <span>API Calls</span>
              </button>}
            <button onClick={() => {
    if (!forceSettingsOpen) {
      setIsSettingsOpen(!isSettingsOpen);
    }
  }} disabled={forceSettingsOpen} className={isSettingsOpen ? 'llm-footer-toggle-button llm-footer-toggle-button-active' : 'llm-footer-toggle-button'} aria-label="Toggle settings">
              <IconChevron size={12} isOpen={isSettingsOpen} className={isSettingsOpen ? 'llm-footer-toggle-icon llm-footer-toggle-icon-open' : 'llm-footer-toggle-icon'} />
              <span>Settings</span>
            </button>
          </div>
        </div>
      </div>}
    </div>;
};

export const CodeEditor = ({file = 'src/hello_world.ts', lines, title = 'Code Example', repo = 'ai-tutorial/typescript-examples', height = '650px', functionName, theme: userTheme}) => {
  const STORAGE_KEY = 'openai_api_key';
  const GEMINI_STORAGE_KEY = 'gemini_api_key';
  const ANTHROPIC_STORAGE_KEY = 'anthropic_api_key';
  const PROVIDER_STORAGE_KEY = 'llm_playground_provider';
  if (!functionName) {
    console.warn('CodeEditor: functionName parameter is required');
  }
  const hasCreatedEnvRef = useRef(false);
  const vmRef = useRef(null);
  const [isMaximized, setIsMaximized] = useState(false);
  const [isCollapsed, setIsCollapsed] = useState(false);
  const [isStuck, setIsStuck] = useState(false);
  const [iframeKey, setIframeKey] = useState(0);
  const [showApiKeyDialog, setShowApiKeyDialog] = useState(false);
  const [apiKey, setApiKey] = useState('');
  const [error, setError] = useState('');
  const [success, setSuccess] = useState(false);
  const [isSubmitting, setIsSubmitting] = useState(false);
  const [isValidating, setIsValidating] = useState(false);
  const [detectedTheme, setDetectedTheme] = useState('dark');
  useEffect(() => {
    if (typeof window === 'undefined') return;
    const checkTheme = () => {
      const isDark = document.documentElement.classList.contains('dark');
      setDetectedTheme(isDark ? 'dark' : 'light');
    };
    checkTheme();
    const observer = new MutationObserver(checkTheme);
    observer.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ['class']
    });
    return () => observer.disconnect();
  }, []);
  const theme = userTheme || detectedTheme;
  const [selectedProvider, setSelectedProvider] = useState(() => {
    if (typeof window === 'undefined') return 'gemini';
    return localStorage.getItem(PROVIDER_STORAGE_KEY) || 'gemini';
  });
  const isApiKeyConfigured = () => {
    const openaiKey = localStorage.getItem(STORAGE_KEY);
    const geminiKey = localStorage.getItem(GEMINI_STORAGE_KEY);
    const anthropicKey = localStorage.getItem(ANTHROPIC_STORAGE_KEY);
    return openaiKey !== null && openaiKey.trim().length > 0 || geminiKey !== null && geminiKey.trim().length > 0 || anthropicKey !== null && anthropicKey.trim().length > 0;
  };
  const dispatchApiKeyChanged = () => {
    if (typeof window !== 'undefined' && window.dispatchEvent) {
      window.dispatchEvent(new CustomEvent('apiKeyChanged', {
        detail: {
          configured: isApiKeyConfigured()
        }
      }));
    }
  };
  const saveApiKey = apiKey => {
    if (apiKey && apiKey.trim()) {
      const trimmedKey = apiKey.trim();
      localStorage.setItem(STORAGE_KEY, trimmedKey);
      dispatchApiKeyChanged();
      return true;
    }
    return false;
  };
  const buildEnvContent = () => {
    const openaiKey = localStorage.getItem(STORAGE_KEY)?.trim();
    const geminiKey = localStorage.getItem(GEMINI_STORAGE_KEY)?.trim();
    const anthropicKey = localStorage.getItem(ANTHROPIC_STORAGE_KEY)?.trim();
    if (!openaiKey && !geminiKey && !anthropicKey) {
      return `OPENAI_MODEL=gpt-4.1-nano
OPENAI_API_KEY=sk-mock-key-1234567890abcdef
GEMINI_MODEL=gemini-2.5-flash-lite
GOOGLE_GENERATIVE_AI_API_KEY=
GOOGLE_API_KEY=
ANTHROPIC_API_KEY=
AI_PROVIDER=openai
# API key not found in browser storage
# To configure your API key:
# 1. For Gemini (free): Go to https://aistudio.google.com/apikey
# 2. For OpenAI: Go to https://platform.openai.com/api-keys
# 3. For Claude: Go to https://console.anthropic.com/settings/keys
# 4. Enter it in the configuration form above this editor
# 5. The .env file will be automatically updated with your key`;
    }
    const envLines = ['# Using the API key(s) you configured. This file will be created when the dialog is loaded.'];
    if (openaiKey) {
      envLines.push(`OPENAI_MODEL=gpt-4.1-nano`);
      envLines.push(`OPENAI_API_KEY=${openaiKey}`);
    }
    if (geminiKey) {
      envLines.push(`GEMINI_MODEL=gemini-2.5-flash-lite`);
      envLines.push(`# Vercel AI SDK uses GOOGLE_GENERATIVE_AI_API_KEY, LangChain uses GOOGLE_API_KEY`);
      envLines.push(`GOOGLE_GENERATIVE_AI_API_KEY=${geminiKey}`);
      envLines.push(`GOOGLE_API_KEY=${geminiKey}`);
    }
    if (anthropicKey) {
      envLines.push(`ANTHROPIC_API_KEY=${anthropicKey}`);
    }
    const provider = anthropicKey ? 'anthropic' : geminiKey ? 'gemini' : 'openai';
    envLines.push(`AI_PROVIDER=${provider}`);
    return envLines.join('\n');
  };
  const updateEnvFile = async vm => {
    if (!vm) return;
    try {
      await vm.applyFsDiff({
        create: {
          'env/.env': buildEnvContent(),
          'env/run.conf': `file=${file}`
        },
        destroy: []
      });
      hasCreatedEnvRef.current = true;
    } catch (error) {
      console.error('Failed to write env files:', error);
      hasCreatedEnvRef.current = false;
    }
  };
  useEffect(() => {
    if (!isApiKeyConfigured()) {
      setShowApiKeyDialog(true);
    }
    const handleApiKeyChanged = () => {
      if (isApiKeyConfigured()) {
        setShowApiKeyDialog(false);
      }
    };
    if (typeof window !== 'undefined') {
      window.addEventListener('apiKeyChanged', handleApiKeyChanged);
      return () => {
        window.removeEventListener('apiKeyChanged', handleApiKeyChanged);
      };
    }
  }, []);
  const validateApiKey = async (key, provider) => {
    try {
      const urls = {
        gemini: 'https://generativelanguage.googleapis.com/v1beta/models?key=' + encodeURIComponent(key.trim()),
        openai: 'https://api.openai.com/v1/models',
        anthropic: 'https://api.anthropic.com/v1/models'
      };
      const headerMap = {
        gemini: {
          'Content-Type': 'application/json'
        },
        openai: {
          'Authorization': `Bearer ${key.trim()}`,
          'Content-Type': 'application/json'
        },
        anthropic: {
          'x-api-key': key.trim(),
          'anthropic-version': '2023-06-01',
          'Content-Type': 'application/json'
        }
      };
      const url = urls[provider];
      const headers = headerMap[provider];
      const response = await fetch(url, {
        method: 'GET',
        headers
      });
      if (response.ok) {
        return {
          valid: true
        };
      } else if (response.status === 401 || response.status === 403) {
        return {
          valid: false,
          error: 'Invalid API key. Please check your key and try again.'
        };
      } else if (response.status === 429) {
        return {
          valid: false,
          error: 'Rate limit exceeded. Please try again later.'
        };
      } else {
        const errorData = await response.json().catch(() => ({}));
        return {
          valid: false,
          error: errorData.error?.message || `API request failed with status ${response.status}`
        };
      }
    } catch (err) {
      if (err.name === 'TypeError' && err.message.includes('fetch')) {
        return {
          valid: false,
          error: 'Network error. Please check your connection and try again.'
        };
      }
      return {
        valid: false,
        error: err.message || 'Failed to validate API key. Please try again.'
      };
    }
  };
  const handleSkipConfiguration = () => {
    const skipKey = 'sk-<configure-your-key>';
    saveApiKey(skipKey);
    setShowApiKeyDialog(false);
  };
  const handleApiKeySubmit = async e => {
    e.preventDefault();
    setError('');
    setSuccess(false);
    setIsSubmitting(true);
    const providerNames = {
      gemini: 'Gemini',
      openai: 'OpenAI',
      anthropic: 'Claude'
    };
    if (!apiKey || !apiKey.trim()) {
      setError(`Please enter your ${providerNames[selectedProvider]} API key`);
      setIsSubmitting(false);
      return;
    }
    const trimmedKey = apiKey.trim();
    if (selectedProvider === 'openai' && !trimmedKey.startsWith('sk-')) {
      setError('Invalid API key format. OpenAI API keys should start with "sk-"');
      setIsSubmitting(false);
      return;
    }
    if (selectedProvider === 'anthropic' && !trimmedKey.startsWith('sk-ant-')) {
      setError('Invalid API key format. Anthropic API keys should start with "sk-ant-"');
      setIsSubmitting(false);
      return;
    }
    setIsValidating(true);
    setError('');
    const validation = await validateApiKey(trimmedKey, selectedProvider);
    setIsValidating(false);
    if (!validation.valid) {
      setError(validation.error || 'Invalid API key. Please check your key and try again.');
      setIsSubmitting(false);
      return;
    }
    try {
      const storageKeys = {
        gemini: GEMINI_STORAGE_KEY,
        openai: STORAGE_KEY,
        anthropic: ANTHROPIC_STORAGE_KEY
      };
      localStorage.setItem(storageKeys[selectedProvider], trimmedKey);
      localStorage.setItem(PROVIDER_STORAGE_KEY, selectedProvider);
      dispatchApiKeyChanged();
      setSuccess(true);
      setApiKey('');
      setTimeout(() => {
        window.location.reload();
      }, 1000);
    } catch (err) {
      setError(err.message || 'Failed to save API key. Please try again.');
      setIsSubmitting(false);
    }
  };
  const baseFilePath = file || 'src/hello_world.ts';
  let filePath = baseFilePath;
  if (typeof lines === 'string' && lines.trim()) {
    const lineParts = lines.split('-');
    if (lineParts.length === 2) {
      filePath = `${filePath}:L${lineParts[0].trim()}-L${lineParts[1].trim()}`;
    } else {
      filePath = `${filePath}:L${lineParts[0].trim()}`;
    }
  } else if (typeof lines === 'object' && lines.start !== undefined) {
    filePath = lines.end !== undefined ? `${filePath}:L${lines.start}-L${lines.end}` : `${filePath}:L${lines.start}`;
  }
  const stackblitzUrl = `https://stackblitz.com/github/${repo}?file=${encodeURIComponent(filePath)}&embed=1&view=editor&theme=${theme}`;
  const loadSDK = () => {
    return new Promise((resolve, reject) => {
      if (window.StackBlitzSDK || window.stackblitzSDK) {
        resolve(window.StackBlitzSDK || window.stackblitzSDK);
        return;
      }
      if (document.querySelector('script[data-stackblitz-sdk]')) {
        const checkInterval = setInterval(() => {
          if (window.StackBlitzSDK || window.stackblitzSDK) {
            clearInterval(checkInterval);
            resolve(window.StackBlitzSDK || window.stackblitzSDK);
          }
        }, 100);
        setTimeout(() => {
          clearInterval(checkInterval);
          reject(new Error('SDK loading timeout'));
        }, 10000);
        return;
      }
      const script = document.createElement('script');
      script.src = 'https://unpkg.com/@stackblitz/sdk/bundles/sdk.umd.js';
      script.async = true;
      script.setAttribute('data-stackblitz-sdk', 'true');
      script.onload = () => {
        const sdk = window.StackBlitzSDK || window.stackblitzSDK;
        if (sdk) {
          resolve(sdk);
        } else {
          reject(new Error('SDK loaded but not available on window'));
        }
      };
      script.onerror = () => {
        reject(new Error('Failed to load StackBlitz SDK'));
      };
      document.head.appendChild(script);
    });
  };
  const LOAD_TIMEOUT_MS = 10000;
  const iframeElRef = useRef(null);
  const reloadCountRef = useRef(0);
  const handleRetry = () => {
    vmRef.current = null;
    hasCreatedEnvRef.current = false;
    reloadCountRef.current = 0;
    setIsStuck(false);
    setIframeKey(prev => prev + 1);
  };
  const iframeRef = iframe => {
    iframeElRef.current = iframe;
  };
  const connectToVM = async iframe => {
    const sdk = await loadSDK();
    return sdk.connect(iframe);
  };
  const handleIframeLoad = async () => {
    const iframe = iframeElRef.current;
    if (!iframe) return;
    if (reloadCountRef.current > 0) {
      try {
        const vm = await connectToVM(iframe);
        vmRef.current = vm;
        await updateEnvFile(vm);
      } catch (_) {}
      return;
    }
    try {
      if (vmRef.current) return;
      const vm = await Promise.race([connectToVM(iframe), new Promise((_, reject) => setTimeout(() => reject(new Error('connect timeout')), LOAD_TIMEOUT_MS))]);
      vmRef.current = vm;
      await updateEnvFile(vm);
    } catch (error) {
      console.error('Failed to connect to StackBlitz VM:', error);
      if (typeof window !== 'undefined' && window.gtag) {
        window.gtag('event', 'load_refresh_error', {
          event_category: 'stackblitz',
          event_label: file,
          error_message: error.message
        });
      }
      reloadCountRef.current = 1;
      setTimeout(() => {
        setIframeKey(prev => prev + 1);
      }, 2000);
    }
  };
  const isSafari = typeof navigator !== 'undefined' && (/^((?!chrome|android).)*safari/i).test(navigator.userAgent);
  if (isSafari) {
    return <div className="code-editor-dialog-container" style={{
      height: height
    }}>
        <div className="code-editor-dialog-box">
          <h2 className="code-editor-dialog-title">
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#f59e0b" strokeWidth="2">
              <path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path>
              <line x1="12" y1="9" x2="12" y2="13"></line>
              <line x1="12" y1="17" x2="12.01" y2="17"></line>
            </svg>
            Browser Not Supported
          </h2>
          <p className="code-editor-dialog-description">
            The interactive code editor is not supported on Safari. Please use <strong>Chrome</strong>, <strong>Edge</strong>, or <strong>Firefox</strong> to run the examples.
          </p>
        </div>
      </div>;
  }
  if (showApiKeyDialog) {
    return <div className="code-editor-dialog-container" style={{
      height: height
    }}>
        <div className="code-editor-dialog-box">
          <h2 className="code-editor-dialog-title">
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#f59e0b" strokeWidth="2">
              <path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path>
              <line x1="12" y1="9" x2="12" y2="13"></line>
              <line x1="12" y1="17" x2="12.01" y2="17"></line>
            </svg>
            Configure API Key
          </h2>

          <p className="code-editor-dialog-description">
            All interactive examples execute entirely within your browser environment, ensuring complete security and privacy.
            Your API key is stored locally in your browser's storage and is never transmitted to external servers.
          </p>

          <div className="llm-provider-tabs" style={{
      marginBottom: '16px'
    }}>
            <button type="button" onClick={() => {
      setSelectedProvider('gemini');
      setError('');
      setApiKey('');
    }} className={`llm-provider-tab ${selectedProvider === 'gemini' ? 'llm-provider-tab-active' : ''}`}>
              Gemini <span className="llm-provider-tab-badge">Free</span>
            </button>
            <button type="button" onClick={() => {
      setSelectedProvider('openai');
      setError('');
      setApiKey('');
    }} className={`llm-provider-tab ${selectedProvider === 'openai' ? 'llm-provider-tab-active' : ''}`}>
              OpenAI
            </button>
            <button type="button" onClick={() => {
      setSelectedProvider('anthropic');
      setError('');
      setApiKey('');
    }} className={`llm-provider-tab ${selectedProvider === 'anthropic' ? 'llm-provider-tab-active' : ''}`}>
              Claude
            </button>
          </div>

          {selectedProvider === 'gemini' && <div className="llm-gemini-recommendation" style={{
      marginBottom: '16px'
    }}>
              Gemini offers a generous free tier — great for learning! Get your free API key at{' '}
              <a href="https://aistudio.google.com/apikey" target="_blank" rel="noopener noreferrer" className="code-editor-link">
                aistudio.google.com/apikey
              </a>
            </div>}

          {selectedProvider === 'openai' && <div className="code-editor-info-box">
              <p className="code-editor-info-box-title">
                Don't have an API key?
              </p>
              <p className="code-editor-info-box-text">
                Get one at{' '}
                <a href="https://platform.openai.com/api-keys" target="_blank" rel="noopener noreferrer" className="code-editor-link">
                  platform.openai.com/api-keys
                </a>
              </p>
            </div>}

          {selectedProvider === 'anthropic' && <div className="code-editor-info-box">
              <p className="code-editor-info-box-title">
                Don't have an API key?
              </p>
              <p className="code-editor-info-box-text">
                Get one at{' '}
                <a href="https://console.anthropic.com/settings/keys" target="_blank" rel="noopener noreferrer" className="code-editor-link">
                  console.anthropic.com/settings/keys
                </a>
              </p>
            </div>}

          <form onSubmit={handleApiKeySubmit}>
            <div className="code-editor-form-group">
              <label htmlFor="api-key-input" className="code-editor-label">
                {selectedProvider === 'gemini' ? 'Gemini' : 'OpenAI'} API Key
              </label>
              <input id="api-key-input" type="password" value={apiKey} onChange={e => {
      setApiKey(e.target.value);
      setError('');
      setSuccess(false);
    }} placeholder={selectedProvider === 'openai' ? 'sk-...' : 'Gemini API Key'} disabled={isSubmitting} className={`code-editor-input ${error ? 'code-editor-input-error' : ''}`} />
            </div>

            {isValidating && <div className="code-editor-message code-editor-message-info">
                <span className="code-editor-message-icon">⏳</span>
                <span>Validating API key...</span>
              </div>}

            {error && !isValidating && <div className="code-editor-message code-editor-message-error">
                <span className="code-editor-message-icon">⚠️</span>
                <span>{error}</span>
              </div>}

            {success && <div className="code-editor-message code-editor-message-success">
                <span className="code-editor-message-icon">✓</span>
                <span>API key saved successfully! Loading editor...</span>
              </div>}

            <button type="submit" disabled={isSubmitting || isValidating || !apiKey.trim()} className="code-editor-button">
              {isValidating ? 'Validating...' : isSubmitting ? 'Saving...' : 'Save API Key'}
            </button>
          </form>

          <button type="button" onClick={handleSkipConfiguration} disabled={isSubmitting || isValidating} className="code-editor-button-secondary">
            Skip Configuration
          </button>

          <div className="code-editor-footer">
            <p className="code-editor-footer-text">
              Alternatively, you may checkout the source code from{' '}
              <a href="https://github.com/ai-tutorial/typescript-examples" target="_blank" rel="noopener noreferrer" className="code-editor-link code-editor-link-break">
                https://github.com/ai-tutorial/typescript-examples
              </a>
              {' '}and run the examples locally.
            </p>
          </div>
        </div>
      </div>;
  }
  const toggleMaximize = () => setIsMaximized(!isMaximized);
  const toggleCollapse = () => setIsCollapsed(!isCollapsed);
  return <div className={`code-editor-wrapper ${isMaximized ? 'maximized' : ''} ${isCollapsed ? 'collapsed' : ''}`} data-theme={theme}>
      <div className="code-editor-header">
        <div className="code-editor-title">
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
            <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path>
            <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path>
          </svg>
          {title}
        </div>
        <div className="code-editor-controls">
          {!isMaximized && <button className="code-editor-collapse-button" onClick={toggleCollapse} title={isCollapsed ? "Expand" : "Collapse"} type="button">
              <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                {isCollapsed ? <polyline points="6 9 12 15 18 9" /> : <polyline points="6 15 12 9 18 15" />}
              </svg>
            </button>}
          <button className="code-editor-maximize-button" onClick={toggleMaximize} title={isMaximized ? "Minimize" : "Maximize (Focus Mode)"} type="button">
            <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              {isMaximized ? <><path d="M4 14h6v6" /><path d="M20 10h-6V4" /><path d="M14 10l7-7" /><path d="M3 21l7-7" /></> : <><path d="M15 3h6v6" /><path d="M9 21H3v-6" /><path d="M21 3l-7 7" /><path d="M3 21l7-7" /></>}
            </svg>
          </button>
        </div>
      </div>

      {!isCollapsed && <div style={{
    position: 'relative',
    height: isMaximized ? 'auto' : height,
    flex: isMaximized ? 1 : 'none'
  }}>
          <iframe key={iframeKey} ref={iframeRef} onLoad={handleIframeLoad} src={stackblitzUrl} className="code-editor-iframe" style={{
    height: '100%',
    flex: isMaximized ? 1 : 'none'
  }} title={title || 'Code Example'} allow="accelerometer; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts" />

          {isStuck && <div className="code-editor-stuck-overlay">
              <div className="code-editor-stuck-box">
                <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#f59e0b" strokeWidth="2">
                  <path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path>
                  <line x1="12" y1="9" x2="12" y2="13"></line>
                  <line x1="12" y1="17" x2="12.01" y2="17"></line>
                </svg>
                <p>StackBlitz is taking too long to load. This can happen when the repository was recently updated.</p>
                <button type="button" className="code-editor-button" onClick={handleRetry} style={{
    marginTop: '8px'
  }}>
                  Retry
                </button>
              </div>
            </div>}
        </div>}
    </div>;
};

The difference between a flaky prototype and a reliable production system often comes down to prompt structure. This page covers XML tags, few-shot examples, and structured outputs that make LLM responses consistent and parseable.

## The Anatomy of a Production Prompt

**The Core Components:**

1. **Role/Persona** - Sets behavior and expertise level ( values: user, assistant, system)
2. **Context** - Background information needed
3. **Instructions** - What to do, step by step
4. **Constraints** - What NOT to do, output format
5. **Examples** - Few-shot demonstrations (optional)
6. **Input** - The actual data to process

If we put all these components together, we get the following prompt. This promt looks more complex than the previous one, but this reflects the complexity of the task and the context needed to complete it when you use the API.

<LLMPlayground
  title="Playground: Structured Prompt Anatomy"
  defaultMode="advanced"
  defaultMessages={[
{
  role: "system",
  content: "You are an experienced customer support specialist for AcmeCo."
},
{
  role: "user",
  content: `[CONTEXT]
AcmeCo sells software subscriptions. Our refund policy: 30-day money-back guarantee, no questions asked. After 30 days, refunds require manager approval.

[INSTRUCTIONS]
Analyze the customer message below and:
- Identify the customer's primary issue
- Determine if this qualifies for automatic refund
- Suggest next steps for the support team

[CONSTRAINTS]
- Be empathetic but concise
- Never promise refunds outside policy
- If uncertain, recommend manager review

[INPUT]
Customer message: I purchased a subscription 25 days ago and the software keeps crashing. I want my money back immediately.`
}
]}
  height="400px"
/>

The playground constructs the final prompt from these components, which is then sent to the OpenAI API as part of the JSON message payload. You can see the exact JSON payload that will be sent in the "OpenAI JSON Message" section after submitting.

**Let's put all this together in code**

<CodeEditor file="src/prompting/structured_prompt_anatomy.ts" functionName="main" lines="22-76" title="Structured Prompt Anatomy" />

**Practice Check:**

* Write a prompt that includes role, context, instructions, constraints, examples, and input.
* Expected: All 6 components present with explicit constraints and a clear output schema.
* **Try this too:** Compare the outputs if you remove the structure and use less powerful models

**In Production:**

* Cost impact: Clear output schemas reduce parsing errors and retries (fewer re-runs).
* Reliability: Structured prompts are easier to validate and monitor.
* Performance: Slight token overhead; mitigate with caching (see Model Selection & Cost Optimization)

## XML Tags: Your Secret Weapon

**Why XML Tags Work:**

* LLMs were trained on HTML/XML (web data)
* Tags create clear boundaries in the context
* Reduced hallucinations in controlled studies

### Practical Implication

Compare these two approaches to structuring prompts:

#### ❌ Antipattern

<LLMPlayground
  title="Playground: Unstructured Prompt (Antipattern)"
  defaultMode="chat"
  defaultInput={`System: You are a helpful assistant.
User question: What is the return policy?
Background context: Our store offers a 30-day return window for all purchases. Items must be in original packaging. Electronics require a receipt.
Previous conversation: Customer asked about shipping times. We responded that standard shipping takes 5-7 business days.`}
  height="400px"
  keepInput={true}
/>

#### ✅ Best Practice

<LLMPlayground
  title="Playground: XML-Tagged Prompt"
  defaultMode="chat"
  defaultInput={`<role>You are a helpful assistant</role>

<context>
<knowledge_base>
Our store offers a 30-day return window for all purchases. Items must be in original packaging. Electronics require a receipt.
</knowledge_base>

<conversation_history>
Customer asked about shipping times. We responded that standard shipping takes 5-7 business days.
</conversation_history>
</context>

<user_question>
What is the return policy?
</user_question>`}
  height="400px"
  keepInput={true}
/>

**In Production:**

* **Cost Impact:** Tagging adds tokens; mitigate with caching .
* **Reliability:** Clear boundaries reduce off-context responses; improves evaluability.
* **Performance:** Slight overhead; offset by fewer retries and clearer parsing.
* **Real Example:** Teams report 40–60% fewer hallucinations when tags + validation are combined.

**Pattern:**

Compare these two approaches:

### Example 1: Schema in Prompt

This approach includes the XML schema and example in the prompt itself, instructing the model to follow the XML structure.

<CodeEditor file="src/prompting/structured_outputs_xml_schema_in_prompt.ts" functionName="main" lines="45-86" title="Structured Outputs: XML Schema in Prompt" />

### Example 2: Structured Outputs (XML Mode)

This approach uses prompt instructions to request XML output combined with XML parsing and validation. Note that OpenAI's API doesn't have a native XML response format like json\_object, so we rely on prompt engineering and parsing.

<CodeEditor file="src/prompting/structured_outputs_xml_mode.ts" functionName="main" lines="45-88" title="Structured Outputs: XML Mode" />

## Few-Shot Examples: Teaching by Showing

**When to Use Few-Shot:**

* Complex or subjective tasks
* Specific output format required
* Edge cases need clarification

<LLMPlayground
  title="Playground: Few-Shot Classification"
  defaultMode="chat"
  defaultInput={`<task>Classify customer sentiment</task>

<instruction>
Return ONLY one word: positive, neutral, or negative.
For each <input>, produce the corresponding <output>.
</instruction>

<examples>
<example>
<input>Your product is amazing! Best purchase ever.</input>
<output>positive</output>
</example>

<example>
<input>It's okay, does the job but nothing special.</input>
<output>neutral</output>
</example>

<example>
<input>Terrible quality. Broke after one week. Demanding refund!</input>
<output>negative</output>
</example>
</examples>

<input>
The shipping was fast and the product arrived in perfect condition. Would buy again!
</input>`}
  height="500px"
  keepInput={true}
/>

**Quality Over Quantity:**

* 3-5 examples usually enough
* More examples = more tokens = higher cost
* Examples should cover edge cases, not just obvious ones

## Structured Outputs with JSON Schemas

**Why:** Schema enforcement reduces parsing errors and retries; makes outputs machine-checkable.

Compare these two approaches:

### Example 1: Schema in Prompt

This approach includes the JSON schema in the prompt itself, instructing the model to follow the schema structure.

<CodeEditor file="src/prompting/structured_outputs_schema_in_prompt.ts" functionName="main" lines="34-67" title="Structured Outputs: JSON Schema in Prompt" />

### Example 2: Structured Outputs (JSON Mode)

This approach uses OpenAI's structured outputs feature (`response_format: json_object`) combined with schema validation. This is more reliable than just including the schema in the prompt.

<CodeEditor file="src/prompting/structured_outputs_json_mode.ts" functionName="main" lines="34-65" title="Structured Outputs: JSON Mode" />

See: [OpenAI Structured Outputs](https://openai.com/index/introducing-structured-outputs-in-the-api/) (2025), [Anthropic Prompt Engineering](https://docs.anthropic.com/en/docs/build-with-claude/prompt-engineering/overview) (2025), [Gemini Prompting Strategies](https://ai.google.dev/gemini-api/docs/prompting-strategies) (2025) in Additional Resources.

## Model-Specific Prompt Optimization

Different models have different "personalities." Here's what works best for each:

### GPT-4 / GPT-4 Turbo

```python Prompt theme={null}
# Strengths: Structured output, following complex instructions
# Best practices:
{
  "role": "senior_analyst",
  "task": "financial_analysis",
  "output_format": {
    "summary": "string",
    "key_metrics": ["string"],
    "recommendation": "buy|hold|sell"
  }
}

Input: Company revenue: $50M, growth: 15% YoY, market share: 8%
# GPT-4 excels at JSON, clear role definitions
```

### Claude (Sonnet/Opus)

```python Prompt theme={null}
# Strengths: Natural language, complex reasoning, long context
# Best practices:
<thinking>
Let me work through this step by step...
</thinking>

Analyze the quarterly financial report and identify key trends and risks.
# Claude benefits from explicit thinking blocks
# Excellent with XML tags and markdown
```

### Gemini 1.5 Pro

```python Prompt theme={null}
# Strengths: Massive context (2M tokens), multimodal
# Best practices:
[Upload entire 500-page PDF]
[Upload 10 images]
[Provide conversation history]

Based on ALL of the above context, answer: What are the main findings from the research study?
# Many teams place the query at the end; validate per task (see Gemini prompting strategies)
# Can handle entire codebases or document sets
```

***

<Quiz>
  <QuizQuestion question="Your prompt has instructions, user input, and context all in one block — the model keeps confusing them. What's the most effective fix?" options={["Make the instructions longer and more detailed", "Wrap each section in XML tags to create explicit boundaries the model can parse", "Switch to a larger model that handles ambiguity better"]} answer={1} explanation="XML tags create unambiguous boundaries between prompt sections, solving the common problem of models confusing instructions with user input or context." />

  <QuizQuestion question="You add 5 few-shot examples but the model's accuracy drops. What's the most likely cause?" options={["The examples are contradictory or show inconsistent patterns the model can't reconcile", "Few-shot always hurts — zero-shot is better", "You need at least 10 examples for few-shot to work"]} answer={0} explanation="Contradictory or inconsistent examples confuse the model more than no examples. Quality and consistency of examples matters more than quantity." />

  <QuizQuestion question="You request JSON output but the model sometimes wraps it in markdown code fences. What's the best production fix?" options={["Ask the model more forcefully to not use code fences", "Use the API's structured output / JSON mode if available, or parse with a regex fallback", "Switch to XML output instead"]} answer={1} explanation="API-level JSON mode guarantees valid JSON. If unavailable, a parsing layer with fallbacks is more reliable than prompt-only solutions." />
</Quiz>
