мини рефакторинг
добавлены пачноты
This commit is contained in:
@@ -12,6 +12,7 @@
|
|||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||||
<Protobuf Include="scip.proto" GrpcServices="None" />
|
<Protobuf Include="scip.proto" GrpcServices="None" />
|
||||||
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.6" />
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.6" />
|
||||||
|
|||||||
@@ -11,12 +11,6 @@ namespace CodeBase.Controllers
|
|||||||
public class CodeController(CodeService service,
|
public class CodeController(CodeService service,
|
||||||
ScipProcessingService scipProcessingService) : ControllerBase
|
ScipProcessingService scipProcessingService) : ControllerBase
|
||||||
{
|
{
|
||||||
[HttpPost("analyze")]
|
|
||||||
public async Task<CodeChunk> AnalyzeRepository(string path, string name)
|
|
||||||
{
|
|
||||||
var chunks = await service.GetCodeChunksAsync(path, name);
|
|
||||||
return chunks[0];
|
|
||||||
}
|
|
||||||
[HttpPost("answer")]
|
[HttpPost("answer")]
|
||||||
public async Task<string> GetAnswer(string question, string name)
|
public async Task<string> GetAnswer(string question, string name)
|
||||||
{
|
{
|
||||||
@@ -25,9 +19,9 @@ namespace CodeBase.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("parser")]
|
[HttpPost("parser")]
|
||||||
public async Task<string> ParserProject(string path, string name, string lang)
|
public async Task<string> ParserProject(string path, string name, Language language)
|
||||||
{
|
{
|
||||||
await scipProcessingService.ProcessAndSaveProjectAsync(path, lang, name);
|
await scipProcessingService.ProcessAndSaveProjectAsync(path, language.ToString(), name);
|
||||||
return "Проект добавлен в базу";
|
return "Проект добавлен в базу";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
14
CodeBase/Models/Enums.cs
Normal file
14
CodeBase/Models/Enums.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Converters;
|
||||||
|
|
||||||
|
namespace CodeBase.Models
|
||||||
|
{
|
||||||
|
[JsonConverter(typeof(StringEnumConverter))]
|
||||||
|
public enum Language
|
||||||
|
{
|
||||||
|
csharp,
|
||||||
|
python,
|
||||||
|
typescript,
|
||||||
|
go
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
namespace CodeBase.Models
|
|
||||||
{
|
|
||||||
public class GraphNodeContext
|
|
||||||
{
|
|
||||||
public string MethodName { get; set; }
|
|
||||||
public string FilePath { get; set; }
|
|
||||||
public string Content { get; set; }
|
|
||||||
|
|
||||||
// Графовые связи, которые мы вытащим из Neo4j
|
|
||||||
|
|
||||||
// Кого вызывает этот метод (и какие классы/енумы использует)
|
|
||||||
public List<string> OutgoingDependencies { get; set; } = new();
|
|
||||||
|
|
||||||
// Кто вызывает этот метод (кто от него зависит)
|
|
||||||
public List<string> IncomingDependencies { get; set; } = new();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,6 +4,7 @@ using CodeBase.Repositories;
|
|||||||
using CodeBase.Services;
|
using CodeBase.Services;
|
||||||
using Microsoft.Build.Locator;
|
using Microsoft.Build.Locator;
|
||||||
using Neo4j.Driver;
|
using Neo4j.Driver;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
if (!MSBuildLocator.IsRegistered)
|
if (!MSBuildLocator.IsRegistered)
|
||||||
{
|
{
|
||||||
@@ -28,12 +29,14 @@ builder.Services.AddTransient<ScipProcessingService>();
|
|||||||
builder.Services.AddTransient<VectorizationService>();
|
builder.Services.AddTransient<VectorizationService>();
|
||||||
builder.Services.AddTransient<LlmService>();
|
builder.Services.AddTransient<LlmService>();
|
||||||
builder.Services.AddScoped<LlmPromptBuilder>();
|
builder.Services.AddScoped<LlmPromptBuilder>();
|
||||||
builder.Services.AddTransient<ChunkService>();
|
|
||||||
builder.Services.AddTransient<CodeService>();
|
builder.Services.AddTransient<CodeService>();
|
||||||
builder.Services.AddTransient<GraphRepository>();
|
|
||||||
builder.Services.AddTransient<ScipRepository>();
|
builder.Services.AddTransient<ScipRepository>();
|
||||||
|
|
||||||
builder.Services.AddControllers();
|
builder.Services.AddControllers().AddJsonOptions(options =>
|
||||||
|
{
|
||||||
|
// Добавляем конвертер строковых енумов глобально
|
||||||
|
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
|
||||||
|
}); ;
|
||||||
builder.Services.AddSwaggerGen();
|
builder.Services.AddSwaggerGen();
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
@@ -58,12 +61,4 @@ app.UseAuthorization();
|
|||||||
|
|
||||||
app.MapControllers();
|
app.MapControllers();
|
||||||
|
|
||||||
using (var scope = app.Services.CreateScope())
|
|
||||||
{
|
|
||||||
var graphRepo = scope.ServiceProvider.GetRequiredService<GraphRepository>();
|
|
||||||
Console.WriteLine("Проверяем и создаем векторный индекс в Neo4j...");
|
|
||||||
await graphRepo.InitializeDbAsync();
|
|
||||||
Console.WriteLine("Индекс готов!");
|
|
||||||
}
|
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
|
|||||||
@@ -1,139 +0,0 @@
|
|||||||
using CodeBase.Models;
|
|
||||||
using Neo4j.Driver;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
public class GraphRepository
|
|
||||||
{
|
|
||||||
private readonly IDriver _driver;
|
|
||||||
|
|
||||||
public GraphRepository(IDriver driver)
|
|
||||||
{
|
|
||||||
_driver = driver;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==========================================
|
|
||||||
// 0. ИНИЦИАЛИЗАЦИЯ (Создаем пространство для векторов)
|
|
||||||
// ==========================================
|
|
||||||
public async Task InitializeDbAsync()
|
|
||||||
{
|
|
||||||
await using var session = _driver.AsyncSession();
|
|
||||||
|
|
||||||
await session.ExecuteWriteAsync(async tx =>
|
|
||||||
{
|
|
||||||
// Говорим базе: "Создай индекс для поиска по сходству, если его еще нет.
|
|
||||||
// Размер вектора 768 (GraphCodeBERT), алгоритм - косинусное расстояние"
|
|
||||||
await tx.RunAsync(@"
|
|
||||||
CREATE VECTOR INDEX code_vectors IF NOT EXISTS
|
|
||||||
FOR (c:CodeChunk) ON (c.vector)
|
|
||||||
OPTIONS { indexConfig: {
|
|
||||||
`vector.dimensions`: 768,
|
|
||||||
`vector.similarity_function`: 'cosine'
|
|
||||||
}}"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==========================================
|
|
||||||
// 1. СОХРАНЕНИЕ УЗЛОВ И ВЕКТОРОВ
|
|
||||||
// ==========================================
|
|
||||||
public async Task SaveChunksAsync(string projectName, List<CodeChunk> chunks)
|
|
||||||
{
|
|
||||||
await using var session = _driver.AsyncSession();
|
|
||||||
|
|
||||||
// 1. Делаем данные "безопасными" для базы
|
|
||||||
var parameters = chunks.Where(c => c.Embedding != null).Select(chunk => new
|
|
||||||
{
|
|
||||||
id = Guid.NewGuid().ToString(),
|
|
||||||
projectName = projectName,
|
|
||||||
filePath = chunk.FilePath,
|
|
||||||
methodName = chunk.EntityName,
|
|
||||||
content = chunk.Content,
|
|
||||||
vector = chunk.Embedding,
|
|
||||||
// Защита от null и пустых строк (база их не переварит в цикле FOREACH)
|
|
||||||
outgoingCalls = chunk.OutgoingCalls != null
|
|
||||||
? chunk.OutgoingCalls.Where(x => !string.IsNullOrWhiteSpace(x)).ToList()
|
|
||||||
: new List<string>()
|
|
||||||
}).ToList();
|
|
||||||
|
|
||||||
await session.ExecuteWriteAsync(async tx =>
|
|
||||||
{
|
|
||||||
await tx.RunAsync(@"
|
|
||||||
UNWIND $batch AS chunk
|
|
||||||
|
|
||||||
// Создаем или обновляем основной метод
|
|
||||||
MERGE (c:CodeChunk { methodName: chunk.methodName })
|
|
||||||
SET c.id = chunk.id,
|
|
||||||
c.projectName = chunk.projectName,
|
|
||||||
c.filePath = chunk.filePath,
|
|
||||||
c.content = chunk.content,
|
|
||||||
c.vector = chunk.vector
|
|
||||||
|
|
||||||
// Рисуем связи только для валидных вызовов
|
|
||||||
FOREACH (calledMethod IN chunk.outgoingCalls |
|
|
||||||
MERGE (target:CodeChunk { methodName: calledMethod })
|
|
||||||
MERGE (c)-[:CALLS]->(target)
|
|
||||||
)
|
|
||||||
", new { batch = parameters });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==========================================
|
|
||||||
// 2. ПОИСК (Пока только по вектору, связи добавим позже)
|
|
||||||
// ==========================================
|
|
||||||
public async Task<List<GraphNodeContext>> SearchAsync(string projectName, float[] queryVector, int topK = 15)
|
|
||||||
{
|
|
||||||
await using var session = _driver.AsyncSession();
|
|
||||||
|
|
||||||
return await session.ExecuteReadAsync(async tx =>
|
|
||||||
{
|
|
||||||
var cursor = await tx.RunAsync(@"
|
|
||||||
// 1. Ищем самые подходящие узлы по векторному сходству
|
|
||||||
CALL db.index.vector.queryNodes('code_vectors', $topK, $queryVector)
|
|
||||||
YIELD node AS c, score
|
|
||||||
WHERE c.projectName = $projectName
|
|
||||||
|
|
||||||
// 2. Ищем стрелочки ВНИЗ (Кого вызывает этот метод?)
|
|
||||||
// OPTIONAL MATCH гарантирует, что мы не потеряем узел, если связей нет
|
|
||||||
OPTIONAL MATCH (c)-[:CALLS]->(out:CodeChunk)
|
|
||||||
// Собираем имена вызываемых методов в массив
|
|
||||||
WITH c, score, collect(DISTINCT out.methodName) AS outgoingDependencies
|
|
||||||
|
|
||||||
// 3. Ищем стрелочки ВВЕРХ (Кто вызывает этот метод?)
|
|
||||||
OPTIONAL MATCH (in:CodeChunk)-[:CALLS]->(c)
|
|
||||||
WITH c, score, outgoingDependencies, collect(DISTINCT in.methodName) AS incomingDependencies
|
|
||||||
|
|
||||||
// 4. Возвращаем готовую структуру для C#
|
|
||||||
RETURN
|
|
||||||
c.methodName AS methodName,
|
|
||||||
c.filePath AS filePath,
|
|
||||||
c.content AS content,
|
|
||||||
outgoingDependencies,
|
|
||||||
incomingDependencies,
|
|
||||||
score
|
|
||||||
ORDER BY score DESC
|
|
||||||
", new { topK, queryVector, projectName });
|
|
||||||
|
|
||||||
var results = new List<GraphNodeContext>();
|
|
||||||
|
|
||||||
while (await cursor.FetchAsync())
|
|
||||||
{
|
|
||||||
var record = cursor.Current;
|
|
||||||
results.Add(new GraphNodeContext
|
|
||||||
{
|
|
||||||
MethodName = record["methodName"].As<string>(),
|
|
||||||
FilePath = record["filePath"].As<string>(),
|
|
||||||
Content = record["content"].As<string>(),
|
|
||||||
|
|
||||||
// Драйвер Neo4j возвращает массивы как IList<object>,
|
|
||||||
// поэтому аккуратно кастуем их в наши списки строк
|
|
||||||
OutgoingDependencies = record["outgoingDependencies"].As<IList<string>>().ToList(),
|
|
||||||
IncomingDependencies = record["incomingDependencies"].As<IList<string>>().ToList()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,190 +0,0 @@
|
|||||||
using CodeBase.Models;
|
|
||||||
using Microsoft.CodeAnalysis;
|
|
||||||
using Microsoft.CodeAnalysis.CSharp;
|
|
||||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|
||||||
using Microsoft.CodeAnalysis.MSBuild;
|
|
||||||
|
|
||||||
namespace CodeBase.Services
|
|
||||||
{
|
|
||||||
public class ChunkService(VectorizationService vectorizationService)
|
|
||||||
{
|
|
||||||
public List<CodeChunk> ChunkCSharpFile(string filePath, string fileContent)
|
|
||||||
{
|
|
||||||
var chunks = new List<CodeChunk>();
|
|
||||||
var syntaxTree = CSharpSyntaxTree.ParseText(fileContent);
|
|
||||||
var root = syntaxTree.GetRoot();
|
|
||||||
|
|
||||||
var compilation = CSharpCompilation.Create("MyAnalysis")
|
|
||||||
.AddSyntaxTrees(syntaxTree)
|
|
||||||
// Подкидываем базовые библиотеки .NET, чтобы он узнал System.Linq и прочее
|
|
||||||
.AddReferences(MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
|
|
||||||
MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location));
|
|
||||||
|
|
||||||
var semanticModel = compilation.GetSemanticModel(syntaxTree);
|
|
||||||
|
|
||||||
// 1. Собираем методы (как и раньше)
|
|
||||||
var methods = root.DescendantNodes().OfType<MethodDeclarationSyntax>();
|
|
||||||
foreach (var method in methods)
|
|
||||||
{
|
|
||||||
var chunk = new CodeChunk
|
|
||||||
{
|
|
||||||
FilePath = filePath,
|
|
||||||
EntityName = method.Identifier.Text,
|
|
||||||
Content = method.ToFullString().Trim()
|
|
||||||
};
|
|
||||||
|
|
||||||
var invocations = method.DescendantNodes().OfType<InvocationExpressionSyntax>();
|
|
||||||
|
|
||||||
foreach (var invocation in invocations)
|
|
||||||
{
|
|
||||||
var symbolInfo = semanticModel.GetSymbolInfo(invocation);
|
|
||||||
|
|
||||||
if (symbolInfo.Symbol is IMethodSymbol methodSymbol)
|
|
||||||
{
|
|
||||||
// Получаем полный путь пространства имен (например, "System.Linq")
|
|
||||||
string namespaceName = methodSymbol.ContainingNamespace.ToString();
|
|
||||||
|
|
||||||
// Пропускаем все системные вызовы .NET
|
|
||||||
if (namespaceName.StartsWith("System") || namespaceName.StartsWith("Microsoft"))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Если это наш метод (например, "Bas.Core.Services"), добавляем его
|
|
||||||
chunk.OutgoingCalls.Add(methodSymbol.Name);
|
|
||||||
Console.WriteLine($"[ПАРСЕР] Добавлена бизнес-связь -> {methodSymbol.Name}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
chunk.OutgoingCalls = chunk.OutgoingCalls.Distinct().ToList();
|
|
||||||
|
|
||||||
chunks.Add(chunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. ДОБАВЛЯЕМ СБОР ENUM (Перечислений)
|
|
||||||
var enums = root.DescendantNodes().OfType<EnumDeclarationSyntax>();
|
|
||||||
foreach (var enumSyntax in enums)
|
|
||||||
{
|
|
||||||
chunks.Add(new CodeChunk
|
|
||||||
{
|
|
||||||
FilePath = filePath,
|
|
||||||
EntityName = enumSyntax.Identifier.Text, // Сохраняем имя енама
|
|
||||||
Content = enumSyntax.ToFullString().Trim()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. ДОБАВЛЯЕМ СБОР КЛАССОВ-МОДЕЛЕЙ (без методов)
|
|
||||||
var classes = root.DescendantNodes().OfType<ClassDeclarationSyntax>();
|
|
||||||
foreach (var classSyntax in classes)
|
|
||||||
{
|
|
||||||
// Берем только свойства, чтобы понимать структуру модели
|
|
||||||
var properties = classSyntax.Members.OfType<PropertyDeclarationSyntax>();
|
|
||||||
if (properties.Any())
|
|
||||||
{
|
|
||||||
chunks.Add(new CodeChunk
|
|
||||||
{
|
|
||||||
FilePath = filePath,
|
|
||||||
EntityName = classSyntax.Identifier.Text,
|
|
||||||
// Сохраняем объявление класса и его свойства
|
|
||||||
Content = $"class {classSyntax.Identifier.Text} {{\n" +
|
|
||||||
string.Join("\n", properties.Select(p => p.ToFullString().Trim())) +
|
|
||||||
"\n}"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return chunks;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<List<CodeChunk>> ParseProjectAsync(string csprojPath)
|
|
||||||
{
|
|
||||||
// 1. Создаем воркспейс и загружаем проект (это может занять пару секунд)
|
|
||||||
using var workspace = MSBuildWorkspace.Create();
|
|
||||||
Console.WriteLine("Загружаем проект и строим семантическую модель...");
|
|
||||||
var project = await workspace.OpenProjectAsync(csprojPath);
|
|
||||||
|
|
||||||
var chunksList = new List<CodeChunk>();
|
|
||||||
|
|
||||||
// 2. Проходимся по всем C#-файлам в проекте
|
|
||||||
foreach (var document in project.Documents)
|
|
||||||
{
|
|
||||||
// Теперь у нас есть ГАРАНТИРОВАННАЯ семантическая модель для каждого файла
|
|
||||||
var semanticModel = await document.GetSemanticModelAsync();
|
|
||||||
var syntaxTree = await document.GetSyntaxTreeAsync();
|
|
||||||
var root = await syntaxTree.GetRootAsync();
|
|
||||||
|
|
||||||
var methods = root.DescendantNodes().OfType<MethodDeclarationSyntax>();
|
|
||||||
|
|
||||||
foreach (var method in methods)
|
|
||||||
{
|
|
||||||
var chunk = new CodeChunk
|
|
||||||
{
|
|
||||||
EntityName = method.Identifier.Text,
|
|
||||||
Content = method.ToFullString(),
|
|
||||||
FilePath = document.FilePath
|
|
||||||
};
|
|
||||||
|
|
||||||
var invocations = method.DescendantNodes().OfType<InvocationExpressionSyntax>();
|
|
||||||
|
|
||||||
foreach (var invocation in invocations)
|
|
||||||
{
|
|
||||||
// 3. Просим у модели 100% точную информацию о вызываемом методе
|
|
||||||
var symbolInfo = semanticModel.GetSymbolInfo(invocation);
|
|
||||||
|
|
||||||
if (symbolInfo.Symbol is IMethodSymbol methodSymbol)
|
|
||||||
{
|
|
||||||
// Получаем пространство имен метода (например, "System.Linq" или "CodeBase.Services")
|
|
||||||
string namespaceName = methodSymbol.ContainingNamespace?.ToString() ?? "";
|
|
||||||
|
|
||||||
// Отсеиваем только системные вызовы, оставляя ВЕСЬ бизнес-код
|
|
||||||
if (!namespaceName.StartsWith("System") && !namespaceName.StartsWith("Microsoft"))
|
|
||||||
{
|
|
||||||
chunk.OutgoingCalls.Add(methodSymbol.Name);
|
|
||||||
Console.WriteLine($"[ПАРСЕР] Успех: {chunk.EntityName} -> {methodSymbol.Name} ({namespaceName})");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1. Ищем вообще ВСЕ слова (идентификаторы) внутри метода
|
|
||||||
var identifiers = method.DescendantNodes().OfType<IdentifierNameSyntax>();
|
|
||||||
|
|
||||||
foreach (var identifier in identifiers)
|
|
||||||
{
|
|
||||||
// 2. Спрашиваем у семантической модели: "Что это за слово?"
|
|
||||||
var symbolInfo = semanticModel.GetSymbolInfo(identifier);
|
|
||||||
|
|
||||||
// 3. Если это тип данных (класс, структура, интерфейс или ЕНУМ)
|
|
||||||
if (symbolInfo.Symbol is INamedTypeSymbol typeSymbol)
|
|
||||||
{
|
|
||||||
string namespaceName = typeSymbol.ContainingNamespace?.ToString() ?? "";
|
|
||||||
|
|
||||||
// Отсеиваем системные типы (string, int, List и т.д.)
|
|
||||||
if (!namespaceName.StartsWith("System") && !namespaceName.StartsWith("Microsoft"))
|
|
||||||
{
|
|
||||||
// Проверяем, что это именно то, что нам нужно
|
|
||||||
if (typeSymbol.TypeKind == TypeKind.Enum)
|
|
||||||
{
|
|
||||||
chunk.OutgoingCalls.Add(typeSymbol.Name);
|
|
||||||
Console.WriteLine($"[ПАРСЕР] Нашли использование енума -> {typeSymbol.Name}");
|
|
||||||
}
|
|
||||||
else if (typeSymbol.TypeKind == TypeKind.Class || typeSymbol.TypeKind == TypeKind.Interface)
|
|
||||||
{
|
|
||||||
chunk.OutgoingCalls.Add(typeSymbol.Name);
|
|
||||||
Console.WriteLine($"[ПАРСЕР] Нашли использование класса/интерфейса -> {typeSymbol.Name}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Убираем дубликаты
|
|
||||||
chunk.OutgoingCalls = chunk.OutgoingCalls.Distinct().ToList();
|
|
||||||
chunksList.Add(chunk);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return chunksList;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,27 +3,10 @@ using CodeBase.Repositories;
|
|||||||
|
|
||||||
namespace CodeBase.Services
|
namespace CodeBase.Services
|
||||||
{
|
{
|
||||||
public class CodeService(ChunkService service,
|
public class CodeService(ScipRepository repository,
|
||||||
ScipRepository repository,
|
|
||||||
VectorizationService vectorizationService,
|
VectorizationService vectorizationService,
|
||||||
LlmService llmService)
|
LlmService llmService)
|
||||||
{
|
{
|
||||||
public async Task<List<CodeChunk>> GetCodeChunksAsync(string path, string name)
|
|
||||||
{
|
|
||||||
if (!Path.Exists(path))
|
|
||||||
{
|
|
||||||
throw new Exception("Путь не найден");
|
|
||||||
}
|
|
||||||
|
|
||||||
var allChunks = await service.ParseProjectAsync(path);
|
|
||||||
|
|
||||||
var chunks = await vectorizationService.EnrichChunksWithVectorsAsync(allChunks);
|
|
||||||
|
|
||||||
//await repository.SaveChunksAsync(name, chunks);
|
|
||||||
|
|
||||||
return chunks;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> GetAnswerAsync(string name, string question)
|
public async Task<string> GetAnswerAsync(string name, string question)
|
||||||
{
|
{
|
||||||
var query = await vectorizationService.GetVectorAsync(question);
|
var query = await vectorizationService.GetVectorAsync(question);
|
||||||
@@ -45,29 +28,5 @@ namespace CodeBase.Services
|
|||||||
|
|
||||||
return results;
|
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)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,3 +16,9 @@ Patch Notes:
|
|||||||
* НУЖЕН РЕФАКТОРИНГ
|
* НУЖЕН РЕФАКТОРИНГ
|
||||||
* НУЖЕН ПЕРЕСМОТР СКАЧИВАНИЯ LLM Внутри Ollama - долго
|
* НУЖЕН ПЕРЕСМОТР СКАЧИВАНИЯ LLM Внутри Ollama - долго
|
||||||
* Нужна проверка бага: парсинг сохраняется не сразу
|
* Нужна проверка бага: парсинг сохраняется не сразу
|
||||||
|
* v2.1.1:
|
||||||
|
* Реализована попытка в мультипарсинг: шарп, с горем пополам питон, должен еще го и ts
|
||||||
|
* почищены лишние файлы, новая репа для работы с бд, отдельно вынесена иинициализация
|
||||||
|
* теперь ручка парсера принимает на вход путь к проекту, название и язык в виде enum
|
||||||
|
* расширение списка языков проиходит через enum и настройку appsetting посредством добавления строки вида язык:команда докера для сборки файла index через Scip
|
||||||
|
* установлен protobuf для работы с Scip
|
||||||
Reference in New Issue
Block a user