@north-light/crouter 0.3.342 → 0.3.343

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -435,7 +435,7 @@ async function streamProviderAttempt(state, providerId, model, context, options,
435
435
  },
436
436
  };
437
437
  const provider = state.runtimeContext?.modelRegistry.getProvider?.(model.provider);
438
- const inner = credential
438
+ const inner = isManagedProvider(providerId)
439
439
  ? streamForProvider(model, context, streamOptions)
440
440
  : state.delegatedProviderStreams.get(providerId)?.(model, context, streamOptions)
441
441
  ?? provider?.streamSimple(model, context, streamOptions)
@@ -598,7 +598,14 @@ async function runManagedProvider(state, model, context, options, stream) {
598
598
  const isLiveRoute = (candidate) => candidate.providerId === model.provider && candidate.model.id === model.id && candidate.thinkingLevel === liveThinking;
599
599
  // A cooling or unauthenticated managed route contributes its diagnostic and may
600
600
  // later re-enter when a credential becomes ready, but it is never selected now.
601
- const candidates = routeCandidates.filter((candidate) => isLiveRoute(candidate) || !isManagedProvider(candidate.providerId) || readSubscriptionPool(candidate.providerId).some((entry) => entry.authFailure === undefined && (entry.rateLimitedUntil || 0) <= Date.now()));
601
+ const candidates = routeCandidates.filter((candidate) => {
602
+ if (isLiveRoute(candidate) || !isManagedProvider(candidate.providerId))
603
+ return true;
604
+ const pool = readSubscriptionPool(candidate.providerId);
605
+ return pool.length === 0
606
+ ? state.runtimeContext.modelRegistry.hasConfiguredAuth(candidate.model)
607
+ : pool.some((entry) => entry.authFailure === undefined && (entry.rateLimitedUntil || 0) <= Date.now());
608
+ });
602
609
  if (!candidates.some(isLiveRoute)) {
603
610
  candidates.unshift({ routeId: `live:${model.provider}/${model.id}`, credentialSource: 'live', rank: liveRank, providerId: model.provider, model, thinkingLevel: liveThinking });
604
611
  }
@@ -625,8 +632,25 @@ async function runManagedProvider(state, model, context, options, stream) {
625
632
  emitTerminalError(stream, candidateModel, `Provider route ${routeId} is unavailable: failed to select ${providerId}/${candidateModel.id}`);
626
633
  return;
627
634
  }
628
- if (!isManagedProvider(providerId)) {
629
- const result = await streamProviderAttempt(state, providerId, candidateModel, context, options, undefined, stream);
635
+ const rawPool = isManagedProvider(providerId) ? readSubscriptionPool(providerId) : [];
636
+ if (!isManagedProvider(providerId) || rawPool.length === 0) {
637
+ const liveKey = isLiveRoute(candidate) ? options?.apiKey : undefined;
638
+ if (isManagedProvider(providerId) && !liveKey && !state.runtimeContext.modelRegistry.hasConfiguredAuth(candidateModel))
639
+ continue;
640
+ const auth = isManagedProvider(providerId) && !liveKey
641
+ ? await state.runtimeContext.modelRegistry.getApiKeyAndHeaders(candidateModel)
642
+ : undefined;
643
+ if (isManagedProvider(providerId) && !liveKey && (!auth?.ok || !auth.apiKey))
644
+ continue;
645
+ const headers = { ...options?.headers };
646
+ for (const [name, value] of Object.entries(auth?.ok ? auth.headers ?? {} : {})) {
647
+ for (const existing of Object.keys(headers))
648
+ if (existing.toLowerCase() === name.toLowerCase())
649
+ delete headers[existing];
650
+ headers[name] = value;
651
+ }
652
+ const attemptOptions = auth?.ok ? { ...options, apiKey: auth.apiKey, headers } : options;
653
+ const result = await streamProviderAttempt(state, providerId, candidateModel, context, attemptOptions, undefined, stream);
630
654
  if (result.result === 'success' || result.result === 'fatal') {
631
655
  state.transportOnlyExhaustedPasses = 0;
632
656
  stream.end();
@@ -641,7 +665,6 @@ async function runManagedProvider(state, model, context, options, stream) {
641
665
  logRouteQueueAdvance(candidate, candidates[0], result);
642
666
  continue;
643
667
  }
644
- const rawPool = readSubscriptionPool(providerId);
645
668
  for (const entry of rawPool)
646
669
  if (entry.authFailure === 'invalid_grant')
647
670
  authFailures.add(`${providerId}/${entry.label}`);
@@ -600,7 +600,7 @@ async function streamProviderAttempt(
600
600
  },
601
601
  };
602
602
  const provider = state.runtimeContext?.modelRegistry.getProvider?.(model.provider);
603
- const inner = credential
603
+ const inner = isManagedProvider(providerId)
604
604
  ? streamForProvider(model, context, streamOptions)
605
605
  : state.delegatedProviderStreams.get(providerId)?.(model, context, streamOptions)
606
606
  ?? provider?.streamSimple(model, context, streamOptions)
@@ -802,7 +802,13 @@ async function runManagedProvider(
802
802
  candidate.providerId === model.provider && candidate.model.id === model.id && candidate.thinkingLevel === liveThinking;
803
803
  // A cooling or unauthenticated managed route contributes its diagnostic and may
804
804
  // later re-enter when a credential becomes ready, but it is never selected now.
805
- const candidates = routeCandidates.filter((candidate) => isLiveRoute(candidate) || !isManagedProvider(candidate.providerId) || readSubscriptionPool(candidate.providerId).some((entry) => entry.authFailure === undefined && (entry.rateLimitedUntil || 0) <= Date.now()));
805
+ const candidates = routeCandidates.filter((candidate) => {
806
+ if (isLiveRoute(candidate) || !isManagedProvider(candidate.providerId)) return true;
807
+ const pool = readSubscriptionPool(candidate.providerId);
808
+ return pool.length === 0
809
+ ? state.runtimeContext!.modelRegistry.hasConfiguredAuth(candidate.model)
810
+ : pool.some((entry) => entry.authFailure === undefined && (entry.rateLimitedUntil || 0) <= Date.now());
811
+ });
806
812
  if (!candidates.some(isLiveRoute)) {
807
813
  candidates.unshift({ routeId: `live:${model.provider}/${model.id}`, credentialSource: 'live', rank: liveRank, providerId: model.provider, model, thinkingLevel: liveThinking });
808
814
  }
@@ -833,8 +839,21 @@ async function runManagedProvider(
833
839
  return;
834
840
  }
835
841
 
836
- if (!isManagedProvider(providerId)) {
837
- const result = await streamProviderAttempt(state, providerId, candidateModel, context, options, undefined, stream);
842
+ const rawPool = isManagedProvider(providerId) ? readSubscriptionPool(providerId) : [];
843
+ if (!isManagedProvider(providerId) || rawPool.length === 0) {
844
+ const liveKey = isLiveRoute(candidate) ? options?.apiKey : undefined;
845
+ if (isManagedProvider(providerId) && !liveKey && !state.runtimeContext!.modelRegistry.hasConfiguredAuth(candidateModel)) continue;
846
+ const auth = isManagedProvider(providerId) && !liveKey
847
+ ? await state.runtimeContext!.modelRegistry.getApiKeyAndHeaders(candidateModel)
848
+ : undefined;
849
+ if (isManagedProvider(providerId) && !liveKey && (!auth?.ok || !auth.apiKey)) continue;
850
+ const headers = { ...options?.headers };
851
+ for (const [name, value] of Object.entries(auth?.ok ? auth.headers ?? {} : {})) {
852
+ for (const existing of Object.keys(headers)) if (existing.toLowerCase() === name.toLowerCase()) delete headers[existing];
853
+ headers[name] = value;
854
+ }
855
+ const attemptOptions = auth?.ok ? { ...options, apiKey: auth.apiKey, headers } : options;
856
+ const result = await streamProviderAttempt(state, providerId, candidateModel, context, attemptOptions, undefined, stream);
838
857
  if (result.result === 'success' || result.result === 'fatal') { state.transportOnlyExhaustedPasses = 0; stream.end(); return; }
839
858
  if (result.result === 'rate_limited' || result.classification?.status !== undefined || result.classification?.hadRetryAfterHeader === true) sawHttpEvidence = true;
840
859
  const cooldownMs = result.result === 'rate_limited' ? result.retryAfterMs ?? DEFAULT_RATE_LIMIT_BACKOFF_MS : TRANSIENT_ROTATE_COOLDOWN_MS;
@@ -843,8 +862,6 @@ async function runManagedProvider(
843
862
  logRouteQueueAdvance(candidate, candidates[0], result);
844
863
  continue;
845
864
  }
846
-
847
- const rawPool = readSubscriptionPool(providerId);
848
865
  for (const entry of rawPool) if (entry.authFailure === 'invalid_grant') authFailures.add(`${providerId}/${entry.label}`);
849
866
  const available = rawPool.filter((entry) => entry.authFailure === undefined && (!entry.rateLimitedUntil || entry.rateLimitedUntil <= Date.now()));
850
867
  const seenLabels = seenReadyLabels.get(routeId) ?? new Set<string>();