Skip to content

Commit 372264c

Browse files
authored
analyze the sentiment of the response instead of just accepting y/n (#55)
When determining whether the user agreed to run the tool, we feed their response to gemini and ask it to respond with "y" if they agreed.
1 parent 3b43ed8 commit 372264c

File tree

1 file changed

+47
-26
lines changed

1 file changed

+47
-26
lines changed

pkgs/dart_mcp/example/dash_client.dart

Lines changed: 47 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)