Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 6 additions & 7 deletions ProjectVG.Api/Controllers/ChatController.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using ProjectVG.Application.Models.Chat;
using ProjectVG.Application.Models.API.Request;
using ProjectVG.Application.Services.Chat;
Expand Down Expand Up @@ -28,12 +27,12 @@ public async Task<IActionResult> ProcessChat([FromBody] ChatRequest request)
throw new ValidationException(ErrorCode.AUTHENTICATION_FAILED);
}

var command = new ProcessChatCommand
{
UserId = userGuid,
Message = request.Message,
CharacterId = request.CharacterId
};
var command = new ChatRequestCommand(
userGuid,
request.CharacterId,
request.Message,
request.RequestAt,
request.UseTTS);
Comment on lines +30 to +35
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

클라이언트 제공 시각의 타임존 정규화 및 메시지 정제

request.RequestAt을 그대로 전달하면 타임존 혼선이 생깁니다. UTC 정규화 후 전달을 권장합니다. 메시지도 트리밍하여 노이즈를 줄이세요.

-            var command = new ChatRequestCommand(
-                userGuid, 
-                request.CharacterId, 
-                request.Message,
-                request.RequestAt,
-                request.UseTTS);
+            var requestedAtUtc = request.RequestAt.Kind == DateTimeKind.Utc
+                ? request.RequestAt
+                : DateTime.SpecifyKind(request.RequestAt, DateTimeKind.Utc);
+            var message = request.Message?.Trim() ?? string.Empty;
+            var command = new ChatRequestCommand(
+                userGuid,
+                request.CharacterId,
+                message,
+                requestedAtUtc,
+                request.UseTTS);

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In ProjectVG.Api/Controllers/ChatController.cs around lines 30 to 35, the code
passes request.RequestAt and request.Message directly into ChatRequestCommand
which can cause timezone confusion and noisy messages; convert request.RequestAt
to UTC (e.g., call ToUniversalTime() or ensure DateTimeKind.Utc) before passing
it, and trim request.Message (e.g., call .Trim() and handle null safely) so the
command receives a normalized UTC timestamp and a trimmed message.


var result = await _chatService.EnqueueChatRequestAsync(command);

