From 53afdb3472acf033da3fedc48e9923352ff04104 Mon Sep 17 00:00:00 2001 From: lacvet Date: Sun, 5 Apr 2026 21:40:43 +0900 Subject: [PATCH] =?UTF-8?q?vLLM=20=EB=AA=A8=EB=8D=B8=20=ED=95=B4=EC=84=9D?= =?UTF-8?q?=20=EB=B0=8F=20max=5Ftokens=20=EC=83=81=ED=95=9C=20=EB=B3=B4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vLLM 연결 시 등록 모델 alias와 실제 모델 ID가 섞여 payload로 전달되던 경로를 보정해 RegisteredModel에서 실제 모델명을 우선 찾아 요청에 사용하도록 수정했다. OpenAI-compatible 일반 대화와 도구 호출 모두 vLLM 서버 허용 범위를 넘지 않도록 max_tokens를 자동 보정하도록 통일했다. 검증: dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify\\ -p:IntermediateOutputPath=obj\\verify\\ (경고 0, 오류 0) --- README.md | 3 +++ docs/DEVELOPMENT.md | 3 +++ src/AxCopilot/Services/LlmService.ToolUse.cs | 6 ++--- src/AxCopilot/Services/LlmService.cs | 24 +++++++++++++++++++- 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 670f71b..0c32633 100644 --- a/README.md +++ b/README.md @@ -1078,3 +1078,6 @@ MIT License - AX Agent 채팅창의 기본 시작 높이를 소폭 늘려, 처음 열었을 때 상하 여백과 프리셋 영역이 더 여유 있게 보이도록 조정했다. - 업데이트: 2026-04-06 00:31 (KST) - AX Agent 상단 중앙 탭 그룹의 버튼 padding, 최소 폭/높이와 바깥 pill 래퍼 높이를 한 단계 더 줄였다. 이제 탭 바깥 테두리 안쪽 여백이 더 살아 있어, 레퍼런스처럼 답답하지 않은 세그먼트 탭 비율로 보인다. +- 업데이트: 2026-04-06 00:38 (KST) + - vLLM 연결 시 등록 모델 alias/실제 모델 ID가 섞여 전달되던 경로를 보정했다. 내부 서비스(Ollama/vLLM)는 현재 선택값이 alias여도 등록 모델의 실제 모델명을 다시 찾아 요청 payload에 넣도록 정리했다. + - vLLM OpenAI-compatible 요청의 `max_tokens`는 서버 허용 범위를 넘지 않도록 자동 보정했다. 일반 대화와 도구 호출 모두 같은 상한 계산을 써 `invalid max_tokens` 오류가 덜 나도록 맞췄다. diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index a60fbef..f07103f 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -4835,3 +4835,6 @@ ow + toggle ?쒓컖 ?몄뼱濡??ㅼ떆 ?뺣젹?덈떎. - 업데이트: 2026-04-06 00:31 (KST) - [ChatWindow.xaml](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml) 의 상단 `채팅 / Cowork / 코드` 탭 그룹에서 각 버튼의 margin, padding, 최소 폭/높이와 바깥 래퍼의 padding, 최소 높이를 한 단계 더 줄였다. - 결과적으로 탭 그룹이 바깥 테두리를 거의 꽉 채우지 않고, pill 바깥선 안쪽에 숨 쉴 여백이 남는 레퍼런스형 비율로 정리됐다. +- 업데이트: 2026-04-06 00:38 (KST) + - [LlmService.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/LlmService.cs) 에서 내부 서비스(Ollama/vLLM) 모델 해석 경로를 보강했다. 현재 선택값이 alias 또는 등록 모델 키여도 `RegisteredModel`에서 실제 모델명을 다시 찾아 payload의 `model` 값으로 보내도록 정리했다. + - 같은 파일에 vLLM용 `max_tokens` 상한 보정 helper를 추가하고, [LlmService.ToolUse.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/LlmService.ToolUse.cs) 의 일반 도구 호출 / OpenAI-compatible tool body 생성에도 같은 값을 쓰게 맞췄다. 이로써 `Model Not Exist`, `invalid max_tokens` 계열 오류를 줄이는 방향으로 정리했다. diff --git a/src/AxCopilot/Services/LlmService.ToolUse.cs b/src/AxCopilot/Services/LlmService.ToolUse.cs index a1aa0cc..f366174 100644 --- a/src/AxCopilot/Services/LlmService.ToolUse.cs +++ b/src/AxCopilot/Services/LlmService.ToolUse.cs @@ -225,7 +225,7 @@ public partial class LlmService return new { model = activeModel, - max_tokens = Math.Max(llm.MaxContextTokens, 4096), + max_tokens = ResolveOpenAiCompatibleMaxTokens(), temperature = llm.Temperature, system = systemPrompt, messages = msgs, @@ -237,7 +237,7 @@ public partial class LlmService return new { model = activeModel, - max_tokens = Math.Max(llm.MaxContextTokens, 4096), + max_tokens = ResolveOpenAiCompatibleMaxTokens(), temperature = llm.Temperature, messages = msgs, tools = toolDefs, @@ -661,7 +661,7 @@ public partial class LlmService ["tools"] = toolDefs, ["stream"] = false, ["temperature"] = ResolveTemperature(), - ["max_tokens"] = llm.MaxContextTokens, + ["max_tokens"] = ResolveOpenAiCompatibleMaxTokens(), }; var effort = ResolveReasoningEffort(); if (!string.IsNullOrWhiteSpace(effort)) diff --git a/src/AxCopilot/Services/LlmService.cs b/src/AxCopilot/Services/LlmService.cs index 718f8b1..960ba2b 100644 --- a/src/AxCopilot/Services/LlmService.cs +++ b/src/AxCopilot/Services/LlmService.cs @@ -249,10 +249,32 @@ public partial class LlmService : IDisposable var llm = _settings.Settings.Llm; var service = NormalizeServiceName(llm.Service); if (service is "ollama" or "vllm" && !string.IsNullOrEmpty(llm.Model)) + { + var registered = FindRegisteredModel(llm, service, llm.Model); + if (registered != null) + { + var registeredModelName = CryptoService.DecryptIfEnabled(registered.EncryptedModelName, llm.EncryptionEnabled); + if (!string.IsNullOrWhiteSpace(registeredModelName)) + return registeredModelName; + } + return CryptoService.DecryptIfEnabled(llm.Model, llm.EncryptionEnabled); + } return llm.Model; } + private int ResolveOpenAiCompatibleMaxTokens() + { + var llm = _settings.Settings.Llm; + var requested = Math.Clamp(llm.MaxContextTokens, 1, 1_000_000); + var service = NormalizeServiceName(llm.Service); + + if (service == "vllm") + return Math.Min(requested, 8192); + + return requested; + } + /// /// 현재 활성 모델에 매칭되는 RegisteredModel을 찾아 엔드포인트/API키를 반환합니다. /// RegisteredModel에 전용 서버 정보가 있으면 그것을 사용하고, 없으면 기본 설정을 사용합니다. @@ -648,7 +670,7 @@ public partial class LlmService : IDisposable ["messages"] = msgs, ["stream"] = stream, ["temperature"] = ResolveTemperature(), - ["max_tokens"] = llm.MaxContextTokens + ["max_tokens"] = ResolveOpenAiCompatibleMaxTokens() }; var effort = ResolveReasoningEffort(); if (!string.IsNullOrWhiteSpace(effort))