LLM
O modulo wippy/llm fornece uma interface unificada para trabalhar com Modelos de Linguagem de Grande Escala (LLMs) de multiplos provedores (OpenAI, Anthropic, Google, modelos locais). Ele suporta geracao de texto, chamada de ferramentas, saida estruturada, embeddings e streaming.
Configuracao
Seção intitulada “Configuracao”Adicione o modulo ao seu projeto:
wippy add wippy/llmwippy installDeclare a dependencia no seu _index.yaml. O modulo LLM requer um armazenamento de ambiente (para chaves de API) e um host de processos:
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:processesA entrada env.storage.os expoe variaveis de ambiente do sistema operacional para os provedores LLM. Defina suas chaves de API como variaveis de ambiente (ex.: OPENAI_API_KEY, ANTHROPIC_API_KEY).
Geracao de Texto
Seção intitulada “Geracao de Texto”Importe a biblioteca llm na sua entrada e chame 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 }O primeiro argumento de generate() pode ser uma string de prompt, um construtor de prompt ou uma tabela de mensagens. O segundo argumento e uma tabela de opcoes.
Opcoes de Generate
Seção intitulada “Opcoes de Generate”| Opcao | Tipo | Descricao |
|---|---|---|
model | string | Nome ou classe do modelo (obrigatorio) |
temperature | number | Controle de aleatoriedade, 0-1 |
max_tokens | number | Maximo de tokens a gerar |
top_p | number | Parametro de amostragem nucleus |
top_k | number | Filtragem top-k |
thinking_effort | number | Profundidade de raciocinio 0-100 (modelos com capacidade de raciocinio) |
tools | table | Array de definicoes de ferramentas |
tool_choice | string | "auto", "none", "any", ou nome da ferramenta |
stream | table | Configuracao de streaming: { reply_to, topic, buffer_size } |
timeout | number | Timeout da requisicao em segundos (padrao 600) |
Estrutura da Resposta
Seção intitulada “Estrutura da Resposta”| Campo | Tipo | Descricao |
|---|---|---|
result | string | Conteudo de texto gerado |
tokens | table | Uso de tokens: prompt_tokens, completion_tokens, thinking_tokens, total_tokens |
finish_reason | string | Motivo da interrupcao da geracao: "stop", "length", "tool_call", "filtered", "error" |
tool_calls | table? | Array de chamadas de ferramentas (se o modelo invocou ferramentas) |
metadata | table | Metadados especificos do provedor |
usage_record | table? | Registro de uso |
Construtor de Prompt
Seção intitulada “Construtor de Prompt”Para conversas com multiplos turnos e prompts complexos, use o construtor de prompt:
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 do Construtor
Seção intitulada “Metodos do Construtor”| Metodo | Descricao |
|---|---|
prompt.new() | Cria um construtor vazio |
prompt.with_system(content) | Cria um construtor com mensagem de sistema |
:add_system(content, meta?) | Adiciona mensagem de sistema |
:add_user(content, meta?) | Adiciona mensagem do usuario |
:add_assistant(content, meta?) | Adiciona mensagem do assistente |
:add_developer(content, meta?) | Adiciona mensagem do desenvolvedor |
:add_message(role, content_parts, name?, meta?) | Adiciona mensagem com papel e partes de conteudo |
:add_function_call(name, args, id?) | Adiciona chamada de ferramenta do assistente |
:add_function_result(name, result, id?) | Adiciona resultado de execucao de ferramenta |
:add_cache_marker(id?) | Marca limite de cache (modelos Claude) |
:get_messages() | Obtem o array de mensagens |
:build() | Obtem tabela { messages = ... } para llm.generate() |
:clone() | Copia profunda do construtor |
:clear() | Remove todas as mensagens |
Todos os metodos add_* retornam o construtor para encadeamento.
Conversas com Multiplos Turnos
Seção intitulada “Conversas com Multiplos Turnos”Construa o contexto ao longo dos turnos adicionando mensagens:
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" })Conteudo Multimodal
Seção intitulada “Conteudo Multimodal”Combine texto e imagens em uma unica mensagem:
local conversation = prompt.new()conversation:add_message(prompt.ROLE.USER, { prompt.text("What's in this image?"), prompt.image("https://example.com/photo.jpg")})| Funcao | Descricao |
|---|---|
prompt.text(content) | Parte de conteudo de texto |
prompt.image(url, mime_type?) | Imagem a partir de URL |
prompt.image_base64(mime_type, data) | Imagem codificada em Base64 |
Constantes de Papel
Seção intitulada “Constantes de Papel”| 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" |
Clonagem
Seção intitulada “Clonagem”Clone um construtor para criar variacoes sem modificar o 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
Seção intitulada “Streaming”Transmita respostas em tempo real usando comunicacao de processos. Isso requer uma 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
Seção intitulada “Tipos de Chunk”| Tipo | Campos | Descricao |
|---|---|---|
"chunk" | content | Fragmento de conteudo de texto |
"thinking" | content | Processo de raciocinio do modelo |
"tool_call" | name, arguments, id | Invocacao de ferramenta |
"error" | error.message, error.type | Erro no stream |
"done" | meta | Stream concluido |
process.lua porque utiliza o sistema de comunicacao de processos do Wippy (process.pid(), process.listen()).
Chamada de Ferramentas
Seção intitulada “Chamada de Ferramentas”Defina ferramentas como schemas inline e passe-as para 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 Chamada de Ferramenta
Seção intitulada “Campos de Chamada de Ferramenta”| Campo | Tipo | Descricao |
|---|---|---|
id | string | Identificador unico da chamada |
name | string | Nome da ferramenta |
arguments | table | Argumentos parseados correspondentes ao schema |
Escolha de Ferramenta
Seção intitulada “Escolha de Ferramenta”| Valor | Comportamento |
|---|---|
"auto" | O modelo decide quando usar ferramentas (padrao) |
"none" | Nunca usar ferramentas |
"any" | Deve usar pelo menos uma ferramenta |
"tool_name" | Deve usar a ferramenta especificada |
Saida Estruturada
Seção intitulada “Saida Estruturada”Gere JSON validado correspondente a um schema:
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. Use tipos de uniao para campos opcionais: type = {"string", "null"}. Defina additionalProperties = false.
Configuracao de Modelo
Seção intitulada “Configuracao de Modelo”Modelos sao definidos como entradas de registro com 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 da Entrada de Modelo
Seção intitulada “Campos da Entrada de Modelo”| Campo | Descricao |
|---|---|
meta.name | Identificador do modelo usado nas chamadas de API |
meta.type | Deve ser llm.model |
meta.capabilities | Lista de funcionalidades: generate, tool_use, structured_output, embed, thinking, vision, caching |
meta.class | Pertencimento a classe: fast, balanced, reasoning, etc. |
meta.priority | Prioridade numerica para resolucao baseada em classe (maior vence) |
max_tokens | Janela de contexto maxima |
output_tokens | Maximo de tokens de saida |
pricing | Custo por milhao de tokens: input, output |
providers | Array com id (entrada do provedor) e provider_model (nome do modelo especifico do provedor) |
Modelos Locais
Seção intitulada “Modelos Locais”Para modelos hospedados localmente (LM Studio, Ollama), defina uma entrada de provedor separada com uma base_url customizada:
- 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.2Resolucao de Modelo
Seção intitulada “Resolucao de Modelo”Modelos podem ser referenciados por nome exato, classe ou prefixo de classe 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" })Ordem de resolucao:
- Corresponder por
meta.nameexato - Corresponder por nome de classe (maior
meta.priorityvence) - Com prefixo
class:, buscar apenas naquela classe
Descoberta de Modelos
Seção intitulada “Descoberta de Modelos”Consulte os modelos disponiveis e suas capacidades em tempo de execucao:
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
Seção intitulada “Embeddings”Gere embeddings vetoriais para busca 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 arraysStatus do Provedor
Seção intitulada “Status do Provedor”Sonde um provedor antes de enviar trabalho. Util para verificacoes de prontidao e monitoramento leve de saude:
local status, err = llm.status({ model = "gpt-4o",})| Opcao | Descricao |
|---|---|
model | Obrigatorio. Modelo a verificar. |
provider_id | Opcional. Ignora a resolucao do modelo e direciona para um provedor especifico. |
Retorna o StatusResponse do provedor (o conteudo depende do provedor).
Tratamento de Erros
Seção intitulada “Tratamento de Erros”Erros sao retornados como o segundo valor de retorno. Em caso de erro, o primeiro valor de retorno e 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 Erro
Seção intitulada “Tipos de Erro”| Constante | Descricao |
|---|---|
llm.ERROR_TYPE.INVALID_REQUEST | Requisicao malformada |
llm.ERROR_TYPE.AUTHENTICATION | Chave de API invalida |
llm.ERROR_TYPE.RATE_LIMIT | Limite de taxa do provedor excedido |
llm.ERROR_TYPE.SERVER_ERROR | Erro no servidor do provedor |
llm.ERROR_TYPE.CONTEXT_LENGTH | Entrada excede a janela de contexto |
llm.ERROR_TYPE.CONTENT_FILTER | Conteudo filtrado pelos sistemas de seguranca |
llm.ERROR_TYPE.TIMEOUT | Timeout da requisicao |
llm.ERROR_TYPE.MODEL_ERROR | Modelo invalido ou indisponivel |
Motivos de Finalizacao
Seção intitulada “Motivos de Finalizacao”| Constante | Descricao |
|---|---|
llm.FINISH_REASON.STOP | Conclusao normal |
llm.FINISH_REASON.LENGTH | Atingiu o maximo de tokens |
llm.FINISH_REASON.CONTENT_FILTER | Conteudo filtrado |
llm.FINISH_REASON.TOOL_CALL | O modelo fez uma chamada de ferramenta |
llm.FINISH_REASON.ERROR | Erro durante a geracao |
Capacidades
Seção intitulada “Capacidades”| Constante | Descricao |
|---|---|
llm.CAPABILITY.GENERATE | Geracao de texto |
llm.CAPABILITY.TOOL_USE | Chamada de ferramentas/funcoes |
llm.CAPABILITY.STRUCTURED_OUTPUT | Saida estruturada JSON |
llm.CAPABILITY.EMBED | Embeddings vetoriais |
llm.CAPABILITY.THINKING | Raciocinio estendido |
llm.CAPABILITY.VISION | Compreensao de imagens |
llm.CAPABILITY.CACHING | Cache de prompt |
Veja Tambem
Seção intitulada “Veja Tambem”- Agentes - Framework de agentes com ferramentas, delegados e memoria
- Construindo um Agente LLM - Tutorial passo a passo
- Visao Geral do Framework - Uso dos modulos do framework