mirror of https://github.com/redis/redis.git
CLI tool added as example / utility.
This commit is contained in:
parent
182737f3cc
commit
6d1a15987b
|
|
@ -0,0 +1 @@
|
|||
venv
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
This tool is similar to redis-cli (but very basic) but allows
|
||||
to specify arguments that are expanded as fectors by calling
|
||||
ollama to get the embedding.
|
||||
|
||||
Whatever is passed as !"foo bar" gets expanded into
|
||||
VALUES ... embedding ...
|
||||
|
||||
You must have ollama running with the mxbai-emb-large model
|
||||
already installed for this to work.
|
||||
|
||||
Example:
|
||||
|
||||
redis> KEYS *
|
||||
1) food_items
|
||||
2) glove_embeddings_bin
|
||||
3) many_movies_mxbai-embed-large_BIN
|
||||
4) many_movies_mxbai-embed-large_NOQUANT
|
||||
5) word_embeddings
|
||||
6) word_embeddings_bin
|
||||
7) glove_embeddings_fp32
|
||||
|
||||
redis> VSIM food_items !"drinks with fruit"
|
||||
1) (Fruit)Juices,Lemonade,100ml,50 cal,210 kJ
|
||||
2) (Fruit)Juices,Limeade,100ml,128 cal,538 kJ
|
||||
3) CannedFruit,Canned Fruit Cocktail,100g,81 cal,340 kJ
|
||||
4) (Fruit)Juices,Energy-Drink,100ml,87 cal,365 kJ
|
||||
5) Fruits,Lime,100g,30 cal,126 kJ
|
||||
6) (Fruit)Juices,Coconut Water,100ml,19 cal,80 kJ
|
||||
7) Fruits,Lemon,100g,29 cal,122 kJ
|
||||
8) (Fruit)Juices,Clamato,100ml,60 cal,252 kJ
|
||||
9) Fruits,Fruit salad,100g,50 cal,210 kJ
|
||||
10) (Fruit)Juices,Capri-Sun,100ml,41 cal,172 kJ
|
||||
|
||||
redis> vsim food_items !"barilla"
|
||||
1) Pasta&Noodles,Spirelli,100g,367 cal,1541 kJ
|
||||
2) Pasta&Noodles,Farfalle,100g,358 cal,1504 kJ
|
||||
3) Pasta&Noodles,Capellini,100g,353 cal,1483 kJ
|
||||
4) Pasta&Noodles,Spaetzle,100g,368 cal,1546 kJ
|
||||
5) Pasta&Noodles,Cappelletti,100g,164 cal,689 kJ
|
||||
6) Pasta&Noodles,Penne,100g,351 cal,1474 kJ
|
||||
7) Pasta&Noodles,Shells,100g,353 cal,1483 kJ
|
||||
8) Pasta&Noodles,Linguine,100g,357 cal,1499 kJ
|
||||
9) Pasta&Noodles,Rotini,100g,353 cal,1483 kJ
|
||||
10) Pasta&Noodles,Rigatoni,100g,353 cal,1483 kJ
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
#!/usr/bin/env python3
|
||||
import redis
|
||||
import requests
|
||||
import re
|
||||
import shlex
|
||||
from prompt_toolkit import PromptSession
|
||||
from prompt_toolkit.history import InMemoryHistory
|
||||
|
||||
def get_embedding(text):
|
||||
"""Get embedding from local Ollama API"""
|
||||
url = "http://localhost:11434/api/embeddings"
|
||||
payload = {
|
||||
"model": "mxbai-embed-large",
|
||||
"prompt": text
|
||||
}
|
||||
try:
|
||||
response = requests.post(url, json=payload)
|
||||
response.raise_for_status()
|
||||
return response.json()['embedding']
|
||||
except requests.exceptions.RequestException as e:
|
||||
raise Exception(f"Failed to get embedding: {str(e)}")
|
||||
|
||||
def process_embedding_patterns(text):
|
||||
"""Process !"text" and !!"text" patterns in the command"""
|
||||
|
||||
def replace_with_embedding(match):
|
||||
text = match.group(1)
|
||||
embedding = get_embedding(text)
|
||||
return f"VALUES {len(embedding)} {' '.join(map(str, embedding))}"
|
||||
|
||||
def replace_with_embedding_and_text(match):
|
||||
text = match.group(1)
|
||||
embedding = get_embedding(text)
|
||||
# Return both the embedding values and the original text as next argument
|
||||
return f'VALUES {len(embedding)} {" ".join(map(str, embedding))} "{text}"'
|
||||
|
||||
# First handle !!"text" pattern (must be done before !"text")
|
||||
text = re.sub(r'!!"([^"]*)"', replace_with_embedding_and_text, text)
|
||||
# Then handle !"text" pattern
|
||||
text = re.sub(r'!"([^"]*)"', replace_with_embedding, text)
|
||||
return text
|
||||
|
||||
def parse_command(command):
|
||||
"""Parse command respecting quoted strings"""
|
||||
try:
|
||||
# Use shlex to properly handle quoted strings
|
||||
return shlex.split(command)
|
||||
except ValueError as e:
|
||||
raise Exception(f"Invalid command syntax: {str(e)}")
|
||||
|
||||
def format_response(response):
|
||||
"""Format the response to match Redis protocol style"""
|
||||
if response is None:
|
||||
return "(nil)"
|
||||
elif isinstance(response, bool):
|
||||
return "+OK" if response else "(error) Operation failed"
|
||||
elif isinstance(response, (list, set)):
|
||||
if not response:
|
||||
return "(empty list or set)"
|
||||
return "\n".join(f"{i+1}) {item}" for i, item in enumerate(response))
|
||||
elif isinstance(response, int):
|
||||
return f"(integer) {response}"
|
||||
else:
|
||||
return str(response)
|
||||
|
||||
def main():
|
||||
# Default connection to localhost:6379
|
||||
r = redis.Redis(host='localhost', port=6379, decode_responses=True)
|
||||
|
||||
try:
|
||||
# Test connection
|
||||
r.ping()
|
||||
print("Connected to Redis. Type your commands (CTRL+D to exit):")
|
||||
print("Special syntax:")
|
||||
print(" !\"text\" - Replace with embedding")
|
||||
print(" !!\"text\" - Replace with embedding and append text as value")
|
||||
print(" \"text\" - Quote strings containing spaces")
|
||||
except redis.ConnectionError:
|
||||
print("Error: Could not connect to Redis server")
|
||||
return
|
||||
|
||||
# Setup prompt session with history
|
||||
session = PromptSession(history=InMemoryHistory())
|
||||
|
||||
# Main loop
|
||||
while True:
|
||||
try:
|
||||
# Read input with line editing support
|
||||
command = session.prompt("redis> ")
|
||||
|
||||
# Skip empty commands
|
||||
if not command.strip():
|
||||
continue
|
||||
|
||||
# Process any embedding patterns before parsing
|
||||
try:
|
||||
processed_command = process_embedding_patterns(command)
|
||||
except Exception as e:
|
||||
print(f"(error) Embedding processing failed: {str(e)}")
|
||||
continue
|
||||
|
||||
# Parse the command respecting quoted strings
|
||||
try:
|
||||
parts = parse_command(processed_command)
|
||||
except Exception as e:
|
||||
print(f"(error) {str(e)}")
|
||||
continue
|
||||
|
||||
if not parts:
|
||||
continue
|
||||
|
||||
cmd = parts[0].lower()
|
||||
args = parts[1:]
|
||||
|
||||
# Execute command
|
||||
try:
|
||||
method = getattr(r, cmd, None)
|
||||
if method is not None:
|
||||
result = method(*args)
|
||||
else:
|
||||
# Use execute_command for unknown commands
|
||||
result = r.execute_command(cmd, *args)
|
||||
print(format_response(result))
|
||||
except AttributeError:
|
||||
print(f"(error) Unknown command '{cmd}'")
|
||||
|
||||
except EOFError:
|
||||
print("\nGoodbye!")
|
||||
break
|
||||
except KeyboardInterrupt:
|
||||
continue # Allow Ctrl+C to clear current line
|
||||
except redis.RedisError as e:
|
||||
print(f"(error) {str(e)}")
|
||||
except Exception as e:
|
||||
print(f"(error) {str(e)}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Reference in New Issue