google-file-search

SKILL.md

Google File Search

Comprehensive skill for implementing Google File Search API with Gemini models for Retrieval-Augmented Generation (RAG).

Overview

Google File Search provides managed RAG capabilities through:

  • Automatic document chunking and embedding generation
  • Semantic search across multiple document types
  • Metadata-based filtering for targeted retrieval
  • Grounding citations showing source documents
  • Persistent storage with file search stores
  • Integration with Gemini 2.5 models

Two Official SDKs are available:

  • TypeScript/JavaScript: @google/genai npm package
  • Python: google-genai pip package

CRITICAL: Use Official SDKs Only

Do NOT use manual REST API calls or deprecated packages. Always use the official SDKs.

TypeScript/JavaScript

npm install @google/genai

Python

pip install google-genai

Security: API Key Handling

CRITICAL: Never hardcode API keys.

// ✅ CORRECT - TypeScript
const ai = new GoogleGenAI({ apiKey: process.env.GOOGLE_API_KEY! });

// ❌ WRONG
const ai = new GoogleGenAI({ apiKey: 'sk-abc123...' });
# ✅ CORRECT - Python
client = genai.Client(api_key=os.getenv("GOOGLE_API_KEY"))

# ❌ WRONG
client = genai.Client(api_key="sk-abc123...")

Get API keys from: https://aistudio.google.com/apikey

TypeScript Implementation

Initialize Client

import { GoogleGenAI } from '@google/genai';

const ai = new GoogleGenAI({ apiKey: process.env.GOOGLE_API_KEY! });

Create File Search Store

const store = await ai.fileSearchStores.create({
  config: { displayName: 'my-knowledge-base' }
});
console.log(`Store created: ${store.name}`);

Upload and Index Documents

Use uploadToFileSearchStore() which uploads AND indexes in one operation.

async function uploadFile(storeName: string, filePath: string, filename: string) {
  let operation = await ai.fileSearchStores.uploadToFileSearchStore({
    file: filePath,
    fileSearchStoreName: storeName,
    config: { displayName: filename }
  });

  // Wait for indexing to complete
  while (!operation.done) {
    await new Promise(resolve => setTimeout(resolve, 2000));
    operation = await ai.operations.get({ operation });
  }
  console.log(`Indexed: ${filename}`);
}

Semantic Search

async function search(storeName: string, query: string) {
  const response = await ai.models.generateContent({
    model: 'gemini-2.5-flash',
    contents: query,
    config: {
      tools: [{
        fileSearch: {
          fileSearchStoreNames: [storeName]
        }
      }]
    }
  });

  const sources = response.candidates?.[0]?.groundingMetadata?.groundingChunks?.map((c: any) => ({
    uri: c.retrievedContext?.uri,
    title: c.retrievedContext?.title
  })) || [];

  return { answer: response.text, sources };
}

Next.js API Route Example

// app/api/rag/route.ts
import { GoogleGenAI } from '@google/genai';
import { NextResponse } from 'next/server';

const ai = new GoogleGenAI({ apiKey: process.env.GOOGLE_API_KEY! });

export async function POST(request: Request) {
  const { query, storeId } = await request.json();

  const response = await ai.models.generateContent({
    model: 'gemini-2.5-flash',
    contents: query,
    config: {
      tools: [{ fileSearch: { fileSearchStoreNames: [storeId] }}],
      systemInstruction: 'Answer based on the documents. Cite sources.'
    }
  });

  const sources = response.candidates?.[0]?.groundingMetadata?.groundingChunks?.map((c: any) => ({
    uri: c.retrievedContext?.uri,
    title: c.retrievedContext?.title
  })) || [];

  return NextResponse.json({ answer: response.text, sources });
}

Python Implementation

Initialize Client

import os
from google import genai
from google.genai import types

client = genai.Client(api_key=os.getenv("GOOGLE_API_KEY"))

Create File Search Store

store = client.file_search_stores.create(
    config={"display_name": "my-knowledge-base"}
)
print(f"Store created: {store.name}")

Upload and Index Documents

Use upload_to_file_search_store() which uploads AND indexes in one operation.

import time
from pathlib import Path

def upload_file(store_name: str, file_path: str, filename: str = None):
    path = Path(file_path)

    operation = client.file_search_stores.upload_to_file_search_store(
        file=str(path),
        file_search_store_name=store_name,
        config={"display_name": filename or path.name}
    )

    # Wait for indexing to complete
    while not operation.done:
        time.sleep(2)
        operation = client.operations.get(operation)

    print(f"Indexed: {path.name}")

Semantic Search

def search(store_name: str, query: str, model: str = "gemini-2.5-flash"):
    file_search = types.FileSearch(file_search_store_names=[store_name])
    tool = types.Tool(file_search=file_search)

    response = client.models.generate_content(
        model=model,
        contents=query,
        config=types.GenerateContentConfig(tools=[tool])
    )

    sources = []
    if response.candidates:
        gm = getattr(response.candidates[0], 'grounding_metadata', None)
        if gm and hasattr(gm, 'grounding_chunks'):
            for chunk in gm.grounding_chunks:
                rc = getattr(chunk, 'retrieved_context', None)
                if rc:
                    sources.append({
                        "uri": getattr(rc, 'uri', ''),
                        "title": getattr(rc, 'title', '')
                    })

    return {"answer": response.text, "sources": sources}

