Skip to content

Commit 3625a71

Browse files
MarkDaoustcopybara-github
authored andcommitted
feat: voice consent signature types across all SDK languages.
PiperOrigin-RevId: 885814555
1 parent 959c01b commit 3625a71

9 files changed

Lines changed: 260 additions & 2 deletions

File tree

src/main/java/com/google/genai/AsyncLive.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import com.google.genai.types.LiveConnectConfig;
2626
import com.google.genai.types.LiveConnectParameters;
2727
import com.google.genai.types.LiveServerMessage;
28+
import com.google.genai.types.LiveServerSetupComplete;
2829
import java.io.IOException;
2930
import java.net.URI;
3031
import java.net.URISyntaxException;
@@ -283,11 +284,13 @@ private void handleIncomingMessage(String message) {
283284
try {
284285
LiveServerMessage initialResponse = LiveServerMessage.fromJson(message);
285286
if (initialResponse.setupComplete().isPresent()) {
287+
LiveServerSetupComplete setupComplete = initialResponse.setupComplete().get();
286288
sessionFuture.complete(
287289
new AsyncSession(
288290
apiClient,
289291
this,
290-
initialResponse.setupComplete().get().sessionId().orElse(null)));
292+
setupComplete.sessionId().orElse(null),
293+
setupComplete));
291294
} else {
292295
sessionFuture.completeExceptionally(
293296
new GenAiIOException(

src/main/java/com/google/genai/AsyncSession.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import com.google.genai.types.LiveClientContent;
2222
import com.google.genai.types.LiveClientMessage;
2323
import com.google.genai.types.LiveClientToolResponse;
24+
import com.google.genai.types.LiveServerSetupComplete;
2425
import com.google.genai.types.LiveSendClientContentParameters;
2526
import com.google.genai.types.LiveSendRealtimeInputParameters;
2627
import com.google.genai.types.LiveSendToolResponseParameters;
@@ -40,11 +41,17 @@ public final class AsyncSession {
4041

4142
private final AsyncLive.GenAiWebSocketClient websocket;
4243
final String sessionId;
44+
private final LiveServerSetupComplete setupComplete;
4345

44-
AsyncSession(ApiClient apiClient, AsyncLive.GenAiWebSocketClient websocket, String sessionId) {
46+
AsyncSession(
47+
ApiClient apiClient,
48+
AsyncLive.GenAiWebSocketClient websocket,
49+
String sessionId,
50+
LiveServerSetupComplete setupComplete) {
4551
this.apiClient = apiClient;
4652
this.websocket = websocket;
4753
this.sessionId = sessionId;
54+
this.setupComplete = setupComplete;
4855
}
4956

5057
/**
@@ -145,4 +152,8 @@ public CompletableFuture<Void> close() {
145152
public String sessionId() {
146153
return sessionId;
147154
}
155+
156+
public LiveServerSetupComplete setupComplete() {
157+
return setupComplete;
158+
}
148159
}

src/main/java/com/google/genai/LiveConverters.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1828,6 +1828,15 @@ ObjectNode replicatedVoiceConfigToVertex(JsonNode fromObject, ObjectNode parentO
18281828
Common.getValueByPath(fromObject, new String[] {"voiceSampleAudio"}));
18291829
}
18301830

1831+
if (!Common.isZero(Common.getValueByPath(fromObject, new String[] {"consentAudio"}))) {
1832+
throw new IllegalArgumentException("consentAudio parameter is not supported in Vertex AI.");
1833+
}
1834+
1835+
if (!Common.isZero(Common.getValueByPath(fromObject, new String[] {"voiceConsentSignature"}))) {
1836+
throw new IllegalArgumentException(
1837+
"voiceConsentSignature parameter is not supported in Vertex AI.");
1838+
}
1839+
18311840
return toObject;
18321841
}
18331842

src/main/java/com/google/genai/Models.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4539,6 +4539,15 @@ ObjectNode replicatedVoiceConfigToVertex(
45394539
Common.getValueByPath(fromObject, new String[] {"voiceSampleAudio"}));
45404540
}
45414541

4542+
if (!Common.isZero(Common.getValueByPath(fromObject, new String[] {"consentAudio"}))) {
4543+
throw new IllegalArgumentException("consentAudio parameter is not supported in Vertex AI.");
4544+
}
4545+
4546+
if (!Common.isZero(Common.getValueByPath(fromObject, new String[] {"voiceConsentSignature"}))) {
4547+
throw new IllegalArgumentException(
4548+
"voiceConsentSignature parameter is not supported in Vertex AI.");
4549+
}
4550+
45424551
return toObject;
45434552
}
45444553

src/main/java/com/google/genai/Tunings.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1257,6 +1257,15 @@ ObjectNode replicatedVoiceConfigToVertex(
12571257
Common.getValueByPath(fromObject, new String[] {"voiceSampleAudio"}));
12581258
}
12591259

1260+
if (!Common.isZero(Common.getValueByPath(fromObject, new String[] {"consentAudio"}))) {
1261+
throw new IllegalArgumentException("consentAudio parameter is not supported in Vertex AI.");
1262+
}
1263+
1264+
if (!Common.isZero(Common.getValueByPath(fromObject, new String[] {"voiceConsentSignature"}))) {
1265+
throw new IllegalArgumentException(
1266+
"voiceConsentSignature parameter is not supported in Vertex AI.");
1267+
}
1268+
12601269
return toObject;
12611270
}
12621271

src/main/java/com/google/genai/types/LiveServerSetupComplete.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,14 @@ public abstract class LiveServerSetupComplete extends JsonSerializable {
3434
@JsonProperty("sessionId")
3535
public abstract Optional<String> sessionId();
3636

37+
/**
38+
* Signature of the verified consent audio. This is populated when the request has a
39+
* ReplicatedVoiceConfig with consent_audio set, if the consent verification was successful. This
40+
* may be used in a subsequent request instead of the consent_audio to verify the same consent.
41+
*/
42+
@JsonProperty("voiceConsentSignature")
43+
public abstract Optional<VoiceConsentSignature> voiceConsentSignature();
44+
3745
/** Instantiates a builder for LiveServerSetupComplete. */
3846
@ExcludeFromGeneratedCoverageReport
3947
public static Builder builder() {
@@ -70,6 +78,41 @@ public Builder clearSessionId() {
7078
return sessionId(Optional.empty());
7179
}
7280

81+
/**
82+
* Setter for voiceConsentSignature.
83+
*
84+
* <p>voiceConsentSignature: Signature of the verified consent audio. This is populated when the
85+
* request has a ReplicatedVoiceConfig with consent_audio set, if the consent verification was
86+
* successful. This may be used in a subsequent request instead of the consent_audio to verify
87+
* the same consent.
88+
*/
89+
@JsonProperty("voiceConsentSignature")
90+
public abstract Builder voiceConsentSignature(VoiceConsentSignature voiceConsentSignature);
91+
92+
/**
93+
* Setter for voiceConsentSignature builder.
94+
*
95+
* <p>voiceConsentSignature: Signature of the verified consent audio. This is populated when the
96+
* request has a ReplicatedVoiceConfig with consent_audio set, if the consent verification was
97+
* successful. This may be used in a subsequent request instead of the consent_audio to verify
98+
* the same consent.
99+
*/
100+
@CanIgnoreReturnValue
101+
public Builder voiceConsentSignature(
102+
VoiceConsentSignature.Builder voiceConsentSignatureBuilder) {
103+
return voiceConsentSignature(voiceConsentSignatureBuilder.build());
104+
}
105+
106+
@ExcludeFromGeneratedCoverageReport
107+
abstract Builder voiceConsentSignature(Optional<VoiceConsentSignature> voiceConsentSignature);
108+
109+
/** Clears the value of voiceConsentSignature field. */
110+
@ExcludeFromGeneratedCoverageReport
111+
@CanIgnoreReturnValue
112+
public Builder clearVoiceConsentSignature() {
113+
return voiceConsentSignature(Optional.empty());
114+
}
115+
73116
public abstract LiveServerSetupComplete build();
74117
}
75118

src/main/java/com/google/genai/types/ReplicatedVoiceConfig.java

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,22 @@ public abstract class ReplicatedVoiceConfig extends JsonSerializable {
4141
@JsonProperty("voiceSampleAudio")
4242
public abstract Optional<byte[]> voiceSampleAudio();
4343

44+
/**
45+
* Recorded consent verifying ownership of the voice. This represents 16-bit signed little-endian
46+
* wav data, with a 24kHz sampling rate.
47+
*/
48+
@JsonProperty("consentAudio")
49+
public abstract Optional<byte[]> consentAudio();
50+
51+
/**
52+
* Signature of a previously verified consent audio. This should be populated with a signature
53+
* generated by the server for a previous request containing the consent_audio field. When
54+
* provided, the signature is verified instead of the consent_audio field to reduce latency.
55+
* Requests will fail if the signature is invalid or expired.
56+
*/
57+
@JsonProperty("voiceConsentSignature")
58+
public abstract Optional<VoiceConsentSignature> voiceConsentSignature();
59+
4460
/** Instantiates a builder for ReplicatedVoiceConfig. */
4561
@ExcludeFromGeneratedCoverageReport
4662
public static Builder builder() {
@@ -97,6 +113,60 @@ public Builder clearVoiceSampleAudio() {
97113
return voiceSampleAudio(Optional.empty());
98114
}
99115

116+
/**
117+
* Setter for consentAudio.
118+
*
119+
* <p>consentAudio: Recorded consent verifying ownership of the voice. This represents 16-bit
120+
* signed little-endian wav data, with a 24kHz sampling rate.
121+
*/
122+
@JsonProperty("consentAudio")
123+
public abstract Builder consentAudio(byte[] consentAudio);
124+
125+
@ExcludeFromGeneratedCoverageReport
126+
abstract Builder consentAudio(Optional<byte[]> consentAudio);
127+
128+
/** Clears the value of consentAudio field. */
129+
@ExcludeFromGeneratedCoverageReport
130+
@CanIgnoreReturnValue
131+
public Builder clearConsentAudio() {
132+
return consentAudio(Optional.empty());
133+
}
134+
135+
/**
136+
* Setter for voiceConsentSignature.
137+
*
138+
* <p>voiceConsentSignature: Signature of a previously verified consent audio. This should be
139+
* populated with a signature generated by the server for a previous request containing the
140+
* consent_audio field. When provided, the signature is verified instead of the consent_audio
141+
* field to reduce latency. Requests will fail if the signature is invalid or expired.
142+
*/
143+
@JsonProperty("voiceConsentSignature")
144+
public abstract Builder voiceConsentSignature(VoiceConsentSignature voiceConsentSignature);
145+
146+
/**
147+
* Setter for voiceConsentSignature builder.
148+
*
149+
* <p>voiceConsentSignature: Signature of a previously verified consent audio. This should be
150+
* populated with a signature generated by the server for a previous request containing the
151+
* consent_audio field. When provided, the signature is verified instead of the consent_audio
152+
* field to reduce latency. Requests will fail if the signature is invalid or expired.
153+
*/
154+
@CanIgnoreReturnValue
155+
public Builder voiceConsentSignature(
156+
VoiceConsentSignature.Builder voiceConsentSignatureBuilder) {
157+
return voiceConsentSignature(voiceConsentSignatureBuilder.build());
158+
}
159+
160+
@ExcludeFromGeneratedCoverageReport
161+
abstract Builder voiceConsentSignature(Optional<VoiceConsentSignature> voiceConsentSignature);
162+
163+
/** Clears the value of voiceConsentSignature field. */
164+
@ExcludeFromGeneratedCoverageReport
165+
@CanIgnoreReturnValue
166+
public Builder clearVoiceConsentSignature() {
167+
return voiceConsentSignature(Optional.empty());
168+
}
169+
100170
public abstract ReplicatedVoiceConfig build();
101171
}
102172

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
// Auto-generated code. Do not edit.
18+
19+
package com.google.genai.types;
20+
21+
import com.fasterxml.jackson.annotation.JsonCreator;
22+
import com.fasterxml.jackson.annotation.JsonProperty;
23+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
24+
import com.google.auto.value.AutoValue;
25+
import com.google.errorprone.annotations.CanIgnoreReturnValue;
26+
import com.google.genai.JsonSerializable;
27+
import java.util.Optional;
28+
29+
/** The signature of the voice consent check. */
30+
@AutoValue
31+
@JsonDeserialize(builder = VoiceConsentSignature.Builder.class)
32+
public abstract class VoiceConsentSignature extends JsonSerializable {
33+
/** The signature string. */
34+
@JsonProperty("signature")
35+
public abstract Optional<String> signature();
36+
37+
/** Instantiates a builder for VoiceConsentSignature. */
38+
@ExcludeFromGeneratedCoverageReport
39+
public static Builder builder() {
40+
return new AutoValue_VoiceConsentSignature.Builder();
41+
}
42+
43+
/** Creates a builder with the same values as this instance. */
44+
public abstract Builder toBuilder();
45+
46+
/** Builder for VoiceConsentSignature. */
47+
@AutoValue.Builder
48+
public abstract static class Builder {
49+
/** For internal usage. Please use `VoiceConsentSignature.builder()` for instantiation. */
50+
@JsonCreator
51+
private static Builder create() {
52+
return new AutoValue_VoiceConsentSignature.Builder();
53+
}
54+
55+
/**
56+
* Setter for signature.
57+
*
58+
* <p>signature: The signature string.
59+
*/
60+
@JsonProperty("signature")
61+
public abstract Builder signature(String signature);
62+
63+
@ExcludeFromGeneratedCoverageReport
64+
abstract Builder signature(Optional<String> signature);
65+
66+
/** Clears the value of signature field. */
67+
@ExcludeFromGeneratedCoverageReport
68+
@CanIgnoreReturnValue
69+
public Builder clearSignature() {
70+
return signature(Optional.empty());
71+
}
72+
73+
public abstract VoiceConsentSignature build();
74+
}
75+
76+
/** Deserializes a JSON string to a VoiceConsentSignature object. */
77+
@ExcludeFromGeneratedCoverageReport
78+
public static VoiceConsentSignature fromJson(String jsonString) {
79+
return JsonSerializable.fromJsonString(jsonString, VoiceConsentSignature.class);
80+
}
81+
}

src/test/java/com/google/genai/AsyncLiveTest.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@
2626
import com.google.genai.types.HttpOptions;
2727
import java.lang.reflect.Method;
2828
import java.net.URI;
29+
import java.util.HashMap;
2930
import java.util.Map;
3031
import java.util.Optional;
32+
import java.util.concurrent.CompletableFuture;
3133
import org.junit.jupiter.api.BeforeEach;
3234
import org.junit.jupiter.api.Test;
3335

@@ -118,4 +120,25 @@ public void testGetWebSocketHeaders_GoogleAiEphemeralToken() throws Exception {
118120

119121
assertEquals("Token auth_tokens/ephemeral-token", headers.get("Authorization"));
120122
}
123+
@Test
124+
public void testOnMessage_PopulatesSetupCompleteWithVoiceConsent() throws Exception {
125+
CompletableFuture<AsyncSession> future = new CompletableFuture<>();
126+
URI uri = new URI("wss://test");
127+
Map<String, String> headers = new HashMap<>();
128+
String setupRequest = "{}";
129+
130+
AsyncLive.GenAiWebSocketClient client =
131+
new AsyncLive.GenAiWebSocketClient(uri, headers, setupRequest, future, apiClient);
132+
133+
String message =
134+
"{\"setupComplete\":{\"voiceConsentSignature\":{\"signature\":\"test_sig\"}}}";
135+
136+
client.onMessage(message);
137+
138+
AsyncSession session = future.get();
139+
assertTrue(session != null);
140+
assertTrue(session.setupComplete() != null);
141+
assertTrue(session.setupComplete().voiceConsentSignature().isPresent());
142+
assertEquals("test_sig", session.setupComplete().voiceConsentSignature().get().signature().get());
143+
}
121144
}

0 commit comments

Comments
 (0)