Skip to content

Commit

Permalink
Advancing Tool Support - Part 2
Browse files Browse the repository at this point in the history
* Enhanced support for functions as tools via FunctionToolCallback (deprecating the existing FunctionInvokingFunctionCallback).
* Aligned JSON Schema generation and parsing logic between function-based and method-based tools.
* Deprecated previous client-side function calling APIs.
* Included AOT configuration for Tool-annotated methods in Spring beans.

Relates to spring-projectsgh-2049

Signed-off-by: Thomas Vitale <[email protected]>
  • Loading branch information
ThomasVitale committed Jan 19, 2025
1 parent b77e084 commit 29b9b1b
Show file tree
Hide file tree
Showing 32 changed files with 1,121 additions and 51 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright 2023-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.ai.aot;

import org.springframework.ai.tool.annotation.Tool;
import org.springframework.aot.generate.GenerationContext;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.ReflectionHints;
import org.springframework.beans.factory.aot.BeanRegistrationAotContribution;
import org.springframework.beans.factory.aot.BeanRegistrationAotProcessor;
import org.springframework.beans.factory.aot.BeanRegistrationCode;
import org.springframework.beans.factory.support.RegisteredBean;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.lang.Nullable;
import org.springframework.util.ReflectionUtils;

import java.util.stream.Stream;

import static org.springframework.core.annotation.MergedAnnotations.SearchStrategy.TYPE_HIERARCHY;

/**
* AOT {@code BeanRegistrationAotProcessor} that detects the presence of the {@link Tool}
* annotation on methods and creates the required reflection hints.
*
* @author Thomas Vitale
* @since 1.0.0
*/
class ToolBeanRegistrationAotProcessor implements BeanRegistrationAotProcessor {

@Override
@Nullable
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
Class<?> beanClass = registeredBean.getBeanClass();
MergedAnnotations.Search search = MergedAnnotations.search(TYPE_HIERARCHY);

boolean hasAnyToolAnnotatedMethods = Stream.of(ReflectionUtils.getDeclaredMethods(beanClass))
.anyMatch(method -> search.from(method).isPresent(Tool.class));

if (hasAnyToolAnnotatedMethods) {
return new AotContribution(beanClass);
}

return null;
}

