AX Agent 채팅 UI 백업 및 claw-code 기준 엔진 재정렬
Some checks failed
Release Gate / gate (push) Has been cancelled
Some checks failed
Release Gate / gate (push) Has been cancelled
- ChatWindow 현재 UI와 엔진 기준본을 etc/chat-ui-backup/2026-04-05-1215에 백업해 회귀 비교 지점을 확보함 - 메시지 컬럼과 컴포저 폭을 claw-code식 단일 축으로 다시 맞추고 입력 셸을 안정적인 하단 컬럼 구조로 정리함 - 입력창 높이를 실제 줄바꿈 수 기준으로 다시 계산해 전송 후 높이가 남는 버그를 줄임 - 메시지 편집/피드백 후 재생성 경로의 직접 UI 버블 주입을 제거하고 RenderMessages 중심으로 통합함 - SendRegenerateAsync가 Cowork/Code에서 ResolveExecutionMode와 RunAgentLoopAsync를 타도록 바꿔 재생성도 동일 엔진 축으로 정렬함 - 검증: dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify\\ -p:IntermediateOutputPath=obj\\verify\\ 경고 0 / 오류 0
This commit is contained in:
128
etc/chat-ui-backup/2026-04-05-1215/AxAgentExecutionEngine.cs
Normal file
128
etc/chat-ui-backup/2026-04-05-1215/AxAgentExecutionEngine.cs
Normal file
@@ -0,0 +1,128 @@
|
||||
using AxCopilot.Models;
|
||||
using AxCopilot.Services;
|
||||
|
||||
namespace AxCopilot.Services.Agent;
|
||||
|
||||
/// <summary>
|
||||
/// AX Agent execution-prep engine.
|
||||
/// Inspired by the `claw-code` split between input preparation and session execution,
|
||||
/// so the UI layer stops owning message assembly and final assistant commit logic.
|
||||
/// </summary>
|
||||
public sealed class AxAgentExecutionEngine
|
||||
{
|
||||
public sealed record PreparedTurn(List<ChatMessage> Messages);
|
||||
public sealed record ExecutionMode(bool UseAgentLoop, bool UseStreamingTransport, string? TaskSystemPrompt);
|
||||
|
||||
public IReadOnlyList<string> BuildPromptStack(
|
||||
string? conversationSystem,
|
||||
string? slashSystem,
|
||||
string? taskSystem = null)
|
||||
{
|
||||
var prompts = new List<string>();
|
||||
if (!string.IsNullOrWhiteSpace(conversationSystem))
|
||||
prompts.Add(conversationSystem.Trim());
|
||||
if (!string.IsNullOrWhiteSpace(slashSystem))
|
||||
prompts.Add(slashSystem.Trim());
|
||||
if (!string.IsNullOrWhiteSpace(taskSystem))
|
||||
prompts.Add(taskSystem.Trim());
|
||||
return prompts;
|
||||
}
|
||||
|
||||
public ExecutionMode ResolveExecutionMode(
|
||||
string runTab,
|
||||
bool streamingEnabled,
|
||||
string resolvedService,
|
||||
string? coworkSystemPrompt,
|
||||
string? codeSystemPrompt)
|
||||
{
|
||||
if (string.Equals(runTab, "Cowork", StringComparison.OrdinalIgnoreCase))
|
||||
return new ExecutionMode(true, false, coworkSystemPrompt);
|
||||
|
||||
if (string.Equals(runTab, "Code", StringComparison.OrdinalIgnoreCase))
|
||||
return new ExecutionMode(true, false, codeSystemPrompt);
|
||||
|
||||
return new ExecutionMode(false, false, null);
|
||||
}
|
||||
|
||||
public PreparedTurn PrepareTurn(
|
||||
ChatConversation conversation,
|
||||
IEnumerable<string?> systemPrompts,
|
||||
string? fileContext = null,
|
||||
IReadOnlyList<ImageAttachment>? images = null)
|
||||
{
|
||||
var outbound = conversation.Messages
|
||||
.Select(CloneMessage)
|
||||
.ToList();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(fileContext))
|
||||
{
|
||||
var lastUserIndex = outbound.FindLastIndex(m => string.Equals(m.Role, "user", StringComparison.OrdinalIgnoreCase));
|
||||
if (lastUserIndex >= 0)
|
||||
outbound[lastUserIndex].Content = (outbound[lastUserIndex].Content ?? string.Empty) + fileContext;
|
||||
}
|
||||
|
||||
if (images is { Count: > 0 })
|
||||
{
|
||||
var lastUserIndex = outbound.FindLastIndex(m => string.Equals(m.Role, "user", StringComparison.OrdinalIgnoreCase));
|
||||
if (lastUserIndex >= 0)
|
||||
outbound[lastUserIndex].Images = images.Select(CloneImage).ToList();
|
||||
}
|
||||
|
||||
var promptList = systemPrompts
|
||||
.Where(prompt => !string.IsNullOrWhiteSpace(prompt))
|
||||
.Select(prompt => prompt!.Trim())
|
||||
.ToList();
|
||||
|
||||
for (var i = promptList.Count - 1; i >= 0; i--)
|
||||
outbound.Insert(0, new ChatMessage { Role = "system", Content = promptList[i] });
|
||||
|
||||
return new PreparedTurn(outbound);
|
||||
}
|
||||
|
||||
public ChatMessage CommitAssistantMessage(
|
||||
ChatSessionStateService? session,
|
||||
ChatConversation conversation,
|
||||
string tab,
|
||||
string content,
|
||||
ChatStorageService? storage = null)
|
||||
{
|
||||
var assistant = new ChatMessage
|
||||
{
|
||||
Role = "assistant",
|
||||
Content = content,
|
||||
};
|
||||
|
||||
if (session != null)
|
||||
{
|
||||
session.AppendMessage(tab, assistant, storage);
|
||||
return assistant;
|
||||
}
|
||||
|
||||
conversation.Messages.Add(assistant);
|
||||
conversation.UpdatedAt = DateTime.Now;
|
||||
return assistant;
|
||||
}
|
||||
|
||||
private static ChatMessage CloneMessage(ChatMessage source)
|
||||
{
|
||||
return new ChatMessage
|
||||
{
|
||||
Role = source.Role,
|
||||
Content = source.Content,
|
||||
Timestamp = source.Timestamp,
|
||||
MetaRunId = source.MetaRunId,
|
||||
AttachedFiles = source.AttachedFiles?.ToList(),
|
||||
Images = source.Images?.Select(CloneImage).ToList(),
|
||||
};
|
||||
}
|
||||
|
||||
private static ImageAttachment CloneImage(ImageAttachment source)
|
||||
{
|
||||
return new ImageAttachment
|
||||
{
|
||||
Base64 = source.Base64,
|
||||
MimeType = source.MimeType,
|
||||
FileName = source.FileName,
|
||||
};
|
||||
}
|
||||
}
|
||||
5175
etc/chat-ui-backup/2026-04-05-1215/ChatWindow.xaml
Normal file
5175
etc/chat-ui-backup/2026-04-05-1215/ChatWindow.xaml
Normal file
File diff suppressed because it is too large
Load Diff
21806
etc/chat-ui-backup/2026-04-05-1215/ChatWindow.xaml.cs
Normal file
21806
etc/chat-ui-backup/2026-04-05-1215/ChatWindow.xaml.cs
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user