feat(chat): add ToolRegistry with validation
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
typedef ToolHandler =
|
||||
Future<Map<String, dynamic>> Function(Map<String, dynamic> args);
|
||||
|
||||
class ToolDefinition {
|
||||
final String name;
|
||||
final String description;
|
||||
final Map<String, dynamic> parameters;
|
||||
final ToolHandler handler;
|
||||
|
||||
ToolDefinition({
|
||||
required this.name,
|
||||
required this.description,
|
||||
required this.parameters,
|
||||
required this.handler,
|
||||
});
|
||||
}
|
||||
|
||||
class ToolRegistry {
|
||||
static final Map<String, ToolDefinition> _tools = {};
|
||||
static bool _initialized = false;
|
||||
|
||||
static void initialize() {
|
||||
if (_initialized) return;
|
||||
|
||||
_tools['create_calendar_event'] = ToolDefinition(
|
||||
name: 'create_calendar_event',
|
||||
description: '创建一个日历事件或待办事项',
|
||||
parameters: {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'title': {
|
||||
'type': 'string',
|
||||
'description': '事件标题',
|
||||
'minLength': 1,
|
||||
'maxLength': 100,
|
||||
},
|
||||
'description': {'type': 'string', 'description': '事件描述'},
|
||||
'startAt': {
|
||||
'type': 'string',
|
||||
'format': 'date-time',
|
||||
'description': '开始时间 (ISO8601)',
|
||||
},
|
||||
'endAt': {
|
||||
'type': 'string',
|
||||
'format': 'date-time',
|
||||
'description': '结束时间 (ISO8601)',
|
||||
},
|
||||
'timezone': {'type': 'string', 'default': 'Asia/Shanghai'},
|
||||
'location': {'type': 'string'},
|
||||
'notes': {'type': 'string'},
|
||||
},
|
||||
'required': ['title', 'startAt'],
|
||||
},
|
||||
handler: _handleCreateCalendarEvent,
|
||||
);
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>> _handleCreateCalendarEvent(
|
||||
Map<String, dynamic> args,
|
||||
) async {
|
||||
final eventId = 'evt_${DateTime.now().millisecondsSinceEpoch}';
|
||||
return {
|
||||
'eventId': eventId,
|
||||
'ok': true,
|
||||
'message': '日程已创建',
|
||||
'title': args['title'],
|
||||
'description': args['description'],
|
||||
'startAt': args['startAt'],
|
||||
'endAt': args['endAt'],
|
||||
'timezone': args['timezone'] ?? 'Asia/Shanghai',
|
||||
'location': args['location'],
|
||||
'color': '#4F46E5',
|
||||
'sourceType': 'agentGenerated',
|
||||
};
|
||||
}
|
||||
|
||||
static ToolDefinition? getTool(String name) => _tools[name];
|
||||
static List<ToolDefinition> getAllTools() => _tools.values.toList();
|
||||
|
||||
static Future<Map<String, dynamic>> execute(
|
||||
String toolName,
|
||||
Map<String, dynamic> args,
|
||||
) async {
|
||||
final tool = _tools[toolName];
|
||||
if (tool == null) throw ToolNotFoundException('Tool not found: $toolName');
|
||||
return tool.handler(args);
|
||||
}
|
||||
|
||||
static ToolValidationResult validateArgs(
|
||||
String toolName,
|
||||
Map<String, dynamic> args,
|
||||
) {
|
||||
final tool = _tools[toolName];
|
||||
if (tool == null)
|
||||
return ToolValidationResult(
|
||||
ok: false,
|
||||
error: 'Tool not found: $toolName',
|
||||
);
|
||||
|
||||
final required = tool.parameters['required'] as List<dynamic>? ?? [];
|
||||
final missing = <String>[];
|
||||
for (final field in required) {
|
||||
if (!args.containsKey(field) || args[field] == null)
|
||||
missing.add(field as String);
|
||||
}
|
||||
|
||||
if (missing.isNotEmpty) {
|
||||
return ToolValidationResult(
|
||||
ok: false,
|
||||
error: 'Missing required fields: ${missing.join(', ')}',
|
||||
);
|
||||
}
|
||||
return ToolValidationResult(ok: true);
|
||||
}
|
||||
}
|
||||
|
||||
class ToolNotFoundException implements Exception {
|
||||
final String message;
|
||||
ToolNotFoundException(this.message);
|
||||
@override
|
||||
String toString() => 'ToolNotFoundException: $message';
|
||||
}
|
||||
|
||||
class ToolValidationResult {
|
||||
final bool ok;
|
||||
final String? error;
|
||||
ToolValidationResult({required this.ok, this.error});
|
||||
}
|
||||
Reference in New Issue
Block a user