Skip to content
Closed
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
10 changes: 9 additions & 1 deletion internal/controller/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ const (
OpenStackLightspeedAppServerNetworkPolicyName = "lightspeed-app-server"
OpenStackLightspeedCertsSecretName = "lightspeed-tls"
OpenStackLightspeedDefaultProvider = "openstack-lightspeed-provider"
OpenStackLightspeedVectorDBPath = "/rag/vector_db/os_product_docs"
RAGInitCopySubPath = "rag-0"
RAGInitCopyDestinationPath = "/rag-data/rag-0"
RAGRuntimeContentPath = "/rag/rag-0"
OpenStackLightspeedVectorDBPath = RAGRuntimeContentPath + "/vector_db/os_product_docs"

ServingCertSecretAnnotationKey = "service.beta.openshift.io/serving-cert-secret-name"

Expand Down Expand Up @@ -93,8 +96,13 @@ const (
LCoreConfigFilename = "lightspeed-stack.yaml"
LCoreConfigMapResourceVersionAnnotation = "ols.openshift.io/lcore-configmap-version"
LlamaStackConfigMapResourceVersionAnnotation = "ols.openshift.io/llamastack-configmap-version"
RAGImageAnnotation = "ols.openshift.io/rag-image"
ActiveOCPRAGVersionAnnotation = "ols.openshift.io/active-ocp-rag-version"
LCoreUserDataMountPath = "/tmp/data"
ForceReloadAnnotationKey = "ols.openshift.io/force-reload"
RAGVolumeName = "rag-data"
RAGVolumeMountPath = "/rag"
RAGInitVolumeMountPath = "/rag-data"

// Data Exporter
ExporterConfigVolumeName = "exporter-config"
Expand Down
55 changes: 53 additions & 2 deletions internal/controller/lcore_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package controller
import (
_ "embed"
"fmt"
"os"

common_helper "github.com/openstack-k8s-operators/lib-common/modules/common/helper"
apiv1beta1 "github.com/openstack-lightspeed/operator/api/v1beta1"
Expand Down Expand Up @@ -197,8 +198,48 @@ func buildLCoreConversationCacheConfig(h *common_helper.Helper, _ *apiv1beta1.Op
}
}

// buildLCoreConfigYAML assembles the complete Lightspeed Core Service configuration and converts to YAML.
// NOTE: MCP servers, quota handlers, and tools approval features are disabled for OpenStack Lightspeed.
func buildLCoreRAGConfig(instance *apiv1beta1.OpenStackLightspeed) map[string]interface{} {
ragIDs := []interface{}{}
for _, rag := range buildLCoreRAGConfigs(instance, instance.Status.ActiveOCPRAGVersion) {
ragID := rag.IndexID
if ragID == "" {
ragID = "rag_" + sanitizeID(rag.Image)
}
ragIDs = append(ragIDs, ragID)
}

return map[string]interface{}{
"inline": []string{
getVectorStoreID(),
},
// NOTE(lpiwowar): RAG will be enabled as a tool once the full migration
// to lightspeed-stack is complete and feature parity has been achieved.
// "tool": ragIDs,
}
}

func buildLCoreBYOKRAGConfig(instance *apiv1beta1.OpenStackLightspeed) []interface{} {
byokRAG := []interface{}{}
for _, rag := range buildLCoreRAGConfigs(instance, instance.Status.ActiveOCPRAGVersion) {
vectorDBID := rag.IndexID
if vectorDBID == "" {
vectorDBID = "rag_" + sanitizeID(rag.Image)
}

byokRAG = append(byokRAG, map[string]interface{}{
"rag_id": getVectorStoreID(),
"rag_type": "inline::faiss",
"embedding_model": "sentence-transformers/all-mpnet-base-v2",
"embedding_dimension": 768,
"vector_db_id": getVectorStoreID(),
"db_path": rag.IndexPath,
"score_multiplier": 1.0,
})
}

return byokRAG
}

func buildLCoreConfigYAML(h *common_helper.Helper, instance *apiv1beta1.OpenStackLightspeed) (string, error) {
// Build the complete config as a map
config := map[string]interface{}{
Expand All @@ -211,6 +252,8 @@ func buildLCoreConfigYAML(h *common_helper.Helper, instance *apiv1beta1.OpenStac
"database": buildLCoreDatabaseConfig(h, instance),
"customization": buildLCoreCustomizationConfig(),
"conversation_cache": buildLCoreConversationCacheConfig(h, instance),
"byok_rag": buildLCoreBYOKRAGConfig(instance),
"rag": buildLCoreRAGConfig(instance),
}

// Convert to YAML
Expand All @@ -221,3 +264,11 @@ func buildLCoreConfigYAML(h *common_helper.Helper, instance *apiv1beta1.OpenStac

return string(yamlBytes), nil
}

func getVectorStoreID() string {
id := os.Getenv("VECTOR_STORE_ID")
if id == "" {
panic("VECTOR_STORE_ID environment variable is not set")
}
return id
}
46 changes: 46 additions & 0 deletions internal/controller/lcore_deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ func buildLCorePodTemplateSpec(h *common_helper.Helper, ctx context.Context, ins
llamaCacheMounts := []corev1.VolumeMount{}
addLlamaCacheVolumesAndMounts(&volumes, &llamaCacheMounts)

// Shared RAG content volume
ragMounts := []corev1.VolumeMount{}
addRAGVolumesAndMounts(&volumes, &ragMounts)

// Build env vars
llamaEnvVars, err := buildLlamaStackEnvVars(h, ctx, instance)
if err != nil {
Expand All @@ -65,6 +69,7 @@ func buildLCorePodTemplateSpec(h *common_helper.Helper, ctx context.Context, ins
llamaStackMounts := []corev1.VolumeMount{llamaMount}
llamaStackMounts = append(llamaStackMounts, sharedMounts...)
llamaStackMounts = append(llamaStackMounts, llamaCacheMounts...)
llamaStackMounts = append(llamaStackMounts, ragMounts...)

llamaStackContainer := corev1.Container{
Name: "llama-stack",
Expand Down Expand Up @@ -112,6 +117,8 @@ func buildLCorePodTemplateSpec(h *common_helper.Helper, ctx context.Context, ins
if err != nil {
return corev1.PodTemplateSpec{}, err
}
annotations[RAGImageAnnotation] = instance.Spec.RAGImage
annotations[ActiveOCPRAGVersionAnnotation] = instance.Status.ActiveOCPRAGVersion

return corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Expand All @@ -120,6 +127,7 @@ func buildLCorePodTemplateSpec(h *common_helper.Helper, ctx context.Context, ins
},
Spec: corev1.PodSpec{
ServiceAccountName: OpenStackLightspeedAppServerServiceAccountName,
InitContainers: []corev1.Container{buildRAGInitContainer(instance)},
Containers: containers,
Volumes: volumes,
},
Expand Down Expand Up @@ -248,6 +256,44 @@ func addLlamaCacheVolumesAndMounts(volumes *[]corev1.Volume, mounts *[]corev1.Vo
})
}

// addRAGVolumesAndMounts adds an emptyDir volume shared by init and app containers for RAG content.
func addRAGVolumesAndMounts(volumes *[]corev1.Volume, mounts *[]corev1.VolumeMount) {
*volumes = append(*volumes, corev1.Volume{
Name: RAGVolumeName,
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{},
},
})
*mounts = append(*mounts, corev1.VolumeMount{
Name: RAGVolumeName,
MountPath: RAGVolumeMountPath,
})
}

// buildRAGInitContainer returns an init container that copies vector DB content from the RAG image.
func buildRAGInitContainer(instance *apiv1beta1.OpenStackLightspeed) corev1.Container {
return corev1.Container{
Name: "rag-init",
Image: instance.Spec.RAGImage,
ImagePullPolicy: corev1.PullIfNotPresent,
Command: []string{
"sh", "-c",
fmt.Sprintf(
"mkdir -p %s && cp -a %s/. %s",
RAGInitCopyDestinationPath,
"/rag",
RAGInitCopyDestinationPath,
),
},
VolumeMounts: []corev1.VolumeMount{
{
Name: RAGVolumeName,
MountPath: RAGInitVolumeMountPath,
},
},
}
}

// addUserCAVolumesAndMounts adds user-provided additional CA certificate volume and mount
// if instance.Spec.TLSCACertBundle is set.
func addUserCAVolumesAndMounts(volumes *[]corev1.Volume, mounts *[]corev1.VolumeMount, instance *apiv1beta1.OpenStackLightspeed, volumeDefaultMode int32) {
Expand Down
90 changes: 66 additions & 24 deletions internal/controller/llama_stack_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,8 @@ func buildLlamaStackVectorDB(_ *common_helper.Helper, _ *apiv1beta1.OpenStackLig
"table_name": "vector_store",
},
"persistence": map[string]interface{}{
"backend": "kv_default",
"namespace": "vector_persistence",
"backend": "rag_backend",
"namespace": "vector_io:faiss",
},
},
},
Expand Down Expand Up @@ -270,6 +270,10 @@ func buildLlamaStackStorage(_ *common_helper.Helper, instance *apiv1beta1.OpenSt
"ca_cert_path": "/etc/certs/postgres-ca/service-ca.crt",
"gss_encmode": "disable",
},
"rag_backend": map[string]interface{}{
"type": "kv_sqlite",
"db_path": "/rag/rag-0/vector_db/os_product_docs/faiss_store.db",
},
}

// Map data stores to backends - all use SQL with table_name
Expand All @@ -294,37 +298,34 @@ func buildLlamaStackStorage(_ *common_helper.Helper, instance *apiv1beta1.OpenSt
}
}

func buildLlamaStackVectorDBs(_ *common_helper.Helper, instance *apiv1beta1.OpenStackLightspeed) []interface{} {
vectorDBs := []interface{}{}
func buildLlamaStackVectorStores(_ *common_helper.Helper, instance *apiv1beta1.OpenStackLightspeed) map[string]interface{} {
var vectorDBs map[string]interface{}

// Use RAG configuration from instance if available
rags := buildLCoreRAGConfigs(instance, instance.Status.ActiveOCPRAGVersion)
if len(rags) > 0 {
for _, rag := range rags {
vectorDB := map[string]interface{}{
"embedding_model": "sentence-transformers/all-mpnet-base-v2",
"embedding_dimension": 768,
"provider_id": "faiss",
}

// Use IndexID if specified, otherwise generate a default
if rag.IndexID != "" {
vectorDB["vector_db_id"] = rag.IndexID
} else {
// Generate a simple ID from the image name
vectorDB["vector_db_id"] = "rag_" + sanitizeID(rag.Image)
}
vectorDBs = map[string]interface{}{
"default_embedding_model": map[string]interface{}{
"provider_id": "sentence-transformers",
// "model_id": "all-mpnet-base-v2",
"model_id": "/rag/rag-0/embeddings_model",
},

vectorDBs = append(vectorDBs, vectorDB)
// "embedding_dimension": 768,
"default_provider_id": "faiss",
// "index_path": rag.IndexPath,
}

vectorDBs["vector_store_id"] = getVectorStoreID()

} else {
// Default fallback if no RAG configured
vectorDBs = append(vectorDBs, map[string]interface{}{
vectorDBs = map[string]interface{}{
"vector_db_id": "my_knowledge_base",
"embedding_model": "sentence-transformers/all-mpnet-base-v2",
"embedding_dimension": 768,
"provider_id": "faiss",
})
}
}

return vectorDBs
Expand All @@ -337,7 +338,7 @@ func buildLlamaStackModels(_ *common_helper.Helper, instance *apiv1beta1.OpenSta
"model_id": "sentence-transformers/all-mpnet-base-v2",
"model_type": "embedding",
"provider_id": "sentence-transformers",
"provider_model_id": "sentence-transformers/all-mpnet-base-v2",
"provider_model_id": "/rag/rag-0/embeddings_model",
"metadata": map[string]interface{}{
"embedding_dimension": 768,
},
Expand Down Expand Up @@ -380,6 +381,47 @@ func buildLlamaStackToolGroups(_ *common_helper.Helper, _ *apiv1beta1.OpenStackL
}
}

func buildLlamaStackRegisteredResources(_ *common_helper.Helper, instance *apiv1beta1.OpenStackLightspeed) map[string]interface{} {
return map[string]interface{}{
"models": []interface{}{
map[string]interface{}{
"model_id": "sentence-transformers/all-mpnet-base-v2",
"model_type": "embedding",
// "provider_model_id": "all-mpnet-base-v2",
"metadata": map[string]interface{}{
"embedding_dimension": 768,
},

"provider_id": "sentence-transformers",
// "provider_model_id": "all-mpnet-base-v2",
"provider_model_id": "/rag/rag-0/embeddings_model",
},

map[string]interface{}{
"model_id": instance.Spec.ModelName,
"model_type": "llm",
"provider_id": "openstack-lightspeed-provider",
"provider_model_id": instance.Spec.ModelName,
"metadata": map[string]interface{}{
"max_tokens": 2048,
},
},
},
"vector_stores": []interface{}{
map[string]interface{}{
"vector_store_id": getVectorStoreID(),
// "model_id": "sentence-transformers/all-mpnet-base-v2",
"provider_id": "faiss",
// "embedding_model": "all-mpnet-base-v2",
// "embedding_model": "sentence-transformers/all-mpnet-base-v2",
"embedding_model": "sentence-transformers//rag/rag-0/embeddings_model",
"embedding_dimension": 768,
// "provider_model_id": "/rag/rag-0/embeddings_model",
},
},
}
}

// buildLlamaStackYAML assembles the complete Llama Stack configuration and converts to YAML
func buildLlamaStackYAML(h *common_helper.Helper, ctx context.Context, instance *apiv1beta1.OpenStackLightspeed) (string, error) {
// Build the complete config as a map
Expand All @@ -400,12 +442,12 @@ func buildLlamaStackYAML(h *common_helper.Helper, ctx context.Context, instance
"tool_runtime": buildLlamaStackToolRuntime(h, instance),
"vector_io": buildLlamaStackVectorDB(h, instance),
}

// Add top-level fields
config["scoring_fns"] = []interface{}{}
config["server"] = buildLlamaStackServerConfig(h, instance)
config["storage"] = buildLlamaStackStorage(h, instance)
config["vector_dbs"] = buildLlamaStackVectorDBs(h, instance)
config["vector_stores"] = buildLlamaStackVectorStores(h, instance)
config["registered_resources"] = buildLlamaStackRegisteredResources(h, instance)
config["models"] = buildLlamaStackModels(h, instance)
config["tool_groups"] = buildLlamaStackToolGroups(h, instance)
config["telemetry"] = map[string]interface{}{
Expand Down
2 changes: 1 addition & 1 deletion internal/controller/ocp_version.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import (

const (
// OpenStackLightspeedOCPVectorDBPath - base path for OCP vector databases
OpenStackLightspeedOCPVectorDBPath = "/rag/ocp_vector_db/ocp"
OpenStackLightspeedOCPVectorDBPath = RAGRuntimeContentPath + "/ocp_vector_db/ocp"

// OpenStackLightspeedOCPIndexPrefix - prefix for OCP index names
OpenStackLightspeedOCPIndexPrefix = "ocp-product-docs"
Expand Down
Loading