Files
AX-Copilot-Codex/src/AxCopilot/Models/ChatModels.cs
lacvet ca006972b2
Some checks failed
Release Gate / gate (push) Has been cancelled
post-compaction 추적과 대화별 사용량 계측 보강
- claude-code의 post-compaction 플래그 흐름을 참고해 compact 직후 첫 응답을 별도로 추적하고 대화 단위 후속 사용량을 집계함

- ChatConversation에 pending post-compaction, 응답 회수, prompt/completion token 누적 필드를 추가하고 AX Agent 컨텍스트 카드 hover에 표시함

- README 및 docs/DEVELOPMENT.md에 2026-04-04 23:47 (KST) 기준 이력을 반영함

- 검증: dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify\\ -p:IntermediateOutputPath=obj\\verify\\ (경고 0 / 오류 0)
2026-04-04 23:54:59 +09:00

337 lines
11 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 System.Text.Json.Serialization;
namespace AxCopilot.Models;
/// <summary>대화 주제 카테고리.</summary>
public static class ChatCategory
{
public const string General = "일반";
public const string Management = "경영";
public const string HR = "인사";
public const string Finance = "재무";
public const string RnD = "연구개발";
public const string Product = "제품분석";
public const string Yield = "수율분석";
public const string MfgTech = "제조기술";
public const string System = "시스템";
public static readonly (string Key, string Label, string Symbol, string Color)[] All =
{
(General, "일반", "\uE8BD", "#6B7280"), // Chat gray
(Management, "경영", "\uE902", "#8B5CF6"), // Briefcase purple
(HR, "인사", "\uE716", "#0EA5E9"), // People sky blue
(Finance, "재무", "\uE8C7", "#D97706"), // Money amber/orange
(RnD, "연구개발", "\uE9A8", "#3B82F6"), // Research blue
(Product, "제품분석", "\uE9D9", "#EC4899"), // Design pink
(Yield, "수율분석", "\uE9F9", "#F59E0B"), // Analytics amber
(MfgTech, "제조기술", "\uE90F", "#10B981"), // Manufacturing emerald
(System, "시스템", "\uE770", "#EF4444"), // IT System red
};
public static string GetSymbol(string? category)
{
if (string.IsNullOrEmpty(category)) return "\uE8BD";
foreach (var (key, _, symbol, _) in All)
if (key == category) return symbol;
return "\uE8BD";
}
public static string GetColor(string? category)
{
if (string.IsNullOrEmpty(category)) return "#6B7280";
foreach (var (key, _, _, color) in All)
if (key == category) return color;
return "#6B7280";
}
}
/// <summary>하나의 대화(세션). 복수의 메시지를 포함합니다.</summary>
public class ChatConversation
{
[JsonPropertyName("id")]
public string Id { get; set; } = Guid.NewGuid().ToString("N");
[JsonPropertyName("title")]
public string Title { get; set; } = "새 대화";
[JsonPropertyName("createdAt")]
public DateTime CreatedAt { get; set; } = DateTime.Now;
[JsonPropertyName("updatedAt")]
public DateTime UpdatedAt { get; set; } = DateTime.Now;
[JsonPropertyName("pinned")]
public bool Pinned { get; set; } = false;
/// <summary>대화가 속한 탭. "Chat" | "Cowork" | "Code".</summary>
[JsonPropertyName("tab")]
public string Tab { get; set; } = "Chat";
/// <summary>대화 주제. ChatCategory 상수 중 하나.</summary>
[JsonPropertyName("category")]
public string Category { get; set; } = ChatCategory.General;
/// <summary>사용자가 보이지 않는 시스템 명령어 (대화별 커스텀 프롬프트).</summary>
[JsonPropertyName("systemCommand")]
public string SystemCommand { get; set; } = "";
/// <summary>대화에 연결된 작업 폴더 경로.</summary>
[JsonPropertyName("workFolder")]
public string WorkFolder { get; set; } = "";
/// <summary>첫 사용자 메시지 요약 (검색용, 최대 100자). 저장 시 자동 갱신.</summary>
[JsonPropertyName("preview")]
public string Preview { get; set; } = "";
/// <summary>분기 원본 대화 ID. null이면 원본 대화.</summary>
[JsonPropertyName("parentId")]
public string? ParentId { get; set; }
/// <summary>분기 라벨 (예: "대안 A", "접근법 2").</summary>
[JsonPropertyName("branchLabel")]
public string? BranchLabel { get; set; }
/// <summary>분기 시점의 메시지 인덱스 (parentId의 Messages[index] 이후부터 분기).</summary>
[JsonPropertyName("branchAtIndex")]
public int? BranchAtIndex { get; set; }
// ─── 대화별 설정 (하단 바에서 변경, 대화마다 독립 저장) ───
/// <summary>파일 접근 권한. null이면 전역 설정 사용. "Ask" | "Plan" | "Auto" | "Deny"</summary>
[JsonPropertyName("permission")]
public string? Permission { get; set; }
/// <summary>데이터 활용 모드. null이면 전역 설정 사용. "active" | "passive" | "none"</summary>
[JsonPropertyName("dataUsage")]
public string? DataUsage { get; set; }
/// <summary>출력 포맷. null이면 전역 설정 사용.</summary>
[JsonPropertyName("outputFormat")]
public string? OutputFormat { get; set; }
/// <summary>무드/디자인 템플릿. null이면 전역 설정 사용.</summary>
[JsonPropertyName("mood")]
public string? Mood { get; set; }
[JsonPropertyName("messages")]
public List<ChatMessage> Messages { get; set; } = new();
[JsonPropertyName("executionEvents")]
public List<ChatExecutionEvent> ExecutionEvents { get; set; } = new();
[JsonPropertyName("draftQueue")]
public List<string> DraftQueue { get; set; } = new();
[JsonPropertyName("draftQueueItems")]
public List<DraftQueueItem> DraftQueueItems { get; set; } = new();
[JsonPropertyName("showExecutionHistory")]
public bool ShowExecutionHistory { get; set; } = true;
[JsonPropertyName("agentRunHistory")]
public List<ChatAgentRunRecord> AgentRunHistory { get; set; } = new();
[JsonPropertyName("conversationFailedOnlyFilter")]
public bool ConversationFailedOnlyFilter { get; set; }
[JsonPropertyName("conversationRunningOnlyFilter")]
public bool ConversationRunningOnlyFilter { get; set; }
[JsonPropertyName("conversationSortMode")]
public string ConversationSortMode { get; set; } = "activity";
[JsonPropertyName("compactionCount")]
public int CompactionCount { get; set; }
[JsonPropertyName("automaticCompactionCount")]
public int AutomaticCompactionCount { get; set; }
[JsonPropertyName("manualCompactionCount")]
public int ManualCompactionCount { get; set; }
[JsonPropertyName("compactionSavedTokens")]
public int CompactionSavedTokens { get; set; }
[JsonPropertyName("sessionMemoryCompactionCount")]
public int SessionMemoryCompactionCount { get; set; }
[JsonPropertyName("microcompactBoundaryCount")]
public int MicrocompactBoundaryCount { get; set; }
[JsonPropertyName("snipCompactionCount")]
public int SnipCompactionCount { get; set; }
[JsonPropertyName("lastCompactionAt")]
public DateTime? LastCompactionAt { get; set; }
[JsonPropertyName("lastCompactionWasAutomatic")]
public bool LastCompactionWasAutomatic { get; set; }
[JsonPropertyName("lastCompactionBeforeTokens")]
public int? LastCompactionBeforeTokens { get; set; }
[JsonPropertyName("lastCompactionAfterTokens")]
public int? LastCompactionAfterTokens { get; set; }
[JsonPropertyName("lastCompactionStageSummary")]
public string? LastCompactionStageSummary { get; set; }
[JsonPropertyName("pendingPostCompaction")]
public bool PendingPostCompaction { get; set; }
[JsonPropertyName("postCompactionResponseCount")]
public int PostCompactionResponseCount { get; set; }
[JsonPropertyName("postCompactionPromptTokens")]
public int PostCompactionPromptTokens { get; set; }
[JsonPropertyName("postCompactionCompletionTokens")]
public int PostCompactionCompletionTokens { get; set; }
}
public class ChatAgentRunRecord
{
[JsonPropertyName("runId")]
public string RunId { get; set; } = "";
[JsonPropertyName("status")]
public string Status { get; set; } = "completed";
[JsonPropertyName("summary")]
public string Summary { get; set; } = "";
[JsonPropertyName("lastIteration")]
public int LastIteration { get; set; }
[JsonPropertyName("startedAt")]
public DateTime StartedAt { get; set; } = DateTime.Now;
[JsonPropertyName("updatedAt")]
public DateTime UpdatedAt { get; set; } = DateTime.Now;
}
public class DraftQueueItem
{
[JsonPropertyName("id")]
public string Id { get; set; } = Guid.NewGuid().ToString("N");
[JsonPropertyName("text")]
public string Text { get; set; } = "";
[JsonPropertyName("priority")]
public string Priority { get; set; } = "next";
[JsonPropertyName("kind")]
public string Kind { get; set; } = "message";
[JsonPropertyName("state")]
public string State { get; set; } = "queued";
[JsonPropertyName("attemptCount")]
public int AttemptCount { get; set; }
[JsonPropertyName("lastError")]
public string? LastError { get; set; }
[JsonPropertyName("nextRetryAt")]
public DateTime? NextRetryAt { get; set; }
[JsonPropertyName("createdAt")]
public DateTime CreatedAt { get; set; } = DateTime.Now;
}
/// <summary>대화 내 개별 메시지.</summary>
public class ChatMessage
{
[JsonPropertyName("role")]
public string Role { get; set; } = "user"; // "user" | "assistant" | "system"
[JsonPropertyName("content")]
public string Content { get; set; } = "";
[JsonPropertyName("timestamp")]
public DateTime Timestamp { get; set; } = DateTime.Now;
[JsonPropertyName("metaKind")]
public string? MetaKind { get; set; }
[JsonPropertyName("metaRunId")]
public string? MetaRunId { get; set; }
/// <summary>피드백 상태. null=없음, "like", "dislike"</summary>
[JsonPropertyName("feedback")]
public string? Feedback { get; set; }
/// <summary>첨부된 파일 경로 목록.</summary>
[JsonPropertyName("attachedFiles")]
public List<string>? AttachedFiles { get; set; }
/// <summary>첨부된 이미지 목록. base64 인코딩된 이미지 데이터.</summary>
[JsonPropertyName("images")]
public List<ImageAttachment>? Images { get; set; }
}
public class ChatExecutionEvent
{
[JsonPropertyName("timestamp")]
public DateTime Timestamp { get; set; } = DateTime.Now;
[JsonPropertyName("runId")]
public string RunId { get; set; } = "";
[JsonPropertyName("type")]
public string Type { get; set; } = "Thinking";
[JsonPropertyName("toolName")]
public string ToolName { get; set; } = "";
[JsonPropertyName("summary")]
public string Summary { get; set; } = "";
[JsonPropertyName("filePath")]
public string? FilePath { get; set; }
[JsonPropertyName("success")]
public bool Success { get; set; } = true;
[JsonPropertyName("stepCurrent")]
public int StepCurrent { get; set; }
[JsonPropertyName("stepTotal")]
public int StepTotal { get; set; }
[JsonPropertyName("steps")]
public List<string>? Steps { get; set; }
[JsonPropertyName("elapsedMs")]
public long ElapsedMs { get; set; }
[JsonPropertyName("inputTokens")]
public int InputTokens { get; set; }
[JsonPropertyName("outputTokens")]
public int OutputTokens { get; set; }
[JsonPropertyName("toolInput")]
public string? ToolInput { get; set; }
[JsonPropertyName("iteration")]
public int Iteration { get; set; }
}
/// <summary>이미지 첨부 데이터. LLM Vision API에 전달되는 base64 인코딩 이미지.</summary>
public class ImageAttachment
{
/// <summary>base64 인코딩된 이미지 데이터.</summary>
[JsonPropertyName("base64")]
public string Base64 { get; set; } = "";
/// <summary>MIME 타입. image/png, image/jpeg 등.</summary>
[JsonPropertyName("mimeType")]
public string MimeType { get; set; } = "image/png";
/// <summary>원본 파일명 또는 설명.</summary>
[JsonPropertyName("fileName")]
public string FileName { get; set; } = "";
}