PHP Classes

How to Use a PHP Cache Library to Get Responses from Artificial Intelligence Services with Fewer Requests Using the Package Prompt Cache PHP: Cache the responses of AI service prompt requests

Recommend this page to a friend!
     
  Info   Example   View files Files   Install with Composer Install with Composer   Download Download   Reputation   Support forum   Blog    
Last Updated Ratings Unique User Downloads Download Rankings
2026-05-28 (18 days ago) RSS 2.0 feedNot yet rated by the usersTotal: Not yet counted Not yet ranked
Version License PHP version Categories
prompt-cache-php 1.0.0MIT/X Consortium ...8.2Cache, Artificial intelligence, PHP 8
Description 

Author

This package can cache the responses of artificial intelligence service prompt requests.

It provides a class that has a function that takes a prompt text and a callback function that can call an artificial intelligence client class to send a request to a service that provides a response to the prompt request.

The class can store the response in a cache storage container, so next time the same artificial intelligence service is called to obtain a response for the same text prompt, the class retrieves the response from the cache storage container.

Currently it can:

- Store and retrieve cached responses for prompts that match exactly the prompt text used to obtain a previous response

- Store and retrieve cached responses for prompts that are semantically similar to prompt text used to obtain a previous response when an exact prompt text match was not previously cached.

- Store and retrieve cached responses for prompts that use cosine similarity with prompt text used to obtain a previous response when an exact prompt text match was not previously cached. The cosine threshold is configurable.

- Stream the responses to split each response into small chunks that can be obtained using multiple retrieve calls to get the whole response.

- Store responses using several different cache driver classes to use different types of storage containers: SQLite, JSON file, and Redis.

- Multiple artificial intelligence service providers: OpenAI, Ollama, and a built-in deterministic fallback.

- Provide statistic counters for requests, exact hits, semantic hits, misses, an estimate of tokens saved, and an approximate USD figure.

Innovation Award
PHP Programming Innovation award nominee
May 2026
Nominee
Vote
Artificial intelligence services are often used to provide responses to requests expressed as text prompts.

The cost of using those services depends on the number of requests sent to these services.

This package can store the responses of artificial intelligence services to minimize the number of requests sent to those services.

Manuel Lemos
Picture of bUxEE
Name: bUxEE <contact>
Classes: 1 package by
Country: Italy Italy
Age: 44
All time rank: Not yet ranked
Week rank: Not yet ranked
Innovation award
Innovation award
Nominee: 1x

Instructions

Example

<?php
/**
 * 1) Exact prompt cache - the most basic case.
 *
 * Run from the package root:
 *
 * php examples/01_exact_cache.php
 *
 * Calling remember() twice with the same prompt only runs the
 * callback once. The second time you get the cached answer back,
 * so your LLM provider never sees the second request.
 */

require __DIR__ . '/autoload.php';

use
PromptCache\PromptCache;
use
PromptCache\PromptCacheManager;
use
PromptCache\Stores\SqliteStore;

// Use an in-memory SQLite store so this example doesn't leave a file
// behind on your disk. In a real app you'd just let the default
// store create storage/prompt-cache.sqlite and forget about it.
PromptCache::swap(new PromptCacheManager(array(
   
'store' => new SqliteStore(':memory:'),
)));

$prompt = 'Write me a one-line haiku about caching.';

$callCount = 0;
$expensive = function () use (&$callCount) {
   
$callCount++;
    echo
" (pretending to hit the LLM here...)\n";
   
// imagine an actual API round trip happened here
   
return "Bytes whisper softly - what was once asked, lingers - answers wake again.";
};

echo
"First call:\n";
$reply = PromptCache::remember($prompt, $expensive);
echo
" $reply\n";

echo
"Second call (same prompt):\n";
$reply = PromptCache::remember($prompt, $expensive);
echo
" $reply\n";

echo
"\nCallback ran $callCount time(s). Should be 1.\n";
echo
"Stats: " . json_encode(PromptCache::stats()) . "\n";


Details

prompt-cache

A small PHP library for caching LLM responses so you don't keep paying to get the same answer back. It does two things: exact match caching (hash the prompt, look it up) and semantic caching (compare embeddings when the exact match misses).

$reply = PromptCache::remember($prompt, function () use ($client, $prompt) {
    return $client->chat($prompt);
});

First call runs the closure. Second call gives you the saved reply. That's it.

Why I wrote this

I had a side project that talked to OpenAI a lot, and the bill was getting silly considering most of the prompts were variations of the same handful of questions. I wanted something like Cache::remember() but for LLM stuff. I couldn't find anything that wasn't tied to a specific SDK or buried inside some big agent framework, so I made one.

