@@ -164,7 +164,7 @@ func (c *GatewayController) Reconcile(ctx context.Context, req ctrl.Request) (ct
164164func schemaToFilterAPI (schema aigv1b1.VersionedAPISchema ) filterapi.VersionedAPISchema {
165165 ret := filterapi.VersionedAPISchema {}
166166 ret .Name = filterapi .APISchemaName (schema .Name )
167- if schema .Name == aigv1b1 .APISchemaOpenAI {
167+ if schema .Name == aigv1b1 .APISchemaOpenAI || schema . Name == aigv1b1 . APISchemaAnthropic {
168168 ret .Prefix = cmp .Or (ptr .Deref (schema .Prefix , "" ), "v1" )
169169 } else {
170170 ret .Version = ptr .Deref (schema .Version , "" )
@@ -367,6 +367,11 @@ func (c *GatewayController) reconcileFilterConfigSecret(
367367 ec .GlobalLLMRequestCosts = append (ec .GlobalLLMRequestCosts , fc )
368368 }
369369
370+ // Models contributed by routes with no Spec.Hostnames. We only promote these to
371+ // ec.UnscopedModels (and merge them into ec.ModelsByHost) when at least one route
372+ // IS hostname-scoped; otherwise the existing ec.Models list already covers them.
373+ var unscopedModels []filterapi.Model
374+
370375 for i := range aiGatewayRoutes {
371376 aiGatewayRoute := & aiGatewayRoutes [i ]
372377 if ! aiGatewayRoute .GetDeletionTimestamp ().IsZero () {
@@ -375,6 +380,7 @@ func (c *GatewayController) reconcileFilterConfigSecret(
375380 }
376381 hasEffectiveRoute = true
377382 routeName := fmt .Sprintf ("%s/%s" , aiGatewayRoute .Namespace , aiGatewayRoute .Name )
383+ hostnames := aiGatewayRoute .Spec .Hostnames
378384 spec := aiGatewayRoute .Spec
379385 routeBackendNamesSet := map [string ]struct {}{}
380386 routeBackendNames := []string {}
@@ -389,11 +395,25 @@ func (c *GatewayController) reconcileFilterConfigSecret(
389395 if (h .Type != nil && * h .Type != gwapiv1 .HeaderMatchExact ) || string (h .Name ) != internalapi .ModelNameHeaderKeyDefault {
390396 continue
391397 }
392- ec . Models = append ( ec . Models , filterapi.Model {
398+ model := filterapi.Model {
393399 Name : h .Value ,
394400 CreatedAt : ptr .Deref [metav1.Time ](rule .ModelsCreatedAt , aiGatewayRoute .CreationTimestamp ).UTC (),
395401 OwnedBy : ptr .Deref (rule .ModelsOwnedBy , defaultOwnedBy ),
396- })
402+ }
403+ ec .Models = append (ec .Models , model )
404+ if len (hostnames ) > 0 {
405+ if ec .ModelsByHost == nil {
406+ ec .ModelsByHost = make (map [string ][]filterapi.Model )
407+ }
408+ for _ , hn := range hostnames {
409+ ec .ModelsByHost [string (hn )] = append (ec .ModelsByHost [string (hn )], model )
410+ }
411+ } else {
412+ // Routes without hostnames are "unscoped": they apply to every host.
413+ // Tracked in unscopedModels for now; only promoted to ec.UnscopedModels
414+ // after the loop if at least one scoped route is also present.
415+ unscopedModels = append (unscopedModels , model )
416+ }
397417 }
398418 }
399419 for backendRefIndex := range rule .BackendRefs {
@@ -483,6 +503,18 @@ func (c *GatewayController) reconcileFilterConfigSecret(
483503 }
484504 }
485505
506+ // If at least one route is hostname-scoped, promote the unscoped models to ec.UnscopedModels
507+ // so the runtime can fall back to them on unmatched hosts, and merge them into every per-host
508+ // list so a host-matched request still sees the models from routes that didn't declare hostnames.
509+ // When no route uses hostname scoping, ec.Models is the sole source of truth and we skip both
510+ // steps to avoid serializing a redundant UnscopedModels duplicate of Models.
511+ if len (ec .ModelsByHost ) > 0 && len (unscopedModels ) > 0 {
512+ ec .UnscopedModels = unscopedModels
513+ for hn := range ec .ModelsByHost {
514+ ec .ModelsByHost [hn ] = append (ec .ModelsByHost [hn ], unscopedModels ... )
515+ }
516+ }
517+
486518 // Configuration for MCP processor.
487519 var effectiveMCPRoute bool
488520 ec .MCPConfig , effectiveMCPRoute = mcpConfig (mcpRoutes )
0 commit comments