Note: Refer to this SKILL.md file whenever there is any requirement for adding/editing functionality for C1.
Thesys C1 Generative UI Development
This skill provides comprehensive guidance for developing applications with the Thesys C1 API and GenUI SDK, enabling AI-powered Generative UI applications.
What is Thesys C1?
Thesys C1 is a Generative UI API that dynamically generates interactive UI components from natural language prompts. Unlike traditional AI that returns text/markdown, C1 generates live, interactive interfaces rendered through React components.
Key Features
-
Interactive Components: Forms, charts, tables, buttons that users can interact with
-
Themeable: Consistent brand styling via <ThemeProvider>
-
Real-time UI Streaming: Progressive rendering as responses generate
-
OpenAI-Compatible API: Drop-in replacement for OpenAI SDKs
-
Robust Error Handling: Graceful degradation with onError prop
What Can You Build?
-
Analytics dashboards with dynamic charts
-
Conversational AI agents with rich UI
-
Internal tools and admin panels
-
E-commerce flows with forms and product displays
-
AI assistants like ChatGPT with Generative UI
Quickstart
Prerequisites
-
Node.js 18+ or Python 3.8+
-
Thesys API key from console.thesys.dev
-
Install the required packages:
npm install @thesysai/genui-sdk@latest @crayonai/react-ui@latest @crayonai/react-core@latest @crayonai/stream@latest openai@latest next@latest react@^19.0.0 react-dom@^19.0.0
Next.js Setup
npx create-c1-app my-app cd my-app npm run dev
Python (FastAPI) Setup
git clone https://github.com/thesysdev/template-c1-fastapi.git cd template-c1-fastapi pip install -r requirements.txt uvicorn main:app --reload
In another terminal for frontend:
npm install && npm run dev
Set your API key:
export THESYS_API_KEY=<your-api-key>
Core Components
<C1Component>
- Render C1 Responses
The fundamental component that renders C1 DSL into a functional micro-frontend:
import { C1Component, ThemeProvider } from "@thesysai/genui-sdk"; import "@crayonai/react-ui/styles/index.css";
<ThemeProvider> <C1Component c1Response={response} isStreaming={isLoading} onAction={({ llmFriendlyMessage, humanFriendlyMessage }) => { // Handle button clicks, form submissions }} updateMessage={(updatedResponse) => { // Persist state changes to database }} /> </ThemeProvider>
<C1Chat>
- Full Conversational UI
A batteries-included chat component with thread management:
import { C1Chat } from "@thesysai/genui-sdk"; import "@crayonai/react-ui/styles/index.css";
<C1Chat apiUrl="/api/chat" agentName="My Assistant" logoUrl="/logo.png" formFactor="full-page" // or "side-panel" />
Key Differences
Feature <C1Component>
<C1Chat>
Render C1 DSL ✅ ✅
Streaming ✅ ✅
Forms & Actions ✅ ✅
Message History DIY ✅ Built-in
Thread Management DIY ✅ Built-in
Chat UI ❌ ✅
How C1 Works
The Flow
-
User sends prompt → Frontend
-
Frontend calls backend → Your API
-
Backend calls C1 API → https://api.thesys.dev/v1/embed
-
C1 returns DSL → Backend
-
Backend streams to frontend → Response
-
<C1Component> renders UI → User sees interactive UI
Backend API Call
import OpenAI from "openai";
const client = new OpenAI({ baseURL: "https://api.thesys.dev/v1/embed", apiKey: process.env.THESYS_API_KEY, });
const response = await client.chat.completions.create({ model: "c1/anthropic/claude-sonnet-4/v-20251230", messages: [ { role: "system", content: "You are a helpful assistant." }, { role: "user", content: "Show me a chart of sales data" } ], stream: true, });
C1 Response Structure
C1 responses use an XML-like structure:
<thinking>Analyzing request...</thinking> <content> <!-- Interactive UI components --> </content> <artifact id="report-1"> <!-- Document content like slides/reports --> </artifact>
Supported Models
Stable Models:
-
c1/anthropic/claude-sonnet-4/v-20251230
-
c1/openai/gpt-5/v-20251230
Experimental Models:
-
c1-exp/anthropic/claude-sonnet-4.5/v-20251230
-
c1-exp/anthropic/claude-haiku-4.5/v-20251230
Artifact Model:
- c1/artifact/v-20251230
Backend API Setup
Install Dependencies
npm install openai @crayonai/stream
Create Message Store
// app/api/chat/messageStore.ts import OpenAI from "openai";
export type DBMessage = OpenAI.Chat.ChatCompletionMessageParam & { id?: string };
const messagesStore: { [threadId: string]: DBMessage[] } = {};
export const getMessageStore = (threadId: string) => { if (!messagesStore[threadId]) { messagesStore[threadId] = []; } return { addMessage: (message: DBMessage) => { messagesStore[threadId].push(message); }, getOpenAICompatibleMessageList: () => { return messagesStore[threadId].map((m) => { const { id, ...rest } = m; return rest; }); }, }; };
Create Chat Endpoint
// app/api/chat/route.ts import { NextRequest, NextResponse } from "next/server"; import OpenAI from "openai"; import { transformStream } from "@crayonai/stream"; import { DBMessage, getMessageStore } from "./messageStore";
export async function POST(req: NextRequest) { const { prompt, threadId, responseId } = await req.json() as { prompt: DBMessage; threadId: string; responseId: string; };
const client = new OpenAI({ baseURL: "https://api.thesys.dev/v1/embed", apiKey: process.env.THESYS_API_KEY, });
const messageStore = getMessageStore(threadId); messageStore.addMessage(prompt);
const llmStream = await client.chat.completions.create({ model: "c1/anthropic/claude-sonnet-4/v-20251230", messages: messageStore.getOpenAICompatibleMessageList(), stream: true, });
const responseStream = transformStream( llmStream, (chunk) => chunk.choices[0].delta.content, { onEnd: ({ accumulated }) => { const message = accumulated.filter(Boolean).join(""); messageStore.addMessage({ role: "assistant", content: message, id: responseId, }); }, } ) as ReadableStream;
return new NextResponse(responseStream, { headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache, no-transform", Connection: "keep-alive", }, }); }
Integration Patterns
C1 as Gateway LLM (Recommended)
Replace your existing LLM endpoint with C1:
const client = new OpenAI({ baseURL: "https://api.thesys.dev/v1/embed", // Change from OpenAI apiKey: process.env.THESYS_API_KEY, });
// Use existing tools and system prompts const response = await client.beta.chat.completions.runTools({ model: "c1/anthropic/claude-sonnet-4/v-20251230", messages: [...], tools: existingTools, stream: true, });
C1 as Presentation Layer
Generate text with your LLM, then visualize with C1:
// 1. Get text from your LLM const textResponse = await yourLLM.generate(prompt);
// 2. Visualize with C1 const uiResponse = await c1Client.chat.completions.create({ model: "c1/anthropic/claude-sonnet-4/v-20251230", messages: [{ role: "user", content: textResponse }], });
C1 as a Tool
Expose C1 as a tool your agent can invoke:
const tools = [{ type: "function", function: { name: "generate_ui", description: "Generate interactive UI for user", parameters: { type: "object", properties: { content: { type: "string", description: "Content to visualize" } } } } }];
If the user wants to visualize their own data, refer to visualize-api-endpoint.md.
Additional References
For detailed information on specific topics, see:
-
Conversational UI & Persistence: State management, useThreadManager , useThreadListManager , database persistence
-
Custom Actions, Components & Thinking States: c1_custom_actions , onAction , custom components, useC1State , thinking states
-
Artifacts: Generating, rendering, editing, and exporting reports/presentations
-
Customizations & Styling: <ThemeProvider> , chart palettes, CSS overrides
-
Migration Guide: Step-by-step migration from text-based LLM to Generative UI
Resources
-
Documentation: https://docs.thesys.dev
-
API Reference: https://docs.thesys.dev/api-reference/getting-started
-
Examples: https://github.com/thesysdev/examples
-
Discord Community: https://discord.gg/Pbv5PsqUSv