1616
1717package org .springframework .ai .model .function ;
1818
19- import java .lang .reflect .Type ;
2019import java .util .function .BiFunction ;
2120import java .util .function .Function ;
2221
2322import com .fasterxml .jackson .annotation .JsonClassDescription ;
23+ import kotlin .jvm .functions .Function1 ;
24+ import kotlin .jvm .functions .Function2 ;
2425
2526import org .springframework .ai .chat .model .ToolContext ;
2627import org .springframework .beans .BeansException ;
27- import org .springframework .cloud .function .context .catalog .FunctionTypeUtils ;
28- import org .springframework .cloud .function .context .config .FunctionContextUtils ;
2928import org .springframework .context .ApplicationContext ;
3029import org .springframework .context .ApplicationContextAware ;
3130import org .springframework .context .annotation .Description ;
3231import org .springframework .context .support .GenericApplicationContext ;
32+ import org .springframework .core .KotlinDetector ;
33+ import org .springframework .core .ResolvableType ;
3334import org .springframework .lang .NonNull ;
3435import org .springframework .lang .Nullable ;
3536import org .springframework .util .StringUtils ;
4950 *
5051 * @author Christian Tzolov
5152 * @author Christopher Smith
53+ * @author Sebastien Deleuze
5254 */
5355public class FunctionCallbackContext implements ApplicationContextAware {
5456
@@ -65,26 +67,13 @@ public void setApplicationContext(@NonNull ApplicationContext applicationContext
6567 this .applicationContext = (GenericApplicationContext ) applicationContext ;
6668 }
6769
68- @ SuppressWarnings ({ "rawtypes" , " unchecked" })
70+ @ SuppressWarnings ({ "unchecked" })
6971 public FunctionCallback getFunctionCallback (@ NonNull String beanName , @ Nullable String defaultDescription ) {
7072
71- Type beanType = FunctionContextUtils .findType (this .applicationContext .getBeanFactory (), beanName );
73+ ResolvableType functionType = TypeResolverHelper .resolveBeanType (this .applicationContext , beanName );
74+ ResolvableType functionInputType = TypeResolverHelper .getFunctionArgumentType (functionType , 0 );
7275
73- if (beanType == null ) {
74- throw new IllegalArgumentException (
75- "Functional bean with name: " + beanName + " does not exist in the context." );
76- }
77-
78- if (!Function .class .isAssignableFrom (FunctionTypeUtils .getRawType (beanType ))
79- && !BiFunction .class .isAssignableFrom (FunctionTypeUtils .getRawType (beanType ))) {
80- throw new IllegalArgumentException (
81- "Function call Bean must be of type Function or BiFunction. Found: " + beanType .getTypeName ());
82- }
83-
84- Type functionInputType = TypeResolverHelper .getFunctionArgumentType (beanType , 0 );
85-
86- Class <?> functionInputClass = FunctionTypeUtils .getRawType (functionInputType );
87- String functionName = beanName ;
76+ Class <?> functionInputClass = functionInputType .toClass ();
8877 String functionDescription = defaultDescription ;
8978
9079 if (!StringUtils .hasText (functionDescription )) {
@@ -114,24 +103,42 @@ public FunctionCallback getFunctionCallback(@NonNull String beanName, @Nullable
114103
115104 Object bean = this .applicationContext .getBean (beanName );
116105
106+ if (KotlinDetector .isKotlinPresent ()) {
107+ if (KotlinDelegate .isKotlinFunction (functionType .toClass ())) {
108+ return FunctionCallbackWrapper .builder (KotlinDelegate .wrapKotlinFunction (bean ))
109+ .withName (beanName )
110+ .withSchemaType (this .schemaType )
111+ .withDescription (functionDescription )
112+ .withInputType (functionInputClass )
113+ .build ();
114+ }
115+ else if (KotlinDelegate .isKotlinBiFunction (functionType .toClass ())) {
116+ return FunctionCallbackWrapper .builder (KotlinDelegate .wrapKotlinBiFunction (bean ))
117+ .withName (beanName )
118+ .withSchemaType (this .schemaType )
119+ .withDescription (functionDescription )
120+ .withInputType (functionInputClass )
121+ .build ();
122+ }
123+ }
117124 if (bean instanceof Function <?, ?> function ) {
118125 return FunctionCallbackWrapper .builder (function )
119- .withName (functionName )
126+ .withName (beanName )
120127 .withSchemaType (this .schemaType )
121128 .withDescription (functionDescription )
122129 .withInputType (functionInputClass )
123130 .build ();
124131 }
125- else if (bean instanceof BiFunction <?, ?, ?> biFunction ) {
126- return FunctionCallbackWrapper .builder ((BiFunction <?, ToolContext , ?>) biFunction )
127- .withName (functionName )
132+ else if (bean instanceof BiFunction <?, ?, ?>) {
133+ return FunctionCallbackWrapper .builder ((BiFunction <?, ToolContext , ?>) bean )
134+ .withName (beanName )
128135 .withSchemaType (this .schemaType )
129136 .withDescription (functionDescription )
130137 .withInputType (functionInputClass )
131138 .build ();
132139 }
133140 else {
134- throw new IllegalArgumentException ( "Bean must be of type Function" );
141+ throw new IllegalStateException ( );
135142 }
136143 }
137144
@@ -141,4 +148,26 @@ public enum SchemaType {
141148
142149 }
143150
151+ private static class KotlinDelegate {
152+
153+ public static boolean isKotlinFunction (Class <?> clazz ) {
154+ return Function1 .class .isAssignableFrom (clazz );
155+ }
156+
157+ @ SuppressWarnings ("unchecked" )
158+ public static Function <?, ?> wrapKotlinFunction (Object function ) {
159+ return t -> ((Function1 <Object , Object >) function ).invoke (t );
160+ }
161+
162+ public static boolean isKotlinBiFunction (Class <?> clazz ) {
163+ return Function2 .class .isAssignableFrom (clazz );
164+ }
165+
166+ @ SuppressWarnings ("unchecked" )
167+ public static BiFunction <?, ToolContext , ?> wrapKotlinBiFunction (Object function ) {
168+ return (t , u ) -> ((Function2 <Object , ToolContext , Object >) function ).invoke (t , u );
169+ }
170+
171+ }
172+
144173}
0 commit comments