MCPcopy
hub / github.com/docker/docker-agent / TestDoCompactAfterHookFires

Function TestDoCompactAfterHookFires

pkg/runtime/session_compaction_test.go:232–286  ·  view source on GitHub ↗

TestDoCompactAfterHookFires verifies that after_compaction fires when a summary was applied (LLM-path or hook-path), and that the hook receives the produced summary text together with the *pre-compaction* token counts (so observability handlers can express "compacted from X to Y").

(t *testing.T)

Source from the content-addressed store, hash-verified

230// *pre-compaction* token counts (so observability handlers can
231// express "compacted from X to Y").
232func TestDoCompactAfterHookFires(t *testing.T) {
233 t.Parallel()
234
235 dir := t.TempDir()
236 logFile := dir + "/after.log"
237
238 const customSummary = "summary from the before hook"
239 beforeJSON := `{"hook_specific_output":{"hook_event_name":"before_compaction","summary":"` + customSummary + `"}}`
240
241 hookCfg := &latest.HooksConfig{
242 BeforeCompaction: []latest.HookDefinition{
243 {Type: "command", Command: "echo '" + beforeJSON + "'", Timeout: 5},
244 },
245 AfterCompaction: []latest.HookDefinition{
246 // Capture summary plus pre-compaction tokens; if the runtime
247 // regresses to passing post-compaction values we'll see
248 // input_tokens == EstimateMessageTokens(summary) instead of
249 // the pre-compaction 1234.
250 {Type: "command", Command: "cat | jq -r '\"\\(.summary)|\\(.input_tokens)|\\(.output_tokens)\"' > " + logFile, Timeout: 5},
251 },
252 }
253
254 prov := &mockProvider{id: "test/mock-model", stream: &mockStream{}}
255 root := agent.New("root", "test",
256 agent.WithModel(prov),
257 agent.WithHooks(hookCfg),
258 )
259 tm := team.New(team.WithAgents(root))
260
261 rt, err := NewLocalRuntime(t.Context(), tm,
262 WithSessionCompaction(false),
263 WithModelStore(mockModelStoreWithLimit{limit: 100_000}),
264 )
265 require.NoError(t, err)
266
267 sess := session.New(session.WithMessages([]session.Item{
268 session.NewMessageItem(&session.Message{Message: chat.Message{Role: chat.MessageRoleUser, Content: "hi"}}),
269 }))
270 // Seed pre-compaction token counts so we can verify the hook
271 // receives them rather than the post-compaction values (which
272 // would be approximately EstimateMessageTokens(summary) and 0).
273 sess.InputTokens = 1234
274 sess.OutputTokens = 567
275
276 events := make(chan Event, 32)
277 rt.compactWithReason(t.Context(), sess, "", compactionReasonThreshold, NewChannelSink(events))
278 close(events)
279 for range events {
280 }
281
282 logged, readErr := os.ReadFile(logFile)
283 require.NoError(t, readErr, "after_compaction hook must have run and produced the log file")
284 assert.Equal(t, customSummary+"|1234|567\n", string(logged),
285 "after_compaction must receive the produced summary and the *pre-compaction* token counts")
286}
287
288// TestDoCompactNoHooksMatchesPriorBehavior is a regression guard: with
289// no compaction-related hooks configured, compactWithReason must still

Callers

nothing calls this directly

Calls 15

compactWithReasonMethod · 0.95
NewFunction · 0.92
WithModelFunction · 0.92
WithHooksFunction · 0.92
NewFunction · 0.92
WithAgentsFunction · 0.92
NewFunction · 0.92
WithMessagesFunction · 0.92
NewMessageItemFunction · 0.92
NewLocalRuntimeFunction · 0.85
WithSessionCompactionFunction · 0.85
WithModelStoreFunction · 0.85

Tested by

no test coverage detected