Skip to content

Commit 92cdb00

Browse files
Pluggable BBR framework (WiP)
1 parent 05081ee commit 92cdb00

File tree

13 files changed

+118
-150
lines changed

13 files changed

+118
-150
lines changed

config/charts/body-based-routing/templates/_helpers.tpl

Lines changed: 0 additions & 12 deletions
This file was deleted.

config/charts/body-based-routing/templates/bbr.yaml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,6 @@ spec:
2828
- containerPort: {{ .Values.bbr.port }}
2929
# health check
3030
- containerPort: {{ .Values.bbr.healthCheckPort }}
31-
envFrom:
32-
- configMapRef:
33-
name: {{ include "body-based-routing.fullname" . }}-plugins-config
3431
---
3532
apiVersion: v1
3633
kind: Service

config/charts/body-based-routing/templates/configmap.yaml

Lines changed: 0 additions & 22 deletions
This file was deleted.

config/charts/body-based-routing/values.yaml

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,6 @@ provider:
1919
inferenceGateway:
2020
name: inference-gateway
2121

22-
plugins:
23-
requestChain:
24-
# - type: ModelSelector
25-
# name: semantic-model-selector
26-
- type: MetadataExtractor
27-
name: simple-model-extractor
28-
29-
responseChain:
30-
# - type: GuardRail
31-
# name: pid-disclosure-blocker
32-
33-
metaDataKeys:
34-
- model
35-
# - temperature
36-
# - max_tokens
22+
env:
23+
- name: REQUEST_PLUGINS_CHAIN
24+
value: "simple-model-extractor"

pkg/bbr/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Body-Based Routing
2-
This package provides an extension that can be deployed to write the `model`
2+
By deafult this package provides a plugable extension that can be to set the `model`
33
HTTP body parameter as a header (`X-Gateway-Model-Name`) so as to enable routing capabilities on the
44
model name.
55

pkg/bbr/framework/interfaces.go

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ import (
2020
bbrplugins "sigs.k8s.io/gateway-api-inference-extension/pkg/bbr/plugins"
2121
)
2222

23+
const (
24+
RequestPluginChain = "REQUEST_PLUGINS_CHAIN"
25+
ResponsePluginChain = "RESPONSE_PLUGINS_CHAIN"
26+
MaxPluginChainLength = 10 //arbitrary number that can be changed **later**; it's here to short-circuit long inputs
27+
MaxNumberOfPlugins = 100 //arbitrary number that can be changed **later**; can be used to optimize memory allocation for the registry **later**
28+
)
29+
2330
// placeholder for Plugin constructors
2431
type PluginFactoryFunc func() bbrplugins.BBRPlugin //any no-argument function that returns bbrplugins.BBRPlugin can be assigned to this type (including a constructor function)
2532

@@ -51,11 +58,3 @@ type PluginsChain interface {
5158
Run(bodyBytes []byte, metaDataKeys []string, registry PluginRegistry) (map[string]string, []byte, error) //return potentially mutated body and all headers map safely merged
5259
String() string
5360
}
54-
55-
// ------------------------- Supported plugin interfaces -----------------------------------------
56-
// Extend this map when new interfaces and/or implementations are added
57-
var SupportedInterfaces = map[string][]string{
58-
"ModelSelector": {"semantic-model-selector"}, //future
59-
"GuardRail": {"bad-words-blocker", "pid-disclosure-blocker"}, //future
60-
"MetadataExtractor": {"simple-model-extractor"},
61-
}

pkg/bbr/framework/registry.go

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ package framework
1818

