NextJS
Get your first full stack agent running in 5 minutes with npm.
Installation
npm i @cascaide-ts/core @cascaide-ts/react @cascaide-ts/postgres-js @cascaide-ts/server-next
Cascaide
Starting a New project
Setting Up in an Existing project
1. Set Enironment Variables
OPENAI_API_KEY='your-key'
DATABASE_URL='your-url'
2. Set Up Postgres DB for Durability and Connection
- Schema:
I am using prisma here, feel free to use whatever.
npm i prisma
generator client {
provider = "prisma-client-js"
output = "../lib/generated/prisma"
binaryTargets = ["native", "rhel-openssl-3.0.x"]
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
enum CascadeStatus {
RUNNING
COMPLETED
ERROR
}
enum ExecutionStatus {
RUNNING
COMPLETED
FAILED
}
model Cascade {
id String @id @default(uuid())
status CascadeStatus @default(RUNNING)
// Use @map to ensure the DB column is lowercase snake_case
FnId Int @default(0) @map("fn_id")
userId String @map("user_id")
executions NodeExecution[]
contextEvents ContextEvent[]
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@index([userId, status])
@@map("cascades")
}
model NodeExecution {
id String @id @default(uuid())
nodeInstanceId String @unique @map("node_instance_id")
cascadeId String @map("cascade_id")
cascade Cascade @relation(fields: [cascadeId], references: [id], onDelete: Cascade)
nodeName String @map("node_name")
functionId Int @map("function_id")
inputContext Json @map("input_context")
fullOutput Json? @map("full_output")
location String
status ExecutionStatus @default(RUNNING)
error String?
startedAt DateTime @default(now()) @map("started_at")
completedAt DateTime? @map("completed_at")
@@unique([cascadeId, functionId])
@@map("node_executions")
}
model ContextEvent {
id Int @id @default(autoincrement())
key String
value Json
functionId Int @map("function_id")
cascadeId String @map("cascade_id")
cascade Cascade @relation(fields: [cascadeId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now()) @map("created_at")
@@index([cascadeId, functionId])
@@map("context_events")
}- Set up the db and geenrate prisma client
npx prisma db push
- Create Sql for persistor
// /lib/db/connection.ts
import postgres from 'postgres';
const connectionString = process.env.DATABASE_URL!;
// Use a singleton pattern for the SQL instance
const globalForSql = global as unknown as { sql: postgres.Sql<{}> };
export const sql = globalForSql.sql || postgres(connectionString, {
max: process.env.NODE_ENV === 'production' ? 10 : 1, // Adjust for serverless
idle_timeout: 20,
connect_timeout: 10,
});
if (process.env.NODE_ENV !== 'production') globalForSql.sql = sql;3. Define Graphs and Configurations
You can copy paste these graphs as a starting point. Check out prebuilt UI components Cascaide Chat UI. It is quite easy to build UI components in cascaide. You control graph execution with the useWorkflow hook and observe with useCascade hook. Check out concepts.
- Client Side Client side confuration has a clientWorkflowGraph and uiComponentRegistry.
// src/components/home/clientConfig.ts
import Chat from "@/components/cascaide-ui/chat";
import { ClientWorkflowConfig } from "@cascaide-ts/react";
export const ClientWorkflowConfig: ClientWorkflowConfig = {
clientWorkflowGraph: {
chat: { name: 'chat', isUINode: true },
// non-UI nodes -> metadata only
ReActAgentNode: { name: 'ReActAgentNode', isUINode: false, isStreaming: true },
ReActToolNode: { name: 'ReActToolNode', isUINode: false, isStreaming: false },
},
uiComponentRegistry: {
chat: Chat,
},
};- Server Side
Check out graphs and configuration section to learn how to define these. You can copy paste this exact ReAct agent from the tutorial there.
// shared/workflowGraph.ts
import { ReActAgentNodeExec, ReActAgentNodePost, ReActAgentNodeFn, ReActAgentNodeSelector, ReActToolNodeExec, ReActToolNodePost, ReActToolNodePrep, ReAcTToolNodeSelector } from '@/components/agents/ReActAgentNodes';
import { ServerWorkflowGraph } from '@cascaide-ts/core';
export const serverWorkflowGraph: ServerWorkflowGraph = {
ReActAgentNode: {
name: 'ReActAgentNode',
prep: { selector: ReActAgentNodeSelector, fn: ReActAgentNodeFn },
exec: ReActAgentNodeExec,
post: ReActAgentNodePost,
isStreaming: true,
isUINode:false
},
ReActToolNode: {
name: 'ReActToolNode',
prep: { selector: ReActToolNodeSelector, fn: ReActToolNodeFn },
exec: ReActToolNodeExec,
post: ReActToolNodePost,
isStreaming: false,
isUINode:false,
},
};4. Define API Routes
- Action Relay Endpoint
// api/workflow/action/route.ts
import { serverWorkflowGraph } from '@/shared/workflowGraph';
import { createWorkflowHandler, WorkflowHandlerConfig } from '@cascaide-ts/server-next';
import {PostgresPersistor} from '@cascaide-ts/postgres-js'
import { sql } from '@/lib/persistence/connection';
// 1. Instantiate the logic provider
const workflowpersistor = new PostgresPersistor(sql);
const MAX_EXECUTION_TIME = 300000;
const SAFE_BUFFER = 6000;
const workflowConfig:WorkflowHandlerConfig={
workflowGraph:serverWorkflowGraph,
persistor: workflowpersistor, // pass your PostgresPersistor here
maxExecutionTime: MAX_EXECUTION_TIME,
safeBuffer: SAFE_BUFFER
}
export const POST = createWorkflowHandler(workflowConfig)
- Persistence Endpoint
// api/workflow/persistence/route.ts
import { createPersistenceHandler } from '@cascaide-ts/server-next';
import {PostgresPersistor} from '@cascaide-ts/postgres-js'
import { sql } from '@/lib/persistence/connection';
// 1. Instantiate the logic provider
const persistor = new PostgresPersistor(sql);
// 2. Export the POST handler generated by the factory
export const POST = createPersistenceHandler(persistor);
- Hydration Endpoint
// api/workflow/hydration/route.ts
import { createHydrationHandler } from '@cascaide-ts/server-next';
import {PostgresPersistor} from '@cascaide-ts/postgres-js'
import { sql } from '@/lib/persistence/connection';
// 1. Instantiate the logic provider
const persistor = new PostgresPersistor(sql);
// 2. Export the POST handler generated by the factory
export const POST = createHydrationHandler(persistor);
5. Set Up WorkflowProvider and WorkflowRenderer
Set up the provider and renderer with the configurations and endpoints described above
'use client'
import {WorkflowProvider,WorkflowRenderer} from '@cascaide-ts/react';
//import { WorkflowRenderer } from '@/components/workflowRenderer';
import { clientWorkflowConfig } from '@/app/clientConfig';
export default function HomePage() {
return (
<WorkflowProvider
initialNodeId="reactAgent_init"
initialNodeName="reactAgent"
config={homeWorkflowConfig}
actionRelayEndpoint='/api/workflow/action'
persistenceEndpoint='/api/workflow/persistence'
hydrationEndpoint='/api/workflow/hydration'
>
<WorkflowRenderer />
</WorkflowProvider>
);
}6. Run!
npm run dev
Ask a question and open redux devtools to observe.
Cascaide Lite
Starting a New project
Setting Up in an Existing project
1. Set Enironment Variables
OPENAI_API_KEY='your-key'2. Define Graphs and configurations
This step is identical to Cascaide. However, the agent and UI implementation changes slightly.
- In lite, selector step becomes an empty shell, you will have to carry around agent traces between nodes
- Chat UI changes in the sense that when activating a node, you have to send the full hsitory
3. Set Up API Routes
In Casacide Lite, you only setup the action relay endpoint. Unlike Cascaide, you set persistor as null.
// api/workflow/action/route.ts
import { serverWorkflowGraph } from '@/shared/workflowGraph';
import { createWorkflowHandler, WorkflowHandlerConfig } from '@cascaide-ts/server-next';
const MAX_EXECUTION_TIME = 300000;
const SAFE_BUFFER = 6000;
const workflowConfig:WorkflowHandlerConfig={
workflowGraph:serverWorkflowGraph,
persistor: null, // set as null, the framework will infer that you are using lite
maxExecutionTime: MAX_EXECUTION_TIME,
safeBuffer: SAFE_BUFFER
}
export const POST = createWorkflowHandler(workflowConfig)
4. Set Up WorkflowProvider and WorkflowRenderer
Similar to Casaide, except you only pass in action relay endpoint.
'use client'
import {WorkflowProvider,WorkflowRenderer} from '@cascaide-ts/react';
//import { WorkflowRenderer } from '@/components/workflowRenderer';
import { clientWorkflowConfig } from '@/app/clientConfig';
export default function HomePage() {
return (
<WorkflowProvider
initialNodeId="reactAgent_init"
initialNodeName="reactAgent"
config={homeWorkflowConfig}
actionRelayEndpoint='/api/workflow/action'
>
<WorkflowRenderer />
</WorkflowProvider>
);
}5. Run!
npm run devOpen redux devtools for observability.