Skip to content

Commit f111aae

Browse files
committed
Revert "Add support for Kotlin functions"
This reverts commit 9cf6633.
1 parent 3fbc310 commit f111aae

File tree

13 files changed

+119
-721
lines changed

13 files changed

+119
-721
lines changed

spring-ai-core/pom.xml

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,18 @@
5454
<version>${jsonschema.version}</version>
5555
</dependency>
5656

57+
<dependency>
58+
<groupId>org.springframework.cloud</groupId>
59+
<artifactId>spring-cloud-function-context</artifactId>
60+
<version>${spring-cloud-function-context.version}</version>
61+
<exclusions>
62+
<exclusion>
63+
<groupId>org.springframework.boot</groupId>
64+
<artifactId>spring-boot-autoconfigure</artifactId>
65+
</exclusion>
66+
</exclusions>
67+
</dependency>
68+
5769
<!-- production dependencies -->
5870
<dependency>
5971
<groupId>org.antlr</groupId>
@@ -126,13 +138,6 @@
126138
<version>${jackson.version}</version>
127139
</dependency>
128140

129-
<dependency>
130-
<groupId>org.jetbrains.kotlin</groupId>
131-
<artifactId>kotlin-stdlib</artifactId>
132-
<version>${kotlin.version}</version>
133-
<optional>true</optional>
134-
</dependency>
135-
136141
<!-- test dependencies -->
137142
<dependency>
138143
<groupId>org.springframework.boot</groupId>
@@ -141,16 +146,16 @@
141146
</dependency>
142147

143148
<dependency>
144-
<groupId>com.fasterxml.jackson.module</groupId>
145-
<artifactId>jackson-module-kotlin</artifactId>
146-
<version>${jackson.version}</version>
149+
<groupId>org.jetbrains.kotlin</groupId>
150+
<artifactId>kotlin-stdlib</artifactId>
151+
<version>${kotlin.version}</version>
147152
<scope>test</scope>
148153
</dependency>
149154

150155
<dependency>
151-
<groupId>io.mockk</groupId>
152-
<artifactId>mockk-jvm</artifactId>
153-
<version>1.13.13</version>
156+
<groupId>com.fasterxml.jackson.module</groupId>
157+
<artifactId>jackson-module-kotlin</artifactId>
158+
<version>${jackson.version}</version>
154159
<scope>test</scope>
155160
</dependency>
156161

spring-ai-core/src/main/java/org/springframework/ai/model/function/FunctionCallbackContext.java

Lines changed: 21 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,20 @@
1616

1717
package org.springframework.ai.model.function;
1818

19+
import java.lang.reflect.Type;
1920
import java.util.function.BiFunction;
2021
import java.util.function.Function;
2122

2223
import com.fasterxml.jackson.annotation.JsonClassDescription;
23-
import kotlin.jvm.functions.Function1;
24-
import kotlin.jvm.functions.Function2;
2524

