001package com.box.sdk;
002
003import com.box.sdk.http.HttpMethod;
004import com.eclipsesource.json.Json;
005import com.eclipsesource.json.JsonArray;
006import com.eclipsesource.json.JsonObject;
007import java.net.URL;
008import java.util.List;
009
010
011public final class BoxAI {
012
013    /**
014     * Ask AI url.
015     */
016    public static final URLTemplate SEND_AI_REQUEST_URL = new URLTemplate("ai/ask");
017    /**
018     * Text gen AI url.
019     */
020    public static final URLTemplate SEND_AI_TEXT_GEN_REQUEST_URL = new URLTemplate("ai/text_gen");
021    /**
022     * AI agent default config url.
023     */
024    public static final URLTemplate AI_AGENT_DEFAULT_CONFIG_URL = new URLTemplate("ai_agent_default");
025    /**
026     * AI extract metadata freeform url.
027     */
028    public static final URLTemplate EXTRACT_METADATA_FREEFORM_URL = new URLTemplate("ai/extract");
029    /**
030     * AI extract metadata structured url.
031     */
032    public static final URLTemplate EXTRACT_METADATA_STRUCTURED_URL = new URLTemplate("ai/extract_structured");
033
034    private BoxAI() {
035    }
036
037    /**
038     * Sends an AI request to supported LLMs and returns an answer specifically focused
039     * on the user's question given the provided items.
040     *
041     * @param api    the API connection to be used by the created user.
042     * @param prompt The prompt provided by the client to be answered by the LLM.
043     * @param items  The items to be processed by the LLM, currently only files are supported.
044     * @param mode   The mode specifies if this request is for a single or multiple items.
045     * @return The response from the AI.
046     */
047    public static BoxAIResponse sendAIRequest(BoxAPIConnection api, String prompt, List<BoxAIItem> items, Mode mode) {
048        return sendAIRequest(api, prompt, items, mode, null, null, null);
049    }
050
051    /**
052     * Sends an AI request to supported LLMs and returns an answer specifically focused
053     * on the user's question given the provided items.
054     *
055     * @param api             the API connection to be used by the created user.
056     * @param prompt          The prompt provided by the client to be answered by the LLM.
057     * @param items           The items to be processed by the LLM, currently only files are supported.
058     * @param mode            The mode specifies if this request is for a single or multiple items.
059     * @param dialogueHistory The history of prompts and answers previously passed to the LLM.
060     *                        This provides additional context to the LLM in generating the response.
061     * @param agent           The AI agent configuration to be used for the request.
062     * @param includeCitations Whether to include citations in the response.
063     * @return The response from the AI.
064     */
065    public static BoxAIResponse sendAIRequest(BoxAPIConnection api, String prompt, List<BoxAIItem> items, Mode mode,
066                                              List<BoxAIDialogueEntry> dialogueHistory, BoxAIAgentAsk agent,
067                                              Boolean includeCitations) {
068        URL url = SEND_AI_REQUEST_URL.build(api.getBaseURL());
069        JsonObject requestJSON = new JsonObject();
070        requestJSON.add("mode", mode.toString());
071        requestJSON.add("prompt", prompt);
072
073        JsonArray itemsJSON = new JsonArray();
074        for (BoxAIItem item : items) {
075            itemsJSON.add(item.getJSONObject());
076        }
077        requestJSON.add("items", itemsJSON);
078
079        if (dialogueHistory != null) {
080            JsonArray dialogueHistoryJSON = new JsonArray();
081            for (BoxAIDialogueEntry dialogueEntry : dialogueHistory) {
082                dialogueHistoryJSON.add(dialogueEntry.getJSONObject());
083            }
084            requestJSON.add("dialogue_history", dialogueHistoryJSON);
085        }
086        if (agent != null) {
087            requestJSON.add("ai_agent", agent.getJSONObject());
088        }
089        if (includeCitations != null) {
090            requestJSON.add("include_citations", includeCitations);
091        }
092
093        BoxJSONRequest req = new BoxJSONRequest(api, url, HttpMethod.POST);
094        req.setBody(requestJSON.toString());
095
096        try (BoxJSONResponse response = req.send()) {
097            JsonObject responseJSON = Json.parse(response.getJSON()).asObject();
098            return new BoxAIResponse(responseJSON);
099        }
100    }
101
102    /**
103     * Sends an AI request to supported LLMs and returns an answer specifically focused on the creation of new text.
104     *
105     * @param api    the API connection to be used by the created user.
106     * @param prompt The prompt provided by the client to be answered by the LLM.
107     * @param items  The items to be processed by the LLM, currently only files are supported.
108     * @return The response from the AI.
109     */
110    public static BoxAIResponse sendAITextGenRequest(BoxAPIConnection api, String prompt, List<BoxAIItem> items) {
111        return sendAITextGenRequest(api, prompt, items, null);
112    }
113
114    /**
115     * Sends an AI request to supported LLMs and returns an answer specifically focused on the creation of new text.
116     *
117     * @param api             the API connection to be used by the created user.
118     * @param prompt          The prompt provided by the client to be answered by the LLM.
119     * @param items           The items to be processed by the LLM, currently only files are supported.
120     * @param dialogueHistory The history of prompts and answers previously passed to the LLM.
121     *                        This provides additional context to the LLM in generating the response.
122     * @return The response from the AI.
123     */
124    public static BoxAIResponse sendAITextGenRequest(BoxAPIConnection api, String prompt, List<BoxAIItem> items,
125                                                     List<BoxAIDialogueEntry> dialogueHistory) {
126        return sendAITextGenRequest(api, prompt, items, dialogueHistory, null);
127    }
128
129    /**
130     * Sends an AI request to supported LLMs and returns an answer specifically focused on the creation of new text.
131     *
132     * @param api             the API connection to be used by the created user.
133     * @param prompt          The prompt provided by the client to be answered by the LLM.
134     * @param items           The items to be processed by the LLM, currently only files are supported.
135     * @param dialogueHistory The history of prompts and answers previously passed to the LLM.
136     *                        This provides additional context to the LLM in generating the response.
137     * @param agent           The AI agent configuration to be used for the request.
138     * @return The response from the AI.
139     */
140    public static BoxAIResponse sendAITextGenRequest(BoxAPIConnection api, String prompt, List<BoxAIItem> items,
141                                                     List<BoxAIDialogueEntry> dialogueHistory,
142                                                     BoxAIAgentTextGen agent) {
143        URL url = SEND_AI_TEXT_GEN_REQUEST_URL.build(api.getBaseURL());
144        JsonObject requestJSON = new JsonObject();
145        requestJSON.add("prompt", prompt);
146
147        JsonArray itemsJSON = new JsonArray();
148        for (BoxAIItem item : items) {
149            itemsJSON.add(item.getJSONObject());
150        }
151        requestJSON.add("items", itemsJSON);
152
153        if (dialogueHistory != null) {
154            JsonArray dialogueHistoryJSON = new JsonArray();
155            for (BoxAIDialogueEntry dialogueEntry : dialogueHistory) {
156                dialogueHistoryJSON.add(dialogueEntry.getJSONObject());
157            }
158            requestJSON.add("dialogue_history", dialogueHistoryJSON);
159        }
160
161        if (agent != null) {
162            requestJSON.add("ai_agent", agent.getJSONObject());
163        }
164
165        BoxJSONRequest req = new BoxJSONRequest(api, url, HttpMethod.POST);
166        req.setBody(requestJSON.toString());
167
168        try (BoxJSONResponse response = req.send()) {
169            JsonObject responseJSON = Json.parse(response.getJSON()).asObject();
170            return new BoxAIResponse(responseJSON);
171        }
172    }
173
174    /**
175     * Get the default AI Agent use for the given mode.
176     *
177     * @param api  The API connection to be used by the created user.
178     * @param mode The mode to filter the agent config to return.
179     * @return A successful response including the default agent configuration.
180     */
181    public static BoxAIAgent getAiAgentDefaultConfig(BoxAPIConnection api, BoxAIAgent.Mode mode) {
182        return getAiAgentDefaultConfig(api, mode, null, null);
183    }
184
185    /**
186     * Get the default AI Agent use for the given mode.
187     *
188     * @param api      The API connection to be used by the created user.
189     * @param mode     The mode to filter the agent config to return.
190     * @param language The language to filter the agent config to return.
191     * @param model    The model to filter the agent config to return.
192     * @return A successful response including the default agent configuration.
193     */
194    public static BoxAIAgent getAiAgentDefaultConfig(BoxAPIConnection api,
195                                                     BoxAIAgent.Mode mode,
196                                                     String language,
197                                                     String model) {
198        QueryStringBuilder builder = new QueryStringBuilder();
199        builder.appendParam("mode", mode.toString());
200        if (language != null) {
201            builder.appendParam("language", language);
202        }
203        if (model != null) {
204            builder.appendParam("model", model);
205        }
206        URL url = AI_AGENT_DEFAULT_CONFIG_URL.buildWithQuery(api.getBaseURL(), builder.toString());
207        BoxAPIRequest req = new BoxAPIRequest(api, url, HttpMethod.GET);
208        try (BoxJSONResponse response = (BoxJSONResponse) req.send()) {
209            JsonObject responseJSON = Json.parse(response.getJSON()).asObject();
210            return BoxAIAgent.parse(responseJSON);
211        }
212    }
213
214    public enum Mode {
215        /**
216         * Multiple items
217         */
218        MULTIPLE_ITEM_QA("multiple_item_qa"),
219
220        /**
221         * Single item
222         */
223        SINGLE_ITEM_QA("single_item_qa");
224
225        private final String mode;
226
227        Mode(String mode) {
228            this.mode = mode;
229        }
230
231        static BoxAI.Mode fromJSONValue(String jsonValue) {
232            if (jsonValue.equals("multiple_item_qa")) {
233                return BoxAI.Mode.MULTIPLE_ITEM_QA;
234            } else if (jsonValue.equals("single_item_qa")) {
235                return BoxAI.Mode.SINGLE_ITEM_QA;
236            } else {
237                System.out.print("Invalid AI mode.");
238                return null;
239            }
240        }
241
242        String toJSONValue() {
243            return this.mode;
244        }
245
246        public String toString() {
247            return this.mode;
248        }
249    }
250
251    /**
252     * Sends an AI request to supported Large Language Models (LLMs) and extracts metadata in form of key-value pairs.
253     * Freeform metadata extraction does not require any metadata template setup before sending the request.
254     *
255     * @param api    the API connection to be used by the created user.
256     * @param prompt The prompt provided by the client to be answered by the LLM.
257     * @param items  The items to be processed by the LLM, currently only files are supported.
258     * @return The response from the AI.
259     */
260    public static BoxAIResponse extractMetadataFreeform(BoxAPIConnection api,
261                                                        String prompt,
262                                                        List<BoxAIItem> items) {
263        return extractMetadataFreeform(api, prompt, items, null);
264    }
265
266    /**
267     * Sends an AI request to supported Large Language Models (LLMs) and extracts metadata in form of key-value pairs.
268     * Freeform metadata extraction does not require any metadata template setup before sending the request.
269     *
270     * @param api    the API connection to be used by the created user.
271     * @param prompt The prompt provided by the client to be answered by the LLM.
272     * @param items  The items to be processed by the LLM, currently only files are supported.
273     * @param agent  The AI agent configuration to be used for the request.
274     * @return The response from the AI.
275     */
276    public static BoxAIResponse extractMetadataFreeform(BoxAPIConnection api,
277                                                        String prompt,
278                                                        List<BoxAIItem> items,
279                                                        BoxAIAgentExtract agent) {
280        URL url = EXTRACT_METADATA_FREEFORM_URL.build(api.getBaseURL());
281
282        JsonObject requestJSON = new JsonObject();
283        JsonArray itemsJSON = new JsonArray();
284        for (BoxAIItem item : items) {
285            itemsJSON.add(item.getJSONObject());
286        }
287        requestJSON.add("items", itemsJSON);
288        requestJSON.add("prompt", prompt);
289        if (agent != null) {
290            requestJSON.add("ai_agent", agent.getJSONObject());
291        }
292
293        BoxJSONRequest req = new BoxJSONRequest(api, url, HttpMethod.POST);
294        req.setBody(requestJSON.toString());
295
296        try (BoxJSONResponse response = req.send()) {
297            JsonObject responseJSON = Json.parse(response.getJSON()).asObject();
298            return new BoxAIResponse(responseJSON);
299        }
300    }
301
302    /**
303     * Sends an AI request to supported Large Language Models (LLMs) and returns extracted metadata as a set of
304     * key-value pairs. For this request, you need to use an already defined metadata template or a define a
305     * schema yourself.
306     *
307     * @param api     The API connection to be used by the created user.
308     * @param items  The items to be processed by the LLM, currently only files are supported.
309     * @param template The metadata template to be used for the request.
310     * @return The response from the AI.
311     */
312    public static BoxAIExtractStructuredResponse extractMetadataStructured(BoxAPIConnection api, List<BoxAIItem> items,
313                                                       BoxAIExtractMetadataTemplate template) {
314        return extractMetadataStructured(api, items, template, null, null);
315    }
316
317    /**
318     * Sends an AI request to supported Large Language Models (LLMs) and returns extracted metadata as a set of
319     * key-value pairs. For this request, you need to use an already defined metadata template or a define a
320     * schema yourself.
321     *
322     * @param api     The API connection to be used by the created user.
323     * @param items  The items to be processed by the LLM, currently only files are supported.
324     * @param template The metadata template to be used for the request.
325     * @param agent The AI agent configuration to be used for the request.
326     * @return The response from the AI.
327     */
328    public static BoxAIExtractStructuredResponse extractMetadataStructured(BoxAPIConnection api, List<BoxAIItem> items,
329                                                       BoxAIExtractMetadataTemplate template,
330                                                       BoxAIAgentExtractStructured agent) {
331        return extractMetadataStructured(api, items, template, null, agent);
332    }
333
334    /**
335     * Sends an AI request to supported Large Language Models (LLMs) and returns extracted metadata as a set of
336     * key-value pairs. For this request, you need to use an already defined metadata template or a define a
337     * schema yourself.
338     *
339     * @param api     The API connection to be used by the created user.
340     * @param items  The items to be processed by the LLM, currently only files are supported.
341     * @param fields The fields to be extracted from the items.
342     * @return The response from the AI.
343     */
344    public static BoxAIExtractStructuredResponse extractMetadataStructured(BoxAPIConnection api, List<BoxAIItem> items,
345                                                       List<BoxAIExtractField> fields) {
346        return extractMetadataStructured(api, items, null, fields, null);
347    }
348
349    /**
350     * Sends an AI request to supported Large Language Models (LLMs) and returns extracted metadata as a set of
351     * key-value pairs. For this request, you need to use an already defined metadata template or a define a
352     * schema yourself.
353     *
354     * @param api     The API connection to be used by the created user.
355     * @param items  The items to be processed by the LLM, currently only files are supported.
356     * @param fields The fields to be extracted from the items.
357     * @param agent The AI agent configuration to be used for the request.
358     * @return The response from the AI.
359     */
360    public static BoxAIExtractStructuredResponse extractMetadataStructured(BoxAPIConnection api, List<BoxAIItem> items,
361                                                       List<BoxAIExtractField> fields,
362                                                       BoxAIAgentExtractStructured agent) {
363        return extractMetadataStructured(api, items, null, fields, agent);
364    }
365
366    /**
367     * Sends an AI request to supported Large Language Models (LLMs) and returns extracted metadata as a set of
368     * key-value pairs. For this request, you need to use an already defined metadata template or a define a
369     * schema yourself.
370     *
371     * @param api     The API connection to be used by the created user.
372     * @param items  The items to be processed by the LLM, currently only files are supported.
373     * @param template The metadata template to be used for the request.
374     * @param fields The fields to be extracted from the items.
375     * @param agent The AI agent configuration to be used for the request.
376     * @return The response from the AI.
377     */
378    private static BoxAIExtractStructuredResponse extractMetadataStructured(BoxAPIConnection api, List<BoxAIItem> items,
379                                                       BoxAIExtractMetadataTemplate template,
380                                                       List<BoxAIExtractField> fields,
381                                                       BoxAIAgentExtractStructured agent) {
382        URL url = EXTRACT_METADATA_STRUCTURED_URL.build(api.getBaseURL());
383
384        JsonObject requestJSON = new JsonObject();
385        JsonArray itemsJSON = new JsonArray();
386        for (BoxAIItem item : items) {
387            itemsJSON.add(item.getJSONObject());
388        }
389        requestJSON.add("items", itemsJSON);
390
391        if (template != null) {
392            requestJSON.add("metadata_template", template.getJSONObject());
393        }
394
395        if (fields != null) {
396            JsonArray fieldsJSON = new JsonArray();
397            for (BoxAIExtractField field : fields) {
398                fieldsJSON.add(field.getJSONObject());
399            }
400            requestJSON.add("fields", fieldsJSON);
401        }
402
403        if (agent != null) {
404            requestJSON.add("ai_agent", agent.getJSONObject());
405        }
406
407        BoxJSONRequest req = new BoxJSONRequest(api, url, HttpMethod.POST);
408        req.setBody(requestJSON.toString());
409
410        try (BoxJSONResponse response = req.send()) {
411            return new BoxAIExtractStructuredResponse(response.getJSON());
412        }
413    }
414}