listModels method

  1. @override
Future<List<AiModel>> listModels()
override

Fetch available models from the provider's API.

Implementation

@override
Future<List<AiModel>> listModels() async {
  try {
    final token = await _auth.getAccessToken();

    // Web: the Gemini REST endpoint blocks browser requests at the CORS
    // preflight. Route through the ai-proxy Edge Function instead.
    if (kIsWeb) {
      if (token.isEmpty) return const [];
      final models = await listModelsViaProxy(
        dio: _dio,
        provider: 'gemini',
        credential: token,
      );
      if (models.isNotEmpty) return models;
      return _provider.availableModels
          .map((m) => AiModel(id: m.id, name: m.name))
          .toList();
    }

    final response = await _dio.get(
      '${_provider.apiUrl}/models',
      options: Options(headers: {'Authorization': 'Bearer $token'}),
    );

    final data = response.data as Map<String, dynamic>;
    final models = data['models'] as List<dynamic>;
    final result = <AiModel>[];
    for (final model in models) {
      final m = model as Map<String, dynamic>;
      final fullName = m['name'] as String; // e.g. "models/gemini-2.5-pro"
      final id = fullName.replaceFirst('models/', '');
      final displayName = m['displayName'] as String? ?? id;
      // Only include models that support generateContent.
      final methods =
          (m['supportedGenerationMethods'] as List<dynamic>?)
              ?.cast<String>() ??
          [];
      if (!methods.contains('generateContent')) continue;
      result.add(AiModel(id: id, name: displayName));
    }
    result.sort((a, b) => a.name.compareTo(b.name));
    return result;
  } catch (e) {
    dev.log('Failed to list Gemini models: $e', name: 'GeminiDatasource');
    return _provider.availableModels
        .map((m) => AiModel(id: m.id, name: m.name))
        .toList();
  }
}