-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Add opaque metadata passthrough to SDK tool definitions
#1864
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -75,6 +75,10 @@ | |
| * controls whether the tool may be deferred (loaded lazily via tool | ||
| * search) rather than always pre-loaded; {@code null} lets the | ||
| * runtime decide | ||
| * @param metadata | ||
| * opaque, host-defined metadata forwarded verbatim to the runtime; | ||
| * keys are namespaced and not part of the stable public API; | ||
| * {@code null} when unset | ||
| * @see SessionConfig#setTools(java.util.List) | ||
| * @see ToolHandler | ||
| * @since 1.0.0 | ||
|
|
@@ -83,7 +87,8 @@ | |
| public record ToolDefinition(@JsonProperty("name") String name, @JsonProperty("description") String description, | ||
| @JsonProperty("parameters") Object parameters, @JsonIgnore ToolHandler handler, | ||
| @JsonProperty("overridesBuiltInTool") Boolean overridesBuiltInTool, | ||
| @JsonProperty("skipPermission") Boolean skipPermission, @JsonProperty("defer") ToolDefer defer) { | ||
| @JsonProperty("skipPermission") Boolean skipPermission, @JsonProperty("defer") ToolDefer defer, | ||
| @JsonProperty("metadata") Map<String, Object> metadata) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems like this would be a breaking change. @edburns What's the recommended way to handle this in Java? Should this be added purely as a setter and not a constructor parameter? Or should it be on a new constructor overload?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. after looking into it more yea this would be a breaking change 😭 it seems like overloading the record wth multiple constructors to maintain backwards compatibility is the recommendation from googling, but ill defer to @edburns for guidance 🙇🏼
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hello @belaltaher8 , thanks for looping me in. Thankfully, even if its a breaking change, it's ok because we have the protection of the
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for doing this, @belaltaher8. From my perspective as the Java SME, these two items are the most important. I realize this may come across as a pain in the ass, but Microsoft has decided that language idiomatic SDK surface area is a key differentiator from our competition. So when we add new features, we need to "localize" them with as much fidelity as possible.
Testing guidance (specific):
I will paste the AI generated sketch of the new inner-annotations to enable
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. /*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------------------------------------------*/
package com.github.copilot.tool;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.github.copilot.CopilotExperimental;
import com.github.copilot.rpc.ToolDefer;
/**
* Marks a method as a Copilot tool. The annotated method will be exposed to the
* model as a callable tool during a session.
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@CopilotExperimental
public @interface CopilotTool {
/** Tool description (sent to the model). */
String value();
/** Tool name. Defaults to method name converted to snake_case. */
String name() default "";
/** Whether this tool overrides a built-in tool. */
boolean overridesBuiltInTool() default false;
/** Whether to skip permission checks. */
boolean skipPermission() default false;
/** Defer configuration for this tool. */
ToolDefer defer() default ToolDefer.NONE;
/**
* Optional metadata bag for host-defined properties.
*
* Example emitted shape:
* Map.of("github.com/copilot:safeForTelemetry",
* Map.of("name", true, "inputsNames", false))
*/
MetadataEntry[] metadata() default {};
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
@interface MetadataEntry {
String key();
MetadataValue value();
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
@interface MetadataValue {
// Scalar options for simple cases
boolean bool() default false;
String str() default "";
// Object-like value for structured cases
MetadataFlag[] flags() default {};
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
@interface MetadataFlag {
String name();
boolean value();
}
}Example usage: @CopilotTool(
value = "Reports phase",
metadata = {
@CopilotTool.MetadataEntry(
key = "github.com/copilot:safeForTelemetry",
value = @CopilotTool.MetadataValue(
flags = {
@CopilotTool.MetadataFlag(name = "name", value = true),
@CopilotTool.MetadataFlag(name = "inputsNames", value = false)
}
)
)
}
)
public String reportPhase(String phase) {
return phase;
} |
||
|
|
||
| /** | ||
| * Creates a tool definition with a JSON schema for parameters. | ||
|
|
@@ -103,7 +108,7 @@ public record ToolDefinition(@JsonProperty("name") String name, @JsonProperty("d | |
| */ | ||
| public static ToolDefinition create(String name, String description, Map<String, Object> schema, | ||
| ToolHandler handler) { | ||
| return new ToolDefinition(name, description, schema, handler, null, null, null); | ||
| return new ToolDefinition(name, description, schema, handler, null, null, null, null); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -127,7 +132,7 @@ public static ToolDefinition create(String name, String description, Map<String, | |
| */ | ||
| public static ToolDefinition createOverride(String name, String description, Map<String, Object> schema, | ||
| ToolHandler handler) { | ||
| return new ToolDefinition(name, description, schema, handler, true, null, null); | ||
| return new ToolDefinition(name, description, schema, handler, true, null, null, null); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -150,7 +155,7 @@ public static ToolDefinition createOverride(String name, String description, Map | |
| */ | ||
| public static ToolDefinition createSkipPermission(String name, String description, Map<String, Object> schema, | ||
| ToolHandler handler) { | ||
| return new ToolDefinition(name, description, schema, handler, null, true, null); | ||
| return new ToolDefinition(name, description, schema, handler, null, true, null, null); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -176,7 +181,32 @@ public static ToolDefinition createSkipPermission(String name, String descriptio | |
| */ | ||
| public static ToolDefinition createWithDefer(String name, String description, Map<String, Object> schema, | ||
| ToolHandler handler, ToolDefer defer) { | ||
| return new ToolDefinition(name, description, schema, handler, null, null, defer); | ||
| return new ToolDefinition(name, description, schema, handler, null, null, defer, null); | ||
| } | ||
|
|
||
| /** | ||
| * Creates a tool definition with opaque, host-defined metadata. | ||
| * <p> | ||
| * Use this factory method to attach namespaced metadata that the SDK forwards | ||
| * verbatim to the runtime. The keys are not part of the stable public API; the | ||
| * runtime may recognize specific keys to inform host-specific behavior. | ||
| * | ||
| * @param name | ||
| * the unique name of the tool | ||
| * @param description | ||
| * a description of what the tool does | ||
| * @param schema | ||
| * the JSON Schema as a {@code Map} | ||
| * @param handler | ||
| * the handler function to execute when invoked | ||
| * @param metadata | ||
| * the opaque metadata map forwarded to the runtime | ||
| * @return a new tool definition with the metadata set | ||
| * @since 1.0.7 | ||
| */ | ||
| public static ToolDefinition createWithMetadata(String name, String description, Map<String, Object> schema, | ||
| ToolHandler handler, Map<String, Object> metadata) { | ||
| return new ToolDefinition(name, description, schema, handler, null, null, null, metadata); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -247,7 +277,7 @@ public static List<ToolDefinition> fromClass(Class<?> clazz) { | |
| */ | ||
| @CopilotExperimental | ||
| public ToolDefinition overridesBuiltInTool(boolean value) { | ||
| return new ToolDefinition(name, description, parameters, handler, value, skipPermission, defer); | ||
| return new ToolDefinition(name, description, parameters, handler, value, skipPermission, defer, metadata); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -261,7 +291,7 @@ public ToolDefinition overridesBuiltInTool(boolean value) { | |
| */ | ||
| @CopilotExperimental | ||
| public ToolDefinition skipPermission(boolean value) { | ||
| return new ToolDefinition(name, description, parameters, handler, overridesBuiltInTool, value, defer); | ||
| return new ToolDefinition(name, description, parameters, handler, overridesBuiltInTool, value, defer, metadata); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -275,7 +305,23 @@ public ToolDefinition skipPermission(boolean value) { | |
| */ | ||
| @CopilotExperimental | ||
| public ToolDefinition defer(ToolDefer value) { | ||
| return new ToolDefinition(name, description, parameters, handler, overridesBuiltInTool, skipPermission, value); | ||
| return new ToolDefinition(name, description, parameters, handler, overridesBuiltInTool, skipPermission, value, | ||
| metadata); | ||
| } | ||
|
|
||
| /** | ||
| * Returns a copy with the opaque {@code metadata} bag set. | ||
| * | ||
| * @param value | ||
| * the opaque, host-defined metadata forwarded verbatim to the | ||
| * runtime; keys are namespaced and not part of the stable public API | ||
| * @return a new {@code ToolDefinition} with the metadata applied | ||
| * @since 1.0.7 | ||
| */ | ||
| @CopilotExperimental | ||
| public ToolDefinition metadata(Map<String, Object> value) { | ||
| return new ToolDefinition(name, description, parameters, handler, overridesBuiltInTool, skipPermission, defer, | ||
| value); | ||
| } | ||
|
|
||
| // ------------------------------------------------------------------ | ||
|
|
@@ -319,7 +365,7 @@ public static <R> ToolDefinition from(String name, String description, Supplier< | |
| R result = handler.get(); | ||
| return CompletableFuture.completedFuture(formatResult(result, mapper)); | ||
| }; | ||
| return new ToolDefinition(name, description, schema, toolHandler, null, null, null); | ||
| return new ToolDefinition(name, description, schema, toolHandler, null, null, null, null); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -361,7 +407,7 @@ public static <T1, R> ToolDefinition from(String name, String description, Param | |
| R result = handler.apply(arg1); | ||
| return CompletableFuture.completedFuture(formatResult(result, mapper)); | ||
| }; | ||
| return new ToolDefinition(name, description, schema, toolHandler, null, null, null); | ||
| return new ToolDefinition(name, description, schema, toolHandler, null, null, null, null); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -409,7 +455,7 @@ public static <T1, T2, R> ToolDefinition from(String name, String description, P | |
| R result = handler.apply(arg1, arg2); | ||
| return CompletableFuture.completedFuture(formatResult(result, mapper)); | ||
| }; | ||
| return new ToolDefinition(name, description, schema, toolHandler, null, null, null); | ||
| return new ToolDefinition(name, description, schema, toolHandler, null, null, null, null); | ||
| } | ||
|
|
||
| // ------------------------------------------------------------------ | ||
|
|
@@ -458,7 +504,7 @@ public static <R> ToolDefinition fromAsync(String name, String description, | |
| } | ||
| return future.thenApply(result -> formatResult(result, mapper)); | ||
| }; | ||
| return new ToolDefinition(name, description, schema, toolHandler, null, null, null); | ||
| return new ToolDefinition(name, description, schema, toolHandler, null, null, null, null); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -506,7 +552,7 @@ public static <T1, R> ToolDefinition fromAsync(String name, String description, | |
| } | ||
| return future.thenApply(result -> formatResult(result, mapper)); | ||
| }; | ||
| return new ToolDefinition(name, description, schema, toolHandler, null, null, null); | ||
| return new ToolDefinition(name, description, schema, toolHandler, null, null, null, null); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -551,7 +597,7 @@ public static <T1, T2, R> ToolDefinition fromAsync(String name, String descripti | |
| } | ||
| return future.thenApply(result -> formatResult(result, mapper)); | ||
| }; | ||
| return new ToolDefinition(name, description, schema, toolHandler, null, null, null); | ||
| return new ToolDefinition(name, description, schema, toolHandler, null, null, null, null); | ||
| } | ||
|
|
||
| // ------------------------------------------------------------------ | ||
|
|
@@ -594,7 +640,7 @@ public static <R> ToolDefinition fromWithToolInvocation(String name, String desc | |
| R result = handler.apply(invocation); | ||
| return CompletableFuture.completedFuture(formatResult(result, mapper)); | ||
| }; | ||
| return new ToolDefinition(name, description, schema, toolHandler, null, null, null); | ||
| return new ToolDefinition(name, description, schema, toolHandler, null, null, null, null); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -640,7 +686,7 @@ public static <T1, R> ToolDefinition fromWithToolInvocation(String name, String | |
| R result = handler.apply(arg1, invocation); | ||
| return CompletableFuture.completedFuture(formatResult(result, mapper)); | ||
| }; | ||
| return new ToolDefinition(name, description, schema, toolHandler, null, null, null); | ||
| return new ToolDefinition(name, description, schema, toolHandler, null, null, null, null); | ||
| } | ||
|
|
||
| // ------------------------------------------------------------------ | ||
|
|
@@ -689,7 +735,7 @@ public static <R> ToolDefinition fromAsyncWithToolInvocation(String name, String | |
| } | ||
| return future.thenApply(result -> formatResult(result, mapper)); | ||
| }; | ||
| return new ToolDefinition(name, description, schema, toolHandler, null, null, null); | ||
| return new ToolDefinition(name, description, schema, toolHandler, null, null, null, null); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -741,7 +787,7 @@ public static <T1, R> ToolDefinition fromAsyncWithToolInvocation(String name, St | |
| } | ||
| return future.thenApply(result -> formatResult(result, mapper)); | ||
| }; | ||
| return new ToolDefinition(name, description, schema, toolHandler, null, null, null); | ||
| return new ToolDefinition(name, description, schema, toolHandler, null, null, null, null); | ||
| } | ||
|
|
||
| // ------------------------------------------------------------------ | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.