Skip to content

Commit

Permalink
Merge pull request #46 from sdcb/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
sdcb authored Jan 20, 2025
2 parents dc8044d + f7dff8b commit 9ce6ae5
Show file tree
Hide file tree
Showing 92 changed files with 647 additions and 346 deletions.
22 changes: 22 additions & 0 deletions .github/workflows/build-container.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@ jobs:
name: chats-fe
path: ./src/BE/wwwroot

- name: sed version
run: |
# file: ./src/BE/Controllers/Admin/GlobalConfigs/VersionController.cs
# const int buildVersion = 0; -> const int buildVersion = github.run_number;
sed -i "s/const int buildVersion = 0;/const int buildVersion = ${{ github.run_number }};/" ./src/BE/Controllers/Admin/GlobalConfigs/VersionController.cs
- name: Build container
run: |
dotnet publish ./src/BE/Chats.BE.csproj -c Release --os linux --arch x64 /t:PublishContainer /p:ContainerRepository=chats
Expand Down Expand Up @@ -143,6 +149,12 @@ jobs:
name: chats-fe
path: ./src/BE/wwwroot

- name: sed version
run: |
# file: ./src/BE/Controllers/Admin/GlobalConfigs/VersionController.cs
# const int buildVersion = 0; -> const int buildVersion = github.run_number;
sed -i "s/const int buildVersion = 0;/const int buildVersion = ${{ github.run_number }};/" ./src/BE/Controllers/Admin/GlobalConfigs/VersionController.cs
- name: Build container
run: |
dotnet publish ./src/BE/Chats.BE.csproj -c Release --os ${{ matrix.os }} --arch ${{ matrix.arch }} /t:PublishContainer /p:ContainerRepository=chats ${{ matrix.args }}
Expand Down Expand Up @@ -228,6 +240,12 @@ jobs:
name: chats-fe
path: ./src/BE/wwwroot

- name: sed version
run: |
# file: ./src/BE/Controllers/Admin/GlobalConfigs/VersionController.cs
# const int buildVersion = 0; -> const int buildVersion = github.run_number;
sed -i "s/const int buildVersion = 0;/const int buildVersion = ${{ github.run_number }};/" ./src/BE/Controllers/Admin/GlobalConfigs/VersionController.cs
- name: build binary
run: |
dotnet publish ./src/BE/Chats.BE.csproj -c Release -o ./Publish ${{ matrix.args }} /p:DeleteExistingFiles=True
Expand Down Expand Up @@ -291,6 +309,8 @@ jobs:
target_commitish: '${{ github.sha }}',
body: `### Full Changelogs
https://github.com/sdcb/chats/compare/${latestTag}...r-${{ github.run_number }}
<details>
### Docker
| Description | Docker Image |
Expand All @@ -317,6 +337,8 @@ jobs:

**NOTE**:
Replace \`r${{ github.run_number }}\` with \`latest\` in the download link to get the latest version, for example: \`${{ vars.MINIO_URL }}/chats/latest/chats-win-x64.7z\`

</details>
`,
draft: false,
prerelease: false,
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,9 @@ C:\Users\ZhouJie\Downloads\chats-win-x64>dir
- **启动应用**:运行 `Chats.BE.exe` 即可启动 Chats 应用,该文件名虽指“后端”,但实际同时包含前端和后端组件。
- **数据库配置**:默认情况下,应用将在当前目录创建名为 `AppData` 的目录,并以 SQLite 作为数据库。命令行参数可用于指定不同的数据库类型:
```pwsh
.\Chats.BE.exe --DBType=mssql --ConnectionStrings:ChatsDB="Data Source=(localdb)\mssqllocaldb; Initial Catalog=ChatsDB; Integrated Security=True"
.\Chats.BE.exe --urls http://+:5000 --DBType=mssql --ConnectionStrings:ChatsDB="Data Source=(localdb)\mssqllocaldb; Initial Catalog=ChatsDB; Integrated Security=True"
```
- 参数 `--urls`:用于指定应用监听的地址和端口。
- 参数 `DBType`:可选 `sqlite``mssql``pgsql`
- 参数 `--ConnectionStrings:ChatsDB`:用于指定数据库的ADO.NET连接字符串。