2625
import org.springframework.ai.chat.model.ToolContext;
2726
import org.springframework.beans.BeansException;
28-
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
29-
import org.springframework.beans.factory.config.BeanDefinition;
27+
import org.springframework.cloud.function.context.catalog.FunctionTypeUtils;
28+
import org.springframework.cloud.function.context.config.FunctionContextUtils;
3029
import org.springframework.context.ApplicationContext;
3130
import org.springframework.context.ApplicationContextAware;
3231
import org.springframework.context.annotation.Description;
3332
import org.springframework.context.support.GenericApplicationContext;
34-
import org.springframework.core.ResolvableType;
3533
import org.springframework.lang.NonNull;
3634
import org.springframework.lang.Nullable;
3735
import org.springframework.util.StringUtils;
@@ -51,7 +49,6 @@
5149
*
5250
* @author Christian Tzolov
5351
* @author Christopher Smith
54-
* @author Sebastien Deleuze
5552
*/
5653
public class FunctionCallbackContext implements ApplicationContextAware {
5754

@@ -71,19 +68,23 @@ public void setApplicationContext(@NonNull ApplicationContext applicationContext
7168
@SuppressWarnings({ "rawtypes", "unchecked" })
7269
public FunctionCallback getFunctionCallback(@NonNull String beanName, @Nullable String defaultDescription) {
7370

74-
BeanDefinition beanDefinition;
75-
try {
76-
beanDefinition = this.applicationContext.getBeanDefinition(beanName);
71+
Type beanType = FunctionContextUtils.findType(this.applicationContext.getBeanFactory(), beanName);
72+
73+
if (beanType == null) {
74+
throw new IllegalArgumentException(
75+
"Functional bean with name: " + beanName + " does not exist in the context.");
7776
}
78-
catch (NoSuchBeanDefinitionException ex) {
77+
78+
if (!Function.class.isAssignableFrom(FunctionTypeUtils.getRawType(beanType))
79+
&& !BiFunction.class.isAssignableFrom(FunctionTypeUtils.getRawType(beanType))) {
7980
throw new IllegalArgumentException(
80-
"Functional bean with name " + beanName + " does not exist in the context.");
81+
"Function call Bean must be of type Function or BiFunction. Found: " + beanType.getTypeName());
8182
}
8283

83-
ResolvableType functionType = beanDefinition.getResolvableType();
84-
ResolvableType functionInputType = TypeResolverHelper.getFunctionArgumentType(functionType.getType(), 0);
84+
Type functionInputType = TypeResolverHelper.getFunctionArgumentType(beanType, 0);
8585

86-
Class<?> functionInputClass = functionInputType.toClass();
86+
Class<?> functionInputClass = FunctionTypeUtils.getRawType(functionInputType);
87+
String functionName = beanName;
8788
String functionDescription = defaultDescription;
8889

8990
if (!StringUtils.hasText(functionDescription)) {
@@ -113,40 +114,24 @@ public FunctionCallback getFunctionCallback(@NonNull String beanName, @Nullable
113114

114115
Object bean = this.applicationContext.getBean(beanName);
115116

116-
if (KotlinDelegate.isKotlinFunction(functionType.toClass())) {
117-
return FunctionCallbackWrapper.builder(KotlinDelegate.wrapKotlinFunction(bean))
118-
.withName(beanName)
119-
.withSchemaType(this.schemaType)
120-
.withDescription(functionDescription)
121-
.withInputType(functionInputClass)
122-
.build();
123-
}
124-
else if (KotlinDelegate.isKotlinBiFunction(functionType.toClass())) {
125-
return FunctionCallbackWrapper.builder(KotlinDelegate.wrapKotlinBiFunction(bean))
126-
.withName(beanName)
127-
.withSchemaType(this.schemaType)
128-
.withDescription(functionDescription)
129-
.withInputType(functionInputClass)
130-
.build();
131-
}
132-
else if (bean instanceof Function<?, ?> function) {
117+
if (bean instanceof Function<?, ?> function) {
133118
return FunctionCallbackWrapper.builder(function)
134-
.withName(beanName)
119+
.withName(functionName)
135120
.withSchemaType(this.schemaType)
136121
.withDescription(functionDescription)
137122
.withInputType(functionInputClass)
138123
.build();
139124
}
140-
else if (bean instanceof BiFunction<?, ?, ?>) {
141-
return FunctionCallbackWrapper.builder((BiFunction<?, ToolContext, ?>) bean)
142-
.withName(beanName)
125+
else if (bean instanceof BiFunction<?, ?, ?> biFunction) {
126+
return FunctionCallbackWrapper.builder((BiFunction<?, ToolContext, ?>) biFunction)
127+
.withName(functionName)
143128
.withSchemaType(this.schemaType)
144129
.withDescription(functionDescription)
145130
.withInputType(functionInputClass)
146131
.build();
147132
}
148133
else {
149-
throw new IllegalStateException();
134+
throw new IllegalArgumentException("Bean must be of type Function");
150135
}
151136
}
152137

@@ -156,26 +141,4 @@ public enum SchemaType {
156141

157142
}
158143

159-
private static class KotlinDelegate {
160-
161-
public static boolean isKotlinFunction(Class<?> clazz) {
162-
return Function1.class.isAssignableFrom(clazz);
163-
}
164-
165-
@SuppressWarnings("unchecked")
166-
public static Function<?, ?> wrapKotlinFunction(Object function) {
167-
return t -> ((Function1<Object, Object>) function).invoke(t);
168-
}
169-
170-
public static boolean isKotlinBiFunction(Class<?> clazz) {
171-
return Function2.class.isAssignableFrom(clazz);
172-
}
173-
174-
@SuppressWarnings("unchecked")
175-
public static BiFunction<?, ToolContext, ?> wrapKotlinBiFunction(Object function) {
176-
return (t, u) -> ((Function2<Object, ToolContext, Object>) function).invoke(t, u);
177-
}
178-
179-
}
180-
181144
}

spring-ai-core/src/main/java/org/springframework/ai/model/function/TypeResolverHelper.java

Lines changed: 69 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,21 @@
1616

1717
package org.springframework.ai.model.function;
1818

19+
import java.lang.reflect.GenericArrayType;
20+
import java.lang.reflect.ParameterizedType;
1921
import java.lang.reflect.Type;
2022
import java.util.function.BiFunction;
2123
import java.util.function.Function;
2224

23-
import kotlin.jvm.functions.Function1;
24-
import kotlin.jvm.functions.Function2;
25+
import net.jodah.typetools.TypeResolver;
2526

26-
import org.springframework.core.KotlinDetector;
27-
import org.springframework.core.ResolvableType;
27+
import org.springframework.cloud.function.context.catalog.FunctionTypeUtils;
2828

2929
/**
3030
* A utility class that provides methods for resolving types and classes related to
3131
* functions.
3232
*
3333
* @author Christian Tzolov
34-
* @author Sebastien Dekeuze
3534
*/
3635
public abstract class TypeResolverHelper {
3736

@@ -69,9 +68,12 @@ public static Class<?> getFunctionOutputClass(Class<? extends Function<?, ?>> fu
6968
* @return The class of the specified function argument.
7069
*/
7170
public static Class<?> getFunctionArgumentClass(Class<? extends Function<?, ?>> functionClass, int argumentIndex) {
72-
ResolvableType resolvableType = ResolvableType.forClass(functionClass).as(Function.class);
73-
return (resolvableType == ResolvableType.NONE ? Object.class
74-
: resolvableType.getGeneric(argumentIndex).toClass());
71+
Type type = TypeResolver.reify(Function.class, functionClass);
72+
73+
var argumentType = type instanceof ParameterizedType
74+
? ((ParameterizedType) type).getActualTypeArguments()[argumentIndex] : Object.class;
75+
76+
return toRawClass(argumentType);
7577
}
7678

7779
/**
@@ -82,65 +84,80 @@ public static Class<?> getFunctionArgumentClass(Class<? extends Function<?, ?>>
8284
*/
8385
public static Class<?> getBiFunctionArgumentClass(Class<? extends BiFunction<?, ?, ?>> biFunctionClass,
8486
int argumentIndex) {
85-
ResolvableType resolvableType = ResolvableType.forClass(biFunctionClass).as(BiFunction.class);
86-
return (resolvableType == ResolvableType.NONE ? Object.class
87-
: resolvableType.getGeneric(argumentIndex).toClass());
87+
Type type = TypeResolver.reify(BiFunction.class, biFunctionClass);
88+
89+
Type argumentType = type instanceof ParameterizedType
90+
? ((ParameterizedType) type).getActualTypeArguments()[argumentIndex] : Object.class;
91+
92+
return toRawClass(argumentType);
93+
}
94+
95+
/**
96+
* Returns the input type of a given function class.
97+
* @param functionClass The class of the function.
98+
* @return The input type of the function.
99+
*/
100+
public static Type getFunctionInputType(Class<? extends Function<?, ?>> functionClass) {
101+
return getFunctionArgumentType(functionClass, 0);
102+
}
103+
104+
/**
105+
* Retrieves the output type of a given function class.
106+
* @param functionClass The function class.
107+
* @return The output type of the function.
108+
*/
109+
public static Type getFunctionOutputType(Class<? extends Function<?, ?>> functionClass) {
110+
return getFunctionArgumentType(functionClass, 1);
88111
}
89112

90113
/**
91114
* Retrieves the type of a specific argument in a given function class.
92-
* @param functionType The function type.
115+
* @param functionClass The function class.
93116
* @param argumentIndex The index of the argument whose type should be retrieved.
94117
* @return The type of the specified function argument.
95-
* @throws IllegalArgumentException if functionType is not a supported type
96118
*/
97-
public static ResolvableType getFunctionArgumentType(Type functionType, int argumentIndex) {
98-
99-
ResolvableType resolvableType = ResolvableType.forType(functionType);
100-
Class<?> resolvableClass = resolvableType.toClass();
101-
ResolvableType functionArgumentResolvableType = ResolvableType.NONE;
119+
public static Type getFunctionArgumentType(Class<? extends Function<?, ?>> functionClass, int argumentIndex) {
120+
Type functionType = TypeResolver.reify(Function.class, functionClass);
121+
return getFunctionArgumentType(functionType, argumentIndex);
122+
}
102123

103-
if (Function.class.isAssignableFrom(resolvableClass)) {
104-
functionArgumentResolvableType = resolvableType.as(Function.class);
105-
}
106-
else if (BiFunction.class.isAssignableFrom(resolvableClass)) {
107-
functionArgumentResolvableType = resolvableType.as(BiFunction.class);
108-
}
109-
else if (KotlinDetector.isKotlinPresent()) {
110-
if (KotlinDelegate.isKotlinFunction(resolvableClass)) {
111-
functionArgumentResolvableType = KotlinDelegate.adaptToKotlinFunctionType(resolvableType);
124+
/**
125+
* Retrieves the type of a specific argument in a given function type.
126+
* @param functionType The function type.
127+
* @param argumentIndex The index of the argument whose type should be retrieved.
128+
* @return The type of the specified function argument.
129+
*/
130+
public static Type getFunctionArgumentType(Type functionType, int argumentIndex) {
131+
132+
// Resolves: https://github.com/spring-projects/spring-ai/issues/726
133+
if (!(functionType instanceof ParameterizedType)) {
134+
Class<?> functionalClass = FunctionTypeUtils.getRawType(functionType);
135+
// Resolves: https://github.com/spring-projects/spring-ai/issues/1576
136+
if (BiFunction.class.isAssignableFrom(functionalClass)) {
137+
functionType = TypeResolver.reify(BiFunction.class, (Class<BiFunction<?, ?, ?>>) functionalClass);
112138
}
113-
else if (KotlinDelegate.isKotlinBiFunction(resolvableClass)) {
114-
functionArgumentResolvableType = KotlinDelegate.adaptToKotlinBiFunctionType(resolvableType);
139+
else {
140+
functionType = FunctionTypeUtils.discoverFunctionTypeFromClass(functionalClass);
115141
}
116142
}
117143

118-
if (functionArgumentResolvableType == ResolvableType.NONE) {
119-
throw new IllegalArgumentException(
120-
"Type must be a Function, BiFunction, Function1 or Function2. Found: " + resolvableType);
121-
}
144+
var argumentType = functionType instanceof ParameterizedType
145+
? ((ParameterizedType) functionType).getActualTypeArguments()[argumentIndex] : Object.class;
122146

123-
return functionArgumentResolvableType.getGeneric(argumentIndex);
147+
return argumentType;
124148
}
125149

126-
private static class KotlinDelegate {
127-
128-
public static boolean isKotlinFunction(Class<?> clazz) {
129-
return Function1.class.isAssignableFrom(clazz);
130-
}
131-
132-
public static ResolvableType adaptToKotlinFunctionType(ResolvableType resolvableType) {
133-
return resolvableType.as(Function1.class);
134-
}
135-
136-
public static boolean isKotlinBiFunction(Class<?> clazz) {
137-
return Function2.class.isAssignableFrom(clazz);
138-
}
139-
140-
public static ResolvableType adaptToKotlinBiFunctionType(ResolvableType resolvableType) {
141-
return resolvableType.as(Function2.class);
142-
}
143-
150+
/**
151+
* Effectively converts {@link Type} which could be {@link ParameterizedType} to raw
152+
* Class (no generics).
153+
* @param type actual {@link Type} instance
154+
* @return instance of {@link Class} as raw representation of the provided
155+
* {@link Type}
156+
*/
157+
public static Class<?> toRawClass(Type type) {
158+
return type != null
159+
? TypeResolver.resolveRawClass(type instanceof GenericArrayType ? type : TypeResolver.reify(type), null)
160+
: null;
144161
}
145162

146163
}

spring-ai-core/src/main/kotlin/org/springframework/ai/model/function/FunctionCallbackWrapperExtensions.kt

Lines changed: 0 additions & 26 deletions
This file was deleted.

0 commit comments

Comments
 (0)