Skip to content

Registre ApiProvider : point d'entrée unifié des provider

源码版本v0.73.1

api-registry.ts est le cœur du registre des provider (registry)—une Map<string, RegisteredApiProvider>, dont la clé est la chaîne api (par ex. "anthropic-messages", "openai-responses") et la valeur un provider enveloppé de wrapStream / wrapStreamSimple. Tous les provider—les 9 intégrés, comme ceux enregistrés dynamiquement par des extensions—passent par le même registre. Le resolveApiProvider de stream.ts ne fait que consulter cette table.

Responsabilités

  1. Stocker les provider : constante de module apiProviderRegistry = new Map<string, RegisteredApiProvider>(), avec la chaîne api comme clé. Voir packages/ai/src/api-registry.ts:35-40.
  2. Envelopper d'une assertion : wrapStream / wrapStreamSimple assertent model.api === api avant d'appeler le vrai stream, sinon jettent Mismatched api. Voir packages/ai/src/api-registry.ts:42-64.
  3. Enregistrer/désenregistrer : registerApiProvider écrit dans la table, unregisterApiProviders(sourceId) supprime par batch selon la source, clearApiProviders vide tout. Voir packages/ai/src/api-registry.ts:66-98.
  4. Consulter la table : getApiProvider(api) renvoie ApiProviderInternal | undefined, appelé par stream.ts. Voir packages/ai/src/api-registry.ts:80-82.

Motivation de design

Pourquoi envelopper le provider d'un wrapStream ? Parce que le système de types ne peut pas attraper « l'appelant a un Model<"openai-responses"> mais le passe à streamAnthropic »—le Model<TApi> de TS est générique, à l'exécution model.api seule détient la vérité. wrapStream ferme par closure l'api du provider au moment du registerApiProvider, au point d'appel il n'y a qu'une vérification model.api !== api, qui transforme l'erreur de type en erreur runtime, et la pile pointe directement vers l'appel discordant.

Pourquoi utiliser un sourceId plutôt qu'un delete direct ? Les provider enregistrés par une extension peuvent vivre au-delà du processus et des redémarrages; on ne permet à une extension de nettoyer que ce qu'elle a enregistré. sourceId donne à unregisterApiProviders une clé de filtrage stable; les provider intégrés ne portent pas de sourceId et ne sont pas supprimés par erreur.

Fichiers clés

L'assertion de wrapStream se résume à une ligne model.api !== api :

typescript
// packages/ai/src/api-registry.ts:42-52
function wrapStream<TApi extends Api, TOptions extends StreamOptions>(
	api: TApi,
	stream: StreamFunction<TApi, TOptions>,
): ApiStreamFunction {
	return (model, context, options) => {
		if (model.api !== api) {
			throw new Error(`Mismatched api: ${model.api} expected ${api}`);
		}
		return stream(model as Model<TApi>, context, options as TOptions);
	};
}

À l'enregistrement, on wrap les deux fonctions et on stocke le internal provider complet :

typescript
// packages/ai/src/api-registry.ts:70-77
apiProviderRegistry.set(provider.api, {
	provider: {
		api: provider.api,
		stream: wrapStream(provider.api, provider.stream),
		streamSimple: wrapStreamSimple(provider.api, provider.streamSimple),
	},
	sourceId,
});

Flux de données

Enregistrement de provider et consultation, en deux temps :

Frontières et échecs

  • api discordant : wrapStream asserte avant d'appeler le vrai stream, voir packages/ai/src/api-registry.ts:47-49. Arrive typiquement quand l'appelant a une mauvaise instance Model (par ex. un modèle OpenAI passé à un provider Anthropic).
  • Enregistrement en doublon : apiProviderRegistry.set écrase, le dernier à s'enregistrer gagne. Une extension peut écraser un provider intégré.
  • Sans sourceId : unregisterApiProviders(sourceId) ne touche pas aux provider intégrés sans sourceId, voir packages/ai/src/api-registry.ts:89-92.
  • getApiProviders : renvoie le tableau de tous les provider, utilisé par l'UI pour lister les « provider disponibles ».
  • Reset de test : clearApiProviders puis registerBuiltInApiProviders(), voir packages/ai/src/providers/register-builtins.ts:398-401.

Récapitulatif

api-registry.ts est une Map + deux fonctions wrap, qui transforment une non-correspondance de type d'énigme runtime en erreur explicite. Le triplet api + stream + streamSimple du provider est défini dans Abstraction provider et intégrations; l'implémentation concrète du stream d'un provider donné se lit dans Implémentation Anthropic SSE. La logique de dispatch de la façade de streaming se lit dans Façade stream/complete.