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:
@@ -0,0 +1,64 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:record/record.dart';
|
||||
|
||||
abstract class VoiceRecorder {
|
||||
Future<void> start();
|
||||
Future<String?> stop();
|
||||
Future<void> dispose();
|
||||
}
|
||||
|
||||
class RecordVoiceRecorder implements VoiceRecorder {
|
||||
final AudioRecorder _recorder;
|
||||
String? _currentPath;
|
||||
|
||||
RecordVoiceRecorder({AudioRecorder? recorder})
|
||||
: _recorder = recorder ?? AudioRecorder();
|
||||
|
||||
@override
|
||||
Future<void> start() async {
|
||||
bool hasPermission;
|
||||
try {
|
||||
hasPermission = await _recorder.hasPermission();
|
||||
} on MissingPluginException catch (_) {
|
||||
throw StateError('录音组件未加载,请完全重启 App 后重试');
|
||||
}
|
||||
if (!hasPermission) {
|
||||
throw StateError('录音权限未授权');
|
||||
}
|
||||
|
||||
final fileName =
|
||||
'voice_${DateTime.now().millisecondsSinceEpoch}_${DateTime.now().microsecond}.wav';
|
||||
final path = '${Directory.systemTemp.path}/$fileName';
|
||||
_currentPath = path;
|
||||
try {
|
||||
await _recorder.start(
|
||||
const RecordConfig(
|
||||
encoder: AudioEncoder.wav,
|
||||
sampleRate: 16000,
|
||||
numChannels: 1,
|
||||
),
|
||||
path: path,
|
||||
);
|
||||
} on MissingPluginException catch (_) {
|
||||
throw StateError('录音组件未加载,请完全重启 App 后重试');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String?> stop() async {
|
||||
String? stoppedPath;
|
||||
try {
|
||||
stoppedPath = await _recorder.stop();
|
||||
} on MissingPluginException catch (_) {
|
||||
throw StateError('录音组件未加载,请完全重启 App 后重试');
|
||||
}
|
||||
return stoppedPath ?? _currentPath;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> dispose() async {
|
||||
await _recorder.dispose();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user