tool_use: add multiplechoice support to Agent
This implements the "dumb" approach - the tool itself just tells
the llm that it rendered the options to the user, and it's done.
If the user selects one of the options, we paste its response text
into the chat input textarea on the frontend. The user is of
course free to ignore the question or the options presented.
This keeps no association between user response and the original
tool_use block that solicited it from the user. I.e. the user
response message doesn't include the original tool_use_id value
it it. It looks as though the user typed it by hand.
diff --git a/loop/agent.go b/loop/agent.go
index 960bf5a..3da8e94 100644
--- a/loop/agent.go
+++ b/loop/agent.go
@@ -828,7 +828,7 @@
convo.Tools = []*llm.Tool{
bashTool, claudetool.Keyword,
claudetool.Think, a.titleTool(), makeDoneTool(a.codereview, a.config.GitUsername, a.config.GitEmail),
- a.codereview.Tool(),
+ a.codereview.Tool(), a.multipleChoiceTool(),
}
if a.config.UseAnthropicEdit {
convo.Tools = append(convo.Tools, claudetool.AnthropicEditTool)
@@ -839,6 +839,61 @@
return convo
}
+func (a *Agent) multipleChoiceTool() *llm.Tool {
+ ret := &llm.Tool{
+ Name: "multiplechoice",
+ Description: "Present the user with an quick way to answer to your question using one of a short list of possible answers you would expect from the user.",
+ InputSchema: json.RawMessage(`{
+ "type": "object",
+ "description": "The question and a list of answers you would expect the user to choose from.",
+ "properties": {
+ "question": {
+ "type": "string",
+ "description": "The text of the multiple-choice question you would like the user, e.g. 'What kinds of test cases would you like me to add?'"
+ },
+ "responseOptions": {
+ "type": "array",
+ "description": "The set of possible answers to let the user quickly choose from, e.g. ['Basic unit test coverage', 'Error return values', 'Malformed input'].",
+ "items": {
+ "type": "object",
+ "properties": {
+ "caption": {
+ "type": "string",
+ "description": "The caption, e.g. 'Basic coverage', 'Error return values', or 'Malformed input' for the response button. Do NOT include options for responses that would end the conversation like 'Ok', 'No thank you', 'This looks good'"
+ },
+ "responseText": {
+ "type": "string",
+ "description": "The full text of the response, e.g. 'Add unit tests for basic test coverage', 'Add unit tests for error return values', or 'Add unit tests for malformed input'"
+ }
+ },
+ "required": ["caption", "responseText"]
+ }
+ }
+ },
+ "required": ["question", "responseOptions"]
+}`),
+ Run: func(ctx context.Context, input json.RawMessage) (string, error) {
+ // The Run logic for "multiplchoice" tool is a no-op on the server.
+ // The UI will present a list of options for the user to select from,
+ // and that's it as far as "executing" the tool_use goes.
+ // When the user *does* select one of the presented options, that
+ // responseText gets sent as a chat message on behalf of the user.
+ return "end your turn and wait for the user to respond", nil
+ },
+ }
+ return ret
+}
+
+type MultipleChoiceOption struct {
+ Caption string `json:"caption"`
+ ResponseText string `json:"responseText"`
+}
+
+type MultipleChoiceParams struct {
+ Question string `json:"question"`
+ ResponseOptions []MultipleChoiceOption `json:"responseOptions"`
+}
+
// branchExists reports whether branchName exists, either locally or in well-known remotes.
func branchExists(dir, branchName string) bool {
refs := []string{
@@ -921,6 +976,11 @@
a.inbox <- msg
}
+func (a *Agent) ToolResultMessage(ctx context.Context, toolCallID, msg string) {
+ a.pushToOutbox(ctx, AgentMessage{Type: UserMessageType, Content: msg, ToolCallId: toolCallID})
+ a.inbox <- msg
+}
+
func (a *Agent) CancelToolUse(toolUseID string, cause error) error {
return a.convo.CancelToolUse(toolUseID, cause)
}