# llm-elasticsearch-cache

A caching layer for LLMs that exploits Elasticsearch, fully compatible with LangChain caching.

## Install

```shell
pip install llm-elasticsearch-cache
```

## Usage

The LangChain cache can be used similarly to the
[other cache integrations](https://python.langchain.com/docs/integrations/llms/llm_caching).

### Basic example

```python
from langchain.globals import set_llm_cache
from llmescache.langchain import ElasticsearchCache
from elasticsearch import Elasticsearch

es_client = Elasticsearch(hosts="http://localhost:9200")
set_llm_cache(
    ElasticsearchCache(
        es_client=es_client, 
        es_index="llm-langchain-cache", 
        metadata={"project": "my_chatgpt_project"}
    )
)
```

The `es_index` parameter can also take aliases. This allows to use the 
[ILM: Manage the index lifecycle](https://www.elastic.co/guide/en/elasticsearch/reference/current/index-lifecycle-management.html)
that we suggest to consider for managing retention and controlling cache growth.

Look at the class docstring for all parameters.

### Index the generated text

The cached data won't be searchable by default.
The developer can customize the building of the Elasticsearch document in order to add indexed text fields,
where to put, for example, the text generated by the LLM.

This can be done by subclassing end overriding a method:

```python
from llmescache.langchain import ElasticsearchCache
from elasticsearch import Elasticsearch
from langchain_core.caches import RETURN_VAL_TYPE
from typing import Any, Dict, List
from langchain.globals import set_llm_cache
import json


class SearchableElasticsearchCache(ElasticsearchCache):

    def build_document(
            self, prompt: str, llm_string: str, return_val: RETURN_VAL_TYPE
    ) -> Dict[str, Any]:
        body = super().build_document(prompt, llm_string, return_val)
        body["parsed_llm_output"] = self._parse_output(body["llm_output"])
        return body

    @staticmethod
    def _parse_output(data: List[str]) -> List[str]:
        return [json.loads(output)["kwargs"]["message"]["kwargs"]["content"] for output in data]


# let's re-use an existing cache index
es_client = Elasticsearch(hosts="http://localhost:9200")
es_client.indices.put_mapping(
    index="llm-langchain-cache", 
    body={"properties": {"parsed_llm_output": {"type": "text", "analyzer": "english"}}}
)
set_llm_cache(SearchableElasticsearchCache(es_client=es_client, es_index="llm-langchain-cache"))
```
