You can use the API access deployment option when you are implementing a custom user interface and you want to send conversation messages using the API.
For each conversational turn at runtime,
you use the
runSession
method to send a user message and receive the agent application response.
Create API access channel
To create an API access channel:
- Click Deploy at the top of the agent builder.
- Click New channel.
- Select Set up API access.
- Provide a channel name.
- Select or create an agent application version.
- Use the optional channel-specific behavior setting if you want to override certain agent application settings for this specific channel.
- Click Create channel.
A dialog window is opened that shows the deployment ID
and a sample curl command.
On Linux and macOS systems,
you can use this sample command to send a test message
to your agent application.
Authentication
The provided curl sample command uses gcloud to authenticate the request
with your user credentials.
For a production system,
you should use service accounts for authentication.
For more information, see
Authenticate to CX Agent Studio.
Required IDs
To send a request,
you need the following static IDs:
PROJECT_ID, REGION_ID, APPLICATION_ID, and DEPLOYMENT_ID.
You can find these IDs in the Deployment ID field
provided in the dialog window's command sample for the channel.
The values are defined in named path segments:
projects/PROJECT_ID/locations/REGION_ID/apps/APPLICATION_ID/deployments/DEPLOYMENT_ID
In addition, you need a dynamic SESSION_ID,
which represents a unique conversation.
This can be a generated string of your choosing,
where the ID matches the regular expression
[a-zA-Z0-9][a-zA-Z0-9-_]{4,62}.
Call runSession
The following samples call the
runSession
method,
which is used to initiate a single turn interaction with the agent application
in a session.
REST
Before using any of the request data, make the following replacements:
- PROJECT_ID: your project ID
- REGION_ID: your region ID
- APPLICATION_ID: your application ID
- SESSION_ID: your session ID
- DEPLOYMENT_ID: your application deployment ID
HTTP method and URL:
POST https://ces.googleapis.com/v1/projects/PROJECT_ID/locations/REGION_ID/apps/APPLICATION_ID/sessions/SESSION_ID:runSession
Request JSON body:
{
"config": {
"session": "projects/PROJECT_ID/locations/REGION_ID/apps/APPLICATION_ID/sessions/SESSION_ID",
"deployment": "projects/PROJECT_ID/locations/REGION_ID/apps/APPLICATION_ID/deployments/DEPLOYMENT_ID",
},
"inputs": [
{
"text": "hi"
}
]
}
To send your request, expand one of these options:
You should receive a JSON response similar to the following:
{
"outputs": [
{
"text": "Hello there!",
"turnCompleted": true,
"turnIndex": 1,
"diagnosticInfo": {...}
}
]
}
Java
To authenticate to CX Agent Studio, set up Application Default Credentials. For more information, see Set up authentication for a local development environment.
/* * Copyright 2025 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.cloud.ces.v1.samples; import com.google.cloud.ces.v1.RunSessionRequest; import com.google.cloud.ces.v1.RunSessionResponse; import com.google.cloud.ces.v1.SessionConfig; import com.google.cloud.ces.v1.SessionInput; import com.google.cloud.ces.v1.SessionServiceClient; import java.util.ArrayList; public class SyncRunSession { public static void main(String[] args) throws Exception { syncRunSession(); } public static void syncRunSession() throws Exception { // This may require specifying regional endpoints when creating the service client as shown in // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library try (SessionServiceClient sessionServiceClient = SessionServiceClient.create()) { RunSessionRequest request = RunSessionRequest.newBuilder() .setConfig(SessionConfig.newBuilder().build()) .addAllInputs(new ArrayList<SessionInput>()) .build(); RunSessionResponse response = sessionServiceClient.runSession(request); } } }
Python
To authenticate to CX Agent Studio, set up Application Default Credentials. For more information, see Set up authentication for a local development environment.
from google.cloud import ces_v1 from google.protobuf import field_mask_pb2 def async_create_app(): with ces_v1.AgentServiceClient() as agent_service_client: list_apps_request = ces_v1.ListAppsRequest( parent="projects/project-name/locations/us" ) try: list_apps_page = agent_service_client.list_apps(request=list_apps_request) app_name = next(iter(list_apps_page)).name except StopIteration: print("No apps found.") return print(f"Using appName {app_name} to grab App") get_app_request = ces_v1.GetAppRequest(name=app_name) app = agent_service_client.get_app(request=get_app_request) print(f"Using app to either createAgent / updateApp {app}") # If there is no agent, please uncomment this part # # Create Agent # create_agent_request = ces_v1.CreateAgentRequest( # parent=app_name, # agent=ces_v1.Agent(display_name="agent-1"), # ) # agent_response = agent_service_client.create_agent(request=create_agent_request) # List Agents list_agents_request = ces_v1.ListAgentsRequest(parent=app_name) try: list_agents_page = agent_service_client.list_agents(request=list_agents_request) agent = next(iter(list_agents_page)) agent_name = agent.name except StopIteration: print(f"No agents found for app {app_name}.") return print(f"Using agent {agent_name} to update Agent") # Update App to have root agent app.root_agent = agent_name update_app_request = ces_v1.UpdateAppRequest( app=app, update_mask=field_mask_pb2.FieldMask(paths=["root_agent"]), ) update_app_response = agent_service_client.update_app(request=update_app_request) print(f"Update App response: {update_app_response}") # runSession with ces_v1.SessionServiceClient() as session_service_client: session_name = f"{app_name}/sessions/session-1" config = ces_v1.SessionConfig(session=session_name) input_ = ces_v1.SessionInput(text="Hello World!") inputs = [input_] session_request = ces_v1.RunSessionRequest(config=config, inputs=inputs) session_response = session_service_client.run_session(request=session_request) print(f"Run session response: {session_response}") if __name__ == "__main__": async_create_app()
Node.js
To authenticate to CX Agent Studio, set up Application Default Credentials. For more information, see Set up authentication for a local development environment.
import {AgentServiceClient, SessionServiceClient} from '../src/v1'; async function main() { const agentServiceClient = new AgentServiceClient(); const sessionServiceClient = new SessionServiceClient(); try { const listAppsRequest = { parent: 'projects/project-name/locations/us', }; const listAppsIterator = agentServiceClient.listAppsAsync(listAppsRequest); let firstApp; for await (const app of listAppsIterator) { firstApp = app; break; } if (!firstApp) { console.log('No apps found.'); return; } const appName = firstApp.name!; console.log(`Using appName ${appName} to grab App`); const [app] = await agentServiceClient.getApp({name: appName}); console.log('Using app to either createAgent / updateApp', app); // If there is no agent, please uncomment this part // // Create Agent // const createAgentRequest = { // parent: appName, // agent: {displayName: 'agent-1'}, // }; // const [agentResponse] = // await agentServiceClient.createAgent(createAgentRequest); // List Agents const listAgentsRequest = {parent: appName}; const listAgentsIterator = agentServiceClient.listAgentsAsync(listAgentsRequest); let agent; for await (const a of listAgentsIterator) { agent = a; break; } if (!agent) { console.log(`No agents found for app ${appName}.`); return; } const agentName = agent.name!; console.log(`Using agent ${agentName} to update Agent`); // Update App to have root agent app.rootAgent = agentName; const updateAppRequest = { app: app, updateMask: { paths: ['root_agent'], }, }; const [updateAppResponse] = await agentServiceClient.updateApp(updateAppRequest); console.log('Update App response:', updateAppResponse); // runSession const sessionName = `${appName}/sessions/session-1`; const config = {session: sessionName}; const input = {text: 'Hello World!'}; const inputs = [input]; const sessionRequest = {config: config, inputs: inputs}; const [sessionResponse] = await sessionServiceClient.runSession(sessionRequest); console.log('Run session response:', sessionResponse); } catch (err) { console.error('An error occurred:', err); } finally { await agentServiceClient.close(); await sessionServiceClient.close(); } } main();
Call BidiRunSession
As an alternative to runSession, you can use
BidiRunSession
to establish a bidirectional streaming connection with the agent application.
Go
To authenticate to CX Agent Studio, set up Application Default Credentials. For more information, see Set up authentication for a local development environment.
// A simple websocket client to interact with BidiRunSession with audio input // and output. // // Initialize your new module: // - mkdir mypackage // - cd mypackage // - go mod init mypackage // // Install non-standard dependencies: // - Install PortAudio C library using instructions at // https://files.portaudio.com/docs/v19-doxydocs/tutorial_start.html // - go get azul3d.org/engine // - go get github.com/gordonklaus/portaudio // - go get github.com/gorilla/websocket // // Usage // // go run main.go -access_token=$(gcloud auth print-access-token) \ // -location=YOUR_LOCATION \ // -app=YOUR_APP_NAME \ // -deployment=YOUR_DEPLOYMENT_NAME package main import ( "bytes" "crypto/rand" "encoding/base64" "encoding/binary" "encoding/hex" "encoding/json" "flag" "fmt" "log" "net/http" "net/url" "os" "os/signal" "strings" "sync" "azul3d.org/engine/audio" "github.com/gordonklaus/portaudio" "github.com/gorilla/websocket" ) var addr = flag.String("addr", "ces.googleapis.com", "API service address.") var location = flag.String("location", "us", "API service location.") var accessToken = flag.String("access_token", "", "Access token.") var app = flag.String("app", "", "App name in the form "+ "projects/PROJECT_ID/locations/REGION_ID/apps/APPLICATION_ID.") var deployment = flag.String("deployment", "", "Deployment name in the form "+ "projects/PROJECT_ID/locations/REGION_ID/apps/APPLICATION_ID/deployments/DEPLOYMENT_ID.") var audioSampleRate = flag.Int("audio_sample_rate", 16000, "Desired audio sample rate in Hz (e.g., 8000, 16000, 24000).") var ( audioBuffer bytes.Buffer bufferMutex sync.Mutex wg sync.WaitGroup ) type BidiSessionServerMessage struct { SessionOutput *SessionOutput `json:"sessionOutput"` RecognitionResult *RecognitionResult `json:"recognitionResult"` InterruptionSignal *InterruptionSignal `json:"interruptionSignal"` EndSession *EndSession `json:"endSession"` } type SessionOutput struct { Audio string `json:"audio"` Text string `json:"text"` } type RecognitionResult struct { Transcript string `json:"transcript"` } type InterruptionSignal struct { } type EndSession struct { Metadata struct{} `json:"metadata"` } const defaultSampleRateHertz = 16000 // 100ms const defaultChunkLength = 100 // 16000 (sample rate hertz) * 100/1000 (100ms) * 2 (16 bit) const defaultChunkSize = defaultSampleRateHertz * defaultChunkLength / 1000 * 2 func main() { flag.Parse() log.SetFlags((log.LstdFlags | log.Lshortfile | log.Lmicroseconds) &^ log.Ldate) u := url.URL{Scheme: "wss", Host: *addr, Path: "/ws/google.cloud.ces.v1.SessionService/BidiRunSession/locations/" + *location} if strings.Contains(*addr, "localhost") { u = url.URL{Scheme: "ws", Host: *addr, Path: "/ws"} } h := make(http.Header) h.Add("Content-Type", "application/json") h.Add("Authorization", "Bearer "+*accessToken) conn, _, err := websocket.DefaultDialer.Dial(u.String(), h) if err != nil { log.Fatal("dial:", err) } interrupt := make(chan os.Signal, 1) signal.Notify(interrupt, os.Interrupt) portaudio.Initialize() defer portaudio.Terminate() inDev, err := portaudio.DefaultInputDevice() if err != nil { log.Fatal(err) } log.Printf("Input device: %v", inDev.Name) outDev, err := portaudio.DefaultOutputDevice() if err != nil { log.Fatal(err) } log.Printf("Output device: %v", outDev.Name) randomBytes := make([]byte, 8) if _, err := rand.Read(randomBytes); err != nil { log.Fatal(err) } session := fmt.Sprintf("%v/sessions/%v", *app, hex.EncodeToString(randomBytes)) log.Printf("Session: %v", session) configMessage := map[string]any{ "config": map[string]any{ "session": session, "inputAudioConfig": map[string]any{ "audioEncoding": "LINEAR16", "sampleRateHertz": *audioSampleRate, }, "outputAudioConfig": map[string]any{ "audioEncoding": "LINEAR16", "sampleRateHertz": *audioSampleRate, }, "deployment": *deployment, // "debugMode": true, }, } jsonConfigMsg, err := json.Marshal(configMessage) if err != nil { log.Fatal(err) } if err = conn.WriteMessage(websocket.TextMessage, []byte(jsonConfigMsg)); err != nil { log.Fatal(err) } log.Printf("Sent config: %s", string(jsonConfigMsg)) framesPerBuffer := defaultChunkSize / 2 stream, err := portaudio.OpenDefaultStream(1, 1, float64(*audioSampleRate), framesPerBuffer, func(in, out []float32) { // Lock the buffer to avoid data race bufferMutex.Lock() defer bufferMutex.Unlock() // Output processing (Server to Speaker) bytesPerSample := 2 bytesToRead := framesPerBuffer * bytesPerSample audioChunk := make([]byte, bytesToRead) nBytes, _ := audioBuffer.Read(audioChunk) if nBytes > 0 && audioBuffer.Len() == 0 { wg.Done() } audioChunk = audioChunk[:nBytes] // LINEAR16 audio encoding var speakerPcm16 []int16 numPcm16Samples := nBytes / 2 speakerPcm16 = make([]int16, numPcm16Samples) for i := 0; i < numPcm16Samples; i++ { if (i*2 + 2) <= len(audioChunk) { speakerPcm16[i] = int16( binary.LittleEndian.Uint16(audioChunk[i*2 : (i+1)*2])) } } for i := 0; i < framesPerBuffer; i++ { if i < len(speakerPcm16) { out[i] = float32(audio.Int16ToFloat64(speakerPcm16[i])) } else { out[i] = 0 } } // Input processing (Mic to Server) micPcm16 := make([]int16, len(in)) for i, f := range in { micPcm16[i] = audio.Float64ToInt16(float64(f)) } // LINEAR16 mic audio var inputBuf []byte buf := new(bytes.Buffer) binary.Write(buf, binary.LittleEndian, micPcm16) inputBuf = buf.Bytes() audioMessage := map[string]any{ "realtimeInput": map[string]any{ "audio": base64.StdEncoding.EncodeToString(inputBuf), }, } jsonAudioMsg, err := json.Marshal(audioMessage) if err != nil { log.Fatal(err) } if err = conn.WriteMessage(websocket.TextMessage, []byte(jsonAudioMsg)); err != nil { // log.Print(err) } }) if err != nil { log.Fatalf("Error opening stream: %v", err) } defer stream.Close() if err := stream.Start(); err != nil { log.Fatalf("Error starting stream: %v", err) } serverHalfClose := make(chan struct{}) go func() { // Signal completion when server half-close is received. defer close(serverHalfClose) for { _, message, err := conn.ReadMessage() if err != nil { log.Printf("websocket read error:%v", err) break } bidiSessionServerMessage := BidiSessionServerMessage{} json.Unmarshal(message, &bidiSessionServerMessage) if bidiSessionServerMessage.SessionOutput != nil && bidiSessionServerMessage.SessionOutput.Audio != "" { } else { log.Println(string(message)) } if bidiSessionServerMessage.InterruptionSignal != nil { bufferMutex.Lock() if audioBuffer.Len() != 0 { audioBuffer.Reset() wg.Done() } bufferMutex.Unlock() } if bidiSessionServerMessage.EndSession != nil { log.Printf("Received EndSession signal, closing the stream...") interrupt <- os.Interrupt break } if bidiSessionServerMessage.SessionOutput != nil && bidiSessionServerMessage.SessionOutput.Audio != "" { audioBytes, err := base64.StdEncoding.DecodeString( bidiSessionServerMessage.SessionOutput.Audio) if err != nil { log.Fatal(err) } bufferMutex.Lock() if audioBuffer.Len() == 0 { wg.Add(1) } audioBuffer.Write(audioBytes) bufferMutex.Unlock() } } }() <-interrupt if strings.Contains(*addr, "localhost") { log.Println( "Received interrupt. Initiating graceful shutdown (half-close)...") if err = conn.WriteMessage( websocket.TextMessage, []byte("half-close")); err != nil { log.Printf("Error sending half-close: %v", err) } } else { log.Fatal("Terminating...") } <-serverHalfClose wg.Wait() log.Println("Graceful termination complete. Closing connection.") }
Java
To authenticate to CX Agent Studio, set up Application Default Credentials. For more information, see Set up authentication for a local development environment.
/* * Copyright 2025 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.example.app; import com.google.auth.oauth2.GoogleCredentials; import com.google.cloud.ces.v1.BidiSessionClientMessage; import com.google.cloud.ces.v1.BidiSessionServerMessage; import com.google.cloud.ces.v1.SessionConfig; import com.google.cloud.ces.v1.SessionInput; import com.google.protobuf.util.JsonFormat; import java.net.URI; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.java_websocket.client.WebSocketClient; import org.java_websocket.handshake.ServerHandshake; public class Hello { public static void main(String[] args) throws Exception { asyncBidiRunSession(); } public static void asyncBidiRunSession() throws Exception { // This may require specifying regional endpoints when creating the service client as shown in // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library String sessionId = "session-name"; if (sessionId.isEmpty()) { sessionId = UUID.randomUUID().toString(); } String appResourceName = "projects/project-name/locations/us/apps/app-name"; String sessionName = appResourceName + "/sessions/" + sessionId; String query = "Do you have t-shirt with a cat on it?"; String query2 = "What is the weather today?"; URI uri = new URI( "wss://ces.googleapis.com/ws/google.cloud.ces.v1.SessionService/BidiRunSession/locations/us"); GoogleCredentials credentials = GoogleCredentials.getApplicationDefault() .createScoped( Collections.singletonList("https://www.googleapis.com/auth/cloud-platform")); credentials.refreshIfExpired(); String token = credentials.getAccessToken().getTokenValue(); Map<String, String> headers = new HashMap<>(); headers.put("Authorization", "Bearer " + token); CountDownLatch latch = new CountDownLatch(1); WebSocketClient client = new WebSocketClient(uri, headers) { @Override public void onOpen(ServerHandshake handshakedata) { System.out.println("WebSocket connection opened"); try { // 1. Send config message BidiSessionClientMessage configMessage = BidiSessionClientMessage.newBuilder() .setConfig(SessionConfig.newBuilder().setSession(sessionName)) .build(); String configJson = JsonFormat.printer() .preservingProtoFieldNames() .omittingInsignificantWhitespace() .print(configMessage); System.out.println("Sending config: " + configJson); send(configJson); // 2. Send query message BidiSessionClientMessage queryMessage = BidiSessionClientMessage.newBuilder() .setRealtimeInput(SessionInput.newBuilder().setText(query)) .build(); String queryJson = JsonFormat.printer() .preservingProtoFieldNames() .omittingInsignificantWhitespace() .print(queryMessage); System.out.println("Sending query: " + queryJson); send(queryJson); // 3. Send query message 2 BidiSessionClientMessage queryMessage2 = BidiSessionClientMessage.newBuilder() .setRealtimeInput(SessionInput.newBuilder().setText(query2)) .build(); String queryJson2 = JsonFormat.printer() .preservingProtoFieldNames() .omittingInsignificantWhitespace() .print(queryMessage2); System.out.println("Sending query 2: " + queryJson2); send(queryJson2); } catch (Exception e) { e.printStackTrace(); latch.countDown(); } } @Override public void onMessage(String message) { System.out.println("==============="); System.out.println("Received message: " + message); try { BidiSessionServerMessage.Builder builder = BidiSessionServerMessage.newBuilder(); JsonFormat.parser().ignoringUnknownFields().merge(message, builder); BidiSessionServerMessage response = builder.build(); System.out.println("Parsed response: " + response); } catch (Exception e) { System.err.println("Failed to parse message: " + e.getMessage()); } } @Override public void onMessage(ByteBuffer bytes) { System.out.println("==============="); String message = StandardCharsets.UTF_8.decode(bytes).toString(); System.out.println("Received message: " + message); } @Override public void onClose(int code, String reason, boolean remote) { System.out.println( "WebSocket connection closed by " + (remote ? "remote peer" : "us") + " with code " + code + " and reason: " + reason); latch.countDown(); } @Override public void onError(Exception ex) { System.err.println("WebSocket error: " + ex.getMessage()); ex.printStackTrace(); latch.countDown(); } }; try { System.out.println("Connecting to WebSocket: " + uri); client.connect(); latch.await(10, TimeUnit.SECONDS); } finally { client.close(); } } }
Python
To authenticate to CX Agent Studio, set up Application Default Credentials. For more information, see Set up authentication for a local development environment.
import threading import time import uuid import google.auth import google.auth.transport.requests import websocket from google.protobuf import json_format from google.cloud import ces_v1 def async_bidi_run_session(): """Demonstrates how to use the BidiRunSession WebSocket endpoint.""" session_id = "session_name" if not session_id: session_id = str(uuid.uuid4()) app_resource_name = "projects/project_name/locations/us/apps/app_name" session_name = f"{app_resource_name}/sessions/{session_id}" query = "Do you have t-shirt with a cat on it?" query2 = "What is the weather today?" uri = "wss://ces.googleapis.com/ws/google.cloud.ces.v1.SessionService/BidiRunSession/locations/us" try: credentials, _ = google.auth.default( scopes=["https://www.googleapis.com/auth/cloud-platform"] ) request = google.auth.transport.requests.Request() credentials.refresh(request) token = credentials.token headers = {"Authorization": f"Bearer {token}"} except Exception as e: print(f"Failed to get credentials: {e}") return def on_open(ws): print("WebSocket connection opened") try: # 1. Send config message config_message = ces_v1.BidiSessionClientMessage( config=ces_v1.SessionConfig(session=session_name) ) config_json = json_format.MessageToJson( config_message._pb, preserving_proto_field_name=False, indent=0 ).replace("\\n", "") print(f"Sending config: {config_json}") ws.send(config_json) # 2. Send query message query_message = ces_v1.BidiSessionClientMessage( realtime_input=ces_v1.SessionInput(text=query) ) query_json = json_format.MessageToJson( query_message._pb, preserving_proto_field_name=False, indent=0 ).replace("\\n", "") print(f"Sending query: {query_json}") ws.send(query_json) # 3. Send query message 2 query_message2 = ces_v1.BidiSessionClientMessage( realtime_input=ces_v1.SessionInput(text=query2) ) query_json2 = json_format.MessageToJson( query_message2._pb, preserving_proto_field_name=False, indent=0 ).replace("\\n", "") print(f"Sending query 2: {query_json2}") ws.send(query_json2) except Exception as e: print(f"Error during on_open: {e}") ws.close() def on_message(ws, message): print("===============") print(f"Received message: {message}") try: response_pb = ces_v1.BidiSessionServerMessage()._pb json_format.Parse( message, response_pb, ignore_unknown_fields=True, ) response = ces_v1.BidiSessionServerMessage(response_pb) print(f"Parsed response: {response}") except Exception as e: print(f"Failed to parse message: {e}") def on_error(ws, error): print(f"WebSocket error: {error}") def on_close(ws, close_status_code, close_msg): print( "WebSocket connection closed" f" with code {close_status_code}" f" and reason: {close_msg}" ) print(f"Connecting to WebSocket: {uri}") ws_app = websocket.WebSocketApp( uri, header=headers, on_open=on_open, on_message=on_message, on_error=on_error, on_close=on_close, ) wst = threading.Thread(target=ws_app.run_forever) wst.daemon = True wst.start() print("Waiting for 10 seconds for messages...") time.sleep(10) print("10 seconds elapsed, closing connection.") ws_app.close() wst.join() if __name__ == "__main__": async_bidi_run_session()
Node.js
To authenticate to CX Agent Studio, set up Application Default Credentials. For more information, see Set up authentication for a local development environment.
import {GoogleAuth} from 'google-gax'; import {WebSocket} from 'ws'; async function main() { const sessionId = 'session-name'; const appResourceName = 'projects/project-name/locations/us/apps/app-name'; const sessionName = `${appResourceName}/sessions/${sessionId}`; const query = 'Do you have t-shirt with a cat on it?'; const query2 = 'What is the weather today?'; console.log(`Starting bidi session with session name: ${sessionName}`); const uri = 'wss://ces.googleapis.com/ws/google.cloud.ces.v1.SessionService/BidiRunSession/locations/us'; let token; try { const auth = new GoogleAuth({ scopes: ['https://www.googleapis.com/auth/cloud-platform'], }); token = await auth.getAccessToken(); } catch (e) { console.error(`Failed to get credentials: ${e}`); return; } const headers = {Authorization: `Bearer ${token}`}; const ws = new WebSocket(uri, {headers}); ws.on('open', () => { console.log('WebSocket connection opened'); try { // 1. Send config message const configMessage = { config: {session: sessionName}, }; const configJson = JSON.stringify(configMessage); console.log(`Sending config: ${configJson}`); ws.send(configJson); // 2. Send query message const queryMessage = { realtimeInput: {text: query}, }; const queryJson = JSON.stringify(queryMessage); console.log(`Sending query: ${queryJson}`); ws.send(queryJson); // 3. Send query message 2 const queryMessage2 = { realtimeInput: {text: query2}, }; const queryJson2 = JSON.stringify(queryMessage2); console.log(`Sending query 2: ${queryJson2}`); ws.send(queryJson2); } catch (e) { console.error(`Error during on_open: ${e}`); ws.close(); } }); ws.on('message', message => { console.log('==============='); console.log(`Received message: ${message}`); }); ws.on('error', err => { console.error(`WebSocket error: ${err}`); }); ws.on('close', (code, reason) => { console.log( `WebSocket connection closed with code ${code} and reason: ${reason}`); }); console.log('Waiting for 10 seconds for messages...'); await new Promise(resolve => setTimeout(resolve, 10000)); console.log('10 seconds elapsed, closing connection.'); ws.close(); } main();
In addition,
if you are using ces.googleapis.com as the apiEndpoint
with a gRPC client library,
you must also set x-goog-request-params in otherArgs to a specific location.
For example,
using the
Node.js client library:
const sessionClient = new SessionServiceClient({
apiEndpoint: 'ces.googleapis.com'
});
const grpc = require('@grpc/grpc-js');
const callOptions = {
otherArgs: {headers: {'x-goog-request-params': `location=locations/us`}}
};
stream = sessionClient.bidiRunSession(callOptions);
if you are using
ces.us.rep.googleapis.com or ces.eu.rep.googleapis.com,
you don't need to set x-goog-request-params.