2323import com .fasterxml .jackson .databind .ObjectMapper ;
2424import com .fasterxml .jackson .databind .SerializationFeature ;
2525import com .fasterxml .jackson .databind .json .JsonMapper ;
26+ import org .slf4j .Logger ;
27+ import org .slf4j .LoggerFactory ;
2628
2729import org .springframework .ai .chat .model .ToolContext ;
2830import org .springframework .ai .model .ModelOptionsUtils ;
3133import org .springframework .ai .model .function .FunctionCallback .MethodInvokerBuilder ;
3234import org .springframework .ai .model .function .FunctionCallbackContext .SchemaType ;
3335import org .springframework .ai .util .JacksonUtils ;
36+ import org .springframework .ai .util .ParsingUtils ;
3437import org .springframework .core .ParameterizedTypeReference ;
3538import org .springframework .util .Assert ;
3639import org .springframework .util .ReflectionUtils ;
40+ import org .springframework .util .StringUtils ;
3741
3842/**
3943 * @author Christian Tzolov
4246
4347public class DefaultFunctionCallbackBuilder implements FunctionCallback .Builder {
4448
49+ private final static Logger logger = LoggerFactory .getLogger (DefaultFunctionCallbackBuilder .class );
50+
4551 private String description ;
4652
4753 private SchemaType schemaType = SchemaType .JSON_SCHEMA ;
@@ -151,7 +157,6 @@ public FunctionInvokerBuilder<I, O> inputType(ParameterizedTypeReference<?> inpu
151157 @ Override
152158 public FunctionCallback build () {
153159
154- Assert .hasText (description , "Description must not be empty" );
155160 Assert .notNull (objectMapper , "ObjectMapper must not be null" );
156161 Assert .hasText (this .name , "Name must not be empty" );
157162 Assert .notNull (responseConverter , "ResponseConverter must not be null" );
@@ -165,10 +170,17 @@ public FunctionCallback build() {
165170 BiFunction <I , ToolContext , O > finalBiFunction = (this .biFunction != null ) ? this .biFunction
166171 : (request , context ) -> this .function .apply (request );
167172
168- return new FunctionCallbackWrapper (this .name , description , inputTypeSchema , this .inputType ,
173+ return new FunctionCallbackWrapper (this .name , this . getDescription () , inputTypeSchema , this .inputType ,
169174 (Function <I , String >) responseConverter , objectMapper , finalBiFunction );
170175 }
171176
177+ private String getDescription () {
178+ if (StringUtils .hasText (description )) {
179+ return description ;
180+ }
181+ return generateDescription (this .name );
182+ }
183+
172184 }
173185
174186 public class MethodInvokerBuilderImpl implements FunctionCallback .MethodInvokerBuilder {
@@ -215,10 +227,29 @@ public FunctionCallback build() {
215227 Assert .isTrue (this .targetClass != null || this .targetObject != null ,
216228 "Target class or object must not be null" );
217229 var method = ReflectionUtils .findMethod (targetClass , methodName , argumentTypes );
218- return new MethodFunctionCallback (this .targetObject , method , description , objectMapper , this .name ,
230+ return new MethodFunctionCallback (this .targetObject , method , this . getDescription () , objectMapper , this .name ,
219231 responseConverter );
220232 }
221233
234+ private String getDescription () {
235+ if (StringUtils .hasText (description )) {
236+ return description ;
237+ }
238+
239+ return generateDescription (StringUtils .hasText (this .name ) ? this .name : this .methodName );
240+ }
241+
242+ }
243+
244+ private String generateDescription (String fromName ) {
245+
246+ String generatedDescription = ParsingUtils .reConcatenateCamelCase (fromName , " " );
247+
248+ logger .warn ("Description is not set! A best effort attempt to generate a description:'{}' from the:'{}'" ,
249+ generatedDescription , fromName );
250+ logger .warn ("It is recommended to set the Description explicitly! Use the 'description()' method!" );
251+
252+ return generatedDescription ;
222253 }
223254
224255}
0 commit comments