using Microsoft.Extensions.Configuration; using VectorSearchApp.Configuration; using VectorSearchApp.Models; using VectorSearchApp.Services; Console.WriteLine("=== Vector Search Address Application ==="); Console.WriteLine("Using sentence-transformers/all-MiniLM-L6-v2 model and Qdrant vector database"); Console.WriteLine(); // Load configuration var configuration = new ConfigurationBuilder() .SetBasePath(AppContext.BaseDirectory) .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .Build(); var appConfig = new AppConfiguration(); configuration.GetSection("Qdrant").Bind(appConfig.Qdrant); configuration.GetSection("Embedding").Bind(appConfig.Embedding); configuration.GetSection("App").Bind(appConfig.App); // Initialize services Console.WriteLine("Initializing services..."); var embeddingService = new EmbeddingService(appConfig.Embedding); // Check if HuggingFace API token is configured if (string.IsNullOrEmpty(appConfig.Embedding.ApiToken)) { Console.WriteLine("Warning: HuggingFace API token is not configured."); Console.WriteLine("The application may have limited functionality without authentication."); Console.WriteLine("To get a free token, visit: https://huggingface.co/settings/tokens"); Console.WriteLine(); } IQdrantService? qdrantService = null; try { qdrantService = new QdrantService(appConfig.Qdrant, appConfig.Embedding.Dimension); Console.WriteLine("Initializing Qdrant collection..."); await qdrantService.InitializeCollectionAsync(); Console.WriteLine($"Collection '{appConfig.Qdrant.CollectionName}' is ready."); } catch (Exception ex) { Console.WriteLine($"Warning: Could not connect to Qdrant at {appConfig.Qdrant.Host}:{appConfig.Qdrant.GrpcPort}"); Console.WriteLine($"Error: {ex.Message}"); Console.WriteLine(); Console.WriteLine("Please ensure Qdrant is running. You can start it with:"); Console.WriteLine(" cd VectorSearchApp && docker-compose up -d"); Console.WriteLine(); Console.WriteLine("The application will continue, but address storage/search will not be available."); Console.WriteLine(); } Console.WriteLine("Type 'exit' to quit at any time."); Console.WriteLine(); while (true) { Console.WriteLine("Options:"); Console.WriteLine(" 1. Add a new address"); Console.WriteLine(" 2. Search for similar addresses"); Console.WriteLine(" 3. Exit"); Console.Write("Select an option: "); var option = Console.ReadLine()?.Trim(); if (option?.ToLower() == "exit" || option == "3") { Console.WriteLine("Goodbye!"); return; } switch (option) { case "1": await AddAddressAsync(embeddingService, qdrantService, appConfig); break; case "2": await SearchAddressesAsync(embeddingService, qdrantService, appConfig); break; default: Console.WriteLine("Invalid option. Please try again."); break; } Console.WriteLine(); } async Task AddAddressAsync(IEmbeddingService embeddingService, IQdrantService? qdrantService, AppConfiguration config) { Console.Write("Enter the address: "); var addressText = Console.ReadLine()?.Trim(); if (string.IsNullOrWhiteSpace(addressText)) { Console.WriteLine("Address cannot be empty."); return; } Console.WriteLine("Generating embedding..."); try { var embedding = await embeddingService.GenerateEmbeddingAsync(addressText); Console.WriteLine($"Embedding generated (dimension: {embedding.Length})"); if (qdrantService != null) { var address = new Address { Id = Guid.NewGuid(), FullAddress = addressText, CreatedAt = DateTime.UtcNow }; Console.WriteLine("Storing in Qdrant..."); await qdrantService.StoreAddressAsync(address, embedding); Console.WriteLine($"Address stored successfully! (ID: {address.Id})"); } else { Console.WriteLine("Address embedding generated but not stored (Qdrant not available)."); } // Display first few values of embedding as confirmation Console.Write("Embedding preview: ["); var previewCount = Math.Min(10, embedding.Length); for (int i = 0; i < previewCount; i++) { Console.Write($"{embedding[i]:F4}"); if (i < previewCount - 1) Console.Write(", "); } if (embedding.Length > previewCount) Console.Write(", ..."); Console.WriteLine("]"); } catch (Exception ex) { Console.WriteLine($"Error generating embedding: {ex.Message}"); } } async Task SearchAddressesAsync(IEmbeddingService embeddingService, IQdrantService? qdrantService, AppConfiguration config) { if (qdrantService == null) { Console.WriteLine("Search is not available because Qdrant is not connected."); return; } Console.Write("Enter search query: "); var query = Console.ReadLine()?.Trim(); if (string.IsNullOrWhiteSpace(query)) { Console.WriteLine("Query cannot be empty."); return; } Console.Write("Enter similarity threshold (0.0-1.0, lower = stricter, press Enter for 0.7): "); var thresholdInput = Console.ReadLine()?.Trim(); float threshold = 0.7f; if (!string.IsNullOrEmpty(thresholdInput) && float.TryParse(thresholdInput, out var parsedThreshold)) { threshold = Math.Clamp(parsedThreshold, 0f, 1f); } Console.Write("Enter max results to return (press Enter for 5, or '2' for top 2 most similar): "); var limitInput = Console.ReadLine()?.Trim(); int limit = 5; if (!string.IsNullOrEmpty(limitInput) && int.TryParse(limitInput, out var parsedLimit)) { limit = Math.Max(1, parsedLimit); } Console.WriteLine("Generating query embedding..."); try { var queryEmbedding = await embeddingService.GenerateEmbeddingAsync(query); Console.WriteLine($"Searching for similar addresses (threshold: {threshold:F2}, max results: {limit})..."); var results = await qdrantService.SearchSimilarAddressesAsync(queryEmbedding, limit: limit, scoreThreshold: threshold); if (results.Count == 0) { Console.WriteLine("No similar addresses found."); return; } Console.WriteLine($"\nFound {results.Count} similar address(es):"); for (int i = 0; i < results.Count; i++) { Console.WriteLine($" {i + 1}. {results[i].FullAddress} (Score: {results[i].Score:F4})"); } } catch (Exception ex) { Console.WriteLine($"Error during search: {ex.Message}"); } }