1919
import (
2020
"fmt"
21-
"slices"
2221

2322
bbrplugins "sigs.k8s.io/gateway-api-inference-extension/pkg/bbr/plugins"
2423
)
@@ -44,10 +43,10 @@ func NewPluginRegistry() PluginRegistry {
4443

4544
// Register a plugin factory by type key (e.g., "ModelSelector", "MetadataExtractor")
4645
func (r *pluginRegistry) RegisterFactory(typeKey string, factory PluginFactoryFunc) error {
47-
//validate whether this interface is supported
48-
_, ok := SupportedInterfaces[typeKey]
49-
if !ok {
50-
err := fmt.Errorf("non-supported plugin interface type %s", typeKey)
46+
//validate whether already registered
47+
alreadyRegistered := r.ContainsFactory(typeKey)
48+
if alreadyRegistered {
49+
err := fmt.Errorf("factory fot plugin interface type %s is already registered", typeKey)
5150
return err
5251
}
5352
r.pluginsFactory[typeKey] = factory
@@ -58,16 +57,13 @@ func (r *pluginRegistry) RegisterFactory(typeKey string, factory PluginFactoryFu
5857
// Register a plugin instance (created through the appropriate factory)
5958
func (r *pluginRegistry) RegisterPlugin(plugin bbrplugins.BBRPlugin) error {
6059
//validate whether this interface is supported
61-
implementations, ok := SupportedInterfaces[plugin.TypedName().Type]
62-
if !ok {
63-
err := fmt.Errorf("non-supported plugin interface type %s", plugin.TypedName().Type)
64-
return err
65-
}
66-
//validate whether this implementation for this plugin interface is supported
67-
if len(implementations) == 0 || !slices.Contains(implementations, plugin.TypedName().Name) {
68-
err := fmt.Errorf("no implementation for plugin interface type %s", plugin.TypedName().Type)
60+
alreadyRegistered := r.ContainsPlugin(plugin.TypedName().Type)
61+
62+
if alreadyRegistered {
63+
err := fmt.Errorf("plugin implementing interface type %s is already registered", plugin.TypedName().Type)
6964
return err
7065
}
66+
7167
// validate that the factory for this plugin is registered: always register factory before the plugin
7268
if _, ok := r.pluginsFactory[plugin.TypedName().Type]; !ok {
7369
err := fmt.Errorf("no plugin factory registered for plugin interface type %s", plugin.TypedName().Type)

pkg/bbr/handlers/request_test.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,19 @@ import (
3030
crmetrics "sigs.k8s.io/controller-runtime/pkg/metrics"
3131

3232
"sigs.k8s.io/gateway-api-inference-extension/pkg/bbr/metrics"
33+
utils "sigs.k8s.io/gateway-api-inference-extension/pkg/bbr/utils"
3334
logutil "sigs.k8s.io/gateway-api-inference-extension/pkg/epp/util/logging"
3435
)
3536

3637
func TestHandleRequestBody(t *testing.T) {
3738
metrics.Register()
3839
ctx := logutil.NewTestLoggerIntoContext(context.Background())
3940

41+
//set environment variables expected by the code under test
42+
//testing a minimal configuration
43+
//request plugin chain always contains the default bbr plugin that extracts a model name and sets it on the X-Gateway-Model-Name header
44+
t.Setenv("REQUEST_PLUGINS_CHAIN", "simple_model_extractor")
45+
4046
tests := []struct {
4147
name string
4248
body map[string]any
@@ -176,9 +182,15 @@ func TestHandleRequestBody(t *testing.T) {
176182
},
177183
}
178184

185+
//Initialize PluginRegistry and request/response PluginsChain instances based on the minimal configuration setting vi env vars
186+
registry, requestChain, responseChain, metaDataKeys, err := utils.InitPlugins()
187+
if err != nil {
188+
t.Fatalf("processRequestBody(): %v", err)
189+
}
190+
179191
for _, test := range tests {
180192
t.Run(test.name, func(t *testing.T) {
181-
server := &Server{streaming: test.streaming}
193+
server := NewServer(test.streaming, registry, requestChain, responseChain, metaDataKeys)
182194
bodyBytes, _ := json.Marshal(test.body)
183195
resp, err := server.HandleRequestBody(ctx, bodyBytes)
184196
if err != nil {

pkg/bbr/handlers/server_test.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,18 @@ import (
2727
"sigs.k8s.io/controller-runtime/pkg/log"
2828

2929
bbrplugins "sigs.k8s.io/gateway-api-inference-extension/pkg/bbr/plugins"
30-
bbrutils "sigs.k8s.io/gateway-api-inference-extension/pkg/bbr/utils"
30+
utils "sigs.k8s.io/gateway-api-inference-extension/pkg/bbr/utils"
3131
logutil "sigs.k8s.io/gateway-api-inference-extension/pkg/epp/util/logging"
3232
)
3333

3434
func TestProcessRequestBody(t *testing.T) {
3535
ctx := logutil.NewTestLoggerIntoContext(context.Background())
3636

37+
//set environment variables expected by the code under test
38+
//testing a minimal configuration
39+
//request plugin chain always contains the default bbr plugin that extracts a model name and sets it on the X-Gateway-Model-Name header
40+
t.Setenv("REQUEST_PLUGINS_CHAIN", "simple_model_extractor")
41+
3742
cases := []struct {
3843
desc string
3944
streaming bool
@@ -127,14 +132,15 @@ func TestProcessRequestBody(t *testing.T) {
127132
},
128133
}
129134

130-
//Initialize PluginRegistry and request/response PluginsChain instances
131-
//Change to hermetic
132-
registry, requestChain, responseChain, metaDataKeys, _ := bbrutils.InitPlugins()
135+
//Initialize PluginRegistry and request/response PluginsChain instances based on the minimal configuration setting vi env vars
136+
registry, requestChain, responseChain, metaDataKeys, err := utils.InitPlugins()
137+
if err != nil {
138+
t.Fatalf("processRequestBody(): %v", err)
139+
}
133140

134141
for _, tc := range cases {
135142
t.Run(tc.desc, func(t *testing.T) {
136143
srv := NewServer(tc.streaming, registry, requestChain, responseChain, metaDataKeys)
137-
//srv := NewServer(tc.streaming)
138144
streamedBody := &streamedBody{}
139145
for i, body := range tc.bodys {
140146
got, err := srv.processRequestBody(context.Background(), body, streamedBody, log.FromContext(ctx))

pkg/bbr/plugins/interfaces.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,21 @@ import (
2020
"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/plugins"
2121
)
2222

23-
// ---------------------------- Well-known custom headers ---------------------------------
24-
const ModelHeader = "X-Gateway-Model-Name"
25-
2623
// ----------------------------------------------------------------------------------------
2724
// ------------------------------------ Defaults ------------------------------------------
28-
const DefaultPluginType = "MetadataExtractor"
29-
const DefaultPluginImplementation = "simple-model-selector"
25+
const (
26+
//The deafult plugin will always be configured for request plugins chain
27+
DefaultPluginType = "MetadataExtractor"
28+
//Even though BBRPlugin is not a K8s resource yet, let's make its naming compliant with K8s resource naming
29+
// Allows: lowercase letters, digits, hyphens, dots.
30+
// Must start and end with a lowercase alphanumeric character.
31+
// Middle characters group can contain lowercase alphanumerics, hyphens, and dots
32+
// Middle and rightmost groups are optional
33+
PluginNamePattern = `^[a-z0-9]([-a-z0-9.]*[a-z0-9])?$`
34+
MaxPluginNameLength = 253
35+
//Well-known custom header set to a model name
36+
ModelHeader = "X-Gateway-Model-Name"
37+
)
3038

3139
// BBRPlugin defines the interface for plugins in the BBR framework should never mutate the body directly.
3240
type BBRPlugin interface {

0 commit comments

Comments
 (0)