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: api.openai.com 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: 'openai',
        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(
      'https://api.openai.com/v1/models',
      options: Options(headers: {'Authorization': 'Bearer $token'}),
    );

    final data = response.data as Map<String, dynamic>;
    final models = data['data'] as List<dynamic>;
    final result = <AiModel>[];
    for (final model in models) {
      final m = model as Map<String, dynamic>;
      final id = m['id'] as String;
      // Filter to chat-compatible models.
      if (!id.startsWith('gpt-') &&
          !id.startsWith('o1-') &&
          !id.startsWith('o3-') &&
          !id.startsWith('o4-') &&
          !id.startsWith('chatgpt-')) {
        continue;
      }
      // Skip snapshot/dated variants (e.g. gpt-4-0613).
      if (RegExp(r'-\d{4}$').hasMatch(id)) continue;
      result.add(AiModel(id: id, name: id));
    }
    result.sort((a, b) => a.id.compareTo(b.id));
    return result;
  } catch (e) {
    dev.log('Failed to list OpenAI models: $e', name: 'OpenAiDatasource');
    return _provider.availableModels
        .map((m) => AiModel(id: m.id, name: m.name))
        .toList();
  }
}