Skip to content

Commit 20c8252

Browse files
feat: [vertexai] add fromFunctionResponse in PartMaker (#10272)
PiperOrigin-RevId: 600847017 Co-authored-by: Jaycee Li <jayceeli@google.com>
1 parent e761894 commit 20c8252

File tree

3 files changed

+114
-1
lines changed

3 files changed

+114
-1
lines changed

java-vertexai/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ If you are using Maven with [BOM][libraries-bom], add this to your pom.xml file:
1818
<dependency>
1919
<groupId>com.google.cloud</groupId>
2020
<artifactId>libraries-bom</artifactId>
21-
<version>26.30.0</version>
21+
<version>26.29.0</version>
2222
<type>pom</type>
2323
<scope>import</scope>
2424
</dependency>

java-vertexai/google-cloud-vertexai/src/main/java/com/google/cloud/vertexai/generativeai/preview/PartMaker.java

+56
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,14 @@
1818

1919
import com.google.cloud.vertexai.api.Blob;
2020
import com.google.cloud.vertexai.api.FileData;
21+
import com.google.cloud.vertexai.api.FunctionResponse;
2122
import com.google.cloud.vertexai.api.Part;
2223
import com.google.protobuf.ByteString;
24+
import com.google.protobuf.NullValue;
25+
import com.google.protobuf.Struct;
26+
import com.google.protobuf.Value;
2327
import java.net.URI;
28+
import java.util.Map;
2429

2530
/** Helper class to create {@link com.google.cloud.vertexai.api.Part} */
2631
public class PartMaker {
@@ -77,4 +82,55 @@ public static Part fromMimeTypeAndData(String mimeType, Object partData) {
7782
}
7883
return part;
7984
}
85+
86+
/**
87+
* Make a {@link com.google.cloud.vertexai.api.Part} from the output of {@link
88+
* com.google.cloud.vertexai.api.FunctionCall}.
89+
*
90+
* @param name a string represents the name of the {@link
91+
* com.google.cloud.vertexai.api.FunctionDeclaration}
92+
* @param response a structured JSON object containing any output from the function call
93+
*/
94+
public static Part fromFunctionResponse(String name, Struct response) {
95+
return Part.newBuilder()
96+
.setFunctionResponse(FunctionResponse.newBuilder().setName(name).setResponse(response))
97+
.build();
98+
}
99+
100+
/**
101+
* Make a {@link com.google.cloud.vertexai.api.Part} from the result output of {@link
102+
* com.google.cloud.vertexai.api.FunctionCall}.
103+
*
104+
* @param name a string represents the name of the {@link
105+
* com.google.cloud.vertexai.api.FunctionDeclaration}
106+
* @param response a map containing the output from the function call, supported output type:
107+
* String, Double, Boolean, null
108+
*/
109+
public static Part fromFunctionResponse(String name, Map<String, Object> response) {
110+
Struct.Builder structBuilder = Struct.newBuilder();
111+
response.forEach(
112+
(key, value) -> {
113+
if (value instanceof String) {
114+
String stringValue = (String) value;
115+
structBuilder.putFields(key, Value.newBuilder().setStringValue(stringValue).build());
116+
} else if (value instanceof Double) {
117+
Double doubleValue = (Double) value;
118+
structBuilder.putFields(key, Value.newBuilder().setNumberValue(doubleValue).build());
119+
} else if (value instanceof Boolean) {
120+
Boolean boolValue = (Boolean) value;
121+
structBuilder.putFields(key, Value.newBuilder().setBoolValue(boolValue).build());
122+
} else if (value == null) {
123+
structBuilder.putFields(
124+
key, Value.newBuilder().setNullValue(NullValue.NULL_VALUE).build());
125+
} else {
126+
throw new IllegalArgumentException(
127+
"The value in the map can only be one of the following format: "
128+
+ "String, Double, Boolean, null.");
129+
}
130+
});
131+
132+
return Part.newBuilder()
133+
.setFunctionResponse(FunctionResponse.newBuilder().setName(name).setResponse(structBuilder))
134+
.build();
135+
}
80136
}

