| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- /*
- Copyright 2026 LocoStack.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package templates
- import (
- "fmt"
- "maps"
- "slices"
- "github.com/LocoStack/loco-operator/api/v1alpha1"
- "github.com/LocoStack/loco-operator/pkg/templates/litellm"
- llamacpp "github.com/LocoStack/loco-operator/pkg/templates/llama_cpp"
- "github.com/LocoStack/loco-operator/pkg/templates/neo4j"
- "github.com/LocoStack/loco-operator/pkg/templates/phoenix"
- "github.com/LocoStack/loco-operator/pkg/templates/postgresql"
- "github.com/LocoStack/loco-operator/pkg/templates/qdrant"
- "github.com/LocoStack/loco-operator/pkg/templates/tei"
- corev1 "k8s.io/api/core/v1"
- )
- type TemplateManager struct {
- templates map[string]v1alpha1.Template
- runtimes map[string]v1alpha1.RuntimeSpec
- }
- func NewTemplateManager() *TemplateManager {
- return &TemplateManager{
- templates: make(map[string]v1alpha1.Template),
- runtimes: make(map[string]v1alpha1.RuntimeSpec),
- }
- }
- func (m *TemplateManager) GetTemplate(name string) (v1alpha1.Template, bool) {
- tmpl, ok := m.templates[name]
- return tmpl, ok
- }
- func (m *TemplateManager) GetRuntime(name string) (v1alpha1.RuntimeSpec, bool) {
- rt, ok := m.runtimes[name]
- return rt, ok
- }
- func (m *TemplateManager) RegisterTemplate(tmpl v1alpha1.Template) {
- m.templates[tmpl.Name] = tmpl
- }
- func (m *TemplateManager) RegisterRuntime(rt v1alpha1.RuntimeSpec) {
- m.runtimes[rt.Name] = rt
- }
- func (m *TemplateManager) ResolveTemplate(tmpl *v1alpha1.Template, defaultKey string) (v1alpha1.Template, error) {
- var templateName string
- if tmpl == nil {
- templateName = defaultKey
- } else {
- templateName = tmpl.Name
- if templateName == "" {
- templateName = defaultKey
- }
- }
- base, ok := m.GetTemplate(templateName)
- if !ok {
- return v1alpha1.Template{}, fmt.Errorf("template %q not found", templateName)
- }
- // resolve runtime if specified in the template or inherited from the base
- if tmpl != nil && tmpl.Spec.Runtime.Name != "" && tmpl.Spec.Runtime.Name != base.Spec.Runtime.Name {
- rtBase, ok := m.GetRuntime(tmpl.Spec.Runtime.Name)
- if !ok {
- return v1alpha1.Template{}, fmt.Errorf("runtime %q not found", tmpl.Spec.Runtime.Name)
- }
- mergeRuntimeSpec(&rtBase, &tmpl.Spec.Runtime)
- tmpl.Spec.Runtime = rtBase
- }
- return mergeTemplates(base, tmpl), nil
- }
- func mergeTemplates(base v1alpha1.Template, override *v1alpha1.Template) v1alpha1.Template {
- merged := base.DeepCopy()
- if override == nil {
- return *merged
- }
- if override.Name != "" && base.Name != override.Name {
- return *override.DeepCopy()
- }
- mergeStringMap(&merged.Metadata.Labels, override.Metadata.Labels)
- mergeStringMap(&merged.Metadata.Annotations, override.Metadata.Annotations)
- mergeTemplateSpec(&merged.Spec, &override.Spec)
- return *merged
- }
- func mergeTemplateSpec(dst, src *v1alpha1.TemplateSpec) {
- mergeRuntimeSpec(&dst.Runtime, &src.Runtime)
- if src.Volumes != nil {
- dst.Volumes = deepCopyVolumes(src.Volumes)
- }
- mergeResourceRequirements(&dst.Resources, &src.Resources)
- mergeStringMap(&dst.NodeSelector, src.NodeSelector)
- if src.Affinity != nil {
- dst.Affinity = src.Affinity.DeepCopy()
- }
- if src.Tolerations != nil {
- dst.Tolerations = deepCopyTolerations(src.Tolerations)
- }
- if src.TopologySpreadConstraints != nil {
- dst.TopologySpreadConstraints = deepCopyTopologySpreadConstraints(src.TopologySpreadConstraints)
- }
- if src.SecurityContext != nil {
- dst.SecurityContext = src.SecurityContext.DeepCopy()
- }
- }
- func mergeRuntimeSpec(dst, src *v1alpha1.RuntimeSpec) {
- if src.Name != "" && dst.Name != "" && src.Name != dst.Name {
- *dst = *src.DeepCopy()
- return
- }
- if src.Name != "" {
- dst.Name = src.Name
- }
- if src.Image != "" {
- dst.Image = src.Image
- }
- if src.Command != nil {
- dst.Command = slices.Clone(src.Command)
- }
- if src.Args != nil {
- dst.Args = slices.Clone(src.Args)
- }
- if src.ExtraArgs != nil {
- dst.ExtraArgs = slices.Clone(src.ExtraArgs)
- }
- if src.Env != nil {
- mergeEnvVars(&dst.Env, src.Env)
- }
- if src.VolumeMounts != nil {
- dst.VolumeMounts = deepCopyVolumeMounts(src.VolumeMounts)
- }
- if src.Port != 0 {
- dst.Port = src.Port
- }
- }
- func mergeResourceRequirements(dst, src *corev1.ResourceRequirements) {
- if src.Limits != nil {
- if dst.Limits == nil {
- dst.Limits = make(corev1.ResourceList)
- }
- maps.Copy(dst.Limits, src.Limits)
- }
- if src.Requests != nil {
- if dst.Requests == nil {
- dst.Requests = make(corev1.ResourceList)
- }
- maps.Copy(dst.Requests, src.Requests)
- }
- if src.Claims != nil {
- dst.Claims = slices.Clone(src.Claims)
- }
- }
- func mergeStringMap(dst *map[string]string, src map[string]string) {
- if src == nil {
- return
- }
- if *dst == nil {
- *dst = make(map[string]string)
- }
- maps.Copy(*dst, src)
- }
- func mergeEnvVars(dst *[]corev1.EnvVar, src []corev1.EnvVar) {
- if src == nil {
- return
- }
- if *dst == nil {
- *dst = make([]corev1.EnvVar, 0, len(src))
- }
- existing := make(map[string]int)
- for i, env := range *dst {
- existing[env.Name] = i
- }
- for _, env := range src {
- if idx, ok := existing[env.Name]; ok {
- env.DeepCopyInto(&(*dst)[idx])
- } else {
- *dst = append(*dst, *env.DeepCopy())
- }
- }
- }
- func deepCopyVolumeMounts(in []corev1.VolumeMount) []corev1.VolumeMount {
- out := make([]corev1.VolumeMount, len(in))
- for i := range in {
- in[i].DeepCopyInto(&out[i])
- }
- return out
- }
- func deepCopyVolumes(in []corev1.Volume) []corev1.Volume {
- out := make([]corev1.Volume, len(in))
- for i := range in {
- in[i].DeepCopyInto(&out[i])
- }
- return out
- }
- func deepCopyTolerations(in []corev1.Toleration) []corev1.Toleration {
- out := make([]corev1.Toleration, len(in))
- for i := range in {
- in[i].DeepCopyInto(&out[i])
- }
- return out
- }
- func deepCopyTopologySpreadConstraints(in []corev1.TopologySpreadConstraint) []corev1.TopologySpreadConstraint {
- out := make([]corev1.TopologySpreadConstraint, len(in))
- for i := range in {
- in[i].DeepCopyInto(&out[i])
- }
- return out
- }
- var Manager = NewTemplateManager()
- func init() {
- Manager.RegisterTemplate(litellm.LiteLLMTemplate)
- Manager.RegisterRuntime(litellm.LiteLLMTemplate.Spec.Runtime)
- Manager.RegisterTemplate(qdrant.QdrantTemplate)
- Manager.RegisterRuntime(qdrant.QdrantTemplate.Spec.Runtime)
- Manager.RegisterTemplate(neo4j.Neo4jTemplate)
- Manager.RegisterRuntime(neo4j.Neo4jTemplate.Spec.Runtime)
- Manager.RegisterTemplate(postgresql.PostgreSQLTemplate)
- Manager.RegisterRuntime(postgresql.PostgreSQLTemplate.Spec.Runtime)
- Manager.RegisterTemplate(phoenix.PhoenixTemplate)
- Manager.RegisterRuntime(phoenix.PhoenixTemplate.Spec.Runtime)
- Manager.RegisterTemplate(llamacpp.LlamaCPPTemplate)
- Manager.RegisterRuntime(llamacpp.LlamaCPPTemplate.Spec.Runtime)
- Manager.RegisterRuntime(llamacpp.LlamaCPPEmbeddingRuntime)
- Manager.RegisterRuntime(llamacpp.LlamaCPPRerankerRuntime)
- Manager.RegisterTemplate(tei.TEITemplate)
- Manager.RegisterRuntime(tei.TEITemplate.Spec.Runtime)
- Manager.RegisterRuntime(tei.TEIARM64Runtime)
- }
|