@gensx/react
Version:
React hooks and components for GenSX AI workflows.
480 lines (380 loc) • 11.4 kB
Markdown
# /react - React Hooks for GenSX
React hooks for interacting with GenSX workflows.
## Installation
```bash
npm install /react
```
## useWorkflow Hook
A React hook for interacting with GenSX workflows.
```typescript
import { useWorkflow } from "@gensx/react";
```
### Features
- **Client-compatible interface**: Accepts the same parameters as the GenSX Client
- **Two modes**: Collection (wait for complete output) and Streaming (real-time chunks)
- **Default configuration**: Set defaults that can be overridden per call
- **Event handling**: Callbacks for all GenSX event types
- **State management**: Tracks loading, errors, output, and progress
- **Abort support**: Stop workflows in progress
- **TypeScript**: Full type safety with GenSX event types
### Basic Usage
```tsx
import { useWorkflow } from "@gensx/react";
function MyComponent() {
const gensx = useWorkflow({
config: {
baseUrl: "/api/gensx",
},
onComplete: (output) => console.log("Done:", output),
});
// Run workflow - streams in real-time
const handleRun = async () => {
await gensx.run({
inputs: { userMessage: "Hello!" },
});
// Output is available in gensx.output as it streams
};
// Override org/project for specific calls
const handleCustom = async () => {
await gensx.run({
org: "different-org",
project: "different-project",
inputs: { data: "custom data" },
});
};
return (
<div>
<button onClick={handleRun}>Run</button>
{gensx.inProgress && <p>Loading...</p>}
{gensx.error && <p>Error: {gensx.error}</p>}
<div>{gensx.output}</div>
</div>
);
}
```
### Hook Options
```typescript
interface WorkflowConfig {
baseUrl: string; // Your API base URL
headers?: Record<string, string>; // Optional request headers
}
interface UseWorkflowConfig<TOutput = unknown> {
config: WorkflowConfig; // All workflow configuration
onStart?: (message: string) => void;
onComplete?: (output: TOutput | null) => void;
onError?: (error: string) => void;
onEvent?: (event: WorkflowMessage) => void;
outputTransformer?: (accumulatedContent: string) => TOutput;
}
interface WorkflowRunConfig<TInputs = unknown> {
inputs: TInputs;
org?: string; // Override org for this run
project?: string; // Override project for this run
environment?: string; // Override environment for this run
}
```
### Return Values
```typescript
interface UseWorkflowResult<TInputs = unknown, TOutput = unknown> {
inProgress: boolean; // Workflow is running
error: string | null; // Error message if any
output: TOutput | null; // Final output (accumulated from stream)
execution: WorkflowMessage[]; // All events received
run: (config: WorkflowRunConfig<TInputs>) => Promise<void>;
stop: () => void;
clear: () => void; // Clear all workflow state
}
```
## True Passthrough API
Your API endpoint can be a simple passthrough to GenSX:
```typescript
// app/api/gensx/route.ts
import { GenSX } from "@gensx/client";
export async function POST(request: Request) {
const body = await request.json();
const { workflowName, org, project, environment, ...inputs } = body;
const gensx = new GenSX({
apiKey: process.env.GENSX_API_KEY!,
});
// Stream the response
const response = await gensx.runRaw(workflowName, {
org,
project,
environment,
inputs,
});
// Return the streaming response
return new Response(response.body, {
headers: {
"Content-Type": "application/x-ndjson",
},
});
}
```
### With Environment Defaults
Your API can provide defaults from environment variables:
```typescript
export async function POST(request: Request) {
const body = await request.json();
const { workflowName, org, project, environment, ...inputs } = body;
// Use environment defaults if not provided
const finalOrg = org || process.env.GENSX_ORG;
const finalProject = project || process.env.GENSX_PROJECT;
const finalEnvironment = environment || process.env.GENSX_ENVIRONMENT;
const gensx = new GenSX({
apiKey: process.env.GENSX_API_KEY!,
});
const response = await gensx.runRaw(workflowName, {
org: finalOrg,
project: finalProject,
environment: finalEnvironment,
inputs,
});
return new Response(response.body, {
headers: { "Content-Type": "application/x-ndjson" },
});
}
```
## GenSX Event Types
```typescript
type WorkflowMessage =
| { type: "start"; workflowName: string }
| { type: "data"; data: JsonValue }
| { type: "event"; label: string; data: JsonValue }
| { type: "end" }
| { type: "error"; error: string }
| { type: "object"; label: string; patches: Operation[]; isInitial: boolean };
```
## Examples
See `src/examples/gensx-example.tsx` for a complete example component.
## Legacy Hook
The original `useWorkflow` hook is still available for backwards compatibility but we recommend using `useWorkflow` for new projects.
## UI Package
React components and hooks for the monorepo.
## Hooks
### useWorkflow
A React hook for interacting with GenSX workflows via your API endpoint. Supports TypeScript generics for type-safe outputs.
**Key Features:**
- ✨ Real-time output updates during streaming
- 🔄 Two modes: Collection (wait for complete output) and Streaming (real-time chunks)
- 📝 TypeScript support with generics
- 🎯 Event callbacks for all workflow events
- 🚀 Automatic output accumulation
```typescript
import { useWorkflow } from "@gensx/react";
// Basic usage
const gensx = useWorkflow({
config: {
baseUrl: "/api/gensx",
},
});
// With type-safe output
interface ChatResponse {
message: string;
confidence: number;
}
const gensx = useWorkflow<UpdateDraftInput, ChatResponse>({
config: {
baseUrl: "/api/gensx",
},
onComplete: (output) => {
// output is typed as ChatResponse | null
if (output) {
console.log(output.message);
console.log(output.confidence);
}
},
});
// Real-time streaming with automatic output accumulation
const gensx = useWorkflow<UpdateDraftInput, string>({
config: {
baseUrl: "/api/gensx",
},
onEvent: (event) => {
// Called for each event
if (event.type === "output") {
console.log("New chunk:", event.content);
}
},
});
// During streaming, gensx.output is updated in real-time
await gensx.run({ inputs: { message: "Hello" } });
// gensx.output contains the accumulated text as it streams
```
#### Hook Options
```typescript
interface UseWorkflowConfig<TInputs = unknown, TOutput = unknown> {
config: WorkflowConfig;
onStart?: (message: string) => void;
onComplete?: (output: TOutput) => void;
onError?: (error: string) => void;
onEvent?: (event: WorkflowMessage) => void;
}
```
#### Hook Return Value
```typescript
interface UseWorkflowResult<TInputs = unknown, TOutput = unknown> {
inProgress: boolean;
error: string | null;
output: TOutput | null;
execution: WorkflowMessage[];
run: (config: WorkflowRunConfig<TInputs>) => Promise<void>;
stop: () => void;
clear: () => void;
}
```
#### Examples
**Streaming with Typed Output:**
```typescript
interface DraftResponse {
content: string;
wordCount: number;
}
const gensx = useWorkflow<UpdateDraftInput, DraftResponse>({
config: {
baseUrl: "/api/gensx",
},
onComplete: (output) => {
if (output) {
setDraft(output.content);
setWordCount(output.wordCount);
}
},
});
await gensx.run({
inputs: { userMessage: "Make it shorter" },
});
// Access the output as it streams
console.log("Current output:", gensx.output);
```
**With Progress Updates:**
```typescript
const gensx = useWorkflow<StoryInput, string>({
config: {
baseUrl: "/api/gensx",
},
onEvent: (event) => {
if (event.type === "output") {
console.log("New chunk:", event.content);
}
},
onComplete: (finalOutput) => {
console.log("Complete:", finalOutput);
},
});
await gensx.run({
inputs: { prompt: "Tell me a story" },
});
```
**With Configuration Overrides:**
```typescript
const gensx = useWorkflow({
config: {
baseUrl: "/api/gensx",
headers: {
"X-Custom-Header": "value",
},
},
});
// Basic run with org/project/environment
await gensx.run({
inputs: { data: "test" },
org: "my-org",
project: "my-project",
environment: "my-env",
});
// Override org/project/environment for specific run
await gensx.run({
org: "different-org",
project: "different-project",
inputs: { data: "test" },
});
```
## useObject Hook
A React hook for accessing streaming object state from GenSX workflows. Now supports efficient JSON patch-based updates for improved performance.
### Features
- **JSON Patch Support**: Efficiently handles incremental object updates using JSON patches
- **String Optimizations**: Automatic optimizations for streaming text scenarios (string-append, string-diff)
- **Real-time Updates**: Object state updates in real-time as patches are received
- **Error Handling**: Graceful error handling with fallback to previous state
- **Type Safety**: Full TypeScript support with generics
### Basic Usage
```tsx
import { useWorkflow, useObject } from "@gensx/react";
function MyComponent() {
const workflow = useWorkflow({
config: { baseUrl: "/api/gensx" },
});
// Get streaming object state
const userProfile = useObject(workflow.execution, "user-profile");
const chatResponse = useObject(workflow.execution, "chat-response");
return (
<div>
<h1>User: {userProfile?.name}</h1>
<p>Age: {userProfile?.age}</p>
<div>
<h2>Chat Response:</h2>
<p>{chatResponse?.content}</p>
</div>
</div>
);
}
```
### Streaming LLM Response
Perfect for streaming LLM responses where text is progressively built up:
```tsx
function StreamingChat() {
const workflow = useWorkflow({
config: { baseUrl: "/api/chat" },
});
const chatResponse = useObject(workflow.execution, "chat-response");
return (
<div>
<button onClick={() => workflow.run({ inputs: { message: "Hello!" } })}>
Send Message
</button>
{chatResponse && (
<div className="chat-bubble">{chatResponse.content}</div>
)}
</div>
);
}
```
### Performance Benefits
The hook automatically handles JSON patch operations for optimal performance:
- **String Append**: Efficiently appends text for streaming scenarios
- **String Diff**: Character-level diffing for complex string changes
- **Incremental Updates**: Only changed parts of objects are transmitted
- **Reduced Bandwidth**: Up to 55% reduction in message size for streaming content
### Type Safety
Use TypeScript generics for type-safe object access:
```tsx
interface ChatResponse {
content: string;
timestamp: number;
isComplete: boolean;
}
const chatResponse = useObject<ChatResponse>(
workflow.execution,
"chat-response",
);
// chatResponse is typed as ChatResponse | undefined
```
## useEvents Hook
A React hook for accessing all events with a specific label from GenSX workflows.
```tsx
import { useEvents } from "@gensx/react";
function ProgressComponent() {
const workflow = useWorkflow({
config: { baseUrl: "/api/gensx" },
});
const progressEvents = useEvents(workflow.execution, "progress");
return (
<div>
{progressEvents.map((event, index) => (
<div key={index}>{event.message}</div>
))}
</div>
);
}
```