using CodeBase.Models; using CodeBase.Warehouse; namespace CodeBase.Services { public class CodeService(ChunkService service, QdrantRepository repository, VectorizationService vectorizationService, LlmService llmService) { public async Task> GetCodeChunksAsync(string path, string name) { if (!Path.Exists(path)) { throw new Exception("Путь не найден"); } var allChunks = new List(); // Находим все .cs файлы во всех вложенных папках var csFiles = Directory.GetFiles(path, "*.cs", SearchOption.AllDirectories); foreach (var filePath in csFiles) { var content = File.ReadAllText(filePath); // Парсим каждый файл отдельно, сохраняя его оригинальный путь var fileChunks = service.ChunkCSharpFile(filePath, content); allChunks.AddRange(fileChunks); } var chunks = await vectorizationService.EnrichChunksWithVectorsAsync(allChunks); await repository.SaveChunksAsync(name, chunks); return chunks; } public async Task GetAnswerAsync(string name, string question) { var query = await vectorizationService.GetVectorAsync(question); var answerVectors = await SearchAsync(query, name, 15); var answer = await llmService.AskQuestionAsync(question, answerVectors); return answer; } // Главный метод поиска public async Task> SearchAsync( float[] queryVector, string name, int topK = 3) // Возвращаем топ-3 результата { var results = await repository.SearchAsync(name, queryVector, topK); return results; } // Математика косинусного сходства private float CalculateCosineSimilarity(float[] vectorA, float[] vectorB) { if (vectorA.Length != vectorB.Length) throw new ArgumentException("Векторы должны быть одинаковой длины (например, 768)."); float dotProduct = 0; float magnitudeA = 0; float magnitudeB = 0; for (int i = 0; i < vectorA.Length; i++) { dotProduct += vectorA[i] * vectorB[i]; magnitudeA += vectorA[i] * vectorA[i]; magnitudeB += vectorB[i] * vectorB[i]; } // Защита от деления на ноль if (magnitudeA == 0 || magnitudeB == 0) return 0; return (float)(dotProduct / (Math.Sqrt(magnitudeA) * Math.Sqrt(magnitudeB))); } } }