199 lines
6.7 KiB
C#
199 lines
6.7 KiB
C#
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}");
|
|
}
|
|
} |