Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions api/v1alpha1/olsconfig_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,9 +251,6 @@ type OLSSpec struct {
// +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Tool Filtering Configuration",xDescriptors={"urn:alm:descriptor:com.tectonic.ui:advanced"}
ToolFilteringConfig *ToolFilteringConfig `json:"toolFilteringConfig,omitempty"`
// Tool execution approval configuration. Controls whether tool calls require user approval before execution.
// ⚠️ WARNING: This feature is not yet fully supported in the current OLS backend version.
// The operator will generate the configuration, but tool approval behavior may not function as expected.
// Please verify backend support before enabling.
// +kubebuilder:validation:Optional
// +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Tools Approval Configuration",xDescriptors={"urn:alm:descriptor:com.tectonic.ui:advanced"}
ToolsApprovalConfig *ToolsApprovalConfig `json:"toolsApprovalConfig,omitempty"`
Expand Down Expand Up @@ -646,7 +643,7 @@ type ToolsApprovalConfig struct {
// 'never' - tools execute without approval
// 'always' - all tool calls require approval
// 'tool_annotations' - approval based on per-tool annotations
// +kubebuilder:default=never
// +kubebuilder:default=tool_annotations
// +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Approval Type"
ApprovalType ApprovalType `json:"approvalType,omitempty"`

Expand Down
13 changes: 4 additions & 9 deletions bundle/manifests/lightspeed-operator.clusterserviceversion.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ metadata:
]
capabilities: Seamless Upgrades
console.openshift.io/operator-monitoring-default: "true"
createdAt: "2026-04-09T11:46:41Z"
createdAt: "2026-04-10T08:19:49Z"
features.operators.openshift.io/cnf: "false"
features.operators.openshift.io/cni: "false"
features.operators.openshift.io/csi: "false"
Expand All @@ -55,7 +55,7 @@ metadata:
operators.operatorframework.io/builder: operator-sdk-v1.36.1
operators.operatorframework.io/project_layout: go.kubebuilder.io/v4
repository: https://github.com/openshift/lightspeed-operator
name: lightspeed-operator.v1.0.11
name: lightspeed-operator.v1.0.10
namespace: openshift-lightspeed
spec:
apiservicedefinitions: {}
Expand Down Expand Up @@ -383,7 +383,6 @@ spec:
- tls.key: Private key (PEM format) - REQUIRED
- ca.crt: CA certificate for console proxy trust (PEM format) - OPTIONAL


If ca.crt is not provided, the OpenShift Console proxy will use the default system trust store.
displayName: TLS Certificate Secret Reference
path: ols.tlsConfig.keyCertSecretRef
Expand All @@ -406,11 +405,7 @@ spec:
- description: Number of tools to retrieve
displayName: Top K
path: ols.toolFilteringConfig.topK
- description: |-
Tool execution approval configuration. Controls whether tool calls require user approval before execution.
⚠️ WARNING: This feature is not yet fully supported in the current OLS backend version.
The operator will generate the configuration, but tool approval behavior may not function as expected.
Please verify backend support before enabling.
- description: Tool execution approval configuration. Controls whether tool calls require user approval before execution.
displayName: Tools Approval Configuration
path: ols.toolsApprovalConfig
x-descriptors:
Expand Down Expand Up @@ -1000,7 +995,7 @@ spec:
provider:
name: Red Hat, Inc
url: https://github.com/openshift/lightspeed-service
version: 1.0.11
version: 1.0.10
relatedImages:
- name: lightspeed-service-api
image: registry.redhat.io/openshift-lightspeed/lightspeed-service-api-rhel9@sha256:7314d1c8cf1469f558376439f6c17713238e5676d7808e07d8183516b1e1a057
Expand Down
9 changes: 3 additions & 6 deletions bundle/manifests/ols.openshift.io_olsconfigs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4728,19 +4728,16 @@ spec:
- message: threshold must be between 0.0 and 1.0
rule: self.threshold >= 0.0 && self.threshold <= 1.0
toolsApprovalConfig:
description: |-
Tool execution approval configuration. Controls whether tool calls require user approval before execution.
⚠️ WARNING: This feature is not yet fully supported in the current OLS backend version.
The operator will generate the configuration, but tool approval behavior may not function as expected.
Please verify backend support before enabling.
description: Tool execution approval configuration. Controls whether
tool calls require user approval before execution.
properties:
approvalTimeout:
default: 600
description: Timeout in seconds for waiting for user approval
minimum: 1
type: integer
approvalType:
default: never
default: tool_annotations
description: |-
Approval strategy for tool execution.
'never' - tools execute without approval
Expand Down
9 changes: 3 additions & 6 deletions config/crd/bases/ols.openshift.io_olsconfigs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4728,19 +4728,16 @@ spec:
- message: threshold must be between 0.0 and 1.0
rule: self.threshold >= 0.0 && self.threshold <= 1.0
toolsApprovalConfig:
description: |-
Tool execution approval configuration. Controls whether tool calls require user approval before execution.
⚠️ WARNING: This feature is not yet fully supported in the current OLS backend version.
The operator will generate the configuration, but tool approval behavior may not function as expected.
Please verify backend support before enabling.
description: Tool execution approval configuration. Controls whether
tool calls require user approval before execution.
properties:
approvalTimeout:
default: 600
description: Timeout in seconds for waiting for user approval
minimum: 1
type: integer
approvalType:
default: never
default: tool_annotations
description: |-
Approval strategy for tool execution.
'never' - tools execute without approval
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -382,11 +382,8 @@ spec:
- description: Number of tools to retrieve
displayName: Top K
path: ols.toolFilteringConfig.topK
- description: |-
Tool execution approval configuration. Controls whether tool calls require user approval before execution.
⚠️ WARNING: This feature is not yet fully supported in the current OLS backend version.
The operator will generate the configuration, but tool approval behavior may not function as expected.
Please verify backend support before enabling.
- description: Tool execution approval configuration. Controls whether tool
calls require user approval before execution.
displayName: Tools Approval Configuration
path: ols.toolsApprovalConfig
x-descriptors:
Expand Down
33 changes: 19 additions & 14 deletions internal/controller/appserver/assets.go
Original file line number Diff line number Diff line change
Expand Up @@ -492,25 +492,30 @@ func GenerateOLSConfigMap(r reconciler.Reconciler, ctx context.Context, cr *olsv
appSrvConfigFile.OLSConfig.ToolFiltering = toolFilteringConfig
}

// Add tools approval configuration if specified
if cr.Spec.OLSConfig.ToolsApprovalConfig != nil {
// Apply defaults for zero values (happens when user specifies toolsApprovalConfig: {})
cfg := cr.Spec.OLSConfig.ToolsApprovalConfig
approvalType := string(cfg.ApprovalType)
approvalTimeout := cfg.ApprovalTimeout

// Apply defaults if not set
// Add tools approval configuration (always present with defaults from CRD)
var approvalType string
var approvalTimeout int

if cr.Spec.OLSConfig.ToolsApprovalConfig == nil {
// Use CRD defaults (must match +kubebuilder:default markers in ToolsApprovalConfig)
approvalType = string(olsv1alpha1.ApprovalTypeToolAnnotations) // CRD default: tool_annotations
approvalTimeout = utils.ToolsApprovalDefaultTimeout // CRD default: 600
} else {
// Use specified values, applying CRD defaults for zero values
approvalType = string(cr.Spec.OLSConfig.ToolsApprovalConfig.ApprovalType)
approvalTimeout = cr.Spec.OLSConfig.ToolsApprovalConfig.ApprovalTimeout

if approvalType == "" {
approvalType = string(olsv1alpha1.ApprovalTypeNever)
approvalType = string(olsv1alpha1.ApprovalTypeToolAnnotations) // CRD default: tool_annotations
}
if approvalTimeout == 0 {
approvalTimeout = 600
approvalTimeout = utils.ToolsApprovalDefaultTimeout // CRD default: 600
}
}

appSrvConfigFile.OLSConfig.ToolsApproval = &utils.ToolsApprovalConfig{
ApprovalType: approvalType,
ApprovalTimeout: approvalTimeout,
}
appSrvConfigFile.OLSConfig.ToolsApproval = &utils.ToolsApprovalConfig{
ApprovalType: approvalType,
ApprovalTimeout: approvalTimeout,
}

// Marshal the configuration to YAML format
Expand Down
29 changes: 20 additions & 9 deletions internal/controller/appserver/assets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ var _ = Describe("App server assets", func() {
"/etc/certs/ols-additional-ca/service-ca.crt",
},
CertificateDirectory: "/etc/certs/cert-bundle",
ToolsApproval: &utils.ToolsApprovalConfig{
ApprovalType: "tool_annotations",
ApprovalTimeout: utils.ToolsApprovalDefaultTimeout,
},
},
LLMProviders: []utils.ProviderConfig{
{
Expand Down Expand Up @@ -225,7 +229,7 @@ var _ = Describe("App server assets", func() {
err = yaml.Unmarshal([]byte(cm.Data[utils.OLSConfigFilename]), &olsConfigMap)
Expect(err).NotTo(HaveOccurred())
Expect(olsConfigMap).To(HaveKeyWithValue("ols_config", HaveKeyWithValue("tools_approval", MatchAllKeys(Keys{
"approval_type": Equal("never"),
"approval_type": Equal("tool_annotations"),
"approval_timeout": BeNumerically("==", 600),
}))))

Expand Down Expand Up @@ -257,15 +261,16 @@ var _ = Describe("App server assets", func() {
"approval_timeout": BeNumerically("==", 120),
}))))

By("not present when config is nil")
By("with default values when config is nil")
cr.Spec.OLSConfig.ToolsApprovalConfig = nil
cm, err = GenerateOLSConfigMap(testReconcilerInstance, context.TODO(), cr)
Expect(err).NotTo(HaveOccurred())
err = yaml.Unmarshal([]byte(cm.Data[utils.OLSConfigFilename]), &olsConfigMap)
Expect(err).NotTo(HaveOccurred())
olsConfig, ok := olsConfigMap["ols_config"].(map[string]interface{})
Expect(ok).To(BeTrue())
Expect(olsConfig).NotTo(HaveKey("tools_approval"))
Expect(olsConfigMap).To(HaveKeyWithValue("ols_config", HaveKeyWithValue("tools_approval", MatchAllKeys(Keys{
"approval_type": Equal("tool_annotations"),
"approval_timeout": BeNumerically("==", 600),
}))))
})

It("should generate configmap with token quota limiters", func() {
Expand Down Expand Up @@ -1286,6 +1291,9 @@ ols_config:
tls_config:
tls_certificate_path: /etc/certs/lightspeed-tls/tls.crt
tls_key_path: /etc/certs/lightspeed-tls/tls.key
tools_approval:
approval_timeout: 600
approval_type: tool_annotations
user_data_collection:
feedback_disabled: false
feedback_storage: /app-root/ols-user-data/feedback
Expand Down Expand Up @@ -1346,6 +1354,9 @@ ols_config:
tls_config:
tls_certificate_path: /etc/certs/lightspeed-tls/tls.crt
tls_key_path: /etc/certs/lightspeed-tls/tls.key
tools_approval:
approval_timeout: 600
approval_type: tool_annotations
user_data_collection:
feedback_disabled: true
feedback_storage: /app-root/ols-user-data/feedback
Expand Down Expand Up @@ -2045,11 +2056,11 @@ var _ = Describe("Helper function unit tests", func() {
It("should return error when proxy CA certificate ConfigMap does not exist", func() {
cr.Spec.OLSConfig.ProxyConfig = &olsv1alpha1.ProxyConfig{
ProxyURL: "http://proxy.example.com:8080",
ProxyCACertificateRef: &olsv1alpha1.ProxyCACertConfigMapRef{
LocalObjectReference: corev1.LocalObjectReference{
Name: "nonexistent-proxy-ca",
ProxyCACertificateRef: &olsv1alpha1.ProxyCACertConfigMapRef{
LocalObjectReference: corev1.LocalObjectReference{
Name: "nonexistent-proxy-ca",
},
},
},
}
// Don't create the ConfigMap - validation should fail
_, err := buildOLSConfig(testReconcilerInstance, ctx, cr, false)
Expand Down
30 changes: 16 additions & 14 deletions internal/controller/lcore/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -894,22 +894,24 @@ func buildLCoreQuotaHandlersConfig(r reconciler.Reconciler, cr *olsv1alpha1.OLSC
// buildLCoreToolsApprovalConfig configures tool execution approval
// Controls whether tool calls require user approval before execution
func buildLCoreToolsApprovalConfig(_ reconciler.Reconciler, cr *olsv1alpha1.OLSConfig) map[string]interface{} {
// If no tools approval config in CR, return nil (not configured)
if cr.Spec.OLSConfig.ToolsApprovalConfig == nil {
return nil
}
var approvalType string
var approvalTimeout int

cfg := cr.Spec.OLSConfig.ToolsApprovalConfig

// Apply defaults if not set
approvalType := string(cfg.ApprovalType)
if approvalType == "" {
approvalType = string(olsv1alpha1.ApprovalTypeNever)
}
if cr.Spec.OLSConfig.ToolsApprovalConfig == nil {
// Use CRD defaults (must match +kubebuilder:default markers in ToolsApprovalConfig)
approvalType = string(olsv1alpha1.ApprovalTypeToolAnnotations) // CRD default: tool_annotations
approvalTimeout = utils.ToolsApprovalDefaultTimeout // CRD default: 600
} else {
// Use specified values, applying CRD defaults for zero values
approvalType = string(cr.Spec.OLSConfig.ToolsApprovalConfig.ApprovalType)
approvalTimeout = cr.Spec.OLSConfig.ToolsApprovalConfig.ApprovalTimeout

approvalTimeout := cfg.ApprovalTimeout
if approvalTimeout == 0 {
approvalTimeout = 600
if approvalType == "" {
approvalType = string(olsv1alpha1.ApprovalTypeToolAnnotations) // CRD default: tool_annotations
}
if approvalTimeout == 0 {
approvalTimeout = utils.ToolsApprovalDefaultTimeout // CRD default: 600
}
}

return map[string]interface{}{
Expand Down
38 changes: 32 additions & 6 deletions internal/controller/lcore/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,7 @@ func TestBuildLCoreToolsApprovalConfig_WithConfig(t *testing.T) {
{
name: "with empty config (defaults applied)",
config: &olsv1alpha1.ToolsApprovalConfig{},
expectedType: "never",
expectedType: "tool_annotations",
expectedTimeout: 600,
},
}
Expand Down Expand Up @@ -563,8 +563,24 @@ func TestBuildLCoreToolsApprovalConfig_WithoutConfig(t *testing.T) {

result := buildLCoreToolsApprovalConfig(r, cr)

if result != nil {
t.Errorf("Expected nil result when ToolsApprovalConfig is nil, got %v", result)
if result == nil {
t.Fatal("Expected non-nil result when ToolsApprovalConfig is nil")
}

approvalType, ok := result["approval_type"].(string)
if !ok {
t.Fatal("Expected approval_type to be string")
}
if approvalType != "tool_annotations" {
t.Errorf("Expected approval_type %q, got %q", "tool_annotations", approvalType)
}

approvalTimeout, ok := result["approval_timeout"].(int)
if !ok {
t.Fatal("Expected approval_timeout to be int")
}
if approvalTimeout != 600 {
t.Errorf("Expected approval_timeout %d, got %d", 600, approvalTimeout)
}
}

Expand Down Expand Up @@ -625,8 +641,18 @@ func TestBuildLCoreConfigYAML_WithoutToolsApproval(t *testing.T) {
t.Fatalf("buildLCoreConfigYAML returned error: %v", err)
}

// Verify YAML does NOT contain tools_approval section
if strings.Contains(yamlStr, "tools_approval:") {
t.Error("Expected YAML NOT to contain 'tools_approval:' section when not configured")
// Verify YAML contains tools_approval section with defaults
if !strings.Contains(yamlStr, "tools_approval:") {
t.Error("Expected YAML to contain 'tools_approval:' section with defaults when not configured")
}

// Verify default approval_type is present
if !strings.Contains(yamlStr, "approval_type: tool_annotations") {
t.Error("Expected YAML to contain 'approval_type: tool_annotations' as default")
}

// Verify default approval_timeout is present
if !strings.Contains(yamlStr, "approval_timeout: 600") {
t.Error("Expected YAML to contain 'approval_timeout: 600' as default")
}
}
2 changes: 2 additions & 0 deletions internal/controller/utils/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,8 @@ ssl_ca_file = '/etc/certs/cm-olspostgresca/service-ca.crt'
OpenShiftMCPServerTimeout = 60
// MCP server SSE read timeout, sec
OpenShiftMCPServerHTTPReadTimeout = 30
// Tools approval timeout default, sec (must match +kubebuilder:default in ToolsApprovalConfig)
ToolsApprovalDefaultTimeout = 600
// Authorization header for OpenShift MCP server
K8S_AUTH_HEADER = "Authorization"
// Constant, defining usage of kubernetes token
Expand Down