listModelsViaProxy function

Future<List<AiModel>> listModelsViaProxy({
  1. required Dio dio,
  2. required String provider,
  3. required String credential,
  4. AiProxyContextResolver contextResolver = defaultAiProxyContext,
})

Fetches the available model list for provider by POSTing to the ai-proxy Edge Function with action: 'list_models'.

Used from each provider's direct datasource (claude_datasource.dart etc.) on web, where calling api.anthropic.com / api.openai.com / the Gemini endpoint directly fails the browser's CORS preflight. Native platforms continue to call the provider API directly for lower latency.

Returns an empty list on any error (no session, HTTP failure, malformed payload) — callers fall back to the static provider-defined model list.

contextResolver is injectable for tests; in production the default reads the singleton Supabase session.

Implementation

Future<List<AiModel>> listModelsViaProxy({
  required Dio dio,
  required String provider,
  required String credential,
  AiProxyContextResolver contextResolver = defaultAiProxyContext,
}) async {
  final ctx = contextResolver();
  if (ctx == null) {
    dev.log(
      'listModelsViaProxy: no active session, skipping',
      name: 'listModelsViaProxy',
    );
    return const [];
  }

  try {
    final response = await dio.post<dynamic>(
      ctx.url,
      data: {
        'action': 'list_models',
        'provider': provider,
        'credential': credential,
      },
      options: Options(headers: ctx.headers, validateStatus: (_) => true),
    );

    final status = response.statusCode ?? 0;
    if (status < 200 || status >= 300) {
      dev.log('listModelsViaProxy: HTTP $status', name: 'listModelsViaProxy');
      return const [];
    }

    final data = response.data;
    if (data is! Map) return const [];
    final models = data['models'];
    if (models is! List) return const [];

    final result = <AiModel>[];
    for (final m in models) {
      if (m is Map && m['id'] is String) {
        result.add(
          AiModel(
            id: m['id'] as String,
            name: (m['name'] as String?) ?? m['id'] as String,
          ),
        );
      }
    }
    result.sort((a, b) => a.name.compareTo(b.name));
    return result;
  } catch (e) {
    dev.log('listModelsViaProxy error: $e', name: 'listModelsViaProxy');
    return const [];
  }
}