Skip to content

Registro ApiProvider: punto único de incorporación de providers ​

源码版本v0.73.1

api-registry.ts es el centro del registro de providers: un Map<string, RegisteredApiProvider> con key el string api (como "anthropic-messages" o "openai-responses"), y como valor un provider envuelto con wrapStream / wrapStreamSimple. Todos los providers —los 9 integrados, los registrados dinámicamente por extensiones— pasan por el mismo registro. El resolveApiProvider de stream.ts simplemente consulta esta tabla.

Responsabilidades ​

  1. Almacenar providers: la constante de módulo apiProviderRegistry = new Map<string, RegisteredApiProvider>(), con key el string api. Ver packages/ai/src/api-registry.ts:35-40.
  2. Envolver con aserción: wrapStream / wrapStreamSimple afirman model.api === api antes de llamar al stream real; si no coincide, lanzan Mismatched api. Ver packages/ai/src/api-registry.ts:42-64.
  3. Registrar/desregistrar: registerApiProvider escribe en la tabla, unregisterApiProviders(sourceId) borra en lote por source, clearApiProviders la vacía. Ver packages/ai/src/api-registry.ts:66-98.
  4. Consultar la tabla: getApiProvider(api) devuelve ApiProviderInternal | undefined; stream.ts lo invoca. Ver packages/ai/src/api-registry.ts:80-82.

Motivación de diseño ​

¿Por qué envolver al provider con un wrapStream? Porque el sistema de tipos no puede garantizar "el llamador tiene un Model<"openai-responses"> pero lo pasa a streamAnthropic": el Model<TApi> de TS es genérico, y en runtime model.api es la verdad. wrapStream captura el api del provider al registrarlo y lo cierra en la aserción; el punto de llamada hace un único chequeo model.api !== api, convirtiendo un error de tipos en un error de runtime con stack apuntando a la invocación equivocada.

¿Por qué usar sourceId en vez de un delete directo? Los providers registrados por extensiones pueden vivir entre procesos y reinicios; una extensión sólo debería poder limpiar los que ella registró. sourceId da a unregisterApiProviders una clave estable de filtrado. Los providers integrados no llevan sourceId al registrarse, así no se borran por accidente.

Archivos clave ​

La aserción de wrapStream es una sola línea, 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);
	};
}

Al registrar se envuelven ambas funciones y se almacena el provider interno completo:

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

Flujo de datos ​

El registro y la consulta son dos segmentos:

Límites y fallos ​

  • api no coincide: wrapStream afirma antes de llamar al stream real. Ver packages/ai/src/api-registry.ts:47-49. Suele ocurrir cuando el llamador tiene una instancia de Model equivocada (pasa un modelo OpenAI a un provider Anthropic).
  • Registro duplicado: apiProviderRegistry.set sobrescribe; gana el último. Las extensiones pueden sobrescribir providers integrados.
  • Sin sourceId: unregisterApiProviders(sourceId) no toca los providers integrados sin sourceId. Ver packages/ai/src/api-registry.ts:89-92.
  • getApiProviders: devuelve el array de todos los providers, útil cuando la UI lista "providers disponibles".
  • Reset de tests: clearApiProviders seguido de registerBuiltInApiProviders(). Ver packages/ai/src/providers/register-builtins.ts:398-401.

Resumen ​

api-registry.ts es un Map + dos funciones de wrap, que convierte un desajuste de tipos de misterio en runtime a un error explícito. El tríptico api + stream + streamSimple del provider se define en abstracción de provider y built-ins; cómo implementa stream un provider concreto en implementación SSE de Anthropic. La lógica de despacho de la fachada en fachada stream/complete.