buildProviderQueueChain function

List<ChainEntry> buildProviderQueueChain(
  1. List<ProviderQueueEntry> entries, {
  2. required Map<String, String> secrets,
  3. StreamFunction streamFactory(
    1. String kind,
    2. String apiKey
    )?,
  4. List<String>? skipped,
})

Builds the ChainEntry list for a resolved queue: one model per entry (catalog defaults + the entry's overrides), a single key stack per entry from its apiKeyEnv (the _2/_3 stack convention rides along free), and the catalog stream factory bound per key.

Entries with no configured key are SKIPPED (the roles precedent) and reported in skipped; a queue that skips everything throws ConfigException.

Implementation

List<ChainEntry> buildProviderQueueChain(
  List<ProviderQueueEntry> entries, {
  required Map<String, String> secrets,
  StreamFunction Function(String kind, String apiKey)? streamFactory,
  List<String>? skipped,
}) {
  final skipped_ = skipped ?? <String>[];
  final factory = streamFactory ?? providerStreamFunction;
  final chain = <ChainEntry>[];
  for (final entry in entries) {
    final keyEnv = entry.apiKeyEnv;
    if (keyEnv == null) {
      throw ConfigException(
        'queue entry ${entry.label} has no apiKeyEnv — keys never ride in '
        'the queue blob; name the env var holding the key',
      );
    }
    final stack = collectKeyStack(secrets, keyEnv);
    if (stack.isEmpty) {
      skipped_.add('${entry.label} (missing API key: set $keyEnv)');
      continue;
    }
    final ring = ApiKeyRing(baseName: keyEnv, credentials: stack);
    final spec = catalogProvider(
      queueKindCatalogName(entry.providerType, entry.baseUrl),
    )!;
    final inner = factory(spec.kind, stack.first.value);
    chain.add(
      ChainEntry(
        model: buildCatalogModel(
          spec.name,
          entry.model,
          baseUrl: entry.baseUrl,
          contextWindow: entry.contextWindow,
          maxTokens: entry.maxTokens,
        ),
        keyRing: ring,
        streamForKey: (apiKey) =>
            inner, // single entry-bound adapter; the key is baked in
      ),
    );
  }
  if (chain.isEmpty) {
    throw ConfigException(
      'the provider queue has no usable entry: ${skipped_.join('; ')}',
    );
  }
  return chain;
}