LLM
El modulo wippy/llm proporciona una interfaz unificada para trabajar con Modelos de Lenguaje Grande de multiples proveedores (OpenAI, Anthropic, Google, modelos locales). Soporta generacion de texto, llamadas a herramientas, salida estructurada, embeddings y streaming.
Configuracion
Sección titulada «Configuracion»Agrega el modulo a tu proyecto:
wippy add wippy/llmwippy installDeclara la dependencia en tu _index.yaml. El modulo LLM requiere un almacenamiento de entorno (para claves API) y un host de procesos:
version: "1.0"namespace: app
entries: - name: os_env kind: env.storage.os
- name: processes kind: process.host lifecycle: auto_start: true
- name: dep.llm kind: ns.dependency component: wippy/llm version: "*" parameters: - name: env_storage value: app:os_env - name: process_host value: app:processesLa entrada env.storage.os expone las variables de entorno del sistema operativo a los proveedores LLM. Configura tus claves API como variables de entorno (por ejemplo, OPENAI_API_KEY, ANTHROPIC_API_KEY).
Generacion de Texto
Sección titulada «Generacion de Texto»Importa la biblioteca llm en tu entrada y llama a generate():
entries: - name: ask kind: function.lua source: file://ask.lua method: handler imports: llm: wippy.llm:llmlocal llm = require("llm")
local function handler() local response, err = llm.generate("What are the three laws of robotics?", { model = "gpt-4o" })
if err then return nil, err end
return response.resultend
return { handler = handler }El primer argumento de generate() puede ser un prompt de texto, un constructor de prompts o una tabla de mensajes. El segundo argumento es una tabla de opciones.
Opciones de Generate
Sección titulada «Opciones de Generate»| Opcion | Tipo | Descripcion |
|---|---|---|
model | string | Nombre o clase del modelo (requerido) |
temperature | number | Control de aleatoriedad, 0-1 |
max_tokens | number | Maximo de tokens a generar |
top_p | number | Parametro de muestreo nucleus |
top_k | number | Filtrado top-k |
thinking_effort | number | Profundidad de razonamiento 0-100 (modelos con capacidad de razonamiento) |
tools | table | Array de definiciones de herramientas |
tool_choice | string | "auto", "none", "any", o nombre de herramienta |
stream | table | Configuracion de streaming: { reply_to, topic, buffer_size } |
timeout | number | Tiempo limite de solicitud en segundos (por defecto 600) |
Estructura de la Respuesta
Sección titulada «Estructura de la Respuesta»| Campo | Tipo | Descripcion |
|---|---|---|
result | string | Contenido de texto generado |
tokens | table | Uso de tokens: prompt_tokens, completion_tokens, thinking_tokens, total_tokens |
finish_reason | string | Razon por la que se detuvo la generacion: "stop", "length", "tool_call", "filtered", "error" |
tool_calls | table? | Array de llamadas a herramientas (si el modelo invoco herramientas) |
metadata | table | Metadatos especificos del proveedor |
usage_record | table? | Registro de seguimiento de uso |
Constructor de Prompts
Sección titulada «Constructor de Prompts»Para conversaciones multi-turno y prompts complejos, usa el constructor de prompts:
imports: llm: wippy.llm:llm prompt: wippy.llm:promptlocal llm = require("llm")local prompt = require("prompt")
local conversation = prompt.new()conversation:add_system("You are a helpful assistant.")conversation:add_user("What is the capital of France?")
local response, err = llm.generate(conversation, { model = "gpt-4o", temperature = 0.7, max_tokens = 500})Metodos del Constructor
Sección titulada «Metodos del Constructor»| Metodo | Descripcion |
|---|---|
prompt.new() | Crear constructor vacio |
prompt.with_system(content) | Crear constructor con mensaje de sistema |
:add_system(content, meta?) | Agregar mensaje de sistema |
:add_user(content, meta?) | Agregar mensaje de usuario |
:add_assistant(content, meta?) | Agregar mensaje de asistente |
:add_developer(content, meta?) | Agregar mensaje de desarrollador |
:add_message(role, content_parts, name?, meta?) | Agregar mensaje con rol y partes de contenido |
:add_function_call(name, args, id?) | Agregar llamada a herramienta del asistente |
:add_function_result(name, result, id?) | Agregar resultado de ejecucion de herramienta |
:add_cache_marker(id?) | Marcar limite de cache (modelos Claude) |
:get_messages() | Obtener array de mensajes |
:build() | Obtener tabla { messages = ... } para llm.generate() |
:clone() | Copia profunda del constructor |
:clear() | Eliminar todos los mensajes |
Todos los metodos add_* retornan el constructor para encadenamiento.
Conversaciones Multi-Turno
Sección titulada «Conversaciones Multi-Turno»Construye contexto a traves de turnos agregando mensajes:
local conversation = prompt.new()conversation:add_system("You are a helpful assistant.")
-- first turnconversation:add_user("What is Lua?")local r1 = llm.generate(conversation, { model = "gpt-4o" })conversation:add_assistant(r1.result)
-- second turn with full contextconversation:add_user("What makes it different from Python?")local r2 = llm.generate(conversation, { model = "gpt-4o" })Contenido Multimodal
Sección titulada «Contenido Multimodal»Combina texto e imagenes en un solo mensaje:
local conversation = prompt.new()conversation:add_message(prompt.ROLE.USER, { prompt.text("What's in this image?"), prompt.image("https://example.com/photo.jpg")})| Funcion | Descripcion |
|---|---|
prompt.text(content) | Parte de contenido de texto |
prompt.image(url, mime_type?) | Imagen desde URL |
prompt.image_base64(mime_type, data) | Imagen codificada en Base64 |
Constantes de Rol
Sección titulada «Constantes de Rol»| Constante | Valor |
|---|---|
prompt.ROLE.SYSTEM | "system" |
prompt.ROLE.USER | "user" |
prompt.ROLE.ASSISTANT | "assistant" |
prompt.ROLE.DEVELOPER | "developer" |
prompt.ROLE.FUNCTION_CALL | "function_call" |
prompt.ROLE.FUNCTION_RESULT | "function_result" |
prompt.ROLE.CACHE_MARKER | "cache_marker" |
Clonacion
Sección titulada «Clonacion»Clona un constructor para crear variaciones sin modificar el original:
local base = prompt.new()base:add_system("You are a helpful assistant.")
local conv1 = base:clone()conv1:add_user("What is AI?")
local conv2 = base:clone()conv2:add_user("What is ML?")Streaming
Sección titulada «Streaming»Transmite respuestas en tiempo real usando comunicacion de procesos. Esto requiere una entrada process.lua:
local llm = require("llm")
local TOPIC = "llm_stream"
local function main() local stream_ch = process.listen(TOPIC)
local response = llm.generate("Write a short story", { model = "gpt-4o", stream = { reply_to = process.pid(), topic = TOPIC, }, })
while true do local chunk, ok = stream_ch:receive() if not ok then break end
if chunk.type == "chunk" then io.write(chunk.content) elseif chunk.type == "thinking" then io.write(chunk.content) elseif chunk.type == "error" then io.print("Error: " .. chunk.error.message) break elseif chunk.type == "done" then break end end
process.unlisten(stream_ch)endTipos de Chunk
Sección titulada «Tipos de Chunk»| Tipo | Campos | Descripcion |
|---|---|---|
"chunk" | content | Fragmento de contenido de texto |
"thinking" | content | Proceso de razonamiento del modelo |
"tool_call" | name, arguments, id | Invocacion de herramienta |
"error" | error.message, error.type | Error en el stream |
"done" | meta | Stream completado |
process.lua porque utiliza el sistema de comunicacion de procesos de Wippy (process.pid(), process.listen()).
Llamadas a Herramientas
Sección titulada «Llamadas a Herramientas»Define herramientas como esquemas inline y pasalas a generate():
local llm = require("llm")local prompt = require("prompt")local json = require("json")
local tools = { { name = "get_weather", description = "Get current weather for a location", schema = { type = "object", properties = { location = { type = "string", description = "City name" }, }, required = { "location" }, }, },}
local conversation = prompt.new()conversation:add_user("What's the weather in Tokyo?")
local response = llm.generate(conversation, { model = "gpt-4o", tools = tools, tool_choice = "auto",})
if response.tool_calls and #response.tool_calls > 0 then for _, tc in ipairs(response.tool_calls) do -- execute the tool and get a result local result = { temperature = 22, condition = "sunny" }
-- add the exchange to the conversation conversation:add_function_call(tc.name, tc.arguments, tc.id) conversation:add_function_result(tc.name, json.encode(result), tc.id) end
-- continue generation with tool results local final = llm.generate(conversation, { model = "gpt-4o" }) print(final.result)endCampos de Llamada a Herramienta
Sección titulada «Campos de Llamada a Herramienta»| Campo | Tipo | Descripcion |
|---|---|---|
id | string | Identificador unico de la llamada |
name | string | Nombre de la herramienta |
arguments | table | Argumentos parseados que coinciden con el esquema |
Eleccion de Herramienta
Sección titulada «Eleccion de Herramienta»| Valor | Comportamiento |
|---|---|
"auto" | El modelo decide cuando usar herramientas (por defecto) |
"none" | Nunca usar herramientas |
"any" | Debe usar al menos una herramienta |
"tool_name" | Debe usar la herramienta especificada |
Salida Estructurada
Sección titulada «Salida Estructurada»Genera JSON validado que coincida con un esquema:
local llm = require("llm")
local schema = { type = "object", properties = { name = { type = "string" }, age = { type = "number" }, hobbies = { type = "array", items = { type = "string" }, }, }, required = { "name", "age", "hobbies" }, additionalProperties = false,}
local response, err = llm.structured_output(schema, "Describe a fictional character", { model = "gpt-4o",})
if not err then print(response.result.name) print(response.result.age)endrequired. Usa tipos union para campos opcionales: type = {"string", "null"}. Establece additionalProperties = false.
Configuracion de Modelos
Sección titulada «Configuracion de Modelos»Los modelos se definen como entradas de registro con meta.type: llm.model:
entries: - name: gpt-4o kind: registry.entry meta: name: gpt-4o type: llm.model title: GPT-4o comment: OpenAI's flagship model capabilities: - generate - tool_use - structured_output - vision class: - balanced priority: 100 max_tokens: 128000 output_tokens: 16384 pricing: input: 2.5 output: 10 providers: - id: wippy.llm.openai:provider provider_model: gpt-4oCampos de Entrada del Modelo
Sección titulada «Campos de Entrada del Modelo»| Campo | Descripcion |
|---|---|
meta.name | Identificador del modelo usado en llamadas API |
meta.type | Debe ser llm.model |
meta.capabilities | Lista de capacidades: generate, tool_use, structured_output, embed, thinking, vision, caching |
meta.class | Pertenencia a clase: fast, balanced, reasoning, etc. |
meta.priority | Prioridad numerica para resolucion basada en clase (mayor gana) |
max_tokens | Ventana de contexto maxima |
output_tokens | Maximo de tokens de salida |
pricing | Costo por millon de tokens: input, output |
providers | Array con id (entrada del proveedor) y provider_model (nombre del modelo especifico del proveedor) |
Modelos Locales
Sección titulada «Modelos Locales»Para modelos alojados localmente (LM Studio, Ollama), define una entrada de proveedor separada con un base_url personalizado:
- name: local_provider kind: registry.entry meta: name: ollama type: llm.provider title: Ollama Local driver: id: wippy.llm.openai:driver options: api_key_env: none base_url: http://127.0.0.1:11434/v1
- name: local-llama kind: registry.entry meta: name: local-llama type: llm.model title: Local Llama capabilities: - generate max_tokens: 4096 output_tokens: 4096 pricing: input: 0 output: 0 providers: - id: app:local_provider provider_model: llama-3.2Resolucion de Modelos
Sección titulada «Resolucion de Modelos»Los modelos pueden referenciarse por nombre exacto, clase o prefijo de clase explicito:
-- exact model namellm.generate("Hello", { model = "gpt-4o" })
-- model class (picks highest priority in that class)llm.generate("Hello", { model = "fast" })
-- explicit class syntaxllm.generate("Hello", { model = "class:reasoning" })Orden de resolucion:
- Coincidencia por
meta.nameexacto - Coincidencia por nombre de clase (mayor
meta.prioritygana) - Con prefijo
class:, busca solo en esa clase
Descubrimiento de Modelos
Sección titulada «Descubrimiento de Modelos»Consulta los modelos disponibles y sus capacidades en tiempo de ejecucion:
local llm = require("llm")
-- all modelslocal models = llm.available_models()
-- filter by capabilitylocal tool_models = llm.available_models("tool_use")local embed_models = llm.available_models("embed")
-- list model classeslocal classes = llm.get_classes()for _, c in ipairs(classes) do print(c.name .. ": " .. c.title)endEmbeddings
Sección titulada «Embeddings»Genera embeddings vectoriales para busqueda semantica:
local llm = require("llm")
-- single textlocal response = llm.embed("The quick brown fox", { model = "text-embedding-3-small", dimensions = 512,})-- response.result is a float array
-- multiple textslocal response = llm.embed({ "First document", "Second document",}, { model = "text-embedding-3-small" })-- response.result is an array of float arraysEstado del Proveedor
Sección titulada «Estado del Proveedor»Sondea un proveedor antes de enviar trabajo. Util para verificaciones de disponibilidad y monitoreo ligero del estado:
local status, err = llm.status({ model = "gpt-4o",})| Opcion | Descripcion |
|---|---|
model | Requerido. Modelo a verificar. |
provider_id | Opcional. Omite la resolucion del modelo y apunta a un proveedor especifico. |
Devuelve el StatusResponse del proveedor (el contenido depende del proveedor).
Manejo de Errores
Sección titulada «Manejo de Errores»Los errores se retornan como el segundo valor de retorno. En caso de error, el primer valor de retorno es nil:
local response, err = llm.generate("Hello", { model = "gpt-4o" })
if err then io.print("Error: " .. tostring(err)) returnend
io.print(response.result)Tipos de Error
Sección titulada «Tipos de Error»| Constante | Descripcion |
|---|---|
llm.ERROR_TYPE.INVALID_REQUEST | Solicitud malformada |
llm.ERROR_TYPE.AUTHENTICATION | Clave API invalida |
llm.ERROR_TYPE.RATE_LIMIT | Limite de tasa del proveedor excedido |
llm.ERROR_TYPE.SERVER_ERROR | Error del servidor del proveedor |
llm.ERROR_TYPE.CONTEXT_LENGTH | La entrada excede la ventana de contexto |
llm.ERROR_TYPE.CONTENT_FILTER | Contenido filtrado por sistemas de seguridad |
llm.ERROR_TYPE.TIMEOUT | Tiempo de solicitud agotado |
llm.ERROR_TYPE.MODEL_ERROR | Modelo invalido o no disponible |
Razones de Finalizacion
Sección titulada «Razones de Finalizacion»| Constante | Descripcion |
|---|---|
llm.FINISH_REASON.STOP | Completado normalmente |
llm.FINISH_REASON.LENGTH | Alcanzo el maximo de tokens |
llm.FINISH_REASON.CONTENT_FILTER | Contenido filtrado |
llm.FINISH_REASON.TOOL_CALL | El modelo realizo una llamada a herramienta |
llm.FINISH_REASON.ERROR | Error durante la generacion |
Capacidades
Sección titulada «Capacidades»| Constante | Descripcion |
|---|---|
llm.CAPABILITY.GENERATE | Generacion de texto |
llm.CAPABILITY.TOOL_USE | Llamadas a herramientas/funciones |
llm.CAPABILITY.STRUCTURED_OUTPUT | Salida estructurada JSON |
llm.CAPABILITY.EMBED | Embeddings vectoriales |
llm.CAPABILITY.THINKING | Razonamiento extendido |
llm.CAPABILITY.VISION | Comprension de imagenes |
llm.CAPABILITY.CACHING | Cache de prompts |
Ver Tambien
Sección titulada «Ver Tambien»- Agentes - Framework de agentes con herramientas, delegados y memoria
- Construir un Agente LLM - Tutorial paso a paso
- Vision General del Framework - Uso de modulos del framework