thesys-generative-ui について
TheSys C1 を使った AI インターフェース構築の開発スキルです。LLM の応答をストリーミング対応のインタラクティブな React コンポーネントとして表示し、既成チャットや独自描画、フロントエンドとバックエンドの連携方法を扱います。
主な機能
- 01応答をインタラクティブな React UI に変換
- 02インターフェースのストリーミング生成
- 03既成の C1Chat または C1Component の利用
- 04テーマと UI アクションのカスタマイズ
- 05JavaScript と Python バックエンドとの連携
- 06Zod スキーマによるツール呼び出し設定
活用シーン
- 01チャット画面へのインタラクティブ UI 追加
- 02AI 生成のチャートやテーブルの表示
- 03動的フォームやワークフローの生成
- 04構造化された検索結果の構築
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ jackspace/ claudeskillz --skill thesys-generative-uiインストール後、Claude Code で/thesys-generative-uiを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
CHANGELOG.mdREADME.mdSKILL.json詳細
TheSys Generative UI の統合
TheSys C1 Generative UI API を使って AI 搭載インターフェースを構築するための完全なスキルです。LLM の応答を、ストリーミング対応のインタラクティブな React コンポーネントに変換します。
TheSys C1 とは?
TheSys C1 は、Large Language Model(LLM)の応答をプレーンテキストではなく、ライブでインタラクティブな React コンポーネントに変換する Generative UI API です。テキストの壁を表示する代わりに、AI アプリケーションではフォーム、チャート、テーブル、検索結果、カスタム UI 要素をリアルタイムでストリーミングできます。
主な革新
従来の LLM アプリケーションでは、テキストが返されるため、開発者が手動で UI に変換する必要があります。
LLM → Text Response → Developer Parses → Manual UI Code → Display
TheSys C1 は、この手作業をなくします。
LLM → C1 API → Interactive React Components → Display
実環境での効果
- 83%高いエンゲージメント - ユーザーはテキストの壁よりもインタラクティブなコンポーネントを好みます
- 開発速度が10倍 - テキストから UI への手動変換が不要です
- コストを80%削減 - 開発時間とメンテナンスを削減します
- 本番環境に対応 - AI ネイティブ製品を開発するチームで利用されています
このスキルを使用する場面
以下を構築する場合に、このスキルを使用してください。
-
リッチ UI を備えたチャットインターフェース
- テキスト以上の機能が必要な会話型インターフェース
- フォームやアクションを備えたカスタマーサポート用チャットボット
- データの可視化を表示する AI アシスタント
-
データ可視化アプリケーション
- AI が生成したチャートを備えた分析ダッシュボード
- 動的なテーブルを備えたビジネスインテリジェンスツール
- 構造化された結果を表示する検索インターフェース
-
動的なフォーム生成
- E コマースの商品コンフィギュレーター
- AI 主導のマルチステップワークフロー
- インテリジェントなフォームを使用したデータ収集
-
AI コパイロットとアシスタント
- コードスニペットやドキュメントを備えた開発者向けツール
- インタラクティブなレッスンを備えた教育プラットフォーム
- 引用や参考資料を備えたリサーチツール
-
検索と探索
- 構造化された結果を表示するセマンティック検索
- 分析結果をハイライト表示するドキュメント分析
- インタラクティブな回答を備えたナレッジベース
このスキルで防げるエラー
- ❌ ストリーミング設定の誤りによるエージェントの空の応答
- ❌ メッセージ配列の問題によってモデルがシステムプロンプトを無視する
- ❌ SDK と API 間のバージョン互換性エラー
- ❌ ThemeProvider がないためテーマが適用されない
- ❌ 不適切な応答変換によるストリーミングの失敗
- ❌ 無効な Zod スキーマによるツール呼び出しのバグ
- ❌ 永続化の欠如によるスレッド状態の消失
- ❌ インポート順序の問題による CSS の競合
- ❌ 古い型定義による TypeScript エラー
- ❌ ヘッダーの不足による CORS エラー
- ❌ リトライロジックがないことによるレート制限時のクラッシュ
- ❌ 環境の問題による認証トークンエラー
フレームワーク別クイックスタート
Vite + React のセットアップ
カスタムバックエンド向けの最も柔軟なセットアップです(お好みのスタック)。
1. 依存関係をインストールする
npm install @thesysai/genui-sdk @crayonai/react-ui @crayonai/react-core @crayonai/stream
npm install openai zod
2. チャットコンポーネントを作成する
ファイル:src/App.tsx
import "@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>
);
}
3. バックエンド API を設定する(Express の例)
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);
Next.js App Router のセットアップ
最も人気のあるフレームワークで、API ルートを備えたフルスタック構成です。
1. 依存関係をインストールする
npm install @thesysai/genui-sdk @crayonai/react-ui @crayonai/react-core
npm install openai
2. チャットページコンポーネントを作成する
ファイル:app/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>
);
}
3. API ルートハンドラーを作成する
ファイル:app/api/chat/route.ts
import { 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",
},
});
}
これで完了です! Generative UI チャットインターフェースが動作するようになりました。
Cloudflare Workers + 静的アセットのセットアップ
お使いのスタック:Workers バックエンドと Vite+React フロントエンドです。
1. Worker バックエンドを作成する(Hono)
ファイル:backend/src/index.ts
import { 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;
2. フロントエンドをセットアップする(Vite+React と同様)
上記の Vite+React の例を使用し、API 呼び出しが Worker のエンドポイントを参照するように設定してください。
3. Wrangler を設定する
ファイル: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_KEY をシークレットとして追加します。
npx wrangler secret put THESYS_API_KEY
コアコンポーネント
<C1Chat> - 組み込みチャットコンポーネント
使用する場面:最小限のセットアップで会話型インターフェースを構築する場合。
C1Chat コンポーネントは、以下の機能を組み込んだ、フル機能のチャット UI です。
- メッセージ履歴
- ストリーミング応答
- スレッド管理
- ローディング状態
- エラー処理
- レスポンシブデザイン
基本的な使い方
import { 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"
/>
);
}
主な Props
apiUrl(必須)- チャット補完用のバックエンドエンドポイントagentName- AI エージェントの表示名logoUrl- エージェントのロゴ/アバターtheme- カスタムテーマオブジェクト(「テーマ設定」セクションを参照)threadManager- 複数スレッドのサポート用(上級者向け)threadListManager- スレッドリスト UI 用(上級者向け)customizeC1- カスタムコンポーネント(フッター、思考状態)
テーマを使用する
import { C1Chat } from "@thesysai/genui-sdk";
import { themePresets } from "@crayonai/react-ui";
<C1Chat
apiUrl="/api/chat"
theme={themePresets.candy} // or 'default', or custom object
/>
<C1Component> - カスタム統合コンポーネント
使用する場面:状態管理と UI レイアウトを完全に制御する必要がある場合です。
C1Component は低レベルのレンダラーです。以下はご自身で処理します。
- データの取得
- 状態の管理
- レイアウト構造
- エラーバウンダリー
基本的な使い方
import { 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>
);
主な Props
c1Response(必須)- C1 API のレスポンス文字列isStreaming- レスポンスのストリーミングが継続中かどうか(読み込みインジケーターを表示)updateMessage- ストリーミング中のレスポンス更新用コールバックonAction- 生成された UI に対するユーザー操作用コールバックllmFriendlyMessage:LLM に送り返すために事前フォーマットされたメッセージrawAction:コンポーネントからの生のアクションデータ
重要:ThemeProvider でラップする必要があります
// ❌ Wrong - theme won't apply
<C1Component c1Response={response} />
// ✅ Correct
<ThemeProvider>
<C1Component c1Response={response} />
</ThemeProvider>
<ThemeProvider> - テーマ設定とカスタマイズ
使用する場面:<C1Component> を使用する場合は常にラップするか、<C1Chat> の外観をカスタマイズします。
テーマプリセット
TheSys には、あらかじめ用意されたテーマが含まれています。
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} />
CSS による上書き
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 styles
AI プロバイダーとの統合
TheSys C1 API は OpenAI 互換です。つまり、OpenAI の API 形式を使用するあらゆる LLM プロバイダーで動作します。
OpenAI との統合
セットアップ
npm install openai
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.thesys.dev/v1/embed",
apiKey: process.env.THESYS_API_KEY, // TheSys API key
});
モデルの選択
TheSys は C1 を通じて OpenAI モデルをサポートしています。
// 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,
});
Anthropic(Claude)との統合
セットアップ
TheSys C1 は、OpenAI 互換エンドポイントを介して Anthropic の Claude モデルをサポートしています。
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"
⚠️ 非推奨モデル:Claude 3.5 Sonnet と Claude 3.7 Sonnet は、推奨されなくなりました。上記の安定版 Claude Sonnet 4 を使用してください。
Claude を使用した例
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,
});
モデルの仕様と料金
以下の表は、TheSys C1 API で利用できる現在の安定版および実験的なモデルを示しています。
| モデル | モデル ID | 入力料金 | 出力料金 | コンテキスト | 最大出力 |
|---|---|---|---|---|---|
| Claude Sonnet 4 | c1/anthropic/claude-sonnet-4/v-20250930 | $6.00/M | $18.00/M | 180K | 64K |
| GPT 5 | c1/openai/gpt-5/v-20250930 | $2.50/M | $12.50/M | 380K | 128K |
| GPT 4.1 (exp) | c1-exp/openai/gpt-4.1/v-20250617 | $4.00/M | $10.00/M | 1M | 32K |
| Claude 3.5 Haiku (exp) | c1-exp/anthropic/claude-3.5-haiku/v-20250709 | $1.60/M | $5.00/M | 180K | 8K |
料金に関する注意:
- 料金は 100 万トークン(M)あたりです
- エンドポイントの種類(embed または visualize)に関係なく、モデル名に基づいて料金が設定されます
- 安定版モデル(
c1/が先頭に付くもの)は、本番環境での使用を推奨します - 実験的なモデル(
c1-exp/が先頭に付くもの)はテスト用で、動作が異なる場合があります
モデルのバージョン:モデル識別子にはバージョンの日付(例:
v-20250930)が含まれています。最新の安定版については、常に TheSys Playground を確認してください。
Cloudflare Workers AI との統合
Workers AI Binding を使ったセットアップ
// 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
// ...
}
};
ハイブリッド方式:Workers AI + C1
// 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,
},
],
}),
});
Python バックエンドとの統合
TheSys は、FastAPI、Flask、または Django を使用したバックエンド実装向けに Python SDK を提供しています。
セットアップ
pip install thesys-genui-sdk openai
FastAPI の例
from 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_responseデコレーター:ストリーミングに適切なレスポンスヘッダーを自動的に設定しますwrite_contentヘルパー:C1Component のレンダリング用にチャンクをフォーマットします- フレームワークに依存しません:FastAPI、Flask、Django など、あらゆる Python Web フレームワークで動作します
Flask の例
from 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);
}
}
transformStream によるストリーミング
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",
},
});
Zod スキーマを使ったツール呼び出し
ツール呼び出しを使うと、AI が関数を呼び出し、データ収集、外部 API 呼び出し、複雑なワークフローのためのインタラクティブな UI を表示できます。
1. Zod でツールを定義する
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),
},
};
2. より複雑な例:注文管理
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),
},
};
3. ツールの実行を実装する
// 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`,
};
}
4. API ルートにツールを統合する
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",
},
});
}
5. UI にツールの結果を表示する
C1Component はツールとのやり取りをフォームとして自動的にレンダリングし、結果を表示します。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);
}}
/>
高度な機能
スレッド管理(複数の会話への対応)
スレッドの切り替え、履歴、永続化により、ユーザーが複数の会話スレッドを利用できるようにします。
1. スレッド API を定義する
バックエンドのエンドポイントを作成します。
GET /api/threads- すべてのスレッドを一覧表示POST /api/threads- 新しいスレッドを作成PUT /api/threads/:id- スレッドのタイトルを更新DELETE /api/threads/:id- スレッドを削除GET /api/threads/:id/messages- スレッドのメッセージを読み込む
2. スレッドマネージャーを実装する
import {
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}
/>
);
}
思考状態(進行状況インジケーター)
処理中に AI が何をしているか(Web 検索、データ分析など)をユーザーに表示します。
1. サーバー側:Think アイテムを書き込む
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",
},
});
}
2. カスタム Think コンポーネント
// 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 }}
/>
メッセージとスレッドの共有
公開 URL を使ってユーザーが会話を共有できるようにします。
1. 共有リンクを生成する
import { C1ShareThread } from "@thesysai/genui-sdk";
const selectedThreadId = threadListManager.selectedThreadId;
<C1ShareThread
generateShareLink={
!selectedThreadId
? undefined
: async () => {
const baseUrl = window.location.origin;
return `${baseUrl}/shared/${selectedThreadId}`;
}
}
/>
2. 共有ビューのページを作成する
// 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));
}
認証の統合(Clerk の例)
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
);
よくあるエラーと解決策
1. エージェントの応答が空になる
問題:AI の応答が空になり、UI に何も表示されません。
原因:ストリーミング変換またはレスポンス形式が正しくありません。
解決策:
// ✅ 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>;
2. モデルがシステムプロンプトに従わない
問題:AI がシステムプロンプトの指示を無視します。
原因:システムプロンプトがメッセージ配列の先頭にないか、形式が正しくありません。
解決策:
// ✅ 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
];
3. バージョンの互換性エラー
問題:TypeError: Cannot read property 'X' of undefined またはコンポーネントのレンダリングエラーが発生します。
原因:SDK のバージョンが一致していません。
解決策:互換性マトリックスを確認してください。
| 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/[email protected] @crayonai/[email protected] @crayonai/[email protected]
4. テーマが適用されない
問題:UI コンポーネントにカスタムテーマが反映されません。
原因:ThemeProvider ラッパーがありません。
解決策:
// ❌ Wrong
<C1Component c1Response={response} />
// ✅ Correct
<ThemeProvider theme={customTheme}>
<C1Component c1Response={response} />
</ThemeProvider>
5. ストリーミングが動作しない
問題:UI がリアルタイムで更新されず、レスポンス全体を待ちます。
原因:ストリーミングを使用していないか、レスポンスヘッダーが正しくありません。
解決策:
// 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
/>
6. ツール呼び出しの失敗
問題:ツールが実行されないか、検証エラーが発生します。
原因:Zod スキーマが無効であるか、ツールの形式が正しくありません。
解決策:
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));
7. スレッドの状態が保持されない
問題:ページを更新するとスレッドが消えます。
原因:バックエンドで永続化しておらず、インメモリストレージを使用しています。
解決策:データベースストレージを実装してください(「本番環境向けパターン」セクションを参照)。
8. CSS の競合
問題:C1 コンポーネントのスタイルがアプリのスタイルと競合します。
原因:CSS のインポート順、またはグローバルスタイルによる上書きです。
解決策:
// ✅ 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 */
}
9. TypeScript の型エラー
問題:TypeScript で型が見つからない、または型に互換性がないというエラーが発生します。
原因:パッケージのバージョンが古い、または型定義が不足しています。
解決策:
# 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
}
}
10. API の CORS エラー
問題:バックエンドの呼び出し時に Access-Control-Allow-Origin エラーが発生します。
原因:API レスポンスに CORS ヘッダーがありません。
解決策:
// 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"],
}));
11. レート制限の問題
問題:API 呼び出しが 429 エラーで失敗し、再試行の仕組みがありません。
原因:レート制限に対するバックオフのロジックがありません。
解決策:
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({...})
);
12. 認証トークンのエラー
問題:API キーを設定しているのに 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_KEY
テンプレートとサンプル
このスキルには、templates/ ディレクトリに 15 個以上の動作するテンプレートが含まれています。
Vite + React(5 個のテンプレート)
basic-chat.tsx- カスタムバックエンドを使用した最小構成の C1Chat セットアップcustom-component.tsx- 手動で状態を管理する C1Component の使用例tool-calling.tsx- ウェブ検索とデータベースクエリのツールtheme-dark-mode.tsx- ダークモード切り替え機能を備えたカスタムテーマ設定package.json- 正確な依存関係のバージョン
Next.js(4 個のテンプレート)
app/page.tsx- C1Chat ページコンポーネントapp/api/chat/route.ts- ストリーミング API ルートハンドラーtool-calling-route.ts- ツール連携機能を備えた API ルートpackage.json- Next.js の依存関係のセットアップ
Cloudflare Workers(3 個のテンプレート)
worker-backend.ts- TheSys プロキシを使用する Hono APIfrontend-setup.tsx- React フロントエンドの設定wrangler.jsonc- Worker のデプロイ設定
共通ユーティリティ(3 個のテンプレート)
theme-config.ts- 再利用可能なテーマ設定tool-schemas.ts- ツール向けの共通 Zod スキーマstreaming-utils.ts- ストリーミング用ヘルパー関数
追加リソース
リファレンスガイド
詳細なガイドについては、references/ ディレクトリを参照してください。
component-api.md- すべてのコンポーネントの props に関する完全なリファレンスai-provider-setup.md- 各 AI プロバイダーの手順を追ったセットアップガイドtool-calling-guide.md- ツール呼び出しパターンの詳細ガイドtheme-customization.md- テーマシステムの詳細解説common-errors.md- 解決策を含む、拡充されたエラー一覧
スクリプト
scripts/install-dependencies.sh- 必要なパッケージをすべてインストールしますscripts/check-versions.sh- パッケージのバージョンを確認します
公式ドキュメント
- TheSys ドキュメント:https://docs.thesys.dev
- C1 プレイグラウンド:https://console.thesys.dev/playground
- GitHub のサンプル:GitHub で「thesysai」を検索してください
- Context7:
/websites/thesys_dev
成功指標
- トークンの節約:約 65~70%(手動実装との比較)
- 防止できるエラー:12 件以上の問題を記録
- 開発速度:10 倍高速(TheSys による)
- ユーザーエンゲージメント:83%がインタラクティブ UI を好む
- パッケージバージョン:最新の安定版(2025 年 10 月)
次のステップ
- フレームワークを選択します(Vite+React、Next.js、または Cloudflare Workers)
templates/から該当するテンプレートをコピーしますTHESYS_API_KEY環境変数を設定しますnpm installで依存関係をインストールします- 開発サーバーを起動します
- テーマと UI コンポーネントをカスタマイズします
- 高度な機能を使うため、ツール呼び出しを追加します
- 適切な永続化機能を設定して、本番環境にデプロイします
質問や問題については、references/common-errors.md ガイドを参照するか、TheSys の公式ドキュメントを確認してください。
最終更新日:2025-10-26 パッケージバージョン:@thesysai/[email protected] 本番環境でのテスト済み:✅ はい 公式標準に準拠:✅ はい