Repository: envoyproxy/ai-gateway
Pull request #234 (2025-01-30 18:01:33)

chore: reorganizes custom router/filterapi package
**Commit Message**:

This does the two re-organization of packages:
* Renames `filterconfig` to `filterapi` 
* Put `extprocapi` into `filterapi/x` package 

The `filterapi/x` package will serve as an experimental
package that facilitates the development of such features.
Notably, custom routers etc while keeping the ability to
introduce breaking changes at any release. This will be 
for advanced users as well as core contributors to iterate
on some advanced features in the single repo.

**Related Issues/PRs (if applicable)**:

For #233 


Patch:
diff --git a/examples/extproc_custom_router/main.go b/examples/extproc_custom_router/main.go
index 49ad512..56fe9fc 100644
--- a/examples/extproc_custom_router/main.go
+++ b/examples/extproc_custom_router/main.go
@@ -4,25 +4,25 @@ import (
 	"fmt"
 
 	"github.com/envoyproxy/ai-gateway/cmd/extproc/mainlib"
-	"github.com/envoyproxy/ai-gateway/extprocapi"
-	"github.com/envoyproxy/ai-gateway/filterconfig"
+	"github.com/envoyproxy/ai-gateway/filterapi"
+	"github.com/envoyproxy/ai-gateway/filterapi/x"
 )
 
