Public docs

Build AI-native interfaces without generated UI code.

Leara lets models compose a small set of registered primitives while your application owns rendering, validation, styling, and the final user experience.

Ten minute setup

Quickstart

The first Leara integration should be one narrow product path: one config file, one server-safe hosted API proxy, one SDK prompt box, and one output region that renders the generated Interface.

01
Install the SDK packages.
02
Add the Leara project API key to server env.
03
Create liara.config.ts or liara.config.js.
04
Add a tiny compose route that keeps the key server-side.
05
Render LiaraPromptBox where the user asks.
06
Render LiaraOutput where the generated UI should appear.

Current published alpha

The SDK is currently testable from npm as @hug_kin/*@alpha. Latest verified baseline: 0.0.1-alpha.12. Official imports and package names are @liara/*.

pnpm
pnpm add @hug_kin/core@alpha @hug_kin/server@alpha @hug_kin/lang@alpha @hug_kin/next@alpha @hug_kin/openai@alpha @hug_kin/react@alpha @hug_kin/primitives@alpha @hug_kin/zod@alpha
npm
npm install @hug_kin/core@alpha @hug_kin/server@alpha @hug_kin/lang@alpha @hug_kin/next@alpha @hug_kin/openai@alpha @hug_kin/react@alpha @hug_kin/primitives@alpha @hug_kin/zod@alpha
yarn
yarn add @hug_kin/core@alpha @hug_kin/server@alpha @hug_kin/lang@alpha @hug_kin/next@alpha @hug_kin/openai@alpha @hug_kin/react@alpha @hug_kin/primitives@alpha @hug_kin/zod@alpha
bun
bun add @hug_kin/core@alpha @hug_kin/server@alpha @hug_kin/lang@alpha @hug_kin/next@alpha @hug_kin/openai@alpha @hug_kin/react@alpha @hug_kin/primitives@alpha @hug_kin/zod@alpha

Intended official scope

pnpm
pnpm add @liara/next @liara/react @liara/primitives
npm
npm install @liara/next @liara/react @liara/primitives
yarn
yarn add @liara/next @liara/react @liara/primitives
bun
bun add @liara/next @liara/react @liara/primitives
LIARA_API_KEY=liara_sk_live_...
LIARA_PROJECT_ID=proj_acme
LIARA_API_BASE_URL=https://api.liara.dev
What this proves
Leara project API keys live only in server environment variables.
The beta happy path does not require a model provider key, custom tools, or primitive registration.
Successful validated Interface outputs consume one UI output credit; errors and fallbacks do not.
Compose responses can expose `meta.billing.credits` for admin, billing, or settings surfaces.
The prompt box is imported from the SDK.
The generated UI appears exactly where the host app renders LiaraOutput.
Host apps can split the prompt input and generated UI into different layout regions.
Trusted context is resolved or sanitized in the server route before it reaches Leara.
Client code receives only serialized Interface output.
LiaraComposer is available when teams want one combined prompt and output component.
LiaraSurface, useLiaraCompose(), and LiaraRenderer() remain available for advanced host UI control.
The host app owns styling through liara.config.ts and primitive bindings.

Files you will add

.env.local
Add a Leara project API key

The browser never receives this key. The host app's route calls the hosted Leara API from server code.

liara.config.ts
Configure the SDK surface

This file controls endpoint, prompt copy, theme defaults, and starter suggestions. Most apps only edit this file after installation.

app/api/liara/compose/route.ts
Proxy the hosted compose API

This is the only server code required for the hosted beta. It keeps the project API key server-side and forwards prompt/context/config to Leara.

components/account-liara-panel.tsx
Place the prompt and output

The SDK prompt box and output renderer can live together or in separate regions. The generated UI renders wherever LiaraOutput is placed.

Official example

Minimal Next.js integration

A copy-pasteable hosted Leara setup: one project API key, one config file, one compose route, one prompt box, and one output region.

Keep the Leara project API key server-side.
Use the hosted compose route for the beta happy path.
Render the SDK prompt box where users ask.
Render LiaraOutput exactly where generated UI should appear.
Use liara.config.ts for theme, copy, suggestions, and SDK behavior.
.env.local
Server keys

The hosted path needs one Leara project API key. No model provider key or primitive registration is required.

LIARA_API_KEY=liara_sk_live_...
LIARA_PROJECT_ID=proj_demo
LIARA_API_BASE_URL=https://api.liara.dev
liara.config.ts
SDK config

The app owns endpoint, prompt copy, theme defaults, suggestions, and primitive behavior.

import { defineLiaraConfig } from "@liara/next";

const liaraConfig = defineLiaraConfig({
  endpoint: "/api/liara/compose",
  runtime: {
    mode: "open",
    interfaceComposer: "liara-lang",
    readonly: true,
  },
  surface: {
    initialPrompt: "Give me a dashboard that shows how the company is going.",
    placeholder: "Ask Leara for a dashboard, workflow, report, or analysis...",
    promptLabel: "Ask Leara",
    submitLabel: "Compose",
    loadingLabel: "Composing...",
    suggestions: [
      "Give me a dashboard that shows how the company is going.",
      "Which customers are at risk before renewal?",
      "What happened with the billing incident this morning?",
    ],
  },
  theme: {
    colorMode: "system",
    font: "Inter",
    radius: 12,
  },
});

export default liaraConfig;
app/api/liara/compose/route.ts
Hosted compose route

The route keeps the project API key server-side and forwards prompt, context, and config to Leara.

import { createHostedLiaraRouteHandler } from "@liara/next";
import liaraConfig from "@/liara.config";

export const POST = createHostedLiaraRouteHandler({
  config: liaraConfig,
  context: ({ clientContext }) => {
    const context =
      typeof clientContext === "object" && clientContext !== null
        ? (clientContext as { workspaceId?: unknown })
        : {};

    return {
      workspaceId:
        typeof context.workspaceId === "string"
          ? context.workspaceId
          : "workspace_demo",
    };
  },
});
components/liara-workspace-panel.tsx
Prompt and output

Place the prompt box where users ask and LiaraOutput where the generated UI should render.

"use client";

import { liaraPrimitiveBindings } from "@liara/primitives";
import {
  LiaraComposerProvider,
  LiaraOutput,
  LiaraPromptBox,
} from "@liara/react/client";
import liaraConfig from "@/liara.config";

export function LiaraWorkspacePanel({
  workspaceId,
}: {
  workspaceId: string;
}) {
  return (
    <LiaraComposerProvider
      config={liaraConfig}
      context={{ workspaceId }}
      primitives={liaraPrimitiveBindings}
    >
      <LiaraPromptBox />

      <section aria-label="Generated Leara interface">
        <LiaraOutput empty={<div>Your generated UI appears here.</div>} />
      </section>
    </LiaraComposerProvider>
  );
}
app/page.tsx
Host page

Leara mounts inside an existing product surface. The host app decides where generated UI appears.

import { LiaraWorkspacePanel } from "@/components/liara-workspace-panel";

export default function DashboardPage() {
  return (
    <main>
      <LiaraWorkspacePanel workspaceId="workspace_demo" />
    </main>
  );
}

Configuration

Create liara.config.ts

The config file is the durable host-app contract. It keeps the endpoint, prompt copy, suggestions, theme, and surface defaults in one place.

import { defineLiaraConfig } from "@liara/next";

const liaraConfig = defineLiaraConfig({
  endpoint: "/api/liara/compose",
  runtime: {
    mode: "open",
    interfaceComposer: "liara-lang",
    readonly: true,
  },
  surface: {
    initialPrompt: "Give me a dashboard that shows how the company is going.",
    placeholder: "Ask Leara for a dashboard, workflow, report, or analysis...",
    promptLabel: "Ask Leara",
    submitLabel: "Compose",
    loadingLabel: "Composing...",
    suggestions: [
      "Give me a dashboard that shows how the company is going.",
      "Which customers are at risk before renewal?",
      "What happened with the billing incident this morning?",
    ],
  },
  theme: {
    colorMode: "system",
    font: "Inter",
    radius: 12,
  },
});

export default liaraConfig;

Transport

Expose a Next.js compose endpoint

The route keeps the Leara project API key on the server and forwards prompt, context, and config to the hosted compose API. Caller-provided context should still be sanitized before it is forwarded.

import { createHostedLiaraRouteHandler } from "@liara/next";
import liaraConfig from "@/liara.config";

export const POST = createHostedLiaraRouteHandler({
  config: liaraConfig,
});

Client

Compose and render in React

Client code never receives secrets. It imports the SDK prompt box, imports the output renderer, and places generated UI in the exact part of the app where it should appear.

"use client";

import { liaraPrimitiveBindings } from "@liara/primitives";
import {
  LiaraComposerProvider,
  LiaraOutput,
  LiaraPromptBox,
} from "@liara/react/client";
import liaraConfig from "@/liara.config";

export function AccountLiaraPanel({ workspaceId }: { workspaceId: string }) {
  return (
    <LiaraComposerProvider
      config={liaraConfig}
      context={{ workspaceId }}
      primitives={liaraPrimitiveBindings}
    >
      <LiaraPromptBox />

      <main id="liara-output">
        <LiaraOutput empty={<div>Your generated UI appears here.</div>} />
      </main>
    </LiaraComposerProvider>
  );
}

Admin surfaces

Show credits outside the prompt

Leara can return credit metadata for the host application, but it should be displayed in admin, billing, or settings UI. The end-user prompt surface should stay focused on the product task.

"use client";

import type { LiaraCreditSnapshot } from "@liara/next";

export function LiaraAdminUsage({
  credits,
}: {
  credits?: LiaraCreditSnapshot;
}) {
  if (!credits) {
    return null;
  }

  return (
    <section aria-label="Leara usage">
      <p>Leara UI output credits</p>
      <strong>{credits.remaining}</strong>
      <span>of {credits.included} remaining</span>
    </section>
  );
}

Check it

Verify the setup

Once the files are in place, test the route before polishing the host UI. A healthy route returns ok: true, an interface.version, and a serializable interface.root.

Run the app

Start the host Next.js application after adding the files.

pnpm dev
Smoke test the endpoint

The response should return ok: true and a serializable Interface tree.

curl -X POST http://localhost:3000/api/liara/compose \
  -H "content-type: application/json" \
  -d '{"prompt":"Show account health and renewal risks.","context":{"accountId":"acct_northstar"}}'

First run

Troubleshooting

Leara errors stay controlled at the client boundary. The server can return code, status, and hint so the host app can help developers without exposing secrets.

MISSING_LIARA_API_KEY

`LIARA_API_KEY` is not available to the server route that proxies the hosted compose API.

Create a project API key in Leara, add `LIARA_API_KEY` to `.env.local`, restart the dev server, and never expose it to Client Components.

Hosted Leara API could not be reached

`LIARA_API_BASE_URL` points to the wrong origin, the network request failed, or the hosted compose API is unavailable.

Verify `LIARA_API_BASE_URL`, test `/api/liara/compose` with curl, and keep the browser pointed at your same-origin proxy.

Leara compose endpoint was not found

`LiaraPromptBox`, `LiaraOutput`, or `LiaraSurface` is using a `config.endpoint` route that does not exist.

Check `config.endpoint`, then add `app/api/liara/compose/route.ts` with `createHostedLiaraRouteHandler()`.

Leara compose endpoint could not be reached

The app server is not running, the endpoint URL points to another origin, or the browser cannot reach the route.

Start the host app, verify the endpoint with curl, and keep relative endpoints such as `/api/liara/compose` for same-origin apps.

PLAN_LIMIT_REACHED

The Leara project has no UI output credits remaining for the current billing period.

Surface this in an admin, billing, or settings view. Do not expose credit-state inside the end-user prompt experience.

RATE_LIMITED

The project is sending too many compose requests in a short window, usually from retries, duplicated form submissions, or parallel agent calls.

Respect the `Retry-After` header, disable duplicate submits while a request is pending, and move bulk/background composition behind a queue.

PROJECT_ACCESS_DENIED, PROJECT_DISABLED, or PROJECT_NOT_FOUND

The host route is using a project id that does not match the configured Leara project key, or the project is disabled.

Verify `LIARA_PROJECT_ID`, rotate the project API key if needed, and keep project selection in server-owned config rather than browser state.

MODEL_CONFIGURATION_ERROR or MODEL_PROVIDER_ERROR

The selected model provider is unavailable, misconfigured, out of quota, or missing a server-side provider key.

Check the provider package, provider key, model name, server network access, and quota from the same environment that runs the compose route.

Invalid Leara Lang or invalid Interface

The model produced a UI shape that failed grammar, primitive, or prop validation.

Inspect the server trace, keep examples small and complete, and add a regression eval for the failing prompt.

Observability

Production-safe logging

Leara can emit structured compose events from the server route. These events are intentionally redacted and should be forwarded to your telemetry system as-is.

Safe to log: event, level, code, status, durationMs, provider, model, primitiveNames, and redacted metadata.
Never log raw prompts, provider API keys, tool input, tool output, auth/session context, tenant records, or customer records.
Custom loggers should forward LiaraSafeLogEvent objects to your telemetry system without adding raw request bodies.
The route adapter checks Leara Cloud entitlements before model work when hosted observability is configured.
Post-compose trace and usage writes use a Leara project API key from server env and can record runtime metadata such as tokens and repairs without a browser session.
Logger and post-compose ingestion failures are ignored by the route adapter so telemetry cannot break an otherwise completed composition.
import type { LiaraSafeLogEvent } from "@liara/next";
import { createLiaraRouteHandler } from "@liara/next";
import { liara, type LiaraRequestContext } from "@/app/liara/runtime";
import { auth } from "@/auth";

function logLiaraEvent(event: LiaraSafeLogEvent) {
  console.info("liara", event);
}

export const POST = createLiaraRouteHandler({
  liara,
  observability: {
    apiKey: process.env.LIARA_API_KEY,
    enforceEntitlements: true,
    endpoint: process.env.LIARA_OBSERVABILITY_ENDPOINT,
    projectId: process.env.LIARA_PROJECT_ID ?? "proj_acme",
    provider: "openai",
    model: process.env.OPENAI_MODEL ?? "gpt-4.1-mini",
  },
  logging: {
    logger: logLiaraEvent,
    provider: "openai",
    model: process.env.OPENAI_MODEL ?? "gpt-4.1-mini",
    metadata: {
      route: "/api/liara/compose",
    },
  },
  context: async ({ clientContext }): Promise<LiaraRequestContext> => {
    const session = await auth();

    if (!session?.user?.email) {
      throw new Error("Authentication required.");
    }

    return {
      tenantId: "tenant_acme",
      userId: session.user.email,
      accountId:
        typeof clientContext === "object" &&
        clientContext !== null &&
        "accountId" in clientContext &&
        typeof clientContext.accountId === "string"
          ? clientContext.accountId
          : "acct_northstar",
    };
  },
});

Language

Default primitives

Primitives describe information patterns, not one-off visual components. Models choose these primitives, but the app renders the actual React views.

Section
Frames an information surface with title, subtitle, and text.
StatGrid
Shows the scalar values users need to compare quickly.
EntityTable
Lets users inspect many records with stable columns.
InsightGrid
Turns observations into short, scannable explanations.
Trend
Shows change over time.
Tabs
Switches between related generated views.

Bring your own key

Model adapters

Leara is not trying to replace model provider SDKs. The first SDK shape expects SaaS teams to bring their own provider keys and implement a small `ModelAdapter`.

import type { ModelAdapter } from "@liara/core";

export const model: ModelAdapter = {
  async generate(request) {
    const response = await provider.responses.create({
      model: "gpt-5-mini",
      input: request.input,
      instructions: request.instructions
    });

    return {
      output: response.output,
      usage: {
        inputTokens: response.usage?.input_tokens,
        outputTokens: response.usage?.output_tokens,
        totalTokens: response.usage?.total_tokens
      }
    };
  }
};

Security model

Server/client boundaries

The central rule is simple: server code composes, client code renders. The only product data that should cross the boundary is the validated Interface and non-sensitive metadata.

Never pass to the client

Runtime instances
Tool functions
Model adapters
Provider API keys
Database clients

Safe client boundary

Serializable Interface
Safe duration metadata
Client renderer bindings
Non-sensitive errors