Expand Down
3 changes: 2 additions & 1 deletion README_EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,9 @@ C:\Users\ZhouJie\Downloads\chats-win-x64>dir
- **Start Application**: Run `Chats.BE.exe` to start the Chats application. Although this filename indicates "backend," it actually contains both frontend and backend components.
- **Database Configuration**: By default, the application will create a directory named `AppData` in the current directory and use SQLite as the database. Command-line parameters can be used to specify a different database type:
```pwsh
.\Chats.BE.exe --DBType=mssql --ConnectionStrings:ChatsDB="Data Source=(localdb)\mssqllocaldb; Initial Catalog=ChatsDB; Integrated Security=True"
.\Chats.BE.exe --urls http://+:5000 --DBType=mssql --ConnectionStrings:ChatsDB="Data Source=(localdb)\mssqllocaldb; Initial Catalog=ChatsDB; Integrated Security=True"
```
- Parameter `--urls`: Used to specify the address and port the application listens on.
- Parameter `DBType`: Options are `sqlite`, `mssql`, or `pgsql`.
- Parameter `--ConnectionStrings:ChatsDB`: For specifying the ADO.NET connection string for the database.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Chats.BE.Services.ChatServices.Extensions;
using Chats.BE.Services.Models.Extensions;
using OpenAI.Chat;
using System.Runtime.CompilerServices;

Expand Down
12 changes: 6 additions & 6 deletions src/BE/Chats.BE.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@

