Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 30 additions & 5 deletions doc/ai.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ an answer based on the provided prompt and items.
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->

- [Send AI request](#send-ai-request)
- [Send AI text generation request](#send-ai-text-generation-request)
- [AI](#ai)
- [Send AI request](#send-ai-request)
- [Send AI text generation request](#send-ai-text-generation-request)
- [Get AI Agent default configuration](#get-ai-agent-default-configuration)

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

Expand All @@ -26,15 +28,17 @@ for a single or multiple items.
BoxAIResponse response = BoxAI.sendAIRequest(
api,
"What is the content of the file?",
Collections.singletonList("123456", BoxAIItem.Type.FILE)),
Collections.singletonList("123456", BoxAIItem.Type.FILE),
BoxAI.Mode.SINGLE_ITEM_QA
);
```

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.

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.
It usually takes a few seconds for the file to be indexed and available for the AI endpoint.

[send-ai-request]: http://opensource.box.com/box-java-sdk/javadoc/com/box/sdk/BoxAI.html#sendAIRequest-com.box.sdk.BoxAPIConnection-java.lang.String-
[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-

Send AI text generation request
--------------
Expand Down Expand Up @@ -62,4 +66,25 @@ BoxAIResponse response = BoxAI.sendAITextGenRequest(
);
```

[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-
You can also provide an AI Agent configuration to customize the behavior of the AI response generation.

[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-

Get AI Agent default configuration
--------------------------

To get the default configuration of the AI Agent, call static
[`getAiAgentDefaultConfig(BoxAPIConnection api, BoxAIAgent.Mode mode, String language, String model)`][get-ai-agent-default-config] method.
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.

<!-- sample get_ai_agent_default -->
```java
BoxAIAgentConfig config = BoxAI.getAiAgentDefaultConfig(
api,
BoxAIAgent.Mode.ASK,
"en",
"openai__gpt_3_5_turbo"
);
```

[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-
51 changes: 51 additions & 0 deletions src/intTest/java/com/box/sdk/BoxAIIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,55 @@ public void askAITextGenItemWithDialogueHistory() throws ParseException, Interru
deleteFile(uploadedFile);
}
}

@Test
public void getAIAgentDefaultConfiguration() {
BoxAPIConnection api = jwtApiForServiceAccount();
BoxAIAgent agent = BoxAI.getAiAgentDefaultConfig(api, BoxAIAgent.Mode.ASK,
"en", "openai__gpt_3_5_turbo");
BoxAIAgentAsk askAgent = (BoxAIAgentAsk) agent;

assertThat(askAgent.getType(), is(equalTo(BoxAIAgentAsk.TYPE)));
assertThat(askAgent.getBasicText().getModel(), is(equalTo("openai__gpt_3_5_turbo")));

BoxAIAgent agent2 = BoxAI.getAiAgentDefaultConfig(api, BoxAIAgent.Mode.TEXT_GEN,
"en", "openai__gpt_3_5_turbo");
BoxAIAgentTextGen textGenAgent = (BoxAIAgentTextGen) agent2;

assertThat(textGenAgent.getType(), is(equalTo(BoxAIAgentTextGen.TYPE)));
assertThat(textGenAgent.getBasicGen().getModel(), is(equalTo("openai__gpt_3_5_turbo")));
}

@Test
public void askAISingleItemWithAgent() throws InterruptedException {
BoxAPIConnection api = jwtApiForServiceAccount();
String fileName = "[askAISingleItem] Test File.txt";
BoxFile uploadedFile = uploadFileToUniqueFolder(api, fileName, "Test file");
BoxAIAgent agent = BoxAI.getAiAgentDefaultConfig(api, BoxAIAgent.Mode.ASK,
"en", "openai__gpt_3_5_turbo_16k");
BoxAIAgentAsk askAgent = (BoxAIAgentAsk) agent;

try {
BoxFile.Info uploadedFileInfo = uploadedFile.getInfo();
// When a file has been just uploaded, AI service may not be ready to return text response
// and 412 is returned
retry(() -> {
BoxAIResponse response = BoxAI.sendAIRequest(
api,
"What is the name of the file?",
Collections.singletonList(new BoxAIItem(uploadedFileInfo.getID(), BoxAIItem.Type.FILE)),
BoxAI.Mode.SINGLE_ITEM_QA,
null,
askAgent,
true
);
assertThat(response.getAnswer(), containsString("Test file"));
assert response.getCreatedAt().before(new Date(System.currentTimeMillis()));
assertThat(response.getCompletionReason(), equalTo("done"));
}, 2, 2000);

} finally {
deleteFile(uploadedFile);
}
}
}
123 changes: 112 additions & 11 deletions src/main/java/com/box/sdk/BoxAI.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,45 @@ public final class BoxAI {
* Text gen AI url.
*/
public static final URLTemplate SEND_AI_TEXT_GEN_REQUEST_URL = new URLTemplate("ai/text_gen");
/**
* AI agent default config url.
*/
public static final URLTemplate AI_AGENT_DEFAULT_CONFIG_URL = new URLTemplate("ai_agent_default");

private BoxAI() {
}

/**
* Sends an AI request to supported LLMs and returns an answer specifically focused
* on the user's question given the provided items.
* @param api the API connection to be used by the created user.
*
* @param api the API connection to be used by the created user.
* @param prompt The prompt provided by the client to be answered by the LLM.
* @param items The items to be processed by the LLM, currently only files are supported.
* @param mode The mode specifies if this request is for a single or multiple items.
* @param items The items to be processed by the LLM, currently only files are supported.
* @param mode The mode specifies if this request is for a single or multiple items.
* @return The response from the AI.
*/
public static BoxAIResponse sendAIRequest(BoxAPIConnection api, String prompt, List<BoxAIItem> items, Mode mode) {
return sendAIRequest(api, prompt, items, mode, null, null, null);
}

/**
* Sends an AI request to supported LLMs and returns an answer specifically focused
* on the user's question given the provided items.
*
* @param api the API connection to be used by the created user.
* @param prompt The prompt provided by the client to be answered by the LLM.
* @param items The items to be processed by the LLM, currently only files are supported.
* @param mode The mode specifies if this request is for a single or multiple items.
* @param dialogueHistory The history of prompts and answers previously passed to the LLM.
* This provides additional context to the LLM in generating the response.
* @param agent The AI agent configuration to be used for the request.
* @param includeCitations Whether to include citations in the response.
* @return The response from the AI.
*/
public static BoxAIResponse sendAIRequest(BoxAPIConnection api, String prompt, List<BoxAIItem> items, Mode mode,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

method description is missing

List<BoxAIDialogueEntry> dialogueHistory, BoxAIAgentAsk agent,
Boolean includeCitations) {
URL url = SEND_AI_REQUEST_URL.build(api.getBaseURL());
JsonObject requestJSON = new JsonObject();
requestJSON.add("mode", mode.toString());
Expand All @@ -43,6 +68,20 @@ public static BoxAIResponse sendAIRequest(BoxAPIConnection api, String prompt, L
}
requestJSON.add("items", itemsJSON);

if (dialogueHistory != null) {
JsonArray dialogueHistoryJSON = new JsonArray();
for (BoxAIDialogueEntry dialogueEntry : dialogueHistory) {
dialogueHistoryJSON.add(dialogueEntry.getJSONObject());
}
requestJSON.add("dialogue_history", dialogueHistoryJSON);
}
if (agent != null) {
requestJSON.add("ai_agent", agent.getJSONObject());
}
if (includeCitations != null) {
requestJSON.add("include_citations", includeCitations);
}

BoxJSONRequest req = new BoxJSONRequest(api, url, HttpMethod.POST);
req.setBody(requestJSON.toString());

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

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

/**
* Sends an AI request to supported LLMs and returns an answer specifically focused on the creation of new text.
* @param api the API connection to be used by the created user.
* @param prompt The prompt provided by the client to be answered by the LLM.
* @param items The items to be processed by the LLM, currently only files are supported.
*
* @param api the API connection to be used by the created user.
* @param prompt The prompt provided by the client to be answered by the LLM.
* @param items The items to be processed by the LLM, currently only files are supported.
* @param dialogueHistory The history of prompts and answers previously passed to the LLM.
* This provides additional context to the LLM in generating the response.
* @return The response from the AI.
*/
public static BoxAIResponse sendAITextGenRequest(
BoxAPIConnection api, String prompt, List<BoxAIItem> items, List<BoxAIDialogueEntry> dialogueHistory
) {
public static BoxAIResponse sendAITextGenRequest(BoxAPIConnection api, String prompt, List<BoxAIItem> items,
List<BoxAIDialogueEntry> dialogueHistory) {
return sendAITextGenRequest(api, prompt, items, dialogueHistory, null);
}

/**
* Sends an AI request to supported LLMs and returns an answer specifically focused on the creation of new text.
*
* @param api the API connection to be used by the created user.
* @param prompt The prompt provided by the client to be answered by the LLM.
* @param items The items to be processed by the LLM, currently only files are supported.
* @param dialogueHistory The history of prompts and answers previously passed to the LLM.
* This provides additional context to the LLM in generating the response.
* @param agent The AI agent configuration to be used for the request.
* @return The response from the AI.
*/
public static BoxAIResponse sendAITextGenRequest(BoxAPIConnection api, String prompt, List<BoxAIItem> items,
Comment thread
congminh1254 marked this conversation as resolved.
List<BoxAIDialogueEntry> dialogueHistory,
BoxAIAgentTextGen agent) {
URL url = SEND_AI_TEXT_GEN_REQUEST_URL.build(api.getBaseURL());
JsonObject requestJSON = new JsonObject();
requestJSON.add("prompt", prompt);
Expand All @@ -93,6 +150,10 @@ public static BoxAIResponse sendAITextGenRequest(
requestJSON.add("dialogue_history", dialogueHistoryJSON);
}

if (agent != null) {
requestJSON.add("ai_agent", agent.getJSONObject());
}

BoxJSONRequest req = new BoxJSONRequest(api, url, HttpMethod.POST);
req.setBody(requestJSON.toString());

Expand All @@ -102,6 +163,46 @@ public static BoxAIResponse sendAITextGenRequest(
}
}

/**
* Get the default AI Agent use for the given mode.
*
* @param api The API connection to be used by the created user.
* @param mode The mode to filter the agent config to return.
* @return A successful response including the default agent configuration.
*/
public static BoxAIAgent getAiAgentDefaultConfig(BoxAPIConnection api, BoxAIAgent.Mode mode) {
return getAiAgentDefaultConfig(api, mode, null, null);
}

/**
* Get the default AI Agent use for the given mode.
*
* @param api The API connection to be used by the created user.
* @param mode The mode to filter the agent config to return.
* @param language The language to filter the agent config to return.
* @param model The model to filter the agent config to return.
* @return A successful response including the default agent configuration.
*/
public static BoxAIAgent getAiAgentDefaultConfig(BoxAPIConnection api,
BoxAIAgent.Mode mode,
String language,
String model) {
QueryStringBuilder builder = new QueryStringBuilder();
builder.appendParam("mode", mode.toString());
if (language != null) {
builder.appendParam("language", language);
}
if (model != null) {
builder.appendParam("model", model);
}
URL url = AI_AGENT_DEFAULT_CONFIG_URL.buildWithQuery(api.getBaseURL(), builder.toString());
BoxAPIRequest req = new BoxAPIRequest(api, url, HttpMethod.GET);
try (BoxJSONResponse response = (BoxJSONResponse) req.send()) {
JsonObject responseJSON = Json.parse(response.getJSON()).asObject();
return BoxAIAgent.parse(responseJSON);
}
}

public enum Mode {
/**
* Multiple items
Expand Down
Loading