@alvandal/ai-ui
v0.1.0
Published
AI UI - Typed UI primitives for AI-to-UI communication
Maintainers
Readme
AI UI (AI Interaction Primitives)
"AI Speaks in Components"
A type-safe, Zod-validated library for structured UI communication between AI models and React applications. Instead of streaming raw markdown, your AI returns structured JSON contracts that render as interactive, validated UI primitives.
🚀 Key Features
- Type Safety: 100% TypeScript with strict Zod schemas. No
any. - Validation Engine: Pure functions to validate AI responses against the Primitive Registry.
- Component Registry: Central catalog mapping primitive types (
numeric_scale,body_map, etc.) to Zod schemas and metadata. - React Renderer: Optimally rendered components with a single
<PrimitiveRenderer />entry point. - State Management: Immutable conversation state tracking with
useAIUIConversationhook. - Zero Hallucination: If the AI sends an invalid primitive, the validation layer rejects it before rendering.
📦 Installation
Since the package is not yet published to the npm registry, you can install it directly from GitHub:
npm install github:aandresalvarez/ai-ui
# Or using SSH
npm install git@github:aandresalvarez/ai-ui.git
# Peer dependencies
npm install react react-dom zod🛠️ Usage
1. The Contract
The AI should be instructed to return JSON matching the InteractionContractSchema.
{
"type": "ui_element",
"element": {
"id": "pain-level",
"type": "numeric_scale",
"label": "How would you rate your pain?",
"required": true,
"version": "1.0",
"constraints": { "min": 0, "max": 10, "step": 1 }
}
}2. React Implementation
Use the provided hook and renderer to drive the UI.
import { useAIUIConversation, PrimitiveRenderer } from "ai-ui";
// Import default styles (or provide your own)
import "ai-ui/dist/react/styles/ai-ui.css";
function App() {
// Initialize conversation state
const { currentPrimitive, submitAnswer, history } =
useAIUIConversation("session-123");
// Handle AI response (mocked or real)
// In a real app, you'd fetch this from your AI endpoint
// and pass it to recordStep(conversationState, response.element)
if (!currentPrimitive) return <div>Loading AI response...</div>;
return (
<div className="survey-container">
<h1>AI Assessment</h1>
<PrimitiveRenderer
primitive={currentPrimitive}
onAnswer={(value) => submitAnswer(value)}
disabled={false}
/>
<div className="history">Previous steps: {history.length}</div>
</div>
);
}🧩 Supported Primitives
The library currently supports 13 core primitives:
| Category | Primitives | Description |
| ------------- | --------------- | ----------------------------------------------- |
| Scale | numeric_scale | Bounded number selection (e.g., pain 0-10) |
| | likert_scale | Agreement scale (Strongly Disagree -> Agree) |
| | rating | Star/Emoji ratings |
| | slider | Continuous value selection |
| Selection | single_choice | Radio button / dropdown selection |
| | multi_choice | Checkbox / multi-select |
| | body_map | NEW! Visual body part selector (Front/Back) |
| Input | text_input | Free text with validation (email, phone, etc.) |
| | date_picker | Date/Time selection |
| | toggle | Boolean switch |
| | file_upload | File attachment with type constraints |
| Advanced | matrix | Grid of questions with shared options |
| | ranking | Drag-and-drop reordering |
🏗️ Architecture
The library is architected with strictly separated concerns:
- Primitives Layer (
src/primitives): Zod schema definitions. Source of truth. - Validation Layer (
src/validation): Pure functions for schema validation. - Registry Layer (
src/registry): Maps types to schemas and metadata. - Contract Layer (
src/contract): Defines the envelope for AI responses. - State Layer (
src/state): Immutable state management helpers. - React Layer (
src/react):components/: Visual implementation of primitives.hooks/: Logic for conversation flow.styles/: CSS variables and base styles.
🧪 Development
Build
npm run buildTest
Includes unit tests for validation and integration tests for full flows.
npm testLocal Verification
Run the adaptive survey example locally:
npx viteReview Diff Helper
When a review diff is empty or ambiguous, use the helper to pick a target and get fallback hints:
npm run review:diffUseful flags:
npm run review:diff -- --target main...HEAD
npm run review:diff -- --non-interactive --print-only📄 License
MIT
