build-flow.api/tests/BuildFlow.Rag.Tests/AiModelRuntimeConfigResolverTests.cs
xiangning 3b13099378 refactor(domain): 重构领域实体结构并更新枚举命名
- 将通用 Entities 命名空间拆分为具体业务实体子目录
- 更新所有实体类型引用为具体的 Entity 后缀类型
- 将 ModelType、ModelProvider、GroupMemberRole 等枚举重命名为 Enum 后缀
- 重构 AiModel 相关配置和映射逻辑适配新的实体结构
- 更新测试代码中的实体类型引用和枚举使用
- 调整应用层处理器中的实体创建和查询逻辑
- 修改 appsettings.json 中的基础设施主机配置为动态占位符模式
2026-07-19 14:02:32 +08:00

139 lines
5.6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using BuildFlow.Domain.Entities.AiModel;
using BuildFlow.Domain.Entities.ChatMessage;
using BuildFlow.Domain.Entities.Conversation;
using BuildFlow.Domain.Entities.Document;
using BuildFlow.Domain.Entities.DocumentChunk;
using BuildFlow.Domain.Entities.KnowledgeBase;
using BuildFlow.Domain.Enums;
using BuildFlow.Infrastructure.AI;
using BuildFlow.Infrastructure.Persistence;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Xunit;
namespace BuildFlow.Rag.Tests;
/// <summary>
/// AI 运行时模型配置解析测试。确保 Chat/Embedding 调用优先使用模型管理里配置的默认本地模型。
/// </summary>
public class AiModelRuntimeConfigResolverTests
{
[Fact]
public async Task ResolveAsyncShouldReplaceLegacyRagOnlyChatInstructions()
{
var services = new ServiceCollection();
var dbName = $"rag-chat-model-{Guid.NewGuid():N}";
services.AddDbContext<RagDbContext>(options =>
options.UseInMemoryDatabase(dbName));
services.AddScoped<AiModelRuntimeConfigResolver>();
services.AddSingleton(Options.Create(new AiOptions
{
BaseUrl = "http://localhost:1234/v1",
ApiKey = "lm-studio",
ChatModel = "fallback-chat",
EmbeddingModel = "fallback-embedding",
DefaultInstructions = "你是 BuildFlow AI 助手。没有知识库上下文时按普通对话直接回答。",
}));
await using var provider = services.BuildServiceProvider();
await using (var scope = provider.CreateAsyncScope())
{
var db = scope.ServiceProvider.GetRequiredService<RagDbContext>();
await db.AiModels.AddAsync(new AiModelEntity
{
Id = Guid.NewGuid(),
Name = "旧对话模型",
Provider = ModelProviderEnum.LMStudio,
ModelId = "qwen/qwen3.6-27b",
BaseUrl = "http://localhost:1234/v1",
ApiKey = "lm-studio",
ModelType = ModelTypeEnum.Chat,
IsActive = true,
IsDefault = true,
DefaultInstructions = "你是一个RAG知识库助手,基于提供的上下文准确回答用户问题。如果上下文中没有相关信息,请如实说明。",
CreatedBy = "test",
UpdatedBy = "test",
OperatorIP = "127.0.0.1",
});
await db.SaveChangesAsync();
}
await using (var scope = provider.CreateAsyncScope())
{
var resolver = scope.ServiceProvider.GetRequiredService<AiModelRuntimeConfigResolver>();
var config = await resolver.ResolveAsync(ModelTypeEnum.Chat, CancellationToken.None);
Assert.Equal("你是 BuildFlow AI 助手。没有知识库上下文时按普通对话直接回答。", config.DefaultInstructions);
}
}
[Fact]
public async Task ResolveAsyncShouldPreferDefaultEmbeddingModelFromDatabase()
{
var services = new ServiceCollection();
var dbName = $"rag-ai-model-{Guid.NewGuid():N}";
services.AddDbContext<RagDbContext>(options =>
options.UseInMemoryDatabase(dbName));
services.AddScoped<AiModelRuntimeConfigResolver>();
services.AddSingleton(Options.Create(new AiOptions
{
BaseUrl = "https://fallback.example/v1",
ApiKey = "fallback-key",
ChatModel = "fallback-chat",
EmbeddingModel = "fallback-embedding",
DefaultInstructions = "fallback instructions",
}));
await using var provider = services.BuildServiceProvider();
await using (var scope = provider.CreateAsyncScope())
{
var db = scope.ServiceProvider.GetRequiredService<RagDbContext>();
await db.AiModels.AddRangeAsync(
new AiModelEntity
{
Id = Guid.NewGuid(),
Name = "旧嵌入模型",
Provider = ModelProviderEnum.OpenAI,
ModelId = "remote-embedding",
BaseUrl = "https://remote.example/v1",
ApiKey = "remote-key",
ModelType = ModelTypeEnum.Embedding,
IsActive = true,
IsDefault = false,
CreatedBy = "test",
UpdatedBy = "test",
OperatorIP = "127.0.0.1",
},
new AiModelEntity
{
Id = Guid.NewGuid(),
Name = "本地嵌入模型",
Provider = ModelProviderEnum.LMStudio,
ModelId = "text-embedding-embeddinggemma-300m",
BaseUrl = "http://localhost:1234/v1",
ApiKey = "lm-studio",
ModelType = ModelTypeEnum.Embedding,
IsActive = true,
IsDefault = true,
CreatedBy = "test",
UpdatedBy = "test",
OperatorIP = "127.0.0.1",
});
await db.SaveChangesAsync();
}
await using (var scope = provider.CreateAsyncScope())
{
var resolver = scope.ServiceProvider.GetRequiredService<AiModelRuntimeConfigResolver>();
var config = await resolver.ResolveAsync(ModelTypeEnum.Embedding, CancellationToken.None);
Assert.Equal("text-embedding-embeddinggemma-300m", config.ModelId);
Assert.Equal("http://localhost:1234/v1", config.BaseUrl);
Assert.Equal("lm-studio", config.ApiKey);
}
}
}