smithery/ericthayer

integrate-gemini

Implement robust Google Gemini API integration with streaming, error handling, and type safety.

Installation

$ npx skills add smithery/ericthayer --skill integrate-gemini

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery/ericthayer.

npx skills add smithery/ericthayer

Browse all from smithery/ericthayer

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Declared
Cline Not declared
OpenCode Not declared

Skill metadata

Parsed from SKILL.md frontmatter.

Declared agents gemini

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 4,431 B
  • docs SUMMARY.md 119 B

History

  1. First recorded snapshot · 0 installs

SKILL.md

integrate-gemini

Use this skill when the user wants to "connect to Gemini", "add AI features", or "fix API generation".

Requirements (from rules/gemini.md)

  1. Safety: NEVER hardcode API keys. Use import.meta.env or similar.
  2. UX: Always show Loading / Streaming states.
  3. Reliability: Handle errors gracefully (network, blocking, rate limits).
  4. Attribution: AI content must be labeled.

Usage

  1. Check for Client Library: Ensure @google/generative-ai is installed. If not, install it.
  2. Create the Hook: Create hooks/useGemini.ts (or similar).
  3. Implement Logic: Use the pattern below to handle the complexities of streaming.

Code Pattern: useGemini Hook

import { useState, useCallback } from 'react';
import { GoogleGenerativeAI, GenerativeModel } from '@google/generative-ai';

// Initialize outside component if key is static, or inside if dynamic
// const genAI = new GoogleGenerativeAI(import.meta.env.VITE_GEMINI_API_KEY);

interface UseGeminiOptions {
  modelName?: string;
  // Gemini 3 Thinking Config
  useThinking?: boolean; 
  thinkingBudget?: number; // e.g., 16384
  onError?: (error: Error) => void;
}

export const useGemini = ({ 
  modelName = "gemini-3-pro-preview", // Updated default to Gemini 3
  useThinking = false,
  thinkingBudget = 16384,
  onError 
}: UseGeminiOptions = {}) => {
  const [response, setResponse] = useState<string>('');
  const [isLoading, setIsLoading] = useState(false);
  const [isStreaming, setIsStreaming] = useState(false);
  const [error, setError] = useState<Error | null>(null);

  const generate = useCallback(async (prompt: string, images?: File[]) => {
    setIsLoading(true);
    setIsStreaming(false);
    setError(null);
    setResponse('');

    try {
      const genAI = new GoogleGenerativeAI(import.meta.env.VITE_GEMINI_API_KEY);
      
      // Determine configuration based on Thinking
      const generationConfig: any = {};
      if (useThinking) {
        generationConfig.thinkingConfig = { thinkingBudget };
        // Must increase maxOutputTokens to accommodate thinking process
        generationConfig.maxOutputTokens = thinkingBudget + 8192; 
      }

      const model = genAI.getGenerativeModel({ 
        model: modelName,
        generationConfig 
      });

      // Prepare contents (Text + optional Images)
      let contents: any[] = [{ text: prompt }];
      if (images && images.length > 0) {
        // Simple example for adding images (implementation varies by input format)
        // Ideally convert File -> Base64 here
        // const imageParts = await Promise.all(images.map(fileToGenerativePart));
        // contents = [...imageParts, { text: prompt }];
      }

      // Use streaming for better UX
      const result = await model.generateContentStream(contents);
      
      setIsLoading(false); // Initial wait is over, now we stream
      setIsStreaming(true);

      let fullText = '';
      for await (const chunk of result.stream) {
        const chunkText = chunk.text();
        fullText += chunkText;
        setResponse(prev => prev + chunkText);
      }
      
      setIsStreaming(false);
      return fullText;

    } catch (err: any) {
      const errorObj = err instanceof Error ? err : new Error(String(err));
      setError(errorObj);
      setIsLoading(false);
      setIsStreaming(false);
      if (onError) onError(errorObj);
      console.error("Gemini API Error:", err);
    }
  }, [modelName, useThinking, thinkingBudget, onError]);

  return {
    generate,
    response,
    isLoading,
    isStreaming,
    error,
    attribution: "Generated by Google Gemini" 
  };
};

UI Implementation Tips

  • Thinking UI: If useThinking is true, the isLoading state might be longer. Considering adding a "Reasoning..." label.
  • Cancel Support: Consider passing an AbortSignal to generate for long-running thinking tasks.
  • Images: If using gemini-3-pro-image-preview, use standard generateContent (not stream) and handle base64 output.
  • Disable Submit: disabled={isLoading || isStreaming}
  • Loading Spinner: Show when isLoading is true.
  • Cursor/Typewriter: Show when isStreaming is true.
  • Error Banner: Display error.message if error exists.