一键导入
gemini-api-caching
Best practices for Gemini API caching strategy including cache versioning, entity-stable keys, and cache busting for WescoBar
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Best practices for Gemini API caching strategy including cache versioning, entity-stable keys, and cache busting for WescoBar
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
| name | gemini-api-caching |
| description | Best practices for Gemini API caching strategy including cache versioning, entity-stable keys, and cache busting for WescoBar |
Implement robust caching strategies for Gemini API calls to reduce cost, improve latency, and provide better user experience while supporting cache invalidation when needed.
From AGENTS.md Section 3: "A robust, multi-layered local storage cache"
// Global cache version constant
const CACHE_VERSION = 'v2';
// Prepend to all cache keys
const getCacheKey = (entity: string, id: string) => {
return `${CACHE_VERSION}-${entity}:${id}`;
};
// Example
const portraitKey = getCacheKey('character-portrait', 'core-pablo');
// Result: "v2-character-portrait:core-pablo"
Purpose: Instant global cache invalidation by bumping version
When to bump:
// ✅ CORRECT: Entity-based key (stable)
const cacheKey = `${CACHE_VERSION}-character-portrait:${character.id}`;
// ❌ WRONG: Prompt-based key (invalidates on prompt changes)
const cacheKey = `${CACHE_VERSION}-${fullPromptText}`;
Why entity-stable:
Entity Types:
character-portrait:<id> - Character portraitsworld-scene:<id> - World/location imagesstory-illustration:<id> - Story illustrationsui-element:<name> - UI-generated imagesasync function getCharacterPortrait(
character: Character,
options?: { forceRebuild?: boolean }
): Promise<string> {
// Check force rebuild flag
if (options?.forceRebuild) {
return await generateNewPortrait(character);
}
// Build cache key
const cacheKey = `${CACHE_VERSION}-character-portrait:${character.id}`;
// Check localStorage cache
const cached = localStorage.getItem(cacheKey);
if (cached) {
console.log(`✅ Cache hit: ${cacheKey}`);
return cached; // Return cached URL
}
console.log(`⚠️ Cache miss: ${cacheKey}`);
// Generate new image
const imageUrl = await generateNewPortrait(character);
// Store in cache
localStorage.setItem(cacheKey, imageUrl);
return imageUrl;
}
async function generateAndCachePortrait(
character: Character
): Promise<string> {
// Generate image via Gemini API
const imageUrl = await geminiService.generateImage({
prompt: buildCharacterPrompt(character),
// ... other options
});
// Cache the result
const cacheKey = `${CACHE_VERSION}-character-portrait:${character.id}`;
localStorage.setItem(cacheKey, imageUrl);
console.log(`💾 Cached: ${cacheKey}`);
return imageUrl;
}
// Option 1: Force rebuild via flag
async function regeneratePortrait(character: Character) {
const imageUrl = await getCharacterPortrait(character, {
forceRebuild: true // Bypass cache
});
return imageUrl;
}
// Option 2: Clear specific cache entry
function clearPortraitCache(character: Character) {
const cacheKey = `${CACHE_VERSION}-character-portrait:${character.id}`;
localStorage.removeItem(cacheKey);
console.log(`🗑️ Cleared cache: ${cacheKey}`);
}
// Option 3: Clear all portraits
function clearAllPortraits() {
const prefix = `${CACHE_VERSION}-character-portrait:`;
Object.keys(localStorage)
.filter(key => key.startsWith(prefix))
.forEach(key => localStorage.removeItem(key));
console.log(`🗑️ Cleared all portrait cache`);
}
// Option 4: Bump global version
// Change CACHE_VERSION = 'v2' → 'v3'
// All old caches become stale automatically
interface CachedImage {
url: string;
timestamp: number;
version: string;
}
function getCachedImage(key: string, maxAge: number = 7 * 24 * 60 * 60 * 1000): string | null {
const cached = localStorage.getItem(key);
if (!cached) return null;
try {
const data: CachedImage = JSON.parse(cached);
// Check version
if (data.version !== CACHE_VERSION) {
localStorage.removeItem(key);
return null;
}
// Check age
const age = Date.now() - data.timestamp;
if (age > maxAge) {
localStorage.removeItem(key);
return null;
}
return data.url;
} catch {
// Invalid format - clear cache
localStorage.removeItem(key);
return null;
}
}
function setCachedImage(key: string, url: string) {
const data: CachedImage = {
url,
timestamp: Date.now(),
version: CACHE_VERSION
};
localStorage.setItem(key, JSON.stringify(data));
}
Combine with gemini-api-rate-limiting skill:
async function generateImagesSequentially(characters: Character[]) {
for (const character of characters) {
// Check cache first (from this skill)
const cached = await getCharacterPortrait(character);
if (cached) {
updateCharacterImage(character.id, cached);
continue; // Skip API call
}
// Cache miss - generate with rate limiting
// (from gemini-api-rate-limiting skill)
try {
const imageUrl = await generateWithTimeout(character, 30000);
updateCharacterImage(character.id, imageUrl);
} catch (error) {
handleGenerationError(character.id, error);
}
// Delay between calls (rate limiting)
await new Promise(resolve => setTimeout(resolve, 2000));
}
}
// In React component
function CharacterCard({ character }: { character: Character }) {
const [imageUrl, setImageUrl] = useState<string | null>(null);
const [isRegenerating, setIsRegenerating] = useState(false);
useEffect(() => {
// Load from cache or generate
getCharacterPortrait(character).then(setImageUrl);
}, [character.id]);
const handleRegenerate = async () => {
setIsRegenerating(true);
// Force rebuild (cache busting)
const newUrl = await regeneratePortrait(character);
setImageUrl(newUrl);
setIsRegenerating(false);
};
return (
<div>
<img src={imageUrl || placeholderImage} alt={character.name} />
<button onClick={handleRegenerate} disabled={isRegenerating}>
{isRegenerating ? 'Regenerating...' : 'Regenerate Portrait'}
</button>
</div>
);
}
// Admin/settings panel for cache management
function CacheManagement() {
const [stats, setStats] = useState({ portraits: 0, scenes: 0, total: 0 });
useEffect(() => {
// Calculate cache stats
const portraitKeys = Object.keys(localStorage)
.filter(key => key.includes('character-portrait'));
setStats({
portraits: portraitKeys.length,
scenes: 0, // Add scene counting
total: localStorage.length
});
}, []);
const clearAllCache = () => {
const confirmed = window.confirm('Clear all cached images? This will regenerate on next load.');
if (confirmed) {
Object.keys(localStorage)
.filter(key => key.startsWith(CACHE_VERSION))
.forEach(key => localStorage.removeItem(key));
alert('Cache cleared!');
window.location.reload();
}
};
return (
<div>
<h3>Gemini API Cache</h3>
<p>Cached portraits: {stats.portraits}</p>
<p>Total cached items: {stats.total}</p>
<button onClick={clearAllCache}>Clear All Cache</button>
</div>
);
}
gemini-api-rate-limiting - Combine cache with rate limitinggemini-api-error-handling - Handle cache failuresfunction getCacheSize(): number {
let total = 0;
for (const key in localStorage) {
if (localStorage.hasOwnProperty(key)) {
total += localStorage[key].length + key.length;
}
}
return total; // bytes
}
// If approaching limit, clear old entries
if (getCacheSize() > 5 * 1024 * 1024) { // 5MB
clearOldestEntries();
}
Detect project stack, apply architecture patterns, wire quality gates, and scaffold features. Use when bootstrapping a project, adding a vertical slice, wiring CI/CD, adding Docker compose, or setting up quality gates.
Generates structured Handoff Pack prompts for delegating work to Gemini with clear scope, acceptance criteria, and output format requirements.
Incrementally improve type safety by replacing string literals with enums, narrowing `any` types, and using shared types. Works in small verified batches. Preserves functionality while improving code quality.
Cloudflare DNS and infrastructure management. Manage DNS records, tunnels, Access policies, SSL certificates, and CDN caching.
Deploy projects to staging or production environments. Repo-agnostic deployment orchestration using SSH MCP for VPS operations and Cloudflare for DNS/SSL. Reads project configuration from deployments.registry.json.
Direct VPS operations via SSH MCP. Manage Docker containers, check logs, run commands, and troubleshoot services on registered VPS servers.