From adbb42b6f21b2cc3821643acfce155421a963aff Mon Sep 17 00:00:00 2001 From: "pattishin@google.com" Date: Tue, 12 Dec 2023 19:54:23 -0800 Subject: [PATCH] refactor: updating codeowners add chat functions use correct testing project refactor: adding system tests + updating corresponding chat samples add countTokens sample refactor: adding in region tags, abstracting out mimetype, adding new image ur refactor: updating gs url in test, fix to args getting passed to sample functions refactor: resolving file paths in tests, adding wait helper function add warning about safety concerns refactor:filling out nonstreamingchat and streamcontent tests add countTokens test refactor: filling out more streaming tests add safety settings test refactor: adding in stream content and multipart content tests feat: add new sendMultiModalPromptWithImage sample refactor: adding region tags update to common prompt fix: resolve linting --- generative-ai/snippets/countTokens.js | 62 ++++++------- generative-ai/snippets/nonStreamingChat.js | 10 +-- generative-ai/snippets/nonStreamingContent.js | 6 +- .../snippets/nonStreamingMultipartContent.js | 8 +- generative-ai/snippets/safetySettings.js | 90 ++++++++++--------- .../snippets/sendMultiModalPromptWithImage.js | 13 ++- .../snippets/sendMultiModalPromptWithVideo.js | 7 +- generative-ai/snippets/streamChat.js | 4 +- generative-ai/snippets/streamContent.js | 4 +- .../snippets/streamMultipartContent.js | 6 +- .../snippets/test/countTokens.test.js | 2 +- .../snippets/test/nonStreamingChat.test.js | 11 +-- .../snippets/test/nonStreamingContent.test.js | 10 +-- .../test/nonStreamingMultipartContent.test.js | 10 +-- .../snippets/test/safetySettings.test.js | 4 +- .../snippets/test/streamChat.test.js | 8 +- .../snippets/test/streamContent.test.js | 11 +-- .../test/streamMultipartContent.test.js | 4 +- 18 files changed, 122 insertions(+), 148 deletions(-) diff --git a/generative-ai/snippets/countTokens.js b/generative-ai/snippets/countTokens.js index a9f75dabbb..68847dc62d 100644 --- a/generative-ai/snippets/countTokens.js +++ b/generative-ai/snippets/countTokens.js @@ -12,41 +12,41 @@ // See the License for the specific language governing permissions and // limitations under the License. -const { VertexAI } = require('@google-cloud/vertexai'); +const {VertexAI} = require('@google-cloud/vertexai'); async function countTokens( - projectId = 'PROJECT_ID', - location = 'LOCATION_ID', - model = 'MODEL' + projectId = 'PROJECT_ID', + location = 'LOCATION_ID', + model = 'MODEL' ) { - // [START aiplatform_gemini_token_count] - - /** - * TODO(developer): Uncomment these variables before running the sample. - */ - // const projectId = 'your-project-id'; - // const location = 'us-central1'; - // const model = 'gemini-pro'; - - // Initialize Vertex with your Cloud project and location - const vertex_ai = new VertexAI({ project: projectId, location: location }); - - // Instantiate the model - const generativeModel = vertex_ai.preview.getGenerativeModel({ - model: model, - }); - - const req = { - contents: [{ role: 'user', parts: [{ text: 'How are you doing today?' }] }], - }; - - const countTokensResp = await generativeModel.countTokens(req); - console.log('count tokens response: ', countTokensResp); - - // [END aiplatform_gemini_token_count] + // [START aiplatform_gemini_token_count] + + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const projectId = 'your-project-id'; + // const location = 'us-central1'; + // const model = 'gemini-pro'; + + // Initialize Vertex with your Cloud project and location + const vertex_ai = new VertexAI({project: projectId, location: location}); + + // Instantiate the model + const generativeModel = vertex_ai.preview.getGenerativeModel({ + model: model, + }); + + const req = { + contents: [{role: 'user', parts: [{text: 'How are you doing today?'}]}], + }; + + const countTokensResp = await generativeModel.countTokens(req); + console.log('count tokens response: ', countTokensResp); + + // [END aiplatform_gemini_token_count] } countTokens(...process.argv.slice(2)).catch(err => { - console.error(err.message); - process.exitCode = 1; + console.error(err.message); + process.exitCode = 1; }); diff --git a/generative-ai/snippets/nonStreamingChat.js b/generative-ai/snippets/nonStreamingChat.js index f64503f5e8..199c387498 100644 --- a/generative-ai/snippets/nonStreamingChat.js +++ b/generative-ai/snippets/nonStreamingChat.js @@ -16,7 +16,7 @@ const {VertexAI} = require('@google-cloud/vertexai'); function wait(time) { return new Promise(resolve => { - setTimeout(resolve, time); + setTimeout(resolve, time); }); } @@ -27,8 +27,8 @@ async function createNonStreamingChat( ) { // TODO: Find better method. Setting delay to give api time to respond, otherwise it will 404 // await wait(10); - - // [START aiplatform_gemini_multiturn_chat] + + // [START aiplatform_gemini_multiturn_chat] /** * TODO(developer): Uncomment these variables before running the sample. */ @@ -63,11 +63,11 @@ async function createNonStreamingChat( const result3 = await chat.sendMessage(chatInput3); const response3 = result3.response.candidates[0].content.parts[0].text; console.log('Chat bot: ', response3); - + // [END aiplatform_gemini_multiturn_chat] } createNonStreamingChat(...process.argv.slice(2)).catch(err => { console.error(err.message); process.exitCode = 1; -}); \ No newline at end of file +}); diff --git a/generative-ai/snippets/nonStreamingContent.js b/generative-ai/snippets/nonStreamingContent.js index c1b5ff9ea8..21936c9d01 100644 --- a/generative-ai/snippets/nonStreamingContent.js +++ b/generative-ai/snippets/nonStreamingContent.js @@ -19,8 +19,8 @@ async function createNonStreamingContent( location = 'LOCATION_ID', model = 'MODEL' ) { - // [START aiplatform_gemini_function_calling] - + // [START aiplatform_gemini_function_calling] + /** * TODO(developer): Uncomment these variables before running the sample. */ @@ -54,7 +54,7 @@ async function createNonStreamingContent( aggregatedResponse.candidates[0].content.parts[0].text; console.log(fullTextResponse); - + // [END aiplatform_gemini_function_calling] } diff --git a/generative-ai/snippets/nonStreamingMultipartContent.js b/generative-ai/snippets/nonStreamingMultipartContent.js index 6e26c6ed7d..e899886d3e 100644 --- a/generative-ai/snippets/nonStreamingMultipartContent.js +++ b/generative-ai/snippets/nonStreamingMultipartContent.js @@ -21,7 +21,7 @@ async function createNonStreamingMultipartContent( image = 'gs://generativeai-downloads/images/scones.jpg', mimeType = 'image/jpeg' ) { - // [START aiplatform_gemini_get_started] + // [START aiplatform_gemini_get_started] /** * TODO(developer): Uncomment these variables before running the sample. @@ -48,7 +48,7 @@ async function createNonStreamingMultipartContent( }; const textPart = { - text: 'Use several paragraphs to describe what is happening in this picture.', + text: 'what is shown in this image?', }; const request = { @@ -71,8 +71,8 @@ async function createNonStreamingMultipartContent( aggregatedResponse.candidates[0].content.parts[0].text; console.log(fullTextResponse); - - // [END aiplatform_gemini_get_started] + + // [END aiplatform_gemini_get_started] } createNonStreamingMultipartContent(...process.argv.slice(2)).catch(err => { diff --git a/generative-ai/snippets/safetySettings.js b/generative-ai/snippets/safetySettings.js index 92c6dad687..deb3c7f046 100644 --- a/generative-ai/snippets/safetySettings.js +++ b/generative-ai/snippets/safetySettings.js @@ -12,59 +12,61 @@ // See the License for the specific language governing permissions and // limitations under the License. -const { VertexAI, HarmCategory, HarmBlockThreshold } = require('@google-cloud/vertexai'); +const { + VertexAI, + HarmCategory, + HarmBlockThreshold, +} = require('@google-cloud/vertexai'); -async function createStreamContent( -) { - // [START aiplatform_gemini_safety_settings] - /** - * TODO(developer): Uncomment these variables before running the sample. - */ - const projectId = 'cloud-llm-preview1'; - const location = 'us-central1'; - const model = 'gemini-pro' +async function createStreamContent() { + // [START aiplatform_gemini_safety_settings] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + const projectId = 'cloud-llm-preview1'; + const location = 'us-central1'; + const model = 'gemini-pro'; - // Initialize Vertex with your Cloud project and location - const vertexAI = new VertexAI({ project: projectId, location: location }); + // Initialize Vertex with your Cloud project and location + const vertexAI = new VertexAI({project: projectId, location: location}); - // Instantiate the model - const generativeModel = vertexAI.preview.getGenerativeModel({ - model: model, - // The following parameters are optional - // They can also be passed to individual content generation requests - safety_settings: [ - { - category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, - threshold: HarmBlockThreshold.BLOCK_LOW_AND_ABOVE, - }, - ], - generation_config: {max_output_tokens: 256}, - }); + // Instantiate the model + const generativeModel = vertexAI.preview.getGenerativeModel({ + model: model, + // The following parameters are optional + // They can also be passed to individual content generation requests + safety_settings: [ + { + category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, + threshold: HarmBlockThreshold.BLOCK_LOW_AND_ABOVE, + }, + ], + generation_config: {max_output_tokens: 256}, + }); - const request = { - contents: [{ role: 'user', parts: [{ text: 'Tell me something dangerous.' }] }], - }; + const request = { + contents: [{role: 'user', parts: [{text: 'Tell me something dangerous.'}]}], + }; - console.log('Prompt:'); - console.log(request.contents[0].parts[0].text); - console.log('Streaming Response Text:'); + console.log('Prompt:'); + console.log(request.contents[0].parts[0].text); + console.log('Streaming Response Text:'); - // Create the response stream - const responseStream = await generativeModel.generateContentStream(request); + // Create the response stream + const responseStream = await generativeModel.generateContentStream(request); - // Log the text response as it streams - for await (const item of responseStream.stream) { - if (item.candidates[0].finishReason === 'SAFETY') { - console.log('This response stream terminated due to safety concerns.') - } else { - process.stdout.write(item.candidates[0].content.parts[0].text); - } + // Log the text response as it streams + for await (const item of responseStream.stream) { + if (item.candidates[0].finishReason === 'SAFETY') { + console.log('This response stream terminated due to safety concerns.'); + } else { + process.stdout.write(item.candidates[0].content.parts[0].text); } - // [END aiplatform_gemini_safety_settings] + } + // [END aiplatform_gemini_safety_settings] } - createStreamContent(...process.argv.slice(3)).catch(err => { - console.error(err.message); - process.exitCode = 1; + console.error(err.message); + process.exitCode = 1; }); diff --git a/generative-ai/snippets/sendMultiModalPromptWithImage.js b/generative-ai/snippets/sendMultiModalPromptWithImage.js index f777c68ec0..f9d1d486a4 100644 --- a/generative-ai/snippets/sendMultiModalPromptWithImage.js +++ b/generative-ai/snippets/sendMultiModalPromptWithImage.js @@ -19,14 +19,11 @@ async function sendMultiModalPromptWithImage( location = 'LOCATION_ID', model = 'MODEL' ) { - // [START aiplatform_gemini_single_turn_multi_image] - - - - // [END aiplatform_gemini_single_turn_multi_image] + // [START aiplatform_gemini_single_turn_multi_image] + // [END aiplatform_gemini_single_turn_multi_image] } sendMultiModalPromptWithImage(...process.argv.slice(2)).catch(err => { - console.error(err.message); - process.exitCode = 1; -}); \ No newline at end of file + console.error(err.message); + process.exitCode = 1; +}); diff --git a/generative-ai/snippets/sendMultiModalPromptWithVideo.js b/generative-ai/snippets/sendMultiModalPromptWithVideo.js index 391a1fea9c..a7564c7855 100644 --- a/generative-ai/snippets/sendMultiModalPromptWithVideo.js +++ b/generative-ai/snippets/sendMultiModalPromptWithVideo.js @@ -19,14 +19,11 @@ async function sendMultiModalPromptWithImage( location = 'LOCATION_ID', model = 'MODEL' ) { - // [START aiplatform_gemini_single_turn_video] - - - + // [START aiplatform_gemini_single_turn_video] // [END aiplatform_gemini_single_turn_video] } sendMultiModalPromptWithImage(...process.argv.slice(2)).catch(err => { console.error(err.message); process.exitCode = 1; -}); \ No newline at end of file +}); diff --git a/generative-ai/snippets/streamChat.js b/generative-ai/snippets/streamChat.js index 1d02257996..b28536129c 100644 --- a/generative-ai/snippets/streamChat.js +++ b/generative-ai/snippets/streamChat.js @@ -19,7 +19,7 @@ async function createStreamChat( location = 'LOCATION_ID', model = 'MODEL' ) { - // [START aiplatform_gemini_multiturn_chat] + // [START aiplatform_gemini_multiturn_chat] /** * TODO(developer): Uncomment these variables before running the sample. @@ -43,7 +43,7 @@ async function createStreamChat( for await (const item of result1.stream) { console.log(item.candidates[0].content.parts[0].text); } - + // [END aiplatform_gemini_multiturn_chat] } diff --git a/generative-ai/snippets/streamContent.js b/generative-ai/snippets/streamContent.js index 17c71ff87b..6f2ed3e2bc 100644 --- a/generative-ai/snippets/streamContent.js +++ b/generative-ai/snippets/streamContent.js @@ -20,7 +20,7 @@ async function createStreamContent( model = 'MODEL' ) { // [START aiplatform_gemini_function_calling] - + /** * TODO(developer): Uncomment these variables before running the sample. */ @@ -50,7 +50,7 @@ async function createStreamContent( for await (const item of responseStream.stream) { process.stdout.write(item.candidates[0].content.parts[0].text); } - + // [END aiplatform_gemini_function_calling] } diff --git a/generative-ai/snippets/streamMultipartContent.js b/generative-ai/snippets/streamMultipartContent.js index f75f414dc5..aa816baf92 100644 --- a/generative-ai/snippets/streamMultipartContent.js +++ b/generative-ai/snippets/streamMultipartContent.js @@ -22,7 +22,7 @@ async function createStreamMultipartContent( mimeType = 'image/jpeg' ) { // [START aiplatform_gemini_get_started] - + /** * TODO(developer): Uncomment these variables before running the sample. */ @@ -48,7 +48,7 @@ async function createStreamMultipartContent( }; const textPart = { - text: 'Use several paragraphs to describe what is happening in this picture.', + text: 'what is shown in this image?', }; const request = { @@ -67,7 +67,7 @@ async function createStreamMultipartContent( for await (const item of responseStream.stream) { process.stdout.write(item.candidates[0].content.parts[0].text); } - + // [END aiplatform_gemini_get_started] } diff --git a/generative-ai/snippets/test/countTokens.test.js b/generative-ai/snippets/test/countTokens.test.js index df15c3cd3c..d168b26fe5 100644 --- a/generative-ai/snippets/test/countTokens.test.js +++ b/generative-ai/snippets/test/countTokens.test.js @@ -30,7 +30,7 @@ describe('Count tokens', async () => { `node ./countTokens.js ${project} ${location} ${model}` ); - // Expect 6 tokens + // Expect 6 tokens assert(output.match('totalTokens: 6')); }); }); diff --git a/generative-ai/snippets/test/nonStreamingChat.test.js b/generative-ai/snippets/test/nonStreamingChat.test.js index b127e33286..24ffe5a58b 100644 --- a/generative-ai/snippets/test/nonStreamingChat.test.js +++ b/generative-ai/snippets/test/nonStreamingChat.test.js @@ -29,17 +29,10 @@ describe('Generative AI NonStreaming Chat', async () => { const output = execSync( `node ./nonStreamingChat.js ${project} ${location} ${model}` ); - // Split up conversation output - const conversation = output.split('\n'); // Ensure that the beginning of the conversation is consistent - assert(conversation[0].match(/User: Hello/)); - assert(conversation[1].match(/Chat bot: Hello! How may I assist you?/)); - assert(conversation[2].match(/User: Can you tell me a scientific fun fact?/)); - assert(conversation[3].match(/Chat bot: Sure, here's a scientific fun fact for you:?/)); - assert(conversation[4] === ''); - - // Assert that user prompts are getting through + assert(output.match(/User: Hello/)); + assert(output.match(/User: Can you tell me a scientific fun fact?/)); assert(output.match(/User: How can I learn more about that?/)); }); }); diff --git a/generative-ai/snippets/test/nonStreamingContent.test.js b/generative-ai/snippets/test/nonStreamingContent.test.js index 2c7ead4e3a..51134de8b2 100644 --- a/generative-ai/snippets/test/nonStreamingContent.test.js +++ b/generative-ai/snippets/test/nonStreamingContent.test.js @@ -29,14 +29,10 @@ describe('Generative AI NonStreaming Content', () => { const output = execSync( `node ./nonStreamingContent.js ${project} ${location} ${model}` ); - // Split up conversation output - const conversation = output.split('\n'); // Ensure that the beginning of the conversation is consistent - assert(conversation[0].match(/Prompt:/)); - assert(conversation[1].match(/What is Node.js?/)); - assert(conversation[2].match(/Non-Streaming Response Text:/)); - assert(conversation[3].match(/Node.js is a JavaScript runtime built on Chrome's V8 JavaScript engine. It is designed to build scalable network applications. Node.js runs JavaScript code outside of a browser./)); - assert(conversation[5].match(/Here are some key features of Node.js:/)); + assert(output.match(/Prompt:/)); + assert(output.match(/What is Node.js/)); + assert(output.match(/Non-Streaming Response Text:/)); }); }); diff --git a/generative-ai/snippets/test/nonStreamingMultipartContent.test.js b/generative-ai/snippets/test/nonStreamingMultipartContent.test.js index af83187c70..5e0888bb86 100644 --- a/generative-ai/snippets/test/nonStreamingMultipartContent.test.js +++ b/generative-ai/snippets/test/nonStreamingMultipartContent.test.js @@ -30,13 +30,11 @@ describe('Generative AI NonStreaming Multipart Content', () => { const output = execSync( `node ./nonStreamingMultipartContent.js ${project} ${location} ${model} ${image}` ); - // Split up conversation output - const conversation = output.split('\n'); // Ensure that the conversation is what we expect for this scone image - assert(conversation[0].match(/Prompt Text:/)); - assert(conversation[1].match(/Use several paragraphs to describe what is happening in this picture./)); - assert(conversation[2].match(/Non-Streaming Response Text:/)); - assert(conversation[3].match(/There are several blueberry scones on a table. They are arranged on a white surface that is covered in blue stains. There is a bowl of blueberries next to the scones. There is a cup of coffee on the table. There are pink flowers on the table. The scones are round and have a crumbly texture. They are topped with blueberries and sugar. The coffee is hot and steaming. The flowers are in bloom and have a sweet fragrance. The table is made of wood and has a rustic appearance. The overall effect of the image is one of beauty and tranquility./)); + assert(output.match(/Prompt Text:/)); + assert(output.match(/what is shown in this image/)); + assert(output.match(/Non-Streaming Response Text:/)); + assert(output.match(/scone/)); }); }); diff --git a/generative-ai/snippets/test/safetySettings.test.js b/generative-ai/snippets/test/safetySettings.test.js index 6cf7293395..265def1791 100644 --- a/generative-ai/snippets/test/safetySettings.test.js +++ b/generative-ai/snippets/test/safetySettings.test.js @@ -31,6 +31,8 @@ describe('Safety settings', async () => { ); // Expect rejection due to safety concerns - assert(output.match('This response stream terminated due to safety concerns')); + assert( + output.match('This response stream terminated due to safety concerns') + ); }); }); diff --git a/generative-ai/snippets/test/streamChat.test.js b/generative-ai/snippets/test/streamChat.test.js index 26e5c27a9b..823fe6ccce 100644 --- a/generative-ai/snippets/test/streamChat.test.js +++ b/generative-ai/snippets/test/streamChat.test.js @@ -31,12 +31,6 @@ describe('Generative AI Stream Chat', () => { ); // Assert that the advice given for learning is what we expect - assert(output.match(/User: How can I learn more about that?/)); - assert(output.match(/**Read books, articles, and reports:**/)); - assert(output.match(/**Attend seminars, workshops, and conferences:**/)); - assert(output.match(/**Take online courses or tutorials:**/)); - assert(output.match(/**Watch documentaries and videos:**/)); - assert(output.match(/**Interview experts:**/)); - assert(output.match(/**Do your own research:**/)); + assert(output.match(/User: How can I learn more about that/)); }); }); diff --git a/generative-ai/snippets/test/streamContent.test.js b/generative-ai/snippets/test/streamContent.test.js index c9c78e50d8..769b68372e 100644 --- a/generative-ai/snippets/test/streamContent.test.js +++ b/generative-ai/snippets/test/streamContent.test.js @@ -29,14 +29,9 @@ describe('Generative AI Stream Content', () => { const output = execSync( `node ./streamContent.js ${project} ${location} ${model}` ); - // Split up conversation output - const conversation = output.split('\n'); -; // Ensure that the beginning of the conversation is consistent - assert(conversation[0].match(/Prompt:/)); - assert(conversation[1].match(/What is Node.js?/)); - assert(conversation[2].match(/Streaming Response Text:/)); - assert(conversation[3].match(/Node.js is a JavaScript runtime built on Chrome's V8 JavaScript engine. It is designed to build scalable network applications. Node.js runs JavaScript code outside of a browser./)); - assert(conversation[5].match(/Here are some key features of Node.js:/)); + assert(output.match(/Prompt:/)); + assert(output.match(/What is Node.js/)); + assert(output.match(/Streaming Response Text:/)); }); }); diff --git a/generative-ai/snippets/test/streamMultipartContent.test.js b/generative-ai/snippets/test/streamMultipartContent.test.js index 939ae2fe8d..d9b4fb061a 100644 --- a/generative-ai/snippets/test/streamMultipartContent.test.js +++ b/generative-ai/snippets/test/streamMultipartContent.test.js @@ -31,11 +31,11 @@ describe('Generative AI Stream Multipart Content', () => { `node ./streamMultipartContent.js ${project} ${location} ${model} ${image}` ); // Split up conversation output - const conversation = output.split('\n'); + const conversation = output.split('\n'); // Ensure that the conversation is what we expect for this scone image assert(conversation[0].match(/Prompt Text:/)); - assert(conversation[1].match(/Use several paragraphs to describe what is happening in this picture./)); + assert(conversation[1].match(/what is shown in this image/)); assert(conversation[2].match(/Streaming Response Text:/)); assert(conversation[3].match(/scones/)); });