Skip to content

Commit

Permalink
Merge pull request #57 from sdcb/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
sdcb authored Feb 9, 2025
2 parents cfde0b4 + e0ae932 commit 6d6349f
Show file tree
Hide file tree
Showing 56 changed files with 2,301 additions and 303 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ public async Task<ActionResult<ChatsResponseWithMessage>> GetAdminMessage(int ch
OutputPrice = x.Usage.OutputCost,
ReasoningTokens = x.Usage.ReasoningTokens,
Duration = x.Usage.TotalDurationMs - x.Usage.PreprocessDurationMs,
ReasoningDuration = x.Usage.ReasoningDurationMs,
FirstTokenLatency = x.Usage.FirstResponseDurationMs,
ModelId = x.Usage.UserModel.ModelId,
ModelName = x.Usage.UserModel.Model.Name,
Expand Down
5 changes: 3 additions & 2 deletions src/BE/Controllers/Admin/ModelKeys/ModelKeysController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,8 @@ public async Task<ActionResult<PossibleModelDto[]>> ListModelKeyPossibleModels(s
{
bool isVision = model.Contains("qvq", StringComparison.OrdinalIgnoreCase) ||
model.Contains("vision", StringComparison.OrdinalIgnoreCase);
short modelReferenceId = isVision ? (short)1401 : (short)1400;
bool isDeepSeek = model.Contains("deepseek", StringComparison.OrdinalIgnoreCase);
short modelReferenceId = isDeepSeek ? (short)1402 : isVision ? (short)1401 : (short)1400;
return new PossibleModelDto()
{
DeploymentName = model,
Expand Down Expand Up @@ -196,7 +197,7 @@ public async Task<ActionResult<PossibleModelDto[]>> ListModelKeyPossibleModels(s
DeploymentName = x.Models.FirstOrDefault(m => m.ModelKeyId == modelKeyId)!.DeploymentName,
ReferenceId = x.Id,
ReferenceName = x.Name,
IsLegacy = x.PublishDate == null || x.PublishDate < new DateOnly(2024, 7, 1),
IsLegacy = x.PublishDate != null && x.PublishDate < new DateOnly(2024, 7, 1),
IsExists = x.Models.Any(m => m.ModelKeyId == modelKeyId),
})
.OrderBy(x => (x.IsLegacy ? 1 : 0) + (x.IsExists ? 2 : 0))
Expand Down
38 changes: 30 additions & 8 deletions src/BE/Controllers/Chats/Chats/ChatController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -400,10 +400,27 @@ private static async Task<ChatSpanResponse> ProcessChatSpan(
try
{
using ChatService s = chatFactory.CreateChatService(userModel.Model);
bool responseStated = false, reasoningStarted = false;
await foreach (InternalChatSegment seg in icc.Run(userBalance.Balance, userModel, s.ChatStreamedFEProcessed(messageToSend, cco, extraDetails, cancellationToken)))
{
if (seg.TextSegment == string.Empty) continue;
await writer.WriteAsync(SseResponseLine.Segment(span.Id, seg.TextSegment), cancellationToken);
if (!string.IsNullOrEmpty(seg.ReasoningSegment))
{
if (!reasoningStarted)
{
await writer.WriteAsync(SseResponseLine.StartReasoning(span.Id), cancellationToken);
reasoningStarted = true;
}
await writer.WriteAsync(SseResponseLine.ReasoningSegment(span.Id, seg.ReasoningSegment), cancellationToken);
}
if (!string.IsNullOrEmpty(seg.Segment))
{
if (!responseStated)
{
await writer.WriteAsync(SseResponseLine.StartResponse(span.Id, icc.ReasoningDurationMs), cancellationToken);
responseStated = true;
}
await writer.WriteAsync(SseResponseLine.Segment(span.Id, seg.Segment), cancellationToken);
}

if (cancellationToken.IsCancellationRequested)
{
Expand All @@ -422,6 +439,12 @@ private static async Task<ChatSpanResponse> ProcessChatSpan(
errorText = e.Message;
logger.LogError(e, "Upstream error: {userMessageId}", req.MessageId);
}
catch (AggregateException e) when (e.InnerException is TaskCanceledException)
{
// do nothing if cancelled
icc.FinishReason = DBFinishReason.Cancelled;
errorText = e.InnerException.ToString();
}
catch (TaskCanceledException)
{
// do nothing if cancelled
Expand All @@ -446,10 +469,6 @@ private static async Task<ChatSpanResponse> ProcessChatSpan(
{
ChatId = chat.Id,
ChatRoleId = (byte)DBChatRole.Assistant,
MessageContents =
[
MessageContent.FromText(icc.FullResponse.TextSegment),
],
SpanId = span.Id,
CreatedAt = DateTime.UtcNow,
};
Expand All @@ -461,10 +480,13 @@ private static async Task<ChatSpanResponse> ProcessChatSpan(
{
dbAssistantMessage.ParentId = req.MessageId;
}
foreach (MessageContent mc in MessageContent.FromFullResponse(icc.FullResponse, errorText))
{
dbAssistantMessage.MessageContents.Add(mc);
}

if (errorText != null)
{
dbAssistantMessage.MessageContents.Add(MessageContent.FromError(errorText));
await writer.WriteAsync(SseResponseLine.Error(span.Id, errorText), cancellationToken);
}
dbAssistantMessage.Usage = icc.ToUserModelUsage(currentUser.Id, await clientInfoTask, isApi: false);
Expand All @@ -490,7 +512,7 @@ private static MessageLiteDto MakeSystemMessage(byte? specifiedSpanId, Chat chat
ChatRoleId = (byte)DBChatRole.System,
MessageContents =
[
MessageContent.FromText(span.SystemPrompt!)
MessageContent.FromContent(span.SystemPrompt!)
],
CreatedAt = DateTime.UtcNow,
SpanId = specifiedSpanId,
Expand Down
3 changes: 1 addition & 2 deletions src/BE/Controllers/Chats/Chats/Dtos/MessageLiteDto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,11 @@ public async Task<ChatMessage> ToOpenAI(FileUrlProvider fup, CancellationToken c
.SelectAwait(async c => await c.ToOpenAI(fup, cancellationToken))
.ToArrayAsync(cancellationToken)),
DBChatRole.Assistant => new AssistantChatMessage(await Content
.Where(x => x.ContentTypeId != (byte)DBMessageContentType.Error)
.Where(x => x.ContentTypeId != (byte)DBMessageContentType.Error && x.ContentTypeId != (byte)DBMessageContentType.Reasoning)
.ToAsyncEnumerable()
.SelectAwait(async x => await x.ToOpenAI(fup, cancellationToken))
.ToArrayAsync(cancellationToken)),
_ => throw new NotImplementedException()
};
}
}

3 changes: 3 additions & 0 deletions src/BE/Controllers/Chats/Chats/Dtos/SseResponseKind.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,7 @@ public enum SseResponseKind
TitleSegment = 5,
ResponseMessage = 6,
ChatLeafMessageId = 7,
ReasoningSegment = 8,
StartResponse = 9,
StartReasoning = 10,
}
30 changes: 30 additions & 0 deletions src/BE/Controllers/Chats/Chats/Dtos/SseResponseLine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,36 @@ public static SseResponseLine Segment(byte spanId, string segment)
};
}

public static SseResponseLine ReasoningSegment(byte spanId, string segment)
{
return new SseResponseLine
{
SpanId = spanId,
Result = segment,
Kind = SseResponseKind.ReasoningSegment,
};
}

public static SseResponseLine StartResponse(byte spanId, int reasoningTimeMs)
{
return new SseResponseLine
{
SpanId = spanId,
Result = reasoningTimeMs,
Kind = SseResponseKind.StartResponse,
};
}

public static SseResponseLine StartReasoning(byte spanId)
{
return new SseResponseLine
{
SpanId = spanId,
Result = null!,
Kind = SseResponseKind.StartReasoning,
};
}

public static SseResponseLine Error(byte spanId, string error)
{
return new SseResponseLine
Expand Down
12 changes: 11 additions & 1 deletion src/BE/Controllers/Chats/Messages/Dtos/MessageDto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ public record ResponseMessageDto : MessageDto
[JsonPropertyName("duration")]
public required int Duration { get; init; }

[JsonPropertyName("reasoningDuration")]
public required int ReasoningDuration { get; init; }

[JsonPropertyName("firstTokenLatency")]
public required int FirstTokenLatency { get; init; }

Expand Down Expand Up @@ -99,7 +102,7 @@ public async Task<MessageContent[]> ToMessageContents(FileUrlProvider fup, Cance
{
return
[
MessageContent.FromText(Text),
MessageContent.FromContent(Text),
..(await (FileIds ?? [])
.ToAsyncEnumerable()
.SelectAwait(async fileId => await fup.CreateFileContent(fileId, cancellationToken))
Expand All @@ -113,6 +116,9 @@ public record MessageContentResponse
[JsonPropertyName("text")]
public required string Text { get; init; }

[JsonPropertyName("think"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public required string? Think { get; init; }

[JsonPropertyName("fileIds"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public required FileDto[]? FileIds { get; init; }

Expand All @@ -132,6 +138,7 @@ public static MessageContentResponse FromSegments(MessageContent[] segments, Fil
return new MessageContentResponse()
{
Text = string.Join("\n", groups[DBMessageContentType.Text].Select(x => x.ToString())),
Think = string.Join("\n", groups[DBMessageContentType.Reasoning].Select(x => x.ToString())) switch { "" => null, var x => x },
FileIds = groups[DBMessageContentType.FileId]
.Select(x => fup.CreateFileDto(x.MessageContentFile!.File))
.ToArray() switch
Expand All @@ -156,6 +163,7 @@ public record FileDto
public record ChatMessageTempUsage
{
public required int Duration { get; init; }
public required int ReasoningDuration { get; init; }
public required int FirstTokenLatency { get; init; }
public required decimal InputPrice { get; init; }
public required int InputTokens { get; init; }
Expand Down Expand Up @@ -213,6 +221,7 @@ public MessageDto ToDto(IUrlEncryptionService urlEncryption, FileUrlProvider fup
OutputPrice = Usage.OutputPrice,
ReasoningTokens = Usage.ReasoningTokens,
Duration = Usage.Duration,
ReasoningDuration = Usage.ReasoningDuration,
FirstTokenLatency = Usage.FirstTokenLatency,
ModelId = Usage.ModelId,
ModelName = Usage.ModelName,
Expand Down Expand Up @@ -240,6 +249,7 @@ public static ChatMessageTemp FromDB(Message assistantMessage)
Usage = assistantMessage.Usage == null ? null : new ChatMessageTempUsage()
{
Duration = assistantMessage.Usage.TotalDurationMs - assistantMessage.Usage.PreprocessDurationMs,
ReasoningDuration = assistantMessage.Usage.ReasoningDurationMs,
FirstTokenLatency = assistantMessage.Usage.FirstResponseDurationMs,
InputPrice = assistantMessage.Usage.InputCost,
InputTokens = assistantMessage.Usage.InputTokens,
Expand Down
1 change: 1 addition & 0 deletions src/BE/Controllers/Chats/Messages/MessagesController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public async Task<ActionResult<MessageDto[]>> GetMessages(string chatId, [FromSe
OutputPrice = x.Usage.OutputCost,
ReasoningTokens = x.Usage.ReasoningTokens,
Duration = x.Usage.TotalDurationMs - x.Usage.PreprocessDurationMs,
ReasoningDuration = x.Usage.ReasoningDurationMs,
FirstTokenLatency = x.Usage.FirstResponseDurationMs,
ModelId = x.Usage.UserModel.ModelId,
ModelName = x.Usage.UserModel.Model.Name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ namespace Chats.BE.Controllers.OpenAICompatible.Dtos;
public record Delta
{
[JsonPropertyName("content")]
public required string Content { get; init; }
public required string? Content { get; init; }

[JsonPropertyName("reasoning_content"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public required string? ReasoningContent { get; init; }
}

public record DeltaChoice
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@ public record ResponseMessage
public required string Role { get; init; }

[JsonPropertyName("content")]
public required string Content { get; init; }
public required string? Content { get; init; }

[JsonPropertyName("reasoning_content"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public required string? ReasoningContent { get; init; }

[JsonPropertyName("refusal")]
public object? Refusal { get; init; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public async Task<ActionResult> ChatCompletion([FromBody] JsonObject json, [From
{
await foreach (InternalChatSegment seg in icc.Run(userBalance.Balance, userModel, s.ChatStreamedSimulated(cco.Stream, [.. cco.Messages], cco.ToCleanCco(), cancellationToken)))
{
if (seg.TextSegment == string.Empty) continue;
if (seg.Segment == string.Empty) continue;

if (cco.Stream)
{
Expand Down
6 changes: 6 additions & 0 deletions src/BE/DB/ChatsDB.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ public ChatsDB(DbContextOptions<ChatsDB> options)

public virtual DbSet<Prompt> Prompts { get; set; }

public virtual DbSet<ReasoningResponseKind> ReasoningResponseKinds { get; set; }

public virtual DbSet<SmsAttempt> SmsAttempts { get; set; }

public virtual DbSet<SmsRecord> SmsRecords { get; set; }
Expand Down Expand Up @@ -330,6 +332,10 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_ModelSetting_ModelProvider");

entity.HasOne(d => d.ReasoningResponseKind).WithMany(p => p.ModelReferences)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_ModelReference_ReasoningResponseKind");

entity.HasOne(d => d.Tokenizer).WithMany(p => p.ModelReferences).HasConstraintName("FK_ModelReference_Tokenizer");
});

Expand Down
10 changes: 7 additions & 3 deletions src/BE/DB/Enums/DBMessageContentType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,21 @@
public enum DBMessageContentType : byte
{
/// <summary>
/// Error content type encoded in UTF-8.
/// Error content type, stored in MessageContentText table
/// </summary>
Error = 0,

/// <summary>
/// Text content type encoded in Unicode.
/// Text content type, stored in MessageContentText table
/// </summary>
Text = 1,

/// <summary>
/// 32bit little-endian integer file ID content type.
/// File ID content type, stored in MessageContentFile table
/// </summary>
FileId = 2,

/// <summary>
/// Reasoning content type, stored in MessageContentText table
Reasoning = 3,
}
1 change: 1 addition & 0 deletions src/BE/DB/Enums/DBModelProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ public enum DBModelProvider
Ollama = 14,
MiniMax = 15,
Doubao = 16,
SiliconFlow = 17,
}
8 changes: 8 additions & 0 deletions src/BE/DB/Enums/DBReasoningResponseKind.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Chats.BE.DB.Enums;

public enum DBReasoningResponseKind : byte
{
NoReasoningOutput = 0,
ReasoningContent = 1,
ThinkTag = 2,
}
25 changes: 24 additions & 1 deletion src/BE/DB/Extensions/MessageContent.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Chats.BE.DB.Enums;
using Chats.BE.Services.FileServices;
using Chats.BE.Services.Models.Dtos;
using OpenAI.Chat;

namespace Chats.BE.DB;
Expand All @@ -22,16 +23,22 @@ public override string ToString()
{
DBMessageContentType.Text => MessageContentText!.Content,
DBMessageContentType.Error => MessageContentText!.Content,
DBMessageContentType.Reasoning => MessageContentText!.Content,
//DBMessageContentType.FileId => MessageContentUtil.ReadFileId(Content).ToString(), // not supported
_ => throw new NotSupportedException(),
};
}

public static MessageContent FromText(string text)
public static MessageContent FromContent(string text)
{
return new MessageContent { MessageContentText = new() { Content = text }, ContentTypeId = (byte)DBMessageContentType.Text };
}

public static MessageContent FromReasoningContent(string text)
{
return new MessageContent { MessageContentText = new() { Content = text }, ContentTypeId = (byte)DBMessageContentType.Reasoning };
}

public static MessageContent FromFile(int fileId, File file)
{
return new MessageContent { MessageContentFile = new() { FileId = fileId, File = file }, ContentTypeId = (byte)DBMessageContentType.FileId };
Expand All @@ -41,4 +48,20 @@ public static MessageContent FromError(string error)
{
return new MessageContent { MessageContentText = new() { Content = error }, ContentTypeId = (byte)DBMessageContentType.Error };
}

public static IEnumerable<MessageContent> FromFullResponse(InternalChatSegment lastSegment, string? errorText)
{
if (errorText is not null)
{
yield return FromError(errorText);
}
if (lastSegment.ReasoningSegment is not null)
{
yield return FromReasoningContent(lastSegment.ReasoningSegment);
}
if (lastSegment.Segment is not null)
{
yield return FromContent(lastSegment.Segment);
}
}
}
Loading

0 comments on commit 6d6349f

Please sign in to comment.