mirror of
https://github.com/aljazceru/Auto-GPT.git
synced 2025-12-17 22:14:28 +01:00
Implement redis memory backend.
This commit is contained in:
21
README.md
21
README.md
@@ -149,6 +149,27 @@ are loaded for the agent at any given time.
|
|||||||
2. Choose the `Starter` plan to avoid being charged.
|
2. Choose the `Starter` plan to avoid being charged.
|
||||||
3. Find your API key and region under the default project in the left sidebar.
|
3. Find your API key and region under the default project in the left sidebar.
|
||||||
|
|
||||||
|
|
||||||
|
## Redis Setup
|
||||||
|
|
||||||
|
Install docker desktop.
|
||||||
|
|
||||||
|
Run:
|
||||||
|
```
|
||||||
|
docker run -d --name redis-stack-server -p 6379:6379 redis/redis-stack-server:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
Set the following environment variables:
|
||||||
|
```
|
||||||
|
MEMORY_BACKEND=redis
|
||||||
|
REDIS_HOST=localhost
|
||||||
|
REDIS_PORT=6379
|
||||||
|
REDIS_PASSWORD=
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that this is not intended to be run facing the internet and is not secure, do not expose redis to the internet without a password or at all really.
|
||||||
|
|
||||||
|
|
||||||
### Setting up environment variables
|
### Setting up environment variables
|
||||||
For Windows Users:
|
For Windows Users:
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -12,3 +12,4 @@ docker
|
|||||||
duckduckgo-search
|
duckduckgo-search
|
||||||
google-api-python-client #(https://developers.google.com/custom-search/v1/overview)
|
google-api-python-client #(https://developers.google.com/custom-search/v1/overview)
|
||||||
pinecone-client==2.2.1
|
pinecone-client==2.2.1
|
||||||
|
redis
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import browse
|
import browse
|
||||||
import json
|
import json
|
||||||
from memory.pinecone import PineconeMemory
|
from memory.pinecone import PineconeMemory
|
||||||
|
from memory.redismem import RedisMemory
|
||||||
import datetime
|
import datetime
|
||||||
import agent_manager as agents
|
import agent_manager as agents
|
||||||
import speak
|
import speak
|
||||||
@@ -52,7 +53,10 @@ def get_command(response):
|
|||||||
|
|
||||||
|
|
||||||
def execute_command(command_name, arguments):
|
def execute_command(command_name, arguments):
|
||||||
memory = PineconeMemory(cfg=cfg)
|
if cfg.memory_backend == "pinecone":
|
||||||
|
memory = PineconeMemory(cfg=cfg)
|
||||||
|
else:
|
||||||
|
memory = RedisMemory(cfg=cfg)
|
||||||
try:
|
try:
|
||||||
if command_name == "google":
|
if command_name == "google":
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,12 @@ class Config(metaclass=Singleton):
|
|||||||
# User agent headers to use when browsing web
|
# User agent headers to use when browsing web
|
||||||
# Some websites might just completely deny request with an error code if no user agent was found.
|
# Some websites might just completely deny request with an error code if no user agent was found.
|
||||||
self.user_agent_header = {"User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36"}
|
self.user_agent_header = {"User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36"}
|
||||||
|
self.redis_host = os.getenv("REDIS_HOST")
|
||||||
|
self.redis_port = os.getenv("REDIS_PORT")
|
||||||
|
self.redis_password = os.getenv("REDIS_PASSWORD")
|
||||||
|
# Note that indexes must be created on db 0 in redis, this is not configureable.
|
||||||
|
|
||||||
|
self.memory_backend = os.getenv("MEMORY_BACKEND", 'pinecone')
|
||||||
# Initialize the OpenAI API client
|
# Initialize the OpenAI API client
|
||||||
openai.api_key = self.openai_api_key
|
openai.api_key = self.openai_api_key
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import json
|
|||||||
import random
|
import random
|
||||||
import commands as cmd
|
import commands as cmd
|
||||||
from memory.pinecone import PineconeMemory
|
from memory.pinecone import PineconeMemory
|
||||||
|
from memory.redismem import RedisMemory
|
||||||
import data
|
import data
|
||||||
import chat
|
import chat
|
||||||
from colorama import Fore, Style
|
from colorama import Fore, Style
|
||||||
@@ -283,8 +284,11 @@ user_input = "Determine which next command to use, and respond using the format
|
|||||||
|
|
||||||
# Initialize memory and make sure it is empty.
|
# Initialize memory and make sure it is empty.
|
||||||
# this is particularly important for indexing and referencing pinecone memory
|
# this is particularly important for indexing and referencing pinecone memory
|
||||||
memory = PineconeMemory(cfg)
|
if cfg.memory_backend == "pinecone":
|
||||||
memory.clear()
|
memory = PineconeMemory(cfg)
|
||||||
|
memory.clear()
|
||||||
|
else:
|
||||||
|
memory = RedisMemory(cfg)
|
||||||
|
|
||||||
print('Using memory of type: ' + memory.__class__.__name__)
|
print('Using memory of type: ' + memory.__class__.__name__)
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
"""Base class for memory providers."""
|
||||||
import abc
|
import abc
|
||||||
from config import AbstractSingleton
|
from config import AbstractSingleton
|
||||||
import openai
|
import openai
|
||||||
|
|||||||
@@ -0,0 +1,135 @@
|
|||||||
|
"""Redis memory provider."""
|
||||||
|
from typing import Any, List, Optional
|
||||||
|
import redis
|
||||||
|
from redis.commands.search.field import VectorField, TextField
|
||||||
|
from redis.commands.search.query import Query
|
||||||
|
from redis.commands.search.indexDefinition import IndexDefinition, IndexType
|
||||||
|
import traceback
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from memory.base import MemoryProviderSingleton, get_ada_embedding
|
||||||
|
|
||||||
|
|
||||||
|
SCHEMA = [
|
||||||
|
TextField("data"),
|
||||||
|
VectorField(
|
||||||
|
"embedding",
|
||||||
|
"HNSW",
|
||||||
|
{
|
||||||
|
"TYPE": "FLOAT32",
|
||||||
|
"DIM": 1536,
|
||||||
|
"DISTANCE_METRIC": "COSINE"
|
||||||
|
}
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class RedisMemory(MemoryProviderSingleton):
|
||||||
|
def __init__(self, cfg):
|
||||||
|
"""
|
||||||
|
Initializes the Redis memory provider.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cfg: The config object.
|
||||||
|
|
||||||
|
Returns: None
|
||||||
|
"""
|
||||||
|
redis_host = cfg.redis_host
|
||||||
|
redis_port = cfg.redis_port
|
||||||
|
redis_password = cfg.redis_password
|
||||||
|
self.dimension = 1536
|
||||||
|
self.redis = redis.Redis(
|
||||||
|
host=redis_host,
|
||||||
|
port=redis_port,
|
||||||
|
password=redis_password,
|
||||||
|
db=0 # Cannot be changed
|
||||||
|
)
|
||||||
|
self.redis.flushall()
|
||||||
|
try:
|
||||||
|
self.redis.ft("gpt").create_index(
|
||||||
|
fields=SCHEMA,
|
||||||
|
definition=IndexDefinition(
|
||||||
|
prefix=["gpt:"],
|
||||||
|
index_type=IndexType.HASH
|
||||||
|
)
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
print("Error creating Redis search index: ", e)
|
||||||
|
self.vec_num = 0
|
||||||
|
|
||||||
|
def add(self, data: str) -> str:
|
||||||
|
"""
|
||||||
|
Adds a data point to the memory.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data: The data to add.
|
||||||
|
|
||||||
|
Returns: Message indicating that the data has been added.
|
||||||
|
"""
|
||||||
|
vector = get_ada_embedding(data)
|
||||||
|
vector = np.array(vector).astype(np.float32).tobytes()
|
||||||
|
data_dict = {
|
||||||
|
b"data": data,
|
||||||
|
"embedding": vector
|
||||||
|
}
|
||||||
|
self.redis.hset(f"gpt:{self.vec_num}", mapping=data_dict)
|
||||||
|
_text = f"Inserting data into memory at index: {self.vec_num}:\n"\
|
||||||
|
f"data: {data}"
|
||||||
|
self.vec_num += 1
|
||||||
|
return _text
|
||||||
|
|
||||||
|
def get(self, data: str) -> Optional[List[Any]]:
|
||||||
|
"""
|
||||||
|
Gets the data from the memory that is most relevant to the given data.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data: The data to compare to.
|
||||||
|
|
||||||
|
Returns: The most relevant data.
|
||||||
|
"""
|
||||||
|
return self.get_relevant(data, 1)
|
||||||
|
|
||||||
|
def clear(self) -> str:
|
||||||
|
"""
|
||||||
|
Clears the redis server.
|
||||||
|
|
||||||
|
Returns: A message indicating that the memory has been cleared.
|
||||||
|
"""
|
||||||
|
self.redis.flushall()
|
||||||
|
return "Obliviated"
|
||||||
|
|
||||||
|
def get_relevant(
|
||||||
|
self,
|
||||||
|
data: str,
|
||||||
|
num_relevant: int = 5
|
||||||
|
) -> Optional[List[Any]]:
|
||||||
|
"""
|
||||||
|
Returns all the data in the memory that is relevant to the given data.
|
||||||
|
Args:
|
||||||
|
data: The data to compare to.
|
||||||
|
num_relevant: The number of relevant data to return.
|
||||||
|
|
||||||
|
Returns: A list of the most relevant data.
|
||||||
|
"""
|
||||||
|
query_embedding = get_ada_embedding(data)
|
||||||
|
base_query = f"*=>[KNN {num_relevant} @embedding $vector AS vector_score]"
|
||||||
|
query = Query(base_query).return_fields(
|
||||||
|
"data",
|
||||||
|
"vector_score"
|
||||||
|
).sort_by("vector_score").dialect(2)
|
||||||
|
query_vector = np.array(query_embedding).astype(np.float32).tobytes()
|
||||||
|
|
||||||
|
try:
|
||||||
|
results = self.redis.ft("gpt").search(
|
||||||
|
query, query_params={"vector": query_vector}
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
print("Error calling Redis search: ", e)
|
||||||
|
return None
|
||||||
|
return list(results.docs)
|
||||||
|
|
||||||
|
def get_stats(self):
|
||||||
|
"""
|
||||||
|
Returns: The stats of the memory index.
|
||||||
|
"""
|
||||||
|
return self.redis.ft("mem").info()
|
||||||
|
|||||||
Reference in New Issue
Block a user