diff --git a/README.md b/README.md
index 60fabe9..f898e5e 100644
--- a/README.md
+++ b/README.md
@@ -1286,3 +1286,6 @@ MIT License
- 업데이트: 2026-04-06 16:55 (KST)
- IBM/CP4D 계열 연결 점검 결과, 일부 환경은 `/icp4d-api/v1/authorize` 호출 시 `username + password`가 아니라 `username + api_key` JSON 본문을 요구하는 것을 확인했다.
- [Cp4dTokenService.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/Cp4dTokenService.cs) 에서 CP4D 토큰 요청을 먼저 `username + password`, 실패 시 `username + api_key`로 한 번 더 시도하도록 보강해, IBM 연결형 vLLM 환경 호환성을 높였다.
+- 업데이트: 2026-04-06 17:01 (KST)
+ - 모델 등록 단계에서 IBM/CP4D 인증 방식을 명확히 고를 수 있게 분기했다. [ModelRegistrationDialog.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ModelRegistrationDialog.cs)에 `CP4D (사용자 이름 + 비밀번호)`와 `CP4D (사용자 이름 + API 키)` 항목을 따로 추가하고, 선택에 따라 마지막 입력 필드 라벨이 `비밀번호` 또는 `API 키`로 바뀌도록 정리했다.
+ - [LlmService.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/LlmService.cs), [SettingsViewModel.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/ViewModels/SettingsViewModel.cs), [AppSettings.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Models/AppSettings.cs) 도 함께 갱신해 `cp4d_password`, `cp4d_api_key` 저장값을 공식 지원하고, 기존 `cp4d` 값은 비밀번호 방식으로 계속 호환되게 유지했다.
diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md
index 5cac9a6..985a55c 100644
--- a/docs/DEVELOPMENT.md
+++ b/docs/DEVELOPMENT.md
@@ -4980,3 +4980,5 @@ ow + toggle ?쒓컖 ?몄뼱濡??ㅼ떆 ?뺣젹?덈떎.
- Document update: 2026-04-06 16:44 (KST) - Moved the separator for the AX Agent internal-settings `운영 모드` block from the bottom edge to the top edge in `ChatWindow.xaml` so the line reads as the start of the operation-mode section rather than trailing under the previous storage block.
- Document update: 2026-04-06 16:49 (KST) - Removed the separator between the AX Agent internal-settings `서비스와 모델` block and the adjacent `등록 모델 관리` block in `ChatWindow.xaml` so the two model-management sections read as one continuous flow instead of two unrelated groups.
- Document update: 2026-04-06 16:55 (KST) - Hardened `Cp4dTokenService.cs` for IBM/CP4D-style token endpoints that expect `username + api_key` instead of `username + password`. The service now tries `username/password` first and automatically retries with `username/api_key` before failing.
+- Document update: 2026-04-06 17:01 (KST) - Split the CP4D auth choice at model-registration time. `ModelRegistrationDialog.cs` now exposes `CP4D (사용자 이름 + 비밀번호)` and `CP4D (사용자 이름 + API 키)` as separate auth types and changes the final credential label accordingly.
+- Document update: 2026-04-06 17:01 (KST) - Updated `LlmService.cs`, `SettingsViewModel.cs`, and `AppSettings.cs` to formally support `cp4d_password` and `cp4d_api_key` while preserving legacy `cp4d` values as the password-based path for backward compatibility.
diff --git a/src/AxCopilot/Models/AppSettings.cs b/src/AxCopilot/Models/AppSettings.cs
index 0ad32b7..e980f5b 100644
--- a/src/AxCopilot/Models/AppSettings.cs
+++ b/src/AxCopilot/Models/AppSettings.cs
@@ -1383,7 +1383,7 @@ public class RegisteredModel
// ── CP4D (IBM Cloud Pak for Data) 인증 ──────────────────────────────
- /// 인증 방식. bearer (기본) | ibm_iam | cp4d
+ /// 인증 방식. bearer (기본) | ibm_iam | cp4d_password | cp4d_api_key
[JsonPropertyName("authType")]
public string AuthType { get; set; } = "bearer";
@@ -1395,7 +1395,7 @@ public class RegisteredModel
[JsonPropertyName("cp4dUsername")]
public string Cp4dUsername { get; set; } = "";
- /// CP4D 비밀번호 또는 API 키 (EncryptionEnabled=true 시 암호화 저장)
+ /// CP4D 비밀번호 또는 API 키 (인증 방식에 따라 사용, EncryptionEnabled=true 시 암호화 저장)
[JsonPropertyName("cp4dPassword")]
public string Cp4dPassword { get; set; } = "";
}
diff --git a/src/AxCopilot/Services/LlmService.cs b/src/AxCopilot/Services/LlmService.cs
index d55556d..04bde33 100644
--- a/src/AxCopilot/Services/LlmService.cs
+++ b/src/AxCopilot/Services/LlmService.cs
@@ -344,7 +344,9 @@ public partial class LlmService : IDisposable
// CP4D 인증 방식인 경우
if (registered != null &&
- registered.AuthType.Equals("cp4d", StringComparison.OrdinalIgnoreCase) &&
+ (registered.AuthType.Equals("cp4d", StringComparison.OrdinalIgnoreCase) ||
+ registered.AuthType.Equals("cp4d_password", StringComparison.OrdinalIgnoreCase) ||
+ registered.AuthType.Equals("cp4d_api_key", StringComparison.OrdinalIgnoreCase)) &&
!string.IsNullOrWhiteSpace(registered.Cp4dUrl))
{
var password = CryptoService.DecryptIfEnabled(registered.Cp4dPassword, llm.EncryptionEnabled);
diff --git a/src/AxCopilot/ViewModels/SettingsViewModel.cs b/src/AxCopilot/ViewModels/SettingsViewModel.cs
index 5702360..8065281 100644
--- a/src/AxCopilot/ViewModels/SettingsViewModel.cs
+++ b/src/AxCopilot/ViewModels/SettingsViewModel.cs
@@ -2028,7 +2028,7 @@ public class RegisteredModelRow : INotifyPropertyChanged
private string _cp4dUsername = "";
private string _cp4dPassword = "";
- /// 인증 방식. bearer | ibm_iam | cp4d
+ /// 인증 방식. bearer | ibm_iam | cp4d_password | cp4d_api_key
public string AuthType
{
get => _authType;
@@ -2059,7 +2059,9 @@ public class RegisteredModelRow : INotifyPropertyChanged
/// 인증 방식 라벨
public string AuthLabel => (_authType ?? "bearer").ToLowerInvariant() switch
{
- "cp4d" => "CP4D",
+ "cp4d" => "CP4D 비밀번호",
+ "cp4d_password" => "CP4D 비밀번호",
+ "cp4d_api_key" => "CP4D API 키",
"ibm_iam" => "IBM IAM",
_ => "Bearer",
};
diff --git a/src/AxCopilot/Views/ModelRegistrationDialog.cs b/src/AxCopilot/Views/ModelRegistrationDialog.cs
index 47f95f2..652be88 100644
--- a/src/AxCopilot/Views/ModelRegistrationDialog.cs
+++ b/src/AxCopilot/Views/ModelRegistrationDialog.cs
@@ -272,13 +272,17 @@ internal sealed class ModelRegistrationDialog : Window
};
var bearerItem = new ComboBoxItem { Content = "Bearer 토큰 (API 키)", Tag = "bearer" };
var ibmIamItem = new ComboBoxItem { Content = "IBM IAM (토큰 교환)", Tag = "ibm_iam" };
- var cp4dItem = new ComboBoxItem { Content = "CP4D (IBM Cloud Pak for Data)", Tag = "cp4d" };
+ var cp4dPasswordItem = new ComboBoxItem { Content = "CP4D (사용자 이름 + 비밀번호)", Tag = "cp4d_password" };
+ var cp4dApiKeyItem = new ComboBoxItem { Content = "CP4D (사용자 이름 + API 키)", Tag = "cp4d_api_key" };
_authTypeBox.Items.Add(bearerItem);
_authTypeBox.Items.Add(ibmIamItem);
- _authTypeBox.Items.Add(cp4dItem);
+ _authTypeBox.Items.Add(cp4dPasswordItem);
+ _authTypeBox.Items.Add(cp4dApiKeyItem);
_authTypeBox.SelectedItem = existingAuthType switch
{
- "cp4d" => cp4dItem,
+ "cp4d" => cp4dPasswordItem,
+ "cp4d_password" => cp4dPasswordItem,
+ "cp4d_api_key" => cp4dApiKeyItem,
"ibm_iam" => ibmIamItem,
_ => bearerItem,
};
@@ -302,8 +306,9 @@ internal sealed class ModelRegistrationDialog : Window
apiKeyPanel.Children.Add(new Border { CornerRadius = new CornerRadius(8), ClipToBounds = true, Child = _apiKeyBox });
stack.Children.Add(apiKeyPanel);
- // ── CP4D 인증: URL + 사용자명 + 비밀번호 ────────────────────────────
- _cp4dPanel = new StackPanel { Visibility = existingAuthType == "cp4d" ? Visibility.Visible : Visibility.Collapsed };
+ // ── CP4D 인증: URL + 사용자명 + 비밀번호/API 키 ───────────────────────
+ var initialCp4dAuth = existingAuthType is "cp4d" or "cp4d_password" or "cp4d_api_key";
+ _cp4dPanel = new StackPanel { Visibility = initialCp4dAuth ? Visibility.Visible : Visibility.Collapsed };
_cp4dPanel.Children.Add(new TextBlock
{
@@ -340,12 +345,13 @@ internal sealed class ModelRegistrationDialog : Window
};
_cp4dPanel.Children.Add(new Border { CornerRadius = new CornerRadius(8), ClipToBounds = true, Child = _cp4dUsernameBox });
- _cp4dPanel.Children.Add(new TextBlock
+ var cp4dSecretLabel = new TextBlock
{
- Text = "비밀번호 / API 키",
+ Text = "비밀번호",
FontSize = 12, FontWeight = FontWeights.SemiBold,
Foreground = primaryText, Margin = new Thickness(0, 10, 0, 6),
- });
+ };
+ _cp4dPanel.Children.Add(cp4dSecretLabel);
_cp4dPasswordBox = new PasswordBox
{
Password = existingCp4dPassword,
@@ -358,14 +364,19 @@ internal sealed class ModelRegistrationDialog : Window
stack.Children.Add(_cp4dPanel);
// 인증 방식 전환 시 패널 표시/숨김
- _authTypeBox.SelectionChanged += (_, _) =>
+ void UpdateAuthPanels()
{
- var isCp4d = AuthType == "cp4d";
+ var isCp4d = AuthType is "cp4d" or "cp4d_password" or "cp4d_api_key";
_cp4dPanel.Visibility = isCp4d ? Visibility.Visible : Visibility.Collapsed;
apiKeyPanel.Visibility = isCp4d ? Visibility.Collapsed : Visibility.Visible;
+ cp4dSecretLabel.Text = AuthType == "cp4d_api_key" ? "API 키" : "비밀번호";
+ }
+ _authTypeBox.SelectionChanged += (_, _) =>
+ {
+ UpdateAuthPanels();
};
// 초기 상태 설정
- apiKeyPanel.Visibility = existingAuthType == "cp4d" ? Visibility.Collapsed : Visibility.Visible;
+ UpdateAuthPanels();
// 보안 안내
var securityNote = new StackPanel