Skip to content

Commit ac53bff

Browse files
authored
Allow skipped requests to go through scheduling profiles (#1387)
* make sure skipped request is can also go through profiles Signed-off-by: bobzetian <bobzetian@google.com> * test(server): add assertions for skipped request routing Signed-off-by: bobzetian <bobzetian@google.com> * epp: unify skip routing and enforce RawPayload globally EPP previously had inconsistent behavior when request parsing was skipped (e.g., for unsupported gRPC paths in the vllmgrpc parser). The vllmgrpc parser returned a nil body on skip, which caused nil-pointer panics in the Scheduling Director when it attempted model rewrite or repackaging. This change unifies the skip story by enforcing a non-nil body with RawPayload globally at the EPP framework level (in server.go) whenever a parser signals Skip. This ensures the Director can safely execute smart routing, admission control, and subsetting for skipped requests without panics, while still bypassing the expensive response-phase interception. The RequestSkipped state comments and logging have also been improved to accurately reflect that skipped requests are smartly routed by the director rather than falling back to random endpoints. TEST=make test-unit Signed-off-by: bobzetian <bobzetian@google.com> * epp: simplify vertexai parser to return nil body on skip Following the global Skip payload-force implementation in server.go, individual parsers no longer need to create and return a dummy body with RawPayload on skip. This change simplifies the vertexai parser to return nil body on skip, aligning it with the vllmgrpc parser and keeping parser implementations boilerplate-free. The vertexai unit test has also been updated. TEST=make test-unit Signed-off-by: bobzetian <bobzetian@google.com> * refactor: rename Skip to SkipResponseProcessing and RequestSkipped to RequestResponseProcessingSkipped To improve readability, code clarity, and explicitly communicate the architectural intent of EPP skip behavior, this change renames the Skip field in the Parser ParseResult struct to SkipResponseProcessing. Additionally, the corresponding internal state constant in server.go is renamed from RequestSkipped to RequestResponseProcessingSkipped. All parser implementations (openai, passthrough, vertexai, vllmgrpc) and their corresponding unit tests, along with the main server unit tests, have been updated to reflect this complete renaming. TEST=make test-unit Signed-off-by: bobzetian <bobzetian@google.com> * enforce populate Body.Payload when SkipResponseProcessing is true Signed-off-by: bobzetian <bobzetian@google.com> --------- Signed-off-by: bobzetian <bobzetian@google.com>
1 parent 7656f17 commit ac53bff

15 files changed

Lines changed: 102 additions & 65 deletions

File tree

pkg/epp/framework/interface/requesthandling/plugins.go

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@ type Parser interface {
3030
// ParseRequest parses the request body and headers and returns the parsed result.
3131
// There are three outcomes based on the return values:
3232
// 1. err != nil: The request is invalid or cannot be parsed. The framework will fail the request early.
33-
// 2. err == nil and result.Skip == true: The request is valid but should not be processed by the scheduling
34-
// framework (e.g., non-inference requests). The framework will fall back to routing the request to a random
35-
// endpoint and skip subsequent interception phases.
36-
// 3. err == nil and result.Skip == false: The request is valid and will be processed by the scheduling framework.
33+
// 2. err == nil and result.SkipResponseProcessing == true: The request is valid but EPP should stop intercepting the stream
34+
// after the request phase. The scheduling director will still route the request, but subsequent
35+
// response interception phases will be skipped.
36+
// 3. err == nil and result.SkipResponseProcessing == false: The request is valid and will be processed by the scheduling framework.
3737
ParseRequest(ctx context.Context, body []byte, headers map[string]string) (*ParseResult, error)
3838

3939
// ParseResponse parses the response payload.
@@ -52,16 +52,15 @@ type Parser interface {
5252
type ParseResult struct {
5353
// Body contains the parsed inference request body.
5454
Body *InferenceRequestBody
55-
// Skip indicates whether to skip the remaining processing phases in the EPP.
56-
// This should only be used for non-inference related requests.
57-
// When set to true, the request will bypass the scheduling director and will be
58-
// routed to a random endpoint. The EPP will also stop intercepting the stream
59-
// for this request (e.g., response headers and body will not be processed).
55+
// SkipResponseProcessing indicates whether to skip EPP stream interception for this request.
56+
// When set to true, the request will still go through the scheduling director
57+
// (allowing routing decisions, profiles, and admission control to run),
58+
// but the EPP will stop intercepting the stream after the request phase completes
59+
// (e.g., response headers and body will not be processed).
6060
//
61-
// Example: In a gRPC parser, if the request path does not match any known
62-
// methods (e.g., neither Generate nor Embed for vLLM), setting Skip to true
63-
// allows the request to proceed to a fallback endpoint without failing.
64-
Skip bool
61+
// This allows fallback or non-standard requests to be routed using the configured
62+
// policies without paying the overhead of response-phase interception.
63+
SkipResponseProcessing bool
6564
}
6665

6766
type ParsedResponse struct {

pkg/epp/framework/plugins/requesthandling/parsers/anthropic/anthropic.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ func (p *AnthropicParser) ParseRequest(_ context.Context, body []byte, headers m
101101
result.Stream = true
102102
}
103103

104-
return &fwkrh.ParseResult{Body: result, Skip: false}, nil
104+
return &fwkrh.ParseResult{Body: result, SkipResponseProcessing: false}, nil
105105
}
106106

107107
func (p *AnthropicParser) ParseResponse(_ context.Context, body []byte, headers map[string]string, _ bool) (*fwkrh.ParsedResponse, error) {

pkg/epp/framework/plugins/requesthandling/parsers/anthropic/anthropic_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -432,8 +432,8 @@ func TestAnthropicParser_ParseRequest(t *testing.T) {
432432
return
433433
}
434434

435-
if got.Skip != false {
436-
t.Errorf("ParseRequest() got.Skip = %v, want false", got.Skip)
435+
if got.SkipResponseProcessing != false {
436+
t.Errorf("ParseRequest() got.SkipResponseProcessing = %v, want false", got.SkipResponseProcessing)
437437
}
438438

439439
if diff := cmp.Diff(tt.want, got.Body); diff != "" {

pkg/epp/framework/plugins/requesthandling/parsers/openai/openai.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ func (p *OpenAIParser) ParseRequest(ctx context.Context, body []byte, headers ma
110110
if stream, ok := bodyMap["stream"].(bool); ok && stream {
111111
extractedBody.Stream = true
112112
}
113-
return &fwkrh.ParseResult{Body: extractedBody, Skip: false}, nil
113+
return &fwkrh.ParseResult{Body: extractedBody, SkipResponseProcessing: false}, nil
114114
}
115115

116116
// ParseResponse extracts usage metadata from the provider's response.

pkg/epp/framework/plugins/requesthandling/parsers/openai/openai_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -809,8 +809,8 @@ func TestOpenAIParser_ParseRequest(t *testing.T) {
809809
return
810810
}
811811

812-
if got.Skip != false {
813-
t.Errorf("ParseRequest() got.Skip = %v, want false", got.Skip)
812+
if got.SkipResponseProcessing != false {
813+
t.Errorf("ParseRequest() got.SkipResponseProcessing = %v, want false", got.SkipResponseProcessing)
814814
}
815815

816816
if diff := cmp.Diff(tt.want, got.Body); diff != "" {

pkg/epp/framework/plugins/requesthandling/parsers/passthrough/passthrough.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ func (p *PassthroughParser) ParseRequest(ctx context.Context, body []byte, heade
7272
Body: &fwkrh.InferenceRequestBody{
7373
Payload: fwkrh.RawPayload(body),
7474
},
75-
Skip: false,
75+
SkipResponseProcessing: false,
7676
}, nil
7777
}
7878

pkg/epp/framework/plugins/requesthandling/parsers/passthrough/passthrough_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ func TestPassthroughParser_ParseRequest(t *testing.T) {
5959
if err != nil {
6060
t.Fatalf("unexpected error: %v", err)
6161
}
62-
if got.Skip != false {
63-
t.Errorf("got.Skip = %v, want false", got.Skip)
62+
if got.SkipResponseProcessing != false {
63+
t.Errorf("got.SkipResponseProcessing = %v, want false", got.SkipResponseProcessing)
6464
}
6565
if diff := cmp.Diff(tt.wantBody, got.Body); diff != "" {
6666
t.Errorf("Unexpected body (-want +got):\n%s", diff)

pkg/epp/framework/plugins/requesthandling/parsers/vertexai/vertexai.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,12 @@ func (p *VertexAIParser) ParseRequest(ctx context.Context, body []byte, headers
114114
return p.parseVertexRequest(ctx, body, headers, &aiplatformpb.RawPredictRequest{}, "RawPredictRequest", openAIResponsesPath)
115115

116116
default:
117-
return &fwkrh.ParseResult{Skip: true}, nil
117+
return &fwkrh.ParseResult{
118+
Body: &fwkrh.InferenceRequestBody{
119+
Payload: fwkrh.RawPayload(body),
120+
},
121+
SkipResponseProcessing: true,
122+
}, nil
118123
}
119124
}
120125

@@ -172,5 +177,5 @@ func (p *VertexAIParser) parseVertexRequest(ctx context.Context, body []byte, he
172177

173178
inferenceRequestBody := parseResult.Body
174179
inferenceRequestBody.Payload = fwkrh.PayloadProto{Message: req}
175-
return &fwkrh.ParseResult{Body: inferenceRequestBody, Skip: parseResult.Skip}, nil
180+
return &fwkrh.ParseResult{Body: inferenceRequestBody, SkipResponseProcessing: parseResult.SkipResponseProcessing}, nil
176181
}

pkg/epp/framework/plugins/requesthandling/parsers/vertexai/vertexai_test.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ func TestParseRequest(t *testing.T) {
9898
Stream: true,
9999
Payload: fwkrh.PayloadProto{Message: reqMsg},
100100
},
101-
Skip: false,
101+
SkipResponseProcessing: false,
102102
},
103103
},
104104
{
@@ -115,7 +115,7 @@ func TestParseRequest(t *testing.T) {
115115
},
116116
Payload: fwkrh.PayloadProto{Message: streamRawPredictReqMsg},
117117
},
118-
Skip: false,
118+
SkipResponseProcessing: false,
119119
},
120120
},
121121
{
@@ -132,15 +132,18 @@ func TestParseRequest(t *testing.T) {
132132
},
133133
Payload: fwkrh.PayloadProto{Message: rawPredictReqMsg},
134134
},
135-
Skip: false,
135+
SkipResponseProcessing: false,
136136
},
137137
},
138138
{
139139
name: "Unsupported Path",
140140
body: []byte{},
141141
headers: map[string]string{":path": "/unsupported/path", "content-type": "application/grpc"},
142142
wantResult: &fwkrh.ParseResult{
143-
Skip: true,
143+
Body: &fwkrh.InferenceRequestBody{
144+
Payload: fwkrh.RawPayload([]byte{}),
145+
},
146+
SkipResponseProcessing: true,
144147
},
145148
},
146149
{

pkg/epp/framework/plugins/requesthandling/parsers/vllmgrpc/vllmgrpc.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ func (p *VllmGRPCParser) ParseRequest(ctx context.Context, body []byte, headers
9898
return nil, err
9999
}
100100
logger.V(logutil.TRACE).Info("parsed EmbedRequest")
101-
return &fwkrh.ParseResult{Body: extractedBody, Skip: false}, nil
101+
return &fwkrh.ParseResult{Body: extractedBody, SkipResponseProcessing: false}, nil
102102

103103
case vllmGeneratePath:
104104
var req pb.GenerateRequest
@@ -110,11 +110,16 @@ func (p *VllmGRPCParser) ParseRequest(ctx context.Context, body []byte, headers
110110
return nil, err
111111
}
112112
logger.V(logutil.TRACE).Info("parsed GenerateRequest")
113-
return &fwkrh.ParseResult{Body: extractedBody, Skip: false}, nil
113+
return &fwkrh.ParseResult{Body: extractedBody, SkipResponseProcessing: false}, nil
114114

115115
default:
116116
logger.V(logutil.TRACE).Info("unsupported gRPC path, skipping", "path", headers[parsers.MethodPathKey])
117-
return &fwkrh.ParseResult{Skip: true}, nil
117+
return &fwkrh.ParseResult{
118+
Body: &fwkrh.InferenceRequestBody{
119+
Payload: fwkrh.RawPayload(body),
120+
},
121+
SkipResponseProcessing: true,
122+
}, nil
118123
}
119124
}
120125

0 commit comments

Comments
 (0)