java-vertexai/google-cloud-vertexai/src/test/java/com/google/cloud/vertexai/generativeai/preview/PartMakerTest.java

+57
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,16 @@
1717
package com.google.cloud.vertexai.generativeai.preview;
1818

1919
import static com.google.common.truth.Truth.assertThat;
20+
import static org.junit.Assert.assertThrows;
2021

2122
import com.google.cloud.vertexai.api.Part;
2223
import com.google.protobuf.ByteString;
24+
import com.google.protobuf.Struct;
25+
import com.google.protobuf.Value;
2326
import java.net.URI;
2427
import java.net.URISyntaxException;
28+
import java.util.HashMap;
29+
import java.util.Map;
2530
import org.junit.Test;
2631
import org.junit.runner.RunWith;
2732
import org.junit.runners.JUnit4;
@@ -72,4 +77,56 @@ public void fromMimeTypeAndData_dataInURI() throws URISyntaxException {
7277
assertThat(part.getFileData().getMimeType()).isEqualTo("image/png");
7378
assertThat(part.getFileData().getFileUri()).isEqualTo(fileUri.toString());
7479
}
80+
81+
@Test
82+
public void testFromFunctionResponseWithStruct() {
83+
String functionName = "getCurrentWeather";
84+
Struct functionResponse =
85+
Struct.newBuilder()
86+
.putFields("currentWeather", Value.newBuilder().setStringValue("Super nice!").build())
87+
.putFields("currentTemperature", Value.newBuilder().setNumberValue(85.0).build())
88+
.putFields("isRaining", Value.newBuilder().setBoolValue(false).build())
89+
.build();
90+
91+
Part part = PartMaker.fromFunctionResponse(functionName, functionResponse);
92+
93+
assertThat(part.getFunctionResponse().getName()).isEqualTo("getCurrentWeather");
94+
assertThat(part.getFunctionResponse().getResponse()).isEqualTo(functionResponse);
95+
}
96+
97+
@Test
98+
public void testFromFunctionResponseWithMap() {
99+
String functionName = "getCurrentWeather";
100+
Map<String, Object> functionResponse = new HashMap<>();
101+
functionResponse.put("currentWeather", "Super nice!");
102+
functionResponse.put("currentTemperature", 85.0);
103+
functionResponse.put("isRaining", false);
104+
functionResponse.put("other", null);
105+
106+
Part part = PartMaker.fromFunctionResponse(functionName, functionResponse);
107+
108+
assertThat(part.getFunctionResponse().getName()).isEqualTo("getCurrentWeather");
109+
110+
Map<String, Value> fieldsMap = part.getFunctionResponse().getResponse().getFieldsMap();
111+
assertThat(fieldsMap.get("currentWeather").getStringValue()).isEqualTo("Super nice!");
112+
assertThat(fieldsMap.get("currentTemperature").getNumberValue()).isEqualTo(85.0);
113+
assertThat(fieldsMap.get("isRaining").getBoolValue()).isEqualTo(false);
114+
assertThat(fieldsMap.get("other").hasNullValue()).isEqualTo(true);
115+
}
116+
117+
@Test
118+
public void testFromFunctionResponseWithInvalidMap() {
119+
String functionName = "getCurrentWeather";
120+
Map<String, Object> invalidResponse = new HashMap<>();
121+
invalidResponse.put("currentWeather", new byte[] {1, 2, 3});
122+
IllegalArgumentException thrown =
123+
assertThrows(
124+
IllegalArgumentException.class,
125+
() -> PartMaker.fromFunctionResponse(functionName, invalidResponse));
126+
assertThat(thrown)
127+
.hasMessageThat()
128+
.isEqualTo(
129+
"The value in the map can only be one of the following format: "
130+
+ "String, Double, Boolean, null.");
131+
}
75132
}

0 commit comments

Comments
 (0)