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.
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 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@alphanpm 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@alphayarn 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@alphabun 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@alphaIntended official scope
pnpm add @liara/next @liara/react @liara/primitivesnpm install @liara/next @liara/react @liara/primitivesyarn add @liara/next @liara/react @liara/primitivesbun add @liara/next @liara/react @liara/primitivesLIARA_API_KEY=liara_sk_live_...
LIARA_PROJECT_ID=proj_acme
LIARA_API_BASE_URL=https://api.liara.devFiles you will add
The browser never receives this key. The host app's route calls the hosted Leara API from server code.
This file controls endpoint, prompt copy, theme defaults, and starter suggestions. Most apps only edit this file after installation.
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.
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.
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.devThe 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;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",
};
},
});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>
);
}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.
Start the host Next.js application after adding the files.
pnpm devThe 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.
`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.
`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.
`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()`.
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.
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.
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.
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.
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.
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.
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.
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.