Попытки в мультипарсинг
This commit is contained in:
@@ -29,7 +29,7 @@ namespace CodeBase.Services
|
||||
var chunk = new CodeChunk
|
||||
{
|
||||
FilePath = filePath,
|
||||
MethodName = method.Identifier.Text,
|
||||
EntityName = method.Identifier.Text,
|
||||
Content = method.ToFullString().Trim()
|
||||
};
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace CodeBase.Services
|
||||
chunks.Add(new CodeChunk
|
||||
{
|
||||
FilePath = filePath,
|
||||
MethodName = enumSyntax.Identifier.Text, // Сохраняем имя енама
|
||||
EntityName = enumSyntax.Identifier.Text, // Сохраняем имя енама
|
||||
Content = enumSyntax.ToFullString().Trim()
|
||||
});
|
||||
}
|
||||
@@ -84,7 +84,7 @@ namespace CodeBase.Services
|
||||
chunks.Add(new CodeChunk
|
||||
{
|
||||
FilePath = filePath,
|
||||
MethodName = classSyntax.Identifier.Text,
|
||||
EntityName = classSyntax.Identifier.Text,
|
||||
// Сохраняем объявление класса и его свойства
|
||||
Content = $"class {classSyntax.Identifier.Text} {{\n" +
|
||||
string.Join("\n", properties.Select(p => p.ToFullString().Trim())) +
|
||||
@@ -119,7 +119,7 @@ namespace CodeBase.Services
|
||||
{
|
||||
var chunk = new CodeChunk
|
||||
{
|
||||
MethodName = method.Identifier.Text,
|
||||
EntityName = method.Identifier.Text,
|
||||
Content = method.ToFullString(),
|
||||
FilePath = document.FilePath
|
||||
};
|
||||
@@ -140,7 +140,7 @@ namespace CodeBase.Services
|
||||
if (!namespaceName.StartsWith("System") && !namespaceName.StartsWith("Microsoft"))
|
||||
{
|
||||
chunk.OutgoingCalls.Add(methodSymbol.Name);
|
||||
Console.WriteLine($"[ПАРСЕР] Успех: {chunk.MethodName} -> {methodSymbol.Name} ({namespaceName})");
|
||||
Console.WriteLine($"[ПАРСЕР] Успех: {chunk.EntityName} -> {methodSymbol.Name} ({namespaceName})");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -184,5 +184,7 @@ namespace CodeBase.Services
|
||||
|
||||
return chunksList;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
using CodeBase.Models;
|
||||
using CodeBase.Repositories;
|
||||
|
||||
namespace CodeBase.Services
|
||||
{
|
||||
public class CodeService(ChunkService service,
|
||||
GraphRepository repository,
|
||||
ScipRepository repository,
|
||||
VectorizationService vectorizationService,
|
||||
LlmService llmService)
|
||||
{
|
||||
@@ -18,7 +19,7 @@ namespace CodeBase.Services
|
||||
|
||||
var chunks = await vectorizationService.EnrichChunksWithVectorsAsync(allChunks);
|
||||
|
||||
await repository.SaveChunksAsync(name, chunks);
|
||||
//await repository.SaveChunksAsync(name, chunks);
|
||||
|
||||
return chunks;
|
||||
}
|
||||
@@ -35,12 +36,12 @@ namespace CodeBase.Services
|
||||
}
|
||||
|
||||
// Главный метод поиска
|
||||
public async Task<List<GraphNodeContext>> SearchAsync(
|
||||
public async Task<List<RetrievedContext>> SearchAsync(
|
||||
float[] queryVector,
|
||||
string name,
|
||||
int topK = 3) // Возвращаем топ-3 результата
|
||||
{
|
||||
var results = await repository.SearchAsync(name, queryVector, topK);
|
||||
var results = await repository.FindSimilarNodesAsync(queryVector, name, topK);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
@@ -4,22 +4,24 @@ using System.Text;
|
||||
|
||||
public class LlmPromptBuilder
|
||||
{
|
||||
public string BuildPrompt(string userQuestion, List<GraphNodeContext> graphContexts)
|
||||
public string BuildPrompt(string userQuestion, List<RetrievedContext> graphContexts)
|
||||
{
|
||||
var promptBuilder = new StringBuilder();
|
||||
|
||||
promptBuilder.AppendLine("Ты — опытный C#-архитектор. Твоя задача — ответить на вопрос пользователя, опираясь ИСКЛЮЧИТЕЛЬНО на предоставленный граф вызовов и зависимостей кода. Не придумывай методы или классы, которых нет в контексте.");
|
||||
// Универсальная роль архитектора без привязки к конкретному языку
|
||||
promptBuilder.AppendLine("Ты — опытный ИТ-архитектор и разработчик. Твоя задача — ответить на вопрос пользователя, опираясь ИСКЛЮЧИТЕЛЬНО на предоставленный граф вызовов и зависимостей кода. Не придумывай методы или классы, которых нет в контексте.");
|
||||
promptBuilder.AppendLine("\nКонтекст из кодовой базы (Граф зависимостей):");
|
||||
|
||||
foreach (var node in graphContexts)
|
||||
{
|
||||
promptBuilder.AppendLine("--------------------------------------------------");
|
||||
promptBuilder.AppendLine($"[ГЛАВНЫЙ УЗЕЛ]");
|
||||
promptBuilder.AppendLine($"Имя: {node.MethodName}");
|
||||
promptBuilder.AppendLine($"[УЗЕЛ ГРАФА]");
|
||||
promptBuilder.AppendLine($"Проект: {node.ProjectName}");
|
||||
promptBuilder.AppendLine($"Имя сущности: {node.EntityName}");
|
||||
promptBuilder.AppendLine($"Файл: {node.FilePath}");
|
||||
|
||||
// Добавляем зависимости ВНИЗ (что использует метод)
|
||||
if (node.OutgoingDependencies.Any())
|
||||
if (node.OutgoingDependencies != null && node.OutgoingDependencies.Any())
|
||||
{
|
||||
promptBuilder.AppendLine("\n[ИСПОЛЬЗУЕТ ВНУТРИ СЕБЯ]:");
|
||||
foreach (var dep in node.OutgoingDependencies.Distinct())
|
||||
@@ -29,7 +31,7 @@ public class LlmPromptBuilder
|
||||
}
|
||||
|
||||
// Добавляем зависимости ВВЕРХ (кто зависит от метода)
|
||||
if (node.IncomingDependencies.Any())
|
||||
if (node.IncomingDependencies != null && node.IncomingDependencies.Any())
|
||||
{
|
||||
promptBuilder.AppendLine("\n[ВЫЗЫВАЕТСЯ ИЗ]:");
|
||||
foreach (var caller in node.IncomingDependencies.Distinct())
|
||||
|
||||
@@ -4,6 +4,7 @@ using OpenAI.Chat;
|
||||
using System;
|
||||
using System.ClientModel;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
|
||||
public class LlmService
|
||||
{
|
||||
@@ -30,7 +31,7 @@ public class LlmService
|
||||
_builder = llmPromptBuilder;
|
||||
}
|
||||
|
||||
public async Task<String> AskQuestionAsync(string userQuestion, List<GraphNodeContext> grafContext)
|
||||
public async Task<String> AskQuestionAsync(string userQuestion, List<RetrievedContext> grafContext)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
75
CodeBase/Services/ScipParser.cs
Normal file
75
CodeBase/Services/ScipParser.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
using CodeBase.Models;
|
||||
using CodeBase.Orchestrators;
|
||||
using CodeBase.Services;
|
||||
using Google.Protobuf;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeBase.Parsers
|
||||
{
|
||||
public class ScipParser(ScipOrchestrator scipOrchestrator)
|
||||
{
|
||||
public async Task<List<CodeChunk>> ParseProjectAsync(string projectRootPath, string language, string name)
|
||||
{
|
||||
var scipFilePath = Path.Combine(projectRootPath, "index.scip");
|
||||
if (!File.Exists(scipFilePath))
|
||||
{
|
||||
await scipOrchestrator.GenerateScipAsync(projectRootPath, language);
|
||||
}
|
||||
|
||||
var filter = new UniversalFileFilter();
|
||||
|
||||
using var stream = File.OpenRead(scipFilePath);
|
||||
var scipIndex = Scip.Index.Parser.ParseFrom(stream);
|
||||
|
||||
var extractedChunks = new List<CodeChunk>();
|
||||
|
||||
foreach (var document in scipIndex.Documents)
|
||||
{
|
||||
if (!filter.IsValidCodeFile(document.RelativePath)) continue;
|
||||
|
||||
var absoluteFilePath = Path.Combine(projectRootPath, document.RelativePath);
|
||||
if (!File.Exists(absoluteFilePath)) continue;
|
||||
|
||||
var fileLines = await File.ReadAllLinesAsync(absoluteFilePath);
|
||||
var declarations = document.Occurrences.Where(o => (o.SymbolRoles & 1) == 1);
|
||||
|
||||
foreach (var occ in declarations)
|
||||
{
|
||||
int startLine = occ.Range[0];
|
||||
int endLine = occ.Range.Count == 3 ? occ.Range[0] : occ.Range[2];
|
||||
|
||||
if (startLine < 0 || endLine >= fileLines.Length) continue;
|
||||
|
||||
var codeSnippet = string.Join(
|
||||
Environment.NewLine,
|
||||
fileLines.Skip(startLine).Take(endLine - startLine + 1)
|
||||
);
|
||||
|
||||
// Возвращаем чанки пока БЕЗ векторов
|
||||
extractedChunks.Add(new CodeChunk
|
||||
{
|
||||
Id = occ.Symbol,
|
||||
EntityName = ExtractSimpleName(occ.Symbol),
|
||||
FilePath = document.RelativePath,
|
||||
Content = codeSnippet,
|
||||
Language = document.Language,
|
||||
ProjectName = name
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return extractedChunks;
|
||||
}
|
||||
|
||||
private string ExtractSimpleName(string scipSymbol)
|
||||
{
|
||||
if (string.IsNullOrEmpty(scipSymbol)) return "Unknown";
|
||||
var parts = scipSymbol.Split(new[] { '#', '.', '(', ')' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
return parts.LastOrDefault() ?? scipSymbol;
|
||||
}
|
||||
}
|
||||
}
|
||||
49
CodeBase/Services/ScipProcessingService.cs
Normal file
49
CodeBase/Services/ScipProcessingService.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using CodeBase.Parsers;
|
||||
using CodeBase.Repositories;
|
||||
|
||||
namespace CodeBase.Services
|
||||
{
|
||||
public class ScipProcessingService
|
||||
{
|
||||
private readonly ScipParser _parser;
|
||||
private readonly VectorizationService _vectorizationService;
|
||||
private readonly ScipRepository _repository;
|
||||
|
||||
// Внедрение зависимостей
|
||||
public ScipProcessingService(
|
||||
ScipParser parser,
|
||||
VectorizationService vectorizationService,
|
||||
ScipRepository repository)
|
||||
{
|
||||
_parser = parser;
|
||||
_vectorizationService = vectorizationService;
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task ProcessAndSaveProjectAsync(string projectRootPath, string lang, string name)
|
||||
{
|
||||
try
|
||||
{
|
||||
Console.WriteLine($"[СЕРВИС] Запуск парсинга проекта: {projectRootPath}");
|
||||
|
||||
// 1. Получаем "сырые" чанки из парсера
|
||||
var chunks = await _parser.ParseProjectAsync(projectRootPath, lang, name);
|
||||
Console.WriteLine($"[СЕРВИС] Найдено {chunks.Count} сущностей. Начинаем векторизацию...");
|
||||
|
||||
// 2. Обогащаем каждый чанк векторным представлением GraphCodeBERT
|
||||
chunks = await _vectorizationService.EnrichChunksWithVectorsAsync(chunks);
|
||||
|
||||
// 3. Сохраняем готовую сборку в графовую базу
|
||||
await _repository.SaveChunksAsync(chunks);
|
||||
|
||||
Console.WriteLine("[СЕРВИС] Проект успешно обработан и сохранен в БД.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[СЕРВИС ОШИБКА] Сбой при обработке проекта: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
52
CodeBase/Services/UniversalFileFilter.cs
Normal file
52
CodeBase/Services/UniversalFileFilter.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
namespace CodeBase.Services
|
||||
{
|
||||
public class UniversalFileFilter
|
||||
{
|
||||
// 1. Оставляем только те языки, которые нам реально интересны
|
||||
private readonly HashSet<string> _allowedExtensions = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
".cs", ".py", ".go", ".ts", ".js", ".java", ".cpp", ".c", ".h", ".json"
|
||||
};
|
||||
|
||||
// 2. Глобальные папки с мусором и зависимостями (универсально для разных стеков)
|
||||
private readonly string[] _ignoredDirectories =
|
||||
{
|
||||
"/obj/", "\\obj\\",
|
||||
"/bin/", "\\bin\\",
|
||||
"/node_modules/", "\\node_modules\\",
|
||||
"/venv/", "\\venv\\",
|
||||
"/.env/", "\\.env\\",
|
||||
"/dist/", "\\dist\\",
|
||||
"/build/", "\\build\\",
|
||||
"/.git/", "\\.git\\"
|
||||
};
|
||||
|
||||
// 3. Паттерны автосгенерированных файлов
|
||||
private readonly string[] _ignoredFileSuffixes =
|
||||
{
|
||||
".g.cs",
|
||||
".designer.cs",
|
||||
".generated.cs",
|
||||
"AssemblyInfo.cs"
|
||||
};
|
||||
|
||||
public bool IsValidCodeFile(string relativePath)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(relativePath)) return false;
|
||||
|
||||
// Проверка 1: Расширение файла
|
||||
var ext = Path.GetExtension(relativePath);
|
||||
if (!_allowedExtensions.Contains(ext)) return false;
|
||||
|
||||
// Проверка 2: Находится ли файл в мусорной папке
|
||||
if (_ignoredDirectories.Any(dir => relativePath.Contains(dir, StringComparison.OrdinalIgnoreCase)))
|
||||
return false;
|
||||
|
||||
// Проверка 3: Является ли файл автосгенерированным
|
||||
if (_ignoredFileSuffixes.Any(suffix => relativePath.EndsWith(suffix, StringComparison.OrdinalIgnoreCase)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,28 @@ namespace CodeBase.Services
|
||||
// Адрес нашего локального Python-сервиса
|
||||
_httpClient.BaseAddress = new Uri("http://localhost:8000/");
|
||||
}
|
||||
private const int MaxCodeLength = 1500;
|
||||
|
||||
private string BuildSafeContext(CodeChunk chunk)
|
||||
{
|
||||
// Метаданные (оставляем всегда целиком)
|
||||
string metadata = $"File: {chunk.FilePath}\nEntity: {chunk.EntityName}\nCode:\n";
|
||||
|
||||
// Вычисляем, сколько символов у нас осталось для самого кода
|
||||
int remainingLength = MaxCodeLength - metadata.Length;
|
||||
|
||||
string code = chunk.Content;
|
||||
|
||||
// Если код слишком длинный — аккуратно отрезаем хвост
|
||||
if (code.Length > remainingLength && remainingLength > 0)
|
||||
{
|
||||
code = code.Substring(0, remainingLength) + "\n...[TRUNCATED]";
|
||||
Console.WriteLine($"[ВЕКТОРИЗАЦИЯ] Метод {chunk.EntityName} слишком длинный. Обрезан до {MaxCodeLength} символов.");
|
||||
}
|
||||
|
||||
return metadata + code;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public async Task<float[]> GetVectorAsync(string text)
|
||||
@@ -55,7 +77,7 @@ namespace CodeBase.Services
|
||||
{
|
||||
// 1. Склеиваем контекст
|
||||
// Мы даем нейросети подсказку о том, где именно лежит этот код
|
||||
string contextText = $"File: {chunk.FilePath}\nClass: {chunk.ClassName}\nMethod: {chunk.MethodName}\nCode:\n{chunk.Content}";
|
||||
string contextText = BuildSafeContext(chunk);
|
||||
|
||||
var requestBody = new VectorizeRequest { text = contextText };
|
||||
|
||||
@@ -71,13 +93,13 @@ namespace CodeBase.Services
|
||||
if (result != null && result.vector != null)
|
||||
{
|
||||
// 4. Сохраняем вектор прямо в наш объект в памяти
|
||||
chunk.Vector = result.vector;
|
||||
Console.WriteLine($"[+] Векторизован метод: {chunk.MethodName}");
|
||||
chunk.Embedding = result.vector;
|
||||
Console.WriteLine($"[+] Векторизована сущность: {chunk.EntityName}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"[-] Ошибка API для {chunk.MethodName}: {response.StatusCode}");
|
||||
Console.WriteLine($"[-] Ошибка API для {chunk.EntityName}: {response.StatusCode}");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user