Skip to content

Commit 095eff6

Browse files
google-genai-botcopybara-github
authored andcommitted
feat: Added JSON Schema for configurable agents
PiperOrigin-RevId: 789922600
1 parent bbf38e3 commit 095eff6

File tree

4 files changed

+368
-0
lines changed

4 files changed

+368
-0
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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+
* http://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+
package com.google.adk.agents;
18+
19+
import com.fasterxml.jackson.annotation.JsonProperty;
20+
import com.google.adk.utils.FeatureDecorator.WorkInProgress;
21+
22+
/** Base configuration for all agents. */
23+
@WorkInProgress
24+
public class BaseAgentConfig {
25+
private String name;
26+
private String description = "";
27+
28+
@JsonProperty(value = "name", required = true)
29+
public String name() {
30+
return name;
31+
}
32+
33+
public void setName(String name) {
34+
this.name = name;
35+
}
36+
37+
@JsonProperty("description")
38+
public String description() {
39+
return description;
40+
}
41+
42+
public void setDescription(String description) {
43+
this.description = description;
44+
}
45+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
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+
* http://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+
package com.google.adk.agents;
18+
19+
import com.fasterxml.jackson.annotation.JsonProperty;
20+
import com.google.adk.utils.FeatureDecorator.WorkInProgress;
21+
22+
/** Configuration for LlmAgent. */
23+
@WorkInProgress
24+
public class LlmAgentConfig extends BaseAgentConfig {
25+
private String model;
26+
private String instruction;
27+
private Boolean disallowTransferToParent;
28+
private Boolean disallowTransferToPeers;
29+
private String outputKey;
30+
31+
// Non-standard accessors with JsonProperty annotations
32+
@JsonProperty("model")
33+
public String model() {
34+
return model;
35+
}
36+
37+
public void setModel(String model) {
38+
this.model = model;
39+
}
40+
41+
@JsonProperty(value = "instruction", required = true)
42+
public String instruction() {
43+
return instruction;
44+
}
45+
46+
public void setInstruction(String instruction) {
47+
this.instruction = instruction;
48+
}
49+
50+
@JsonProperty("disallow_transfer_to_parent")
51+
public Boolean disallowTransferToParent() {
52+
return disallowTransferToParent;
53+
}
54+
55+
public void setDisallowTransferToParent(Boolean disallowTransferToParent) {
56+
this.disallowTransferToParent = disallowTransferToParent;
57+
}
58+
59+
@JsonProperty("disallow_transfer_to_peers")
60+
public Boolean disallowTransferToPeers() {
61+
return disallowTransferToPeers;
62+
}
63+
64+
public void setDisallowTransferToPeers(Boolean disallowTransferToPeers) {
65+
this.disallowTransferToPeers = disallowTransferToPeers;
66+
}
67+
68+
@JsonProperty("output_key")
69+
public String outputKey() {
70+
return outputKey;
71+
}
72+
73+
public void setOutputKey(String outputKey) {
74+
this.outputKey = outputKey;
75+
}
76+
}
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
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+
* http://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+
package com.google.adk.utils;
18+
19+
import java.lang.annotation.ElementType;
20+
import java.lang.annotation.Retention;
21+
import java.lang.annotation.RetentionPolicy;
22+
import java.lang.annotation.Target;
23+
import java.lang.reflect.AnnotatedElement;
24+
import java.lang.reflect.Constructor;
25+
import java.lang.reflect.Method;
26+
import java.util.Locale;
27+
import org.slf4j.Logger;
28+
import org.slf4j.LoggerFactory;
29+
30+
/**
31+
* A utility class for handling feature decorators.
32+
*
33+
* <p>This class provides methods for checking for feature decorators on classes and methods and
34+
* handling them appropriately.
35+
*/
36+
public final class FeatureDecorator {
37+
38+
/**
39+
* Mark a class or a function as a work in progress.
40+
*
41+
* <p>By default, decorated functions/classes will raise RuntimeError when used. Set
42+
* ADK_ALLOW_WIP_FEATURES=true environment variable to bypass this restriction. ADK users are not
43+
* supposed to set this environment variable.
44+
*
45+
* <p>Sample usage:
46+
*
47+
* <pre>
48+
* {@literal @}WorkInProgress("This feature is not ready for production use.")
49+
* public void myWipFunction() {
50+
* // ...
51+
* }
52+
* </pre>
53+
*/
54+
@Retention(RetentionPolicy.RUNTIME)
55+
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR})
56+
public @interface WorkInProgress {
57+
/**
58+
* The message to be displayed when the feature is used. If not provided, a default message will
59+
* be used.
60+
*/
61+
String value() default
62+
"This feature is a work in progress and is not working completely. ADK users are not"
63+
+ " supposed to use it.";
64+
}
65+
66+
/**
67+
* Mark a class or a function as an experimental feature.
68+
*
69+
* <p>Sample usage:
70+
*
71+
* <pre>
72+
* // Use with default message
73+
* {@literal @}Experimental
74+
* public class ExperimentalClass {
75+
* // ...
76+
* }
77+
*
78+
* // Use with custom message
79+
* {@literal @}Experimental("This API may have breaking change in the future.")
80+
* public class CustomExperimentalClass {
81+
* // ...
82+
* }
83+
* </pre>
84+
*/
85+
@Retention(RetentionPolicy.RUNTIME)
86+
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR})
87+
public @interface Experimental {
88+
/**
89+
* The message to be displayed when the feature is used. If not provided, a default message will
90+
* be used.
91+
*/
92+
String value() default
93+
"This feature is experimental and may change or be removed in future versions without"
94+
+ " notice. It may introduce breaking changes at any time.";
95+
}
96+
97+
private static final Logger logger = LoggerFactory.getLogger(FeatureDecorator.class);
98+
99+
/**
100+
* Handles the feature decorators for the given class.
101+
*
102+
* @param clazz the class to handle the decorators for
103+
*/
104+
public static void handle(Class<?> clazz) {
105+
handleExperimental(clazz);
106+
handleWorkInProgress(clazz);
107+
}
108+
109+
/**
110+
* Handles the feature decorators for the given method.
111+
*
112+
* @param method the method to handle the decorators for
113+
*/
114+
public static void handle(Method method) {
115+
handleExperimental(method);
116+
handleWorkInProgress(method);
117+
}
118+
119+
/**
120+
* Handles the feature decorators for the given constructor.
121+
*
122+
* @param constructor the constructor to handle the decorators for
123+
*/
124+
public static void handle(Constructor<?> constructor) {
125+
handleExperimental(constructor);
126+
handleWorkInProgress(constructor);
127+
}
128+
129+
private static void handleExperimental(AnnotatedElement element) {
130+
if (element.isAnnotationPresent(Experimental.class)) {
131+
Experimental annotation = element.getAnnotation(Experimental.class);
132+
String message = annotation.value();
133+
logger.warn("[EXPERIMENTAL] {}: {}", getElementName(element), message);
134+
}
135+
}
136+
137+
private static void handleWorkInProgress(AnnotatedElement element) {
138+
if (element.isAnnotationPresent(WorkInProgress.class)) {
139+
String bypassEnvVar = "ADK_ALLOW_WIP_FEATURES";
140+
String envValue = System.getenv(bypassEnvVar);
141+
if (envValue == null || !envValue.toLowerCase(Locale.ROOT).equals("true")) {
142+
WorkInProgress annotation = element.getAnnotation(WorkInProgress.class);
143+
String message = annotation.value();
144+
throw new UnsupportedOperationException(
145+
"[WIP] " + getElementName(element) + ": " + message);
146+
}
147+
}
148+
}
149+
150+
private static String getElementName(AnnotatedElement element) {
151+
if (element instanceof Class) {
152+
return ((Class<?>) element).getSimpleName();
153+
} else if (element instanceof Method method) {
154+
return method.getName();
155+
} else if (element instanceof Constructor) {
156+
return ((Constructor<?>) element).getDeclaringClass().getSimpleName();
157+
}
158+
return "UnknownElement";
159+
}
160+
161+
private FeatureDecorator() {}
162+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
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+
* http://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+
package com.google.adk.utils;
18+
19+
import static org.junit.Assert.assertThrows;
20+
import static org.junit.Assert.assertTrue;
21+
22+
import com.google.adk.utils.FeatureDecorator.Experimental;
23+
import com.google.adk.utils.FeatureDecorator.WorkInProgress;
24+
import org.junit.Test;
25+
import org.junit.runner.RunWith;
26+
import org.junit.runners.JUnit4;
27+
28+
@RunWith(JUnit4.class)
29+
public final class FeatureDecoratorTest {
30+
31+
@WorkInProgress("This is a WIP class.")
32+
private static class WipClass {}
33+
34+
@Experimental("This is an experimental class.")
35+
private static class ExperimentalClass {}
36+
37+
private static class TestClass {
38+
@WorkInProgress("This is a WIP method.")
39+
@SuppressWarnings("unused") // Used via reflection in tests
40+
private void wipMethod() {}
41+
42+
@Experimental("This is an experimental method.")
43+
@SuppressWarnings("unused") // Used via reflection in tests
44+
private void experimentalMethod() {}
45+
}
46+
47+
@Test
48+
public void handle_wipClass_throwsException() {
49+
UnsupportedOperationException exception =
50+
assertThrows(
51+
UnsupportedOperationException.class, () -> FeatureDecorator.handle(WipClass.class));
52+
53+
assertTrue(exception.getMessage().contains("[WIP] WipClass: This is a WIP class."));
54+
}
55+
56+
@Test
57+
public void handle_wipMethod_throwsException() throws NoSuchMethodException {
58+
UnsupportedOperationException exception =
59+
assertThrows(
60+
UnsupportedOperationException.class,
61+
() -> FeatureDecorator.handle(TestClass.class.getDeclaredMethod("wipMethod")));
62+
63+
assertTrue(exception.getMessage().contains("[WIP] wipMethod: This is a WIP method."));
64+
}
65+
66+
@Test
67+
public void handle_experimentalClass_doesNotThrow() {
68+
// Simply verify that handling experimental classes doesn't throw
69+
FeatureDecorator.handle(ExperimentalClass.class);
70+
// If we get here without exception, the test passes
71+
}
72+
73+
@Test
74+
public void handle_experimentalMethod_doesNotThrow() throws NoSuchMethodException {
75+
// Simply verify that handling experimental methods doesn't throw
76+
FeatureDecorator.handle(TestClass.class.getDeclaredMethod("experimentalMethod"));
77+
// If we get here without exception, the test passes
78+
}
79+
80+
@Test
81+
public void handle_normalClass_doesNotThrow() {
82+
// Verify that handling non-annotated classes works fine
83+
FeatureDecorator.handle(TestClass.class);
84+
}
85+
}

0 commit comments

Comments
 (0)