From 6b606f2117f6f1039894ad6cdcddb2b328f7eaaf Mon Sep 17 00:00:00 2001 From: sdcb Date: Fri, 6 Dec 2024 12:46:59 +0800 Subject: [PATCH 1/2] supports aliyun oss --- src/BE/Chats.BE.csproj | 1 + src/BE/Services/Common/MaskedKeyUtils.cs | 12 ++++++++ .../Conversations/ConversationService.cs | 2 +- .../FileServices/FileUploadRequest.cs | 22 ++++++++++++++- src/BE/Services/FileServices/IFileService.cs | 28 +++++++++++++++++++ .../AliyunOSS/AliyunOSSFileService.cs | 17 +++++++---- .../Implementations/AwsS3/AwsS3FileService.cs | 2 +- .../Admin/Files/FileServiceModal.tsx | 5 ++-- 8 files changed, 79 insertions(+), 10 deletions(-) diff --git a/src/BE/Chats.BE.csproj b/src/BE/Chats.BE.csproj index 0bef3253..ea27d80b 100644 --- a/src/BE/Chats.BE.csproj +++ b/src/BE/Chats.BE.csproj @@ -8,6 +8,7 @@ + diff --git a/src/BE/Services/Common/MaskedKeyUtils.cs b/src/BE/Services/Common/MaskedKeyUtils.cs index eab00e4c..fcb4e883 100644 --- a/src/BE/Services/Common/MaskedKeyUtils.cs +++ b/src/BE/Services/Common/MaskedKeyUtils.cs @@ -30,6 +30,11 @@ public static string ToMasked(this string key) return null; } + if (!IsProbablyValidJson(jsonKey)) + { + return ToMaskedNull(jsonKey); + } + try { using JsonDocument document = JsonDocument.Parse(jsonKey); @@ -75,6 +80,13 @@ public static string ToMasked(this string key) } } + private static bool IsProbablyValidJson(string json) + { + json = json.Trim(); + return (json.StartsWith('{') && json.EndsWith('}')) || + (json.StartsWith('[') && json.EndsWith(']')); + } + public static bool SeemsMasked(this string? key) { return key is not null && key.Contains("****"); diff --git a/src/BE/Services/Conversations/ConversationService.cs b/src/BE/Services/Conversations/ConversationService.cs index 78d8d3ee..a7c98aa3 100644 --- a/src/BE/Services/Conversations/ConversationService.cs +++ b/src/BE/Services/Conversations/ConversationService.cs @@ -10,7 +10,7 @@ namespace Chats.BE.Services.Conversations; public abstract partial class ConversationService : IDisposable { - public const float DefaultTemperature = 0.8f; + public const float DefaultTemperature = 0.5f; public const string DefaultPrompt = "你是{{MODEL_NAME}},请仔细遵循用户指令并认真回复,当前日期: {{CURRENT_DATE}}"; internal protected Model Model { get; } diff --git a/src/BE/Services/FileServices/FileUploadRequest.cs b/src/BE/Services/FileServices/FileUploadRequest.cs index b64527d1..20e23a5b 100644 --- a/src/BE/Services/FileServices/FileUploadRequest.cs +++ b/src/BE/Services/FileServices/FileUploadRequest.cs @@ -2,10 +2,30 @@ namespace Chats.BE.Services.FileServices; +/// +/// Represents a request to upload a file. +/// +/// +/// The property will not be disposed by the file service. +/// public record FileUploadRequest { + /// + /// Gets the name of the file. + /// public required string FileName { get; init; } + + /// + /// Gets the content type of the file. + /// public required string ContentType { get; init; } + + /// + /// Gets the stream containing the file data. + /// + /// + /// The stream will not be disposed by the file service. + /// public required Stream Stream { get; init; } } @@ -13,8 +33,8 @@ public record CreateDownloadUrlRequest { public required int FileId { get; init; } public required string StorageKey { get; init; } - public TimeSpan ValidPeriod { get; init; } = TimeSpan.FromHours(2); + public DateTime ValidEnd => DateTime.UtcNow + ValidPeriod; public static CreateDownloadUrlRequest FromFile(DB.File file) { diff --git a/src/BE/Services/FileServices/IFileService.cs b/src/BE/Services/FileServices/IFileService.cs index 6780d277..376245eb 100644 --- a/src/BE/Services/FileServices/IFileService.cs +++ b/src/BE/Services/FileServices/IFileService.cs @@ -1,8 +1,36 @@ namespace Chats.BE.Services.FileServices; +/// +/// Interface for file service operations such as upload, download, and creating download URLs. +/// public interface IFileService { + /// + /// Uploads a file to the storage. The caller must dispose the stream. + /// + /// The file upload request containing file details. + /// Token to monitor for cancellation requests. + /// A task that represents the asynchronous operation. The task result contains the storage key of the uploaded file. + /// + /// The inside will not be disposed by the file service. + /// Task Upload(FileUploadRequest request, CancellationToken cancellationToken); + + /// + /// Downloads a file from the storage. + /// + /// The storage key of the file to download. + /// Token to monitor for cancellation requests. + /// + /// A task that represents the asynchronous operation. The task result contains the file stream. + /// The caller must dispose the stream. + /// Task Download(string storageKey, CancellationToken cancellationToken); + + /// + /// Creates a download URL for a file. + /// + /// The request containing details for creating the download URL. + /// The URI of the created download URL. Uri CreateDownloadUrl(CreateDownloadUrlRequest request); } diff --git a/src/BE/Services/FileServices/Implementations/AliyunOSS/AliyunOSSFileService.cs b/src/BE/Services/FileServices/Implementations/AliyunOSS/AliyunOSSFileService.cs index be6bbd0c..81e090cd 100644 --- a/src/BE/Services/FileServices/Implementations/AliyunOSS/AliyunOSSFileService.cs +++ b/src/BE/Services/FileServices/Implementations/AliyunOSS/AliyunOSSFileService.cs @@ -1,21 +1,28 @@ - +using Aliyun.OSS; namespace Chats.BE.Services.FileServices.Implementations.AliyunOSS; -public class AliyunOSSFileService(AliyunOssConfig aliyunOssConfig) : IFileService +public class AliyunOSSFileService(AliyunOssConfig config) : IFileService { + private readonly OssClient _oss = new(config.Endpoint, config.AccessKeyId, config.AccessKeySecret); + public Uri CreateDownloadUrl(CreateDownloadUrlRequest req) { - throw new NotImplementedException(); + return _oss.GeneratePresignedUri(config.Bucket, req.StorageKey, req.ValidEnd, SignHttpMethod.Get); } public Task Download(string storageKey, CancellationToken cancellationToken) { - throw new NotImplementedException(); + cancellationToken.ThrowIfCancellationRequested(); + OssObject obj = _oss.GetObject(config.Bucket, storageKey); + return Task.FromResult(obj.ResponseStream); } public Task Upload(FileUploadRequest request, CancellationToken cancellationToken) { - throw new NotImplementedException(); + cancellationToken.ThrowIfCancellationRequested(); + SuggestedStorageInfo ssi = SuggestedStorageInfo.FromFileName(request.FileName); + _ = _oss.PutObject(config.Bucket, ssi.StorageKey, request.Stream); + return Task.FromResult(ssi.StorageKey); } } diff --git a/src/BE/Services/FileServices/Implementations/AwsS3/AwsS3FileService.cs b/src/BE/Services/FileServices/Implementations/AwsS3/AwsS3FileService.cs index 6eab92e4..ab700c18 100644 --- a/src/BE/Services/FileServices/Implementations/AwsS3/AwsS3FileService.cs +++ b/src/BE/Services/FileServices/Implementations/AwsS3/AwsS3FileService.cs @@ -38,7 +38,7 @@ public Uri CreateDownloadUrl(CreateDownloadUrlRequest req) { BucketName = _config.Bucket, Key = req.StorageKey, - Expires = DateTime.UtcNow + req.ValidPeriod, + Expires = req.ValidEnd, Verb = HttpVerb.GET }); return new Uri(url); diff --git a/src/FE/components/Admin/Files/FileServiceModal.tsx b/src/FE/components/Admin/Files/FileServiceModal.tsx index 83dfff87..eac92fdc 100644 --- a/src/FE/components/Admin/Files/FileServiceModal.tsx +++ b/src/FE/components/Admin/Files/FileServiceModal.tsx @@ -152,8 +152,8 @@ export const FileServiceModal = (props: IProps) => { const formatConfigs = (config: string) => { try { - JSON.parse(config); - return config; + const parsed = JSON.parse(config); + return JSON.stringify(parsed, null, 2); } catch { return config; } @@ -182,6 +182,7 @@ export const FileServiceModal = (props: IProps) => { getFileServiceTypeInitialConfig(fileServiceTypeId).then((res) => { form.setValue('configs', formatConfigs(res)); }); + form.setValue('name', t(feFileServiceTypes[fileServiceTypeId].name)); } }); return () => subscription.unsubscribe(); From d36d5e5050f452ce6da62ed9fb47fd501e2f2ce7 Mon Sep 17 00:00:00 2001 From: sdcb Date: Fri, 6 Dec 2024 12:52:01 +0800 Subject: [PATCH 2/2] fix temperature --- .../Chats/Conversations/ConversationController.cs | 10 +++++----- src/BE/DB/Extensions/ModelReference.cs | 10 ++++++++++ .../Conversations/ConversationServiceExtensions.cs | 1 + 3 files changed, 16 insertions(+), 5 deletions(-) create mode 100644 src/BE/DB/Extensions/ModelReference.cs diff --git a/src/BE/Controllers/Chats/Conversations/ConversationController.cs b/src/BE/Controllers/Chats/Conversations/ConversationController.cs index 8828e029..3c38ec5a 100644 --- a/src/BE/Controllers/Chats/Conversations/ConversationController.cs +++ b/src/BE/Controllers/Chats/Conversations/ConversationController.cs @@ -94,6 +94,8 @@ public async Task StartConversationStreamed( thisChat.Title = request.UserMessage.Text[..Math.Min(50, request.UserMessage.Text.Length)]; thisChat.ModelId = request.ModelId; + thisChat.EnableSearch = request.UserModelConfig.EnableSearch; + thisChat.Temperature = request.UserModelConfig.Temperature; } else { @@ -101,8 +103,8 @@ public async Task StartConversationStreamed( { UserModelConfig = new JsonUserModelConfig { - EnableSearch = thisChat.EnableSearch, - Temperature = thisChat.Temperature, + EnableSearch = request.UserModelConfig.EnableSearch ?? thisChat.EnableSearch, + Temperature = request.UserModelConfig.Temperature ?? thisChat.Temperature, } }; } @@ -156,9 +158,7 @@ ..await GetMessageTree(existingMessages, messageId).ToAsyncEnumerable().SelectAw using ConversationService s = conversationFactory.CreateConversationService(userModel.Model); ChatCompletionOptions cco = new() { - Temperature = request.UserModelConfig.Temperature != null - ? Math.Clamp(request.UserModelConfig.Temperature.Value, (float)userModel.Model.ModelReference.MinTemperature, (float)userModel.Model.ModelReference.MaxTemperature) - : null, + Temperature = request.UserModelConfig.Temperature, EndUserId = currentUser.Id.ToString(), }; await foreach (InternalChatSegment seg in icc.Run(userBalance.Balance, userModel, s.ChatStreamedFEProcessed(messageToSend, cco, cancellationToken))) diff --git a/src/BE/DB/Extensions/ModelReference.cs b/src/BE/DB/Extensions/ModelReference.cs new file mode 100644 index 00000000..94b12baf --- /dev/null +++ b/src/BE/DB/Extensions/ModelReference.cs @@ -0,0 +1,10 @@ +namespace Chats.BE.DB; + +public partial class ModelReference +{ + public float? UnnormalizeTemperature(float? temperature) + { + if (temperature == null) return null; + return temperature * (float)(MaxTemperature - MinTemperature) + (float)MinTemperature; + } +} diff --git a/src/BE/Services/Conversations/ConversationServiceExtensions.cs b/src/BE/Services/Conversations/ConversationServiceExtensions.cs index d4fe34b1..93174308 100644 --- a/src/BE/Services/Conversations/ConversationServiceExtensions.cs +++ b/src/BE/Services/Conversations/ConversationServiceExtensions.cs @@ -74,6 +74,7 @@ private ChatMessage[] FEProcessMessages(IReadOnlyList messages, Cha { options.RemoveAllowSearch(); } + options.Temperature = Model.ModelReference.UnnormalizeTemperature(options.Temperature); return filteredMessage; }