-// newCustomRouter implements [extprocapi.NewCustomRouter].
-func newCustomRouter(defaultRouter extprocapi.Router, config *filterconfig.Config) extprocapi.Router {
+// newCustomRouter implements [x.NewCustomRouter].
+func newCustomRouter(defaultRouter x.Router, config *filterapi.Config) x.Router {
 	// You can poke the current configuration of the routes, and the list of backends
 	// specified in the AIGatewayRoute.Rules, etc.
 	return &myCustomRouter{config: config, defaultRouter: defaultRouter}
 }
 
-// myCustomRouter implements [extprocapi.Router].
+// myCustomRouter implements [filterapi.Router].
 type myCustomRouter struct {
-	config        *filterconfig.Config
-	defaultRouter extprocapi.Router
+	config        *filterapi.Config
+	defaultRouter x.Router
 }
 
-// Calculate implements [extprocapi.Router.Calculate].
-func (m *myCustomRouter) Calculate(headers map[string]string) (backend *filterconfig.Backend, err error) {
+// Calculate implements [x.Router.Calculate].
+func (m *myCustomRouter) Calculate(headers map[string]string) (backend *filterapi.Backend, err error) {
 	// Simply logs the headers and delegates the calculation to the default router.
 	modelName, ok := headers[m.config.ModelNameHeaderKey]
 	if !ok {
@@ -35,7 +35,7 @@ func (m *myCustomRouter) Calculate(headers map[string]string) (backend *filterco
 // This demonstrates how to build a custom router for the external processor.
 func main() {
 	// Initializes the custom router.
-	extprocapi.NewCustomRouter = newCustomRouter
+	x.NewCustomRouter = newCustomRouter
 	// Executes the main function of the external processor.
 	mainlib.Main()
 }
diff --git a/filterconfig/filterconfig.go b/filterapi/filterconfig.go
similarity index 98%
rename from filterconfig/filterconfig.go
rename to filterapi/filterconfig.go
index d22c7df..5d4e3fe 100644
--- a/filterconfig/filterconfig.go
+++ b/filterapi/filterconfig.go
@@ -1,4 +1,4 @@
-// Package filterconfig provides the configuration for the AI Gateway-implemented filter
+// Package filterapi provides the configuration for the AI Gateway-implemented filter
 // which is currently an external processor (See https://github.com/envoyproxy/ai-gateway/issues/90).
 //
 // This is a public package so that the filter can be testable without
@@ -7,7 +7,7 @@
 // This configuration must be decoupled from the Envoy Gateway types as well as its implementation
 // details. Also, the configuration must not be tied with k8s so it can be tested and iterated
 // without the need for the k8s cluster.
-package filterconfig
+package filterapi
 
 import (
 	"os"
diff --git a/extprocapi/extproc.go b/filterapi/x/x.go
similarity index 65%
rename from extprocapi/extproc.go
rename to filterapi/x/x.go
index 7efbfa2..5fc102a 100644
--- a/extprocapi/extproc.go
+++ b/filterapi/x/x.go
@@ -1,7 +1,7 @@
-// Package extprocapi is for building a custom external process.
-package extprocapi
+// Package x is an experimental package that provides the customizability of the AI Gateway filter.
+package x
 
-import "github.com/envoyproxy/ai-gateway/filterconfig"
+import "github.com/envoyproxy/ai-gateway/filterapi"
 
 // NewCustomRouter is the function to create a custom router over the default router.
 // This is nil by default and can be set by the custom build of external processor.
@@ -13,7 +13,7 @@ var NewCustomRouter NewCustomRouterFn
 // This is called when the new configuration is loaded.
 //
 // The defaultRouter can be used to delegate the calculation to the default router implementation.
-type NewCustomRouterFn func(defaultRouter Router, config *filterconfig.Config) Router
+type NewCustomRouterFn func(defaultRouter Router, config *filterapi.Config) Router
 
 // Router is the interface for the router.
 //
@@ -21,9 +21,9 @@ type NewCustomRouterFn func(defaultRouter Router, config *filterconfig.Config) R
 type Router interface {
 	// Calculate determines the backend to route to based on the request headers.
 	//
-	// The request headers include the populated [filterconfig.Config.ModelNameHeaderKey]
-	// with the parsed model name based on the [filterconfig.Config] given to the NewCustomRouterFn.
+	// The request headers include the populated [filterapi.Config.ModelNameHeaderKey]
+	// with the parsed model name based on the [filterapi.Config] given to the NewCustomRouterFn.
 	//
 	// Returns the backend.
-	Calculate(requestHeaders map[string]string) (backend *filterconfig.Backend, err error)
+	Calculate(requestHeaders map[string]string) (backend *filterapi.Backend, err error)
 }
diff --git a/internal/controller/ai_gateway_route.go b/internal/controller/ai_gateway_route.go
index 9dfa25c..b08ac1c 100644
--- a/internal/controller/ai_gateway_route.go
+++ b/internal/controller/ai_gateway_route.go
@@ -18,7 +18,7 @@ import (
 	gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
 
 	aigv1a1 "github.com/envoyproxy/ai-gateway/api/v1alpha1"
-	"github.com/envoyproxy/ai-gateway/filterconfig"
+	"github.com/envoyproxy/ai-gateway/filterapi"
 )
 
 const (
@@ -152,7 +152,7 @@ func (c *aiGatewayRouteController) ensuresExtProcConfigMapExists(ctx context.Con
 					Name:      name,
 					Namespace: aiGatewayRoute.Namespace,
 				},
-				Data: map[string]string{expProcConfigFileName: filterconfig.DefaultConfig},
+				Data: map[string]string{expProcConfigFileName: filterapi.DefaultConfig},
 			}
 			if err := ctrlutil.SetControllerReference(aiGatewayRoute, configMap, c.client.Scheme()); err != nil {
 				c.logger.Error(err, "failed to set controller reference for service", "namespace", configMap.Namespace, "name", configMap.Name)
diff --git a/internal/controller/sink.go b/internal/controller/sink.go
index 33194b4..f0f307d 100644
--- a/internal/controller/sink.go
+++ b/internal/controller/sink.go
@@ -21,7 +21,7 @@ import (
 	"sigs.k8s.io/yaml"
 
 	aigv1a1 "github.com/envoyproxy/ai-gateway/api/v1alpha1"
-	"github.com/envoyproxy/ai-gateway/filterconfig"
+	"github.com/envoyproxy/ai-gateway/filterapi"
 	"github.com/envoyproxy/ai-gateway/internal/llmcostcel"
 )
 
@@ -259,17 +259,17 @@ func (c *configSink) updateExtProcConfigMap(aiGatewayRoute *aigv1a1.AIGatewayRou
 		panic(fmt.Errorf("failed to get configmap %s: %w", extProcName(aiGatewayRoute), err))
 	}
 
-	ec := &filterconfig.Config{UUID: uuid}
+	ec := &filterapi.Config{UUID: uuid}
 	spec := &aiGatewayRoute.Spec
 
-	ec.Schema.Name = filterconfig.APISchemaName(spec.APISchema.Name)
+	ec.Schema.Name = filterapi.APISchemaName(spec.APISchema.Name)
 	ec.Schema.Version = spec.APISchema.Version
 	ec.ModelNameHeaderKey = aigv1a1.AIModelHeaderKey
 	ec.SelectedBackendHeaderKey = selectedBackendHeaderKey
-	ec.Rules = make([]filterconfig.RouteRule, len(spec.Rules))
+	ec.Rules = make([]filterapi.RouteRule, len(spec.Rules))
 	for i := range spec.Rules {
 		rule := &spec.Rules[i]
-		ec.Rules[i].Backends = make([]filterconfig.Backend, len(rule.BackendRefs))
+		ec.Rules[i].Backends = make([]filterapi.Backend, len(rule.BackendRefs))
 		for j := range rule.BackendRefs {
 			backend := &rule.BackendRefs[j]
 			key := fmt.Sprintf("%s.%s", backend.Name, aiGatewayRoute.Namespace)
@@ -279,7 +279,7 @@ func (c *configSink) updateExtProcConfigMap(aiGatewayRoute *aigv1a1.AIGatewayRou
 			if err != nil {
 				return fmt.Errorf("failed to get AIServiceBackend %s: %w", key, err)
 			} else {
-				ec.Rules[i].Backends[j].Schema.Name = filterconfig.APISchemaName(backendObj.Spec.APISchema.Name)
+				ec.Rules[i].Backends[j].Schema.Name = filterapi.APISchemaName(backendObj.Spec.APISchema.Name)
 				ec.Rules[i].Backends[j].Schema.Version = backendObj.Spec.APISchema.Version
 			}
 
@@ -294,16 +294,16 @@ func (c *configSink) updateExtProcConfigMap(aiGatewayRoute *aigv1a1.AIGatewayRou
 
 				switch backendSecurityPolicy.Spec.Type {
 				case aigv1a1.BackendSecurityPolicyTypeAPIKey:
-					ec.Rules[i].Backends[j].Auth = &filterconfig.BackendAuth{
-						APIKey: &filterconfig.APIKeyAuth{Filename: path.Join(backendSecurityMountPath(volumeName), "/apiKey")},
+					ec.Rules[i].Backends[j].Auth = &filterapi.BackendAuth{
+						APIKey: &filterapi.APIKeyAuth{Filename: path.Join(backendSecurityMountPath(volumeName), "/apiKey")},
 					}
 				case aigv1a1.BackendSecurityPolicyTypeAWSCredentials:
 					if backendSecurityPolicy.Spec.AWSCredentials == nil {
 						return fmt.Errorf("AWSCredentials type selected but not defined %s", backendSecurityPolicy.Name)
 					}
 					if backendSecurityPolicy.Spec.AWSCredentials.CredentialsFile != nil {
-						ec.Rules[i].Backends[j].Auth = &filterconfig.BackendAuth{
-							AWSAuth: &filterconfig.AWSAuth{
+						ec.Rules[i].Backends[j].Auth = &filterapi.BackendAuth{
+							AWSAuth: &filterapi.AWSAuth{
 								CredentialFileName: path.Join(backendSecurityMountPath(volumeName), "/credentials"),
 								Region:             backendSecurityPolicy.Spec.AWSCredentials.Region,
 							},
@@ -315,7 +315,7 @@ func (c *configSink) updateExtProcConfigMap(aiGatewayRoute *aigv1a1.AIGatewayRou
 				}
 			}
 		}
-		ec.Rules[i].Headers = make([]filterconfig.HeaderMatch, len(rule.Matches))
+		ec.Rules[i].Headers = make([]filterapi.HeaderMatch, len(rule.Matches))
 		for j, match := range rule.Matches {
 			ec.Rules[i].Headers[j].Name = match.Headers[0].Name
 			ec.Rules[i].Headers[j].Value = match.Headers[0].Value
@@ -324,16 +324,16 @@ func (c *configSink) updateExtProcConfigMap(aiGatewayRoute *aigv1a1.AIGatewayRou
 
 	ec.MetadataNamespace = aigv1a1.AIGatewayFilterMetadataNamespace
 	for _, cost := range aiGatewayRoute.Spec.LLMRequestCosts {
-		fc := filterconfig.LLMRequestCost{MetadataKey: cost.MetadataKey}
+		fc := filterapi.LLMRequestCost{MetadataKey: cost.MetadataKey}
 		switch cost.Type {
 		case aigv1a1.LLMRequestCostTypeInputToken:
-			fc.Type = filterconfig.LLMRequestCostTypeInputToken
+			fc.Type = filterapi.LLMRequestCostTypeInputToken
 		case aigv1a1.LLMRequestCostTypeOutputToken:
-			fc.Type = filterconfig.LLMRequestCostTypeOutputToken
+			fc.Type = filterapi.LLMRequestCostTypeOutputToken
 		case aigv1a1.LLMRequestCostTypeTotalToken:
-			fc.Type = filterconfig.LLMRequestCostTypeTotalToken
+			fc.Type = filterapi.LLMRequestCostTypeTotalToken
 		case aigv1a1.LLMRequestCostTypeCEL:
-			fc.Type = filterconfig.LLMRequestCostTypeCELExpression
+			fc.Type = filterapi.LLMRequestCostTypeCELExpression
 			expr := *cost.CELExpression
 			// Sanity check the CEL expression.
 			_, err := llmcostcel.NewProgram(expr)
diff --git a/internal/extproc/backendauth/api_key.go b/internal/extproc/backendauth/api_key.go
index 231624a..3a94fb0 100644
--- a/internal/extproc/backendauth/api_key.go
+++ b/internal/extproc/backendauth/api_key.go
@@ -8,7 +8,7 @@ import (
 	corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
 	extprocv3 "github.com/envoyproxy/go-control-plane/envoy/service/ext_proc/v3"
 
-	"github.com/envoyproxy/ai-gateway/filterconfig"
+	"github.com/envoyproxy/ai-gateway/filterapi"
 )
 
 // apiKeyHandler implements [Handler] for api key authz.
@@ -16,7 +16,7 @@ type apiKeyHandler struct {
 	apiKey string
 }
 
-func newAPIKeyHandler(auth *filterconfig.APIKeyAuth) (Handler, error) {
+func newAPIKeyHandler(auth *filterapi.APIKeyAuth) (Handler, error) {
 	secret, err := os.ReadFile(auth.Filename)
 	if err != nil {
 		return nil, fmt.Errorf("failed to read api key file: %w", err)
diff --git a/internal/extproc/backendauth/auth.go b/internal/extproc/backendauth/auth.go
index 6264230..e837584 100644
--- a/internal/extproc/backendauth/auth.go
+++ b/internal/extproc/backendauth/auth.go
@@ -5,7 +5,7 @@ import (
 
 	extprocv3 "github.com/envoyproxy/go-control-plane/envoy/service/ext_proc/v3"
 
-	"github.com/envoyproxy/ai-gateway/filterconfig"
+	"github.com/envoyproxy/ai-gateway/filterapi"
 )
 
 // Handler is the interface that deals with the backend auth for a specific backend.
@@ -17,7 +17,7 @@ type Handler interface {
 }
 
 // NewHandler returns a new implementation of [Handler] based on the configuration.
-func NewHandler(config *filterconfig.BackendAuth) (Handler, error) {
+func NewHandler(config *filterapi.BackendAuth) (Handler, error) {
 	if config.AWSAuth != nil {
 		return newAWSHandler(config.AWSAuth)
 	} else if config.APIKey != nil {
diff --git a/internal/extproc/backendauth/aws.go b/internal/extproc/backendauth/aws.go
index dc5b88c..65fbf09 100644
--- a/internal/extproc/backendauth/aws.go
+++ b/internal/extproc/backendauth/aws.go
@@ -17,7 +17,7 @@ import (
 	corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
 	extprocv3 "github.com/envoyproxy/go-control-plane/envoy/service/ext_proc/v3"
 
-	"github.com/envoyproxy/ai-gateway/filterconfig"
+	"github.com/envoyproxy/ai-gateway/filterapi"
 )
 
 // awsHandler implements [Handler] for AWS Bedrock authz.
@@ -27,7 +27,7 @@ type awsHandler struct {
 	region      string
 }
 
-func newAWSHandler(awsAuth *filterconfig.AWSAuth) (*awsHandler, error) {
+func newAWSHandler(awsAuth *filterapi.AWSAuth) (*awsHandler, error) {
 	var credentials aws.Credentials
 	var region string
 
diff --git a/internal/extproc/processor.go b/internal/extproc/processor.go
index 1e9cd61..7af7842 100644
--- a/internal/extproc/processor.go
+++ b/internal/extproc/processor.go
@@ -14,8 +14,8 @@ import (
 	"github.com/google/cel-go/cel"
 	"google.golang.org/protobuf/types/known/structpb"
 
-	"github.com/envoyproxy/ai-gateway/extprocapi"
-	"github.com/envoyproxy/ai-gateway/filterconfig"
+	"github.com/envoyproxy/ai-gateway/filterapi"
+	"github.com/envoyproxy/ai-gateway/filterapi/x"
 	"github.com/envoyproxy/ai-gateway/internal/extproc/backendauth"
 	"github.com/envoyproxy/ai-gateway/internal/extproc/router"
 	"github.com/envoyproxy/ai-gateway/internal/extproc/translator"
@@ -27,16 +27,16 @@ import (
 type processorConfig struct {
 	uuid                                         string
 	bodyParser                                   router.RequestBodyParser
-	router                                       extprocapi.Router
+	router                                       x.Router
 	modelNameHeaderKey, selectedBackendHeaderKey string
-	factories                                    map[filterconfig.VersionedAPISchema]translator.Factory
+	factories                                    map[filterapi.VersionedAPISchema]translator.Factory
 	backendAuthHandlers                          map[string]backendauth.Handler
 	metadataNamespace                            string
 	requestCosts                                 []processorConfigRequestCost
 }
 
 type processorConfigRequestCost struct {
-	*filterconfig.LLMRequestCost
+	*filterapi.LLMRequestCost
 	celProg cel.Program
 }
 
@@ -219,13 +219,13 @@ func (p *Processor) maybeBuildDynamicMetadata() (*structpb.Struct, error) {
 		c := &p.config.requestCosts[i]
 		var cost uint32
 		switch c.Type {
-		case filterconfig.LLMRequestCostTypeInputToken:
+		case filterapi.LLMRequestCostTypeInputToken:
 			cost = p.costs.InputTokens
-		case filterconfig.LLMRequestCostTypeOutputToken:
+		case filterapi.LLMRequestCostTypeOutputToken:
 			cost = p.costs.OutputTokens
-		case filterconfig.LLMRequestCostTypeTotalToken:
+		case filterapi.LLMRequestCostTypeTotalToken:
 			cost = p.costs.TotalTokens
-		case filterconfig.LLMRequestCostTypeCELExpression:
+		case filterapi.LLMRequestCostTypeCELExpression:
 			costU64, err := llmcostcel.EvaluateProgram(
 				c.celProg,
 				p.requestHeaders[p.config.modelNameHeaderKey],
diff --git a/internal/extproc/router/request_body.go b/internal/extproc/router/request_body.go
index d1361fa..724e039 100644
--- a/internal/extproc/router/request_body.go
+++ b/internal/extproc/router/request_body.go
@@ -6,7 +6,7 @@ import (
 
 	extprocv3 "github.com/envoyproxy/go-control-plane/envoy/service/ext_proc/v3"
 
-	"github.com/envoyproxy/ai-gateway/filterconfig"
+	"github.com/envoyproxy/ai-gateway/filterapi"
 	"github.com/envoyproxy/ai-gateway/internal/apischema/openai"
 )
 
@@ -14,8 +14,8 @@ import (
 type RequestBodyParser func(path string, body *extprocv3.HttpBody) (modelName string, rb RequestBody, err error)
 
 // NewRequestBodyParser creates a new RequestBodyParser based on the schema.
-func NewRequestBodyParser(schema filterconfig.VersionedAPISchema) (RequestBodyParser, error) {
-	if schema.Name == filterconfig.APISchemaOpenAI {
+func NewRequestBodyParser(schema filterapi.VersionedAPISchema) (RequestBodyParser, error) {
+	if schema.Name == filterapi.APISchemaOpenAI {
 		return openAIParseBody, nil
 	}
 	return nil, fmt.Errorf("unsupported API schema: %s", schema)
diff --git a/internal/extproc/router/router.go b/internal/extproc/router/router.go
index acb8996..87f5b4f 100644
--- a/internal/extproc/router/router.go
+++ b/internal/extproc/router/router.go
@@ -6,18 +6,18 @@ import (
 
 	"golang.org/x/exp/rand"
 
-	"github.com/envoyproxy/ai-gateway/extprocapi"
-	"github.com/envoyproxy/ai-gateway/filterconfig"
+	"github.com/envoyproxy/ai-gateway/filterapi"
+	"github.com/envoyproxy/ai-gateway/filterapi/x"
 )
 
-// router implements [extprocapi.Router].
+// router implements [filterapi.Router].
 type router struct {
-	rules []filterconfig.RouteRule
+	rules []filterapi.RouteRule
 	rng   *rand.Rand
 }
 
-// NewRouter creates a new [extprocapi.Router] implementation for the given config.
-func NewRouter(config *filterconfig.Config, newCustomFn extprocapi.NewCustomRouterFn) (extprocapi.Router, error) {
+// NewRouter creates a new [filterapi.Router] implementation for the given config.
+func NewRouter(config *filterapi.Config, newCustomFn x.NewCustomRouterFn) (x.Router, error) {
 	r := &router{rules: config.Rules, rng: rand.New(rand.NewSource(uint64(time.Now().UnixNano())))} //nolint:gosec
 	if newCustomFn != nil {
 		customRouter := newCustomFn(r, config)
@@ -26,9 +26,9 @@ func NewRouter(config *filterconfig.Config, newCustomFn extprocapi.NewCustomRout
 	return r, nil
 }
 
-// Calculate implements [extprocapi.Router.Calculate].
-func (r *router) Calculate(headers map[string]string) (backend *filterconfig.Backend, err error) {
-	var rule *filterconfig.RouteRule
+// Calculate implements [filterapi.Router.Calculate].
+func (r *router) Calculate(headers map[string]string) (backend *filterapi.Backend, err error) {
+	var rule *filterapi.RouteRule
 	for i := range r.rules {
 		_rule := &r.rules[i]
 		for _, hdr := range _rule.Headers {
@@ -46,7 +46,7 @@ func (r *router) Calculate(headers map[string]string) (backend *filterconfig.Bac
 	return r.selectBackendFromRule(rule), nil
 }
 
-func (r *router) selectBackendFromRule(rule *filterconfig.RouteRule) (backend *filterconfig.Backend) {
+func (r *router) selectBackendFromRule(rule *filterapi.RouteRule) (backend *filterapi.Backend) {
 	// Each backend has a weight, so we randomly select depending on the weight.
 	// This is a pretty naive implementation and can be buggy, so fix it later.
 	totalWeight := 0
diff --git a/internal/extproc/server.go b/internal/extproc/server.go
index fb67aff..533e341 100644
--- a/internal/extproc/server.go
+++ b/internal/extproc/server.go
@@ -16,8 +16,8 @@ import (
 	"google.golang.org/grpc/health/grpc_health_v1"
 	"google.golang.org/grpc/status"
 
-	"github.com/envoyproxy/ai-gateway/extprocapi"
-	"github.com/envoyproxy/ai-gateway/filterconfig"
+	"github.com/envoyproxy/ai-gateway/filterapi"
+	"github.com/envoyproxy/ai-gateway/filterapi/x"
 	"github.com/envoyproxy/ai-gateway/internal/extproc/backendauth"
 	"github.com/envoyproxy/ai-gateway/internal/extproc/router"
 	"github.com/envoyproxy/ai-gateway/internal/extproc/translator"
@@ -44,17 +44,17 @@ func NewServer[P ProcessorIface](logger *slog.Logger, newProcessor func(*process
 }
 
 // LoadConfig updates the configuration of the external processor.
-func (s *Server[P]) LoadConfig(config *filterconfig.Config) error {
+func (s *Server[P]) LoadConfig(config *filterapi.Config) error {
 	bodyParser, err := router.NewRequestBodyParser(config.Schema)
 	if err != nil {
 		return fmt.Errorf("cannot create request body parser: %w", err)
 	}
-	rt, err := router.NewRouter(config, extprocapi.NewCustomRouter)
+	rt, err := router.NewRouter(config, x.NewCustomRouter)
 	if err != nil {
 		return fmt.Errorf("cannot create router: %w", err)
 	}
 
-	factories := make(map[filterconfig.VersionedAPISchema]translator.Factory)
+	factories := make(map[filterapi.VersionedAPISchema]translator.Factory)
 	backendAuthHandlers := make(map[string]backendauth.Handler)
 	for _, r := range config.Rules {
 		for _, b := range r.Backends {
diff --git a/internal/extproc/translator/translator.go b/internal/extproc/translator/translator.go
index ed2eb60..aa2b1e9 100644
--- a/internal/extproc/translator/translator.go
+++ b/internal/extproc/translator/translator.go
@@ -8,7 +8,7 @@ import (
 	extprocv3http "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ext_proc/v3"
 	extprocv3 "github.com/envoyproxy/go-control-plane/envoy/service/ext_proc/v3"
 
-	"github.com/envoyproxy/ai-gateway/filterconfig"
+	"github.com/envoyproxy/ai-gateway/filterapi"
 	"github.com/envoyproxy/ai-gateway/internal/extproc/router"
 )
 
@@ -35,13 +35,13 @@ func isGoodStatusCode(code int) bool {
 type Factory func(path string) (Translator, error)
 
 // NewFactory returns a callback function that creates a translator for the given API schema combination.
-func NewFactory(in, out filterconfig.VersionedAPISchema) (Factory, error) {
-	if in.Name == filterconfig.APISchemaOpenAI {
+func NewFactory(in, out filterapi.VersionedAPISchema) (Factory, error) {
+	if in.Name == filterapi.APISchemaOpenAI {
 		// TODO: currently, we ignore the LLMAPISchema."Version" field.
 		switch out.Name {
-		case filterconfig.APISchemaOpenAI:
+		case filterapi.APISchemaOpenAI:
 			return newOpenAIToOpenAITranslator, nil
-		case filterconfig.APISchemaAWSBedrock:
+		case filterapi.APISchemaAWSBedrock:
 			return newOpenAIToAWSBedrockTranslator, nil
 		}
 	}
diff --git a/internal/extproc/watcher.go b/internal/extproc/watcher.go
index 14dce61..70ffab5 100644
--- a/internal/extproc/watcher.go
+++ b/internal/extproc/watcher.go
@@ -8,13 +8,13 @@ import (
 	"strings"
 	"time"
 
-	"github.com/envoyproxy/ai-gateway/filterconfig"
+	"github.com/envoyproxy/ai-gateway/filterapi"
 )
 
-// ConfigReceiver is an interface that can receive *filterconfig.Config updates.
+// ConfigReceiver is an interface that can receive *filterapi.Config updates.
 type ConfigReceiver interface {
 	// LoadConfig updates the configuration.
-	LoadConfig(config *filterconfig.Config) error
+	LoadConfig(config *filterapi.Config) error
 }
 
 type configWatcher struct {
@@ -81,7 +81,7 @@ func (cw *configWatcher) loadConfig(ctx context.Context) error {
 		cw.diff(previous, current)
 	}
 
-	cfg, err := filterconfig.UnmarshalConfigYaml(cw.path)
+	cfg, err := filterapi.UnmarshalConfigYaml(cw.path)
 	if err != nil {
 		return err
 	}


Test patch:
diff --git a/filterconfig/filterconfig_test.go b/filterapi/filterconfig_test.go
similarity index 91%
rename from filterconfig/filterconfig_test.go
rename to filterapi/filterconfig_test.go
index b55acb5..aca35fe 100644
--- a/filterconfig/filterconfig_test.go
+++ b/filterapi/filterconfig_test.go
@@ -1,4 +1,4 @@
-package filterconfig_test
+package filterapi_test
 
 import (
 	"log/slog"
@@ -9,7 +9,7 @@ import (
 	"github.com/stretchr/testify/require"
 	"k8s.io/apimachinery/pkg/util/yaml"
 
-	"github.com/envoyproxy/ai-gateway/filterconfig"
+	"github.com/envoyproxy/ai-gateway/filterapi"
 	"github.com/envoyproxy/ai-gateway/internal/extproc"
 )
 
@@ -18,8 +18,8 @@ func TestDefaultConfig(t *testing.T) {
 	require.NoError(t, err)
 	require.NotNil(t, server)
 
-	var cfg filterconfig.Config
-	err = yaml.Unmarshal([]byte(filterconfig.DefaultConfig), &cfg)
+	var cfg filterapi.Config
+	err = yaml.Unmarshal([]byte(filterapi.DefaultConfig), &cfg)
 	require.NoError(t, err)
 
 	err = server.LoadConfig(&cfg)
@@ -66,7 +66,7 @@ rules:
     value: gpt4.4444
 `
 	require.NoError(t, os.WriteFile(configPath, []byte(config), 0o600))
-	cfg, err := filterconfig.UnmarshalConfigYaml(configPath)
+	cfg, err := filterapi.UnmarshalConfigYaml(configPath)
 	require.NoError(t, err)
 	require.Equal(t, "ai_gateway_llm_ns", cfg.MetadataNamespace)
 	require.Equal(t, "token_usage_key", cfg.LLMRequestCosts[0].MetadataKey)
diff --git a/internal/controller/ai_gateway_route_test.go b/internal/controller/ai_gateway_route_test.go
index 2d073f5..9cc7b6b 100644
--- a/internal/controller/ai_gateway_route_test.go
+++ b/internal/controller/ai_gateway_route_test.go
@@ -17,7 +17,7 @@ import (
 	gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
 
 	aigv1a1 "github.com/envoyproxy/ai-gateway/api/v1alpha1"
-	"github.com/envoyproxy/ai-gateway/filterconfig"
+	"github.com/envoyproxy/ai-gateway/filterapi"
 )
 
 func Test_extProcName(t *testing.T) {
@@ -46,7 +46,7 @@ func TestAIGatewayRouteController_ensuresExtProcConfigMapExists(t *testing.T) {
 	require.Equal(t, extProcName(aiGatewayRoute), configMap.Name)
 	require.Equal(t, "default", configMap.Namespace)
 	require.Equal(t, ownerRef, configMap.OwnerReferences)
-	require.Equal(t, filterconfig.DefaultConfig, configMap.Data[expProcConfigFileName])
+	require.Equal(t, filterapi.DefaultConfig, configMap.Data[expProcConfigFileName])
 
 	// Doing it again should not fail.
 	err = c.ensuresExtProcConfigMapExists(context.Background(), aiGatewayRoute)
diff --git a/internal/controller/sink_test.go b/internal/controller/sink_test.go
index da15154..5eb4da0 100644
--- a/internal/controller/sink_test.go
+++ b/internal/controller/sink_test.go
@@ -25,7 +25,7 @@ import (
 	gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
 
 	aigv1a1 "github.com/envoyproxy/ai-gateway/api/v1alpha1"
-	"github.com/envoyproxy/ai-gateway/filterconfig"
+	"github.com/envoyproxy/ai-gateway/filterapi"
 )
 
 func TestConfigSink_init(t *testing.T) {
@@ -315,7 +315,7 @@ func Test_updateExtProcConfigMap(t *testing.T) {
 	for _, tc := range []struct {
 		name  string
 		route *aigv1a1.AIGatewayRoute
-		exp   *filterconfig.Config
+		exp   *filterapi.Config
 	}{
 		{
 			name: "basic",
@@ -369,46 +369,46 @@ func Test_updateExtProcConfigMap(t *testing.T) {
 					},
 				},
 			},
-			exp: &filterconfig.Config{
+			exp: &filterapi.Config{
 				UUID:                     string(uuid2.NewUUID()),
-				Schema:                   filterconfig.VersionedAPISchema{Name: filterconfig.APISchemaOpenAI, Version: "v123"},
+				Schema:                   filterapi.VersionedAPISchema{Name: filterapi.APISchemaOpenAI, Version: "v123"},
 				ModelNameHeaderKey:       aigv1a1.AIModelHeaderKey,
 				MetadataNamespace:        aigv1a1.AIGatewayFilterMetadataNamespace,
 				SelectedBackendHeaderKey: selectedBackendHeaderKey,
-				Rules: []filterconfig.RouteRule{
+				Rules: []filterapi.RouteRule{
 					{
-						Backends: []filterconfig.Backend{
-							{Name: "apple.ns", Weight: 1, Schema: filterconfig.VersionedAPISchema{Name: filterconfig.APISchemaAWSBedrock}, Auth: &filterconfig.BackendAuth{
-								APIKey: &filterconfig.APIKeyAuth{
+						Backends: []filterapi.Backend{
+							{Name: "apple.ns", Weight: 1, Schema: filterapi.VersionedAPISchema{Name: filterapi.APISchemaAWSBedrock}, Auth: &filterapi.BackendAuth{
+								APIKey: &filterapi.APIKeyAuth{
 									Filename: "/etc/backend_security_policy/rule0-backref0-some-backend-security-policy-1/apiKey",
 								},
 							}}, {Name: "pineapple.ns", Weight: 2},
 						},
-						Headers: []filterconfig.HeaderMatch{{Name: aigv1a1.AIModelHeaderKey, Value: "some-ai"}},
+						Headers: []filterapi.HeaderMatch{{Name: aigv1a1.AIModelHeaderKey, Value: "some-ai"}},
 					},
 					{
-						Backends: []filterconfig.Backend{{Name: "cat.ns", Weight: 1, Auth: &filterconfig.BackendAuth{
-							APIKey: &filterconfig.APIKeyAuth{
+						Backends: []filterapi.Backend{{Name: "cat.ns", Weight: 1, Auth: &filterapi.BackendAuth{
+							APIKey: &filterapi.APIKeyAuth{
 								Filename: "/etc/backend_security_policy/rule1-backref0-some-backend-security-policy-1/apiKey",
 							},
 						}}},
-						Headers: []filterconfig.HeaderMatch{{Name: aigv1a1.AIModelHeaderKey, Value: "another-ai"}},
+						Headers: []filterapi.HeaderMatch{{Name: aigv1a1.AIModelHeaderKey, Value: "another-ai"}},
 					},
 					{
-						Backends: []filterconfig.Backend{{Name: "pen.ns", Weight: 2, Auth: &filterconfig.BackendAuth{
-							AWSAuth: &filterconfig.AWSAuth{
+						Backends: []filterapi.Backend{{Name: "pen.ns", Weight: 2, Auth: &filterapi.BackendAuth{
+							AWSAuth: &filterapi.AWSAuth{
 								CredentialFileName: "/etc/backend_security_policy/rule2-backref0-some-backend-security-policy-2/credentials",
 								Region:             "us-east-1",
 							},
 						}}},
-						Headers: []filterconfig.HeaderMatch{{Name: aigv1a1.AIModelHeaderKey, Value: "another-ai-2"}},
+						Headers: []filterapi.HeaderMatch{{Name: aigv1a1.AIModelHeaderKey, Value: "another-ai-2"}},
 					},
 				},
-				LLMRequestCosts: []filterconfig.LLMRequestCost{
-					{Type: filterconfig.LLMRequestCostTypeOutputToken, MetadataKey: "output-token"},
-					{Type: filterconfig.LLMRequestCostTypeInputToken, MetadataKey: "input-token"},
-					{Type: filterconfig.LLMRequestCostTypeTotalToken, MetadataKey: "total-token"},
-					{Type: filterconfig.LLMRequestCostTypeCELExpression, MetadataKey: "cel-token", CELExpression: "model == 'cool_model' ?  input_tokens * output_tokens : total_tokens"},
+				LLMRequestCosts: []filterapi.LLMRequestCost{
+					{Type: filterapi.LLMRequestCostTypeOutputToken, MetadataKey: "output-token"},
+					{Type: filterapi.LLMRequestCostTypeInputToken, MetadataKey: "input-token"},
+					{Type: filterapi.LLMRequestCostTypeTotalToken, MetadataKey: "total-token"},
+					{Type: filterapi.LLMRequestCostTypeCELExpression, MetadataKey: "cel-token", CELExpression: "model == 'cool_model' ?  input_tokens * output_tokens : total_tokens"},
 				},
 			},
 		},
@@ -427,7 +427,7 @@ func Test_updateExtProcConfigMap(t *testing.T) {
 			require.NotNil(t, cm)
 
 			data := cm.Data[expProcConfigFileName]
-			var actual filterconfig.Config
+			var actual filterapi.Config
 			require.NoError(t, yaml.Unmarshal([]byte(data), &actual))
 			require.Equal(t, tc.exp, &actual)
 		})
diff --git a/internal/extproc/backendauth/api_key_test.go b/internal/extproc/backendauth/api_key_test.go
index 69e9b52..cfb3ce1 100644
--- a/internal/extproc/backendauth/api_key_test.go
+++ b/internal/extproc/backendauth/api_key_test.go
@@ -8,7 +8,7 @@ import (
 	extprocv3 "github.com/envoyproxy/go-control-plane/envoy/service/ext_proc/v3"
 	"github.com/stretchr/testify/require"
 
-	"github.com/envoyproxy/ai-gateway/filterconfig"
+	"github.com/envoyproxy/ai-gateway/filterapi"
 )
 
 func TestNewAPIKeyHandler(t *testing.T) {
@@ -21,7 +21,7 @@ func TestNewAPIKeyHandler(t *testing.T) {
 	require.NoError(t, err)
 	require.NoError(t, f.Sync())
 
-	auth := filterconfig.APIKeyAuth{Filename: apiKeyFile}
+	auth := filterapi.APIKeyAuth{Filename: apiKeyFile}
 	handler, err := newAPIKeyHandler(&auth)
 	require.NoError(t, err)
 	require.NotNil(t, handler)
@@ -39,7 +39,7 @@ func TestApiKeyHandler_Do(t *testing.T) {
 	require.NoError(t, err)
 	require.NoError(t, f.Sync())
 
-	auth := filterconfig.APIKeyAuth{Filename: apiKeyFile}
+	auth := filterapi.APIKeyAuth{Filename: apiKeyFile}
 	handler, err := newAPIKeyHandler(&auth)
 	require.NoError(t, err)
 	require.NotNil(t, handler)
diff --git a/internal/extproc/backendauth/aws_test.go b/internal/extproc/backendauth/aws_test.go
index d7eda29..3a3da56 100644
--- a/internal/extproc/backendauth/aws_test.go
+++ b/internal/extproc/backendauth/aws_test.go
@@ -8,14 +8,14 @@ import (
 	extprocv3 "github.com/envoyproxy/go-control-plane/envoy/service/ext_proc/v3"
 	"github.com/stretchr/testify/require"
 
-	"github.com/envoyproxy/ai-gateway/filterconfig"
+	"github.com/envoyproxy/ai-gateway/filterapi"
 )
 
 func TestNewAWSHandler(t *testing.T) {
 	t.Setenv("AWS_ACCESS_KEY_ID", "test")
 	t.Setenv("AWS_SECRET_ACCESS_KEY", "secret")
 
-	handler, err := newAWSHandler(&filterconfig.AWSAuth{})
+	handler, err := newAWSHandler(&filterapi.AWSAuth{})
 	require.NoError(t, err)
 	require.NotNil(t, handler)
 }
@@ -35,7 +35,7 @@ func TestAWSHandler_Do(t *testing.T) {
 	require.NoError(t, err)
 	require.NoError(t, file.Sync())
 
-	credentialFileHandler, err := newAWSHandler(&filterconfig.AWSAuth{
+	credentialFileHandler, err := newAWSHandler(&filterapi.AWSAuth{
 		CredentialFileName: awsCredentialFile,
 		Region:             "us-east-1",
 	})
diff --git a/internal/extproc/mocks_test.go b/internal/extproc/mocks_test.go
index f0e6842..d1f2d5f 100644
--- a/internal/extproc/mocks_test.go
+++ b/internal/extproc/mocks_test.go
@@ -12,8 +12,8 @@ import (
 	"github.com/stretchr/testify/require"
 	"google.golang.org/grpc/metadata"
 
-	"github.com/envoyproxy/ai-gateway/extprocapi"
-	"github.com/envoyproxy/ai-gateway/filterconfig"
+	"github.com/envoyproxy/ai-gateway/filterapi"
+	"github.com/envoyproxy/ai-gateway/filterapi/x"
 	"github.com/envoyproxy/ai-gateway/internal/extproc/router"
 	"github.com/envoyproxy/ai-gateway/internal/extproc/translator"
 )
@@ -21,7 +21,7 @@ import (
 var (
 	_ ProcessorIface        = &mockProcessor{}
 	_ translator.Translator = &mockTranslator{}
-	_ extprocapi.Router     = &mockRouter{}
+	_ x.Router              = &mockRouter{}
 )
 
 func newMockProcessor(_ *processorConfig, _ *slog.Logger) *mockProcessor {
@@ -111,14 +111,14 @@ type mockRouter struct {
 	t                     *testing.T
 	expHeaders            map[string]string
 	retBackendName        string
-	retVersionedAPISchema filterconfig.VersionedAPISchema
+	retVersionedAPISchema filterapi.VersionedAPISchema
 	retErr                error
 }
 
 // Calculate implements [router.Router.Calculate].
-func (m mockRouter) Calculate(headers map[string]string) (*filterconfig.Backend, error) {
+func (m mockRouter) Calculate(headers map[string]string) (*filterapi.Backend, error) {
 	require.Equal(m.t, m.expHeaders, headers)
-	b := &filterconfig.Backend{Name: m.retBackendName, Schema: m.retVersionedAPISchema}
+	b := &filterapi.Backend{Name: m.retBackendName, Schema: m.retVersionedAPISchema}
 	return b, m.retErr
 }
 
diff --git a/internal/extproc/processor_test.go b/internal/extproc/processor_test.go
index 5f9f687..862f847 100644
--- a/internal/extproc/processor_test.go
+++ b/internal/extproc/processor_test.go
@@ -10,7 +10,7 @@ import (
 	extprocv3 "github.com/envoyproxy/go-control-plane/envoy/service/ext_proc/v3"
 	"github.com/stretchr/testify/require"
 
-	"github.com/envoyproxy/ai-gateway/filterconfig"
+	"github.com/envoyproxy/ai-gateway/filterapi"
 	"github.com/envoyproxy/ai-gateway/internal/extproc/router"
 	"github.com/envoyproxy/ai-gateway/internal/extproc/translator"
 	"github.com/envoyproxy/ai-gateway/internal/llmcostcel"
@@ -74,15 +74,15 @@ func TestProcessor_ProcessResponseBody(t *testing.T) {
 		p := &Processor{translator: mt, logger: slog.Default(), config: &processorConfig{
 			metadataNamespace: "ai_gateway_llm_ns",
 			requestCosts: []processorConfigRequestCost{
-				{LLMRequestCost: &filterconfig.LLMRequestCost{Type: filterconfig.LLMRequestCostTypeOutputToken, MetadataKey: "output_token_usage"}},
-				{LLMRequestCost: &filterconfig.LLMRequestCost{Type: filterconfig.LLMRequestCostTypeInputToken, MetadataKey: "input_token_usage"}},
+				{LLMRequestCost: &filterapi.LLMRequestCost{Type: filterapi.LLMRequestCostTypeOutputToken, MetadataKey: "output_token_usage"}},
+				{LLMRequestCost: &filterapi.LLMRequestCost{Type: filterapi.LLMRequestCostTypeInputToken, MetadataKey: "input_token_usage"}},
 				{
 					celProg:        celProgInt,
-					LLMRequestCost: &filterconfig.LLMRequestCost{Type: filterconfig.LLMRequestCostTypeCELExpression, MetadataKey: "cel_int"},
+					LLMRequestCost: &filterapi.LLMRequestCost{Type: filterapi.LLMRequestCostTypeCELExpression, MetadataKey: "cel_int"},
 				},
 				{
 					celProg:        celProgUint,
-					LLMRequestCost: &filterconfig.LLMRequestCost{Type: filterconfig.LLMRequestCostTypeCELExpression, MetadataKey: "cel_uint"},
+					LLMRequestCost: &filterapi.LLMRequestCost{Type: filterapi.LLMRequestCostTypeCELExpression, MetadataKey: "cel_uint"},
 				},
 			},
 		}}
@@ -125,11 +125,11 @@ func TestProcessor_ProcessRequestBody(t *testing.T) {
 		rbp := mockRequestBodyParser{t: t, retModelName: "some-model", expPath: "/foo"}
 		rt := mockRouter{
 			t: t, expHeaders: headers, retBackendName: "some-backend",
-			retVersionedAPISchema: filterconfig.VersionedAPISchema{Name: "some-schema", Version: "v10.0"},
+			retVersionedAPISchema: filterapi.VersionedAPISchema{Name: "some-schema", Version: "v10.0"},
 		}
 		p := &Processor{config: &processorConfig{
 			bodyParser: rbp.impl, router: rt,
-			factories: make(map[filterconfig.VersionedAPISchema]translator.Factory),
+			factories: make(map[filterapi.VersionedAPISchema]translator.Factory),
 		}, requestHeaders: headers, logger: slog.Default()}
 		_, err := p.ProcessRequestBody(context.Background(), &extprocv3.HttpBody{})
 		require.ErrorContains(t, err, "failed to find factory for output schema {\"some-schema\" \"v10.0\"}")
@@ -139,12 +139,12 @@ func TestProcessor_ProcessRequestBody(t *testing.T) {
 		rbp := mockRequestBodyParser{t: t, retModelName: "some-model", expPath: "/foo"}
 		rt := mockRouter{
 			t: t, expHeaders: headers, retBackendName: "some-backend",
-			retVersionedAPISchema: filterconfig.VersionedAPISchema{Name: "some-schema", Version: "v10.0"},
+			retVersionedAPISchema: filterapi.VersionedAPISchema{Name: "some-schema", Version: "v10.0"},
 		}
 		factory := mockTranslatorFactory{t: t, retErr: errors.New("test error"), expPath: "/foo"}
 		p := &Processor{config: &processorConfig{
 			bodyParser: rbp.impl, router: rt,
-			factories: map[filterconfig.VersionedAPISchema]translator.Factory{
+			factories: map[filterapi.VersionedAPISchema]translator.Factory{
 				{Name: "some-schema", Version: "v10.0"}: factory.impl,
 			},
 		}, requestHeaders: headers, logger: slog.Default()}
@@ -156,12 +156,12 @@ func TestProcessor_ProcessRequestBody(t *testing.T) {
 		rbp := mockRequestBodyParser{t: t, retModelName: "some-model", expPath: "/foo"}
 		rt := mockRouter{
 			t: t, expHeaders: headers, retBackendName: "some-backend",
-			retVersionedAPISchema: filterconfig.VersionedAPISchema{Name: "some-schema", Version: "v10.0"},
+			retVersionedAPISchema: filterapi.VersionedAPISchema{Name: "some-schema", Version: "v10.0"},
 		}
 		factory := mockTranslatorFactory{t: t, retTranslator: mockTranslator{t: t, retErr: errors.New("test error")}, expPath: "/foo"}
 		p := &Processor{config: &processorConfig{
 			bodyParser: rbp.impl, router: rt,
-			factories: map[filterconfig.VersionedAPISchema]translator.Factory{
+			factories: map[filterapi.VersionedAPISchema]translator.Factory{
 				{Name: "some-schema", Version: "v10.0"}: factory.impl,
 			},
 		}, requestHeaders: headers, logger: slog.Default()}
@@ -174,7 +174,7 @@ func TestProcessor_ProcessRequestBody(t *testing.T) {
 		rbp := mockRequestBodyParser{t: t, retModelName: "some-model", expPath: "/foo", retRb: someBody}
 		rt := mockRouter{
 			t: t, expHeaders: headers, retBackendName: "some-backend",
-			retVersionedAPISchema: filterconfig.VersionedAPISchema{Name: "some-schema", Version: "v10.0"},
+			retVersionedAPISchema: filterapi.VersionedAPISchema{Name: "some-schema", Version: "v10.0"},
 		}
 		headerMut := &extprocv3.HeaderMutation{}
 		bodyMut := &extprocv3.BodyMutation{}
@@ -182,7 +182,7 @@ func TestProcessor_ProcessRequestBody(t *testing.T) {
 		factory := mockTranslatorFactory{t: t, retTranslator: mt, expPath: "/foo"}
 		p := &Processor{config: &processorConfig{
 			bodyParser: rbp.impl, router: rt,
-			factories: map[filterconfig.VersionedAPISchema]translator.Factory{
+			factories: map[filterapi.VersionedAPISchema]translator.Factory{
 				{Name: "some-schema", Version: "v10.0"}: factory.impl,
 			},
 			selectedBackendHeaderKey: "x-ai-gateway-backend-key",
diff --git a/internal/extproc/router/request_body_test.go b/internal/extproc/router/request_body_test.go
index e42469e..dc76f9f 100644
--- a/internal/extproc/router/request_body_test.go
+++ b/internal/extproc/router/request_body_test.go
@@ -7,18 +7,18 @@ import (
 	extprocv3 "github.com/envoyproxy/go-control-plane/envoy/service/ext_proc/v3"
 	"github.com/stretchr/testify/require"
 
-	"github.com/envoyproxy/ai-gateway/filterconfig"
+	"github.com/envoyproxy/ai-gateway/filterapi"
 	"github.com/envoyproxy/ai-gateway/internal/apischema/openai"
 )
 
 func TestNewRequestBodyParser(t *testing.T) {
 	t.Run("ok", func(t *testing.T) {
-		res, err := NewRequestBodyParser(filterconfig.VersionedAPISchema{Name: filterconfig.APISchemaOpenAI})
+		res, err := NewRequestBodyParser(filterapi.VersionedAPISchema{Name: filterapi.APISchemaOpenAI})
 		require.NotNil(t, res)
 		require.NoError(t, err)
 	})
 	t.Run("error", func(t *testing.T) {
-		res, err := NewRequestBodyParser(filterconfig.VersionedAPISchema{Name: "foo"})
+		res, err := NewRequestBodyParser(filterapi.VersionedAPISchema{Name: "foo"})
 		require.Nil(t, res)
 		require.Error(t, err)
 	})
diff --git a/internal/extproc/router/router_test.go b/internal/extproc/router/router_test.go
index 15f7f2c..c6a359b 100644
--- a/internal/extproc/router/router_test.go
+++ b/internal/extproc/router/router_test.go
@@ -5,20 +5,20 @@ import (
 
 	"github.com/stretchr/testify/require"
 
-	"github.com/envoyproxy/ai-gateway/extprocapi"
-	"github.com/envoyproxy/ai-gateway/filterconfig"
+	"github.com/envoyproxy/ai-gateway/filterapi"
+	"github.com/envoyproxy/ai-gateway/filterapi/x"
 )
 
-// dummyCustomRouter implements [extprocapi.Router].
+// dummyCustomRouter implements [filterapi.Router].
 type dummyCustomRouter struct{ called bool }
 
-func (c *dummyCustomRouter) Calculate(map[string]string) (*filterconfig.Backend, error) {
+func (c *dummyCustomRouter) Calculate(map[string]string) (*filterapi.Backend, error) {
 	c.called = true
 	return nil, nil
 }
 
 func TestRouter_NewRouter_Custom(t *testing.T) {
-	r, err := NewRouter(&filterconfig.Config{}, func(defaultRouter extprocapi.Router, config *filterconfig.Config) extprocapi.Router {
+	r, err := NewRouter(&filterapi.Config{}, func(defaultRouter x.Router, config *filterapi.Config) x.Router {
 		require.NotNil(t, defaultRouter)
 		_, ok := defaultRouter.(*router)
 		require.True(t, ok) // Checking if the default router is correctly passed.
@@ -34,23 +34,23 @@ func TestRouter_NewRouter_Custom(t *testing.T) {
 }
 
 func TestRouter_Calculate(t *testing.T) {
-	outSchema := filterconfig.VersionedAPISchema{Name: filterconfig.APISchemaOpenAI}
-	_r, err := NewRouter(&filterconfig.Config{
-		Rules: []filterconfig.RouteRule{
+	outSchema := filterapi.VersionedAPISchema{Name: filterapi.APISchemaOpenAI}
+	_r, err := NewRouter(&filterapi.Config{
+		Rules: []filterapi.RouteRule{
 			{
-				Backends: []filterconfig.Backend{
+				Backends: []filterapi.Backend{
 					{Name: "foo", Schema: outSchema, Weight: 1},
 					{Name: "bar", Schema: outSchema, Weight: 3},
 				},
-				Headers: []filterconfig.HeaderMatch{
+				Headers: []filterapi.HeaderMatch{
 					{Name: "x-model-name", Value: "llama3.3333"},
 				},
 			},
 			{
-				Backends: []filterconfig.Backend{
+				Backends: []filterapi.Backend{
 					{Name: "openai", Schema: outSchema},
 				},
-				Headers: []filterconfig.HeaderMatch{
+				Headers: []filterapi.HeaderMatch{
 					{Name: "x-model-name", Value: "gpt4.4444"},
 				},
 			},
@@ -87,15 +87,15 @@ func TestRouter_Calculate(t *testing.T) {
 }
 
 func TestRouter_selectBackendFromRule(t *testing.T) {
-	_r, err := NewRouter(&filterconfig.Config{}, nil)
+	_r, err := NewRouter(&filterapi.Config{}, nil)
 	require.NoError(t, err)
 	r, ok := _r.(*router)
 	require.True(t, ok)
 
-	outSchema := filterconfig.VersionedAPISchema{Name: filterconfig.APISchemaOpenAI}
+	outSchema := filterapi.VersionedAPISchema{Name: filterapi.APISchemaOpenAI}
 
-	rule := &filterconfig.RouteRule{
-		Backends: []filterconfig.Backend{
+	rule := &filterapi.RouteRule{
+		Backends: []filterapi.Backend{
 			{Name: "foo", Schema: outSchema, Weight: 1},
 			{Name: "bar", Schema: outSchema, Weight: 3},
 		},
diff --git a/internal/extproc/server_test.go b/internal/extproc/server_test.go
index f82551e..4d44ebf 100644
--- a/internal/extproc/server_test.go
+++ b/internal/extproc/server_test.go
@@ -15,7 +15,7 @@ import (
 	"google.golang.org/grpc/health/grpc_health_v1"
 	"google.golang.org/grpc/status"
 
-	"github.com/envoyproxy/ai-gateway/filterconfig"
+	"github.com/envoyproxy/ai-gateway/filterapi"
 	"github.com/envoyproxy/ai-gateway/internal/llmcostcel"
 )
 
@@ -30,29 +30,29 @@ func requireNewServerWithMockProcessor(t *testing.T) *Server[*mockProcessor] {
 func TestServer_LoadConfig(t *testing.T) {
 	t.Run("invalid input schema", func(t *testing.T) {
 		s := requireNewServerWithMockProcessor(t)
-		err := s.LoadConfig(&filterconfig.Config{
-			Schema: filterconfig.VersionedAPISchema{Name: "some-invalid-schema"},
+		err := s.LoadConfig(&filterapi.Config{
+			Schema: filterapi.VersionedAPISchema{Name: "some-invalid-schema"},
 		})
 		require.Error(t, err)
 		require.ErrorContains(t, err, "cannot create request body parser")
 	})
 	t.Run("ok", func(t *testing.T) {
-		config := &filterconfig.Config{
+		config := &filterapi.Config{
 			MetadataNamespace: "ns",
-			LLMRequestCosts: []filterconfig.LLMRequestCost{
-				{MetadataKey: "key", Type: filterconfig.LLMRequestCostTypeOutputToken},
-				{MetadataKey: "cel_key", Type: filterconfig.LLMRequestCostTypeCELExpression, CELExpression: "1 + 1"},
+			LLMRequestCosts: []filterapi.LLMRequestCost{
+				{MetadataKey: "key", Type: filterapi.LLMRequestCostTypeOutputToken},
+				{MetadataKey: "cel_key", Type: filterapi.LLMRequestCostTypeCELExpression, CELExpression: "1 + 1"},
 			},
-			Schema:                   filterconfig.VersionedAPISchema{Name: filterconfig.APISchemaOpenAI},
+			Schema:                   filterapi.VersionedAPISchema{Name: filterapi.APISchemaOpenAI},
 			SelectedBackendHeaderKey: "x-ai-eg-selected-backend",
 			ModelNameHeaderKey:       "x-model-name",
-			Rules: []filterconfig.RouteRule{
+			Rules: []filterapi.RouteRule{
 				{
-					Backends: []filterconfig.Backend{
-						{Name: "kserve", Schema: filterconfig.VersionedAPISchema{Name: filterconfig.APISchemaOpenAI}},
-						{Name: "awsbedrock", Schema: filterconfig.VersionedAPISchema{Name: filterconfig.APISchemaAWSBedrock}},
+					Backends: []filterapi.Backend{
+						{Name: "kserve", Schema: filterapi.VersionedAPISchema{Name: filterapi.APISchemaOpenAI}},
+						{Name: "awsbedrock", Schema: filterapi.VersionedAPISchema{Name: filterapi.APISchemaAWSBedrock}},
 					},
-					Headers: []filterconfig.HeaderMatch{
+					Headers: []filterapi.HeaderMatch{
 						{
 							Name:  "x-model-name",
 							Value: "llama3.3333",
@@ -60,10 +60,10 @@ func TestServer_LoadConfig(t *testing.T) {
 					},
 				},
 				{
-					Backends: []filterconfig.Backend{
-						{Name: "openai", Schema: filterconfig.VersionedAPISchema{Name: filterconfig.APISchemaOpenAI}},
+					Backends: []filterapi.Backend{
+						{Name: "openai", Schema: filterapi.VersionedAPISchema{Name: filterapi.APISchemaOpenAI}},
 					},
-					Headers: []filterconfig.HeaderMatch{
+					Headers: []filterapi.HeaderMatch{
 						{
 							Name:  "x-model-name",
 							Value: "gpt4.4444",
@@ -83,13 +83,13 @@ func TestServer_LoadConfig(t *testing.T) {
 		require.Equal(t, "x-ai-eg-selected-backend", s.config.selectedBackendHeaderKey)
 		require.Equal(t, "x-model-name", s.config.modelNameHeaderKey)
 		require.Len(t, s.config.factories, 2)
-		require.NotNil(t, s.config.factories[filterconfig.VersionedAPISchema{Name: filterconfig.APISchemaOpenAI}])
-		require.NotNil(t, s.config.factories[filterconfig.VersionedAPISchema{Name: filterconfig.APISchemaAWSBedrock}])
+		require.NotNil(t, s.config.factories[filterapi.VersionedAPISchema{Name: filterapi.APISchemaOpenAI}])
+		require.NotNil(t, s.config.factories[filterapi.VersionedAPISchema{Name: filterapi.APISchemaAWSBedrock}])
 
 		require.Len(t, s.config.requestCosts, 2)
-		require.Equal(t, filterconfig.LLMRequestCostTypeOutputToken, s.config.requestCosts[0].Type)
+		require.Equal(t, filterapi.LLMRequestCostTypeOutputToken, s.config.requestCosts[0].Type)
 		require.Equal(t, "key", s.config.requestCosts[0].MetadataKey)
-		require.Equal(t, filterconfig.LLMRequestCostTypeCELExpression, s.config.requestCosts[1].Type)
+		require.Equal(t, filterapi.LLMRequestCostTypeCELExpression, s.config.requestCosts[1].Type)
 		require.Equal(t, "1 + 1", s.config.requestCosts[1].CELExpression)
 		prog := s.config.requestCosts[1].celProg
 		require.NotNil(t, prog)
diff --git a/internal/extproc/translator/translator_test.go b/internal/extproc/translator/translator_test.go
index 64c4202..bec8389 100644
--- a/internal/extproc/translator/translator_test.go
+++ b/internal/extproc/translator/translator_test.go
@@ -5,21 +5,21 @@ import (
 
 	"github.com/stretchr/testify/require"
 
-	"github.com/envoyproxy/ai-gateway/filterconfig"
+	"github.com/envoyproxy/ai-gateway/filterapi"
 )
 
 func TestNewFactory(t *testing.T) {
 	t.Run("error", func(t *testing.T) {
 		_, err := NewFactory(
-			filterconfig.VersionedAPISchema{Name: "Foo", Version: "v100"},
-			filterconfig.VersionedAPISchema{Name: "Bar", Version: "v123"},
+			filterapi.VersionedAPISchema{Name: "Foo", Version: "v100"},
+			filterapi.VersionedAPISchema{Name: "Bar", Version: "v123"},
 		)
 		require.ErrorContains(t, err, "unsupported API schema combination: client={Foo v100}, backend={Bar v123}")
 	})
 	t.Run("openai to openai", func(t *testing.T) {
 		f, err := NewFactory(
-			filterconfig.VersionedAPISchema{Name: filterconfig.APISchemaOpenAI},
-			filterconfig.VersionedAPISchema{Name: filterconfig.APISchemaOpenAI},
+			filterapi.VersionedAPISchema{Name: filterapi.APISchemaOpenAI},
+			filterapi.VersionedAPISchema{Name: filterapi.APISchemaOpenAI},
 		)
 		require.NoError(t, err)
 		require.NotNil(t, f)
@@ -32,8 +32,8 @@ func TestNewFactory(t *testing.T) {
 	})
 	t.Run("openai to aws bedrock", func(t *testing.T) {
 		f, err := NewFactory(
-			filterconfig.VersionedAPISchema{Name: filterconfig.APISchemaOpenAI},
-			filterconfig.VersionedAPISchema{Name: filterconfig.APISchemaAWSBedrock},
+			filterapi.VersionedAPISchema{Name: filterapi.APISchemaOpenAI},
+			filterapi.VersionedAPISchema{Name: filterapi.APISchemaAWSBedrock},
 		)
 		require.NoError(t, err)
 		require.NotNil(t, f)
diff --git a/internal/extproc/watcher_test.go b/internal/extproc/watcher_test.go
index 46000c6..3f383bf 100644
--- a/internal/extproc/watcher_test.go
+++ b/internal/extproc/watcher_test.go
@@ -12,24 +12,24 @@ import (
 
 	"github.com/stretchr/testify/require"
 
-	"github.com/envoyproxy/ai-gateway/filterconfig"
+	"github.com/envoyproxy/ai-gateway/filterapi"
 )
 
 // mockReceiver is a mock implementation of Receiver.
 type mockReceiver struct {
-	cfg *filterconfig.Config
+	cfg *filterapi.Config
 	mux sync.Mutex
 }
 
 // LoadConfig implements ConfigReceiver.
-func (m *mockReceiver) LoadConfig(cfg *filterconfig.Config) error {
+func (m *mockReceiver) LoadConfig(cfg *filterapi.Config) error {
 	m.mux.Lock()
 	defer m.mux.Unlock()
 	m.cfg = cfg
 	return nil
 }
 
-func (m *mockReceiver) getConfig() *filterconfig.Config {
+func (m *mockReceiver) getConfig() *filterapi.Config {
 	m.mux.Lock()
 	defer m.mux.Unlock()
 	return m.cfg
diff --git a/tests/extproc/custom_extproc_test.go b/tests/extproc/custom_extproc_test.go
index 44f21a1..b86ef2e 100644
--- a/tests/extproc/custom_extproc_test.go
+++ b/tests/extproc/custom_extproc_test.go
@@ -15,7 +15,7 @@ import (
 	"github.com/openai/openai-go/option"
 	"github.com/stretchr/testify/require"
 
-	"github.com/envoyproxy/ai-gateway/filterconfig"
+	"github.com/envoyproxy/ai-gateway/filterapi"
 	"github.com/envoyproxy/ai-gateway/tests/internal/testupstreamlib"
 )
 
@@ -25,15 +25,15 @@ func TestExtProcCustomRouter(t *testing.T) {
 	requireRunEnvoy(t, "/dev/null")
 	requireTestUpstream(t)
 	configPath := t.TempDir() + "/extproc-config.yaml"
-	requireWriteFilterConfig(t, configPath, &filterconfig.Config{
+	requireWriteFilterConfig(t, configPath, &filterapi.Config{
 		Schema: openAISchema,
 		// This can be any header key, but it must match the envoy.yaml routing configuration.
 		SelectedBackendHeaderKey: "x-selected-backend-name",
 		ModelNameHeaderKey:       "x-model-name",
-		Rules: []filterconfig.RouteRule{
+		Rules: []filterapi.RouteRule{
 			{
-				Backends: []filterconfig.Backend{{Name: "testupstream", Schema: openAISchema}},
-				Headers:  []filterconfig.HeaderMatch{{Name: "x-model-name", Value: "something-cool"}},
+				Backends: []filterapi.Backend{{Name: "testupstream", Schema: openAISchema}},
+				Headers:  []filterapi.HeaderMatch{{Name: "x-model-name", Value: "something-cool"}},
 			},
 		},
 	})
diff --git a/tests/extproc/extproc_test.go b/tests/extproc/extproc_test.go
index cb0be1f..950c3be 100644
--- a/tests/extproc/extproc_test.go
+++ b/tests/extproc/extproc_test.go
@@ -16,7 +16,7 @@ import (
 	"github.com/stretchr/testify/require"
 	"sigs.k8s.io/yaml"
 
-	"github.com/envoyproxy/ai-gateway/filterconfig"
+	"github.com/envoyproxy/ai-gateway/filterapi"
 )
 
 const listenerAddress = "http://localhost:1062"
@@ -25,14 +25,14 @@ const listenerAddress = "http://localhost:1062"
 var envoyYamlBase string
 
 var (
-	openAISchema     = filterconfig.VersionedAPISchema{Name: filterconfig.APISchemaOpenAI}
-	awsBedrockSchema = filterconfig.VersionedAPISchema{Name: filterconfig.APISchemaAWSBedrock}
+	openAISchema     = filterapi.VersionedAPISchema{Name: filterapi.APISchemaOpenAI}
+	awsBedrockSchema = filterapi.VersionedAPISchema{Name: filterapi.APISchemaAWSBedrock}
 )
 
 // requireExtProc starts the external processor with the provided executable and configPath
 // with additional environment variables.
 //
-// The config must be in YAML format specified in [filterconfig.Config] type.
+// The config must be in YAML format specified in [filterapi.Config] type.
 func requireExtProc(t *testing.T, stdout io.Writer, executable, configPath string, envs ...string) {
 	cmd := exec.Command(executable)
 	cmd.Stdout = stdout
@@ -93,8 +93,8 @@ func getEnvVarOrSkip(t *testing.T, envVar string) string {
 	return value
 }
 
-// requireWriteFilterConfig writes the provided [filterconfig.Config] to the configPath in YAML format.
-func requireWriteFilterConfig(t *testing.T, configPath string, config *filterconfig.Config) {
+// requireWriteFilterConfig writes the provided [filterapi.Config] to the configPath in YAML format.
+func requireWriteFilterConfig(t *testing.T, configPath string, config *filterapi.Config) {
 	configBytes, err := yaml.Marshal(config)
 	require.NoError(t, err)
 	require.NoError(t, os.WriteFile(configPath, configBytes, 0o600))
diff --git a/tests/extproc/real_providers_test.go b/tests/extproc/real_providers_test.go
index 0dd0a92..1178824 100644
--- a/tests/extproc/real_providers_test.go
+++ b/tests/extproc/real_providers_test.go
@@ -17,7 +17,7 @@ import (
 	"github.com/openai/openai-go/option"
 	"github.com/stretchr/testify/require"
 
-	"github.com/envoyproxy/ai-gateway/filterconfig"
+	"github.com/envoyproxy/ai-gateway/filterapi"
 )
 
 // TestRealProviders tests the end-to-end flow of the external processor with Envoy and real providers.
@@ -58,31 +58,31 @@ func TestWithRealProviders(t *testing.T) {
 	require.NoError(t, err)
 	require.NoError(t, awsFile.Sync())
 
-	requireWriteFilterConfig(t, configPath, &filterconfig.Config{
+	requireWriteFilterConfig(t, configPath, &filterapi.Config{
 		MetadataNamespace: "ai_gateway_llm_ns",
-		LLMRequestCosts: []filterconfig.LLMRequestCost{
-			{MetadataKey: "used_token", Type: filterconfig.LLMRequestCostTypeInputToken},
-			{MetadataKey: "some_cel", Type: filterconfig.LLMRequestCostTypeCELExpression, CELExpression: "1+1"},
+		LLMRequestCosts: []filterapi.LLMRequestCost{
+			{MetadataKey: "used_token", Type: filterapi.LLMRequestCostTypeInputToken},
+			{MetadataKey: "some_cel", Type: filterapi.LLMRequestCostTypeCELExpression, CELExpression: "1+1"},
 		},
 		Schema: openAISchema,
 		// This can be any header key, but it must match the envoy.yaml routing configuration.
 		SelectedBackendHeaderKey: "x-selected-backend-name",
 		ModelNameHeaderKey:       "x-model-name",
-		Rules: []filterconfig.RouteRule{
+		Rules: []filterapi.RouteRule{
 			{
-				Backends: []filterconfig.Backend{{Name: "openai", Schema: openAISchema, Auth: &filterconfig.BackendAuth{
-					APIKey: &filterconfig.APIKeyAuth{Filename: apiKeyFilePath},
+				Backends: []filterapi.Backend{{Name: "openai", Schema: openAISchema, Auth: &filterapi.BackendAuth{
+					APIKey: &filterapi.APIKeyAuth{Filename: apiKeyFilePath},
 				}}},
-				Headers: []filterconfig.HeaderMatch{{Name: "x-model-name", Value: "gpt-4o-mini"}},
+				Headers: []filterapi.HeaderMatch{{Name: "x-model-name", Value: "gpt-4o-mini"}},
 			},
 			{
-				Backends: []filterconfig.Backend{
-					{Name: "aws-bedrock", Schema: awsBedrockSchema, Auth: &filterconfig.BackendAuth{AWSAuth: &filterconfig.AWSAuth{
+				Backends: []filterapi.Backend{
+					{Name: "aws-bedrock", Schema: awsBedrockSchema, Auth: &filterapi.BackendAuth{AWSAuth: &filterapi.AWSAuth{
 						CredentialFileName: awsFilePath,
 						Region:             "us-east-1",
 					}}},
 				},
-				Headers: []filterconfig.HeaderMatch{
+				Headers: []filterapi.HeaderMatch{
 					{Name: "x-model-name", Value: "us.meta.llama3-2-1b-instruct-v1:0"},
 					{Name: "x-model-name", Value: "us.anthropic.claude-3-5-sonnet-20240620-v1:0"},
 				},
diff --git a/tests/extproc/testupstream_test.go b/tests/extproc/testupstream_test.go
index 6354ddf..eb426fe 100644
--- a/tests/extproc/testupstream_test.go
+++ b/tests/extproc/testupstream_test.go
@@ -15,7 +15,7 @@ import (
 	"github.com/google/go-cmp/cmp"
 	"github.com/stretchr/testify/require"
 
-	"github.com/envoyproxy/ai-gateway/filterconfig"
+	"github.com/envoyproxy/ai-gateway/filterapi"
 	"github.com/envoyproxy/ai-gateway/tests/internal/testupstreamlib"
 )
 
@@ -29,23 +29,23 @@ func TestWithTestUpstream(t *testing.T) {
 	configPath := t.TempDir() + "/extproc-config.yaml"
 	requireTestUpstream(t)
 
-	requireWriteFilterConfig(t, configPath, &filterconfig.Config{
+	requireWriteFilterConfig(t, configPath, &filterapi.Config{
 		MetadataNamespace: "ai_gateway_llm_ns",
-		LLMRequestCosts: []filterconfig.LLMRequestCost{
-			{MetadataKey: "used_token", Type: filterconfig.LLMRequestCostTypeInputToken},
+		LLMRequestCosts: []filterapi.LLMRequestCost{
+			{MetadataKey: "used_token", Type: filterapi.LLMRequestCostTypeInputToken},
 		},
 		Schema: openAISchema,
 		// This can be any header key, but it must match the envoy.yaml routing configuration.
 		SelectedBackendHeaderKey: "x-selected-backend-name",
 		ModelNameHeaderKey:       "x-model-name",
-		Rules: []filterconfig.RouteRule{
+		Rules: []filterapi.RouteRule{
 			{
-				Backends: []filterconfig.Backend{{Name: "testupstream", Schema: openAISchema}},
-				Headers:  []filterconfig.HeaderMatch{{Name: "x-test-backend", Value: "openai"}},
+				Backends: []filterapi.Backend{{Name: "testupstream", Schema: openAISchema}},
+				Headers:  []filterapi.HeaderMatch{{Name: "x-test-backend", Value: "openai"}},
 			},
 			{
-				Backends: []filterconfig.Backend{{Name: "testupstream", Schema: awsBedrockSchema}},
-				Headers:  []filterconfig.HeaderMatch{{Name: "x-test-backend", Value: "aws-bedrock"}},
+				Backends: []filterapi.Backend{{Name: "testupstream", Schema: awsBedrockSchema}},
+				Headers:  []filterapi.HeaderMatch{{Name: "x-test-backend", Value: "aws-bedrock"}},
 			},
 		},
 	})