From 22ab0b50921d003c29e86079c5652556ad45c7d1 Mon Sep 17 00:00:00 2001 From: Omkar Joshi <103182931+omkarjoshi0304@users.noreply.github.com> Date: Fri, 14 Aug 2026 12:13:37 +0100 Subject: [PATCH 1/2] Regenerate CRD manifests to pick up updated k8s.io/api doc comment Unrelated to the Anthropic changes; just re-syncs generated output with the currently pinned k8s.io/api version. Co-authored-by: Cursor --- ...ightspeed.openstack.org_openstacklightspeeds.yaml | 12 ++++++------ ...ightspeed.openstack.org_openstacklightspeeds.yaml | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/bundle/manifests/lightspeed.openstack.org_openstacklightspeeds.yaml b/bundle/manifests/lightspeed.openstack.org_openstacklightspeeds.yaml index ef72ab9..f107cfb 100644 --- a/bundle/manifests/lightspeed.openstack.org_openstacklightspeeds.yaml +++ b/bundle/manifests/lightspeed.openstack.org_openstacklightspeeds.yaml @@ -206,7 +206,7 @@ spec: Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. - This field depends on the + This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. This field is immutable. It can only be set for containers. @@ -274,7 +274,7 @@ spec: Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. - This field depends on the + This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. This field is immutable. It can only be set for containers. @@ -342,7 +342,7 @@ spec: Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. - This field depends on the + This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. This field is immutable. It can only be set for containers. @@ -409,7 +409,7 @@ spec: Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. - This field depends on the + This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. This field is immutable. It can only be set for containers. @@ -476,7 +476,7 @@ spec: Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. - This field depends on the + This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. This field is immutable. It can only be set for containers. @@ -543,7 +543,7 @@ spec: Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. - This field depends on the + This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. This field is immutable. It can only be set for containers. diff --git a/config/crd/bases/lightspeed.openstack.org_openstacklightspeeds.yaml b/config/crd/bases/lightspeed.openstack.org_openstacklightspeeds.yaml index 52dd00d..59837dc 100644 --- a/config/crd/bases/lightspeed.openstack.org_openstacklightspeeds.yaml +++ b/config/crd/bases/lightspeed.openstack.org_openstacklightspeeds.yaml @@ -206,7 +206,7 @@ spec: Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. - This field depends on the + This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. This field is immutable. It can only be set for containers. @@ -274,7 +274,7 @@ spec: Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. - This field depends on the + This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. This field is immutable. It can only be set for containers. @@ -342,7 +342,7 @@ spec: Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. - This field depends on the + This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. This field is immutable. It can only be set for containers. @@ -409,7 +409,7 @@ spec: Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. - This field depends on the + This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. This field is immutable. It can only be set for containers. @@ -476,7 +476,7 @@ spec: Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. - This field depends on the + This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. This field is immutable. It can only be set for containers. @@ -543,7 +543,7 @@ spec: Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. - This field depends on the + This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. This field is immutable. It can only be set for containers. From 1c57a6b70513e2c42762241ce58415e4953db0d3 Mon Sep 17 00:00:00 2001 From: Omkar Joshi <103182931+omkarjoshi0304@users.noreply.github.com> Date: Fri, 14 Aug 2026 12:13:37 +0100 Subject: [PATCH 2/2] Add Anthropic as a supported LLM provider Adds anthropic to the LLMEndpointType enum and generates a remote::anthropic inference provider config. Co-authored-by: Cursor --- api/v1beta1/openstacklightspeed_types.go | 2 +- ...ed.openstack.org_openstacklightspeeds.yaml | 1 + ...ed.openstack.org_openstacklightspeeds.yaml | 1 + internal/controller/constants.go | 4 ++++ internal/controller/llama_stack_config.go | 22 +++++++++++++------ .../controller/llama_stack_config_test.go | 10 +++++++++ 6 files changed, 32 insertions(+), 8 deletions(-) diff --git a/api/v1beta1/openstacklightspeed_types.go b/api/v1beta1/openstacklightspeed_types.go index db81b28..c31c988 100644 --- a/api/v1beta1/openstacklightspeed_types.go +++ b/api/v1beta1/openstacklightspeed_types.go @@ -206,7 +206,7 @@ type OpenStackLightspeedCore struct { LLMEndpoint string `json:"llmEndpoint"` // +kubebuilder:validation:Required - // +kubebuilder:validation:Enum=azure_openai;openai;watsonx;rhoai_vllm;rhelai_vllm;gemini + // +kubebuilder:validation:Enum=azure_openai;openai;watsonx;rhoai_vllm;rhelai_vllm;gemini;anthropic // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Provider Type" // Type of the provider serving the LLM LLMEndpointType string `json:"llmEndpointType"` diff --git a/bundle/manifests/lightspeed.openstack.org_openstacklightspeeds.yaml b/bundle/manifests/lightspeed.openstack.org_openstacklightspeeds.yaml index f107cfb..0e416b0 100644 --- a/bundle/manifests/lightspeed.openstack.org_openstacklightspeeds.yaml +++ b/bundle/manifests/lightspeed.openstack.org_openstacklightspeeds.yaml @@ -105,6 +105,7 @@ spec: - rhoai_vllm - rhelai_vllm - gemini + - anthropic type: string llmProjectID: description: Project ID for LLM providers that require it (e.g., WatsonX) diff --git a/config/crd/bases/lightspeed.openstack.org_openstacklightspeeds.yaml b/config/crd/bases/lightspeed.openstack.org_openstacklightspeeds.yaml index 59837dc..49edd5a 100644 --- a/config/crd/bases/lightspeed.openstack.org_openstacklightspeeds.yaml +++ b/config/crd/bases/lightspeed.openstack.org_openstacklightspeeds.yaml @@ -105,6 +105,7 @@ spec: - rhoai_vllm - rhelai_vllm - gemini + - anthropic type: string llmProjectID: description: Project ID for LLM providers that require it (e.g., WatsonX) diff --git a/internal/controller/constants.go b/internal/controller/constants.go index 4e60dfe..a98d1f0 100644 --- a/internal/controller/constants.go +++ b/internal/controller/constants.go @@ -163,6 +163,7 @@ const ( RHELAIVLLMProviderName = "rhelai_vllm" RHOAIVLLMProviderName = "rhoai_vllm" GeminiProviderName = "gemini" + AnthropicProviderName = "anthropic" AzureOpenAIProviderName = "azure_openai" OpenAIProviderName = "openai" WatsonXProviderName = "watsonx" @@ -188,6 +189,9 @@ const ( // EnvVarSuffixAPIKey is the environment variable suffix for API key credentials EnvVarSuffixAPIKey = "_API_KEY" + // APIKeyConfigField is the Llama Stack provider config field name for API key credentials + APIKeyConfigField = "api_key" + // VectorDBVolumeName is the name of the volume used by init containers to // store discovered values from vector DB images. VectorDBVolumeName = "vector-db-discovered-values" diff --git a/internal/controller/llama_stack_config.go b/internal/controller/llama_stack_config.go index be8c3c6..2adc3ab 100644 --- a/internal/controller/llama_stack_config.go +++ b/internal/controller/llama_stack_config.go @@ -123,7 +123,7 @@ func buildLlamaStackInferenceProviders(_ *common_helper.Helper, _ context.Contex // Map provider types to Llama Stack provider types switch provider.Type { - case OpenAIProviderName, GeminiProviderName, RHOAIVLLMProviderName, RHELAIVLLMProviderName: + case OpenAIProviderName, GeminiProviderName, AnthropicProviderName, RHOAIVLLMProviderName, RHELAIVLLMProviderName: config := map[string]interface{}{} // Determine the appropriate Llama Stack provider type: // - OpenAI uses remote::openai @@ -132,10 +132,13 @@ func buildLlamaStackInferenceProviders(_ *common_helper.Helper, _ context.Contex switch provider.Type { case OpenAIProviderName: providerConfig["provider_type"] = "remote::openai" - apiKeyField = "api_key" + apiKeyField = APIKeyConfigField case GeminiProviderName: providerConfig["provider_type"] = "remote::gemini" - apiKeyField = "api_key" + apiKeyField = APIKeyConfigField + case AnthropicProviderName: + providerConfig["provider_type"] = "remote::anthropic" + apiKeyField = APIKeyConfigField default: providerConfig["provider_type"] = "remote::vllm" apiKeyField = "api_token" @@ -143,7 +146,12 @@ func buildLlamaStackInferenceProviders(_ *common_helper.Helper, _ context.Contex // Llama Stack will substitute ${env.VAR_NAME} with the actual env var value config[apiKeyField] = fmt.Sprintf("${env.%s%s}", envVarName, EnvVarSuffixAPIKey) - // Add custom URL if specified + // Add custom URL if specified. Note: remote::gemini and remote::anthropic + // have fixed, well-known API endpoints and their Llama Stack config schemas + // don't define a base_url field at all, so setting llmEndpoint for those + // providers has no effect on the generated config - Llama Stack silently + // ignores it on its end. That's upstream (llama-stack/OGX) behavior, not + // something this operator works around. if provider.URL != "" { config["base_url"] = provider.URL } @@ -156,7 +164,7 @@ func buildLlamaStackInferenceProviders(_ *common_helper.Helper, _ context.Contex // Azure supports both API key and client credentials authentication // Always include api_key (required by LiteLLM's Pydantic validation) - config["api_key"] = fmt.Sprintf("${env.%s_API_KEY}", envVarName) + config[APIKeyConfigField] = fmt.Sprintf("${env.%s_API_KEY}", envVarName) // Also include client credentials fields (will be empty if not using client credentials) config["client_id"] = fmt.Sprintf("${env.%s_CLIENT_ID:=}", envVarName) @@ -180,7 +188,7 @@ func buildLlamaStackInferenceProviders(_ *common_helper.Helper, _ context.Contex config := map[string]interface{}{} config["base_url"] = provider.URL - config["api_key"] = fmt.Sprintf("${env.%s_API_KEY}", envVarName) + config[APIKeyConfigField] = fmt.Sprintf("${env.%s_API_KEY}", envVarName) if provider.WatsonProjectID != "" { config["project_id"] = provider.WatsonProjectID @@ -190,7 +198,7 @@ func buildLlamaStackInferenceProviders(_ *common_helper.Helper, _ context.Contex default: supportedProviders := []string{ - OpenAIProviderName, GeminiProviderName, RHOAIVLLMProviderName, RHELAIVLLMProviderName, + OpenAIProviderName, GeminiProviderName, AnthropicProviderName, RHOAIVLLMProviderName, RHELAIVLLMProviderName, AzureOpenAIProviderName, WatsonXProviderName, } return nil, fmt.Errorf( diff --git a/internal/controller/llama_stack_config_test.go b/internal/controller/llama_stack_config_test.go index 7169455..8e0df15 100644 --- a/internal/controller/llama_stack_config_test.go +++ b/internal/controller/llama_stack_config_test.go @@ -35,6 +35,10 @@ func getOpenStackLightspeedProvidersInstance(provider string) *apiv1beta1.OpenSt instance.Spec.LLMEndpointType = GeminiProviderName instance.Spec.ModelName = "gemini-2.0-flash" return instance + case AnthropicProviderName: + instance.Spec.LLMEndpointType = AnthropicProviderName + instance.Spec.ModelName = "claude-sonnet-4-5" + return instance case RHOAIVLLMProviderName: instance.Spec.LLMEndpointType = RHOAIVLLMProviderName instance.Spec.LLMEndpoint = "https://vllm.example.com/v1" @@ -100,6 +104,11 @@ var _ = Describe("Llama Stack config", func() { Expect(config["api_key"]).To(Equal("${env.OPENSTACK_LIGHTSPEED_PROVIDER_API_KEY}")) Expect(config).NotTo(HaveKey("base_url")) }), + Entry("for anthropic", AnthropicProviderName, "remote::anthropic", + func(config map[string]interface{}, _ *apiv1beta1.OpenStackLightspeed) { + Expect(config["api_key"]).To(Equal("${env.OPENSTACK_LIGHTSPEED_PROVIDER_API_KEY}")) + Expect(config).NotTo(HaveKey("base_url")) + }), Entry("for rhoai_vllm", RHOAIVLLMProviderName, "remote::vllm", func(config map[string]interface{}, instance *apiv1beta1.OpenStackLightspeed) { Expect(config["api_token"]).To(Equal("${env.OPENSTACK_LIGHTSPEED_PROVIDER_API_KEY}")) @@ -147,6 +156,7 @@ var _ = Describe("Llama Stack config", func() { }, Entry("for openai", OpenAIProviderName), Entry("for gemini", GeminiProviderName), + Entry("for anthropic", AnthropicProviderName), Entry("for rhoai_vllm", RHOAIVLLMProviderName), Entry("for rhelai_vllm", RHELAIVLLMProviderName), Entry("for azure_openai", AzureOpenAIProviderName),