Ir al contenido

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.

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.

wu.ai.conversation.getHistory('analytics'); // mensajes, sin marcas de tiempo internas
wu.ai.conversation.inject('assistant', 'You already greeted the user.', { namespace: 'chat' });
wu.ai.conversation.clear('analytics'); // vaciarlo, conservarlo
wu.ai.conversation.clearAll();
wu.ai.conversation.getNamespaces();
wu.ai.conversation.deleteNamespace('analytics'); // aborta lo que esté en vuelo + elimina

inject() añade un mensaje sin enviar nada: útil para sembrar contexto o reproducir una sesión anterior.

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.

  1. Recoge los esquemas de tools de todas las acciones registradas.
  2. Envía el historial junto con las tools.
  3. Si no hay tool_calls en la respuesta → listo, devuelve.
  4. 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 como content.
  5. Ejecuta cada llamada a tool, añade un mensaje tool con 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.

wu.ai.abort('analytics'); // un namespace
wu.ai.abort(); // todo

Cada 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.

El prompt de sistema se resuelve en este orden:

  1. options.systemPrompt (cadena o función, con await).
  2. config.systemPrompt de init().
  3. 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.
  4. '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.