@@ -3,8 +3,12 @@ using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Api.Data;
|
||||
|
||||
|
||||
public sealed class CvMatcherDbContext : DbContext
|
||||
{
|
||||
public const string SchemaName = "cvMatcher";
|
||||
public const string MigrationTableName = "_Migrations";
|
||||
|
||||
public CvMatcherDbContext(DbContextOptions<CvMatcherDbContext> options) : base(options)
|
||||
{
|
||||
}
|
||||
@@ -14,9 +18,11 @@ public sealed class CvMatcherDbContext : DbContext
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.HasDefaultSchema(SchemaName);
|
||||
|
||||
modelBuilder.Entity<CvMatchResultEntity>(entity =>
|
||||
{
|
||||
entity.ToTable("CvMatchResults");
|
||||
entity.ToTable("Results");
|
||||
entity.HasKey(x => x.Id);
|
||||
entity.Property(x => x.Id).HasMaxLength(64);
|
||||
entity.Property(x => x.CvDocumentId).HasMaxLength(64).IsRequired();
|
||||
@@ -28,7 +34,7 @@ public sealed class CvMatcherDbContext : DbContext
|
||||
|
||||
modelBuilder.Entity<CvMatcherChatCacheEntity>(entity =>
|
||||
{
|
||||
entity.ToTable("CvMatcherChatCache");
|
||||
entity.ToTable("ChatCache");
|
||||
entity.HasKey(x => x.CacheKey);
|
||||
entity.Property(x => x.CacheKey).HasMaxLength(64);
|
||||
entity.Property(x => x.Model).HasMaxLength(120).IsRequired();
|
||||
|
||||
@@ -21,7 +21,7 @@ public sealed class EfMatcherRepository : IMatcherRepository
|
||||
public async Task InitializeAsync(CancellationToken ct)
|
||||
{
|
||||
_logger.LogInformation("Ensuring CV matcher database schema exists using EF Core");
|
||||
await _db.Database.EnsureCreatedAsync(ct);
|
||||
//await _db.Database.EnsureCreatedAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<JobMatchResponse?> GetMatchAsync(string cvDocumentId, string jobDocumentId, CancellationToken ct)
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Api.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Api.Migrations
|
||||
{
|
||||
[DbContext(typeof(CvMatcherDbContext))]
|
||||
[Migration("20260507140442_InitialCvMatcherSchema")]
|
||||
partial class InitialCvMatcherSchema
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasDefaultSchema("cvMatcher")
|
||||
.HasAnnotation("ProductVersion", "10.0.7")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Api.Data.Entities.CvMatchResultEntity", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("SYSUTCDATETIME()");
|
||||
|
||||
b.Property<string>("CvDocumentId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<string>("JobDocumentId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<string>("ResultJson")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("Score")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CvDocumentId", "JobDocumentId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Results", "cvMatcher");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Api.Data.Entities.CvMatcherChatCacheEntity", b =>
|
||||
{
|
||||
b.Property<string>("CacheKey")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("SYSUTCDATETIME()");
|
||||
|
||||
b.Property<string>("Model")
|
||||
.IsRequired()
|
||||
.HasMaxLength(120)
|
||||
.HasColumnType("nvarchar(120)");
|
||||
|
||||
b.Property<string>("ResponseText")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<decimal>("Temperature")
|
||||
.HasColumnType("decimal(4,2)");
|
||||
|
||||
b.HasKey("CacheKey");
|
||||
|
||||
b.ToTable("ChatCache", "cvMatcher");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Api.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class InitialCvMatcherSchema : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.EnsureSchema(
|
||||
name: "cvMatcher");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ChatCache",
|
||||
schema: "cvMatcher",
|
||||
columns: table => new
|
||||
{
|
||||
CacheKey = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
||||
Model = table.Column<string>(type: "nvarchar(120)", maxLength: 120, nullable: false),
|
||||
Temperature = table.Column<decimal>(type: "decimal(4,2)", nullable: false),
|
||||
ResponseText = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false, defaultValueSql: "SYSUTCDATETIME()")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ChatCache", x => x.CacheKey);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Results",
|
||||
schema: "cvMatcher",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
||||
CvDocumentId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
||||
JobDocumentId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
||||
ResultJson = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||
Score = table.Column<int>(type: "int", nullable: false),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false, defaultValueSql: "SYSUTCDATETIME()")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Results", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Results_CvDocumentId_JobDocumentId",
|
||||
schema: "cvMatcher",
|
||||
table: "Results",
|
||||
columns: new[] { "CvDocumentId", "JobDocumentId" },
|
||||
unique: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "ChatCache",
|
||||
schema: "cvMatcher");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Results",
|
||||
schema: "cvMatcher");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Api.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Api.Migrations
|
||||
{
|
||||
[DbContext(typeof(CvMatcherDbContext))]
|
||||
partial class CvMatcherDbContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasDefaultSchema("cvMatcher")
|
||||
.HasAnnotation("ProductVersion", "10.0.7")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Api.Data.Entities.CvMatchResultEntity", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("SYSUTCDATETIME()");
|
||||
|
||||
b.Property<string>("CvDocumentId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<string>("JobDocumentId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<string>("ResultJson")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("Score")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CvDocumentId", "JobDocumentId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Results", "cvMatcher");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Api.Data.Entities.CvMatcherChatCacheEntity", b =>
|
||||
{
|
||||
b.Property<string>("CacheKey")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("SYSUTCDATETIME()");
|
||||
|
||||
b.Property<string>("Model")
|
||||
.IsRequired()
|
||||
.HasMaxLength(120)
|
||||
.HasColumnType("nvarchar(120)");
|
||||
|
||||
b.Property<string>("ResponseText")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<decimal>("Temperature")
|
||||
.HasColumnType("decimal(4,2)");
|
||||
|
||||
b.HasKey("CacheKey");
|
||||
|
||||
b.ToTable("ChatCache", "cvMatcher");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,6 @@ using Serilog;
|
||||
using Shared.Models.Settings;
|
||||
using StartupHelpers;
|
||||
using System.Reflection;
|
||||
using System.Xml.Linq;
|
||||
|
||||
StartupExtensions.LoadDotEnvFile();
|
||||
|
||||
@@ -56,7 +55,10 @@ try
|
||||
var configuration = builder.Configuration;
|
||||
var connectionString = builder.Services.GetConfiguredDbConnectionString(configuration);
|
||||
|
||||
options.UseSqlServer(connectionString);
|
||||
options.UseSqlServer(connectionString, sql =>
|
||||
{
|
||||
sql.MigrationsHistoryTable(CvMatcherDbContext.MigrationTableName, CvMatcherDbContext.SchemaName);
|
||||
});
|
||||
});
|
||||
|
||||
builder.Services.AddScoped<IMatcherRepository, EfMatcherRepository>();
|
||||
|
||||
@@ -20,11 +20,5 @@
|
||||
<DependentUpon>.env</DependentUpon>
|
||||
</None>
|
||||
<None Include="docker-compose.yml" />
|
||||
<None Include="docker-compose.staging.yml">
|
||||
<DependentUpon>docker-compose.yml</DependentUpon>
|
||||
</None>
|
||||
<None Include="docker-compose.production.yml">
|
||||
<DependentUpon>docker-compose.yml</DependentUpon>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,94 +0,0 @@
|
||||
services:
|
||||
api:
|
||||
image: registry.easysoft.ro/apps/myai-api:production
|
||||
container_name: myai-api
|
||||
environment:
|
||||
- APP_ENVIRONMENT_NAME=easySoft.ro-Production
|
||||
- ASPNETCORE_ENVIRONMENT=Production
|
||||
- ASPNETCORE_URLS=http://+:8080
|
||||
- Smtp__Host=mail.easysoft.ro
|
||||
- Smtp__Port=587
|
||||
- Smtp__Username=no-reply@myai.ro
|
||||
- Smtp__Password=${Smtp__Password}
|
||||
- Smtp__UseStartTls=true
|
||||
- FileStorage__Path=Files
|
||||
- FileStorage__DefaultFileName=
|
||||
- FileStorage__ToEmail=webmaster@myai.ro
|
||||
- FileStorage__SubjectPrefix=[File Download]
|
||||
- Captcha__Provider=Recaptcha
|
||||
- Captcha__SecretKey=${Captcha__SecretKey}
|
||||
- Captcha__PublicKey=6LfR3NUsAAAAAH1bFYTKlgwp9SBKf5IRB2IOrhBe
|
||||
- Captcha__MinimumScore=0.5
|
||||
- Google__TagManagerId=GTM-NHWC9N2K
|
||||
- Google__MapKey=
|
||||
- Contact__ToEmail=contact@myai.ro
|
||||
- Contact__SubjectPrefix=[Contact]
|
||||
- Subscribe__ToEmail=contact@myai.ro
|
||||
- Subscribe__SubjectPrefix=[Subscribe]
|
||||
- Cors__AllowedOrigins__0=https://myai.ro
|
||||
- Logging__LogLevel__Default=Information
|
||||
- Logging__LogLevel__Microsoft=Warning
|
||||
- Logging__LogLevel__Microsoft__AspNetCore=Warning
|
||||
- Logging__LogLevel__Api=Information
|
||||
- Serilog__WriteTo__2__Args__fromEmail=no-reply@myai.ro
|
||||
- Serilog__WriteTo__2__Args__toEmail=webmaster@myai.ro
|
||||
- Serilog__WriteTo__2__Args__mailServer=mail.easysoft.ro
|
||||
- Serilog__WriteTo__2__Args__networkCredential__userName=no-reply@myai.ro
|
||||
- Serilog__WriteTo__2__Args__networkCredential__password=${Smtp__Password}
|
||||
- Serilog__WriteTo__2__Args__port=587
|
||||
- Serilog__WriteTo__2__Args__enableSsl=true
|
||||
- Ai__OpenAI__ApiKey=${Ai__OpenAI__ApiKey}
|
||||
- Ai__OpenAI__ChatModel=${Ai__OpenAI__ChatModel}
|
||||
- Ai__OpenAI__EmbeddingModel=${Ai__OpenAI__EmbeddingModel}
|
||||
- Ai__OpenAI__TimeoutSeconds=${Ai__OpenAI__TimeoutSeconds}
|
||||
# Database (shared)
|
||||
- Database__Host=${Database__Host}
|
||||
- Database__Port=${Database__Port}
|
||||
- Database__Name=${Database__Name}
|
||||
- Database__User=${Database__User}
|
||||
- Database__Password=${Database__Password}
|
||||
- Database__TrustServerCertificate=${Database__TrustServerCertificate}
|
||||
# Internal API protection
|
||||
- InternalApi__ApiKey=${InternalApi__ApiKey}
|
||||
- InternalApi__RequireApiKey=${InternalApi__RequireApiKey}
|
||||
# CvMatcher API settings used by this service
|
||||
- CvMatcherApi__BaseUrl=${CvMatcherApi__BaseUrl}
|
||||
- CvMatcherApi__InternalApiKey=${CvMatcherApi__InternalApiKey}
|
||||
# RAG settings
|
||||
- Rag__MaxFileSizeMb=${Rag__MaxFileSizeMb}
|
||||
- Rag__ChunkSize=${Rag__ChunkSize}
|
||||
- Rag__ChunkOverlap=${Rag__ChunkOverlap}
|
||||
- Rag__MaxTextChars=${Rag__MaxTextChars}
|
||||
- Rag__DefaultTopK=${Rag__DefaultTopK}
|
||||
- Rag__MaxTopK=${Rag__MaxTopK}
|
||||
- Rag__ClassifyWithAi=${Rag__ClassifyWithAi}
|
||||
volumes:
|
||||
- myai_api_logs:/app/logs
|
||||
- /opt/myai/files:/app/Files
|
||||
networks:
|
||||
- myai-network
|
||||
extra_hosts:
|
||||
- "mail.easysoft.ro:10.0.0.225"
|
||||
restart: unless-stopped
|
||||
labels:
|
||||
- "com.centurylinklabs.watchtower.enable=true"
|
||||
|
||||
web:
|
||||
image: registry.easysoft.ro/apps/myai-web:production
|
||||
container_name: myai-web
|
||||
depends_on:
|
||||
- api
|
||||
ports:
|
||||
- "5140:8080"
|
||||
networks:
|
||||
- myai-network
|
||||
restart: unless-stopped
|
||||
labels:
|
||||
- "com.centurylinklabs.watchtower.enable=true"
|
||||
|
||||
networks:
|
||||
myai-network:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
myai_api_logs:
|
||||
@@ -1,79 +0,0 @@
|
||||
services:
|
||||
api:
|
||||
image: registry.easysoft.ro/apps/myai-api:staging
|
||||
container_name: myai-api
|
||||
environment:
|
||||
- APP_ENVIRONMENT_NAME=myAi.ro-Staging
|
||||
- ASPNETCORE_ENVIRONMENT=Staging
|
||||
- ASPNETCORE_URLS=http://+:8080
|
||||
- Smtp__Host=mail.easysoft.ro
|
||||
- Smtp__Port=587
|
||||
- Smtp__Username=no-reply-staging@easysoft.ro
|
||||
- Smtp__Password=${Smtp__Password}
|
||||
- Smtp__UseStartTls=true
|
||||
- FileStorage__Path=Files
|
||||
- FileStorage__DefaultFileName=
|
||||
- FileStorage__ToEmail=webmaster-staging@easysoft.ro
|
||||
- FileStorage__SubjectPrefix=[File Download]
|
||||
- Captcha__Provider=Recaptcha
|
||||
- Captcha__SecretKey=${Captcha__SecretKey}
|
||||
- Captcha__PublicKey=6LfR3NUsAAAAAH1bFYTKlgwp9SBKf5IRB2IOrhBe
|
||||
- Captcha__MinimumScore=0.5
|
||||
- Google__TagManagerId=GTM-NHWC9N2K
|
||||
- Google__MapKey=
|
||||
- Contact__ToEmail=contact-staging@easysoft.ro
|
||||
- Contact__SubjectPrefix=[Contact]
|
||||
- Subscribe__ToEmail=contact-staging@easysoft.ro
|
||||
- Subscribe__SubjectPrefix=[Subscribe]
|
||||
- Cors__AllowedOrigins__0=https://myai.easysoft.ro
|
||||
- Logging__LogLevel__Default=Information
|
||||
- Logging__LogLevel__Microsoft=Warning
|
||||
- Logging__LogLevel__Microsoft__AspNetCore=Warning
|
||||
- Logging__LogLevel__Api=Information
|
||||
- Serilog__WriteTo__2__Args__fromEmail=no-reply-staging@easysoft.ro
|
||||
- Serilog__WriteTo__2__Args__toEmail=webmaster-staging@easysoft.ro
|
||||
- Serilog__WriteTo__2__Args__mailServer=mail.easysoft.ro
|
||||
- Serilog__WriteTo__2__Args__networkCredential__userName=no-reply-staging@easysoft.ro
|
||||
- Serilog__WriteTo__2__Args__networkCredential__password=${Smtp__Password}
|
||||
- Serilog__WriteTo__2__Args__port=587
|
||||
- Serilog__WriteTo__2__Args__enableSsl=true
|
||||
- OpenAI__ApiKey=${OpenAI__ApiKey}
|
||||
- OpenAI__ChatModel=gpt-4o-mini
|
||||
- OpenAI__EmbeddingModel=text-embedding-3-small
|
||||
- OpenAI__TimeoutSeconds=60
|
||||
- Rag__MaxPdfSizeMb=5
|
||||
- Rag__ChunkSize=900
|
||||
- Rag__ChunkOverlap=150
|
||||
- Rag__CvTtlMinutes=60
|
||||
- Rag__MaxJobTextChars=20000
|
||||
- Rag__TopK=6
|
||||
volumes:
|
||||
- myai_api_logs:/app/logs
|
||||
- /opt/myai/files:/app/Files
|
||||
networks:
|
||||
- myai-network
|
||||
extra_hosts:
|
||||
- "mail.easysoft.ro:10.0.0.225"
|
||||
restart: unless-stopped
|
||||
labels:
|
||||
- "com.centurylinklabs.watchtower.enable=true"
|
||||
|
||||
web:
|
||||
image: registry.easysoft.ro/apps/myai-web:staging
|
||||
container_name: myai-web
|
||||
depends_on:
|
||||
- api
|
||||
ports:
|
||||
- "5140:8080"
|
||||
networks:
|
||||
- myai-network
|
||||
restart: unless-stopped
|
||||
labels:
|
||||
- "com.centurylinklabs.watchtower.enable=true"
|
||||
|
||||
networks:
|
||||
myai-network:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
myai_api_logs:
|
||||
@@ -11,20 +11,56 @@ services:
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
# ASP.NET
|
||||
- ASPNETCORE_ENVIRONMENT=${ASPNETCORE_ENVIRONMENT:-Development}
|
||||
- ASPNETCORE_URLS=http://+:8080
|
||||
- # Database settings (read by the apps as Database:Host/Port/Name/User/Password)
|
||||
- Database__Host=${Database__Host:-mssql}
|
||||
- ASPNETCORE_URLS=${ASPNETCORE_URLS:-http://+:8080}
|
||||
- APP_ENVIRONMENT_NAME=${APP_ENVIRONMENT_NAME:-myai.local}
|
||||
- LogEnvironmentOnStartup=${LogEnvironmentOnStartup:-true}
|
||||
|
||||
# Database: matches rag-api appsettings Database section
|
||||
- Database__Host=${Database__Host:-sqlserver}
|
||||
- Database__Port=${Database__Port:-1433}
|
||||
- Database__Name=${Database__Name:-MyAiCvMatcher}
|
||||
- Database__Name=${Database__Name:-MyAiDb}
|
||||
- Database__User=${Database__User:-sa}
|
||||
- Database__Password=${Database__Password:-Your_strong_password123}
|
||||
- Database__Password=${Database__Password:-}
|
||||
- Database__TrustServerCertificate=${Database__TrustServerCertificate:-true}
|
||||
- InternalApi__RequireApiKey=true
|
||||
|
||||
# InternalApi: matches rag-api appsettings InternalApi section
|
||||
- InternalApi__ApiKey=${InternalApi__ApiKey:-change-this-internal-key}
|
||||
- InternalApi__RequireApiKey=${InternalApi__RequireApiKey:-false}
|
||||
|
||||
# Rag: matches rag-api appsettings Rag section
|
||||
- Rag__MaxFileSizeMb=${Rag__MaxFileSizeMb:-8}
|
||||
- Rag__ChunkSize=${Rag__ChunkSize:-900}
|
||||
- Rag__ChunkOverlap=${Rag__ChunkOverlap:-150}
|
||||
- Rag__MaxTextChars=${Rag__MaxTextChars:-60000}
|
||||
- Rag__DefaultTopK=${Rag__DefaultTopK:-20}
|
||||
- Rag__MaxTopK=${Rag__MaxTopK:-50}
|
||||
- Rag__ClassifyWithAi=${Rag__ClassifyWithAi:-false}
|
||||
|
||||
# Ai: matches rag-api appsettings Ai section
|
||||
- Ai__Provider=${Ai__Provider:-OpenAI}
|
||||
- Ai__OpenAI__ApiKey=${Ai__OpenAI__ApiKey:-}
|
||||
- Ai__OpenAI__ChatModel=${Ai__OpenAI__ChatModel:-gpt-4o-mini}
|
||||
- Ai__OpenAI__EmbeddingModel=${Ai__OpenAI__EmbeddingModel:-text-embedding-3-small}
|
||||
- Ai__OpenAI__TimeoutSeconds=${Ai__OpenAI__TimeoutSeconds:-90}
|
||||
- Ai__Ollama__BaseUrl=${Ai__Ollama__BaseUrl:-http://host.docker.internal:11434}
|
||||
- Ai__Ollama__ChatModel=${Ai__Ollama__ChatModel:-llama3.1:8b}
|
||||
- Ai__Ollama__EmbeddingModel=${Ai__Ollama__EmbeddingModel:-nomic-embed-text}
|
||||
- Ai__Ollama__TimeoutSeconds=${Ai__Ollama__TimeoutSeconds:-180}
|
||||
|
||||
# Logging / Serilog
|
||||
- Logging__LogLevel__Default=${Logging__LogLevel__Default:-Information}
|
||||
- Logging__LogLevel__Microsoft=${Logging__LogLevel__Microsoft:-Warning}
|
||||
- Logging__LogLevel__Microsoft__AspNetCore=${Logging__LogLevel__Microsoft__AspNetCore:-Warning}
|
||||
- Logging__LogLevel__Api=${Logging__LogLevel__Api:-Information}
|
||||
- Serilog__WriteTo__2__Args__fromEmail=${Serilog__WriteTo__2__Args__fromEmail:-}
|
||||
- Serilog__WriteTo__2__Args__toEmail=${Serilog__WriteTo__2__Args__toEmail:-}
|
||||
- Serilog__WriteTo__2__Args__mailServer=${Serilog__WriteTo__2__Args__mailServer:-}
|
||||
- Serilog__WriteTo__2__Args__networkCredential__userName=${Serilog__WriteTo__2__Args__networkCredential__userName:-}
|
||||
- Serilog__WriteTo__2__Args__networkCredential__password=${Serilog__WriteTo__2__Args__networkCredential__password:-}
|
||||
- Serilog__WriteTo__2__Args__port=${Serilog__WriteTo__2__Args__port:-587}
|
||||
- Serilog__WriteTo__2__Args__enableSsl=${Serilog__WriteTo__2__Args__enableSsl:-true}
|
||||
networks:
|
||||
- myai-network
|
||||
restart: unless-stopped
|
||||
@@ -41,47 +77,133 @@ services:
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
# ASP.NET
|
||||
- ASPNETCORE_ENVIRONMENT=${ASPNETCORE_ENVIRONMENT:-Development}
|
||||
- ASPNETCORE_URLS=http://+:8080
|
||||
- # Database settings (cv-matcher uses same keys)
|
||||
- Database__Host=${Database__Host:-mssql}
|
||||
- ASPNETCORE_URLS=${ASPNETCORE_URLS:-http://+:8080}
|
||||
- APP_ENVIRONMENT_NAME=${APP_ENVIRONMENT_NAME:-myai.local}
|
||||
- LogEnvironmentOnStartup=${LogEnvironmentOnStartup:-true}
|
||||
|
||||
# Database: matches cv-matcher-api appsettings Database section
|
||||
- Database__Host=${Database__Host:-sqlserver}
|
||||
- Database__Port=${Database__Port:-1433}
|
||||
- Database__Name=${Database__Name:-MyAiCvMatcher}
|
||||
- Database__Name=${Database__Name:-MyAiDb}
|
||||
- Database__User=${Database__User:-sa}
|
||||
- Database__Password=${Database__Password:-Your_strong_password123}
|
||||
- Database__Password=${Database__Password:-}
|
||||
- Database__TrustServerCertificate=${Database__TrustServerCertificate:-true}
|
||||
- InternalApi__RequireApiKey=true
|
||||
|
||||
# InternalApi: matches cv-matcher-api appsettings InternalApi section
|
||||
- InternalApi__ApiKey=${InternalApi__ApiKey:-change-this-internal-key}
|
||||
- RagApi__BaseUrl=http://rag-api:8080
|
||||
- RagApi__InternalApiKey=${InternalApi__ApiKey:-change-this-internal-key}
|
||||
- InternalApi__RequireApiKey=${InternalApi__RequireApiKey:-false}
|
||||
|
||||
# RagApi: matches cv-matcher-api appsettings RagApi section
|
||||
# IMPORTANT: this must use the container-internal port, not the host-mapped port.
|
||||
- RagApi__BaseUrl=${RagApi__BaseUrl:-http://rag-api:8080}
|
||||
- RagApi__InternalApiKey=${RagApi__InternalApiKey:-${InternalApi__ApiKey:-change-this-internal-key}}
|
||||
|
||||
# Ai: matches cv-matcher-api appsettings Ai section
|
||||
- Ai__Provider=${Ai__Provider:-OpenAI}
|
||||
- Ai__OpenAI__ApiKey=${Ai__OpenAI__ApiKey:-}
|
||||
- Ai__OpenAI__ChatModel=${Ai__OpenAI__ChatModel:-gpt-4o-mini}
|
||||
- Ai__OpenAI__TimeoutSeconds=${Ai__OpenAI__TimeoutSeconds:-90}
|
||||
- Ai__Ollama__BaseUrl=${Ai__Ollama__BaseUrl:-http://host.docker.internal:11434}
|
||||
- Ai__Ollama__ChatModel=${Ai__Ollama__ChatModel:-llama3.1:8b}
|
||||
- Ai__Ollama__TimeoutSeconds=${Ai__Ollama__TimeoutSeconds:-180}
|
||||
|
||||
# Matcher: matches cv-matcher-api appsettings Matcher section
|
||||
- Matcher__TopK=${Matcher__TopK:-10}
|
||||
- Matcher__DeepScoreTopN=${Matcher__DeepScoreTopN:-5}
|
||||
- Matcher__MaxJobTextChars=${Matcher__MaxJobTextChars:-60000}
|
||||
|
||||
# Logging / Serilog
|
||||
- Logging__LogLevel__Default=${Logging__LogLevel__Default:-Information}
|
||||
- Logging__LogLevel__Microsoft=${Logging__LogLevel__Microsoft:-Warning}
|
||||
- Logging__LogLevel__Microsoft__AspNetCore=${Logging__LogLevel__Microsoft__AspNetCore:-Warning}
|
||||
- Logging__LogLevel__Api=${Logging__LogLevel__Api:-Information}
|
||||
- Serilog__WriteTo__2__Args__fromEmail=${Serilog__WriteTo__2__Args__fromEmail:-}
|
||||
- Serilog__WriteTo__2__Args__toEmail=${Serilog__WriteTo__2__Args__toEmail:-}
|
||||
- Serilog__WriteTo__2__Args__mailServer=${Serilog__WriteTo__2__Args__mailServer:-}
|
||||
- Serilog__WriteTo__2__Args__networkCredential__userName=${Serilog__WriteTo__2__Args__networkCredential__userName:-}
|
||||
- Serilog__WriteTo__2__Args__networkCredential__password=${Serilog__WriteTo__2__Args__networkCredential__password:-}
|
||||
- Serilog__WriteTo__2__Args__port=${Serilog__WriteTo__2__Args__port:-587}
|
||||
- Serilog__WriteTo__2__Args__enableSsl=${Serilog__WriteTo__2__Args__enableSsl:-true}
|
||||
networks:
|
||||
- myai-network
|
||||
restart: unless-stopped
|
||||
|
||||
api:
|
||||
depends_on:
|
||||
- cv-matcher-api
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: api/Dockerfile
|
||||
container_name: myai-api
|
||||
depends_on:
|
||||
- cv-matcher-api
|
||||
ports:
|
||||
- "8080:8080"
|
||||
env_file:
|
||||
- ../api/.env
|
||||
- .env
|
||||
# Keep this only if api/.env contains api-specific overrides not present in docker-compose/.env.
|
||||
# - ../api/.env
|
||||
environment:
|
||||
# ASP.NET
|
||||
- ASPNETCORE_ENVIRONMENT=${ASPNETCORE_ENVIRONMENT:-Development}
|
||||
- ASPNETCORE_URLS=${ASPNETCORE_URLS:-http://+:8080}
|
||||
- Cors__AllowedOrigins__0=http://localhost:5000
|
||||
- Cors__AllowedOrigins__1=http://web:8080
|
||||
- CvMatcherApi__BaseUrl=http://cv-matcher-api:8080
|
||||
- CvMatcherApi__InternalApiKey=${CvMatcherApi__InternalApiKey:-change-this-internal-key}
|
||||
- APP_ENVIRONMENT_NAME=${APP_ENVIRONMENT_NAME:-myai.local}
|
||||
- LogEnvironmentOnStartup=${LogEnvironmentOnStartup:-true}
|
||||
|
||||
# Google: matches api appsettings Google section
|
||||
- Google__TagManagerId=${Google__TagManagerId:-}
|
||||
- Google__MapKey=${Google__MapKey:-}
|
||||
|
||||
# Contact / Subscribe: matches api appsettings Contact and Subscribe sections
|
||||
- Contact__ToEmail=${Contact__ToEmail:-}
|
||||
- Contact__FromEmail=${Contact__FromEmail:-${Smtp__Username:-}}
|
||||
- Contact__SubjectPrefix=${Contact__SubjectPrefix:-}
|
||||
- Subscribe__ToEmail=${Subscribe__ToEmail:-}
|
||||
- Subscribe__SubjectPrefix=${Subscribe__SubjectPrefix:-}
|
||||
|
||||
# SMTP: matches api appsettings Smtp section
|
||||
- Smtp__Host=${Smtp__Host:-mail.example.com}
|
||||
- Smtp__Port=${Smtp__Port:-587}
|
||||
- Smtp__Username=${Smtp__Username:-}
|
||||
- Smtp__Password=${Smtp__Password:-}
|
||||
- Smtp__UseStartTls=${Smtp__UseStartTls:-false}
|
||||
|
||||
# Captcha: matches api appsettings Captcha section
|
||||
- Captcha__Provider=${Captcha__Provider:-Recaptcha}
|
||||
- Captcha__SecretKey=${Captcha__SecretKey:-}
|
||||
- Captcha__PublicKey=${Captcha__PublicKey:-}
|
||||
- Captcha__MinimumScore=${Captcha__MinimumScore:-0.5}
|
||||
|
||||
# FileStorage: matches api appsettings FileStorage section
|
||||
- FileStorage__Path=${FileStorage__Path:-Files}
|
||||
- FileStorage__DefaultFileName=${FileStorage__DefaultFileName:-}
|
||||
- FileStorage__ToEmail=${FileStorage__ToEmail:-}
|
||||
- FileStorage__FromEmail=${FileStorage__FromEmail:-${Smtp__Username:-}}
|
||||
- FileStorage__SubjectPrefix=${FileStorage__SubjectPrefix:-[File Download]}
|
||||
|
||||
# CvMatcherApi: matches api appsettings CvMatcherApi section
|
||||
- CvMatcherApi__BaseUrl=${CvMatcherApi__BaseUrl:-http://cv-matcher-api:8080}
|
||||
- CvMatcherApi__InternalApiKey=${CvMatcherApi__InternalApiKey:-${InternalApi__ApiKey:-change-this-internal-key}}
|
||||
|
||||
# CORS: not in the uploaded api appsettings, but used by your API startup config.
|
||||
- Cors__AllowedOrigins__0=${Cors__AllowedOrigins__0:-http://localhost:5000}
|
||||
- Cors__AllowedOrigins__1=${Cors__AllowedOrigins__1:-http://web:8080}
|
||||
|
||||
# Logging / Serilog
|
||||
- Logging__LogLevel__Default=${Logging__LogLevel__Default:-Information}
|
||||
- Logging__LogLevel__Microsoft=${Logging__LogLevel__Microsoft:-Warning}
|
||||
- Logging__LogLevel__Microsoft__AspNetCore=${Logging__LogLevel__Microsoft__AspNetCore:-Warning}
|
||||
- Logging__LogLevel__Api=${Logging__LogLevel__Api:-Information}
|
||||
- Serilog__WriteTo__2__Args__fromEmail=${Serilog__WriteTo__2__Args__fromEmail:-}
|
||||
- Serilog__WriteTo__2__Args__toEmail=${Serilog__WriteTo__2__Args__toEmail:-}
|
||||
- Serilog__WriteTo__2__Args__mailServer=${Serilog__WriteTo__2__Args__mailServer:-}
|
||||
- Serilog__WriteTo__2__Args__networkCredential__userName=${Serilog__WriteTo__2__Args__networkCredential__userName:-}
|
||||
- Serilog__WriteTo__2__Args__networkCredential__password=${Serilog__WriteTo__2__Args__networkCredential__password:-}
|
||||
- Serilog__WriteTo__2__Args__port=${Serilog__WriteTo__2__Args__port:-587}
|
||||
- Serilog__WriteTo__2__Args__enableSsl=${Serilog__WriteTo__2__Args__enableSsl:-true}
|
||||
volumes:
|
||||
- ../api/logs:/app/logs
|
||||
- ${FileStorage__Path:-../Files}:/app/Files
|
||||
networks:
|
||||
- myai-network
|
||||
restart: unless-stopped
|
||||
@@ -100,6 +222,7 @@ services:
|
||||
environment:
|
||||
- ASPNETCORE_ENVIRONMENT=${ASPNETCORE_ENVIRONMENT:-Development}
|
||||
- ASPNETCORE_URLS=${ASPNETCORE_URLS:-http://+:8080}
|
||||
- APP_ENVIRONMENT_NAME=${APP_ENVIRONMENT_NAME:-myai.local}
|
||||
networks:
|
||||
- myai-network
|
||||
restart: unless-stopped
|
||||
|
||||
@@ -5,6 +5,9 @@ namespace Api.Data;
|
||||
|
||||
public sealed class RagDbContext : DbContext
|
||||
{
|
||||
public const string SchemaName = "rag";
|
||||
public const string MigrationTableName = "_Migrations";
|
||||
|
||||
public RagDbContext(DbContextOptions<RagDbContext> options) : base(options)
|
||||
{
|
||||
}
|
||||
@@ -16,9 +19,11 @@ public sealed class RagDbContext : DbContext
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.HasDefaultSchema(SchemaName);
|
||||
|
||||
modelBuilder.Entity<RagDocumentEntity>(entity =>
|
||||
{
|
||||
entity.ToTable("RagDocuments");
|
||||
entity.ToTable("Documents");
|
||||
entity.HasKey(x => x.Id);
|
||||
entity.Property(x => x.Id).HasMaxLength(64);
|
||||
entity.Property(x => x.DocumentType).HasMaxLength(80).IsRequired();
|
||||
@@ -34,7 +39,7 @@ public sealed class RagDbContext : DbContext
|
||||
|
||||
modelBuilder.Entity<RagChunkEntity>(entity =>
|
||||
{
|
||||
entity.ToTable("RagChunks");
|
||||
entity.ToTable("Chunks");
|
||||
entity.HasKey(x => x.Id);
|
||||
entity.Property(x => x.Id).HasMaxLength(64);
|
||||
entity.Property(x => x.DocumentId).HasMaxLength(64).IsRequired();
|
||||
@@ -48,7 +53,7 @@ public sealed class RagDbContext : DbContext
|
||||
|
||||
modelBuilder.Entity<RagEmbeddingCacheEntity>(entity =>
|
||||
{
|
||||
entity.ToTable("RagEmbeddingCache");
|
||||
entity.ToTable("EmbeddingCache");
|
||||
entity.HasKey(x => x.CacheKey);
|
||||
entity.Property(x => x.CacheKey).HasMaxLength(64);
|
||||
entity.Property(x => x.Model).HasMaxLength(120).IsRequired();
|
||||
@@ -60,7 +65,7 @@ public sealed class RagDbContext : DbContext
|
||||
|
||||
modelBuilder.Entity<RagChatCompletionCacheEntity>(entity =>
|
||||
{
|
||||
entity.ToTable("RagChatCompletionCache");
|
||||
entity.ToTable("ChatCompletionCache");
|
||||
entity.HasKey(x => x.CacheKey);
|
||||
entity.Property(x => x.CacheKey).HasMaxLength(64);
|
||||
entity.Property(x => x.Model).HasMaxLength(120).IsRequired();
|
||||
|
||||
@@ -20,7 +20,7 @@ public sealed class EfRagRepository : IRagRepository
|
||||
public async Task InitializeAsync(CancellationToken ct)
|
||||
{
|
||||
_logger.LogInformation("Ensuring RAG database schema exists using EF Core");
|
||||
await _db.Database.EnsureCreatedAsync(ct);
|
||||
//await _db.Database.EnsureCreatedAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<RagDocumentRecord?> GetDocumentByTextHashAsync(string textHash, string? sourceUrl, CancellationToken ct)
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Api.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Api.Migrations
|
||||
{
|
||||
[DbContext(typeof(RagDbContext))]
|
||||
[Migration("20260507140305_InitialRagSchema")]
|
||||
partial class InitialRagSchema
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasDefaultSchema("rag")
|
||||
.HasAnnotation("ProductVersion", "10.0.7")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Api.Data.Entities.RagChatCompletionCacheEntity", b =>
|
||||
{
|
||||
b.Property<string>("CacheKey")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("SYSUTCDATETIME()");
|
||||
|
||||
b.Property<string>("Model")
|
||||
.IsRequired()
|
||||
.HasMaxLength(120)
|
||||
.HasColumnType("nvarchar(120)");
|
||||
|
||||
b.Property<string>("ResponseText")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<decimal>("Temperature")
|
||||
.HasColumnType("decimal(4,2)");
|
||||
|
||||
b.HasKey("CacheKey");
|
||||
|
||||
b.ToTable("ChatCompletionCache", "rag");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Api.Data.Entities.RagChunkEntity", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<int>("ChunkIndex")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("DocumentId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<byte[]>("Embedding")
|
||||
.IsRequired()
|
||||
.HasColumnType("varbinary(max)");
|
||||
|
||||
b.Property<string>("Text")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("DocumentId");
|
||||
|
||||
b.ToTable("Chunks", "rag");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Api.Data.Entities.RagDocumentEntity", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("SYSUTCDATETIME()");
|
||||
|
||||
b.Property<string>("DocumentType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(80)
|
||||
.HasColumnType("nvarchar(80)");
|
||||
|
||||
b.Property<string>("MetadataJson")
|
||||
.IsRequired()
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.HasDefaultValue("{}");
|
||||
|
||||
b.Property<string>("RawText")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("SourceUrl")
|
||||
.HasMaxLength(1200)
|
||||
.HasColumnType("nvarchar(1200)");
|
||||
|
||||
b.Property<string>("TextHash")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasMaxLength(300)
|
||||
.HasColumnType("nvarchar(300)");
|
||||
|
||||
b.Property<double>("TypeConfidence")
|
||||
.HasColumnType("float");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("DocumentType");
|
||||
|
||||
b.HasIndex("TextHash");
|
||||
|
||||
b.ToTable("Documents", "rag");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Api.Data.Entities.RagEmbeddingCacheEntity", b =>
|
||||
{
|
||||
b.Property<string>("CacheKey")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("SYSUTCDATETIME()");
|
||||
|
||||
b.Property<string>("Model")
|
||||
.IsRequired()
|
||||
.HasMaxLength(120)
|
||||
.HasColumnType("nvarchar(120)");
|
||||
|
||||
b.Property<string>("TextHash")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<byte[]>("Vector")
|
||||
.IsRequired()
|
||||
.HasColumnType("varbinary(max)");
|
||||
|
||||
b.HasKey("CacheKey");
|
||||
|
||||
b.HasIndex("TextHash");
|
||||
|
||||
b.ToTable("EmbeddingCache", "rag");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Api.Data.Entities.RagChunkEntity", b =>
|
||||
{
|
||||
b.HasOne("Api.Data.Entities.RagDocumentEntity", "Document")
|
||||
.WithMany("Chunks")
|
||||
.HasForeignKey("DocumentId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Document");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Api.Data.Entities.RagDocumentEntity", b =>
|
||||
{
|
||||
b.Navigation("Chunks");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Api.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class InitialRagSchema : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.EnsureSchema(
|
||||
name: "rag");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ChatCompletionCache",
|
||||
schema: "rag",
|
||||
columns: table => new
|
||||
{
|
||||
CacheKey = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
||||
Model = table.Column<string>(type: "nvarchar(120)", maxLength: 120, nullable: false),
|
||||
Temperature = table.Column<decimal>(type: "decimal(4,2)", nullable: false),
|
||||
ResponseText = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false, defaultValueSql: "SYSUTCDATETIME()")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ChatCompletionCache", x => x.CacheKey);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Documents",
|
||||
schema: "rag",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
||||
DocumentType = table.Column<string>(type: "nvarchar(80)", maxLength: 80, nullable: false),
|
||||
Title = table.Column<string>(type: "nvarchar(300)", maxLength: 300, nullable: false),
|
||||
SourceUrl = table.Column<string>(type: "nvarchar(1200)", maxLength: 1200, nullable: true),
|
||||
RawText = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||
TextHash = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
||||
TypeConfidence = table.Column<double>(type: "float", nullable: false),
|
||||
MetadataJson = table.Column<string>(type: "nvarchar(max)", nullable: false, defaultValue: "{}"),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false, defaultValueSql: "SYSUTCDATETIME()")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Documents", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "EmbeddingCache",
|
||||
schema: "rag",
|
||||
columns: table => new
|
||||
{
|
||||
CacheKey = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
||||
Model = table.Column<string>(type: "nvarchar(120)", maxLength: 120, nullable: false),
|
||||
TextHash = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
||||
Vector = table.Column<byte[]>(type: "varbinary(max)", nullable: false),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false, defaultValueSql: "SYSUTCDATETIME()")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_EmbeddingCache", x => x.CacheKey);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Chunks",
|
||||
schema: "rag",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
||||
DocumentId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
||||
ChunkIndex = table.Column<int>(type: "int", nullable: false),
|
||||
Text = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||
Embedding = table.Column<byte[]>(type: "varbinary(max)", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Chunks", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Chunks_Documents_DocumentId",
|
||||
column: x => x.DocumentId,
|
||||
principalSchema: "rag",
|
||||
principalTable: "Documents",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Chunks_DocumentId",
|
||||
schema: "rag",
|
||||
table: "Chunks",
|
||||
column: "DocumentId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Documents_DocumentType",
|
||||
schema: "rag",
|
||||
table: "Documents",
|
||||
column: "DocumentType");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Documents_TextHash",
|
||||
schema: "rag",
|
||||
table: "Documents",
|
||||
column: "TextHash");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EmbeddingCache_TextHash",
|
||||
schema: "rag",
|
||||
table: "EmbeddingCache",
|
||||
column: "TextHash");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "ChatCompletionCache",
|
||||
schema: "rag");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Chunks",
|
||||
schema: "rag");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "EmbeddingCache",
|
||||
schema: "rag");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Documents",
|
||||
schema: "rag");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Api.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Api.Migrations
|
||||
{
|
||||
[DbContext(typeof(RagDbContext))]
|
||||
partial class RagDbContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasDefaultSchema("rag")
|
||||
.HasAnnotation("ProductVersion", "10.0.7")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Api.Data.Entities.RagChatCompletionCacheEntity", b =>
|
||||
{
|
||||
b.Property<string>("CacheKey")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("SYSUTCDATETIME()");
|
||||
|
||||
b.Property<string>("Model")
|
||||
.IsRequired()
|
||||
.HasMaxLength(120)
|
||||
.HasColumnType("nvarchar(120)");
|
||||
|
||||
b.Property<string>("ResponseText")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<decimal>("Temperature")
|
||||
.HasColumnType("decimal(4,2)");
|
||||
|
||||
b.HasKey("CacheKey");
|
||||
|
||||
b.ToTable("ChatCompletionCache", "rag");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Api.Data.Entities.RagChunkEntity", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<int>("ChunkIndex")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("DocumentId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<byte[]>("Embedding")
|
||||
.IsRequired()
|
||||
.HasColumnType("varbinary(max)");
|
||||
|
||||
b.Property<string>("Text")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("DocumentId");
|
||||
|
||||
b.ToTable("Chunks", "rag");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Api.Data.Entities.RagDocumentEntity", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("SYSUTCDATETIME()");
|
||||
|
||||
b.Property<string>("DocumentType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(80)
|
||||
.HasColumnType("nvarchar(80)");
|
||||
|
||||
b.Property<string>("MetadataJson")
|
||||
.IsRequired()
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.HasDefaultValue("{}");
|
||||
|
||||
b.Property<string>("RawText")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("SourceUrl")
|
||||
.HasMaxLength(1200)
|
||||
.HasColumnType("nvarchar(1200)");
|
||||
|
||||
b.Property<string>("TextHash")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasMaxLength(300)
|
||||
.HasColumnType("nvarchar(300)");
|
||||
|
||||
b.Property<double>("TypeConfidence")
|
||||
.HasColumnType("float");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("DocumentType");
|
||||
|
||||
b.HasIndex("TextHash");
|
||||
|
||||
b.ToTable("Documents", "rag");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Api.Data.Entities.RagEmbeddingCacheEntity", b =>
|
||||
{
|
||||
b.Property<string>("CacheKey")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("SYSUTCDATETIME()");
|
||||
|
||||
b.Property<string>("Model")
|
||||
.IsRequired()
|
||||
.HasMaxLength(120)
|
||||
.HasColumnType("nvarchar(120)");
|
||||
|
||||
b.Property<string>("TextHash")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<byte[]>("Vector")
|
||||
.IsRequired()
|
||||
.HasColumnType("varbinary(max)");
|
||||
|
||||
b.HasKey("CacheKey");
|
||||
|
||||
b.HasIndex("TextHash");
|
||||
|
||||
b.ToTable("EmbeddingCache", "rag");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Api.Data.Entities.RagChunkEntity", b =>
|
||||
{
|
||||
b.HasOne("Api.Data.Entities.RagDocumentEntity", "Document")
|
||||
.WithMany("Chunks")
|
||||
.HasForeignKey("DocumentId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Document");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Api.Data.Entities.RagDocumentEntity", b =>
|
||||
{
|
||||
b.Navigation("Chunks");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-1
@@ -36,7 +36,10 @@ try
|
||||
var configuration = builder.Configuration;
|
||||
var connectionString = builder.Services.GetConfiguredDbConnectionString(configuration);
|
||||
|
||||
options.UseSqlServer(connectionString);
|
||||
options.UseSqlServer(connectionString, sql =>
|
||||
{
|
||||
sql.MigrationsHistoryTable(RagDbContext.MigrationTableName, RagDbContext.SchemaName);
|
||||
});
|
||||
});
|
||||
|
||||
builder.Services.AddHttpClient<RagAiClient>();
|
||||
|
||||
Reference in New Issue
Block a user