1717package org .springframework .ai .anthropic ;
1818
1919import java .io .IOException ;
20- import java .util .ArrayList ;
21- import java .util .Arrays ;
22- import java .util .List ;
23- import java .util .Map ;
20+ import java .util .*;
2421import java .util .stream .Collectors ;
2522
23+ import ch .qos .logback .classic .Level ;
24+ import ch .qos .logback .classic .LoggerContext ;
25+ import ch .qos .logback .classic .spi .ILoggingEvent ;
26+ import ch .qos .logback .core .Appender ;
27+ import ch .qos .logback .core .AppenderBase ;
28+ import ch .qos .logback .core .read .ListAppender ;
29+ import org .junit .Rule ;
2630import org .junit .jupiter .api .Test ;
2731import org .junit .jupiter .api .condition .EnabledIfEnvironmentVariable ;
32+ import org .junit .jupiter .api .extension .ExtendWith ;
2833import org .junit .jupiter .params .ParameterizedTest ;
2934import org .junit .jupiter .params .provider .ValueSource ;
3035import org .slf4j .Logger ;
3136import org .slf4j .LoggerFactory ;
37+ import org .springframework .boot .test .system .OutputCaptureExtension ;
38+ import org .springframework .boot .test .system .OutputCaptureRule ;
3239import reactor .core .publisher .Flux ;
3340
3441import org .springframework .ai .anthropic .api .AnthropicApi ;
@@ -336,10 +343,17 @@ void streamFunctionCallUsageTest() {
336343
337344 List <Message > messages = new ArrayList <>(List .of (userMessage ));
338345
346+ var mockService = new MockWeatherService ();
347+
348+ MemoryAppender appender = new MemoryAppender ();
349+ appender .setContext ((LoggerContext ) LoggerFactory .getILoggerFactory ());
350+ MockWeatherService .log .addAppender (appender );
351+ appender .start ();
352+
339353 var promptOptions = AnthropicChatOptions .builder ()
340354 .model (AnthropicApi .ChatModel .CLAUDE_3_5_SONNET .getName ())
341355 .functionCallbacks (List .of (FunctionCallback .builder ()
342- .function ("getCurrentWeather" , new MockWeatherService () )
356+ .function ("getCurrentWeather" , mockService )
343357 .description (
344358 "Get the weather in location. Return temperature in 36°F or 36°C format. Use multi-turn if needed." )
345359 .inputType (MockWeatherService .Request .class )
@@ -352,9 +366,12 @@ void streamFunctionCallUsageTest() {
352366
353367 logger .info ("Response: {}" , chatResponse );
354368 Usage usage = chatResponse .getMetadata ().getUsage ();
369+ appender .stop ();
355370
356371 assertThat (usage ).isNotNull ();
372+ assertThat (appender .getLoggedEvents ().size ()).isEqualTo (3 );
357373 assertThat (usage .getTotalTokens ()).isLessThan (4000 ).isGreaterThan (1800 );
374+
358375 }
359376
360377 @ Test
@@ -417,4 +434,39 @@ public AnthropicChatModel openAiChatModel(AnthropicApi api) {
417434
418435 }
419436
437+ public static class MemoryAppender extends ListAppender <ILoggingEvent > {
438+
439+ public void reset () {
440+ this .list .clear ();
441+ }
442+
443+ public boolean contains (String string , Level level ) {
444+ return this .list .stream ()
445+ .anyMatch (event -> event .toString ().contains (string ) && event .getLevel ().equals (level ));
446+ }
447+
448+ public int countEventsForLogger (String loggerName ) {
449+ return (int ) this .list .stream ().filter (event -> event .getLoggerName ().contains (loggerName )).count ();
450+ }
451+
452+ public List <ILoggingEvent > search (String string ) {
453+ return this .list .stream ().filter (event -> event .toString ().contains (string )).collect (Collectors .toList ());
454+ }
455+
456+ public List <ILoggingEvent > search (String string , Level level ) {
457+ return this .list .stream ()
458+ .filter (event -> event .toString ().contains (string ) && event .getLevel ().equals (level ))
459+ .collect (Collectors .toList ());
460+ }
461+
462+ public int getSize () {
463+ return this .list .size ();
464+ }
465+
466+ public List <ILoggingEvent > getLoggedEvents () {
467+ return Collections .unmodifiableList (this .list );
468+ }
469+
470+ }
471+
420472}
0 commit comments