@@ -45,12 +45,12 @@ import (
4545
4646// reconcileGateway creates or updates InferencePool and HTTPRoute resources
4747// for a ModelDeployment that has gateway integration enabled.
48- func (r * ModelDeploymentReconciler ) reconcileGateway (ctx context.Context , md * airunwayv1alpha1.ModelDeployment ) error {
48+ func (r * ModelDeploymentReconciler ) reconcileGateway (ctx context.Context , md * airunwayv1alpha1.ModelDeployment ) ( bool , error ) {
4949 logger := log .FromContext (ctx )
5050
5151 // Skip if no gateway detector configured
5252 if r .GatewayDetector == nil {
53- return nil
53+ return false , nil
5454 }
5555
5656 // Skip if gateway CRDs are not available
@@ -60,21 +60,21 @@ func (r *ModelDeploymentReconciler) reconcileGateway(ctx context.Context, md *ai
6060 logger .Info ("Gateway explicitly enabled but Gateway API Inference Extension CRDs not found" , "name" , md .Name )
6161 r .setCondition (md , airunwayv1alpha1 .ConditionTypeGatewayReady , metav1 .ConditionFalse , "CRDsNotAvailable" , "Gateway API Inference Extension CRDs are not installed in the cluster" )
6262 }
63- return nil
63+ return false , nil
6464 }
6565
6666 // Skip if explicitly disabled
6767 if md .Spec .Gateway != nil && md .Spec .Gateway .Enabled != nil && ! * md .Spec .Gateway .Enabled {
6868 logger .V (1 ).Info ("Gateway integration explicitly disabled" , "name" , md .Name )
69- return nil
69+ return false , nil
7070 }
7171
7272 // Resolve gateway configuration
7373 gwConfig , err := r .resolveGatewayConfig (ctx , md )
7474 if err != nil {
7575 logger .Info ("No gateway found for routing, skipping gateway reconciliation" , "reason" , err .Error ())
7676 r .setCondition (md , airunwayv1alpha1 .ConditionTypeGatewayReady , metav1 .ConditionFalse , "NoGateway" , err .Error ())
77- return nil
77+ return false , nil
7878 }
7979
8080 // Determine target port for InferencePool (needs the pod/container port, not service port)
@@ -97,25 +97,26 @@ func (r *ModelDeploymentReconciler) reconcileGateway(ctx context.Context, md *ai
9797 // Create or update InferencePool
9898 if err := r .reconcileInferencePool (ctx , md , port ); err != nil {
9999 r .setCondition (md , airunwayv1alpha1 .ConditionTypeGatewayReady , metav1 .ConditionFalse , "InferencePoolFailed" , err .Error ())
100- return fmt .Errorf ("reconciling InferencePool: %w" , err )
100+ return false , fmt .Errorf ("reconciling InferencePool: %w" , err )
101101 }
102102
103103 // Create or update EPP (Endpoint Picker Proxy) for the InferencePool
104104 if err := r .reconcileEPP (ctx , md ); err != nil {
105105 r .setCondition (md , airunwayv1alpha1 .ConditionTypeGatewayReady , metav1 .ConditionFalse , "EPPFailed" , err .Error ())
106- return fmt .Errorf ("reconciling EPP: %w" , err )
106+ return false , fmt .Errorf ("reconciling EPP: %w" , err )
107107 }
108108
109109 // Resolve model name early (needed for HTTPRoute header match and status)
110- modelName := r .resolveModelName (ctx , md )
110+ resolution := r .resolveModelNameResolution (ctx , md )
111+ modelName := resolution .name
111112
112113 // Create or update HTTPRoute (skip if user provides their own)
113114 if md .Spec .Gateway != nil && md .Spec .Gateway .HTTPRouteRef != "" {
114115 logger .V (1 ).Info ("Using user-provided HTTPRoute" , "httpRouteRef" , md .Spec .Gateway .HTTPRouteRef )
115116 } else {
116117 if err := r .reconcileHTTPRoute (ctx , md , gwConfig , modelName ); err != nil {
117118 r .setCondition (md , airunwayv1alpha1 .ConditionTypeGatewayReady , metav1 .ConditionFalse , "HTTPRouteFailed" , err .Error ())
118- return fmt .Errorf ("reconciling HTTPRoute: %w" , err )
119+ return false , fmt .Errorf ("reconciling HTTPRoute: %w" , err )
119120 }
120121 }
121122
@@ -128,7 +129,7 @@ func (r *ModelDeploymentReconciler) reconcileGateway(ctx context.Context, md *ai
128129 r .setCondition (md , airunwayv1alpha1 .ConditionTypeGatewayReady , metav1 .ConditionTrue , "GatewayConfigured" , "InferencePool and HTTPRoute created" )
129130
130131 logger .Info ("Gateway resources reconciled" , "name" , md .Name , "gateway" , gwConfig .GatewayName , "model" , modelName )
131- return nil
132+ return resolution . retry , nil
132133}
133134
134135// resolveGatewayConfig determines which Gateway to use as the HTTPRoute parent.
@@ -647,15 +648,24 @@ func (r *ModelDeploymentReconciler) resolveGatewayEndpoint(ctx context.Context,
647648 return ""
648649}
649650
651+ type modelNameResolution struct {
652+ name string
653+ retry bool
654+ }
655+
650656// resolveModelName determines the model name for gateway routing.
651657// Priority: spec.gateway.modelName > spec.model.servedName > auto-discovered from /v1/models > spec.model.id
652658func (r * ModelDeploymentReconciler ) resolveModelName (ctx context.Context , md * airunwayv1alpha1.ModelDeployment ) string {
659+ return r .resolveModelNameResolution (ctx , md ).name
660+ }
661+
662+ func (r * ModelDeploymentReconciler ) resolveModelNameResolution (ctx context.Context , md * airunwayv1alpha1.ModelDeployment ) modelNameResolution {
653663 // Use explicit overrides first
654664 if md .Spec .Gateway != nil && md .Spec .Gateway .ModelName != "" {
655- return md .Spec .Gateway .ModelName
665+ return modelNameResolution { name : md .Spec .Gateway .ModelName }
656666 }
657667 if shouldUseServedNameForGateway (md ) {
658- return md .Spec .Model .ServedName
668+ return modelNameResolution { name : md .Spec .Model .ServedName }
659669 }
660670
661671 // Auto-discover from the running model server
@@ -670,11 +680,19 @@ func (r *ModelDeploymentReconciler) resolveModelName(ctx context.Context, md *ai
670680 }
671681 if discovered := r .discoverModelName (ctx , md .Status .Endpoint .Service , md .Namespace , port ); discovered != "" {
672682 log .FromContext (ctx ).Info ("Auto-discovered model name from server" , "name" , md .Name , "modelName" , discovered )
673- return discovered
683+ return modelNameResolution {name : discovered }
684+ }
685+
686+ return modelNameResolution {
687+ name : md .Spec .Model .ID ,
688+ retry : shouldRetryGatewayModelNameDiscovery (md ),
674689 }
675690 }
676691
677- return md .Spec .Model .ID
692+ return modelNameResolution {
693+ name : md .Spec .Model .ID ,
694+ retry : shouldRetryGatewayModelNameDiscovery (md ),
695+ }
678696}
679697
680698func shouldUseServedNameForGateway (md * airunwayv1alpha1.ModelDeployment ) bool {
@@ -689,6 +707,20 @@ func shouldUseServedNameForGateway(md *airunwayv1alpha1.ModelDeployment) bool {
689707 return true
690708}
691709
710+ func shouldRetryGatewayModelNameDiscovery (md * airunwayv1alpha1.ModelDeployment ) bool {
711+ if md .ResolvedEngineType () != airunwayv1alpha1 .EngineTypeLlamaCpp {
712+ return false
713+ }
714+ if resolvedProviderName (md ) != "kaito" {
715+ return false
716+ }
717+ if md .Spec .Gateway != nil && md .Spec .Gateway .ModelName != "" {
718+ return false
719+ }
720+
721+ return true
722+ }
723+
692724func resolvedProviderName (md * airunwayv1alpha1.ModelDeployment ) string {
693725 if md .Spec .Provider != nil && md .Spec .Provider .Name != "" {
694726 return md .Spec .Provider .Name
0 commit comments