Skip to content

Commit 2be4e9e

Browse files
fix(cli): allow minimal installs without a provider API key
Treat the minimal install profile as a provider-less bootstrap path so kagent install --profile minimal no longer blocks on OPENAI_API_KEY. Add a chart flag to skip rendering the default ModelConfig and enable it in the minimal profile so the installation does not create a broken default provider configuration. Refs #1622 Signed-off-by: Asish Kumar <officialasishkumar@gmail.com>
1 parent 85e038c commit 2be4e9e

File tree

10 files changed

+130
-20
lines changed

10 files changed

+130
-20
lines changed

go/core/cli/internal/cli/agent/install.go

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,24 @@ type InstallCfg struct {
2424
Profile string
2525
}
2626

27+
func resolveInstallProfile(profile string) string {
28+
profile = strings.TrimSpace(profile)
29+
if profile == "" {
30+
return ""
31+
}
32+
33+
if slices.Contains(profiles.Profiles, profile) {
34+
return profile
35+
}
36+
37+
fmt.Fprintf(os.Stderr, "Invalid --profile value (%s), defaulting to demo\n", profile)
38+
return profiles.ProfileDemo
39+
}
40+
41+
func shouldRequireProviderCredentials(profile string, modelProvider v1alpha2.ModelProvider) bool {
42+
return profiles.InstallsDefaultModelConfig(profile) && GetProviderAPIKey(modelProvider) != ""
43+
}
44+
2745
// installChart installs or upgrades a Helm chart with the given parameters
2846
func installChart(ctx context.Context, chartName string, namespace string, registry string, version string, setValues []string, inlineValues string) (string, error) {
2947
args := []string{
@@ -78,12 +96,12 @@ func InstallCmd(ctx context.Context, cfg *InstallCfg) *PortForward {
7896

7997
// get model provider from KAGENT_DEFAULT_MODEL_PROVIDER environment variable or use DefaultModelProvider
8098
modelProvider := GetModelProvider()
99+
selectedProfile := resolveInstallProfile(cfg.Profile)
81100

82-
// If model provider is openai, check if the API key is set
83101
apiKeyName := GetProviderAPIKey(modelProvider)
84102
apiKeyValue := os.Getenv(apiKeyName)
85103

86-
if apiKeyName != "" && apiKeyValue == "" {
104+
if shouldRequireProviderCredentials(selectedProfile, modelProvider) && apiKeyValue == "" {
87105
fmt.Fprintf(os.Stderr, "%s is not set\n", apiKeyName)
88106
fmt.Fprintf(os.Stderr, "Please set the %s environment variable\n", apiKeyName)
89107
return nil
@@ -92,13 +110,9 @@ func InstallCmd(ctx context.Context, cfg *InstallCfg) *PortForward {
92110
helmConfig := setupHelmConfig(modelProvider, apiKeyValue)
93111

94112
// setup profile if provided
95-
if cfg.Profile = strings.TrimSpace(cfg.Profile); cfg.Profile != "" {
96-
if !slices.Contains(profiles.Profiles, cfg.Profile) {
97-
fmt.Fprintf(os.Stderr, "Invalid --profile value (%s), defaulting to demo\n", cfg.Profile)
98-
cfg.Profile = profiles.ProfileDemo
99-
}
100-
101-
helmConfig.inlineValues = profiles.GetProfileYaml(cfg.Profile)
113+
if selectedProfile != "" {
114+
cfg.Profile = selectedProfile
115+
helmConfig.inlineValues = profiles.GetProfileYaml(selectedProfile)
102116
}
103117

104118
return install(ctx, cfg.Config, helmConfig, modelProvider)
@@ -120,22 +134,19 @@ func InteractiveInstallCmd(ctx context.Context, c *ishell.Context) *PortForward
120134
// get model provider from KAGENT_DEFAULT_MODEL_PROVIDER environment variable or use DefaultModelProvider
121135
modelProvider := GetModelProvider()
122136

123-
// if model provider is openai, check if the api key is set
137+
// Add profile selection
138+
profileIdx := c.MultiChoice(profiles.Profiles, "Select a profile:")
139+
selectedProfile := profiles.Profiles[profileIdx]
140+
124141
apiKeyName := GetProviderAPIKey(modelProvider)
125142
apiKeyValue := os.Getenv(apiKeyName)
126-
127-
if apiKeyName != "" && apiKeyValue == "" {
143+
if shouldRequireProviderCredentials(selectedProfile, modelProvider) && apiKeyValue == "" {
128144
fmt.Fprintf(os.Stderr, "%s is not set\n", apiKeyName)
129145
fmt.Fprintf(os.Stderr, "Please set the %s environment variable\n", apiKeyName)
130146
return nil
131147
}
132148

133149
helmConfig := setupHelmConfig(modelProvider, apiKeyValue)
134-
135-
// Add profile selection
136-
profileIdx := c.MultiChoice(profiles.Profiles, "Select a profile:")
137-
selectedProfile := profiles.Profiles[profileIdx]
138-
139150
helmConfig.inlineValues = profiles.GetProfileYaml(selectedProfile)
140151

141152
return install(ctx, cfg, helmConfig, modelProvider)
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package cli
2+
3+
import (
4+
"testing"
5+
6+
"github.com/kagent-dev/kagent/go/api/v1alpha2"
7+
"github.com/kagent-dev/kagent/go/core/cli/internal/profiles"
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
func TestResolveInstallProfile(t *testing.T) {
12+
t.Run("empty profile remains empty", func(t *testing.T) {
13+
assert.Equal(t, "", resolveInstallProfile(""))
14+
})
15+
16+
t.Run("valid profile is preserved", func(t *testing.T) {
17+
assert.Equal(t, profiles.ProfileMinimal, resolveInstallProfile(" minimal "))
18+
})
19+
20+
t.Run("invalid profile falls back to demo", func(t *testing.T) {
21+
assert.Equal(t, profiles.ProfileDemo, resolveInstallProfile("unknown"))
22+
})
23+
}
24+
25+
func TestShouldRequireProviderCredentials(t *testing.T) {
26+
tests := []struct {
27+
name string
28+
profile string
29+
modelProvider v1alpha2.ModelProvider
30+
want bool
31+
}{
32+
{
33+
name: "default install requires credentials for openai",
34+
profile: "",
35+
modelProvider: v1alpha2.ModelProviderOpenAI,
36+
want: true,
37+
},
38+
{
39+
name: "minimal install skips credentials for openai",
40+
profile: profiles.ProfileMinimal,
41+
modelProvider: v1alpha2.ModelProviderOpenAI,
42+
want: false,
43+
},
44+
{
45+
name: "demo install still requires credentials for anthropic",
46+
profile: profiles.ProfileDemo,
47+
modelProvider: v1alpha2.ModelProviderAnthropic,
48+
want: true,
49+
},
50+
{
51+
name: "ollama never requires credentials",
52+
profile: profiles.ProfileDemo,
53+
modelProvider: v1alpha2.ModelProviderOllama,
54+
want: false,
55+
},
56+
}
57+
58+
for _, tt := range tests {
59+
t.Run(tt.name, func(t *testing.T) {
60+
assert.Equal(t, tt.want, shouldRequireProviderCredentials(tt.profile, tt.modelProvider))
61+
})
62+
}
63+
}

go/core/cli/internal/profiles/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ KAgent's profiles provide a simpler way to set up KAgent in a configured way bas
44

55
Currently, there are two profiles:
66
1. `Demo`: For an installation of kagent that includes all our agents. This is useful for demo purposes and new users.
7-
2. `Minimal`: (default) For an installation that does not include any pre-defined agent. This is useful for users who want to start from scratch.
7+
2. `Minimal`: (default) For an installation that does not include any pre-defined agent or a default model configuration. This is useful for users who want to start from scratch.
88

99
**Important**: When adding a new profile or updating a name, make sure to update the proper embeddings for it.

go/core/cli/internal/profiles/minimal.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# The minimal profile does not install any agents, and is meant as a bare minimum installation for kagent.
22
# This is useful for users who only want to set up kagent without any extra agents.
3+
providers:
4+
createDefaultModelConfig: false
5+
36
agents:
47
k8s-agent:
58
enabled: false

go/core/cli/internal/profiles/profiles.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,12 @@ func GetProfileYaml(profile string) string {
2525
return DemoProfileYaml
2626
}
2727
}
28+
29+
func InstallsDefaultModelConfig(profile string) bool {
30+
switch profile {
31+
case ProfileMinimal:
32+
return false
33+
default:
34+
return true
35+
}
36+
}

helm/kagent/templates/modelconfig-secret.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
{{- if ne .Values.providers.createDefaultModelConfig false }}
12
{{- $dot := . }}
23
{{- $model := index $dot.Values.providers $dot.Values.providers.default }}
34
{{- if and $model.apiKeySecretRef $model.apiKey }}
@@ -13,3 +14,4 @@ type: Opaque
1314
data:
1415
{{ $model.apiKeySecretKey | default (printf "%s_API_KEY" $model.provider | upper) }}: {{ $model.apiKey | b64enc }}
1516
{{- end }}
17+
{{- end }}

helm/kagent/templates/modelconfig.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
{{- if ne .Values.providers.createDefaultModelConfig false }}
12
{{- $dot := . }}
23
{{- $defaultProfider := .Values.providers.default | default "openAI" }}
34
{{- $model := index .Values.providers $defaultProfider }}
@@ -31,3 +32,4 @@ spec:
3132
{{- toYaml $model.config | nindent 4 }}
3233
{{- end }}
3334
{{- end }}
35+
{{- end }}

helm/kagent/tests/modelconfig-secret_test.yaml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,4 +99,14 @@ tests:
9999
asserts:
100100
- equal:
101101
path: metadata.namespace
102-
value: custom-namespace
102+
value: custom-namespace
103+
104+
- it: should not render secret when default modelconfig is disabled
105+
set:
106+
providers:
107+
createDefaultModelConfig: false
108+
openAI:
109+
apiKey: "test-key"
110+
asserts:
111+
- hasDocuments:
112+
count: 0

helm/kagent/tests/modelconfig_test.yaml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,4 +113,12 @@ tests:
113113
asserts:
114114
- equal:
115115
path: metadata.namespace
116-
value: custom-namespace
116+
value: custom-namespace
117+
118+
- it: should not render modelconfig when disabled
119+
set:
120+
providers:
121+
createDefaultModelConfig: false
122+
asserts:
123+
- hasDocuments:
124+
count: 0

helm/kagent/values.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,8 @@ ui:
266266
# https://kagent.dev/docs/getting-started/configuring-providers
267267

268268
providers:
269+
# -- Create the default ModelConfig resource during installation.
270+
createDefaultModelConfig: true
269271
default: openAI
270272
openAI:
271273
provider: OpenAI

0 commit comments

Comments
 (0)