Skip to content

Commit bf49fb5

Browse files
committed
Add unit tests and comments
1 parent dfea9cf commit bf49fb5

File tree

3 files changed

+136
-7
lines changed

3 files changed

+136
-7
lines changed

pkg/epp/requestcontrol/director.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ type Director struct {
9191
defaultPriority int
9292
}
9393

94-
// getInferenceObjective creates inferenceObjective based on reqCtx.
94+
// getInferenceObjective fetches the inferenceObjective from the datastore otherwise creates a new one based on reqCtx.
9595
func (d *Director) getInferenceObjective(logger logr.Logger, reqCtx *handlers.RequestContext) *v1alpha2.InferenceObjective {
9696
infObjective := d.datastore.ObjectiveGet(reqCtx.ObjectiveKey)
9797
if infObjective == nil {
@@ -109,7 +109,7 @@ func (d *Director) getInferenceObjective(logger logr.Logger, reqCtx *handlers.Re
109109
}
110110

111111
// resolveTargetModel is a helper that resolves targetModel
112-
// and updates the reqCtx and ctx.
112+
// and updates the reqCtx.
113113
func (d *Director) resolveTargetModel(reqCtx *handlers.RequestContext) error {
114114
requestBodyMap := reqCtx.Request.Body
115115
var ok bool
@@ -168,18 +168,19 @@ func (d *Director) HandleRequest(ctx context.Context, reqCtx *handlers.RequestCo
168168
logger.V(logutil.DEFAULT).Info("Request rejected by admission control", "error", err)
169169
return reqCtx, err
170170
}
171-
copyOfCandidatePods := d.toSchedulerPodMetrics(candidatePods)
171+
snapshotOfCandidatePods := d.toSchedulerPodMetrics(candidatePods)
172172

173173
// Prepare per request data
174-
d.runPrepareDataPlugins(ctx, reqCtx.SchedulingRequest, copyOfCandidatePods)
174+
// TODO(rahulgurnani): Add retries and timeout in the preparedata step.
175+
d.runPrepareDataPlugins(ctx, reqCtx.SchedulingRequest, snapshotOfCandidatePods)
175176

176177
// Run admit request plugins
177-
if !d.runAdmitRequestPlugins(ctx, reqCtx.SchedulingRequest, copyOfCandidatePods) {
178+
if !d.runAdmitRequestPlugins(ctx, reqCtx.SchedulingRequest, snapshotOfCandidatePods) {
178179
logger.V(logutil.DEFAULT).Info("Request cannot be admitted")
179180
return reqCtx, errutil.Error{Code: errutil.Internal, Msg: "request cannot be admitted"}
180181
}
181182

182-
result, err := d.scheduler.Schedule(ctx, reqCtx.SchedulingRequest, copyOfCandidatePods)
183+
result, err := d.scheduler.Schedule(ctx, reqCtx.SchedulingRequest, snapshotOfCandidatePods)
183184
if err != nil {
184185
return reqCtx, errutil.Error{Code: errutil.InferencePoolResourceExhausted, Msg: fmt.Errorf("failed to find target pod: %w", err).Error()}
185186
}

pkg/epp/requestcontrol/director_test.go

Lines changed: 117 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,45 @@ func (ds *mockDatastore) PodList(predicate func(backendmetrics.PodMetrics) bool)
9393
return res
9494
}
9595

96+
type mockPrepareRequestDataPlugin struct {
97+
tn plugins.TypedName
98+
prepareDataCalled bool
99+
}
100+
101+
func newmockPrepareRequestDataPlugin(name string) *mockPrepareRequestDataPlugin {
102+
return &mockPrepareRequestDataPlugin{
103+
tn: plugins.TypedName{Type: "mock-prepare-request-data", Name: name},
104+
}
105+
}
106+
107+
func (m *mockPrepareRequestDataPlugin) TypedName() plugins.TypedName {
108+
return m.tn
109+
}
110+
111+
func (m *mockPrepareRequestDataPlugin) PrepareData(ctx context.Context, request *schedulingtypes.LLMRequest, pods []schedulingtypes.Pod) {
112+
m.prepareDataCalled = true
113+
}
114+
115+
type mockAdmitRequestPlugins struct {
116+
tn plugins.TypedName
117+
admitRequestCalled bool
118+
}
119+
120+
func newmockAdmitRequestPlugins(name string) *mockAdmitRequestPlugins {
121+
return &mockAdmitRequestPlugins{
122+
tn: plugins.TypedName{Type: "mock-admit-data", Name: name},
123+
}
124+
}
125+
126+
func (m *mockAdmitRequestPlugins) TypedName() plugins.TypedName {
127+
return m.tn
128+
}
129+
130+
func (m *mockAdmitRequestPlugins) Admit(ctx context.Context, request *schedulingtypes.LLMRequest, pods []schedulingtypes.Pod) bool {
131+
m.admitRequestCalled = true
132+
return true
133+
}
134+
96135
func TestDirector_HandleRequest(t *testing.T) {
97136
ctx := logutil.NewTestLoggerIntoContext(context.Background())
98137

@@ -211,6 +250,10 @@ func TestDirector_HandleRequest(t *testing.T) {
211250
wantReqCtx *handlers.RequestContext // Fields to check in the returned RequestContext
212251
wantMutatedBodyModel string // Expected model in reqCtx.Request.Body after PostDispatch
213252
targetModelName string // Expected model name after target model resolution
253+
prepareDataCalled bool
254+
admitRequestCalled bool
255+
prepareDataPlugins *mockPrepareRequestDataPlugin
256+
admitRequestPlugins *mockAdmitRequestPlugins
214257
}{
215258
{
216259
name: "successful completions request",
@@ -265,6 +308,66 @@ func TestDirector_HandleRequest(t *testing.T) {
265308
wantMutatedBodyModel: model,
266309
targetModelName: model,
267310
},
311+
{
312+
name: "successful chat completions request with prepare data plugins",
313+
reqBodyMap: map[string]any{
314+
"model": model,
315+
"messages": []any{
316+
map[string]any{
317+
"role": "user",
318+
"content": "critical prompt",
319+
},
320+
},
321+
},
322+
mockAdmissionController: &mockAdmissionController{admitErr: nil},
323+
schedulerMockSetup: func(m *mockScheduler) {
324+
m.scheduleResults = defaultSuccessfulScheduleResults
325+
},
326+
wantReqCtx: &handlers.RequestContext{
327+
TargetModelName: model,
328+
TargetPod: &backend.Pod{
329+
NamespacedName: types.NamespacedName{Namespace: "default", Name: "pod1"},
330+
Address: "192.168.1.100",
331+
Port: "8000",
332+
MetricsHost: "192.168.1.100:8000",
333+
},
334+
TargetEndpoint: "192.168.1.100:8000,192.168.2.100:8000,192.168.4.100:8000",
335+
},
336+
wantMutatedBodyModel: model,
337+
targetModelName: model,
338+
prepareDataCalled: true,
339+
prepareDataPlugins: newmockPrepareRequestDataPlugin("test-plugin"),
340+
},
341+
{
342+
name: "successful chat completions request with admit request plugins",
343+
reqBodyMap: map[string]any{
344+
"model": model,
345+
"messages": []any{
346+
map[string]any{
347+
"role": "user",
348+
"content": "critical prompt",
349+
},
350+
},
351+
},
352+
mockAdmissionController: &mockAdmissionController{admitErr: nil},
353+
schedulerMockSetup: func(m *mockScheduler) {
354+
m.scheduleResults = defaultSuccessfulScheduleResults
355+
},
356+
wantReqCtx: &handlers.RequestContext{
357+
TargetModelName: model,
358+
TargetPod: &backend.Pod{
359+
NamespacedName: types.NamespacedName{Namespace: "default", Name: "pod1"},
360+
Address: "192.168.1.100",
361+
Port: "8000",
362+
MetricsHost: "192.168.1.100:8000",
363+
},
364+
TargetEndpoint: "192.168.1.100:8000,192.168.2.100:8000,192.168.4.100:8000",
365+
},
366+
wantMutatedBodyModel: model,
367+
targetModelName: model,
368+
admitRequestCalled: true,
369+
admitRequestPlugins: newmockAdmitRequestPlugins("test-plugin"),
370+
},
268371
{
269372
name: "successful chat completions request with multiple messages",
270373
reqBodyMap: map[string]any{
@@ -414,7 +517,14 @@ func TestDirector_HandleRequest(t *testing.T) {
414517
if test.schedulerMockSetup != nil {
415518
test.schedulerMockSetup(mockSched)
416519
}
417-
director := NewDirectorWithConfig(ds, mockSched, test.mockAdmissionController, NewConfig())
520+
config := NewConfig()
521+
if test.prepareDataPlugins != nil {
522+
config = config.WithPrepareDataPlugins(test.prepareDataPlugins)
523+
}
524+
if test.admitRequestPlugins != nil {
525+
config = config.WithAdmitRequestPlugins(test.admitRequestPlugins)
526+
}
527+
director := NewDirectorWithConfig(ds, mockSched, test.mockAdmissionController, config)
418528

419529
reqCtx := &handlers.RequestContext{
420530
Request: &handlers.Request{
@@ -458,6 +568,12 @@ func TestDirector_HandleRequest(t *testing.T) {
458568
assert.Equal(t, test.wantMutatedBodyModel, returnedReqCtx.Request.Body["model"],
459569
"Mutated reqCtx.Request.Body model mismatch")
460570
}
571+
if test.admitRequestPlugins != nil {
572+
assert.True(t, test.admitRequestPlugins.admitRequestCalled, "AdmitRequestPlugins not called")
573+
}
574+
if test.prepareDataPlugins != nil {
575+
assert.True(t, test.prepareDataPlugins.prepareDataCalled, "PrepareDataPlugins not called")
576+
}
461577
})
462578
}
463579
}

pkg/epp/requestcontrol/request_control_config.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,18 @@ func (c *Config) WithResponseCompletePlugins(plugins ...ResponseComplete) *Confi
7070
return c
7171
}
7272

73+
// WithPrepareDataPlugins sets the given plugins as the PrepareData plugins.
74+
func (c *Config) WithPrepareDataPlugins(plugins ...PrepareData) *Config {
75+
c.prepareDataPlugins = plugins
76+
return c
77+
}
78+
79+
// WithAdmitRequestPlugins sets the given plugins as the AdmitRequest plugins.
80+
func (c *Config) WithAdmitRequestPlugins(plugins ...AdmitRequest) *Config {
81+
c.admitRequestPlugins = plugins
82+
return c
83+
}
84+
7385
// AddPlugins adds the given plugins to the Config.
7486
// The type of each plugin is checked and added to the corresponding list of plugins in the Config.
7587
// If a plugin implements multiple plugin interfaces, it will be added to each corresponding list.

0 commit comments

Comments
 (0)