Skip to content

Commit

Permalink
refactor(core): extract common function callback builder functionality
Browse files Browse the repository at this point in the history
Extracts shared function callback builder functionality into DefaultCommonCallbackInvokingSpec
base class, reducing code duplication across builder implementations.
Makes FunctionInvokingSpec and MethodInvokingSpec extend CommonCallbackInvokingSpec for better
code organization. Also fixes function/description builder order in Anthropic tests.

- Introduced a common base class for function callback builders to centralize shared logic
- Standardized the order of method chaining for function and description in multiple AI model test classes
- Refactored test cases across various AI model integrations
- Corrected builder method order from .description().function() to .function().description()
  and .description().method() to .method().description()
- Updated multiple test files to consistently use .function() before .description()
- Updated documentation examples to reflect new builder method order
- Modified DefaultFunctionCallbackResolver to maintain new builder method order
- Updated DefaultChatClient and ChatClient test classes to reflect new builder pattern
- Simplified callback specification by removing parent spec reference
- Removed cascading getter logic for description, schema type, and other properties
- Minor adjustments to function callback builder and invoking specs
  • Loading branch information
tzolov authored and ilayaperumalg committed Nov 28, 2024
1 parent dcc8d5b commit 6a195ee
Show file tree
Hide file tree
Showing 72 changed files with 403 additions and 318 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -276,9 +276,9 @@ void functionCallTest() {
var promptOptions = AnthropicChatOptions.builder()
.withModel(AnthropicApi.ChatModel.CLAUDE_3_OPUS.getName())
.withFunctionCallbacks(List.of(FunctionCallback.builder()
.function("getCurrentWeather", new MockWeatherService())
.description(
"Get the weather in location. Return temperature in 36°F or 36°C format. Use multi-turn if needed.")
.function("getCurrentWeather", new MockWeatherService())
.inputType(MockWeatherService.Request.class)
.build()))
.build();
Expand All @@ -304,9 +304,9 @@ void streamFunctionCallTest() {
var promptOptions = AnthropicChatOptions.builder()
.withModel(AnthropicApi.ChatModel.CLAUDE_3_5_SONNET.getName())
.withFunctionCallbacks(List.of(FunctionCallback.builder()
.function("getCurrentWeather", new MockWeatherService())
.description(
"Get the weather in location. Return temperature in 36°F or 36°C format. Use multi-turn if needed.")
.function("getCurrentWeather", new MockWeatherService())
.inputType(MockWeatherService.Request.class)
.build()))
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,8 +250,8 @@ void defaultFunctionCallTest() {
// @formatter:off
String response = ChatClient.builder(this.chatModel)
.defaultFunctions(FunctionCallback.builder()
.description("Get the weather in location")
.function("getCurrentWeather", new MockWeatherService())
.description("Get the weather in location")
.inputType(MockWeatherService.Request.class)
.build())
.defaultUser(u -> u.text("What's the weather like in San Francisco, Tokyo, and Paris? Use Celsius."))
Expand All @@ -273,8 +273,8 @@ void streamFunctionCallTest() {
Flux<String> response = ChatClient.create(this.chatModel).prompt()
.user("What's the weather like in San Francisco, Tokyo, and Paris? Use Celsius.")
.functions(FunctionCallback.builder()
.description("Get the weather in location")
.function("getCurrentWeather", new MockWeatherService())
.description("Get the weather in location")
.inputType(MockWeatherService.Request.class)
.build())
.stream()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ void methodGetWeatherStatic() {
String response = ChatClient.create(this.chatModel).prompt()
.user("What's the weather like in San Francisco, Tokyo, and Paris? Use Celsius.")
.functions(FunctionCallback.builder()
.description("Get the weather in location")
.method("getWeatherStatic", String.class, Unit.class)
.description("Get the weather in location")
.targetClass(TestFunctionClass.class)
.build())
.call()
Expand All @@ -102,8 +102,8 @@ void methodTurnLightNoResponse() {
String response = ChatClient.create(this.chatModel).prompt()
.user("Turn light on in the living room.")
.functions(FunctionCallback.builder()
.description("Turn light on in the living room.")
.method("turnLight", String.class, boolean.class)
.description("Turn light on in the living room.")
.targetObject(targetObject)
.build())
.call()
Expand All @@ -125,8 +125,8 @@ void methodGetWeatherNonStatic() {
String response = ChatClient.create(this.chatModel).prompt()
.user("What's the weather like in San Francisco, Tokyo, and Paris? Use Celsius.")
.functions(FunctionCallback.builder()
.description("Get the weather in location")
.method("getWeatherNonStatic", String.class, Unit.class)
.description("Get the weather in location")
.targetObject(targetObject)
.build())
.call()
Expand All @@ -147,8 +147,8 @@ void methodGetWeatherToolContext() {
String response = ChatClient.create(this.chatModel).prompt()
.user("What's the weather like in San Francisco, Tokyo, and Paris? Use Celsius.")
.functions(FunctionCallback.builder()
.description("Get the weather in location")
.method("getWeatherWithContext", String.class, Unit.class, ToolContext.class)
.description("Get the weather in location")
.targetObject(targetObject)
.build())
.toolContext(Map.of("tool", "value"))
Expand All @@ -174,8 +174,8 @@ void methodGetWeatherToolContextButNonContextMethod() {
assertThatThrownBy(() -> ChatClient.create(this.chatModel).prompt()
.user("What's the weather like in San Francisco, Tokyo, and Paris? Use Celsius.")
.functions(FunctionCallback.builder()
.description("Get the weather in location")
.method("getWeatherNonStatic", String.class, Unit.class)
.description("Get the weather in location")
.targetObject(targetObject)
.build())
.toolContext(Map.of("tool", "value"))
Expand All @@ -195,8 +195,8 @@ void methodNoParameters() {
String response = ChatClient.create(this.chatModel).prompt()
.user("Turn light on in the living room.")
.functions(FunctionCallback.builder()
.description("Can turn lights on in the Living Room")
.method("turnLivingRoomLightOn")
.description("Can turn lights on in the Living Room")
.targetObject(targetObject)
.build())
.call()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ void functionCallTest() {
var promptOptions = AzureOpenAiChatOptions.builder()
.withDeploymentName(this.selectedModel)
.withFunctionCallbacks(List.of(FunctionCallback.builder()
.description("Get the current weather in a given location")
.function("getCurrentWeather", new MockWeatherService())
.description("Get the current weather in a given location")
.inputType(MockWeatherService.Request.class)
.build()))
.build();
Expand All @@ -94,8 +94,8 @@ void functionCallSequentialTest() {
var promptOptions = AzureOpenAiChatOptions.builder()
.withDeploymentName(this.selectedModel)
.withFunctionCallbacks(List.of(FunctionCallback.builder()
.description("Get the current weather in a given location")
.function("getCurrentWeather", new MockWeatherService())
.description("Get the current weather in a given location")
.inputType(MockWeatherService.Request.class)
.build()))
.build();
Expand All @@ -116,8 +116,8 @@ void streamFunctionCallTest() {
var promptOptions = AzureOpenAiChatOptions.builder()
.withDeploymentName(this.selectedModel)
.withFunctionCallbacks(List.of(FunctionCallback.builder()
.description("Get the current weather in a given location")
.function("getCurrentWeather", new MockWeatherService())
.description("Get the current weather in a given location")
.inputType(MockWeatherService.Request.class)
.build()))
.build();
Expand Down Expand Up @@ -153,8 +153,8 @@ void functionCallSequentialAndStreamTest() {
var promptOptions = AzureOpenAiChatOptions.builder()
.withDeploymentName(this.selectedModel)
.withFunctionCallbacks(List.of(FunctionCallback.builder()
.description("Get the current weather in a given location")
.function("getCurrentWeather", new MockWeatherService())
.description("Get the current weather in a given location")
.inputType(MockWeatherService.Request.class)
.build()))
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,10 +214,10 @@ void functionCallTest() {
String response = ChatClient.create(this.chatModel)
.prompt("What's the weather like in San Francisco, Tokyo, and Paris? Return the temperature in Celsius.")
.functions(FunctionCallback.builder()
.description("Get the weather in location")
.function("getCurrentWeather", new MockWeatherService())
.inputType(MockWeatherService.Request.class)
.build())
.function("getCurrentWeather", new MockWeatherService())
.description("Get the weather in location")
.inputType(MockWeatherService.Request.class)
.build())
.call()
.content();
// @formatter:on
Expand All @@ -234,10 +234,10 @@ void functionCallWithUsageMetadataTest() {
ChatResponse response = ChatClient.create(this.chatModel)
.prompt("What's the weather like in San Francisco, Tokyo, and Paris? Return the temperature in Celsius.")
.functions(FunctionCallback.builder()
.description("Get the weather in location")
.function("getCurrentWeather", new MockWeatherService())
.inputType(MockWeatherService.Request.class)
.build())
.function("getCurrentWeather", new MockWeatherService())
.description("Get the weather in location")
.inputType(MockWeatherService.Request.class)
.build())
.call()
.chatResponse();
// @formatter:on
Expand Down Expand Up @@ -269,10 +269,10 @@ void functionCallWithAdvisorTest() {
String response = ChatClient.create(this.chatModel)
.prompt("What's the weather like in San Francisco, Tokyo, and Paris? Return the temperature in Celsius.")
.functions(FunctionCallback.builder()
.description("Get the weather in location")
.function("getCurrentWeather", new MockWeatherService())
.inputType(MockWeatherService.Request.class)
.build())
.function("getCurrentWeather", new MockWeatherService())
.description("Get the weather in location")
.inputType(MockWeatherService.Request.class)
.build())
.advisors(new SimpleLoggerAdvisor())
.call()
.content();
Expand All @@ -289,8 +289,8 @@ void defaultFunctionCallTest() {
// @formatter:off
String response = ChatClient.builder(this.chatModel)
.defaultFunctions(FunctionCallback.builder()
.description("Get the weather in location")
.function("getCurrentWeather", new MockWeatherService())
.description("Get the weather in location")
.inputType(MockWeatherService.Request.class)
.build())
.defaultUser(u -> u.text("What's the weather like in San Francisco, Tokyo, and Paris? Return the temperature in Celsius."))
Expand All @@ -312,8 +312,8 @@ void streamFunctionCallTest() {
Flux<ChatResponse> response = ChatClient.create(this.chatModel).prompt()
.user("What's the weather like in San Francisco, Tokyo, and Paris? Return the temperature in Celsius.")
.functions(FunctionCallback.builder()
.description("Get the weather in location")
.function("getCurrentWeather", new MockWeatherService())
.description("Get the weather in location")
.inputType(MockWeatherService.Request.class)
.build())
.stream()
Expand Down Expand Up @@ -354,8 +354,8 @@ void singularStreamFunctionCallTest() {
Flux<String> response = ChatClient.create(this.chatModel).prompt()
.user("What's the weather like in Paris? Return the temperature in Celsius.")
.functions(FunctionCallback.builder()
.description("Get the weather in location")
.function("getCurrentWeather", new MockWeatherService())
.description("Get the weather in location")
.inputType(MockWeatherService.Request.class)
.build())
.stream()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,8 @@ public void callWithToolUse() {
.willReturn(converseResponseFinal);

FunctionCallback functionCallback = FunctionCallback.builder()
.description("Gets the weather in location")
.function("getCurrentWeather", (Request request) -> "15.0°C")
.description("Gets the weather in location")
.inputType(Request.class)
.build();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,9 +255,9 @@ void functionCallTest() {

var promptOptions = FunctionCallingOptions.builder()
.withFunctionCallbacks(List.of(FunctionCallback.builder()
.function("getCurrentWeather", new MockWeatherService())
.description(
"Get the weather in location. Return temperature in 36°F or 36°C format. Use multi-turn if needed.")
.function("getCurrentWeather", new MockWeatherService())
.inputType(MockWeatherService.Request.class)
.build()))
.build();
Expand All @@ -283,9 +283,9 @@ void streamFunctionCallTest() {
var promptOptions = FunctionCallingOptions.builder()
.withModel("anthropic.claude-3-5-sonnet-20240620-v1:0")
.withFunctionCallbacks(List.of(FunctionCallback.builder()
.function("getCurrentWeather", new MockWeatherService())
.description(
"Get the weather in location. Return temperature in 36°F or 36°C format. Use multi-turn if needed.")
.function("getCurrentWeather", new MockWeatherService())
.inputType(MockWeatherService.Request.class)
.build()))
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ public static void main(String[] args) {
PortableFunctionCallingOptions.builder()
.withModel(modelId)
.withFunctionCallbacks(List.of(FunctionCallback.builder()
.description("Get the weather in location")
.function("getCurrentWeather", new MockWeatherService())
.description("Get the weather in location")
.inputType(MockWeatherService.Request.class)
.build()))
.build());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ public static void main(String[] args) {
PortableFunctionCallingOptions.builder()
.withModel(modelId)
.withFunctionCallbacks(List.of(FunctionCallback.builder()
.description("Get the weather in location")
.function("getCurrentWeather", new MockWeatherService())
.description("Get the weather in location")
.inputType(MockWeatherService.Request.class)
.build()))
.build());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ public void promptOptionsTools() {
MiniMaxChatOptions.builder()
.withModel("PROMPT_MODEL")
.withFunctionCallbacks(List.of(FunctionCallback.builder()
.description("Get the weather in location")
.function(TOOL_FUNCTION_NAME, new MockWeatherService())
.description("Get the weather in location")
.inputType(MockWeatherService.Request.class)
.build()))
.build()),
Expand All @@ -95,8 +95,8 @@ public void defaultOptionsTools() {
MiniMaxChatOptions.builder()
.withModel("DEFAULT_MODEL")
.withFunctionCallbacks(List.of(FunctionCallback.builder()
.description("Get the weather in location")
.function(TOOL_FUNCTION_NAME, new MockWeatherService())
.description("Get the weather in location")
.inputType(MockWeatherService.Request.class)
.build()))
.build());
Expand Down Expand Up @@ -127,8 +127,8 @@ public void defaultOptionsTools() {
request = client.createRequest(new Prompt("Test message content",
MiniMaxChatOptions.builder()
.withFunctionCallbacks(List.of(FunctionCallback.builder()
.description("Overridden function description")
.function(TOOL_FUNCTION_NAME, new MockWeatherService())
.description("Overridden function description")
.inputType(MockWeatherService.Request.class)
.build()))
.build()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,10 +226,10 @@ void functionCallTest() {
.options(MistralAiChatOptions.builder().withModel(MistralAiApi.ChatModel.SMALL).withToolChoice(ToolChoice.AUTO).build())
.user(u -> u.text("What's the weather like in San Francisco, Tokyo, and Paris? Use parallel function calling if required. Response should be in Celsius."))
.functions(FunctionCallback.builder()
.description("Get the weather in location")
.function("getCurrentWeather", new MockWeatherService())
.inputType(MockWeatherService.Request.class)
.build())
.function("getCurrentWeather", new MockWeatherService())
.description("Get the weather in location")
.inputType(MockWeatherService.Request.class)
.build())
.call()
.content();
// @formatter:on
Expand All @@ -248,8 +248,8 @@ void defaultFunctionCallTest() {
String response = ChatClient.builder(this.chatModel)
.defaultOptions(MistralAiChatOptions.builder().withModel(MistralAiApi.ChatModel.SMALL).build())
.defaultFunctions(FunctionCallback.builder()
.description("Get the weather in location")
.function("getCurrentWeather", new MockWeatherService())
.description("Get the weather in location")
.inputType(MockWeatherService.Request.class)
.build())
.defaultUser(u -> u.text("What's the weather like in San Francisco, Tokyo, and Paris? Use parallel function calling if required. Response should be in Celsius."))
Expand All @@ -272,8 +272,8 @@ void streamFunctionCallTest() {
.options(MistralAiChatOptions.builder().withModel(MistralAiApi.ChatModel.SMALL).build())
.user("What's the weather like in San Francisco, Tokyo, and Paris? Use parallel function calling if required. Response should be in Celsius.")
.functions(FunctionCallback.builder()
.description("Get the weather in location")
.function("getCurrentWeather", new MockWeatherService())
.description("Get the weather in location")
.inputType(MockWeatherService.Request.class)
.build())
.stream()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,8 +194,8 @@ void functionCallTest() {
var promptOptions = MistralAiChatOptions.builder()
.withModel(MistralAiApi.ChatModel.SMALL.getValue())
.withFunctionCallbacks(List.of(FunctionCallback.builder()
.description("Get the weather in location")
.function("getCurrentWeather", new MockWeatherService())
.description("Get the weather in location")
.inputType(MockWeatherService.Request.class)
.build()))
.build();
Expand All @@ -217,8 +217,8 @@ void streamFunctionCallTest() {
var promptOptions = MistralAiChatOptions.builder()
.withModel(MistralAiApi.ChatModel.SMALL.getValue())
.withFunctionCallbacks(List.of(FunctionCallback.builder()
.description("Get the weather in location")
.function("getCurrentWeather", new MockWeatherService())
.description("Get the weather in location")
.inputType(MockWeatherService.Request.class)
.build()))
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ void functionCallTest() {
var promptOptions = MoonshotChatOptions.builder()
.withModel(MoonshotApi.ChatModel.MOONSHOT_V1_8K.getValue())
.withFunctionCallbacks(List.of(FunctionCallback.builder()
.description("Get the weather in location")
.function("getCurrentWeather", new MockWeatherService())
.description("Get the weather in location")
.inputType(MockWeatherService.Request.class)
.build()))
.build();
Expand All @@ -87,8 +87,8 @@ void streamFunctionCallTest() {

var promptOptions = MoonshotChatOptions.builder()
.withFunctionCallbacks(List.of(FunctionCallback.builder()
.description("Get the weather in location")
.function("getCurrentWeather", new MockWeatherService())
.description("Get the weather in location")
.build()))
.build();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,9 @@ void functionCallTest() {
var promptOptions = OllamaOptions.builder()
.withModel(MODEL)
.withFunctionCallbacks(List.of(FunctionCallback.builder()
.function("getCurrentWeather", new MockWeatherService())
.description(
"Find the weather conditions, forecasts, and temperatures for a location, like a city or state.")
.function("getCurrentWeather", new MockWeatherService())
.inputType(MockWeatherService.Request.class)
.build()))
.build();
Expand All @@ -89,9 +89,9 @@ void streamFunctionCallTest() {
var promptOptions = OllamaOptions.builder()
.withModel(MODEL)
.withFunctionCallbacks(List.of(FunctionCallback.builder()
.function("getCurrentWeather", new MockWeatherService())
.description(
"Find the weather conditions, forecasts, and temperatures for a location, like a city or state.")
.function("getCurrentWeather", new MockWeatherService())
.inputType(MockWeatherService.Request.class)
.build()))
.build();
Expand Down
Loading

0 comments on commit 6a195ee

Please sign in to comment.