@@ -120,35 +120,56 @@ final class DashClient extends MCPClient with RootsSupport {
120120 Future <String ?> _handleFunctionCall (gemini.FunctionCall functionCall) async {
121121 await _chatToUser (
122122 'It looks like you want to invoke tool ${functionCall .name } with args '
123- '${jsonEncode (functionCall .args )}, is that correct? (y/n) ' ,
123+ '${jsonEncode (functionCall .args )}, is that correct?' ,
124124 );
125- final answer = await stdinQueue.next;
126- chatHistory.add (gemini.Content .text (answer));
127- if (answer == 'y' ) {
128- chatHistory.add (gemini.Content .model ([functionCall]));
129- final connection = connectionForFunction[functionCall.name]! ;
130- final result = await connection.callTool (
131- CallToolRequest (name: functionCall.name, arguments: functionCall.args),
132- );
133- final response = StringBuffer ();
134- for (var content in result.content) {
135- switch (content) {
136- case final TextContent content when content.isText:
137- response.writeln (content.text);
138- case final ImageContent content when content.isImage:
139- chatHistory.add (
140- gemini.Content .data ('image/png' , base64Decode (content.data)),
141- );
142- response.writeln ('Image added to context' );
143- default :
144- response.writeln ('Got unsupported response type ${content .type }' );
145- }
125+ final userResponse = await stdinQueue.next;
126+ final wasApproval = await _analyzeSentiment (userResponse);
127+
128+ // If they did not approve the action, just treat their response as a
129+ // prompt.
130+ if (! wasApproval) return userResponse;
131+
132+ chatHistory.add (gemini.Content .model ([functionCall]));
133+ final connection = connectionForFunction[functionCall.name]! ;
134+ final result = await connection.callTool (
135+ CallToolRequest (name: functionCall.name, arguments: functionCall.args),
136+ );
137+ final response = StringBuffer ();
138+ for (var content in result.content) {
139+ switch (content) {
140+ case final TextContent content when content.isText:
141+ response.writeln (content.text);
142+ case final ImageContent content when content.isImage:
143+ chatHistory.add (
144+ gemini.Content .data ('image/png' , base64Decode (content.data)),
145+ );
146+ response.writeln ('Image added to context' );
147+ default :
148+ response.writeln ('Got unsupported response type ${content .type }' );
146149 }
147- await _chatToUser (response.toString ());
148- return null ;
149- } else {
150- return answer;
151150 }
151+ await _chatToUser (response.toString ());
152+ return null ;
153+ }
154+
155+ /// Analyzes a user [message] to see if it looks like they approved of the
156+ /// previous action.
157+ Future <bool > _analyzeSentiment (String message) async {
158+ if (message == 'y' || message == 'yes' ) return true ;
159+ final sentimentResult =
160+ (await model.generateContent ([
161+ gemini.Content .text (
162+ 'Analyze the sentiment of the following response. If you are '
163+ 'highly confident that the user approves of running the previous '
164+ 'action then respond with a single character "y".' ,
165+ ),
166+ gemini.Content .text (message),
167+ ])).candidates.single.content;
168+ final response = StringBuffer ();
169+ for (var part in sentimentResult.parts.whereType< gemini.TextPart > ()) {
170+ response.write (part.text.trim ());
171+ }
172+ return response.toString () == 'y' ;
152173 }
153174
154175 /// Connects us to a local [DashChatBotServer] .
0 commit comments