Expand Down
24 changes: 12 additions & 12 deletions ProjectVG.Api/Middleware/GlobalExceptionHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ private ErrorResponse CreateErrorResponse(Exception exception, HttpContext conte

private ErrorResponse HandleValidationException(ValidationException exception, HttpContext context)
{
_logger.LogWarning(exception, "유효성 검사 예외 발생: {ErrorCode} - {Message}", exception.ErrorCode.ToString(), exception.Message);
_logger.LogWarning(exception, "유효성 검사 예외 발생: {ErrorCode} - {UserPrompt}", exception.ErrorCode.ToString(), exception.Message);

var details = exception.ValidationErrors?.Select(e => e.ErrorMessage ?? string.Empty).ToList();

Expand All @@ -117,7 +117,7 @@ private ErrorResponse HandleValidationException(ValidationException exception, H

private ErrorResponse HandleNotFoundException(NotFoundException exception, HttpContext context)
{
_logger.LogWarning(exception, "리소스를 찾을 수 없음: {ErrorCode} - {Message}", exception.ErrorCode.ToString(), exception.Message);
_logger.LogWarning(exception, "리소스를 찾을 수 없음: {ErrorCode} - {UserPrompt}", exception.ErrorCode.ToString(), exception.Message);

return new ErrorResponse {
ErrorCode = exception.ErrorCode.ToString(),
Expand All @@ -130,7 +130,7 @@ private ErrorResponse HandleNotFoundException(NotFoundException exception, HttpC

private ErrorResponse HandleAuthenticationException(AuthenticationException exception, HttpContext context)
{
_logger.LogWarning(exception, "인증 실패: {ErrorCode} - {Message}", exception.ErrorCode.ToString(), exception.Message);
_logger.LogWarning(exception, "인증 실패: {ErrorCode} - {UserPrompt}", exception.ErrorCode.ToString(), exception.Message);

return new ErrorResponse {
ErrorCode = exception.ErrorCode.ToString(),
Expand All @@ -143,7 +143,7 @@ private ErrorResponse HandleAuthenticationException(AuthenticationException exce

private ErrorResponse HandleProjectVGException(ProjectVGException exception, HttpContext context)
{
_logger.LogWarning(exception, "ProjectVG 예외 발생: {ErrorCode} - {Message}", exception.ErrorCode.ToString(), exception.Message);
_logger.LogWarning(exception, "ProjectVG 예외 발생: {ErrorCode} - {UserPrompt}", exception.ErrorCode.ToString(), exception.Message);

return new ErrorResponse {
ErrorCode = exception.ErrorCode.ToString(),
Expand All @@ -156,7 +156,7 @@ private ErrorResponse HandleProjectVGException(ProjectVGException exception, Htt

private ErrorResponse HandleExternalServiceException(ExternalServiceException exception, HttpContext context)
{
_logger.LogError(exception, "외부 서비스 오류 발생: {ServiceName} - {Endpoint} - {Message}",
_logger.LogError(exception, "외부 서비스 오류 발생: {ServiceName} - {Endpoint} - {UserPrompt}",
exception.ServiceName, exception.Endpoint, exception.Message);

return new ErrorResponse {
Expand Down Expand Up @@ -206,7 +206,7 @@ private ErrorResponse HandleDbUpdateException(DbUpdateException exception, HttpC

private ErrorResponse HandleKeyNotFoundException(KeyNotFoundException exception, HttpContext context)
{
_logger.LogWarning(exception, "리소스를 찾을 수 없음: {Message}", exception.Message);
_logger.LogWarning(exception, "리소스를 찾을 수 없음: {UserPrompt}", exception.Message);

return new ErrorResponse {
ErrorCode = "RESOURCE_NOT_FOUND",
Expand All @@ -219,7 +219,7 @@ private ErrorResponse HandleKeyNotFoundException(KeyNotFoundException exception,

private ErrorResponse HandleArgumentException(ArgumentException exception, HttpContext context)
{
_logger.LogWarning(exception, "잘못된 인수: {Message}", exception.Message);
_logger.LogWarning(exception, "잘못된 인수: {UserPrompt}", exception.Message);

return new ErrorResponse {
ErrorCode = "INVALID_ARGUMENT",
Expand All @@ -233,7 +233,7 @@ private ErrorResponse HandleArgumentException(ArgumentException exception, HttpC

private ErrorResponse HandleInvalidOperationException(InvalidOperationException exception, HttpContext context)
{
_logger.LogWarning(exception, "잘못된 작업: {Message}", exception.Message);
_logger.LogWarning(exception, "잘못된 작업: {UserPrompt}", exception.Message);

return new ErrorResponse {
ErrorCode = "INVALID_OPERATION",
Expand All @@ -247,7 +247,7 @@ private ErrorResponse HandleInvalidOperationException(InvalidOperationException

private ErrorResponse HandleUnauthorizedAccessException(UnauthorizedAccessException exception, HttpContext context)
{
_logger.LogWarning(exception, "권한 없음: {Message}", exception.Message);
_logger.LogWarning(exception, "권한 없음: {UserPrompt}", exception.Message);

return new ErrorResponse {
ErrorCode = "UNAUTHORIZED",
Expand All @@ -260,7 +260,7 @@ private ErrorResponse HandleUnauthorizedAccessException(UnauthorizedAccessExcept

private ErrorResponse HandleTimeoutException(TimeoutException exception, HttpContext context)
{
_logger.LogWarning(exception, "타임아웃 발생: {Message}", exception.Message);
_logger.LogWarning(exception, "타임아웃 발생: {UserPrompt}", exception.Message);

return new ErrorResponse {
ErrorCode = "TIMEOUT",
Expand All @@ -273,7 +273,7 @@ private ErrorResponse HandleTimeoutException(TimeoutException exception, HttpCon

private ErrorResponse HandleHttpRequestException(HttpRequestException exception, HttpContext context)
{
_logger.LogError(exception, "HTTP 요청 오류: {Message}", exception.Message);
_logger.LogError(exception, "HTTP 요청 오류: {UserPrompt}", exception.Message);

return new ErrorResponse {
ErrorCode = "HTTP_REQUEST_ERROR",
Expand All @@ -289,7 +289,7 @@ private ErrorResponse HandleGenericException(Exception exception, HttpContext co
var exceptionType = exception.GetType().Name;
var isDevelopment = _environment.IsDevelopment();

_logger.LogError(exception, "예상치 못한 예외 발생: {ExceptionType} - {Message}", exceptionType, exception.Message);
_logger.LogError(exception, "예상치 못한 예외 발생: {ExceptionType} - {UserPrompt}", exceptionType, exception.Message);

return new ErrorResponse {
ErrorCode = "INTERNAL_SERVER_ERROR",
Expand Down
6 changes: 3 additions & 3 deletions ProjectVG.Api/Models/Chat/Request/ChatRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@ namespace ProjectVG.Application.Models.API.Request
{
public class ChatRequest
{
[JsonPropertyName("session_id")]
public string SessionId { get; set; } = string.Empty;

[JsonPropertyName("message")]
public string Message { get; set; } = string.Empty;

Expand All @@ -19,5 +16,8 @@ public class ChatRequest

[JsonPropertyName("use_tts")]
public bool UseTTS { get; set; } = true;

[JsonPropertyName("request_at")]
public DateTime RequestAt { get; set; }
Comment on lines +20 to +21
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

요청 시간 타입을 DateTimeOffset으로 변경 권장(타임존/서머타임 안전)

DateTime은 Kind/타임존 불명 이슈로 순서 판단·보관 시 오류가 납니다. DateTimeOffset 사용과 서버 UTC 기본값을 권장합니다.

-        [JsonPropertyName("request_at")]
-        public DateTime RequestAt { get; set; }
+        [JsonPropertyName("request_at")]
+        public DateTimeOffset RequestAt { get; set; } = DateTimeOffset.UtcNow;

클라이언트에는 ISO 8601(예: 2025-08-28T16:20:00+09:00)로 전송하도록 문서화해 주세요.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
[JsonPropertyName("request_at")]
public DateTime RequestAt { get; set; }
[JsonPropertyName("request_at")]
public DateTimeOffset RequestAt { get; set; } = DateTimeOffset.UtcNow;
🤖 Prompt for AI Agents
In ProjectVG.Api/Models/Chat/Request/ChatRequest.cs around lines 20-21, the
RequestAt property uses System.DateTime which can lead to timezone/Kind
ambiguity; change its type to System.DateTimeOffset, initialize or validate it
to use UTC offsets (e.g., default to DateTimeOffset.UtcNow where appropriate),
update any serializers/converters if needed to preserve offsets, and run/adjust
unit tests and model bindings; also document that clients must send the
timestamp in ISO 8601 with offset (e.g., 2025-08-28T16:20:00+09:00).

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public static IServiceCollection AddApplicationServices(this IServiceCollection
{
// Auth Services
services.AddScoped<IAuthService, AuthService>();
services.AddSingleton<IOAuth2Service, OAuth2Service>();
services.AddScoped<IOAuth2Service, OAuth2Service>();
services.AddScoped<IOAuth2ProviderFactory, OAuth2ProviderFactory>();

// User Services
Expand Down Expand Up @@ -48,8 +48,9 @@ public static IServiceCollection AddApplicationServices(this IServiceCollection
services.AddScoped<ChatLLMProcessor>();
services.AddScoped<ChatTTSProcessor>();
services.AddScoped<ChatResultProcessor>();

// Chat Services - Handlers
services.AddScoped<ChatSuccessHandler>();
services.AddScoped<ChatFailureHandler>();

// Chat Services - Cost Tracking Decorators
Expand Down
7 changes: 4 additions & 3 deletions ProjectVG.Application/Models/Character/CharacterDto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@ public class CharacterDto
public bool IsActive { get; set; } = true;
public string Personality { get; set; } = string.Empty;
public string SpeechStyle { get; set; } = string.Empty;
public string Summary { get; set; } = string.Empty;
public string UserAlias { get; set; } = string.Empty;
public string VoiceId { get; set; } = string.Empty;
Comment on lines +14 to 16
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

DTO에 ImageUrl 누락 — 도메인과 매핑 정합성 확보 필요

도메인에 ImageUrl이 추가되었는데 DTO에 노출되지 않습니다. API/클라이언트에서 이미지가 필요하다면 DTO에 포함하세요.

         public string SpeechStyle { get; set; } = string.Empty;
         public string Summary { get; set; } = string.Empty;
         public string UserAlias { get; set; } = string.Empty;
+        public string ImageUrl { get; set; } = string.Empty;
         public string VoiceId { get; set; } = string.Empty;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public string Summary { get; set; } = string.Empty;
public string UserAlias { get; set; } = string.Empty;
public string VoiceId { get; set; } = string.Empty;
public string SpeechStyle { get; set; } = string.Empty;
public string Summary { get; set; } = string.Empty;
public string UserAlias { get; set; } = string.Empty;
public string ImageUrl { get; set; } = string.Empty;
public string VoiceId { get; set; } = string.Empty;
🤖 Prompt for AI Agents
In ProjectVG.Application/Models/Character/CharacterDto.cs around lines 14 to 16,
the DTO is missing the ImageUrl property added to the domain model; add a public
string ImageUrl { get; set; } = string.Empty; to the DTO so API responses
include the character image and mapping between domain and DTO remains
consistent (also update any mapping/profile code to map the domain ImageUrl to
this new DTO property).


public CharacterDto()
{
}

public CharacterDto(Domain.Entities.Characters.Character character)
{
Expand All @@ -26,6 +25,8 @@ public CharacterDto(Domain.Entities.Characters.Character character)
IsActive = character.IsActive;
Personality = character.Personality;
SpeechStyle = character.SpeechStyle;
UserAlias = character.UserAlias;
Summary = character.Summary;
VoiceId = character.VoiceId;
}
}
Expand Down
35 changes: 0 additions & 35 deletions ProjectVG.Application/Models/Chat/ChatMessageSegment.cs

This file was deleted.

94 changes: 78 additions & 16 deletions ProjectVG.Application/Models/Chat/ChatProcessContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public class ChatProcessContext
public Guid CharacterId { get; private set; }
public string UserMessage { get; private set; } = string.Empty;
public string MemoryStore { get; private set; } = string.Empty;
public DateTime UserRequestAt { get; private set; } = DateTime.Now;
public bool UseTTS { get; private set; } = true;

public CharacterDto? Character { get; private set; }
Expand All @@ -18,33 +19,27 @@ public class ChatProcessContext

public string Response { get; private set; } = string.Empty;
public double Cost { get; private set; }
public List<ChatMessageSegment> Segments { get; private set; } = new List<ChatMessageSegment>();

public string FullText => string.Join(" ", Segments.Where(s => s.HasText).Select(s => s.Text));
public bool HasAudio => Segments.Any(s => s.HasAudio);
public bool HasText => Segments.Any(s => s.HasText);

public List<ChatSegment> Segments { get; private set; } = new List<ChatSegment>();

public ChatProcessContext(ProcessChatCommand command)
public ChatProcessContext(ChatRequestCommand command)
{
SessionId = command.SessionId;
UserId = command.UserId;
CharacterId = command.CharacterId;
UserMessage = command.Message;
UserMessage = command.UserPrompt;
MemoryStore = command.UserId.ToString();
UseTTS = command.UseTTS;
UserRequestAt = command.UserRequestAt;
}
Comment on lines +24 to 32
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

SessionId 미설정: 컨텍스트에 세션 식별자 주입 경로 추가 필요

현재 컨텍스트에 SessionId가 설정되지 않습니다(후속 WebSocket 메시지에 포함됨). 공개 세터 없이 내부 메서드로 설정할 수 있도록 진입점을 추가해 주세요. ChatService에서 호출하도록 이어서 코멘트 드립니다.

         public void AddCost(double additionalCost)
         {
             Cost += additionalCost;
         }
+
+        // 세션 식별자 설정용 진입점 (서비스/핸들러에서 필요)
+        public void SetSessionId(string sessionId)
+        {
+            SessionId = sessionId;
+        }

Also applies to: 34-49

🤖 Prompt for AI Agents
In ProjectVG.Application/Models/Chat/ChatProcessContext.cs around lines 24-32
(and similarly for the constructor logic at 34-49), the SessionId is not being
set on the context; add an internal injector so ChatService can assign the
session identifier without exposing a public setter. Modify the SessionId
property to keep its setter non-public (private or protected) and add a single
internal method like SetSessionId(string sessionId) that assigns the property
(or backing field); update the constructors to leave SessionId unset and ensure
ChatService calls the new internal method to inject the session value.


public ChatProcessContext(
ProcessChatCommand command,
ChatRequestCommand command,
CharacterDto character,
IEnumerable<ConversationHistory> conversationHistory,
IEnumerable<string> memoryContext)
{
SessionId = command.SessionId;
UserId = command.UserId;
CharacterId = command.CharacterId;
UserMessage = command.Message;
UserMessage = command.UserPrompt;
MemoryStore = command.UserId.ToString();
UseTTS = command.UseTTS;

Expand All @@ -53,7 +48,7 @@ public ChatProcessContext(
MemoryContext = memoryContext;
}

public void SetResponse(string response, List<ChatMessageSegment> segments, double cost)
public void SetResponse(string response, List<ChatSegment> segments, double cost)
{
Response = response;
Segments = segments;
Expand All @@ -69,9 +64,76 @@ public IEnumerable<string> ParseConversationHistory(int count = 5)
{
if (ConversationHistory == null) return Enumerable.Empty<string>();

return ConversationHistory
.Take(count)
.Select(h => $"{h.Role}: {h.Content}");
return ConversationHistory.Take(count).Select(h => $"{h.Role}: {h.Content}");
}

public string ToDebugString()
{
var sb = new System.Text.StringBuilder();

// Request 기본 정보
sb.AppendLine($"[ChatProcessContext Debug Info]");
sb.AppendLine($"=== REQUEST INFO ===");
sb.AppendLine($"SessionId: {SessionId}");
sb.AppendLine($"UserId: {UserId}");
sb.AppendLine($"CharacterId: {CharacterId}");
sb.AppendLine($"UserMessage: \"{UserMessage}\"");
sb.AppendLine($"UseTTS: {UseTTS}");
sb.AppendLine($"UserRequestAt: {UserRequestAt:yyyy-MM-dd HH:mm:ss}");
sb.AppendLine($"Character: {Character?.Name ?? "null"}");

// LLM 전처리 정보
sb.AppendLine($"=== LLM PREPROCESSING INFO ===");

// ConversationHistory 전체 내용
sb.AppendLine($"ConversationHistory ({ConversationHistory?.Count() ?? 0} items):");
if (ConversationHistory != null && ConversationHistory.Any())
{
foreach (var history in ConversationHistory)
{
sb.AppendLine($" - {history.Role}: \"{history.Content}\"");
}
}
else
{
sb.AppendLine(" (No conversation history)");
}

// MemoryContext 전체 내용
sb.AppendLine($"MemoryContext ({MemoryContext?.Count() ?? 0} items):");
if (MemoryContext != null && MemoryContext.Any())
{
foreach (var memory in MemoryContext)
{
sb.AppendLine($" - \"{memory}\"");
}
}
else
{
sb.AppendLine(" (No memory context)");
}

// 결과 정보
sb.AppendLine($"=== RESULT INFO ===");
sb.AppendLine($"Response: \"{Response}\"");
sb.AppendLine($"Cost: {Cost:F4}");

// Segments 전체 내용
sb.AppendLine($"Segments ({Segments?.Count ?? 0} items):");
if (Segments != null && Segments.Any())
{
for (int i = 0; i < Segments.Count; i++)
{
var segment = Segments[i];
sb.AppendLine($" [{i}] Type: {segment.Type}, Content: \"{segment.Content}\"");
}
}
else
{
sb.AppendLine(" (No segments)");
}

return sb.ToString();
}
Comment on lines +70 to 137
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

디버그 문자열의 PII/과도한 로그 출력 완화

Information 레벨 로깅에서 전체 프롬프트/응답/메모리를 그대로 출력하면 PII/정보 과다 노출 우려가 큽니다. 길이 제한 및 UTC 표기를 적용하세요(로깅 레벨은 ChatService에서 별도 코멘트).

-            sb.AppendLine($"UserMessage: \"{UserMessage}\"");
+            sb.AppendLine($"UserMessage: \"{Truncate(UserMessage, 200)}\"");
-            sb.AppendLine($"UserRequestAt: {UserRequestAt:yyyy-MM-dd HH:mm:ss}");
+            sb.AppendLine($"UserRequestAt: {UserRequestAt:O}");
...
-                    sb.AppendLine($"  - {history.Role}: \"{history.Content}\"");
+                    sb.AppendLine($"  - {history.Role}: \"{Truncate(history.Content, 200)}\"");
...
-                    sb.AppendLine($"  - \"{memory}\"");
+                    sb.AppendLine($"  - \"{Truncate(memory, 200)}\"");
...
-            sb.AppendLine($"Response: \"{Response}\"");
+            sb.AppendLine($"Response: \"{Truncate(Response, 400)}\"");
...
-            return sb.ToString();
+            return sb.ToString();
+
+            static string Truncate(string? s, int max)
+                => string.IsNullOrEmpty(s) ? "" : (s.Length <= max ? s : s.Substring(0, max) + "…");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public string ToDebugString()
{
var sb = new System.Text.StringBuilder();
// Request 기본 정보
sb.AppendLine($"[ChatProcessContext Debug Info]");
sb.AppendLine($"=== REQUEST INFO ===");
sb.AppendLine($"SessionId: {SessionId}");
sb.AppendLine($"UserId: {UserId}");
sb.AppendLine($"CharacterId: {CharacterId}");
sb.AppendLine($"UserMessage: \"{UserMessage}\"");
sb.AppendLine($"UseTTS: {UseTTS}");
sb.AppendLine($"UserRequestAt: {UserRequestAt:yyyy-MM-dd HH:mm:ss}");
sb.AppendLine($"Character: {Character?.Name ?? "null"}");
// LLM 전처리 정보
sb.AppendLine($"=== LLM PREPROCESSING INFO ===");
// ConversationHistory 전체 내용
sb.AppendLine($"ConversationHistory ({ConversationHistory?.Count() ?? 0} items):");
if (ConversationHistory != null && ConversationHistory.Any())
{
foreach (var history in ConversationHistory)
{
sb.AppendLine($" - {history.Role}: \"{history.Content}\"");
}
}
else
{
sb.AppendLine(" (No conversation history)");
}
// MemoryContext 전체 내용
sb.AppendLine($"MemoryContext ({MemoryContext?.Count() ?? 0} items):");
if (MemoryContext != null && MemoryContext.Any())
{
foreach (var memory in MemoryContext)
{
sb.AppendLine($" - \"{memory}\"");
}
}
else
{
sb.AppendLine(" (No memory context)");
}
// 결과 정보
sb.AppendLine($"=== RESULT INFO ===");
sb.AppendLine($"Response: \"{Response}\"");
sb.AppendLine($"Cost: {Cost:F4}");
// Segments 전체 내용
sb.AppendLine($"Segments ({Segments?.Count ?? 0} items):");
if (Segments != null && Segments.Any())
{
for (int i = 0; i < Segments.Count; i++)
{
var segment = Segments[i];
sb.AppendLine($" [{i}] Type: {segment.Type}, Content: \"{segment.Content}\"");
}
}
else
{
sb.AppendLine(" (No segments)");
}
return sb.ToString();
}
public string ToDebugString()
{
var sb = new System.Text.StringBuilder();
// Request 기본 정보
sb.AppendLine($"[ChatProcessContext Debug Info]");
sb.AppendLine($"=== REQUEST INFO ===");
sb.AppendLine($"SessionId: {SessionId}");
sb.AppendLine($"UserId: {UserId}");
sb.AppendLine($"CharacterId: {CharacterId}");
sb.AppendLine($"UserMessage: \"{Truncate(UserMessage, 200)}\"");
sb.AppendLine($"UseTTS: {UseTTS}");
sb.AppendLine($"UserRequestAt: {UserRequestAt:O}");
sb.AppendLine($"Character: {Character?.Name ?? "null"}");
// LLM 전처리 정보
sb.AppendLine($"=== LLM PREPROCESSING INFO ===");
// ConversationHistory 전체 내용
sb.AppendLine($"ConversationHistory ({ConversationHistory?.Count() ?? 0} items):");
if (ConversationHistory != null && ConversationHistory.Any())
{
foreach (var history in ConversationHistory)
{
sb.AppendLine($" - {history.Role}: \"{Truncate(history.Content, 200)}\"");
}
}
else
{
sb.AppendLine(" (No conversation history)");
}
// MemoryContext 전체 내용
sb.AppendLine($"MemoryContext ({MemoryContext?.Count() ?? 0} items):");
if (MemoryContext != null && MemoryContext.Any())
{
foreach (var memory in MemoryContext)
{
sb.AppendLine($" - \"{Truncate(memory, 200)}\"");
}
}
else
{
sb.AppendLine(" (No memory context)");
}
// 결과 정보
sb.AppendLine($"=== RESULT INFO ===");
sb.AppendLine($"Response: \"{Truncate(Response, 400)}\"");
sb.AppendLine($"Cost: {Cost:F4}");
// Segments 전체 내용
sb.AppendLine($"Segments ({Segments?.Count ?? 0} items):");
if (Segments != null && Segments.Any())
{
for (int i = 0; i < Segments.Count; i++)
{
var segment = Segments[i];
sb.AppendLine($" [{i}] Type: {segment.Type}, Content: \"{segment.Content}\"");
}
}
else
{
sb.AppendLine(" (No segments)");
}
return sb.ToString();
static string Truncate(string? s, int max)
=> string.IsNullOrEmpty(s) ? "" : (s.Length <= max ? s : s.Substring(0, max) + "…");
}
🤖 Prompt for AI Agents
In ProjectVG.Application/Models/Chat/ChatProcessContext.cs around lines 70-137,
the current ToDebugString emits full prompt/response/memory which risks
PII/excessive logs; update it to truncate long text fields to a safe max length
(e.g., 200 chars) and show an ellipsis plus length when truncated, avoid
printing raw full items for ConversationHistory/MemoryContext/Response/Segments
(show first N items, e.g., 5, and for each include role/type, a truncated
content or a hash/short fingerprint), and format timestamps in UTC
(UserRequestAt: yyyy-MM-dd HH:mm:ss 'UTC'). Ensure null checks remain and
include counts for omitted items (e.g., "+X more") so the debug string is
compact and safe for information-level logging.

}
}
Loading