코드 탭 컨텍스트 영속화와 Auto budget 복구 적용
- AxAgentExecutionEngine에서 시스템 프롬프트 중복을 제거하고 structured tool_use/tool_result 전사본을 conversation.Messages로 동기화해 다음 턴과 저장 이력에서도 코드 작업 컨텍스트가 유지되도록 수정 - AgentQueryContextBuilder와 ContextCondenser에 post-compact tool snippet 복원, recent window 확대, tool result 보존 강화 로직을 추가해 장기 코드 실행 중 빌드/파일 근거 손실을 줄임 - MaxContextTokens=0 Auto 모드를 AppSettings, SettingsService 마이그레이션, 설정 UI, 오버레이 UI, 컨텍스트 사용량 표시, LLM 요청 본문에 연결하고 Auto 모드에서는 provider output cap 강제 주입을 제거 - 관련 회귀 테스트와 문서 README/DEVELOPMENT/CODE_CONTEXT_RELIABILITY_PLAN을 갱신하고 깨진 진단 문자열 기대값을 영어 기준으로 정리 검증: - dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_context_reliability_followup\\ -p:IntermediateOutputPath=obj\\verify_context_reliability_followup\\ - dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "AxAgentExecutionEngineTests|AgentQueryContextBuilderTests|ContextCondenserTests|SettingsServiceTests|AgentLoopDiagnosticsFormatterTests" -p:OutputPath=bin\\verify_context_reliability_followup_tests\\ -p:IntermediateOutputPath=obj\\verify_context_reliability_followup_tests\\ - dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "AgentLoopQueryAssemblyServiceTests|AgentLoopPreLlmStageServiceTests|AgentLoopLlmRequestPreparationServiceTests|AgentMessageInvariantHelperTests|CodeTaskWorkingSetServiceTests|AgentLoopE2ETests" -p:OutputPath=bin\\verify_context_reliability_followup_tests2\\ -p:IntermediateOutputPath=obj\\verify_context_reliability_followup_tests2\\ - dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_context_reliability_final\\ -p:IntermediateOutputPath=obj\\verify_context_reliability_final\\
This commit is contained in:
@@ -301,14 +301,25 @@ public partial class LlmService : ILlmService
|
||||
return llm.Model;
|
||||
}
|
||||
|
||||
private int ResolveOpenAiCompatibleMaxTokens()
|
||||
private int? ResolveConfiguredMaxOutputTokens()
|
||||
{
|
||||
var llm = _settings.Settings.Llm;
|
||||
var requested = Math.Clamp(llm.MaxContextTokens, 1, 1_000_000);
|
||||
return llm.MaxContextTokens > 0
|
||||
? Math.Clamp(llm.MaxContextTokens, 1, 1_000_000)
|
||||
: null;
|
||||
}
|
||||
|
||||
private int? ResolveOpenAiCompatibleMaxTokens()
|
||||
{
|
||||
var llm = _settings.Settings.Llm;
|
||||
var requested = ResolveConfiguredMaxOutputTokens();
|
||||
if (!requested.HasValue)
|
||||
return null;
|
||||
|
||||
var service = NormalizeServiceName(llm.Service);
|
||||
|
||||
if (service == "vllm")
|
||||
return Math.Min(requested, 8192);
|
||||
return Math.Min(requested.Value, 8192);
|
||||
|
||||
return requested;
|
||||
}
|
||||
@@ -469,16 +480,19 @@ public partial class LlmService : ILlmService
|
||||
var temperature = ResolveTemperature();
|
||||
var maxTokens = ResolveOpenAiCompatibleMaxTokens();
|
||||
IbmDiagDebug($"[IBM진단] BuildIbmDeploymentBody 완료: finalMessages={msgs.Count}건, temp={temperature}, maxTokens={maxTokens}");
|
||||
return new
|
||||
var parameters = new Dictionary<string, object?>
|
||||
{
|
||||
messages = msgs,
|
||||
parameters = new
|
||||
{
|
||||
temperature,
|
||||
max_new_tokens = maxTokens
|
||||
},
|
||||
// Qwen3.5 thinking 모드 비활성화: 활성화되면 content=null, reasoning_content에만 출력됨
|
||||
chat_template_kwargs = new { enable_thinking = false },
|
||||
["temperature"] = temperature,
|
||||
};
|
||||
if (maxTokens.HasValue)
|
||||
parameters["max_new_tokens"] = maxTokens.Value;
|
||||
|
||||
return new Dictionary<string, object?>
|
||||
{
|
||||
["messages"] = msgs,
|
||||
["parameters"] = parameters,
|
||||
// Qwen3.5 thinking mode is disabled to keep content in the normal response field.
|
||||
["chat_template_kwargs"] = new { enable_thinking = false },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1155,7 +1169,6 @@ public partial class LlmService : ILlmService
|
||||
|
||||
private object BuildOpenAiBody(List<ChatMessage> messages, bool stream)
|
||||
{
|
||||
var llm = _settings.Settings.Llm;
|
||||
var msgs = BuildMessageList(messages, openAiVision: true);
|
||||
var body = new Dictionary<string, object?>
|
||||
{
|
||||
@@ -1163,8 +1176,10 @@ public partial class LlmService : ILlmService
|
||||
["messages"] = msgs,
|
||||
["stream"] = stream,
|
||||
["temperature"] = ResolveTemperature(),
|
||||
["max_tokens"] = ResolveOpenAiCompatibleMaxTokens()
|
||||
};
|
||||
var maxTokens = ResolveOpenAiCompatibleMaxTokens();
|
||||
if (maxTokens.HasValue)
|
||||
body["max_tokens"] = maxTokens.Value;
|
||||
// 스트리밍 시 마지막 청크에 토큰 사용량을 포함하도록 요청 (vLLM/OpenAI 호환)
|
||||
if (stream)
|
||||
body["stream_options"] = new { include_usage = true };
|
||||
@@ -1298,19 +1313,23 @@ public partial class LlmService : ILlmService
|
||||
});
|
||||
}
|
||||
|
||||
if (systemInstruction != null)
|
||||
return new
|
||||
{
|
||||
systemInstruction,
|
||||
contents,
|
||||
generationConfig = new { temperature = ResolveTemperature(), maxOutputTokens = llm.MaxContextTokens }
|
||||
};
|
||||
|
||||
return new
|
||||
var generationConfig = new Dictionary<string, object?>
|
||||
{
|
||||
contents,
|
||||
generationConfig = new { temperature = ResolveTemperature(), maxOutputTokens = llm.MaxContextTokens }
|
||||
["temperature"] = ResolveTemperature(),
|
||||
};
|
||||
var maxOutputTokens = ResolveConfiguredMaxOutputTokens();
|
||||
if (maxOutputTokens.HasValue)
|
||||
generationConfig["maxOutputTokens"] = maxOutputTokens.Value;
|
||||
|
||||
var body = new Dictionary<string, object?>
|
||||
{
|
||||
["contents"] = contents,
|
||||
["generationConfig"] = generationConfig,
|
||||
};
|
||||
if (systemInstruction != null)
|
||||
body["systemInstruction"] = systemInstruction;
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
@@ -1422,7 +1441,6 @@ public partial class LlmService : ILlmService
|
||||
|
||||
private object BuildSigmoidBody(List<ChatMessage> messages, bool stream)
|
||||
{
|
||||
var llm = _settings.Settings.Llm;
|
||||
var msgs = new List<object>();
|
||||
|
||||
foreach (var m in messages)
|
||||
@@ -1444,27 +1462,32 @@ public partial class LlmService : ILlmService
|
||||
}
|
||||
|
||||
var activeModel = ResolveModel();
|
||||
var maxTokens = ResolveConfiguredMaxOutputTokens();
|
||||
if (!string.IsNullOrEmpty(_systemPrompt))
|
||||
{
|
||||
return new
|
||||
var body = new Dictionary<string, object?>
|
||||
{
|
||||
model = activeModel,
|
||||
max_tokens = llm.MaxContextTokens,
|
||||
temperature = ResolveTemperature(),
|
||||
system = _systemPrompt,
|
||||
messages = msgs,
|
||||
stream
|
||||
["model"] = activeModel,
|
||||
["temperature"] = ResolveTemperature(),
|
||||
["system"] = _systemPrompt,
|
||||
["messages"] = msgs,
|
||||
["stream"] = stream,
|
||||
};
|
||||
if (maxTokens.HasValue)
|
||||
body["max_tokens"] = maxTokens.Value;
|
||||
return body;
|
||||
}
|
||||
|
||||
return new
|
||||
var fallbackBody = new Dictionary<string, object?>
|
||||
{
|
||||
model = activeModel,
|
||||
max_tokens = llm.MaxContextTokens,
|
||||
temperature = ResolveTemperature(),
|
||||
messages = msgs,
|
||||
stream
|
||||
["model"] = activeModel,
|
||||
["temperature"] = ResolveTemperature(),
|
||||
["messages"] = msgs,
|
||||
["stream"] = stream,
|
||||
};
|
||||
if (maxTokens.HasValue)
|
||||
fallbackBody["max_tokens"] = maxTokens.Value;
|
||||
return fallbackBody;
|
||||
}
|
||||
|
||||
// ─── 공용 헬퍼 ─────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user