OrkaJS
Orka.JS

Cache

Évitez les appels LLM et embedding redondants grâce à un cache intelligent. Réduisez la latence, économisez des coûts et améliorez le débit avec des caches en mémoire ou Redis.

Pourquoi le Cache ?

Les appels API LLM sont coûteux et lents. Lorsque le même prompt est envoyé plusieurs fois (ex : questions utilisateur répétées, traitement par lots ou itérations de développement), le cache évite les appels API redondants en retournant instantanément les résultats précédemment calculés.

0ms

Latence cache hit (vs 500-3000ms appel API)

$0

Coût par réponse en cache

100%

Résultats déterministes

# Architecture

Le système de cache d'Orka AI est composé de trois couches qui fonctionnent ensemble :

1. Cache Store (Backend de Stockage)

Où les données en cache sont stockées. Choisissez entre MemoryCache (en processus, rapide, sans dépendances) ou RedisCache (distribué, persistant, partagé entre instances).

2. CachedLLM (Cache de Réponses LLM)

Enveloppe n'importe quel LLMAdapter et met en cache les réponses generate(). Même prompt + options = réponse en cache instantanée.

3. CachedEmbeddings (Cache d'Embeddings)

Met en cache les vecteurs d'embedding par texte d'entrée. Évite de recalculer les embeddings pour les chunks de texte déjà vus.

# MemoryCache (En Mémoire)

Le store de cache le plus simple. Les données sont stockées en processus via une Map. Aucune dépendance externe requise. Idéal pour le développement, les serveurs mono-instance et les processus de courte durée.

memory-cache.ts
import { MemoryCache } from 'orkajs/cache/memory';
 
const cache = new MemoryCache({
maxSize: 1000, // Maximum number of entries (default: 1000)
ttlMs: 1000 * 60 * 30, // Time-to-live: 30 minutes (optional)
namespace: 'my-app' // Key prefix for isolation (optional)
});
 
// Basic operations
await cache.set('key', { data: 'value' });
const value = await cache.get('key'); // { data: 'value' }
const exists = await cache.has('key'); // true
await cache.delete('key');
await cache.clear();
 
// Get cache statistics
const stats = cache.getStats();
console.log(stats);
// { hits: 42, misses: 8, size: 150, hitRate: 0.84 }

🧠 Fonctionnalités MemoryCache

  • Zéro dépendances — fonctionne immédiatement
  • Support TTL — les entrées expirent automatiquement
  • Taille max avec éviction type LRU (plus ancien d'abord)
  • Isolation par namespace — plusieurs caches dans une instance
  • Statistiques hit/miss pour le monitoring

# RedisCache (Distribué)

Pour les environnements de production avec plusieurs instances de serveur, Redis fournit un cache partagé et persistant. Les données survivent aux redémarrages du serveur et sont accessibles depuis n'importe quelle instance.

📦 Installation Requise

RedisCache nécessite le package redis :

npm install redis
import { RedisCache } from 'orkajs/cache/redis';
 
const cache = new RedisCache({
url: 'redis://localhost:6379', // Redis connection URL
keyPrefix: 'orka:', // Key prefix (default: 'orka:')
ttlMs: 1000 * 60 * 60, // TTL: 1 hour (optional)
});
 
// Connect to Redis (auto-connects on first operation)
await cache.connect();
 
// Same API as MemoryCache
await cache.set('key', { data: 'value' });
const value = await cache.get('key');
 
// Disconnect when done
await cache.disconnect();

MemoryCache

  • Pas de dépendances
  • Le plus rapide (en processus)
  • Perdu au redémarrage
  • Non partagé entre instances

RedisCache

  • Persistant entre redémarrages
  • Partagé entre instances
  • Nécessite un serveur Redis
  • Latence réseau (~1ms)

# CachedLLM — Cache de Réponses LLM

CachedLLM enveloppe n'importe quel LLMAdapter et met en cache les réponses de manière transparente. Il implémente l'interface LLMAdapter, vous pouvez donc l'utiliser comme remplacement direct partout où vous utilisez un LLM.

cached-llm.ts
import { OpenAIAdapter } from 'orkajs/adapters/openai';
import { MemoryCache } from 'orkajs/cache/memory';
import { CachedLLM } from 'orkajs/cache/llm';
 
// 1. Create your LLM adapter
const llm = new OpenAIAdapter({
apiKey: process.env.OPENAI_API_KEY!,
model: 'gpt-4o-mini'
});
 
// 2. Create a cache store
const cache = new MemoryCache({ maxSize: 500, ttlMs: 1000 * 60 * 30 });
 
// 3. Wrap with CachedLLM
const cachedLLM = new CachedLLM(llm, cache, {
ttlMs: 1000 * 60 * 60 // Override TTL: cache for 1 hour
});
 
// First call — hits the API (~800ms)
const result1 = await cachedLLM.generate('What is TypeScript?');
console.log(result1.content); // "TypeScript is a typed superset..."
 
// Second call — instant from cache (~0ms)
const result2 = await cachedLLM.generate('What is TypeScript?');
console.log(result2.content); // Same response, from cache
 
// Use in Orka config — transparent replacement
import { createOrka } from 'orkajs';
 
const orka = createOrka({
llm: cachedLLM, // ← Drop-in replacement
vectorDB: /* ... */
});
 
// All orka.ask(), orka.generate(), etc. now use caching
const answer = await orka.ask({
question: 'What is TypeScript?',
knowledge: 'docs'
});

🔑 Génération de Clé de Cache

La clé de cache est générée à partir du prompt ET des options (temperature, maxTokens, systemPrompt, etc.). Cela signifie :

  • Même prompt + mêmes options = cache hit ✅
  • Même prompt + température différente = cache miss ❌ (clé différente)

# CachedEmbeddings — Cache d'Embeddings

Embedder le même texte plusieurs fois est du gaspillage. CachedEmbeddings met en cache les vecteurs d'embedding par texte d'entrée et n'envoie à l'API que les textes non mis en cache.

cached-embeddings.ts
import { OpenAIAdapter } from 'orkajs/adapters/openai';
import { MemoryCache } from 'orkajs/cache/memory';
import { CachedEmbeddings } from 'orkajs/cache/embeddings';
 
const llm = new OpenAIAdapter({ apiKey: process.env.OPENAI_API_KEY! });
const cache = new MemoryCache({ maxSize: 10000 });
 
const cachedEmbed = new CachedEmbeddings(llm, cache, {
ttlMs: 1000 * 60 * 60 * 24 // Cache embeddings for 24 hours
});
 
// First call — computes all 3 embeddings via API
const embeddings1 = await cachedEmbed.embed([
'Hello world',
'TypeScript is great',
'Orka AI framework'
]);
 
// Second call — all 3 from cache (0 API calls)
const embeddings2 = await cachedEmbed.embed([
'Hello world',
'TypeScript is great',
'Orka AI framework'
]);
 
// Mixed call — only "New text" hits the API, others from cache
const embeddings3 = await cachedEmbed.embed([
'Hello world', // ← from cache
'New text here', // ← API call
'Orka AI framework' // ← from cache
]);

Batching Intelligent

CachedEmbeddings vérifie le cache pour chaque texte individuellement, puis regroupe uniquement les textes non mis en cache en un seul appel API. Cela signifie que si vous embeddez 100 textes et 80 sont en cache, seuls 20 sont envoyés à l'API en un seul lot.

# Configuration Production avec Redis

En production, utilisez RedisCache pour partager le cache entre plusieurs instances de serveur et persister les données entre les redémarrages.

redis-cache.ts
import { createOrka, OpenAIAdapter } from 'orkajs/core';
import { RedisCache } from 'orkajs/cache/redis';
import { CachedLLM } from 'orkajs/cache/llm';
import { CachedEmbeddings } from 'orkajs/cache/embeddings';
 
// Shared Redis cache
const redisCache = new RedisCache({
url: process.env.REDIS_URL!, // e.g. 'redis://redis:6379'
keyPrefix: 'orka:prod:',
ttlMs: 1000 * 60 * 60 * 4 // 4 hours
});
 
const llm = new OpenAIAdapter({
apiKey: process.env.OPENAI_API_KEY!,
model: 'gpt-4o-mini'
});
 
// Cache both LLM responses and embeddings
const cachedLLM = new CachedLLM(llm, redisCache);
const cachedEmbed = new CachedEmbeddings(llm, redisCache);
 
const orka = createOrka({
llm: cachedLLM,
vectorDB: /* ... */
});
 
// All operations now use Redis-backed caching
const result = await orka.ask({
question: 'How do I deploy my app?',
knowledge: 'documentation'
});
 
// Monitor cache performance
const stats = redisCache.getStats();
console.log(`Cache hit rate: ${(stats.hitRate * 100).toFixed(1)}%`);
 
// Cleanup on shutdown
process.on('SIGTERM', async () => {
await redisCache.disconnect();
});

# Cache Store Personnalisé

Implémentez l'interface CacheStore pour créer votre propre backend de cache (ex : DynamoDB, Memcached, SQLite).

dynamodb-cache.ts
import type { CacheStore } from 'orkajs/cache';
 
class DynamoDBCache implements CacheStore {
readonly name = 'dynamodb-cache';
 
async get<T>(key: string): Promise<T | undefined> {
// Your DynamoDB get logic
}
 
async set<T>(key: string, value: T, ttlMs?: number): Promise<void> {
// Your DynamoDB put logic
}
 
async delete(key: string): Promise<boolean> {
// Your DynamoDB delete logic
}
 
async clear(): Promise<void> {
// Your DynamoDB scan + delete logic
}
 
async has(key: string): Promise<boolean> {
// Your DynamoDB exists check
}
}
 
// Use with CachedLLM
const cache = new DynamoDBCache();
const cachedLLM = new CachedLLM(llm, cache);

Bonnes Pratiques

1. Définissez des TTL Appropriés

TTL court (5-30 min) pour le contenu dynamique. TTL long (heures/jours) pour les bases de connaissances stables. Pas de TTL pour les données immuables comme les embeddings.

2. Surveillez les Taux de Hit

Utilisez getStats() pour surveiller l'efficacité du cache. Un taux de hit inférieur à 50% peut indiquer que le cache est trop petit ou le TTL trop court.

3. Ne Cachez Pas les Appels Non-Déterministes

Si vous utilisez une température élevée (>0.8) pour la génération créative, le cache peut retourner des sorties créatives obsolètes. Envisagez de désactiver le cache pour les tâches créatives.

4. Utilisez les Namespaces

Utilisez des namespaces ou préfixes de clé différents pour les différents environnements (dev, staging, prod) pour éviter la pollution du cache.

Imports Tree-shakeable

// ✅ Import only what you need
import { MemoryCache } from 'orkajs/cache/memory';
import { CachedLLM } from 'orkajs/cache/llm';
 
// ✅ Or import from index
import { MemoryCache, RedisCache, CachedLLM, CachedEmbeddings } from 'orkajs/cache';