feat(agent): add voice input capability and standardize tool naming

- Add voice recording with transcribe endpoint (ASR) for multimodal input
- Android: add RECORD_AUDIO and INTERNET permissions
- Refactor tool naming: frontend tools use 'front.' prefix, backend tools use 'back.'
- Migrate calendar tools: create_calendar_event -> back.mutate/list/delete events
- Add calendar_event_list.v1 and calendar_operation.v1 UI card types
- Update all Flutter and Python tests to match new tool naming conventions
- Add record package dependency for voice recording
This commit is contained in:
zl-q
2026-03-09 00:10:09 +08:00
parent 6c83e35a69
commit 3ac09475ad
30 changed files with 1593 additions and 438 deletions
+38 -3
View File
@@ -194,7 +194,7 @@ void main() {
service.onEvent(
ToolCallStartEvent(
toolCallId: 'tc_1',
toolCallName: 'create_calendar_event',
toolCallName: 'back.mutate_calendar_event',
),
);
},
@@ -203,7 +203,7 @@ void main() {
(s) {
final item = s.items.first;
return item is ToolCallItem &&
item.toolName == 'create_calendar_event' &&
item.toolName == 'back.mutate_calendar_event' &&
item.status == ToolCallStatus.pending;
},
'has pending tool call',
@@ -220,7 +220,7 @@ void main() {
ToolCallItem(
id: 'tc_1',
callId: 'tc_1',
toolName: 'navigate_to_route',
toolName: 'front.navigate_to_route',
args: {'target': '/calendar/dayweek', '__nonce': 'nonce_1'},
status: ToolCallStatus.executing,
timestamp: DateTime.now(),
@@ -241,5 +241,40 @@ void main() {
isA<ChatState>().having((s) => s.items.isEmpty, 'items empty', true),
],
);
blocTest<ChatBloc, ChatState>(
'toolCallResult with ui in payload.result adds ToolResultItem',
build: () => chatBloc,
seed: () => ChatState(
items: [
ToolCallItem(
id: 'tc_2',
callId: 'tc_2',
toolName: 'back.mutate_calendar_event',
args: {'operation': 'create'},
status: ToolCallStatus.executing,
timestamp: DateTime.now(),
sender: MessageSender.ai,
),
],
),
act: (bloc) {
service.onEvent(
ToolCallResultEvent(
messageId: 'msg_tool_2',
toolCallId: 'tc_2',
content:
'{"result":{"type":"calendar_operation.v1","version":"v1","data":{"operation":"delete","ok":true,"message":"done"},"actions":[]}}',
),
);
},
expect: () => [
isA<ChatState>().having(
(s) => s.items.first is ToolResultItem,
'first item is ToolResultItem',
true,
),
],
);
});
}