Agentes que renderizan componentes ricos e interactivos en línea — tablas, gráficos, formularios, diffs — no solo texto. Sin necesidad de código de frontend.
Las interfaces de chatbot tradicionales renderizan todo como burbujas de texto. Pero los agentes producen datos estructurados — resultados de consultas, informes de validación, estado de despliegue, diffs de comparación. Renderizar todo eso como markdown es una experiencia deficiente.
La UI Generativa de Haira permite que las herramientas controlen de forma declarativa cómo aparece su salida. Una consulta de base de datos se renderiza como tabla. Un resultado de validación se renderiza como tarjeta de estado. Un pipeline de despliegue se renderiza como rastreador de progreso. Todo en línea en el chat, sin escribir código de frontend.
Las herramientas devuelven componentes de UI usando el módulo ui. El runtime hace dos cosas con cada resultado:
Ninguna ruta se sacrifica. El usuario ve una tabla elegante, el agente ve datos estructurados.
import "ui"
tool query_database(query: string) -> any {
"""Executes a SQL query and displays results."""
rows, err = postgres.query(db, query)
if err != nil {
return ui.status_card("error", "Query Failed", conv.to_string(err))
}
return ui.table("Query Results", headers, rows)
}Habilita la UI en un agente con una sola línea:
agent DataExplorer {
provider: OpenAI
tools: [query_database, search_data]
ui: ui
memory: conversation(max_turns: 30)
}import "ui"
ui.status_card("success", "Deploy Complete", "All 3 services updated")
ui.table("Results", ["Name", "Email"], [["Alice", "a@co"], ["Bob", "b@co"]])
ui.key_value("Server Info", {"Region": "us-east-1", "Status": "healthy"})
ui.chart("bar", "Revenue", ["Q1", "Q2", "Q3", "Q4"], [dataset])
ui.confirm("Delete this record?", "Yes, delete", "Cancel")
ui.group(
ui.status_card("success", "Query Complete", "42 rows"),
ui.table("Results", headers, rows)
)Todo workflow de Haira obtiene automáticamente una UI web. Define un workflow, Haira genera el formulario:
@webui(title: "File Summarizer", description: "Upload a file and get an AI summary")
@post("/summarize")
workflow Summarize(document: file, context: string) -> { summary: string } {
content, err = io.read_file(document)
if err != nil { return { summary: "Failed to read file." } }
reply, err = Summarizer.ask("Summarize: ${content}")
if err != nil { return { summary: "AI error." } }
return { summary: reply }
}El decorador @webui establece el título y la descripción. Los parámetros file se renderizan como entradas de subida. Los workflows de streaming (-> stream) obtienen una interfaz de chat completa.
Los workflows de streaming obtienen la experiencia más rica — streaming de tokens en tiempo real, tarjetas de ejecución de herramientas y componentes de UI en línea:
@webhook("/chat")
workflow Chat(message: string, session_id: string) -> stream {
return Assistant.stream(message, session: session_id)
}
fn main() {
http.Server([Chat]).listen(8080)
}La UI de chat se comunica mediante el protocolo ARP — gestionando deltas de texto, eventos del ciclo de vida de herramientas y renderizado de componentes ricos mediante WebSocket o SSE.
Para necesidades específicas del dominio, coloca Componentes Web de TypeScript en un directorio components/:
export class HairaGanttChart extends HTMLElement {
connectedCallback() {
this.attachShadow({ mode: "open" });
}
setProps(props) {
// Render your custom UI
}
}
export default {
tag: "haira-gantt-chart",
component: HairaGanttChart,
};Los componentes personalizados heredan el tema de Haira mediante propiedades personalizadas de CSS y pueden despachar eventos haira-action que se convierten en mensajes de chat. El compilador los descubre, empaqueta e incrusta en tiempo de compilación.
ui.*Sin repositorio de frontend separado. Sin cliente de API que mantener. Sin biblioteca de componentes que instalar. Un archivo .haira, un binario, UI completa.