Loading...
Loading...
Production-ready skill for integrating TheSys C1 Generative UI API into React applications. This skill should be used when building AI-powered interfaces that stream interactive components (forms, charts, tables) instead of plain text responses. Covers complete integration patterns for Vite+React, Next.js, and Cloudflare Workers with OpenAI, Anthropic Claude, and Cloudflare Workers AI. Includes tool calling with Zod schemas, theming, thread management, and production deployment. Prevents 12+ common integration errors and provides working templates for chat interfaces, data visualization, and dynamic forms. Use this skill when implementing conversational UIs, AI assistants, search interfaces, or any application requiring real-time generative user interfaces with streaming LLM responses. Keywords: TheSys C1, TheSys Generative UI, @thesysai/genui-sdk, generative UI, AI UI, streaming UI components, interactive components, AI forms, AI charts, AI tables, conversational UI, AI assistants UI, React generative UI, Vite generative UI, Next.js generative UI, Cloudflare Workers generative UI, OpenAI generative UI, Claude generative UI, Anthropic UI, Cloudflare Workers AI UI, tool calling UI, Zod schemas UI, thread management, theming UI, chat interface, data visualization, dynamic forms, streaming LLM UI
npx skill4agent add jackspace/claudeskillz thesys-generative-uiLLM → Text Response → Developer Parses → Manual UI Code → DisplayLLM → C1 API → Interactive React Components → Displaynpm install @thesysai/genui-sdk @crayonai/react-ui @crayonai/react-core @crayonai/stream
npm install openai zodsrc/App.tsximport "@crayonai/react-ui/styles/index.css";
import { ThemeProvider, C1Component } from "@thesysai/genui-sdk";
import { useState } from "react";
export default function App() {
const [isLoading, setIsLoading] = useState(false);
const [c1Response, setC1Response] = useState("");
const [question, setQuestion] = useState("");
const makeApiCall = async (query: string) => {
setIsLoading(true);
setC1Response("");
try {
const response = await fetch("/api/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ prompt: query }),
});
const data = await response.json();
setC1Response(data.response);
} catch (error) {
console.error("Error:", error);
} finally {
setIsLoading(false);
}
};
return (
<div className="container">
<h1>AI Assistant</h1>
<form onSubmit={(e) => {
e.preventDefault();
makeApiCall(question);
}}>
<input
type="text"
value={question}
onChange={(e) => setQuestion(e.target.value)}
placeholder="Ask me anything..."
/>
<button type="submit" disabled={isLoading}>
{isLoading ? "Processing..." : "Send"}
</button>
</form>
{c1Response && (
<ThemeProvider>
<C1Component
c1Response={c1Response}
isStreaming={isLoading}
updateMessage={(message) => setC1Response(message)}
onAction={({ llmFriendlyMessage }) => {
if (!isLoading) {
makeApiCall(llmFriendlyMessage);
}
}}
/>
</ThemeProvider>
)}
</div>
);
}import express from "express";
import OpenAI from "openai";
import { transformStream } from "@crayonai/stream";
const app = express();
app.use(express.json());
const client = new OpenAI({
baseURL: "https://api.thesys.dev/v1/embed",
apiKey: process.env.THESYS_API_KEY,
});
app.post("/api/chat", async (req, res) => {
const { prompt } = req.body;
const stream = await client.chat.completions.create({
model: "c1/openai/gpt-5/v-20250930", // or any C1-compatible model
messages: [
{ role: "system", content: "You are a helpful assistant." },
{ role: "user", content: prompt },
],
stream: true,
});
// Transform OpenAI stream to C1 response
const c1Stream = transformStream(stream, (chunk) => {
return chunk.choices[0]?.delta?.content || "";
});
res.json({ response: await streamToString(c1Stream) });
});
async function streamToString(stream: ReadableStream) {
const reader = stream.getReader();
let result = "";
while (true) {
const { done, value } = await reader.read();
if (done) break;
result += value;
}
return result;
}
app.listen(3000);npm install @thesysai/genui-sdk @crayonai/react-ui @crayonai/react-core
npm install openaiapp/page.tsx"use client";
import { C1Chat } from "@thesysai/genui-sdk";
import "@crayonai/react-ui/styles/index.css";
export default function Home() {
return (
<div className="min-h-screen">
<C1Chat apiUrl="/api/chat" />
</div>
);
}app/api/chat/route.tsimport { NextRequest, NextResponse } from "next/server";
import OpenAI from "openai";
import { transformStream } from "@crayonai/stream";
const client = new OpenAI({
baseURL: "https://api.thesys.dev/v1/embed",
apiKey: process.env.THESYS_API_KEY,
});
export async function POST(req: NextRequest) {
const { prompt } = await req.json();
const stream = await client.chat.completions.create({
model: "c1/openai/gpt-5/v-20250930",
messages: [
{ role: "system", content: "You are a helpful AI assistant." },
{ role: "user", content: prompt },
],
stream: true,
});
// Transform to C1-compatible stream
const responseStream = transformStream(stream, (chunk) => {
return chunk.choices[0]?.delta?.content || "";
}) as ReadableStream<string>;
return new NextResponse(responseStream, {
headers: {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache, no-transform",
"Connection": "keep-alive",
},
});
}backend/src/index.tsimport { Hono } from "hono";
import { cors } from "hono/cors";
const app = new Hono();
app.use("/*", cors());
app.post("/api/chat", async (c) => {
const { prompt } = await c.req.json();
// Use Cloudflare Workers AI or proxy to OpenAI
const response = await fetch("https://api.thesys.dev/v1/embed/chat/completions", {
method: "POST",
headers: {
"Authorization": `Bearer ${c.env.THESYS_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "c1/openai/gpt-5/v-20250930",
messages: [
{ role: "system", content: "You are a helpful assistant." },
{ role: "user", content: prompt },
],
stream: false, // or handle streaming
}),
});
const data = await response.json();
return c.json(data);
});
export default app;wrangler.jsonc{
"name": "thesys-chat-worker",
"compatibility_date": "2025-10-26",
"main": "backend/src/index.ts",
"vars": {
"ENVIRONMENT": "production"
},
"assets": {
"directory": "dist",
"binding": "ASSETS"
}
}THESYS_API_KEYnpx wrangler secret put THESYS_API_KEY<C1Chat>C1Chatimport { C1Chat } from "@thesysai/genui-sdk";
import "@crayonai/react-ui/styles/index.css";
export default function App() {
return (
<C1Chat
apiUrl="/api/chat"
agentName="My AI Assistant"
logoUrl="https://example.com/logo.png"
/>
);
}apiUrlagentNamelogoUrlthemethreadManagerthreadListManagercustomizeC1import { C1Chat } from "@thesysai/genui-sdk";
import { themePresets } from "@crayonai/react-ui";
<C1Chat
apiUrl="/api/chat"
theme={themePresets.candy} // or 'default', or custom object
/><C1Component>C1Componentimport { C1Component, ThemeProvider } from "@thesysai/genui-sdk";
import "@crayonai/react-ui/styles/index.css";
const [c1Response, setC1Response] = useState("");
const [isStreaming, setIsStreaming] = useState(false);
// ... fetch logic
return (
<ThemeProvider>
<C1Component
c1Response={c1Response}
isStreaming={isStreaming}
updateMessage={(message) => setC1Response(message)}
onAction={({ llmFriendlyMessage }) => {
// Handle interactive actions (button clicks, form submissions)
console.log("User action:", llmFriendlyMessage);
// Make new API call with llmFriendlyMessage
}}
/>
</ThemeProvider>
);c1ResponseisStreamingupdateMessageonActionllmFriendlyMessagerawAction// ❌ Wrong - theme won't apply
<C1Component c1Response={response} />
// ✅ Correct
<ThemeProvider>
<C1Component c1Response={response} />
</ThemeProvider><ThemeProvider><C1Component><C1Chat>import { themePresets } from "@crayonai/react-ui";
// Available presets:
// - themePresets.default
// - themePresets.candy
// ... (check docs for full list)
<C1Chat theme={themePresets.candy} />import { useSystemTheme } from "./hooks/useSystemTheme"; // custom hook
export default function App() {
const systemTheme = useSystemTheme(); // 'light' | 'dark'
return (
<C1Chat
apiUrl="/api/chat"
theme={{ ...themePresets.default, mode: systemTheme }}
/>
);
}const customTheme = {
mode: "dark", // 'light' | 'dark' | 'system'
colors: {
primary: "#3b82f6",
secondary: "#8b5cf6",
background: "#1f2937",
foreground: "#f9fafb",
// ... more colors
},
fonts: {
body: "Inter, sans-serif",
heading: "Poppins, sans-serif",
},
borderRadius: "12px",
spacing: {
base: "16px",
},
};
<C1Chat theme={customTheme} />custom.css/* Override specific component styles */
.c1-chat-container {
max-width: 900px;
margin: 0 auto;
}
.c1-message-user {
background-color: #3b82f6 !important;
}
.c1-message-assistant {
background-color: #6b7280 !important;
}import "@crayonai/react-ui/styles/index.css";
import "./custom.css"; // AFTER the default stylesnpm install openaiimport OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.thesys.dev/v1/embed",
apiKey: process.env.THESYS_API_KEY, // TheSys API key
});// GPT 5 (Stable - Recommended for Production)
model: "c1/openai/gpt-5/v-20250930"
// GPT 4.1 (Experimental)
model: "c1-exp/openai/gpt-4.1/v-20250617"const response = await client.chat.completions.create({
model: "c1/openai/gpt-5/v-20250930",
messages: [
{
role: "system",
content: "You are a helpful assistant that generates interactive UI components.",
},
{
role: "user",
content: "Show me a comparison table of the top 3 project management tools.",
},
],
stream: true, // Enable streaming
temperature: 0.7,
max_tokens: 2000,
});import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.thesys.dev/v1/embed",
apiKey: process.env.THESYS_API_KEY,
});// Claude Sonnet 4 (Stable - Recommended for Production)
model: "c1/anthropic/claude-sonnet-4/v-20250930"
// Claude 3.5 Haiku (Experimental)
model: "c1-exp/anthropic/claude-3.5-haiku/v-20250709"⚠️ Deprecated Models: Claude 3.5 Sonnet and Claude 3.7 Sonnet are no longer recommended. Use the stable Claude Sonnet 4 version above.
const response = await client.chat.completions.create({
model: "c1/anthropic/claude-sonnet-4/v-20250930",
messages: [
{
role: "system",
content: "You are Claude, an AI assistant that creates interactive interfaces.",
},
{
role: "user",
content: "Create a product comparison chart for electric vehicles.",
},
],
stream: true,
temperature: 0.8,
max_tokens: 4096,
});| Model | Model ID | Input Price | Output Price | Context | Max Output |
|---|---|---|---|---|---|
| Claude Sonnet 4 | | $6.00/M | $18.00/M | 180K | 64K |
| GPT 5 | | $2.50/M | $12.50/M | 380K | 128K |
| GPT 4.1 (exp) | | $4.00/M | $10.00/M | 1M | 32K |
| Claude 3.5 Haiku (exp) | | $1.60/M | $5.00/M | 180K | 8K |
c1/c1-exp/Model Versions: Model identifiers include version dates (e.g.,). Always check the TheSys Playground for the latest stable versions.v-20250930
// In your Cloudflare Worker
export default {
async fetch(request: Request, env: Env) {
// Use Workers AI directly (cheaper for some use cases)
const aiResponse = await env.AI.run('@cf/meta/llama-3-8b-instruct', {
messages: [
{ role: "system", content: "You are a helpful assistant." },
{ role: "user", content: "Hello!" },
],
});
// Then transform to C1 format and send to frontend
// ...
}
};// Option 1: Use Workers AI for processing, C1 for UI generation
const thinkingResponse = await env.AI.run('@cf/meta/llama-3-8b-instruct', {
messages: [{ role: "user", content: "Analyze this data..." }],
});
// Then use C1 to generate UI from the analysis
const c1Response = await fetch("https://api.thesys.dev/v1/embed/chat/completions", {
method: "POST",
headers: {
"Authorization": `Bearer ${env.THESYS_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "c1/openai/gpt-5/v-20250930",
messages: [
{
role: "system",
content: "Generate a chart visualization for this data.",
},
{
role: "user",
content: thinkingResponse.response,
},
],
}),
});pip install thesys-genui-sdk openaifrom fastapi import FastAPI
from fastapi.responses import StreamingResponse
from thesys_genui_sdk import with_c1_response, write_content
import openai
import os
app = FastAPI()
client = openai.OpenAI(
base_url="https://api.thesys.dev/v1/embed",
api_key=os.getenv("THESYS_API_KEY")
)
@app.post("/api/chat")
@with_c1_response # Automatically handles streaming headers
async def chat_endpoint(request: dict):
prompt = request.get("prompt")
stream = client.chat.completions.create(
model="c1/anthropic/claude-sonnet-4/v-20250930",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": prompt}
],
stream=True
)
# Stream chunks to frontend
async def generate():
for chunk in stream:
content = chunk.choices[0].delta.content
if content:
yield write_content(content)
return StreamingResponse(generate(), media_type="text/event-stream")@with_c1_responsewrite_contentfrom flask import Flask, request, Response
from thesys_genui_sdk import with_c1_response, write_content
import openai
import os
app = Flask(__name__)
client = openai.OpenAI(
base_url="https://api.thesys.dev/v1/embed",
api_key=os.getenv("THESYS_API_KEY")
)
@app.route("/api/chat", methods=["POST"])
@with_c1_response
def chat():
data = request.get_json()
prompt = data.get("prompt")
stream = client.chat.completions.create(
model="c1/openai/gpt-5/v-20250930",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": prompt}
],
stream=True
)
def generate():
for chunk in stream:
content = chunk.choices[0].delta.content
if content:
yield write_content(content)
return Response(generate(), mimetype="text/event-stream")try {
const response = await client.chat.completions.create({
model: "c1/openai/gpt-5/v-20250930",
messages: [...],
stream: true,
});
// Process stream...
} catch (error) {
if (error.status === 429) {
// Rate limit - implement exponential backoff
await new Promise(resolve => setTimeout(resolve, 1000));
// Retry...
} else if (error.status === 401) {
// Invalid API key
console.error("Authentication failed. Check THESYS_API_KEY");
} else {
// Other errors
console.error("API Error:", error);
}
}import { transformStream } from "@crayonai/stream";
const llmStream = await client.chat.completions.create({
model: "c1/openai/gpt-5/v-20250930",
messages: [...],
stream: true,
});
// Transform OpenAI stream to C1 stream
const c1Stream = transformStream(llmStream, (chunk) => {
return chunk.choices[0]?.delta?.content || "";
}) as ReadableStream<string>;
return new Response(c1Stream, {
headers: {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache, no-transform",
"Connection": "keep-alive",
},
});import { z } from "zod";
import zodToJsonSchema from "zod-to-json-schema";
// Define the tool schema
const webSearchSchema = z.object({
query: z.string().describe("The search query"),
max_results: z.number().int().min(1).max(10).default(5)
.describe("Maximum number of results to return"),
});
// Convert to OpenAI tool format
export const webSearchTool = {
type: "function" as const,
function: {
name: "web_search",
description: "Search the web for current information",
parameters: zodToJsonSchema(webSearchSchema),
},
};import { z } from "zod";
// Discriminated union for different product types
const productOrderSchema = z.discriminatedUnion("type", [
z.object({
type: z.literal("gloves"),
size: z.enum(["S", "M", "L", "XL"]),
color: z.string(),
quantity: z.number().int().min(1),
}),
z.object({
type: z.literal("hat"),
style: z.enum(["beanie", "baseball", "fedora"]),
color: z.string(),
quantity: z.number().int().min(1),
}),
z.object({
type: z.literal("scarf"),
length: z.enum(["short", "medium", "long"]),
material: z.enum(["wool", "cotton", "silk"]),
quantity: z.number().int().min(1),
}),
]);
const createOrderSchema = z.object({
customer_email: z.string().email(),
items: z.array(productOrderSchema).min(1),
shipping_address: z.object({
street: z.string(),
city: z.string(),
state: z.string(),
zip: z.string(),
}),
});
export const createOrderTool = {
type: "function" as const,
function: {
name: "create_order",
description: "Create a new order for products",
parameters: zodToJsonSchema(createOrderSchema),
},
};// tools.ts
import { TavilySearchAPIClient } from "@tavily/core";
const tavily = new TavilySearchAPIClient({
apiKey: process.env.TAVILY_API_KEY,
});
export async function executeWebSearch(query: string, max_results: number) {
const results = await tavily.search(query, {
maxResults: max_results,
includeAnswer: true,
});
return {
query,
results: results.results.map((r) => ({
title: r.title,
url: r.url,
snippet: r.content,
})),
answer: results.answer,
};
}
export async function executeCreateOrder(orderData: z.infer<typeof createOrderSchema>) {
// Validate with Zod
const validated = createOrderSchema.parse(orderData);
// Save to database
const orderId = await saveOrderToDatabase(validated);
return {
success: true,
orderId,
message: `Order ${orderId} created successfully`,
};
}import { NextRequest, NextResponse } from "next/server";
import OpenAI from "openai";
import { transformStream } from "@crayonai/stream";
import { webSearchTool, createOrderTool } from "./tools";
const client = new OpenAI({
baseURL: "https://api.thesys.dev/v1/embed",
apiKey: process.env.THESYS_API_KEY,
});
export async function POST(req: NextRequest) {
const { prompt } = await req.json();
const llmStream = await client.beta.chat.completions.runTools({
model: "c1/anthropic/claude-sonnet-4/v-20250930",
messages: [
{
role: "system",
content: "You are a helpful shopping assistant. Use tools to search for products and create orders.",
},
{
role: "user",
content: prompt,
},
],
stream: true,
tools: [webSearchTool, createOrderTool],
toolChoice: "auto", // Let AI decide when to use tools
});
// Handle tool execution
llmStream.on("message", async (event) => {
if (event.tool_calls) {
for (const toolCall of event.tool_calls) {
if (toolCall.function.name === "web_search") {
const args = JSON.parse(toolCall.function.arguments);
const result = await executeWebSearch(args.query, args.max_results);
// Send result back to LLM...
} else if (toolCall.function.name === "create_order") {
const args = JSON.parse(toolCall.function.arguments);
const result = await executeCreateOrder(args);
// Send result back to LLM...
}
}
}
});
const responseStream = transformStream(llmStream, (chunk) => {
return chunk.choices[0]?.delta?.content || "";
}) as ReadableStream<string>;
return new NextResponse(responseStream, {
headers: {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache, no-transform",
"Connection": "keep-alive",
},
});
}onAction<C1Component
c1Response={c1Response}
onAction={async ({ llmFriendlyMessage, rawAction }) => {
console.log("Tool action triggered:", rawAction);
// Make API call with llmFriendlyMessage to continue conversation
await makeApiCall(llmFriendlyMessage);
}}
/>GET /api/threadsPOST /api/threadsPUT /api/threads/:idDELETE /api/threads/:idGET /api/threads/:id/messagesimport {
useThreadListManager,
useThreadManager,
} from "@thesysai/genui-sdk";
import { Thread, Message, UserMessage } from "@crayonai/react-core";
export default function App() {
const threadListManager = useThreadListManager({
// Fetch all threads
fetchThreadList: async (): Promise<Thread[]> => {
const response = await fetch("/api/threads");
return response.json();
},
// Delete thread
deleteThread: async (threadId: string): Promise<void> => {
await fetch(`/api/threads/${threadId}`, { method: "DELETE" });
},
// Update thread title
updateThread: async (thread: Thread): Promise<Thread> => {
const response = await fetch(`/api/threads/${thread.threadId}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ title: thread.title }),
});
return response.json();
},
// Create new thread
createThread: async (firstMessage: UserMessage): Promise<Thread> => {
const response = await fetch("/api/threads", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
title: firstMessage.message || "New Chat",
}),
});
return response.json();
},
// URL synchronization
onSwitchToNew: () => {
window.history.replaceState(null, "", window.location.pathname);
},
onSelectThread: (threadId: string) => {
const url = new URL(window.location.href);
url.searchParams.set("threadId", threadId);
window.history.replaceState(null, "", url.toString());
},
});
const threadManager = useThreadManager({
threadListManager,
// Load messages for selected thread
loadThread: async (threadId: string): Promise<Message[]> => {
const response = await fetch(`/api/threads/${threadId}/messages`);
return response.json();
},
// Handle message updates (e.g., feedback)
onUpdateMessage: async ({ message }: { message: Message }) => {
if (threadListManager.selectedThreadId) {
await fetch(
`/api/threads/${threadListManager.selectedThreadId}/message`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(message),
}
);
}
},
});
return (
<C1Chat
threadManager={threadManager}
threadListManager={threadListManager}
/>
);
}import { makeC1Response } from "@thesysai/genui-sdk/server";
export async function POST(req: NextRequest) {
const c1Response = makeC1Response();
// Initial thinking state
c1Response.writeThinkItem({
title: "Thinking…",
description: "Analyzing your question and planning the response.",
});
const { prompt } = await req.json();
// Update thinking state when calling tools
const llmStream = await client.beta.chat.completions.runTools({
model: "c1/anthropic/claude-sonnet-4/v-20250930",
messages: [...],
tools: [
getWebSearchTool(() => {
c1Response.writeThinkItem({
title: "Searching the web…",
description: "Finding the most relevant and up-to-date information.",
});
}),
],
});
transformStream(
llmStream,
(chunk) => {
const content = chunk.choices[0]?.delta?.content;
if (content) {
c1Response.writeContent(content);
}
return content;
},
{
onEnd: () => {
c1Response.end();
},
}
);
return new NextResponse(c1Response.responseStream, {
headers: {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache, no-transform",
"Connection": "keep-alive",
},
});
}// CustomThink.tsx
import { ThinkItem } from "@crayonai/react-core";
export function CustomThink({ item }: { item: ThinkItem }) {
return (
<div className="custom-think">
<div className="spinner" />
<div>
<h4>{item.title}</h4>
<p>{item.description}</p>
</div>
</div>
);
}
// In your app
<C1Chat
apiUrl="/api/chat"
customizeC1={{ thinkComponent: CustomThink }}
/>import { C1ShareThread } from "@thesysai/genui-sdk";
const selectedThreadId = threadListManager.selectedThreadId;
<C1ShareThread
generateShareLink={
!selectedThreadId
? undefined
: async () => {
const baseUrl = window.location.origin;
return `${baseUrl}/shared/${selectedThreadId}`;
}
}
/>// app/shared/[threadId]/page.tsx
"use client";
import { C1ChatViewer } from "@thesysai/genui-sdk";
import { Message } from "@crayonai/react-core";
import { use, useEffect, useState } from "react";
import "@crayonai/react-ui/styles/index.css";
export default function ViewSharedThread({
params,
}: {
params: Promise<{ threadId: string }>;
}) {
const { threadId } = use(params);
const [messages, setMessages] = useState<Message[]>([]);
useEffect(() => {
const fetchMessages = async () => {
const response = await fetch(`/api/share/${threadId}`);
const data = await response.json();
setMessages(data);
};
fetchMessages();
}, [threadId]);
if (!messages.length) return <div>Loading...</div>;
return <C1ChatViewer messages={messages} />;
}// ❌ Bad - loses data on restart
const messageStore = new Map<string, Message[]>();
// ✅ Good - use a database
import { db } from "./database"; // D1, PostgreSQL, etc.
export async function saveMessage(threadId: string, message: Message) {
await db.insert(messages).values({
threadId,
role: message.role,
content: message.content,
createdAt: new Date(),
});
}
export async function getThreadMessages(threadId: string): Promise<Message[]> {
return db.select().from(messages).where(eq(messages.threadId, threadId));
}import { auth } from "@clerk/nextjs";
export async function POST(req: NextRequest) {
const { userId } = auth();
if (!userId) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
// Proceed with chat logic, scoping to user
const userThreads = await db
.select()
.from(threads)
.where(eq(threads.userId, userId));
// ...
}import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(10, "1 m"), // 10 requests per minute
});
export async function POST(req: NextRequest) {
const { userId } = auth();
const { success } = await ratelimit.limit(userId);
if (!success) {
return NextResponse.json(
{ error: "Rate limit exceeded. Please try again later." },
{ status: 429 }
);
}
// Proceed...
}import { ErrorBoundary } from "react-error-boundary";
function ErrorFallback({ error, resetErrorBoundary }) {
return (
<div role="alert">
<h2>Something went wrong</h2>
<pre>{error.message}</pre>
<button onClick={resetErrorBoundary}>Try again</button>
</div>
);
}
export default function App() {
return (
<ErrorBoundary FallbackComponent={ErrorFallback}>
<C1Chat apiUrl="/api/chat" />
</ErrorBoundary>
);
}// 1. Lazy load C1Chat
import { lazy, Suspense } from "react";
const C1Chat = lazy(() =>
import("@thesysai/genui-sdk").then((mod) => ({ default: mod.C1Chat }))
);
export default function App() {
return (
<Suspense fallback={<div>Loading chat...</div>}>
<C1Chat apiUrl="/api/chat" />
</Suspense>
);
}
// 2. Memoize expensive computations
import { useMemo } from "react";
const threadListManager = useMemo(
() =>
useThreadListManager({
// ... config
}),
[] // Empty deps - only create once
);// ✅ Use transformStream helper
import { transformStream } from "@crayonai/stream";
const c1Stream = transformStream(llmStream, (chunk) => {
return chunk.choices[0]?.delta?.content || ""; // Fallback to empty string
}) as ReadableStream<string>;// ✅ System prompt MUST be first
const messages = [
{ role: "system", content: "You are a helpful assistant." }, // FIRST!
...conversationHistory,
{ role: "user", content: userPrompt },
];
// ❌ Wrong - system prompt after user messages
const messages = [
{ role: "user", content: "Hello" },
{ role: "system", content: "..." }, // TOO LATE
];TypeError: Cannot read property 'X' of undefined| C1 Version | @thesysai/genui-sdk | @crayonai/react-ui | @crayonai/react-core |
|---|---|---|---|
| v-20250930 | ~0.6.40 | ~0.8.42 | ~0.7.6 |
# Update to compatible versions
npm install @thesysai/genui-sdk@0.6.40 @crayonai/react-ui@0.8.42 @crayonai/react-core@0.7.6ThemeProvider// ❌ Wrong
<C1Component c1Response={response} />
// ✅ Correct
<ThemeProvider theme={customTheme}>
<C1Component c1Response={response} />
</ThemeProvider>// 1. Enable streaming in API call
const stream = await client.chat.completions.create({
model: "c1/openai/gpt-5/v-20250930",
messages: [...],
stream: true, // ✅ IMPORTANT
});
// 2. Set proper response headers
return new NextResponse(responseStream, {
headers: {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache, no-transform",
"Connection": "keep-alive",
},
});
// 3. Pass isStreaming prop
<C1Component
c1Response={response}
isStreaming={true} // ✅ Shows loading indicator
/>import { z } from "zod";
import zodToJsonSchema from "zod-to-json-schema";
// ✅ Proper Zod schema with descriptions
const toolSchema = z.object({
query: z.string().describe("Search query"), // DESCRIBE all fields
limit: z.number().int().min(1).max(100).describe("Max results"),
});
// ✅ Convert to OpenAI format
const tool = {
type: "function" as const,
function: {
name: "search_web",
description: "Search the web for information", // Clear description
parameters: zodToJsonSchema(toolSchema), // Convert schema
},
};
// ✅ Validate incoming tool calls
const args = toolSchema.parse(JSON.parse(toolCall.function.arguments));// ✅ Correct import order
import "@crayonai/react-ui/styles/index.css"; // C1 styles FIRST
import "./your-app.css"; // Your styles SECOND
// In your CSS, use specificity if needed
.your-custom-class .c1-message {
/* Override specific styles */
}# Update packages
npm install @thesysai/genui-sdk@latest @crayonai/react-ui@latest @crayonai/react-core@latest
# If still errors, check tsconfig.json
{
"compilerOptions": {
"moduleResolution": "bundler", // or "node16"
"skipLibCheck": true // Skip type checking for node_modules
}
}Access-Control-Allow-Origin// Next.js API Route
export async function POST(req: NextRequest) {
const response = new NextResponse(stream, {
headers: {
"Content-Type": "text/event-stream",
"Access-Control-Allow-Origin": "*", // Or specific domain
"Access-Control-Allow-Methods": "POST, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type",
},
});
return response;
}
// Express
app.use(cors({
origin: "http://localhost:5173", // Your frontend URL
methods: ["POST", "OPTIONS"],
}));async function callApiWithRetry(apiCall, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await apiCall();
} catch (error) {
if (error.status === 429 && i < maxRetries - 1) {
const waitTime = Math.pow(2, i) * 1000; // Exponential backoff
await new Promise((resolve) => setTimeout(resolve, waitTime));
continue;
}
throw error;
}
}
}
// Usage
const response = await callApiWithRetry(() =>
client.chat.completions.create({...})
);401 Unauthorized# .env file (Next.js)
THESYS_API_KEY=your_api_key_here
# Verify it's loaded
# In your code:
if (!process.env.THESYS_API_KEY) {
throw new Error("THESYS_API_KEY is not set");
}
# For Vite, use VITE_ prefix for client-side
VITE_THESYS_API_KEY=your_key # Client-side
THESYS_API_KEY=your_key # Server-side
# Access in Vite
const apiKey = import.meta.env.VITE_THESYS_API_KEY;
# For Cloudflare Workers, use wrangler secrets
npx wrangler secret put THESYS_API_KEYtemplates/basic-chat.tsxcustom-component.tsxtool-calling.tsxtheme-dark-mode.tsxpackage.jsonapp/page.tsxapp/api/chat/route.tstool-calling-route.tspackage.jsonworker-backend.tsfrontend-setup.tsxwrangler.jsonctheme-config.tstool-schemas.tsstreaming-utils.tsreferences/component-api.mdai-provider-setup.mdtool-calling-guide.mdtheme-customization.mdcommon-errors.mdscripts/install-dependencies.shscripts/check-versions.sh/websites/thesys_devtemplates/THESYS_API_KEYnpm installreferences/common-errors.md