diff --git a/README.md b/README.md index a5e3fc3..ebb7c6a 100644 --- a/README.md +++ b/README.md @@ -2138,3 +2138,10 @@ MIT License - `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_permission_policy_cleanup\\ -p:IntermediateOutputPath=obj\\verify_permission_policy_cleanup\\` 경고 0 / 오류 0 - `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "OperationModePolicyTests|OperationModeReadinessTests|AgentLoopE2ETests" -p:OutputPath=bin\\verify_permission_policy_cleanup_tests\\ -p:IntermediateOutputPath=obj\\verify_permission_policy_cleanup_tests\\` 통과 46 - `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "LlmOperationModeTests" -p:OutputPath=bin\\verify_permission_policy_llm_tests\\ -p:IntermediateOutputPath=obj\\verify_permission_policy_llm_tests\\` 통과 3 + +업데이트: 2026-04-15 16:40 (KST) +- 사내 모드의 외부 LLM 정의를 테스트와 문서로 고정했습니다. 현재 기준 외부 LLM은 `Gemini`, `Claude`만 의미하며, `Ollama`, `vLLM`은 사내/사외 모드 모두 사용 가능합니다. +- [LlmOperationModeTests.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot.Tests/Services/LlmOperationModeTests.cs)를 UTF-8 기준으로 정리하고, 사내 모드에서 `Ollama`, `vLLM`이 외부 LLM 차단 가드에 막히지 않고 다음 단계까지 진행되는 회귀 테스트를 추가했습니다. +- 검증: + - `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_internal_llm_scope\\ -p:IntermediateOutputPath=obj\\verify_internal_llm_scope\\` 경고 0 / 오류 0 + - `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "LlmOperationModeTests" -p:OutputPath=bin\\verify_internal_llm_scope_tests\\ -p:IntermediateOutputPath=obj\\verify_internal_llm_scope_tests\\` 통과 5 diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index 86ad701..893ecda 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -1470,3 +1470,16 @@ UI ?붿옄???€洹쒕え 由ы뙥?좊쭅 ???꾪뿕 ?묒뾽 ??湲곕줉???덉쟾 - `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_permission_policy_cleanup\\ -p:IntermediateOutputPath=obj\\verify_permission_policy_cleanup\\` 경고 0 / 오류 0 - `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "OperationModePolicyTests|OperationModeReadinessTests|AgentLoopE2ETests" -p:OutputPath=bin\\verify_permission_policy_cleanup_tests\\ -p:IntermediateOutputPath=obj\\verify_permission_policy_cleanup_tests\\` 통과 46 - `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "LlmOperationModeTests" -p:OutputPath=bin\\verify_permission_policy_llm_tests\\ -p:IntermediateOutputPath=obj\\verify_permission_policy_llm_tests\\` 통과 3 + +업데이트: 2026-04-15 16:40 (KST) + +### 사내 모드 외부 LLM 범위 고정 +- `src/AxCopilot.Tests/Services/LlmOperationModeTests.cs` + - 인코딩이 깨져 있던 기존 테스트를 UTF-8 기준으로 정리했습니다. + - 사내 모드에서 `Gemini`, `Claude`만 외부 LLM 차단 대상으로 보고, `Ollama`, `vLLM`은 차단 가드를 통과해야 한다는 회귀 테스트를 추가했습니다. +- 정책 기준 + - 외부 LLM: `Gemini`, `Claude` + - 사내/사외 모두 허용: `Ollama`, `vLLM` +- 검증 + - `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_internal_llm_scope\\ -p:IntermediateOutputPath=obj\\verify_internal_llm_scope\\` 경고 0 / 오류 0 + - `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "LlmOperationModeTests" -p:OutputPath=bin\\verify_internal_llm_scope_tests\\ -p:IntermediateOutputPath=obj\\verify_internal_llm_scope_tests\\` 통과 5 diff --git a/src/AxCopilot.Tests/Services/LlmOperationModeTests.cs b/src/AxCopilot.Tests/Services/LlmOperationModeTests.cs index 8448403..af2e3bf 100644 --- a/src/AxCopilot.Tests/Services/LlmOperationModeTests.cs +++ b/src/AxCopilot.Tests/Services/LlmOperationModeTests.cs @@ -22,8 +22,8 @@ public class LlmOperationModeTests [new ChatMessage { Role = "user", Content = "test" }]); var ex = await action.Should().ThrowAsync(); - ex.Which.Message.Should().Contain("사내 모드"); - ex.Which.Message.Should().Contain("외부 LLM 호출이 차단"); + ex.Which.Message.Should().Contain("외부 LLM 호출이 차단됩니다"); + ex.Which.Message.Should().Contain("Gemini"); } [Fact] @@ -42,7 +42,7 @@ public class LlmOperationModeTests tools); var ex = await action.Should().ThrowAsync(); - ex.Which.Message.Should().Contain("사내 모드"); + ex.Which.Message.Should().Contain("외부 LLM 호출이 차단됩니다"); ex.Which.Message.Should().Contain("Claude"); } @@ -61,8 +61,49 @@ public class LlmOperationModeTests [new ChatMessage { Role = "user", Content = "test" }]); var ex = await action.Should().ThrowAsync(); - ex.Which.Message.Should().NotContain("사내 모드에서는 외부 LLM 호출이 차단"); - ex.Which.Message.Should().Contain("API 키"); + ex.Which.Message.Should().NotContain("외부 LLM 호출이 차단됩니다"); + ex.Which.Message.Should().Contain("API"); + } + + [Fact] + public async Task SendAsync_InternalMode_AllowsOllamaToProceedPastExternalLlmGuard() + { + var settings = new SettingsService(); + settings.Settings.OperationMode = OperationModePolicy.InternalMode; + settings.Settings.Llm.Service = "ollama"; + settings.Settings.Llm.Model = "qwen2.5-coder"; + settings.Settings.Llm.OllamaModel = "qwen2.5-coder"; + settings.Settings.Llm.OllamaEndpoint = "http://127.0.0.1:1"; + + using var llm = new LlmService(settings); + + var action = async () => await llm.SendAsync( + [new ChatMessage { Role = "user", Content = "test" }]); + + var ex = await action.Should().ThrowAsync(); + ex.Which.Message.Should().NotContain("외부 LLM 호출이 차단됩니다"); + ex.Which.Message.Should().NotContain("Gemini"); + ex.Which.Message.Should().NotContain("Claude"); + } + + [Fact] + public async Task SendAsync_InternalMode_AllowsVllmToProceedPastExternalLlmGuard() + { + var settings = new SettingsService(); + settings.Settings.OperationMode = OperationModePolicy.InternalMode; + settings.Settings.Llm.Service = "vllm"; + settings.Settings.Llm.Model = "qwen2.5-coder"; + settings.Settings.Llm.VllmModel = "qwen2.5-coder"; + settings.Settings.Llm.VllmEndpoint = "http://127.0.0.1:1"; + + using var llm = new LlmService(settings); + + var action = async () => await llm.SendAsync( + [new ChatMessage { Role = "user", Content = "test" }]); + + var ex = await action.Should().ThrowAsync(); + ex.Which.Message.Should().NotContain("외부 LLM 호출이 차단됩니다"); + ex.Which.Message.Should().NotContain("Gemini"); + ex.Which.Message.Should().NotContain("Claude"); } } -