@@ -117,6 +117,15 @@ func (r *ModelDeploymentReconciler) reconcileGateway(ctx context.Context, md *ai
117117 // Determine the HTTPRoute backend via the GAIE InferencePool/EPP path.
118118 poolName , poolNamespace := md .Name , md .Namespace
119119
120+ // Two independent extension points exist:
121+ // 1. InferencePool delegation (e.g. Dynamo): the provider's upstream
122+ // operator creates the InferencePool AND the EPP. The controller
123+ // skips both. Opt-in via gatewayCapabilities.ManagesInferencePool.
124+ // 2. EPP customization (e.g. llm-d): the controller creates the
125+ // InferencePool and the EPP scaffolding, but uses the provider-
126+ // supplied EPP image and plugin config. Opt-in via
127+ // gatewayCapabilities.EndpointPicker.
128+
120129 // Use provider managed inference pool if it exists,
121130 // otherwise use the default inference pool.
122131 if ok , err := r .providerInferencePoolExistsOrCreateDefault (ctx , md , gatewayCapabilities , gwConfig ); ok && err == nil {
@@ -146,9 +155,12 @@ func (r *ModelDeploymentReconciler) reconcileGateway(ctx context.Context, md *ai
146155
147156 if gatewayCapabilities != nil && gatewayCapabilities .ManagesInferencePool {
148157 logger .Info ("Skipping EPP creation, provider manages EPP" , "provider" , resolvedProviderName (md ))
149- } else { // Use default EPP
150- // Create or update EPP (EndPoint Picker) for the InferencePool
151- if err := r .reconcileEPP (ctx , md ); err != nil {
158+ } else { // Use controller-managed EPP (default or provider-customized).
159+ var eppOverrides * airunwayv1alpha1.EndpointPickerCapabilities
160+ if gatewayCapabilities != nil {
161+ eppOverrides = gatewayCapabilities .EndpointPicker
162+ }
163+ if err := r .reconcileEPP (ctx , md , eppOverrides ); err != nil {
152164 r .setCondition (md , airunwayv1alpha1 .ConditionTypeGatewayReady , metav1 .ConditionFalse , "EPPFailed" , err .Error ())
153165 return fmt .Errorf ("reconciling EPP: %w" , err )
154166 }
@@ -381,8 +393,11 @@ func resolveProviderPoolField(pattern, mdName, mdNamespace, fallback string) str
381393}
382394
383395// reconcileEPP creates or updates the Endpoint Picker Proxy deployment and service
384- // for a ModelDeployment's InferencePool.
385- func (r * ModelDeploymentReconciler ) reconcileEPP (ctx context.Context , md * airunwayv1alpha1.ModelDeployment ) error {
396+ // for a ModelDeployment's InferencePool. When overrides is non-nil, its Image
397+ // and ConfigData take precedence over the controller's defaults.
398+ func (r * ModelDeploymentReconciler ) reconcileEPP (ctx context.Context , md * airunwayv1alpha1.ModelDeployment , overrides * airunwayv1alpha1.EndpointPickerCapabilities ) error {
399+ logger := log .FromContext (ctx )
400+
386401 eppName := md .Name + "-epp"
387402 eppPort := r .GatewayDetector .EPPServicePort
388403 if eppPort == 0 {
@@ -392,6 +407,9 @@ func (r *ModelDeploymentReconciler) reconcileEPP(ctx context.Context, md *airunw
392407 if eppImage == "" {
393408 eppImage = "registry.k8s.io/gateway-api-inference-extension/epp:" + gateway .DefaultGAIEVersion
394409 }
410+ if overrides != nil && overrides .Image != "" {
411+ eppImage = overrides .Image
412+ }
395413
396414 labels := map [string ]string {
397415 "app.kubernetes.io/name" : eppName ,
@@ -480,10 +498,15 @@ func (r *ModelDeploymentReconciler) reconcileEPP(ctx context.Context, md *airunw
480498 },
481499 }
482500 if _ , err := ctrl .CreateOrUpdate (ctx , r .Client , cm , func () error {
483- cm .Data = map [string ]string {
484- "default-plugins.yaml" : `apiVersion: inference.networking.x-k8s.io/v1alpha1
501+ pluginsYAML := `apiVersion: inference.networking.x-k8s.io/v1alpha1
485502kind: EndpointPickerConfig
486- ` ,
503+ `
504+ if overrides != nil && overrides .ConfigData != "" {
505+ logger .V (1 ).Info ("Using provider overrides for EPP plugins config" )
506+ pluginsYAML = overrides .ConfigData
507+ }
508+ cm .Data = map [string ]string {
509+ "default-plugins.yaml" : pluginsYAML ,
487510 }
488511 return ctrl .SetControllerReference (md , cm , r .Scheme )
489512 }); err != nil {
@@ -942,7 +965,8 @@ func (r *ModelDeploymentReconciler) labelModelPods(ctx context.Context, md *airu
942965
943966 // List pods matching the service selector
944967 var pods corev1.PodList
945- if err := r .List (ctx , & pods ,
968+ if err := r .List (
969+ ctx , & pods ,
946970 client .InNamespace (md .Namespace ),
947971 client .MatchingLabels (svc .Spec .Selector ),
948972 ); err != nil {
@@ -1120,6 +1144,9 @@ func (r *ModelDeploymentReconciler) cleanupGatewayResources(ctx context.Context,
11201144 if gatewayCapabilities , err = r .resolveProviderGatewayCapabilities (ctx , md ); err != nil {
11211145 logger .V (1 ).Info ("Could not resolve provider gateway capabilities, proceeding without provider-specific gateway capabilities" , "error" , err )
11221146 }
1147+ // Only true delegation (ManagesInferencePool: true) means the provider
1148+ // owns the pool + EPP. EndpointPicker-only customization still leaves the
1149+ // pool and EPP scaffolding owned by the controller, so they must be cleaned up here.
11231150 providerManagedPool := gatewayCapabilities != nil && gatewayCapabilities .ManagesInferencePool
11241151
11251152 eppName := md .Name + "-epp"
@@ -1209,6 +1236,10 @@ func (r *ModelDeploymentReconciler) cleanupGatewayResources(ctx context.Context,
12091236func (r * ModelDeploymentReconciler ) providerInferencePoolExistsOrCreateDefault (ctx context.Context , md * airunwayv1alpha1.ModelDeployment , gatewayCapabilitities * airunwayv1alpha1.GatewayCapabilities , gwConfig * gateway.GatewayConfig ) (bool , error ) {
12101237 logger := log .FromContext (ctx )
12111238
1239+ // Only treat the pool as provider-managed when the provider has explicitly
1240+ // opted in via ManagesInferencePool. Providers that only customize the EPP
1241+ // (gatewayCapabilities.EndpointPicker without ManagesInferencePool) still
1242+ // rely on the controller to create the default InferencePool.
12121243 if gatewayCapabilitities != nil && gatewayCapabilitities .ManagesInferencePool {
12131244 // Provider manages the pool.
12141245 return true , nil
0 commit comments