Skip to content

stream/complete : façade d'entrée des appels LLM

源码版本v0.73.1

stream.ts est la couche la plus fine côté exposition de @mariozechner/pi-ai—quatre fonctions stream / complete / streamSimple / completeSimple, plus un resolveApiProvider interne. Elle ne fait pas d'analyse SSE, ne construit pas le corps de requête, ne gère pas les retries; elle route juste le triplet (model, context, options) vers le provider correspondant selon model.api. Tu peux la voir comme « le bureau de dispatch entre l'appelant et l'implémentation provider » : l'appelant ne voit qu'un type de retour unifié AssistantMessageEventStream, sans savoir si en dessous c'est du SSE Anthropic, des OpenAI Responses ou du Bedrock Converse.

Responsabilités

Cette couche fait quatre choses :

  1. Dispatch : stream trouve le provider dans le registre (registry) selon model.api et transfère (model, context, options). Voir packages/ai/src/stream.ts:25-32.
  2. Retour synchrone du flux : même si le provider sous-jacent s'initialise en async (lazy loading), stream retourne synchroniquement un AssistantMessageEventStream, et les événements sont pushés quand ils arrivent. Voir packages/ai/src/stream.ts:30-31.
  3. Entrée simplifiée : streamSimple / completeSimple prennent un SimpleStreamOptions (qui n'expose que reasoning, thinkingBudgets, etc., champs habituels côté UI), le provider traduit lui-même les simple options en options complètes. Voir packages/ai/src/stream.ts:43-50.
  4. Erreur si provider absent : resolveApiProvider jette No API provider registered for api: <api> si rien n'est enregistré, pas d'échec silencieux au point d'appel. Voir packages/ai/src/stream.ts:17-23.

Motivation de design

Pourquoi une couche aussi fine ? Parce que les deux choses qui intéressent l'appelant—« quel modèle appeler » et « streaming ou résultat final »—sont découplées du provider concret. stream.ts réduit ces deux dimensions à quatre fonctions; l'AgentSession de pi-coding-agent, les outils CLI et les extensions utilisent toutes la même entrée, sans avoir à importer directement le module provider. Ainsi l'implémentation provider peut être lazy-loadée, remplacée, ou étendue par de nouveaux api enregistrés par les extensions—le code appelant reste intact.

complete ne réécrit pas une logique non-streaming; il réutilise stream puis s.result()—l'infrastructure de streaming et le retour one-shot partagent le même pipeline, le provider n'a qu'à implémenter la version streaming.

Fichiers clés

Toute l'implémentation de stream tient en resolve + transfert, sans aucune logique métier :

typescript
// packages/ai/src/stream.ts:25-32
export function stream<TApi extends Api>(
	model: Model<TApi>,
	context: Context,
	options?: ProviderStreamOptions,
): AssistantMessageEventStream {
	const provider = resolveApiProvider(model.api);
	return provider.stream(model, context, options as StreamOptions);
}

complete réutilise stream puis prend result(), pas de duplication de la logique de dispatch :

typescript
// packages/ai/src/stream.ts:34-41
export async function complete<TApi extends Api>(
	model: Model<TApi>,
	context: Context,
	options?: ProviderStreamOptions,
): Promise<AssistantMessage> {
	const s = stream(model, context, options);
	return s.result();
}

Flux de données

La chaîne de dispatch de stream(model, ctx, opts) :

Frontières et échecs

  • Provider manquant : resolveApiProvider jette directement, ne renvoie pas de flux vide, voir packages/ai/src/stream.ts:19-21. Quand un provider enregistré dynamiquement par une extension n'est pas enregistré, l'appelant le sait immédiatement.
  • Provider lazy-loadé : le module provider concret est enveloppé par createLazyStream, stream retourne un outer stream synchro, puis une fois le module chargé, forwardStream vers l'inner, voir packages/ai/src/providers/register-builtins.ts:159-178. stream lui-même ne sait pas qu'il y a du lazy.
  • Assertion de type : options as StreamOptions caste ProviderStreamOptions (avec champs customs d'extension) vers le type attendu par le provider, qui lit les champs dont il a besoin.
  • options optionnel : les trois fonctions acceptent options? absent, le provider se rabat sur des valeurs par défaut.

Récapitulatif

stream.ts est une façade de dispatch de 60 lignes qui route (model, context, options) vers le provider du registre. stream / complete sont les versions toutes options, streamSimple / completeSimple sont les versions sous-ensemble adaptées à l'UI. La structure du registre (registry) elle-même se lit dans Registre des provider, l'enregistrement des 9 provider intégrés dans Abstraction provider et intégrations, et le AssistantMessageEventStream retourné dans Itérateur async EventStream.