FastAPI Endpoint Example

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from google import genai
from google.genai import types
import os

app = FastAPI()
client = genai.Client(api_key=os.getenv("GOOGLE_API_KEY"))

class RAGQuery(BaseModel):
    query: str
    store_id: str

@app.post("/rag/search")
async def rag_search(request: RAGQuery):
    try:
        file_search = types.FileSearch(file_search_store_names=[request.store_id])
        tool = types.Tool(file_search=file_search)

        response = client.models.generate_content(
            model="gemini-2.5-flash",
            contents=request.query,
            config=types.GenerateContentConfig(tools=[tool])
        )

        sources = []
        if response.candidates:
            gm = getattr(response.candidates[0], 'grounding_metadata', None)
            if gm and hasattr(gm, 'grounding_chunks'):
                for chunk in gm.grounding_chunks:
                    rc = getattr(chunk, 'retrieved_context', None)
                    if rc:
                        sources.append({
                            "uri": getattr(rc, 'uri', ''),
                            "title": getattr(rc, 'title', '')
                        })

        return {"answer": response.text, "sources": sources}
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

Supported Models

  • gemini-2.5-flash: Fast model for quick responses (RECOMMENDED)
  • gemini-2.5-pro: Production model for complex reasoning

Supported File Types

Documents: PDF, DOCX, ODT, PPTX, XLSX, CSV, TXT, MD Code: Python, JavaScript, Java, TypeScript, Go, Rust, SQL Data: JSON, XML, YAML, HTML Archives: ZIP (automatically extracted)

Over 100 MIME types supported.

Storage Limits

Per-Document:

  • Maximum file size: 100 MB
  • Recommended store size: Under 20 GB

Total Storage by Tier:

  • Free: 1 GB
  • Tier 1: 10 GB
  • Tier 2: 100 GB
  • Tier 3: 1 TB

Chunking Configuration

TypeScript

await ai.fileSearchStores.uploadToFileSearchStore({
  file: filePath,
  fileSearchStoreName: storeName,
  config: {
    displayName: filename,
    chunkingConfig: {
      whiteSpaceConfig: {
        maxTokensPerChunk: 200,
        maxOverlapTokens: 20
      }
    }
  }
});

Python

client.file_search_stores.upload_to_file_search_store(
    file=file_path,
    file_search_store_name=store_name,
    config={
        "display_name": filename,
        "chunking_config": {
            "white_space_config": {
                "max_tokens_per_chunk": 200,
                "max_overlap_tokens": 20
            }
        }
    }
)

Metadata Filtering

Adding Metadata (TypeScript)

config: {
  displayName: filename,
  customMetadata: [
    { key: 'author', stringValue: 'John Doe' },
    { key: 'year', numericValue: 2024 }
  ]
}

Adding Metadata (Python)

config={
    "display_name": filename,
    "custom_metadata": [
        {"key": "author", "string_value": "John Doe"},
        {"key": "year", "numeric_value": 2024}
    ]
}

Filtering Queries (Python)

file_search = types.FileSearch(
    file_search_store_names=[store_name]
)
file_search.metadata_filter = 'author="John Doe" AND year >= 2024'

Best Practices

  1. Chunk Size Optimization

    • Technical docs: 300-500 tokens
    • General content: 200-300 tokens
    • Code: 100-200 tokens
    • Use overlap for context preservation
  2. Metadata Strategy

    • Add author, date, category during upload
    • Use consistent naming conventions
    • Plan filtering needs upfront
  3. Store Organization

    • Separate stores by domain/project
    • Keep stores under 20 GB for optimal retrieval
    • Name stores descriptively
  4. Citation Handling

    • Always extract grounding metadata
    • Display sources to users
    • Enable fact-checking workflows

Troubleshooting

Issue: Files not uploading

  • Check file size (max 100 MB)
  • Verify file type is supported
  • Ensure API key has correct permissions

Issue: Poor search results

  • Adjust chunking configuration
  • Add relevant metadata for filtering
  • Try different chunk sizes

Issue: Missing citations

  • Check response for grounding_metadata
  • Ensure store has indexed documents
  • Verify model supports grounding

Available Templates

  • templates/typescript-client.ts - Complete TypeScript client
  • templates/python-client.py - Complete Python client
  • templates/store-config.json - Store configuration
  • templates/chunking-config.json - Chunking settings
  • templates/env.example - Environment variables

References

Version

Skill Version: 2.0.0 Last Updated: 2026-01-30 Compatible With: Gemini 2.5 Pro/Flash, @google/genai (TS), google-genai (Python)

Weekly Installs
4
GitHub Stars
3
First Seen
Jan 28, 2026
Installed on
codex4
gemini-cli4
opencode3
antigravity3
claude-code3
github-copilot3