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