@@ -47,6 +47,7 @@ import { VertexAI } from '../public-types';
4747import { ApiSettings } from '../types/internal' ;
4848import { VertexAIService } from '../service' ;
4949import { _isFirebaseServerApp } from '@firebase/app' ;
50+ import { LiveClientContent , LiveClientSetup , LiveGenerationConfig , LiveServerContent } from '../types/live' ;
5051
5152/**
5253 * Class for generative model APIs.
@@ -190,6 +191,55 @@ export class GenerativeModel {
190191 ) ;
191192 }
192193
194+ async startLiveSession ( config ?: LiveGenerationConfig ) : Promise < LiveSession > {
195+ const _bidiGoogleAI = true ;
196+ const _baseDailyUrl = 'daily-firebaseml.sandbox.googleapis.com' ;
197+ const _apiUrl =
198+ 'ws/google.firebase.machinelearning.v2beta.LlmBidiService/BidiGenerateContent?key=' ;
199+ const _baseGAIUrl = 'generativelanguage.googleapis.com' ;
200+ const _apiGAIUrl = 'ws/google.ai.generativelanguage.v1alpha.GenerativeService.BidiGenerateContent?key=' ;
201+ const model = 'gemini-2.0-flash-exp'
202+
203+ let url ;
204+ let modelString = '' ;
205+ if ( _bidiGoogleAI ) {
206+ const gaiApiKey = '' ;
207+ url = `wss://${ _baseGAIUrl } /${ _apiGAIUrl } ${ gaiApiKey } ` ;
208+ modelString = `models/${ model } ` ;
209+ } else {
210+ url = `wss://${ _baseDailyUrl } /${ _apiUrl } ${ this . _apiSettings . apiKey } ` ;
211+ modelString =
212+ `projects/${ this . _apiSettings . project } /locations/${ this . _apiSettings . location } /publishers/google/models/${ model } ` ;
213+ }
214+
215+ const socket = new WebSocket ( url )
216+
217+ socket . onopen = ( ) => {
218+ const liveClientSetup : LiveClientSetup = {
219+ setup : {
220+ model : modelString ,
221+ generation_config : config
222+ }
223+ }
224+ socket . send ( JSON . stringify ( liveClientSetup ) ) ;
225+ }
226+
227+ const setupComplete = new Promise ( ( resolve , reject ) => {
228+ socket . onmessage = async ( event ) => {
229+ console . log ( 'received message in `startLiveSession`' )
230+ const msg = JSON . parse ( await ( event . data as Blob ) . text ( ) ) ;
231+ if ( msg . setupComplete ) {
232+ resolve ( 'setup complete.' ) ;
233+ } else {
234+ reject ( 'first message did not contain `setup_complete`' ) ;
235+ }
236+ } ;
237+ } ) ;
238+
239+ await setupComplete ;
240+ return new LiveSession ( socket ) ;
241+ }
242+
193243 /**
194244 * Counts the tokens in the provided request.
195245 */
@@ -200,3 +250,44 @@ export class GenerativeModel {
200250 return countTokens ( this . _apiSettings , this . model , formattedParams ) ;
201251 }
202252}
253+
254+ export class LiveSession {
255+ constructor ( private socket : WebSocket ) {
256+ console . log ( 'started new LiveSession' ) ;
257+ this . socket . onclose = ( event ) => {
258+ console . log ( 'websocket closed' , event ) ;
259+ }
260+
261+ this . socket . onerror = ( event ) => {
262+ console . log ( 'websocket error:' , event )
263+ }
264+ }
265+
266+ send ( data : string , turnComplete : boolean ) {
267+ if ( ! this . socket . OPEN ) {
268+ throw new Error ( "Cannot send message. Live connection was closed." )
269+ }
270+ const msg : LiveClientContent = {
271+ client_content : {
272+ turns : [ {
273+ role : 'user' ,
274+ parts : [ {
275+ text : data
276+ } ]
277+ } ] ,
278+ turn_complete : turnComplete
279+ } ,
280+ }
281+ this . socket . send ( JSON . stringify ( msg ) ) ;
282+ }
283+
284+ // Assumes the setup_complete message was already received
285+ onMessage ( callback : ( content : LiveServerContent ) => void ) {
286+ console . log ( "setting onMessage callback" ) ;
287+ this . socket . onmessage = async ( event ) => {
288+ console . log ( "triggering onMessage callback" ) ;
289+ const content : LiveServerContent = JSON . parse ( await ( event . data as Blob ) . text ( ) )
290+ callback ( content ) ;
291+ }
292+ }
293+ }
0 commit comments