66import com .fasterxml .jackson .core .JsonProcessingException ;
77import com .fasterxml .jackson .core .type .TypeReference ;
88import com .fasterxml .jackson .databind .ObjectMapper ;
9+ import com .fasterxml .jackson .databind .node .ObjectNode ;
910import com .github .victools .jsonschema .generator .Option ;
1011import com .github .victools .jsonschema .generator .OptionPreset ;
1112import com .github .victools .jsonschema .generator .SchemaGenerator ;
2627import javax .annotation .Nullable ;
2728import lombok .AccessLevel ;
2829import lombok .AllArgsConstructor ;
29- import lombok .Data ;
3030import lombok .Getter ;
3131import lombok .RequiredArgsConstructor ;
32- import lombok .experimental .Accessors ;
32+ import lombok .Setter ;
33+ import lombok .Value ;
34+ import lombok .With ;
3335import lombok .extern .slf4j .Slf4j ;
3436
3537/**
3638 * Represents an OpenAI tool that can be used to define a function call in an OpenAI Chat Completion
3739 * request. This tool generates a JSON schema based on the provided class representing the
3840 * function's request structure.
3941 *
40- * @param <InputT> the type of the input argument for the function
4142 * @see <a href="https://platform.openai.com/docs/guides/gpt/function-calling"/>OpenAI Function
4243 * @since 1.7.0
4344 */
4445@ Slf4j
4546@ Beta
46- @ Data
47+ @ Value
48+ @ With
4749@ Getter (AccessLevel .PACKAGE )
48- @ Accessors (chain = true )
4950@ AllArgsConstructor (access = AccessLevel .PRIVATE )
50- public class OpenAiTool < InputT > {
51+ public class OpenAiTool {
5152
5253 private static final ObjectMapper JACKSON = new ObjectMapper ();
5354
5455 /** The schema generator used to create JSON schemas. */
5556 @ Nonnull private static final SchemaGenerator GENERATOR = createSchemaGenerator ();
5657
5758 /** The name of the function. */
58- @ Nonnull private String name ;
59+ @ Setter (AccessLevel .NONE )
60+ @ Nonnull
61+ String name ;
62+
63+ /** The function to execute a string argument to tool result object. */
64+ @ Setter (AccessLevel .NONE )
65+ @ Nonnull
66+ Function <String , Object > functionExecutor ;
5967
60- /** The model class for function request. */
61- @ Nonnull private Class <InputT > requestClass ;
68+ /** schema to be used for the function call. */
69+ @ Setter (AccessLevel .NONE )
70+ @ Nonnull
71+ ObjectNode schema ;
6272
6373 /** An optional description of the function. */
64- @ Nullable private String description ;
74+ @ Nullable String description ;
6575
6676 /** An optional flag indicating whether the function parameters should be treated strictly. */
67- @ Nullable private Boolean strict ;
77+ @ Nullable Boolean strict ;
6878
69- /** The function to be called. */
70- @ Nullable private Function <InputT , ?> function ;
79+ /**
80+ * Instantiates a OpenAiTool builder instance on behalf of an executable function.
81+ *
82+ * @param function the function to be executed.
83+ * @return an OpenAiTool builder instance.
84+ * @param <InputT> the type of the function input-argument class.
85+ */
86+ @ Nonnull
87+ public static <InputT > Builder1 <InputT > forFunction (@ Nonnull final Function <InputT , ?> function ) {
88+ return inputClass ->
89+ name -> {
90+ final Function <String , Object > exec =
91+ s -> function .apply (deserializeArgument (inputClass , s ));
92+ final var schema = GENERATOR .generateSchema (inputClass );
93+ return new OpenAiTool (name , exec , schema , null , null );
94+ };
95+ }
7196
7297 /**
73- * Constructs an {@code OpenAiFunctionTool} with the specified name and a model class that
74- * captures the request to the function.
98+ * Creates a new OpenAiTool instance with the specified function and input class.
7599 *
76- * @param name the name of the function
77- * @param requestClass the model class for function request
100+ * @param <InputT> the type of the input class.
78101 */
79- public OpenAiTool (@ Nonnull final String name , @ Nonnull final Class <InputT > requestClass ) {
80- this (name , requestClass , null , null , null );
102+ public interface Builder1 <InputT > {
103+ /**
104+ * Sets the name of the function.
105+ *
106+ * @param inputClass the class of the input object.
107+ * @return a new OpenAiTool instance with the specified function and input class.
108+ */
109+ @ Nonnull
110+ Builder2 withArgument (@ Nonnull final Class <InputT > inputClass );
81111 }
82112
83- @ Nonnull
84- Object execute (@ Nonnull final InputT argument ) {
85- if (getFunction () == null ) {
86- throw new IllegalStateException (
87- "Tool " + name + " is missing a method reference to execute." );
113+ /** Creates a new OpenAiTool instance with the specified name. */
114+ public interface Builder2 {
115+ /**
116+ * Sets the name of the function.
117+ *
118+ * @param name the name of the function
119+ * @return a new OpenAiTool instance with the specified name
120+ */
121+ @ Nonnull
122+ OpenAiTool withName (@ Nonnull final String name );
123+ }
124+
125+ @ Nullable
126+ private static <T > T deserializeArgument (@ Nonnull final Class <T > cl , @ Nonnull final String s ) {
127+ try {
128+ return JACKSON .readValue (s , cl );
129+ } catch (JsonProcessingException e ) {
130+ throw new IllegalArgumentException ("Failed to parse JSON string to class " + cl , e );
88131 }
89- return getFunction ().apply (argument );
90132 }
91133
92134 ChatCompletionTool createChatCompletionTool () {
93- final var schema = GENERATOR .generateSchema (getRequestClass ());
94135 final var schemaMap =
95136 OpenAiUtils .getOpenAiObjectMapper ()
96- .convertValue (schema , new TypeReference <Map <String , Object >>() {});
137+ .convertValue (getSchema () , new TypeReference <Map <String , Object >>() {});
97138
98139 return new ChatCompletionTool ()
99140 .type (FUNCTION )
@@ -128,7 +169,7 @@ private static SchemaGenerator createSchemaGenerator() {
128169 @ Beta
129170 @ Nonnull
130171 public static Execution execute (
131- @ Nonnull final List <OpenAiTool <?> > tools , @ Nonnull final OpenAiAssistantMessage msg )
172+ @ Nonnull final List <OpenAiTool > tools , @ Nonnull final OpenAiAssistantMessage msg )
132173 throws IllegalArgumentException {
133174 final var result = new LinkedHashMap <OpenAiFunctionCall , Object >();
134175
@@ -148,10 +189,11 @@ public static Execution execute(
148189 }
149190
150191 @ Nonnull
151- private static <I > Object executeFunction (
152- @ Nonnull final OpenAiTool <I > tool , @ Nonnull final OpenAiFunctionCall toolCall ) {
153- final I arguments = toolCall .getArgumentsAsObject (tool .getRequestClass ());
154- return tool .execute (arguments );
192+ private static Object executeFunction (
193+ @ Nonnull final OpenAiTool tool , @ Nonnull final OpenAiFunctionCall toolCall ) {
194+ final Function <String , Object > executor = tool .getFunctionExecutor ();
195+ final String arguments = toolCall .getArguments ();
196+ return executor .apply (arguments );
155197 }
156198
157199 @ Nonnull
0 commit comments