Conversaciones
Cada mensaje pasa por un namespace: un historial de mensajes aislado. Un widget de chat, un trigger en segundo plano y un panel de administración pueden hablar con el mismo modelo sin ver nunca los turnos de los demás.
const { content } = await wu.ai.send('What is in my cart?');const analytics = await wu.ai.send('Explain this spike', { namespace: 'analytics' });function send(message: string, options?: { namespace?: string; systemPrompt?: string | (() => string | Promise<string>); templateVars?: object; temperature?: number; maxTokens?: number; provider?: string; responseFormat?: 'json' | { type: 'json_schema'; schema: object; name?: string }; signal?: AbortSignal;}): Promise<{ content: string; tool_results?: Array<{ tool: string; params: object; result: any; success: boolean }>; usage?: { prompt_tokens: number; completion_tokens: number }; namespace: string;}>;send() ejecuta internamente todo el bucle de tool calling: envía, ejecuta las tools que pidió el modelo, devuelve los resultados y repite hasta que el modelo responde sin llamar a ninguna tool.
Una petición bloqueada no lanza. Se resuelve con content: '[blocked] <reason>': un límite de peticiones, un circuit breaker abierto o la protección de bucles. Una abortada se resuelve con content: '[aborted]'. Solo lanzan los errores reales de transporte o de proveedor.
stream()
Sección titulada «stream()»for await (const chunk of wu.ai.stream('Tell me about this page')) { switch (chunk.type) { case 'text': output.textContent += chunk.content; break; case 'tool_result': renderTool(chunk.tool, chunk.result); break; case 'usage': showTokens(chunk.usage); break; case 'error': showError(chunk.error); break; case 'done': finish(); break; }}| Tipo de chunk | Contenido |
|---|---|
text |
content — un delta de texto |
tool_result |
tool, result, success |
tool_calls_done |
count — terminó la ronda, el bucle continúa |
usage |
usage |
error |
error |
done |
— terminal |
stream() refleja el bucle de tools de send(): acumula los deltas de las llamadas a tools, las ejecuta, emite los resultados y vuelve a hacer streaming con esos resultados en el historial.
function json(message: string, options?: SendOptions & { schema?: object; schemaName?: string; // 'response' por defecto}): Promise<{ data: object | null; raw: string; error?: string; usage?: object; namespace: string }>;const { data, error } = await wu.ai.json('List 5 colors', { schema: { type: 'object', properties: { colors: { type: 'array', items: { type: 'string' } } } },});data es null y error se rellena cuando el modelo devolvió algo que no es JSON. Consulta Proveedores para ver cómo implementa cada backend el modo JSON: el de Anthropic es un truco de prompt, no un modo nativo.
Namespaces
Sección titulada «Namespaces»wu.ai.conversation.getHistory('analytics'); // mensajes, sin marcas de tiempo internaswu.ai.conversation.inject('assistant', 'You already greeted the user.', { namespace: 'chat' });wu.ai.conversation.clear('analytics'); // vaciarlo, conservarlowu.ai.conversation.clearAll();wu.ai.conversation.getNamespaces();wu.ai.conversation.deleteNamespace('analytics'); // aborta lo que esté en vuelo + eliminainject() añade un mensaje sin enviar nada: útil para sembrar contexto o reproducir una sesión anterior.
Configuración
Sección titulada «Configuración»wu.ai.init({ conversation: { maxHistoryMessages: 30, maxToolRounds: 3 } });| Opción | Por defecto | Significado |
|---|---|---|
maxHistoryMessages |
50 |
Por namespace. Los mensajes de sistema siempre se conservan; sobreviven los N mensajes no-sistema más recientes |
maxToolRounds |
5 |
Rondas de ejecución de tools por llamada antes de que wu se rinda |
defaultNamespace |
'default' |
|
systemPrompt |
null |
Cadena o función; se puede sobrescribir por llamada |
temperature |
undefined |
Valor por defecto del proveedor |
maxTokens |
undefined |
Valor por defecto del proveedor |
namespaceTTL |
30 * 60_000 (30 min) |
Inactividad antes de recoger un namespace. 0 lo desactiva |
gcInterval |
5 * 60_000 (5 min) |
Cada cuánto puede ejecutarse el barrido |
El truncado tiene cuidado con los pares de tools: tras el recorte, se eliminan también los mensajes tool iniciales cuyo turno assistant con la llamada se descartó, de forma que el proveedor nunca ve un resultado de tool huérfano.
El barrido del GC es perezoso: se ejecuta al acceder a un namespace, como mucho una vez por gcInterval. Nunca elimina el namespace por defecto ni uno con una petición en vuelo.
El bucle de tool calling
Sección titulada «El bucle de tool calling»- Recoge los esquemas de tools de todas las acciones registradas.
- Envía el historial junto con las tools.
- Si no hay
tool_callsen la respuesta → listo, devuelve. - Si los hay, incrementa el contador de rondas. Pasado
maxToolRounds, wu añade[wu-ai] Tool call loop limit (N) reached…como mensaje del assistant y lo devuelve comocontent. - Ejecuta cada llamada a tool, añade un mensaje
toolcon el resultado en JSON (o{ error }), y vuelve al bucle.
Alcanzar el límite de rondas de tools no es una excepción: lo recibes como texto. Compruébalo si te importa.
Abortar
Sección titulada «Abortar»wu.ai.abort('analytics'); // un namespacewu.ai.abort(); // todoCada namespace mantiene un AbortController; iniciar una petición nueva cancela la anterior de ese namespace. Un options.signal externo se combina con el interno mediante AbortSignal.any donde está disponible, con un fallback basado en listeners que también gestiona una señal ya abortada.
Prompt de sistema y contexto
Sección titulada «Prompt de sistema y contexto»El prompt de sistema se resuelve en este orden:
options.systemPrompt(cadena o función, con await).config.systemPromptdeinit().- Generado automáticamente por el módulo de contexto: recoge una instantánea (claves del store, eventos recientes, apps montadas, tus colectores personalizados) y la renderiza junto con la lista de tools.
'You are an AI assistant connected to a web application via Wu Framework.'
Se reconstruye en cada llamada y reemplaza el mensaje de sistema existente del namespace, de modo que el modelo siempre ve el estado actual.
Configura o amplía el contexto recolectado:
wu.ai.context.configure({ budget: 2000, sources: { store: { enabled: true } } });
wu.ai.context.register('dashboard', { collector: () => ({ visibleKpis: currentKpis() }), priority: 'high',});
const snapshot = wu.ai.context.getSnapshot();Los valores inyectados en los prompts pasan por un sanitizador: las claves de aspecto sensible (password, token, apiKey, secret, credential, authorization, cookie, session) se reemplazan por [REDACTED], los valores se truncan y el contenido no confiable se envuelve en etiquetas <user_data>. Reduce la superficie de inyección de prompts; no la elimina.
templateVars interpola los huecos {{var}} y {{a.b.c}} de tu mensaje, fusionados sobre las propias variables de interpolación del contexto.
