Skip to content

Commit 3cb2c7c

Browse files
authored
feat: Support AI Agent (#1265)
1 parent 02193eb commit 3cb2c7c

20 files changed

+2022
-29
lines changed

doc/ai.md

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ an answer based on the provided prompt and items.
77
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
88
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
99

10-
- [Send AI request](#send-ai-request)
11-
- [Send AI text generation request](#send-ai-text-generation-request)
10+
- [AI](#ai)
11+
- [Send AI request](#send-ai-request)
12+
- [Send AI text generation request](#send-ai-text-generation-request)
13+
- [Get AI Agent default configuration](#get-ai-agent-default-configuration)
1214

1315
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
1416

@@ -26,15 +28,17 @@ for a single or multiple items.
2628
BoxAIResponse response = BoxAI.sendAIRequest(
2729
api,
2830
"What is the content of the file?",
29-
Collections.singletonList("123456", BoxAIItem.Type.FILE)),
31+
Collections.singletonList("123456", BoxAIItem.Type.FILE),
3032
BoxAI.Mode.SINGLE_ITEM_QA
3133
);
3234
```
3335

36+
You can also provide a list of dialogue history entries to provide additional context to the LLM in generating the response, AI Agent configuration and flag to indicate whether citations should be returned.
37+
3438
NOTE: The AI endpoint may return a 412 status code if you use for your request a file which has just been updated to the box.
3539
It usually takes a few seconds for the file to be indexed and available for the AI endpoint.
3640

37-
[send-ai-request]: http://opensource.box.com/box-java-sdk/javadoc/com/box/sdk/BoxAI.html#sendAIRequest-com.box.sdk.BoxAPIConnection-java.lang.String-
41+
[send-ai-request]: https://opensource.box.com/box-java-sdk/javadoc/com/box/sdk/BoxAI.html#sendAIRequest-com.box.sdk.BoxAPIConnection-java.lang.String-java.util.List-com.box.sdk.BoxAI.Mode-
3842

3943
Send AI text generation request
4044
--------------
@@ -62,4 +66,25 @@ BoxAIResponse response = BoxAI.sendAITextGenRequest(
6266
);
6367
```
6468

65-
[send-ai-text-gen-request]: http://opensource.box.com/box-java-sdk/javadoc/com/box/sdk/BoxAI.html#sendAITextGenRequest-com.box.sdk.BoxAPIConnection-java.lang.String-
69+
You can also provide an AI Agent configuration to customize the behavior of the AI response generation.
70+
71+
[send-ai-text-gen-request]: https://opensource.box.com/box-java-sdk/javadoc/com/box/sdk/BoxAI.html#sendAITextGenRequest-com.box.sdk.BoxAPIConnection-java.lang.String-java.util.List-java.util.List-
72+
73+
Get AI Agent default configuration
74+
--------------------------
75+
76+
To get the default configuration of the AI Agent, call static
77+
[`getAiAgentDefaultConfig(BoxAPIConnection api, BoxAIAgent.Mode mode, String language, String model)`][get-ai-agent-default-config] method.
78+
In the request you have to provide the mode of the AI Agent, the language and the model, with the model is required while the language and mode are optional.
79+
80+
<!-- sample get_ai_agent_default -->
81+
```java
82+
BoxAIAgentConfig config = BoxAI.getAiAgentDefaultConfig(
83+
api,
84+
BoxAIAgent.Mode.ASK,
85+
"en",
86+
"openai__gpt_3_5_turbo"
87+
);
88+
```
89+
90+
[get-ai-agent-default-config]: http://opensource.box.com/box-java-sdk/javadoc/com/box/sdk/BoxAI.html#getAiAgentDefaultConfig-com.box.sdk.BoxAPIConnection-com.box.sdk.ai.BoxAIAgent.Mode-java.lang.String-java.lang.String-

src/intTest/java/com/box/sdk/BoxAIIT.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,4 +141,55 @@ public void askAITextGenItemWithDialogueHistory() throws ParseException, Interru
141141
deleteFile(uploadedFile);
142142
}
143143
}
144+
145+
@Test
146+
public void getAIAgentDefaultConfiguration() {
147+
BoxAPIConnection api = jwtApiForServiceAccount();
148+
BoxAIAgent agent = BoxAI.getAiAgentDefaultConfig(api, BoxAIAgent.Mode.ASK,
149+
"en", "openai__gpt_3_5_turbo");
150+
BoxAIAgentAsk askAgent = (BoxAIAgentAsk) agent;
151+
152+
assertThat(askAgent.getType(), is(equalTo(BoxAIAgentAsk.TYPE)));
153+
assertThat(askAgent.getBasicText().getModel(), is(equalTo("openai__gpt_3_5_turbo")));
154+
155+
BoxAIAgent agent2 = BoxAI.getAiAgentDefaultConfig(api, BoxAIAgent.Mode.TEXT_GEN,
156+
"en", "openai__gpt_3_5_turbo");
157+
BoxAIAgentTextGen textGenAgent = (BoxAIAgentTextGen) agent2;
158+
159+
assertThat(textGenAgent.getType(), is(equalTo(BoxAIAgentTextGen.TYPE)));
160+
assertThat(textGenAgent.getBasicGen().getModel(), is(equalTo("openai__gpt_3_5_turbo")));
161+
}
162+
163+
@Test
164+
public void askAISingleItemWithAgent() throws InterruptedException {
165+
BoxAPIConnection api = jwtApiForServiceAccount();
166+
String fileName = "[askAISingleItem] Test File.txt";
167+
BoxFile uploadedFile = uploadFileToUniqueFolder(api, fileName, "Test file");
168+
BoxAIAgent agent = BoxAI.getAiAgentDefaultConfig(api, BoxAIAgent.Mode.ASK,
169+
"en", "openai__gpt_3_5_turbo_16k");
170+
BoxAIAgentAsk askAgent = (BoxAIAgentAsk) agent;
171+
172+
try {
173+
BoxFile.Info uploadedFileInfo = uploadedFile.getInfo();
174+
// When a file has been just uploaded, AI service may not be ready to return text response
175+
// and 412 is returned
176+
retry(() -> {
177+
BoxAIResponse response = BoxAI.sendAIRequest(
178+
api,
179+
"What is the name of the file?",
180+
Collections.singletonList(new BoxAIItem(uploadedFileInfo.getID(), BoxAIItem.Type.FILE)),
181+
BoxAI.Mode.SINGLE_ITEM_QA,
182+
null,
183+
askAgent,
184+
true
185+
);
186+
assertThat(response.getAnswer(), containsString("Test file"));
187+
assert response.getCreatedAt().before(new Date(System.currentTimeMillis()));
188+
assertThat(response.getCompletionReason(), equalTo("done"));
189+
}, 2, 2000);
190+
191+
} finally {
192+
deleteFile(uploadedFile);
193+
}
194+
}
144195
}

src/main/java/com/box/sdk/BoxAI.java

Lines changed: 112 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,45 @@ public final class BoxAI {
1818
* Text gen AI url.
1919
*/
2020
public static final URLTemplate SEND_AI_TEXT_GEN_REQUEST_URL = new URLTemplate("ai/text_gen");
21+
/**
22+
* AI agent default config url.
23+
*/
24+
public static final URLTemplate AI_AGENT_DEFAULT_CONFIG_URL = new URLTemplate("ai_agent_default");
2125

2226
private BoxAI() {
2327
}
2428

2529
/**
2630
* Sends an AI request to supported LLMs and returns an answer specifically focused
2731
* on the user's question given the provided items.
28-
* @param api the API connection to be used by the created user.
32+
*
33+
* @param api the API connection to be used by the created user.
2934
* @param prompt The prompt provided by the client to be answered by the LLM.
30-
* @param items The items to be processed by the LLM, currently only files are supported.
31-
* @param mode The mode specifies if this request is for a single or multiple items.
35+
* @param items The items to be processed by the LLM, currently only files are supported.
36+
* @param mode The mode specifies if this request is for a single or multiple items.
3237
* @return The response from the AI.
3338
*/
3439
public static BoxAIResponse sendAIRequest(BoxAPIConnection api, String prompt, List<BoxAIItem> items, Mode mode) {
40+
return sendAIRequest(api, prompt, items, mode, null, null, null);
41+
}
42+
43+
/**
44+
* Sends an AI request to supported LLMs and returns an answer specifically focused
45+
* on the user's question given the provided items.
46+
*
47+
* @param api the API connection to be used by the created user.
48+
* @param prompt The prompt provided by the client to be answered by the LLM.
49+
* @param items The items to be processed by the LLM, currently only files are supported.
50+
* @param mode The mode specifies if this request is for a single or multiple items.
51+
* @param dialogueHistory The history of prompts and answers previously passed to the LLM.
52+
* This provides additional context to the LLM in generating the response.
53+
* @param agent The AI agent configuration to be used for the request.
54+
* @param includeCitations Whether to include citations in the response.
55+
* @return The response from the AI.
56+
*/
57+
public static BoxAIResponse sendAIRequest(BoxAPIConnection api, String prompt, List<BoxAIItem> items, Mode mode,
58+
List<BoxAIDialogueEntry> dialogueHistory, BoxAIAgentAsk agent,
59+
Boolean includeCitations) {
3560
URL url = SEND_AI_REQUEST_URL.build(api.getBaseURL());
3661
JsonObject requestJSON = new JsonObject();
3762
requestJSON.add("mode", mode.toString());
@@ -43,6 +68,20 @@ public static BoxAIResponse sendAIRequest(BoxAPIConnection api, String prompt, L
4368
}
4469
requestJSON.add("items", itemsJSON);
4570

71+
if (dialogueHistory != null) {
72+
JsonArray dialogueHistoryJSON = new JsonArray();
73+
for (BoxAIDialogueEntry dialogueEntry : dialogueHistory) {
74+
dialogueHistoryJSON.add(dialogueEntry.getJSONObject());
75+
}
76+
requestJSON.add("dialogue_history", dialogueHistoryJSON);
77+
}
78+
if (agent != null) {
79+
requestJSON.add("ai_agent", agent.getJSONObject());
80+
}
81+
if (includeCitations != null) {
82+
requestJSON.add("include_citations", includeCitations);
83+
}
84+
4685
BoxJSONRequest req = new BoxJSONRequest(api, url, HttpMethod.POST);
4786
req.setBody(requestJSON.toString());
4887

@@ -54,9 +93,10 @@ public static BoxAIResponse sendAIRequest(BoxAPIConnection api, String prompt, L
5493

5594
/**
5695
* Sends an AI request to supported LLMs and returns an answer specifically focused on the creation of new text.
57-
* @param api the API connection to be used by the created user.
96+
*
97+
* @param api the API connection to be used by the created user.
5898
* @param prompt The prompt provided by the client to be answered by the LLM.
59-
* @param items The items to be processed by the LLM, currently only files are supported.
99+
* @param items The items to be processed by the LLM, currently only files are supported.
60100
* @return The response from the AI.
61101
*/
62102
public static BoxAIResponse sendAITextGenRequest(BoxAPIConnection api, String prompt, List<BoxAIItem> items) {
@@ -65,16 +105,33 @@ public static BoxAIResponse sendAITextGenRequest(BoxAPIConnection api, String pr
65105

66106
/**
67107
* Sends an AI request to supported LLMs and returns an answer specifically focused on the creation of new text.
68-
* @param api the API connection to be used by the created user.
69-
* @param prompt The prompt provided by the client to be answered by the LLM.
70-
* @param items The items to be processed by the LLM, currently only files are supported.
108+
*
109+
* @param api the API connection to be used by the created user.
110+
* @param prompt The prompt provided by the client to be answered by the LLM.
111+
* @param items The items to be processed by the LLM, currently only files are supported.
71112
* @param dialogueHistory The history of prompts and answers previously passed to the LLM.
72113
* This provides additional context to the LLM in generating the response.
73114
* @return The response from the AI.
74115
*/
75-
public static BoxAIResponse sendAITextGenRequest(
76-
BoxAPIConnection api, String prompt, List<BoxAIItem> items, List<BoxAIDialogueEntry> dialogueHistory
77-
) {
116+
public static BoxAIResponse sendAITextGenRequest(BoxAPIConnection api, String prompt, List<BoxAIItem> items,
117+
List<BoxAIDialogueEntry> dialogueHistory) {
118+
return sendAITextGenRequest(api, prompt, items, dialogueHistory, null);
119+
}
120+
121+
/**
122+
* Sends an AI request to supported LLMs and returns an answer specifically focused on the creation of new text.
123+
*
124+
* @param api the API connection to be used by the created user.
125+
* @param prompt The prompt provided by the client to be answered by the LLM.
126+
* @param items The items to be processed by the LLM, currently only files are supported.
127+
* @param dialogueHistory The history of prompts and answers previously passed to the LLM.
128+
* This provides additional context to the LLM in generating the response.
129+
* @param agent The AI agent configuration to be used for the request.
130+
* @return The response from the AI.
131+
*/
132+
public static BoxAIResponse sendAITextGenRequest(BoxAPIConnection api, String prompt, List<BoxAIItem> items,
133+
List<BoxAIDialogueEntry> dialogueHistory,
134+
BoxAIAgentTextGen agent) {
78135
URL url = SEND_AI_TEXT_GEN_REQUEST_URL.build(api.getBaseURL());
79136
JsonObject requestJSON = new JsonObject();
80137
requestJSON.add("prompt", prompt);
@@ -93,6 +150,10 @@ public static BoxAIResponse sendAITextGenRequest(
93150
requestJSON.add("dialogue_history", dialogueHistoryJSON);
94151
}
95152

153+
if (agent != null) {
154+
requestJSON.add("ai_agent", agent.getJSONObject());
155+
}
156+
96157
BoxJSONRequest req = new BoxJSONRequest(api, url, HttpMethod.POST);
97158
req.setBody(requestJSON.toString());
98159

@@ -102,6 +163,46 @@ public static BoxAIResponse sendAITextGenRequest(
102163
}
103164
}
104165

166+
/**
167+
* Get the default AI Agent use for the given mode.
168+
*
169+
* @param api The API connection to be used by the created user.
170+
* @param mode The mode to filter the agent config to return.
171+
* @return A successful response including the default agent configuration.
172+
*/
173+
public static BoxAIAgent getAiAgentDefaultConfig(BoxAPIConnection api, BoxAIAgent.Mode mode) {
174+
return getAiAgentDefaultConfig(api, mode, null, null);
175+
}
176+
177+
/**
178+
* Get the default AI Agent use for the given mode.
179+
*
180+
* @param api The API connection to be used by the created user.
181+
* @param mode The mode to filter the agent config to return.
182+
* @param language The language to filter the agent config to return.
183+
* @param model The model to filter the agent config to return.
184+
* @return A successful response including the default agent configuration.
185+
*/
186+
public static BoxAIAgent getAiAgentDefaultConfig(BoxAPIConnection api,
187+
BoxAIAgent.Mode mode,
188+
String language,
189+
String model) {
190+
QueryStringBuilder builder = new QueryStringBuilder();
191+
builder.appendParam("mode", mode.toString());
192+
if (language != null) {
193+
builder.appendParam("language", language);
194+
}
195+
if (model != null) {
196+
builder.appendParam("model", model);
197+
}
198+
URL url = AI_AGENT_DEFAULT_CONFIG_URL.buildWithQuery(api.getBaseURL(), builder.toString());
199+
BoxAPIRequest req = new BoxAPIRequest(api, url, HttpMethod.GET);
200+
try (BoxJSONResponse response = (BoxJSONResponse) req.send()) {
201+
JsonObject responseJSON = Json.parse(response.getJSON()).asObject();
202+
return BoxAIAgent.parse(responseJSON);
203+
}
204+
}
205+
105206
public enum Mode {
106207
/**
107208
* Multiple items

0 commit comments

Comments
 (0)