private static class AotContribution implements BeanRegistrationAotContribution {

private final MemberCategory[] memberCategories = new MemberCategory[] { MemberCategory.INVOKE_DECLARED_METHODS,
MemberCategory.INVOKE_PUBLIC_METHODS };

private final Class<?> toolClass;

public AotContribution(Class<?> toolClass) {
this.toolClass = toolClass;
}

@Override
public void applyTo(GenerationContext generationContext, BeanRegistrationCode beanRegistrationCode) {
ReflectionHints reflectionHints = generationContext.getRuntimeHints().reflection();
reflectionHints.registerType(toolClass, memberCategories);
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright 2023-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

@NonNullApi
@NonNullFields
package org.springframework.ai.aot;

import org.springframework.lang.NonNullApi;
import org.springframework.lang.NonNullFields;
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 the original author or authors.
* Copyright 2023-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -25,6 +25,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;

import org.springframework.ai.chat.model.ToolContext;
import org.springframework.ai.tool.function.FunctionToolCallback;
import org.springframework.util.Assert;

/**
Expand All @@ -41,7 +42,9 @@
* @param <I> the 3rd party service input type.
* @param <O> the 3rd party service output type.
* @author Christian Tzolov
* @deprecated in favor of {@link FunctionToolCallback}.
*/
@Deprecated
abstract class AbstractFunctionCallback<I, O> implements BiFunction<I, ToolContext, O>, FunctionCallback {

private final String name;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2024-2024 the original author or authors.
* Copyright 2024-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -26,9 +26,16 @@

import org.springframework.ai.model.function.FunctionCallback.CommonCallbackInvokingSpec;
import org.springframework.ai.model.function.FunctionCallback.SchemaType;
import org.springframework.ai.tool.function.FunctionToolCallback;
import org.springframework.ai.tool.method.MethodToolCallback;
import org.springframework.ai.util.JacksonUtils;
import org.springframework.util.Assert;

/**
* @deprecated Use specific builder for the type of tool you need, e.g.
* {@link FunctionToolCallback.Builder} and {@link MethodToolCallback.Builder}.
*/
@Deprecated
public class DefaultCommonCallbackInvokingSpec<B extends CommonCallbackInvokingSpec<B>>
implements CommonCallbackInvokingSpec<B> {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 the original author or authors.
* Copyright 2023-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -31,6 +31,8 @@
import org.springframework.ai.model.function.FunctionCallback.FunctionInvokingSpec;
import org.springframework.ai.model.function.FunctionCallback.MethodInvokingSpec;
import org.springframework.ai.model.function.FunctionCallback.SchemaType;
import org.springframework.ai.tool.function.FunctionToolCallback;
import org.springframework.ai.tool.method.MethodToolCallback;
import org.springframework.ai.util.ParsingUtils;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.util.Assert;
Expand All @@ -42,7 +44,10 @@
*
* @author Christian Tzolov
* @since 1.0.0
* @deprecated Use specific builder for the type of tool you need, e.g.
* {@link FunctionToolCallback.Builder} and {@link MethodToolCallback.Builder}.
*/
@Deprecated
public class DefaultFunctionCallbackBuilder implements FunctionCallback.Builder {

private static final Logger logger = LoggerFactory.getLogger(DefaultFunctionCallbackBuilder.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2024-2024 the original author or authors.
* Copyright 2024-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -25,6 +25,7 @@
import java.util.Set;

import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.model.tool.DefaultToolCallingChatOptions;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
Expand All @@ -35,7 +36,9 @@
* @author Christian Tzolov
* @author Thomas Vitale
* @author Ilayaperumal Gopinathan
* @deprecated in favor of {@link DefaultToolCallingChatOptions}.
*/
@Deprecated
public class DefaultFunctionCallingOptions implements FunctionCallingOptions {

private List<FunctionCallback> functionCallbacks = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2024-2024 the original author or authors.
* Copyright 2024-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -22,6 +22,7 @@
import java.util.Map;
import java.util.Set;

import org.springframework.ai.model.tool.DefaultToolCallingChatOptions;
import org.springframework.util.Assert;

/**
Expand All @@ -30,7 +31,9 @@
* @author Christian Tzolov
* @author Thomas Vitale
* @author Ilayaperumal Gopinathan
* @deprecated in favor of {@link DefaultToolCallingChatOptions.Builder}.
*/
@Deprecated
public class DefaultFunctionCallingOptionsBuilder implements FunctionCallingOptions.Builder {

private final DefaultFunctionCallingOptions options;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 the original author or authors.
* Copyright 2023-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -24,14 +24,19 @@
import com.fasterxml.jackson.databind.ObjectMapper;

import org.springframework.ai.chat.model.ToolContext;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.function.FunctionToolCallback;
import org.springframework.ai.tool.method.MethodToolCallback;
import org.springframework.core.ParameterizedTypeReference;

/**
* Represents a model function call handler. Implementations are registered with the
* Models and called on prompts that trigger the function call.
*
* @author Christian Tzolov
* @deprecated in favor of {@link ToolCallback}.
*/
@Deprecated
public interface FunctionCallback {

/**
Expand Down Expand Up @@ -115,7 +120,11 @@ enum SchemaType {
* <li>{@link FunctionInvokingSpec} - The function invoking builder interface.
* <li>{@link MethodInvokingSpec} - The method invoking builder interface.
* </ul>
*
* @deprecated Use specific builder for the type of tool you need, e.g.
* {@link FunctionToolCallback.Builder} and {@link MethodToolCallback.Builder}.
*/
@Deprecated
interface Builder {

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 the original author or authors.
* Copyright 2023-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -21,14 +21,17 @@
import java.util.Set;

import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.model.tool.ToolCallingChatOptions;

/**
* FunctionCallingOptions is a set of options that can be used to configure the function
* calling behavior of the ChatModel.
*
* @author Christian Tzolov
* @author Ilayaperumal Gopinathan
* @deprecated in favor of {@link ToolCallingChatOptions}.
*/
@Deprecated
public interface FunctionCallingOptions extends ChatOptions {

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 the original author or authors.
* Copyright 2023-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -23,6 +23,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;

import org.springframework.ai.chat.model.ToolContext;
import org.springframework.ai.tool.function.FunctionToolCallback;
import org.springframework.util.Assert;

/**
Expand All @@ -34,7 +35,9 @@
* @param <I> the input type
* @param <O> the output type
* @author Christian Tzolov
* @deprecated in favor of {@link FunctionToolCallback}.
*/
@Deprecated
public final class FunctionInvokingFunctionCallback<I, O> extends AbstractFunctionCallback<I, O> {

private final BiFunction<I, ToolContext, O> biFunction;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 the original author or authors.
* Copyright 2023-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -34,6 +34,7 @@

import org.springframework.ai.chat.model.ToolContext;
import org.springframework.ai.model.ModelOptionsUtils;
import org.springframework.ai.tool.method.MethodToolCallback;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
Expand All @@ -51,7 +52,9 @@
*
* @author Christian Tzolov
* @since 1.0.0
* @deprecated in favor of {@link MethodToolCallback}.
*/
@Deprecated
public class MethodInvokingFunctionCallback implements FunctionCallback {

private static final Logger logger = LoggerFactory.getLogger(MethodInvokingFunctionCallback.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,21 @@ default ToolMetadata getToolMetadata() {
}

@Override
@Deprecated // Call getToolDefinition().name() instead
default String getName() {
return getToolDefinition().name();
}

@Override
@Deprecated // Call getToolDefinition().description() instead
default String getDescription() {
return getToolDefinition().description();
}

@Override
@Deprecated // Call getToolDefinition().inputTypeSchema() instead
default String getInputTypeSchema() {
return getToolDefinition().inputTypeSchema();
return getToolDefinition().inputSchema();
}

}
Loading

0 comments on commit 29b9b1b

Please sign in to comment.