Skip to content

Conversation

@Hawra2020
Copy link
Contributor

No description provided.

@CLAassistant
Copy link

CLAassistant commented Apr 28, 2025

CLA assistant check
All committers have signed the CLA.

@coveralls
Copy link

coveralls commented Apr 28, 2025

Pull Request Test Coverage Report for Build 14999587544

Details

  • 117 of 135 (86.67%) changed or added relevant lines in 3 files are covered.
  • 1 unchanged line in 1 file lost coverage.
  • Overall coverage increased (+0.1%) to 85.38%

Changes Missing Coverage Covered Lines Changed/Added Lines %
src/commands/ai/extract.js 31 33 93.94%
src/box-command.js 20 27 74.07%
src/commands/ai/extract-structured.js 66 75 88.0%
Files with Coverage Reduction New Missed Lines %
src/box-command.js 1 75.72%
Totals Coverage Status
Change from base Build 14168264651: 0.1%
Covered Lines: 4507
Relevant Lines: 5091

💛 - Coveralls

'Sends an AI request to supported LLMs and extracts metadata in the form of key value pairs';
AiExtractCommand.examples = [
'box ai:extract --items=id=12345,type=file --prompt "firstName, lastName, location, yearOfBirth, company"',
'box ai:extract --items=id=12345,type=file --prompt "firstName, lastName" --ai_agent="id=14031;type=ai_agent_extract;basic_text.llm_endpoint_params.type=openai_params;basic_text.llm_endpoint_params.frequency_penalty=1.5;basic_text.llm_endpoint_params.presence_penalty=1.5;basic_text.llm_endpoint_params.stop=<|im_end|>;basic_text.llm_endpoint_params.temperature=0;basic_text.llm_endpoint_params.top_p=1;basic_text.model=azure__openai__gpt_4o_mini;basic_text.num_tokens_for_completion=8400;basic_text.prompt_template=It is {current_date}, consider these travel options {content} and answer the {user_question}.;basic_text.system_message=You are a helpful travel assistant specialized in budget travel;long_text.embeddings.model=azure__openai__text_embedding_ada_002;long_text.embeddings.strategy.id=basic;long_text.embeddings.strategy.num_tokens_per_chunk=64;long_text.llm_endpoint_params.type=openai_params;long_text.llm_endpoint_params.frequency_penalty=1.5;long_text.llm_endpoint_params.presence_penalty=1.5;long_text.llm_endpoint_params.stop=<|im_end|>;long_text.llm_endpoint_params.temperature=0;long_text.llm_endpoint_params.top_p=1;long_text.model=azure__openai__gpt_4o_mini;long_text.num_tokens_for_completion=8400;long_text.prompt_template=It is {current_date}, consider these travel options {content} and answer the {user_question}.;long_text.system_message=You are a helpful travel assistant specialized in budget travel"',
Copy link
Member

Choose a reason for hiding this comment

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

I think the AI Agent should be a JSON object?

})
};

module.exports = AiExtractCommand; No newline at end of file
Copy link
Member

Choose a reason for hiding this comment

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

Alway keep newline at end of file

});
});