Requirements

  • PHP 8.2 or newer
  • PDO with the sqlite driver (for the default storage)
  • predis/predis if you want to use Redis instead
  • cURL if you use the OpenAI or Ollama embedding providers

Install

composer require prompt-cache/prompt-cache

The first time you call it, it creates a sqlite file at storage/prompt-cache.sqlite and you're done. No config to write unless you want to.

Basic usage

use PromptCache\PromptCache;

$prompt = 'Summarise this article in three bullet points: ...';

$summary = PromptCache::remember($prompt, function () use ($prompt) {
    return $myOpenAi->chat($prompt);  // or anthropic, mistral, whatever
});

The prompt gets normalised (extra whitespace flattened, timestamps and UUIDs replaced with placeholders) before it's hashed, so two prompts that only differ by formatting still hit the same cache row.

Semantic cache

When the exact hash doesn't match, semantic() looks at previously stored prompts and picks the closest one. If the similarity is above the threshold (0.92 by default), you get the old answer back.

$reply = PromptCache::semantic(
    $prompt,
    fn () => $client->chat($prompt)
);

Tighten or loosen the threshold per call if you want:

$reply = PromptCache::semantic(
    $prompt,
    fn () => $client->chat($prompt),
    0.88
);

I deliberately didn't depend on any LLM SDK. You hand me a closure, I either run it or I don't. Use whatever client you like.

Streaming

For streaming responses you get a Generator back. First call streams from upstream while quietly stitching the chunks together for the cache. Second call replays from the cache, same chunked interface, no upstream traffic.

foreach (PromptCache::stream($prompt, fn () => $client->stream($prompt)) as $chunk) {
    echo $chunk;
}

Stats

I added counters mostly because I wanted to see for myself how much I was actually saving. There's a stats() method that gives you a running total:

print_r(PromptCache::stats());
/*
Array (
    [requests]            => 1200
    [exact_hits]          => 400
    [semantic_hits]       => 300
    [misses]              => 500
    [tokens_saved]        => 2838282
    [estimated_usd_saved] => 482.22
)
*/

The token count is a back-of-the-envelope figure (4 chars per token), not a real tokeniser, so treat the dollar number as a rough sanity check, not a billing reconciliation.

Debug

If you want to see what it's actually doing:

PromptCache::debug(true);

You'll see hits, misses, similarity scores and embedding timings written to STDERR (or error_log() outside of CLI). Or pass a callable and route the events yourself:

PromptCache::debug(function ($event, $data) {
    Log::info("prompt-cache.$event", $data);
});

Storage drivers

Three options shipped. Pick whichever fits.

| Driver | Class | When to use it | |---------|------------------------------------|-------------------------------------------------| | SQLite | PromptCache\Stores\SqliteStore | Default. No setup. Good for most apps. | | File | PromptCache\Stores\FileStore | One JSON file. Handy for shipping a warm cache. | | Redis | PromptCache\Stores\RedisStore | When you have multiple workers sharing a cache. |

Want a different backend? Implement PromptCache\Contracts\Store. It has eight methods, none of them surprising.

Embedding providers

| Provider | Class | Notes | |----------|--------------------------------------------------|----------------------------------------------------| | Null | PromptCache\Embeddings\NullEmbeddingProvider | Local CRC32 thing. No keys needed. Rough quality. | | OpenAI | PromptCache\Embeddings\OpenAIEmbeddingProvider | Uses text-embedding-3-small by default. | | Ollama | PromptCache\Embeddings\OllamaEmbeddingProvider | Hits a local Ollama server. |

The Null provider exists so the semantic API works out of the box without anyone having to wire up an API key. It's not great. Use one of the real ones when it matters.

Laravel

The service provider auto-discovers. If you want to tweak settings, publish the config:

php artisan vendor:publish --tag=prompt-cache-config

Then use the facade wherever:

use PromptCache;

$reply = PromptCache::semantic($prompt, fn () => $client->chat($prompt));

Or set things from .env:

PROMPT_CACHE_DRIVER=redis
PROMPT_CACHE_EMBEDDINGS=openai
OPENAI_API_KEY=sk-...
PROMPT_CACHE_THRESHOLD=0.9

Examples

There's a folder of runnable scripts under examples/:

  • `01_exact_cache.php` - the most basic case
  • `02_semantic_cache.php` - rephrased prompt that still hits
  • `03_streaming.php` - stream, cache, replay
  • `04_stats_and_debug.php` - counters and the debug logger
  • `05_openai_real.php` - real OpenAI embeddings, needs `OPENAI_API_KEY`

Run them with php examples/01_exact_cache.php from the package root. They use a tiny autoloader so they work without `composer install`.

Tests

composer install
vendor/bin/pest

The suite covers the exact cache, semantic cache, cosine similarity, streaming, sqlite persistence, the file store, the normaliser and the stats counters.

What this isn't

It's a cache. There's no agent framework here, no RAG helpers, no chain-of-thought thing. Bring your own LLM library and put this in front of it.

License

MIT. See LICENSE.


  Files folder image Files (44)  
File Role Description
Files folder imageconfig (1 file)
Files folder imagedocs (1 file)
Files folder imageexamples (6 files)
Files folder imagesrc (2 files, 9 directories)
Files folder imagetests (9 files)
Accessible without login Plain text file CHANGELOG.md Data Auxiliary data
Accessible without login Plain text file composer.json Data Auxiliary data
Accessible without login Plain text file LICENSE Lic. License text
Accessible without login Plain text file phpunit.xml.dist Data Auxiliary data
Accessible without login Plain text file README.md Doc. Documentation

  Files folder image Files (44)  /  config  
File Role Description
  Accessible without login Plain text file prompt-cache.php Aux. Configuration script

  Files folder image Files (44)  /  docs  
File Role Description
  Accessible without login HTML file index.html Doc. Documentation

  Files folder image Files (44)  /  examples  
File Role Description
  Accessible without login Plain text file 01_exact_cache.php Example Example script
  Plain text file 02_semantic_cache.php Class Class source
  Accessible without login Plain text file 03_streaming.php Example Example script
  Accessible without login Plain text file 04_stats_and_debug.php Example Example script
  Accessible without login Plain text file 05_openai_real.php Example Example script
  Accessible without login Plain text file autoload.php Aux. Configuration script

  Files folder image Files (44)  /  src  
File Role Description
Files folder imageContracts (3 files)
Files folder imageEmbeddings (3 files)
Files folder imageExceptions (3 files)
Files folder imageLaravel (2 files)
Files folder imageNormalizers (1 file)
Files folder imageSimilarity (1 file)
Files folder imageStats (1 file)
Files folder imageStores (3 files)
Files folder imageSupport (3 files)
  Plain text file PromptCache.php Class Class source
  Plain text file PromptCacheManager.php Class Class source

  Files folder image Files (44)  /  src  /  Contracts  
File Role Description
  Plain text file EmbeddingProvider.php Class Class source
  Plain text file Normalizer.php Class Class source
  Plain text file Store.php Class Class source

  Files folder image Files (44)  /  src  /  Embeddings  
File Role Description
  Plain text file NullEmbeddingProvider.php Class Class source
  Plain text file OllamaEmbeddingProvider.php Class Class source
  Plain text file OpenAIEmbeddingProvider.php Class Class source

  Files folder image Files (44)  /  src  /  Exceptions  
File Role Description
  Plain text file EmbeddingException.php Class Class source
  Plain text file PromptCacheException.php Class Class source
  Plain text file StoreException.php Class Class source

  Files folder image Files (44)  /  src  /  Laravel  
File Role Description
  Plain text file PromptCacheFacade.php Class Class source
  Plain text file PromptCacheServiceProvider.php Class Class source

  Files folder image Files (44)  /  src  /  Normalizers  
File Role Description
  Plain text file DefaultNormalizer.php Class Class source

  Files folder image Files (44)  /  src  /  Similarity  
File Role Description
  Plain text file Cosine.php Class Class source

  Files folder image Files (44)  /  src  /  Stats  
File Role Description
  Plain text file Stats.php Class Class source

  Files folder image Files (44)  /  src  /  Stores  
File Role Description
  Plain text file FileStore.php Class Class source
  Plain text file RedisStore.php Class Class source
  Plain text file SqliteStore.php Class Class source

  Files folder image Files (44)  /  src  /  Support  
File Role Description
  Plain text file Debug.php Class Class source
  Plain text file TokenEstimator.php Class Class source
  Plain text file Uuid.php Class Class source

  Files folder image Files (44)  /  tests  
File Role Description
  Accessible without login Plain text file ExactCacheTest.php Example Example script
  Accessible without login Plain text file FileStoreTest.php Example Example script
  Accessible without login Plain text file NormalizerTest.php Example Example script
  Accessible without login Plain text file Pest.php Example Example script
  Plain text file SemanticCacheTest.php Class Class source
  Accessible without login Plain text file SimilarityTest.php Example Example script
  Accessible without login Plain text file SqlitePersistenceTest.php Example Example script
  Accessible without login Plain text file StatsTest.php Example Example script
  Accessible without login Plain text file StreamingTest.php Example Example script

The PHP Classes site has supported package installation using the Composer tool since 2013, as you may verify by reading this instructions page.
Install with Composer Install with Composer
 Version Control Unique User Downloads  
 100%
Total:0
This week:0