<ItemGroup>
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" />
<PackageReference Include="AWSSDK.S3" Version="3.7.412" />
<PackageReference Include="AWSSDK.S3" Version="3.7.412.2" />
<PackageReference Include="Azure.AI.OpenAI" Version="2.1.0" />
<PackageReference Include="Azure.Storage.Blobs" Version="12.23.0" /> <!-- fix security issue from Azure.AI.OpenAI -->
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.1">
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand All @@ -22,16 +22,16 @@
<PackageReference Include="Microsoft.ML.Tokenizers" Version="1.0.1" />
<PackageReference Include="Microsoft.ML.Tokenizers.Data.Cl100kBase" Version="1.0.1" />
<PackageReference Include="Microsoft.ML.Tokenizers.Data.O200kBase" Version="1.0.1" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.2" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.3" />
<PackageReference Include="OpenAI" Version="2.1.0" />
<PackageReference Include="Sdcb.DashScope" Version="2.0.0" />
<PackageReference Include="Sdcb.WenXinQianFan" Version="1.2.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.2.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.3.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.3.1" />
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
<PackageReference Include="System.Runtime.Caching" Version="9.0.1" />
<PackageReference Include="TencentCloudSDK.Hunyuan" Version="3.0.1165" />
<PackageReference Include="TencentCloudSDK.Sms" Version="3.0.1165" />
<PackageReference Include="TencentCloudSDK.Hunyuan" Version="3.0.1166" />
<PackageReference Include="TencentCloudSDK.Sms" Version="3.0.1166" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
using Chats.BE.Controllers.Common.Dtos;
using Chats.BE.DB;
using Chats.BE.Infrastructure;
using Chats.BE.Services.ChatServices;
using Chats.BE.Services.Models;
using Chats.BE.Services.FileServices;
using Chats.BE.Services.UrlEncryption;
using Microsoft.AspNetCore.Mvc;
Expand Down Expand Up @@ -100,6 +100,7 @@ public async Task<ActionResult<ChatsResponseWithMessage>> GetAdminMessage(int ch
.ToArray(),
CreatedAt = x.CreatedAt,
SpanId = x.SpanId,
Edited = x.Edited,
Usage = x.Usage == null ? null : new ChatMessageTempUsage()
{
InputTokens = x.Usage.InputTokens,
Expand Down
15 changes: 4 additions & 11 deletions src/BE/Controllers/Admin/AdminModels/AdminModelsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
using Chats.BE.DB.Jsons;
using Chats.BE.Infrastructure;
using Chats.BE.Services;
using Chats.BE.Services.ChatServices;
using Chats.BE.Services.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.EntityFrameworkCore;
Expand Down Expand Up @@ -122,13 +122,6 @@ public async Task<ActionResult<int>> FastCreateModel([FromBody] ValidateModelReq
return BadRequest($"Invalid ModelReferenceId: {req.ModelReferenceId}");
}

bool hasExistingModel = await db.Models
.AnyAsync(x => x.ModelKeyId == req.ModelKeyId && x.ModelReferenceId == req.ModelReferenceId && x.DeploymentName == req.DeploymentName, cancellationToken);
if (hasExistingModel)
{
return BadRequest("Model already exists");
}

Model toCreate = new()
{
ModelKeyId = req.ModelKeyId,
Expand Down Expand Up @@ -182,7 +175,7 @@ public async Task<ActionResult> DeleteModel(short modelId, CancellationToken can
[HttpPost("models/validate")]
public async Task<ActionResult<ModelValidateResult>> ValidateModel(
[FromBody] ValidateModelRequest req,
[FromServices] ChatFactory conversationFactory,
[FromServices] ChatFactory chatFactory,
CancellationToken cancellationToken)
{
ModelKey? modelKey = await db.ModelKeys
Expand All @@ -204,12 +197,12 @@ public async Task<ActionResult<ModelValidateResult>> ValidateModel(
return this.BadRequestMessage($"Model reference id: {req.ModelReferenceId} not found");
}

ModelValidateResult result = await conversationFactory.ValidateModel(modelKey, modelReference, req.DeploymentName, cancellationToken);
ModelValidateResult result = await chatFactory.ValidateModel(modelKey, modelReference, req.DeploymentName, cancellationToken);
return Ok(result);
}

[HttpGet("user-models/{userId:int}")]
public async Task<ActionResult<IEnumerable<UserModelDto>>> GetUserModels(int userId, CancellationToken cancellationToken)
public async Task<ActionResult<UserModelDto[]>> GetUserModels(int userId, CancellationToken cancellationToken)
{
UserModelDto[] userModels = await db.Models
.Where(x => !x.IsDeleted)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Chats.BE.Controllers.Admin.GlobalConfigs.Dtos;
using Chats.BE.Controllers.Common;
using Chats.BE.DB;
using Chats.BE.Services.Configs;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Text.Json;
Expand Down
7 changes: 4 additions & 3 deletions src/BE/Controllers/Admin/GlobalConfigs/VersionController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,17 @@ namespace Chats.BE.Controllers.Admin.GlobalConfigs;
[AuthorizeAdmin, Route("api/version")]
public class VersionController : ControllerBase
{
// it will be replaced by CI/CD pipeline
const int buildVersion = 0;

[HttpGet("current")]
public ActionResult GetCurrentVersion()
[HttpGet]
public ActionResult<int> GetCurrentVersion()
{
return Ok(buildVersion);
}

[HttpPost("check-update")]
public async Task<ActionResult> CheckUpdate(CancellationToken cancellationToken)
public async Task<ActionResult<CheckUpdateResponse>> CheckUpdate(CancellationToken cancellationToken)
{
string tagName = await GitHubReleaseChecker.SdcbChats.GetLatestReleaseTagNameAsync(cancellationToken);
bool hasNewVersion = GitHubReleaseChecker.IsNewVersionAvailableAsync(tagName, buildVersion);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using Chats.BE.DB;
using Chats.BE.Services.ChatServices;
using Chats.BE.Services.Models;
using System.Text.Json.Serialization;

namespace Chats.BE.Controllers.Admin.ModelKeys.Dtos;
Expand Down
156 changes: 73 additions & 83 deletions src/BE/Controllers/Admin/ModelKeys/ModelKeysController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
using Chats.BE.Controllers.Admin.ModelKeys.Dtos;
using Chats.BE.Controllers.Common;
using Chats.BE.DB;
using Chats.BE.DB.Enums;
using Chats.BE.Services.Common;
using Chats.BE.Services.ChatServices;
using Chats.BE.Services.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

Expand Down Expand Up @@ -114,75 +115,8 @@ public async Task<ActionResult> DeleteModelKey(short modelKeyId, CancellationTok
return NoContent();
}

[HttpPost("{modelKeyId:int}/auto-create-models")]
public async Task<ActionResult<AutoCreateModelResult[]>> AutoCreateModels(short modelKeyId, [FromServices] ChatFactory conversationFactory, CancellationToken cancellationToken)
{
ModelKey? modelKey = await db
.ModelKeys
.Include(x => x.Models)
.AsSplitQuery()
.FirstOrDefaultAsync(x => x.Id == modelKeyId, cancellationToken);

if (modelKey == null)
{
return NotFound();
}

HashSet<short> existingModelRefIds = modelKey.Models
.Select(x => x.ModelReferenceId)
.ToHashSet();

ModelReference[] readyRefs = await db.ModelReferences
.Include(x => x.CurrencyCodeNavigation)
.Where(x => !x.IsLegacy && x.ProviderId == modelKey.ModelProviderId)
.ToArrayAsync(cancellationToken);

ParepareAutoCreateModelResult[] scanedModels = await Task.WhenAll(readyRefs
.Select(async r => existingModelRefIds.Contains(r.Id)
? ParepareAutoCreateModelResult.ModelAlreadyExists(r)
: ParepareAutoCreateModelResult.FromModelValidateResult(await conversationFactory.ValidateModel(modelKey, r, r.Name, cancellationToken), r)));

FileService? fileService = await db.FileServices
.OrderByDescending(x => x.Id)
.FirstOrDefaultAsync(cancellationToken);
AutoCreateModelResult[] results = await scanedModels
.ToAsyncEnumerable()
.SelectAwait(async (m) =>
{
if (!m.IsValidationPassed)
{
return m.ToResult(null);
}

db.Models.Add(new Model
{
ModelKeyId = modelKeyId,
ModelReferenceId = m.ModelReference.Id,
Name = m.ModelReference.Name,
CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow,
DeploymentName = null, // don't need to specify deploymentName because it's auto created
IsDeleted = false,
InputTokenPrice1M = m.ModelReference.InputTokenPrice1M * m.ModelReference.CurrencyCodeNavigation.ExchangeRate,
OutputTokenPrice1M = m.ModelReference.OutputTokenPrice1M * m.ModelReference.CurrencyCodeNavigation.ExchangeRate,
});
try
{
await db.SaveChangesAsync(cancellationToken);
return m.ToResult(null);
}
catch (Exception ex)
{
return m.ToResult(ex.Message);
}
})
.ToArrayAsync(cancellationToken);

return Ok(results);
}

[HttpGet("{modelKeyId:int}/possible-models")]
public async Task<ActionResult<PossibleModelDto[]>> ListModelKeyPossibleModels(short modelKeyId, CancellationToken cancellationToken)
public async Task<ActionResult<PossibleModelDto[]>> ListModelKeyPossibleModels(short modelKeyId, [FromServices] ChatFactory cf, CancellationToken cancellationToken)
{
ModelKey? modelKey = await db
.ModelKeys
Expand All @@ -195,21 +129,77 @@ public async Task<ActionResult<PossibleModelDto[]>> ListModelKeyPossibleModels(s
return NotFound();
}

PossibleModelDto[] readyRefs = await db.ModelReferences
.Where(x => x.ProviderId == modelKey.ModelProviderId)
.OrderBy(x => x.Name)
.Select(x => new PossibleModelDto()
DBModelProvider modelProvider = (DBModelProvider)modelKey.ModelProviderId;
ModelLoader? loader = cf.CreateModelLoader(modelProvider);
if (loader != null)
{
string[] models = await loader.ListModels(modelKey, cancellationToken);
HashSet<string> existsDeploymentNames = await db.Models
.Where(x => x.ModelKeyId == modelKeyId && x.DeploymentName != null)
.Select(x => x.DeploymentName!)
.ToHashSetAsync(cancellationToken);

if (modelProvider == DBModelProvider.Ollama)
{
DeploymentName = x.Models.FirstOrDefault(m => m.ModelKeyId == modelKeyId)!.DeploymentName,
ReferenceId = x.Id,
ReferenceName = x.Name,
IsLegacy = x.IsLegacy,
IsExists = x.Models.Any(m => m.ModelKeyId == modelKeyId),
})
.OrderBy(x => (x.IsLegacy ? 1 : 0) + (x.IsExists ? 2 : 0))
.ThenByDescending(x => x.ReferenceId)
.ToArrayAsync(cancellationToken);
Dictionary<short, ModelReference> referenceOptions = await db.ModelReferences
.Where(x => x.ProviderId == modelKey.ModelProviderId)
.ToDictionaryAsync(k => k.Id, v => v, cancellationToken);

return Ok(readyRefs);
return Ok(models.Select(model =>
{
bool isVision = model.Contains("qvq", StringComparison.OrdinalIgnoreCase) ||
model.Contains("vision", StringComparison.OrdinalIgnoreCase);
short modelReferenceId = isVision ? (short)1401 : (short)1400;
return new PossibleModelDto()
{
DeploymentName = model,
ReferenceId = modelReferenceId,
ReferenceName = referenceOptions[modelReferenceId].Name,
IsLegacy = referenceOptions[modelReferenceId].IsLegacy,
IsExists = existsDeploymentNames.Contains(model),
};
}));
}
else
{
Dictionary<string, ModelReference> referenceOptions = await db.ModelReferences
.Where(x => x.ProviderId == modelKey.ModelProviderId)
.ToDictionaryAsync(k => k.Name, v => v, cancellationToken);
HashSet<string> referenceOptionNames = [.. referenceOptions.Keys];

return Ok(models.Select(model =>
{
string bestMatch = FuzzyMatcher.FindBestMatch(model, referenceOptionNames);

return new PossibleModelDto()
{
DeploymentName = model,
ReferenceId = referenceOptions[bestMatch].Id,
ReferenceName = referenceOptions[bestMatch].Name,
IsLegacy = false,
IsExists = existsDeploymentNames.Contains(model),
};
}).ToArray());
}
}
else
{
PossibleModelDto[] readyRefs = await db.ModelReferences
.Where(x => x.ProviderId == modelKey.ModelProviderId)
.OrderBy(x => x.Name)
.Select(x => new PossibleModelDto()
{
DeploymentName = x.Models.FirstOrDefault(m => m.ModelKeyId == modelKeyId)!.DeploymentName,
ReferenceId = x.Id,
ReferenceName = x.Name,
IsLegacy = x.IsLegacy,
IsExists = x.Models.Any(m => m.ModelKeyId == modelKeyId),
})
.OrderBy(x => (x.IsLegacy ? 1 : 0) + (x.IsExists ? 2 : 0))
.ThenByDescending(x => x.ReferenceId)
.ToArrayAsync(cancellationToken);

return Ok(readyRefs);
}
}
}
Loading

0 comments on commit 9ce6ae5

Please sign in to comment.