Skip to content

ApiProvider-Registrierung: einheitlicher Einstiegspunkt für Provider ​

源码版本v0.73.1

api-registry.ts ist das Zentrum der Provider-Registrierung (provider registry) — eine Map<string, RegisteredApiProvider>, deren Key der api-String ist (z. B. "anthropic-messages", "openai-responses"), und deren Wert ein um wrapStream / wrapStreamSimple gewickelter Provider ist. Alle Provider — die 9 eingebauten wie die dynamisch registrierten aus Erweiterungen — laufen über dieselbe Registrierung. resolveApiProvider in stream.ts ist einfach ein Lookup in dieser Tabelle.

Verantwortung ​

  1. Provider speichern: modulinterne Konstante apiProviderRegistry = new Map<string, RegisteredApiProvider>(), mit api-String als Key. Siehe packages/ai/src/api-registry.ts:35-40.
  2. Assertion-Wickel: wrapStream / wrapStreamSimple prüfen vor dem echten Stream-Aufruf, dass model.api === api, und werfen sonst Mismatched api. Siehe packages/ai/src/api-registry.ts:42-64.
  3. Registrieren/Deregistrieren: registerApiProvider schreibt in die Tabelle, unregisterApiProviders(sourceId) löscht pro source gebündelt, clearApiProviders leert alles. Siehe packages/ai/src/api-registry.ts:66-98.
  4. Lookup: getApiProvider(api) gibt ApiProviderInternal | undefined zurück, aufgerufen von stream.ts. Siehe packages/ai/src/api-registry.ts:80-82.

Entwurfsmotivation ​

Warum wrapStream um den Provider wickeln? Weil das Typsystem Fehler der Form „Aufrufer hat ein Model<"openai-responses">, reicht es aber an streamAnthropic" nicht abfängt — Model<TApi> in TS ist generisch; zur Laufzeit ist model.api die Wahrheit. wrapStream schließt beim registerApiProvider-Aufruf den api-Wert in die Assertion ein, sodass am Aufrufort nur ein model.api !== api-Check nötig ist; der Typfehler wird zu einem Laufzeitfehler und der Stacktrace zeigt direkt auf den falschen Aufruf.

Warum ein sourceId statt direktem delete? Erweiterungs-Provider können prozess- und restart-übergreifend sein; Erweiterungen dürfen nur ihre eigenen Registrierungen löschen. sourceId gibt unregisterApiProviders einen stabilen Filterkey, und eingebaute Provider werden ohne sourceId registriert, sodass sie nicht versehentlich gelöscht werden.

Wichtige Dateien ​

Die Assertion in wrapStream ist eine Zeile 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);
	};
}

Bei der Registrierung werden beide Funktionen gewrappt und der vollständige interne Provider gespeichert:

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,
});

Datenfluss ​

Provider-Registrierung und Lookup in zwei Abschnitten:

Grenzen und Fehler ​

  • api passt nicht: wrapStream prüft vor dem echten Stream-Aufruf; siehe packages/ai/src/api-registry.ts:47-49. Tritt auf, wenn der Aufrufer eine falsche Model-Instanz hat (z. B. OpenAI-Modell an Anthropic-Provider).
  • Doppelte Registrierung: apiProviderRegistry.set überschreibt direkt; der zuletzt Registrierte gewinnt. Erweiterungen können eingebaute Provider überschreiben.
  • Fehlender sourceId: unregisterApiProviders(sourceId) lässt eingebaute Provider ohne sourceId unangetastet; siehe packages/ai/src/api-registry.ts:89-92.
  • getApiProviders: gibt ein Array aller Provider zurück; verwendet, wenn die UI eine Liste „verfügbarer Provider" anzeigt.
  • Test-Reset: clearApiProviders gefolgt von registerBuiltInApiProviders(); siehe packages/ai/src/providers/register-builtins.ts:398-401.

Zusammenfassung ​

api-registry.ts ist eine Map plus zwei Wrap-Funktionen; sie machen aus einem Typ-Mismatch zur Laufzeit einen klaren Fehler statt eines Rätsels. Das Dreigestirn aus api + stream + streamSimple eines Providers ist in Provider-Abstraktion und Built-ins definiert; wie eine konkrete Familie stream implementiert, steht in Anthropic SSE-Implementierung. Die Verteillogik der Fassade steht in stream/complete-Fassade.