describe('ai:extract-structured', () => {
Copy link
Member

Choose a reason for hiding this comment

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

You also need add another test with AI Agent including in the params

const assert = require('chai').assert;
const { TEST_API_ROOT, getFixture } = require('../helpers/test-helper');
const {TEST_API_ROOT, getFixture} = require('../helpers/test-helper');
const fs = require('fs');
Copy link
Member

Choose a reason for hiding this comment

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

Remove fs

*/

function removeUndefinedValues(obj) {
Copy link
Member

Choose a reason for hiding this comment

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

We have warning in Github Action Run:
image

Comment on lines 91 to 107
FLAGS
-h, --help Show CLI help
-q, --quiet Suppress any non-error output to stderr
-s, --save Save report to default reports folder on disk
-t, --token=<value> Provide a token to perform this call
-v, --verbose Show verbose output, which can be helpful for debugging
-y, --yes Automatically respond yes to all confirmation prompts
--as-user=<value> Provide an ID for a user
--bulk-file-path=<value> File path to bulk .csv or .json objects
--csv Output formatted CSV
--fields=<value> Comma separated list of fields to show
--items=<value>... (required) The items for the AI request
--json Output formatted JSON
--no-color Turn off colors for logging
--prompt=<value> (required) The prompt for the AI request
--save-to-file-path=<value> Override default file path to save report
Copy link
Member

Choose a reason for hiding this comment

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

Why we don't have the AI Agent in this list of flag?

docs/ai.md Outdated

```
USAGE
$ box ai:extract --prompt <value> --items <value>... --ai_agent <value> [-t <value>] [--as-user <value>] [--no-color] [--json |
Copy link
Member

Choose a reason for hiding this comment

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

The flag ai_agent is inconsistent with other params, normally we use kebab case for flags, like no-color or as-user,...

Copy link
Member

@congminh1254 congminh1254 left a comment

Choose a reason for hiding this comment

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

Please keep this coding practice:

  • Always newline at end of file.
  • For comment, please have a spacing and uppercase for first character, like
// This is comment

but not

//this is comment

@congminh1254
Copy link
Member

Please check the failing CI before asking for review PR

@Hawra2020 Hawra2020 changed the title feat: Introduce new endpoints to cli sdk for /ai/extract and /ai/extract structured feat: Introduce new endpoints to CLI SDK for /ai/extract and /ai/extract structured May 9, 2025
Copy link
Member

@congminh1254 congminh1254 left a comment

Choose a reason for hiding this comment

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

I pushed some change about eslint, please pull before fixing your code.

}
}

AiExtractStructuredCommand.description = 'Extract structured metadata from a file using Box AI';
Copy link
Member

Choose a reason for hiding this comment

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

The description should reuse from Box API, like: "Sends an AI request to supported Large Language Models (LLMs) and returns extracted metadata as a set of key-value pairs."

Comment on lines 37 to 38
'box ai:extract-structured --items="id=12345,type=file" --fields "key=firstName,type=string,description=Person first name,prompt=What is the first name?,displayName=First name" --fields "key=lastName,type=string,description=Person last name,prompt=What is the last name?,displayName=Last name" --ai-agent \'{"type":"ai_agent_extract","basicText":{"llmEndpointParams":{"type":"openai_params","frequencyPenalty": 1.5,"presencePenalty": 1.5,"stop": "<|im_end|>","temperature": 0,"topP": 1},"model": "azure__openai__gpt_4o_mini","numTokensForCompletion": 8400,"promptTemplate": "It is, consider these travel options and answer the.","systemMessage": "You are a helpful travel assistant specialized in budget travel"},"longText":{"embeddings":{ "model": "azure__openai__text_embedding_ada_002","strategy":{"id": "basic","numTokensPerChunk": 64}},"llmEndpointParams":{"type":"openai_params","frequencyPenalty": 1.5,"presencePenalty": 1.5,"stop": "<|im_end|>","temperature": 0,"topP": 1},"model":"azure__openai__gpt_4o_mini","numTokensForCompletion":8400,"promptTemplate":"It is , consider these travel options and answer the.","systemMessage":"You are a helpful travel assistant specialized in budget travel"}}\'',
'box ai:extract-structured --items="id=12345,type=file" --metadata-template="type=metadata_template,scope=enterprise,template_key=test" --ai-agent \'{"type":"ai_agent_extract","basicText":{"llmEndpointParams":{"type":"openai_params","frequencyPenalty": 1.5,"presencePenalty": 1.5,"stop": "<|im_end|>","temperature": 0,"topP": 1},"model": "azure__openai__gpt_4o_mini","numTokensForCompletion": 8400,"promptTemplate": "It is, consider these travel options and answer the.","systemMessage": "You are a helpful travel assistant specialized in budget travel"},"longText":{"embeddings":{ "model": "azure__openai__text_embedding_ada_002","strategy":{"id": "basic","numTokensPerChunk": 64}},"llmEndpointParams":{"type":"openai_params","frequencyPenalty": 1.5,"presencePenalty": 1.5,"stop": "<|im_end|>","temperature": 0,"topP": 1},"model":"azure__openai__gpt_4o_mini","numTokensForCompletion":8400,"promptTemplate":"It is , consider these travel options and answer the.","systemMessage":"You are a helpful travel assistant specialized in budget travel"}}\'',
Copy link
Member

Choose a reason for hiding this comment

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

The example is too complicated and hard to read, should only one example (without the AI Agent) or the second example can with the AI Agent but with a few main fields.

},
}),
'metadata-template': Flags.string({
description: 'metadata template to use for the AI request',
Copy link
Member

Choose a reason for hiding this comment

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

Upper case for Metadata.

For most of the description, you should reuse the description we have from the Box API:
image

And you can let user know which field is able to put into this metadata template field.

fields: Flags.string({
multiple: true,
description:
'JSON string of fields to extract (e.g., [{"key":"firstName","type":"string","description":"Person first name","prompt":"What is the first name?","displayName":"First name"}])',
Copy link
Member

Choose a reason for hiding this comment

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

Not a JSON string

Comment on lines 99 to 147
const fields = {
key: '',
type: '',
description: '',
prompt: '',
displayName: '',
};

const obj = utils.parseStringToObject(input, ['key', 'type', 'description', 'prompt', 'displayName']);
for (const key in obj) {
if (key === 'key') {
fields.key = obj[key];
} else if (key === 'type') {
fields.type = obj[key];
} else if (key === 'description') {
fields.description = obj[key];
} else if (key === 'prompt') {
fields.prompt = obj[key];
} else if (key === 'displayName') {
fields.displayName = obj[key];
} else {
throw new Error(`Invalid item key ${key}`);
}
}
Copy link
Member

Choose a reason for hiding this comment

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

Missing options.

}),
'ai-agent': Flags.string({
required: false,
description: 'The AI agent to be used for extraction (e.g., key=value pairs with semicolons or file:config.json)',
Copy link
Member

Choose a reason for hiding this comment

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

Not in key=value format

...BoxCommand.flags,
prompt: Flags.string({
required: true,
description: 'The prompt for the AI request',
Copy link
Member

Choose a reason for hiding this comment

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

Reuse description from Box API, but short version (should the first sentence, for example)

}),
items: Flags.string({
required: true,
description: 'The items for the AI request',
Copy link
Member

Choose a reason for hiding this comment

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

Reuse description from Box API, but short version (should the first sentence, for example)

}),
'ai-agent': Flags.string({
required: false,
description: 'The AI agent to be used for extraction',
Copy link
Member

Choose a reason for hiding this comment

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

Should let user know it's in JSON format and can provide an simple example for this field

AiExtractStructuredCommand.description = 'Sends an AI request to supported Large Language Models (LLMs) and returns extracted metadata as a set of key-value pairs. For this request, you either need a metadata template or a list of fields you want to extract. Input is either a metadata template or a list of fields to ensure the structure.';
AiExtractStructuredCommand.examples = [
'box ai:extract-structured --items="id=12345,type=file" --fields "key=hobby,type=multiSelect,description=Person hobby,prompt=What is your hobby?,displayName=Hobby,options=Guitar;Books"',
'box ai:extract-structured --items="id=12345,type=file" --fields "key=firstName,type=string,description=Person first name,prompt=What is the first name?,displayName=First name" --fields "key=lastName,type=string,description=Person last name,prompt=What is the last name?,displayName=Last name"',
Copy link
Member

Choose a reason for hiding this comment

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

Remove this example

AiExtractStructuredCommand.examples = [
'box ai:extract-structured --items="id=12345,type=file" --fields "key=hobby,type=multiSelect,description=Person hobby,prompt=What is your hobby?,displayName=Hobby,options=Guitar;Books"',
'box ai:extract-structured --items="id=12345,type=file" --fields "key=firstName,type=string,description=Person first name,prompt=What is the first name?,displayName=First name" --fields "key=lastName,type=string,description=Person last name,prompt=What is the last name?,displayName=Last name"',
'box ai:extract-structured --items="id=12345,type=file" --metadata-template="type=metadata_template,scope=enterprise,template_key=test" --ai-agent \'{"type":"ai_agent_extract_structured","basicText":{"llmEndpointParams":{"type":"openai_params","frequencyPenalty": 1.5,"presencePenalty": 1.5,"stop": "<|im_end|>","temperature": 0,"topP": 1},"model": "azure__openai__gpt_4o_mini","numTokensForCompletion": 8400,"promptTemplate": "It is, consider these travel options and answer the.","systemMessage": "You are a helpful travel assistant specialized in budget travel"},"longText":{"embeddings":{ "model": "azure__openai__text_embedding_ada_002","strategy":{"id": "basic","numTokensPerChunk": 64}},"llmEndpointParams":{"type":"openai_params","frequencyPenalty": 1.5,"presencePenalty": 1.5,"stop": "<|im_end|>","temperature": 0,"topP": 1},"model":"azure__openai__gpt_4o_mini","numTokensForCompletion":8400,"promptTemplate":"It is , consider these travel options and answer the.","systemMessage":"You are a helpful travel assistant specialized in budget travel"}}\'',
Copy link
Member

Choose a reason for hiding this comment

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

Remove long text

multiple: true,
description: 'The fields to be extracted from the provided items.',
parse(input) {
const fields = {
Copy link
Member

Choose a reason for hiding this comment

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

Don't assign empty string by default

required: false,
description: 'The AI agent to be used for the extraction.',
parse(input) {
return JSON.parse(input);
Copy link
Member

Choose a reason for hiding this comment

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

Add try catch

try {
return JSON.parse(input);
} catch (error) {
throw ('Error parsing ai agent ', error);
Copy link
Member

Choose a reason for hiding this comment

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

AI in upper case

}),
'ai-agent': Flags.string({
required: false,
description: 'The AI agent to be used for the extraction.',
Copy link
Member

Choose a reason for hiding this comment

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

Should be JSON, and example

description: 'Person first name',
prompt: 'What is the first name?',
displayName: 'First name',
options: [],
Copy link
Member

Choose a reason for hiding this comment

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

Add testing for the options

@Hawra2020 Hawra2020 force-pushed the sdk-3983-introduce-new-endpoints-to-cli-sdk-for-/ai/extract-and-/ai/extract_structured branch from 3e3365a to 3082107 Compare May 13, 2025 10:00
@Hawra2020 Hawra2020 force-pushed the sdk-3983-introduce-new-endpoints-to-cli-sdk-for-/ai/extract-and-/ai/extract_structured branch from 3082107 to 71a29c4 Compare May 13, 2025 13:17
@congminh1254 congminh1254 force-pushed the sdk-3983-introduce-new-endpoints-to-cli-sdk-for-/ai/extract-and-/ai/extract_structured branch from 16935da to 71a29c4 Compare May 13, 2025 14:01
congminh1254
congminh1254 previously approved these changes May 13, 2025
@congminh1254 congminh1254 force-pushed the sdk-3983-introduce-new-endpoints-to-cli-sdk-for-/ai/extract-and-/ai/extract_structured branch from 75c906f to 5cf2710 Compare May 13, 2025 14:42
@congminh1254 congminh1254 reopened this May 13, 2025
@congminh1254 congminh1254 changed the title feat: Introduce new endpoints to CLI SDK for /ai/extract and /ai/extract structured feat: Support AI Extract endpoints May 13, 2025
@congminh1254 congminh1254 merged commit 0b4ff6b into main May 13, 2025
27 checks passed
@congminh1254 congminh1254 deleted the sdk-3983-introduce-new-endpoints-to-cli-sdk-for-/ai/extract-and-/ai/extract_structured branch May 13, 2025 15:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants