Skip to content

Commit 862bdba

Browse files
author
Guiners
committed
adding samples, test, lints
1 parent 823397a commit 862bdba

File tree

7 files changed

+337
-13
lines changed

7 files changed

+337
-13
lines changed

genai/live/live-audio-with-txt.js

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ async function generateContent(
4545
},
4646
};
4747

48-
4948
const responseQueue = [];
5049

5150
async function waitMessage() {
@@ -55,23 +54,23 @@ async function generateContent(
5554
return responseQueue.shift();
5655
}
5756

58-
async function handleTurn(session) {
57+
async function handleTurn() {
5958
const audioChunks = [];
6059
let done = false;
6160

6261
while (!done) {
6362
const message = await waitMessage();
6463

6564
const sc = message.serverContent;
66-
if (sc?.modelTurn?.parts) {
65+
if (sc.modelTurn.parts) {
6766
for (const part of sc.modelTurn.parts) {
68-
if (part.inlineData?.data) {
67+
if (part.inlineData.data) {
6968
audioChunks.push(Buffer.from(part.inlineData.data));
7069
}
7170
}
7271
}
7372

74-
if (sc?.turnComplete) {
73+
if (sc.turnComplete) {
7574
done = true;
7675
}
7776
}
@@ -92,7 +91,7 @@ async function generateContent(
9291
console.log('> ', textInput, '\n');
9392

9493
await session.sendClientContent({
95-
turns: [{ role: 'user', parts: [{ text: textInput }] }],
94+
turns: [{role: 'user', parts: [{text: textInput}]}],
9695
});
9796

9897
const audioChunks = await handleTurn(session);
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// [START googlegenaisdk_live_audio_with_txt]
16+
17+
'use strict';
18+
19+
const {GoogleGenAI, Modality} = require('@google/genai');
20+
21+
const GOOGLE_CLOUD_PROJECT = process.env.GOOGLE_CLOUD_PROJECT;
22+
const GOOGLE_CLOUD_LOCATION = process.env.GOOGLE_CLOUD_LOCATION || 'global';
23+
24+
// (DEVELOPER) put here your memory corpus
25+
const MEMORY_CORPUS =
26+
'projects/cloud-ai-devrel-softserve/locations/us-central1/ragCorpora/2305843009213693952';
27+
28+
async function generateContent(
29+
memoryCorpus = MEMORY_CORPUS,
30+
projectId = GOOGLE_CLOUD_PROJECT,
31+
location = GOOGLE_CLOUD_LOCATION
32+
) {
33+
const ai = new GoogleGenAI({
34+
vertexai: true,
35+
project: projectId,
36+
location: location,
37+
});
38+
39+
const modelId = 'gemini-2.0-flash-live-preview-04-09';
40+
41+
// RAG store config
42+
const ragStore = {
43+
ragResources: [
44+
{
45+
ragCorpus: memoryCorpus, // Use memory corpus if you want to store context
46+
},
47+
],
48+
storeContext: true, // sink context into your memory corpus
49+
};
50+
51+
const config = {
52+
responseModalities: [Modality.TEXT],
53+
tools: [
54+
{
55+
retrieval: {
56+
vertexRagStore: ragStore,
57+
},
58+
},
59+
],
60+
};
61+
62+
const responseQueue = [];
63+
64+
async function waitMessage() {
65+
while (responseQueue.length === 0) {
66+
await new Promise(resolve => setTimeout(resolve, 100));
67+
}
68+
return responseQueue.shift();
69+
}
70+
71+
async function handleTurn() {
72+
const turns = [];
73+
let done = false;
74+
while (!done) {
75+
const message = await waitMessage();
76+
turns.push(message);
77+
if (message.serverContent && message.serverContent.turnComplete) {
78+
done = true;
79+
}
80+
}
81+
return turns;
82+
}
83+
84+
const session = await ai.live.connect({
85+
model: modelId,
86+
config: config,
87+
callbacks: {
88+
onmessage: msg => responseQueue.push(msg),
89+
onerror: e => console.error('Error:', e.message),
90+
},
91+
});
92+
93+
const textInput =
94+
"What year did Mariusz Pudzianowski win World's Strongest Man?";
95+
console.log('> ', textInput, '\n');
96+
97+
await session.sendClientContent({
98+
turns: [{role: 'user', parts: [{text: textInput}]}],
99+
});
100+
101+
const turns = await handleTurn();
102+
const response = [];
103+
104+
for (const turn of turns) {
105+
if (turn.text) {
106+
response.push(turn.text);
107+
}
108+
}
109+
110+
console.log(response.join(''));
111+
session.close();
112+
113+
return response;
114+
}
115+
// Example output:
116+
// > What year did Mariusz Pudzianowski win World's Strongest Man?
117+
// Mariusz Pudzianowski won World's Strongest Man in 2002, 2003, 2005, 2007, and 2008.
118+
// [END googlegenaisdk_live_audio_with_txt]
119+
120+
module.exports = {
121+
generateContent,
122+
};

genai/live/live-transcribe-with-audio.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,26 +47,26 @@ async function generateContent(
4747
return responseQueue.shift();
4848
}
4949

50-
async function handleTurn(session) {
50+
async function handleTurn() {
5151
const turns = [];
5252
let done = false;
53-
let outputMessage = [];
53+
const outputMessage = [];
5454
while (!done) {
5555
const message = await waitMessage();
5656
turns.push(message);
5757

5858
const sc = message.serverContent;
59-
if (sc?.modelTurn) {
59+
if (sc.modelTurn) {
6060
console.log('Model turn:', sc.modelTurn);
6161
}
62-
if (sc?.inputTranscription) {
62+
if (sc.inputTranscription) {
6363
console.log('Input transcript:', sc.inputTranscription.text);
6464
}
65-
if (sc?.outputTranscription?.text) {
65+
if (sc.outputTranscription.text) {
6666
outputMessage.push(sc.outputTranscription.text);
6767
}
6868

69-
if (sc?.turnComplete) {
69+
if (sc.turnComplete) {
7070
done = true;
7171
}
7272
}

genai/live/live-txt-with-audio.js

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// [START googlegenaisdk_live_txt_with_audio]
16+
17+
'use strict';
18+
19+
const {GoogleGenAI, Modality} = require('@google/genai');
20+
const fetch = require('node-fetch');
21+
22+
const GOOGLE_CLOUD_PROJECT = process.env.GOOGLE_CLOUD_PROJECT;
23+
const GOOGLE_CLOUD_LOCATION = process.env.GOOGLE_CLOUD_LOCATION || 'global';
24+
25+
async function generateContent(
26+
projectId = GOOGLE_CLOUD_PROJECT,
27+
location = GOOGLE_CLOUD_LOCATION
28+
) {
29+
const ai = new GoogleGenAI({
30+
vertexai: true,
31+
project: projectId,
32+
location: location,
33+
});
34+
35+
const modelId = 'gemini-2.0-flash-live-preview-04-09';
36+
const config = {
37+
responseModalities: [Modality.TEXT],
38+
};
39+
40+
const responseQueue = [];
41+
42+
async function waitMessage() {
43+
while (responseQueue.length === 0) {
44+
await new Promise(resolve => setTimeout(resolve, 100));
45+
}
46+
return responseQueue.shift();
47+
}
48+
49+
async function handleTurn() {
50+
const turns = [];
51+
let done = false;
52+
while (!done) {
53+
const message = await waitMessage();
54+
turns.push(message);
55+
if (message.serverContent && message.serverContent.turnComplete) {
56+
done = true;
57+
}
58+
}
59+
return turns;
60+
}
61+
62+
const session = await ai.live.connect({
63+
model: modelId,
64+
config: config,
65+
callbacks: {
66+
onmessage: msg => responseQueue.push(msg),
67+
onerror: e => console.error('Error:', e.message),
68+
},
69+
});
70+
71+
const audioUrl =
72+
'https://storage.googleapis.com/generativeai-downloads/data/16000.wav';
73+
74+
console.log('> Answer to this audio url', audioUrl);
75+
76+
const res = await fetch(audioUrl);
77+
if (!res.ok) throw new Error(`Failed to fetch audio: ${res.status}`);
78+
const arrayBuffer = await res.arrayBuffer();
79+
const audioBytes = Buffer.from(arrayBuffer).toString('base64');
80+
81+
await session.sendRealtimeInput({
82+
media: {
83+
data: audioBytes,
84+
mimeType: 'audio/pcm;rate=16000',
85+
},
86+
});
87+
88+
const turns = await handleTurn();
89+
90+
const response = [];
91+
for (const turn of turns) {
92+
if (turn.text) {
93+
response.push(turn.text);
94+
}
95+
}
96+
97+
console.log('Final response:', response.join(''));
98+
session.close();
99+
100+
return response;
101+
}
102+
103+
// Example output:
104+
//> Answer to this audio url https://storage.googleapis.com/generativeai-downloads/data/16000.wav
105+
// Final response: Yes, I can hear you. How are you doing today?
106+
// [END googlegenaisdk_live_txt_with_audio]
107+
108+
module.exports = {
109+
generateContent,
110+
};

genai/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
"dependencies": {
1616
"@google/genai": "1.12.0",
1717
"axios": "^1.6.2",
18-
"supertest": "^7.0.0"
18+
"supertest": "^7.0.0",
19+
"node-fetch": "^3.3.2"
1920
},
2021
"devDependencies": {
2122
"c8": "^10.0.0",
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
'use strict';
16+
17+
const {assert} = require('chai');
18+
const sinon = require('sinon');
19+
const {describe, it, beforeEach, afterEach} = require('mocha');
20+
21+
const sample = require('../live/live-ground-ragengine-with-txt');
22+
23+
describe('live-ground-ragengine-with-txt', () => {
24+
let mockClient, mockSession;
25+
26+
beforeEach(() => {
27+
mockSession = {
28+
async *receive() {
29+
yield {
30+
text: 'Mariusz Pudzianowski won in 2002, 2003, 2005, 2007, and 2008.',
31+
};
32+
},
33+
sendClientContent: sinon.stub().resolves(),
34+
close: sinon.stub().resolves(),
35+
};
36+
37+
mockClient = {
38+
aio: {
39+
live: {
40+
connect: sinon.stub().resolves(mockSession),
41+
},
42+
},
43+
};
44+
45+
sinon.stub(require('@google/genai'), 'GoogleGenAI').returns(mockClient);
46+
});
47+
48+
afterEach(() => {
49+
sinon.restore();
50+
});
51+
52+
it('should return text from mocked RAG session', async function () {
53+
this.timeout(5000);
54+
55+
const output = await sample.generateContent();
56+
57+
console.log('Generated output:', output);
58+
59+
assert.isArray(output);
60+
assert.isNotEmpty(output);
61+
});
62+
});

0 commit comments

Comments
 (0)